PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/standard/dir.c

http://github.com/php/php-src
C | 585 lines | 417 code | 96 blank | 72 comment | 79 complexity | 27eec7112234965276e59167356b7a83 MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | http://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Thies C. Arntzen <thies@thieso.net> |
  14. +----------------------------------------------------------------------+
  15. */
  16. /* {{{ includes/startup/misc */
  17. #include "php.h"
  18. #include "fopen_wrappers.h"
  19. #include "file.h"
  20. #include "php_dir.h"
  21. #include "php_string.h"
  22. #include "php_scandir.h"
  23. #include "basic_functions.h"
  24. #include "dir_arginfo.h"
  25. #if HAVE_UNISTD_H
  26. #include <unistd.h>
  27. #endif
  28. #include <errno.h>
  29. #ifdef PHP_WIN32
  30. #include "win32/readdir.h"
  31. #endif
  32. #ifdef HAVE_GLOB
  33. #ifndef PHP_WIN32
  34. #include <glob.h>
  35. #else
  36. #include "win32/glob.h"
  37. #endif
  38. #endif
  39. typedef struct {
  40. zend_resource *default_dir;
  41. } php_dir_globals;
  42. #ifdef ZTS
  43. #define DIRG(v) ZEND_TSRMG(dir_globals_id, php_dir_globals *, v)
  44. int dir_globals_id;
  45. #else
  46. #define DIRG(v) (dir_globals.v)
  47. php_dir_globals dir_globals;
  48. #endif
  49. static zend_class_entry *dir_class_entry_ptr;
  50. #define FETCH_DIRP() \
  51. ZEND_PARSE_PARAMETERS_START(0, 1) \
  52. Z_PARAM_OPTIONAL \
  53. Z_PARAM_RESOURCE(id) \
  54. ZEND_PARSE_PARAMETERS_END(); \
  55. if (ZEND_NUM_ARGS() == 0) { \
  56. myself = getThis(); \
  57. if (myself) { \
  58. if ((tmp = zend_hash_str_find(Z_OBJPROP_P(myself), "handle", sizeof("handle")-1)) == NULL) { \
  59. zend_throw_error(NULL, "Unable to find my handle property"); \
  60. RETURN_THROWS(); \
  61. } \
  62. if ((dirp = (php_stream *)zend_fetch_resource_ex(tmp, "Directory", php_file_le_stream())) == NULL) { \
  63. RETURN_THROWS(); \
  64. } \
  65. } else { \
  66. if (!DIRG(default_dir)) { \
  67. zend_type_error("No resource supplied"); \
  68. RETURN_THROWS(); \
  69. } else if ((dirp = (php_stream *)zend_fetch_resource(DIRG(default_dir), "Directory", php_file_le_stream())) == NULL) { \
  70. RETURN_THROWS(); \
  71. } \
  72. } \
  73. } else { \
  74. if ((dirp = (php_stream *)zend_fetch_resource(Z_RES_P(id), "Directory", php_file_le_stream())) == NULL) { \
  75. RETURN_THROWS(); \
  76. } \
  77. }
  78. static void php_set_default_dir(zend_resource *res)
  79. {
  80. if (DIRG(default_dir)) {
  81. zend_list_delete(DIRG(default_dir));
  82. }
  83. if (res) {
  84. GC_ADDREF(res);
  85. }
  86. DIRG(default_dir) = res;
  87. }
  88. PHP_RINIT_FUNCTION(dir)
  89. {
  90. DIRG(default_dir) = NULL;
  91. return SUCCESS;
  92. }
  93. PHP_MINIT_FUNCTION(dir)
  94. {
  95. static char dirsep_str[2], pathsep_str[2];
  96. zend_class_entry dir_class_entry;
  97. INIT_CLASS_ENTRY(dir_class_entry, "Directory", class_Directory_methods);
  98. dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry);
  99. #ifdef ZTS
  100. ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
  101. #endif
  102. dirsep_str[0] = DEFAULT_SLASH;
  103. dirsep_str[1] = '\0';
  104. REGISTER_STRING_CONSTANT("DIRECTORY_SEPARATOR", dirsep_str, CONST_CS|CONST_PERSISTENT);
  105. pathsep_str[0] = ZEND_PATHS_SEPARATOR;
  106. pathsep_str[1] = '\0';
  107. REGISTER_STRING_CONSTANT("PATH_SEPARATOR", pathsep_str, CONST_CS|CONST_PERSISTENT);
  108. REGISTER_LONG_CONSTANT("SCANDIR_SORT_ASCENDING", PHP_SCANDIR_SORT_ASCENDING, CONST_CS | CONST_PERSISTENT);
  109. REGISTER_LONG_CONSTANT("SCANDIR_SORT_DESCENDING", PHP_SCANDIR_SORT_DESCENDING, CONST_CS | CONST_PERSISTENT);
  110. REGISTER_LONG_CONSTANT("SCANDIR_SORT_NONE", PHP_SCANDIR_SORT_NONE, CONST_CS | CONST_PERSISTENT);
  111. #ifdef HAVE_GLOB
  112. #ifdef GLOB_BRACE
  113. REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE, CONST_CS | CONST_PERSISTENT);
  114. #else
  115. # define GLOB_BRACE 0
  116. #endif
  117. #ifdef GLOB_MARK
  118. REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK, CONST_CS | CONST_PERSISTENT);
  119. #else
  120. # define GLOB_MARK 0
  121. #endif
  122. #ifdef GLOB_NOSORT
  123. REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT, CONST_CS | CONST_PERSISTENT);
  124. #else
  125. # define GLOB_NOSORT 0
  126. #endif
  127. #ifdef GLOB_NOCHECK
  128. REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK, CONST_CS | CONST_PERSISTENT);
  129. #else
  130. # define GLOB_NOCHECK 0
  131. #endif
  132. #ifdef GLOB_NOESCAPE
  133. REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE, CONST_CS | CONST_PERSISTENT);
  134. #else
  135. # define GLOB_NOESCAPE 0
  136. #endif
  137. #ifdef GLOB_ERR
  138. REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR, CONST_CS | CONST_PERSISTENT);
  139. #else
  140. # define GLOB_ERR 0
  141. #endif
  142. #ifndef GLOB_ONLYDIR
  143. # define GLOB_ONLYDIR (1<<30)
  144. # define GLOB_EMULATE_ONLYDIR
  145. # define GLOB_FLAGMASK (~GLOB_ONLYDIR)
  146. #else
  147. # define GLOB_FLAGMASK (~0)
  148. #endif
  149. /* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */
  150. #define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR)
  151. REGISTER_LONG_CONSTANT("GLOB_ONLYDIR", GLOB_ONLYDIR, CONST_CS | CONST_PERSISTENT);
  152. REGISTER_LONG_CONSTANT("GLOB_AVAILABLE_FLAGS", GLOB_AVAILABLE_FLAGS, CONST_CS | CONST_PERSISTENT);
  153. #endif /* HAVE_GLOB */
  154. return SUCCESS;
  155. }
  156. /* }}} */
  157. /* {{{ internal functions */
  158. static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
  159. {
  160. char *dirname;
  161. size_t dir_len;
  162. zval *zcontext = NULL;
  163. php_stream_context *context = NULL;
  164. php_stream *dirp;
  165. ZEND_PARSE_PARAMETERS_START(1, 2)
  166. Z_PARAM_PATH(dirname, dir_len)
  167. Z_PARAM_OPTIONAL
  168. Z_PARAM_RESOURCE(zcontext)
  169. ZEND_PARSE_PARAMETERS_END();
  170. context = php_stream_context_from_zval(zcontext, 0);
  171. dirp = php_stream_opendir(dirname, REPORT_ERRORS, context);
  172. if (dirp == NULL) {
  173. RETURN_FALSE;
  174. }
  175. dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
  176. php_set_default_dir(dirp->res);
  177. if (createobject) {
  178. object_init_ex(return_value, dir_class_entry_ptr);
  179. add_property_stringl(return_value, "path", dirname, dir_len);
  180. add_property_resource(return_value, "handle", dirp->res);
  181. php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
  182. } else {
  183. php_stream_to_zval(dirp, return_value);
  184. }
  185. }
  186. /* }}} */
  187. /* {{{ proto resource|false opendir(string path[, resource context])
  188. Open a directory and return a dir_handle */
  189. PHP_FUNCTION(opendir)
  190. {
  191. _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  192. }
  193. /* }}} */
  194. /* {{{ proto object|false dir(string directory[, resource context])
  195. Directory class with properties, handle and class and methods read, rewind and close */
  196. PHP_FUNCTION(getdir)
  197. {
  198. _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  199. }
  200. /* }}} */
  201. /* {{{ proto bool closedir([resource dir_handle])
  202. Close directory connection identified by the dir_handle */
  203. PHP_FUNCTION(closedir)
  204. {
  205. zval *id = NULL, *tmp, *myself;
  206. php_stream *dirp;
  207. zend_resource *res;
  208. FETCH_DIRP();
  209. if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
  210. zend_argument_type_error(1, "must be a valid Directory resource");
  211. RETURN_THROWS();
  212. }
  213. res = dirp->res;
  214. zend_list_close(dirp->res);
  215. if (res == DIRG(default_dir)) {
  216. php_set_default_dir(NULL);
  217. }
  218. }
  219. /* }}} */
  220. #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
  221. /* {{{ proto bool chroot(string directory)
  222. Change root directory */
  223. PHP_FUNCTION(chroot)
  224. {
  225. char *str;
  226. int ret;
  227. size_t str_len;
  228. ZEND_PARSE_PARAMETERS_START(1, 1)
  229. Z_PARAM_PATH(str, str_len)
  230. ZEND_PARSE_PARAMETERS_END();
  231. ret = chroot(str);
  232. if (ret != 0) {
  233. php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
  234. RETURN_FALSE;
  235. }
  236. php_clear_stat_cache(1, NULL, 0);
  237. ret = chdir("/");
  238. if (ret != 0) {
  239. php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
  240. RETURN_FALSE;
  241. }
  242. RETURN_TRUE;
  243. }
  244. /* }}} */
  245. #endif
  246. /* {{{ proto bool chdir(string directory)
  247. Change the current directory */
  248. PHP_FUNCTION(chdir)
  249. {
  250. char *str;
  251. int ret;
  252. size_t str_len;
  253. ZEND_PARSE_PARAMETERS_START(1, 1)
  254. Z_PARAM_PATH(str, str_len)
  255. ZEND_PARSE_PARAMETERS_END();
  256. if (php_check_open_basedir(str)) {
  257. RETURN_FALSE;
  258. }
  259. ret = VCWD_CHDIR(str);
  260. if (ret != 0) {
  261. php_error_docref(NULL, E_WARNING, "%s (errno %d)", strerror(errno), errno);
  262. RETURN_FALSE;
  263. }
  264. if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
  265. efree(BG(CurrentStatFile));
  266. BG(CurrentStatFile) = NULL;
  267. }
  268. if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
  269. efree(BG(CurrentLStatFile));
  270. BG(CurrentLStatFile) = NULL;
  271. }
  272. RETURN_TRUE;
  273. }
  274. /* }}} */
  275. /* {{{ proto mixed getcwd(void)
  276. Gets the current directory */
  277. PHP_FUNCTION(getcwd)
  278. {
  279. char path[MAXPATHLEN];
  280. char *ret=NULL;
  281. ZEND_PARSE_PARAMETERS_NONE();
  282. #if HAVE_GETCWD
  283. ret = VCWD_GETCWD(path, MAXPATHLEN);
  284. #elif HAVE_GETWD
  285. ret = VCWD_GETWD(path);
  286. #endif
  287. if (ret) {
  288. RETURN_STRING(path);
  289. } else {
  290. RETURN_FALSE;
  291. }
  292. }
  293. /* }}} */
  294. /* {{{ proto void rewinddir([resource dir_handle])
  295. Rewind dir_handle back to the start */
  296. PHP_FUNCTION(rewinddir)
  297. {
  298. zval *id = NULL, *tmp, *myself;
  299. php_stream *dirp;
  300. FETCH_DIRP();
  301. if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
  302. zend_argument_type_error(1, "must be a valid Directory resource");
  303. RETURN_THROWS();
  304. }
  305. php_stream_rewinddir(dirp);
  306. }
  307. /* }}} */
  308. /* {{{ proto string|false readdir([resource dir_handle])
  309. Read directory entry from dir_handle */
  310. PHP_FUNCTION(readdir)
  311. {
  312. zval *id = NULL, *tmp, *myself;
  313. php_stream *dirp;
  314. php_stream_dirent entry;
  315. FETCH_DIRP();
  316. if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
  317. zend_argument_type_error(1, "must be a valid Directory resource");
  318. RETURN_THROWS();
  319. }
  320. if (php_stream_readdir(dirp, &entry)) {
  321. RETURN_STRINGL(entry.d_name, strlen(entry.d_name));
  322. }
  323. RETURN_FALSE;
  324. }
  325. /* }}} */
  326. #ifdef HAVE_GLOB
  327. /* {{{ proto array|false glob(string pattern [, int flags])
  328. Find pathnames matching a pattern */
  329. PHP_FUNCTION(glob)
  330. {
  331. size_t cwd_skip = 0;
  332. #ifdef ZTS
  333. char cwd[MAXPATHLEN];
  334. char work_pattern[MAXPATHLEN];
  335. char *result;
  336. #endif
  337. char *pattern = NULL;
  338. size_t pattern_len;
  339. zend_long flags = 0;
  340. glob_t globbuf;
  341. size_t n;
  342. int ret;
  343. zend_bool basedir_limit = 0;
  344. ZEND_PARSE_PARAMETERS_START(1, 2)
  345. Z_PARAM_PATH(pattern, pattern_len)
  346. Z_PARAM_OPTIONAL
  347. Z_PARAM_LONG(flags)
  348. ZEND_PARSE_PARAMETERS_END();
  349. if (pattern_len >= MAXPATHLEN) {
  350. php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
  351. RETURN_FALSE;
  352. }
  353. if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
  354. php_error_docref(NULL, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
  355. RETURN_FALSE;
  356. }
  357. #ifdef ZTS
  358. if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
  359. result = VCWD_GETCWD(cwd, MAXPATHLEN);
  360. if (!result) {
  361. cwd[0] = '\0';
  362. }
  363. #ifdef PHP_WIN32
  364. if (IS_SLASH(*pattern)) {
  365. cwd[2] = '\0';
  366. }
  367. #endif
  368. cwd_skip = strlen(cwd)+1;
  369. snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
  370. pattern = work_pattern;
  371. }
  372. #endif
  373. memset(&globbuf, 0, sizeof(glob_t));
  374. globbuf.gl_offs = 0;
  375. if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
  376. #ifdef GLOB_NOMATCH
  377. if (GLOB_NOMATCH == ret) {
  378. /* Some glob implementation simply return no data if no matches
  379. were found, others return the GLOB_NOMATCH error code.
  380. We don't want to treat GLOB_NOMATCH as an error condition
  381. so that PHP glob() behaves the same on both types of
  382. implementations and so that 'foreach (glob() as ...'
  383. can be used for simple glob() calls without further error
  384. checking.
  385. */
  386. goto no_results;
  387. }
  388. #endif
  389. RETURN_FALSE;
  390. }
  391. /* now catch the FreeBSD style of "no matches" */
  392. if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
  393. #ifdef GLOB_NOMATCH
  394. no_results:
  395. #endif
  396. #ifndef PHP_WIN32
  397. /* Paths containing '*', '?' and some other chars are
  398. illegal on Windows but legit on other platforms. For
  399. this reason the direct basedir check against the glob
  400. query is senseless on windows. For instance while *.txt
  401. is a pretty valid filename on EXT3, it's invalid on NTFS. */
  402. if (PG(open_basedir) && *PG(open_basedir)) {
  403. if (php_check_open_basedir_ex(pattern, 0)) {
  404. RETURN_FALSE;
  405. }
  406. }
  407. #endif
  408. array_init(return_value);
  409. return;
  410. }
  411. array_init(return_value);
  412. for (n = 0; n < (size_t)globbuf.gl_pathc; n++) {
  413. if (PG(open_basedir) && *PG(open_basedir)) {
  414. if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0)) {
  415. basedir_limit = 1;
  416. continue;
  417. }
  418. }
  419. /* we need to do this every time since GLOB_ONLYDIR does not guarantee that
  420. * all directories will be filtered. GNU libc documentation states the
  421. * following:
  422. * If the information about the type of the file is easily available
  423. * non-directories will be rejected but no extra work will be done to
  424. * determine the information for each file. I.e., the caller must still be
  425. * able to filter directories out.
  426. */
  427. if (flags & GLOB_ONLYDIR) {
  428. zend_stat_t s;
  429. if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
  430. continue;
  431. }
  432. if (S_IFDIR != (s.st_mode & S_IFMT)) {
  433. continue;
  434. }
  435. }
  436. add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip);
  437. }
  438. globfree(&globbuf);
  439. if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
  440. zend_array_destroy(Z_ARR_P(return_value));
  441. RETURN_FALSE;
  442. }
  443. }
  444. /* }}} */
  445. #endif
  446. /* {{{ proto array|false scandir(string dir [, int sorting_order [, resource context]])
  447. List files & directories inside the specified path */
  448. PHP_FUNCTION(scandir)
  449. {
  450. char *dirn;
  451. size_t dirn_len;
  452. zend_long flags = 0;
  453. zend_string **namelist;
  454. int n, i;
  455. zval *zcontext = NULL;
  456. php_stream_context *context = NULL;
  457. ZEND_PARSE_PARAMETERS_START(1, 3)
  458. Z_PARAM_PATH(dirn, dirn_len)
  459. Z_PARAM_OPTIONAL
  460. Z_PARAM_LONG(flags)
  461. Z_PARAM_RESOURCE(zcontext)
  462. ZEND_PARSE_PARAMETERS_END();
  463. if (dirn_len < 1) {
  464. zend_argument_value_error(1, "cannot be empty");
  465. RETURN_THROWS();
  466. }
  467. if (zcontext) {
  468. context = php_stream_context_from_zval(zcontext, 0);
  469. }
  470. if (flags == PHP_SCANDIR_SORT_ASCENDING) {
  471. n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
  472. } else if (flags == PHP_SCANDIR_SORT_NONE) {
  473. n = php_stream_scandir(dirn, &namelist, context, NULL);
  474. } else {
  475. n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
  476. }
  477. if (n < 0) {
  478. php_error_docref(NULL, E_WARNING, "(errno %d): %s", errno, strerror(errno));
  479. RETURN_FALSE;
  480. }
  481. array_init(return_value);
  482. for (i = 0; i < n; i++) {
  483. add_next_index_str(return_value, namelist[i]);
  484. }
  485. if (n) {
  486. efree(namelist);
  487. }
  488. }
  489. /* }}} */