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 7f919ed0e3
commit 52be4ee14b
5 changed files with 175 additions and 70 deletions

View File

@ -94,6 +94,14 @@ class Vfs
* Name of the lock table
*/
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!
*
@ -618,8 +626,10 @@ class Vfs
if ($options['url'])
{
$lstat = @lstat($path);
$stat = array_slice($lstat,13); // remove numerical indices 0-12
if (($stat = @lstat($path)))
{
$stat = array_slice($stat,13); // remove numerical indices 0-12
}
}
else
{
@ -2160,6 +2170,21 @@ class Vfs
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.
*
@ -2277,7 +2302,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 int $time =null modification time (unix timestamp), default null = current time
@ -2286,11 +2311,11 @@ class Vfs
*/
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!
*
@ -2300,35 +2325,35 @@ class Vfs
*/
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!
*
* @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
*/
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!
*
* @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
*/
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,15 +432,26 @@ 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 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!
* @return boolean true on success, false otherwise
* @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
*/
static function touch($url,$time=null,$atime=null)
function stream_metadata($url, $option, $value)
{
if ($option != STREAM_META_TOUCH)
{
return false; // not implemented / supported
}
$path = Vfs::decodePath(Vfs::parse_url($url, PHP_URL_PATH));
$parent = dirname($path);
@ -450,55 +461,9 @@ class StreamWrapper implements Vfs\StreamWrapperIface
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
return false;
}
return touch($path,$time,$atime);
}
/**
* 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 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;
array_unshift($value, $path);
return call_user_func_array('touch', $value);
}
/**

View File

@ -329,6 +329,29 @@ class StreamWrapper extends LinksParent
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
*/

View File

@ -835,6 +835,49 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
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
*
@ -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 $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
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
* @return boolean
*/
static function chown($url,$owner)
protected function chown($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
* @return boolean
*/
static function chgrp($url,$owner)
protected function chgrp($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
* @return boolean
*/
static function chmod($url,$mode)
protected function chmod($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);
}
/**
* 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.
*