Przeglądaj źródła

add wrapper to ensure we don't get an mtime that is lower than we know it is

Signed-off-by: Robin Appelman <robin@icewind.nl>
tags/v28.0.0beta1
Robin Appelman 9 miesięcy temu
rodzic
commit
5bf34979fe

+ 1
- 0
lib/composer/composer/autoload_classmap.php Wyświetl plik

@@ -1346,6 +1346,7 @@ return array(
'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php',
'OC\\Files\\Storage\\Wrapper\\Encryption' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encryption.php',
'OC\\Files\\Storage\\Wrapper\\Jail' => $baseDir . '/lib/private/Files/Storage/Wrapper/Jail.php',
'OC\\Files\\Storage\\Wrapper\\KnownMtime' => $baseDir . '/lib/private/Files/Storage/Wrapper/KnownMtime.php',
'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php',
'OC\\Files\\Storage\\Wrapper\\Quota' => $baseDir . '/lib/private/Files/Storage/Wrapper/Quota.php',
'OC\\Files\\Storage\\Wrapper\\Wrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/Wrapper.php',

+ 1
- 0
lib/composer/composer/autoload_static.php Wyświetl plik

@@ -1379,6 +1379,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php',
'OC\\Files\\Storage\\Wrapper\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encryption.php',
'OC\\Files\\Storage\\Wrapper\\Jail' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Jail.php',
'OC\\Files\\Storage\\Wrapper\\KnownMtime' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/KnownMtime.php',
'OC\\Files\\Storage\\Wrapper\\PermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/PermissionsMask.php',
'OC\\Files\\Storage\\Wrapper\\Quota' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Quota.php',
'OC\\Files\\Storage\\Wrapper\\Wrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Wrapper.php',

+ 142
- 0
lib/private/Files/Storage/Wrapper/KnownMtime.php Wyświetl plik

@@ -0,0 +1,142 @@
<?php

namespace OC\Files\Storage\Wrapper;

use OCP\Cache\CappedMemoryCache;
use OCP\Files\Storage\IStorage;
use Psr\Clock\ClockInterface;

/**
* Wrapper that overwrites the mtime return by stat/getMetaData if the returned value
* is lower than when we last modified the file.
*
* This is useful because some storage servers can return an outdated mtime right after writes
*/
class KnownMtime extends Wrapper {
private CappedMemoryCache $knowMtimes;
private ClockInterface $clock;

public function __construct($arguments) {
parent::__construct($arguments);
$this->knowMtimes = new CappedMemoryCache();
$this->clock = $arguments['clock'];
}

public function file_put_contents($path, $data) {
$result = parent::file_put_contents($path, $data);
if ($result) {
$now = $this->clock->now()->getTimestamp();
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}

public function stat($path) {
$stat = parent::stat($path);
if ($stat) {
$this->applyKnownMtime($path, $stat);
}
return $stat;
}

public function getMetaData($path) {
$stat = parent::getMetaData($path);
if ($stat) {
$this->applyKnownMtime($path, $stat);
}
return $stat;
}

private function applyKnownMtime(string $path, array &$stat) {
if (isset($stat['mtime'])) {
$knownMtime = $this->knowMtimes->get($path) ?? 0;
$stat['mtime'] = max($stat['mtime'], $knownMtime);
}
}

public function filemtime($path) {
$knownMtime = $this->knowMtimes->get($path) ?? 0;
return max(parent::filemtime($path), $knownMtime);
}

public function mkdir($path) {
$result = parent::mkdir($path);
if ($result) {
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}

public function rmdir($path) {
$result = parent::rmdir($path);
if ($result) {
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}

public function unlink($path) {
$result = parent::unlink($path);
if ($result) {
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}

public function rename($source, $target) {
$result = parent::rename($source, $target);
if ($result) {
$this->knowMtimes->set($target, $this->clock->now()->getTimestamp());
$this->knowMtimes->set($source, $this->clock->now()->getTimestamp());
}
return $result;
}

public function copy($source, $target) {
$result = parent::copy($source, $target);
if ($result) {
$this->knowMtimes->set($target, $this->clock->now()->getTimestamp());
}
return $result;
}

public function fopen($path, $mode) {
$result = parent::fopen($path, $mode);
if ($result && $mode === 'w') {
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}

public function touch($path, $mtime = null) {
$result = parent::touch($path, $mtime);
if ($result) {
$this->knowMtimes->set($path, $mtime ?? $this->clock->now()->getTimestamp());
}
return $result;
}

public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
$result = parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
if ($result) {
$this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp());
}
return $result;
}

public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
$result = parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
if ($result) {
$this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp());
}
return $result;
}

public function writeStream(string $path, $stream, int $size = null): int {
$result = parent::writeStream($path, $stream, $size);
if ($result) {
$this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
}
return $result;
}
}

+ 71
- 0
tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php Wyświetl plik

@@ -0,0 +1,71 @@
<?php
/**
* Copyright (c) 2014 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 lib\Files\Storage\Wrapper;

use OC\Files\Storage\Temporary;
use OC\Files\Storage\Wrapper\KnownMtime;
use OCP\Constants;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Clock\ClockInterface;
use Test\Files\Storage\Storage;

/**
* @group DB
*/
class KnownMtimeTest extends Storage {
/** @var Temporary */
private $sourceStorage;

/** @var ClockInterface|MockObject */
private $clock;
private int $fakeTime = 0;

protected function setUp(): void {
parent::setUp();
$this->fakeTime = 0;
$this->sourceStorage = new Temporary([]);
$this->clock = $this->createMock(ClockInterface::class);
$this->clock->method('now')->willReturnCallback(function () {
if ($this->fakeTime) {
return new \DateTimeImmutable("@{$this->fakeTime}");
} else {
return new \DateTimeImmutable();
}
});
$this->instance = $this->getWrappedStorage();
}

protected function tearDown(): void {
$this->sourceStorage->cleanUp();
parent::tearDown();
}

protected function getWrappedStorage() {
return new KnownMtime([
'storage' => $this->sourceStorage,
'clock' => $this->clock,
]);
}

public function testNewerKnownMtime() {
$future = time() + 1000;
$this->fakeTime = $future;

$this->instance->file_put_contents('foo.txt', 'bar');

// fuzzy match since the clock might have ticked
$this->assertLessThan(2, abs(time() - $this->sourceStorage->filemtime('foo.txt')));
$this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->stat('foo.txt')['mtime']);
$this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->getMetaData('foo.txt')['mtime']);

$this->assertEquals($future, $this->instance->filemtime('foo.txt'));
$this->assertEquals($future, $this->instance->stat('foo.txt')['mtime']);
$this->assertEquals($future, $this->instance->getMetaData('foo.txt')['mtime']);
}
}

Ładowanie…
Anuluj
Zapisz