Skip to content

Commit c6c07c0

Browse files
committed
don't believe sftp when it tells us the mtime is less than we know it is
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent 5ee4c9e commit c6c07c0

File tree

1 file changed

+22
-3
lines changed
  • apps/files_external/lib/Lib/Storage

1 file changed

+22
-3
lines changed

apps/files_external/lib/Lib/Storage/SFTP.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
namespace OCA\Files_External\Lib\Storage;
88

99
use Icewind\Streams\CountWrapper;
10+
use Icewind\Streams\CallbackWrapper;
1011
use Icewind\Streams\IteratorDirectory;
1112
use Icewind\Streams\RetryWrapper;
1213
use OC\Files\Storage\Common;
1314
use OC\Files\View;
1415
use OCP\Constants;
1516
use OCP\Files\FileInfo;
1617
use OCP\Files\IMimeTypeDetector;
18+
use OCP\Cache\CappedMemoryCache;
1719
use phpseclib\Net\SFTP\Stream;
1820

1921
/**
@@ -32,6 +34,8 @@ class SFTP extends Common {
3234
* @var \phpseclib\Net\SFTP
3335
*/
3436
protected $client;
37+
private CappedMemoryCache $knownMTimes;
38+
3539
private IMimeTypeDetector $mimeTypeDetector;
3640

3741
public const COPY_CHUNK_SIZE = 8 * 1024 * 1024;
@@ -87,6 +91,9 @@ public function __construct(array $parameters) {
8791

8892
$this->root = '/' . ltrim($this->root, '/');
8993
$this->root = rtrim($this->root, '/') . '/';
94+
95+
$this->knownMTimes = new CappedMemoryCache();
96+
9097
$this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class);
9198
}
9299

@@ -297,6 +304,7 @@ public function unlink(string $path): bool {
297304
}
298305

299306
public function fopen(string $path, string $mode) {
307+
$path = $this->cleanPath($path);
300308
try {
301309
$absPath = $this->absPath($path);
302310
$connection = $this->getConnection();
@@ -317,7 +325,13 @@ public function fopen(string $path, string $mode) {
317325
// the SFTPWriteStream doesn't go through the "normal" methods so it doesn't clear the stat cache.
318326
$connection->_remove_from_stat_cache($absPath);
319327
$context = stream_context_create(['sftp' => ['session' => $connection]]);
320-
return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
328+
$fh = fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
329+
if ($fh) {
330+
$fh = CallbackWrapper::wrap($fh, null, null, function() use ($path) {
331+
$this->knownMTimes->set($path, time());
332+
});
333+
}
334+
return $fh;
321335
case 'a':
322336
case 'ab':
323337
case 'r+':
@@ -343,14 +357,13 @@ public function touch(string $path, ?int $mtime = null): bool {
343357
return false;
344358
}
345359
if (!$this->file_exists($path)) {
346-
$this->getConnection()->put($this->absPath($path), '');
360+
return $this->getConnection()->put($this->absPath($path), '');
347361
} else {
348362
return false;
349363
}
350364
} catch (\Exception $e) {
351365
return false;
352366
}
353-
return true;
354367
}
355368

356369
/**
@@ -379,11 +392,17 @@ public function rename(string $source, string $target): bool {
379392
*/
380393
public function stat(string $path): array|false {
381394
try {
395+
$path = $this->cleanPath($path);
382396
$stat = $this->getConnection()->stat($this->absPath($path));
383397

384398
$mtime = isset($stat['mtime']) ? (int)$stat['mtime'] : -1;
385399
$size = isset($stat['size']) ? (int)$stat['size'] : 0;
386400

401+
// the mtime can't be less than when we last touched it
402+
if ($knownMTime = $this->knownMTimes->get($path)) {
403+
$mtime = max($mtime, $knownMTime);
404+
}
405+
387406
return [
388407
'mtime' => $mtime,
389408
'size' => $size,

0 commit comments

Comments
 (0)