diff --git a/lib/files/exceptions.php b/lib/files/exceptions.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a3c40ab0c07ae9f531b92a4a8a69373e38faad4
--- /dev/null
+++ b/lib/files/exceptions.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files;
+
+class NotFoundException extends \Exception {
+}
+
+class NotPermittedException extends \Exception {
+}
+
+class AlreadyExistsException extends \Exception {
+}
+
+class NotEnoughSpaceException extends \Exception {
+}
diff --git a/lib/files/node/file.php b/lib/files/node/file.php
new file mode 100644
index 0000000000000000000000000000000000000000..0ad5d68ec664e1f800a584386f6bd75638fcb0f0
--- /dev/null
+++ b/lib/files/node/file.php
@@ -0,0 +1,155 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\NotPermittedException;
+
+class File extends Node {
+	/**
+	 * @return string
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function getContent() {
+		if ($this->checkPermissions(\OCP\PERMISSION_READ)) {
+			/**
+			 * @var \OC\Files\Storage\Storage $storage;
+			 */
+			return $this->view->file_get_contents($this->path);
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $data
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function putContent($data) {
+		if ($this->checkPermissions(\OCP\PERMISSION_UPDATE)) {
+			$this->sendHooks(array('preWrite'));
+			$this->view->file_put_contents($this->path, $data);
+			$this->sendHooks(array('postWrite'));
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getMimeType() {
+		return $this->view->getMimeType($this->path);
+	}
+
+	/**
+	 * @param string $mode
+	 * @return resource
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function fopen($mode) {
+		$preHooks = array();
+		$postHooks = array();
+		$requiredPermissions = \OCP\PERMISSION_READ;
+		switch ($mode) {
+			case 'r+':
+			case 'rb+':
+			case 'w+':
+			case 'wb+':
+			case 'x+':
+			case 'xb+':
+			case 'a+':
+			case 'ab+':
+			case 'w':
+			case 'wb':
+			case 'x':
+			case 'xb':
+			case 'a':
+			case 'ab':
+				$preHooks[] = 'preWrite';
+				$postHooks[] = 'postWrite';
+				$requiredPermissions |= \OCP\PERMISSION_UPDATE;
+				break;
+		}
+
+		if ($this->checkPermissions($requiredPermissions)) {
+			$this->sendHooks($preHooks);
+			$result = $this->view->fopen($this->path, $mode);
+			$this->sendHooks($postHooks);
+			return $result;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	public function delete() {
+		if ($this->checkPermissions(\OCP\PERMISSION_DELETE)) {
+			$this->sendHooks(array('preDelete'));
+			$this->view->unlink($this->path);
+			$nonExisting = new NonExistingFile($this->root, $this->view, $this->path);
+			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
+			$this->exists = false;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function copy($targetPath) {
+		$targetPath = $this->normalizePath($targetPath);
+		$parent = $this->root->get(dirname($targetPath));
+		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+			$nonExisting = new NonExistingFile($this->root, $this->view, $targetPath);
+			$this->root->emit('\OC\Files', 'preCopy', array($this, $nonExisting));
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->view->copy($this->path, $targetPath);
+			$targetNode = $this->root->get($targetPath);
+			$this->root->emit('\OC\Files', 'postCopy', array($this, $targetNode));
+			$this->root->emit('\OC\Files', 'postWrite', array($targetNode));
+			return $targetNode;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function move($targetPath) {
+		$targetPath = $this->normalizePath($targetPath);
+		$parent = $this->root->get(dirname($targetPath));
+		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+			$nonExisting = new NonExistingFile($this->root, $this->view, $targetPath);
+			$this->root->emit('\OC\Files', 'preRename', array($this, $nonExisting));
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->view->rename($this->path, $targetPath);
+			$targetNode = $this->root->get($targetPath);
+			$this->root->emit('\OC\Files', 'postRename', array($this, $targetNode));
+			$this->root->emit('\OC\Files', 'postWrite', array($targetNode));
+			$this->path = $targetPath;
+			return $targetNode;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $type
+	 * @param bool $raw
+	 * @return string
+	 */
+	public function hash($type, $raw = false) {
+		return $this->view->hash($type, $this->path, $raw);
+	}
+}
diff --git a/lib/files/node/folder.php b/lib/files/node/folder.php
new file mode 100644
index 0000000000000000000000000000000000000000..f710ae5ae9a9140aa0fd90098c5e9e7df5d237c4
--- /dev/null
+++ b/lib/files/node/folder.php
@@ -0,0 +1,382 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\Cache\Scanner;
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+
+class Folder extends Node {
+	/**
+	 * @param string $path path relative to the folder
+	 * @return string
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function getFullPath($path) {
+		if (!$this->isValidPath($path)) {
+			throw new NotPermittedException();
+		}
+		return $this->path . $this->normalizePath($path);
+	}
+
+	/**
+	 * @param string $path
+	 * @throws \OC\Files\NotFoundException
+	 * @return string
+	 */
+	public function getRelativePath($path) {
+		if ($this->path === '' or $this->path === '/') {
+			return $this->normalizePath($path);
+		}
+		if (strpos($path, $this->path) !== 0) {
+			throw new NotFoundException();
+		} else {
+			$path = substr($path, strlen($this->path));
+			if (strlen($path) === 0) {
+				return '/';
+			} else {
+				return $this->normalizePath($path);
+			}
+		}
+	}
+
+	/**
+	 * check if a node is a (grand-)child of the folder
+	 *
+	 * @param \OC\Files\Node\Node $node
+	 * @return bool
+	 */
+	public function isSubNode($node) {
+		return strpos($node->getPath(), $this->path . '/') === 0;
+	}
+
+	/**
+	 * get the content of this directory
+	 *
+	 * @throws \OC\Files\NotFoundException
+	 * @return Node[]
+	 */
+	public function getDirectoryListing() {
+		$result = array();
+
+		/**
+		 * @var \OC\Files\Storage\Storage $storage
+		 */
+		list($storage, $internalPath) = $this->view->resolvePath($this->path);
+		if ($storage) {
+			$cache = $storage->getCache($internalPath);
+			$permissionsCache = $storage->getPermissionsCache($internalPath);
+
+			//trigger cache update check
+			$this->view->getFileInfo($this->path);
+
+			$files = $cache->getFolderContents($internalPath);
+			$permissions = $permissionsCache->getDirectoryPermissions($this->getId(), $this->root->getUser()->getUID());
+		} else {
+			$files = array();
+		}
+
+		//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
+		$mounts = $this->root->getMountsIn($this->path);
+		$dirLength = strlen($this->path);
+		foreach ($mounts as $mount) {
+			$subStorage = $mount->getStorage();
+			if ($subStorage) {
+				$subCache = $subStorage->getCache('');
+
+				if ($subCache->getStatus('') === Cache::NOT_FOUND) {
+					$subScanner = $subStorage->getScanner('');
+					$subScanner->scanFile('');
+				}
+
+				$rootEntry = $subCache->get('');
+				if ($rootEntry) {
+					$relativePath = trim(substr($mount->getMountPoint(), $dirLength), '/');
+					if ($pos = strpos($relativePath, '/')) {
+						//mountpoint inside subfolder add size to the correct folder
+						$entryName = substr($relativePath, 0, $pos);
+						foreach ($files as &$entry) {
+							if ($entry['name'] === $entryName) {
+								if ($rootEntry['size'] >= 0) {
+									$entry['size'] += $rootEntry['size'];
+								} else {
+									$entry['size'] = -1;
+								}
+							}
+						}
+					} else { //mountpoint in this folder, add an entry for it
+						$rootEntry['name'] = $relativePath;
+						$rootEntry['storageObject'] = $subStorage;
+
+						//remove any existing entry with the same name
+						foreach ($files as $i => $file) {
+							if ($file['name'] === $rootEntry['name']) {
+								$files[$i] = null;
+								break;
+							}
+						}
+						$files[] = $rootEntry;
+					}
+				}
+			}
+		}
+
+		foreach ($files as $file) {
+			if ($file) {
+				if (isset($permissions[$file['fileid']])) {
+					$file['permissions'] = $permissions[$file['fileid']];
+				}
+				$node = $this->createNode($this->path . '/' . $file['name'], $file);
+				$result[] = $node;
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * @param string $path
+	 * @param array $info
+	 * @return File|Folder
+	 */
+	protected function createNode($path, $info = array()) {
+		if (!isset($info['mimetype'])) {
+			$isDir = $this->view->is_dir($path);
+		} else {
+			$isDir = $info['mimetype'] === 'httpd/unix-directory';
+		}
+		if ($isDir) {
+			return new Folder($this->root, $this->view, $path);
+		} else {
+			return new File($this->root, $this->view, $path);
+		}
+	}
+
+	/**
+	 * Get the node at $path
+	 *
+	 * @param string $path
+	 * @return \OC\Files\Node\Node
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function get($path) {
+		return $this->root->get($this->getFullPath($path));
+	}
+
+	/**
+	 * @param string $path
+	 * @return bool
+	 */
+	public function nodeExists($path) {
+		try {
+			$this->get($path);
+			return true;
+		} catch (NotFoundException $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * @param string $path
+	 * @return Folder
+	 * @throws NotPermittedException
+	 */
+	public function newFolder($path) {
+		if ($this->checkPermissions(\OCP\PERMISSION_CREATE)) {
+			$fullPath = $this->getFullPath($path);
+			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
+			$this->view->mkdir($fullPath);
+			$node = new Folder($this->root, $this->view, $fullPath);
+			$this->root->emit('\OC\Files', 'postWrite', array($node));
+			$this->root->emit('\OC\Files', 'postCreate', array($node));
+			return $node;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $path
+	 * @return File
+	 * @throws NotPermittedException
+	 */
+	public function newFile($path) {
+		if ($this->checkPermissions(\OCP\PERMISSION_CREATE)) {
+			$fullPath = $this->getFullPath($path);
+			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
+			$this->view->touch($fullPath);
+			$node = new File($this->root, $this->view, $fullPath);
+			$this->root->emit('\OC\Files', 'postWrite', array($node));
+			$this->root->emit('\OC\Files', 'postCreate', array($node));
+			return $node;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * search for files with the name matching $query
+	 *
+	 * @param string $query
+	 * @return Node[]
+	 */
+	public function search($query) {
+		return $this->searchCommon('%' . $query . '%', 'search');
+	}
+
+	/**
+	 * search for files by mimetype
+	 *
+	 * @param string $mimetype
+	 * @return Node[]
+	 */
+	public function searchByMime($mimetype) {
+		return $this->searchCommon($mimetype, 'searchByMime');
+	}
+
+	/**
+	 * @param string $query
+	 * @param string $method
+	 * @return Node[]
+	 */
+	private function searchCommon($query, $method) {
+		$files = array();
+		$rootLength = strlen($this->path);
+		/**
+		 * @var \OC\Files\Storage\Storage $storage
+		 */
+		list($storage, $internalPath) = $this->view->resolvePath($this->path);
+		$internalRootLength = strlen($internalPath);
+
+		$cache = $storage->getCache('');
+
+		$results = $cache->$method($query);
+		foreach ($results as $result) {
+			if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
+				$result['internalPath'] = $result['path'];
+				$result['path'] = substr($result['path'], $internalRootLength);
+				$result['storage'] = $storage;
+				$files[] = $result;
+			}
+		}
+
+		$mounts = $this->root->getMountsIn($this->path);
+		foreach ($mounts as $mount) {
+			$storage = $mount->getStorage();
+			if ($storage) {
+				$cache = $storage->getCache('');
+
+				$relativeMountPoint = substr($mount->getMountPoint(), $rootLength);
+				$results = $cache->$method($query);
+				foreach ($results as $result) {
+					$result['internalPath'] = $result['path'];
+					$result['path'] = $relativeMountPoint . $result['path'];
+					$result['storage'] = $storage;
+					$files[] = $result;
+				}
+			}
+		}
+
+		$result = array();
+		foreach ($files as $file) {
+			$result[] = $this->createNode($this->normalizePath($this->path . '/' . $file['path']), $file);
+		}
+
+		return $result;
+	}
+
+	/**
+	 * @param $id
+	 * @return Node[]
+	 */
+	public function getById($id) {
+		$nodes = $this->root->getById($id);
+		$result = array();
+		foreach ($nodes as $node) {
+			$pathPart = substr($node->getPath(), 0, strlen($this->getPath()) + 1);
+			if ($this->path === '/' or $pathPart === $this->getPath() . '/') {
+				$result[] = $node;
+			}
+		}
+		return $result;
+	}
+
+	public function getFreeSpace() {
+		return $this->view->free_space($this->path);
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isCreatable() {
+		return $this->checkPermissions(\OCP\PERMISSION_CREATE);
+	}
+
+	public function delete() {
+		if ($this->checkPermissions(\OCP\PERMISSION_DELETE)) {
+			$this->sendHooks(array('preDelete'));
+			$this->view->rmdir($this->path);
+			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path);
+			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
+			$this->exists = false;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function copy($targetPath) {
+		$targetPath = $this->normalizePath($targetPath);
+		$parent = $this->root->get(dirname($targetPath));
+		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+			$nonExisting = new NonExistingFolder($this->root, $this->view, $targetPath);
+			$this->root->emit('\OC\Files', 'preCopy', array($this, $nonExisting));
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->view->copy($this->path, $targetPath);
+			$targetNode = $this->root->get($targetPath);
+			$this->root->emit('\OC\Files', 'postCopy', array($this, $targetNode));
+			$this->root->emit('\OC\Files', 'postWrite', array($targetNode));
+			return $targetNode;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function move($targetPath) {
+		$targetPath = $this->normalizePath($targetPath);
+		$parent = $this->root->get(dirname($targetPath));
+		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+			$nonExisting = new NonExistingFolder($this->root, $this->view, $targetPath);
+			$this->root->emit('\OC\Files', 'preRename', array($this, $nonExisting));
+			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
+			$this->view->rename($this->path, $targetPath);
+			$targetNode = $this->root->get($targetPath);
+			$this->root->emit('\OC\Files', 'postRename', array($this, $targetNode));
+			$this->root->emit('\OC\Files', 'postWrite', array($targetNode));
+			$this->path = $targetPath;
+			return $targetNode;
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+}
diff --git a/lib/files/node/node.php b/lib/files/node/node.php
new file mode 100644
index 0000000000000000000000000000000000000000..a71f78750645e03be922419727bf4eaa2f493185
--- /dev/null
+++ b/lib/files/node/node.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\Cache\Scanner;
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+
+require_once 'files/exceptions.php';
+
+class Node {
+	/**
+	 * @var \OC\Files\View $view
+	 */
+	protected $view;
+
+	/**
+	 * @var \OC\Files\Node\Root $root
+	 */
+	protected $root;
+
+	/**
+	 * @var string $path
+	 */
+	protected $path;
+
+	/**
+	 * @param \OC\Files\View $view
+	 * @param \OC\Files\Node\Root Root $root
+	 * @param string $path
+	 */
+	public function __construct($root, $view, $path) {
+		$this->view = $view;
+		$this->root = $root;
+		$this->path = $path;
+	}
+
+	/**
+	 * @param string[] $hooks
+	 */
+	protected function sendHooks($hooks) {
+		foreach ($hooks as $hook) {
+			$this->root->emit('\OC\Files', $hook, array($this));
+		}
+	}
+
+	/**
+	 * @param int $permissions
+	 * @return bool
+	 */
+	protected function checkPermissions($permissions) {
+		return ($this->getPermissions() & $permissions) == $permissions;
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function move($targetPath) {
+		return;
+	}
+
+	public function delete() {
+		return;
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @return \OC\Files\Node\Node
+	 */
+	public function copy($targetPath) {
+		return;
+	}
+
+	/**
+	 * @param int $mtime
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function touch($mtime = null) {
+		if ($this->checkPermissions(\OCP\PERMISSION_UPDATE)) {
+			$this->sendHooks(array('preTouch'));
+			$this->view->touch($this->path, $mtime);
+			$this->sendHooks(array('postTouch'));
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * @return \OC\Files\Storage\Storage
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function getStorage() {
+		list($storage,) = $this->view->resolvePath($this->path);
+		return $storage;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getPath() {
+		return $this->path;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getInternalPath() {
+		list(, $internalPath) = $this->view->resolvePath($this->path);
+		return $internalPath;
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getId() {
+		$info = $this->view->getFileInfo($this->path);
+		return $info['fileid'];
+	}
+
+	/**
+	 * @return array
+	 */
+	public function stat() {
+		return $this->view->stat($this->path);
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getMTime() {
+		return $this->view->filemtime($this->path);
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getSize() {
+		return $this->view->filesize($this->path);
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getEtag() {
+		$info = $this->view->getFileInfo($this->path);
+		return $info['etag'];
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getPermissions() {
+		$info = $this->view->getFileInfo($this->path);
+		return $info['permissions'];
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isReadable() {
+		return $this->checkPermissions(\OCP\PERMISSION_READ);
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isUpdateable() {
+		return $this->checkPermissions(\OCP\PERMISSION_UPDATE);
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isDeletable() {
+		return $this->checkPermissions(\OCP\PERMISSION_DELETE);
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isShareable() {
+		return $this->checkPermissions(\OCP\PERMISSION_SHARE);
+	}
+
+	/**
+	 * @return Node
+	 */
+	public function getParent() {
+		return $this->root->get(dirname($this->path));
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getName() {
+		return basename($this->path);
+	}
+
+	/**
+	 * @param string $path
+	 * @return string
+	 */
+	protected function normalizePath($path) {
+		if ($path === '' or $path === '/') {
+			return '/';
+		}
+		//no windows style slashes
+		$path = str_replace('\\', '/', $path);
+		//add leading slash
+		if ($path[0] !== '/') {
+			$path = '/' . $path;
+		}
+		//remove duplicate slashes
+		while (strpos($path, '//') !== false) {
+			$path = str_replace('//', '/', $path);
+		}
+		//remove trailing slash
+		$path = rtrim($path, '/');
+
+		return $path;
+	}
+
+	/**
+	 * check if the requested path is valid
+	 *
+	 * @param string $path
+	 * @return bool
+	 */
+	public function isValidPath($path) {
+		if (!$path || $path[0] !== '/') {
+			$path = '/' . $path;
+		}
+		if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
+			return false;
+		}
+		return true;
+	}
+}
diff --git a/lib/files/node/nonexistingfile.php b/lib/files/node/nonexistingfile.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f18450efee5f34b3069c210f3534a1ff0542a3d
--- /dev/null
+++ b/lib/files/node/nonexistingfile.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\NotFoundException;
+
+class NonExistingFile extends File {
+	/**
+	 * @param string $newPath
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function rename($newPath) {
+		throw new NotFoundException();
+	}
+
+	public function delete() {
+		throw new NotFoundException();
+	}
+
+	public function copy($newPath) {
+		throw new NotFoundException();
+	}
+
+	public function touch($mtime = null) {
+		throw new NotFoundException();
+	}
+
+	public function getId() {
+		throw new NotFoundException();
+	}
+
+	public function stat() {
+		throw new NotFoundException();
+	}
+
+	public function getMTime() {
+		throw new NotFoundException();
+	}
+
+	public function getSize() {
+		throw new NotFoundException();
+	}
+
+	public function getEtag() {
+		throw new NotFoundException();
+	}
+
+	public function getPermissions() {
+		throw new NotFoundException();
+	}
+
+	public function isReadable() {
+		throw new NotFoundException();
+	}
+
+	public function isUpdateable() {
+		throw new NotFoundException();
+	}
+
+	public function isDeletable() {
+		throw new NotFoundException();
+	}
+
+	public function isShareable() {
+		throw new NotFoundException();
+	}
+
+	public function getContent() {
+		throw new NotFoundException();
+	}
+
+	public function putContent($data) {
+		throw new NotFoundException();
+	}
+
+	public function getMimeType() {
+		throw new NotFoundException();
+	}
+
+	public function fopen($mode) {
+		throw new NotFoundException();
+	}
+}
diff --git a/lib/files/node/nonexistingfolder.php b/lib/files/node/nonexistingfolder.php
new file mode 100644
index 0000000000000000000000000000000000000000..0249a026245ca77753b9c4b6f44a03d30ce74382
--- /dev/null
+++ b/lib/files/node/nonexistingfolder.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\NotFoundException;
+
+class NonExistingFolder extends Folder {
+	/**
+	 * @param string $newPath
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function rename($newPath) {
+		throw new NotFoundException();
+	}
+
+	public function delete() {
+		throw new NotFoundException();
+	}
+
+	public function copy($newPath) {
+		throw new NotFoundException();
+	}
+
+	public function touch($mtime = null) {
+		throw new NotFoundException();
+	}
+
+	public function getId() {
+		throw new NotFoundException();
+	}
+
+	public function stat() {
+		throw new NotFoundException();
+	}
+
+	public function getMTime() {
+		throw new NotFoundException();
+	}
+
+	public function getSize() {
+		throw new NotFoundException();
+	}
+
+	public function getEtag() {
+		throw new NotFoundException();
+	}
+
+	public function getPermissions() {
+		throw new NotFoundException();
+	}
+
+	public function isReadable() {
+		throw new NotFoundException();
+	}
+
+	public function isUpdateable() {
+		throw new NotFoundException();
+	}
+
+	public function isDeletable() {
+		throw new NotFoundException();
+	}
+
+	public function isShareable() {
+		throw new NotFoundException();
+	}
+
+	public function get($path) {
+		throw new NotFoundException();
+	}
+
+	public function getDirectoryListing() {
+		throw new NotFoundException();
+	}
+
+	public function nodeExists($path) {
+		return false;
+	}
+
+	public function newFolder($path) {
+		throw new NotFoundException();
+	}
+
+	public function newFile($path) {
+		throw new NotFoundException();
+	}
+
+	public function search($pattern) {
+		throw new NotFoundException();
+	}
+
+	public function searchByMime($mime) {
+		throw new NotFoundException();
+	}
+
+	public function getById($id) {
+		throw new NotFoundException();
+	}
+
+	public function getFreeSpace() {
+		throw new NotFoundException();
+	}
+
+	public function isCreatable() {
+		throw new NotFoundException();
+	}
+}
diff --git a/lib/files/node/root.php b/lib/files/node/root.php
new file mode 100644
index 0000000000000000000000000000000000000000..f88d8c294c7499bedc2c6d39e4a248aa0eebead3
--- /dev/null
+++ b/lib/files/node/root.php
@@ -0,0 +1,337 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\Cache\Scanner;
+use OC\Files\Mount\Manager;
+use OC\Files\Mount\Mount;
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+use OC\Hooks\Emitter;
+use OC\Hooks\PublicEmitter;
+
+/**
+ * Class Root
+ *
+ * Hooks available in scope \OC\Files
+ * - preWrite(\OC\Files\Node\Node $node)
+ * - postWrite(\OC\Files\Node\Node $node)
+ * - preCreate(\OC\Files\Node\Node $node)
+ * - postCreate(\OC\Files\Node\Node $node)
+ * - preDelete(\OC\Files\Node\Node $node)
+ * - postDelete(\OC\Files\Node\Node $node)
+ * - preTouch(\OC\Files\Node\Node $node, int $mtime)
+ * - postTouch(\OC\Files\Node\Node $node)
+ * - preCopy(\OC\Files\Node\Node $source, \OC\Files\Node\Node $target)
+ * - postCopy(\OC\Files\Node\Node $source, \OC\Files\Node\Node $target)
+ * - preRename(\OC\Files\Node\Node $source, \OC\Files\Node\Node $target)
+ * - postRename(\OC\Files\Node\Node $source, \OC\Files\Node\Node $target)
+ *
+ * @package OC\Files\Node
+ */
+class Root extends Folder implements Emitter {
+
+	/**
+	 * @var \OC\Files\Mount\Manager $mountManager
+	 */
+	private $mountManager;
+
+	/**
+	 * @var \OC\Hooks\PublicEmitter
+	 */
+	private $emitter;
+
+	/**
+	 * @var \OC\User\User $user
+	 */
+	private $user;
+
+	/**
+	 * @param \OC\Files\Mount\Manager $manager
+	 * @param \OC\Files\View $view
+	 * @param \OC\User\User $user
+	 */
+	public function __construct($manager, $view, $user) {
+		parent::__construct($this, $view, '');
+		$this->mountManager = $manager;
+		$this->user = $user;
+		$this->emitter = new PublicEmitter();
+	}
+
+	/**
+	 * Get the user for which the filesystem is setup
+	 *
+	 * @return \OC\User\User
+	 */
+	public function getUser() {
+		return $this->user;
+	}
+
+	/**
+	 * @param string $scope
+	 * @param string $method
+	 * @param callable $callback
+	 */
+	public function listen($scope, $method, $callback) {
+		$this->emitter->listen($scope, $method, $callback);
+	}
+
+	/**
+	 * @param string $scope optional
+	 * @param string $method optional
+	 * @param callable $callback optional
+	 */
+	public function removeListener($scope = null, $method = null, $callback = null) {
+		$this->emitter->removeListener($scope, $method, $callback);
+	}
+
+	/**
+	 * @param string $scope
+	 * @param string $method
+	 * @param array $arguments
+	 */
+	public function emit($scope, $method, $arguments = array()) {
+		$this->emitter->emit($scope, $method, $arguments);
+	}
+
+	/**
+	 * @param \OC\Files\Storage\Storage $storage
+	 * @param string $mountPoint
+	 * @param array $arguments
+	 */
+	public function mount($storage, $mountPoint, $arguments = array()) {
+		$mount = new Mount($storage, $mountPoint, $arguments);
+		$this->mountManager->addMount($mount);
+	}
+
+	/**
+	 * @param string $mountPoint
+	 * @return \OC\Files\Mount\Mount
+	 */
+	public function getMount($mountPoint) {
+		return $this->mountManager->find($mountPoint);
+	}
+
+	/**
+	 * @param string $mountPoint
+	 * @return \OC\Files\Mount\Mount[]
+	 */
+	public function getMountsIn($mountPoint) {
+		return $this->mountManager->findIn($mountPoint);
+	}
+
+	/**
+	 * @param string $storageId
+	 * @return \OC\Files\Mount\Mount[]
+	 */
+	public function getMountByStorageId($storageId) {
+		return $this->mountManager->findByStorageId($storageId);
+	}
+
+	/**
+	 * @param int $numericId
+	 * @return Mount[]
+	 */
+	public function getMountByNumericStorageId($numericId) {
+		return $this->mountManager->findByNumericId($numericId);
+	}
+
+	/**
+	 * @param \OC\Files\Mount\Mount $mount
+	 */
+	public function unMount($mount) {
+		$this->mountManager->remove($mount);
+	}
+
+	/**
+	 * @param string $path
+	 * @throws \OC\Files\NotFoundException
+	 * @throws \OC\Files\NotPermittedException
+	 * @return Node
+	 */
+	public function get($path) {
+		$path = $this->normalizePath($path);
+		if ($this->isValidPath($path)) {
+			$fullPath = $this->getFullPath($path);
+			if ($this->view->file_exists($fullPath)) {
+				return $this->createNode($fullPath);
+			} else {
+				throw new NotFoundException();
+			}
+		} else {
+			throw new NotPermittedException();
+		}
+	}
+
+	/**
+	 * search file by id
+	 *
+	 * An array is returned because in the case where a single storage is mounted in different places the same file
+	 * can exist in different places
+	 *
+	 * @param int $id
+	 * @throws \OC\Files\NotFoundException
+	 * @return Node[]
+	 */
+	public function getById($id) {
+		$result = Cache::getById($id);
+		if (is_null($result)) {
+			throw new NotFoundException();
+		} else {
+			list($storageId, $internalPath) = $result;
+			$nodes = array();
+			$mounts = $this->mountManager->findByStorageId($storageId);
+			foreach ($mounts as $mount) {
+				$nodes[] = $this->get($mount->getMountPoint() . $internalPath);
+			}
+			return $nodes;
+		}
+
+	}
+
+	//most operations cant be done on the root
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function rename($targetPath) {
+		throw new NotPermittedException();
+	}
+
+	public function delete() {
+		throw new NotPermittedException();
+	}
+
+	/**
+	 * @param string $targetPath
+	 * @throws \OC\Files\NotPermittedException
+	 * @return \OC\Files\Node\Node
+	 */
+	public function copy($targetPath) {
+		throw new NotPermittedException();
+	}
+
+	/**
+	 * @param int $mtime
+	 * @throws \OC\Files\NotPermittedException
+	 */
+	public function touch($mtime = null) {
+		throw new NotPermittedException();
+	}
+
+	/**
+	 * @return \OC\Files\Storage\Storage
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function getStorage() {
+		throw new NotFoundException();
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getPath() {
+		return '/';
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getInternalPath() {
+		return '';
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getId() {
+		return null;
+	}
+
+	/**
+	 * @return array
+	 */
+	public function stat() {
+		return null;
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getMTime() {
+		return null;
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getSize() {
+		return null;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getEtag() {
+		return null;
+	}
+
+	/**
+	 * @return int
+	 */
+	public function getPermissions() {
+		return \OCP\PERMISSION_CREATE;
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isReadable() {
+		return false;
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isUpdateable() {
+		return false;
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isDeletable() {
+		return false;
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function isShareable() {
+		return false;
+	}
+
+	/**
+	 * @return Node
+	 * @throws \OC\Files\NotFoundException
+	 */
+	public function getParent() {
+		throw new NotFoundException();
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getName() {
+		return '';
+	}
+}
diff --git a/lib/files/view.php b/lib/files/view.php
index 8aee12bf6fe2b157e490a40beb2697383032e6ae..3a1fdd415b3e2013bb8f92f72c2b10fc84e44c36 100644
--- a/lib/files/view.php
+++ b/lib/files/view.php
@@ -30,7 +30,7 @@ class View {
 	private $internal_path_cache = array();
 	private $storage_cache = array();
 
-	public function __construct($root) {
+	public function __construct($root = '') {
 		$this->fakeRoot = $root;
 	}
 
diff --git a/tests/lib/files/node/file.php b/tests/lib/files/node/file.php
new file mode 100644
index 0000000000000000000000000000000000000000..707106373b585893b9bd03212a9c15ec40ff9ec8
--- /dev/null
+++ b/tests/lib/files/node/file.php
@@ -0,0 +1,664 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Node;
+
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+use OC\Files\View;
+
+class File extends \PHPUnit_Framework_TestCase {
+	private $user;
+
+	public function setUp() {
+		$this->user = new \OC\User\User('', new \OC_User_Dummy);
+	}
+
+	public function testDelete() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->exactly(2))
+			->method('emit')
+			->will($this->returnValue(true));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$view->expects($this->once())
+			->method('unlink')
+			->with('/bar/foo')
+			->will($this->returnValue(true));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->delete();
+	}
+
+	public function testDeleteHooks() {
+		$test = $this;
+		$hooksRun = 0;
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$preListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertInstanceOf('\OC\Files\Node\File', $node);
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$test->assertEquals(1, $node->getId());
+			$hooksRun++;
+		};
+
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$postListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertInstanceOf('\OC\Files\Node\NonExistingFile', $node);
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$hooksRun++;
+		};
+
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+		$root->listen('\OC\Files', 'preDelete', $preListener);
+		$root->listen('\OC\Files', 'postDelete', $postListener);
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL, 'fileid' => 1)));
+
+		$view->expects($this->once())
+			->method('unlink')
+			->with('/bar/foo')
+			->will($this->returnValue(true));
+
+		$view->expects($this->any())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array(null, 'foo')));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->delete();
+		$this->assertEquals(2, $hooksRun);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testDeleteNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->delete();
+	}
+
+	public function testGetContent() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$hook = function ($file) {
+			throw new \Exception('Hooks are not supposed to be called');
+		};
+
+		$root->listen('\OC\Files', 'preWrite', $hook);
+		$root->listen('\OC\Files', 'postWrite', $hook);
+
+		$view->expects($this->once())
+			->method('file_get_contents')
+			->with('/bar/foo')
+			->will($this->returnValue('bar'));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('bar', $node->getContent());
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testGetContentNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => 0)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->getContent();
+	}
+
+	public function testPutContent() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$view->expects($this->once())
+			->method('file_put_contents')
+			->with('/bar/foo', 'bar')
+			->will($this->returnValue(true));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->putContent('bar');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testPutContentNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->putContent('bar');
+	}
+
+	public function testGetMimeType() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->once())
+			->method('getMimeType')
+			->with('/bar/foo')
+			->will($this->returnValue('text/plain'));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('text/plain', $node->getMimeType());
+	}
+
+	public function testFOpenRead() {
+		$stream = fopen('php://memory', 'w+');
+		fwrite($stream, 'bar');
+		rewind($stream);
+
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$hook = function ($file) {
+			throw new \Exception('Hooks are not supposed to be called');
+		};
+
+		$root->listen('\OC\Files', 'preWrite', $hook);
+		$root->listen('\OC\Files', 'postWrite', $hook);
+
+		$view->expects($this->once())
+			->method('fopen')
+			->with('/bar/foo', 'r')
+			->will($this->returnValue($stream));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$fh = $node->fopen('r');
+		$this->assertEquals($stream, $fh);
+		$this->assertEquals('bar', fread($fh, 3));
+	}
+
+	public function testFOpenWrite() {
+		$stream = fopen('php://memory', 'w+');
+
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, new $view, $this->user);
+
+		$hooksCalled = 0;
+		$hook = function ($file) use (&$hooksCalled) {
+			$hooksCalled++;
+		};
+
+		$root->listen('\OC\Files', 'preWrite', $hook);
+		$root->listen('\OC\Files', 'postWrite', $hook);
+
+		$view->expects($this->once())
+			->method('fopen')
+			->with('/bar/foo', 'w')
+			->will($this->returnValue($stream));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$fh = $node->fopen('w');
+		$this->assertEquals($stream, $fh);
+		fwrite($fh, 'bar');
+		rewind($fh);
+		$this->assertEquals('bar', fread($stream, 3));
+		$this->assertEquals(2, $hooksCalled);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testFOpenReadNotPermitted() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$hook = function ($file) {
+			throw new \Exception('Hooks are not supposed to be called');
+		};
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => 0)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->fopen('r');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testFOpenReadWriteNoReadPermissions() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$hook = function () {
+			throw new \Exception('Hooks are not supposed to be called');
+		};
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_UPDATE)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->fopen('w');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testFOpenReadWriteNoWritePermissions() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, new $view, $this->user);
+
+		$hook = function () {
+			throw new \Exception('Hooks are not supposed to be called');
+		};
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$node->fopen('w');
+	}
+
+	public function testCopySameStorage() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->any())
+			->method('copy')
+			->with('/bar/foo', '/bar/asd');
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL, 'fileid' => 3)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\Folder($root, $view, '/bar');
+		$newNode = new \OC\Files\Node\File($root, $view, '/bar/asd');
+
+		$root->expects($this->exactly(2))
+			->method('get')
+			->will($this->returnValueMap(array(
+				array('/bar/asd', $newNode),
+				array('/bar', $parentNode)
+			)));
+
+		$target = $node->copy('/bar/asd');
+		$this->assertInstanceOf('\OC\Files\Node\File', $target);
+		$this->assertEquals(3, $target->getId());
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testCopyNotPermitted() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$root->expects($this->never())
+			->method('getMount');
+
+		$storage->expects($this->never())
+			->method('copy');
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ, 'fileid' => 3)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\Folder($root, $view, '/bar');
+
+		$root->expects($this->once())
+			->method('get')
+			->will($this->returnValueMap(array(
+				array('/bar', $parentNode)
+			)));
+
+		$node->copy('/bar/asd');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotFoundException
+	 */
+	public function testCopyNoParent() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->never())
+			->method('copy');
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar/asd')
+			->will($this->throwException(new NotFoundException()));
+
+		$node->copy('/bar/asd/foo');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testCopyParentIsFile() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->never())
+			->method('copy');
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\File($root, $view, '/bar');
+
+		$root->expects($this->once())
+			->method('get')
+			->will($this->returnValueMap(array(
+				array('/bar', $parentNode)
+			)));
+
+		$node->copy('/bar/asd');
+	}
+
+	public function testMoveSameStorage() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->any())
+			->method('rename')
+			->with('/bar/foo', '/bar/asd');
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL, 'fileid' => 1)));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\Folder($root, $view, '/bar');
+
+		$root->expects($this->any())
+			->method('get')
+			->will($this->returnValueMap(array(array('/bar', $parentNode), array('/bar/asd', $node))));
+
+		$target = $node->move('/bar/asd');
+		$this->assertInstanceOf('\OC\Files\Node\File', $target);
+		$this->assertEquals(1, $target->getId());
+		$this->assertEquals('/bar/asd', $node->getPath());
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testMoveNotPermitted() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$view->expects($this->never())
+			->method('rename');
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\Folder($root, $view, '/bar');
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar')
+			->will($this->returnValue($parentNode));
+
+		$node->move('/bar/asd');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotFoundException
+	 */
+	public function testMoveNoParent() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$storage->expects($this->never())
+			->method('rename');
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\Folder($root, $view, '/bar');
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar')
+			->will($this->throwException(new NotFoundException()));
+
+		$node->move('/bar/asd');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testMoveParentIsFile() {
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+
+		$view->expects($this->never())
+			->method('rename');
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$parentNode = new \OC\Files\Node\File($root, $view, '/bar');
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar')
+			->will($this->returnValue($parentNode));
+
+		$node->move('/bar/asd');
+	}
+}
diff --git a/tests/lib/files/node/folder.php b/tests/lib/files/node/folder.php
new file mode 100644
index 0000000000000000000000000000000000000000..691aa612c7f55405afdaa0bf06b10fdc829026ed
--- /dev/null
+++ b/tests/lib/files/node/folder.php
@@ -0,0 +1,479 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\Node\Node;
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+use OC\Files\View;
+
+class Folder extends \PHPUnit_Framework_TestCase {
+	private $user;
+
+	public function setUp() {
+		$this->user = new \OC\User\User('', new \OC_User_Dummy);
+	}
+
+	public function testDelete() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		$root->expects($this->exactly(2))
+			->method('emit')
+			->will($this->returnValue(true));
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$view->expects($this->once())
+			->method('rmdir')
+			->with('/bar/foo')
+			->will($this->returnValue(true));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->delete();
+	}
+
+	public function testDeleteHooks() {
+		$test = $this;
+		$hooksRun = 0;
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$preListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertInstanceOf('\OC\Files\Node\Folder', $node);
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$hooksRun++;
+		};
+
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$postListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertInstanceOf('\OC\Files\Node\NonExistingFolder', $node);
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$hooksRun++;
+		};
+
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+		$root->listen('\OC\Files', 'preDelete', $preListener);
+		$root->listen('\OC\Files', 'postDelete', $postListener);
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL, 'fileid' => 1)));
+
+		$view->expects($this->once())
+			->method('rmdir')
+			->with('/bar/foo')
+			->will($this->returnValue(true));
+
+		$view->expects($this->any())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array(null, 'foo')));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->delete();
+		$this->assertEquals(2, $hooksRun);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testDeleteNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->delete();
+	}
+
+	public function testGetDirectoryContent() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$cache = $this->getMock('\OC\Files\Cache\Cache', array(), array(''));
+		$cache->expects($this->any())
+			->method('getStatus')
+			->with('foo')
+			->will($this->returnValue(Cache::COMPLETE));
+
+		$cache->expects($this->once())
+			->method('getFolderContents')
+			->with('foo')
+			->will($this->returnValue(array(
+				array('fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'),
+				array('fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory')
+			)));
+
+		$permissionsCache = $this->getMock('\OC\Files\Cache\Permissions', array(), array('/'));
+		$permissionsCache->expects($this->once())
+			->method('getDirectoryPermissions')
+			->will($this->returnValue(array(2 => \OCP\PERMISSION_ALL)));
+
+		$root->expects($this->once())
+			->method('getMountsIn')
+			->with('/bar/foo')
+			->will($this->returnValue(array()));
+
+		$storage->expects($this->any())
+			->method('getPermissionsCache')
+			->will($this->returnValue($permissionsCache));
+		$storage->expects($this->any())
+			->method('getCache')
+			->will($this->returnValue($cache));
+
+		$view->expects($this->any())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array($storage, 'foo')));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$children = $node->getDirectoryListing();
+		$this->assertEquals(2, count($children));
+		$this->assertInstanceOf('\OC\Files\Node\File', $children[0]);
+		$this->assertInstanceOf('\OC\Files\Node\Folder', $children[1]);
+		$this->assertEquals('asd', $children[0]->getName());
+		$this->assertEquals('qwerty', $children[1]->getName());
+	}
+
+	public function testGet() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar/foo/asd');
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->get('asd');
+	}
+
+	public function testNodeExists() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$child = new \OC\Files\Node\Folder($root, $view, '/bar/foo/asd');
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar/foo/asd')
+			->will($this->returnValue($child));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$this->assertTrue($node->nodeExists('asd'));
+	}
+
+	public function testNodeExistsNotExists() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$root->expects($this->once())
+			->method('get')
+			->with('/bar/foo/asd')
+			->will($this->throwException(new NotFoundException()));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$this->assertFalse($node->nodeExists('asd'));
+	}
+
+	public function testNewFolder() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$view->expects($this->once())
+			->method('mkdir')
+			->with('/bar/foo/asd')
+			->will($this->returnValue(true));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$child = new \OC\Files\Node\Folder($root, $view, '/bar/foo/asd');
+		$result = $node->newFolder('asd');
+		$this->assertEquals($child, $result);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testNewFolderNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->newFolder('asd');
+	}
+
+	public function testNewFile() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$view->expects($this->once())
+			->method('touch')
+			->with('/bar/foo/asd')
+			->will($this->returnValue(true));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$child = new \OC\Files\Node\File($root, $view, '/bar/foo/asd');
+		$result = $node->newFile('asd');
+		$this->assertEquals($child, $result);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testNewFileNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$node->newFile('asd');
+	}
+
+	public function testGetFreeSpace() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('free_space')
+			->with('/bar/foo')
+			->will($this->returnValue(100));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$this->assertEquals(100, $node->getFreeSpace());
+	}
+
+	public function testSearch() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+		$cache = $this->getMock('\OC\Files\Cache\Cache', array(), array(''));
+
+		$storage->expects($this->once())
+			->method('getCache')
+			->will($this->returnValue($cache));
+
+		$cache->expects($this->once())
+			->method('search')
+			->with('%qw%')
+			->will($this->returnValue(array(
+				array('fileid' => 3, 'path' => 'foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain')
+			)));
+
+		$root->expects($this->once())
+			->method('getMountsIn')
+			->with('/bar/foo')
+			->will($this->returnValue(array()));
+
+		$view->expects($this->once())
+			->method('resolvePath')
+			->will($this->returnValue(array($storage, 'foo')));
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$result = $node->search('qw');
+		$this->assertEquals(1, count($result));
+		$this->assertEquals('/bar/foo/qwerty', $result[0]->getPath());
+	}
+
+	public function testSearchSubStorages() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+		$cache = $this->getMock('\OC\Files\Cache\Cache', array(), array(''));
+		$subCache = $this->getMock('\OC\Files\Cache\Cache', array(), array(''));
+		$subStorage = $this->getMock('\OC\Files\Storage\Storage');
+		$subMount = $this->getMock('\OC\Files\Mount\Mount', array(), array(null, ''));
+
+		$subMount->expects($this->once())
+			->method('getStorage')
+			->will($this->returnValue($subStorage));
+
+		$subMount->expects($this->once())
+			->method('getMountPoint')
+			->will($this->returnValue('/bar/foo/bar/'));
+
+		$storage->expects($this->once())
+			->method('getCache')
+			->will($this->returnValue($cache));
+
+		$subStorage->expects($this->once())
+			->method('getCache')
+			->will($this->returnValue($subCache));
+
+		$cache->expects($this->once())
+			->method('search')
+			->with('%qw%')
+			->will($this->returnValue(array(
+				array('fileid' => 3, 'path' => 'foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain')
+			)));
+
+		$subCache->expects($this->once())
+			->method('search')
+			->with('%qw%')
+			->will($this->returnValue(array(
+				array('fileid' => 4, 'path' => 'asd/qweasd', 'name' => 'qweasd', 'size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain')
+			)));
+
+		$root->expects($this->once())
+			->method('getMountsIn')
+			->with('/bar/foo')
+			->will($this->returnValue(array($subMount)));
+
+		$view->expects($this->once())
+			->method('resolvePath')
+			->will($this->returnValue(array($storage, 'foo')));
+
+
+		$node = new \OC\Files\Node\Folder($root, $view, '/bar/foo');
+		$result = $node->search('qw');
+		$this->assertEquals(2, count($result));
+	}
+
+	public function testIsSubNode() {
+		$file = new Node(null, null, '/foo/bar');
+		$folder = new \OC\Files\Node\Folder(null, null, '/foo');
+		$this->assertTrue($folder->isSubNode($file));
+		$this->assertFalse($folder->isSubNode($folder));
+
+		$file = new Node(null, null, '/foobar');
+		$this->assertFalse($folder->isSubNode($file));
+	}
+}
diff --git a/tests/lib/files/node/integration.php b/tests/lib/files/node/integration.php
new file mode 100644
index 0000000000000000000000000000000000000000..c99b6f99ebcda671e5f1a77c1f36c5c995c04e83
--- /dev/null
+++ b/tests/lib/files/node/integration.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\Mount\Manager;
+use OC\Files\Node\Root;
+use OC\Files\NotFoundException;
+use OC\Files\NotPermittedException;
+use OC\Files\Storage\Temporary;
+use OC\Files\View;
+use OC\User\User;
+
+class IntegrationTests extends \PHPUnit_Framework_TestCase {
+	/**
+	 * @var \OC\Files\Node\Root $root
+	 */
+	private $root;
+
+	/**
+	 * @var \OC\Files\Storage\Storage[]
+	 */
+	private $storages;
+
+	/**
+	 * @var \OC\Files\View $view
+	 */
+	private $view;
+
+	public function setUp() {
+		\OC\Files\Filesystem::init('', '');
+		\OC\Files\Filesystem::clearMounts();
+		$manager = \OC\Files\Filesystem::getMountManager();
+
+		\OC_Hook::clear('OC_Filesystem');
+
+		\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook');
+		\OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook');
+		\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook');
+		\OC_Hook::connect('OC_Filesystem', 'post_touch', '\OC\Files\Cache\Updater', 'touchHook');
+
+		$user = new User('', new \OC_User_Dummy);
+		$this->view = new View();
+		$this->root = new Root($manager, $this->view, $user);
+		$storage = new Temporary(array());
+		$subStorage = new Temporary(array());
+		$this->storages[] = $storage;
+		$this->storages[] = $subStorage;
+		$this->root->mount($storage, '/');
+		$this->root->mount($subStorage, '/substorage/');
+	}
+
+	public function tearDown() {
+		foreach ($this->storages as $storage) {
+			$storage->getCache()->clear();
+		}
+		\OC\Files\Filesystem::clearMounts();
+	}
+
+	public function testBasicFile() {
+		$file = $this->root->newFile('/foo.txt');
+		$this->assertCount(2, $this->root->getDirectoryListing());
+		$this->assertTrue($this->root->nodeExists('/foo.txt'));
+		$id = $file->getId();
+		$this->assertInstanceOf('\OC\Files\Node\File', $file);
+		$file->putContent('qwerty');
+		$this->assertEquals('text/plain', $file->getMimeType());
+		$this->assertEquals('qwerty', $file->getContent());
+		$this->assertFalse($this->root->nodeExists('/bar.txt'));
+		$file->move('/bar.txt');
+		$this->assertFalse($this->root->nodeExists('/foo.txt'));
+		$this->assertTrue($this->root->nodeExists('/bar.txt'));
+		$this->assertEquals('bar.txt', $file->getName());
+		$this->assertEquals('bar.txt', $file->getInternalPath());
+
+		$file->move('/substorage/bar.txt');
+		$this->assertNotEquals($id, $file->getId());
+		$this->assertEquals('qwerty', $file->getContent());
+	}
+
+	public function testBasicFolder() {
+		$folder = $this->root->newFolder('/foo');
+		$this->assertTrue($this->root->nodeExists('/foo'));
+		$file = $folder->newFile('/bar');
+		$this->assertTrue($this->root->nodeExists('/foo/bar'));
+		$file->putContent('qwerty');
+
+		$listing = $folder->getDirectoryListing();
+		$this->assertEquals(1, count($listing));
+		$this->assertEquals($file->getId(), $listing[0]->getId());
+		$this->assertEquals($file->getStorage(), $listing[0]->getStorage());
+
+
+		$rootListing = $this->root->getDirectoryListing();
+		$this->assertEquals(2, count($rootListing));
+
+		$folder->move('/asd');
+		/**
+		 * @var \OC\Files\Node\File $file
+		 */
+		$file = $folder->get('/bar');
+		$this->assertInstanceOf('\OC\Files\Node\File', $file);
+		$this->assertFalse($this->root->nodeExists('/foo/bar'));
+		$this->assertTrue($this->root->nodeExists('/asd/bar'));
+		$this->assertEquals('qwerty', $file->getContent());
+		$folder->move('/substorage/foo');
+		/**
+		 * @var \OC\Files\Node\File $file
+		 */
+		$file = $folder->get('/bar');
+		$this->assertInstanceOf('\OC\Files\Node\File', $file);
+		$this->assertTrue($this->root->nodeExists('/substorage/foo/bar'));
+		$this->assertEquals('qwerty', $file->getContent());
+	}
+}
diff --git a/tests/lib/files/node/node.php b/tests/lib/files/node/node.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa9d2a382eb26dbe16b7913476a54e2dd683d415
--- /dev/null
+++ b/tests/lib/files/node/node.php
@@ -0,0 +1,330 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Node;
+
+class Node extends \PHPUnit_Framework_TestCase {
+	private $user;
+
+	public function setUp() {
+		$this->user = new \OC\User\User('', new \OC_User_Dummy);
+	}
+
+	public function testStat() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$stat = array(
+			'fileid' => 1,
+			'size' => 100,
+			'etag' => 'qwerty',
+			'mtime' => 50,
+			'permissions' => 0
+		);
+
+		$view->expects($this->once())
+			->method('stat')
+			->with('/bar/foo')
+			->will($this->returnValue($stat));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals($stat, $node->stat());
+	}
+
+	public function testGetId() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$stat = array(
+			'fileid' => 1,
+			'size' => 100,
+			'etag' => 'qwerty',
+			'mtime' => 50
+		);
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue($stat));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals(1, $node->getId());
+	}
+
+	public function testGetSize() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('filesize')
+			->with('/bar/foo')
+			->will($this->returnValue(100));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals(100, $node->getSize());
+	}
+
+	public function testGetEtag() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$stat = array(
+			'fileid' => 1,
+			'size' => 100,
+			'etag' => 'qwerty',
+			'mtime' => 50
+		);
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue($stat));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('qwerty', $node->getEtag());
+	}
+
+	public function testGetMTime() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$view->expects($this->once())
+			->method('filemtime')
+			->with('/bar/foo')
+			->will($this->returnValue(50));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals(50, $node->getMTime());
+	}
+
+	public function testGetStorage() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$view->expects($this->once())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array($storage, 'foo')));
+
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals($storage, $node->getStorage());
+	}
+
+	public function testGetPath() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('/bar/foo', $node->getPath());
+	}
+
+	public function testGetInternalPath() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+		/**
+		 * @var \OC\Files\Storage\Storage | \PHPUnit_Framework_MockObject_MockObject $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+
+		$view->expects($this->once())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array($storage, 'foo')));
+
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('foo', $node->getInternalPath());
+	}
+
+	public function testGetName() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$node = new \OC\Files\Node\File($root, $view, '/bar/foo');
+		$this->assertEquals('foo', $node->getName());
+	}
+
+	public function testTouchSetMTime() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->once())
+			->method('touch')
+			->with('/bar/foo', 100)
+			->will($this->returnValue(true));
+
+		$view->expects($this->once())
+			->method('filemtime')
+			->with('/bar/foo')
+			->will($this->returnValue(100));
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$node = new \OC\Files\Node\Node($root, $view, '/bar/foo');
+		$node->touch(100);
+		$this->assertEquals(100, $node->getMTime());
+	}
+
+	public function testTouchHooks() {
+		$test = $this;
+		$hooksRun = 0;
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$preListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$hooksRun++;
+		};
+
+		/**
+		 * @param \OC\Files\Node\File $node
+		 */
+		$postListener = function ($node) use (&$test, &$hooksRun) {
+			$test->assertEquals('foo', $node->getInternalPath());
+			$test->assertEquals('/bar/foo', $node->getPath());
+			$hooksRun++;
+		};
+
+		/**
+		 * @var \OC\Files\Mount\Manager $manager
+		 */
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+		$root->listen('\OC\Files', 'preTouch', $preListener);
+		$root->listen('\OC\Files', 'postTouch', $postListener);
+
+		$view->expects($this->once())
+			->method('touch')
+			->with('/bar/foo', 100)
+			->will($this->returnValue(true));
+
+		$view->expects($this->any())
+			->method('resolvePath')
+			->with('/bar/foo')
+			->will($this->returnValue(array(null, 'foo')));
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_ALL)));
+
+		$node = new \OC\Files\Node\Node($root, $view, '/bar/foo');
+		$node->touch(100);
+		$this->assertEquals(2, $hooksRun);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testTouchNotPermitted() {
+		$manager = $this->getMock('\OC\Files\Mount\Manager');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = $this->getMock('\OC\Files\Node\Root', array(), array($manager, $view, $this->user));
+		$root->expects($this->any())
+			->method('getUser')
+			->will($this->returnValue($this->user));
+
+		$view->expects($this->any())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('permissions' => \OCP\PERMISSION_READ)));
+
+		$node = new \OC\Files\Node\Node($root, $view, '/bar/foo');
+		$node->touch(100);
+	}
+}
diff --git a/tests/lib/files/node/root.php b/tests/lib/files/node/root.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b356ec6d9732b0fb4d5495ad914c0cb8cb470d8
--- /dev/null
+++ b/tests/lib/files/node/root.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Node;
+
+use OC\Files\Cache\Cache;
+use OC\Files\NotPermittedException;
+use OC\Files\Mount\Manager;
+
+class Root extends \PHPUnit_Framework_TestCase {
+	private $user;
+
+	public function setUp() {
+		$this->user = new \OC\User\User('', new \OC_User_Dummy);
+	}
+
+	public function testGet() {
+		$manager = new Manager();
+		/**
+		 * @var \OC\Files\Storage\Storage $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$view->expects($this->once())
+			->method('getFileInfo')
+			->with('/bar/foo')
+			->will($this->returnValue(array('fileid' => 10, 'path' => 'bar/foo', 'name', 'mimetype' => 'text/plain')));
+
+		$view->expects($this->once())
+			->method('is_dir')
+			->with('/bar/foo')
+			->will($this->returnValue(false));
+
+		$view->expects($this->once())
+			->method('file_exists')
+			->with('/bar/foo')
+			->will($this->returnValue(true));
+
+		$root->mount($storage, '');
+		$node = $root->get('/bar/foo');
+		$this->assertEquals(10, $node->getId());
+		$this->assertInstanceOf('\OC\Files\Node\File', $node);
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotFoundException
+	 */
+	public function testGetNotFound() {
+		$manager = new Manager();
+		/**
+		 * @var \OC\Files\Storage\Storage $storage
+		 */
+		$storage = $this->getMock('\OC\Files\Storage\Storage');
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$view->expects($this->once())
+			->method('file_exists')
+			->with('/bar/foo')
+			->will($this->returnValue(false));
+
+		$root->mount($storage, '');
+		$root->get('/bar/foo');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotPermittedException
+	 */
+	public function testGetInvalidPath() {
+		$manager = new Manager();
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$root->get('/../foo');
+	}
+
+	/**
+	 * @expectedException \OC\Files\NotFoundException
+	 */
+	public function testGetNoStorages() {
+		$manager = new Manager();
+		/**
+		 * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
+		 */
+		$view = $this->getMock('\OC\Files\View');
+		$root = new \OC\Files\Node\Root($manager, $view, $this->user);
+
+		$root->get('/bar/foo');
+	}
+}