summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2023-09-19 12:39:55 +0200
committerArthur Schiwon <blizzz@arthur-schiwon.de>2023-10-10 09:34:20 +0200
commit36eef0be67eef2405e85b4d4bc60a1272d84ecab (patch)
treece409f988dfcf2eedce8d5fde30dc6d5ef7064ac /lib/private
parentf14a01cc1ef6fbff80bc1fbc512799d5aaa62ca7 (diff)
downloadnextcloud-server-36eef0be67eef2405e85b4d4bc60a1272d84ecab.tar.gz
nextcloud-server-36eef0be67eef2405e85b4d4bc60a1272d84ecab.zip
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>
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/Files/Storage/Wrapper/KnownMtime.php142
1 files changed, 142 insertions, 0 deletions
diff --git a/lib/private/Files/Storage/Wrapper/KnownMtime.php b/lib/private/Files/Storage/Wrapper/KnownMtime.php
new file mode 100644
index 00000000000..dde209c44ab
--- /dev/null
+++ b/lib/private/Files/Storage/Wrapper/KnownMtime.php
@@ -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;
+ }
+}