Back to index

php5  5.3.10
dir.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Thies C. Arntzen <thies@thieso.net>                          |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: dir.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 /* {{{ includes/startup/misc */
00022 
00023 #include "php.h"
00024 #include "fopen_wrappers.h"
00025 #include "file.h"
00026 #include "php_dir.h"
00027 #include "php_string.h"
00028 #include "php_scandir.h"
00029 #include "basic_functions.h"
00030 
00031 #ifdef HAVE_DIRENT_H
00032 #include <dirent.h>
00033 #endif
00034 
00035 #if HAVE_UNISTD_H
00036 #include <unistd.h>
00037 #endif
00038 
00039 #include <errno.h>
00040 
00041 #ifdef PHP_WIN32
00042 #include "win32/readdir.h"
00043 #endif
00044 
00045 
00046 #ifdef HAVE_GLOB
00047 #ifndef PHP_WIN32
00048 #include <glob.h>
00049 #else
00050 #include "win32/glob.h"
00051 #endif
00052 #endif
00053 
00054 typedef struct {
00055        int default_dir;
00056 } php_dir_globals;
00057 
00058 #ifdef ZTS
00059 #define DIRG(v) TSRMG(dir_globals_id, php_dir_globals *, v)
00060 int dir_globals_id;
00061 #else
00062 #define DIRG(v) (dir_globals.v)
00063 php_dir_globals dir_globals;
00064 #endif
00065 
00066 #if 0
00067 typedef struct {
00068        int id;
00069        DIR *dir;
00070 } php_dir;
00071 
00072 static int le_dirp;
00073 #endif
00074 
00075 static zend_class_entry *dir_class_entry_ptr;
00076 
00077 #define FETCH_DIRP() \
00078        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &id) == FAILURE) { \
00079               return; \
00080        } \
00081        if (ZEND_NUM_ARGS() == 0) { \
00082               myself = getThis(); \
00083               if (myself) { \
00084                      if (zend_hash_find(Z_OBJPROP_P(myself), "handle", sizeof("handle"), (void **)&tmp) == FAILURE) { \
00085                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find my handle property"); \
00086                             RETURN_FALSE; \
00087                      } \
00088                      ZEND_FETCH_RESOURCE(dirp, php_stream *, tmp, -1, "Directory", php_file_le_stream()); \
00089               } else { \
00090                      ZEND_FETCH_RESOURCE(dirp, php_stream *, 0, DIRG(default_dir), "Directory", php_file_le_stream()); \
00091               } \
00092        } else { \
00093               dirp = (php_stream *) zend_fetch_resource(&id TSRMLS_CC, -1, "Directory", NULL, 1, php_file_le_stream()); \
00094               if (!dirp) \
00095                      RETURN_FALSE; \
00096        } 
00097        
00098 /* {{{ arginfo */
00099 ZEND_BEGIN_ARG_INFO_EX(arginfo_dir, 0, 0, 0)
00100        ZEND_ARG_INFO(0, dir_handle)
00101 ZEND_END_ARG_INFO()
00102 /* }}} */
00103 
00104 static const zend_function_entry php_dir_class_functions[] = {
00105        PHP_FALIAS(close,    closedir,            arginfo_dir)
00106        PHP_FALIAS(rewind,   rewinddir,           arginfo_dir)
00107        PHP_NAMED_FE(read,  php_if_readdir, arginfo_dir)
00108        {NULL, NULL, NULL}
00109 };
00110 
00111 
00112 static void php_set_default_dir(int id TSRMLS_DC)
00113 {
00114        if (DIRG(default_dir)!=-1) {
00115               zend_list_delete(DIRG(default_dir));
00116        }
00117 
00118        if (id != -1) {
00119               zend_list_addref(id);
00120        }
00121        
00122        DIRG(default_dir) = id;
00123 }
00124 
00125 PHP_RINIT_FUNCTION(dir)
00126 {
00127        DIRG(default_dir) = -1;
00128        return SUCCESS;
00129 }
00130 
00131 PHP_MINIT_FUNCTION(dir)
00132 {
00133        static char dirsep_str[2], pathsep_str[2];
00134        zend_class_entry dir_class_entry;
00135 
00136        INIT_CLASS_ENTRY(dir_class_entry, "Directory", php_dir_class_functions);
00137        dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry TSRMLS_CC);
00138 
00139 #ifdef ZTS
00140        ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
00141 #endif
00142 
00143        dirsep_str[0] = DEFAULT_SLASH;
00144        dirsep_str[1] = '\0';
00145        REGISTER_STRING_CONSTANT("DIRECTORY_SEPARATOR", dirsep_str, CONST_CS|CONST_PERSISTENT);
00146 
00147        pathsep_str[0] = ZEND_PATHS_SEPARATOR;
00148        pathsep_str[1] = '\0';
00149        REGISTER_STRING_CONSTANT("PATH_SEPARATOR", pathsep_str, CONST_CS|CONST_PERSISTENT);
00150 
00151 #ifdef HAVE_GLOB
00152 
00153 #ifdef GLOB_BRACE
00154        REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE, CONST_CS | CONST_PERSISTENT);
00155 #else
00156 # define GLOB_BRACE 0
00157 #endif
00158 
00159 #ifdef GLOB_MARK
00160        REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK, CONST_CS | CONST_PERSISTENT);
00161 #else
00162 # define GLOB_MARK 0
00163 #endif
00164 
00165 #ifdef GLOB_NOSORT
00166        REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT, CONST_CS | CONST_PERSISTENT);
00167 #else 
00168 # define GLOB_NOSORT 0
00169 #endif
00170 
00171 #ifdef GLOB_NOCHECK
00172        REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK, CONST_CS | CONST_PERSISTENT);
00173 #else 
00174 # define GLOB_NOCHECK 0
00175 #endif
00176 
00177 #ifdef GLOB_NOESCAPE
00178        REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE, CONST_CS | CONST_PERSISTENT);
00179 #else 
00180 # define GLOB_NOESCAPE 0
00181 #endif
00182 
00183 #ifdef GLOB_ERR
00184        REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR, CONST_CS | CONST_PERSISTENT);
00185 #else 
00186 # define GLOB_ERR 0
00187 #endif
00188 
00189 #ifndef GLOB_ONLYDIR
00190 # define GLOB_ONLYDIR (1<<30)
00191 # define GLOB_EMULATE_ONLYDIR
00192 # define GLOB_FLAGMASK (~GLOB_ONLYDIR)
00193 #else
00194 # define GLOB_FLAGMASK (~0)
00195 #endif
00196 
00197 /* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */
00198 #define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR)
00199 
00200        REGISTER_LONG_CONSTANT("GLOB_ONLYDIR", GLOB_ONLYDIR, CONST_CS | CONST_PERSISTENT);
00201        REGISTER_LONG_CONSTANT("GLOB_AVAILABLE_FLAGS", GLOB_AVAILABLE_FLAGS, CONST_CS | CONST_PERSISTENT);
00202 
00203 #endif /* HAVE_GLOB */
00204 
00205        return SUCCESS;
00206 }
00207 /* }}} */
00208 
00209 /* {{{ internal functions */
00210 static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
00211 {
00212        char *dirname;
00213        int dir_len;
00214        zval *zcontext = NULL;
00215        php_stream_context *context = NULL;
00216        php_stream *dirp;
00217 
00218        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &dirname, &dir_len, &zcontext) == FAILURE) {
00219               RETURN_NULL();
00220        }
00221 
00222        context = php_stream_context_from_zval(zcontext, 0);
00223        
00224        dirp = php_stream_opendir(dirname, ENFORCE_SAFE_MODE|REPORT_ERRORS, context);
00225 
00226        if (dirp == NULL) {
00227               RETURN_FALSE;
00228        }
00229 
00230        dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
00231               
00232        php_set_default_dir(dirp->rsrc_id TSRMLS_CC);
00233 
00234        if (createobject) {
00235               object_init_ex(return_value, dir_class_entry_ptr);
00236               add_property_stringl(return_value, "path", dirname, dir_len, 1);
00237               add_property_resource(return_value, "handle", dirp->rsrc_id);
00238               php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
00239        } else {
00240               php_stream_to_zval(dirp, return_value);
00241        }
00242 }
00243 /* }}} */
00244 
00245 /* {{{ proto mixed opendir(string path[, resource context])
00246    Open a directory and return a dir_handle */
00247 PHP_FUNCTION(opendir)
00248 {
00249        _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
00250 }
00251 /* }}} */
00252 
00253 /* {{{ proto object dir(string directory[, resource context])
00254    Directory class with properties, handle and class and methods read, rewind and close */
00255 PHP_FUNCTION(getdir)
00256 {
00257        _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
00258 }
00259 /* }}} */
00260 
00261 /* {{{ proto void closedir([resource dir_handle])
00262    Close directory connection identified by the dir_handle */
00263 PHP_FUNCTION(closedir)
00264 {
00265        zval *id = NULL, **tmp, *myself;
00266        php_stream *dirp;
00267        int rsrc_id;
00268 
00269        FETCH_DIRP();
00270 
00271        if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
00272               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
00273               RETURN_FALSE;
00274        }
00275 
00276        rsrc_id = dirp->rsrc_id;
00277        zend_list_delete(dirp->rsrc_id);
00278 
00279        if (rsrc_id == DIRG(default_dir)) {
00280               php_set_default_dir(-1 TSRMLS_CC);
00281        }
00282 }
00283 /* }}} */
00284 
00285 #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
00286 /* {{{ proto bool chroot(string directory)
00287    Change root directory */
00288 PHP_FUNCTION(chroot)
00289 {
00290        char *str;
00291        int ret, str_len;
00292        
00293        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
00294               RETURN_FALSE;
00295        }
00296        
00297        ret = chroot(str);
00298        if (ret != 0) {
00299               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
00300               RETURN_FALSE;
00301        }
00302 
00303        php_clear_stat_cache(1, NULL, 0 TSRMLS_CC);
00304        
00305        ret = chdir("/");
00306        
00307        if (ret != 0) {
00308               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
00309               RETURN_FALSE;
00310        }
00311 
00312        RETURN_TRUE;
00313 }
00314 /* }}} */
00315 #endif
00316 
00317 /* {{{ proto bool chdir(string directory)
00318    Change the current directory */
00319 PHP_FUNCTION(chdir)
00320 {
00321        char *str;
00322        int ret, str_len;
00323        
00324        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
00325               RETURN_FALSE;
00326        }
00327 
00328        if (strlen(str) != str_len) {
00329               RETURN_FALSE;
00330        }
00331 
00332        if ((PG(safe_mode) && !php_checkuid(str, NULL, CHECKUID_CHECK_FILE_AND_DIR)) || php_check_open_basedir(str TSRMLS_CC)) {
00333               RETURN_FALSE;
00334        }
00335        ret = VCWD_CHDIR(str);
00336        
00337        if (ret != 0) {
00338               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
00339               RETURN_FALSE;
00340        }
00341 
00342        if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
00343               efree(BG(CurrentStatFile));
00344               BG(CurrentStatFile) = NULL;
00345        }
00346        if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
00347               efree(BG(CurrentLStatFile));
00348               BG(CurrentLStatFile) = NULL;
00349        }
00350 
00351        RETURN_TRUE;
00352 }
00353 /* }}} */
00354 
00355 /* {{{ proto mixed getcwd(void)
00356    Gets the current directory */
00357 PHP_FUNCTION(getcwd)
00358 {
00359        char path[MAXPATHLEN];
00360        char *ret=NULL;
00361        
00362        if (zend_parse_parameters_none() == FAILURE) {
00363               return;
00364        }
00365 
00366 #if HAVE_GETCWD
00367        ret = VCWD_GETCWD(path, MAXPATHLEN);
00368 #elif HAVE_GETWD
00369        ret = VCWD_GETWD(path);
00370 #endif
00371 
00372        if (ret) {
00373               RETURN_STRING(path, 1);
00374        } else {
00375               RETURN_FALSE;
00376        }
00377 }
00378 /* }}} */
00379 
00380 /* {{{ proto void rewinddir([resource dir_handle])
00381    Rewind dir_handle back to the start */
00382 PHP_FUNCTION(rewinddir)
00383 {
00384        zval *id = NULL, **tmp, *myself;
00385        php_stream *dirp;
00386        
00387        FETCH_DIRP();
00388 
00389        if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
00390               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
00391               RETURN_FALSE;
00392        }
00393 
00394        php_stream_rewinddir(dirp);
00395 }
00396 /* }}} */
00397 
00398 /* {{{ proto string readdir([resource dir_handle])
00399    Read directory entry from dir_handle */
00400 PHP_NAMED_FUNCTION(php_if_readdir)
00401 {
00402        zval *id = NULL, **tmp, *myself;
00403        php_stream *dirp;
00404        php_stream_dirent entry;
00405 
00406        FETCH_DIRP();
00407 
00408        if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
00409               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
00410               RETURN_FALSE;
00411        }
00412 
00413        if (php_stream_readdir(dirp, &entry)) {
00414               RETURN_STRINGL(entry.d_name, strlen(entry.d_name), 1);
00415        }
00416        RETURN_FALSE;
00417 }
00418 /* }}} */
00419 
00420 #ifdef HAVE_GLOB
00421 /* {{{ proto array glob(string pattern [, int flags])
00422    Find pathnames matching a pattern */
00423 PHP_FUNCTION(glob)
00424 {
00425        int cwd_skip = 0;
00426 #ifdef ZTS
00427        char cwd[MAXPATHLEN];
00428        char work_pattern[MAXPATHLEN];
00429        char *result;
00430 #endif
00431        char *pattern = NULL;
00432        int pattern_len;
00433        long flags = 0;
00434        glob_t globbuf;
00435        int n;
00436        int ret;
00437        zend_bool basedir_limit = 0;
00438 
00439        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &pattern, &pattern_len, &flags) == FAILURE) {
00440               return;
00441        }
00442 
00443        if (strlen(pattern) != pattern_len) {
00444               RETURN_FALSE;
00445        }
00446 
00447        if (pattern_len >= MAXPATHLEN) {
00448               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
00449               RETURN_FALSE;
00450        }
00451 
00452        if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
00453               php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
00454               RETURN_FALSE;
00455        }
00456 
00457 #ifdef ZTS 
00458        if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
00459               result = VCWD_GETCWD(cwd, MAXPATHLEN);    
00460               if (!result) {
00461                      cwd[0] = '\0';
00462               }
00463 #ifdef PHP_WIN32
00464               if (IS_SLASH(*pattern)) {
00465                      cwd[2] = '\0';
00466               }
00467 #endif
00468               cwd_skip = strlen(cwd)+1;
00469 
00470               snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
00471               pattern = work_pattern;
00472        } 
00473 #endif
00474 
00475        
00476        memset(&globbuf, 0, sizeof(glob_t));
00477        globbuf.gl_offs = 0;
00478        if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
00479 #ifdef GLOB_NOMATCH
00480               if (GLOB_NOMATCH == ret) {
00481                      /* Some glob implementation simply return no data if no matches
00482                         were found, others return the GLOB_NOMATCH error code.
00483                         We don't want to treat GLOB_NOMATCH as an error condition
00484                         so that PHP glob() behaves the same on both types of 
00485                         implementations and so that 'foreach (glob() as ...'
00486                         can be used for simple glob() calls without further error
00487                         checking.
00488                      */
00489                      goto no_results;
00490               }
00491 #endif
00492               RETURN_FALSE;
00493        }
00494 
00495        /* now catch the FreeBSD style of "no matches" */
00496        if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
00497 no_results:
00498               if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
00499                      struct stat s;
00500 
00501                      if (0 != VCWD_STAT(pattern, &s) || S_IFDIR != (s.st_mode & S_IFMT)) {
00502                             RETURN_FALSE;
00503                      }
00504               }
00505               array_init(return_value);
00506               return;
00507        }
00508 
00509        array_init(return_value);
00510        for (n = 0; n < globbuf.gl_pathc; n++) {
00511               if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
00512                      if (PG(safe_mode) && (!php_checkuid_ex(globbuf.gl_pathv[n], NULL, CHECKUID_CHECK_FILE_AND_DIR, CHECKUID_NO_ERRORS))) {
00513                             basedir_limit = 1;
00514                             continue;
00515                      } else if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0 TSRMLS_CC)) {
00516                             basedir_limit = 1;
00517                             continue;
00518                      }
00519               }
00520               /* we need to do this everytime since GLOB_ONLYDIR does not guarantee that
00521                * all directories will be filtered. GNU libc documentation states the
00522                * following: 
00523                * If the information about the type of the file is easily available 
00524                * non-directories will be rejected but no extra work will be done to 
00525                * determine the information for each file. I.e., the caller must still be 
00526                * able to filter directories out. 
00527                */
00528               if (flags & GLOB_ONLYDIR) {
00529                      struct stat s;
00530 
00531                      if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
00532                             continue;
00533                      }
00534 
00535                      if (S_IFDIR != (s.st_mode & S_IFMT)) {
00536                             continue;
00537                      }
00538               }
00539               add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip, 1);
00540        }
00541 
00542        globfree(&globbuf);
00543 
00544        if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
00545               zval_dtor(return_value);
00546               RETURN_FALSE;
00547        }
00548 }
00549 /* }}} */
00550 #endif 
00551 
00552 /* {{{ proto array scandir(string dir [, int sorting_order [, resource context]])
00553    List files & directories inside the specified path */
00554 PHP_FUNCTION(scandir)
00555 {
00556        char *dirn;
00557        int dirn_len;
00558        long flags = 0;
00559        char **namelist;
00560        int n, i;
00561        zval *zcontext = NULL;
00562        php_stream_context *context = NULL;
00563 
00564        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &dirn, &dirn_len, &flags, &zcontext) == FAILURE) {
00565               return;
00566        }
00567 
00568        if (strlen(dirn) != dirn_len) {
00569               RETURN_FALSE;
00570        }
00571 
00572        if (dirn_len < 1) {
00573               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Directory name cannot be empty");
00574               RETURN_FALSE;
00575        }
00576 
00577        if (zcontext) {
00578               context = php_stream_context_from_zval(zcontext, 0);
00579        }
00580 
00581        if (!flags) {
00582               n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
00583        } else {
00584               n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
00585        }
00586        if (n < 0) {
00587               php_error_docref(NULL TSRMLS_CC, E_WARNING, "(errno %d): %s", errno, strerror(errno));
00588               RETURN_FALSE;
00589        }
00590        
00591        array_init(return_value);
00592 
00593        for (i = 0; i < n; i++) {
00594               add_next_index_string(return_value, namelist[i], 0);
00595        }
00596 
00597        if (n) {
00598               efree(namelist);
00599        }
00600 }
00601 /* }}} */
00602 
00603 /*
00604  * Local variables:
00605  * tab-width: 4
00606  * c-basic-offset: 4
00607  * End:
00608  * vim600: sw=4 ts=4 fdm=marker
00609  * vim<600: sw=4 ts=4
00610  */