diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index 2055de25..6be9174a 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -901,7 +901,7 @@ class Net_SFTP extends Net_SSH2 } else { $temp = $dir . '/' . $shortname; } - $this->_update_stat_cache($temp, (object) array('stat' => $attributes)); + $this->_update_stat_cache($temp, (object) array('lstat' => $attributes)); } // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the // final SSH_FXP_STATUS packet should tell us that, already. @@ -1072,6 +1072,12 @@ class Net_SFTP extends Net_SSH2 $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i=>$dir) { + // if $temp is an object that means one of two things. + // 1. a file was deleted and changed to a directory behind phpseclib's back + // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to + if (is_object($temp)) { + $temp = array(); + } if (!isset($temp[$dir])) { $temp[$dir] = array(); } @@ -1161,7 +1167,7 @@ class Net_SFTP extends Net_SSH2 if ($this->use_stat_cache) { $result = $this->_query_stat_cache($filename); - if (is_array($result) && isset($result['.'])) { + if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { return $result['.']->stat; } if (is_object($result) && isset($result->stat)) { diff --git a/tests/Functional/Net/SFTPUserStoryTest.php b/tests/Functional/Net/SFTPUserStoryTest.php index e4fdb389..0b93bd37 100644 --- a/tests/Functional/Net/SFTPUserStoryTest.php +++ b/tests/Functional/Net/SFTPUserStoryTest.php @@ -594,5 +594,52 @@ class Functional_Net_SFTPUserStoryTest extends PhpseclibFunctionalTestCase $sftp->stat(self::$scratchDir), 'Failed asserting that stat on a deleted directory returns false' ); + + return $sftp; + } + + /** + * @depends testDeleteEmptyDir + * @group github735 + */ + public function testStatVsLstat($sftp) + { + $this->assertTrue($sftp->mkdir(self::$scratchDir)); + $this->assertTrue($sftp->chdir(self::$scratchDir)); + $this->assertTrue($sftp->put('text.txt', 'zzzzz')); + $this->assertTrue($sftp->symlink('text.txt', 'link.txt')); + $this->assertTrue($sftp->mkdir('subdir')); + $this->assertTrue($sftp->symlink('subdir', 'linkdir')); + + $sftp->clearStatCache(); + + // pre-populate the stat cache + $sftp->nlist(); + + $stat = $sftp->stat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_REGULAR); + $stat = $sftp->lstat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK); + + $stat = $sftp->stat('linkdir'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_DIRECTORY); + $stat = $sftp->lstat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK); + + $sftp->disableStatCache(); + + $sftp->nlist(); + + $stat = $sftp->stat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_REGULAR); + $stat = $sftp->lstat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK); + + $stat = $sftp->stat('linkdir'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_DIRECTORY); + $stat = $sftp->lstat('link.txt'); + $this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK); + + $sftp->enableStatCache(); } }