From 4d6d716a925ada639a7b7626508e755a832e0c7f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 29 Aug 2023 16:33:06 +0200 Subject: [PATCH] don't believe sftp when it tells us the mtime is less than we know it is Signed-off-by: Robin Appelman --- apps/files_external/lib/Lib/Storage/SFTP.php | 25 ++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/files_external/lib/Lib/Storage/SFTP.php b/apps/files_external/lib/Lib/Storage/SFTP.php index 7a4a4a1194460..b8593f30c102f 100644 --- a/apps/files_external/lib/Lib/Storage/SFTP.php +++ b/apps/files_external/lib/Lib/Storage/SFTP.php @@ -36,16 +36,18 @@ */ namespace OCA\Files_External\Lib\Storage; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use Icewind\Streams\RetryWrapper; use OCP\IRequest; +use OCP\Cache\CappedMemoryCache; use phpseclib\Net\SFTP\Stream; /** * Uses phpseclib's Net\SFTP class and the Net\SFTP\Stream stream wrapper to * provide access to SFTP servers. */ -class SFTP extends \OC\Files\Storage\Common { +class SFTP extends \OC\Files\Storage\Common { private $host; private $user; private $root; @@ -57,6 +59,7 @@ class SFTP extends \OC\Files\Storage\Common { * @var \phpseclib\Net\SFTP */ protected $client; + private CappedMemoryCache $knownMTimes; /** * @param string $host protocol://server:port @@ -112,6 +115,8 @@ public function __construct($params) { $this->root = '/' . ltrim($this->root, '/'); $this->root = rtrim($this->root, '/') . '/'; + + $this->knownMTimes = new CappedMemoryCache(); } /** @@ -369,6 +374,7 @@ public function unlink($path) { * {@inheritdoc} */ public function fopen($path, $mode) { + $path = $this->cleanPath($path); try { $absPath = $this->absPath($path); switch ($mode) { @@ -385,7 +391,13 @@ public function fopen($path, $mode) { case 'wb': SFTPWriteStream::register(); $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]); - return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context); + $fh = fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context); + if ($fh) { + $fh = CallbackWrapper::wrap($fh, null, null, function() use ($path) { + $this->knownMTimes->set($path, time()); + }); + } + return $fh; case 'a': case 'ab': case 'r+': @@ -414,14 +426,13 @@ public function touch($path, $mtime = null) { return false; } if (!$this->file_exists($path)) { - $this->getConnection()->put($this->absPath($path), ''); + return $this->getConnection()->put($this->absPath($path), ''); } else { return false; } } catch (\Exception $e) { return false; } - return true; } /** @@ -455,11 +466,17 @@ public function rename($source, $target) { */ public function stat($path) { try { + $path = $this->cleanPath($path); $stat = $this->getConnection()->stat($this->absPath($path)); $mtime = $stat ? $stat['mtime'] : -1; $size = $stat ? $stat['size'] : 0; + // the mtime can't be less than when we last touched it + if ($knownMTime = $this->knownMTimes->get($path)) { + $mtime = max($mtime, $knownMTime); + } + return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1]; } catch (\Exception $e) { return false;