@@ -203,7 +203,9 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =
203203 $ fileId = $ cacheData ['fileid ' ];
204204 $ data ['fileid ' ] = $ fileId ;
205205 // only reuse data if the file hasn't explicitly changed
206- if (isset ($ data ['storage_mtime ' ]) && isset ($ cacheData ['storage_mtime ' ]) && $ data ['storage_mtime ' ] === $ cacheData ['storage_mtime ' ]) {
206+ $ mtimeUnchanged = isset ($ data ['storage_mtime ' ]) && isset ($ cacheData ['storage_mtime ' ]) && $ data ['storage_mtime ' ] === $ cacheData ['storage_mtime ' ];
207+ // if the folder is marked as unscanned, never reuse etags
208+ if ($ mtimeUnchanged && $ cacheData ['size ' ] !== -1 ) {
207209 $ data ['mtime ' ] = $ cacheData ['mtime ' ];
208210 if (($ reuseExisting & self ::REUSE_SIZE ) && ($ data ['size ' ] === -1 )) {
209211 $ data ['size ' ] = $ cacheData ['size ' ];
@@ -220,6 +222,11 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =
220222
221223 // Only update metadata that has changed
222224 $ newData = array_diff_assoc ($ data , $ cacheData ->getData ());
225+
226+ // make it known to the caller that etag has been changed and needs propagation
227+ if (isset ($ newData ['etag ' ])) {
228+ $ data ['etag_changed ' ] = true ;
229+ }
223230 } else {
224231 // we only updated unencrypted_size if it's already set
225232 unset($ data ['unencrypted_size ' ]);
@@ -388,16 +395,20 @@ protected function getExistingChildren($folderId) {
388395 * @param int|float $oldSize the size of the folder before (re)scanning the children
389396 * @return int|float the size of the scanned folder or -1 if the size is unknown at this stage
390397 */
391- protected function scanChildren (string $ path , $ recursive , int $ reuse , int $ folderId , bool $ lock , int |float $ oldSize ) {
398+ protected function scanChildren (string $ path , $ recursive , int $ reuse , int $ folderId , bool $ lock , int |float $ oldSize, & $ etagChanged = false ) {
392399 if ($ reuse === -1 ) {
393400 $ reuse = ($ recursive === self ::SCAN_SHALLOW ) ? self ::REUSE_ETAG | self ::REUSE_SIZE : self ::REUSE_ETAG ;
394401 }
395402 $ this ->emit ('\OC\Files\Cache\Scanner ' , 'scanFolder ' , [$ path , $ this ->storageId ]);
396403 $ size = 0 ;
397- $ childQueue = $ this ->handleChildren ($ path , $ recursive , $ reuse , $ folderId , $ lock , $ size );
404+ $ childQueue = $ this ->handleChildren ($ path , $ recursive , $ reuse , $ folderId , $ lock , $ size, $ etagChanged );
398405
399406 foreach ($ childQueue as $ child => [$ childId , $ childSize ]) {
400- $ childSize = $ this ->scanChildren ($ child , $ recursive , $ reuse , $ childId , $ lock , $ childSize );
407+ // "etag changed" propagates up, but not down, so we pass `false` to the children even if we already know that the etag of the current folder changed
408+ $ childEtagChanged = false ;
409+ $ childSize = $ this ->scanChildren ($ child , $ recursive , $ reuse , $ childId , $ lock , $ childSize , $ childEtagChanged );
410+ $ etagChanged |= $ childEtagChanged ;
411+
401412 if ($ childSize === -1 ) {
402413 $ size = -1 ;
403414 } elseif ($ size !== -1 ) {
@@ -410,8 +421,17 @@ protected function scanChildren(string $path, $recursive, int $reuse, int $folde
410421 if ($ this ->storage ->instanceOfStorage (Encryption::class)) {
411422 $ this ->cache ->calculateFolderSize ($ path );
412423 } else {
413- if ($ this ->cacheActive && $ oldSize !== $ size ) {
414- $ this ->cache ->update ($ folderId , ['size ' => $ size ]);
424+ if ($ this ->cacheActive ) {
425+ $ updatedData = [];
426+ if ($ oldSize !== $ size ) {
427+ $ updatedData ['size ' ] = $ size ;
428+ }
429+ if ($ etagChanged ) {
430+ $ updatedData ['etag ' ] = uniqid ();
431+ }
432+ if ($ updatedData ) {
433+ $ this ->cache ->update ($ folderId , $ updatedData );
434+ }
415435 }
416436 }
417437 $ this ->emit ('\OC\Files\Cache\Scanner ' , 'postScanFolder ' , [$ path , $ this ->storageId ]);
@@ -421,7 +441,7 @@ protected function scanChildren(string $path, $recursive, int $reuse, int $folde
421441 /**
422442 * @param bool|IScanner::SCAN_RECURSIVE_INCOMPLETE $recursive
423443 */
424- private function handleChildren (string $ path , $ recursive , int $ reuse , int $ folderId , bool $ lock , int |float &$ size ): array {
444+ private function handleChildren (string $ path , $ recursive , int $ reuse , int $ folderId , bool $ lock , int |float &$ size, bool & $ etagChanged ): array {
425445 // we put this in it's own function so it cleans up the memory before we start recursing
426446 $ existingChildren = $ this ->getExistingChildren ($ folderId );
427447 $ newChildren = iterator_to_array ($ this ->storage ->getDirectoryContent ($ path ));
@@ -469,6 +489,10 @@ private function handleChildren(string $path, $recursive, int $reuse, int $folde
469489 } elseif ($ size !== -1 ) {
470490 $ size += $ data ['size ' ];
471491 }
492+
493+ if (isset ($ data ['etag_changed ' ]) && $ data ['etag_changed ' ]) {
494+ $ etagChanged = true ;
495+ }
472496 }
473497 } catch (Exception $ ex ) {
474498 // might happen if inserting duplicate while a scanning
0 commit comments