aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-08-15 15:28:30 +0200
committerGitHub <noreply@github.com>2022-08-15 15:28:30 +0200
commit51b9847fad73a1ca67dbf504358d90bd8f9e71d8 (patch)
treeb104cf1c540dd1dd195ca5fd30c42b888012cbab /lib/private/Files
parent6d6662ec68c8e15c4c6bfdf1c694794badd412d7 (diff)
parentcb97e8f15c75cc46e345ebfc79dcad1b9c48bd01 (diff)
downloadnextcloud-server-51b9847fad73a1ca67dbf504358d90bd8f9e71d8.tar.gz
nextcloud-server-51b9847fad73a1ca67dbf504358d90bd8f9e71d8.zip
Merge branch 'master' into display-name-cache-public
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'lib/private/Files')
-rw-r--r--lib/private/Files/AppData/AppData.php2
-rw-r--r--lib/private/Files/Cache/Cache.php43
-rw-r--r--lib/private/Files/Cache/CacheEntry.php8
-rw-r--r--lib/private/Files/Cache/CacheQueryBuilder.php14
-rw-r--r--lib/private/Files/Cache/Propagator.php17
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php23
-rw-r--r--lib/private/Files/Cache/SearchBuilder.php8
-rw-r--r--lib/private/Files/Cache/Storage.php4
-rw-r--r--lib/private/Files/Cache/StorageGlobal.php2
-rw-r--r--lib/private/Files/Cache/Updater.php4
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheJail.php2
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheWrapper.php2
-rw-r--r--lib/private/Files/Config/UserMountCache.php4
-rw-r--r--lib/private/Files/FileInfo.php29
-rw-r--r--lib/private/Files/Filesystem.php2
-rw-r--r--lib/private/Files/Mount/Manager.php2
-rw-r--r--lib/private/Files/Mount/MoveableMount.php1
-rw-r--r--lib/private/Files/Node/File.php1
-rw-r--r--lib/private/Files/Node/Folder.php3
-rw-r--r--lib/private/Files/Node/Root.php4
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php2
-rw-r--r--lib/private/Files/ObjectStore/S3ConnectionTrait.php31
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php10
-rw-r--r--lib/private/Files/SetupManager.php43
-rw-r--r--lib/private/Files/SimpleFS/NewSimpleFile.php38
-rw-r--r--lib/private/Files/SimpleFS/SimpleFile.php44
-rw-r--r--lib/private/Files/SimpleFS/SimpleFolder.php13
-rw-r--r--lib/private/Files/Storage/Common.php1
-rw-r--r--lib/private/Files/Storage/Local.php14
-rw-r--r--lib/private/Files/Storage/Wrapper/Encoding.php2
-rw-r--r--lib/private/Files/Storage/Wrapper/Encryption.php98
-rw-r--r--lib/private/Files/Stream/SeekableHttpStream.php13
-rw-r--r--lib/private/Files/View.php4
33 files changed, 303 insertions, 185 deletions
diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php
index 471de799c2f..237fcb42e03 100644
--- a/lib/private/Files/AppData/AppData.php
+++ b/lib/private/Files/AppData/AppData.php
@@ -26,7 +26,7 @@ declare(strict_types=1);
*/
namespace OC\Files\AppData;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\SimpleFS\SimpleFolder;
use OC\SystemConfig;
use OCP\Files\Folder;
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 949079dfa22..f23635aa01b 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -37,6 +37,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Cache;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
@@ -63,7 +64,7 @@ use Psr\Log\LoggerInterface;
/**
* Metadata cache for a storage
*
- * The cache stores the metadata for all files and folders in a storage and is kept up to date trough the following mechanisms:
+ * The cache stores the metadata for all files and folders in a storage and is kept up to date through the following mechanisms:
*
* - Scanner: scans the storage and updates the cache where needed
* - Watcher: checks for changes made to the filesystem outside of the Nextcloud instance and rescans files and folder when a change is detected
@@ -188,6 +189,7 @@ class Cache implements ICache {
$data['fileid'] = (int)$data['fileid'];
$data['parent'] = (int)$data['parent'];
$data['size'] = 0 + $data['size'];
+ $data['unencrypted_size'] = 0 + ($data['unencrypted_size'] ?? 0);
$data['mtime'] = (int)$data['mtime'];
$data['storage_mtime'] = (int)$data['storage_mtime'];
$data['encryptedVersion'] = (int)$data['encrypted'];
@@ -428,7 +430,7 @@ class Cache implements ICache {
protected function normalizeData(array $data): array {
$fields = [
'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
- 'etag', 'permissions', 'checksum', 'storage'];
+ 'etag', 'permissions', 'checksum', 'storage', 'unencrypted_size'];
$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
$doNotCopyStorageMTime = false;
@@ -538,7 +540,7 @@ class Cache implements ICache {
public function remove($file) {
$entry = $this->get($file);
- if ($entry) {
+ if ($entry instanceof ICacheEntry) {
$query = $this->getQueryBuilder();
$query->delete('filecache')
->whereFileId($entry->getId());
@@ -580,7 +582,7 @@ class Cache implements ICache {
$parentIds = [$entry->getId()];
$queue = [$entry->getId()];
- // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
+ // we walk depth first through the file tree, removing all filecache_extended attributes while we walk
// and collecting all folder ids to later use to delete the filecache entries
while ($entryId = array_pop($queue)) {
$children = $this->getFolderContentsById($entryId);
@@ -873,8 +875,16 @@ class Cache implements ICache {
$id = $entry['fileid'];
$query = $this->getQueryBuilder();
- $query->selectAlias($query->func()->sum('size'), 'f1')
- ->selectAlias($query->func()->min('size'), 'f2')
+ $query->selectAlias($query->func()->sum('size'), 'size_sum')
+ ->selectAlias($query->func()->min('size'), 'size_min')
+ // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
+ ->selectAlias($query->func()->sum(
+ $query->func()->case([
+ ['when' => $query->expr()->eq('unencrypted_size', $query->expr()->literal(0, IQueryBuilder::PARAM_INT)), 'then' => 'size'],
+ ], 'unencrypted_size')
+ ), 'unencrypted_sum')
+ ->selectAlias($query->func()->min('unencrypted_size'), 'unencrypted_min')
+ ->selectAlias($query->func()->max('unencrypted_size'), 'unencrypted_max')
->from('filecache')
->whereStorageId($this->getNumericStorageId())
->whereParent($id);
@@ -884,7 +894,7 @@ class Cache implements ICache {
$result->closeCursor();
if ($row) {
- [$sum, $min] = array_values($row);
+ ['size_sum' => $sum, 'size_min' => $min, 'unencrypted_sum' => $unencryptedSum, 'unencrypted_min' => $unencryptedMin, 'unencrypted_max' => $unencryptedMax] = $row;
$sum = 0 + $sum;
$min = 0 + $min;
if ($min === -1) {
@@ -892,8 +902,23 @@ class Cache implements ICache {
} else {
$totalSize = $sum;
}
+ if ($unencryptedMin === -1 || $min === -1) {
+ $unencryptedTotal = $unencryptedMin;
+ } else {
+ $unencryptedTotal = $unencryptedSum;
+ }
if ($entry['size'] !== $totalSize) {
- $this->update($id, ['size' => $totalSize]);
+ // only set unencrypted size for a folder if any child entries have it set
+ if ($unencryptedMax > 0) {
+ $this->update($id, [
+ 'size' => $totalSize,
+ 'unencrypted_size' => $unencryptedTotal,
+ ]);
+ } else {
+ $this->update($id, [
+ 'size' => $totalSize,
+ ]);
+ }
}
}
}
@@ -927,7 +952,7 @@ class Cache implements ICache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
$query = $this->getQueryBuilder();
diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php
index 12f0273fb6e..8ac76acf6d1 100644
--- a/lib/private/Files/Cache/CacheEntry.php
+++ b/lib/private/Files/Cache/CacheEntry.php
@@ -132,4 +132,12 @@ class CacheEntry implements ICacheEntry {
public function __clone() {
$this->data = array_merge([], $this->data);
}
+
+ public function getUnencryptedSize(): int {
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return $this->data['size'];
+ }
+ }
}
diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php
index b5a9101877c..496a8361d77 100644
--- a/lib/private/Files/Cache/CacheQueryBuilder.php
+++ b/lib/private/Files/Cache/CacheQueryBuilder.php
@@ -41,12 +41,16 @@ class CacheQueryBuilder extends QueryBuilder {
parent::__construct($connection, $systemConfig, $logger);
}
- public function selectFileCache(string $alias = null) {
+ public function selectFileCache(string $alias = null, bool $joinExtendedCache = true) {
$name = $alias ? $alias : 'filecache';
- $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime',
- 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
- ->from('filecache', $name)
- ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime',
+ 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'unencrypted_size')
+ ->from('filecache', $name);
+
+ if ($joinExtendedCache) {
+ $this->addSelect('metadata_etag', 'creation_time', 'upload_time');
+ $this->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ }
$this->alias = $name;
diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php
index 270b2b013f5..a0953baa785 100644
--- a/lib/private/Files/Cache/Propagator.php
+++ b/lib/private/Files/Cache/Propagator.php
@@ -24,6 +24,7 @@
namespace OC\Files\Cache;
+use OC\Files\Storage\Wrapper\Encryption;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Storage\IReliableEtagStorage;
@@ -65,7 +66,7 @@ class Propagator implements IPropagator {
* @param int $sizeDifference number of bytes the file has grown
*/
public function propagateChange($internalPath, $time, $sizeDifference = 0) {
- // Do not propogate changes in ignored paths
+ // Do not propagate changes in ignored paths
foreach ($this->ignore as $ignore) {
if (strpos($internalPath, $ignore) === 0) {
return;
@@ -113,6 +114,20 @@ class Propagator implements IPropagator {
->andWhere($builder->expr()->in('path_hash', $hashParams))
->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT)));
+ if ($this->storage->instanceOfStorage(Encryption::class)) {
+ // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
+ $builder->set('unencrypted_size', $builder->func()->greatest(
+ $builder->func()->add(
+ $builder->func()->case([
+ ['when' => $builder->expr()->eq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT)), 'then' => 'size']
+ ], 'unencrypted_size'),
+ $builder->createNamedParameter($sizeDifference)
+ ),
+ $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
+ ));
+ }
+
+ $a = $builder->getSQL();
$builder->execute();
}
}
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index 3bf9abf3524..3529ede9746 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -28,6 +28,7 @@ namespace OC\Files\Cache;
use OC\Files\Search\QueryOptimizer\QueryOptimizer;
use OC\Files\Search\SearchBinaryOperator;
use OC\SystemConfig;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\IMimeTypeLoader;
@@ -102,7 +103,7 @@ class QuerySearchHelper {
$builder = $this->getQueryBuilder();
- $query = $builder->selectFileCache('file');
+ $query = $builder->selectFileCache('file', false);
if ($this->searchBuilder->shouldJoinTags($searchQuery->getSearchOperation())) {
$user = $searchQuery->getUser();
@@ -110,13 +111,21 @@ class QuerySearchHelper {
throw new \InvalidArgumentException("Searching by tag requires the user to be set in the query");
}
$query
- ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
- ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
+ ->leftJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
+ ->leftJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
$builder->expr()->eq('tagmap.type', 'tag.type'),
- $builder->expr()->eq('tagmap.categoryid', 'tag.id')
+ $builder->expr()->eq('tagmap.categoryid', 'tag.id'),
+ $builder->expr()->eq('tag.type', $builder->createNamedParameter('files')),
+ $builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID()))
))
- ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
- ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID())));
+ ->leftJoin('file', 'systemtag_object_mapping', 'systemtagmap', $builder->expr()->andX(
+ $builder->expr()->eq('file.fileid', $builder->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)),
+ $builder->expr()->eq('systemtagmap.objecttype', $builder->createNamedParameter('files'))
+ ))
+ ->leftJoin('systemtagmap', 'systemtag', 'systemtag', $builder->expr()->andX(
+ $builder->expr()->eq('systemtag.id', 'systemtagmap.systemtagid'),
+ $builder->expr()->eq('systemtag.visibility', $builder->createNamedParameter(true))
+ ));
}
$storageFilters = array_values(array_map(function (ICache $cache) {
@@ -149,7 +158,7 @@ class QuerySearchHelper {
$result->closeCursor();
- // loop trough all caches for each result to see if the result matches that storage
+ // loop through all caches for each result to see if the result matches that storage
// results are grouped by the same array keys as the caches argument to allow the caller to distringuish the source of the results
$results = array_fill_keys(array_keys($caches), []);
foreach ($rawEntries as $rawEntry) {
diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php
index c8c442bcb8c..b5f548dd563 100644
--- a/lib/private/Files/Cache/SearchBuilder.php
+++ b/lib/private/Files/Cache/SearchBuilder.php
@@ -80,7 +80,7 @@ class SearchBuilder {
return $shouldJoin || $this->shouldJoinTags($operator);
}, false);
} elseif ($operator instanceof ISearchComparison) {
- return $operator->getField() === 'tagname' || $operator->getField() === 'favorite';
+ return $operator->getField() === 'tagname' || $operator->getField() === 'favorite' || $operator->getField() === 'systemtag';
}
return false;
}
@@ -163,8 +163,12 @@ class SearchBuilder {
} elseif ($field === 'favorite') {
$field = 'tag.category';
$value = self::TAG_FAVORITE;
+ } elseif ($field === 'name') {
+ $field = 'file.name';
} elseif ($field === 'tagname') {
$field = 'tag.category';
+ } elseif ($field === 'systemtag') {
+ $field = 'systemtag.name';
} elseif ($field === 'fileid') {
$field = 'file.fileid';
} elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL && $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)) {
@@ -182,6 +186,7 @@ class SearchBuilder {
'path' => 'string',
'size' => 'integer',
'tagname' => 'string',
+ 'systemtag' => 'string',
'favorite' => 'boolean',
'fileid' => 'integer',
'storage' => 'integer',
@@ -193,6 +198,7 @@ class SearchBuilder {
'path' => ['eq', 'like', 'clike'],
'size' => ['eq', 'gt', 'lt', 'gte', 'lte'],
'tagname' => ['eq', 'like'],
+ 'systemtag' => ['eq', 'like'],
'favorite' => ['eq'],
'fileid' => ['eq'],
'storage' => ['eq'],
diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php
index fb9e5500658..f77c9b71dd7 100644
--- a/lib/private/Files/Cache/Storage.php
+++ b/lib/private/Files/Cache/Storage.php
@@ -40,7 +40,7 @@ use Psr\Log\LoggerInterface;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
@@ -135,7 +135,7 @@ class Storage {
* Get the numeric of the storage with the provided string id
*
* @param $storageId
- * @return int|null either the numeric storage id or null if the storage id is not knwon
+ * @return int|null either the numeric storage id or null if the storage id is not known
*/
public static function getNumericStorageId($storageId) {
$storageId = self::adjustStorageId($storageId);
diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php
index a898c435415..74cbd5abdb2 100644
--- a/lib/private/Files/Cache/StorageGlobal.php
+++ b/lib/private/Files/Cache/StorageGlobal.php
@@ -33,7 +33,7 @@ use OCP\IDBConnection;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php
index 98fb51fe264..f8c187996e6 100644
--- a/lib/private/Files/Cache/Updater.php
+++ b/lib/private/Files/Cache/Updater.php
@@ -73,14 +73,14 @@ class Updater implements IUpdater {
}
/**
- * Disable updating the cache trough this updater
+ * Disable updating the cache through this updater
*/
public function disable() {
$this->enabled = false;
}
/**
- * Re-enable the updating of the cache trough this updater
+ * Re-enable the updating of the cache through this updater
*/
public function enable() {
$this->enabled = true;
diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php
index 7183a6c0d2a..4053042edd9 100644
--- a/lib/private/Files/Cache/Wrapper/CacheJail.php
+++ b/lib/private/Files/Cache/Wrapper/CacheJail.php
@@ -267,7 +267,7 @@ class CacheJail extends CacheWrapper {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
// not supported
diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
index e5300dc75f5..66ae83fd144 100644
--- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php
+++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
@@ -267,7 +267,7 @@ class CacheWrapper extends Cache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
return $this->getCache()->getIncomplete();
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index c326eeb0b6c..685057a7860 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Config;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\ICachedMountFileInfo;
@@ -42,7 +42,7 @@ use OCP\IUserManager;
use Psr\Log\LoggerInterface;
/**
- * Cache mounts points per user in the cache so we can easilly look them up
+ * Cache mounts points per user in the cache so we can easily look them up
*/
class UserMountCache implements IUserMountCache {
private IDBConnection $connection;
diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php
index 6389544184f..47c893ebbf1 100644
--- a/lib/private/Files/FileInfo.php
+++ b/lib/private/Files/FileInfo.php
@@ -101,7 +101,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
$this->data = $data;
$this->mount = $mount;
$this->owner = $owner;
- $this->rawSize = $this->data['size'] ?? 0;
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] !== 0) {
+ $this->rawSize = $this->data['unencrypted_size'];
+ } else {
+ $this->rawSize = $this->data['size'] ?? 0;
+ }
}
public function offsetSet($offset, $value): void {
@@ -208,7 +212,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
public function getSize($includeMounts = true) {
if ($includeMounts) {
$this->updateEntryfromSubMounts();
- return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+ }
} else {
return $this->rawSize;
}
@@ -386,14 +395,26 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* @param string $entryPath full path of the child entry
*/
public function addSubEntry($data, $entryPath) {
- $this->data['size'] += isset($data['size']) ? $data['size'] : 0;
+ if (!$data) {
+ return;
+ }
+ $hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0;
+ if ($hasUnencryptedSize) {
+ $subSize = $data['unencrypted_size'];
+ } else {
+ $subSize = $data['size'] ?: 0;
+ }
+ $this->data['size'] += $subSize;
+ if ($hasUnencryptedSize) {
+ $this->data['unencrypted_size'] += $subSize;
+ }
if (isset($data['mtime'])) {
$this->data['mtime'] = max($this->data['mtime'], $data['mtime']);
}
if (isset($data['etag'])) {
// prefix the etag with the relative path of the subentry to propagate etag on mount moves
$relativeEntryPath = substr($entryPath, strlen($this->getPath()));
- // attach the permissions to propagate etag on permision changes of submounts
+ // attach the permissions to propagate etag on permission changes of submounts
$permissions = isset($data['permissions']) ? $data['permissions'] : 0;
$this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions;
}
diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php
index 20b44e2736a..9542666b03c 100644
--- a/lib/private/Files/Filesystem.php
+++ b/lib/private/Files/Filesystem.php
@@ -37,7 +37,7 @@
*/
namespace OC\Files;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MountPoint;
use OC\User\NoUserException;
use OCP\EventDispatcher\IEventDispatcher;
diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php
index 69285018d17..9ba0e504058 100644
--- a/lib/private/Files/Mount/Manager.php
+++ b/lib/private/Files/Mount/Manager.php
@@ -29,7 +29,7 @@ declare(strict_types=1);
namespace OC\Files\Mount;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OC\Files\SetupManager;
use OC\Files\SetupManagerFactory;
diff --git a/lib/private/Files/Mount/MoveableMount.php b/lib/private/Files/Mount/MoveableMount.php
index 7ebb7fa2c87..a7372153d75 100644
--- a/lib/private/Files/Mount/MoveableMount.php
+++ b/lib/private/Files/Mount/MoveableMount.php
@@ -37,7 +37,6 @@ interface MoveableMount {
/**
* Remove the mount points
*
- * @return mixed
* @return bool
*/
public function removeMount();
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index e125715e6a8..d8a6741dc6e 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -131,7 +131,6 @@ class File extends Node implements \OCP\Files\File {
$this->view->unlink($this->path);
$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
$this->fileInfo = null;
} else {
throw new NotPermittedException();
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 9c15f0edf41..42562c99bcb 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -262,7 +262,7 @@ class Folder extends Node implements \OCP\Files\Folder {
$searchHelper = \OC::$server->get(QuerySearchHelper::class);
$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
- // loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
+ // loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
$mount = $mountByMountPoint[$relativeMountPoint];
return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
@@ -388,7 +388,6 @@ class Folder extends Node implements \OCP\Files\Folder {
$this->view->rmdir($this->path);
$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
} else {
throw new NotPermittedException('No delete permission for path');
}
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index 6dd65a4291d..ca930c1002c 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -32,7 +32,7 @@
namespace OC\Files\Node;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\FileInfo;
use OC\Files\Mount\Manager;
use OC\Files\Mount\MountPoint;
@@ -427,7 +427,7 @@ class Root extends Folder implements IRootFolder {
$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
}
- // when a user has access trough the same storage trough multiple paths
+ // when a user has access through the same storage through multiple paths
// (such as an external storage that is both mounted for a user and shared to the user)
// the mount cache will only hold a single entry for the storage
// this can lead to issues as the different ways the user has access to a storage can have different permissions
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index adb3928b28a..898f64d97c2 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -335,6 +335,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$handle = fopen($tmpFile, $mode);
return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
$this->writeBack($tmpFile, $path);
+ unlink($tmpFile);
});
case 'a':
case 'ab':
@@ -352,6 +353,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$handle = fopen($tmpFile, $mode);
return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
$this->writeBack($tmpFile, $path);
+ unlink($tmpFile);
});
}
return false;
diff --git a/lib/private/Files/ObjectStore/S3ConnectionTrait.php b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
index c3836749c6d..8286321450d 100644
--- a/lib/private/Files/ObjectStore/S3ConnectionTrait.php
+++ b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
@@ -28,6 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OC\Files\ObjectStore;
use Aws\ClientResolver;
@@ -121,15 +122,6 @@ trait S3ConnectionTrait {
)
);
- // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
- if (!isset($this->params['primary_storage'])) {
- /** @var ICertificateManager $certManager */
- $certManager = \OC::$server->get(ICertificateManager::class);
- $certPath = $certManager->getAbsoluteBundlePath();
- } else {
- $certPath = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
- }
-
$options = [
'version' => isset($this->params['version']) ? $this->params['version'] : 'latest',
'credentials' => $provider,
@@ -139,7 +131,7 @@ trait S3ConnectionTrait {
'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
'csm' => false,
'use_arn_region' => false,
- 'http' => ['verify' => $certPath],
+ 'http' => ['verify' => $this->getCertificateBundlePath()],
];
if ($this->getProxy()) {
$options['http']['proxy'] = $this->getProxy();
@@ -152,7 +144,7 @@ trait S3ConnectionTrait {
if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
$logger = \OC::$server->get(LoggerInterface::class);
$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
- ['app' => 'objectstore']);
+ ['app' => 'objectstore']);
}
if ($this->params['verify_bucket_exists'] && !$this->connection->doesBucketExist($this->bucket)) {
@@ -203,7 +195,7 @@ trait S3ConnectionTrait {
/**
* This function creates a credential provider based on user parameter file
*/
- protected function paramCredentialProvider() : callable {
+ protected function paramCredentialProvider(): callable {
return function () {
$key = empty($this->params['key']) ? null : $this->params['key'];
$secret = empty($this->params['secret']) ? null : $this->params['secret'];
@@ -218,4 +210,19 @@ trait S3ConnectionTrait {
return new RejectedPromise(new CredentialsException($msg));
};
}
+
+ protected function getCertificateBundlePath(): ?string {
+ if ((int)($this->params['use_nextcloud_bundle'] ?? "0")) {
+ // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
+ if (!isset($this->params['primary_storage'])) {
+ /** @var ICertificateManager $certManager */
+ $certManager = \OC::$server->get(ICertificateManager::class);
+ return $certManager->getAbsoluteBundlePath();
+ } else {
+ return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
+ }
+ } else {
+ return null;
+ }
+ }
}
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 4e54a26e98a..9d692e01a23 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -43,6 +43,8 @@ trait S3ObjectTrait {
*/
abstract protected function getConnection();
+ abstract protected function getCertificateBundlePath(): ?string;
+
/**
* @param string $urn the unified resource name used to identify the object
* @return resource stream with the read data
@@ -67,8 +69,14 @@ trait S3ObjectTrait {
'http' => [
'protocol_version' => $request->getProtocolVersion(),
'header' => $headers,
- ],
+ ]
];
+ $bundle = $this->getCertificateBundlePath();
+ if ($bundle) {
+ $opts['ssl'] = [
+ 'cafile' => $bundle
+ ];
+ }
if ($this->getProxy()) {
$opts['http']['proxy'] = $this->getProxy();
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index 040ba6b898f..5782a5a72a6 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -82,6 +82,7 @@ class SetupManager {
private IConfig $config;
private bool $listeningForProviders;
private array $fullSetupRequired = [];
+ private bool $setupBuiltinWrappersDone = false;
public function __construct(
IEventLogger $eventLogger,
@@ -121,6 +122,15 @@ class SetupManager {
}
private function setupBuiltinWrappers() {
+ if ($this->setupBuiltinWrappersDone) {
+ return;
+ }
+ $this->setupBuiltinWrappersDone = true;
+
+ // load all filesystem apps before, so no setup-hook gets lost
+ OC_App::loadApps(['filesystem']);
+ $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
+
Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
if ($storage->instanceOfStorage(Common::class)) {
$storage->setMountOptions($mount->getOptions());
@@ -188,6 +198,8 @@ class SetupManager {
}
return $storage;
});
+
+ Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
}
/**
@@ -223,6 +235,9 @@ class SetupManager {
return;
}
$this->setupUsers[] = $user->getUID();
+
+ $this->setupBuiltinWrappers();
+
$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
@@ -321,14 +336,8 @@ class SetupManager {
$this->eventLogger->start('setup_root_fs', 'Setup root filesystem');
- // load all filesystem apps before, so no setup-hook gets lost
- OC_App::loadApps(['filesystem']);
- $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
-
$this->setupBuiltinWrappers();
- Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
-
$rootMounts = $this->mountProviderCollection->getRootMounts();
foreach ($rootMounts as $rootMountProvider) {
$this->mountManager->addMount($rootMountProvider);
@@ -380,13 +389,9 @@ class SetupManager {
return;
}
- // for the user's home folder, it's always the home mount
- if (rtrim($path) === "/" . $user->getUID() . "/files") {
- if ($includeChildren) {
- $this->setupForUser($user);
- } else {
- $this->oneTimeUserSetup($user);
- }
+ // for the user's home folder, and includes children we need everything always
+ if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) {
+ $this->setupForUser($user);
return;
}
@@ -403,6 +408,10 @@ class SetupManager {
return;
}
+ if (!$this->isSetupStarted($user)) {
+ $this->oneTimeUserSetup($user);
+ }
+
$mounts = [];
if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
$setupProviders[] = $cachedMount->getMountProvider();
@@ -554,10 +563,10 @@ class SetupManager {
});
$genericEvents = [
- '\OCA\Circles::onCircleCreation',
- '\OCA\Circles::onCircleDestruction',
- '\OCA\Circles::onMemberNew',
- '\OCA\Circles::onMemberLeaving',
+ 'OCA\Circles\Events\CreatingCircleEvent',
+ 'OCA\Circles\Events\DestroyingCircleEvent',
+ 'OCA\Circles\Events\AddingCircleMemberEvent',
+ 'OCA\Circles\Events\RemovingCircleMemberEvent',
];
foreach ($genericEvents as $genericEvent) {
diff --git a/lib/private/Files/SimpleFS/NewSimpleFile.php b/lib/private/Files/SimpleFS/NewSimpleFile.php
index 76fc69ebbe7..b2a183b7d29 100644
--- a/lib/private/Files/SimpleFS/NewSimpleFile.php
+++ b/lib/private/Files/SimpleFS/NewSimpleFile.php
@@ -34,15 +34,12 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class NewSimpleFile implements ISimpleFile {
- private $parentFolder;
- private $name;
- /** @var File|null */
- private $file = null;
+ private Folder $parentFolder;
+ private string $name;
+ private ?File $file = null;
/**
* File constructor.
- *
- * @param File $file
*/
public function __construct(Folder $parentFolder, string $name) {
$this->parentFolder = $parentFolder;
@@ -51,19 +48,15 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->name;
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
if ($this->file) {
return $this->file->getSize();
} else {
@@ -73,10 +66,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
if ($this->file) {
return $this->file->getEtag();
} else {
@@ -86,10 +77,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
if ($this->file) {
return $this->file->getMTime();
} else {
@@ -100,11 +89,10 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the content
*
- * @return string
* @throws NotFoundException
* @throws NotPermittedException
*/
- public function getContent() {
+ public function getContent(): string {
if ($this->file) {
$result = $this->file->getContent();
@@ -125,7 +113,7 @@ class NewSimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
if ($this->file) {
$this->file->putContent($data);
@@ -139,7 +127,7 @@ class NewSimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -147,7 +135,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -171,7 +159,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
if ($this->file) {
$this->file->delete();
}
@@ -182,7 +170,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
if ($this->file) {
return $this->file->getMimeType();
} else {
diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php
index 21a2fd92dcb..a2571ac50e8 100644
--- a/lib/private/Files/SimpleFS/SimpleFile.php
+++ b/lib/private/Files/SimpleFS/SimpleFile.php
@@ -30,52 +30,37 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFile implements ISimpleFile {
+ private File $file;
- /** @var File $file */
- private $file;
-
- /**
- * File constructor.
- *
- * @param File $file
- */
public function __construct(File $file) {
$this->file = $file;
}
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->file->getName();
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
return $this->file->getSize();
}
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
return $this->file->getEtag();
}
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
return $this->file->getMTime();
}
@@ -84,9 +69,8 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
* @throws NotFoundException
- * @return string
*/
- public function getContent() {
+ public function getContent(): string {
$result = $this->file->getContent();
if ($result === false) {
@@ -103,9 +87,9 @@ class SimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
- return $this->file->putContent($data);
+ $this->file->putContent($data);
} catch (NotFoundException $e) {
$this->checkFile();
}
@@ -113,7 +97,7 @@ class SimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -121,7 +105,7 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -145,16 +129,14 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
$this->file->delete();
}
/**
* Get the MimeType
- *
- * @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
return $this->file->getMimeType();
}
@@ -179,7 +161,7 @@ class SimpleFile implements ISimpleFile {
/**
* Open the file as stream for writing, resulting resource can be operated as stream like the result from php's own fopen
*
- * @return resource
+ * @return resource|false
* @throws \OCP\Files\NotPermittedException
* @since 14.0.0
*/
diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php
index cd2a712019e..263c25a8873 100644
--- a/lib/private/Files/SimpleFS/SimpleFolder.php
+++ b/lib/private/Files/SimpleFS/SimpleFolder.php
@@ -29,6 +29,7 @@ use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFolder implements ISimpleFolder {
@@ -44,11 +45,11 @@ class SimpleFolder implements ISimpleFolder {
$this->folder = $folder;
}
- public function getName() {
+ public function getName(): string {
return $this->folder->getName();
}
- public function getDirectoryListing() {
+ public function getDirectoryListing(): array {
$listing = $this->folder->getDirectoryListing();
$fileListing = array_map(function (Node $file) {
@@ -63,15 +64,15 @@ class SimpleFolder implements ISimpleFolder {
return array_values($fileListing);
}
- public function delete() {
+ public function delete(): void {
$this->folder->delete();
}
- public function fileExists($name) {
+ public function fileExists(string $name): bool {
return $this->folder->nodeExists($name);
}
- public function getFile($name) {
+ public function getFile(string $name): ISimpleFile {
$file = $this->folder->get($name);
if (!($file instanceof File)) {
@@ -81,7 +82,7 @@ class SimpleFolder implements ISimpleFolder {
return new SimpleFile($file);
}
- public function newFile($name, $content = null) {
+ public function newFile(string $name, $content = null): ISimpleFile {
if ($content === null) {
// delay creating the file until it's written to
return new NewSimpleFile($this->folder, $name);
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index 3c970ee75f5..a7bc44e10e2 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -228,6 +228,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
while ($file = readdir($dir)) {
if (!Filesystem::isIgnoredDir($file)) {
if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
+ closedir($dir);
return false;
}
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index ee8a8c7d161..4996572a40e 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -15,6 +15,7 @@
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Klaas Freitag <freitag@owncloud.com>
* @author Lukas Reschke <lukas@statuscode.ch>
+ * @author Martin Brugnara <martin@0x6d62.eu>
* @author Michael Gapczynski <GapczynskiM@gmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
@@ -66,6 +67,8 @@ class Local extends \OC\Files\Storage\Common {
private IMimeTypeDetector $mimeTypeDetector;
+ private $defUMask;
+
public function __construct($arguments) {
if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
throw new \InvalidArgumentException('No data directory set for local storage');
@@ -84,6 +87,7 @@ class Local extends \OC\Files\Storage\Common {
$this->dataDirLength = strlen($this->realDataDir);
$this->config = \OC::$server->get(IConfig::class);
$this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class);
+ $this->defUMask = $this->config->getSystemValue('localstorage.umask', 0022);
}
public function __destruct() {
@@ -95,7 +99,7 @@ class Local extends \OC\Files\Storage\Common {
public function mkdir($path) {
$sourcePath = $this->getSourcePath($path);
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = @mkdir($sourcePath, 0777, true);
umask($oldMask);
return $result;
@@ -273,7 +277,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
if (!is_null($mtime)) {
$result = @touch($this->getSourcePath($path), $mtime);
} else {
@@ -292,7 +296,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function file_put_contents($path, $data) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = file_put_contents($this->getSourcePath($path), $data);
umask($oldMask);
return $result;
@@ -365,7 +369,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->is_dir($path1)) {
return parent::copy($path1, $path2);
} else {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = copy($this->getSourcePath($path1), $this->getSourcePath($path2));
umask($oldMask);
return $result;
@@ -373,7 +377,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function fopen($path, $mode) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = fopen($this->getSourcePath($path), $mode);
umask($oldMask);
return $result;
diff --git a/lib/private/Files/Storage/Wrapper/Encoding.php b/lib/private/Files/Storage/Wrapper/Encoding.php
index d6201dc8877..ac9cc248ce6 100644
--- a/lib/private/Files/Storage/Wrapper/Encoding.php
+++ b/lib/private/Files/Storage/Wrapper/Encoding.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Storage\Wrapper;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OCP\Files\Storage\IStorage;
use OCP\ICache;
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php
index 4cfe932cc9f..d5bf929101f 100644
--- a/lib/private/Files/Storage/Wrapper/Encryption.php
+++ b/lib/private/Files/Storage/Wrapper/Encryption.php
@@ -33,6 +33,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Storage\Wrapper;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
@@ -41,6 +42,7 @@ use OC\Encryption\Util;
use OC\Files\Cache\CacheEntry;
use OC\Files\Filesystem;
use OC\Files\Mount\Manager;
+use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Storage\LocalTempFileTrait;
use OC\Memcache\ArrayCache;
use OCP\Encryption\Exceptions\GenericEncryptionException;
@@ -139,28 +141,36 @@ class Encryption extends Wrapper {
$size = $this->unencryptedSize[$fullPath];
// update file cache
if ($info instanceof ICacheEntry) {
- $info = $info->getData();
$info['encrypted'] = $info['encryptedVersion'];
} else {
if (!is_array($info)) {
$info = [];
}
$info['encrypted'] = true;
+ $info = new CacheEntry($info);
}
- $info['size'] = $size;
- $this->getCache()->put($path, $info);
+ if ($size !== $info->getUnencryptedSize()) {
+ $this->getCache()->update($info->getId(), [
+ 'unencrypted_size' => $size
+ ]);
+ }
return $size;
}
if (isset($info['fileid']) && $info['encrypted']) {
- return $this->verifyUnencryptedSize($path, $info['size']);
+ return $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
}
return $this->storage->filesize($path);
}
+ /**
+ * @param string $path
+ * @param array $data
+ * @return array
+ */
private function modifyMetaData(string $path, array $data): array {
$fullPath = $this->getFullPath($path);
$info = $this->getCache()->get($path);
@@ -170,7 +180,7 @@ class Encryption extends Wrapper {
$data['size'] = $this->unencryptedSize[$fullPath];
} else {
if (isset($info['fileid']) && $info['encrypted']) {
- $data['size'] = $this->verifyUnencryptedSize($path, $info['size']);
+ $data['size'] = $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
$data['encrypted'] = true;
}
}
@@ -478,7 +488,7 @@ class Encryption extends Wrapper {
*
* @return int unencrypted size
*/
- protected function verifyUnencryptedSize($path, $unencryptedSize) {
+ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): int {
$size = $this->storage->filesize($path);
$result = $unencryptedSize;
@@ -510,7 +520,7 @@ class Encryption extends Wrapper {
*
* @return int calculated unencrypted size
*/
- protected function fixUnencryptedSize($path, $size, $unencryptedSize) {
+ protected function fixUnencryptedSize(string $path, int $size, int $unencryptedSize): int {
$headerSize = $this->getHeaderSize($path);
$header = $this->getHeader($path);
$encryptionModule = $this->getEncryptionModule($path);
@@ -581,7 +591,9 @@ class Encryption extends Wrapper {
$cache = $this->storage->getCache();
if ($cache) {
$entry = $cache->get($path);
- $cache->update($entry['fileid'], ['size' => $newUnencryptedSize]);
+ $cache->update($entry['fileid'], [
+ 'unencrypted_size' => $newUnencryptedSize
+ ]);
}
return $newUnencryptedSize;
@@ -621,7 +633,12 @@ class Encryption extends Wrapper {
* @param bool $preserveMtime
* @return bool
*/
- public function moveFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = true) {
+ public function moveFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = true
+ ) {
if ($sourceStorage === $this) {
return $this->rename($sourceInternalPath, $targetInternalPath);
}
@@ -656,7 +673,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @return bool
*/
- public function copyFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false, $isRename = false) {
+ public function copyFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = false,
+ $isRename = false
+ ) {
// TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed:
// - call $this->storage->copyFromStorage() instead of $this->copyBetweenStorage
@@ -676,7 +699,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @param bool $keepEncryptionVersion
*/
- private function updateEncryptedVersion(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, $keepEncryptionVersion) {
+ private function updateEncryptedVersion(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $isRename,
+ $keepEncryptionVersion
+ ) {
$isEncrypted = $this->encryptionManager->isEnabled() && $this->shouldEncrypt($targetInternalPath);
$cacheInformation = [
'encrypted' => $isEncrypted,
@@ -725,7 +754,13 @@ class Encryption extends Wrapper {
* @return bool
* @throws \Exception
*/
- private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) {
+ private function copyBetweenStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime,
+ $isRename
+ ) {
// for versions we have nothing to do, because versions should always use the
// key from the original file. Just create a 1:1 copy and done
@@ -743,7 +778,7 @@ class Encryption extends Wrapper {
if (isset($info['encrypted']) && $info['encrypted'] === true) {
$this->updateUnencryptedSize(
$this->getFullPath($targetInternalPath),
- $info['size']
+ $info->getUnencryptedSize()
);
}
$this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, true);
@@ -808,13 +843,6 @@ class Encryption extends Wrapper {
return (bool)$result;
}
- /**
- * get the path to a local version of the file.
- * The local version of the file can be temporary and doesn't have to be persistent across requests
- *
- * @param string $path
- * @return string
- */
public function getLocalFile($path) {
if ($this->encryptionManager->isEnabled()) {
$cachedFile = $this->getCachedFile($path);
@@ -825,11 +853,6 @@ class Encryption extends Wrapper {
return $this->storage->getLocalFile($path);
}
- /**
- * Returns the wrapped storage's value for isLocal()
- *
- * @return bool wrapped storage's isLocal() value
- */
public function isLocal() {
if ($this->encryptionManager->isEnabled()) {
return false;
@@ -837,15 +860,11 @@ class Encryption extends Wrapper {
return $this->storage->isLocal();
}
- /**
- * see https://www.php.net/manual/en/function.stat.php
- * only the following keys are required in the result: size and mtime
- *
- * @param string $path
- * @return array
- */
public function stat($path) {
$stat = $this->storage->stat($path);
+ if (!$stat) {
+ return false;
+ }
$fileSize = $this->filesize($path);
$stat['size'] = $fileSize;
$stat[7] = $fileSize;
@@ -853,14 +872,6 @@ class Encryption extends Wrapper {
return $stat;
}
- /**
- * see https://www.php.net/manual/en/function.hash.php
- *
- * @param string $type
- * @param string $path
- * @param bool $raw
- * @return string
- */
public function hash($type, $path, $raw = false) {
$fh = $this->fopen($path, 'rb');
$ctx = hash_init($type);
@@ -1068,6 +1079,13 @@ class Encryption extends Wrapper {
[$count, $result] = \OC_Helper::streamCopy($stream, $target);
fclose($stream);
fclose($target);
+
+ // object store, stores the size after write and doesn't update this during scan
+ // manually store the unencrypted size
+ if ($result && $this->getWrapperStorage()->instanceOfStorage(ObjectStoreStorage::class)) {
+ $this->getCache()->put($path, ['unencrypted_size' => $count]);
+ }
+
return $count;
}
}
diff --git a/lib/private/Files/Stream/SeekableHttpStream.php b/lib/private/Files/Stream/SeekableHttpStream.php
index af797c7720d..820a681bd07 100644
--- a/lib/private/Files/Stream/SeekableHttpStream.php
+++ b/lib/private/Files/Stream/SeekableHttpStream.php
@@ -24,6 +24,7 @@
namespace OC\Files\Stream;
use Icewind\Streams\File;
+use Icewind\Streams\Wrapper;
/**
* A stream wrapper that uses http range requests to provide a seekable stream for http reading
@@ -92,6 +93,18 @@ class SeekableHttpStream implements File {
}
$responseHead = stream_get_meta_data($this->current)['wrapper_data'];
+
+ while ($responseHead instanceof Wrapper) {
+ $wrapperOptions = stream_context_get_options($responseHead->context);
+ foreach ($wrapperOptions as $options) {
+ if (isset($options['source']) && is_resource($options['source'])) {
+ $responseHead = stream_get_meta_data($options['source'])['wrapper_data'];
+ continue 2;
+ }
+ }
+ throw new \Exception("Failed to get source stream from stream wrapper of " . get_class($responseHead));
+ }
+
$rangeHeaders = array_values(array_filter($responseHead, function ($v) {
return preg_match('#^content-range:#i', $v) === 1;
}));
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index a555aeb24f1..e5394e72ffe 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1159,7 +1159,7 @@ class View {
try {
$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
} catch (LockedException $e) {
- // release the shared lock we acquired before quiting
+ // release the shared lock we acquired before quitting
$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
throw $e;
}
@@ -1720,7 +1720,7 @@ class View {
/**
* Get the path of a file by id, relative to the view
*
- * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
+ * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
*
* @param int $id
* @param int|null $storageId