diff options
author | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-24 19:45:43 +0200 |
---|---|---|
committer | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-24 21:37:35 +0200 |
commit | dedf392751e1b27163f9dd49b2a54f410727c823 (patch) | |
tree | 2d4d0265d7c574caed62dfe25cd718d79141be04 /lib/private/Files/ObjectStore | |
parent | dc5c570d7caa3095a3cb4ab2b5a51bf772d7de4c (diff) | |
download | nextcloud-server-dedf392751e1b27163f9dd49b2a54f410727c823.tar.gz nextcloud-server-dedf392751e1b27163f9dd49b2a54f410727c823.zip |
Move \OC\Files to PSR-4
Diffstat (limited to 'lib/private/Files/ObjectStore')
-rw-r--r-- | lib/private/Files/ObjectStore/HomeObjectStoreStorage.php | 68 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/NoopScanner.php | 79 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/ObjectStoreStorage.php | 407 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/Swift.php | 154 |
4 files changed, 708 insertions, 0 deletions
diff --git a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php new file mode 100644 index 00000000000..6a330e2dab3 --- /dev/null +++ b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php @@ -0,0 +1,68 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Files\ObjectStore; + +use OC\User\User; + +class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IHomeStorage { + + /** + * The home user storage requires a user object to create a unique storage id + * @param array $params + */ + public function __construct($params) { + if ( ! isset($params['user']) || ! $params['user'] instanceof User) { + throw new \Exception('missing user object in parameters'); + } + $this->user = $params['user']; + parent::__construct($params); + } + + public function getId () { + return 'object::user:' . $this->user->getUID(); + } + + /** + * get the owner of a path + * + * @param string $path The path to get the owner + * @return false|string uid + */ + public function getOwner($path) { + if (is_object($this->user)) { + return $this->user->getUID(); + } + return false; + } + + /** + * @param string $path, optional + * @return \OC\User\User + */ + public function getUser($path = null) { + return $this->user; + } + + +}
\ No newline at end of file diff --git a/lib/private/Files/ObjectStore/NoopScanner.php b/lib/private/Files/ObjectStore/NoopScanner.php new file mode 100644 index 00000000000..f5316175ecf --- /dev/null +++ b/lib/private/Files/ObjectStore/NoopScanner.php @@ -0,0 +1,79 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Files\ObjectStore; +use \OC\Files\Cache\Scanner; +use \OC\Files\Storage\Storage; + +class NoopScanner extends Scanner { + + public function __construct(Storage $storage) { + //we don't need the storage, so do nothing here + } + + /** + * scan a single file and store it in the cache + * + * @param string $file + * @param int $reuseExisting + * @param int $parentId + * @param array|null $cacheData existing data in the cache for the file to be scanned + * @return array an array of metadata of the scanned file + */ + public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { + return array(); + } + + /** + * scan a folder and all it's children + * + * @param string $path + * @param bool $recursive + * @param int $reuse + * @return array with the meta data of the scanned file or folder + */ + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { + return array(); + } + + /** + * scan all the files and folders in a folder + * + * @param string $path + * @param bool $recursive + * @param int $reuse + * @param array $folderData existing cache data for the folder to be scanned + * @return int the size of the scanned folder or -1 if the size is unknown at this stage + */ + protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null, $lock = true) { + return 0; + } + + /** + * walk over any folders that are not fully scanned yet and scan them + */ + public function backgroundScan() { + //noop + } +} diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php new file mode 100644 index 00000000000..8c643dbdcc7 --- /dev/null +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -0,0 +1,407 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Files\ObjectStore; + +use Icewind\Streams\IteratorDirectory; +use OC\Files\Cache\CacheEntry; +use OCP\Files\ObjectStore\IObjectStore; + +class ObjectStoreStorage extends \OC\Files\Storage\Common { + + /** + * @var array + */ + private static $tmpFiles = array(); + /** + * @var \OCP\Files\ObjectStore\IObjectStore $objectStore + */ + protected $objectStore; + /** + * @var string $id + */ + protected $id; + /** + * @var \OC\User\User $user + */ + protected $user; + + public function __construct($params) { + if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) { + $this->objectStore = $params['objectstore']; + } else { + throw new \Exception('missing IObjectStore instance'); + } + if (isset($params['storageid'])) { + $this->id = 'object::store:' . $params['storageid']; + } else { + $this->id = 'object::store:' . $this->objectStore->getStorageId(); + } + //initialize cache with root directory in cache + if (!$this->is_dir('/')) { + $this->mkdir('/'); + } + } + + public function mkdir($path) { + $path = $this->normalizePath($path); + + if ($this->file_exists($path)) { + return false; + } + + $mTime = time(); + $data = [ + 'mimetype' => 'httpd/unix-directory', + 'size' => 0, + 'mtime' => $mTime, + 'storage_mtime' => $mTime, + 'permissions' => \OCP\Constants::PERMISSION_ALL, + ]; + if ($path === '') { + //create root on the fly + $data['etag'] = $this->getETag(''); + $this->getCache()->put('', $data); + return true; + } else { + // if parent does not exist, create it + $parent = $this->normalizePath(dirname($path)); + $parentType = $this->filetype($parent); + if ($parentType === false) { + if (!$this->mkdir($parent)) { + // something went wrong + return false; + } + } else if ($parentType === 'file') { + // parent is a file + return false; + } + // finally create the new dir + $mTime = time(); // update mtime + $data['mtime'] = $mTime; + $data['storage_mtime'] = $mTime; + $data['etag'] = $this->getETag($path); + $this->getCache()->put($path, $data); + return true; + } + } + + /** + * @param string $path + * @return string + */ + private function normalizePath($path) { + $path = trim($path, '/'); + //FIXME why do we sometimes get a path like 'files//username'? + $path = str_replace('//', '/', $path); + + // dirname('/folder') returns '.' but internally (in the cache) we store the root as '' + if (!$path || $path === '.') { + $path = ''; + } + + return $path; + } + + /** + * Object Stores use a NoopScanner because metadata is directly stored in + * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere. + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner + * @return \OC\Files\ObjectStore\NoopScanner + */ + public function getScanner($path = '', $storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->scanner)) { + $this->scanner = new NoopScanner($storage); + } + return $this->scanner; + } + + public function getId() { + return $this->id; + } + + public function rmdir($path) { + $path = $this->normalizePath($path); + + if (!$this->is_dir($path)) { + return false; + } + + $this->rmObjects($path); + + $this->getCache()->remove($path); + + return true; + } + + private function rmObjects($path) { + $children = $this->getCache()->getFolderContents($path); + foreach ($children as $child) { + if ($child['mimetype'] === 'httpd/unix-directory') { + $this->rmObjects($child['path']); + } else { + $this->unlink($child['path']); + } + } + } + + public function unlink($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + + if ($stat && isset($stat['fileid'])) { + if ($stat['mimetype'] === 'httpd/unix-directory') { + return $this->rmdir($path); + } + try { + $this->objectStore->deleteObject($this->getURN($stat['fileid'])); + } catch (\Exception $ex) { + if ($ex->getCode() !== 404) { + \OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } else { + //removing from cache is ok as it does not exist in the objectstore anyway + } + } + $this->getCache()->remove($path); + return true; + } + return false; + } + + public function stat($path) { + $path = $this->normalizePath($path); + $cacheEntry = $this->getCache()->get($path); + if ($cacheEntry instanceof CacheEntry) { + return $cacheEntry->getData(); + } else { + return false; + } + } + + /** + * Override this method if you need a different unique resource identifier for your object storage implementation. + * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users. + * You may need a mapping table to store your URN if it cannot be generated from the fileid. + * + * @param int $fileId the fileid + * @return null|string the unified resource name used to identify the object + */ + protected function getURN($fileId) { + if (is_numeric($fileId)) { + return 'urn:oid:' . $fileId; + } + return null; + } + + public function opendir($path) { + $path = $this->normalizePath($path); + + try { + $files = array(); + $folderContents = $this->getCache()->getFolderContents($path); + foreach ($folderContents as $file) { + $files[] = $file['name']; + } + + return IteratorDirectory::wrap($files); + } catch (\Exception $e) { + \OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR); + return false; + } + } + + public function filetype($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + if ($stat) { + if ($stat['mimetype'] === 'httpd/unix-directory') { + return 'dir'; + } + return 'file'; + } else { + return false; + } + } + + public function fopen($path, $mode) { + $path = $this->normalizePath($path); + + switch ($mode) { + case 'r': + case 'rb': + $stat = $this->stat($path); + if (is_array($stat)) { + try { + return $this->objectStore->readObject($this->getURN($stat['fileid'])); + } catch (\Exception $ex) { + \OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } + } else { + return false; + } + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path, '.') !== false) { + $ext = substr($path, strrpos($path, '.')); + } else { + $ext = ''; + } + $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tmpFiles[$tmpFile] = $path; + + return fopen('close://' . $tmpFile, $mode); + } + return false; + } + + public function file_exists($path) { + $path = $this->normalizePath($path); + return (bool)$this->stat($path); + } + + public function rename($source, $target) { + $source = $this->normalizePath($source); + $target = $this->normalizePath($target); + $this->remove($target); + $this->getCache()->move($source, $target); + $this->touch(dirname($target)); + return true; + } + + public function getMimeType($path) { + $path = $this->normalizePath($path); + $stat = $this->stat($path); + if (is_array($stat)) { + return $stat['mimetype']; + } else { + return false; + } + } + + public function touch($path, $mtime = null) { + if (is_null($mtime)) { + $mtime = time(); + } + + $path = $this->normalizePath($path); + $dirName = dirname($path); + $parentExists = $this->is_dir($dirName); + if (!$parentExists) { + return false; + } + + $stat = $this->stat($path); + if (is_array($stat)) { + // update existing mtime in db + $stat['mtime'] = $mtime; + $this->getCache()->update($stat['fileid'], $stat); + } else { + $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); + // create new file + $stat = array( + 'etag' => $this->getETag($path), + 'mimetype' => $mimeType, + 'size' => 0, + 'mtime' => $mtime, + 'storage_mtime' => $mtime, + 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, + ); + $fileId = $this->getCache()->put($path, $stat); + try { + //read an empty file from memory + $this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r')); + } catch (\Exception $ex) { + $this->getCache()->remove($path); + \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR); + return false; + } + } + return true; + } + + public function writeBack($tmpFile) { + if (!isset(self::$tmpFiles[$tmpFile])) { + return; + } + + $path = self::$tmpFiles[$tmpFile]; + $stat = $this->stat($path); + if (empty($stat)) { + // create new file + $stat = array( + 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, + ); + } + // update stat with new data + $mTime = time(); + $stat['size'] = filesize($tmpFile); + $stat['mtime'] = $mTime; + $stat['storage_mtime'] = $mTime; + $stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile); + $stat['etag'] = $this->getETag($path); + + $fileId = $this->getCache()->put($path, $stat); + try { + //upload to object storage + $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r')); + } catch (\Exception $ex) { + $this->getCache()->remove($path); + \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR); + throw $ex; // make this bubble up + } + } + + /** + * external changes are not supported, exclusive access to the object storage is assumed + * + * @param string $path + * @param int $time + * @return false + */ + public function hasUpdated($path, $time) { + return false; + } +} diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php new file mode 100644 index 00000000000..4af09dca254 --- /dev/null +++ b/lib/private/Files/ObjectStore/Swift.php @@ -0,0 +1,154 @@ +<?php +/** + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Files\ObjectStore; + +use Guzzle\Http\Exception\ClientErrorResponseException; +use OCP\Files\ObjectStore\IObjectStore; +use OpenCloud\OpenStack; +use OpenCloud\Rackspace; + +class Swift implements IObjectStore { + /** + * @var \OpenCloud\OpenStack + */ + private $client; + + /** + * @var array + */ + private $params; + + /** + * @var \OpenCloud\ObjectStore\Service + */ + private $objectStoreService; + + /** + * @var \OpenCloud\ObjectStore\Resource\Container + */ + private $container; + + public function __construct($params) { + if (!isset($params['container'])) { + $params['container'] = 'owncloud'; + } + if (!isset($params['autocreate'])) { + // should only be true for tests + $params['autocreate'] = false; + } + + if (isset($params['apiKey'])) { + $this->client = new Rackspace($params['url'], $params); + } else { + $this->client = new OpenStack($params['url'], $params); + } + $this->params = $params; + } + + protected function init() { + if ($this->container) { + return; + } + + // the OpenCloud client library will default to 'cloudFiles' if $serviceName is null + $serviceName = null; + if (isset($this->params['serviceName'])) { + $serviceName = $this->params['serviceName']; + } + + // the OpenCloud client library will default to 'publicURL' if $urlType is null + $urlType = null; + if (isset($this->params['urlType'])) { + $urlType = $this->params['urlType']; + } + $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); + + try { + $this->container = $this->objectStoreService->getContainer($this->params['container']); + } catch (ClientErrorResponseException $ex) { + // if the container does not exist and autocreate is true try to create the container on the fly + if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) { + $this->container = $this->objectStoreService->createContainer($this->params['container']); + } else { + throw $ex; + } + } + } + + /** + * @return string the container name where objects are stored + */ + public function getStorageId() { + return $this->params['container']; + } + + /** + * @param string $urn the unified resource name used to identify the object + * @param resource $stream stream with the data to write + * @throws Exception from openstack lib when something goes wrong + */ + public function writeObject($urn, $stream) { + $this->init(); + $this->container->uploadObject($urn, $stream); + } + + /** + * @param string $urn the unified resource name used to identify the object + * @return resource stream with the read data + * @throws Exception from openstack lib when something goes wrong + */ + public function readObject($urn) { + $this->init(); + $object = $this->container->getObject($urn); + + // we need to keep a reference to objectContent or + // the stream will be closed before we can do anything with it + /** @var $objectContent \Guzzle\Http\EntityBody * */ + $objectContent = $object->getContent(); + $objectContent->rewind(); + + $stream = $objectContent->getStream(); + // save the object content in the context of the stream to prevent it being gc'd until the stream is closed + stream_context_set_option($stream, 'swift','content', $objectContent); + + return $stream; + } + + /** + * @param string $urn Unified Resource Name + * @return void + * @throws Exception from openstack lib when something goes wrong + */ + public function deleteObject($urn) { + $this->init(); + // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242 + $this->container->dataObject()->setName($urn)->delete(); + } + + public function deleteContainer($recursive = false) { + $this->init(); + $this->container->delete($recursive); + } + +} |