Completed
Push — master ( 492a8c...2369ad )
by
unknown
21:34
created
lib/public/Files/Cache/IPropagator.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -19,30 +19,30 @@
 block discarded – undo
19 19
  */
20 20
 #[Consumable(since: '9.0.0')]
21 21
 interface IPropagator {
22
-	/**
23
-	 * Mark the beginning of a propagation batch.
24
-	 *
25
-	 * Note that not all cache setups support propagation in which case this will be a noop
26
-	 *
27
-	 * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent
28
-	 * before the batch is committed.
29
-	 *
30
-	 * @since 9.1.0
31
-	 */
32
-	public function beginBatch(): void;
22
+    /**
23
+     * Mark the beginning of a propagation batch.
24
+     *
25
+     * Note that not all cache setups support propagation in which case this will be a noop
26
+     *
27
+     * Batching for cache setups that do support it has to be explicit since the cache state is not fully consistent
28
+     * before the batch is committed.
29
+     *
30
+     * @since 9.1.0
31
+     */
32
+    public function beginBatch(): void;
33 33
 
34
-	/**
35
-	 * Commit the active propagation batch.
36
-	 *
37
-	 * @since 9.1.0
38
-	 */
39
-	public function commitBatch(): void;
34
+    /**
35
+     * Commit the active propagation batch.
36
+     *
37
+     * @since 9.1.0
38
+     */
39
+    public function commitBatch(): void;
40 40
 
41
-	/**
42
-	 * @param string $internalPath
43
-	 * @param int $time
44
-	 * @param int $sizeDifference The number of bytes the file has grown.
45
-	 * @since 9.0.0
46
-	 */
47
-	public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void;
41
+    /**
42
+     * @param string $internalPath
43
+     * @param int $time
44
+     * @param int $sizeDifference The number of bytes the file has grown.
45
+     * @since 9.0.0
46
+     */
47
+    public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void;
48 48
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/HomePropagator.php 1 patch
Indentation   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -13,7 +13,7 @@
 block discarded – undo
13 13
 use OCP\IDBConnection;
14 14
 
15 15
 class HomePropagator extends Propagator {
16
-	public function __construct(IStorage $storage, IDBConnection $connection) {
17
-		parent::__construct($storage, $connection, ignore: ['files_encryption']);
18
-	}
16
+    public function __construct(IStorage $storage, IDBConnection $connection) {
17
+        parent::__construct($storage, $connection, ignore: ['files_encryption']);
18
+    }
19 19
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/Wrapper/JailPropagator.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -13,11 +13,11 @@
 block discarded – undo
13 13
 use Override;
14 14
 
15 15
 class JailPropagator extends Propagator {
16
-	#[Override]
17
-	public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void {
18
-		/** @var Jail $jail */
19
-		$jail = $this->storage;
20
-		[$storage, $sourceInternalPath] = $jail->resolvePath($internalPath);
21
-		$storage->getPropagator()->propagateChange($sourceInternalPath, $time, $sizeDifference);
22
-	}
16
+    #[Override]
17
+    public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void {
18
+        /** @var Jail $jail */
19
+        $jail = $this->storage;
20
+        [$storage, $sourceInternalPath] = $jail->resolvePath($internalPath);
21
+        $storage->getPropagator()->propagateChange($sourceInternalPath, $time, $sizeDifference);
22
+    }
23 23
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/Propagator.php 2 patches
Indentation   +174 added lines, -174 removed lines patch added patch discarded remove patch
@@ -22,178 +22,178 @@
 block discarded – undo
22 22
 use Psr\Log\LoggerInterface;
23 23
 
24 24
 class Propagator implements IPropagator {
25
-	public const MAX_RETRIES = 3;
26
-
27
-	private bool $inBatch = false;
28
-	private array $batch = [];
29
-	private ClockInterface $clock;
30
-
31
-	public function __construct(
32
-		protected readonly IStorage $storage,
33
-		private readonly IDBConnection $connection,
34
-		private readonly array $ignore = [],
35
-	) {
36
-		$this->clock = Server::get(ClockInterface::class);
37
-	}
38
-
39
-	#[Override]
40
-	public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void {
41
-		// Do not propagate changes in ignored paths
42
-		foreach ($this->ignore as $ignore) {
43
-			if (str_starts_with($internalPath, $ignore)) {
44
-				return;
45
-			}
46
-		}
47
-
48
-		$time = min($time, $this->clock->now()->getTimestamp());
49
-
50
-		$storageId = $this->storage->getCache()->getNumericStorageId();
51
-
52
-		$parents = $this->getParents($internalPath);
53
-
54
-		if ($this->inBatch) {
55
-			foreach ($parents as $parent) {
56
-				$this->addToBatch($parent, $time, $sizeDifference);
57
-			}
58
-			return;
59
-		}
60
-
61
-		$parentHashes = array_map('md5', $parents);
62
-		$etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag
63
-
64
-		$builder = $this->connection->getQueryBuilder();
65
-		$hashParams = array_map(function ($hash) use ($builder) {
66
-			return $builder->expr()->literal($hash);
67
-		}, $parentHashes);
68
-
69
-		$builder->update('filecache')
70
-			->set('mtime', $builder->func()->greatest('mtime', $builder->createNamedParameter($time, IQueryBuilder::PARAM_INT)))
71
-			->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
72
-			->andWhere($builder->expr()->in('path_hash', $hashParams));
73
-		if (!$this->storage->instanceOfStorage(IReliableEtagStorage::class)) {
74
-			$builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR));
75
-		}
76
-
77
-		if ($sizeDifference !== 0) {
78
-			$hasCalculatedSize = $builder->expr()->gt('size', $builder->expr()->literal(-1, IQUeryBuilder::PARAM_INT));
79
-			$sizeColumn = $builder->getColumnName('size');
80
-			$newSize = $builder->func()->greatest(
81
-				$builder->func()->add('size', $builder->createNamedParameter($sizeDifference)),
82
-				$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
83
-			);
84
-
85
-			// Only update if row had a previously calculated size
86
-			$builder->set('size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newSize ELSE $sizeColumn END"));
87
-
88
-			if ($this->storage->instanceOfStorage(Encryption::class)) {
89
-				// in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
90
-				$hasUnencryptedSize = $builder->expr()->neq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT));
91
-				$sizeColumn = $builder->getColumnName('size');
92
-				$unencryptedSizeColumn = $builder->getColumnName('unencrypted_size');
93
-				$newUnencryptedSize = $builder->func()->greatest(
94
-					$builder->func()->add(
95
-						$builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $unencryptedSizeColumn ELSE $sizeColumn END"),
96
-						$builder->createNamedParameter($sizeDifference)
97
-					),
98
-					$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
99
-				);
100
-
101
-				// Only update if row had a previously calculated size
102
-				$builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END"));
103
-			}
104
-		}
105
-
106
-		for ($i = 0; $i < self::MAX_RETRIES; $i++) {
107
-			try {
108
-				$builder->executeStatement();
109
-				break;
110
-			} catch (DbalException $e) {
111
-				if (!$e->isRetryable()) {
112
-					throw $e;
113
-				}
114
-
115
-				/** @var LoggerInterface $loggerInterface */
116
-				$loggerInterface = \OCP\Server::get(LoggerInterface::class);
117
-				$loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]);
118
-			}
119
-		}
120
-	}
121
-
122
-	/**
123
-	 * @return string[]
124
-	 */
125
-	protected function getParents(string $path): array {
126
-		$parts = explode('/', $path);
127
-		$parent = '';
128
-		$parents = [];
129
-		foreach ($parts as $part) {
130
-			$parents[] = $parent;
131
-			$parent = trim($parent . '/' . $part, '/');
132
-		}
133
-		return $parents;
134
-	}
135
-
136
-	#[Override]
137
-	public function beginBatch(): void {
138
-		$this->inBatch = true;
139
-	}
140
-
141
-	private function addToBatch(string $internalPath, int $time, int $sizeDifference): void {
142
-		if (!isset($this->batch[$internalPath])) {
143
-			$this->batch[$internalPath] = [
144
-				'hash' => md5($internalPath),
145
-				'time' => $time,
146
-				'size' => $sizeDifference,
147
-			];
148
-		} else {
149
-			$this->batch[$internalPath]['size'] += $sizeDifference;
150
-			if ($time > $this->batch[$internalPath]['time']) {
151
-				$this->batch[$internalPath]['time'] = $time;
152
-			}
153
-		}
154
-	}
155
-
156
-	#[Override]
157
-	public function commitBatch(): void {
158
-		if (!$this->inBatch) {
159
-			throw new \BadMethodCallException('Not in batch');
160
-		}
161
-		$this->inBatch = false;
162
-
163
-		$this->connection->beginTransaction();
164
-
165
-		$query = $this->connection->getQueryBuilder();
166
-		$storageId = $this->storage->getCache()->getNumericStorageId();
167
-
168
-		$query->update('filecache')
169
-			->set('mtime', $query->func()->greatest('mtime', $query->createParameter('time')))
170
-			->set('etag', $query->expr()->literal(uniqid()))
171
-			->where($query->expr()->eq('storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
172
-			->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash')));
173
-
174
-		$sizeQuery = $this->connection->getQueryBuilder();
175
-		$sizeQuery->update('filecache')
176
-			->set('size', $sizeQuery->func()->add('size', $sizeQuery->createParameter('size')))
177
-			->where($query->expr()->eq('storage', $sizeQuery->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
178
-			->andWhere($query->expr()->eq('path_hash', $sizeQuery->createParameter('hash')))
179
-			->andWhere($sizeQuery->expr()->gt('size', $sizeQuery->createNamedParameter(-1, IQueryBuilder::PARAM_INT)));
180
-
181
-		foreach ($this->batch as $item) {
182
-			$query->setParameter('time', $item['time'], IQueryBuilder::PARAM_INT);
183
-			$query->setParameter('hash', $item['hash']);
184
-
185
-			$query->executeStatement();
186
-
187
-			if ($item['size']) {
188
-				$sizeQuery->setParameter('size', $item['size'], IQueryBuilder::PARAM_INT);
189
-				$sizeQuery->setParameter('hash', $item['hash']);
190
-
191
-				$sizeQuery->executeStatement();
192
-			}
193
-		}
194
-
195
-		$this->batch = [];
196
-
197
-		$this->connection->commit();
198
-	}
25
+    public const MAX_RETRIES = 3;
26
+
27
+    private bool $inBatch = false;
28
+    private array $batch = [];
29
+    private ClockInterface $clock;
30
+
31
+    public function __construct(
32
+        protected readonly IStorage $storage,
33
+        private readonly IDBConnection $connection,
34
+        private readonly array $ignore = [],
35
+    ) {
36
+        $this->clock = Server::get(ClockInterface::class);
37
+    }
38
+
39
+    #[Override]
40
+    public function propagateChange(string $internalPath, int $time, int $sizeDifference = 0): void {
41
+        // Do not propagate changes in ignored paths
42
+        foreach ($this->ignore as $ignore) {
43
+            if (str_starts_with($internalPath, $ignore)) {
44
+                return;
45
+            }
46
+        }
47
+
48
+        $time = min($time, $this->clock->now()->getTimestamp());
49
+
50
+        $storageId = $this->storage->getCache()->getNumericStorageId();
51
+
52
+        $parents = $this->getParents($internalPath);
53
+
54
+        if ($this->inBatch) {
55
+            foreach ($parents as $parent) {
56
+                $this->addToBatch($parent, $time, $sizeDifference);
57
+            }
58
+            return;
59
+        }
60
+
61
+        $parentHashes = array_map('md5', $parents);
62
+        $etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag
63
+
64
+        $builder = $this->connection->getQueryBuilder();
65
+        $hashParams = array_map(function ($hash) use ($builder) {
66
+            return $builder->expr()->literal($hash);
67
+        }, $parentHashes);
68
+
69
+        $builder->update('filecache')
70
+            ->set('mtime', $builder->func()->greatest('mtime', $builder->createNamedParameter($time, IQueryBuilder::PARAM_INT)))
71
+            ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
72
+            ->andWhere($builder->expr()->in('path_hash', $hashParams));
73
+        if (!$this->storage->instanceOfStorage(IReliableEtagStorage::class)) {
74
+            $builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR));
75
+        }
76
+
77
+        if ($sizeDifference !== 0) {
78
+            $hasCalculatedSize = $builder->expr()->gt('size', $builder->expr()->literal(-1, IQUeryBuilder::PARAM_INT));
79
+            $sizeColumn = $builder->getColumnName('size');
80
+            $newSize = $builder->func()->greatest(
81
+                $builder->func()->add('size', $builder->createNamedParameter($sizeDifference)),
82
+                $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
83
+            );
84
+
85
+            // Only update if row had a previously calculated size
86
+            $builder->set('size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newSize ELSE $sizeColumn END"));
87
+
88
+            if ($this->storage->instanceOfStorage(Encryption::class)) {
89
+                // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
90
+                $hasUnencryptedSize = $builder->expr()->neq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT));
91
+                $sizeColumn = $builder->getColumnName('size');
92
+                $unencryptedSizeColumn = $builder->getColumnName('unencrypted_size');
93
+                $newUnencryptedSize = $builder->func()->greatest(
94
+                    $builder->func()->add(
95
+                        $builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $unencryptedSizeColumn ELSE $sizeColumn END"),
96
+                        $builder->createNamedParameter($sizeDifference)
97
+                    ),
98
+                    $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
99
+                );
100
+
101
+                // Only update if row had a previously calculated size
102
+                $builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END"));
103
+            }
104
+        }
105
+
106
+        for ($i = 0; $i < self::MAX_RETRIES; $i++) {
107
+            try {
108
+                $builder->executeStatement();
109
+                break;
110
+            } catch (DbalException $e) {
111
+                if (!$e->isRetryable()) {
112
+                    throw $e;
113
+                }
114
+
115
+                /** @var LoggerInterface $loggerInterface */
116
+                $loggerInterface = \OCP\Server::get(LoggerInterface::class);
117
+                $loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]);
118
+            }
119
+        }
120
+    }
121
+
122
+    /**
123
+     * @return string[]
124
+     */
125
+    protected function getParents(string $path): array {
126
+        $parts = explode('/', $path);
127
+        $parent = '';
128
+        $parents = [];
129
+        foreach ($parts as $part) {
130
+            $parents[] = $parent;
131
+            $parent = trim($parent . '/' . $part, '/');
132
+        }
133
+        return $parents;
134
+    }
135
+
136
+    #[Override]
137
+    public function beginBatch(): void {
138
+        $this->inBatch = true;
139
+    }
140
+
141
+    private function addToBatch(string $internalPath, int $time, int $sizeDifference): void {
142
+        if (!isset($this->batch[$internalPath])) {
143
+            $this->batch[$internalPath] = [
144
+                'hash' => md5($internalPath),
145
+                'time' => $time,
146
+                'size' => $sizeDifference,
147
+            ];
148
+        } else {
149
+            $this->batch[$internalPath]['size'] += $sizeDifference;
150
+            if ($time > $this->batch[$internalPath]['time']) {
151
+                $this->batch[$internalPath]['time'] = $time;
152
+            }
153
+        }
154
+    }
155
+
156
+    #[Override]
157
+    public function commitBatch(): void {
158
+        if (!$this->inBatch) {
159
+            throw new \BadMethodCallException('Not in batch');
160
+        }
161
+        $this->inBatch = false;
162
+
163
+        $this->connection->beginTransaction();
164
+
165
+        $query = $this->connection->getQueryBuilder();
166
+        $storageId = $this->storage->getCache()->getNumericStorageId();
167
+
168
+        $query->update('filecache')
169
+            ->set('mtime', $query->func()->greatest('mtime', $query->createParameter('time')))
170
+            ->set('etag', $query->expr()->literal(uniqid()))
171
+            ->where($query->expr()->eq('storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
172
+            ->andWhere($query->expr()->eq('path_hash', $query->createParameter('hash')));
173
+
174
+        $sizeQuery = $this->connection->getQueryBuilder();
175
+        $sizeQuery->update('filecache')
176
+            ->set('size', $sizeQuery->func()->add('size', $sizeQuery->createParameter('size')))
177
+            ->where($query->expr()->eq('storage', $sizeQuery->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
178
+            ->andWhere($query->expr()->eq('path_hash', $sizeQuery->createParameter('hash')))
179
+            ->andWhere($sizeQuery->expr()->gt('size', $sizeQuery->createNamedParameter(-1, IQueryBuilder::PARAM_INT)));
180
+
181
+        foreach ($this->batch as $item) {
182
+            $query->setParameter('time', $item['time'], IQueryBuilder::PARAM_INT);
183
+            $query->setParameter('hash', $item['hash']);
184
+
185
+            $query->executeStatement();
186
+
187
+            if ($item['size']) {
188
+                $sizeQuery->setParameter('size', $item['size'], IQueryBuilder::PARAM_INT);
189
+                $sizeQuery->setParameter('hash', $item['hash']);
190
+
191
+                $sizeQuery->executeStatement();
192
+            }
193
+        }
194
+
195
+        $this->batch = [];
196
+
197
+        $this->connection->commit();
198
+    }
199 199
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -62,7 +62,7 @@  discard block
 block discarded – undo
62 62
 		$etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag
63 63
 
64 64
 		$builder = $this->connection->getQueryBuilder();
65
-		$hashParams = array_map(function ($hash) use ($builder) {
65
+		$hashParams = array_map(function($hash) use ($builder) {
66 66
 			return $builder->expr()->literal($hash);
67 67
 		}, $parentHashes);
68 68
 
@@ -114,7 +114,7 @@  discard block
 block discarded – undo
114 114
 
115 115
 				/** @var LoggerInterface $loggerInterface */
116 116
 				$loggerInterface = \OCP\Server::get(LoggerInterface::class);
117
-				$loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]);
117
+				$loggerInterface->warning('Retrying propagation query after retryable exception.', ['exception' => $e]);
118 118
 			}
119 119
 		}
120 120
 	}
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
 		$parents = [];
129 129
 		foreach ($parts as $part) {
130 130
 			$parents[] = $parent;
131
-			$parent = trim($parent . '/' . $part, '/');
131
+			$parent = trim($parent.'/'.$part, '/');
132 132
 		}
133 133
 		return $parents;
134 134
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/Wrapper/Jail.php 1 patch
Indentation   +241 added lines, -241 removed lines patch added patch discarded remove patch
@@ -25,245 +25,245 @@
 block discarded – undo
25 25
  * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage
26 26
  */
27 27
 class Jail extends Wrapper {
28
-	/**
29
-	 * @var string
30
-	 */
31
-	protected $rootPath;
32
-
33
-	/**
34
-	 * @param array $parameters ['storage' => $storage, 'root' => $root]
35
-	 *
36
-	 * $storage: The storage that will be wrapper
37
-	 * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage
38
-	 */
39
-	public function __construct(array $parameters) {
40
-		parent::__construct($parameters);
41
-		$this->rootPath = $parameters['root'];
42
-	}
43
-
44
-	public function getUnjailedPath(string $path): string {
45
-		return trim(Filesystem::normalizePath($this->rootPath . '/' . $path), '/');
46
-	}
47
-
48
-	/**
49
-	 * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper
50
-	 */
51
-	public function getUnjailedStorage(): IStorage {
52
-		return $this->storage;
53
-	}
54
-
55
-
56
-	public function getJailedPath(string $path): ?string {
57
-		$root = rtrim($this->rootPath, '/') . '/';
58
-
59
-		if ($path !== $this->rootPath && !str_starts_with($path, $root)) {
60
-			return null;
61
-		} else {
62
-			$path = substr($path, strlen($this->rootPath));
63
-			return trim($path, '/');
64
-		}
65
-	}
66
-
67
-	public function getId(): string {
68
-		return parent::getId();
69
-	}
70
-
71
-	public function mkdir(string $path): bool {
72
-		return $this->getWrapperStorage()->mkdir($this->getUnjailedPath($path));
73
-	}
74
-
75
-	public function rmdir(string $path): bool {
76
-		return $this->getWrapperStorage()->rmdir($this->getUnjailedPath($path));
77
-	}
78
-
79
-	public function opendir(string $path) {
80
-		return $this->getWrapperStorage()->opendir($this->getUnjailedPath($path));
81
-	}
82
-
83
-	public function is_dir(string $path): bool {
84
-		return $this->getWrapperStorage()->is_dir($this->getUnjailedPath($path));
85
-	}
86
-
87
-	public function is_file(string $path): bool {
88
-		return $this->getWrapperStorage()->is_file($this->getUnjailedPath($path));
89
-	}
90
-
91
-	public function stat(string $path): array|false {
92
-		return $this->getWrapperStorage()->stat($this->getUnjailedPath($path));
93
-	}
94
-
95
-	public function filetype(string $path): string|false {
96
-		return $this->getWrapperStorage()->filetype($this->getUnjailedPath($path));
97
-	}
98
-
99
-	public function filesize(string $path): int|float|false {
100
-		return $this->getWrapperStorage()->filesize($this->getUnjailedPath($path));
101
-	}
102
-
103
-	public function isCreatable(string $path): bool {
104
-		return $this->getWrapperStorage()->isCreatable($this->getUnjailedPath($path));
105
-	}
106
-
107
-	public function isReadable(string $path): bool {
108
-		return $this->getWrapperStorage()->isReadable($this->getUnjailedPath($path));
109
-	}
110
-
111
-	public function isUpdatable(string $path): bool {
112
-		return $this->getWrapperStorage()->isUpdatable($this->getUnjailedPath($path));
113
-	}
114
-
115
-	public function isDeletable(string $path): bool {
116
-		return $this->getWrapperStorage()->isDeletable($this->getUnjailedPath($path));
117
-	}
118
-
119
-	public function isSharable(string $path): bool {
120
-		return $this->getWrapperStorage()->isSharable($this->getUnjailedPath($path));
121
-	}
122
-
123
-	public function getPermissions(string $path): int {
124
-		return $this->getWrapperStorage()->getPermissions($this->getUnjailedPath($path));
125
-	}
126
-
127
-	public function file_exists(string $path): bool {
128
-		return $this->getWrapperStorage()->file_exists($this->getUnjailedPath($path));
129
-	}
130
-
131
-	public function filemtime(string $path): int|false {
132
-		return $this->getWrapperStorage()->filemtime($this->getUnjailedPath($path));
133
-	}
134
-
135
-	public function file_get_contents(string $path): string|false {
136
-		return $this->getWrapperStorage()->file_get_contents($this->getUnjailedPath($path));
137
-	}
138
-
139
-	public function file_put_contents(string $path, mixed $data): int|float|false {
140
-		return $this->getWrapperStorage()->file_put_contents($this->getUnjailedPath($path), $data);
141
-	}
142
-
143
-	public function unlink(string $path): bool {
144
-		return $this->getWrapperStorage()->unlink($this->getUnjailedPath($path));
145
-	}
146
-
147
-	public function rename(string $source, string $target): bool {
148
-		return $this->getWrapperStorage()->rename($this->getUnjailedPath($source), $this->getUnjailedPath($target));
149
-	}
150
-
151
-	public function copy(string $source, string $target): bool {
152
-		return $this->getWrapperStorage()->copy($this->getUnjailedPath($source), $this->getUnjailedPath($target));
153
-	}
154
-
155
-	public function fopen(string $path, string $mode) {
156
-		return $this->getWrapperStorage()->fopen($this->getUnjailedPath($path), $mode);
157
-	}
158
-
159
-	public function getMimeType(string $path): string|false {
160
-		return $this->getWrapperStorage()->getMimeType($this->getUnjailedPath($path));
161
-	}
162
-
163
-	public function hash(string $type, string $path, bool $raw = false): string|false {
164
-		return $this->getWrapperStorage()->hash($type, $this->getUnjailedPath($path), $raw);
165
-	}
166
-
167
-	public function free_space(string $path): int|float|false {
168
-		return $this->getWrapperStorage()->free_space($this->getUnjailedPath($path));
169
-	}
170
-
171
-	public function touch(string $path, ?int $mtime = null): bool {
172
-		return $this->getWrapperStorage()->touch($this->getUnjailedPath($path), $mtime);
173
-	}
174
-
175
-	public function getLocalFile(string $path): string|false {
176
-		return $this->getWrapperStorage()->getLocalFile($this->getUnjailedPath($path));
177
-	}
178
-
179
-	public function hasUpdated(string $path, int $time): bool {
180
-		return $this->getWrapperStorage()->hasUpdated($this->getUnjailedPath($path), $time);
181
-	}
182
-
183
-	public function getCache(string $path = '', ?IStorage $storage = null): ICache {
184
-		$sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path));
185
-		return new CacheJail($sourceCache, $this->rootPath);
186
-	}
187
-
188
-	public function getOwner(string $path): string|false {
189
-		return $this->getWrapperStorage()->getOwner($this->getUnjailedPath($path));
190
-	}
191
-
192
-	public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher {
193
-		$sourceWatcher = $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $this->getWrapperStorage());
194
-		return new JailWatcher($sourceWatcher, $this->rootPath);
195
-	}
196
-
197
-	public function getETag(string $path): string|false {
198
-		return $this->getWrapperStorage()->getETag($this->getUnjailedPath($path));
199
-	}
200
-
201
-	public function getMetaData(string $path): ?array {
202
-		return $this->getWrapperStorage()->getMetaData($this->getUnjailedPath($path));
203
-	}
204
-
205
-	public function acquireLock(string $path, int $type, ILockingProvider $provider): void {
206
-		$this->getWrapperStorage()->acquireLock($this->getUnjailedPath($path), $type, $provider);
207
-	}
208
-
209
-	public function releaseLock(string $path, int $type, ILockingProvider $provider): void {
210
-		$this->getWrapperStorage()->releaseLock($this->getUnjailedPath($path), $type, $provider);
211
-	}
212
-
213
-	public function changeLock(string $path, int $type, ILockingProvider $provider): void {
214
-		$this->getWrapperStorage()->changeLock($this->getUnjailedPath($path), $type, $provider);
215
-	}
216
-
217
-	/**
218
-	 * Resolve the path for the source of the share.
219
-	 *
220
-	 * @return array{0: IStorage, 1: string}
221
-	 */
222
-	public function resolvePath(string $path): array {
223
-		return [$this->getWrapperStorage(), $this->getUnjailedPath($path)];
224
-	}
225
-
226
-	public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
227
-		if ($sourceStorage === $this) {
228
-			return $this->copy($sourceInternalPath, $targetInternalPath);
229
-		}
230
-		return $this->getWrapperStorage()->copyFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
231
-	}
232
-
233
-	public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
234
-		if ($sourceStorage === $this) {
235
-			return $this->rename($sourceInternalPath, $targetInternalPath);
236
-		}
237
-		return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
238
-	}
239
-
240
-	public function getPropagator(?IStorage $storage = null): IPropagator {
241
-		if (isset($this->propagator)) {
242
-			return $this->propagator;
243
-		}
244
-
245
-		if (!$storage) {
246
-			$storage = $this;
247
-		}
248
-		$this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection());
249
-		return $this->propagator;
250
-	}
251
-
252
-	public function writeStream(string $path, $stream, ?int $size = null): int {
253
-		$storage = $this->getWrapperStorage();
254
-		if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
255
-			/** @var IWriteStreamStorage $storage */
256
-			return $storage->writeStream($this->getUnjailedPath($path), $stream, $size);
257
-		} else {
258
-			$target = $this->fopen($path, 'w');
259
-			$count = Files::streamCopy($stream, $target);
260
-			fclose($stream);
261
-			fclose($target);
262
-			return $count;
263
-		}
264
-	}
265
-
266
-	public function getDirectoryContent(string $directory): \Traversable {
267
-		return $this->getWrapperStorage()->getDirectoryContent($this->getUnjailedPath($directory));
268
-	}
28
+    /**
29
+     * @var string
30
+     */
31
+    protected $rootPath;
32
+
33
+    /**
34
+     * @param array $parameters ['storage' => $storage, 'root' => $root]
35
+     *
36
+     * $storage: The storage that will be wrapper
37
+     * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage
38
+     */
39
+    public function __construct(array $parameters) {
40
+        parent::__construct($parameters);
41
+        $this->rootPath = $parameters['root'];
42
+    }
43
+
44
+    public function getUnjailedPath(string $path): string {
45
+        return trim(Filesystem::normalizePath($this->rootPath . '/' . $path), '/');
46
+    }
47
+
48
+    /**
49
+     * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper
50
+     */
51
+    public function getUnjailedStorage(): IStorage {
52
+        return $this->storage;
53
+    }
54
+
55
+
56
+    public function getJailedPath(string $path): ?string {
57
+        $root = rtrim($this->rootPath, '/') . '/';
58
+
59
+        if ($path !== $this->rootPath && !str_starts_with($path, $root)) {
60
+            return null;
61
+        } else {
62
+            $path = substr($path, strlen($this->rootPath));
63
+            return trim($path, '/');
64
+        }
65
+    }
66
+
67
+    public function getId(): string {
68
+        return parent::getId();
69
+    }
70
+
71
+    public function mkdir(string $path): bool {
72
+        return $this->getWrapperStorage()->mkdir($this->getUnjailedPath($path));
73
+    }
74
+
75
+    public function rmdir(string $path): bool {
76
+        return $this->getWrapperStorage()->rmdir($this->getUnjailedPath($path));
77
+    }
78
+
79
+    public function opendir(string $path) {
80
+        return $this->getWrapperStorage()->opendir($this->getUnjailedPath($path));
81
+    }
82
+
83
+    public function is_dir(string $path): bool {
84
+        return $this->getWrapperStorage()->is_dir($this->getUnjailedPath($path));
85
+    }
86
+
87
+    public function is_file(string $path): bool {
88
+        return $this->getWrapperStorage()->is_file($this->getUnjailedPath($path));
89
+    }
90
+
91
+    public function stat(string $path): array|false {
92
+        return $this->getWrapperStorage()->stat($this->getUnjailedPath($path));
93
+    }
94
+
95
+    public function filetype(string $path): string|false {
96
+        return $this->getWrapperStorage()->filetype($this->getUnjailedPath($path));
97
+    }
98
+
99
+    public function filesize(string $path): int|float|false {
100
+        return $this->getWrapperStorage()->filesize($this->getUnjailedPath($path));
101
+    }
102
+
103
+    public function isCreatable(string $path): bool {
104
+        return $this->getWrapperStorage()->isCreatable($this->getUnjailedPath($path));
105
+    }
106
+
107
+    public function isReadable(string $path): bool {
108
+        return $this->getWrapperStorage()->isReadable($this->getUnjailedPath($path));
109
+    }
110
+
111
+    public function isUpdatable(string $path): bool {
112
+        return $this->getWrapperStorage()->isUpdatable($this->getUnjailedPath($path));
113
+    }
114
+
115
+    public function isDeletable(string $path): bool {
116
+        return $this->getWrapperStorage()->isDeletable($this->getUnjailedPath($path));
117
+    }
118
+
119
+    public function isSharable(string $path): bool {
120
+        return $this->getWrapperStorage()->isSharable($this->getUnjailedPath($path));
121
+    }
122
+
123
+    public function getPermissions(string $path): int {
124
+        return $this->getWrapperStorage()->getPermissions($this->getUnjailedPath($path));
125
+    }
126
+
127
+    public function file_exists(string $path): bool {
128
+        return $this->getWrapperStorage()->file_exists($this->getUnjailedPath($path));
129
+    }
130
+
131
+    public function filemtime(string $path): int|false {
132
+        return $this->getWrapperStorage()->filemtime($this->getUnjailedPath($path));
133
+    }
134
+
135
+    public function file_get_contents(string $path): string|false {
136
+        return $this->getWrapperStorage()->file_get_contents($this->getUnjailedPath($path));
137
+    }
138
+
139
+    public function file_put_contents(string $path, mixed $data): int|float|false {
140
+        return $this->getWrapperStorage()->file_put_contents($this->getUnjailedPath($path), $data);
141
+    }
142
+
143
+    public function unlink(string $path): bool {
144
+        return $this->getWrapperStorage()->unlink($this->getUnjailedPath($path));
145
+    }
146
+
147
+    public function rename(string $source, string $target): bool {
148
+        return $this->getWrapperStorage()->rename($this->getUnjailedPath($source), $this->getUnjailedPath($target));
149
+    }
150
+
151
+    public function copy(string $source, string $target): bool {
152
+        return $this->getWrapperStorage()->copy($this->getUnjailedPath($source), $this->getUnjailedPath($target));
153
+    }
154
+
155
+    public function fopen(string $path, string $mode) {
156
+        return $this->getWrapperStorage()->fopen($this->getUnjailedPath($path), $mode);
157
+    }
158
+
159
+    public function getMimeType(string $path): string|false {
160
+        return $this->getWrapperStorage()->getMimeType($this->getUnjailedPath($path));
161
+    }
162
+
163
+    public function hash(string $type, string $path, bool $raw = false): string|false {
164
+        return $this->getWrapperStorage()->hash($type, $this->getUnjailedPath($path), $raw);
165
+    }
166
+
167
+    public function free_space(string $path): int|float|false {
168
+        return $this->getWrapperStorage()->free_space($this->getUnjailedPath($path));
169
+    }
170
+
171
+    public function touch(string $path, ?int $mtime = null): bool {
172
+        return $this->getWrapperStorage()->touch($this->getUnjailedPath($path), $mtime);
173
+    }
174
+
175
+    public function getLocalFile(string $path): string|false {
176
+        return $this->getWrapperStorage()->getLocalFile($this->getUnjailedPath($path));
177
+    }
178
+
179
+    public function hasUpdated(string $path, int $time): bool {
180
+        return $this->getWrapperStorage()->hasUpdated($this->getUnjailedPath($path), $time);
181
+    }
182
+
183
+    public function getCache(string $path = '', ?IStorage $storage = null): ICache {
184
+        $sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path));
185
+        return new CacheJail($sourceCache, $this->rootPath);
186
+    }
187
+
188
+    public function getOwner(string $path): string|false {
189
+        return $this->getWrapperStorage()->getOwner($this->getUnjailedPath($path));
190
+    }
191
+
192
+    public function getWatcher(string $path = '', ?IStorage $storage = null): IWatcher {
193
+        $sourceWatcher = $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $this->getWrapperStorage());
194
+        return new JailWatcher($sourceWatcher, $this->rootPath);
195
+    }
196
+
197
+    public function getETag(string $path): string|false {
198
+        return $this->getWrapperStorage()->getETag($this->getUnjailedPath($path));
199
+    }
200
+
201
+    public function getMetaData(string $path): ?array {
202
+        return $this->getWrapperStorage()->getMetaData($this->getUnjailedPath($path));
203
+    }
204
+
205
+    public function acquireLock(string $path, int $type, ILockingProvider $provider): void {
206
+        $this->getWrapperStorage()->acquireLock($this->getUnjailedPath($path), $type, $provider);
207
+    }
208
+
209
+    public function releaseLock(string $path, int $type, ILockingProvider $provider): void {
210
+        $this->getWrapperStorage()->releaseLock($this->getUnjailedPath($path), $type, $provider);
211
+    }
212
+
213
+    public function changeLock(string $path, int $type, ILockingProvider $provider): void {
214
+        $this->getWrapperStorage()->changeLock($this->getUnjailedPath($path), $type, $provider);
215
+    }
216
+
217
+    /**
218
+     * Resolve the path for the source of the share.
219
+     *
220
+     * @return array{0: IStorage, 1: string}
221
+     */
222
+    public function resolvePath(string $path): array {
223
+        return [$this->getWrapperStorage(), $this->getUnjailedPath($path)];
224
+    }
225
+
226
+    public function copyFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
227
+        if ($sourceStorage === $this) {
228
+            return $this->copy($sourceInternalPath, $targetInternalPath);
229
+        }
230
+        return $this->getWrapperStorage()->copyFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
231
+    }
232
+
233
+    public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
234
+        if ($sourceStorage === $this) {
235
+            return $this->rename($sourceInternalPath, $targetInternalPath);
236
+        }
237
+        return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getUnjailedPath($targetInternalPath));
238
+    }
239
+
240
+    public function getPropagator(?IStorage $storage = null): IPropagator {
241
+        if (isset($this->propagator)) {
242
+            return $this->propagator;
243
+        }
244
+
245
+        if (!$storage) {
246
+            $storage = $this;
247
+        }
248
+        $this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection());
249
+        return $this->propagator;
250
+    }
251
+
252
+    public function writeStream(string $path, $stream, ?int $size = null): int {
253
+        $storage = $this->getWrapperStorage();
254
+        if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
255
+            /** @var IWriteStreamStorage $storage */
256
+            return $storage->writeStream($this->getUnjailedPath($path), $stream, $size);
257
+        } else {
258
+            $target = $this->fopen($path, 'w');
259
+            $count = Files::streamCopy($stream, $target);
260
+            fclose($stream);
261
+            fclose($target);
262
+            return $count;
263
+        }
264
+    }
265
+
266
+    public function getDirectoryContent(string $directory): \Traversable {
267
+        return $this->getWrapperStorage()->getDirectoryContent($this->getUnjailedPath($directory));
268
+    }
269 269
 }
Please login to merge, or discard this patch.