Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/private/Files/Cache/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -1042,7 +1042,7 @@ protected function calculateFolderSizeInner(string $path, $entry = null, bool $i
$id = $entry['fileid'];

$query = $this->getQueryBuilder();
$query->select('size', 'unencrypted_size')
$query->select('size', 'unencrypted_size', 'encrypted')
->from('filecache')
->whereStorageId($this->getNumericStorageId())
->whereParent($id);
Expand All @@ -1062,7 +1062,7 @@ protected function calculateFolderSizeInner(string $path, $entry = null, bool $i
return Util::numericToNumber($row['unencrypted_size']);
}, $rows);
$unencryptedSizes = array_map(function (array $row) {
return Util::numericToNumber(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']);
return Util::numericToNumber($row['encrypted'] ? $row['unencrypted_size'] : $row['size']);
}, $rows);

$sum = array_sum($sizes);
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Cache/CacheEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function __clone() {

#[\Override]
public function getUnencryptedSize(): int {
if ($this->data['encrypted'] && isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
if ($this->data['encrypted'] && isset($this->data['unencrypted_size'])) {
return $this->data['unencrypted_size'];
} else {
return $this->data['size'] ?? 0;
Expand Down
11 changes: 8 additions & 3 deletions lib/private/Files/Cache/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,10 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =
}
}

// we only updated unencrypted_size if it's already set
if (isset($cacheData['unencrypted_size']) && $cacheData['unencrypted_size'] === 0) {
// Skip updating unencrypted_size only when both cached and new values are 0
if (isset($cacheData['unencrypted_size'])
&& $cacheData['unencrypted_size'] === 0
&& isset($data['unencrypted_size']) && $data['unencrypted_size'] === 0) {
unset($data['unencrypted_size']);
}

Expand All @@ -203,7 +205,10 @@ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData =
$data['etag_changed'] = true;
}
} else {
unset($data['unencrypted_size']);
// For new files, only preserve unencrypted_size when the file is encrypted
if (!isset($data['encrypted']) || !$data['encrypted']) {
unset($data['unencrypted_size']);
}
$newData = $data;
$fileId = -1;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/private/Files/FileInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function __construct(
private ?IUser $owner = null,
) {
$this->mount = $mount;
if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] !== 0) {
if (($this->data['encrypted'] ?? false) && isset($this->data['unencrypted_size'])) {
$this->rawSize = $this->data['unencrypted_size'];
} else {
$this->rawSize = $this->data['size'] ?? 0;
Expand Down Expand Up @@ -174,7 +174,7 @@ public function getSize($includeMounts = true) {
if ($includeMounts) {
$this->updateEntryFromSubMounts();

if ($this->isEncrypted() && isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
if ($this->isEncrypted() && isset($this->data['unencrypted_size'])) {
return $this->data['unencrypted_size'];
} else {
return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
Expand Down Expand Up @@ -356,7 +356,7 @@ public function addSubEntry($data, $entryPath) {
if (!$data) {
return;
}
$hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0;
$hasUnencryptedSize = !empty($data['encrypted']) && isset($data['unencrypted_size']);
if ($hasUnencryptedSize) {
$subSize = $data['unencrypted_size'];
} else {
Expand Down
1 change: 1 addition & 0 deletions lib/private/Files/Storage/Wrapper/Encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): in
if ($unencryptedSize < 0
|| ($size > 0 && $unencryptedSize === $size)
|| $unencryptedSize > $size
|| ($unencryptedSize === 0 && $size > $this->util->getHeaderSize())
) {
// check if we already calculate the unencrypted size for the
// given path to avoid recursions
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Stream/Encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Encryption extends Wrapper {
protected string $cache;
protected ?int $size = null;
protected int $position;
protected ?int $unencryptedSize = null;
protected int|float|null $unencryptedSize = null;
protected int $headerSize;
protected int $unencryptedBlockSize;
protected array $header;
Expand Down
32 changes: 32 additions & 0 deletions tests/lib/Files/Cache/CacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ public function testCacheEntryGetters(): void {
$this->assertEquals($entry->getUnencryptedSize(), 100);
}

public function testGetUnencryptedSizeEncryptedZeroByte(): void {
$file1 = 'encrypted_zero';
$this->cache->put($file1, ['size' => 8192, 'mtime' => 50, 'mimetype' => 'application/octet-stream', 'encrypted' => 1, 'unencrypted_size' => 0]);
$entry = $this->cache->get($file1);

// getUnencryptedSize() must return 0 (the true plaintext size), not 8192 (the encrypted on-disk size)
$this->assertEquals(0, $entry->getUnencryptedSize());
$this->assertTrue($entry->isEncrypted());
}

public function testPartial(): void {
$file1 = 'foo';

Expand Down Expand Up @@ -292,6 +302,28 @@ public function testEncryptedFolder(): void {
$this->assertFalse($this->cache->inCache('folder/bar'));
}

public function testCalculateFolderSizeWithEncryptedZeroByte(): void {
$folder = 'enc_folder';
$this->cache->put($folder, ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);

// Child: a zero-byte encrypted file — on-disk size is 8192 (header), but plaintext is 0
$child = $folder . '/empty.enc';
$this->cache->put($child, [
'size' => 8192,
'mtime' => 20,
'mimetype' => 'application/octet-stream',
'encrypted' => 1,
'unencrypted_size' => 0,
]);

$folderSize = $this->cache->calculateFolderSize($folder);

// The folder's unencrypted_size must reflect the true plaintext total (0), not the encrypted on-disk size (8192)
$entry = $this->cache->get($folder);
$this->assertEquals(0, $entry['unencrypted_size'], 'Folder unencrypted_size should be 0 for zero-byte encrypted children');
$this->assertEquals(8192, $entry['size'], 'Folder size should equal encrypted on-disk size');
}

public function testRootFolderSizeForNonHomeStorage(): void {
$dir1 = 'knownsize';
$dir2 = 'unknownsize';
Expand Down
24 changes: 24 additions & 0 deletions tests/lib/Files/FileInfoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@ protected function setUp(): void {
$this->config = $this->getMockBuilder(IConfig::class)->getMock();
}

private function makeFileInfo(array $data): FileInfo {
$storage = new Temporary();
return new FileInfo('', $storage, '', $data, new MountPoint($storage, '/foo/files'));
}

public function testGetSizeEncryptedZeroByte(): void {
$info = $this->makeFileInfo(['encrypted' => true, 'size' => 8192, 'unencrypted_size' => 0]);
// Both paths must report the true plaintext size (0), not the on-disk encrypted size (8192)
$this->assertSame(0, $info->getSize(true));
$this->assertSame(0, $info->getSize(false));
}

public function testGetSizeEncryptedNonZero(): void {
$info = $this->makeFileInfo(['encrypted' => true, 'size' => 16384, 'unencrypted_size' => 5000]);
$this->assertSame(5000, $info->getSize(true));
$this->assertSame(5000, $info->getSize(false));
}

public function testGetSizeNonEncrypted(): void {
$info = $this->makeFileInfo(['encrypted' => false, 'size' => 100]);
$this->assertSame(100, $info->getSize(true));
$this->assertSame(100, $info->getSize(false));
}

public function testIsMountedHomeStorage(): void {
$user = $this->createMock(IUser::class);
$user->method('getUID')
Expand Down
6 changes: 5 additions & 1 deletion tests/lib/Files/Storage/Wrapper/EncryptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,11 @@ public static function dataTestVerifyUnencryptedSize(): array {
[120, 80, false, 80],
[120, 120, false, 80],
[120, -1, false, 80],
[120, -1, true, -1]
[120, -1, true, -1],
// Zero-byte encrypted file: on-disk size equals header only (8192) — should NOT recalculate
[8192, 0, false, 0],
// Encrypted file with content beyond header but unencrypted_size=0 — SHOULD recalculate
[16384, 0, false, 80],
];
}

Expand Down
Loading