diff options
Diffstat (limited to 'lib/private/files/view.php')
-rw-r--r-- | lib/private/files/view.php | 356 |
1 files changed, 209 insertions, 147 deletions
diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 7dd83588ec6..2656e34cddf 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -15,15 +15,14 @@ * @author Michael Gapczynski <GapczynskiM@gmail.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <icewind@owncloud.com> - * @author Robin McCorkell <rmccorkell@karoshi.org.uk> + * @author Robin McCorkell <robin@mccorkell.me.uk> * @author Roeland Jago Douma <rullzer@owncloud.com> - * @author Roman Geber <rgeber@owncloudapps.com> * @author Sam Tuke <mail@samtuke.com> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Thomas Tanghus <thomas@tanghus.net> * @author Vincent Petry <pvince81@owncloud.com> * - * @copyright Copyright (c) 2015, ownCloud, Inc. + * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify @@ -44,13 +43,18 @@ namespace OC\Files; use Icewind\Streams\CallbackWrapper; -use OC\Files\Cache\Updater; use OC\Files\Mount\MoveableMount; +use OC\Files\Storage\Storage; +use OC\User\User; +use OCP\Constants; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\Files\ReservedWordException; +use OCP\Files\Storage\ILockingStorage; +use OCP\IUser; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; @@ -74,9 +78,6 @@ class View { /** @var string */ private $fakeRoot = ''; - /** @var \OC\Files\Cache\Updater */ - protected $updater; - /** * @var \OCP\Lock\ILockingProvider */ @@ -84,6 +85,10 @@ class View { private $lockingEnabled; + private $updaterEnabled = true; + + private $userManager; + /** * @param string $root * @throws \Exception If $root contains an invalid path @@ -97,9 +102,9 @@ class View { } $this->fakeRoot = $root; - $this->updater = new Updater($this); $this->lockingProvider = \OC::$server->getLockingProvider(); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); + $this->userManager = \OC::$server->getUserManager(); } public function getAbsolutePath($path = '/') { @@ -284,6 +289,35 @@ class View { } } + public function disableCacheUpdate() { + $this->updaterEnabled = false; + } + + public function enableCacheUpdate() { + $this->updaterEnabled = true; + } + + protected function writeUpdate(Storage $storage, $internalPath, $time = null) { + if ($this->updaterEnabled) { + if (is_null($time)) { + $time = time(); + } + $storage->getUpdater()->update($internalPath, $time); + } + } + + protected function removeUpdate(Storage $storage, $internalPath) { + if ($this->updaterEnabled) { + $storage->getUpdater()->remove($internalPath); + } + } + + protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { + if ($this->updaterEnabled) { + $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } + } + /** * @param string $path * @return bool|mixed @@ -567,7 +601,7 @@ class View { fclose($target); fclose($data); - $this->updater->update($path); + $this->writeUpdate($storage, $internalPath); $this->changeLock($path, ILockingProvider::LOCK_SHARED); @@ -687,28 +721,25 @@ class View { } else { $result = false; } - // moving a file/folder within the same mount point + // moving a file/folder within the same mount point } elseif ($storage1 == $storage2) { if ($storage1) { $result = $storage1->rename($internalPath1, $internalPath2); } else { $result = false; } - // moving a file/folder between storages (from $storage1 to $storage2) + // moving a file/folder between storages (from $storage1 to $storage2) } else { $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { // if it was a rename from a part file to a regular file it was a write and not a rename operation - $this->updater->update($path2); + + $this->writeUpdate($storage2, $internalPath2); } else if ($result) { if ($internalPath1 !== '') { // dont do a cache update for moved mounts - $this->updater->rename($path1, $path2); - } else { // only do etag propagation - $this->getUpdater()->getPropagator()->addChange($path1); - $this->getUpdater()->getPropagator()->addChange($path2); - $this->getUpdater()->getPropagator()->propagateChanges(); + $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); } } @@ -805,7 +836,7 @@ class View { $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); } - $this->updater->update($path2); + $this->writeUpdate($storage2, $internalPath2); $this->changeLock($path2, ILockingProvider::LOCK_SHARED); $lockTypePath2 = ILockingProvider::LOCK_SHARED; @@ -885,7 +916,7 @@ class View { $source = $this->fopen($path, 'r'); if ($source) { $extension = pathinfo($path, PATHINFO_EXTENSION); - $tmpFile = \OC_Helper::tmpFile($extension); + $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); file_put_contents($tmpFile, $source); return $tmpFile; } else { @@ -1015,6 +1046,7 @@ class View { } $run = $this->runHooks($hooks, $path); + /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { @@ -1036,13 +1068,13 @@ class View { } if (in_array('delete', $hooks) and $result) { - $this->updater->remove($path); + $this->removeUpdate($storage, $internalPath); } if (in_array('write', $hooks) and $operation !== 'fopen') { - $this->updater->update($path); + $this->writeUpdate($storage, $internalPath); } if (in_array('touch', $hooks)) { - $this->updater->update($path, $extraParam); + $this->writeUpdate($storage, $internalPath, $extraParam); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { @@ -1164,19 +1196,73 @@ class View { } /** + * @param string $ownerId + * @return \OC\User\User + */ + private function getUserObjectForOwner($ownerId) { + $owner = $this->userManager->get($ownerId); + if ($owner instanceof IUser) { + return $owner; + } else { + return new User($ownerId, null); + } + } + + /** + * Get file info from cache + * + * If the file is not in cached it will be scanned + * If the file has changed on storage the cache will be updated + * + * @param \OC\Files\Storage\Storage $storage + * @param string $internalPath + * @param string $relativePath + * @return array|bool + */ + private function getCacheEntry($storage, $internalPath, $relativePath) { + $cache = $storage->getCache($internalPath); + $data = $cache->get($internalPath); + $watcher = $storage->getWatcher($internalPath); + + try { + // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data + if (!$data || $data['size'] === -1) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + if (!$storage->file_exists($internalPath)) { + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + return false; + } + $scanner = $storage->getScanner($internalPath); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + $watcher->update($internalPath, $data); + $storage->getPropagator()->propagateChange($internalPath, time()); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } + } catch (LockedException $e) { + // if the file is locked we just use the old cache info + } + + return $data; + } + + /** * get the filesystem info * * @param string $path * @param boolean|string $includeMountPoints true to add mountpoint sizes, * 'ext' to add only ext storage mount point sizes. Defaults to true. * defaults to true - * @return \OC\Files\FileInfo|bool False if file does not exist + * @return \OC\Files\FileInfo|false False if file does not exist */ public function getFileInfo($path, $includeMountPoints = true) { $this->assertPathLength($path); - $data = array(); if (!Filesystem::isValidPath($path)) { - return $data; + return false; } if (Cache\Scanner::isPartialFile($path)) { return $this->getPartFileInfo($path); @@ -1187,48 +1273,27 @@ class View { $mount = Filesystem::getMountManager()->find($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($path); - $data = null; if ($storage) { - $cache = $storage->getCache($internalPath); + $data = $this->getCacheEntry($storage, $internalPath, $relativePath); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); + if (!$data instanceof ICacheEntry) { + return false; + } - try { - // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data - if (!$data) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - return false; - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if ($mount instanceof MoveableMount && $internalPath === '') { + $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } + $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); + $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + if ($data and isset($data['fileid'])) { - // upgrades from oc6 or lower might not have the permissions set in the file cache - if ($data['permissions'] === 0) { - $data['permissions'] = $storage->getPermissions($data['path']); - $cache->update($data['fileid'], array('permissions' => $data['permissions'])); - } if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mount points to the folder $extOnly = ($includeMountPoints === 'ext'); - $mountPoints = Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); + $mounts = Filesystem::getMountManager()->findIn($path); + foreach ($mounts as $mount) { + $subStorage = $mount->getStorage(); if ($subStorage) { // exclude shared storage ? if ($extOnly && $subStorage instanceof \OC\Files\Storage\Shared) { @@ -1236,22 +1301,16 @@ class View { } $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); - $data['size'] += isset($rootEntry['size']) ? $rootEntry['size'] : 0; + $info->addSubEntry($rootEntry, $mount->getMountPoint()); } } } } - } - if (!$data) { - return false; - } - if ($mount instanceof MoveableMount && $internalPath === '') { - $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; + return $info; } - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); - return new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + return false; } /** @@ -1263,9 +1322,8 @@ class View { */ public function getDirectoryContent($directory, $mimetype_filter = '') { $this->assertPathLength($directory); - $result = array(); if (!Filesystem::isValidPath($directory)) { - return $result; + return []; } $path = $this->getAbsolutePath($directory); $path = Filesystem::normalizePath($path); @@ -1276,50 +1334,26 @@ class View { $cache = $storage->getCache($internalPath); $user = \OC_User::getUser(); - /** - * @var \OC\Files\FileInfo[] $files - */ - $files = array(); + $data = $this->getCacheEntry($storage, $internalPath, $directory); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); - try { - if (!$data or $data['size'] === -1) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - return array(); - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } else if ($watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { + return []; } $folderId = $data['fileid']; $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter - foreach ($contents as $content) { - if ($content['permissions'] === 0) { - $content['permissions'] = $storage->getPermissions($content['path']); - $cache->update($content['fileid'], array('permissions' => $content['permissions'])); - } - // if sharing was disabled for the user we remove the share permissions - if (\OCP\Util::isSharingDisabledForUser()) { + $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); + /** + * @var \OC\Files\FileInfo[] $files + */ + $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { + if ($sharingDisabled) { $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } - $owner = \OC::$server->getUserManager()->get($storage->getOwner($content['path'])); - $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); - } + $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); + return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); + }, $contents); //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mounts = Filesystem::getMountManager()->findIn($path); @@ -1330,7 +1364,8 @@ class View { if ($subStorage) { $subCache = $subStorage->getCache(''); - if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) { + $rootEntry = $subCache->get(''); + if (!$rootEntry) { $subScanner = $subStorage->getScanner(''); try { $subScanner->scanFile(''); @@ -1348,17 +1383,17 @@ class View { ); continue; } + $rootEntry = $subCache->get(''); } - $rootEntry = $subCache->get(''); - if ($rootEntry) { + if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { $relativePath = trim(substr($mountPoint, $dirLength), '/'); if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; + if ($entry->getName() === $entryName) { + $entry->addSubEntry($rootEntry, $mountPoint); } } } else { //mountpoint in this folder, add an entry for it @@ -1387,7 +1422,7 @@ class View { $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } - $owner = \OC::$server->getUserManager()->get($subStorage->getOwner('')); + $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); } } @@ -1395,23 +1430,19 @@ class View { } if ($mimetype_filter) { - foreach ($files as $file) { + $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { if (strpos($mimetype_filter, '/')) { - if ($file['mimetype'] === $mimetype_filter) { - $result[] = $file; - } + return $file->getMimetype() === $mimetype_filter; } else { - if ($file['mimepart'] === $mimetype_filter) { - $result[] = $file; - } + return $file->getMimePart() === $mimetype_filter; } - } - } else { - $result = $files; + }); } - } - return $result; + return $files; + } else { + return []; + } } /** @@ -1543,10 +1574,15 @@ class View { * Get the owner for a file or folder * * @param string $path - * @return string + * @return string the user id of the owner + * @throws NotFoundException */ public function getOwner($path) { - return $this->basicOperation('getOwner', $path); + $info = $this->getFileInfo($path); + if (!$info) { + throw new NotFoundException($path . ' not found while trying to get owner'); + } + return $info->getOwner()->getUID(); } /** @@ -1692,13 +1728,6 @@ class View { } /** - * @return Updater - */ - public function getUpdater() { - return $this->updater; - } - - /** * @param string $path * @param string $fileName * @throws InvalidPathException @@ -1812,11 +1841,14 @@ class View { $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { try { - $mount->getStorage()->acquireLock( - $mount->getInternalPath($absolutePath), - $type, - $this->lockingProvider - ); + $storage = $mount->getStorage(); + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { + $storage->acquireLock( + $mount->getInternalPath($absolutePath), + $type, + $this->lockingProvider + ); + } } catch (\OCP\Lock\LockedException $e) { // rethrow with the a human-readable path throw new \OCP\Lock\LockedException( @@ -1850,11 +1882,14 @@ class View { $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { try { - $mount->getStorage()->changeLock( - $mount->getInternalPath($absolutePath), - $type, - $this->lockingProvider - ); + $storage = $mount->getStorage(); + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { + $storage->changeLock( + $mount->getInternalPath($absolutePath), + $type, + $this->lockingProvider + ); + } } catch (\OCP\Lock\LockedException $e) { // rethrow with the a human-readable path throw new \OCP\Lock\LockedException( @@ -1885,11 +1920,14 @@ class View { $mount = $this->getMountForLock($absolutePath, $lockMountPoint); if ($mount) { - $mount->getStorage()->releaseLock( - $mount->getInternalPath($absolutePath), - $type, - $this->lockingProvider - ); + $storage = $mount->getStorage(); + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { + $storage->releaseLock( + $mount->getInternalPath($absolutePath), + $type, + $this->lockingProvider + ); + } } return true; @@ -1989,4 +2027,28 @@ class View { } return ''; } + + /** + * @param string $filename + * @return array + * @throws \OC\User\NoUserException + * @throws NotFoundException + */ + public function getUidAndFilename($filename) { + $info = $this->getFileInfo($filename); + if (!$info instanceof \OCP\Files\FileInfo) { + throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); + } + $uid = $info->getOwner()->getUID(); + if ($uid != \OCP\User::getUser()) { + Filesystem::initMountPoints($uid); + $ownerView = new View('/' . $uid . '/files'); + try { + $filename = $ownerView->getPath($info['fileid']); + } catch (NotFoundException $e) { + throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); + } + } + return [$uid, $filename]; + } } |