implement PHP 5.4+ stream_metadata method for VFS, allowing to use that functionality from other stream-wrappers like php-smbclient

This commit is contained in:
Ralf Becker 2016-07-21 09:51:36 +02:00
parent e6a8f393a4
commit 14908a4172
5 changed files with 175 additions and 70 deletions

View File

@ -94,6 +94,14 @@ class Vfs
* Name of the lock table * Name of the lock table
*/ */
const LOCK_TABLE = 'egw_locks'; const LOCK_TABLE = 'egw_locks';
/**
* How much should be logged to the apache error-log
*
* 0 = Nothing
* 1 = only errors
* 2 = all function calls and errors (contains passwords too!)
*/
const LOG_LEVEL = 1;
/** /**
* Current user has root rights, no access checks performed! * Current user has root rights, no access checks performed!
* *
@ -603,8 +611,10 @@ class Vfs
if ($options['url']) if ($options['url'])
{ {
$lstat = @lstat($path); if (($stat = @lstat($path)))
$stat = array_slice($lstat,13); // remove numerical indices 0-12 {
$stat = array_slice($stat,13); // remove numerical indices 0-12
}
} }
else else
{ {
@ -2145,6 +2155,21 @@ class Vfs
return $vfs->resolve_url_symlinks($_path, $file_exists, $resolve_last_symlink, $stat); return $vfs->resolve_url_symlinks($_path, $file_exists, $resolve_last_symlink, $stat);
} }
/**
* Resolve the given path according to our fstab
*
* @param string $_path
* @param boolean $do_symlink =true is a direct match allowed, default yes (must be false for a lstat or readlink!)
* @param boolean $use_symlinkcache =true
* @param boolean $replace_user_pass_host =true replace $user,$pass,$host in url, default true, if false result is not cached
* @param boolean $fix_url_query =false true append relativ path to url query parameter, default not
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
*/
static function resolve_url($_path,$do_symlink=true,$use_symlinkcache=true,$replace_user_pass_host=true,$fix_url_query=false)
{
Vfs\StreamWrapper::resolve_url($_path, $do_symlink, $use_symlinkcache, $replace_user_pass_host, $fix_url_query);
}
/** /**
* This method is called in response to mkdir() calls on URL paths associated with the wrapper. * This method is called in response to mkdir() calls on URL paths associated with the wrapper.
* *
@ -2262,7 +2287,7 @@ class Vfs
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * touch just running on VFS path
* *
* @param string $path * @param string $path
* @param int $time =null modification time (unix timestamp), default null = current time * @param int $time =null modification time (unix timestamp), default null = current time
@ -2271,11 +2296,11 @@ class Vfs
*/ */
static function touch($path,$time=null,$atime=null) static function touch($path,$time=null,$atime=null)
{ {
return self::_call_on_backend('touch',array($path,$time,$atime)); return $path[0] == '/' && touch(self::PREFIX.$path, $time, $atime);
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * chmod just running on VFS path
* *
* Requires owner or root rights! * Requires owner or root rights!
* *
@ -2285,35 +2310,35 @@ class Vfs
*/ */
static function chmod($path,$mode) static function chmod($path,$mode)
{ {
return self::_call_on_backend('chmod',array($path,$mode)); return $path[0] == '/' && chmod(self::PREFIX.$path, $mode);
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * chmod just running on VFS path
* *
* Requires root rights! * Requires root rights!
* *
* @param string $path * @param string $path
* @param int $owner numeric user id * @param int|string $owner numeric user id or account-name
* @return boolean true on success, false otherwise * @return boolean true on success, false otherwise
*/ */
static function chown($path,$owner) static function chown($path,$owner)
{ {
return self::_call_on_backend('chown',array($path,$owner)); return $path[0] == '/' && chown(self::PREFIX.$path, $owner);
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * chgrp just running on VFS path
* *
* Requires owner or root rights! * Requires owner or root rights!
* *
* @param string $path * @param string $path
* @param int $group numeric group id * @param int|string $group numeric group id or group-name
* @return boolean true on success, false otherwise * @return boolean true on success, false otherwise
*/ */
static function chgrp($path,$group) static function chgrp($path,$group)
{ {
return self::_call_on_backend('chgrp',array($path,$group)); return $path[0] == '/' && chown(self::PREFIX.$path, $group);
} }
/** /**

View File

@ -432,16 +432,27 @@ class StreamWrapper implements Vfs\StreamWrapperIface
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* We only implement touch, as other functionality would require webserver to run as root.
* *
* @param string $url * @param string $url
* @param int $time =null modification time (unix timestamp), default null = current time * @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs! * @param array|int|string $value
* @return boolean true on success, false otherwise * - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/ */
static function touch($url,$time=null,$atime=null) function stream_metadata($url, $option, $value)
{ {
$path = Vfs::decodePath(Vfs::parse_url($url,PHP_URL_PATH)); if ($option != STREAM_META_TOUCH)
{
return false; // not implemented / supported
}
$path = Vfs::decodePath(Vfs::parse_url($url, PHP_URL_PATH));
$parent = dirname($path); $parent = dirname($path);
// check access rights (in real filesystem AND by mount perms) // check access rights (in real filesystem AND by mount perms)
@ -450,55 +461,9 @@ class StreamWrapper implements Vfs\StreamWrapperIface
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!"); if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
return false; return false;
} }
return touch($path,$time,$atime);
}
/** array_unshift($value, $path);
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static return call_user_func_array('touch', $value);
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param string $mode mode string see Vfs::mode2int
* @return boolean true on success, false otherwise
*/
static function chmod($path,$mode)
{
unset($path, $mode); // not used, but required by interface
return false;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param int $owner numeric user id
* @return boolean true on success, false otherwise
*/
static function chown($path,$owner)
{
unset($path, $owner); // not used, but required by interface
return false;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param int $group numeric group id
* @return boolean true on success, false otherwise
*/
static function chgrp($path,$group)
{
unset($path, $group); // not used, but required by interface
return false;
} }
/** /**

View File

@ -329,6 +329,29 @@ class StreamWrapper extends LinksParent
return parent::dir_opendir($url, $options); return parent::dir_opendir($url, $options);
} }
/**
* Reimplemented to create an entry directory on the fly AND delete our stat cache!
*
* @param string $url
* @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
*/
protected function touch($url,$time=null,$atime=null)
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$time,$atime)");
if (!($stat = self::url_stat($url,STREAM_URL_STAT_QUIET)))
{
// file does not exist --> create an empty one
if (!($f = fopen(self::SCHEME.'://default'.Vfs::parse_url($url,PHP_URL_PATH),'w')) || !fclose($f))
{
return false;
}
}
return is_null($time) ? true : parent::touch($url,$time,$atime);
}
/** /**
* Register this stream-wrapper * Register this stream-wrapper
*/ */

View File

@ -835,6 +835,49 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
return $ret; return $ret;
} }
/**
* StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* We use protected helper methods touch, chmod, chown and chgrp to implement the functionality.
*
* @param string $path
* @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param array|int|string $value
* - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/
function stream_metadata($path, $option, $value)
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path, $option, ".array2string($value).")");
switch($option)
{
case STREAM_META_TOUCH:
return $this->touch($path, $value[0]); // atime is not supported
case STREAM_META_ACCESS:
return $this->chmod($path, $value);
case STREAM_META_OWNER_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'u')) === false)
return false;
// fall through
case STREAM_META_OWNER:
return $this->chown($path, $value);
case STREAM_META_GROUP_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'g')) === false)
return false;
// fall through
case STREAM_META_GROUP:
return $this->chgrp($path, $value);
}
return false;
}
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * This is not (yet) a stream-wrapper function, but it's necessary and can be used static
* *
@ -842,7 +885,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $time =null modification time (unix timestamp), default null = current time * @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs! * @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
*/ */
static function touch($url,$time=null,$atime=null) protected function touch($url,$time=null,$atime=null)
{ {
unset($atime); // not used unset($atime); // not used
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $time)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $time)");
@ -880,7 +923,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $owner * @param int $owner
* @return boolean * @return boolean
*/ */
static function chown($url,$owner) protected function chown($url,$owner)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
@ -925,7 +968,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $owner * @param int $owner
* @return boolean * @return boolean
*/ */
static function chgrp($url,$owner) protected function chgrp($url,$owner)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
@ -971,7 +1014,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $mode * @param int $mode
* @return boolean * @return boolean
*/ */
static function chmod($url,$mode) protected function chmod($url,$mode)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $mode)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $mode)");

View File

@ -480,6 +480,55 @@ class StreamWrapper implements StreamWrapperIface
return fstat($this->opened_stream); return fstat($this->opened_stream);
} }
/**
* StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* @param string $path
* @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param array|int|string $value
* - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/
function stream_metadata($path, $option, $value)
{
if (!($url = $this->resolve_url_symlinks($path, $option == STREAM_META_TOUCH, false))) // true,false file need to exist, but do not resolve last component
{
return false;
}
if (self::url_is_readonly($url))
{
return false;
}
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path', $option, ".array2string($value).") url=$url");
switch($option)
{
case STREAM_META_TOUCH:
return touch($url, $value[0]); // atime is not supported
case STREAM_META_ACCESS:
return chmod($url, $value);
case STREAM_META_OWNER_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'u')) === false)
return false;
// fall through
case STREAM_META_OWNER:
return chown($url, $value);
case STREAM_META_GROUP_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'g')) === false)
return false;
// fall through
case STREAM_META_GROUP:
return chgrp($url, $value);
}
return false;
}
/** /**
* This method is called in response to unlink() calls on URL paths associated with the wrapper. * This method is called in response to unlink() calls on URL paths associated with the wrapper.
* *