/** * @param string $path * @return string */ protected function toTmpFile($path) { //no longer in the storage api, still useful here $source = $this->fopen($path, 'r'); if (!$source) { return false; } if ($pos = strrpos($path, '.')) { $extension = substr($path, $pos); } else { $extension = ''; } $tmpFile = \OC_Helper::tmpFile($extension); $target = fopen($tmpFile, 'w'); \OC_Helper::streamCopy($source, $target); fclose($target); return $tmpFile; }
/** * copy file between two storages * * @param Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @param bool $isRename * @return bool * @throws \Exception */ private function copyBetweenStorage(Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) { // for versions we have nothing to do, because versions should always use the // key from the original file. Just create a 1:1 copy and done if ($this->isVersion($targetInternalPath) || $this->isVersion($sourceInternalPath)) { $result = $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); if ($result) { $info = $this->getCache('', $sourceStorage)->get($sourceInternalPath); // make sure that we update the unencrypted size for the version if (isset($info['encrypted']) && $info['encrypted'] === true) { $this->updateUnencryptedSize($this->getFullPath($targetInternalPath), $info['size']); } } return $result; } // first copy the keys that we reuse the existing file key on the target location // and don't create a new one which would break versions for example. $mount = $this->mountManager->findByStorageId($sourceStorage->getId()); if (count($mount) === 1) { $mountPoint = $mount[0]->getMountPoint(); $source = $mountPoint . '/' . $sourceInternalPath; $target = $this->getFullPath($targetInternalPath); $this->copyKeys($source, $target); } else { $this->logger->error('Could not find mount point, can\'t keep encryption keys'); } if ($sourceStorage->is_dir($sourceInternalPath)) { $dh = $sourceStorage->opendir($sourceInternalPath); $result = $this->mkdir($targetInternalPath); if (is_resource($dh)) { while ($result and ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file); } } } } else { try { $source = $sourceStorage->fopen($sourceInternalPath, 'r'); $target = $this->fopen($targetInternalPath, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); fclose($source); fclose($target); } catch (\Exception $e) { fclose($source); fclose($target); throw $e; } if ($result) { if ($preserveMtime) { $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath)); } $isEncrypted = $this->encryptionManager->isEnabled() && $this->mount->getOption('encrypt', true) ? 1 : 0; // in case of a rename we need to manipulate the source cache because // this information will be kept for the new target if ($isRename) { $sourceStorage->getCache()->put($sourceInternalPath, ['encrypted' => $isEncrypted]); } else { $this->getCache()->put($targetInternalPath, ['encrypted' => $isEncrypted]); } } else { // delete partially written target file $this->unlink($targetInternalPath); // delete cache entry that was created by fopen $this->getCache()->remove($targetInternalPath); } } return (bool) $result; }
/** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @param bool $preserveMtime * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) { if ($sourceStorage === $this) { return $this->copy($sourceInternalPath, $targetInternalPath); } if ($sourceStorage->is_dir($sourceInternalPath)) { $dh = $sourceStorage->opendir($sourceInternalPath); $result = $this->mkdir($targetInternalPath); if (is_resource($dh)) { while ($result and ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file); } } } } else { $source = $sourceStorage->fopen($sourceInternalPath, 'r'); // TODO: call fopen in a way that we execute again all storage wrappers // to avoid that we bypass storage wrappers which perform important actions // for this operation. Same is true for all other operations which // are not the same as the original one.Once this is fixed we also // need to adjust the encryption wrapper. $target = $this->fopen($targetInternalPath, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); if ($result and $preserveMtime) { $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath)); } fclose($source); fclose($target); if (!$result) { // delete partially written target file $this->unlink($targetInternalPath); // delete cache entry that was created by fopen $this->getCache()->remove($targetInternalPath); } } return (bool) $result; }
/** * @param string $path * @param mixed $data * @return bool|mixed * @throws \Exception */ public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path)) { $path = $this->getRelativePath($absolutePath); $this->lockFile($path, ILockingProvider::LOCK_SHARED); $exists = $this->file_exists($path); $run = true; if ($this->shouldEmitHooks($path)) { $this->emit_file_hooks_pre($exists, $path, $run); } if (!$run) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); return false; } $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = $this->resolvePath($path); $target = $storage->fopen($internalPath, 'w'); if ($target) { list(, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); $this->writeUpdate($storage, $internalPath); $this->changeLock($path, ILockingProvider::LOCK_SHARED); if ($this->shouldEmitHooks($path) && $result !== false) { $this->emit_file_hooks_post($exists, $path); } $this->unlockFile($path, ILockingProvider::LOCK_SHARED); return $result; } else { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); return false; } } else { return false; } } else { $hooks = $this->file_exists($path) ? array('update', 'write') : array('create', 'write'); return $this->basicOperation('file_put_contents', $path, $hooks, $data); } }
public function copy($path1, $path2) { $postFix1 = substr($path1, -1, 1) === '/' ? '/' : ''; $postFix2 = substr($path2, -1, 1) === '/' ? '/' : ''; $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); if (OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2, OC_Filesystem::signal_param_run => &$run)); $exists = $this->file_exists($path2); if ($run and !$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } if ($run) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } } if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { if ($storage = $this->getStorage($path1 . $postFix1)) { $result = $storage->copy($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); } } else { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $result = OC_Helper::streamCopy($source, $target); } if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2)); if (!$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array(OC_Filesystem::signal_param_path => $path2)); } OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array(OC_Filesystem::signal_param_path => $path2)); } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions OC_FileCache_Update::update($path2, $this->fakeRoot); OC_Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); } return $result; } } }
/** * Stream copy file contents from $path1 to $path2 * * @param View $view view to use for copying * @param string $path1 source file to copy * @param string $path2 target file * * @return bool true for success, false otherwise */ private static function copyFileContents($view, $path1, $path2) { /** @var \OC\Files\Storage\Storage $storage1 */ list($storage1, $internalPath1) = $view->resolvePath($path1); /** @var \OC\Files\Storage\Storage $storage2 */ list($storage2, $internalPath2) = $view->resolvePath($path2); $view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE); $view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE); // TODO add a proper way of overwriting a file while maintaining file ids if ($storage1->instanceOfStorage('\\OC\\Files\\ObjectStore\\ObjectStoreStorage') || $storage2->instanceOfStorage('\\OC\\Files\\ObjectStore\\ObjectStoreStorage')) { $source = $storage1->fopen($internalPath1, 'r'); $target = $storage2->fopen($internalPath2, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); fclose($source); fclose($target); if ($result !== false) { $storage1->unlink($internalPath1); } } else { $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } $view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE); $view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE); return $result !== false; }
/** * copy the contents of one stream to another * @param resource source * @param resource target * @return int the number of bytes copied */ public static function streamCopy($source, $target) { list($count, $result) = \OC_Helper::streamCopy($source, $target); return $count; }
public function testStreamCopyNotEnoughSpace() { $instance = $this->getLimitedStorage(9); $inputStream = fopen('data://text/plain,foobarqwerty', 'r'); $outputStream = $instance->fopen('files/foo', 'w+'); list($count, $result) = \OC_Helper::streamCopy($inputStream, $outputStream); $this->assertEquals(9, $count); $this->assertFalse($result); fclose($inputStream); fclose($outputStream); }
/** * @param string $path * @param mixed $data * @return bool|mixed */ public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path)) { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); $run = true; if ($this->shouldEmitHooks($path)) { $this->emit_file_hooks_pre($exists, $path, $run); } if (!$run) { return false; } $target = $this->fopen($path, 'w'); if ($target) { list($count, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); $this->updater->update($path); if ($this->shouldEmitHooks($path) && $result !== false) { $this->emit_file_hooks_post($exists, $path); } return $result; } else { return false; } } else { return false; } } else { $hooks = $this->file_exists($path) ? array('update', 'write') : array('create', 'write'); return $this->basicOperation('file_put_contents', $path, $hooks, $data); } }
/** * copy the contents of one stream to another * @param resource source * @param resource target * @return int the number of bytes copied */ public static function streamCopy($source, $target) { return \OC_Helper::streamCopy($source, $target); }
public function copy($path1, $path2) { $postFix1 = substr($path1, -1, 1) === '/' ? '/' : ''; $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) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; $exists = $this->file_exists($path2); if ($this->shouldEmitHooks()) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_copy, array(Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2), Filesystem::signal_param_run => &$run)); if ($run and !$exists) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(Filesystem::signal_param_path => $this->getHookPath($path2), Filesystem::signal_param_run => &$run)); } if ($run) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(Filesystem::signal_param_path => $this->getHookPath($path2), Filesystem::signal_param_run => &$run)); } } if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); if ($storage) { $result = $storage->copy($internalPath1, $internalPath2); } else { $result = false; } } else { if ($this->is_dir($path1) && ($dh = $this->opendir($path1))) { $result = $this->mkdir($path2); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { 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); fclose($source); fclose($target); } } if ($this->shouldEmitHooks() && $result !== false) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_copy, array(Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2))); if (!$exists) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(Filesystem::signal_param_path => $this->getHookPath($path2))); } \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(Filesystem::signal_param_path => $this->getHookPath($path2))); } return $result; } else { return false; } } else { return false; } }
/** * Updates the data * * The data argument is a readable stream resource. * * After a successful put operation, you may choose to return an ETag. The * etag must always be surrounded by double-quotes. These quotes must * appear in the actual string you're returning. * * Clients may use the ETag from a PUT request to later on make sure that * when they update the file, the contents haven't changed in the mean * time. * * If you don't plan to store the file byte-by-byte, and you return a * different object on a subsequent GET you are strongly recommended to not * return an ETag, and just return null. * * @param resource $data * * @throws Forbidden * @throws UnsupportedMediaType * @throws BadRequest * @throws Exception * @throws EntityTooLarge * @throws ServiceUnavailable * @throws FileLocked * @return string|null */ public function put($data) { try { $exists = $this->fileView->file_exists($this->path); if ($this->info && $exists && !$this->info->isUpdateable()) { throw new Forbidden(); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("File is not updatable: " . $e->getMessage()); } // verify path of the target $this->verifyPath(); // chunked handling if (isset($_SERVER['HTTP_OC_CHUNKED'])) { return $this->createFileChunked($data); } list($partStorage) = $this->fileView->resolvePath($this->path); $needsPartFile = $this->needsPartFile($partStorage) && strlen($this->path) > 1; if ($needsPartFile) { // mark file as partial while uploading (ignored by the scanner) $partFilePath = $this->path . '.ocTransferId' . rand() . '.part'; } else { // upload file directly as the final path $partFilePath = $this->path; } try { $this->fileView->lockFile($this->path, ILockingProvider::LOCK_EXCLUSIVE); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share) /** @var \OC\Files\Storage\Storage $partStorage */ list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = $this->fileView->resolvePath($this->path); try { $target = $partStorage->fopen($internalPartPath, 'wb'); if ($target === false) { \OC_Log::write('webdav', '\\OC\\Files\\Filesystem::fopen() failed', \OC_Log::ERROR); $partStorage->unlink($internalPartPath); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Exception('Could not write file contents'); } list($count, ) = \OC_Helper::streamCopy($data, $target); fclose($target); // if content length is sent by client: // double check if the file was fully received // compare expected and actual size if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] !== 'LOCK') { $expected = $_SERVER['CONTENT_LENGTH']; if ($count != $expected) { $partStorage->unlink($internalPartPath); throw new BadRequest('expected filesize ' . $expected . ' got ' . $count); } } } catch (NotPermittedException $e) { // a more general case - due to whatever reason the content could not be written throw new Forbidden($e->getMessage()); } catch (EntityTooLargeException $e) { // the file is too big to be stored throw new EntityTooLarge($e->getMessage()); } catch (InvalidContentException $e) { // the file content is not permitted throw new UnsupportedMediaType($e->getMessage()); } catch (InvalidPathException $e) { // the path for the file was not valid // TODO: find proper http status code for this case throw new Forbidden($e->getMessage()); } catch (LockNotAcquiredException $e) { // the file is currently being written to by another process throw new FileLocked($e->getMessage(), $e->getCode(), $e); } catch (GenericEncryptionException $e) { // returning 503 will allow retry of the operation at a later point in time throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage()); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to write file contents: " . $e->getMessage()); } try { $view = \OC\Files\Filesystem::getView(); $run = true; if ($view) { $hookPath = $view->getRelativePath($this->fileView->getAbsolutePath($this->path)); if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(\OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run)); } else { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(\OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run)); } \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(\OC\Files\Filesystem::signal_param_path => $hookPath, \OC\Files\Filesystem::signal_param_run => &$run)); } if ($needsPartFile) { // rename to correct path try { if ($run) { $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); $fileExists = $storage->file_exists($internalPath); } if (!$run || $renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', 'renaming part file to final file failed', \OC_Log::ERROR); $partStorage->unlink($internalPartPath); throw new Exception('Could not rename part file to final file'); } } catch (\OCP\Files\LockNotAcquiredException $e) { // the file is currently being written to by another process throw new FileLocked($e->getMessage(), $e->getCode(), $e); } } // since we skipped the view we need to scan and emit the hooks ourselves $partStorage->getScanner()->scanFile($internalPath); if ($view) { $this->fileView->getUpdater()->propagate($hookPath); if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(\OC\Files\Filesystem::signal_param_path => $hookPath)); } else { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(\OC\Files\Filesystem::signal_param_path => $hookPath)); } \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(\OC\Files\Filesystem::signal_param_path => $hookPath)); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { if ($this->fileView->touch($this->path, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } $this->refreshInfo(); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage()); } $this->fileView->unlockFile($this->path, ILockingProvider::LOCK_EXCLUSIVE); return '"' . $this->info->getEtag() . '"'; }
public function copy($path1, $path2) { if ($this->is_dir($path1)) { $this->remove($path2); $dir = $this->opendir($path1); $this->mkdir($path2); while ($file = readdir($dir)) { if (!Filesystem::isIgnoredDir($file)) { if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) { return false; } } } closedir($dir); return true; } else { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); $this->removeCachedFile($path2); return $result; } }
public function copy($path1, $path2) { $absolutePath1 = $this->getAbsolutePath($path1); $absolutePath2 = $this->getAbsolutePath($path2); if (OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2, OC_Filesystem::signal_param_run => &$run)); $exists = $this->file_exists($path2); if ($run and !$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } if ($run) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } if ($run) { $mp1 = $this->getMountPoint($path1); $mp2 = $this->getMountPoint($path2); if ($mp1 == $mp2) { if ($storage = $this->getStorage($path1)) { $result = $storage->copy($this->getInternalPath($path1), $this->getInternalPath($path2)); } } else { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); $count = OC_Helper::streamCopy($data, $target); } OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2)); if (!$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array(OC_Filesystem::signal_param_path => $path2)); } OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array(OC_Filesystem::signal_param_path => $path2)); return $result; } } }
/** * @dataProvider streamCopyDataProvider */ public function testStreamCopy($expectedCount, $expectedResult, $source, $target) { if (is_string($source)) { $source = fopen($source, 'r'); } if (is_string($target)) { $target = fopen($target, 'w'); } list($count, $result) = \OC_Helper::streamCopy($source, $target); if (is_resource($source)) { fclose($source); } if (is_resource($target)) { fclose($target); } $this->assertSame($expectedCount, $count); $this->assertSame($expectedResult, $result); }
/** * Copy a file/folder from the source path to target path * * @param string $path1 source path * @param string $path2 target path * @param bool $preserveMtime whether to preserve mtime on the copy * * @return bool|mixed */ public function copy($path1, $path2, $preserveMtime = false) { $postFix1 = substr($path1, -1, 1) === '/' ? '/' : ''; $postFix2 = substr($path2, -1, 1) === '/' ? '/' : ''; $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); if (Filesystem::isValidPath($path2) and Filesystem::isValidPath($path1) and !Filesystem::isFileBlacklisted($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; $exists = $this->file_exists($path2); if ($this->shouldEmitHooks()) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_copy, array(Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2), Filesystem::signal_param_run => &$run)); $this->emit_file_hooks_pre($exists, $path2, $run); } if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); if ($storage) { $result = $storage->copy($internalPath1, $internalPath2); if (!$result) { // delete partially written target file $storage->unlink($internalPath2); $storage->getCache()->remove($internalPath2); } } else { $result = false; } } else { if ($this->is_dir($path1) && ($dh = $this->opendir($path1))) { $result = $this->mkdir($path2); if ($preserveMtime) { $this->touch($path2, $this->filemtime($path1)); } if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file, $preserveMtime)) { $result = false; } } } } } else { list($storage2, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); list(, $result) = \OC_Helper::streamCopy($source, $target); if ($result && $preserveMtime) { $this->touch($path2, $this->filemtime($path1)); } fclose($source); fclose($target); if (!$result) { // delete partially written target file $storage2->unlink($internalPath2); $storage2->getCache()->remove($internalPath2); } } } $this->updater->update($path2); if ($this->shouldEmitHooks() && $result !== false) { \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_copy, array(Filesystem::signal_param_oldpath => $this->getHookPath($path1), Filesystem::signal_param_newpath => $this->getHookPath($path2))); $this->emit_file_hooks_post($exists, $path2); } return $result; } else { return false; } } else { return false; } }
public function copy($path1, $path2) { // Copy the file if CREATE permission is granted if ($this->isCreatable(dirname($path2))) { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); return OC_Helper::streamCopy($source, $target); } return false; }
/** * Updates the data * * The data argument is a readable stream resource. * * After a successful put operation, you may choose to return an ETag. The * etag must always be surrounded by double-quotes. These quotes must * appear in the actual string you're returning. * * Clients may use the ETag from a PUT request to later on make sure that * when they update the file, the contents haven't changed in the mean * time. * * If you don't plan to store the file byte-by-byte, and you return a * different object on a subsequent GET you are strongly recommended to not * return an ETag, and just return null. * * @param resource $data * * @throws Forbidden * @throws UnsupportedMediaType * @throws BadRequest * @throws Exception * @throws EntityTooLarge * @throws ServiceUnavailable * @throws FileLocked * @return string|null */ public function put($data) { try { $exists = $this->fileView->file_exists($this->path); if ($this->info && $exists && !$this->info->isUpdateable()) { throw new Forbidden(); } } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("File is not updatable: " . $e->getMessage()); } // verify path of the target $this->verifyPath(); // chunked handling if (isset($_SERVER['HTTP_OC_CHUNKED'])) { try { return $this->createFileChunked($data); } catch (\Exception $e) { $this->convertToSabreException($e); } } list($partStorage) = $this->fileView->resolvePath($this->path); $needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1); if ($needsPartFile) { // mark file as partial while uploading (ignored by the scanner) $partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part'; } else { // upload file directly as the final path $partFilePath = $this->path; } // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share) /** @var \OC\Files\Storage\Storage $partStorage */ list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = $this->fileView->resolvePath($this->path); try { $target = $partStorage->fopen($internalPartPath, 'wb'); if ($target === false) { \OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::fopen() failed', \OCP\Util::ERROR); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Exception('Could not write file contents'); } list($count, $result) = \OC_Helper::streamCopy($data, $target); fclose($target); if ($result === false) { $expected = -1; if (isset($_SERVER['CONTENT_LENGTH'])) { $expected = $_SERVER['CONTENT_LENGTH']; } throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )'); } // if content length is sent by client: // double check if the file was fully received // compare expected and actual size if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] !== 'LOCK') { $expected = $_SERVER['CONTENT_LENGTH']; if ($count != $expected) { throw new BadRequest('expected filesize ' . $expected . ' got ' . $count); } } } catch (\Exception $e) { if ($needsPartFile) { $partStorage->unlink($internalPartPath); } $this->convertToSabreException($e); } try { $view = \OC\Files\Filesystem::getView(); if ($view) { $run = $this->emitPreHooks($exists); } else { $run = true; } try { $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); } catch (LockedException $e) { if ($needsPartFile) { $partStorage->unlink($internalPartPath); } throw new FileLocked($e->getMessage(), $e->getCode(), $e); } if ($needsPartFile) { // rename to correct path try { if ($run) { $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); $fileExists = $storage->file_exists($internalPath); } if (!$run || $renameOkay === false || $fileExists === false) { \OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR); throw new Exception('Could not rename part file to final file'); } } catch (\Exception $e) { $partStorage->unlink($internalPartPath); $this->convertToSabreException($e); } } // since we skipped the view we need to scan and emit the hooks ourselves $this->fileView->getUpdater()->update($this->path); try { $this->changeLock(ILockingProvider::LOCK_SHARED); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } if ($view) { $this->emitPostHooks($exists); } // allow sync clients to send the mtime along in a header $request = \OC::$server->getRequest(); if (isset($request->server['HTTP_X_OC_MTIME'])) { if ($this->fileView->touch($this->path, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } $this->refreshInfo(); } catch (StorageNotAvailableException $e) { throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage()); } return '"' . $this->info->getEtag() . '"'; }
public function copy($path1, $path2) { $postFix1 = substr($path1, -1, 1) === '/' ? '/' : ''; $postFix2 = substr($path2, -1, 1) === '/' ? '/' : ''; $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); if (OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); if ($path1 == null or $path2 == null) { return false; } $run = true; if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2, OC_Filesystem::signal_param_run => &$run)); $exists = $this->file_exists($path2); if ($run and !$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } if ($run) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array(OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); } } if ($run) { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { if ($storage = $this->getStorage($path1 . $postFix1)) { $result = $storage->copy($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); } } else { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $result = OC_Helper::streamCopy($source, $target); } if ($this->fakeRoot == OC_Filesystem::getRoot()) { // If the file to be copied originates within // the user's data directory OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array(OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath => $path2)); if (!$exists) { OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array(OC_Filesystem::signal_param_path => $path2)); } OC_Hook::emit(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array(OC_Filesystem::signal_param_path => $path2)); } else { // If this is not a normal file copy operation // and the file originates somewhere else // (e.g. a version rollback operation), do not // perform all the other post_write actions // Update webdav properties OC_Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); $splitPath2 = explode('/', $path2); // Only cache information about files // that are being copied from within // the user files directory. Caching // other files, like VCS backup files, // serves no purpose if ($splitPath2[1] == 'files') { OC_FileCache_Update::update($path2, $this->fakeRoot); } } return $result; } } }