]> source.dussan.org Git - nextcloud-server.git/commitdiff
Refactor Cache\Updater to work outside of the users home
authorRobin Appelman <icewind@owncloud.com>
Mon, 4 Aug 2014 12:29:46 +0000 (14:29 +0200)
committerRobin Appelman <icewind@owncloud.com>
Fri, 5 Sep 2014 09:53:23 +0000 (11:53 +0200)
lib/private/files/cache/changepropagator.php
lib/private/files/cache/updater.php
lib/private/files/view.php
tests/lib/files/cache/updater.php

index 30f2e675e2ee56ae31bc8f4fe28df4b009549ae8..2967c8f62597c8dc046504e026b8e62915f14fb0 100644 (file)
@@ -57,9 +57,11 @@ class ChangePropagator {
                         */
 
                        list($storage, $internalPath) = $this->view->resolvePath($parent);
-                       $cache = $storage->getCache();
-                       $id = $cache->getId($internalPath);
-                       $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
+                       if ($storage) {
+                               $cache = $storage->getCache();
+                               $id = $cache->getId($internalPath);
+                               $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
+                       }
                }
        }
 
index 0e5e07c587d1aeb5ccf7441b1d5bb333429b540a..a59dc7c08c64f17bedc2b7c0ed4f0f1aa83a79bb 100644 (file)
 namespace OC\Files\Cache;
 
 /**
- * listen to filesystem hooks and change the cache accordingly
+ * Update the cache and propagate changes
  */
 class Updater {
+       /**
+        * @var \OC\Files\View
+        */
+       protected $view;
 
        /**
-        * resolve a path to a storage and internal path
-        *
-        * @param string $path the relative path
-        * @return array an array consisting of the storage and the internal path
+        * @var \OC\Files\Cache\ChangePropagator
+        */
+       protected $propagator;
+
+       /**
+        * @param \OC\Files\View $view
         */
-       static public function resolvePath($path) {
-               $view = \OC\Files\Filesystem::getView();
-               return $view->resolvePath($path);
+       public function __construct($view) {
+               $this->view = $view;
+               $this->propagator = new ChangePropagator($view);
        }
 
        /**
-        * perform a write update
+        * Update the cache for $path
         *
-        * @param string $path the relative path of the file
+        * @param string $path
         */
-       static public function writeUpdate($path) {
+       public function update($path) {
                /**
                 * @var \OC\Files\Storage\Storage $storage
                 * @var string $internalPath
                 */
-               list($storage, $internalPath) = self::resolvePath($path);
+               list($storage, $internalPath) = $this->view->resolvePath($path);
                if ($storage) {
+                       $this->propagator->addChange($path);
                        $cache = $storage->getCache($internalPath);
                        $scanner = $storage->getScanner($internalPath);
                        $data = $scanner->scan($internalPath, Scanner::SCAN_SHALLOW);
+                       $this->correctParentStorageMtime($storage, $internalPath);
                        $cache->correctFolderSize($internalPath, $data);
-                       self::correctFolder($path, $storage->filemtime($internalPath));
-                       self::correctParentStorageMtime($storage, $internalPath);
                }
        }
 
        /**
-        * perform a delete update
+        * Remove $path from the cache
         *
-        * @param string $path the relative path of the file
+        * @param string $path
         */
-       static public function deleteUpdate($path) {
+       public function remove($path) {
                /**
                 * @var \OC\Files\Storage\Storage $storage
                 * @var string $internalPath
                 */
-               list($storage, $internalPath) = self::resolvePath($path);
+               list($storage, $internalPath) = $this->view->resolvePath($path);
                if ($storage) {
                        $parent = dirname($internalPath);
                        if ($parent === '.') {
                                $parent = '';
                        }
+                       $this->propagator->addChange($path);
                        $cache = $storage->getCache($internalPath);
                        $cache->remove($internalPath);
                        $cache->correctFolderSize($parent);
-                       self::correctFolder($path, time());
-                       self::correctParentStorageMtime($storage, $internalPath);
+                       $this->correctParentStorageMtime($storage, $internalPath);
                }
        }
 
        /**
-        * preform a rename update
-        *
-        * @param string $from the relative path of the source file
-        * @param string $to the relative path of the target file
+        * @param string $source
+        * @param string $target
         */
-       static public function renameUpdate($from, $to) {
+       public function rename($source, $target) {
                /**
-                * @var \OC\Files\Storage\Storage $storageFrom
-                * @var \OC\Files\Storage\Storage $storageTo
-                * @var string $internalFrom
-                * @var string $internalTo
+                * @var \OC\Files\Storage\Storage $sourceStorage
+                * @var \OC\Files\Storage\Storage $targetStorage
+                * @var string $sourceInternalPath
+                * @var string $targetInternalPath
                 */
-               list($storageFrom, $internalFrom) = self::resolvePath($from);
+               list($sourceStorage, $sourceInternalPath) = $this->view->resolvePath($source);
                // if it's a moved mountpoint we dont need to do anything
-               if ($internalFrom === '') {
+               if ($sourceInternalPath === '') {
                        return;
                }
-               list($storageTo, $internalTo) = self::resolvePath($to);
-               if ($storageFrom && $storageTo) {
-                       if ($storageFrom === $storageTo) {
-                               $cache = $storageFrom->getCache($internalFrom);
-                               $cache->move($internalFrom, $internalTo);
-                               if (pathinfo($internalFrom, PATHINFO_EXTENSION) !== pathinfo($internalTo, PATHINFO_EXTENSION)) {
-                                       // redetect mime type change
-                                       $mimeType = $storageTo->getMimeType($internalTo);
-                                       $fileId = $storageTo->getCache()->getId($internalTo);
-                                       $storageTo->getCache()->update($fileId, array('mimetype' => $mimeType));
+               list($targetStorage, $targetInternalPath) = $this->view->resolvePath($target);
+
+               if ($sourceStorage && $targetStorage) {
+                       if ($sourceStorage === $targetStorage) {
+                               $cache = $sourceStorage->getCache($sourceInternalPath);
+                               $cache->move($sourceInternalPath, $targetInternalPath);
+
+                               if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) {
+                                       // handle mime type change
+                                       $mimeType = $sourceStorage->getMimeType($targetInternalPath);
+                                       $fileId = $cache->getId($targetInternalPath);
+                                       $cache->update($fileId, array('mimetype' => $mimeType));
                                }
-                               $cache->correctFolderSize($internalFrom);
-                               $cache->correctFolderSize($internalTo);
-                               self::correctFolder($from, time());
-                               self::correctFolder($to, time());
-                               self::correctParentStorageMtime($storageFrom, $internalFrom);
-                               self::correctParentStorageMtime($storageTo, $internalTo);
-                       } else {
-                               self::deleteUpdate($from);
-                               self::writeUpdate($to);
-                       }
-               }
-       }
-
-       /**
-        * get file owner and path
-        * @param string $filename
-        * @return string[] with the owner's uid and the owner's path
-        */
-       private static function getUidAndFilename($filename) {
-
-               $uid = \OC\Files\Filesystem::getOwner($filename);
-               \OC\Files\Filesystem::initMountPoints($uid);
 
-               $filename = (strpos($filename, '/') !== 0) ? '/' . $filename : $filename;
-               if ($uid != \OCP\User::getUser()) {
-                       $info = \OC\Files\Filesystem::getFileInfo($filename);
-                       if (!$info) {
-                               return array($uid, '/files' . $filename);
+                               $cache->correctFolderSize($sourceInternalPath);
+                               $cache->correctFolderSize($targetInternalPath);
+                               $this->correctParentStorageMtime($sourceStorage, $sourceInternalPath);
+                               $this->correctParentStorageMtime($targetStorage, $targetInternalPath);
+                               $this->propagator->addChange($source);
+                               $this->propagator->addChange($target);
+                       } else {
+                               $this->remove($source);
+                               $this->update($target);
                        }
-                       $ownerView = new \OC\Files\View('/' . $uid . '/files');
-                       $filename = $ownerView->getPath($info['fileid']);
                }
-               return array($uid, '/files' . $filename);
        }
 
        /**
-        * Update the mtime and ETag of all parent folders
+        * propagate the updates to their parent folders
         *
-        * @param string $path
-        * @param string $time
+        * @param int $time (optional) the mtime to set for the folders, if not set the current time is used
         */
-       static public function correctFolder($path, $time) {
-               if ($path !== '' && $path !== '/' && $path !== '\\') {
-                       list($owner, $realPath) = self::getUidAndFilename(dirname($path));
-
-                       /**
-                        * @var \OC\Files\Storage\Storage $storage
-                        * @var string $internalPath
-                        */
-                       $view = new \OC\Files\View('/' . $owner);
-
-                       list($storage, $internalPath) = $view->resolvePath($realPath);
-                       $cache = $storage->getCache();
-                       $id = $cache->getId($internalPath);
-
-                       while ($id !== -1) {
-                               $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath)));
-                               if ($realPath !== '') {
-                                       $realPath = dirname($realPath);
-                                       if ($realPath === DIRECTORY_SEPARATOR) {
-                                               $realPath = "";
-                                       }
-                                       // check storage for parent in case we change the storage in this step
-                                       list($storage, $internalPath) = $view->resolvePath($realPath);
-                                       $cache = $storage->getCache();
-                                       $id = $cache->getId($internalPath);
-                               } else {
-                                       $id = -1;
-                               }
-                       }
-               }
+       public function propagate($time = null) {
+               $this->propagator->propagateChanges($time);
        }
 
        /**
@@ -177,52 +133,17 @@ class Updater {
         * @param \OC\Files\Storage\Storage $storage
         * @param string $internalPath
         */
-       static private function correctParentStorageMtime($storage, $internalPath) {
+       private function correctParentStorageMtime($storage, $internalPath) {
                $cache = $storage->getCache();
                $parentId = $cache->getParentId($internalPath);
                $parent = dirname($internalPath);
-
-               if ($parent === '.' || $parent === '/' || $parent === '\\') {
-                       $parent = '';
-               }
-
                if ($parentId != -1) {
                        $cache->update($parentId, array('storage_mtime' => $storage->filemtime($parent)));
                }
        }
 
-       /**
-        * @param array $params
-        */
-       static public function writeHook($params) {
-               self::writeUpdate($params['path']);
-       }
-
-       /**
-        * @param array $params
-        */
-       static public function touchHook($params) {
-               $path = $params['path'];
-               list($storage, $internalPath) = self::resolvePath($path);
-               $cache = $storage->getCache();
-               $id = $cache->getId($internalPath);
-               if ($id !== -1) {
-                       $cache->update($id, array('etag' => $storage->getETag($internalPath)));
-               }
-               self::writeUpdate($path);
-       }
-
-       /**
-        * @param array $params
-        */
-       static public function renameHook($params) {
-               self::renameUpdate($params['oldpath'], $params['newpath']);
-       }
-
-       /**
-        * @param array $params
-        */
-       static public function deleteHook($params) {
-               self::deleteUpdate($params['path']);
+       public function __destruct() {
+               // propagate any leftover changes
+               $this->propagator->propagateChanges();
        }
 }
index f97f846bb5a08229e526aa5c7d4aa62adf9af2de..439dbe068fc7dea5917f03300d649629994f4a4e 100644 (file)
@@ -31,8 +31,14 @@ use OC\Files\Mount\MoveableMount;
 class View {
        private $fakeRoot = '';
 
+       /**
+        * @var \OC\Files\Cache\Updater
+        */
+       protected $updater;
+
        public function __construct($root = '') {
                $this->fakeRoot = $root;
+               $this->updater = new Updater($this);
        }
 
        public function getAbsolutePath($path = '/') {
@@ -158,7 +164,10 @@ class View {
         * for \OC\Files\Storage\Storage via basicOperation().
         */
        public function mkdir($path) {
-               return $this->basicOperation('mkdir', $path, array('create', 'write'));
+               $result = $this->basicOperation('mkdir', $path, array('create', 'write'));
+               $this->updater->update($path);
+               $this->updater->propagate();
+               return $result;
        }
 
        /**
@@ -168,10 +177,10 @@ class View {
         * @param string $path relative to data/
         * @return boolean
         */
-       protected function removeMount($mount, $path){
+       protected function removeMount($mount, $path) {
                if ($mount instanceof MoveableMount) {
                        // cut of /user/files to get the relative path to data/user/files
-                       $pathParts= explode('/', $path, 4);
+                       $pathParts = explode('/', $path, 4);
                        $relPath = '/' . $pathParts[3];
                        \OC_Hook::emit(
                                Filesystem::CLASSNAME, "umount",
@@ -194,13 +203,16 @@ class View {
        }
 
        public function rmdir($path) {
-               $absolutePath= $this->getAbsolutePath($path);
+               $absolutePath = $this->getAbsolutePath($path);
                $mount = Filesystem::getMountManager()->find($absolutePath);
                if ($mount->getInternalPath($absolutePath) === '') {
                        return $this->removeMount($mount, $path);
                }
                if ($this->is_dir($path)) {
-                       return $this->basicOperation('rmdir', $path, array('delete'));
+                       $result = $this->basicOperation('rmdir', $path, array('delete'));
+                       $this->updater->remove($path);
+                       $this->updater->propagate();
+                       return $result;
                } else {
                        return false;
                }
@@ -310,9 +322,14 @@ class View {
                        if(!$this->file_exists($path)){
                                return false;
                        }
+                       if (is_null($mtime)) {
+                               $mtime = time();
+                       }
                        //if native touch fails, we emulate it by changing the mtime in the cache
                        $this->putFileInfo($path, array('mtime' => $mtime));
                }
+               $this->updater->update($path);
+               $this->updater->propagate($mtime);
                return true;
        }
 
@@ -374,10 +391,9 @@ class View {
                                        list ($count, $result) = \OC_Helper::streamCopy($data, $target);
                                        fclose($target);
                                        fclose($data);
+                                       $this->updater->update($path);
+                                       $this->updater->propagate();
                                        if ($this->shouldEmitHooks($path) && $result !== false) {
-                                               Updater::writeHook(array(
-                                                       'path' => $this->getHookPath($path)
-                                               ));
                                                $this->emit_file_hooks_post($exists, $path);
                                        }
                                        \OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count);
@@ -390,7 +406,10 @@ class View {
                        }
                } else {
                        $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
-                       return $this->basicOperation('file_put_contents', $path, $hooks, $data);
+                       $result = $this->basicOperation('file_put_contents', $path, $hooks, $data);
+                       $this->updater->update($path);
+                       $this->updater->propagate();
+                       return $result;
                }
        }
 
@@ -405,7 +424,10 @@ class View {
                if ($mount->getInternalPath($absolutePath) === '') {
                        return $this->removeMount($mount, $absolutePath);
                }
-               return $this->basicOperation('unlink', $path, array('delete'));
+               $result = $this->basicOperation('unlink', $path, array('delete'));
+               $this->updater->remove($path);
+               $this->updater->propagate();
+               return $result;
        }
 
        /**
@@ -495,15 +517,16 @@ class View {
                                                }
                                        }
                                }
-                               if ($this->shouldEmitHooks() && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
+                               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
-                                       Updater::writeHook(array('path' => $this->getHookPath($path2)));
-                                       $this->emit_file_hooks_post($exists, $path2);
+                                       $this->updater->update($path2);
+                                       $this->updater->propagate();
+                                       if ($this->shouldEmitHooks()) {
+                                               $this->emit_file_hooks_post($exists, $path2);
+                                       }
                                } elseif ($this->shouldEmitHooks() && $result !== false) {
-                                       Updater::renameHook(array(
-                                               'oldpath' => $this->getHookPath($path1),
-                                               'newpath' => $this->getHookPath($path2)
-                                       ));
+                                       $this->updater->rename($path1, $path2);
+                                       $this->updater->propagate();
                                        \OC_Hook::emit(
                                                Filesystem::CLASSNAME,
                                                Filesystem::signal_post_rename,
@@ -582,6 +605,8 @@ class View {
                                                fclose($target);
                                        }
                                }
+                               $this->updater->update($path2);
+                               $this->updater->propagate();
                                if ($this->shouldEmitHooks() && $result !== false) {
                                        \OC_Hook::emit(
                                                Filesystem::CLASSNAME,
@@ -814,16 +839,6 @@ class View {
                $run = true;
                if ($this->shouldEmitHooks($path)) {
                        foreach ($hooks as $hook) {
-                               // manually triger updater hooks to ensure they are called first
-                               if ($post) {
-                                       if ($hook == 'write') {
-                                               Updater::writeHook(array('path' => $path));
-                                       } elseif ($hook == 'touch') {
-                                               Updater::touchHook(array('path' => $path));
-                                       } else if ($hook == 'delete') {
-                                               Updater::deleteHook(array('path' => $path));
-                                       }
-                               }
                                if ($hook != 'read') {
                                        \OC_Hook::emit(
                                                Filesystem::CLASSNAME,
index b874e418100d27b107e568b62211d142442db9db..39c521c6ef63e08b9c545cca1ebcb6500a3ad8f0 100644 (file)
@@ -266,7 +266,6 @@ class Updater extends \PHPUnit_Framework_TestCase {
                $cachedData = $this->cache->get('foo.txt');
                $this->assertInternalType('string', $fooCachedData['etag']);
                $this->assertInternalType('string', $cachedData['etag']);
-               $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']);
                $this->assertGreaterThanOrEqual($fooCachedData['mtime'], $cachedData['mtime']);
 
                $cachedData = $this->cache->get('');