diff --git a/config/config.sample.php b/config/config.sample.php index f7861c3c94c47..3da6e1a110417 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1863,6 +1863,16 @@ */ 'updatedirectory' => '', +/** + * Override where Nextcloud stores the ``appdata_INSTANCEID`` directory. Useful + * when using remote object storage where local system disks provide faster data + * access or when using shared storage for ``appdata_INSTANCEID`` between + * multiple Nextcloud systems. Defaults to `datadirectory` if unset. + * + * The Web server user must have write access to this directory. + */ +'appdataroot' => '', + /** * 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..3e1d8235e7fba 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -28,6 +28,9 @@ use OCP\Cache\CappedMemoryCache; 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 +58,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 ($appdataroot = $systemConfig->getValue('appdataroot', null)) { + $instanceId = $systemConfig->getValue('instanceid', null); + if ($instanceId === null) { + throw new \RuntimeException('no instance id!'); + } + + $folderName = 'appdata_' . $instanceId; + + $arguments = [ + 'datadir' => $appdataroot, + ]; + $storage = new LocalRootStorage($arguments); + $mount = new MountPoint($storage, $folderName, $arguments); + \OC::$server->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 0b6bc497ea36d..0c31457cd794f 100644 --- a/lib/private/Files/Cache/LocalRootScanner.php +++ b/lib/private/Files/Cache/LocalRootScanner.php @@ -44,6 +44,6 @@ public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $loc private function shouldScanPath(string $path): bool { $path = trim($path, '/'); - return $path === '' || strpos($path, 'appdata_') === 0 || strpos($path, '__groupfolders') === 0; + return $path === '' || strpos($path, 'appdata_') === 0 || strpos($path, '__groupfolders') === 0 || strpos($this->storage->getMountPoint(), '/appdata_') === 0; } } diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index a18c4c2a138cb..3cd4223e99674 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -38,6 +38,7 @@ class SystemConfig { protected $sensitiveValues = [ 'instanceid' => true, 'datadirectory' => true, + 'appdataroot' => 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..34c31c7cbdc33 100644 --- a/tests/lib/Files/AppData/AppDataTest.php +++ b/tests/lib/Files/AppData/AppDataTest.php @@ -53,6 +53,11 @@ protected function setUp(): void { ->method('getValue') ->with('instanceid', null) ->willReturn('iid'); + + $this->systemConfig->expects($this->any()) + ->method('getValue') + ->with('appdataroot', null) + ->willReturn('/path'); } private function setupAppFolder() {