/** * @param string $path * @param string $mode * @return resource */ public function fopen($path, $mode) { $fullPath = $this->buildPath($path); try { switch ($mode) { case 'r': case 'rb': if (!$this->file_exists($path)) { return false; } return $this->share->read($fullPath); case 'w': case 'wb': $source = $this->share->write($fullPath); return CallBackWrapper::wrap($source, null, null, function () use($fullPath) { unset($this->statCache[$fullPath]); }); case 'a': case 'ab': case 'r+': case 'w+': case 'wb+': case 'a+': case 'x': case 'x+': case 'c': case 'c+': //emulate these if (strrpos($path, '.') !== false) { $ext = substr($path, strrpos($path, '.')); } else { $ext = ''; } if ($this->file_exists($path)) { if (!$this->isUpdatable($path)) { return false; } $tmpFile = $this->getCachedFile($path); } else { if (!$this->isCreatable(dirname($path))) { return false; } $tmpFile = \OCP\Files::tmpFile($ext); } $source = fopen($tmpFile, $mode); $share = $this->share; return CallbackWrapper::wrap($source, null, null, function () use($tmpFile, $fullPath, $share) { unset($this->statCache[$fullPath]); $share->put($tmpFile, $fullPath); unlink($tmpFile); }); } return false; } catch (NotFoundException $e) { return false; } catch (ForbiddenException $e) { return false; } catch (ConnectException $e) { throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e); } }
/** * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * * @param string $operation * @param string $path * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed * @throws \Exception * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { $postFix = substr($path, -1, 1) === '/' ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { // always a shared lock during pre-hooks so the hook can read the file $this->lockFile($path, ILockingProvider::LOCK_SHARED); } $run = $this->runHooks($hooks, $path); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); } try { if (!is_null($extraParam)) { $result = $storage->{$operation}($internalPath, $extraParam); } else { $result = $storage->{$operation}($internalPath); } } catch (\Exception $e) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else { if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } throw $e; } if (in_array('delete', $hooks) and $result) { $this->removeUpdate($storage, $internalPath); } if (in_array('write', $hooks) and $operation !== 'fopen') { $this->writeUpdate($storage, $internalPath); } if (in_array('touch', $hooks)) { $this->writeUpdate($storage, $internalPath, $extraParam); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { $this->changeLock($path, ILockingProvider::LOCK_SHARED); } $unlockLater = false; if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { $unlockLater = true; $result = CallbackWrapper::wrap($result, null, null, function () use($hooks, $path) { if (in_array('write', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else { if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } }); } if ($this->shouldEmitHooks($path) && $result !== false) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } } if (!$unlockLater && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } return $result; } else { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } return null; }
/** * {@inheritdoc} */ public function fopen($path, $mode) { $fullPath = $this->buildPath($path); $useExisting = true; switch ($mode) { case 'r': case 'rb': try { return $this->flysystem->readStream($fullPath); } catch (FileNotFoundException $e) { return false; } case 'w': case 'w+': case 'wb': case 'wb+': $useExisting = false; case 'a': case 'ab': case 'r+': case 'a+': case 'x': case 'x+': case 'c': case 'c+': //emulate these if ($useExisting and $this->file_exists($path)) { if (!$this->isUpdatable($path)) { return false; } $tmpFile = $this->getCachedFile($path); } else { if (!$this->isCreatable(dirname($path))) { return false; } $tmpFile = \OCP\Files::tmpFile(); } $source = fopen($tmpFile, $mode); return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath) { $this->flysystem->putStream($fullPath, fopen($tmpFile, 'r')); unlink($tmpFile); }); } return false; }
/** * Open a writable stream to a remote file * * @param string $target * @return resource a write only stream to upload a remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($target) { $target = $this->escapePath($target); // since returned stream is closed by the caller we need to create a new instance // since we can't re-use the same file descriptor over multiple calls $workgroupArgument = $this->server->getWorkgroup() ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : ''; $command = sprintf('%s %s --authentication-file=%s %s', $this->system->getSmbclientPath(), $workgroupArgument, System::getFD(3), escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)); $connection = new Connection($command); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); $fh = $connection->getFileInputStream(); $connection->write('put ' . System::getFD(4) . ' ' . $target); $connection->write('exit'); // use a close callback to ensure the upload is finished before continuing // this also serves as a way to keep the connection in scope return CallbackWrapper::wrap($fh, null, null, function () use($connection, $target) { $connection->close(false); // dont terminate, give the upload some time }); }
/** * Open a writable stream to a remote file * * @param string $target * @return resource a write only stream to upload a remote file * * @throws \Icewind\SMB\Exception\NotFoundException * @throws \Icewind\SMB\Exception\InvalidTypeException */ public function write($target) { $target = $this->escapePath($target); // close the single quote, open a double quote where we put the single quote... $target = str_replace('\'', '\'"\'"\'', $target); // since returned stream is closed by the caller we need to create a new instance // since we can't re-use the same file descriptor over multiple calls $command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'put /proc/self/fd/4 %s\'', Server::CLIENT, $this->server->getHost(), $this->name, $target); $connection = new RawConnection($command); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); $fh = $connection->getFileInputStream(); // use a close callback to ensure the upload is finished before continuing // this also serves as a way to keep the connection in scope return CallbackWrapper::wrap($fh, null, null, function () use($connection) { $connection->close(false); // dont terminate, give the upload some time }); }
/** * @param resource $source * @param callable $read * @param callable $write * @param callable $close * @param callable $readDir * @return resource */ protected function wrapSource($source, $read = null, $write = null, $close = null, $readDir = null) { return \Icewind\Streams\CallbackWrapper::wrap($source, $read, $write, $close, $readDir); }