Skip to content

Commit 7a81700

Browse files
Antreesybackportbot[bot]
authored andcommitted
fix(AmazonS3#getDirectoryMetaData): map root path to filecache key
normalizePath('') returns '.' for S3 object keys, but the filecache stores the storage root under the key ''. getDirectoryMetaData() was calling getCache()->get('.') which looks up by md5('.'), never matching the root entry stored at md5(''). The cache miss caused getDirectoryMetaData to return synthetic data with time() as mtime/storage_mtime and uniqid() as etag on every call. The scanner then saw a storage_mtime mismatch and wrote the fabricated timestamps back to the cache on every occ files:scan run, regardless of whether any S3 content had changed. Introduce getCachePath() to translate the normalized root path '.' back to '' before any filecache lookup. Assisted-by: ClaudeCode:claude-sonnet-4-6 Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
1 parent 17e1f03 commit 7a81700

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ private function isRoot(string $path): bool {
7070
return $path === '.';
7171
}
7272

73+
private function getCachePath(string $path): string {
74+
// normalizePath() converts '' to '.' for S3 object keys, but filecache stores the root as ''
75+
return $this->isRoot($path) ? '' : $path;
76+
}
77+
7378
private function cleanKey(string $path): string {
7479
if ($this->isRoot($path)) {
7580
return '/';
@@ -667,7 +672,7 @@ private function getDirectoryMetaData(string $path): ?array {
667672
if ($this->versioningEnabled() && !$this->doesDirectoryExist($path)) {
668673
return null;
669674
}
670-
$cacheEntry = $this->getCache()->get($path);
675+
$cacheEntry = $this->getCache()->get($this->getCachePath($path));
671676
if ($cacheEntry instanceof CacheEntry) {
672677
return $cacheEntry->getData();
673678
} else {

apps/files_external/tests/Storage/Amazons3Test.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
namespace OCA\Files_External\Tests\Storage;
1010

11+
use OC\Files\Cache\Scanner;
1112
use OCA\Files_External\Lib\Storage\AmazonS3;
1213

1314
/**
@@ -38,7 +39,29 @@ protected function tearDown(): void {
3839
parent::tearDown();
3940
}
4041

41-
public function testStat(): void {
42-
$this->markTestSkipped('S3 doesn\'t update the parents folder mtime');
42+
/**
43+
* Regression test for the '.' vs '' root path mismatch in getDirectoryMetaData.
44+
*
45+
* normalizePath('') returns '.' for S3 object keys, but the filecache stores the
46+
* storage root under the key ''. Before the fix, getCache()->get('.') returned false,
47+
* causing getDirectoryMetaData to return a fabricated time() on every call, which
48+
* made getCacheEntry always see a changed storage_mtime and fire propagateChange.
49+
*/
50+
public function testStatRootPreservesStorageMtimeFromCache(): void {
51+
$this->instance->getScanner()->scan('', Scanner::SCAN_SHALLOW);
52+
53+
$cachedRoot = $this->instance->getCache()->get('');
54+
$this->assertNotFalse($cachedRoot, 'Root entry must exist in cache after scan');
55+
56+
$cachedStorageMtime = $cachedRoot['storage_mtime'];
57+
58+
$stat = $this->instance->stat('');
59+
$this->assertNotFalse($stat, 'stat(\'\') must return data');
60+
$this->assertEquals(
61+
$cachedStorageMtime,
62+
$stat['storage_mtime'],
63+
'stat(\'\') must return storage_mtime from the cache entry, not a fabricated time()'
64+
);
4365
}
66+
4467
}

0 commit comments

Comments
 (0)