filestat.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  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. | https://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: Jim Winstead <jimw@php.net> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "php.h"
  17. #include "fopen_wrappers.h"
  18. #include "php_globals.h"
  19. #include <stdlib.h>
  20. #include <sys/stat.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <ctype.h>
  24. #include <time.h>
  25. #if HAVE_UNISTD_H
  26. # include <unistd.h>
  27. #endif
  28. #if HAVE_SYS_PARAM_H
  29. # include <sys/param.h>
  30. #endif
  31. #if HAVE_SYS_VFS_H
  32. # include <sys/vfs.h>
  33. #endif
  34. #ifdef OS2
  35. # define INCL_DOS
  36. # include <os2.h>
  37. #endif
  38. #if defined(__APPLE__)
  39. /*
  40. Apple statvfs has an interger overflow in libc copying to statvfs.
  41. cvt_statfs_to_statvfs(struct statfs *from, struct statvfs *to) {
  42. to->f_blocks = (fsblkcnt_t)from->f_blocks;
  43. */
  44. # undef HAVE_SYS_STATVFS_H
  45. # undef HAVE_STATVFS
  46. #endif
  47. #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
  48. # include <sys/statvfs.h>
  49. #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_STATFS)
  50. # include <sys/statfs.h>
  51. #elif defined(HAVE_SYS_MOUNT_H) && defined(HAVE_STATFS)
  52. # include <sys/mount.h>
  53. #endif
  54. #if HAVE_PWD_H
  55. # ifdef PHP_WIN32
  56. # include "win32/pwd.h"
  57. # else
  58. # include <pwd.h>
  59. # endif
  60. #endif
  61. #if HAVE_GRP_H
  62. # include <grp.h>
  63. #endif
  64. #if HAVE_UTIME
  65. # ifdef PHP_WIN32
  66. # include <sys/utime.h>
  67. # else
  68. # include <utime.h>
  69. # endif
  70. #endif
  71. #ifdef PHP_WIN32
  72. #include "win32/winutil.h"
  73. #endif
  74. #include "basic_functions.h"
  75. #include "php_filestat.h"
  76. PHP_RINIT_FUNCTION(filestat) /* {{{ */
  77. {
  78. BG(CurrentStatFile)=NULL;
  79. BG(CurrentLStatFile)=NULL;
  80. return SUCCESS;
  81. }
  82. /* }}} */
  83. PHP_RSHUTDOWN_FUNCTION(filestat) /* {{{ */
  84. {
  85. if (BG(CurrentStatFile)) {
  86. zend_string_release(BG(CurrentStatFile));
  87. BG(CurrentStatFile) = NULL;
  88. }
  89. if (BG(CurrentLStatFile)) {
  90. zend_string_release(BG(CurrentLStatFile));
  91. BG(CurrentLStatFile) = NULL;
  92. }
  93. return SUCCESS;
  94. }
  95. /* }}} */
  96. static int php_disk_total_space(char *path, double *space) /* {{{ */
  97. #if defined(WINDOWS) /* {{{ */
  98. {
  99. ULARGE_INTEGER FreeBytesAvailableToCaller;
  100. ULARGE_INTEGER TotalNumberOfBytes;
  101. ULARGE_INTEGER TotalNumberOfFreeBytes;
  102. PHP_WIN32_IOUTIL_INIT_W(path)
  103. if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
  104. char *err = php_win_err();
  105. php_error_docref(NULL, E_WARNING, "%s", err);
  106. php_win_err_free(err);
  107. PHP_WIN32_IOUTIL_CLEANUP_W()
  108. return FAILURE;
  109. }
  110. /* i know - this is ugly, but i works <thies@thieso.net> */
  111. *space = TotalNumberOfBytes.HighPart * (double) (((zend_ulong)1) << 31) * 2.0 + TotalNumberOfBytes.LowPart;
  112. PHP_WIN32_IOUTIL_CLEANUP_W()
  113. return SUCCESS;
  114. }
  115. /* }}} */
  116. #elif defined(OS2) /* {{{ */
  117. {
  118. double bytestotal = 0;
  119. FSALLOCATE fsinfo;
  120. char drive = path[0] & 95;
  121. if (DosQueryFSInfo( drive ? drive - 64 : 0, FSIL_ALLOC, &fsinfo, sizeof( fsinfo ) ) == 0) {
  122. bytestotal = (double)fsinfo.cbSector * fsinfo.cSectorUnit * fsinfo.cUnit;
  123. *space = bytestotal;
  124. return SUCCESS;
  125. }
  126. return FAILURE;
  127. }
  128. /* }}} */
  129. #else /* {{{ if !defined(OS2) && !defined(WINDOWS) */
  130. {
  131. double bytestotal = 0;
  132. #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
  133. struct statvfs buf;
  134. #elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
  135. struct statfs buf;
  136. #endif
  137. #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
  138. if (statvfs(path, &buf)) {
  139. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  140. return FAILURE;
  141. }
  142. if (buf.f_frsize) {
  143. bytestotal = (((double)buf.f_blocks) * ((double)buf.f_frsize));
  144. } else {
  145. bytestotal = (((double)buf.f_blocks) * ((double)buf.f_bsize));
  146. }
  147. #elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
  148. if (statfs(path, &buf)) {
  149. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  150. return FAILURE;
  151. }
  152. bytestotal = (((double)buf.f_bsize) * ((double)buf.f_blocks));
  153. #endif
  154. *space = bytestotal;
  155. return SUCCESS;
  156. }
  157. #endif
  158. /* }}} */
  159. /* }}} */
  160. /* {{{ Get total disk space for filesystem that path is on */
  161. PHP_FUNCTION(disk_total_space)
  162. {
  163. double bytestotal;
  164. char *path, fullpath[MAXPATHLEN];
  165. size_t path_len;
  166. ZEND_PARSE_PARAMETERS_START(1, 1)
  167. Z_PARAM_PATH(path, path_len)
  168. ZEND_PARSE_PARAMETERS_END();
  169. if (!expand_filepath(path, fullpath)) {
  170. RETURN_FALSE;
  171. }
  172. if (php_check_open_basedir(fullpath)) {
  173. RETURN_FALSE;
  174. }
  175. if (php_disk_total_space(fullpath, &bytestotal) == SUCCESS) {
  176. RETURN_DOUBLE(bytestotal);
  177. }
  178. RETURN_FALSE;
  179. }
  180. /* }}} */
  181. static int php_disk_free_space(char *path, double *space) /* {{{ */
  182. #if defined(WINDOWS) /* {{{ */
  183. {
  184. ULARGE_INTEGER FreeBytesAvailableToCaller;
  185. ULARGE_INTEGER TotalNumberOfBytes;
  186. ULARGE_INTEGER TotalNumberOfFreeBytes;
  187. PHP_WIN32_IOUTIL_INIT_W(path)
  188. if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
  189. char *err = php_win_err();
  190. php_error_docref(NULL, E_WARNING, "%s", err);
  191. php_win_err_free(err);
  192. PHP_WIN32_IOUTIL_CLEANUP_W()
  193. return FAILURE;
  194. }
  195. *space = FreeBytesAvailableToCaller.HighPart * (double) (1ULL << 32) + FreeBytesAvailableToCaller.LowPart;
  196. PHP_WIN32_IOUTIL_CLEANUP_W()
  197. return SUCCESS;
  198. }
  199. /* }}} */
  200. #elif defined(OS2) /* {{{ */
  201. {
  202. double bytesfree = 0;
  203. FSALLOCATE fsinfo;
  204. char drive = path[0] & 95;
  205. if (DosQueryFSInfo( drive ? drive - 64 : 0, FSIL_ALLOC, &fsinfo, sizeof( fsinfo ) ) == 0) {
  206. bytesfree = (double)fsinfo.cbSector * fsinfo.cSectorUnit * fsinfo.cUnitAvail;
  207. *space = bytesfree;
  208. return SUCCESS;
  209. }
  210. return FAILURE;
  211. }
  212. /* }}} */
  213. #else /* {{{ if !defined(OS2) && !defined(WINDOWS) */
  214. {
  215. double bytesfree = 0;
  216. #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
  217. struct statvfs buf;
  218. #elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
  219. struct statfs buf;
  220. #endif
  221. #if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
  222. if (statvfs(path, &buf)) {
  223. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  224. return FAILURE;
  225. }
  226. if (buf.f_frsize) {
  227. bytesfree = (((double)buf.f_bavail) * ((double)buf.f_frsize));
  228. } else {
  229. bytesfree = (((double)buf.f_bavail) * ((double)buf.f_bsize));
  230. }
  231. #elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
  232. if (statfs(path, &buf)) {
  233. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  234. return FAILURE;
  235. }
  236. bytesfree = (((double)buf.f_bsize) * ((double)buf.f_bavail));
  237. #endif
  238. *space = bytesfree;
  239. return SUCCESS;
  240. }
  241. #endif
  242. /* }}} */
  243. /* }}} */
  244. /* {{{ Get free disk space for filesystem that path is on */
  245. PHP_FUNCTION(disk_free_space)
  246. {
  247. double bytesfree;
  248. char *path, fullpath[MAXPATHLEN];
  249. size_t path_len;
  250. ZEND_PARSE_PARAMETERS_START(1, 1)
  251. Z_PARAM_PATH(path, path_len)
  252. ZEND_PARSE_PARAMETERS_END();
  253. if (!expand_filepath(path, fullpath)) {
  254. RETURN_FALSE;
  255. }
  256. if (php_check_open_basedir(fullpath)) {
  257. RETURN_FALSE;
  258. }
  259. if (php_disk_free_space(fullpath, &bytesfree) == SUCCESS) {
  260. RETURN_DOUBLE(bytesfree);
  261. }
  262. RETURN_FALSE;
  263. }
  264. /* }}} */
  265. #ifndef PHP_WIN32
  266. PHPAPI int php_get_gid_by_name(const char *name, gid_t *gid)
  267. {
  268. #if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
  269. struct group gr;
  270. struct group *retgrptr;
  271. long grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
  272. char *grbuf;
  273. if (grbuflen < 1) {
  274. return FAILURE;
  275. }
  276. grbuf = emalloc(grbuflen);
  277. if (getgrnam_r(name, &gr, grbuf, grbuflen, &retgrptr) != 0 || retgrptr == NULL) {
  278. efree(grbuf);
  279. return FAILURE;
  280. }
  281. efree(grbuf);
  282. *gid = gr.gr_gid;
  283. #else
  284. struct group *gr = getgrnam(name);
  285. if (!gr) {
  286. return FAILURE;
  287. }
  288. *gid = gr->gr_gid;
  289. #endif
  290. return SUCCESS;
  291. }
  292. #endif
  293. static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
  294. {
  295. char *filename;
  296. size_t filename_len;
  297. zend_string *group_str;
  298. zend_long group_long;
  299. #if !defined(WINDOWS)
  300. gid_t gid;
  301. int ret;
  302. #endif
  303. php_stream_wrapper *wrapper;
  304. ZEND_PARSE_PARAMETERS_START(2, 2)
  305. Z_PARAM_PATH(filename, filename_len)
  306. Z_PARAM_STR_OR_LONG(group_str, group_long)
  307. ZEND_PARSE_PARAMETERS_END();
  308. wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
  309. if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
  310. if(wrapper && wrapper->wops->stream_metadata) {
  311. int option;
  312. void *value;
  313. if (group_str) {
  314. option = PHP_STREAM_META_GROUP_NAME;
  315. value = ZSTR_VAL(group_str);
  316. } else {
  317. option = PHP_STREAM_META_GROUP;
  318. value = &group_long;
  319. }
  320. if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL)) {
  321. RETURN_TRUE;
  322. } else {
  323. RETURN_FALSE;
  324. }
  325. } else {
  326. #if !defined(WINDOWS)
  327. /* On Windows, we expect regular chgrp to fail silently by default */
  328. php_error_docref(NULL, E_WARNING, "Can not call chgrp() for a non-standard stream");
  329. #endif
  330. RETURN_FALSE;
  331. }
  332. }
  333. #if defined(WINDOWS)
  334. /* We have no native chgrp on Windows, nothing left to do if stream doesn't have own implementation */
  335. RETURN_FALSE;
  336. #else
  337. if (group_str) {
  338. if (php_get_gid_by_name(ZSTR_VAL(group_str), &gid) != SUCCESS) {
  339. php_error_docref(NULL, E_WARNING, "Unable to find gid for %s", ZSTR_VAL(group_str));
  340. RETURN_FALSE;
  341. }
  342. } else {
  343. gid = (gid_t) group_long;
  344. }
  345. /* Check the basedir */
  346. if (php_check_open_basedir(filename)) {
  347. RETURN_FALSE;
  348. }
  349. if (do_lchgrp) {
  350. #if HAVE_LCHOWN
  351. ret = VCWD_LCHOWN(filename, -1, gid);
  352. #endif
  353. } else {
  354. ret = VCWD_CHOWN(filename, -1, gid);
  355. }
  356. if (ret == -1) {
  357. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  358. RETURN_FALSE;
  359. }
  360. RETURN_TRUE;
  361. #endif
  362. }
  363. /* }}} */
  364. /* {{{ Change file group */
  365. PHP_FUNCTION(chgrp)
  366. {
  367. php_do_chgrp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  368. }
  369. /* }}} */
  370. /* {{{ Change symlink group */
  371. #if HAVE_LCHOWN
  372. PHP_FUNCTION(lchgrp)
  373. {
  374. php_do_chgrp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  375. }
  376. #endif
  377. /* }}} */
  378. #ifndef PHP_WIN32
  379. PHPAPI uid_t php_get_uid_by_name(const char *name, uid_t *uid)
  380. {
  381. #if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
  382. struct passwd pw;
  383. struct passwd *retpwptr = NULL;
  384. long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
  385. char *pwbuf;
  386. if (pwbuflen < 1) {
  387. return FAILURE;
  388. }
  389. pwbuf = emalloc(pwbuflen);
  390. if (getpwnam_r(name, &pw, pwbuf, pwbuflen, &retpwptr) != 0 || retpwptr == NULL) {
  391. efree(pwbuf);
  392. return FAILURE;
  393. }
  394. efree(pwbuf);
  395. *uid = pw.pw_uid;
  396. #else
  397. struct passwd *pw = getpwnam(name);
  398. if (!pw) {
  399. return FAILURE;
  400. }
  401. *uid = pw->pw_uid;
  402. #endif
  403. return SUCCESS;
  404. }
  405. #endif
  406. static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
  407. {
  408. char *filename;
  409. size_t filename_len;
  410. zend_string *user_str;
  411. zend_long user_long;
  412. #if !defined(WINDOWS)
  413. uid_t uid;
  414. int ret;
  415. #endif
  416. php_stream_wrapper *wrapper;
  417. ZEND_PARSE_PARAMETERS_START(2, 2)
  418. Z_PARAM_PATH(filename, filename_len)
  419. Z_PARAM_STR_OR_LONG(user_str, user_long)
  420. ZEND_PARSE_PARAMETERS_END();
  421. wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
  422. if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
  423. if(wrapper && wrapper->wops->stream_metadata) {
  424. int option;
  425. void *value;
  426. if (user_str) {
  427. option = PHP_STREAM_META_OWNER_NAME;
  428. value = ZSTR_VAL(user_str);
  429. } else {
  430. option = PHP_STREAM_META_OWNER;
  431. value = &user_long;
  432. }
  433. if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL)) {
  434. RETURN_TRUE;
  435. } else {
  436. RETURN_FALSE;
  437. }
  438. } else {
  439. #if !defined(WINDOWS)
  440. /* On Windows, we expect regular chown to fail silently by default */
  441. php_error_docref(NULL, E_WARNING, "Can not call chown() for a non-standard stream");
  442. #endif
  443. RETURN_FALSE;
  444. }
  445. }
  446. #if defined(WINDOWS)
  447. /* We have no native chown on Windows, nothing left to do if stream doesn't have own implementation */
  448. RETURN_FALSE;
  449. #else
  450. if (user_str) {
  451. if (php_get_uid_by_name(ZSTR_VAL(user_str), &uid) != SUCCESS) {
  452. php_error_docref(NULL, E_WARNING, "Unable to find uid for %s", ZSTR_VAL(user_str));
  453. RETURN_FALSE;
  454. }
  455. } else {
  456. uid = (uid_t) user_long;
  457. }
  458. /* Check the basedir */
  459. if (php_check_open_basedir(filename)) {
  460. RETURN_FALSE;
  461. }
  462. if (do_lchown) {
  463. #if HAVE_LCHOWN
  464. ret = VCWD_LCHOWN(filename, uid, -1);
  465. #endif
  466. } else {
  467. ret = VCWD_CHOWN(filename, uid, -1);
  468. }
  469. if (ret == -1) {
  470. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  471. RETURN_FALSE;
  472. }
  473. RETURN_TRUE;
  474. #endif
  475. }
  476. /* }}} */
  477. /* {{{ Change file owner */
  478. PHP_FUNCTION(chown)
  479. {
  480. php_do_chown(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  481. }
  482. /* }}} */
  483. /* {{{ Change file owner */
  484. #if HAVE_LCHOWN
  485. PHP_FUNCTION(lchown)
  486. {
  487. RETVAL_TRUE;
  488. php_do_chown(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  489. }
  490. #endif
  491. /* }}} */
  492. /* {{{ Change file mode */
  493. PHP_FUNCTION(chmod)
  494. {
  495. char *filename;
  496. size_t filename_len;
  497. zend_long mode;
  498. int ret;
  499. mode_t imode;
  500. php_stream_wrapper *wrapper;
  501. ZEND_PARSE_PARAMETERS_START(2, 2)
  502. Z_PARAM_PATH(filename, filename_len)
  503. Z_PARAM_LONG(mode)
  504. ZEND_PARSE_PARAMETERS_END();
  505. wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
  506. if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
  507. if(wrapper && wrapper->wops->stream_metadata) {
  508. if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_ACCESS, &mode, NULL)) {
  509. RETURN_TRUE;
  510. } else {
  511. RETURN_FALSE;
  512. }
  513. } else {
  514. php_error_docref(NULL, E_WARNING, "Can not call chmod() for a non-standard stream");
  515. RETURN_FALSE;
  516. }
  517. }
  518. /* Check the basedir */
  519. if (php_check_open_basedir(filename)) {
  520. RETURN_FALSE;
  521. }
  522. imode = (mode_t) mode;
  523. ret = VCWD_CHMOD(filename, imode);
  524. if (ret == -1) {
  525. php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
  526. RETURN_FALSE;
  527. }
  528. RETURN_TRUE;
  529. }
  530. /* }}} */
  531. #if HAVE_UTIME
  532. /* {{{ Set modification time of file */
  533. PHP_FUNCTION(touch)
  534. {
  535. char *filename;
  536. size_t filename_len;
  537. zend_long filetime = 0, fileatime = 0;
  538. bool filetime_is_null = 1, fileatime_is_null = 1;
  539. int ret;
  540. FILE *file;
  541. struct utimbuf newtimebuf;
  542. struct utimbuf *newtime = &newtimebuf;
  543. php_stream_wrapper *wrapper;
  544. ZEND_PARSE_PARAMETERS_START(1, 3)
  545. Z_PARAM_PATH(filename, filename_len)
  546. Z_PARAM_OPTIONAL
  547. Z_PARAM_LONG_OR_NULL(filetime, filetime_is_null)
  548. Z_PARAM_LONG_OR_NULL(fileatime, fileatime_is_null)
  549. ZEND_PARSE_PARAMETERS_END();
  550. if (!filename_len) {
  551. RETURN_FALSE;
  552. }
  553. if (filetime_is_null && fileatime_is_null) {
  554. newtime = NULL;
  555. } else if (!filetime_is_null && fileatime_is_null) {
  556. newtime->modtime = newtime->actime = filetime;
  557. } else if (filetime_is_null && !fileatime_is_null) {
  558. zend_argument_value_error(2, "cannot be null when argument #3 ($atime) is an integer");
  559. RETURN_THROWS();
  560. } else {
  561. newtime->modtime = filetime;
  562. newtime->actime = fileatime;
  563. }
  564. wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
  565. if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
  566. if(wrapper && wrapper->wops->stream_metadata) {
  567. if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_TOUCH, newtime, NULL)) {
  568. RETURN_TRUE;
  569. } else {
  570. RETURN_FALSE;
  571. }
  572. } else {
  573. php_stream *stream;
  574. if(!filetime_is_null || !fileatime_is_null) {
  575. php_error_docref(NULL, E_WARNING, "Can not call touch() for a non-standard stream");
  576. RETURN_FALSE;
  577. }
  578. stream = php_stream_open_wrapper_ex(filename, "c", REPORT_ERRORS, NULL, NULL);
  579. if(stream != NULL) {
  580. php_stream_close(stream);
  581. RETURN_TRUE;
  582. } else {
  583. RETURN_FALSE;
  584. }
  585. }
  586. }
  587. /* Check the basedir */
  588. if (php_check_open_basedir(filename)) {
  589. RETURN_FALSE;
  590. }
  591. /* create the file if it doesn't exist already */
  592. if (VCWD_ACCESS(filename, F_OK) != 0) {
  593. file = VCWD_FOPEN(filename, "w");
  594. if (file == NULL) {
  595. php_error_docref(NULL, E_WARNING, "Unable to create file %s because %s", filename, strerror(errno));
  596. RETURN_FALSE;
  597. }
  598. fclose(file);
  599. }
  600. ret = VCWD_UTIME(filename, newtime);
  601. if (ret == -1) {
  602. php_error_docref(NULL, E_WARNING, "Utime failed: %s", strerror(errno));
  603. RETURN_FALSE;
  604. }
  605. RETURN_TRUE;
  606. }
  607. /* }}} */
  608. #endif
  609. /* {{{ php_clear_stat_cache() */
  610. PHPAPI void php_clear_stat_cache(bool clear_realpath_cache, const char *filename, size_t filename_len)
  611. {
  612. /* always clear CurrentStatFile and CurrentLStatFile even if filename is not NULL
  613. * as it may contain outdated data (e.g. "nlink" for a directory when deleting a file
  614. * in this directory, as shown by lstat_stat_variation9.phpt) */
  615. if (BG(CurrentStatFile)) {
  616. zend_string_release(BG(CurrentStatFile));
  617. BG(CurrentStatFile) = NULL;
  618. }
  619. if (BG(CurrentLStatFile)) {
  620. zend_string_release(BG(CurrentLStatFile));
  621. BG(CurrentLStatFile) = NULL;
  622. }
  623. if (clear_realpath_cache) {
  624. if (filename != NULL) {
  625. realpath_cache_del(filename, filename_len);
  626. } else {
  627. realpath_cache_clean();
  628. }
  629. }
  630. }
  631. /* }}} */
  632. /* {{{ Clear file stat cache */
  633. PHP_FUNCTION(clearstatcache)
  634. {
  635. bool clear_realpath_cache = 0;
  636. char *filename = NULL;
  637. size_t filename_len = 0;
  638. ZEND_PARSE_PARAMETERS_START(0, 2)
  639. Z_PARAM_OPTIONAL
  640. Z_PARAM_BOOL(clear_realpath_cache)
  641. Z_PARAM_PATH(filename, filename_len)
  642. ZEND_PARSE_PARAMETERS_END();
  643. php_clear_stat_cache(clear_realpath_cache, filename, filename_len);
  644. }
  645. /* }}} */
  646. #define IS_LINK_OPERATION(__t) ((__t) == FS_TYPE || (__t) == FS_IS_LINK || (__t) == FS_LSTAT || (__t) == FS_LPERMS)
  647. #define IS_EXISTS_CHECK(__t) ((__t) == FS_EXISTS || (__t) == FS_IS_W || (__t) == FS_IS_R || (__t) == FS_IS_X || (__t) == FS_IS_FILE || (__t) == FS_IS_DIR || (__t) == FS_IS_LINK || (__t) == FS_LPERMS)
  648. #define IS_ABLE_CHECK(__t) ((__t) == FS_IS_R || (__t) == FS_IS_W || (__t) == FS_IS_X)
  649. #define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS)
  650. /* {{{ php_stat */
  651. PHPAPI void php_stat(zend_string *filename, int type, zval *return_value)
  652. {
  653. zend_stat_t *stat_sb;
  654. php_stream_statbuf ssb;
  655. int flags = 0, rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */
  656. const char *local = NULL;
  657. php_stream_wrapper *wrapper = NULL;
  658. if (IS_ACCESS_CHECK(type)) {
  659. if (!ZSTR_LEN(filename) || CHECK_NULL_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename))) {
  660. if (ZSTR_LEN(filename) && !IS_EXISTS_CHECK(type)) {
  661. php_error_docref(NULL, E_WARNING, "Filename contains null byte");
  662. }
  663. RETURN_FALSE;
  664. }
  665. if ((wrapper = php_stream_locate_url_wrapper(ZSTR_VAL(filename), &local, 0)) == &php_plain_files_wrapper
  666. && php_check_open_basedir(local)) {
  667. RETURN_FALSE;
  668. }
  669. if (wrapper == &php_plain_files_wrapper) {
  670. switch (type) {
  671. #ifdef F_OK
  672. case FS_EXISTS:
  673. RETURN_BOOL(VCWD_ACCESS(local, F_OK) == 0);
  674. break;
  675. #endif
  676. #ifdef W_OK
  677. case FS_IS_W:
  678. RETURN_BOOL(VCWD_ACCESS(local, W_OK) == 0);
  679. break;
  680. #endif
  681. #ifdef R_OK
  682. case FS_IS_R:
  683. RETURN_BOOL(VCWD_ACCESS(local, R_OK) == 0);
  684. break;
  685. #endif
  686. #ifdef X_OK
  687. case FS_IS_X:
  688. RETURN_BOOL(VCWD_ACCESS(local, X_OK) == 0);
  689. break;
  690. #endif
  691. }
  692. }
  693. }
  694. if (IS_LINK_OPERATION(type)) {
  695. flags |= PHP_STREAM_URL_STAT_LINK;
  696. }
  697. if (IS_EXISTS_CHECK(type)) {
  698. flags |= PHP_STREAM_URL_STAT_QUIET;
  699. }
  700. do {
  701. /* Try to hit the cache first */
  702. if (flags & PHP_STREAM_URL_STAT_LINK) {
  703. if (filename == BG(CurrentLStatFile)
  704. || (BG(CurrentLStatFile)
  705. && zend_string_equal_content(filename, BG(CurrentLStatFile)))) {
  706. memcpy(&ssb, &BG(lssb), sizeof(php_stream_statbuf));
  707. break;
  708. }
  709. } else {
  710. if (filename == BG(CurrentStatFile)
  711. || (BG(CurrentStatFile)
  712. && zend_string_equal_content(filename, BG(CurrentStatFile)))) {
  713. memcpy(&ssb, &BG(ssb), sizeof(php_stream_statbuf));
  714. break;
  715. }
  716. }
  717. if (!wrapper) {
  718. if (!ZSTR_LEN(filename) || CHECK_NULL_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename))) {
  719. if (ZSTR_LEN(filename) && !IS_EXISTS_CHECK(type)) {
  720. php_error_docref(NULL, E_WARNING, "Filename contains null byte");
  721. }
  722. RETURN_FALSE;
  723. }
  724. if ((wrapper = php_stream_locate_url_wrapper(ZSTR_VAL(filename), &local, 0)) == &php_plain_files_wrapper
  725. && php_check_open_basedir(local)) {
  726. RETURN_FALSE;
  727. }
  728. }
  729. if (!wrapper
  730. || !wrapper->wops->url_stat
  731. || wrapper->wops->url_stat(wrapper, local, flags | PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR, &ssb, NULL)) {
  732. /* Error Occurred */
  733. if (!IS_EXISTS_CHECK(type)) {
  734. php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", ZSTR_VAL(filename));
  735. }
  736. RETURN_FALSE;
  737. }
  738. /* Drop into cache */
  739. if (flags & PHP_STREAM_URL_STAT_LINK) {
  740. if (BG(CurrentLStatFile)) {
  741. zend_string_release(BG(CurrentLStatFile));
  742. }
  743. BG(CurrentLStatFile) = zend_string_copy(filename);
  744. memcpy(&BG(lssb), &ssb, sizeof(php_stream_statbuf));
  745. }
  746. if (!(flags & PHP_STREAM_URL_STAT_LINK)
  747. || !S_ISLNK(ssb.sb.st_mode)) {
  748. if (BG(CurrentStatFile)) {
  749. zend_string_release(BG(CurrentStatFile));
  750. }
  751. BG(CurrentStatFile) = zend_string_copy(filename);
  752. memcpy(&BG(ssb), &ssb, sizeof(php_stream_statbuf));
  753. }
  754. } while (0);
  755. stat_sb = &ssb.sb;
  756. if (type >= FS_IS_W && type <= FS_IS_X) {
  757. if(ssb.sb.st_uid==getuid()) {
  758. rmask=S_IRUSR;
  759. wmask=S_IWUSR;
  760. xmask=S_IXUSR;
  761. } else if(ssb.sb.st_gid==getgid()) {
  762. rmask=S_IRGRP;
  763. wmask=S_IWGRP;
  764. xmask=S_IXGRP;
  765. } else {
  766. int groups, n, i;
  767. gid_t *gids;
  768. groups = getgroups(0, NULL);
  769. if(groups > 0) {
  770. gids=(gid_t *)safe_emalloc(groups, sizeof(gid_t), 0);
  771. n=getgroups(groups, gids);
  772. for(i=0;i<n;i++){
  773. if(ssb.sb.st_gid==gids[i]) {
  774. rmask=S_IRGRP;
  775. wmask=S_IWGRP;
  776. xmask=S_IXGRP;
  777. break;
  778. }
  779. }
  780. efree(gids);
  781. }
  782. }
  783. }
  784. if (IS_ABLE_CHECK(type) && getuid() == 0) {
  785. /* root has special perms on plain_wrapper */
  786. if (wrapper == &php_plain_files_wrapper) {
  787. if (type == FS_IS_X) {
  788. xmask = S_IXROOT;
  789. } else {
  790. RETURN_TRUE;
  791. }
  792. }
  793. }
  794. switch (type) {
  795. case FS_PERMS:
  796. case FS_LPERMS:
  797. RETURN_LONG((zend_long)ssb.sb.st_mode);
  798. case FS_INODE:
  799. RETURN_LONG((zend_long)ssb.sb.st_ino);
  800. case FS_SIZE:
  801. RETURN_LONG((zend_long)ssb.sb.st_size);
  802. case FS_OWNER:
  803. RETURN_LONG((zend_long)ssb.sb.st_uid);
  804. case FS_GROUP:
  805. RETURN_LONG((zend_long)ssb.sb.st_gid);
  806. case FS_ATIME:
  807. RETURN_LONG((zend_long)ssb.sb.st_atime);
  808. case FS_MTIME:
  809. RETURN_LONG((zend_long)ssb.sb.st_mtime);
  810. case FS_CTIME:
  811. RETURN_LONG((zend_long)ssb.sb.st_ctime);
  812. case FS_TYPE:
  813. if (S_ISLNK(ssb.sb.st_mode)) {
  814. RETURN_STRING("link");
  815. }
  816. switch(ssb.sb.st_mode & S_IFMT) {
  817. case S_IFIFO: RETURN_STRING("fifo");
  818. case S_IFCHR: RETURN_STRING("char");
  819. case S_IFDIR: RETURN_STRING("dir");
  820. case S_IFBLK: RETURN_STRING("block");
  821. case S_IFREG: RETURN_STRING("file");
  822. #if defined(S_IFSOCK) && !defined(PHP_WIN32)
  823. case S_IFSOCK: RETURN_STRING("socket");
  824. #endif
  825. }
  826. php_error_docref(NULL, E_NOTICE, "Unknown file type (%d)", ssb.sb.st_mode&S_IFMT);
  827. RETURN_STRING("unknown");
  828. case FS_IS_W:
  829. RETURN_BOOL((ssb.sb.st_mode & wmask) != 0);
  830. case FS_IS_R:
  831. RETURN_BOOL((ssb.sb.st_mode&rmask)!=0);
  832. case FS_IS_X:
  833. RETURN_BOOL((ssb.sb.st_mode&xmask)!=0);
  834. case FS_IS_FILE:
  835. RETURN_BOOL(S_ISREG(ssb.sb.st_mode));
  836. case FS_IS_DIR:
  837. RETURN_BOOL(S_ISDIR(ssb.sb.st_mode));
  838. case FS_IS_LINK:
  839. RETURN_BOOL(S_ISLNK(ssb.sb.st_mode));
  840. case FS_EXISTS:
  841. RETURN_TRUE; /* the false case was done earlier */
  842. case FS_LSTAT:
  843. /* FALLTHROUGH */
  844. case FS_STAT: {
  845. char *stat_sb_names[] = {
  846. "dev", "ino", "mode", "nlink", "uid", "gid", "rdev",
  847. "size", "atime", "mtime", "ctime", "blksize", "blocks"
  848. };
  849. zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev,
  850. stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks;
  851. zval *stat_sb_addresses[] = {
  852. &stat_dev, &stat_ino, &stat_mode, &stat_nlink, &stat_uid, &stat_gid, &stat_rdev,
  853. &stat_size, &stat_atime, &stat_mtime, &stat_ctime, &stat_blksize, &stat_blocks
  854. };
  855. size_t i, size_stat_sb = sizeof(stat_sb_addresses) / sizeof(*stat_sb_addresses);
  856. array_init(return_value);
  857. ZVAL_LONG(&stat_dev, stat_sb->st_dev);
  858. ZVAL_LONG(&stat_ino, stat_sb->st_ino);
  859. ZVAL_LONG(&stat_mode, stat_sb->st_mode);
  860. ZVAL_LONG(&stat_nlink, stat_sb->st_nlink);
  861. ZVAL_LONG(&stat_uid, stat_sb->st_uid);
  862. ZVAL_LONG(&stat_gid, stat_sb->st_gid);
  863. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  864. ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
  865. #else
  866. ZVAL_LONG(&stat_rdev, -1);
  867. #endif
  868. ZVAL_LONG(&stat_size, stat_sb->st_size);
  869. ZVAL_LONG(&stat_atime, stat_sb->st_atime);
  870. ZVAL_LONG(&stat_mtime, stat_sb->st_mtime);
  871. ZVAL_LONG(&stat_ctime, stat_sb->st_ctime);
  872. #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
  873. ZVAL_LONG(&stat_blksize, stat_sb->st_blksize);
  874. #else
  875. ZVAL_LONG(&stat_blksize,-1);
  876. #endif
  877. #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
  878. ZVAL_LONG(&stat_blocks, stat_sb->st_blocks);
  879. #else
  880. ZVAL_LONG(&stat_blocks,-1);
  881. #endif
  882. for (i = 0; i < size_stat_sb; i++) {
  883. /* Store numeric indexes in proper order */
  884. zend_hash_next_index_insert(Z_ARRVAL_P(return_value), stat_sb_addresses[i]);
  885. }
  886. for (i = 0; i < size_stat_sb; i++) {
  887. /* Store string indexes referencing the same zval */
  888. zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[i], strlen(stat_sb_names[i]), stat_sb_addresses[i]);
  889. }
  890. return;
  891. }
  892. }
  893. php_error_docref(NULL, E_WARNING, "Didn't understand stat call");
  894. RETURN_FALSE;
  895. }
  896. /* }}} */
  897. /* another quickie macro to make defining similar functions easier */
  898. /* {{{ FileFunction(name, funcnum) */
  899. #define FileFunction(name, funcnum) \
  900. ZEND_NAMED_FUNCTION(name) { \
  901. zend_string *filename; \
  902. \
  903. ZEND_PARSE_PARAMETERS_START(1, 1) \
  904. Z_PARAM_STR(filename) \
  905. ZEND_PARSE_PARAMETERS_END(); \
  906. \
  907. php_stat(filename, funcnum, return_value); \
  908. }
  909. /* }}} */
  910. /* {{{ Get file permissions */
  911. FileFunction(PHP_FN(fileperms), FS_PERMS)
  912. /* }}} */
  913. /* {{{ Get file inode */
  914. FileFunction(PHP_FN(fileinode), FS_INODE)
  915. /* }}} */
  916. /* {{{ Get file size */
  917. FileFunction(PHP_FN(filesize), FS_SIZE)
  918. /* }}} */
  919. /* {{{ Get file owner */
  920. FileFunction(PHP_FN(fileowner), FS_OWNER)
  921. /* }}} */
  922. /* {{{ Get file group */
  923. FileFunction(PHP_FN(filegroup), FS_GROUP)
  924. /* }}} */
  925. /* {{{ Get last access time of file */
  926. FileFunction(PHP_FN(fileatime), FS_ATIME)
  927. /* }}} */
  928. /* {{{ Get last modification time of file */
  929. FileFunction(PHP_FN(filemtime), FS_MTIME)
  930. /* }}} */
  931. /* {{{ Get inode modification time of file */
  932. FileFunction(PHP_FN(filectime), FS_CTIME)
  933. /* }}} */
  934. /* {{{ Get file type */
  935. FileFunction(PHP_FN(filetype), FS_TYPE)
  936. /* }}} */
  937. /* {{{ Returns true if file can be written */
  938. FileFunction(PHP_FN(is_writable), FS_IS_W)
  939. /* }}} */
  940. /* {{{ Returns true if file can be read */
  941. FileFunction(PHP_FN(is_readable), FS_IS_R)
  942. /* }}} */
  943. /* {{{ Returns true if file is executable */
  944. FileFunction(PHP_FN(is_executable), FS_IS_X)
  945. /* }}} */
  946. /* {{{ Returns true if file is a regular file */
  947. FileFunction(PHP_FN(is_file), FS_IS_FILE)
  948. /* }}} */
  949. /* {{{ Returns true if file is directory */
  950. FileFunction(PHP_FN(is_dir), FS_IS_DIR)
  951. /* }}} */
  952. /* {{{ Returns true if file is symbolic link */
  953. FileFunction(PHP_FN(is_link), FS_IS_LINK)
  954. /* }}} */
  955. /* {{{ Returns true if filename exists */
  956. FileFunction(PHP_FN(file_exists), FS_EXISTS)
  957. /* }}} */
  958. /* {{{ Give information about a file or symbolic link */
  959. FileFunction(PHP_FN(lstat), FS_LSTAT)
  960. /* }}} */
  961. /* {{{ Give information about a file */
  962. FileFunction(PHP_FN(stat), FS_STAT)
  963. /* }}} */
  964. /* {{{ Get current size of realpath cache */
  965. PHP_FUNCTION(realpath_cache_size)
  966. {
  967. ZEND_PARSE_PARAMETERS_NONE();
  968. RETURN_LONG(realpath_cache_size());
  969. }
  970. /* {{{ Get current size of realpath cache */
  971. PHP_FUNCTION(realpath_cache_get)
  972. {
  973. realpath_cache_bucket **buckets = realpath_cache_get_buckets(), **end = buckets + realpath_cache_max_buckets();
  974. ZEND_PARSE_PARAMETERS_NONE();
  975. array_init(return_value);
  976. while(buckets < end) {
  977. realpath_cache_bucket *bucket = *buckets;
  978. while(bucket) {
  979. zval entry;
  980. array_init(&entry);
  981. /* bucket->key is unsigned long */
  982. if (ZEND_LONG_MAX >= bucket->key) {
  983. add_assoc_long_ex(&entry, "key", sizeof("key") - 1, bucket->key);
  984. } else {
  985. add_assoc_double_ex(&entry, "key", sizeof("key") - 1, (double)bucket->key);
  986. }
  987. add_assoc_bool_ex(&entry, "is_dir", sizeof("is_dir") - 1, bucket->is_dir);
  988. add_assoc_stringl_ex(&entry, "realpath", sizeof("realpath") - 1, bucket->realpath, bucket->realpath_len);
  989. add_assoc_long_ex(&entry, "expires", sizeof("expires") - 1, bucket->expires);
  990. #ifdef PHP_WIN32
  991. add_assoc_bool_ex(&entry, "is_rvalid", sizeof("is_rvalid") - 1, bucket->is_rvalid);
  992. add_assoc_bool_ex(&entry, "is_wvalid", sizeof("is_wvalid") - 1, bucket->is_wvalid);
  993. add_assoc_bool_ex(&entry, "is_readable", sizeof("is_readable") - 1, bucket->is_readable);
  994. add_assoc_bool_ex(&entry, "is_writable", sizeof("is_writable") - 1, bucket->is_writable);
  995. #endif
  996. zend_hash_str_update(Z_ARRVAL_P(return_value), bucket->path, bucket->path_len, &entry);
  997. bucket = bucket->next;
  998. }
  999. buckets++;
  1000. }
  1001. }