diff --git a/lib/private/files/storage/flysystem.php b/lib/private/files/storage/flysystem.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d8dee106221368eb1daaa1a101b9cb595f4e03b
--- /dev/null
+++ b/lib/private/files/storage/flysystem.php
@@ -0,0 +1,243 @@
+<?php
+/**
+ * Copyright (c) 2015 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\Storage;
+
+use Icewind\Streams\CallbackWrapper;
+use Icewind\Streams\IteratorDirectory;
+use League\Flysystem\AdapterInterface;
+use League\Flysystem\FileNotFoundException;
+use League\Flysystem\Filesystem;
+use League\Flysystem\Plugin\GetWithMetadata;
+
+/**
+ * Generic adapter between flysystem adapters and owncloud's storage system
+ *
+ * To use: subclass and call $this->buildFlysystem with the flysystem adapter of choice
+ */
+abstract class Flysystem extends Common {
+	/**
+	 * @var Filesystem
+	 */
+	protected $flysystem;
+
+	/**
+	 * @var string
+	 */
+	protected $root = '';
+
+	/**
+	 * Initialize the storage backend with a flyssytem adapter
+	 *
+	 * @param \League\Flysystem\AdapterInterface $adapter
+	 */
+	protected function buildFlySystem(AdapterInterface $adapter) {
+		$this->flysystem = new Filesystem($adapter);
+		$this->flysystem->addPlugin(new GetWithMetadata());
+	}
+
+	protected function buildPath($path) {
+		$fullPath = \OC\Files\Filesystem::normalizePath($this->root . '/' . $path);
+		return ltrim($fullPath, '/');
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function file_get_contents($path) {
+		return $this->flysystem->read($this->buildPath($path));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function file_put_contents($path, $data) {
+		return $this->flysystem->put($this->buildPath($path), $data);
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function file_exists($path) {
+		return $this->flysystem->has($this->buildPath($path));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function unlink($path) {
+		if ($this->is_dir($path)) {
+			return $this->rmdir($path);
+		}
+		try {
+			return $this->flysystem->delete($this->buildPath($path));
+		} catch (FileNotFoundException $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function rename($source, $target) {
+		if ($this->file_exists($target)) {
+			$this->unlink($target);
+		}
+		return $this->flysystem->rename($this->buildPath($source), $this->buildPath($target));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function copy($source, $target) {
+		if ($this->file_exists($target)) {
+			$this->unlink($target);
+		}
+		return $this->flysystem->copy($this->buildPath($source), $this->buildPath($target));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function filesize($path) {
+		if ($this->is_dir($path)) {
+			return 0;
+		} else {
+			return $this->flysystem->getSize($this->buildPath($path));
+		}
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function mkdir($path) {
+		if ($this->file_exists($path)) {
+			return false;
+		}
+		return $this->flysystem->createDir($this->buildPath($path));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function filemtime($path) {
+		return $this->flysystem->getTimestamp($this->buildPath($path));
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function rmdir($path) {
+		try {
+			return @$this->flysystem->deleteDir($this->buildPath($path));
+		} catch (FileNotFoundException $e) {
+			return false;
+		}
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function opendir($path) {
+		try {
+			$content = $this->flysystem->listContents($this->buildPath($path));
+		} catch (FileNotFoundException $e) {
+			return false;
+		}
+		$names = array_map(function ($object) {
+			return $object['basename'];
+		}, $content);
+		return IteratorDirectory::wrap($names);
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function fopen($path, $mode) {
+		$fullPath = $this->buildPath($path);
+		$useExisting = true;
+		switch ($mode) {
+			case 'r':
+			case 'rb':
+				try {
+					return $this->flysystem->readStream($fullPath);
+				} catch (FileNotFoundException $e) {
+					return false;
+				}
+			case 'w':
+			case 'w+':
+			case 'wb':
+			case 'wb+':
+				$useExisting = false;
+			case 'a':
+			case 'ab':
+			case 'r+':
+			case 'a+':
+			case 'x':
+			case 'x+':
+			case 'c':
+			case 'c+':
+				//emulate these
+				if ($useExisting and $this->file_exists($path)) {
+					if (!$this->isUpdatable($path)) {
+						return false;
+					}
+					$tmpFile = $this->getCachedFile($path);
+				} else {
+					if (!$this->isCreatable(dirname($path))) {
+						return false;
+					}
+					$tmpFile = \OCP\Files::tmpFile();
+				}
+				$source = fopen($tmpFile, $mode);
+				return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath) {
+					$this->flysystem->putStream($fullPath, fopen($tmpFile, 'r'));
+					unlink($tmpFile);
+				});
+		}
+		return false;
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function touch($path, $mtime = null) {
+		if ($this->file_exists($path)) {
+			return false;
+		} else {
+			$this->file_put_contents($path, '');
+			return true;
+		}
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function stat($path) {
+		$info = $this->flysystem->getWithMetadata($this->buildPath($path), ['timestamp', 'size']);
+		return [
+			'mtime' => $info['timestamp'],
+			'size' => $info['size']
+		];
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function filetype($path) {
+		if ($path === '' or $path === '/' or $path === '.') {
+			return 'dir';
+		}
+		try {
+			$info = $this->flysystem->getMetadata($this->buildPath($path));
+		} catch (FileNotFoundException $e) {
+			return false;
+		}
+		return $info['type'];
+	}
+}