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

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | phar:// stream wrapper support                                       |
                      4:   +----------------------------------------------------------------------+
1.1.1.2 ! misho       5:   | Copyright (c) 2005-2013 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: #if (PHP_MAJOR_VERSION < 6)
                    172:        result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
                    173: #else
                    174:        result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength);
                    175: #endif
                    176: 
                    177:        if (result < 0) {
                    178:                return -1;
                    179:        } else if (result > 0) {
                    180:                return 1;
                    181:        } else {
                    182:                return 0;
                    183:        }
                    184: }
                    185: /* }}} */
                    186: 
                    187: /**
                    188:  * Create a opendir() directory stream handle by iterating over each of the
                    189:  * files in a phar and retrieving its relative path.  From this, construct
                    190:  * a list of files/directories that are "in" the directory represented by dir
                    191:  */
                    192: static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */
                    193: {
                    194:        HashTable *data;
                    195:        int dirlen = strlen(dir);
                    196:        phar_zstr key;
                    197:        char *entry, *found, *save, *str_key;
                    198:        uint keylen;
                    199:        ulong unused;
                    200: 
                    201:        ALLOC_HASHTABLE(data);
                    202:        zend_hash_init(data, 64, zend_get_hash_value, NULL, 0);
                    203: 
                    204:        if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) {
                    205:                /* make empty root directory for empty phar */
                    206:                /* make empty directory for .phar magic directory */
                    207:                efree(dir);
                    208:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    209:        }
                    210: 
                    211:        zend_hash_internal_pointer_reset(manifest);
                    212: 
                    213:        while (FAILURE != zend_hash_has_more_elements(manifest)) {
                    214:                if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
                    215:                        break;
                    216:                }
                    217: 
                    218:                PHAR_STR(key, str_key);
                    219: 
                    220:                if (keylen <= (uint)dirlen) {
                    221:                        if (keylen < (uint)dirlen || !strncmp(str_key, dir, dirlen)) {
                    222:                                PHAR_STR_FREE(str_key);
                    223:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    224:                                        break;
                    225:                                }
                    226:                                continue;
                    227:                        }
                    228:                }
                    229: 
                    230:                if (*dir == '/') {
                    231:                        /* root directory */
                    232:                        if (keylen >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
                    233:                                PHAR_STR_FREE(str_key);
                    234:                                /* do not add any magic entries to this directory */
                    235:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    236:                                        break;
                    237:                                }
                    238:                                continue;
                    239:                        }
                    240: 
                    241:                        if (NULL != (found = (char *) memchr(str_key, '/', keylen))) {
                    242:                                /* the entry has a path separator and is a subdirectory */
                    243:                                entry = (char *) safe_emalloc(found - str_key, 1, 1);
                    244:                                memcpy(entry, str_key, found - str_key);
                    245:                                keylen = found - str_key;
                    246:                                entry[keylen] = '\0';
                    247:                        } else {
                    248:                                entry = (char *) safe_emalloc(keylen, 1, 1);
                    249:                                memcpy(entry, str_key, keylen);
                    250:                                entry[keylen] = '\0';
                    251:                        }
                    252: 
                    253:                        PHAR_STR_FREE(str_key);
                    254:                        goto PHAR_ADD_ENTRY;
                    255:                } else {
                    256:                        if (0 != memcmp(str_key, dir, dirlen)) {
                    257:                                /* entry in directory not found */
                    258:                                PHAR_STR_FREE(str_key);
                    259:                                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    260:                                        break;
                    261:                                }
                    262:                                continue;
                    263:                        } else {
                    264:                                if (str_key[dirlen] != '/') {
                    265:                                        PHAR_STR_FREE(str_key);
                    266:                                        if (SUCCESS != zend_hash_move_forward(manifest)) {
                    267:                                                break;
                    268:                                        }
                    269:                                        continue;
                    270:                                }
                    271:                        }
                    272:                }
                    273: 
                    274:                save = str_key;
                    275:                save += dirlen + 1; /* seek to just past the path separator */
                    276: 
                    277:                if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
                    278:                        /* is subdirectory */
                    279:                        save -= dirlen + 1;
                    280:                        entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
                    281:                        memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
                    282:                        keylen = found - save - dirlen - 1;
                    283:                        entry[keylen] = '\0';
                    284:                } else {
                    285:                        /* is file */
                    286:                        save -= dirlen + 1;
                    287:                        entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
                    288:                        memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
                    289:                        entry[keylen - dirlen - 1] = '\0';
                    290:                        keylen = keylen - dirlen - 1;
                    291:                }
                    292:                PHAR_STR_FREE(str_key);
                    293: PHAR_ADD_ENTRY:
                    294:                if (keylen) {
                    295:                        phar_add_empty(data, entry, keylen);
                    296:                }
                    297: 
                    298:                efree(entry);
                    299: 
                    300:                if (SUCCESS != zend_hash_move_forward(manifest)) {
                    301:                        break;
                    302:                }
                    303:        }
                    304: 
                    305:        if (FAILURE != zend_hash_has_more_elements(data)) {
                    306:                efree(dir);
                    307:                if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) {
                    308:                        FREE_HASHTABLE(data);
                    309:                        return NULL;
                    310:                }
                    311:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    312:        } else {
                    313:                efree(dir);
                    314:                return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
                    315:        }
                    316: }
                    317: /* }}}*/
                    318: 
                    319: /**
                    320:  * Open a directory handle within a phar archive
                    321:  */
                    322: 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) /* {{{ */
                    323: {
                    324:        php_url *resource = NULL;
                    325:        php_stream *ret;
                    326:        char *internal_file, *error, *str_key;
                    327:        phar_zstr key;
                    328:        uint keylen;
                    329:        ulong unused;
                    330:        phar_archive_data *phar;
                    331:        phar_entry_info *entry = NULL;
                    332:        uint host_len;
                    333: 
                    334:        if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
                    335:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar url \"%s\" is unknown", path);
                    336:                return NULL;
                    337:        }
                    338: 
                    339:        /* we must have at the very least phar://alias.phar/ */
                    340:        if (!resource->scheme || !resource->host || !resource->path) {
                    341:                if (resource->host && !resource->path) {
                    342:                        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);
                    343:                        php_url_free(resource);
                    344:                        return NULL;
                    345:                }
                    346:                php_url_free(resource);
                    347:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
                    348:                return NULL;
                    349:        }
                    350: 
                    351:        if (strcasecmp("phar", resource->scheme)) {
                    352:                php_url_free(resource);
                    353:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path);
                    354:                return NULL;
                    355:        }
                    356: 
                    357:        host_len = strlen(resource->host);
                    358:        phar_request_initialize(TSRMLS_C);
                    359:        internal_file = resource->path + 1; /* strip leading "/" */
                    360: 
                    361:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    362:                if (error) {
                    363:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
                    364:                        efree(error);
                    365:                } else {
                    366:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar file \"%s\" is unknown", resource->host);
                    367:                }
                    368:                php_url_free(resource);
                    369:                return NULL;
                    370:        }
                    371: 
                    372:        if (error) {
                    373:                efree(error);
                    374:        }
                    375: 
                    376:        if (*internal_file == '\0') {
                    377:                /* root directory requested */
                    378:                internal_file = estrndup(internal_file - 1, 1);
                    379:                ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    380:                php_url_free(resource);
                    381:                return ret;
                    382:        }
                    383: 
                    384:        if (!phar->manifest.arBuckets) {
                    385:                php_url_free(resource);
                    386:                return NULL;
                    387:        }
                    388: 
                    389:        if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry) && !entry->is_dir) {
                    390:                php_url_free(resource);
                    391:                return NULL;
                    392:        } else if (entry && entry->is_dir) {
                    393:                if (entry->is_mounted) {
                    394:                        php_url_free(resource);
                    395:                        return php_stream_opendir(entry->tmp, options, context);
                    396:                }
                    397:                internal_file = estrdup(internal_file);
                    398:                php_url_free(resource);
                    399:                return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    400:        } else {
                    401:                int i_len = strlen(internal_file);
                    402: 
                    403:                /* search for directory */
                    404:                zend_hash_internal_pointer_reset(&phar->manifest);
                    405:                while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
                    406:                        if (HASH_KEY_NON_EXISTANT != 
                    407:                                        zend_hash_get_current_key_ex(
                    408:                                                &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
                    409:                                PHAR_STR(key, str_key);
                    410:                                if (keylen > (uint)i_len && 0 == memcmp(str_key, internal_file, i_len)) {
                    411:                                        PHAR_STR_FREE(str_key);
                    412:                                        /* directory found */
                    413:                                        internal_file = estrndup(internal_file,
                    414:                                                        i_len);
                    415:                                        php_url_free(resource);
                    416:                                        return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
                    417:                                }
                    418:                                PHAR_STR_FREE(str_key);
                    419:                        }
                    420: 
                    421:                        if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
                    422:                                break;
                    423:                        }
                    424:                }
                    425:        }
                    426: 
                    427:        php_url_free(resource);
                    428:        return NULL;
                    429: }
                    430: /* }}} */
                    431: 
                    432: /**
                    433:  * Make a new directory within a phar archive
                    434:  */
                    435: int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
                    436: {
                    437:        phar_entry_info entry, *e;
                    438:        phar_archive_data *phar = NULL;
                    439:        char *error, *arch, *entry2;
                    440:        int arch_len, entry_len;
                    441:        php_url *resource = NULL;
                    442:        uint host_len;
                    443: 
                    444:        /* pre-readonly check, we need to know if this is a data phar */
                    445:        if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
                    446:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
                    447:                return 0;
                    448:        }
                    449: 
                    450:        if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
                    451:                phar = NULL;
                    452:        }
                    453: 
                    454:        efree(arch);
                    455:        efree(entry2);
                    456: 
                    457:        if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
                    458:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", write operations disabled", url_from);
                    459:                return 0;
                    460:        }
                    461: 
                    462:        if ((resource = phar_parse_url(wrapper, url_from, "w", options TSRMLS_CC)) == NULL) {
                    463:                return 0;
                    464:        }
                    465: 
                    466:        /* we must have at the very least phar://alias.phar/internalfile.php */
                    467:        if (!resource->scheme || !resource->host || !resource->path) {
                    468:                php_url_free(resource);
                    469:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from);
                    470:                return 0;
                    471:        }
                    472: 
                    473:        if (strcasecmp("phar", resource->scheme)) {
                    474:                php_url_free(resource);
                    475:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from);
                    476:                return 0;
                    477:        }
                    478: 
                    479:        host_len = strlen(resource->host);
                    480: 
                    481:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    482:                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);
                    483:                efree(error);
                    484:                php_url_free(resource);
                    485:                return 0;
                    486:        }
                    487: 
                    488:        if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error, 1 TSRMLS_CC))) {
                    489:                /* directory exists, or is a subdirectory of an existing file */
                    490:                if (e->is_temp_dir) {
                    491:                        efree(e->filename);
                    492:                        efree(e);
                    493:                }
                    494:                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);
                    495:                php_url_free(resource);
                    496:                return 0;
                    497:        }
                    498: 
                    499:        if (error) {
                    500:                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);
                    501:                efree(error);
                    502:                php_url_free(resource);
                    503:                return 0;
                    504:        }
                    505: 
                    506:        if (phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC)) {
                    507:                /* entry exists as a file */
                    508:                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);
                    509:                php_url_free(resource);
                    510:                return 0;
                    511:        }
                    512: 
                    513:        if (error) {
                    514:                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);
                    515:                efree(error);
                    516:                php_url_free(resource);
                    517:                return 0;
                    518:        }
                    519: 
                    520:        memset((void *) &entry, 0, sizeof(phar_entry_info));
                    521: 
                    522:        /* strip leading "/" */
                    523:        if (phar->is_zip) {
                    524:                entry.is_zip = 1;
                    525:        }
                    526: 
                    527:        entry.filename = estrdup(resource->path + 1);
                    528: 
                    529:        if (phar->is_tar) {
                    530:                entry.is_tar = 1;
                    531:                entry.tar_type = TAR_DIR;
                    532:        }
                    533: 
                    534:        entry.filename_len = strlen(resource->path + 1);
                    535:        php_url_free(resource);
                    536:        entry.is_dir = 1;
                    537:        entry.phar = phar;
                    538:        entry.is_modified = 1;
                    539:        entry.is_crc_checked = 1;
                    540:        entry.flags = PHAR_ENT_PERM_DEF_DIR;
                    541:        entry.old_flags = PHAR_ENT_PERM_DEF_DIR;
                    542: 
                    543:        if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                    544:                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);
                    545:                efree(error);
                    546:                efree(entry.filename);
                    547:                return 0;
                    548:        }
                    549: 
                    550:        phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
                    551: 
                    552:        if (error) {
                    553:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error);
                    554:                zend_hash_del(&phar->manifest, entry.filename, entry.filename_len);
                    555:                efree(error);
                    556:                return 0;
                    557:        }
                    558: 
                    559:        phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC);
                    560:        return 1;
                    561: }
                    562: /* }}} */
                    563: 
                    564: /**
                    565:  * Remove a directory within a phar archive
                    566:  */
                    567: int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
                    568: {
                    569:        phar_entry_info *entry;
                    570:        phar_archive_data *phar = NULL;
                    571:        char *error, *arch, *entry2;
                    572:        int arch_len, entry_len;
                    573:        php_url *resource = NULL;
                    574:        uint host_len;
                    575:        phar_zstr key;
                    576:        char *str_key;
                    577:        uint key_len;
                    578:        ulong unused;
                    579:        uint path_len;
                    580: 
                    581:        /* pre-readonly check, we need to know if this is a data phar */
                    582:        if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
                    583:                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);
                    584:                return 0;
                    585:        }
                    586: 
                    587:        if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
                    588:                phar = NULL;
                    589:        }
                    590: 
                    591:        efree(arch);
                    592:        efree(entry2);
                    593: 
                    594:        if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
                    595:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rmdir directory \"%s\", write operations disabled", url);
                    596:                return 0;
                    597:        }
                    598: 
                    599:        if ((resource = phar_parse_url(wrapper, url, "w", options TSRMLS_CC)) == NULL) {
                    600:                return 0;
                    601:        }
                    602: 
                    603:        /* we must have at the very least phar://alias.phar/internalfile.php */
                    604:        if (!resource->scheme || !resource->host || !resource->path) {
                    605:                php_url_free(resource);
                    606:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
                    607:                return 0;
                    608:        }
                    609: 
                    610:        if (strcasecmp("phar", resource->scheme)) {
                    611:                php_url_free(resource);
                    612:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
                    613:                return 0;
                    614:        }
                    615: 
                    616:        host_len = strlen(resource->host);
                    617: 
                    618:        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
                    619:                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);
                    620:                efree(error);
                    621:                php_url_free(resource);
                    622:                return 0;
                    623:        }
                    624: 
                    625:        path_len = strlen(resource->path+1);
                    626: 
                    627:        if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, path_len, 2, &error, 1 TSRMLS_CC))) {
                    628:                if (error) {
                    629:                        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);
                    630:                        efree(error);
                    631:                } else {
                    632:                        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);
                    633:                }
                    634:                php_url_free(resource);
                    635:                return 0;
                    636:        }
                    637: 
                    638:        if (!entry->is_deleted) {
                    639:                for (zend_hash_internal_pointer_reset(&phar->manifest);
                    640:                HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL);
                    641:                zend_hash_move_forward(&phar->manifest)) {
                    642: 
                    643:                        PHAR_STR(key, str_key);
                    644: 
                    645:                        if (key_len > path_len && 
                    646:                                memcmp(str_key, resource->path+1, path_len) == 0 && 
                    647:                                IS_SLASH(str_key[path_len])) {
                    648:                                PHAR_STR_FREE(str_key);
                    649:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
                    650:                                if (entry->is_temp_dir) {
                    651:                                        efree(entry->filename);
                    652:                                        efree(entry);
                    653:                                }
                    654:                                php_url_free(resource);
                    655:                                return 0;
                    656:                        }
                    657:                        PHAR_STR_FREE(str_key);
                    658:                }
                    659: 
                    660:                for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
                    661:                        HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL);
                    662:                        zend_hash_move_forward(&phar->virtual_dirs)) {
                    663:        
                    664:                        PHAR_STR(key, str_key);
                    665:        
                    666:                        if (key_len > path_len && 
                    667:                                memcmp(str_key, resource->path+1, path_len) == 0 && 
                    668:                                IS_SLASH(str_key[path_len])) {
                    669:                                PHAR_STR_FREE(str_key);
                    670:                                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
                    671:                                if (entry->is_temp_dir) {
                    672:                                        efree(entry->filename);
                    673:                                        efree(entry);
                    674:                                }
                    675:                                php_url_free(resource);
                    676:                                return 0;
                    677:                        }
                    678:                        PHAR_STR_FREE(str_key);
                    679:                }
                    680:        }
                    681: 
                    682:        if (entry->is_temp_dir) {
                    683:                zend_hash_del(&phar->virtual_dirs, resource->path+1, path_len);
                    684:                efree(entry->filename);
                    685:                efree(entry);
                    686:        } else {
                    687:                entry->is_deleted = 1;
                    688:                entry->is_modified = 1;
                    689:                phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
                    690: 
                    691:                if (error) {
                    692:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
                    693:                        php_url_free(resource);
                    694:                        efree(error);
                    695:                        return 0;
                    696:                }
                    697:        }
                    698: 
                    699:        php_url_free(resource);
                    700:        return 1;
                    701: }
                    702: /* }}} */
                    703: 
                    704: /*
                    705:  * Local variables:
                    706:  * tab-width: 4
                    707:  * c-basic-offset: 4
                    708:  * End:
                    709:  * vim600: noet sw=4 ts=4 fdm=marker
                    710:  * vim<600: noet sw=4 ts=4
                    711:  */

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