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