Annotation of embedaddon/php/ext/phar/dirstream.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | phar:// stream wrapper support                                       |
                      4:   +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:   | Copyright (c) 2005-2014 The PHP Group                                |
1.1       misho       6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.01 of the PHP license,      |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_01.txt.                                 |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Authors: Gregory Beaver <cellog@php.net>                             |
                     16:   |          Marcus Boerger <helly@php.net>                              |
                     17:   +----------------------------------------------------------------------+
                     18: */
                     19: 
                     20: #define PHAR_DIRSTREAM 1
                     21: #include "phar_internal.h"
                     22: #include "dirstream.h"
                     23: 
                     24: BEGIN_EXTERN_C()
                     25: void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_dir TSRMLS_DC);
                     26: END_EXTERN_C()
                     27: 
                     28: php_stream_ops phar_dir_ops = {
                     29:        phar_dir_write, /* write */
                     30:        phar_dir_read,  /* read  */
                     31:        phar_dir_close, /* close */
                     32:        phar_dir_flush, /* flush */
                     33:        "phar dir",
                     34:        phar_dir_seek,  /* seek */
                     35:        NULL,           /* cast */
                     36:        NULL,           /* stat */
                     37:        NULL, /* set option */
                     38: };
                     39: 
                     40: /**
                     41:  * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle
                     42:  */
                     43: static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC)  /* {{{ */
                     44: {
                     45:        HashTable *data = (HashTable *)stream->abstract;
                     46: 
                     47:        if (data && data->arBuckets) {
                     48:                zend_hash_destroy(data);
                     49:                data->arBuckets = 0;
                     50:                FREE_HASHTABLE(data);
                     51:                stream->abstract = NULL;
                     52:        }
                     53: 
                     54:        return 0;
                     55: }
                     56: /* }}} */
                     57: 
                     58: /**
                     59:  * Used for seeking on a phar directory handle
                     60:  */
                     61: static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
                     62: {
                     63:        HashTable *data = (HashTable *)stream->abstract;
                     64: 
                     65:        if (!data) {
                     66:                return -1;
                     67:        }
                     68: 
                     69:        if (whence == SEEK_END) {
                     70:                whence = SEEK_SET;
                     71:                offset = zend_hash_num_elements(data) + offset;
                     72:        }
                     73: 
                     74:        if (whence == SEEK_SET) {
                     75:                zend_hash_internal_pointer_reset(data);
                     76:        }
                     77: 
                     78:        if (offset < 0) {
                     79:                return -1;
                     80:        } else {
                     81:                *newoffset = 0;
                     82:                while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
                     83:                        ++(*newoffset);
                     84:                }
                     85:                return 0;
                     86:        }
                     87: }
                     88: /* }}} */
                     89: 
                     90: /**
                     91:  * Used for readdir() on an opendir()ed phar directory handle
                     92:  */
                     93: static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
                     94: {
                     95:        size_t to_read;
                     96:        HashTable *data = (HashTable *)stream->abstract;
                     97:        phar_zstr key;
                     98:        char *str_key;
                     99:        uint keylen;
                    100:        ulong unused;
                    101: 
                    102:        if (FAILURE == zend_hash_has_more_elements(data)) {
                    103:                return 0;
                    104:        }
                    105: 
                    106:        if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) {
                    107:                return 0;
                    108:        }
                    109: 
                    110:        PHAR_STR(key, str_key);
                    111:        zend_hash_move_forward(data);
                    112:        to_read = MIN(keylen, count);
                    113: 
                    114:        if (to_read == 0 || count < keylen) {
                    115:                PHAR_STR_FREE(str_key);
                    116:                return 0;
                    117:        }
                    118: 
                    119:        memset(buf, 0, sizeof(php_stream_dirent));
                    120:        memcpy(((php_stream_dirent *) buf)->d_name, str_key, to_read);
                    121:        PHAR_STR_FREE(str_key);
                    122:        ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
                    123: 
                    124:        return sizeof(php_stream_dirent);
                    125: }
                    126: /* }}} */
                    127: 
                    128: /**
                    129:  * Dummy: Used for writing to a phar directory (i.e. not used)
                    130:  */
                    131: static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
                    132: {
                    133:        return 0;
                    134: }
                    135: /* }}} */
                    136: 
                    137: /**
                    138:  * Dummy: Used for flushing writes to a phar directory (i.e. not used)
                    139:  */
                    140: static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */
                    141: {
                    142:        return EOF;
                    143: }
                    144: /* }}} */
                    145: 
                    146: /**
                    147:  * add an empty element with a char * key to a hash table, avoiding duplicates
                    148:  *
                    149:  * This is used to get a unique listing of virtual directories within a phar,
                    150:  * for iterating over opendir()ed phar directories.
                    151:  */
                    152: static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength)  /* {{{ */
                    153: {
                    154:        void *dummy = (char *) 1;
                    155: 
                    156:        return zend_hash_update(ht, arKey, nKeyLength, (void *) &dummy, sizeof(void *), NULL);
                    157: }
                    158: /* }}} */
                    159: 
                    160: /**
                    161:  * Used for sorting directories alphabetically
                    162:  */
                    163: static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC)  /* {{{ */
                    164: {
                    165:        Bucket *f;
                    166:        Bucket *s;
                    167:        int result;
                    168: 
                    169:        f = *((Bucket **) a);
                    170:        s = *((Bucket **) b);
                    171:        result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
                    172: 
                    173:        if (result < 0) {
                    174:                return -1;
                    175:        } else if (result > 0) {
                    176:                return 1;
                    177:        } else {
                    178:                return 0;
                    179:        }
                    180: }
                    181: /* }}} */
                    182: 
                    183: /**
                    184:  * Create a opendir() directory stream handle by iterating over each of the
                    185:  * files in a phar and retrieving its relative path.  From this, construct
                    186:  * a list of files/directories that are "in" the directory represented by dir
                    187:  */
                    188: static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */
                    189: {
                    190:        HashTable *data;
                    191:        int dirlen = strlen(dir);
                    192:        phar_zstr key;
                    193:        char *entry, *found, *save, *str_key;
                    194:        uint keylen;
                    195:        ulong unused;
                    196: 
                    197:        ALLOC_HASHTABLE(data);
                    198:        zend_hash_init(data, 64, zend_get_hash_value, NULL, 0);
                    199: 
                    200:        if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) {
                    201:                /* make empty root directory for empty phar */
                    202:                /* make empty directory for .phar magic directory */
                    203:                efree(dir);
                    204:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    205:        }
                    206: 
                    207:        zend_hash_internal_pointer_reset(manifest);
                    208: 
                    209:        while (FAILURE != zend_hash_has_more_elements(manifest)) {
                    210:                if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
                    211:                        break;
                    212:                }
                    213: 
                    214:                PHAR_STR(key, str_key);
                    215: 
                    216:                if (keylen <= (uint)dirlen) {
                    217:                        if (keylen < (uint)dirlen || !strncmp(str_key, dir, dirlen)) {
                    218:                                PHAR_STR_FREE(str_key);
                    219:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    220:                                        break;
                    221:                                }
                    222:                                continue;
                    223:                        }
                    224:                }
                    225: 
                    226:                if (*dir == '/') {
                    227:                        /* root directory */
                    228:                        if (keylen >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
                    229:                                PHAR_STR_FREE(str_key);
                    230:                                /* do not add any magic entries to this directory */
                    231:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    232:                                        break;
                    233:                                }
                    234:                                continue;
                    235:                        }
                    236: 
                    237:                        if (NULL != (found = (char *) memchr(str_key, '/', keylen))) {
                    238:                                /* the entry has a path separator and is a subdirectory */
                    239:                                entry = (char *) safe_emalloc(found - str_key, 1, 1);
                    240:                                memcpy(entry, str_key, found - str_key);
                    241:                                keylen = found - str_key;
                    242:                                entry[keylen] = '\0';
                    243:                        } else {
                    244:                                entry = (char *) safe_emalloc(keylen, 1, 1);
                    245:                                memcpy(entry, str_key, keylen);
                    246:                                entry[keylen] = '\0';
                    247:                        }
                    248: 
                    249:                        PHAR_STR_FREE(str_key);
                    250:                        goto PHAR_ADD_ENTRY;
                    251:                } else {
                    252:                        if (0 != memcmp(str_key, dir, dirlen)) {
                    253:                                /* entry in directory not found */
                    254:                                PHAR_STR_FREE(str_key);
                    255:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    256:                                        break;
                    257:                                }
                    258:                                continue;
                    259:                        } else {
                    260:                                if (str_key[dirlen] != '/') {
                    261:                                        PHAR_STR_FREE(str_key);
                    262:                                        if (SUCCESS != zend_hash_move_forward(manifest)) {
                    263:                                                break;
                    264:                                        }
                    265:                                        continue;
                    266:                                }
                    267:                        }
                    268:                }
                    269: 
                    270:                save = str_key;
                    271:                save += dirlen + 1; /* seek to just past the path separator */
                    272: 
                    273:                if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
                    274:                        /* is subdirectory */
                    275:                        save -= dirlen + 1;
                    276:                        entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
                    277:                        memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
                    278:                        keylen = found - save - dirlen - 1;
                    279:                        entry[keylen] = '\0';
                    280:                } else {
                    281:                        /* is file */
                    282:                        save -= dirlen + 1;
                    283:                        entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
                    284:                        memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
                    285:                        entry[keylen - dirlen - 1] = '\0';
                    286:                        keylen = keylen - dirlen - 1;
                    287:                }
                    288:                PHAR_STR_FREE(str_key);
                    289: PHAR_ADD_ENTRY:
                    290:                if (keylen) {
                    291:                        phar_add_empty(data, entry, keylen);
                    292:                }
                    293: 
                    294:                efree(entry);
                    295: 
                    296:                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    297:                        break;
                    298:                }
                    299:        }
                    300: 
                    301:        if (FAILURE != zend_hash_has_more_elements(data)) {
                    302:                efree(dir);
                    303:                if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) {
                    304:                        FREE_HASHTABLE(data);
                    305:                        return NULL;
                    306:                }
                    307:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    308:        } else {
                    309:                efree(dir);
                    310:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    311:        }
                    312: }
                    313: /* }}}*/
                    314: 
                    315: /**
                    316:  * Open a directory handle within a phar archive
                    317:  */
                    318: php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
                    319: {
                    320:        php_url *resource = NULL;
                    321:        php_stream *ret;
                    322:        char *internal_file, *error, *str_key;
                    323:        phar_zstr key;
                    324:        uint keylen;
                    325:        ulong unused;
                    326:        phar_archive_data *phar;
                    327:        phar_entry_info *entry = NULL;
                    328:        uint host_len;
                    329: 
                    330:        if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
                    331:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar url \"%s\" is unknown", path);
                    332:                return NULL;
                    333:        }
                    334: 
                    335:        /* we must have at the very least phar://alias.phar/ */
                    336:        if (!resource->scheme || !resource->host || !resource->path) {
                    337:                if (resource->host && !resource->path) {
                    338:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, resource->host);
                    339:                        php_url_free(resource);
                    340:                        return NULL;
                    341:                }
                    342:                php_url_free(resource);
                    343:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
                    344:                return NULL;
                    345:        }
                    346: 
                    347:        if (strcasecmp("phar", resource->scheme)) {
                    348:                php_url_free(resource);
                    349:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path);
                    350:                return NULL;
                    351:        }
                    352: 
                    353:        host_len = strlen(resource->host);
                    354:        phar_request_initialize(TSRMLS_C);
                    355:        internal_file = resource->path + 1; /* strip leading "/" */
                    356: 
                    357:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    358:                if (error) {
                    359:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
                    360:                        efree(error);
                    361:                } else {
                    362:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar file \"%s\" is unknown", resource->host);
                    363:                }
                    364:                php_url_free(resource);
                    365:                return NULL;
                    366:        }
                    367: 
                    368:        if (error) {
                    369:                efree(error);
                    370:        }
                    371: 
                    372:        if (*internal_file == '\0') {
                    373:                /* root directory requested */
                    374:                internal_file = estrndup(internal_file - 1, 1);
                    375:                ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    376:                php_url_free(resource);
                    377:                return ret;
                    378:        }
                    379: 
                    380:        if (!phar->manifest.arBuckets) {
                    381:                php_url_free(resource);
                    382:                return NULL;
                    383:        }
                    384: 
                    385:        if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry) && !entry->is_dir) {
                    386:                php_url_free(resource);
                    387:                return NULL;
                    388:        } else if (entry && entry->is_dir) {
                    389:                if (entry->is_mounted) {
                    390:                        php_url_free(resource);
                    391:                        return php_stream_opendir(entry->tmp, options, context);
                    392:                }
                    393:                internal_file = estrdup(internal_file);
                    394:                php_url_free(resource);
                    395:                return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    396:        } else {
                    397:                int i_len = strlen(internal_file);
                    398: 
                    399:                /* search for directory */
                    400:                zend_hash_internal_pointer_reset(&phar->manifest);
                    401:                while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
                    402:                        if (HASH_KEY_NON_EXISTANT != 
                    403:                                        zend_hash_get_current_key_ex(
                    404:                                                &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
                    405:                                PHAR_STR(key, str_key);
                    406:                                if (keylen > (uint)i_len && 0 == memcmp(str_key, internal_file, i_len)) {
                    407:                                        PHAR_STR_FREE(str_key);
                    408:                                        /* directory found */
                    409:                                        internal_file = estrndup(internal_file,
                    410:                                                        i_len);
                    411:                                        php_url_free(resource);
                    412:                                        return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    413:                                }
                    414:                                PHAR_STR_FREE(str_key);
                    415:                        }
                    416: 
                    417:                        if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
                    418:                                break;
                    419:                        }
                    420:                }
                    421:        }
                    422: 
                    423:        php_url_free(resource);
                    424:        return NULL;
                    425: }
                    426: /* }}} */
                    427: 
                    428: /**
                    429:  * Make a new directory within a phar archive
                    430:  */
                    431: int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
                    432: {
                    433:        phar_entry_info entry, *e;
                    434:        phar_archive_data *phar = NULL;
                    435:        char *error, *arch, *entry2;
                    436:        int arch_len, entry_len;
                    437:        php_url *resource = NULL;
                    438:        uint host_len;
                    439: 
                    440:        /* pre-readonly check, we need to know if this is a data phar */
                    441:        if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
                    442:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
                    443:                return 0;
                    444:        }
                    445: 
                    446:        if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
                    447:                phar = NULL;
                    448:        }
                    449: 
                    450:        efree(arch);
                    451:        efree(entry2);
                    452: 
                    453:        if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
                    454:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", write operations disabled", url_from);
                    455:                return 0;
                    456:        }
                    457: 
                    458:        if ((resource = phar_parse_url(wrapper, url_from, "w", options TSRMLS_CC)) == NULL) {
                    459:                return 0;
                    460:        }
                    461: 
                    462:        /* we must have at the very least phar://alias.phar/internalfile.php */
                    463:        if (!resource->scheme || !resource->host || !resource->path) {
                    464:                php_url_free(resource);
                    465:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from);
                    466:                return 0;
                    467:        }
                    468: 
                    469:        if (strcasecmp("phar", resource->scheme)) {
                    470:                php_url_free(resource);
                    471:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from);
                    472:                return 0;
                    473:        }
                    474: 
                    475:        host_len = strlen(resource->host);
                    476: 
                    477:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    478:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error);
                    479:                efree(error);
                    480:                php_url_free(resource);
                    481:                return 0;
                    482:        }
                    483: 
                    484:        if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error, 1 TSRMLS_CC))) {
                    485:                /* directory exists, or is a subdirectory of an existing file */
                    486:                if (e->is_temp_dir) {
                    487:                        efree(e->filename);
                    488:                        efree(e);
                    489:                }
                    490:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", resource->path+1, resource->host);
                    491:                php_url_free(resource);
                    492:                return 0;
                    493:        }
                    494: 
                    495:        if (error) {
                    496:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
                    497:                efree(error);
                    498:                php_url_free(resource);
                    499:                return 0;
                    500:        }
                    501: 
                    502:        if (phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC)) {
                    503:                /* entry exists as a file */
                    504:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host);
                    505:                php_url_free(resource);
                    506:                return 0;
                    507:        }
                    508: 
                    509:        if (error) {
                    510:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
                    511:                efree(error);
                    512:                php_url_free(resource);
                    513:                return 0;
                    514:        }
                    515: 
                    516:        memset((void *) &entry, 0, sizeof(phar_entry_info));
                    517: 
                    518:        /* strip leading "/" */
                    519:        if (phar->is_zip) {
                    520:                entry.is_zip = 1;
                    521:        }
                    522: 
                    523:        entry.filename = estrdup(resource->path + 1);
                    524: 
                    525:        if (phar->is_tar) {
                    526:                entry.is_tar = 1;
                    527:                entry.tar_type = TAR_DIR;
                    528:        }
                    529: 
                    530:        entry.filename_len = strlen(resource->path + 1);
                    531:        php_url_free(resource);
                    532:        entry.is_dir = 1;
                    533:        entry.phar = phar;
                    534:        entry.is_modified = 1;
                    535:        entry.is_crc_checked = 1;
                    536:        entry.flags = PHAR_ENT_PERM_DEF_DIR;
                    537:        entry.old_flags = PHAR_ENT_PERM_DEF_DIR;
                    538: 
                    539:        if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                    540:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", entry.filename, phar->fname);
                    541:                efree(error);
                    542:                efree(entry.filename);
                    543:                return 0;
                    544:        }
                    545: 
                    546:        phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
                    547: 
                    548:        if (error) {
                    549:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error);
                    550:                zend_hash_del(&phar->manifest, entry.filename, entry.filename_len);
                    551:                efree(error);
                    552:                return 0;
                    553:        }
                    554: 
                    555:        phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC);
                    556:        return 1;
                    557: }
                    558: /* }}} */
                    559: 
                    560: /**
                    561:  * Remove a directory within a phar archive
                    562:  */
                    563: int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
                    564: {
                    565:        phar_entry_info *entry;
                    566:        phar_archive_data *phar = NULL;
                    567:        char *error, *arch, *entry2;
                    568:        int arch_len, entry_len;
                    569:        php_url *resource = NULL;
                    570:        uint host_len;
                    571:        phar_zstr key;
                    572:        char *str_key;
                    573:        uint key_len;
                    574:        ulong unused;
                    575:        uint path_len;
                    576: 
                    577:        /* pre-readonly check, we need to know if this is a data phar */
                    578:        if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
                    579:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
                    580:                return 0;
                    581:        }
                    582: 
                    583:        if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
                    584:                phar = NULL;
                    585:        }
                    586: 
                    587:        efree(arch);
                    588:        efree(entry2);
                    589: 
                    590:        if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
                    591:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rmdir directory \"%s\", write operations disabled", url);
                    592:                return 0;
                    593:        }
                    594: 
                    595:        if ((resource = phar_parse_url(wrapper, url, "w", options TSRMLS_CC)) == NULL) {
                    596:                return 0;
                    597:        }
                    598: 
                    599:        /* we must have at the very least phar://alias.phar/internalfile.php */
                    600:        if (!resource->scheme || !resource->host || !resource->path) {
                    601:                php_url_free(resource);
                    602:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
                    603:                return 0;
                    604:        }
                    605: 
                    606:        if (strcasecmp("phar", resource->scheme)) {
                    607:                php_url_free(resource);
                    608:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
                    609:                return 0;
                    610:        }
                    611: 
                    612:        host_len = strlen(resource->host);
                    613: 
                    614:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    615:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error);
                    616:                efree(error);
                    617:                php_url_free(resource);
                    618:                return 0;
                    619:        }
                    620: 
                    621:        path_len = strlen(resource->path+1);
                    622: 
                    623:        if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, path_len, 2, &error, 1 TSRMLS_CC))) {
                    624:                if (error) {
                    625:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
                    626:                        efree(error);
                    627:                } else {
                    628:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", resource->path+1, resource->host);
                    629:                }
                    630:                php_url_free(resource);
                    631:                return 0;
                    632:        }
                    633: 
                    634:        if (!entry->is_deleted) {
                    635:                for (zend_hash_internal_pointer_reset(&phar->manifest);
                    636:                HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL);
                    637:                zend_hash_move_forward(&phar->manifest)) {
                    638: 
                    639:                        PHAR_STR(key, str_key);
                    640: 
                    641:                        if (key_len > path_len && 
                    642:                                memcmp(str_key, resource->path+1, path_len) == 0 && 
                    643:                                IS_SLASH(str_key[path_len])) {
                    644:                                PHAR_STR_FREE(str_key);
                    645:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
                    646:                                if (entry->is_temp_dir) {
                    647:                                        efree(entry->filename);
                    648:                                        efree(entry);
                    649:                                }
                    650:                                php_url_free(resource);
                    651:                                return 0;
                    652:                        }
                    653:                        PHAR_STR_FREE(str_key);
                    654:                }
                    655: 
                    656:                for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
                    657:                        HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL);
                    658:                        zend_hash_move_forward(&phar->virtual_dirs)) {
                    659:        
                    660:                        PHAR_STR(key, str_key);
                    661:        
                    662:                        if (key_len > path_len && 
                    663:                                memcmp(str_key, resource->path+1, path_len) == 0 && 
                    664:                                IS_SLASH(str_key[path_len])) {
                    665:                                PHAR_STR_FREE(str_key);
                    666:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
                    667:                                if (entry->is_temp_dir) {
                    668:                                        efree(entry->filename);
                    669:                                        efree(entry);
                    670:                                }
                    671:                                php_url_free(resource);
                    672:                                return 0;
                    673:                        }
                    674:                        PHAR_STR_FREE(str_key);
                    675:                }
                    676:        }
                    677: 
                    678:        if (entry->is_temp_dir) {
                    679:                zend_hash_del(&phar->virtual_dirs, resource->path+1, path_len);
                    680:                efree(entry->filename);
                    681:                efree(entry);
                    682:        } else {
                    683:                entry->is_deleted = 1;
                    684:                entry->is_modified = 1;
                    685:                phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
                    686: 
                    687:                if (error) {
                    688:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
                    689:                        php_url_free(resource);
                    690:                        efree(error);
                    691:                        return 0;
                    692:                }
                    693:        }
                    694: 
                    695:        php_url_free(resource);
                    696:        return 1;
                    697: }
                    698: /* }}} */
                    699: 
                    700: /*
                    701:  * Local variables:
                    702:  * tab-width: 4
                    703:  * c-basic-offset: 4
                    704:  * End:
                    705:  * vim600: noet sw=4 ts=4 fdm=marker
                    706:  * vim<600: noet sw=4 ts=4
                    707:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>