diff options
author | Michael Gapczynski <mtgap@owncloud.com> | 2013-06-04 18:19:08 -0400 |
---|---|---|
committer | Michael Gapczynski <mtgap@owncloud.com> | 2013-06-04 18:19:08 -0400 |
commit | cb9fe475c4481993d2c95b040f7abb653f0971e7 (patch) | |
tree | 874e56e888745676fe815c3c8f75e2741ce275af /lib/files | |
parent | 9ead7c4776bd512c2a50ab917b99dcdc37e8db68 (diff) | |
parent | c59ee99d932eb9a5d7df8fdfb6c318abfdef88b7 (diff) | |
download | nextcloud-server-cb9fe475c4481993d2c95b040f7abb653f0971e7.tar.gz nextcloud-server-cb9fe475c4481993d2c95b040f7abb653f0971e7.zip |
Merge branch 'master' into hook-improvements
Conflicts:
lib/files/view.php
Diffstat (limited to 'lib/files')
-rw-r--r-- | lib/files/cache/backgroundwatcher.php | 104 | ||||
-rw-r--r-- | lib/files/cache/cache.php | 151 | ||||
-rw-r--r-- | lib/files/cache/legacy.php | 22 | ||||
-rw-r--r-- | lib/files/cache/permissions.php | 35 | ||||
-rw-r--r-- | lib/files/cache/scanner.php | 46 | ||||
-rw-r--r-- | lib/files/cache/storage.php | 59 | ||||
-rw-r--r-- | lib/files/cache/updater.php | 9 | ||||
-rw-r--r-- | lib/files/cache/upgrade.php | 4 | ||||
-rw-r--r-- | lib/files/cache/watcher.php | 2 | ||||
-rw-r--r-- | lib/files/filesystem.php | 95 | ||||
-rw-r--r-- | lib/files/mount/manager.php | 120 | ||||
-rw-r--r-- | lib/files/mount/mount.php (renamed from lib/files/mount.php) | 106 | ||||
-rw-r--r-- | lib/files/storage/common.php | 28 | ||||
-rw-r--r-- | lib/files/storage/local.php | 437 | ||||
-rw-r--r-- | lib/files/storage/storage.php | 5 | ||||
-rw-r--r-- | lib/files/view.php | 106 |
16 files changed, 881 insertions, 448 deletions
diff --git a/lib/files/cache/backgroundwatcher.php b/lib/files/cache/backgroundwatcher.php new file mode 100644 index 00000000000..8933101577d --- /dev/null +++ b/lib/files/cache/backgroundwatcher.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +use \OC\Files\Mount; +use \OC\Files\Filesystem; + +class BackgroundWatcher { + static $folderMimetype = null; + + static private function getFolderMimetype() { + if (!is_null(self::$folderMimetype)) { + return self::$folderMimetype; + } + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?'); + $result = $query->execute(array('httpd/unix-directory')); + $row = $result->fetchRow(); + return $row['id']; + } + + static private function checkUpdate($id) { + $cacheItem = Cache::getById($id); + if (is_null($cacheItem)) { + return; + } + list($storageId, $internalPath) = $cacheItem; + $mounts = Filesystem::getMountByStorageId($storageId); + + if (count($mounts) === 0) { + //if the storage we need isn't mounted on default, try to find a user that has access to the storage + $permissionsCache = new Permissions($storageId); + $users = $permissionsCache->getUsers($id); + if (count($users) === 0) { + return; + } + Filesystem::initMountPoints($users[0]); + $mounts = Filesystem::getMountByStorageId($storageId); + if (count($mounts) === 0) { + return; + } + } + $storage = $mounts[0]->getStorage(); + $watcher = new Watcher($storage); + $watcher->checkUpdate($internalPath); + } + + /** + * get the next fileid in the cache + * + * @param int $previous + * @param bool $folder + * @return int + */ + static private function getNextFileId($previous, $folder) { + if ($folder) { + $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `fileid` > ? AND mimetype = ' . self::getFolderMimetype() . ' ORDER BY `fileid` ASC', 1); + } else { + $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `fileid` > ? AND mimetype != ' . self::getFolderMimetype() . ' ORDER BY `fileid` ASC', 1); + } + $result = $query->execute(array($previous)); + if ($row = $result->fetchRow()) { + return $row['fileid']; + } else { + return 0; + } + } + + static public function checkNext() { + // check both 1 file and 1 folder, this way new files are detected quicker because there are less folders than files usually + $previousFile = \OC_Appconfig::getValue('files', 'backgroundwatcher_previous_file', 0); + $previousFolder = \OC_Appconfig::getValue('files', 'backgroundwatcher_previous_folder', 0); + $nextFile = self::getNextFileId($previousFile, false); + $nextFolder = self::getNextFileId($previousFolder, true); + \OC_Appconfig::setValue('files', 'backgroundwatcher_previous_file', $nextFile); + \OC_Appconfig::setValue('files', 'backgroundwatcher_previous_folder', $nextFolder); + if ($nextFile > 0) { + self::checkUpdate($nextFile); + } + if ($nextFolder > 0) { + self::checkUpdate($nextFolder); + } + } + + static public function checkAll() { + $previous = 0; + $next = 1; + while ($next != 0) { + $next = self::getNextFileId($previous, true); + self::checkUpdate($next); + } + $previous = 0; + $next = 1; + while ($next != 0) { + $next = self::getNextFileId($previous, false); + self::checkUpdate($next); + } + } +} diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 71b70abe3fe..cae2e63e4dc 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -30,11 +30,9 @@ class Cache { private $storageId; /** - * numeric storage id - * - * @var int $numericId + * @var Storage $storageCache */ - private $numericId; + private $storageCache; private $mimetypeIds = array(); private $mimetypes = array(); @@ -52,19 +50,11 @@ class Cache { $this->storageId = md5($this->storageId); } - $query = \OC_DB::prepare('SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?'); - $result = $query->execute(array($this->storageId)); - if ($row = $result->fetchRow()) { - $this->numericId = $row['numeric_id']; - } else { - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*storages`(`id`) VALUES(?)'); - $query->execute(array($this->storageId)); - $this->numericId = \OC_DB::insertid('*PREFIX*storages'); - } + $this->storageCache = new Storage($storage); } public function getNumericStorageId() { - return $this->numericId; + return $this->storageCache->getNumericId(); } /** @@ -110,14 +100,17 @@ class Cache { */ public function get($file) { if (is_string($file) or $file == '') { + // normalize file + $file = $this->normalize($file); + $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($this->numericId, md5($file)); + $params = array($this->getNumericStorageId(), md5($file)); } else { //file id $where = 'WHERE `fileid` = ?'; $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); $data = $result->fetchRow(); @@ -133,9 +126,13 @@ class Cache { $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; $data['encrypted'] = (bool)$data['encrypted']; + $data['unencrypted_size'] = (int)$data['unencrypted_size']; $data['storage'] = $this->storageId; $data['mimetype'] = $this->getMimetype($data['mimetype']); $data['mimepart'] = $this->getMimetype($data['mimepart']); + if ($data['storage_mtime'] == 0) { + $data['storage_mtime'] = $data['mtime']; + } } return $data; @@ -151,13 +148,20 @@ class Cache { $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` - FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` + FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC'); + $result = $query->execute(array($fileId)); + if (\OC_DB::isError($result)) { + \OCP\Util::writeLog('cache', 'getFolderContents failed: ' . $result->getMessage(), \OCP\Util::ERROR); + } $files = $result->fetchAll(); foreach ($files as &$file) { $file['mimetype'] = $this->getMimetype($file['mimetype']); $file['mimepart'] = $this->getMimetype($file['mimepart']); + if ($file['storage_mtime'] == 0) { + $file['storage_mtime'] = $file['mtime']; + } } return $files; } else { @@ -178,6 +182,9 @@ class Cache { $this->update($id, $data); return $id; } else { + // normalize file + $file = $this->normalize($file); + if (isset($this->partial[$file])) { //add any saved partial data $data = array_merge($this->partial[$file], $data); unset($this->partial[$file]); @@ -198,14 +205,14 @@ class Cache { list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; - $params[] = $this->numericId; + $params[] = $this->getNumericStorageId(); $valuesPlaceholder = array_fill(0, count($queryParts), '?'); $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ')' . ' VALUES(' . implode(', ', $valuesPlaceholder) . ')'); $result = $query->execute($params); if (\OC_DB::isError($result)) { - \OCP\Util::writeLog('cache', 'Insert to cache failed: '.$result, \OCP\Util::ERROR); + \OCP\Util::writeLog('cache', 'Insert to cache failed: ' . $result->getMessage(), \OCP\Util::ERROR); } return (int)\OC_DB::insertid('*PREFIX*filecache'); @@ -219,6 +226,17 @@ class Cache { * @param array $data */ public function update($id, array $data) { + + if(isset($data['path'])) { + // normalize path + $data['path'] = $this->normalize($data['path']); + } + + if(isset($data['name'])) { + // normalize path + $data['name'] = $this->normalize($data['name']); + } + list($queryParts, $params) = $this->buildParts($data); $params[] = $id; @@ -234,7 +252,7 @@ class Cache { * @return array */ function buildParts(array $data) { - $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted', 'etag'); + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'unencrypted_size', 'etag'); $params = array(); $queryParts = array(); foreach ($data as $name => $value) { @@ -246,6 +264,11 @@ class Cache { $params[] = $this->getMimetypeId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; $value = $this->getMimetypeId($value); + } elseif ($name === 'storage_mtime') { + if (!isset($data['mtime'])) { + $params[] = $value; + $queryParts[] = '`mtime`'; + } } $params[] = $value; $queryParts[] = '`' . $name . '`'; @@ -261,10 +284,13 @@ class Cache { * @return int */ public function getId($file) { + // normalize file + $file = $this->normalize($file); + $pathHash = md5($file); $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $result = $query->execute(array($this->numericId, $pathHash)); + $result = $query->execute(array($this->getNumericStorageId(), $pathHash)); if ($row = $result->fetchRow()) { return $row['fileid']; @@ -328,19 +354,26 @@ class Cache { * @param string $target */ public function move($source, $target) { - $sourceId = $this->getId($source); - $newParentId = $this->getParentId($target); + // normalize source and target + $source = $this->normalize($source); + $target = $this->normalize($target); - //find all child entries - $query = \OC_DB::prepare('SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `path` LIKE ?'); - $result = $query->execute(array($source . '/%')); - $childEntries = $result->fetchAll(); - $sourceLength = strlen($source); - $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ? WHERE `fileid` = ?'); + $sourceData = $this->get($source); + $sourceId = $sourceData['fileid']; + $newParentId = $this->getParentId($target); - foreach ($childEntries as $child) { - $targetPath = $target . substr($child['path'], $sourceLength); - $query->execute(array($targetPath, md5($targetPath), $child['fileid'])); + if ($sourceData['mimetype'] === 'httpd/unix-directory') { + //find all child entries + $query = \OC_DB::prepare('SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path` LIKE ?'); + $result = $query->execute(array($this->getNumericStorageId(), $source . '/%')); + $childEntries = $result->fetchAll(); + $sourceLength = strlen($source); + $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ? WHERE `fileid` = ?'); + + foreach ($childEntries as $child) { + $targetPath = $target . substr($child['path'], $sourceLength); + $query->execute(array($targetPath, md5($targetPath), $child['fileid'])); + } } $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ?, `name` = ?, `parent` =?' @@ -353,7 +386,7 @@ class Cache { */ public function clear() { $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage = ?'); - $query->execute(array($this->numericId)); + $query->execute(array($this->getNumericStorageId())); $query = \OC_DB::prepare('DELETE FROM `*PREFIX*storages` WHERE id = ?'); $query->execute(array($this->storageId)); @@ -365,9 +398,15 @@ class Cache { * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { + // normalize file + $file = $this->normalize($file); + $pathHash = md5($file); $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $result = $query->execute(array($this->numericId, $pathHash)); + $result = $query->execute(array($this->getNumericStorageId(), $pathHash)); + if( \OC_DB::isError($result)) { + \OCP\Util::writeLog('cache', 'get status failed: ' . $result->getMessage(), \OCP\Util::ERROR); + } if ($row = $result->fetchRow()) { if ((int)$row['size'] === -1) { return self::SHALLOW; @@ -390,11 +429,15 @@ class Cache { * @return array of file data */ public function search($pattern) { + + // normalize pattern + $pattern = $this->normalize($pattern); + $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `storage` = ?' ); - $result = $query->execute(array($pattern, $this->numericId)); + $result = $query->execute(array($pattern, $this->getNumericStorageId())); $files = array(); while ($row = $result->fetchRow()) { $row['mimetype'] = $this->getMimetype($row['mimetype']); @@ -417,11 +460,11 @@ class Cache { $where = '`mimepart` = ?'; } $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?' ); $mimetype = $this->getMimetypeId($mimetype); - $result = $query->execute(array($mimetype, $this->numericId)); + $result = $query->execute(array($mimetype, $this->getNumericStorageId())); $files = array(); while ($row = $result->fetchRow()) { $row['mimetype'] = $this->getMimetype($row['mimetype']); @@ -440,7 +483,7 @@ class Cache { $this->calculateFolderSize($path); if ($path !== '') { $parent = dirname($path); - if ($parent === '.') { + if ($parent === '.' or $parent === '/') { $parent = ''; } $this->correctFolderSize($parent); @@ -459,7 +502,7 @@ class Cache { return 0; } $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); - $result = $query->execute(array($id, $this->numericId)); + $result = $query->execute(array($id, $this->getNumericStorageId())); $totalSize = 0; $hasChilds = 0; while ($row = $result->fetchRow()) { @@ -486,7 +529,7 @@ class Cache { */ public function getAll() { $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'); - $result = $query->execute(array($this->numericId)); + $result = $query->execute(array($this->getNumericStorageId())); $ids = array(); while ($row = $result->fetchRow()) { $ids[] = $row['fileid']; @@ -505,8 +548,11 @@ class Cache { */ public function getIncomplete() { $query = \OC_DB::prepare('SELECT `path` FROM `*PREFIX*filecache`' - . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC LIMIT 1'); - $result = $query->execute(array($this->numericId)); + . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC',1); + $result = $query->execute(array($this->getNumericStorageId())); + if (\OC_DB::isError($result)) { + \OCP\Util::writeLog('cache', 'getIncomplete failed: ' . $result->getMessage(), \OCP\Util::ERROR); + } if ($row = $result->fetchRow()) { return $row['path']; } else { @@ -517,6 +563,7 @@ class Cache { /** * get the storage id of the storage for a file and the internal path of the file * + * @param int $id * @return array, first element holding the storage id, second the path */ static public function getById($id) { @@ -529,12 +576,20 @@ class Cache { return null; } - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?'); - $result = $query->execute(array($numericId)); - if ($row = $result->fetchRow()) { - return array($row['id'], $path); + if ($id = Storage::getStorageId($numericId)) { + return array($id, $path); } else { return null; } } + + /** + * normalize the given path + * @param $path + * @return string + */ + public function normalize($path) { + + return \OC_Util::normalizeUnicode($path); + } } diff --git a/lib/files/cache/legacy.php b/lib/files/cache/legacy.php index 9556a2639a3..b8e2548639b 100644 --- a/lib/files/cache/legacy.php +++ b/lib/files/cache/legacy.php @@ -80,7 +80,7 @@ class Legacy { } $result = $query->execute(array($path)); $data = $result->fetchRow(); - $data['etag'] = $this->getEtag($data['path']); + $data['etag'] = $this->getEtag($data['path'], $data['user']); return $data; } @@ -90,12 +90,24 @@ class Legacy { * @param type $path * @return string */ - function getEtag($path) { + function getEtag($path, $user = null) { static $query = null; - list(, $user, , $relativePath) = explode('/', $path, 4); - if (is_null($relativePath)) { + + $pathDetails = explode('/', $path, 4); + if((!$user) && !isset($pathDetails[1])) { + //no user!? Too odd, return empty string. + return ''; + } else if(!$user) { + //guess user from path, if no user passed. + $user = $pathDetails[1]; + } + + if(!isset($pathDetails[3]) || is_null($pathDetails[3])) { $relativePath = ''; + } else { + $relativePath = $pathDetails[3]; } + if(is_null($query)){ $query = \OC_DB::prepare('SELECT `propertyvalue` FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = \'{DAV:}getetag\''); } @@ -118,7 +130,7 @@ class Legacy { $result = $query->execute(array($id)); $data = $result->fetchAll(); foreach ($data as $i => $item) { - $data[$i]['etag'] = $this->getEtag($item['path']); + $data[$i]['etag'] = $this->getEtag($item['path'], $item['user']); } return $data; } diff --git a/lib/files/cache/permissions.php b/lib/files/cache/permissions.php index a5c9c144054..29c30b0f36c 100644 --- a/lib/files/cache/permissions.php +++ b/lib/files/cache/permissions.php @@ -86,6 +86,26 @@ class Permissions { } /** + * get the permissions for all files in a folder + * + * @param int $parentId + * @param string $user + * @return int[] + */ + public function getDirectoryPermissions($parentId, $user) { + $query = \OC_DB::prepare('SELECT `*PREFIX*permissions`.`fileid`, `permissions` + FROM `*PREFIX*permissions` INNER JOIN `*PREFIX*filecache` ON `*PREFIX*permissions`.`fileid` = `*PREFIX*filecache`.`fileid` + WHERE `*PREFIX*filecache`.`parent` = ? AND `*PREFIX*permissions`.`user` = ?'); + + $result = $query->execute(array($parentId, $user)); + $filePermissions = array(); + while ($row = $result->fetchRow()) { + $filePermissions[$row['fileid']] = $row['permissions']; + } + return $filePermissions; + } + + /** * remove the permissions for a file * * @param int $fileId @@ -107,4 +127,19 @@ class Permissions { $query->execute(array($fileId, $user)); } } + + /** + * get the list of users which have permissions stored for a file + * + * @param int $fileId + */ + public function getUsers($fileId) { + $query = \OC_DB::prepare('SELECT `user` FROM `*PREFIX*permissions` WHERE `fileid` = ?'); + $result = $query->execute(array($fileId)); + $users = array(); + while ($row = $result->fetchRow()) { + $users[] = $row['user']; + } + return $users; + } } diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index f019d4fc608..8f9a7921956 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -51,6 +51,7 @@ class Scanner { $data['size'] = $this->storage->filesize($path); } $data['etag'] = $this->storage->getETag($path); + $data['storage_mtime'] = $data['mtime']; return $data; } @@ -62,31 +63,36 @@ class Scanner { * @return array with metadata of the scanned file */ public function scanFile($file, $checkExisting = false) { - if (!self::isIgnoredFile($file)) { + if ( ! self::isPartialFile($file) + and ! \OC\Files\Filesystem::isFileBlacklisted($file) + ) { \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { if ($file) { $parent = dirname($file); - if ($parent === '.') { + if ($parent === '.' or $parent === '/') { $parent = ''; } if (!$this->cache->inCache($parent)) { $this->scanFile($parent); } } - if($cacheData = $this->cache->get($file)) { + $newData = $data; + if ($cacheData = $this->cache->get($file)) { + if ($checkExisting && $data['size'] === -1) { + $data['size'] = $cacheData['size']; + } if ($data['mtime'] === $cacheData['mtime'] && $data['size'] === $cacheData['size']) { $data['etag'] = $cacheData['etag']; } + // Only update metadata that has changed + $newData = array_diff($data, $cacheData); } - if ($checkExisting and $cacheData) { - if ($data['size'] === -1) { - $data['size'] = $cacheData['size']; - } + if (!empty($newData)) { + $this->cache->put($file, $newData); } - $this->cache->put($file, $data); } return $data; } @@ -113,7 +119,7 @@ class Scanner { \OC_DB::beginTransaction(); while ($file = readdir($dh)) { $child = ($path) ? $path . '/' . $file : $file; - if (!$this->isIgnoredDir($file)) { + if (!\OC\Files\Filesystem::isIgnoredDir($file)) { $data = $this->scanFile($child, $recursive === self::SCAN_SHALLOW); if ($data) { if ($data['size'] === -1) { @@ -148,28 +154,14 @@ class Scanner { } /** - * @brief check if the directory should be ignored when scanning - * NOTE: the special directories . and .. would cause never ending recursion - * @param String $dir - * @return boolean - */ - private function isIgnoredDir($dir) { - if ($dir === '.' || $dir === '..') { - return true; - } - return false; - } - /** * @brief check if the file should be ignored when scanning * NOTE: files with a '.part' extension are ignored as well! * prevents unfinished put requests to be scanned * @param String $file * @return boolean */ - public static function isIgnoredFile($file) { - if (pathinfo($file, PATHINFO_EXTENSION) === 'part' - || \OC\Files\Filesystem::isFileBlacklisted($file) - ) { + public static function isPartialFile($file) { + if (pathinfo($file, PATHINFO_EXTENSION) === 'part') { return true; } return false; @@ -179,9 +171,11 @@ class Scanner { * walk over any folders that are not fully scanned yet and scan them */ public function backgroundScan() { - while (($path = $this->cache->getIncomplete()) !== false) { + $lastPath = null; + while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) { $this->scan($path); $this->cache->correctFolderSize($path); + $lastPath = $path; } } } diff --git a/lib/files/cache/storage.php b/lib/files/cache/storage.php new file mode 100644 index 00000000000..72de376798c --- /dev/null +++ b/lib/files/cache/storage.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +/** + * Class Storage + * + * cache storage specific data + * + * @package OC\Files\Cache + */ +class Storage { + private $storageId; + private $numericId; + + /** + * @param \OC\Files\Storage\Storage|string $storage + */ + public function __construct($storage) { + if ($storage instanceof \OC\Files\Storage\Storage) { + $this->storageId = $storage->getId(); + } else { + $this->storageId = $storage; + } + if (strlen($this->storageId) > 64) { + $this->storageId = md5($this->storageId); + } + + $query = \OC_DB::prepare('SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?'); + $result = $query->execute(array($this->storageId)); + if ($row = $result->fetchRow()) { + $this->numericId = $row['numeric_id']; + } else { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*storages`(`id`) VALUES(?)'); + $query->execute(array($this->storageId)); + $this->numericId = \OC_DB::insertid('*PREFIX*storages'); + } + } + + public function getNumericId() { + return $this->numericId; + } + + public static function getStorageId($numericId) { + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?'); + $result = $query->execute(array($numericId)); + if ($row = $result->fetchRow()) { + return $row['id']; + } else { + return null; + } + } +} diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index 92a16d9d9b6..417a47f3830 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -132,7 +132,14 @@ class Updater { * @param array $params */ static public function touchHook($params) { - self::writeUpdate($params['path']); + $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); } /** diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 797f4e6ba8c..ca044ba81de 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -127,6 +127,10 @@ class Upgrade { * @return array */ function getNewData($data) { + //Make sure there is a path, otherwise we can do nothing. + if(!isset($data['path'])) { + return false; + } $newData = $data; /** * @var \OC\Files\Storage\Storage $storage diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php index 31059ec7f56..8bfd4602f3a 100644 --- a/lib/files/cache/watcher.php +++ b/lib/files/cache/watcher.php @@ -43,7 +43,7 @@ class Watcher { */ public function checkUpdate($path) { $cachedEntry = $this->cache->get($path); - if ($this->storage->hasUpdated($path, $cachedEntry['mtime'])) { + if ($this->storage->hasUpdated($path, $cachedEntry['storage_mtime'])) { if ($this->storage->is_dir($path)) { $this->scanner->scan($path, Scanner::SCAN_SHALLOW); } else { diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index c8e62956f19..d3fddf8c421 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -34,6 +34,11 @@ const FREE_SPACE_UNKNOWN = -2; const FREE_SPACE_UNLIMITED = -3; class Filesystem { + /** + * @var Mount\Manager $mounts + */ + private static $mounts; + public static $loaded = false; /** * @var \OC\Files\View $defaultInstance @@ -147,7 +152,10 @@ class Filesystem { * @return string */ static public function getMountPoint($path) { - $mount = Mount::find($path); + if (!self::$mounts) { + \OC_Util::setupFS(); + } + $mount = self::$mounts->find($path); if ($mount) { return $mount->getMountPoint(); } else { @@ -162,8 +170,11 @@ class Filesystem { * @return string[] */ static public function getMountPoints($path) { + if (!self::$mounts) { + \OC_Util::setupFS(); + } $result = array(); - $mounts = Mount::findIn($path); + $mounts = self::$mounts->findIn($path); foreach ($mounts as $mount) { $result[] = $mount->getMountPoint(); } @@ -177,18 +188,46 @@ class Filesystem { * @return \OC\Files\Storage\Storage */ public static function getStorage($mountPoint) { - $mount = Mount::find($mountPoint); + if (!self::$mounts) { + \OC_Util::setupFS(); + } + $mount = self::$mounts->find($mountPoint); return $mount->getStorage(); } /** + * @param $id + * @return Mount\Mount[] + */ + public static function getMountByStorageId($id) { + if (!self::$mounts) { + \OC_Util::setupFS(); + } + return self::$mounts->findByStorageId($id); + } + + /** + * @param $id + * @return Mount\Mount[] + */ + public static function getMountByNumericId($id) { + if (!self::$mounts) { + \OC_Util::setupFS(); + } + return self::$mounts->findByNumericId($id); + } + + /** * resolve a path to a storage and internal path * * @param string $path * @return array consisting of the storage and the internal path */ static public function resolvePath($path) { - $mount = Mount::find($path); + if (!self::$mounts) { + \OC_Util::setupFS(); + } + $mount = self::$mounts->find($path); if ($mount) { return array($mount->getStorage(), $mount->getInternalPath($path)); } else { @@ -202,6 +241,10 @@ class Filesystem { } self::$defaultInstance = new View($root); + if (!self::$mounts) { + self::$mounts = new Mount\Manager(); + } + //load custom mount config self::initMountPoints($user); @@ -210,6 +253,12 @@ class Filesystem { return true; } + static public function initMounts() { + if (!self::$mounts) { + self::$mounts = new Mount\Manager(); + } + } + /** * Initialize system and personal mount points for a user * @@ -287,9 +336,9 @@ class Filesystem { } /** - * fill in the correct values for $user, and $password placeholders + * fill in the correct values for $user * - * @param string $input + * @param string $user * @param string $input * @return string */ @@ -311,6 +360,7 @@ class Filesystem { */ static public function tearDown() { self::clearMounts(); + self::$defaultInstance = null; } /** @@ -327,7 +377,9 @@ class Filesystem { * clear all mounts and storage backends */ public static function clearMounts() { - Mount::clear(); + if (self::$mounts) { + self::$mounts->clear(); + } } /** @@ -338,7 +390,11 @@ class Filesystem { * @param string $mountpoint */ static public function mount($class, $arguments, $mountpoint) { - new Mount($class, $mountpoint, $arguments); + if (!self::$mounts) { + \OC_Util::setupFS(); + } + $mount = new Mount\Mount($class, $mountpoint, $arguments); + self::$mounts->addMount($mount); } /** @@ -423,6 +479,19 @@ class Filesystem { } /** + * @brief check if the directory should be ignored when scanning + * NOTE: the special directories . and .. would cause never ending recursion + * @param String $dir + * @return boolean + */ + static public function isIgnoredDir($dir) { + if ($dir === '.' || $dir === '..') { + return true; + } + return false; + } + + /** * following functions are equivalent to their php builtin equivalents for arguments/return values. */ static public function mkdir($path) { @@ -585,9 +654,8 @@ class Filesystem { $path = substr($path, 0, -1); } //normalize unicode if possible - if (class_exists('Normalizer')) { - $path = \Normalizer::normalize($path); - } + $path = \OC_Util::normalizeUnicode($path); + return $path; } @@ -665,9 +733,4 @@ class Filesystem { } } -\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); -\OC_Hook::connect('OC_Filesystem', 'post_touch', '\OC\Files\Cache\Updater', 'touchHook'); -\OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); -\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); - \OC_Util::setupFS(); diff --git a/lib/files/mount/manager.php b/lib/files/mount/manager.php new file mode 100644 index 00000000000..25a5fe241cc --- /dev/null +++ b/lib/files/mount/manager.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Mount; + +use \OC\Files\Filesystem; + +class Manager { + /** + * @var Mount[] + */ + private $mounts = array(); + + /** + * @param Mount $mount + */ + public function addMount($mount) { + $this->mounts[$mount->getMountPoint()] = $mount; + } + + /** + * Find the mount for $path + * + * @param $path + * @return Mount + */ + public function find($path) { + \OC_Util::setupFS(); + $path = $this->formatPath($path); + if (isset($this->mounts[$path])) { + return $this->mounts[$path]; + } + + \OC_Hook::emit('OC_Filesystem', 'get_mountpoint', array('path' => $path)); + $foundMountPoint = ''; + $mountPoints = array_keys($this->mounts); + foreach ($mountPoints as $mountpoint) { + if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { + $foundMountPoint = $mountpoint; + } + } + if (isset($this->mounts[$foundMountPoint])) { + return $this->mounts[$foundMountPoint]; + } else { + return null; + } + } + + /** + * Find all mounts in $path + * + * @param $path + * @return Mount[] + */ + public function findIn($path) { + \OC_Util::setupFS(); + $path = $this->formatPath($path); + $result = array(); + $pathLength = strlen($path); + $mountPoints = array_keys($this->mounts); + foreach ($mountPoints as $mountPoint) { + if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { + $result[] = $this->mounts[$mountPoint]; + } + } + return $result; + } + + public function clear() { + $this->mounts = array(); + } + + /** + * Find mounts by storage id + * + * @param string $id + * @return Mount[] + */ + public function findByStorageId($id) { + \OC_Util::setupFS(); + if (strlen($id) > 64) { + $id = md5($id); + } + $result = array(); + foreach ($this->mounts as $mount) { + if ($mount->getStorageId() === $id) { + $result[] = $mount; + } + } + return $result; + } + + /** + * Find mounts by numeric storage id + * + * @param string $id + * @return Mount + */ + public function findByNumericId($id) { + $storageId = \OC\Files\Cache\Storage::getStorageId($id); + return $this->findByStorageId($storageId); + } + + /** + * @param string $path + * @return string + */ + private function formatPath($path) { + $path = Filesystem::normalizePath($path); + if (strlen($path) > 1) { + $path .= '/'; + } + return $path; + } +} diff --git a/lib/files/mount.php b/lib/files/mount/mount.php index 59c98e9dcc8..69b8285ab4c 100644 --- a/lib/files/mount.php +++ b/lib/files/mount/mount.php @@ -6,13 +6,12 @@ * See the COPYING-README file. */ -namespace OC\Files; +namespace OC\Files\Mount; + +use \OC\Files\Filesystem; class Mount { - /** - * @var Mount[] - */ - static private $mounts = array(); + /** * @var \OC\Files\Storage\Storage $storage @@ -33,7 +32,7 @@ class Mount { $arguments = array(); } - $mountpoint = self::formatPath($mountpoint); + $mountpoint = $this->formatPath($mountpoint); if ($storage instanceof \OC\Files\Storage\Storage) { $this->class = get_class($storage); $this->storage = $storage; @@ -46,8 +45,6 @@ class Mount { $this->arguments = $arguments; } $this->mountPoint = $mountpoint; - - self::$mounts[$this->mountPoint] = $this; } /** @@ -58,6 +55,8 @@ class Mount { } /** + * create the storage that is mounted + * * @return \OC\Files\Storage\Storage */ private function createStorage() { @@ -121,100 +120,11 @@ class Mount { * @param string $path * @return string */ - private static function formatPath($path) { + private function formatPath($path) { $path = Filesystem::normalizePath($path); if (strlen($path) > 1) { $path .= '/'; } return $path; } - - /** - * Find the mount for $path - * - * @param $path - * @return Mount - */ - public static function find($path) { - $path = self::formatPath($path); - if (isset(self::$mounts[$path])) { - return self::$mounts[$path]; - } - - \OC_Hook::emit('OC_Filesystem', 'get_mountpoint', array('path' => $path)); - $foundMountPoint = ''; - $mountPoints = array_keys(self::$mounts); - foreach ($mountPoints as $mountpoint) { - if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { - $foundMountPoint = $mountpoint; - } - } - if (isset(self::$mounts[$foundMountPoint])) { - return self::$mounts[$foundMountPoint]; - } else { - return null; - } - } - - /** - * Find all mounts in $path - * - * @param $path - * @return Mount[] - */ - public static function findIn($path) { - $path = self::formatPath($path); - $result = array(); - $pathLength = strlen($path); - $mountPoints = array_keys(self::$mounts); - foreach ($mountPoints as $mountPoint) { - if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { - $result[] = self::$mounts[$mountPoint]; - } - } - return $result; - } - - public static function clear() { - self::$mounts = array(); - } - - /** - * Find mounts by storage id - * - * @param string $id - * @return Mount[] - */ - public static function findByStorageId($id) { - if (strlen($id) > 64) { - $id = md5($id); - } - $result = array(); - foreach (self::$mounts as $mount) { - if ($mount->getStorageId() === $id) { - $result[] = $mount; - } - } - return $result; - } - - /** - * Find mounts by numeric storage id - * - * @param string $id - * @return Mount - */ - public static function findByNumericId($id) { - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?'); - $result = $query->execute(array($id))->fetchOne(); - if ($result) { - $id = $result; - foreach (self::$mounts as $mount) { - if ($mount->getStorageId() === $id) { - return $mount; - } - } - } - return false; - } } diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 38fe5e546f6..3da13ac4df0 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -25,6 +25,7 @@ abstract class Common implements \OC\Files\Storage\Storage { private $scanner; private $permissioncache; private $watcher; + private $storageCache; public function __construct($parameters) { } @@ -137,27 +138,21 @@ abstract class Common implements \OC\Files\Storage\Storage { */ public function deleteAll($directory, $empty = false) { $directory = trim($directory, '/'); - - if (!$this->file_exists(\OCP\USER::getUser() . '/' . $directory) - || !$this->is_dir(\OCP\USER::getUser() . '/' . $directory) - ) { - return false; - } elseif (!$this->isReadable(\OCP\USER::getUser() . '/' . $directory)) { + if (!$this->is_dir($directory) || !$this->isReadable($directory)) { return false; } else { - $directoryHandle = $this->opendir(\OCP\USER::getUser() . '/' . $directory); + $directoryHandle = $this->opendir($directory); while ($contents = readdir($directoryHandle)) { - if ($contents != '.' && $contents != '..') { - $path = $directory . "/" . $contents; + if (!\OC\Files\Filesystem::isIgnoredDir($contents)) { + $path = $directory . '/' . $contents; if ($this->is_dir($path)) { $this->deleteAll($path); } else { - $this->unlink(\OCP\USER::getUser() . '/' . $path); // TODO: make unlink use same system path as is_dir + $this->unlink($path); } } } - //$this->closedir( $directoryHandle ); // TODO: implement closedir in OC_FSV - if ($empty == false) { + if ($empty === false) { if (!$this->rmdir($directory)) { return false; } @@ -300,6 +295,13 @@ abstract class Common implements \OC\Files\Storage\Storage { return $this->watcher; } + public function getStorageCache(){ + if (!isset($this->storageCache)) { + $this->storageCache = new \OC\Files\Cache\Storage($this); + } + return $this->storageCache; + } + /** * get the owner of a path * @@ -361,7 +363,7 @@ abstract class Common implements \OC\Files\Storage\Storage { * get the free space in the storage * * @param $path - * return int + * @return int */ public function free_space($path) { return \OC\Files\FREE_SPACE_UNKNOWN; diff --git a/lib/files/storage/local.php b/lib/files/storage/local.php index 81e32587fca..d684905bf9a 100644 --- a/lib/files/storage/local.php +++ b/lib/files/storage/local.php @@ -14,252 +14,277 @@ if (\OC_Util::runningOnWindows()) { } } else { -/** - * for local filestore, we only have to map the paths - */ -class Local extends \OC\Files\Storage\Common{ - protected $datadir; - public function __construct($arguments) { - $this->datadir=$arguments['datadir']; - if(substr($this->datadir, -1)!=='/') { - $this->datadir.='/'; + /** + * for local filestore, we only have to map the paths + */ + class Local extends \OC\Files\Storage\Common { + protected $datadir; + + public function __construct($arguments) { + $this->datadir = $arguments['datadir']; + if (substr($this->datadir, -1) !== '/') { + $this->datadir .= '/'; + } } - } - public function __destruct() { - } - public function getId(){ - return 'local::'.$this->datadir; - } - public function mkdir($path) { - return @mkdir($this->datadir.$path); - } - public function rmdir($path) { - return @rmdir($this->datadir.$path); - } - public function opendir($path) { - return opendir($this->datadir.$path); - } - public function is_dir($path) { - if(substr($path, -1)=='/') { - $path=substr($path, 0, -1); + + public function __destruct() { } - return is_dir($this->datadir.$path); - } - public function is_file($path) { - return is_file($this->datadir.$path); - } - public function stat($path) { - $fullPath = $this->datadir . $path; - $statResult = stat($fullPath); - if ($statResult['size'] < 0) { - $size = self::getFileSizeFromOS($fullPath); - $statResult['size'] = $size; - $statResult[7] = $size; + public function getId() { + return 'local::' . $this->datadir; } - return $statResult; - } - public function filetype($path) { - $filetype=filetype($this->datadir.$path); - if($filetype=='link') { - $filetype=filetype(realpath($this->datadir.$path)); + + public function mkdir($path) { + return @mkdir($this->datadir . $path); } - return $filetype; - } - public function filesize($path) { - if($this->is_dir($path)) { - return 0; - }else{ - $fullPath = $this->datadir . $path; - $fileSize = filesize($fullPath); - if ($fileSize < 0) { - return self::getFileSizeFromOS($fullPath); + + public function rmdir($path) { + return @rmdir($this->datadir . $path); + } + + public function opendir($path) { + return opendir($this->datadir . $path); + } + + public function is_dir($path) { + if (substr($path, -1) == '/') { + $path = substr($path, 0, -1); } + return is_dir($this->datadir . $path); + } - return $fileSize; + public function is_file($path) { + return is_file($this->datadir . $path); } - } - public function isReadable($path) { - return is_readable($this->datadir.$path); - } - public function isUpdatable($path) { - return is_writable($this->datadir.$path); - } - public function file_exists($path) { - return file_exists($this->datadir.$path); - } - public function filemtime($path) { - return filemtime($this->datadir.$path); - } - public function touch($path, $mtime=null) { - // sets the modification time of the file to the given value. - // If mtime is nil the current time is set. - // note that the access time of the file always changes to the current time. - if($this->file_exists($path) and !$this->isUpdatable($path)) { - return false; + + public function stat($path) { + $fullPath = $this->datadir . $path; + $statResult = stat($fullPath); + + if ($statResult['size'] < 0) { + $size = self::getFileSizeFromOS($fullPath); + $statResult['size'] = $size; + $statResult[7] = $size; + } + return $statResult; } - if(!is_null($mtime)) { - $result=touch( $this->datadir.$path, $mtime ); - }else{ - $result=touch( $this->datadir.$path); + + public function filetype($path) { + $filetype = filetype($this->datadir . $path); + if ($filetype == 'link') { + $filetype = filetype(realpath($this->datadir . $path)); + } + return $filetype; } - if( $result ) { - clearstatcache( true, $this->datadir.$path ); + + public function filesize($path) { + if ($this->is_dir($path)) { + return 0; + } else { + $fullPath = $this->datadir . $path; + $fileSize = filesize($fullPath); + if ($fileSize < 0) { + return self::getFileSizeFromOS($fullPath); + } + + return $fileSize; + } } - return $result; - } - public function file_get_contents($path) { - return file_get_contents($this->datadir.$path); - } - public function file_put_contents($path, $data) {//trigger_error("$path = ".var_export($path, 1)); - return file_put_contents($this->datadir.$path, $data); - } - public function unlink($path) { - return $this->delTree($path); - } - public function rename($path1, $path2) { - if (!$this->isUpdatable($path1)) { - \OC_Log::write('core', 'unable to rename, file is not writable : '.$path1, \OC_Log::ERROR); - return false; + public function isReadable($path) { + return is_readable($this->datadir . $path); } - if(! $this->file_exists($path1)) { - \OC_Log::write('core', 'unable to rename, file does not exists : '.$path1, \OC_Log::ERROR); - return false; + + public function isUpdatable($path) { + return is_writable($this->datadir . $path); } - if($return=rename($this->datadir.$path1, $this->datadir.$path2)) { + public function file_exists($path) { + return file_exists($this->datadir . $path); } - return $return; - } - public function copy($path1, $path2) { - if($this->is_dir($path2)) { - if(!$this->file_exists($path2)) { - $this->mkdir($path2); + + public function filemtime($path) { + return filemtime($this->datadir . $path); + } + + public function touch($path, $mtime = null) { + // sets the modification time of the file to the given value. + // If mtime is nil the current time is set. + // note that the access time of the file always changes to the current time. + if ($this->file_exists($path) and !$this->isUpdatable($path)) { + return false; } - $source=substr($path1, strrpos($path1, '/')+1); - $path2.=$source; + if (!is_null($mtime)) { + $result = touch($this->datadir . $path, $mtime); + } else { + $result = touch($this->datadir . $path); + } + if ($result) { + clearstatcache(true, $this->datadir . $path); + } + + return $result; } - return copy($this->datadir.$path1, $this->datadir.$path2); - } - public function fopen($path, $mode) { - if($return=fopen($this->datadir.$path, $mode)) { - switch($mode) { - case 'r': - break; - case 'r+': - case 'w+': - case 'x+': - case 'a+': - break; - case 'w': - case 'x': - case 'a': - break; + + public function file_get_contents($path) { + return file_get_contents($this->datadir . $path); + } + + public function file_put_contents($path, $data) { //trigger_error("$path = ".var_export($path, 1)); + return file_put_contents($this->datadir . $path, $data); + } + + public function unlink($path) { + return $this->delTree($path); + } + + public function rename($path1, $path2) { + if (!$this->isUpdatable($path1)) { + \OC_Log::write('core', 'unable to rename, file is not writable : ' . $path1, \OC_Log::ERROR); + return false; + } + if (!$this->file_exists($path1)) { + \OC_Log::write('core', 'unable to rename, file does not exists : ' . $path1, \OC_Log::ERROR); + return false; } + + if ($return = rename($this->datadir . $path1, $this->datadir . $path2)) { + } + return $return; } - return $return; - } - public function getMimeType($path) { - if($this->isReadable($path)) { - return \OC_Helper::getMimeType($this->datadir . $path); - }else{ - return false; + public function copy($path1, $path2) { + if ($this->is_dir($path2)) { + if (!$this->file_exists($path2)) { + $this->mkdir($path2); + } + $source = substr($path1, strrpos($path1, '/') + 1); + $path2 .= $source; + } + return copy($this->datadir . $path1, $this->datadir . $path2); } - } - private function delTree($dir) { - $dirRelative=$dir; - $dir=$this->datadir.$dir; - if (!file_exists($dir)) return true; - if (!is_dir($dir) || is_link($dir)) return unlink($dir); - foreach (scandir($dir) as $item) { - if ($item == '.' || $item == '..') continue; - if(is_file($dir.'/'.$item)) { - if(unlink($dir.'/'.$item)) { + public function fopen($path, $mode) { + if ($return = fopen($this->datadir . $path, $mode)) { + switch ($mode) { + case 'r': + break; + case 'r+': + case 'w+': + case 'x+': + case 'a+': + break; + case 'w': + case 'x': + case 'a': + break; } - }elseif(is_dir($dir.'/'.$item)) { - if (!$this->delTree($dirRelative. "/" . $item)) { - return false; - }; } + return $return; } - if($return=rmdir($dir)) { + + public function getMimeType($path) { + if ($this->isReadable($path)) { + return \OC_Helper::getMimeType($this->datadir . $path); + } else { + return false; + } } - return $return; - } - private static function getFileSizeFromOS($fullPath) { - $name = strtolower(php_uname('s')); - // Windows OS: we use COM to access the filesystem - if (strpos($name, 'win') !== false) { - if (class_exists('COM')) { - $fsobj = new \COM("Scripting.FileSystemObject"); - $f = $fsobj->GetFile($fullPath); - return $f->Size; + private function delTree($dir) { + $dirRelative = $dir; + $dir = $this->datadir . $dir; + if (!file_exists($dir)) return true; + if (!is_dir($dir) || is_link($dir)) return unlink($dir); + foreach (scandir($dir) as $item) { + if ($item == '.' || $item == '..') continue; + if (is_file($dir . '/' . $item)) { + if (unlink($dir . '/' . $item)) { + } + } elseif (is_dir($dir . '/' . $item)) { + if (!$this->delTree($dirRelative . "/" . $item)) { + return false; + }; + } } - } else if (strpos($name, 'bsd') !== false) { - if (\OC_Helper::is_function_enabled('exec')) { - return (float)exec('stat -f %z ' . escapeshellarg($fullPath)); + if ($return = rmdir($dir)) { } - } else if (strpos($name, 'linux') !== false) { - if (\OC_Helper::is_function_enabled('exec')) { - return (float)exec('stat -c %s ' . escapeshellarg($fullPath)); + return $return; + } + + private static function getFileSizeFromOS($fullPath) { + $name = strtolower(php_uname('s')); + // Windows OS: we use COM to access the filesystem + if (strpos($name, 'win') !== false) { + if (class_exists('COM')) { + $fsobj = new \COM("Scripting.FileSystemObject"); + $f = $fsobj->GetFile($fullPath); + return $f->Size; + } + } else if (strpos($name, 'bsd') !== false) { + if (\OC_Helper::is_function_enabled('exec')) { + return (float)exec('stat -f %z ' . escapeshellarg($fullPath)); + } + } else if (strpos($name, 'linux') !== false) { + if (\OC_Helper::is_function_enabled('exec')) { + return (float)exec('stat -c %s ' . escapeshellarg($fullPath)); + } + } else { + \OC_Log::write('core', + 'Unable to determine file size of "' . $fullPath . '". Unknown OS: ' . $name, + \OC_Log::ERROR); } - } else { - \OC_Log::write('core', - 'Unable to determine file size of "'.$fullPath.'". Unknown OS: '.$name, - \OC_Log::ERROR); + + return 0; } - return 0; - } + public function hash($path, $type, $raw = false) { + return hash_file($type, $this->datadir . $path, $raw); + } - public function hash($path, $type, $raw=false) { - return hash_file($type, $this->datadir.$path, $raw); - } + public function free_space($path) { + $space = @disk_free_space($this->datadir . $path); + if ($space === false) { + return \OC\Files\FREE_SPACE_UNKNOWN; + } + return $space; + } - public function free_space($path) { - $space = @disk_free_space($this->datadir.$path); - if($space === false){ - return \OC\Files\FREE_SPACE_UNKNOWN; + public function search($query) { + return $this->searchInDir($query); } - return $space; - } - public function search($query) { - return $this->searchInDir($query); - } - public function getLocalFile($path) { - return $this->datadir.$path; - } - public function getLocalFolder($path) { - return $this->datadir.$path; - } + public function getLocalFile($path) { + return $this->datadir . $path; + } - protected function searchInDir($query, $dir='') { - $files=array(); - foreach (scandir($this->datadir.$dir) as $item) { - if ($item == '.' || $item == '..') continue; - if(strstr(strtolower($item), strtolower($query))!==false) { - $files[]=$dir.'/'.$item; - } - if(is_dir($this->datadir.$dir.'/'.$item)) { - $files=array_merge($files, $this->searchInDir($query, $dir.'/'.$item)); + public function getLocalFolder($path) { + return $this->datadir . $path; + } + + protected function searchInDir($query, $dir = '') { + $files = array(); + foreach (scandir($this->datadir . $dir) as $item) { + if ($item == '.' || $item == '..') continue; + if (strstr(strtolower($item), strtolower($query)) !== false) { + $files[] = $dir . '/' . $item; + } + if (is_dir($this->datadir . $dir . '/' . $item)) { + $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item)); + } } + return $files; } - return $files; - } - /** - * check if a file or folder has been updated since $time - * @param string $path - * @param int $time - * @return bool - */ - public function hasUpdated($path, $time) { - return $this->filemtime($path)>$time; + /** + * check if a file or folder has been updated since $time + * + * @param string $path + * @param int $time + * @return bool + */ + public function hasUpdated($path, $time) { + return $this->filemtime($path) > $time; + } } } -} diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index 1da82da2163..c96caebf4af 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -329,6 +329,11 @@ interface Storage { public function getWatcher($path = ''); /** + * @return \OC\Files\Cache\Storage + */ + public function getStorageCache(); + + /** * get the ETag for a file or folder * * @param string $path diff --git a/lib/files/view.php b/lib/files/view.php index d2d1a99818b..e2fc8d965b8 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -245,14 +245,17 @@ class View { if (!is_null($mtime) and !is_numeric($mtime)) { $mtime = strtotime($mtime); } - + $hooks = array('touch'); - + if (!$this->file_exists($path)) { $hooks[] = 'write'; } - - return $this->basicOperation('touch', $path, $hooks, $mtime); + $result = $this->basicOperation('touch', $path, $hooks, $mtime); + if (!$result) { //if native touch fails, we emulate it by changing the mtime in the cache + $this->putFileInfo($path, array('mtime' => $mtime)); + } + return true; } public function file_get_contents($path) { @@ -263,11 +266,13 @@ class View { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (\OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) - && Filesystem::isValidPath($path)) { + and Filesystem::isValidPath($path) + and !Filesystem::isFileBlacklisted($path) + ) { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); $run = true; - if ($this->fakeRoot == Filesystem::getRoot() && ! Cache\Scanner::isIgnoredFile($path) ) { + if ($this->fakeRoot == Filesystem::getRoot() && !Cache\Scanner::isPartialFile($path)) { if (!$exists) { \OC_Hook::emit( Filesystem::CLASSNAME, @@ -295,7 +300,7 @@ class View { list ($count, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); - if ($this->fakeRoot == Filesystem::getRoot() && ! Cache\Scanner::isIgnoredFile($path) && $result !== false) { + if ($this->fakeRoot == Filesystem::getRoot() && !Cache\Scanner::isPartialFile($path) && $result !== false) { if (!$exists) { \OC_Hook::emit( Filesystem::CLASSNAME, @@ -335,8 +340,12 @@ class View { $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); - if (\OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) - and Filesystem::isValidPath($path2)) { + if ( + \OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) + and Filesystem::isValidPath($path2) + and Filesystem::isValidPath($path1) + and !Filesystem::isFileBlacklisted($path2) + ) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); @@ -344,7 +353,7 @@ class View { return false; } $run = true; - if ($this->fakeRoot == Filesystem::getRoot()) { + if ($this->fakeRoot == Filesystem::getRoot() && !Cache\Scanner::isPartialFile($path1)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_rename, array( @@ -362,19 +371,28 @@ class View { list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); if ($storage) { $result = $storage->rename($internalPath1, $internalPath2); + \OC_FileProxy::runPostProxies('rename', $absolutePath1, $absolutePath2); } else { $result = false; } } else { - $source = $this->fopen($path1 . $postFix1, 'r'); - $target = $this->fopen($path2 . $postFix2, 'w'); - list($count, $result) = \OC_Helper::streamCopy($source, $target); - list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); - if ($result !== false) { - $storage1->unlink($internalPath1); + if ($this->is_dir($path1)) { + $result = $this->copy($path1, $path2); + if ($result === true) { + list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); + $result = $storage1->deleteAll($internalPath1); + } + } else { + $source = $this->fopen($path1 . $postFix1, 'r'); + $target = $this->fopen($path2 . $postFix2, 'w'); + list($count, $result) = \OC_Helper::streamCopy($source, $target); + if ($result !== false) { + list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); + $storage1->unlink($internalPath1); + } } } - if ($this->fakeRoot == Filesystem::getRoot() && $result !== false) { + if ($this->fakeRoot == Filesystem::getRoot() && !Cache\Scanner::isPartialFile($path1) && $result !== false) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_rename, @@ -398,7 +416,12 @@ class View { $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); - if (\OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and Filesystem::isValidPath($path2)) { + if ( + \OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) + and Filesystem::isValidPath($path2) + and Filesystem::isValidPath($path1) + and !Filesystem::isFileBlacklisted($path2) + ) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); @@ -450,9 +473,18 @@ class View { $result = false; } } else { - $source = $this->fopen($path1 . $postFix1, 'r'); - $target = $this->fopen($path2 . $postFix2, 'w'); - list($count, $result) = \OC_Helper::streamCopy($source, $target); + if ($this->is_dir($path1) && ($dh = $this->opendir($path1))) { + $result = $this->mkdir($path2); + while ($file = readdir($dh)) { + if (!Filesystem::isIgnoredDir($file)) { + $result = $this->copy($path1 . '/' . $file, $path2 . '/' . $file); + } + } + } else { + $source = $this->fopen($path1 . $postFix1, 'r'); + $target = $this->fopen($path2 . $postFix2, 'w'); + list($count, $result) = \OC_Helper::streamCopy($source, $target); + } } if ($this->fakeRoot == Filesystem::getRoot() && $result !== false) { \OC_Hook::emit( @@ -600,7 +632,10 @@ class View { private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); - if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and Filesystem::isValidPath($path)) { + if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) + and Filesystem::isValidPath($path) + and !Filesystem::isFileBlacklisted($path) + ) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; @@ -629,7 +664,7 @@ class View { private function runHooks($hooks, $path, $post = false) { $prefix = ($post) ? 'post_' : ''; $run = true; - if (Filesystem::$loaded and $this->fakeRoot == Filesystem::getRoot() && ! Cache\Scanner::isIgnoredFile($path) ) { + if (Filesystem::$loaded and $this->fakeRoot == Filesystem::getRoot()) { foreach ($hooks as $hook) { if ($hook != 'read') { \OC_Hook::emit( @@ -726,6 +761,9 @@ class View { $data['permissions'] = $permissions; } } + + $data = \OC_FileProxy::runPostProxies('getFileInfo', $path, $data); + return $data; } @@ -761,18 +799,18 @@ class View { } $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user); $ids = array(); foreach ($files as $i => $file) { $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $ids[] = $file['fileid']; - $permissions = $permissionsCache->get($file['fileid'], $user); - if ($permissions === -1) { - $permissions = $storage->getPermissions($file['path']); - $permissionsCache->set($file['fileid'], $user, $permissions); + if (!isset($permissions[$file['fileid']])) { + $permissions[$file['fileid']] = $storage->getPermissions($file['path']); + $permissionsCache->set($file['fileid'], $user, $permissions[$file['fileid']]); } - $files[$i]['permissions'] = $permissions; + $files[$i]['permissions'] = $permissions[$file['fileid']]; } //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders @@ -933,11 +971,11 @@ class View { } /** - * Get the owner for a file or folder - * - * @param string $path - * @return string - */ + * Get the owner for a file or folder + * + * @param string $path + * @return string + */ public function getOwner($path) { return $this->basicOperation('getOwner', $path); } @@ -971,7 +1009,7 @@ class View { */ public function getPath($id) { list($storage, $internalPath) = Cache\Cache::getById($id); - $mounts = Mount::findByStorageId($storage); + $mounts = Filesystem::getMountByStorageId($storage); foreach ($mounts as $mount) { /** * @var \OC\Files\Mount $mount |