server/lib/private/cache/file.php
Vincent Petry b495ca4924 Added part files for when writing chunks
Whenever a chunk is being written, it is now written to a part file
first. This is to avoid a concurrent chunk upload to believe that the
former chunk already exists, even though it's not fully written.

This makes sure that the final file assembly is only done when all
chunks were successfully written.

Also added an extra unique id to chunk parts in case the same chunk is
uploaded twice concurrently.
2015-03-16 10:13:13 +01:00

161 lines
3.9 KiB
PHP

<?php
/**
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OC\Cache;
use OC\Files\Filesystem;
use OC\Files\View;
use OCP\Security\ISecureRandom;
class File {
protected $storage;
/**
* Returns the cache storage for the logged in user
*
* @return \OC\Files\View cache storage
*/
protected function getStorage() {
if (isset($this->storage)) {
return $this->storage;
}
if (\OC_User::isLoggedIn()) {
$rootView = new View();
$user = \OC::$server->getUserSession()->getUser();
Filesystem::initMountPoints($user->getUID());
if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
$rootView->mkdir('/' . $user->getUID() . '/cache');
}
$this->storage = new View('/' . $user->getUID() . '/cache');
return $this->storage;
} else {
\OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR);
throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
}
}
/**
* @param string $key
*/
public function get($key) {
$result = null;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($this->hasKey($key)) {
$storage = $this->getStorage();
$result = $storage->file_get_contents($key);
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* Returns the size of the stored/cached data
*
* @param string $key
* @return int
*/
public function size($key) {
$result = 0;
$proxyStatus = \OC_FileProxy::$enabled;
\OC_FileProxy::$enabled = false;
if ($this->hasKey($key)) {
$storage = $this->getStorage();
$result = $storage->filesize($key);
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
/**
* @param string $key
*/
public function set($key, $value, $ttl = 0) {
$storage = $this->getStorage();
$result = false;
$proxyStatus = \OC_FileProxy::$enabled;
// unique id to avoid chunk collision, just in case
$uniqueId = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(
16,
ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
);
// use part file to prevent hasKey() to find the key
// while it is being written
$keyPart = $key . '.' . $uniqueId . '.part';
\OC_FileProxy::$enabled = false;
if ($storage and $storage->file_put_contents($keyPart, $value)) {
if ($ttl === 0) {
$ttl = 86400; // 60*60*24
}
$result = $storage->touch($keyPart, time() + $ttl);
$result &= $storage->rename($keyPart, $key);
}
\OC_FileProxy::$enabled = $proxyStatus;
return $result;
}
public function hasKey($key) {
$storage = $this->getStorage();
if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
return true;
}
return false;
}
/**
* @param string $key
*/
public function remove($key) {
$storage = $this->getStorage();
if (!$storage) {
return false;
}
return $storage->unlink($key);
}
public function clear($prefix = '') {
$storage = $this->getStorage();
if ($storage and $storage->is_dir('/')) {
$dh = $storage->opendir('/');
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
$storage->unlink('/' . $file);
}
}
}
}
return true;
}
public function gc() {
$storage = $this->getStorage();
if ($storage and $storage->is_dir('/')) {
$now = time();
$dh = $storage->opendir('/');
if (!is_resource($dh)) {
return null;
}
while (($file = readdir($dh)) !== false) {
if ($file != '.' and $file != '..') {
$mtime = $storage->filemtime('/' . $file);
if ($mtime < $now) {
$storage->unlink('/' . $file);
}
}
}
}
}
public static function loginListener() {
$c = new self();
$c->gc();
}
}