Skip to content

Commit

Permalink
Merge pull request #36857 from nextcloud/scan-repair-unencrypted-size
Browse files Browse the repository at this point in the history
fix unencrypted_size for folders when scanning the filesystem with encryption enabled
  • Loading branch information
icewind1991 authored Apr 24, 2023
2 parents e8a48b5 + 5267a74 commit 4953296
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 40 deletions.
31 changes: 27 additions & 4 deletions lib/private/Files/Cache/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -899,10 +899,23 @@ public function getIncompleteChildrenCount($fileId) {
* calculate the size of a folder and set it in the cache
*
* @param string $path
* @param array $entry (optional) meta data of the folder
* @param array|null|ICacheEntry $entry (optional) meta data of the folder
* @return int
*/
public function calculateFolderSize($path, $entry = null) {
return $this->calculateFolderSizeInner($path, $entry);
}


/**
* inner function because we can't add new params to the public function without breaking any child classes
*
* @param string $path
* @param array|null|ICacheEntry $entry (optional) meta data of the folder
* @param bool $ignoreUnknown don't mark the folder size as unknown if any of it's children are unknown
* @return int
*/
protected function calculateFolderSizeInner(string $path, $entry = null, bool $ignoreUnknown = false) {
$totalSize = 0;
if (is_null($entry) || !isset($entry['fileid'])) {
$entry = $this->get($path);
Expand All @@ -914,6 +927,9 @@ public function calculateFolderSize($path, $entry = null) {
$query->select('size', 'unencrypted_size')
->from('filecache')
->whereParent($id);
if ($ignoreUnknown) {
$query->andWhere($query->expr()->gte('size', $query->createNamedParameter(0)));
}

$result = $query->execute();
$rows = $result->fetchAll();
Expand Down Expand Up @@ -954,9 +970,16 @@ public function calculateFolderSize($path, $entry = null) {
$unencryptedTotal = 0;
$unencryptedMax = 0;
}
if ($entry['size'] !== $totalSize) {
// only set unencrypted size for a folder if any child entries have it set, or the folder is empty
if ($unencryptedMax > 0 || $totalSize === 0) {

// only set unencrypted size for a folder if any child entries have it set, or the folder is empty
$shouldWriteUnEncryptedSize = $unencryptedMax > 0 || $totalSize === 0 || $entry['unencrypted_size'] > 0;
if ($entry['size'] !== $totalSize || ($entry['unencrypted_size'] !== $unencryptedTotal && $shouldWriteUnEncryptedSize)) {
if ($shouldWriteUnEncryptedSize) {
// if all children have an unencrypted size of 0, just set the folder unencrypted size to 0 instead of summing the sizes
if ($unencryptedMax === 0) {
$unencryptedTotal = 0;
}

$this->update($id, [
'size' => $totalSize,
'unencrypted_size' => $unencryptedTotal,
Expand Down
34 changes: 3 additions & 31 deletions lib/private/Files/Cache/HomeCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class HomeCache extends Cache {
* get the size of a folder and set it in the cache
*
* @param string $path
* @param array $entry (optional) meta data of the folder
* @param array|null|ICacheEntry $entry (optional) meta data of the folder
* @return int
*/
public function calculateFolderSize($path, $entry = null) {
Expand All @@ -44,37 +44,9 @@ public function calculateFolderSize($path, $entry = null) {
} elseif ($path === '' or $path === '/') {
// since the size of / isn't used (the size of /files is used instead) there is no use in calculating it
return 0;
} else {
return $this->calculateFolderSizeInner($path, $entry, true);
}

$totalSize = 0;
if (is_null($entry)) {
$entry = $this->get($path);
}
if ($entry && $entry['mimetype'] === 'httpd/unix-directory') {
$id = $entry['fileid'];

$query = $this->connection->getQueryBuilder();
$query->selectAlias($query->func()->sum('size'), 'f1')
->from('filecache')
->where($query->expr()->eq('parent', $query->createNamedParameter($id)))
->andWhere($query->expr()->eq('storage', $query->createNamedParameter($this->getNumericStorageId())))
->andWhere($query->expr()->gte('size', $query->createNamedParameter(0)));

$result = $query->execute();
$row = $result->fetch();
$result->closeCursor();

if ($row) {
[$sum] = array_values($row);
$totalSize = 0 + $sum;
$entry['size'] += 0;
if ($entry['size'] !== $totalSize) {
$this->update($id, ['size' => $totalSize]);
}
}
$result->closeCursor();
}
return $totalSize;
}

/**
Expand Down
20 changes: 18 additions & 2 deletions lib/private/Files/Cache/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
namespace OC\Files\Cache;

use Doctrine\DBAL\Exception;
use OC\Files\Storage\Wrapper\Encryption;
use OCP\Files\Cache\IScanner;
use OCP\Files\ForbiddenException;
use OCP\Files\NotFoundException;
Expand Down Expand Up @@ -208,9 +209,17 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =
$data['etag'] = $etag;
}
}

// we only updated unencrypted_size if it's already set
if ($cacheData['unencrypted_size'] === 0) {
unset($data['unencrypted_size']);
}

// Only update metadata that has changed
$newData = array_diff_assoc($data, $cacheData->getData());
} else {
// we only updated unencrypted_size if it's already set
unset($data['unencrypted_size']);
$newData = $data;
$fileId = -1;
}
Expand Down Expand Up @@ -396,8 +405,15 @@ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse
}
}
$oldSize = $data['size'] ?? null;
if ($this->cacheActive && $oldSize !== $size) {
$this->cache->update($folderId, ['size' => $size]);

// for encrypted storages, we trigger a regular folder size calculation instead of using the calculated size
// to make sure we also updated the unencrypted-size where applicable
if ($this->storage->instanceOfStorage(Encryption::class)) {
$this->cache->calculateFolderSize($path);
} else {
if ($this->cacheActive && $oldSize !== $size) {
$this->cache->update($folderId, ['size' => $size]);
}
}
$this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', [$path, $this->storageId]);
return $size;
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Cache/Wrapper/CacheJail.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public function correctFolderSize($path, $data = null, $isBackgroundScan = false
* get the size of a folder and set it in the cache
*
* @param string $path
* @param array $entry (optional) meta data of the folder
* @param array|null|ICacheEntry $entry (optional) meta data of the folder
* @return int
*/
public function calculateFolderSize($path, $entry = null) {
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Cache/Wrapper/CacheWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public function correctFolderSize($path, $data = null, $isBackgroundScan = false
* get the size of a folder and set it in the cache
*
* @param string $path
* @param array $entry (optional) meta data of the folder
* @param array|null|ICacheEntry $entry (optional) meta data of the folder
* @return int
*/
public function calculateFolderSize($path, $entry = null) {
Expand Down
5 changes: 4 additions & 1 deletion lib/private/Files/Storage/Wrapper/Encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,12 @@ private function modifyMetaData(string $path, array $data): array {
if (isset($this->unencryptedSize[$fullPath])) {
$data['encrypted'] = true;
$data['size'] = $this->unencryptedSize[$fullPath];
$data['unencrypted_size'] = $data['size'];
} else {
if (isset($info['fileid']) && $info['encrypted']) {
$data['size'] = $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
$data['encrypted'] = true;
$data['unencrypted_size'] = $data['size'];
}
}

Expand Down Expand Up @@ -496,7 +498,8 @@ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): in
$result = $unencryptedSize;

if ($unencryptedSize < 0 ||
($size > 0 && $unencryptedSize === $size)
($size > 0 && $unencryptedSize === $size) ||
$unencryptedSize > $size
) {
// check if we already calculate the unencrypted size for the
// given path to avoid recursions
Expand Down

0 comments on commit 4953296

Please sign in to comment.