diff --git a/config/config.sample.php b/config/config.sample.php index 32354966949da..0cc2eec984f86 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1924,6 +1924,15 @@ */ 'updatedirectory' => '', +/** + * Override where Nextcloud stores the ``appdata_INSTANCEID`` directory. Useful + * when using remote object storage where local system disks provide faster data + * access. Defaults to `datadirectory` if unset. + * + * The Web server user must have write access to this directory. + */ +'appdatadirectory' => '', + /** * Blacklist a specific file or files and disallow the upload of files * with this name. ``.htaccess`` is blocked by default. diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php index 237fcb42e0376..86fff41167458 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -27,7 +27,11 @@ namespace OC\Files\AppData; use OCP\Cache\CappedMemoryCache; +use OC\Files\Filesystem; use OC\Files\SimpleFS\SimpleFolder; +use OC\Files\Node\LazyRoot; +use OC\Files\Storage\LocalRootStorage; +use OC\Files\Mount\MountPoint; use OC\SystemConfig; use OCP\Files\Folder; use OCP\Files\IAppData; @@ -55,10 +59,28 @@ class AppData implements IAppData { public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig, string $appId) { - $this->rootFolder = $rootFolder; $this->config = $systemConfig; $this->appId = $appId; $this->folders = new CappedMemoryCache(); + + $this->rootFolder = new LazyRoot(function () use ($rootFolder, $systemConfig) { + if ($appdatadirectory = $systemConfig->getValue('appdatadirectory', null)) { + $instanceId = $systemConfig->getValue('instanceid', null); + if ($instanceId === null) { + throw new \RuntimeException('no instance id!'); + } + + $folderName = 'appdata_' . $instanceId; + + $arguments = [ + 'datadir' => $appdatadirectory, + ]; + $storage = new LocalRootStorage($arguments); + $mount = new MountPoint($storage, $folderName, $arguments); + Filesystem::getMountManager()->addMount($mount); + } + return $rootFolder; + }); } private function getAppDataFolderName() { diff --git a/lib/private/Files/Cache/LocalRootScanner.php b/lib/private/Files/Cache/LocalRootScanner.php index df5ddd0075bc5..13e153e83e98f 100644 --- a/lib/private/Files/Cache/LocalRootScanner.php +++ b/lib/private/Files/Cache/LocalRootScanner.php @@ -25,6 +25,8 @@ */ namespace OC\Files\Cache; +use OC\Files\Filesystem; + class LocalRootScanner extends Scanner { public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) { if ($this->shouldScanPath($file)) { @@ -43,7 +45,11 @@ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $loc } private function shouldScanPath(string $path): bool { + $storageId = $this->storage->getId(); + $mount = Filesystem::getMountManager()->findByStorageId($storageId); + $mountPoint = sizeof($mount) == 1 ? $mount[0]->getMountPoint() : "null"; + $path = trim($path, '/'); - return $path === '' || str_starts_with($path, 'appdata_') || str_starts_with($path, '__groupfolders'); + return $path === '' || str_starts_with($path, 'appdata_') || str_starts_with($path, '__groupfolders') || str_starts_with($mountPoint, '/appdata_'); } } diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index a18c4c2a138cb..c78efb9094fe5 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -38,6 +38,7 @@ class SystemConfig { protected $sensitiveValues = [ 'instanceid' => true, 'datadirectory' => true, + 'appdatadirectory' => true, 'dbname' => true, 'dbhost' => true, 'dbpassword' => true, diff --git a/tests/lib/Files/AppData/AppDataTest.php b/tests/lib/Files/AppData/AppDataTest.php index d06d7a9a91e41..a7adee897a65d 100644 --- a/tests/lib/Files/AppData/AppDataTest.php +++ b/tests/lib/Files/AppData/AppDataTest.php @@ -51,8 +51,10 @@ protected function setUp(): void { $this->systemConfig->expects($this->any()) ->method('getValue') - ->with('instanceid', null) - ->willReturn('iid'); + ->willReturnMap([ + ['instanceid', null, 'iid'], + ['appdatadirectory', null, '/path'], + ]); } private function setupAppFolder() {