KnownMtime.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\Files\Storage\Wrapper;
  7. use OCP\Cache\CappedMemoryCache;
  8. use OCP\Files\Storage\IStorage;
  9. use Psr\Clock\ClockInterface;
  10. /**
  11. * Wrapper that overwrites the mtime return by stat/getMetaData if the returned value
  12. * is lower than when we last modified the file.
  13. *
  14. * This is useful because some storage servers can return an outdated mtime right after writes
  15. */
  16. class KnownMtime extends Wrapper {
  17. private CappedMemoryCache $knowMtimes;
  18. private ClockInterface $clock;
  19. public function __construct(array $parameters) {
  20. parent::__construct($parameters);
  21. $this->knowMtimes = new CappedMemoryCache();
  22. $this->clock = $parameters['clock'];
  23. }
  24. public function file_put_contents(string $path, mixed $data): int|float|false {
  25. $result = parent::file_put_contents($path, $data);
  26. if ($result) {
  27. $now = $this->clock->now()->getTimestamp();
  28. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  29. }
  30. return $result;
  31. }
  32. public function stat(string $path): array|false {
  33. $stat = parent::stat($path);
  34. if ($stat) {
  35. $this->applyKnownMtime($path, $stat);
  36. }
  37. return $stat;
  38. }
  39. public function getMetaData(string $path): ?array {
  40. $stat = parent::getMetaData($path);
  41. if ($stat) {
  42. $this->applyKnownMtime($path, $stat);
  43. }
  44. return $stat;
  45. }
  46. private function applyKnownMtime(string $path, array &$stat): void {
  47. if (isset($stat['mtime'])) {
  48. $knownMtime = $this->knowMtimes->get($path) ?? 0;
  49. $stat['mtime'] = max($stat['mtime'], $knownMtime);
  50. }
  51. }
  52. public function filemtime(string $path): int|false {
  53. $knownMtime = $this->knowMtimes->get($path) ?? 0;
  54. return max(parent::filemtime($path), $knownMtime);
  55. }
  56. public function mkdir(string $path): bool {
  57. $result = parent::mkdir($path);
  58. if ($result) {
  59. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  60. }
  61. return $result;
  62. }
  63. public function rmdir(string $path): bool {
  64. $result = parent::rmdir($path);
  65. if ($result) {
  66. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  67. }
  68. return $result;
  69. }
  70. public function unlink(string $path): bool {
  71. $result = parent::unlink($path);
  72. if ($result) {
  73. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  74. }
  75. return $result;
  76. }
  77. public function rename(string $source, string $target): bool {
  78. $result = parent::rename($source, $target);
  79. if ($result) {
  80. $this->knowMtimes->set($target, $this->clock->now()->getTimestamp());
  81. $this->knowMtimes->set($source, $this->clock->now()->getTimestamp());
  82. }
  83. return $result;
  84. }
  85. public function copy(string $source, string $target): bool {
  86. $result = parent::copy($source, $target);
  87. if ($result) {
  88. $this->knowMtimes->set($target, $this->clock->now()->getTimestamp());
  89. }
  90. return $result;
  91. }
  92. public function fopen(string $path, string $mode) {
  93. $result = parent::fopen($path, $mode);
  94. if ($result && $mode === 'w') {
  95. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  96. }
  97. return $result;
  98. }
  99. public function touch(string $path, ?int $mtime = null): bool {
  100. $result = parent::touch($path, $mtime);
  101. if ($result) {
  102. $this->knowMtimes->set($path, $mtime ?? $this->clock->now()->getTimestamp());
  103. }
  104. return $result;
  105. }
  106. public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
  107. $result = parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  108. if ($result) {
  109. $this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp());
  110. }
  111. return $result;
  112. }
  113. public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
  114. $result = parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  115. if ($result) {
  116. $this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp());
  117. }
  118. return $result;
  119. }
  120. public function writeStream(string $path, $stream, ?int $size = null): int {
  121. $result = parent::writeStream($path, $stream, $size);
  122. if ($result) {
  123. $this->knowMtimes->set($path, $this->clock->now()->getTimestamp());
  124. }
  125. return $result;
  126. }
  127. }