From f642ad39614970de67358d80517bc1d76a006e0e Mon Sep 17 00:00:00 2001
From: Vincent Petry <pvince81@owncloud.com>
Date: Wed, 8 Jan 2014 13:17:36 +0100
Subject: [PATCH] Prevent deleting storage root

Storage mount points are not deletable, so make sure that the unlink
operation and its hooks aren't run in such cases.

Note that some storages might recursively delete their contents when
calling unlink on their root. This fix prevents that as well.
---
 lib/private/files/view.php | 13 ++++++++++++
 tests/lib/files/view.php   | 42 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index ac45a88133..8893911ed5 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -336,6 +336,19 @@ class View {
 	}
 
 	public function unlink($path) {
+		if ($path === '' || $path === '/') {
+			// do not allow deleting the root
+			return false;
+		}
+		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
+		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
+		list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
+		if (!$internalPath || $internalPath === '' || $internalPath === '/') {
+			// do not allow deleting the storage's root / the mount point
+			// because for some storages it might delete the whole contents
+			// but isn't supposed to work that way
+			return false;
+		}
 		return $this->basicOperation('unlink', $path, array('delete'));
 	}
 
diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php
index b59cef9f0d..76a7fd5f1c 100644
--- a/tests/lib/files/view.php
+++ b/tests/lib/files/view.php
@@ -306,6 +306,48 @@ class View extends \PHPUnit_Framework_TestCase {
 		$this->assertTrue($rootView->file_exists('anotherfolder/bar.txt'));
 	}
 
+	/**
+	 * @medium
+	 */
+	function testUnlink() {
+		$storage1 = $this->getTestStorage();
+		$storage2 = $this->getTestStorage();
+		\OC\Files\Filesystem::mount($storage1, array(), '/');
+		\OC\Files\Filesystem::mount($storage2, array(), '/substorage');
+
+		$rootView = new \OC\Files\View('');
+		$rootView->file_put_contents('/foo.txt', 'asd');
+		$rootView->file_put_contents('/substorage/bar.txt', 'asd');
+
+		$this->assertTrue($rootView->file_exists('foo.txt'));
+		$this->assertTrue($rootView->file_exists('substorage/bar.txt'));
+
+		$this->assertTrue($rootView->unlink('foo.txt'));
+		$this->assertTrue($rootView->unlink('substorage/bar.txt'));
+
+		$this->assertFalse($rootView->file_exists('foo.txt'));
+		$this->assertFalse($rootView->file_exists('substorage/bar.txt'));
+	}
+
+	/**
+	 * @medium
+	 */
+	function testUnlinkRootMustFail() {
+		$storage1 = $this->getTestStorage();
+		$storage2 = $this->getTestStorage();
+		\OC\Files\Filesystem::mount($storage1, array(), '/');
+		\OC\Files\Filesystem::mount($storage2, array(), '/substorage');
+
+		$rootView = new \OC\Files\View('');
+		$rootView->file_put_contents('/foo.txt', 'asd');
+		$rootView->file_put_contents('/substorage/bar.txt', 'asd');
+
+		$this->assertFalse($rootView->unlink(''));
+		$this->assertFalse($rootView->unlink('/'));
+		$this->assertFalse($rootView->unlink('substorage'));
+		$this->assertFalse($rootView->unlink('/substorage'));
+	}
+
 	/**
 	 * @medium
 	 */
-- 
GitLab