diff options
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/Preview/BackgroundCleanupJob.php | 94 | ||||
-rw-r--r-- | lib/private/Preview/Storage/Root.php | 71 | ||||
-rw-r--r-- | lib/private/Server.php | 4 |
3 files changed, 151 insertions, 18 deletions
diff --git a/lib/private/Preview/BackgroundCleanupJob.php b/lib/private/Preview/BackgroundCleanupJob.php index 15bb6bd7188..e0546c4a11c 100644 --- a/lib/private/Preview/BackgroundCleanupJob.php +++ b/lib/private/Preview/BackgroundCleanupJob.php @@ -27,8 +27,9 @@ declare(strict_types=1); namespace OC\Preview; use OC\BackgroundJob\TimedJob; -use OC\Files\AppData\Factory; +use OC\Preview\Storage\Root; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\IMimeTypeLoader; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IDBConnection; @@ -38,28 +39,47 @@ class BackgroundCleanupJob extends TimedJob { /** @var IDBConnection */ private $connection; - /** @var Factory */ - private $appDataFactory; + /** @var Root */ + private $previewFolder; /** @var bool */ private $isCLI; + /** @var IMimeTypeLoader */ + private $mimeTypeLoader; + public function __construct(IDBConnection $connection, - Factory $appDataFactory, + Root $previewFolder, + IMimeTypeLoader $mimeTypeLoader, bool $isCLI) { // Run at most once an hour $this->setInterval(3600); $this->connection = $connection; - $this->appDataFactory = $appDataFactory; + $this->previewFolder = $previewFolder; $this->isCLI = $isCLI; + $this->mimeTypeLoader = $mimeTypeLoader; } public function run($argument) { - $previews = $this->appDataFactory->get('preview'); + foreach ($this->getDeletedFiles() as $fileId) { + try { + $preview = $this->previewFolder->getFolder((string)$fileId); + $preview->delete(); + } catch (NotFoundException $e) { + // continue + } catch (NotPermittedException $e) { + // continue + } + } + } - $previewFodlerId = $previews->getId(); + private function getDeletedFiles(): \Iterator { + yield from $this->getOldPreviewLocations(); + yield from $this->getNewPreviewLocations(); + } + private function getOldPreviewLocations(): \Iterator { $qb = $this->connection->getQueryBuilder(); $qb->select('a.name') ->from('filecache', 'a') @@ -69,7 +89,9 @@ class BackgroundCleanupJob extends TimedJob { ->where( $qb->expr()->isNull('b.fileid') )->andWhere( - $qb->expr()->eq('a.parent', $qb->createNamedParameter($previewFodlerId)) + $qb->expr()->eq('a.parent', $qb->createNamedParameter($this->previewFolder->getId())) + )->andWhere( + $qb->expr()->like('a.name', $qb->createNamedParameter('__%')) ); if (!$this->isCLI) { @@ -79,14 +101,54 @@ class BackgroundCleanupJob extends TimedJob { $cursor = $qb->execute(); while ($row = $cursor->fetch()) { - try { - $preview = $previews->getFolder($row['name']); - $preview->delete(); - } catch (NotFoundException $e) { - // continue - } catch (NotPermittedException $e) { - // continue - } + yield $row['name']; + } + + $cursor->closeCursor(); + } + + private function getNewPreviewLocations(): \Iterator { + $qb = $this->connection->getQueryBuilder(); + $qb->select('path', 'mimetype') + ->from('filecache') + ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($this->previewFolder->getId()))); + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === null) { + return []; + } + + /* + * This lovely like is the result of the way the new previews are stored + * We take the md5 of the name (fileid) and split the first 7 chars. That way + * there are not a gazillion files in the root of the preview appdata. + */ + $like = $this->connection->escapeLikeParameter($data['path']) . '/_/_/_/_/_/_/_/%'; + + $qb = $this->connection->getQueryBuilder(); + $qb->select('a.name') + ->from('filecache', 'a') + ->leftJoin('a', 'filecache', 'b', $qb->expr()->eq( + $qb->expr()->castColumn('a.name', IQueryBuilder::PARAM_INT), 'b.fileid' + )) + ->where( + $qb->expr()->andX( + $qb->expr()->isNull('b.fileid'), + $qb->expr()->like('a.path', $qb->createNamedParameter($like)), + $qb->expr()->eq('a.mimetype', $qb->createNamedParameter($this->mimeTypeLoader->getId('httpd/unix-directory'))) + ) + ); + + if (!$this->isCLI) { + $qb->setMaxResults(10); + } + + $cursor = $qb->execute(); + + while ($row = $cursor->fetch()) { + yield $row['name']; } $cursor->closeCursor(); diff --git a/lib/private/Preview/Storage/Root.php b/lib/private/Preview/Storage/Root.php new file mode 100644 index 00000000000..0e9d92a44c2 --- /dev/null +++ b/lib/private/Preview/Storage/Root.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Preview\Storage; + +use OC\Files\AppData\AppData; +use OC\SystemConfig; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFolder; + +class Root extends AppData { + public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig) { + parent::__construct($rootFolder, $systemConfig, 'preview'); + } + + + public function getFolder(string $name): ISimpleFolder { + $internalFolder = $this->getInternalFolder($name); + + try { + return parent::getFolder($internalFolder); + } catch (NotFoundException $e) { + /* + * The new folder structure is not found. + * Lets try the old one + */ + } + + return parent::getFolder($name); + } + + public function newFolder(string $name): ISimpleFolder { + $internalFolder = $this->getInternalFolder($name); + return parent::newFolder($internalFolder); + } + + /* + * Do not allow directory listing on this special root + * since it gets to big and time consuming + */ + public function getDirectoryListing(): array { + return []; + } + + private function getInternalFolder(string $name): string { + return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index cf4f8cc097b..4fe89e3098e 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -281,7 +281,7 @@ class Server extends ServerContainer implements IServerContainer { return new PreviewManager( $c->getConfig(), $c->getRootFolder(), - $c->getAppDataDir('preview'), + new \OC\Preview\Storage\Root($c->getRootFolder(), $c->getSystemConfig(), 'preview'), $c->getEventDispatcher(), $c->getGeneratorHelper(), $c->getSession()->get('user_id') @@ -291,7 +291,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(\OC\Preview\Watcher::class, function (Server $c) { return new \OC\Preview\Watcher( - $c->getAppDataDir('preview') + new \OC\Preview\Storage\Root($c->getRootFolder(), $c->getSystemConfig(), 'preview') ); }); |