From a7fa4cc4ff4a21956b4bce2cc3ea5fd255ae8fe9 Mon Sep 17 00:00:00 2001 From: terrafrost Date: Mon, 26 May 2014 15:39:30 -0500 Subject: [PATCH] SFTP: add more stat functions also don't use stat cache for SFTP/Stream.php --- phpseclib/Net/SFTP.php | 206 +++++++++++++++++++++++++--------- phpseclib/Net/SFTP/Stream.php | 1 + 2 files changed, 157 insertions(+), 50 deletions(-) diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 103a7cd5..6bb5b066 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -538,7 +538,7 @@ class Net_SFTP extends Net_SSH2 } /** - * Disable the cache + * Disable the stat cache * * @access public */ @@ -548,7 +548,7 @@ class Net_SFTP extends Net_SSH2 } /** - * Enable the cache + * Enable the stat cache * * @access public */ @@ -557,6 +557,16 @@ class Net_SFTP extends Net_SSH2 $this->use_stat_cache = true; } + /** + * Clear the stat cache + * + * @access public + */ + function clearStatCache() + { + $this->stat_cache = array(); + } + /** * Returns the current directory name * @@ -934,7 +944,7 @@ class Net_SFTP extends Net_SSH2 * Save files / directories to cache * * @param String $path - * @param optional Boolean $file + * @param Mixed $value * @access private */ function _update_stat_cache($path, $value) @@ -942,7 +952,7 @@ class Net_SFTP extends Net_SSH2 // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - $temp = &$this->cache; + $temp = &$this->stat_cache; foreach ($dirs as $dir) { if (!isset($temp[$dir])) { $temp[$dir] = array(); @@ -958,14 +968,14 @@ class Net_SFTP extends Net_SSH2 * Remove files / directories from cache * * @param String $path - * @param optional Boolean $file + * @return Boolean * @access private */ function _remove_from_stat_cache($path) { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - $temp = &$this->cache; + $temp = &$this->stat_cache; foreach ($dirs as $dir) { if ($dir == end($dirs)) { unset($temp[$dir]); @@ -991,10 +1001,10 @@ class Net_SFTP extends Net_SSH2 { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - $temp = &$this->cache; + $temp = &$this->stat_cache; foreach ($dirs as $dir) { if (!isset($temp[$dir])) { - return false; + return NULL; } $temp = &$temp[$dir]; } @@ -1033,10 +1043,13 @@ class Net_SFTP extends Net_SSH2 $stat = $this->_stat($filename, NET_SFTP_STAT); if ($stat === false) { - $this->_update_stat_cache($filename, 0); + $this->_remove_from_stat_cache($filename); return false; } if (isset($stat['type'])) { + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } $this->_update_stat_cache($filename, (object) $stat); return $stat; } @@ -1047,6 +1060,9 @@ class Net_SFTP extends Net_SSH2 NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } $this->_update_stat_cache($filename, (object) $stat); return $stat; @@ -1084,9 +1100,13 @@ class Net_SFTP extends Net_SSH2 $lstat = $this->_stat($filename, NET_SFTP_LSTAT); if ($lstat === false) { + $this->_remove_from_stat_cache($filename); return false; } if (isset($lstat['type'])) { + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } $this->_update_stat_cache($filename, (object) $lstat); return $lstat; } @@ -1105,6 +1125,9 @@ class Net_SFTP extends Net_SSH2 NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } $this->_update_stat_cache($filename, (object) $lstat); return $lstat; @@ -1530,7 +1553,7 @@ class Net_SFTP extends Net_SSH2 // the following will do a soft delete, which would be useful if you deleted a file // and then tried to do a stat on the deleted file. the above, in contrast, does // a hard delete - //$this->_update_stat_cache($dir, 0); + //$this->_update_stat_cache($dir, false); return true; } @@ -1586,6 +1609,8 @@ class Net_SFTP extends Net_SSH2 return false; } + $this->_remove_from_stat_cache($remote_file); + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." // in practice, it doesn't seem to do that. @@ -1991,9 +2016,9 @@ class Net_SFTP extends Net_SSH2 $result = $this->_query_stat_cache($path); - if ($result !== false) { + if (isset($result)) { // return true if $result is an array or if it's int(1) - return $result !== 0; + return $result !== false; } } @@ -2009,19 +2034,11 @@ class Net_SFTP extends Net_SSH2 */ function is_dir($path) { - if ($this->use_stat_cache) { - $path = $this->_realpath($path); - - $result = $this->_query_stat_cache($path); - - if (is_object($result) && isset($result->type)) { - return $result->type === NET_SFTP_TYPE_DIRECTORY; - } + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; } - - $result = $this->stat($path); - - return $result['type'] === NET_SFTP_TYPE_DIRECTORY; + return $result === NET_SFTP_TYPE_DIRECTORY; } /** @@ -2033,19 +2050,11 @@ class Net_SFTP extends Net_SSH2 */ function is_file($path) { - if ($this->use_stat_cache) { - $path = $this->_realpath($path); - - $result = $this->_query_stat_cache($path); - - if (is_object($result) && isset($result->type)) { - return $result->type === NET_SFTP_TYPE_REGULAR; - } + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; } - - $result = $this->stat($path); - - return $result['type'] === NET_SFTP_TYPE_REGULAR; + return $result === NET_SFTP_TYPE_REGULAR; } /** @@ -2057,43 +2066,139 @@ class Net_SFTP extends Net_SSH2 */ function is_link($path) { - if ($this->use_stat_cache) { - $path = $this->_realpath($path); - - $result = $this->_query_stat_cache($path); - - if (is_object($result) && isset($result->type)) { - return $result->type === NET_SFTP_TYPE_SYMLINK; - } + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; } + return $result === NET_SFTP_TYPE_SYMLINK; + } - $result = $this->stat($path); + /** + * Gets last access time of file + * + * @param String $path + * @return Mixed + * @access public + */ + function fileatime($path) + { + return $this->_get_stat_cache_prop($path, 'atime'); + } - return $result['type'] === NET_SFTP_TYPE_SYMLINK; + /** + * Gets file modification time + * + * @param String $path + * @return Mixed + * @access public + */ + function filemtime($path) + { + return $this->_get_stat_cache_prop($path, 'mtime'); + } + + /** + * Gets file permissions + * + * @param String $path + * @return Mixed + * @access public + */ + function fileperms($path) + { + return $this->_get_stat_cache_prop($path, 'permissions'); + } + + /** + * Gets file owner + * + * @param String $path + * @return Mixed + * @access public + */ + function fileowner($path) + { + return $this->_get_stat_cache_prop($path, 'uid'); + } + + /** + * Gets file group + * + * @param String $path + * @return Mixed + * @access public + */ + function filegroup($path) + { + return $this->_get_stat_cache_prop($path, 'gid'); } /** * Gets file size * * @param String $path - * @return Boolean + * @return Mixed * @access public */ function filesize($path) + { + return $this->_get_stat_cache_prop($path, 'size'); + } + + /** + * Gets file type + * + * @param String $path + * @return Mixed + * @access public + */ + function filetype($path) + { + $type = $this->_get_stat_cache_prop($path, 'type'); + if ($type === false) { + return false; + } + + switch ($type) { + case NET_SFTP_BLOCK_DEVICE: return 'block'; + case NET_SFTP_TYPE_CHAR_DEVICE: return 'char'; + case NET_SFTP_TYPE_DIRECTORY: return 'dir'; + case NET_SFTP_TYPE_FIFO: return 'fifo'; + case NET_SFTP_TYPE_REGULAR: return 'file'; + case NET_SFTP_TYPE_SYMLINK: return 'link'; + default: return false; + } + } + + /** + * Return a stat properity + * + * Uses cache if appropriate. + * + * @param String $path + * @param String $prop + * @return Mixed + * @access private + */ + function _get_stat_cache_prop($path, $prop) { if ($this->use_stat_cache) { $path = $this->_realpath($path); $result = $this->_query_stat_cache($path); - if (is_object($result) && isset($result->size)) { - return $result->size; + if (is_object($result) && isset($result->$prop)) { + return $result->$prop; } } $result = $this->stat($path); - return $result['size']; + if ($result === false || !isset($result[$prop])) { + return false; + } + + return $result[$prop]; } /** @@ -2139,6 +2244,7 @@ class Net_SFTP extends Net_SSH2 // atime and mtime attributes //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname)); $this->_remove_from_stat_cache($oldname); + $this->_remove_from_stat_cache($newname); return true; } diff --git a/phpseclib/Net/SFTP/Stream.php b/phpseclib/Net/SFTP/Stream.php index a85e0a56..7b197ee7 100644 --- a/phpseclib/Net/SFTP/Stream.php +++ b/phpseclib/Net/SFTP/Stream.php @@ -209,6 +209,7 @@ class Net_SFTP_Stream $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; } else { $this->sftp = new Net_SFTP($host, $port); + $this->disableStatCache(); if (isset($this->notification) && is_callable($this->notification)) { /* if !is_callable($this->notification) we could do this: