Passed
Push — master ( ef8634...fb78cd )
by Roeland
16:39 queued 06:00
created
lib/private/Files/Storage/Local.php 1 patch
Indentation   +438 added lines, -438 removed lines patch added patch discarded remove patch
@@ -49,442 +49,442 @@
 block discarded – undo
49 49
  * for local filestore, we only have to map the paths
50 50
  */
51 51
 class Local extends \OC\Files\Storage\Common {
52
-	protected $datadir;
53
-
54
-	protected $dataDirLength;
55
-
56
-	protected $allowSymlinks = false;
57
-
58
-	protected $realDataDir;
59
-
60
-	public function __construct($arguments) {
61
-		if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
62
-			throw new \InvalidArgumentException('No data directory set for local storage');
63
-		}
64
-		$this->datadir = str_replace('//', '/', $arguments['datadir']);
65
-		// some crazy code uses a local storage on root...
66
-		if ($this->datadir === '/') {
67
-			$this->realDataDir = $this->datadir;
68
-		} else {
69
-			$realPath = realpath($this->datadir) ?: $this->datadir;
70
-			$this->realDataDir = rtrim($realPath, '/') . '/';
71
-		}
72
-		if (substr($this->datadir, -1) !== '/') {
73
-			$this->datadir .= '/';
74
-		}
75
-		$this->dataDirLength = strlen($this->realDataDir);
76
-	}
77
-
78
-	public function __destruct() {
79
-	}
80
-
81
-	public function getId() {
82
-		return 'local::' . $this->datadir;
83
-	}
84
-
85
-	public function mkdir($path) {
86
-		return @mkdir($this->getSourcePath($path), 0777, true);
87
-	}
88
-
89
-	public function rmdir($path) {
90
-		if (!$this->isDeletable($path)) {
91
-			return false;
92
-		}
93
-		try {
94
-			$it = new \RecursiveIteratorIterator(
95
-				new \RecursiveDirectoryIterator($this->getSourcePath($path)),
96
-				\RecursiveIteratorIterator::CHILD_FIRST
97
-			);
98
-			/**
99
-			 * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
100
-			 * This bug is fixed in PHP 5.5.9 or before
101
-			 * See #8376
102
-			 */
103
-			$it->rewind();
104
-			while ($it->valid()) {
105
-				/**
106
-				 * @var \SplFileInfo $file
107
-				 */
108
-				$file = $it->current();
109
-				if (in_array($file->getBasename(), array('.', '..'))) {
110
-					$it->next();
111
-					continue;
112
-				} elseif ($file->isDir()) {
113
-					rmdir($file->getPathname());
114
-				} elseif ($file->isFile() || $file->isLink()) {
115
-					unlink($file->getPathname());
116
-				}
117
-				$it->next();
118
-			}
119
-			return rmdir($this->getSourcePath($path));
120
-		} catch (\UnexpectedValueException $e) {
121
-			return false;
122
-		}
123
-	}
124
-
125
-	public function opendir($path) {
126
-		return opendir($this->getSourcePath($path));
127
-	}
128
-
129
-	public function is_dir($path) {
130
-		if (substr($path, -1) == '/') {
131
-			$path = substr($path, 0, -1);
132
-		}
133
-		return is_dir($this->getSourcePath($path));
134
-	}
135
-
136
-	public function is_file($path) {
137
-		return is_file($this->getSourcePath($path));
138
-	}
139
-
140
-	public function stat($path) {
141
-		clearstatcache();
142
-		$fullPath = $this->getSourcePath($path);
143
-		$statResult = stat($fullPath);
144
-		if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
145
-			$filesize = $this->filesize($path);
146
-			$statResult['size'] = $filesize;
147
-			$statResult[7] = $filesize;
148
-		}
149
-		return $statResult;
150
-	}
151
-
152
-	public function filetype($path) {
153
-		$filetype = filetype($this->getSourcePath($path));
154
-		if ($filetype == 'link') {
155
-			$filetype = filetype(realpath($this->getSourcePath($path)));
156
-		}
157
-		return $filetype;
158
-	}
159
-
160
-	public function filesize($path) {
161
-		if ($this->is_dir($path)) {
162
-			return 0;
163
-		}
164
-		$fullPath = $this->getSourcePath($path);
165
-		if (PHP_INT_SIZE === 4) {
166
-			$helper = new \OC\LargeFileHelper;
167
-			return $helper->getFileSize($fullPath);
168
-		}
169
-		return filesize($fullPath);
170
-	}
171
-
172
-	public function isReadable($path) {
173
-		return is_readable($this->getSourcePath($path));
174
-	}
175
-
176
-	public function isUpdatable($path) {
177
-		return is_writable($this->getSourcePath($path));
178
-	}
179
-
180
-	public function file_exists($path) {
181
-		return file_exists($this->getSourcePath($path));
182
-	}
183
-
184
-	public function filemtime($path) {
185
-		$fullPath = $this->getSourcePath($path);
186
-		clearstatcache(true, $fullPath);
187
-		if (!$this->file_exists($path)) {
188
-			return false;
189
-		}
190
-		if (PHP_INT_SIZE === 4) {
191
-			$helper = new \OC\LargeFileHelper();
192
-			return $helper->getFileMtime($fullPath);
193
-		}
194
-		return filemtime($fullPath);
195
-	}
196
-
197
-	public function touch($path, $mtime = null) {
198
-		// sets the modification time of the file to the given value.
199
-		// If mtime is nil the current time is set.
200
-		// note that the access time of the file always changes to the current time.
201
-		if ($this->file_exists($path) and !$this->isUpdatable($path)) {
202
-			return false;
203
-		}
204
-		if (!is_null($mtime)) {
205
-			$result = @touch($this->getSourcePath($path), $mtime);
206
-		} else {
207
-			$result = @touch($this->getSourcePath($path));
208
-		}
209
-		if ($result) {
210
-			clearstatcache(true, $this->getSourcePath($path));
211
-		}
212
-
213
-		return $result;
214
-	}
215
-
216
-	public function file_get_contents($path) {
217
-		return file_get_contents($this->getSourcePath($path));
218
-	}
219
-
220
-	public function file_put_contents($path, $data) {
221
-		return file_put_contents($this->getSourcePath($path), $data);
222
-	}
223
-
224
-	public function unlink($path) {
225
-		if ($this->is_dir($path)) {
226
-			return $this->rmdir($path);
227
-		} else if ($this->is_file($path)) {
228
-			return unlink($this->getSourcePath($path));
229
-		} else {
230
-			return false;
231
-		}
232
-
233
-	}
234
-
235
-	private function treeContainsBlacklistedFile(string $path): bool {
236
-		$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
237
-		foreach ($iterator as $file) {
238
-			/** @var \SplFileInfo $file */
239
-			if (Filesystem::isFileBlacklisted($file->getBasename())) {
240
-				return true;
241
-			}
242
-		}
243
-
244
-		return false;
245
-	}
246
-
247
-	public function rename($path1, $path2) {
248
-		$srcParent = dirname($path1);
249
-		$dstParent = dirname($path2);
250
-
251
-		if (!$this->isUpdatable($srcParent)) {
252
-			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, ILogger::ERROR);
253
-			return false;
254
-		}
255
-
256
-		if (!$this->isUpdatable($dstParent)) {
257
-			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, ILogger::ERROR);
258
-			return false;
259
-		}
260
-
261
-		if (!$this->file_exists($path1)) {
262
-			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, ILogger::ERROR);
263
-			return false;
264
-		}
265
-
266
-		if ($this->is_dir($path2)) {
267
-			$this->rmdir($path2);
268
-		} else if ($this->is_file($path2)) {
269
-			$this->unlink($path2);
270
-		}
271
-
272
-		if ($this->is_dir($path1)) {
273
-			// we can't move folders across devices, use copy instead
274
-			$stat1 = stat(dirname($this->getSourcePath($path1)));
275
-			$stat2 = stat(dirname($this->getSourcePath($path2)));
276
-			if ($stat1['dev'] !== $stat2['dev']) {
277
-				$result = $this->copy($path1, $path2);
278
-				if ($result) {
279
-					$result &= $this->rmdir($path1);
280
-				}
281
-				return $result;
282
-			}
283
-
284
-			if ($this->treeContainsBlacklistedFile($this->getSourcePath($path1))) {
285
-				throw new ForbiddenException('Invalid path', false);
286
-			}
287
-		}
288
-
289
-		return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
290
-	}
291
-
292
-	public function copy($path1, $path2) {
293
-		if ($this->is_dir($path1)) {
294
-			return parent::copy($path1, $path2);
295
-		} else {
296
-			return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
297
-		}
298
-	}
299
-
300
-	public function fopen($path, $mode) {
301
-		return fopen($this->getSourcePath($path), $mode);
302
-	}
303
-
304
-	public function hash($type, $path, $raw = false) {
305
-		return hash_file($type, $this->getSourcePath($path), $raw);
306
-	}
307
-
308
-	public function free_space($path) {
309
-		$sourcePath = $this->getSourcePath($path);
310
-		// using !is_dir because $sourcePath might be a part file or
311
-		// non-existing file, so we'd still want to use the parent dir
312
-		// in such cases
313
-		if (!is_dir($sourcePath)) {
314
-			// disk_free_space doesn't work on files
315
-			$sourcePath = dirname($sourcePath);
316
-		}
317
-		$space = @disk_free_space($sourcePath);
318
-		if ($space === false || is_null($space)) {
319
-			return \OCP\Files\FileInfo::SPACE_UNKNOWN;
320
-		}
321
-		return $space;
322
-	}
323
-
324
-	public function search($query) {
325
-		return $this->searchInDir($query);
326
-	}
327
-
328
-	public function getLocalFile($path) {
329
-		return $this->getSourcePath($path);
330
-	}
331
-
332
-	public function getLocalFolder($path) {
333
-		return $this->getSourcePath($path);
334
-	}
335
-
336
-	/**
337
-	 * @param string $query
338
-	 * @param string $dir
339
-	 * @return array
340
-	 */
341
-	protected function searchInDir($query, $dir = '') {
342
-		$files = array();
343
-		$physicalDir = $this->getSourcePath($dir);
344
-		foreach (scandir($physicalDir) as $item) {
345
-			if (\OC\Files\Filesystem::isIgnoredDir($item))
346
-				continue;
347
-			$physicalItem = $physicalDir . '/' . $item;
348
-
349
-			if (strstr(strtolower($item), strtolower($query)) !== false) {
350
-				$files[] = $dir . '/' . $item;
351
-			}
352
-			if (is_dir($physicalItem)) {
353
-				$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
354
-			}
355
-		}
356
-		return $files;
357
-	}
358
-
359
-	/**
360
-	 * check if a file or folder has been updated since $time
361
-	 *
362
-	 * @param string $path
363
-	 * @param int $time
364
-	 * @return bool
365
-	 */
366
-	public function hasUpdated($path, $time) {
367
-		if ($this->file_exists($path)) {
368
-			return $this->filemtime($path) > $time;
369
-		} else {
370
-			return true;
371
-		}
372
-	}
373
-
374
-	/**
375
-	 * Get the source path (on disk) of a given path
376
-	 *
377
-	 * @param string $path
378
-	 * @return string
379
-	 * @throws ForbiddenException
380
-	 */
381
-	public function getSourcePath($path) {
382
-		if (Filesystem::isFileBlacklisted($path)) {
383
-			throw new ForbiddenException('Invalid path', false);
384
-		}
385
-
386
-		$fullPath = $this->datadir . $path;
387
-		$currentPath = $path;
388
-		if ($this->allowSymlinks || $currentPath === '') {
389
-			return $fullPath;
390
-		}
391
-		$pathToResolve = $fullPath;
392
-		$realPath = realpath($pathToResolve);
393
-		while ($realPath === false) { // for non existing files check the parent directory
394
-			$currentPath = dirname($currentPath);
395
-			if ($currentPath === '' || $currentPath === '.') {
396
-				return $fullPath;
397
-			}
398
-			$realPath = realpath($this->datadir . $currentPath);
399
-		}
400
-		if ($realPath) {
401
-			$realPath = $realPath . '/';
402
-		}
403
-		if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
404
-			return $fullPath;
405
-		}
406
-
407
-		\OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", ILogger::ERROR);
408
-		throw new ForbiddenException('Following symlinks is not allowed', false);
409
-	}
410
-
411
-	/**
412
-	 * {@inheritdoc}
413
-	 */
414
-	public function isLocal() {
415
-		return true;
416
-	}
417
-
418
-	/**
419
-	 * get the ETag for a file or folder
420
-	 *
421
-	 * @param string $path
422
-	 * @return string
423
-	 */
424
-	public function getETag($path) {
425
-		if ($this->is_file($path)) {
426
-			$stat = $this->stat($path);
427
-			return md5(
428
-				$stat['mtime'] .
429
-				$stat['ino'] .
430
-				$stat['dev'] .
431
-				$stat['size']
432
-			);
433
-		} else {
434
-			return parent::getETag($path);
435
-		}
436
-	}
437
-
438
-	/**
439
-	 * @param IStorage $sourceStorage
440
-	 * @param string $sourceInternalPath
441
-	 * @param string $targetInternalPath
442
-	 * @param bool $preserveMtime
443
-	 * @return bool
444
-	 */
445
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
446
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
447
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
448
-				/**
449
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
450
-				 */
451
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
452
-			}
453
-			/**
454
-			 * @var \OC\Files\Storage\Local $sourceStorage
455
-			 */
456
-			$rootStorage = new Local(['datadir' => '/']);
457
-			return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
458
-		} else {
459
-			return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
460
-		}
461
-	}
462
-
463
-	/**
464
-	 * @param IStorage $sourceStorage
465
-	 * @param string $sourceInternalPath
466
-	 * @param string $targetInternalPath
467
-	 * @return bool
468
-	 */
469
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
470
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
471
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
472
-				/**
473
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
474
-				 */
475
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
476
-			}
477
-			/**
478
-			 * @var \OC\Files\Storage\Local $sourceStorage
479
-			 */
480
-			$rootStorage = new Local(['datadir' => '/']);
481
-			return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
482
-		} else {
483
-			return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
484
-		}
485
-	}
486
-
487
-	public function writeStream(string $path, $stream, int $size = null): int {
488
-		return (int)file_put_contents($this->getSourcePath($path), $stream);
489
-	}
52
+    protected $datadir;
53
+
54
+    protected $dataDirLength;
55
+
56
+    protected $allowSymlinks = false;
57
+
58
+    protected $realDataDir;
59
+
60
+    public function __construct($arguments) {
61
+        if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
62
+            throw new \InvalidArgumentException('No data directory set for local storage');
63
+        }
64
+        $this->datadir = str_replace('//', '/', $arguments['datadir']);
65
+        // some crazy code uses a local storage on root...
66
+        if ($this->datadir === '/') {
67
+            $this->realDataDir = $this->datadir;
68
+        } else {
69
+            $realPath = realpath($this->datadir) ?: $this->datadir;
70
+            $this->realDataDir = rtrim($realPath, '/') . '/';
71
+        }
72
+        if (substr($this->datadir, -1) !== '/') {
73
+            $this->datadir .= '/';
74
+        }
75
+        $this->dataDirLength = strlen($this->realDataDir);
76
+    }
77
+
78
+    public function __destruct() {
79
+    }
80
+
81
+    public function getId() {
82
+        return 'local::' . $this->datadir;
83
+    }
84
+
85
+    public function mkdir($path) {
86
+        return @mkdir($this->getSourcePath($path), 0777, true);
87
+    }
88
+
89
+    public function rmdir($path) {
90
+        if (!$this->isDeletable($path)) {
91
+            return false;
92
+        }
93
+        try {
94
+            $it = new \RecursiveIteratorIterator(
95
+                new \RecursiveDirectoryIterator($this->getSourcePath($path)),
96
+                \RecursiveIteratorIterator::CHILD_FIRST
97
+            );
98
+            /**
99
+             * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
100
+             * This bug is fixed in PHP 5.5.9 or before
101
+             * See #8376
102
+             */
103
+            $it->rewind();
104
+            while ($it->valid()) {
105
+                /**
106
+                 * @var \SplFileInfo $file
107
+                 */
108
+                $file = $it->current();
109
+                if (in_array($file->getBasename(), array('.', '..'))) {
110
+                    $it->next();
111
+                    continue;
112
+                } elseif ($file->isDir()) {
113
+                    rmdir($file->getPathname());
114
+                } elseif ($file->isFile() || $file->isLink()) {
115
+                    unlink($file->getPathname());
116
+                }
117
+                $it->next();
118
+            }
119
+            return rmdir($this->getSourcePath($path));
120
+        } catch (\UnexpectedValueException $e) {
121
+            return false;
122
+        }
123
+    }
124
+
125
+    public function opendir($path) {
126
+        return opendir($this->getSourcePath($path));
127
+    }
128
+
129
+    public function is_dir($path) {
130
+        if (substr($path, -1) == '/') {
131
+            $path = substr($path, 0, -1);
132
+        }
133
+        return is_dir($this->getSourcePath($path));
134
+    }
135
+
136
+    public function is_file($path) {
137
+        return is_file($this->getSourcePath($path));
138
+    }
139
+
140
+    public function stat($path) {
141
+        clearstatcache();
142
+        $fullPath = $this->getSourcePath($path);
143
+        $statResult = stat($fullPath);
144
+        if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
145
+            $filesize = $this->filesize($path);
146
+            $statResult['size'] = $filesize;
147
+            $statResult[7] = $filesize;
148
+        }
149
+        return $statResult;
150
+    }
151
+
152
+    public function filetype($path) {
153
+        $filetype = filetype($this->getSourcePath($path));
154
+        if ($filetype == 'link') {
155
+            $filetype = filetype(realpath($this->getSourcePath($path)));
156
+        }
157
+        return $filetype;
158
+    }
159
+
160
+    public function filesize($path) {
161
+        if ($this->is_dir($path)) {
162
+            return 0;
163
+        }
164
+        $fullPath = $this->getSourcePath($path);
165
+        if (PHP_INT_SIZE === 4) {
166
+            $helper = new \OC\LargeFileHelper;
167
+            return $helper->getFileSize($fullPath);
168
+        }
169
+        return filesize($fullPath);
170
+    }
171
+
172
+    public function isReadable($path) {
173
+        return is_readable($this->getSourcePath($path));
174
+    }
175
+
176
+    public function isUpdatable($path) {
177
+        return is_writable($this->getSourcePath($path));
178
+    }
179
+
180
+    public function file_exists($path) {
181
+        return file_exists($this->getSourcePath($path));
182
+    }
183
+
184
+    public function filemtime($path) {
185
+        $fullPath = $this->getSourcePath($path);
186
+        clearstatcache(true, $fullPath);
187
+        if (!$this->file_exists($path)) {
188
+            return false;
189
+        }
190
+        if (PHP_INT_SIZE === 4) {
191
+            $helper = new \OC\LargeFileHelper();
192
+            return $helper->getFileMtime($fullPath);
193
+        }
194
+        return filemtime($fullPath);
195
+    }
196
+
197
+    public function touch($path, $mtime = null) {
198
+        // sets the modification time of the file to the given value.
199
+        // If mtime is nil the current time is set.
200
+        // note that the access time of the file always changes to the current time.
201
+        if ($this->file_exists($path) and !$this->isUpdatable($path)) {
202
+            return false;
203
+        }
204
+        if (!is_null($mtime)) {
205
+            $result = @touch($this->getSourcePath($path), $mtime);
206
+        } else {
207
+            $result = @touch($this->getSourcePath($path));
208
+        }
209
+        if ($result) {
210
+            clearstatcache(true, $this->getSourcePath($path));
211
+        }
212
+
213
+        return $result;
214
+    }
215
+
216
+    public function file_get_contents($path) {
217
+        return file_get_contents($this->getSourcePath($path));
218
+    }
219
+
220
+    public function file_put_contents($path, $data) {
221
+        return file_put_contents($this->getSourcePath($path), $data);
222
+    }
223
+
224
+    public function unlink($path) {
225
+        if ($this->is_dir($path)) {
226
+            return $this->rmdir($path);
227
+        } else if ($this->is_file($path)) {
228
+            return unlink($this->getSourcePath($path));
229
+        } else {
230
+            return false;
231
+        }
232
+
233
+    }
234
+
235
+    private function treeContainsBlacklistedFile(string $path): bool {
236
+        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
237
+        foreach ($iterator as $file) {
238
+            /** @var \SplFileInfo $file */
239
+            if (Filesystem::isFileBlacklisted($file->getBasename())) {
240
+                return true;
241
+            }
242
+        }
243
+
244
+        return false;
245
+    }
246
+
247
+    public function rename($path1, $path2) {
248
+        $srcParent = dirname($path1);
249
+        $dstParent = dirname($path2);
250
+
251
+        if (!$this->isUpdatable($srcParent)) {
252
+            \OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, ILogger::ERROR);
253
+            return false;
254
+        }
255
+
256
+        if (!$this->isUpdatable($dstParent)) {
257
+            \OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, ILogger::ERROR);
258
+            return false;
259
+        }
260
+
261
+        if (!$this->file_exists($path1)) {
262
+            \OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, ILogger::ERROR);
263
+            return false;
264
+        }
265
+
266
+        if ($this->is_dir($path2)) {
267
+            $this->rmdir($path2);
268
+        } else if ($this->is_file($path2)) {
269
+            $this->unlink($path2);
270
+        }
271
+
272
+        if ($this->is_dir($path1)) {
273
+            // we can't move folders across devices, use copy instead
274
+            $stat1 = stat(dirname($this->getSourcePath($path1)));
275
+            $stat2 = stat(dirname($this->getSourcePath($path2)));
276
+            if ($stat1['dev'] !== $stat2['dev']) {
277
+                $result = $this->copy($path1, $path2);
278
+                if ($result) {
279
+                    $result &= $this->rmdir($path1);
280
+                }
281
+                return $result;
282
+            }
283
+
284
+            if ($this->treeContainsBlacklistedFile($this->getSourcePath($path1))) {
285
+                throw new ForbiddenException('Invalid path', false);
286
+            }
287
+        }
288
+
289
+        return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
290
+    }
291
+
292
+    public function copy($path1, $path2) {
293
+        if ($this->is_dir($path1)) {
294
+            return parent::copy($path1, $path2);
295
+        } else {
296
+            return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
297
+        }
298
+    }
299
+
300
+    public function fopen($path, $mode) {
301
+        return fopen($this->getSourcePath($path), $mode);
302
+    }
303
+
304
+    public function hash($type, $path, $raw = false) {
305
+        return hash_file($type, $this->getSourcePath($path), $raw);
306
+    }
307
+
308
+    public function free_space($path) {
309
+        $sourcePath = $this->getSourcePath($path);
310
+        // using !is_dir because $sourcePath might be a part file or
311
+        // non-existing file, so we'd still want to use the parent dir
312
+        // in such cases
313
+        if (!is_dir($sourcePath)) {
314
+            // disk_free_space doesn't work on files
315
+            $sourcePath = dirname($sourcePath);
316
+        }
317
+        $space = @disk_free_space($sourcePath);
318
+        if ($space === false || is_null($space)) {
319
+            return \OCP\Files\FileInfo::SPACE_UNKNOWN;
320
+        }
321
+        return $space;
322
+    }
323
+
324
+    public function search($query) {
325
+        return $this->searchInDir($query);
326
+    }
327
+
328
+    public function getLocalFile($path) {
329
+        return $this->getSourcePath($path);
330
+    }
331
+
332
+    public function getLocalFolder($path) {
333
+        return $this->getSourcePath($path);
334
+    }
335
+
336
+    /**
337
+     * @param string $query
338
+     * @param string $dir
339
+     * @return array
340
+     */
341
+    protected function searchInDir($query, $dir = '') {
342
+        $files = array();
343
+        $physicalDir = $this->getSourcePath($dir);
344
+        foreach (scandir($physicalDir) as $item) {
345
+            if (\OC\Files\Filesystem::isIgnoredDir($item))
346
+                continue;
347
+            $physicalItem = $physicalDir . '/' . $item;
348
+
349
+            if (strstr(strtolower($item), strtolower($query)) !== false) {
350
+                $files[] = $dir . '/' . $item;
351
+            }
352
+            if (is_dir($physicalItem)) {
353
+                $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
354
+            }
355
+        }
356
+        return $files;
357
+    }
358
+
359
+    /**
360
+     * check if a file or folder has been updated since $time
361
+     *
362
+     * @param string $path
363
+     * @param int $time
364
+     * @return bool
365
+     */
366
+    public function hasUpdated($path, $time) {
367
+        if ($this->file_exists($path)) {
368
+            return $this->filemtime($path) > $time;
369
+        } else {
370
+            return true;
371
+        }
372
+    }
373
+
374
+    /**
375
+     * Get the source path (on disk) of a given path
376
+     *
377
+     * @param string $path
378
+     * @return string
379
+     * @throws ForbiddenException
380
+     */
381
+    public function getSourcePath($path) {
382
+        if (Filesystem::isFileBlacklisted($path)) {
383
+            throw new ForbiddenException('Invalid path', false);
384
+        }
385
+
386
+        $fullPath = $this->datadir . $path;
387
+        $currentPath = $path;
388
+        if ($this->allowSymlinks || $currentPath === '') {
389
+            return $fullPath;
390
+        }
391
+        $pathToResolve = $fullPath;
392
+        $realPath = realpath($pathToResolve);
393
+        while ($realPath === false) { // for non existing files check the parent directory
394
+            $currentPath = dirname($currentPath);
395
+            if ($currentPath === '' || $currentPath === '.') {
396
+                return $fullPath;
397
+            }
398
+            $realPath = realpath($this->datadir . $currentPath);
399
+        }
400
+        if ($realPath) {
401
+            $realPath = $realPath . '/';
402
+        }
403
+        if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
404
+            return $fullPath;
405
+        }
406
+
407
+        \OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", ILogger::ERROR);
408
+        throw new ForbiddenException('Following symlinks is not allowed', false);
409
+    }
410
+
411
+    /**
412
+     * {@inheritdoc}
413
+     */
414
+    public function isLocal() {
415
+        return true;
416
+    }
417
+
418
+    /**
419
+     * get the ETag for a file or folder
420
+     *
421
+     * @param string $path
422
+     * @return string
423
+     */
424
+    public function getETag($path) {
425
+        if ($this->is_file($path)) {
426
+            $stat = $this->stat($path);
427
+            return md5(
428
+                $stat['mtime'] .
429
+                $stat['ino'] .
430
+                $stat['dev'] .
431
+                $stat['size']
432
+            );
433
+        } else {
434
+            return parent::getETag($path);
435
+        }
436
+    }
437
+
438
+    /**
439
+     * @param IStorage $sourceStorage
440
+     * @param string $sourceInternalPath
441
+     * @param string $targetInternalPath
442
+     * @param bool $preserveMtime
443
+     * @return bool
444
+     */
445
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
446
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
447
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
448
+                /**
449
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
450
+                 */
451
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
452
+            }
453
+            /**
454
+             * @var \OC\Files\Storage\Local $sourceStorage
455
+             */
456
+            $rootStorage = new Local(['datadir' => '/']);
457
+            return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
458
+        } else {
459
+            return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
460
+        }
461
+    }
462
+
463
+    /**
464
+     * @param IStorage $sourceStorage
465
+     * @param string $sourceInternalPath
466
+     * @param string $targetInternalPath
467
+     * @return bool
468
+     */
469
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
470
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
471
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
472
+                /**
473
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
474
+                 */
475
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
476
+            }
477
+            /**
478
+             * @var \OC\Files\Storage\Local $sourceStorage
479
+             */
480
+            $rootStorage = new Local(['datadir' => '/']);
481
+            return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
482
+        } else {
483
+            return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
484
+        }
485
+    }
486
+
487
+    public function writeStream(string $path, $stream, int $size = null): int {
488
+        return (int)file_put_contents($this->getSourcePath($path), $stream);
489
+    }
490 490
 }
Please login to merge, or discard this patch.