php-src/ext/standard/filestat.c
Peter Kokot 8d3f8ca12a Remove unused Git attributes ident
The $Id$ keywords were used in Subversion where they can be substituted
with filename, last revision number change, last changed date, and last
user who changed it.

In Git this functionality is different and can be done with Git attribute
ident. These need to be defined manually for each file in the
.gitattributes file and are afterwards replaced with 40-character
hexadecimal blob object name which is based only on the particular file
contents.

This patch simplifies handling of $Id$ keywords by removing them since
they are not used anymore.
2018-07-25 00:53:25 +02:00

1157 lines
31 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Jim Winstead <jimw@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "fopen_wrappers.h"
#include "php_globals.h"
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#if HAVE_SYS_VFS_H
# include <sys/vfs.h>
#endif
#ifdef OS2
# define INCL_DOS
# include <os2.h>
#endif
#if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
# include <sys/statvfs.h>
#elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_STATFS)
# include <sys/statfs.h>
#elif defined(HAVE_SYS_MOUNT_H) && defined(HAVE_STATFS)
# include <sys/mount.h>
#endif
#if HAVE_PWD_H
# ifdef PHP_WIN32
# include "win32/pwd.h"
# else
# include <pwd.h>
# endif
#endif
#if HAVE_GRP_H
# ifdef PHP_WIN32
# include "win32/grp.h"
# else
# include <grp.h>
# endif
#endif
#if HAVE_UTIME
# ifdef PHP_WIN32
# include <sys/utime.h>
# else
# include <utime.h>
# endif
#endif
#ifdef PHP_WIN32
#include "win32/winutil.h"
#endif
#include "basic_functions.h"
#include "php_filestat.h"
PHP_RINIT_FUNCTION(filestat) /* {{{ */
{
BG(CurrentStatFile)=NULL;
BG(CurrentLStatFile)=NULL;
return SUCCESS;
}
/* }}} */
PHP_RSHUTDOWN_FUNCTION(filestat) /* {{{ */
{
if (BG(CurrentStatFile)) {
efree (BG(CurrentStatFile));
BG(CurrentStatFile) = NULL;
}
if (BG(CurrentLStatFile)) {
efree (BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL;
}
return SUCCESS;
}
/* }}} */
static int php_disk_total_space(char *path, double *space) /* {{{ */
#if defined(WINDOWS) /* {{{ */
{
ULARGE_INTEGER FreeBytesAvailableToCaller;
ULARGE_INTEGER TotalNumberOfBytes;
ULARGE_INTEGER TotalNumberOfFreeBytes;
PHP_WIN32_IOUTIL_INIT_W(path)
if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
php_error_docref(NULL, E_WARNING, "%s", php_win_err());
PHP_WIN32_IOUTIL_CLEANUP_W()
return FAILURE;
}
/* i know - this is ugly, but i works <thies@thieso.net> */
*space = TotalNumberOfBytes.HighPart * (double) (((zend_ulong)1) << 31) * 2.0 + TotalNumberOfBytes.LowPart;
PHP_WIN32_IOUTIL_CLEANUP_W()
return SUCCESS;
}
/* }}} */
#elif defined(OS2) /* {{{ */
{
double bytestotal = 0;
FSALLOCATE fsinfo;
char drive = path[0] & 95;
if (DosQueryFSInfo( drive ? drive - 64 : 0, FSIL_ALLOC, &fsinfo, sizeof( fsinfo ) ) == 0) {
bytestotal = (double)fsinfo.cbSector * fsinfo.cSectorUnit * fsinfo.cUnit;
*space = bytestotal;
return SUCCESS;
}
return FAILURE;
}
/* }}} */
#else /* {{{ if !defined(OS2) && !defined(WINDOWS) */
{
double bytestotal = 0;
#if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
struct statvfs buf;
#elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
struct statfs buf;
#endif
#if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
if (statvfs(path, &buf)) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
return FAILURE;
}
if (buf.f_frsize) {
bytestotal = (((double)buf.f_blocks) * ((double)buf.f_frsize));
} else {
bytestotal = (((double)buf.f_blocks) * ((double)buf.f_bsize));
}
#elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
if (statfs(path, &buf)) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
return FAILURE;
}
bytestotal = (((double)buf.f_bsize) * ((double)buf.f_blocks));
#endif
*space = bytestotal;
return SUCCESS;
}
#endif
/* }}} */
/* }}} */
/* {{{ proto float disk_total_space(string path)
Get total disk space for filesystem that path is on */
PHP_FUNCTION(disk_total_space)
{
double bytestotal;
char *path;
size_t path_len;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH(path, path_len)
ZEND_PARSE_PARAMETERS_END();
if (php_check_open_basedir(path)) {
RETURN_FALSE;
}
if (php_disk_total_space(path, &bytestotal) == SUCCESS) {
RETURN_DOUBLE(bytestotal);
}
RETURN_FALSE;
}
/* }}} */
static int php_disk_free_space(char *path, double *space) /* {{{ */
#if defined(WINDOWS) /* {{{ */
{
ULARGE_INTEGER FreeBytesAvailableToCaller;
ULARGE_INTEGER TotalNumberOfBytes;
ULARGE_INTEGER TotalNumberOfFreeBytes;
PHP_WIN32_IOUTIL_INIT_W(path)
if (GetDiskFreeSpaceExW(pathw, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == 0) {
php_error_docref(NULL, E_WARNING, "%s", php_win_err());
PHP_WIN32_IOUTIL_CLEANUP_W()
return FAILURE;
}
/* i know - this is ugly, but i works <thies@thieso.net> */
*space = FreeBytesAvailableToCaller.HighPart * (double) (((zend_ulong)1) << 31) * 2.0 + FreeBytesAvailableToCaller.LowPart;
PHP_WIN32_IOUTIL_CLEANUP_W()
return SUCCESS;
}
/* }}} */
#elif defined(OS2) /* {{{ */
{
double bytesfree = 0;
FSALLOCATE fsinfo;
char drive = path[0] & 95;
if (DosQueryFSInfo( drive ? drive - 64 : 0, FSIL_ALLOC, &fsinfo, sizeof( fsinfo ) ) == 0) {
bytesfree = (double)fsinfo.cbSector * fsinfo.cSectorUnit * fsinfo.cUnitAvail;
*space = bytesfree;
return SUCCESS;
}
return FAILURE;
}
/* }}} */
#else /* {{{ if !defined(OS2) && !defined(WINDOWS) */
{
double bytesfree = 0;
#if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
struct statvfs buf;
#elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
struct statfs buf;
#endif
#if defined(HAVE_SYS_STATVFS_H) && defined(HAVE_STATVFS)
if (statvfs(path, &buf)) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
return FAILURE;
}
if (buf.f_frsize) {
bytesfree = (((double)buf.f_bavail) * ((double)buf.f_frsize));
} else {
bytesfree = (((double)buf.f_bavail) * ((double)buf.f_bsize));
}
#elif (defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_MOUNT_H)) && defined(HAVE_STATFS)
if (statfs(path, &buf)) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
return FAILURE;
}
bytesfree = (((double)buf.f_bsize) * ((double)buf.f_bavail));
#endif
*space = bytesfree;
return SUCCESS;
}
#endif
/* }}} */
/* }}} */
/* {{{ proto float disk_free_space(string path)
Get free disk space for filesystem that path is on */
PHP_FUNCTION(disk_free_space)
{
double bytesfree;
char *path;
size_t path_len;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH(path, path_len)
ZEND_PARSE_PARAMETERS_END();
if (php_check_open_basedir(path)) {
RETURN_FALSE;
}
if (php_disk_free_space(path, &bytesfree) == SUCCESS) {
RETURN_DOUBLE(bytesfree);
}
RETURN_FALSE;
}
/* }}} */
#ifndef PHP_WIN32
PHPAPI int php_get_gid_by_name(const char *name, gid_t *gid)
{
#if defined(ZTS) && defined(HAVE_GETGRNAM_R) && defined(_SC_GETGR_R_SIZE_MAX)
struct group gr;
struct group *retgrptr;
long grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
char *grbuf;
if (grbuflen < 1) {
return FAILURE;
}
grbuf = emalloc(grbuflen);
if (getgrnam_r(name, &gr, grbuf, grbuflen, &retgrptr) != 0 || retgrptr == NULL) {
efree(grbuf);
return FAILURE;
}
efree(grbuf);
*gid = gr.gr_gid;
#else
struct group *gr = getgrnam(name);
if (!gr) {
return FAILURE;
}
*gid = gr->gr_gid;
#endif
return SUCCESS;
}
#endif
static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
{
char *filename;
size_t filename_len;
zval *group;
#if !defined(WINDOWS)
gid_t gid;
int ret;
#endif
php_stream_wrapper *wrapper;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_PATH(filename, filename_len)
Z_PARAM_ZVAL(group)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
if(wrapper && wrapper->wops->stream_metadata) {
int option;
void *value;
if (Z_TYPE_P(group) == IS_LONG) {
option = PHP_STREAM_META_GROUP;
value = &Z_LVAL_P(group);
} else if (Z_TYPE_P(group) == IS_STRING) {
option = PHP_STREAM_META_GROUP_NAME;
value = Z_STRVAL_P(group);
} else {
php_error_docref(NULL, E_WARNING, "parameter 2 should be string or int, %s given", zend_zval_type_name(group));
RETURN_FALSE;
}
if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
} else {
#if !defined(WINDOWS)
/* On Windows, we expect regular chgrp to fail silently by default */
php_error_docref(NULL, E_WARNING, "Can not call chgrp() for a non-standard stream");
#endif
RETURN_FALSE;
}
}
#if defined(WINDOWS)
/* We have no native chgrp on Windows, nothing left to do if stream doesn't have own implementation */
RETURN_FALSE;
#else
if (Z_TYPE_P(group) == IS_LONG) {
gid = (gid_t)Z_LVAL_P(group);
} else if (Z_TYPE_P(group) == IS_STRING) {
if(php_get_gid_by_name(Z_STRVAL_P(group), &gid) != SUCCESS) {
php_error_docref(NULL, E_WARNING, "Unable to find gid for %s", Z_STRVAL_P(group));
RETURN_FALSE;
}
} else {
php_error_docref(NULL, E_WARNING, "parameter 2 should be string or int, %s given", zend_zval_type_name(group));
RETURN_FALSE;
}
/* Check the basedir */
if (php_check_open_basedir(filename)) {
RETURN_FALSE;
}
if (do_lchgrp) {
#if HAVE_LCHOWN
ret = VCWD_LCHOWN(filename, -1, gid);
#endif
} else {
ret = VCWD_CHOWN(filename, -1, gid);
}
if (ret == -1) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
RETURN_TRUE;
#endif
}
/* }}} */
/* {{{ proto bool chgrp(string filename, mixed group)
Change file group */
PHP_FUNCTION(chgrp)
{
php_do_chgrp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto bool lchgrp(string filename, mixed group)
Change symlink group */
#if HAVE_LCHOWN
PHP_FUNCTION(lchgrp)
{
# if !defined(WINDOWS)
php_do_chgrp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
# else
RETURN_FALSE;
# endif
}
#endif
/* }}} */
#ifndef PHP_WIN32
PHPAPI uid_t php_get_uid_by_name(const char *name, uid_t *uid)
{
#if defined(ZTS) && defined(_SC_GETPW_R_SIZE_MAX) && defined(HAVE_GETPWNAM_R)
struct passwd pw;
struct passwd *retpwptr = NULL;
long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char *pwbuf;
if (pwbuflen < 1) {
return FAILURE;
}
pwbuf = emalloc(pwbuflen);
if (getpwnam_r(name, &pw, pwbuf, pwbuflen, &retpwptr) != 0 || retpwptr == NULL) {
efree(pwbuf);
return FAILURE;
}
efree(pwbuf);
*uid = pw.pw_uid;
#else
struct passwd *pw = getpwnam(name);
if (!pw) {
return FAILURE;
}
*uid = pw->pw_uid;
#endif
return SUCCESS;
}
#endif
static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
{
char *filename;
size_t filename_len;
zval *user;
#if !defined(WINDOWS)
uid_t uid;
int ret;
#endif
php_stream_wrapper *wrapper;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_PATH(filename, filename_len)
Z_PARAM_ZVAL(user)
ZEND_PARSE_PARAMETERS_END();
wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
if(wrapper && wrapper->wops->stream_metadata) {
int option;
void *value;
if (Z_TYPE_P(user) == IS_LONG) {
option = PHP_STREAM_META_OWNER;
value = &Z_LVAL_P(user);
} else if (Z_TYPE_P(user) == IS_STRING) {
option = PHP_STREAM_META_OWNER_NAME;
value = Z_STRVAL_P(user);
} else {
php_error_docref(NULL, E_WARNING, "parameter 2 should be string or int, %s given", zend_zval_type_name(user));
RETURN_FALSE;
}
if(wrapper->wops->stream_metadata(wrapper, filename, option, value, NULL)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
} else {
#if !defined(WINDOWS)
/* On Windows, we expect regular chown to fail silently by default */
php_error_docref(NULL, E_WARNING, "Can not call chown() for a non-standard stream");
#endif
RETURN_FALSE;
}
}
#if defined(WINDOWS)
/* We have no native chown on Windows, nothing left to do if stream doesn't have own implementation */
RETURN_FALSE;
#else
if (Z_TYPE_P(user) == IS_LONG) {
uid = (uid_t)Z_LVAL_P(user);
} else if (Z_TYPE_P(user) == IS_STRING) {
if(php_get_uid_by_name(Z_STRVAL_P(user), &uid) != SUCCESS) {
php_error_docref(NULL, E_WARNING, "Unable to find uid for %s", Z_STRVAL_P(user));
RETURN_FALSE;
}
} else {
php_error_docref(NULL, E_WARNING, "parameter 2 should be string or int, %s given", zend_zval_type_name(user));
RETURN_FALSE;
}
/* Check the basedir */
if (php_check_open_basedir(filename)) {
RETURN_FALSE;
}
if (do_lchown) {
#if HAVE_LCHOWN
ret = VCWD_LCHOWN(filename, uid, -1);
#endif
} else {
ret = VCWD_CHOWN(filename, uid, -1);
}
if (ret == -1) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
RETURN_TRUE;
#endif
}
/* }}} */
/* {{{ proto bool chown(string filename, mixed user)
Change file owner */
PHP_FUNCTION(chown)
{
php_do_chown(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto bool chown(string filename, mixed user)
Change file owner */
#if HAVE_LCHOWN
PHP_FUNCTION(lchown)
{
# if !defined(WINDOWS)
RETVAL_TRUE;
php_do_chown(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
# else
RETURN_FALSE;
# endif
}
#endif
/* }}} */
/* {{{ proto bool chmod(string filename, int mode)
Change file mode */
PHP_FUNCTION(chmod)
{
char *filename;
size_t filename_len;
zend_long mode;
int ret;
mode_t imode;
php_stream_wrapper *wrapper;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_PATH(filename, filename_len)
Z_PARAM_LONG(mode)
ZEND_PARSE_PARAMETERS_END();
wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
if(wrapper && wrapper->wops->stream_metadata) {
if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_ACCESS, &mode, NULL)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
} else {
php_error_docref(NULL, E_WARNING, "Can not call chmod() for a non-standard stream");
RETURN_FALSE;
}
}
/* Check the basedir */
if (php_check_open_basedir(filename)) {
RETURN_FALSE;
}
imode = (mode_t) mode;
ret = VCWD_CHMOD(filename, imode);
if (ret == -1) {
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
#if HAVE_UTIME
/* {{{ proto bool touch(string filename [, int time [, int atime]])
Set modification time of file */
PHP_FUNCTION(touch)
{
char *filename;
size_t filename_len;
zend_long filetime = 0, fileatime = 0;
int ret, argc = ZEND_NUM_ARGS();
FILE *file;
struct utimbuf newtimebuf;
struct utimbuf *newtime = &newtimebuf;
php_stream_wrapper *wrapper;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_PATH(filename, filename_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(filetime)
Z_PARAM_LONG(fileatime)
ZEND_PARSE_PARAMETERS_END();
if (!filename_len) {
RETURN_FALSE;
}
switch (argc) {
case 1:
#ifdef HAVE_UTIME_NULL
newtime = NULL;
#else
newtime->modtime = newtime->actime = time(NULL);
#endif
break;
case 2:
newtime->modtime = newtime->actime = filetime;
break;
case 3:
newtime->modtime = filetime;
newtime->actime = fileatime;
break;
default:
/* Never reached */
WRONG_PARAM_COUNT;
}
wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
if(wrapper != &php_plain_files_wrapper || strncasecmp("file://", filename, 7) == 0) {
if(wrapper && wrapper->wops->stream_metadata) {
if(wrapper->wops->stream_metadata(wrapper, filename, PHP_STREAM_META_TOUCH, newtime, NULL)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
} else {
php_stream *stream;
if(argc > 1) {
php_error_docref(NULL, E_WARNING, "Can not call touch() for a non-standard stream");
RETURN_FALSE;
}
stream = php_stream_open_wrapper_ex(filename, "c", REPORT_ERRORS, NULL, NULL);
if(stream != NULL) {
php_stream_close(stream);
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
}
/* Check the basedir */
if (php_check_open_basedir(filename)) {
RETURN_FALSE;
}
/* create the file if it doesn't exist already */
if (VCWD_ACCESS(filename, F_OK) != 0) {
file = VCWD_FOPEN(filename, "w");
if (file == NULL) {
php_error_docref(NULL, E_WARNING, "Unable to create file %s because %s", filename, strerror(errno));
RETURN_FALSE;
}
fclose(file);
}
ret = VCWD_UTIME(filename, newtime);
if (ret == -1) {
php_error_docref(NULL, E_WARNING, "Utime failed: %s", strerror(errno));
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
#endif
/* {{{ php_clear_stat_cache()
*/
PHPAPI void php_clear_stat_cache(zend_bool clear_realpath_cache, const char *filename, size_t filename_len)
{
/* always clear CurrentStatFile and CurrentLStatFile even if filename is not NULL
* as it may contain outdated data (e.g. "nlink" for a directory when deleting a file
* in this directory, as shown by lstat_stat_variation9.phpt) */
if (BG(CurrentStatFile)) {
efree(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL;
}
if (BG(CurrentLStatFile)) {
efree(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL;
}
if (clear_realpath_cache) {
if (filename != NULL) {
realpath_cache_del(filename, filename_len);
} else {
realpath_cache_clean();
}
}
}
/* }}} */
/* {{{ proto void clearstatcache([bool clear_realpath_cache[, string filename]])
Clear file stat cache */
PHP_FUNCTION(clearstatcache)
{
zend_bool clear_realpath_cache = 0;
char *filename = NULL;
size_t filename_len = 0;
ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(clear_realpath_cache)
Z_PARAM_PATH(filename, filename_len)
ZEND_PARSE_PARAMETERS_END();
php_clear_stat_cache(clear_realpath_cache, filename, filename_len);
}
/* }}} */
#define IS_LINK_OPERATION(__t) ((__t) == FS_TYPE || (__t) == FS_IS_LINK || (__t) == FS_LSTAT)
#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)
#define IS_ABLE_CHECK(__t) ((__t) == FS_IS_R || (__t) == FS_IS_W || (__t) == FS_IS_X)
#define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS)
/* {{{ php_stat
*/
PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zval *return_value)
{
zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev,
stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks;
zend_stat_t *stat_sb;
php_stream_statbuf ssb;
int flags = 0, rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */
char *stat_sb_names[13] = {
"dev", "ino", "mode", "nlink", "uid", "gid", "rdev",
"size", "atime", "mtime", "ctime", "blksize", "blocks"
};
const char *local;
php_stream_wrapper *wrapper;
if (!filename_length) {
RETURN_FALSE;
}
if ((wrapper = php_stream_locate_url_wrapper(filename, &local, 0)) == &php_plain_files_wrapper && php_check_open_basedir(local)) {
RETURN_FALSE;
}
if (IS_ACCESS_CHECK(type)) {
if (wrapper == &php_plain_files_wrapper) {
switch (type) {
#ifdef F_OK
case FS_EXISTS:
RETURN_BOOL(VCWD_ACCESS(local, F_OK) == 0);
break;
#endif
#ifdef W_OK
case FS_IS_W:
RETURN_BOOL(VCWD_ACCESS(local, W_OK) == 0);
break;
#endif
#ifdef R_OK
case FS_IS_R:
RETURN_BOOL(VCWD_ACCESS(local, R_OK) == 0);
break;
#endif
#ifdef X_OK
case FS_IS_X:
RETURN_BOOL(VCWD_ACCESS(local, X_OK) == 0);
break;
#endif
}
}
}
if (IS_LINK_OPERATION(type)) {
flags |= PHP_STREAM_URL_STAT_LINK;
}
if (IS_EXISTS_CHECK(type)) {
flags |= PHP_STREAM_URL_STAT_QUIET;
}
if (php_stream_stat_path_ex((char *)filename, flags, &ssb, NULL)) {
/* Error Occurred */
if (!IS_EXISTS_CHECK(type)) {
php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", filename);
}
RETURN_FALSE;
}
stat_sb = &ssb.sb;
if (type >= FS_IS_W && type <= FS_IS_X) {
if(ssb.sb.st_uid==getuid()) {
rmask=S_IRUSR;
wmask=S_IWUSR;
xmask=S_IXUSR;
} else if(ssb.sb.st_gid==getgid()) {
rmask=S_IRGRP;
wmask=S_IWGRP;
xmask=S_IXGRP;
} else {
int groups, n, i;
gid_t *gids;
groups = getgroups(0, NULL);
if(groups > 0) {
gids=(gid_t *)safe_emalloc(groups, sizeof(gid_t), 0);
n=getgroups(groups, gids);
for(i=0;i<n;i++){
if(ssb.sb.st_gid==gids[i]) {
rmask=S_IRGRP;
wmask=S_IWGRP;
xmask=S_IXGRP;
break;
}
}
efree(gids);
}
}
}
if (IS_ABLE_CHECK(type) && getuid() == 0) {
/* root has special perms on plain_wrapper */
if (wrapper == &php_plain_files_wrapper) {
if (type == FS_IS_X) {
xmask = S_IXROOT;
} else {
RETURN_TRUE;
}
}
}
switch (type) {
case FS_PERMS:
RETURN_LONG((zend_long)ssb.sb.st_mode);
case FS_INODE:
RETURN_LONG((zend_long)ssb.sb.st_ino);
case FS_SIZE:
RETURN_LONG((zend_long)ssb.sb.st_size);
case FS_OWNER:
RETURN_LONG((zend_long)ssb.sb.st_uid);
case FS_GROUP:
RETURN_LONG((zend_long)ssb.sb.st_gid);
case FS_ATIME:
RETURN_LONG((zend_long)ssb.sb.st_atime);
case FS_MTIME:
RETURN_LONG((zend_long)ssb.sb.st_mtime);
case FS_CTIME:
RETURN_LONG((zend_long)ssb.sb.st_ctime);
case FS_TYPE:
if (S_ISLNK(ssb.sb.st_mode)) {
RETURN_STRING("link");
}
switch(ssb.sb.st_mode & S_IFMT) {
case S_IFIFO: RETURN_STRING("fifo");
case S_IFCHR: RETURN_STRING("char");
case S_IFDIR: RETURN_STRING("dir");
case S_IFBLK: RETURN_STRING("block");
case S_IFREG: RETURN_STRING("file");
#if defined(S_IFSOCK) && !defined(PHP_WIN32)
case S_IFSOCK: RETURN_STRING("socket");
#endif
}
php_error_docref(NULL, E_NOTICE, "Unknown file type (%d)", ssb.sb.st_mode&S_IFMT);
RETURN_STRING("unknown");
case FS_IS_W:
RETURN_BOOL((ssb.sb.st_mode & wmask) != 0);
case FS_IS_R:
RETURN_BOOL((ssb.sb.st_mode&rmask)!=0);
case FS_IS_X:
RETURN_BOOL((ssb.sb.st_mode&xmask)!=0);
case FS_IS_FILE:
RETURN_BOOL(S_ISREG(ssb.sb.st_mode));
case FS_IS_DIR:
RETURN_BOOL(S_ISDIR(ssb.sb.st_mode));
case FS_IS_LINK:
RETURN_BOOL(S_ISLNK(ssb.sb.st_mode));
case FS_EXISTS:
RETURN_TRUE; /* the false case was done earlier */
case FS_LSTAT:
/* FALLTHROUGH */
case FS_STAT:
array_init(return_value);
ZVAL_LONG(&stat_dev, stat_sb->st_dev);
ZVAL_LONG(&stat_ino, stat_sb->st_ino);
ZVAL_LONG(&stat_mode, stat_sb->st_mode);
ZVAL_LONG(&stat_nlink, stat_sb->st_nlink);
ZVAL_LONG(&stat_uid, stat_sb->st_uid);
ZVAL_LONG(&stat_gid, stat_sb->st_gid);
#ifdef HAVE_ST_RDEV
# ifdef PHP_WIN32
/* It is unsigned, so if a negative came from userspace, it'll
convert to UINT_MAX, but we wan't to keep the userspace value.
Almost the same as in php_if_fstat. */
if ((int)stat_sb->st_rdev < 0) {
ZVAL_LONG(&stat_rdev, (int)stat_sb->st_rdev);
} else {
ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
}
# else
ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
# endif
#else
ZVAL_LONG(&stat_rdev, -1);
#endif
ZVAL_LONG(&stat_size, stat_sb->st_size);
ZVAL_LONG(&stat_atime, stat_sb->st_atime);
ZVAL_LONG(&stat_mtime, stat_sb->st_mtime);
ZVAL_LONG(&stat_ctime, stat_sb->st_ctime);
#ifdef HAVE_ST_BLKSIZE
ZVAL_LONG(&stat_blksize, stat_sb->st_blksize);
#else
ZVAL_LONG(&stat_blksize,-1);
#endif
#ifdef HAVE_ST_BLOCKS
ZVAL_LONG(&stat_blocks, stat_sb->st_blocks);
#else
ZVAL_LONG(&stat_blocks,-1);
#endif
/* Store numeric indexes in proper order */
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_dev);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ino);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mode);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_nlink);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_uid);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_gid);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_rdev);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_size);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_atime);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mtime);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ctime);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blksize);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks);
/* Store string indexes referencing the same zval*/
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize);
zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks);
return;
}
php_error_docref(NULL, E_WARNING, "Didn't understand stat call");
RETURN_FALSE;
}
/* }}} */
/* another quickie macro to make defining similar functions easier */
/* {{{ FileFunction(name, funcnum) */
#define FileFunction(name, funcnum) \
ZEND_NAMED_FUNCTION(name) { \
char *filename; \
size_t filename_len; \
\
ZEND_PARSE_PARAMETERS_START(1, 1) \
Z_PARAM_PATH(filename, filename_len) \
ZEND_PARSE_PARAMETERS_END(); \
\
php_stat(filename, filename_len, funcnum, return_value); \
}
/* }}} */
/* {{{ proto int fileperms(string filename)
Get file permissions */
FileFunction(PHP_FN(fileperms), FS_PERMS)
/* }}} */
/* {{{ proto int fileinode(string filename)
Get file inode */
FileFunction(PHP_FN(fileinode), FS_INODE)
/* }}} */
/* {{{ proto int filesize(string filename)
Get file size */
FileFunction(PHP_FN(filesize), FS_SIZE)
/* }}} */
/* {{{ proto int fileowner(string filename)
Get file owner */
FileFunction(PHP_FN(fileowner), FS_OWNER)
/* }}} */
/* {{{ proto int filegroup(string filename)
Get file group */
FileFunction(PHP_FN(filegroup), FS_GROUP)
/* }}} */
/* {{{ proto int fileatime(string filename)
Get last access time of file */
FileFunction(PHP_FN(fileatime), FS_ATIME)
/* }}} */
/* {{{ proto int filemtime(string filename)
Get last modification time of file */
FileFunction(PHP_FN(filemtime), FS_MTIME)
/* }}} */
/* {{{ proto int filectime(string filename)
Get inode modification time of file */
FileFunction(PHP_FN(filectime), FS_CTIME)
/* }}} */
/* {{{ proto string filetype(string filename)
Get file type */
FileFunction(PHP_FN(filetype), FS_TYPE)
/* }}} */
/* {{{ proto bool is_writable(string filename)
Returns true if file can be written */
FileFunction(PHP_FN(is_writable), FS_IS_W)
/* }}} */
/* {{{ proto bool is_readable(string filename)
Returns true if file can be read */
FileFunction(PHP_FN(is_readable), FS_IS_R)
/* }}} */
/* {{{ proto bool is_executable(string filename)
Returns true if file is executable */
FileFunction(PHP_FN(is_executable), FS_IS_X)
/* }}} */
/* {{{ proto bool is_file(string filename)
Returns true if file is a regular file */
FileFunction(PHP_FN(is_file), FS_IS_FILE)
/* }}} */
/* {{{ proto bool is_dir(string filename)
Returns true if file is directory */
FileFunction(PHP_FN(is_dir), FS_IS_DIR)
/* }}} */
/* {{{ proto bool is_link(string filename)
Returns true if file is symbolic link */
FileFunction(PHP_FN(is_link), FS_IS_LINK)
/* }}} */
/* {{{ proto bool file_exists(string filename)
Returns true if filename exists */
FileFunction(PHP_FN(file_exists), FS_EXISTS)
/* }}} */
/* {{{ proto array lstat(string filename)
Give information about a file or symbolic link */
FileFunction(php_if_lstat, FS_LSTAT)
/* }}} */
/* {{{ proto array stat(string filename)
Give information about a file */
FileFunction(php_if_stat, FS_STAT)
/* }}} */
/* {{{ proto bool realpath_cache_size()
Get current size of realpath cache */
PHP_FUNCTION(realpath_cache_size)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
RETURN_LONG(realpath_cache_size());
}
/* {{{ proto bool realpath_cache_get()
Get current size of realpath cache */
PHP_FUNCTION(realpath_cache_get)
{
realpath_cache_bucket **buckets = realpath_cache_get_buckets(), **end = buckets + realpath_cache_max_buckets();
if (zend_parse_parameters_none() == FAILURE) {
return;
}
array_init(return_value);
while(buckets < end) {
realpath_cache_bucket *bucket = *buckets;
while(bucket) {
zval entry;
array_init(&entry);
/* bucket->key is unsigned long */
if (ZEND_LONG_MAX >= bucket->key) {
add_assoc_long_ex(&entry, "key", sizeof("key") - 1, bucket->key);
} else {
add_assoc_double_ex(&entry, "key", sizeof("key") - 1, (double)bucket->key);
}
add_assoc_bool_ex(&entry, "is_dir", sizeof("is_dir") - 1, bucket->is_dir);
add_assoc_stringl_ex(&entry, "realpath", sizeof("realpath") - 1, bucket->realpath, bucket->realpath_len);
add_assoc_long_ex(&entry, "expires", sizeof("expires") - 1, bucket->expires);
#ifdef PHP_WIN32
add_assoc_bool_ex(&entry, "is_rvalid", sizeof("is_rvalid") - 1, bucket->is_rvalid);
add_assoc_bool_ex(&entry, "is_wvalid", sizeof("is_wvalid") - 1, bucket->is_wvalid);
add_assoc_bool_ex(&entry, "is_readable", sizeof("is_readable") - 1, bucket->is_readable);
add_assoc_bool_ex(&entry, "is_writable", sizeof("is_writable") - 1, bucket->is_writable);
#endif
zend_hash_str_update(Z_ARRVAL_P(return_value), bucket->path, bucket->path_len, &entry);
bucket = bucket->next;
}
buckets++;
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/