mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/10] dentry cache support
@ 2018-05-31 15:04 Sascha Hauer
  2018-05-31 15:04 ` [PATCH 01/10] fs: dentry cache implementation Sascha Hauer
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

This series adds the Linux dcache implementation to barebox.

Until now every filesystem driver resolves the full path to a file for
itself. This leads to code duplication and is error prone since
resolving paths is a complicated task. Also it can narrow down the
lookup performance since barebox only knows ASCII paths and has no way
of caching lookups. Since with this barebox provides a interface to
dentries much like the Linux Kernel does it gets easier to share
filesystem code between barebox and Linux.

With this series we get the Linux dcache implementation. The path
resolving code from fs/namei.c is nearly taken as-is, minus the RCU and
locking code. Dcaching is made simple as of now: We simply cache
everything and never release any dentries. Although we do reference
counting for inodes and dentries it is effectively not used yet.  We
never free anything until a fs is unmounted in which case we free
everything no matter if references are taken or not.

To still support filesystems with the old API I now introduced a wrapper
layer which binds between the new dentry cache API and old filesystems.
Hopefully this can go away soon once all filesystems are converted.

Changes since v1:

- introduce wrapper layer so we do not have to mark any FS as broken
  anymore
  - change squashfs and cramfs to the new API
  - let tftp return FILESIZE_MAX as inode size as Philipp suggested
  - lots of small changes

Sascha Hauer (10):
  fs: dentry cache implementation
  fs: ramfs: Switch to dentry cache implementation
  fs: devfs: Switch to dentry cache implementation
  fs: ext4: Switch to dentry cache implementation
  fs: ubifs: Switch to dentry cache implementation
  fs: nfs: Switch to dentry cache implementation
  fs: tftp: Switch to dentry cache implementation
  fs: cramfs: Switch to dentry cache implementation
  fs: squashfs: Switch to dentry cache implementation
  block: Adjust cache sizes

 common/block.c         |    4 +-
 fs/Kconfig             |   18 +
 fs/Makefile            |    3 +-
 fs/cramfs/cramfs.c     |  523 +++---
 fs/devfs.c             |  150 +-
 fs/ext4/ext_barebox.c  |  279 +--
 fs/ext4/ext_common.h   |    3 +
 fs/fat/Kconfig         |    1 +
 fs/fs.c                | 3632 ++++++++++++++++++++++++++--------------
 fs/legacy.c            |  315 ++++
 fs/libfs.c             |   97 ++
 fs/nfs.c               |  542 +++---
 fs/pstore/Kconfig      |    1 +
 fs/ramfs.c             |  401 ++---
 fs/squashfs/Makefile   |    2 +
 fs/squashfs/dir.c      |  232 +++
 fs/squashfs/inode.c    |    9 +
 fs/squashfs/namei.c    |   17 +-
 fs/squashfs/squashfs.c |  186 +-
 fs/squashfs/squashfs.h |    9 +-
 fs/squashfs/super.c    |   13 +-
 fs/squashfs/symlink.c  |   82 +
 fs/tftp.c              |   97 +-
 fs/ubifs/Makefile      |    2 +-
 fs/ubifs/dir.c         |  410 +++++
 fs/ubifs/super.c       |  153 +-
 fs/ubifs/ubifs.c       |  341 +---
 fs/ubifs/ubifs.h       |    7 +-
 include/dirent.h       |    3 +
 include/fs.h           |   38 +-
 include/linux/dcache.h |  109 +-
 include/linux/fs.h     |  131 +-
 include/linux/mount.h  |    3 +
 include/linux/namei.h  |   52 +
 include/linux/stat.h   |    2 +
 35 files changed, 4832 insertions(+), 3035 deletions(-)
 create mode 100644 fs/legacy.c
 create mode 100644 fs/libfs.c
 create mode 100644 fs/squashfs/dir.c
 create mode 100644 fs/squashfs/symlink.c
 create mode 100644 fs/ubifs/dir.c
 create mode 100644 include/linux/namei.h

-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 01/10] fs: dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 02/10] fs: ramfs: Switch to " Sascha Hauer
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

This adds the Linux dentry cache implementation to barebox.

Until now every filesystem driver resolves the full path to a file for
itself. This leads to code duplication and is error prone since
resolving paths is a complicated task. Also it can narrow down the
lookup performance since barebox only knows ASCII paths and has no way
of caching lookups.

With this patch we get the Linux dcache implementation. The path
resolving code from fs/namei.c is nearly taken as-is, minus the RCU
and locking code. Dcaching is made simple as of now: We simply cache
everything and never release any dentries. Although we do reference
counting for inodes and dentries it is effectively not used yet.
We never free anything until a fs is unmounted in which case we free
everything no matter if references are taken or not.

This patch also contains a wrapper in fs/legacy.c to support
filesystems with the old API.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig             |   23 +
 fs/Makefile            |    3 +-
 fs/ext4/Kconfig        |    1 +
 fs/fat/Kconfig         |    1 +
 fs/fs.c                | 3632 ++++++++++++++++++++++++++--------------
 fs/legacy.c            |  315 ++++
 fs/libfs.c             |   97 ++
 fs/pstore/Kconfig      |    1 +
 fs/squashfs/Kconfig    |    1 +
 fs/squashfs/super.c    |    9 -
 fs/ubifs/Kconfig       |    3 +
 include/dirent.h       |    3 +
 include/fs.h           |   38 +-
 include/linux/dcache.h |  109 +-
 include/linux/fs.h     |  131 +-
 include/linux/mount.h  |    3 +
 include/linux/namei.h  |   52 +
 include/linux/stat.h   |    2 +
 18 files changed, 3137 insertions(+), 1287 deletions(-)
 create mode 100644 fs/legacy.c
 create mode 100644 fs/libfs.c
 create mode 100644 include/linux/namei.h

diff --git a/fs/Kconfig b/fs/Kconfig
index 3512000556..b60314b1ec 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -6,12 +6,24 @@ config FS
 	default y
 	select FILETYPE
 
+config FS_LEGACY
+	bool
+	help
+	  invisible option selected by filesystem drivers which haven't
+	  been ported to dentry cache.
+
+if FS_LEGACY
+comment "Some selected filesystems still use the legacy FS API."
+comment "Consider updating them."
+endif
+
 config FS_AUTOMOUNT
 	bool
 
 config FS_CRAMFS
 	bool
 	select ZLIB
+	select FS_LEGACY
 	prompt "cramfs support"
 
 source fs/ext4/Kconfig
@@ -19,15 +31,18 @@ source fs/ext4/Kconfig
 config FS_RAMFS
 	bool
 	default y
+	select FS_LEGACY
 	prompt "ramfs support"
 
 config FS_DEVFS
 	bool
 	default y
+	select FS_LEGACY
 	prompt "devfs support"
 
 config FS_TFTP
 	bool
+	select FS_LEGACY
 	prompt "tftp support"
 	depends on NET
 
@@ -35,14 +50,17 @@ config FS_OMAP4_USBBOOT
 	bool
 	prompt "Filesystem over usb boot"
 	depends on OMAP4_USBBOOT
+	select FS_LEGACY
 
 config FS_NFS
 	depends on NET
+	select FS_LEGACY
 	bool
 	prompt "nfs support"
 
 config FS_EFI
 	depends on EFI_BOOTUP
+	select FS_LEGACY
 	bool
 	prompt "EFI filesystem support"
 	help
@@ -51,6 +69,7 @@ config FS_EFI
 
 config FS_EFIVARFS
 	depends on EFI_BOOTUP
+	select FS_LEGACY
 	bool
 	prompt "EFI variable filesystem support (efivarfs)"
 	help
@@ -62,6 +81,7 @@ source fs/ubifs/Kconfig
 config FS_BPKFS
 	bool
 	select CRC32
+	select FS_LEGACY
 	prompt "BPKFS support"
 	help
 	  Simple update file format developed for Somfy, tools and library are
@@ -78,10 +98,12 @@ config FS_BPKFS
 config FS_UIMAGEFS
 	bool
 	select CRC32
+	select FS_LEGACY
 	prompt "uImage FS support"
 
 config FS_SMHFS
 	depends on ARM_SEMIHOSTING
+	select FS_LEGACY
 	bool
 	prompt "Semihosting FS support"
 	help
@@ -95,6 +117,7 @@ source fs/squashfs/Kconfig
 config FS_RATP
 	bool
 	depends on RATP
+	select FS_LEGACY
 	prompt "RATP filesystem support"
 	help
 	  This enables support for transferring files over RATP. A host can
diff --git a/fs/Makefile b/fs/Makefile
index 8e3fd78e92..ac3e6a03aa 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -2,9 +2,10 @@ obj-$(CONFIG_FS_CRAMFS)	+= cramfs/
 obj-$(CONFIG_FS_EXT4)	+= ext4/
 obj-$(CONFIG_FS_RAMFS)	+= ramfs.o
 obj-y			+= devfs-core.o
+obj-$(CONFIG_FS_LEGACY) += legacy.o
 obj-$(CONFIG_FS_DEVFS)	+= devfs.o
 obj-$(CONFIG_FS_FAT)	+= fat/
-obj-y	+= fs.o
+obj-y	+= fs.o libfs.o
 obj-$(CONFIG_FS_UBIFS)	+= ubifs/
 obj-$(CONFIG_FS_TFTP)	+= tftp.o
 obj-$(CONFIG_FS_OMAP4_USBBOOT)	+= omap4_usbbootfs.o
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index f36043d9a7..8643e9d859 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -1,3 +1,4 @@
 config FS_EXT4
 	bool
+	select FS_LEGACY
 	prompt "ext4 filesystem support"
diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig
index 0699728494..b1def851cf 100644
--- a/fs/fat/Kconfig
+++ b/fs/fat/Kconfig
@@ -1,5 +1,6 @@
 menuconfig FS_FAT
 	bool
+	select FS_LEGACY
 	prompt "FAT filesystem support"
 
 if FS_FAT
diff --git a/fs/fs.c b/fs/fs.c
index b66cc9b178..e492460abc 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -36,6 +36,7 @@
 #include <block.h>
 #include <libfile.h>
 #include <parseopt.h>
+#include <linux/namei.h>
 
 char *mkmodestr(unsigned long mode, char *str)
 {
@@ -69,8 +70,12 @@ char *mkmodestr(unsigned long mode, char *str)
 EXPORT_SYMBOL(mkmodestr);
 
 static char *cwd;
+static struct dentry *cwd_dentry;
+static struct vfsmount *cwd_mnt;
 
 static FILE *files;
+static struct dentry *d_root;
+static struct vfsmount *mnt_root;
 
 static int init_fs(void)
 {
@@ -84,226 +89,40 @@ static int init_fs(void)
 
 postcore_initcall(init_fs);
 
-char *normalise_path(const char *pathname)
-{
-	char *path = xzalloc(strlen(pathname) + strlen(cwd) + 2);
-        char *in, *out, *slashes[32];
-	int sl = 0;
-
-	debug("in: %s\n", pathname);
-
-	if (*pathname != '/')
-		strcpy(path, cwd);
-	strcat(path, "/");
-	strcat(path, pathname);
-
-	slashes[0] = in = out = path;
-
-	while (*in) {
-		if(*in == '/') {
-			slashes[sl++] = out;
-			*out++ = *in++;
-			while(*in == '/')
-				in++;
-		} else {
-			if (*in == '.' && (*(in + 1) == '/' || !*(in + 1))) {
-				sl--;
-				if (sl < 0)
-					sl = 0;
-				out = slashes[sl];
-				in++;
-				continue;
-			}
-			if (*in == '.' && *(in + 1) == '.') {
-				sl -= 2;
-				if (sl < 0)
-					sl = 0;
-				out = slashes[sl];
-				in += 2;
-				continue;
-			}
-                        *out++ = *in++;
-		}
-	}
-
-	*out-- = 0;
-
-	/*
-	 * Remove trailing slash
-	 */
-	if (*out == '/')
-		*out = 0;
-
-	if (!*path) {
-		*path = '/';
-		*(path + 1) = 0;
-	}
-
-	return path;
-}
-EXPORT_SYMBOL(normalise_path);
-
-static int __lstat(const char *filename, struct stat *s);
 static struct fs_device_d *get_fsdevice_by_path(const char *path);
 
-static char *__canonicalize_path(const char *_pathname, int level)
-{
-	char *path, *freep;
-	char *outpath;
-	int ret;
-	struct stat s;
-
-	if (level > 10)
-		return ERR_PTR(-ELOOP);
-
-	path = freep = xstrdup(_pathname);
-
-	if (*path == '/' || !strcmp(cwd, "/"))
-		outpath = xstrdup("");
-	else
-		outpath = __canonicalize_path(cwd, level + 1);
-
-	while (1) {
-		char *p = strsep(&path, "/");
-		char *tmp;
-		char link[PATH_MAX] = {};
-		struct fs_device_d *fsdev;
-
-		if (!p)
-			break;
-		if (p[0] == '\0')
-			continue;
-		if (!strcmp(p, "."))
-			continue;
-		if (!strcmp(p, "..")) {
-			tmp = xstrdup(dirname(outpath));
-			free(outpath);
-			outpath = tmp;
-			continue;
-		}
-
-		tmp = basprintf("%s/%s", outpath, p);
-		free(outpath);
-		outpath = tmp;
-
-		/*
-		 * Don't bother filesystems without link support
-		 * with an additional stat() call.
-		 */
-		fsdev = get_fsdevice_by_path(outpath);
-		if (!fsdev || !fsdev->driver->readlink)
-			continue;
-
-		ret = __lstat(outpath, &s);
-		if (ret)
-			goto out;
-
-		if (!S_ISLNK(s.st_mode))
-			continue;
-
-		ret = readlink(outpath, link, PATH_MAX - 1);
-		if (ret < 0)
-			goto out;
-
-		if (link[0] == '/') {
-			free(outpath);
-			outpath = __canonicalize_path(link, level + 1);
-		} else {
-			tmp = basprintf("%s/%s", dirname(outpath), link);
-			free(outpath);
-			outpath = __canonicalize_path(tmp, level + 1);
-			free(tmp);
-		}
-
-		if (IS_ERR(outpath))
-			goto out;
-	}
-out:
-	free(freep);
-
-	if (!*outpath) {
-		free(outpath);
-		outpath = xstrdup("/");
-	}
-
-	return outpath;
-}
+LIST_HEAD(fs_device_list);
 
-/*
- * canonicalize_path - resolve links in path
- * @pathname: The input path
- *
- * This function resolves all links in @pathname and returns
- * a path without links in it.
- *
- * Return: Path with links resolved. Allocated, must be freed after use.
- */
-char *canonicalize_path(const char *pathname)
+struct vfsmount *mntget(struct vfsmount *mnt)
 {
-	char *r, *p = __canonicalize_path(pathname, 0);
-
-	if (IS_ERR(p))
-		return ERR_CAST(p);
+	if (!mnt)
+		return NULL;
 
-	r = normalise_path(p);
-	free(p);
+	mnt->ref++;
 
-	return r;
+	return mnt;
 }
 
-/*
- * canonicalize_dir - resolve links in path
- * @pathname: The input path
- *
- * This function resolves all links except the last one. Needed to give
- * access to the link itself.
- *
- * Return: Path with links resolved. Allocated, must be freed after use.
- */
-static char *canonicalize_dir(const char *pathname)
+void mntput(struct vfsmount *mnt)
 {
-	char *f, *d, *r, *ret, *p;
-	char *freep1, *freep2;
-
-	freep1 = xstrdup(pathname);
-	freep2 = xstrdup(pathname);
-	f = basename(freep1);
-	d = dirname(freep2);
-
-	p = __canonicalize_path(d, 0);
-	if (IS_ERR(p)) {
-		ret = ERR_CAST(p);
-		goto out;
-	}
-
-	r = basprintf("%s/%s", p, f);
-
-	ret = normalise_path(r);
-
-	free(r);
-	free(p);
-out:
-	free(freep1);
-	free(freep2);
+	if (!mnt)
+		return;
 
-	return ret;
+	mnt->ref--;
 }
 
-LIST_HEAD(fs_device_list);
-static struct fs_device_d *fs_dev_root;
-
-static struct fs_device_d *get_fsdevice_by_path(const char *path)
+struct vfsmount *lookup_mnt(struct path *path)
 {
-	struct fs_device_d *fsdev = NULL;
+	struct fs_device_d *fsdev;
 
 	for_each_fs_device(fsdev) {
-		int len = strlen(fsdev->path);
-		if (!strncmp(path, fsdev->path, len) &&
-				(path[len] == '/' || path[len] == 0))
-			return fsdev;
+		if (path->dentry == fsdev->vfsmount.mountpoint) {
+			mntget(&fsdev->vfsmount);
+			return &fsdev->vfsmount;
+		}
 	}
 
-	return fs_dev_root;
+	return NULL;
 }
 
 /*
@@ -348,6 +167,8 @@ static void put_file(FILE *f)
 	free(f->path);
 	f->path = NULL;
 	f->in_use = 0;
+	iput(f->f_inode);
+	dput(f->dentry);
 }
 
 static int check_fd(int fd)
@@ -360,388 +181,284 @@ static int check_fd(int fd)
 	return 0;
 }
 
-#ifdef CONFIG_FS_AUTOMOUNT
-
-#define AUTOMOUNT_IS_FILE (1 << 0)
+int create(struct dentry *dir, struct dentry *dentry)
+{
+	struct inode *inode;
 
-struct automount {
-	char *path;
-	char *cmd;
-	struct list_head list;
-	unsigned int flags;
-};
+	if (d_is_negative(dir))
+		return -ENOENT;
 
-static LIST_HEAD(automount_list);
+	inode = d_inode(dir);
 
-void automount_remove(const char *_path)
-{
-	char *path = normalise_path(_path);
-	struct automount *am;
+	if (!inode->i_op->create)
+		return -EROFS;
 
-	list_for_each_entry(am, &automount_list, list) {
-		if (!strcmp(path, am->path))
-			goto found;
-	}
+	return inode->i_op->create(inode, dentry, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);
+}
 
-	return;
-found:
-	list_del(&am->list);
-	free(am->path);
-	free(am->cmd);
-	free(am);
+int creat(const char *pathname, mode_t mode)
+{
+	return open(pathname, O_CREAT | O_WRONLY | O_TRUNC);
 }
-EXPORT_SYMBOL(automount_remove);
+EXPORT_SYMBOL(creat);
 
-int automount_add(const char *path, const char *cmd)
+int ftruncate(int fd, loff_t length)
 {
-	struct automount *am = xzalloc(sizeof(*am));
-	struct stat s;
+	struct fs_driver_d *fsdrv;
+	FILE *f;
 	int ret;
 
-	am->path = normalise_path(path);
-	am->cmd = xstrdup(cmd);
+	if (check_fd(fd))
+		return -errno;
 
-	automount_remove(am->path);
+	f = &files[fd];
 
-	ret = stat(path, &s);
-	if (!ret) {
-		/*
-		 * If it exists it must be a directory
-		 */
-		if (!S_ISDIR(s.st_mode))
-			return -ENOTDIR;
-	} else {
-		am->flags |= AUTOMOUNT_IS_FILE;
-	}
+	fsdrv = f->fsdev->driver;
 
-	list_add_tail(&am->list, &automount_list);
+	ret = fsdrv->truncate(&f->fsdev->dev, f, length);
+	if (ret)
+		return ret;
+
+	f->size = length;
 
 	return 0;
 }
-EXPORT_SYMBOL(automount_add);
 
-void cdev_create_default_automount(struct cdev *cdev)
+int ioctl(int fd, int request, void *buf)
 {
-	char *path, *cmd;
-
-	path = basprintf("/mnt/%s", cdev->name);
-	cmd = basprintf("mount %s", cdev->name);
+	struct fs_driver_d *fsdrv;
+	FILE *f;
+	int ret;
 
-	make_directory(path);
-	automount_add(path, cmd);
+	if (check_fd(fd))
+		return -errno;
 
-	free(cmd);
-	free(path);
-}
+	f = &files[fd];
 
-void automount_print(void)
-{
-	struct automount *am;
+	fsdrv = f->fsdev->driver;
 
-	list_for_each_entry(am, &automount_list, list)
-		printf("%-20s %s\n", am->path, am->cmd);
+	if (fsdrv->ioctl)
+		ret = fsdrv->ioctl(&f->fsdev->dev, f, request, buf);
+	else
+		ret = -ENOSYS;
+	if (ret)
+		errno = -ret;
+	return ret;
 }
-EXPORT_SYMBOL(automount_print);
 
-static void automount_mount(const char *path, int instat)
+static ssize_t __read(FILE *f, void *buf, size_t count)
 {
-	struct automount *am;
+	struct fs_driver_d *fsdrv;
 	int ret;
-	static int in_automount;
-
-	if (in_automount)
-		return;
-
-	in_automount++;
 
-	if (fs_dev_root != get_fsdevice_by_path(path))
+	if ((f->flags & O_ACCMODE) == O_WRONLY) {
+		ret = -EBADF;
 		goto out;
+	}
 
-	list_for_each_entry(am, &automount_list, list) {
-		int len_path = strlen(path);
-		int len_am_path = strlen(am->path);
-
-		/*
-		 * stat is a bit special. We do not want to trigger
-		 * automount when someone calls stat() on the automount
-		 * directory itself.
-		 */
-		if (instat && !(am->flags & AUTOMOUNT_IS_FILE) &&
-				len_path == len_am_path) {
-			continue;
-		}
+	fsdrv = f->fsdev->driver;
 
-		if (len_path < len_am_path)
-			continue;
+	if (f->size != FILE_SIZE_STREAM && f->pos + count > f->size)
+		count = f->size - f->pos;
 
-		if (strncmp(path, am->path, len_am_path))
-			continue;
+	if (!count)
+		return 0;
 
-		if (*(path + len_am_path) != 0 && *(path + len_am_path) != '/')
-			continue;
+	ret = fsdrv->read(&f->fsdev->dev, f, buf, count);
+out:
+	if (ret < 0)
+		errno = -ret;
+	return ret;
+}
 
-		setenv("automount_path", am->path);
-		export("automount_path");
-		ret = run_command(am->cmd);
-		setenv("automount_path", NULL);
+ssize_t pread(int fd, void *buf, size_t count, loff_t offset)
+{
+	loff_t pos;
+	FILE *f;
+	int ret;
 
-		if (ret)
-			printf("running automount command '%s' failed\n",
-					am->cmd);
+	if (check_fd(fd))
+		return -errno;
 
-		break;
-	}
-out:
-	in_automount--;
-}
+	f = &files[fd];
 
-BAREBOX_MAGICVAR(automount_path, "mountpath passed to automount scripts");
+	pos = f->pos;
+	f->pos = offset;
+	ret = __read(f, buf, count);
+	f->pos = pos;
 
-#else
-static void automount_mount(const char *path, int instat)
-{
+	return ret;
 }
-#endif /* CONFIG_FS_AUTOMOUNT */
+EXPORT_SYMBOL(pread);
 
-static struct fs_device_d *get_fs_device_and_root_path(char **path)
+ssize_t read(int fd, void *buf, size_t count)
 {
-	struct fs_device_d *fsdev;
+	FILE *f;
+	int ret;
 
-	automount_mount(*path, 0);
+	if (check_fd(fd))
+		return -errno;
 
-	fsdev = get_fsdevice_by_path(*path);
-	if (!fsdev)
-		return NULL;
-	if (fsdev != fs_dev_root)
-		*path += strlen(fsdev->path);
+	f = &files[fd];
 
-	return fsdev;
+	ret = __read(f, buf, count);
+
+	if (ret > 0)
+		f->pos += ret;
+	return ret;
 }
+EXPORT_SYMBOL(read);
 
-static int dir_is_empty(const char *pathname)
+static ssize_t __write(FILE *f, const void *buf, size_t count)
 {
-	DIR *dir;
-	struct dirent *d;
-	int ret = 1;
+	struct fs_driver_d *fsdrv;
+	int ret;
 
-	dir = opendir(pathname);
-	if (!dir) {
-		errno = ENOENT;
-		return -ENOENT;
+	if (!(f->flags & O_ACCMODE)) {
+		ret = -EBADF;
+		goto out;
 	}
 
-	while ((d = readdir(dir))) {
-		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
-				continue;
-		ret = 0;
-		break;
+	fsdrv = f->fsdev->driver;
+	if (f->size != FILE_SIZE_STREAM && f->pos + count > f->size) {
+		ret = fsdrv->truncate(&f->fsdev->dev, f, f->pos + count);
+		if (ret) {
+			if (ret != -ENOSPC)
+				goto out;
+			count = f->size - f->pos;
+			if (!count)
+				goto out;
+		} else {
+			f->size = f->pos + count;
+			f->f_inode->i_size = f->size;
+		}
 	}
-
-	closedir(dir);
+	ret = fsdrv->write(&f->fsdev->dev, f, buf, count);
+out:
+	if (ret < 0)
+		errno = -ret;
 	return ret;
 }
 
-static int parent_check_directory(const char *path)
+ssize_t pwrite(int fd, const void *buf, size_t count, loff_t offset)
 {
-	struct stat s;
+	loff_t pos;
+	FILE *f;
 	int ret;
-	char *dir = dirname(xstrdup(path));
 
-	ret = lstat(dir, &s);
-
-	free(dir);
-
-	if (ret)
-		return -ENOENT;
+	if (check_fd(fd))
+		return -errno;
 
-	if (!S_ISDIR(s.st_mode))
-		return -ENOTDIR;
+	f = &files[fd];
 
-	return 0;
-}
+	pos = f->pos;
+	f->pos = offset;
+	ret = __write(f, buf, count);
+	f->pos = pos;
 
-const char *getcwd(void)
-{
-	return cwd;
+	return ret;
 }
-EXPORT_SYMBOL(getcwd);
+EXPORT_SYMBOL(pwrite);
 
-int chdir(const char *pathname)
+ssize_t write(int fd, const void *buf, size_t count)
 {
-	char *p = normalise_path(pathname);
+	FILE *f;
 	int ret;
-	struct stat s;
-
-	ret = stat(p, &s);
-	if (ret)
-		goto out;
-
-	if (!S_ISDIR(s.st_mode)) {
-		ret = -ENOTDIR;
-		goto out;
-	}
-
-	automount_mount(p, 0);
 
-	strcpy(cwd, p);
+	if (check_fd(fd))
+		return -errno;
 
-out:
-	free(p);
+	f = &files[fd];
 
-	if (ret)
-		errno = -ret;
+	ret = __write(f, buf, count);
 
+	if (ret > 0)
+		f->pos += ret;
 	return ret;
 }
-EXPORT_SYMBOL(chdir);
+EXPORT_SYMBOL(write);
 
-int unlink(const char *pathname)
+int flush(int fd)
 {
-	struct fs_device_d *fsdev;
 	struct fs_driver_d *fsdrv;
-	char *p = canonicalize_dir(pathname);
-	char *freep = p;
+	FILE *f;
 	int ret;
-	struct stat s;
-
-	ret = lstat(p, &s);
-	if (ret)
-		goto out;
 
-	if (S_ISDIR(s.st_mode)) {
-		ret = -EISDIR;
-		goto out;
-	}
+	if (check_fd(fd))
+		return -errno;
 
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENOENT;
-		goto out;
-	}
-	fsdrv = fsdev->driver;
+	f = &files[fd];
 
-	if (!fsdrv->unlink) {
-		ret = -ENOSYS;
-		goto out;
-	}
+	fsdrv = f->fsdev->driver;
+	if (fsdrv->flush)
+		ret = fsdrv->flush(&f->fsdev->dev, f);
+	else
+		ret = 0;
 
-	ret = fsdrv->unlink(&fsdev->dev, p);
-	if (ret)
-		errno = -ret;
-out:
-	free(freep);
 	if (ret)
 		errno = -ret;
+
 	return ret;
 }
-EXPORT_SYMBOL(unlink);
 
-int open(const char *pathname, int flags, ...)
+loff_t lseek(int fildes, loff_t offset, int whence)
 {
-	struct fs_device_d *fsdev;
 	struct fs_driver_d *fsdrv;
 	FILE *f;
-	int exist_err = 0;
-	struct stat s;
-	char *path;
-	char *freep;
+	loff_t pos;
 	int ret;
 
-	path = canonicalize_path(pathname);
-	if (IS_ERR(path)) {
-		ret = PTR_ERR(path);
-		goto out2;
-	}
-
-	exist_err = stat(path, &s);
-
-	freep = path;
-
-	if (!exist_err && S_ISDIR(s.st_mode)) {
-		ret = -EISDIR;
-		goto out1;
-	}
-
-	if (exist_err && !(flags & O_CREAT)) {
-		ret = exist_err;
-		goto out1;
-	}
-
-	if (exist_err) {
-		ret = parent_check_directory(path);
-		if (ret)
-			goto out1;
-	}
-
-	f = get_file();
-	if (!f) {
-		ret = -EMFILE;
-		goto out1;
-	}
+	if (check_fd(fildes))
+		return -1;
 
-	fsdev = get_fs_device_and_root_path(&path);
-	if (!fsdev) {
-		ret = -ENOENT;
+	f = &files[fildes];
+	fsdrv = f->fsdev->driver;
+	if (!fsdrv->lseek) {
+		ret = -ENOSYS;
 		goto out;
 	}
 
-	fsdrv = fsdev->driver;
-
-	f->fsdev = fsdev;
-	f->flags = flags;
-
-	if ((flags & O_ACCMODE) && !fsdrv->write) {
-		ret = -EROFS;
-		goto out;
-	}
+	ret = -EINVAL;
 
-	if (exist_err) {
-		if (NULL != fsdrv->create)
-			ret = fsdrv->create(&fsdev->dev, path,
-					S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);
-		else
-			ret = -EROFS;
-		if (ret)
+	switch (whence) {
+	case SEEK_SET:
+		if (f->size != FILE_SIZE_STREAM && offset > f->size)
 			goto out;
-	}
-
-	f->path = xstrdup(path);
-
-	ret = fsdrv->open(&fsdev->dev, f, path);
-	if (ret)
-		goto out;
-
-	if (flags & O_TRUNC) {
-		ret = fsdrv->truncate(&fsdev->dev, f, 0);
-		f->size = 0;
-		if (ret)
+		if (offset < 0)
+			goto out;
+		pos = offset;
+		break;
+	case SEEK_CUR:
+		if (f->size != FILE_SIZE_STREAM && offset + f->pos > f->size)
+			goto out;
+		pos = f->pos + offset;
+		break;
+	case SEEK_END:
+		if (offset > 0)
 			goto out;
+		pos = f->size + offset;
+		break;
+	default:
+		goto out;
 	}
 
-	if (flags & O_APPEND)
-		f->pos = f->size;
+	pos = fsdrv->lseek(&f->fsdev->dev, f, pos);
+	if (pos < 0) {
+		errno = -pos;
+		return -1;
+	}
 
-	free(freep);
-	return f->no;
+	return pos;
 
 out:
-	put_file(f);
-out1:
-	free(freep);
-out2:
 	if (ret)
 		errno = -ret;
-	return ret;
-}
-EXPORT_SYMBOL(open);
 
-int creat(const char *pathname, mode_t mode)
-{
-	return open(pathname, O_CREAT | O_WRONLY | O_TRUNC);
+	return -1;
 }
-EXPORT_SYMBOL(creat);
+EXPORT_SYMBOL(lseek);
 
-int ftruncate(int fd, loff_t length)
+int erase(int fd, loff_t count, loff_t offset)
 {
 	struct fs_driver_d *fsdrv;
 	FILE *f;
@@ -749,21 +466,28 @@ int ftruncate(int fd, loff_t length)
 
 	if (check_fd(fd))
 		return -errno;
-
 	f = &files[fd];
+	if (offset >= f->size)
+		return 0;
+	if (count == ERASE_SIZE_ALL || count > f->size - offset)
+		count = f->size - offset;
+	if (count < 0)
+		return -EINVAL;
 
 	fsdrv = f->fsdev->driver;
+	if (fsdrv->erase)
+		ret = fsdrv->erase(&f->fsdev->dev, f, count, offset);
+	else
+		ret = -ENOSYS;
 
-	ret = fsdrv->truncate(&f->fsdev->dev, f, length);
 	if (ret)
-		return ret;
-
-	f->size = length;
+		errno = -ret;
 
-	return 0;
+	return ret;
 }
+EXPORT_SYMBOL(erase);
 
-int ioctl(int fd, int request, void *buf)
+int protect(int fd, size_t count, loff_t offset, int prot)
 {
 	struct fs_driver_d *fsdrv;
 	FILE *f;
@@ -771,387 +495,2013 @@ int ioctl(int fd, int request, void *buf)
 
 	if (check_fd(fd))
 		return -errno;
-
 	f = &files[fd];
+	if (offset >= f->size)
+		return 0;
+	if (count > f->size - offset)
+		count = f->size - offset;
 
 	fsdrv = f->fsdev->driver;
-
-	if (fsdrv->ioctl)
-		ret = fsdrv->ioctl(&f->fsdev->dev, f, request, buf);
+	if (fsdrv->protect)
+		ret = fsdrv->protect(&f->fsdev->dev, f, count, offset, prot);
 	else
 		ret = -ENOSYS;
+
 	if (ret)
 		errno = -ret;
+
 	return ret;
 }
+EXPORT_SYMBOL(protect);
 
-static ssize_t __read(FILE *f, void *buf, size_t count)
+int protect_file(const char *file, int prot)
 {
-	struct fs_driver_d *fsdrv;
-	int ret;
-
-	if ((f->flags & O_ACCMODE) == O_WRONLY) {
-		ret = -EBADF;
-		goto out;
-	}
+	int fd, ret;
 
-	fsdrv = f->fsdev->driver;
+	fd = open(file, O_WRONLY);
+	if (fd < 0)
+		return fd;
 
-	if (f->size != FILE_SIZE_STREAM && f->pos + count > f->size)
-		count = f->size - f->pos;
+	ret = protect(fd, ~0, 0, prot);
 
-	if (!count)
-		return 0;
+	close(fd);
 
-	ret = fsdrv->read(&f->fsdev->dev, f, buf, count);
-out:
-	if (ret < 0)
-		errno = -ret;
 	return ret;
 }
 
-ssize_t pread(int fd, void *buf, size_t count, loff_t offset)
+void *memmap(int fd, int flags)
 {
-	loff_t pos;
+	struct fs_driver_d *fsdrv;
 	FILE *f;
+	void *retp = (void *)-1;
 	int ret;
 
 	if (check_fd(fd))
-		return -errno;
+		return retp;
 
 	f = &files[fd];
 
-	pos = f->pos;
-	f->pos = offset;
-	ret = __read(f, buf, count);
-	f->pos = pos;
+	fsdrv = f->fsdev->driver;
 
-	return ret;
+	if (fsdrv->memmap)
+		ret = fsdrv->memmap(&f->fsdev->dev, f, &retp, flags);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		errno = -ret;
+
+	return retp;
 }
-EXPORT_SYMBOL(pread);
+EXPORT_SYMBOL(memmap);
 
-ssize_t read(int fd, void *buf, size_t count)
+int close(int fd)
 {
+	struct fs_driver_d *fsdrv;
 	FILE *f;
-	int ret;
+	int ret = 0;
 
 	if (check_fd(fd))
 		return -errno;
 
 	f = &files[fd];
 
-	ret = __read(f, buf, count);
+	fsdrv = f->fsdev->driver;
+
+	if (fsdrv->close)
+		ret = fsdrv->close(&f->fsdev->dev, f);
+
+	put_file(f);
+
+	if (ret)
+		errno = -ret;
 
-	if (ret > 0)
-		f->pos += ret;
 	return ret;
 }
-EXPORT_SYMBOL(read);
+EXPORT_SYMBOL(close);
 
-static ssize_t __write(FILE *f, const void *buf, size_t count)
+static int fs_match(struct device_d *dev, struct driver_d *drv)
 {
-	struct fs_driver_d *fsdrv;
+	return strcmp(dev->name, drv->name) ? -1 : 0;
+}
+
+static int fs_probe(struct device_d *dev)
+{
+	struct fs_device_d *fsdev = dev_to_fs_device(dev);
+	struct driver_d *drv = dev->driver;
+	struct fs_driver_d *fsdrv = container_of(drv, struct fs_driver_d, drv);
 	int ret;
 
-	if (!(f->flags & O_ACCMODE)) {
-		ret = -EBADF;
-		goto out;
+	ret = dev->driver->probe(dev);
+	if (ret)
+		return ret;
+
+	fsdev->driver = fsdrv;
+
+	list_add_tail(&fsdev->list, &fs_device_list);
+
+	if (IS_ENABLED(CONFIG_FS_LEGACY) && !fsdev->sb.s_root) {
+		ret = fs_init_legacy(fsdev);
+		if (ret)
+			return ret;
 	}
 
-	fsdrv = f->fsdev->driver;
-	if (f->size != FILE_SIZE_STREAM && f->pos + count > f->size) {
-		ret = fsdrv->truncate(&f->fsdev->dev, f, f->pos + count);
-		if (ret) {
-			if (ret != -ENOSPC)
-				goto out;
-			count = f->size - f->pos;
-			if (!count)
-				goto out;
-		} else {
-			f->size = f->pos + count;
-		}
+	return 0;
+}
+
+void dentry_kill(struct dentry *dentry)
+{
+	if (dentry->d_inode)
+		iput(dentry->d_inode);
+
+	if (!IS_ROOT(dentry))
+		dput(dentry->d_parent);
+
+	list_del(&dentry->d_child);
+	free(dentry->name);
+	free(dentry);
+}
+
+int dentry_delete_subtree(struct super_block *sb, struct dentry *parent)
+{
+	struct dentry *dentry, *tmp;
+
+	if (!parent)
+		return 0;
+
+	list_for_each_entry_safe(dentry, tmp, &parent->d_subdirs, d_child)
+		dentry_delete_subtree(sb, dentry);
+
+	dentry_kill(parent);
+
+	return 0;
+}
+
+static void destroy_inode(struct inode *inode)
+{
+	if (inode->i_sb->s_op->destroy_inode)
+		inode->i_sb->s_op->destroy_inode(inode);
+	else
+		free(inode);
+}
+
+static void fs_remove(struct device_d *dev)
+{
+	struct fs_device_d *fsdev = dev_to_fs_device(dev);
+	struct super_block *sb = &fsdev->sb;
+	struct inode *inode, *tmp;
+
+	if (fsdev->dev.driver) {
+		dev->driver->remove(dev);
+		list_del(&fsdev->list);
 	}
-	ret = fsdrv->write(&f->fsdev->dev, f, buf, count);
-out:
-	if (ret < 0)
-		errno = -ret;
-	return ret;
+
+	free(fsdev->path);
+	free(fsdev->options);
+
+	if (fsdev->cdev)
+		cdev_close(fsdev->cdev);
+
+	if (fsdev->loop && fsdev->cdev)
+		cdev_remove_loop(fsdev->cdev);
+
+	dput(sb->s_root);
+	dentry_delete_subtree(sb, sb->s_root);
+
+	list_for_each_entry_safe(inode, tmp, &sb->s_inodes, i_sb_list)
+		destroy_inode(inode);
+
+	if (fsdev->vfsmount.mountpoint)
+		fsdev->vfsmount.mountpoint->d_flags &= ~DCACHE_MOUNTED;
+
+	mntput(fsdev->vfsmount.parent);
+
+	free(fsdev->backingstore);
+	free(fsdev);
 }
 
-ssize_t pwrite(int fd, const void *buf, size_t count, loff_t offset)
+struct bus_type fs_bus = {
+	.name = "fs",
+	.match = fs_match,
+	.probe = fs_probe,
+	.remove = fs_remove,
+};
+
+static int fs_bus_init(void)
 {
-	loff_t pos;
-	FILE *f;
-	int ret;
+	return bus_register(&fs_bus);
+}
+pure_initcall(fs_bus_init);
 
-	if (check_fd(fd))
-		return -errno;
+int register_fs_driver(struct fs_driver_d *fsdrv)
+{
+	fsdrv->drv.bus = &fs_bus;
+	register_driver(&fsdrv->drv);
 
-	f = &files[fd];
+	return 0;
+}
+EXPORT_SYMBOL(register_fs_driver);
 
-	pos = f->pos;
-	f->pos = offset;
-	ret = __write(f, buf, count);
-	f->pos = pos;
+static const char *detect_fs(const char *filename, const char *fsoptions)
+{
+	enum filetype type;
+	struct driver_d *drv;
+	struct fs_driver_d *fdrv;
+	bool loop = false;
+	unsigned long long offset = 0;
 
-	return ret;
+	parseopt_b(fsoptions, "loop", &loop);
+	parseopt_llu_suffix(fsoptions, "offset", &offset);
+	if (loop)
+		type = file_name_detect_type_offset(filename, offset);
+	else
+		type = cdev_detect_type(filename);
+
+	if (type == filetype_unknown)
+		return NULL;
+
+	bus_for_each_driver(&fs_bus, drv) {
+		fdrv = drv_to_fs_driver(drv);
+
+		if (type == fdrv->type)
+			return drv->name;
+	}
+
+	return NULL;
 }
-EXPORT_SYMBOL(pwrite);
 
-ssize_t write(int fd, const void *buf, size_t count)
+int fsdev_open_cdev(struct fs_device_d *fsdev)
 {
-	FILE *f;
-	int ret;
+	unsigned long long offset = 0;
 
-	if (check_fd(fd))
-		return -errno;
+	parseopt_b(fsdev->options, "loop", &fsdev->loop);
+	parseopt_llu_suffix(fsdev->options, "offset", &offset);
+	if (fsdev->loop)
+		fsdev->cdev = cdev_create_loop(fsdev->backingstore, O_RDWR,
+					       offset);
+	else
+		fsdev->cdev = cdev_open(fsdev->backingstore, O_RDWR);
+	if (!fsdev->cdev)
+		return -EINVAL;
 
-	f = &files[fd];
+	fsdev->dev.parent = fsdev->cdev->dev;
+	fsdev->parent_device = fsdev->cdev->dev;
 
-	ret = __write(f, buf, count);
+	return 0;
+}
 
-	if (ret > 0)
-		f->pos += ret;
-	return ret;
+static void init_super(struct super_block *sb)
+{
+	INIT_LIST_HEAD(&sb->s_inodes);
 }
-EXPORT_SYMBOL(write);
 
-int flush(int fd)
-{
-	struct fs_driver_d *fsdrv;
-	FILE *f;
-	int ret;
+static int fsdev_umount(struct fs_device_d *fsdev)
+{
+	if (fsdev->vfsmount.ref)
+		return -EBUSY;
+
+	return unregister_device(&fsdev->dev);
+}
+
+/**
+ * umount_by_cdev Use a cdev struct to umount all mounted filesystems
+ * @param cdev cdev to the according device
+ * @return 0 on success or if cdev was not mounted, -errno otherwise
+ */
+int umount_by_cdev(struct cdev *cdev)
+{
+	struct fs_device_d *fs;
+	struct fs_device_d *fs_tmp;
+	int first_error = 0;
+
+	for_each_fs_device_safe(fs_tmp, fs) {
+		int ret;
+
+		if (fs->cdev == cdev) {
+			ret = fsdev_umount(fs);
+			if (ret) {
+				pr_err("Failed umounting %s, %d, continuing anyway\n",
+				       fs->path, ret);
+				if (!first_error)
+					first_error = ret;
+			}
+		}
+	}
+
+	return first_error;
+}
+EXPORT_SYMBOL(umount_by_cdev);
+
+struct readdir_entry {
+	struct dirent d;
+	struct list_head list;
+};
+
+struct readdir_callback {
+	struct dir_context ctx;
+	DIR *dir;
+};
+
+static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
+		      loff_t offset, u64 ino, unsigned int d_type)
+{
+	struct readdir_callback *rd = container_of(ctx, struct readdir_callback, ctx);
+	struct readdir_entry *entry;
+
+	entry = xzalloc(sizeof(*entry));
+	if (!entry)
+		return -ENOMEM;
+
+	memcpy(entry->d.d_name, name, namlen);
+	list_add_tail(&entry->list, &rd->dir->entries);
+
+	return 0;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+	struct readdir_entry *entry;
+
+	if (!dir)
+		return NULL;
+
+	if (list_empty(&dir->entries))
+		return NULL;
+
+	entry = list_first_entry(&dir->entries, struct readdir_entry, list);
+
+	list_del(&entry->list);
+	strcpy(dir->d.d_name, entry->d.d_name);
+	free(entry);
+
+	return &dir->d;
+}
+EXPORT_SYMBOL(readdir);
+
+static void stat_inode(struct inode *inode, struct stat *s)
+{
+	s->st_dev = 0;
+	s->st_ino = inode->i_ino;
+	s->st_mode = inode->i_mode;
+	s->st_uid = inode->i_uid;
+	s->st_gid = inode->i_gid;
+	s->st_size = inode->i_size;
+}
+
+int fstat(int fd, struct stat *s)
+{
+	FILE *f;
+	struct fs_device_d *fsdev;
+
+	if (check_fd(fd))
+		return -errno;
+
+	f = &files[fd];
+
+	fsdev = f->fsdev;
+
+	stat_inode(f->f_inode, s);
+
+	return 0;
+}
+EXPORT_SYMBOL(fstat);
+
+/*
+ * cdev_get_mount_path - return the path a cdev is mounted on
+ *
+ * If a cdev is mounted return the path it's mounted on, NULL
+ * otherwise.
+ */
+const char *cdev_get_mount_path(struct cdev *cdev)
+{
+	struct fs_device_d *fsdev;
+
+	for_each_fs_device(fsdev) {
+		if (fsdev->cdev && fsdev->cdev == cdev)
+			return fsdev->path;
+	}
+
+	return NULL;
+}
+
+/*
+ * cdev_mount_default - mount a cdev to the default path
+ *
+ * If a cdev is already mounted return the path it's mounted on, otherwise
+ * mount it to /mnt/<cdevname> and return the path. Returns an error pointer
+ * on failure.
+ */
+const char *cdev_mount_default(struct cdev *cdev, const char *fsoptions)
+{
+	const char *path;
+	char *newpath, *devpath;
+	int ret;
+
+	/*
+	 * If this cdev is already mounted somewhere use this path
+	 * instead of mounting it again to avoid corruption on the
+	 * filesystem. Note this ignores eventual fsoptions though.
+	 */
+	path = cdev_get_mount_path(cdev);
+	if (path)
+		return path;
+
+	newpath = basprintf("/mnt/%s", cdev->name);
+	make_directory(newpath);
+
+	devpath = basprintf("/dev/%s", cdev->name);
+
+	ret = mount(devpath, NULL, newpath, fsoptions);
+
+	free(devpath);
+
+	if (ret) {
+		free(newpath);
+		return ERR_PTR(ret);
+	}
+
+	return cdev_get_mount_path(cdev);
+}
+
+/*
+ * mount_all - iterate over block devices and mount all devices we are able to
+ */
+void mount_all(void)
+{
+	struct device_d *dev;
+	struct block_device *bdev;
+
+	if (!IS_ENABLED(CONFIG_BLOCK))
+		return;
+
+	for_each_device(dev)
+		device_detect(dev);
+
+	for_each_block_device(bdev) {
+		struct cdev *cdev = &bdev->cdev;
+
+		list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list)
+			cdev_mount_default(cdev, NULL);
+	}
+}
+
+void fsdev_set_linux_rootarg(struct fs_device_d *fsdev, const char *str)
+{
+	fsdev->linux_rootarg = xstrdup(str);
+
+	dev_add_param_fixed(&fsdev->dev, "linux.bootargs", fsdev->linux_rootarg);
+}
+
+/**
+ * path_get_linux_rootarg() - Given a path return a suitable root= option for
+ *                            Linux
+ * @path: The path
+ *
+ * Return: A string containing the root= option or an ERR_PTR. the returned
+ *         string must be freed by the caller.
+ */
+char *path_get_linux_rootarg(const char *path)
+{
+	struct fs_device_d *fsdev;
+	const char *str;
+
+	fsdev = get_fsdevice_by_path(path);
+	if (!fsdev)
+		return ERR_PTR(-EINVAL);
+
+	str = dev_get_param(&fsdev->dev, "linux.bootargs");
+	if (!str)
+		return ERR_PTR(-ENOSYS);
+
+	return xstrdup(str);
+}
+
+/**
+ * __is_tftp_fs() - return true when path is mounted on TFTP
+ * @path: The path
+ *
+ * Do not use directly, use is_tftp_fs instead.
+ *
+ * Return: true when @path is on TFTP, false otherwise
+ */
+bool __is_tftp_fs(const char *path)
+{
+	struct fs_device_d *fsdev;
+
+	fsdev = get_fsdevice_by_path(path);
+	if (!fsdev)
+		return false;
+
+	if (strcmp(fsdev->driver->drv.name, "tftp"))
+		return false;
+
+	return true;
+}
+
+/* inode.c */
+unsigned int get_next_ino(void)
+{
+	static unsigned int ino;
+
+	return ++ino;
+}
+
+void drop_nlink(struct inode *inode)
+{
+	WARN_ON(inode->i_nlink == 0);
+	inode->__i_nlink--;
+}
+
+void inc_nlink(struct inode *inode)
+{
+	inode->__i_nlink++;
+}
+
+static struct inode *alloc_inode(struct super_block *sb)
+{
+	static const struct inode_operations empty_iops;
+	static const struct file_operations no_open_fops;
+	struct inode *inode;
+
+	if (sb->s_op->alloc_inode)
+		inode = sb->s_op->alloc_inode(sb);
+	else
+		inode = xzalloc(sizeof(*inode));
+
+	inode->i_op = &empty_iops;
+	inode->i_fop = &no_open_fops;
+	inode->__i_nlink = 1;
+	inode->i_count = 1;
+
+	return inode;
+}
+
+struct inode *new_inode(struct super_block *sb)
+{
+	struct inode *inode;
+
+	inode = alloc_inode(sb);
+	if (!inode)
+		return NULL;
+
+	inode->i_sb = sb;
+
+	list_add(&inode->i_sb_list, &sb->s_inodes);
+
+	return inode;
+}
+
+void iput(struct inode *inode)
+{
+	if (!inode->i_count)
+		return;
+
+	inode->i_count--;
+}
+
+struct inode *iget(struct inode *inode)
+{
+	inode->i_count++;
+
+	return inode;
+}
+
+/* dcache.c */
+
+/*
+ * refcounting is implemented but right now we do not do anything with
+ * the refcounting information. Dentries are never freed unless the
+ * filesystem they are on is unmounted. In this case we do not care
+ * about the refcounts so we may free up a dentry that is actually used
+ * (file is opened). This leaves room for improvements.
+ */
+void dput(struct dentry *dentry)
+{
+	if (!dentry)
+		return;
+
+	if (!dentry->d_count)
+		return;
+
+	dentry->d_count--;
+}
+
+struct dentry *dget(struct dentry *dentry)
+{
+	if (!dentry)
+		return NULL;
+
+	dentry->d_count++;
+
+	return dentry;
+}
+
+const struct qstr slash_name = QSTR_INIT("/", 1);
+
+void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
+{
+	dentry->d_op = op;
+}
+
+/**
+ * __d_alloc	-	allocate a dcache entry
+ * @sb: filesystem it will belong to
+ * @name: qstr of the name
+ *
+ * Allocates a dentry. It returns %NULL if there is insufficient memory
+ * available. On a success the dentry is returned. The name passed in is
+ * copied and the copy passed in may be reused after this call.
+ */
+struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
+{
+	struct dentry *dentry;
+
+	dentry = xzalloc(sizeof(*dentry));
+	if (!dentry)
+		return NULL;
+
+	if (!name)
+		name = &slash_name;
+
+	dentry->name = malloc(name->len + 1);
+	if (!dentry->name)
+		return NULL;
+
+	memcpy(dentry->name, name->name, name->len);
+	dentry->name[name->len] = 0;
+
+	dentry->d_name.len = name->len;
+	dentry->d_name.name = dentry->name;
+
+	dentry->d_count = 1;
+	dentry->d_parent = dentry;
+	dentry->d_sb = sb;
+	INIT_LIST_HEAD(&dentry->d_subdirs);
+	INIT_LIST_HEAD(&dentry->d_child);
+	d_set_d_op(dentry, dentry->d_sb->s_d_op);
+
+	return dentry;
+}
+
+/**
+ * d_alloc	-	allocate a dcache entry
+ * @parent: parent of entry to allocate
+ * @name: qstr of the name
+ *
+ * Allocates a dentry. It returns %NULL if there is insufficient memory
+ * available. On a success the dentry is returned. The name passed in is
+ * copied and the copy passed in may be reused after this call.
+ */
+struct dentry *d_alloc(struct dentry *parent, const struct qstr *name)
+{
+	struct dentry *dentry = __d_alloc(parent->d_sb, name);
+	if (!dentry)
+		return NULL;
+
+	dget(parent);
+
+	dentry->d_parent = parent;
+	list_add(&dentry->d_child, &parent->d_subdirs);
+
+	return dentry;
+}
+
+struct dentry *d_alloc_anon(struct super_block *sb)
+{
+	return __d_alloc(sb, NULL);
+}
+
+static unsigned d_flags_for_inode(struct inode *inode)
+{
+        if (!inode)
+                return DCACHE_MISS_TYPE;
+
+	if (S_ISDIR(inode->i_mode))
+		return DCACHE_DIRECTORY_TYPE;
+
+	if (inode->i_op->get_link)
+		return DCACHE_SYMLINK_TYPE;
+
+	return DCACHE_REGULAR_TYPE;
+}
+
+void d_instantiate(struct dentry *dentry, struct inode *inode)
+{
+	dentry->d_inode = inode;
+	dentry->d_flags &= ~DCACHE_ENTRY_TYPE;
+	dentry->d_flags |= d_flags_for_inode(inode);
+}
+
+struct dentry *d_make_root(struct inode *inode)
+{
+	struct dentry *res;
+
+	if (!inode)
+		return NULL;
+
+	res = d_alloc_anon(inode->i_sb);
+	if (!res)
+		return NULL;
+
+	d_instantiate(res, inode);
+
+	return res;
+}
+
+void d_add(struct dentry *dentry, struct inode *inode)
+{
+	dentry->d_inode = inode;
+	dentry->d_flags &= ~DCACHE_ENTRY_TYPE;
+	dentry->d_flags |= d_flags_for_inode(inode);
+}
+
+static bool d_same_name(const struct dentry *dentry,
+			const struct dentry *parent,
+			const struct qstr *name)
+{
+	if (dentry->d_name.len != name->len)
+		return false;
+
+	return strncmp(dentry->d_name.name, name->name, name->len) == 0;
+}
+
+struct dentry *d_lookup(const struct dentry *parent, const struct qstr *name)
+{
+	struct dentry *dentry;
+
+	list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
+		if (!d_same_name(dentry, parent, name))
+			continue;
+
+		dget(dentry);
+
+		return dentry;
+	}
+
+	return NULL;
+}
+
+void d_invalidate(struct dentry *dentry)
+{
+}
+
+static inline void __d_clear_type_and_inode(struct dentry *dentry)
+{
+	dentry->d_flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
+
+	dentry->d_inode = NULL;
+}
+
+/*
+ * Release the dentry's inode, using the filesystem
+ * d_iput() operation if defined.
+ */
+static void dentry_unlink_inode(struct dentry * dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	__d_clear_type_and_inode(dentry);
+	iput(inode);
+}
+
+void d_delete(struct dentry * dentry)
+{
+	dentry_unlink_inode(dentry);
+}
+
+/*
+ * These are the Linux name resolve functions from fs/namei.c
+ *
+ * The implementation is more or less directly ported from the
+ * Linux Kernel (as of Linux-4.16) minus the RCU and locking code.
+ */
+
+enum {WALK_FOLLOW = 1, WALK_MORE = 2};
+
+/*
+ * Define EMBEDDED_LEVELS to MAXSYMLINKS so we do not have to
+ * dynamically allocate a path stack.
+ */
+#define EMBEDDED_LEVELS MAXSYMLINKS
+
+struct nameidata {
+	struct path	path;
+	struct qstr	last;
+	struct inode	*inode; /* path.dentry.d_inode */
+	unsigned int	flags;
+	unsigned	seq, m_seq;
+	int		last_type;
+	unsigned	depth;
+	int		total_link_count;
+	struct saved {
+		struct path link;
+		const char *name;
+		unsigned seq;
+	} *stack, internal[EMBEDDED_LEVELS];
+	struct filename	*name;
+	struct nameidata *saved;
+	struct inode	*link_inode;
+	unsigned	root_seq;
+	int		dfd;
+};
+
+struct filename {
+	char *name;
+	int refcnt;
+};
+
+static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
+{
+	p->stack = p->internal;
+	p->dfd = dfd;
+	p->name = name;
+	p->total_link_count = 0;
+}
+
+void path_get(const struct path *path)
+{
+	mntget(path->mnt);
+	dget(path->dentry);
+}
+
+void path_put(const struct path *path)
+{
+	dput(path->dentry);
+	mntput(path->mnt);
+}
+
+static inline void get_root(struct path *root)
+{
+	root->dentry = d_root;
+	root->mnt = mnt_root;
+
+	path_get(root);
+}
+
+static inline void get_pwd(struct path *pwd)
+{
+	if (!cwd_dentry) {
+		cwd_dentry = d_root;
+		cwd_mnt = mnt_root;
+	}
+
+	pwd->dentry = cwd_dentry;
+	pwd->mnt = cwd_mnt;
+
+	path_get(pwd);
+}
+
+static inline void put_link(struct nameidata *nd)
+{
+	struct saved *last = nd->stack + --nd->depth;
+	path_put(&last->link);
+}
+
+static int automount_mount(struct dentry *dentry);
+
+static void path_put_conditional(struct path *path, struct nameidata *nd)
+{
+	dput(path->dentry);
+	if (path->mnt != nd->path.mnt)
+		mntput(path->mnt);
+}
+
+static int follow_automount(struct path *path, struct nameidata *nd,
+			    bool *need_mntput)
+{
+	/* We don't want to mount if someone's just doing a stat -
+	 * unless they're stat'ing a directory and appended a '/' to
+	 * the name.
+	 *
+	 * We do, however, want to mount if someone wants to open or
+	 * create a file of any type under the mountpoint, wants to
+	 * traverse through the mountpoint or wants to open the
+	 * mounted directory.  Also, autofs may mark negative dentries
+	 * as being automount points.  These will need the attentions
+	 * of the daemon to instantiate them before they can be used.
+	 */
+	if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
+			LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
+			path->dentry->d_inode)
+		return -EISDIR;
+
+	return automount_mount(path->dentry);
+}
+
+/*
+ * Handle a dentry that is managed in some way.
+ * - Flagged for transit management (autofs)
+ * - Flagged as mountpoint
+ * - Flagged as automount point
+ *
+ * This may only be called in refwalk mode.
+ *
+ * Serialization is taken care of in namespace.c
+ */
+static int follow_managed(struct path *path, struct nameidata *nd)
+{
+	struct vfsmount *mnt = path->mnt;
+	unsigned managed = path->dentry->d_flags;
+	bool need_mntput = false;
+	int ret = 0;
+
+	while (managed = path->dentry->d_flags,
+		managed &= DCACHE_MANAGED_DENTRY,
+		managed != 0) {
+
+		if (managed & DCACHE_MOUNTED) {
+			struct vfsmount *mounted = lookup_mnt(path);
+
+			if (mounted) {
+				dput(path->dentry);
+				if (need_mntput)
+					mntput(path->mnt);
+				path->mnt = mounted;
+				path->dentry = dget(mounted->mnt_root);
+				need_mntput = true;
+				continue;
+			}
+		}
+
+		/* Handle an automount point */
+		if (managed & DCACHE_NEED_AUTOMOUNT) {
+			ret = follow_automount(path, nd, &need_mntput);
+			if (ret < 0)
+				break;
+			continue;
+		}
+
+		/* We didn't change the current path point */
+		break;
+	}
+
+	if (need_mntput && path->mnt == mnt)
+		mntput(path->mnt);
+	if (ret == -EISDIR || !ret)
+		ret = 1;
+	if (need_mntput)
+		nd->flags |= LOOKUP_JUMPED;
+	if (ret < 0)
+		path_put_conditional(path, nd);
+	return ret;
+}
+
+static struct dentry *__lookup_hash(const struct qstr *name,
+		struct dentry *base, unsigned int flags)
+{
+	struct dentry *dentry;
+	struct dentry *old;
+	struct inode *dir = base->d_inode;
+
+	if (!base)
+		return ERR_PTR(-ENOENT);
+
+	dentry = d_lookup(base, name);
+	if (dentry)
+		return dentry;
+
+	dentry = d_alloc(base, name);
+	if (unlikely(!dentry))
+		return ERR_PTR(-ENOMEM);
+
+	old = dir->i_op->lookup(dir, dentry, flags);
+	if (IS_ERR(old)) {
+		dput(dentry);
+		return old;
+	}
+
+	if (unlikely(old)) {
+		dput(dentry);
+		dentry = old;
+	}
+
+	return dentry;
+}
+
+static int lookup_fast(struct nameidata *nd, struct path *path)
+{
+	struct dentry *dentry, *parent = nd->path.dentry;
+
+	dentry = __lookup_hash(&nd->last, parent, 0);
+	if (IS_ERR(dentry))
+		return PTR_ERR(dentry);
+
+	if (d_is_negative(dentry)) {
+		dput(dentry);
+		return -ENOENT;
+	}
+
+	path->dentry = dentry;
+	path->mnt = nd->path.mnt;
+
+	return follow_managed(path, nd);
+}
+
+/*
+ * follow_up - Find the mountpoint of path's vfsmount
+ *
+ * Given a path, find the mountpoint of its source file system.
+ * Replace @path with the path of the mountpoint in the parent mount.
+ * Up is towards /.
+ *
+ * Return 1 if we went up a level and 0 if we were already at the
+ * root.
+ */
+int follow_up(struct path *path)
+{
+	struct vfsmount *parent, *mnt = path->mnt;
+	struct dentry *mountpoint;
+
+	parent = mnt->parent;
+	if (parent == mnt)
+		return 0;
+
+	mntget(parent);
+	mountpoint = dget(mnt->mountpoint);
+	dput(path->dentry);
+	path->dentry = mountpoint;
+	mntput(path->mnt);
+	path->mnt = mnt->parent;
+
+	return 1;
+}
+
+static void follow_mount(struct path *path)
+{
+	while (d_mountpoint(path->dentry)) {
+		struct vfsmount *mounted = lookup_mnt(path);
+		if (!mounted)
+			break;
+		dput(path->dentry);
+		path->mnt = mounted;
+		path->dentry = dget(mounted->mnt_root);
+	}
+}
+
+static int path_parent_directory(struct path *path)
+{
+	struct dentry *old = path->dentry;
+
+	path->dentry = dget(path->dentry->d_parent);
+	dput(old);
+
+	return 0;
+}
+
+static int follow_dotdot(struct nameidata *nd)
+{
+	while (1) {
+		if (nd->path.dentry != nd->path.mnt->mnt_root) {
+			int ret = path_parent_directory(&nd->path);
+			if (ret)
+				return ret;
+			break;
+		}
+
+		if (!follow_up(&nd->path))
+			break;
+	}
+
+	follow_mount(&nd->path);
+
+	nd->inode = nd->path.dentry->d_inode;
+
+	return 0;
+}
+
+static inline int handle_dots(struct nameidata *nd, int type)
+{
+	if (type == LAST_DOTDOT) {
+		return follow_dotdot(nd);
+	}
+	return 0;
+}
+
+static inline void path_to_nameidata(const struct path *path,
+					struct nameidata *nd)
+{
+	dput(nd->path.dentry);
+	if (nd->path.mnt != path->mnt)
+		mntput(nd->path.mnt);
+	nd->path.mnt = path->mnt;
+	nd->path.dentry = path->dentry;
+}
+
+static const char *get_link(struct nameidata *nd)
+{
+	struct saved *last = nd->stack + nd->depth - 1;
+	struct dentry *dentry = last->link.dentry;
+	struct inode *inode = nd->link_inode;
+	const char *res;
+
+	nd->last_type = LAST_BIND;
+	res = inode->i_link;
+	if (!res) {
+		res = inode->i_op->get_link(dentry, inode);
+		if (IS_ERR_OR_NULL(res))
+			return res;
+	}
+	if (*res == '/') {
+		while (unlikely(*++res == '/'))
+			;
+	}
+	if (!*res)
+		res = NULL;
+	return res;
+}
+
+static int pick_link(struct nameidata *nd, struct path *link,
+		     struct inode *inode)
+{
+	struct saved *last;
+
+	if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) {
+		path_to_nameidata(link, nd);
+		return -ELOOP;
+	}
+
+	if (link->mnt == nd->path.mnt)
+		mntget(link->mnt);
+
+	last = nd->stack + nd->depth++;
+	last->link = *link;
+	nd->link_inode = inode;
+
+	return 1;
+}
+
+/*
+ * Do we need to follow links? We _really_ want to be able
+ * to do this check without having to look at inode->i_op,
+ * so we keep a cache of "no, this doesn't need follow_link"
+ * for the common case.
+ */
+static inline int step_into(struct nameidata *nd, struct path *path,
+			    int flags, struct inode *inode)
+{
+	if (!(flags & WALK_MORE) && nd->depth)
+		put_link(nd);
+
+	if (likely(!d_is_symlink(path->dentry)) ||
+	   !(flags & WALK_FOLLOW || nd->flags & LOOKUP_FOLLOW)) {
+		/* not a symlink or should not follow */
+		path_to_nameidata(path, nd);
+		nd->inode = inode;
+		return 0;
+	}
+
+	return pick_link(nd, path, inode);
+}
+
+static int walk_component(struct nameidata *nd, int flags)
+{
+	struct path path;
+	int err;
+
+	/*
+	 * "." and ".." are special - ".." especially so because it has
+	 * to be able to know about the current root directory and
+	 * parent relationships.
+	 */
+	if (nd->last_type != LAST_NORM) {
+		err = handle_dots(nd, nd->last_type);
+		if (!(flags & WALK_MORE) && nd->depth)
+			put_link(nd);
+		return err;
+	}
+
+	err = lookup_fast(nd, &path);
+	if (err < 0)
+		return err;
+
+	if (err == 0) {
+		path.mnt = nd->path.mnt;
+		err = follow_managed(&path, nd);
+		if (err < 0)
+			return err;
+
+		if (d_is_negative(path.dentry)) {
+			path_to_nameidata(&path, nd);
+			return -ENOENT;
+		}
+	}
+
+	return step_into(nd, &path, flags, d_inode(path.dentry));
+}
+
+static int component_len(const char *name, char separator)
+{
+	int len = 0;
+
+	while (name[len] && name[len] != separator)
+		len++;
+
+	return len;
+}
+
+struct filename *getname(const char *filename)
+{
+	struct filename *result;
+
+	result = malloc(sizeof(*result));
+	if (!result)
+		return NULL;
+
+	result->name = strdup(filename);
+	if (!result->name) {
+		free(result);
+		return NULL;
+	}
+
+	result->refcnt = 1;
+
+	return result;
+}
+
+void putname(struct filename *name)
+{
+	BUG_ON(name->refcnt <= 0);
+
+	if (--name->refcnt > 0)
+		return;
+
+	free(name->name);
+	free(name);
+}
+
+static struct fs_device_d *get_fsdevice_by_dentry(struct dentry *dentry)
+{
+	struct super_block *sb;
+
+	sb = dentry->d_sb;
+
+	return container_of(sb, struct fs_device_d, sb);
+}
+
+static bool dentry_is_tftp(struct dentry *dentry)
+{
+	struct fs_device_d *fsdev;
+
+	fsdev = get_fsdevice_by_dentry(dentry);
+	if (!fsdev)
+		return false;
+
+	if (strcmp(fsdev->driver->drv.name, "tftp"))
+		return false;
+
+	return true;
+}
+
+/*
+ * Name resolution.
+ * This is the basic name resolution function, turning a pathname into
+ * the final dentry. We expect 'base' to be positive and a directory.
+ *
+ * Returns 0 and nd will have valid dentry and mnt on success.
+ * Returns error and drops reference to input namei data on failure.
+ */
+static int link_path_walk(const char *name, struct nameidata *nd)
+{
+	int err;
+	char separator = '/';
+
+	while (*name=='/')
+		name++;
+	if (!*name)
+		return 0;
+
+	/* At this point we know we have a real path component. */
+	for(;;) {
+		int len;
+		int type;
+
+		len = component_len(name, separator);
+
+		type = LAST_NORM;
+		if (name[0] == '.') switch (len) {
+			case 2:
+				if (name[1] == '.') {
+					type = LAST_DOTDOT;
+					nd->flags |= LOOKUP_JUMPED;
+				}
+				break;
+			case 1:
+				type = LAST_DOT;
+		}
+		if (likely(type == LAST_NORM))
+			nd->flags &= ~LOOKUP_JUMPED;
+
+		nd->last.len = len;
+		nd->last.name = name;
+		nd->last_type = type;
+
+		name += len;
+		if (!*name)
+			goto OK;
+
+		/*
+		 * If it wasn't NUL, we know it was '/'. Skip that
+		 * slash, and continue until no more slashes.
+		 */
+		do {
+			name++;
+		} while (unlikely(*name == separator));
+
+		if (unlikely(!*name)) {
+OK:
+			/* pathname body, done */
+			if (!nd->depth)
+				return 0;
+			name = nd->stack[nd->depth - 1].name;
+			/* trailing symlink, done */
+			if (!name)
+				return 0;
+			/* last component of nested symlink */
+			err = walk_component(nd, WALK_FOLLOW);
+		} else {
+			/* not the last component */
+			err = walk_component(nd, WALK_FOLLOW | WALK_MORE);
+		}
+
+		if (err < 0)
+			return err;
+
+		/*
+		 * barebox specific hack for TFTP. TFTP does not support
+		 * looking up directories, only the files in directories.
+		 * Since the filename is not known at this point we replace
+		 * the path separator with an invalid char so that TFTP will
+		 * get the full remaining path including slashes.
+		 */
+		if (dentry_is_tftp(nd->path.dentry))
+			separator = 0x1;
+
+		if (err) {
+			const char *s = get_link(nd);
+
+			if (IS_ERR(s))
+				return PTR_ERR(s);
+			err = 0;
+			if (unlikely(!s)) {
+				/* jumped */
+				put_link(nd);
+			} else {
+				nd->stack[nd->depth - 1].name = name;
+				name = s;
+				continue;
+			}
+		}
+		if (unlikely(!d_can_lookup(nd->path.dentry)))
+			return -ENOTDIR;
+	}
+}
+
+static const char *path_init(struct nameidata *nd, unsigned flags)
+{
+	const char *s = nd->name->name;
+
+	nd->last_type = LAST_ROOT; /* if there are only slashes... */
+	nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
+	nd->depth = 0;
+
+	nd->path.mnt = NULL;
+	nd->path.dentry = NULL;
+
+	if (*s == '/') {
+		get_root(&nd->path);
+		return s;
+	} else if (nd->dfd == AT_FDCWD) {
+		get_pwd(&nd->path);
+		nd->inode = nd->path.dentry->d_inode;
+		return s;
+	}
+
+	return s;
+}
+
+static const char *trailing_symlink(struct nameidata *nd)
+{
+	const char *s;
+
+	nd->flags |= LOOKUP_PARENT;
+	nd->stack[0].name = NULL;
+	s = get_link(nd);
+
+	return s ? s : "";
+}
+
+static inline int lookup_last(struct nameidata *nd)
+{
+	if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
+		nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+
+	nd->flags &= ~LOOKUP_PARENT;
+	return walk_component(nd, 0);
+}
+
+static void terminate_walk(struct nameidata *nd)
+{
+	int i;
+
+	path_put(&nd->path);
+	for (i = 0; i < nd->depth; i++)
+		path_put(&nd->stack[i].link);
+
+	nd->depth = 0;
+}
+
+/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
+static int path_parentat(struct nameidata *nd, unsigned flags,
+				struct path *parent)
+{
+	const char *s = path_init(nd, flags);
+	int err;
+
+	if (IS_ERR(s))
+		return PTR_ERR(s);
+
+	err = link_path_walk(s, nd);
+	if (!err) {
+		*parent = nd->path;
+		nd->path.mnt = NULL;
+		nd->path.dentry = NULL;
+	}
+	terminate_walk(nd);
+	return err;
+}
+
+static struct filename *filename_parentat(int dfd, struct filename *name,
+				unsigned int flags, struct path *parent,
+				struct qstr *last, int *type)
+{
+	int retval;
+	struct nameidata nd;
+
+	if (IS_ERR(name))
+		return name;
+
+	set_nameidata(&nd, dfd, name);
+
+	retval = path_parentat(&nd, flags, parent);
+	if (likely(!retval)) {
+		*last = nd.last;
+		*type = nd.last_type;
+	} else {
+		putname(name);
+		name = ERR_PTR(retval);
+	}
+
+	return name;
+}
+
+static struct dentry *filename_create(int dfd, struct filename *name,
+				struct path *path, unsigned int lookup_flags)
+{
+	struct dentry *dentry = ERR_PTR(-EEXIST);
+	struct qstr last;
+	int type;
+	int error;
+	bool is_dir = (lookup_flags & LOOKUP_DIRECTORY);
+
+	/*
+	 * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any
+	 * other flags passed in are ignored!
+	 */
+	lookup_flags &= LOOKUP_REVAL;
+
+	name = filename_parentat(dfd, name, 0, path, &last, &type);
+	if (IS_ERR(name))
+		return ERR_CAST(name);
+
+	/*
+	 * Yucky last component or no last component at all?
+	 * (foo/., foo/.., /////)
+	 */
+	if (unlikely(type != LAST_NORM))
+		goto out;
+
+	/*
+	 * Do the final lookup.
+	 */
+	lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+	dentry = __lookup_hash(&last, path->dentry, lookup_flags);
+	if (IS_ERR(dentry))
+		goto unlock;
+
+	error = -EEXIST;
+	if (d_is_positive(dentry))
+		goto fail;
+
+	/*
+	 * Special case - lookup gave negative, but... we had foo/bar/
+	 * From the vfs_mknod() POV we just have a negative dentry -
+	 * all is fine. Let's be bastards - you had / on the end, you've
+	 * been asking for (non-existent) directory. -ENOENT for you.
+	 */
+	if (unlikely(!is_dir && last.name[last.len])) {
+		error = -ENOENT;
+		goto fail;
+	}
+	putname(name);
+	return dentry;
+fail:
+	dput(dentry);
+	dentry = ERR_PTR(error);
+unlock:
+out:
+	path_put(path);
+	putname(name);
+	return dentry;
+}
+
+static int filename_lookup(int dfd, struct filename *name, unsigned flags,
+			   struct path *path)
+{
+	int err;
+	struct nameidata nd;
+	const char *s;
+
+	set_nameidata(&nd, dfd, name);
+
+	s = path_init(&nd, flags);
+
+	while (!(err = link_path_walk(s, &nd)) && ((err = lookup_last(&nd)) > 0)) {
+		s = trailing_symlink(&nd);
+		if (IS_ERR(s)) {
+			err = PTR_ERR(s);
+			break;
+		}
+	}
+
+	if (!err && nd.flags & LOOKUP_DIRECTORY)
+		if (!d_can_lookup(nd.path.dentry))
+			err = -ENOTDIR;
+	if (!err) {
+		*path = nd.path;
+		nd.path.mnt = NULL;
+		nd.path.dentry = NULL;
+	}
+
+	terminate_walk(&nd);
+	putname(name);
+
+	return err;
+}
+
+static struct fs_device_d *get_fsdevice_by_path(const char *pathname)
+{
+	struct fs_device_d *fsdev;
+	struct path path;
+	int ret;
+
+	ret = filename_lookup(AT_FDCWD, getname(pathname), 0, &path);
+	if (ret)
+		return NULL;
+
+	fsdev = get_fsdevice_by_dentry(path.dentry);
+
+	path_put(&path);
+
+	return fsdev;
+}
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int error;
+
+	if (!dir->i_op->rmdir)
+		return -EPERM;
+
+	dget(dentry);
+
+	error = dir->i_op->rmdir(dir, dentry);
+	if (error)
+		goto out;
+
+	dentry->d_inode->i_flags |= S_DEAD;
+
+out:
+	dput(dentry);
+
+	if (!error)
+		d_delete(dentry);
+
+	return error;
+}
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	int error;
+
+	if (!dir->i_op->mkdir)
+		return -EPERM;
+
+	mode &= (S_IRWXUGO|S_ISVTX);
+
+	error = dir->i_op->mkdir(dir, dentry, mode);
+
+	return error;
+}
+
+/* libfs.c */
+
+/* ---------------------------------------------------------------- */
+int mkdir (const char *pathname, mode_t mode)
+{
+	struct dentry *dentry;
+	struct path path;
+	int error;
+	unsigned int lookup_flags = LOOKUP_DIRECTORY;
+
+	dentry = filename_create(AT_FDCWD, getname(pathname), &path, lookup_flags);
+	if (IS_ERR(dentry)) {
+		error = PTR_ERR(dentry);
+		goto out;
+	}
+
+	error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+
+	dput(dentry);
+	path_put(&path);
+out:
+	if (error)
+		errno = -error;
+
+	return error;
+}
+EXPORT_SYMBOL(mkdir);
+
+int rmdir (const char *pathname)
+{
+	int error = 0;
+	struct filename *name;
+	struct dentry *dentry;
+	struct path path;
+	struct qstr last;
+	int type;
+
+	name = filename_parentat(AT_FDCWD, getname(pathname), 0,
+				&path, &last, &type);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
+
+	switch (type) {
+	case LAST_DOTDOT:
+		error = -ENOTEMPTY;
+		goto out;
+	case LAST_DOT:
+		error = -EINVAL;
+		goto out;
+	case LAST_ROOT:
+		error = -EBUSY;
+		goto out;
+	}
+
+	dentry = __lookup_hash(&last, path.dentry, 0);
+	if (d_is_negative(dentry)) {
+		error = -ENOENT;
+		goto out;
+	}
+	if (d_mountpoint(dentry)) {
+		error = -EBUSY;
+		goto out;
+	}
+
+	if (!d_is_dir(dentry)) {
+		error = -ENOTDIR;
+		goto out;
+	}
+
+	error = vfs_rmdir(path.dentry->d_inode, dentry);
+
+	dput(dentry);
+out:
+	path_put(&path);
+	putname(name);
+
+	if (error)
+		errno = -error;
+
+	return error;
+}
+EXPORT_SYMBOL(rmdir);
+
+int open(const char *pathname, int flags, ...)
+{
+	struct fs_device_d *fsdev;
+	struct fs_driver_d *fsdrv;
+	struct super_block *sb;
+	FILE *f;
+	int error = 0;
+	struct inode *inode = NULL;
+	struct dentry *dentry = NULL;
+	struct nameidata nd;
+	const char *s;
+
+	set_nameidata(&nd, AT_FDCWD, getname(pathname));
+	s = path_init(&nd, LOOKUP_FOLLOW);
+
+	while (1) {
+		error = link_path_walk(s, &nd);
+		if (error)
+			break;
+
+		if (!d_is_dir(nd.path.dentry)) {
+			error = -ENOTDIR;
+			break;
+		}
+
+		dentry = __lookup_hash(&nd.last, nd.path.dentry, 0);
+		if (IS_ERR(dentry)) {
+			error = PTR_ERR(dentry);
+			break;
+		}
+
+		if (!d_is_symlink(dentry))
+			break;
 
-	if (check_fd(fd))
-		return -errno;
+		dput(dentry);
 
-	f = &files[fd];
+		error = lookup_last(&nd);
+		if (error <= 0)
+			break;
 
-	fsdrv = f->fsdev->driver;
-	if (fsdrv->flush)
-		ret = fsdrv->flush(&f->fsdev->dev, f);
-	else
-		ret = 0;
+		s = trailing_symlink(&nd);
+		if (IS_ERR(s)) {
+			error = PTR_ERR(s);
+			break;
+		}
+	}
 
-	if (ret)
-		errno = -ret;
+	terminate_walk(&nd);
+	putname(nd.name);
 
-	return ret;
-}
+	if (error)
+		return error;
 
-loff_t lseek(int fildes, loff_t offset, int whence)
-{
-	struct fs_driver_d *fsdrv;
-	FILE *f;
-	loff_t pos;
-	int ret;
+	if (d_is_negative(dentry)) {
+		if (flags & O_CREAT) {
+			error = create(nd.path.dentry, dentry);
+			if (error)
+				goto out1;
+		} else {
+			dput(dentry);
+			error = -ENOENT;
+			goto out1;
+		}
+	} else {
+		if (d_is_dir(dentry) && !dentry_is_tftp(dentry)) {
+			error = -EISDIR;
+			goto out1;
+		}
+	}
 
-	if (check_fd(fildes))
-		return -1;
+	inode = d_inode(dentry);
 
-	f = &files[fildes];
-	fsdrv = f->fsdev->driver;
-	if (!fsdrv->lseek) {
-		ret = -ENOSYS;
-		goto out;
+	f = get_file();
+	if (!f) {
+		error = -EMFILE;
+		goto out1;
 	}
 
-	ret = -EINVAL;
+	f->path = xstrdup(pathname);
+	f->dentry = dentry;
+	f->f_inode = iget(inode);
+	f->flags = flags;
+	f->size = inode->i_size;
 
-	switch (whence) {
-	case SEEK_SET:
-		if (f->size != FILE_SIZE_STREAM && offset > f->size)
-			goto out;
-		if (offset < 0)
-			goto out;
-		pos = offset;
-		break;
-	case SEEK_CUR:
-		if (f->size != FILE_SIZE_STREAM && offset + f->pos > f->size)
-			goto out;
-		pos = f->pos + offset;
-		break;
-	case SEEK_END:
-		if (offset > 0)
+	sb = inode->i_sb;
+	fsdev = container_of(sb, struct fs_device_d, sb);
+	fsdrv = fsdev->driver;
+
+	f->fsdev = fsdev;
+
+	if (fsdrv->open) {
+		char *pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+		error = fsdrv->open(&fsdev->dev, f, pathname);
+		free(pathname);
+		if (error)
 			goto out;
-		pos = f->size + offset;
-		break;
-	default:
-		goto out;
 	}
 
-	pos = fsdrv->lseek(&f->fsdev->dev, f, pos);
-	if (pos < 0) {
-		errno = -pos;
-		return -1;
+	if (flags & O_TRUNC) {
+		error = fsdrv->truncate(&fsdev->dev, f, 0);
+		f->size = 0;
+		inode->i_size = 0;
+		if (error)
+			goto out;
 	}
 
-	return pos;
+	if (flags & O_APPEND)
+		f->pos = f->size;
+
+	return f->no;
 
 out:
-	if (ret)
-		errno = -ret;
+	put_file(f);
+out1:
 
-	return -1;
+	if (error)
+		errno = -error;
+	return error;
 }
-EXPORT_SYMBOL(lseek);
+EXPORT_SYMBOL(open);
 
-int erase(int fd, loff_t count, loff_t offset)
+int unlink(const char *pathname)
 {
-	struct fs_driver_d *fsdrv;
-	FILE *f;
 	int ret;
+	struct dentry *dentry;
+	struct inode *inode;
+	struct path path;
 
-	if (check_fd(fd))
-		return -errno;
-	f = &files[fd];
-	if (offset >= f->size)
-		return 0;
-	if (count == ERASE_SIZE_ALL || count > f->size - offset)
-		count = f->size - offset;
-	if (count < 0)
-		return -EINVAL;
+	ret = filename_lookup(AT_FDCWD, getname(pathname), 0, &path);
+	if (ret)
+		goto out;
 
-	fsdrv = f->fsdev->driver;
-	if (fsdrv->erase)
-		ret = fsdrv->erase(&f->fsdev->dev, f, count, offset);
-	else
-		ret = -ENOSYS;
+	dentry = path.dentry;
 
-	if (ret)
-		errno = -ret;
+	if (d_is_dir(dentry)) {
+		ret = -EISDIR;
+		goto out_put;
+	}
 
-	return ret;
-}
-EXPORT_SYMBOL(erase);
+	inode = d_inode(dentry->d_parent);
 
-int protect(int fd, size_t count, loff_t offset, int prot)
-{
-	struct fs_driver_d *fsdrv;
-	FILE *f;
-	int ret;
+	if (!inode->i_op->unlink) {
+		ret = -EPERM;
+		goto out_put;
+	}
 
-	if (check_fd(fd))
-		return -errno;
-	f = &files[fd];
-	if (offset >= f->size)
-		return 0;
-	if (count > f->size - offset)
-		count = f->size - offset;
+	ret = inode->i_op->unlink(inode, dentry);
+	if (ret)
+		goto out_put;
 
-	fsdrv = f->fsdev->driver;
-	if (fsdrv->protect)
-		ret = fsdrv->protect(&f->fsdev->dev, f, count, offset, prot);
-	else
-		ret = -ENOSYS;
+	d_delete(dentry);
 
+out_put:
+	path_put(&path);
+out:
 	if (ret)
 		errno = -ret;
-
 	return ret;
 }
-EXPORT_SYMBOL(protect);
+EXPORT_SYMBOL(unlink);
 
-int protect_file(const char *file, int prot)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
-	int fd, ret;
+	if (!dir->i_op->symlink)
+		return -EPERM;
 
-	fd = open(file, O_WRONLY);
-	if (fd < 0)
-		return fd;
+	return dir->i_op->symlink(dir, dentry, oldname);
+}
 
-	ret = protect(fd, ~0, 0, prot);
+int symlink(const char *pathname, const char *newpath)
+{
+	struct dentry *dentry;
+	struct path path;
+	int error;
+	unsigned int lookup_flags = LOOKUP_DIRECTORY;
 
-	close(fd);
+	dentry = filename_create(AT_FDCWD, getname(newpath), &path, lookup_flags);
+	if (IS_ERR(dentry)) {
+		error = PTR_ERR(dentry);
+		goto out;
+	}
 
-	return ret;
+	error = vfs_symlink(path.dentry->d_inode, dentry, pathname);
+out:
+	if (error)
+		errno = -error;
+
+	return error;
 }
+EXPORT_SYMBOL(symlink);
 
-void *memmap(int fd, int flags)
+static void release_dir(DIR *d)
+{
+	struct readdir_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &d->entries, list) {
+		free(entry);
+	}
+
+	free(d);
+}
+
+DIR *opendir(const char *pathname)
 {
-	struct fs_driver_d *fsdrv;
-	FILE *f;
-	void *retp = (void *)-1;
 	int ret;
+	struct dentry *dir;
+	struct inode *inode;
+	struct file file = {};
+	DIR *d;
+	struct path path = {};
+	struct readdir_callback rd = {
+		.ctx = {
+			.actor = fillonedir,
+		},
+	};
+
+	ret = filename_lookup(AT_FDCWD, getname(pathname),
+			      LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
+	if (ret)
+		goto out;
 
-	if (check_fd(fd))
-		return retp;
+	dir = path.dentry;
 
-	f = &files[fd];
+	if (d_is_negative(dir)) {
+		ret = -ENOENT;
+		goto out_put;
+	}
 
-	fsdrv = f->fsdev->driver;
+	inode = d_inode(dir);
 
-	if (fsdrv->memmap)
-		ret = fsdrv->memmap(&f->fsdev->dev, f, &retp, flags);
-	else
-		ret = -EINVAL;
+	if (!S_ISDIR(inode->i_mode)) {
+		ret = -ENOTDIR;
+		goto out_put;
+	}
+
+	file.f_path.dentry = dir;
+	file.f_op = dir->d_inode->i_fop;
+
+	d = xzalloc(sizeof(*d));
 
+	INIT_LIST_HEAD(&d->entries);
+	rd.dir = d;
+
+	ret = file.f_op->iterate(&file, &rd.ctx);
 	if (ret)
-		errno = -ret;
+		goto out_release;
 
-	return retp;
-}
-EXPORT_SYMBOL(memmap);
+	path_put(&path);
 
-int close(int fd)
-{
-	struct fs_driver_d *fsdrv;
-	FILE *f;
-	int ret;
+	return d;
 
-	if (check_fd(fd))
-		return -errno;
+out_release:
+	release_dir(d);
+out_put:
+	path_put(&path);
+out:
+	errno = -ret;
 
-	f = &files[fd];
+	return NULL;
+}
+EXPORT_SYMBOL(opendir);
 
-	fsdrv = f->fsdev->driver;
-	ret = fsdrv->close(&f->fsdev->dev, f);
+int closedir(DIR *dir)
+{
+	int ret;
 
-	put_file(f);
+	if (!dir) {
+		errno = EBADF;
+		return -EBADF;
+	}
 
-	if (ret)
-		errno = -ret;
+	release_dir(dir);
 
 	return ret;
 }
-EXPORT_SYMBOL(close);
+EXPORT_SYMBOL(closedir);
 
 int readlink(const char *pathname, char *buf, size_t bufsiz)
 {
-	struct fs_driver_d *fsdrv;
-	struct fs_device_d *fsdev;
-	char *p = canonicalize_dir(pathname);
-	char *freep = p;
 	int ret;
-	struct stat s;
+	struct dentry *dentry;
+	struct inode *inode;
+	const char *link;
+	struct path path = {};
 
-	ret = lstat(pathname, &s);
+	ret = filename_lookup(AT_FDCWD, getname(pathname), 0, &path);
 	if (ret)
 		goto out;
 
-	if (!S_ISLNK(s.st_mode)) {
+	dentry = path.dentry;
+
+	if (!d_is_symlink(dentry)) {
 		ret = -EINVAL;
-		goto out;
+		goto out_put;
 	}
 
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENODEV;
-		goto out;
+	inode = d_inode(dentry);
+
+	if (!inode->i_op->get_link) {
+		ret = -EPERM;
+		goto out_put;
 	}
-	fsdrv = fsdev->driver;
 
-	if (fsdrv->readlink)
-		ret = fsdrv->readlink(&fsdev->dev, p, buf, bufsiz);
-	else
-		ret = -ENOSYS;
+	link = inode->i_op->get_link(dentry, inode);
+	if (IS_ERR(link)) {
+		ret =  PTR_ERR(link);
+		goto out_put;
+	}
 
-	if (ret)
-		goto out;
+	strncpy(buf, link, bufsiz);
 
+	ret = 0;
+out_put:
+	path_put(&path);
 out:
-	free(freep);
-
 	if (ret)
 		errno = -ret;
 
@@ -1159,225 +2509,223 @@ out:
 }
 EXPORT_SYMBOL(readlink);
 
-int symlink(const char *pathname, const char *newpath)
+static int stat_filename(const char *filename, struct stat *s, unsigned int flags)
 {
-	struct fs_driver_d *fsdrv;
-	struct fs_device_d *fsdev;
-	char *p;
 	int ret;
-	struct stat s;
+	struct dentry *dentry;
+	struct inode *inode;
+	struct path path = {};
 
-	p = canonicalize_path(newpath);
-	if (IS_ERR(p)) {
-		ret = PTR_ERR(p);
+	ret = filename_lookup(AT_FDCWD, getname(filename), flags, &path);
+	if (ret)
 		goto out;
-	}
 
-	ret = lstat(p, &s);
-	if (!ret) {
-		ret = -EEXIST;
-		goto out;
-	}
+	dentry = path.dentry;
 
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENODEV;
-		goto out;
+	if (d_is_negative(dentry)) {
+		ret = -ENOENT;
+		goto out_put;
 	}
-	fsdrv = fsdev->driver;
 
-	if (fsdrv->symlink) {
-		ret = fsdrv->symlink(&fsdev->dev, pathname, p);
-	} else {
-		ret = -EPERM;
-	}
+	inode = d_inode(dentry);
 
-out:
-	free(p);
-	if (ret)
-		errno = -ret;
+	stat_inode(inode, s);
 
+	ret = 0;
+out_put:
+	path_put(&path);
+out:
 	return ret;
 }
-EXPORT_SYMBOL(symlink);
 
-static int fs_match(struct device_d *dev, struct driver_d *drv)
+int stat(const char *filename, struct stat *s)
+{
+	return stat_filename(filename, s, LOOKUP_FOLLOW);
+}
+EXPORT_SYMBOL(stat);
+
+int lstat(const char *filename, struct stat *s)
 {
-	return strcmp(dev->name, drv->name) ? -1 : 0;
+	return stat_filename(filename, s, 0);
 }
+EXPORT_SYMBOL(lstat);
 
-static int fs_probe(struct device_d *dev)
+static char *__dpath(struct dentry *dentry, struct dentry *root)
 {
-	struct fs_device_d *fsdev = dev_to_fs_device(dev);
-	struct driver_d *drv = dev->driver;
-	struct fs_driver_d *fsdrv = container_of(drv, struct fs_driver_d, drv);
-	int ret;
+	char *res, *ppath;
 
-	ret = dev->driver->probe(dev);
-	if (ret)
-		return ret;
+	if (dentry == root)
+		return NULL;
+	if (dentry == d_root)
+		return NULL;
 
-	fsdev->driver = fsdrv;
+	while (IS_ROOT(dentry)) {
+		struct fs_device_d *fsdev;
 
-	list_add_tail(&fsdev->list, &fs_device_list);
+		for_each_fs_device(fsdev) {
+			if (dentry == fsdev->vfsmount.mnt_root) {
+				dentry = fsdev->vfsmount.mountpoint;
+				break;
+			}
+		}
+	}
 
-	if (!fs_dev_root)
-		fs_dev_root = fsdev;
+	ppath = __dpath(dentry->d_parent, root);
+	if (ppath)
+		res = basprintf("%s/%s", ppath, dentry->name);
+	else
+		res = basprintf("/%s", dentry->name);
+	free(ppath);
 
-	return 0;
+	return res;
 }
 
-static void fs_remove(struct device_d *dev)
+/**
+ * dpath - return path of a dentry
+ * @dentry: The dentry to return the path from
+ * @root:   The dentry up to which the path is followed
+ *
+ * Get the path of a dentry. The path is followed up to
+ * @root or the root ("/") dentry, whatever is found first.
+ *
+ * Return: Dynamically allocated string containing the path
+ */
+char *dpath(struct dentry *dentry, struct dentry *root)
 {
-	struct fs_device_d *fsdev = dev_to_fs_device(dev);
-
-	if (fsdev->dev.driver) {
-		dev->driver->remove(dev);
-		list_del(&fsdev->list);
-	}
+	char *res;
 
-	free(fsdev->path);
-	free(fsdev->options);
+	if (dentry == root)
+		return strdup("/");
 
-	if (fsdev == fs_dev_root)
-		fs_dev_root = NULL;
+	res = __dpath(dentry, root);
 
-	if (fsdev->cdev)
-		cdev_close(fsdev->cdev);
+	return res;
+}
 
-	if (fsdev->loop)
-		cdev_remove_loop(fsdev->cdev);
+/**
+ * canonicalize_path - resolve links in path
+ * @pathname: The input path
+ *
+ * This function resolves all links in @pathname and returns
+ * a path without links in it.
+ *
+ * Return: Path with links resolved. Allocated, must be freed after use.
+ */
+char *canonicalize_path(const char *pathname)
+{
+	char *res = NULL;
+	struct path path;
+	int ret;
 
-	free(fsdev->backingstore);
-	free(fsdev);
-}
+	ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+	if (ret)
+		goto out;
 
-struct bus_type fs_bus = {
-	.name = "fs",
-	.match = fs_match,
-	.probe = fs_probe,
-	.remove = fs_remove,
-};
+	res = dpath(path.dentry, d_root);
+out:
+	if (ret)
+		errno = -ret;
 
-static int fs_bus_init(void)
-{
-	return bus_register(&fs_bus);
+	return res;
 }
-pure_initcall(fs_bus_init);
 
-int register_fs_driver(struct fs_driver_d *fsdrv)
+const char *getcwd(void)
 {
-	fsdrv->drv.bus = &fs_bus;
-	register_driver(&fsdrv->drv);
-
-	return 0;
+	return cwd;
 }
-EXPORT_SYMBOL(register_fs_driver);
+EXPORT_SYMBOL(getcwd);
 
-static const char *detect_fs(const char *filename, const char *fsoptions)
+int chdir(const char *pathname)
 {
-	enum filetype type;
-	struct driver_d *drv;
-	struct fs_driver_d *fdrv;
-	bool loop = false;
-	unsigned long long offset = 0;
-
-	parseopt_b(fsoptions, "loop", &loop);
-	parseopt_llu_suffix(fsoptions, "offset", &offset);
-	if (loop)
-		type = file_name_detect_type_offset(filename, offset);
-	else
-		type = cdev_detect_type(filename);
-
-	if (type == filetype_unknown)
-		return NULL;
+	char *realpath;
+	struct path path;
+	int ret;
 
-	bus_for_each_driver(&fs_bus, drv) {
-		fdrv = drv_to_fs_driver(drv);
+	ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+	if (ret)
+		goto out;
 
-		if (type == fdrv->type)
-			return drv->name;
+	if (!d_is_dir(path.dentry)) {
+		ret = -ENOTDIR;
+		goto out;
 	}
 
-	return NULL;
-}
-
-int fsdev_open_cdev(struct fs_device_d *fsdev)
-{
-	unsigned long long offset = 0;
+	realpath = dpath(path.dentry, d_root);
+	strcpy(cwd, realpath);
+	free(realpath);
+	cwd_dentry = path.dentry;
+	cwd_mnt = path.mnt;
 
-	parseopt_b(fsdev->options, "loop", &fsdev->loop);
-	parseopt_llu_suffix(fsdev->options, "offset", &offset);
-	if (fsdev->loop)
-		fsdev->cdev = cdev_create_loop(fsdev->backingstore, O_RDWR,
-					       offset);
-	else
-		fsdev->cdev = cdev_open(fsdev->backingstore, O_RDWR);
-	if (!fsdev->cdev)
-		return -EINVAL;
+	ret = 0;
 
-	fsdev->dev.parent = fsdev->cdev->dev;
-	fsdev->parent_device = fsdev->cdev->dev;
+out:
+	if (ret)
+		errno = -ret;
 
-	return 0;
+	return ret;
 }
+EXPORT_SYMBOL(chdir);
 
 /*
  * Mount a device to a directory.
  * We do this by registering a new device on which the filesystem
  * driver will match.
  */
-int mount(const char *device, const char *fsname, const char *_path,
+int mount(const char *device, const char *fsname, const char *pathname,
 		const char *fsoptions)
 {
 	struct fs_device_d *fsdev;
 	int ret;
-	char *path = normalise_path(_path);
-
-	if (!fsoptions)
-		fsoptions = "";
+	struct path path = {};
 
-	debug("mount: %s on %s type %s, options=%s\n",
-			device, path, fsname, fsoptions);
+	if (d_root) {
+		ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+		if (ret)
+			goto out;
 
-	if (fs_dev_root) {
-		struct stat s;
+		if (!d_is_dir(path.dentry)) {
+			ret = -ENOTDIR;
+			goto out;
+		}
 
-		fsdev = get_fsdevice_by_path(path);
-		if (fsdev != fs_dev_root) {
-			printf("sorry, no nested mounts\n");
+		if (IS_ROOT(path.dentry) || d_mountpoint(path.dentry)) {
 			ret = -EBUSY;
-			goto err_free_path;
-		}
-		ret = lstat(path, &s);
-		if (ret)
-			goto err_free_path;
-		if (!S_ISDIR(s.st_mode)) {
-			ret = -ENOTDIR;
-			goto err_free_path;
+			goto out;
 		}
 	} else {
-		/* no mtab, so we only allow to mount on '/' */
-		if (*path != '/' || *(path + 1)) {
-			ret = -ENOTDIR;
-			goto err_free_path;
-		}
+		if (pathname[0] != '/' || pathname[1])
+			return -EINVAL;
 	}
 
+	if (!fsoptions)
+		fsoptions = "";
+
+	debug("mount: %s on %s type %s, options=%s\n",
+			device, pathname, fsname, fsoptions);
+
 	if (!fsname)
 		fsname = detect_fs(device, fsoptions);
 
-	if (!fsname)
-		return -ENOENT;
+	if (!fsname) {
+		ret = -ENOENT;
+		goto out;
+	}
 
 	fsdev = xzalloc(sizeof(struct fs_device_d));
 	fsdev->backingstore = xstrdup(device);
 	safe_strncpy(fsdev->dev.name, fsname, MAX_DRIVER_NAME);
 	fsdev->dev.id = get_free_deviceid(fsdev->dev.name);
-	fsdev->path = xstrdup(path);
 	fsdev->dev.bus = &fs_bus;
 	fsdev->options = xstrdup(fsoptions);
 
+	init_super(&fsdev->sb);
+
+	if (path.mnt)
+		mntget(path.mnt);
+
+	if (d_root)
+		fsdev->path = dpath(path.dentry, d_root);
+
 	ret = register_device(&fsdev->dev);
 	if (ret)
 		goto err_register;
@@ -1391,6 +2739,21 @@ int mount(const char *device, const char *fsname, const char *_path,
 		goto err_no_driver;
 	}
 
+	if (d_root) {
+		fsdev->vfsmount.mountpoint = path.dentry;
+		fsdev->vfsmount.parent = path.mnt;
+		fsdev->vfsmount.mountpoint->d_flags |= DCACHE_MOUNTED;
+	} else {
+		d_root = fsdev->sb.s_root;
+		path.dentry = d_root;
+		mnt_root = &fsdev->vfsmount;
+		fsdev->vfsmount.mountpoint = d_root;
+		fsdev->vfsmount.parent = &fsdev->vfsmount;
+		fsdev->path = xstrdup("/");
+	}
+
+	fsdev->vfsmount.mnt_root = fsdev->sb.s_root;
+
 	if (!fsdev->linux_rootarg && fsdev->cdev && fsdev->cdev->partuuid[0] != 0) {
 		char *str = basprintf("root=PARTUUID=%s",
 					fsdev->cdev->partuuid);
@@ -1398,7 +2761,7 @@ int mount(const char *device, const char *fsname, const char *_path,
 		fsdev_set_linux_rootarg(fsdev, str);
 	}
 
-	free(path);
+	path_put(&path);
 
 	return 0;
 
@@ -1406,8 +2769,8 @@ err_no_driver:
 	unregister_device(&fsdev->dev);
 err_register:
 	fs_remove(&fsdev->dev);
-err_free_path:
-	free(path);
+out:
+	path_put(&path);
 
 	errno = -ret;
 
@@ -1415,69 +2778,39 @@ err_free_path:
 }
 EXPORT_SYMBOL(mount);
 
-static int fsdev_umount(struct fs_device_d *fsdev)
-{
-	return unregister_device(&fsdev->dev);
-}
-
-/**
- * umount_by_cdev Use a cdev struct to umount all mounted filesystems
- * @param cdev cdev to the according device
- * @return 0 on success or if cdev was not mounted, -errno otherwise
- */
-int umount_by_cdev(struct cdev *cdev)
+int umount(const char *pathname)
 {
-	struct fs_device_d *fs;
-	struct fs_device_d *fs_tmp;
-	int first_error = 0;
+	struct fs_device_d *fsdev = NULL, *f;
+	struct path path = {};
+	int ret;
 
-	for_each_fs_device_safe(fs_tmp, fs) {
-		int ret;
+	ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+	if (ret)
+		return ret;
 
-		if (fs->cdev == cdev) {
-			ret = fsdev_umount(fs);
-			if (ret) {
-				pr_err("Failed umounting %s, %d, continuing anyway\n",
-				       fs->path, ret);
-				if (!first_error)
-					first_error = ret;
-			}
-		}
+	if (path.dentry == d_root) {
+		path_put(&path);
+		return -EBUSY;
 	}
 
-	return first_error;
-}
-EXPORT_SYMBOL(umount_by_cdev);
-
-int umount(const char *pathname)
-{
-	struct fs_device_d *fsdev = NULL, *f;
-	char *p = normalise_path(pathname);
-
 	for_each_fs_device(f) {
-		if (!strcmp(p, f->path)) {
+		if (path.dentry == f->vfsmount.mnt_root) {
 			fsdev = f;
 			break;
 		}
 	}
 
+	path_put(&path);
+
 	if (!fsdev) {
-		struct cdev *cdev = cdev_open(p, O_RDWR);
+		struct cdev *cdev = cdev_open(pathname, O_RDWR);
 
 		if (cdev) {
-			free(p);
 			cdev_close(cdev);
 			return umount_by_cdev(cdev);
 		}
 	}
 
-	free(p);
-
-	if (f == fs_dev_root && !list_is_singular(&fs_device_list)) {
-		errno = EBUSY;
-		return -EBUSY;
-	}
-
 	if (!fsdev) {
 		errno = EFAULT;
 		return -EFAULT;
@@ -1487,386 +2820,249 @@ int umount(const char *pathname)
 }
 EXPORT_SYMBOL(umount);
 
-DIR *opendir(const char *pathname)
-{
-	DIR *dir = NULL;
-	struct fs_device_d *fsdev;
-	struct fs_driver_d *fsdrv;
-	char *p = canonicalize_path(pathname);
-	char *freep = p;
-	int ret;
-	struct stat s;
-
-	ret = stat(pathname, &s);
-	if (ret)
-		goto out;
-
-	if (!S_ISDIR(s.st_mode)) {
-		ret = -ENOTDIR;
-		goto out;
-	}
-
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENOENT;
-		goto out;
-	}
-	fsdrv = fsdev->driver;
-
-	debug("opendir: fsdrv: %p\n",fsdrv);
-
-	dir = fsdrv->opendir(&fsdev->dev, p);
-	if (dir) {
-		dir->dev = &fsdev->dev;
-		dir->fsdrv = fsdrv;
-	} else {
-		/*
-		 * FIXME: The fs drivers should return ERR_PTR here so that
-		 * we are able to forward the error
-		 */
-		ret = -EINVAL;
-	}
-
-out:
-	free(freep);
-
-	if (ret)
-		errno = -ret;
-
-	return dir;
-}
-EXPORT_SYMBOL(opendir);
-
-struct dirent *readdir(DIR *dir)
-{
-	struct dirent *ent;
-
-	if (!dir)
-		return NULL;
-
-	ent = dir->fsdrv->readdir(dir->dev, dir);
+#ifdef CONFIG_FS_AUTOMOUNT
 
-	if (!ent)
-		errno = EBADF;
+#define AUTOMOUNT_IS_FILE (1 << 0)
 
-	return ent;
-}
-EXPORT_SYMBOL(readdir);
+struct automount {
+	char *path;
+	struct dentry *dentry;
+	char *cmd;
+	struct list_head list;
+	unsigned int flags;
+};
 
-int closedir(DIR *dir)
+static LIST_HEAD(automount_list);
+
+static void automount_remove_dentry(struct dentry *dentry)
 {
-	int ret;
+	struct automount *am;
 
-	if (!dir) {
-		errno = EBADF;
-		return -EBADF;
+	list_for_each_entry(am, &automount_list, list) {
+		if (dentry == am->dentry)
+			goto found;
 	}
 
-	ret = dir->fsdrv->closedir(dir->dev, dir);
-	if (ret)
-		errno = -ret;
-
-	return ret;
+	return;
+found:
+	list_del(&am->list);
+	dput(am->dentry);
+	free(am->path);
+	free(am->cmd);
+	free(am);
 }
-EXPORT_SYMBOL(closedir);
 
-int stat(const char *filename, struct stat *s)
+void automount_remove(const char *pathname)
 {
-	char *path = canonicalize_path(filename);
+	struct path path;
 	int ret;
 
-	if (IS_ERR(path))
-		return PTR_ERR(path);
-
-	ret = lstat(path, s);
+	ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+	if (ret)
+		return;
 
-	free(path);
+	automount_remove_dentry(path.dentry);
 
-	return ret;
+	path_put(&path);
 }
-EXPORT_SYMBOL(stat);
+EXPORT_SYMBOL(automount_remove);
 
-static int __lstat(const char *filename, struct stat *s)
+int automount_add(const char *pathname, const char *cmd)
 {
-	struct fs_driver_d *fsdrv;
-	struct fs_device_d *fsdev;
-	char *f = normalise_path(filename);
-	char *freep = f;
+	struct automount *am = xzalloc(sizeof(*am));
+	struct path path;
 	int ret;
 
-	automount_mount(f, 1);
-
-	memset(s, 0, sizeof(struct stat));
+	ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+	if (ret)
+		return ret;
 
-	fsdev = get_fsdevice_by_path(f);
-	if (!fsdev) {
-		ret = -ENOENT;
+	if (!d_is_dir(path.dentry)) {
+		ret = -ENOTDIR;
 		goto out;
 	}
 
-	if (fsdev != fs_dev_root && strcmp(f, fsdev->path))
-		f += strlen(fsdev->path);
-	else
-		fsdev = fs_dev_root;
+	am->path = dpath(path.dentry, d_root);
+	am->dentry = dget(path.dentry);
+	am->dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+	am->cmd = xstrdup(cmd);
 
-	fsdrv = fsdev->driver;
+	automount_remove_dentry(am->dentry);
 
-	if (*f == 0)
-		f = "/";
+	list_add_tail(&am->list, &automount_list);
 
-	ret = fsdrv->stat(&fsdev->dev, f, s);
+	ret = 0;
 out:
-	free(freep);
-
-	if (ret)
-		errno = -ret;
+	path_put(&path);
 
 	return ret;
 }
+EXPORT_SYMBOL(automount_add);
 
-int lstat(const char *filename, struct stat *s)
+void cdev_create_default_automount(struct cdev *cdev)
 {
-	char *f = canonicalize_dir(filename);
-	int ret;
-
-	if (IS_ERR(f))
-		return PTR_ERR(f);
+	char *path, *cmd;
 
-	ret = __lstat(f, s);
+	path = basprintf("/mnt/%s", cdev->name);
+	cmd = basprintf("mount %s", cdev->name);
 
-	free(f);
+	make_directory(path);
+	automount_add(path, cmd);
 
-	return ret;
+	free(cmd);
+	free(path);
 }
-EXPORT_SYMBOL(lstat);
 
-int fstat(int fd, struct stat *s)
+void automount_print(void)
 {
-	FILE *f;
-	struct fs_device_d *fsdev;
-
-	if (check_fd(fd))
-		return -errno;
-
-	f = &files[fd];
-
-	fsdev = f->fsdev;
+	struct automount *am;
 
-	return fsdev->driver->stat(&fsdev->dev, f->path, s);
+	list_for_each_entry(am, &automount_list, list)
+		printf("%-20s %s\n", am->path, am->cmd);
 }
-EXPORT_SYMBOL(fstat);
+EXPORT_SYMBOL(automount_print);
 
-int mkdir (const char *pathname, mode_t mode)
+static int automount_mount(struct dentry *dentry)
 {
-	struct fs_driver_d *fsdrv;
-	struct fs_device_d *fsdev;
-	char *p = canonicalize_path(pathname);
-	char *freep = p;
-	int ret;
-	struct stat s;
-
-	ret = parent_check_directory(p);
-	if (ret)
-		goto out;
-
-	ret = stat(pathname, &s);
-	if (!ret) {
-		ret = -EEXIST;
-		goto out;
-	}
-
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENOENT;
-		goto out;
-	}
-	fsdrv = fsdev->driver;
-
-	if (fsdrv->mkdir)
-		ret = fsdrv->mkdir(&fsdev->dev, p);
-	else
-		ret = -EROFS;
-out:
-	free(freep);
+	struct automount *am;
+	int ret = -ENOENT;
+	static int in_automount;
 
-	if (ret)
-		errno = -ret;
+	if (in_automount)
+		return -EINVAL;
 
-	return ret;
-}
-EXPORT_SYMBOL(mkdir);
+	in_automount++;
 
-int rmdir (const char *pathname)
-{
-	struct fs_driver_d *fsdrv;
-	struct fs_device_d *fsdev;
-	char *p = canonicalize_path(pathname);
-	char *freep = p;
-	int ret;
-	struct stat s;
+	list_for_each_entry(am, &automount_list, list) {
+		if (am->dentry != dentry)
+			continue;
 
-	ret = lstat(pathname, &s);
-	if (ret)
-		goto out;
-	if (!S_ISDIR(s.st_mode)) {
-		ret = -ENOTDIR;
-		goto out;
-	}
+		setenv("automount_path", am->path);
+		export("automount_path");
+		ret = run_command(am->cmd);
+		setenv("automount_path", NULL);
 
-	if (!dir_is_empty(pathname)) {
-		ret = -ENOTEMPTY;
-		goto out;
-	}
+		if (ret) {
+			printf("running automount command '%s' failed\n",
+					am->cmd);
+			ret = -ENODEV;
+		}
 
-	fsdev = get_fs_device_and_root_path(&p);
-	if (!fsdev) {
-		ret = -ENODEV;
-		goto out;
+		break;
 	}
-	fsdrv = fsdev->driver;
-
-	if (fsdrv->rmdir)
-		ret = fsdrv->rmdir(&fsdev->dev, p);
-	else
-		ret = -EROFS;
-out:
-	free(freep);
 
-	if (ret)
-		errno = -ret;
+	in_automount--;
 
 	return ret;
 }
-EXPORT_SYMBOL(rmdir);
-
-/*
- * cdev_get_mount_path - return the path a cdev is mounted on
- *
- * If a cdev is mounted return the path it's mounted on, NULL
- * otherwise.
- */
-const char *cdev_get_mount_path(struct cdev *cdev)
-{
-	struct fs_device_d *fsdev;
 
-	for_each_fs_device(fsdev) {
-		if (fsdev->cdev && fsdev->cdev == cdev)
-			return fsdev->path;
-	}
+BAREBOX_MAGICVAR(automount_path, "mountpath passed to automount scripts");
 
-	return NULL;
+#else
+static int automount_mount(const char *path)
+{
+	return 0;
 }
+#endif /* CONFIG_FS_AUTOMOUNT */
+
+#ifdef DEBUG
 
 /*
- * cdev_mount_default - mount a cdev to the default path
- *
- * If a cdev is already mounted return the path it's mounted on, otherwise
- * mount it to /mnt/<cdevname> and return the path. Returns an error pointer
- * on failure.
+ * Some debug commands, helpful to debug the dcache implementation
  */
-const char *cdev_mount_default(struct cdev *cdev, const char *fsoptions)
+#include <command.h>
+
+static int do_lookup_dentry(int argc, char *argv[])
 {
-	const char *path;
-	char *newpath, *devpath;
+	struct path path;
 	int ret;
+	char *canon;
+	char mode[16];
 
-	/*
-	 * If this cdev is already mounted somewhere use this path
-	 * instead of mounting it again to avoid corruption on the
-	 * filesystem. Note this ignores eventual fsoptions though.
-	 */
-	path = cdev_get_mount_path(cdev);
-	if (path)
-		return path;
-
-	newpath = basprintf("/mnt/%s", cdev->name);
-	make_directory(newpath);
-
-	devpath = basprintf("/dev/%s", cdev->name);
-
-	ret = mount(devpath, NULL, newpath, fsoptions);
-
-	free(devpath);
+	if (argc < 2)
+		return COMMAND_ERROR_USAGE;
 
+	ret = filename_lookup(AT_FDCWD, getname(argv[1]), 0, &path);
 	if (ret) {
-		free(newpath);
-		return ERR_PTR(ret);
+		printf("Cannot lookup path \"%s\": %s\n",
+		       argv[1], strerror(-ret));
+		return 1;
 	}
 
-	return cdev_get_mount_path(cdev);
-}
+	canon = canonicalize_path(argv[1]);
 
-/*
- * mount_all - iterate over block devices and mount all devices we are able to
- */
-void mount_all(void)
-{
-	struct device_d *dev;
-	struct block_device *bdev;
+	printf("path \"%s\":\n", argv[1]);
+	printf("dentry: 0x%p\n", path.dentry);
+	printf("dentry refcnt: %d\n", path.dentry->d_count);
+	if (path.dentry->d_inode) {
+		struct inode *inode = path.dentry->d_inode;
+		printf("inode: 0x%p\n", inode);
+		printf("inode refcnt: %d\n", inode->i_count);
+		printf("Type: %s\n", mkmodestr(inode->i_mode, mode));
+	}
+	printf("canonical path: \"%s\"\n", canon);
 
-	if (!IS_ENABLED(CONFIG_BLOCK))
-		return;
+	path_put(&path);
+	free(canon);
 
-	for_each_device(dev)
-		device_detect(dev);
+        return 0;
+}
 
-	for_each_block_device(bdev) {
-		struct cdev *cdev = &bdev->cdev;
+BAREBOX_CMD_START(lookup_dentry)
+        .cmd            = do_lookup_dentry,
+BAREBOX_CMD_END
 
-		list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list)
-			cdev_mount_default(cdev, NULL);
+static struct dentry *debug_follow_mount(struct dentry *dentry)
+{
+	struct fs_device_d *fsdev;
+	unsigned managed = dentry->d_flags;
+
+	if (managed & DCACHE_MOUNTED) {
+		for_each_fs_device(fsdev) {
+			if (dentry == fsdev->vfsmount.mountpoint)
+				return fsdev->vfsmount.mnt_root;
+		}
+		return NULL;
+	} else {
+		return dentry;
 	}
 }
 
-void fsdev_set_linux_rootarg(struct fs_device_d *fsdev, const char *str)
+static void debug_dump_dentries(struct dentry *parent, int indent)
 {
-	fsdev->linux_rootarg = xstrdup(str);
+	int i;
+	struct dentry *dentry, *mp;
 
-	dev_add_param_fixed(&fsdev->dev, "linux.bootargs", fsdev->linux_rootarg);
-}
+	for (i = 0; i < indent; i++)
+		printf("\t");
+again:
+	printf("%s d: %p refcnt: %d, inode %p refcnt %d\n",
+	       parent->name, parent, parent->d_count, parent->d_inode,
+		parent->d_inode ? parent->d_inode->i_count : -1);
 
-/**
- * path_get_linux_rootarg() - Given a path return a suitable root= option for
- *                            Linux
- * @path: The path
- *
- * Return: A string containing the root= option or an ERR_PTR. the returned
- *         string must be freed by the caller.
- */
-char *path_get_linux_rootarg(const char *path)
-{
-	struct fs_device_d *fsdev;
-	const char *str;
+	mp = debug_follow_mount(parent);
+	if (mp != parent) {
+		for (i = 0; i < indent; i++)
+			printf("\t");
+		printf("MOUNT: ");
 
-	fsdev = get_fsdevice_by_path(path);
-	if (!fsdev)
-		return ERR_PTR(-EINVAL);
+		parent = mp;
 
-	str = dev_get_param(&fsdev->dev, "linux.bootargs");
-	if (!str)
-		return ERR_PTR(-ENOSYS);
+		goto again;
+	}
 
-	return xstrdup(str);
+	list_for_each_entry(dentry, &parent->d_subdirs, d_child)
+		debug_dump_dentries(dentry, indent + 1);
 }
 
-/**
- * __is_tftp_fs() - return true when path is mounted on TFTP
- * @path: The path
- *
- * Do not use directly, use is_tftp_fs instead.
- *
- * Return: true when @path is on TFTP, false otherwise
- */
-bool __is_tftp_fs(const char *path)
+static int do_debug_fs_dump(int argc, char *argv[])
 {
-	struct fs_device_d *fsdev;
-
-	fsdev = get_fsdevice_by_path(path);
-	if (!fsdev)
-		return false;
-
-	if (strcmp(fsdev->driver->drv.name, "tftp"))
-		return false;
+	debug_dump_dentries(d_root, 0);
 
-	return true;
+        return 0;
 }
+
+BAREBOX_CMD_START(debug_fs_dump)
+        .cmd            = do_debug_fs_dump,
+BAREBOX_CMD_END
+#endif
diff --git a/fs/legacy.c b/fs/legacy.c
new file mode 100644
index 0000000000..fc6a18f408
--- /dev/null
+++ b/fs/legacy.c
@@ -0,0 +1,315 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <common.h>
+#include <fs.h>
+
+static struct inode *legacy_get_inode(struct super_block *sb, const struct inode *dir,
+				      umode_t mode);
+
+static int legacy_iterate(struct file *file, struct dir_context *ctx)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *dir = d_inode(dentry);
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct dir *d;
+	struct dirent *dirent;
+	char *pathname;
+
+	dir_emit_dots(file, ctx);
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	d = fsdev->driver->opendir(&fsdev->dev, pathname);
+	while (1) {
+		dirent = fsdev->driver->readdir(&fsdev->dev, d);
+		if (!dirent)
+			break;
+
+		dir_emit(ctx, dirent->d_name, strlen(dirent->d_name), 0, DT_UNKNOWN);
+	}
+
+	fsdev->driver->closedir(&fsdev->dev, d);
+
+	free(pathname);
+
+	return 0;
+}
+
+static struct dentry *legacy_lookup(struct inode *dir, struct dentry *dentry,
+				    unsigned int flags)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct inode *inode;
+	char *pathname;
+	struct stat s;
+	int ret;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->stat(&fsdev->dev, pathname, &s);
+	if (!ret) {
+		inode = legacy_get_inode(sb, dir, s.st_mode);
+
+		inode->i_size = s.st_size;
+		inode->i_mode = s.st_mode;
+
+		d_add(dentry, inode);
+	}
+
+	return NULL;
+}
+
+static int legacy_create(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct inode *inode;
+	char *pathname;
+	int ret;
+
+	if (!fsdev->driver->create)
+		return -EROFS;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->create(&fsdev->dev, pathname, mode | S_IFREG);
+
+	free(pathname);
+
+	if (ret)
+		return ret;
+
+	inode = legacy_get_inode(sb, dir, mode | S_IFREG);
+
+	d_instantiate(dentry, inode);
+
+	return 0;
+}
+
+static int legacy_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct inode *inode;
+	char *pathname;
+	int ret;
+
+	if (!fsdev->driver->mkdir)
+		return -EROFS;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->mkdir(&fsdev->dev, pathname);
+
+	free(pathname);
+
+	if (ret)
+		return ret;
+
+	inode = legacy_get_inode(sb, dir, mode | S_IFDIR);
+
+	d_instantiate(dentry, inode);
+
+	return 0;
+}
+
+static int legacy_dir_is_empty(struct dentry *dentry)
+{
+	struct inode *dir = d_inode(dentry);
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct dir *d;
+	struct dirent *dirent;
+	char *pathname;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	d = fsdev->driver->opendir(&fsdev->dev, pathname);
+	dirent = fsdev->driver->readdir(&fsdev->dev, d);
+
+	fsdev->driver->closedir(&fsdev->dev, d);
+
+	free(pathname);
+
+	return dirent ? 0 : 1;
+}
+
+static int legacy_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	char *pathname;
+	int ret;
+
+	if (!fsdev->driver->rmdir)
+		return -EROFS;
+
+	if (!legacy_dir_is_empty(dentry))
+		return -ENOTEMPTY;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->rmdir(&fsdev->dev, pathname);
+
+	free(pathname);
+
+	if (ret)
+		return ret;
+
+	drop_nlink(d_inode(dentry));
+	dput(dentry);
+	drop_nlink(dir);
+
+	return 0;
+}
+
+static int legacy_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	char *pathname;
+	int ret;
+
+	if (!fsdev->driver->unlink)
+		return -EROFS;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->unlink(&fsdev->dev, pathname);
+
+	free(pathname);
+
+	if (ret)
+		return ret;
+
+	drop_nlink(d_inode(dentry));
+	dput(dentry);
+
+	return 0;
+}
+
+static int legacy_symlink(struct inode *dir, struct dentry *dentry,
+			  const char *dest)
+{
+	struct super_block *sb = dir->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct inode *inode;
+	char *pathname;
+	int ret;
+
+	if (!fsdev->driver->symlink)
+		return -ENOSYS;
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->symlink(&fsdev->dev, dest, pathname);
+
+	free(pathname);
+
+	if (ret)
+		return ret;
+
+	inode = legacy_get_inode(sb, dir, S_IFLNK);
+	inode->i_link = xstrdup(dest);
+
+	d_instantiate(dentry, inode);
+
+	return 0;
+}
+
+static const char *legacy_get_link(struct dentry *dentry, struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	char *pathname;
+	int ret;
+	char link[PATH_MAX] = {};
+
+	if (!fsdev->driver->readlink)
+		return ERR_PTR(-ENOSYS);
+
+	pathname = dpath(dentry, fsdev->vfsmount.mnt_root);
+
+	ret = fsdev->driver->readlink(&fsdev->dev, pathname, link, PATH_MAX - 1);
+
+	free(pathname);
+
+	if (ret)
+		return NULL;
+
+	inode->i_link = xstrdup(link);
+
+	return inode->i_link;
+}
+
+static const struct super_operations legacy_s_ops;
+static const struct inode_operations legacy_file_inode_operations;
+
+static const struct inode_operations legacy_dir_inode_operations = {
+	.lookup = legacy_lookup,
+	.create = legacy_create,
+	.mkdir = legacy_mkdir,
+	.rmdir = legacy_rmdir,
+	.unlink = legacy_unlink,
+	.symlink = legacy_symlink,
+};
+
+static const struct file_operations legacy_dir_operations = {
+	.iterate = legacy_iterate,
+};
+
+static const struct inode_operations legacy_symlink_inode_operations = {
+	.get_link = legacy_get_link,
+};
+
+static struct inode *legacy_get_inode(struct super_block *sb, const struct inode *dir,
+				      umode_t mode)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+
+	switch (mode & S_IFMT) {
+	default:
+		return NULL;
+	case S_IFREG:
+	case S_IFCHR:
+		inode->i_op = &legacy_file_inode_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &legacy_dir_inode_operations;
+		inode->i_fop = &legacy_dir_operations;
+		inc_nlink(inode);
+		break;
+	case S_IFLNK:
+		inode->i_op = &legacy_symlink_inode_operations;
+		break;
+	}
+
+	return inode;
+}
+
+int fs_init_legacy(struct fs_device_d *fsdev)
+{
+	struct inode *inode;
+
+	fsdev->sb.s_op = &legacy_s_ops;
+	inode = legacy_get_inode(&fsdev->sb, NULL, S_IFDIR);
+	fsdev->sb.s_root = d_make_root(inode);
+
+	return 0;
+}
diff --git a/fs/libfs.c b/fs/libfs.c
new file mode 100644
index 0000000000..af8f0f7462
--- /dev/null
+++ b/fs/libfs.c
@@ -0,0 +1,97 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <common.h>
+#include <fs.h>
+#include <linux/fs.h>
+
+/*
+ * Lookup the data. This is trivial - if the dentry didn't already
+ * exist, we know it is negative.  Set d_op to delete negative dentries.
+ */
+struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+{
+	return NULL;
+}
+
+/* Relationship between i_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct inode *inode)
+{
+	return (inode->i_mode >> 12) & 15;
+}
+
+int dcache_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	struct dentry *d;
+
+	dir_emit_dots(file, ctx);
+
+	list_for_each_entry(d, &dentry->d_subdirs, d_child) {
+		if (d_is_negative(d))
+			continue;
+		dir_emit(ctx, d->d_name.name, d->d_name.len,
+				d_inode(d)->i_ino, dt_type(d_inode(d)));
+	}
+
+        return 0;
+}
+
+const struct file_operations simple_dir_operations = {
+	.iterate = dcache_readdir,
+};
+
+int simple_empty(struct dentry *dentry)
+{
+	struct dentry *child;
+	int ret = 0;
+
+	list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+		if (d_is_positive(child))
+			goto out;
+	}
+	ret = 1;
+out:
+	return ret;
+}
+
+int simple_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+
+	drop_nlink(inode);
+	dput(dentry);
+
+	return 0;
+}
+
+int simple_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	if (IS_ROOT(dentry))
+		return -EBUSY;
+
+	if (!simple_empty(dentry))
+		return -ENOTEMPTY;
+
+	drop_nlink(d_inode(dentry));
+	simple_unlink(dir, dentry);
+	drop_nlink(dir);
+
+	return 0;
+}
+
+const char *simple_get_link(struct dentry *dentry, struct inode *inode)
+{
+	return inode->i_link;
+}
+
+const struct inode_operations simple_symlink_inode_operations = {
+	.get_link = simple_get_link,
+};
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index 0c6f136920..0e042cb162 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -1,4 +1,5 @@
 menuconfig FS_PSTORE
+	select FS_LEGACY
 	bool
 	prompt "pstore fs support"
 	help
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index 19b8297af6..fce05c5730 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -1,6 +1,7 @@
 menuconfig FS_SQUASHFS
 	bool
 	prompt "squashfs support"
+	select FS_LEGACY
 	help
 	  Saying Y here includes support for SquashFS 4.0 (a Compressed
 	  Read-Only File System).  Squashfs is a highly compressed read-only
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 4c730e09e4..34e92e3c1d 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -39,15 +39,6 @@
 #include "squashfs.h"
 #include "decompressor.h"
 
-static struct dentry *d_make_root(struct inode *inode)
-{
-	struct dentry *de = malloc(sizeof(struct dentry));
-	de->d_name.name = "/";
-	de->d_name.len = strlen("/");
-	de->d_inode = inode;
-	return de;
-}
-
 static const struct squashfs_decompressor *supported_squashfs_filesystem(short
 	major, short minor, short id)
 {
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 889a2be97a..9aa0172289 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -1,6 +1,9 @@
 menuconfig FS_UBIFS
 	bool
 	depends on MTD_UBI
+	select FS_LEGACY
+# Due to duplicate definition of iput
+	depends on BROKEN
 	prompt "ubifs support"
 
 if FS_UBIFS
diff --git a/include/dirent.h b/include/dirent.h
index 5ee4c2063e..1df5d90452 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -1,6 +1,8 @@
 #ifndef __DIRENT_H
 #define __DIRENT_H
 
+#include <linux/list.h>
+
 struct dirent {
 	char d_name[256];
 };
@@ -11,6 +13,7 @@ typedef struct dir {
 	struct node_d *node;
 	struct dirent d;
 	void *priv; /* private data for the fs driver */
+	struct list_head entries;
 } DIR;
 
 DIR *opendir(const char *pathname);
diff --git a/include/fs.h b/include/fs.h
index e6fcd044dd..181318f404 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -31,14 +31,15 @@ typedef struct filep {
 	/* private fields. Mapping between FILE and filedescriptor number     */
 	int no;
 	char in_use;
+
+	struct inode *f_inode;
+	struct dentry *dentry;
 } FILE;
 
 #define FS_DRIVER_NO_DEV	1
 
 struct fs_driver_d {
 	int (*probe) (struct device_d *dev);
-	int (*mkdir)(struct device_d *dev, const char *pathname);
-	int (*rmdir)(struct device_d *dev, const char *pathname);
 
 	/* create a file. The file is guaranteed to not exist */
 	int (*create)(struct device_d *dev, const char *pathname, mode_t mode);
@@ -47,11 +48,6 @@ struct fs_driver_d {
 	/* Truncate a file to given size */
 	int (*truncate)(struct device_d *dev, FILE *f, ulong size);
 
-	int (*symlink)(struct device_d *dev, const char *pathname,
-		       const char *newpath);
-	int (*readlink)(struct device_d *dev, const char *pathname, char *name,
-			size_t size);
-
 	int (*open)(struct device_d *dev, FILE *f, const char *pathname);
 	int (*close)(struct device_d *dev, FILE *f);
 	int (*read)(struct device_d *dev, FILE *f, void *buf, size_t size);
@@ -59,11 +55,6 @@ struct fs_driver_d {
 	int (*flush)(struct device_d *dev, FILE *f);
 	loff_t (*lseek)(struct device_d *dev, FILE *f, loff_t pos);
 
-	struct dir* (*opendir)(struct device_d *dev, const char *pathname);
-	struct dirent* (*readdir)(struct device_d *dev, struct dir *dir);
-	int (*closedir)(struct device_d *dev, DIR *dir);
-	int (*stat)(struct device_d *dev, const char *file, struct stat *stat);
-
 	int (*ioctl)(struct device_d *dev, FILE *f, int request, void *buf);
 	int (*erase)(struct device_d *dev, FILE *f, loff_t count,
 			loff_t offset);
@@ -72,6 +63,18 @@ struct fs_driver_d {
 
 	int (*memmap)(struct device_d *dev, FILE *f, void **map, int flags);
 
+	/* legacy */
+	int (*mkdir)(struct device_d *dev, const char *pathname);
+	int (*rmdir)(struct device_d *dev, const char *pathname);
+	int (*symlink)(struct device_d *dev, const char *pathname,
+		       const char *newpath);
+	int (*readlink)(struct device_d *dev, const char *pathname, char *name,
+			size_t size);
+	struct dir* (*opendir)(struct device_d *dev, const char *pathname);
+	struct dirent* (*readdir)(struct device_d *dev, struct dir *dir);
+	int (*closedir)(struct device_d *dev, DIR *dir);
+	int (*stat)(struct device_d *dev, const char *file, struct stat *stat);
+
 	struct driver_d drv;
 
 	enum filetype type;
@@ -99,6 +102,10 @@ struct fs_device_d {
 	struct list_head list;
 	char *options;
 	char *linux_rootarg;
+
+	struct super_block sb;
+
+	struct vfsmount vfsmount;
 };
 
 bool __is_tftp_fs(const char *path);
@@ -135,12 +142,6 @@ int ls(const char *path, ulong flags);
 
 char *mkmodestr(unsigned long mode, char *str);
 
-/*
- * This function turns 'path' into an absolute path and removes all occurrences
- * of "..", "." and double slashes. The returned string must be freed wit free().
- */
-char *normalise_path(const char *path);
-
 char *canonicalize_path(const char *pathname);
 
 char *get_mounted_path(const char *path);
@@ -154,6 +155,7 @@ void automount_remove(const char *_path);
 int automount_add(const char *path, const char *cmd);
 void automount_print(void);
 
+int fs_init_legacy(struct fs_device_d *fsdev);
 int fsdev_open_cdev(struct fs_device_d *fsdev);
 const char *cdev_get_mount_path(struct cdev *cdev);
 const char *cdev_mount_default(struct cdev *cdev, const char *fsoptions);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index dfb466722c..16244129bf 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -53,6 +53,10 @@ struct dentry {
 	spinlock_t d_lock;		/* per dentry lock */
 	struct inode *d_inode;		/* Where the name belongs to - NULL is
 					 * negative */
+
+	unsigned int d_count;
+	const struct dentry_operations *d_op;
+
 	/*
 	 * The next three fields are touched by __d_lookup.  Place them here
 	 * so they all fit in a cache line.
@@ -65,8 +69,8 @@ struct dentry {
 	/*
 	 * d_child and d_rcu can share memory
 	 */
+	struct list_head d_child;       /* child of parent list */
 	struct list_head d_subdirs;	/* our children */
-	struct list_head d_alias;	/* inode alias list */
 	unsigned long d_time;		/* used by d_revalidate */
 	struct super_block *d_sb;	/* The root of the dentry tree */
 	void *d_fsdata;			/* fs-specific data */
@@ -74,7 +78,108 @@ struct dentry {
 	struct dcookie_struct *d_cookie; /* cookie, if any */
 #endif
 	int d_mounted;
-	unsigned char d_iname[DNAME_INLINE_LEN_MIN];	/* small names */
+	unsigned char *name;		/* all names */
+};
+
+struct dentry_operations {
 };
 
+struct dentry * d_make_root(struct inode *);
+void d_add(struct dentry *, struct inode *);
+struct dentry * d_alloc_anon(struct super_block *);
+void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op);
+void d_instantiate(struct dentry *dentry, struct inode *inode);
+void d_delete(struct dentry *);
+struct dentry *dget(struct dentry *);
+void dput(struct dentry *);
+
+#define DCACHE_ENTRY_TYPE		0x00700000
+#define DCACHE_MISS_TYPE		0x00000000 /* Negative dentry (maybe fallthru to nowhere) */
+#define DCACHE_WHITEOUT_TYPE		0x00100000 /* Whiteout dentry (stop pathwalk) */
+#define DCACHE_DIRECTORY_TYPE		0x00200000 /* Normal directory */
+#define DCACHE_AUTODIR_TYPE		0x00300000 /* Lookupless directory (presumed automount) */
+#define DCACHE_REGULAR_TYPE		0x00400000 /* Regular file type (or fallthru to such) */
+#define DCACHE_SPECIAL_TYPE		0x00500000 /* Other file type (or fallthru to such) */
+#define DCACHE_SYMLINK_TYPE		0x00600000 /* Symlink (or fallthru to such) */
+
+#define DCACHE_FALLTHRU			0x01000000 /* Fall through to lower layer */
+#define DCACHE_CANT_MOUNT		0x00000100
+#define DCACHE_MOUNTED                  0x00010000 /* is a mountpoint */
+#define DCACHE_NEED_AUTOMOUNT           0x00020000 /* handle automount on this dir */
+#define DCACHE_MANAGED_DENTRY \
+		(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT)
+
+static inline bool d_mountpoint(const struct dentry *dentry)
+{
+	return dentry->d_flags & DCACHE_MOUNTED;
+}
+
+/*
+ * Directory cache entry type accessor functions.
+ */
+static inline unsigned __d_entry_type(const struct dentry *dentry)
+{
+	return dentry->d_flags & DCACHE_ENTRY_TYPE;
+}
+
+static inline bool d_is_miss(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_MISS_TYPE;
+}
+
+static inline bool d_can_lookup(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_DIRECTORY_TYPE;
+}
+
+static inline bool d_is_autodir(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_AUTODIR_TYPE;
+}
+
+static inline bool d_is_dir(const struct dentry *dentry)
+{
+	return d_can_lookup(dentry) || d_is_autodir(dentry);
+}
+
+static inline bool d_is_symlink(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_SYMLINK_TYPE;
+}
+
+static inline bool d_is_reg(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_REGULAR_TYPE;
+}
+
+static inline bool d_is_special(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_SPECIAL_TYPE;
+}
+
+static inline bool d_is_file(const struct dentry *dentry)
+{
+	return d_is_reg(dentry) || d_is_special(dentry);
+}
+
+static inline bool d_is_negative(const struct dentry *dentry)
+{
+	// TODO: check d_is_whiteout(dentry) also.
+	return d_is_miss(dentry);
+}
+
+static inline bool d_is_positive(const struct dentry *dentry)
+{
+	return !d_is_negative(dentry);
+}
+
+static inline struct inode *d_inode(const struct dentry *dentry)
+{
+	return dentry->d_inode;
+}
+
+#define IS_ROOT(x) ((x) == (x)->d_parent)
+
+char *dpath(struct dentry *dentry, struct dentry *root);
+
 #endif	/* __LINUX_DCACHE_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 153c464470..4550e8feeb 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -34,7 +34,18 @@
 #define DT_SOCK		12
 #define DT_WHT		14
 
+/*
+ * This is the "filldir" function type, used by readdir() to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+struct dir_context;
+typedef int (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64,
+			 unsigned);
+
 struct dir_context {
+	const filldir_t actor;
 	loff_t pos;
 };
 
@@ -94,12 +105,8 @@ struct inode {
 	};
 	uid_t			i_uid;
 	gid_t			i_gid;
-	dev_t			i_rdev;
 	u64			i_version;
 	loff_t			i_size;
-#ifdef __NEED_I_SIZE_ORDERED
-	seqcount_t		i_size_seqcount;
-#endif
 	struct timespec		i_atime;
 	struct timespec		i_mtime;
 	struct timespec		i_ctime;
@@ -107,39 +114,19 @@ struct inode {
 	blkcnt_t		i_blocks;
 	unsigned short          i_bytes;
 	umode_t			i_mode;
-	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
-	struct mutex		i_mutex;
-	struct rw_semaphore	i_alloc_sem;
 	const struct inode_operations	*i_op;
 	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
 	struct super_block	*i_sb;
-	struct file_lock	*i_flock;
-#ifdef CONFIG_QUOTA
-	struct dquot		*i_dquot[MAXQUOTAS];
-#endif
-	struct list_head	i_devices;
-	int			i_cindex;
 
 	__u32			i_generation;
 
-#ifdef CONFIG_DNOTIFY
-	unsigned long		i_dnotify_mask; /* Directory notify events */
-	struct dnotify_struct	*i_dnotify; /* for directory notifications */
-#endif
-
-#ifdef CONFIG_INOTIFY
-	struct list_head	inotify_watches; /* watches on this inode */
-	struct mutex		inotify_mutex;	/* protects the watches list */
-#endif
-
 	unsigned long		i_state;
-	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
 	unsigned int		i_flags;
+	unsigned int		i_count;
+
+	char			*i_link;
 
-#ifdef CONFIG_SECURITY
-	void			*i_security;
-#endif
 	void			*i_private; /* fs or device private pointer */
 };
 
@@ -199,19 +186,9 @@ struct super_block {
 	   Cannot be worse than a second */
 	u32		   s_time_gran;
 
-	/*
-	 * Filesystem subtype.  If non-empty the filesystem type field
-	 * in /proc/mounts will be "type.subtype"
-	 */
-	char *s_subtype;
-
-	/*
-	 * Saved mount options for lazy filesystems using
-	 * generic_show_options()
-	 */
-	char *s_options;
-
 	/* Number of inodes with nlink == 0 but still referenced */
+
+	const struct dentry_operations *s_d_op; /* default d_op for dentries */
 };
 
 struct file_system_type {
@@ -405,4 +382,80 @@ static inline loff_t i_size_read(const struct inode *inode)
 	return inode->i_size;
 }
 
+struct inode *new_inode(struct super_block *sb);
+unsigned int get_next_ino(void);
+void iput(struct inode *);
+struct inode *iget(struct inode *);
+void inc_nlink(struct inode *inode);
+
+struct inode_operations {
+	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
+
+	const char *(*get_link) (struct dentry *dentry, struct inode *inode);
+
+	int (*create) (struct inode *,struct dentry *, umode_t);
+	int (*link) (struct dentry *,struct inode *,struct dentry *);
+	int (*unlink) (struct inode *,struct dentry *);
+	int (*symlink) (struct inode *,struct dentry *,const char *);
+	int (*mkdir) (struct inode *,struct dentry *,umode_t);
+	int (*rmdir) (struct inode *,struct dentry *);
+	int (*rename) (struct inode *, struct dentry *,
+		       struct inode *, struct dentry *, unsigned int);
+};
+
+static inline ino_t parent_ino(struct dentry *dentry)
+{
+	return dentry->d_parent->d_inode->i_ino;
+}
+
+static inline bool dir_emit(struct dir_context *ctx,
+			    const char *name, int namelen,
+			    u64 ino, unsigned type)
+{
+	return ctx->actor(ctx, name, namelen, ctx->pos, ino, type) == 0;
+}
+
+static inline bool dir_emit_dot(struct file *file, struct dir_context *ctx)
+{
+	return ctx->actor(ctx, ".", 1, ctx->pos,
+			file->f_path.dentry->d_inode->i_ino, DT_DIR) == 0;
+}
+static inline bool dir_emit_dotdot(struct file *file, struct dir_context *ctx)
+{
+	return ctx->actor(ctx, "..", 2, ctx->pos,
+			parent_ino(file->f_path.dentry), DT_DIR) == 0;
+}
+
+static inline void dir_emit_dots(struct file *file, struct dir_context *ctx)
+{
+	if (ctx->pos == 0) {
+		dir_emit_dot(file, ctx);
+		ctx->pos = 1;
+	}
+	if (ctx->pos == 1) {
+		dir_emit_dotdot(file, ctx);
+		ctx->pos = 2;
+	}
+}
+
+struct file_operations {
+	int (*iterate) (struct file *, struct dir_context *);
+	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
+	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
+	int (*ioctl) (struct file *, int request, void *buf);
+	int (*truncate) (struct file *, loff_t);
+};
+
+void drop_nlink(struct inode *inode);
+
+extern const struct file_operations simple_dir_operations;
+extern const struct inode_operations simple_symlink_inode_operations;
+
+int simple_empty(struct dentry *dentry);
+int simple_unlink(struct inode *dir, struct dentry *dentry);
+int simple_rmdir(struct inode *dir, struct dentry *dentry);
+struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags);
+int dcache_readdir(struct file *, struct dir_context *);
+const char *simple_get_link(struct dentry *dentry, struct inode *inode);
+
 #endif /* _LINUX_FS_H */
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 57d5ba9523..9557365fb5 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -14,8 +14,11 @@
 
 struct vfsmount {
 	struct dentry *mnt_root;	/* root of the mounted tree */
+	struct dentry *mountpoint;	/* where it's mounted (barebox specific, no support */
+	struct vfsmount *parent;	/* for bind mounts and the like) */
 	struct super_block *mnt_sb;	/* pointer to superblock */
 	int mnt_flags;
+	int ref;
 };
 
 #endif /* _LINUX_MOUNT_H */
diff --git a/include/linux/namei.h b/include/linux/namei.h
new file mode 100644
index 0000000000..8ed7f8a1cd
--- /dev/null
+++ b/include/linux/namei.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_NAMEI_H
+#define _LINUX_NAMEI_H
+
+#include <linux/kernel.h>
+#include <linux/path.h>
+
+enum { MAX_NESTED_LINKS = 8 };
+
+#define MAXSYMLINKS 40
+
+/*
+ * Type of the last component on LOOKUP_PARENT
+ */
+enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
+
+/*
+ * The bitmask for a lookup event:
+ *  - follow links at the end
+ *  - require a directory
+ *  - ending slashes ok even for nonexistent files
+ *  - internal "there are more path components" flag
+ *  - dentry cache is untrusted; force a real lookup
+ *  - suppress terminal automount
+ */
+#define LOOKUP_FOLLOW		0x0001
+#define LOOKUP_DIRECTORY	0x0002
+#define LOOKUP_AUTOMOUNT	0x0004
+
+#define LOOKUP_PARENT		0x0010
+#define LOOKUP_REVAL		0x0020
+#define LOOKUP_RCU		0x0040
+#define LOOKUP_NO_REVAL		0x0080
+
+/*
+ * Intent data
+ */
+#define LOOKUP_OPEN		0x0100
+#define LOOKUP_CREATE		0x0200
+#define LOOKUP_EXCL		0x0400
+#define LOOKUP_RENAME_TARGET	0x0800
+
+#define LOOKUP_JUMPED		0x1000
+#define LOOKUP_ROOT		0x2000
+#define LOOKUP_EMPTY		0x4000
+#define LOOKUP_DOWN		0x8000
+
+#define AT_FDCWD                -100    /* Special value used to indicate
+                                           openat should use the current
+                                           working directory. */
+
+#endif /* _LINUX_NAMEI_H */
diff --git a/include/linux/stat.h b/include/linux/stat.h
index af022c5c79..87fe068396 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -42,6 +42,8 @@ extern "C" {
 #define S_IWOTH 00002		/* read permission for other */
 #define S_IXOTH 00001		/* execute/search permission for other */
 
+#define S_IRWXUGO	(S_IRWXU|S_IRWXG|S_IRWXO)
+
 struct stat {
 	unsigned short st_dev;
 	unsigned short __pad1;
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 02/10] fs: ramfs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
  2018-05-31 15:04 ` [PATCH 01/10] fs: dentry cache implementation Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 03/10] fs: devfs: " Sascha Hauer
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig |   1 -
 fs/ramfs.c | 401 +++++++++++++----------------------------------------
 2 files changed, 99 insertions(+), 303 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index b60314b1ec..2428a44766 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -31,7 +31,6 @@ source fs/ext4/Kconfig
 config FS_RAMFS
 	bool
 	default y
-	select FS_LEGACY
 	prompt "ramfs support"
 
 config FS_DEVFS
diff --git a/fs/ramfs.c b/fs/ramfs.c
index a97f0a6ebf..fb8d0580d8 100644
--- a/fs/ramfs.c
+++ b/fs/ramfs.c
@@ -35,6 +35,7 @@ struct ramfs_chunk {
 };
 
 struct ramfs_inode {
+	struct inode inode;
 	char *name;
 	struct ramfs_inode *parent;
 	struct ramfs_inode *next;
@@ -52,84 +53,52 @@ struct ramfs_inode {
 	struct ramfs_chunk *recent_chunkp;
 };
 
+static inline struct ramfs_inode *to_ramfs_inode(struct inode *inode)
+{
+	return container_of(inode, struct ramfs_inode, inode);
+}
+
 struct ramfs_priv {
 	struct ramfs_inode root;
 };
 
 /* ---------------------------------------------------------------*/
-static struct ramfs_inode * lookup(struct ramfs_inode *node, const char *name)
-{
-	debug("lookup: %s in %p\n",name, node);
-	if(!S_ISDIR(node->mode))
-		return NULL;
-
-	node = node->child;
-	if (!node)
-		return NULL;
-
-	while (node) {
-		debug("lookup: %s\n", node->name);
-		if (!strcmp(node->name, name)) {
-			debug("lookup: found: 0x%p\n",node);
-			return node;
-		}
-		node = node->next;
-	}
-        return NULL;
-}
 
-static struct ramfs_inode* rlookup(struct ramfs_priv *priv, const char *path)
-{
-	struct ramfs_inode *node = &priv->root;
-        static char *buf;
-        char *part;
-
-	debug("rlookup %s in %p\n",path, node);
-        buf = strdup(path);
-
-        part = strtok(buf, "/");
-        if (!part)
-		goto out;
-
-        do {
-                node = lookup(node, part);
-                if (!node)
-			goto out;
-                part = strtok(NULL, "/");
-        } while(part);
-
-out:
-	free(buf);
-        return node;
-}
+static const struct super_operations ramfs_ops;
+static const struct inode_operations ramfs_dir_inode_operations;
+static const struct inode_operations ramfs_file_inode_operations;
+static const struct inode_operations ramfs_symlink_inode_operations;
+static const struct file_operations ramfs_file_operations;
 
-static struct ramfs_inode* rlookup_parent(struct ramfs_priv *priv, const char *pathname, char **file)
+static struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir,
+				     umode_t mode)
 {
-	char *path;
-	struct ramfs_inode *node;
+	struct inode *inode = new_inode(sb);
 
-	pathname++;
-	path = strdup(pathname);
+	if (!inode)
+		return NULL;
 
-	if ((*file = strrchr((char *) pathname, '/'))) {
-		char *tmp;
-		(*file)++;
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
 
-		tmp = strrchr(path, '/');
-		*tmp = 0;
-		node = rlookup(priv, path);
-		if (!node) {
-			errno = -ENOENT;
-			goto out;
-		}
-	} else {
-		*file = (char *)pathname;
-		node = &priv->root;
+	switch (mode & S_IFMT) {
+	default:
+		return NULL;
+	case S_IFREG:
+		inode->i_op = &ramfs_file_inode_operations;
+		inode->i_fop = &ramfs_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &ramfs_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
+		inc_nlink(inode);
+		break;
+	case S_IFLNK:
+		inode->i_op = &ramfs_symlink_inode_operations;
+		break;
 	}
 
-out:
-	free(path);
-	return node;
+	return inode;
 }
 
 static int chunks = 0;
@@ -158,168 +127,75 @@ static void ramfs_put_chunk(struct ramfs_chunk *data)
 	chunks--;
 }
 
-static struct ramfs_inode* ramfs_get_inode(void)
-{
-	struct ramfs_inode *node = xzalloc(sizeof(struct ramfs_inode));
-	return node;
-}
-
-static void ramfs_put_inode(struct ramfs_inode *node)
-{
-	struct ramfs_chunk *data = node->data;
-
-	while (data) {
-		struct ramfs_chunk *tmp = data->next;
-		ramfs_put_chunk(data);
-		data = tmp;
-	}
-
-	free(node->symlink);
-	free(node->name);
-	free(node);
-}
-
-static struct ramfs_inode* node_insert(struct ramfs_inode *parent_node, const char *filename, ulong mode)
-{
-	struct ramfs_inode *node, *new_node = ramfs_get_inode();
-	new_node->name = strdup(filename);
-	new_node->mode = mode;
-
-	node = parent_node->child;
-
-	if (S_ISDIR(mode)) {
-		struct ramfs_inode *n = ramfs_get_inode();
-		n->name = strdup(".");
-		n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
-		n->child = n;
-		n->parent = new_node;
-		new_node->child = n;
-		n = ramfs_get_inode();
-		n->name = strdup("..");
-		n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
-		n->parent = new_node;
-		n->child = parent_node->child;
-		new_node->child->next = n;
-	}
-
-	while (node->next)
-		node = node->next;
-
-	node->next = new_node;
-	return new_node;
-}
-
 /* ---------------------------------------------------------------*/
 
-static int __ramfs_create(struct device_d *dev, const char *pathname,
-			  mode_t mode, const char *symlink)
+static int
+ramfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node;
-	char *file;
-	char *__symlink = NULL;
+	struct inode *inode = ramfs_get_inode(dir->i_sb, dir, mode);
 
-	node = rlookup_parent(priv, pathname, &file);
-	if (!node)
-		return -ENOENT;
-
-	if (symlink) {
-		__symlink = strdup(symlink);
-		if (!__symlink)
-			return -ENOMEM;
-	}
+	if (!inode)
+		return -ENOSPC;
 
-	node = node_insert(node, file, mode);
-	if (!node) {
-		free(__symlink);
-		return -ENOMEM;
+	if (inode) {
+		d_instantiate(dentry, inode);
+		dget(dentry);   /* Extra count - pin the dentry in core */
 	}
 
-	node->symlink = __symlink;
-
 	return 0;
 }
 
-static int ramfs_create(struct device_d *dev, const char *pathname, mode_t mode)
+static int ramfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-	return __ramfs_create(dev, pathname, mode, NULL);
-}
+	int ret;
 
-static int ramfs_unlink(struct device_d *dev, const char *pathname)
-{
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node, *lastnode;
-	char *file;
-
-	node = rlookup_parent(priv, pathname, &file);
-
-	lastnode = node->child->next;
-	node = lastnode->next;
-
-	while (node) {
-		if (!strcmp(node->name, file)) {
-			struct ramfs_inode *tmp;
-			tmp = node;
-			lastnode->next = node->next;
-			ramfs_put_inode(tmp);
-			return 0;
-		}
-		lastnode = node;
-		node = node->next;
-	};
-	return -ENOENT;
-}
+	ret = ramfs_mknod(dir, dentry, mode | S_IFDIR);
+	if (!ret)
+		inc_nlink(dir);
 
-static int ramfs_mkdir(struct device_d *dev, const char *pathname)
-{
-	return ramfs_create(dev, pathname, S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
+	return ret;
 }
 
-static int ramfs_rmdir(struct device_d *dev, const char *pathname)
+static int ramfs_create(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node, *lastnode;
-	char *file;
-
-	node = rlookup_parent(priv, pathname, &file);
-
-	lastnode = node->child->next;
-	node = lastnode->next;
-
-	while (node) {
-		if (!strcmp(node->name, file)) {
-			struct ramfs_inode *tmp;
-			tmp = node;
-			lastnode->next = node->next;
-			ramfs_put_inode(tmp->child->next);
-			ramfs_put_inode(tmp->child);
-			ramfs_put_inode(tmp);
-			return 0;
-		}
-		lastnode = node;
-		node = node->next;
-	};
-	return -ENOENT;
+	return ramfs_mknod(dir, dentry, mode | S_IFREG);
 }
 
-static int ramfs_open(struct device_d *dev, FILE *file, const char *filename)
+static int ramfs_symlink(struct inode *dir, struct dentry *dentry,
+			 const char *symname)
 {
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node = rlookup(priv, filename);
+	struct inode *inode;
 
-	if (!node)
-		return -ENOENT;
+	inode = ramfs_get_inode(dir->i_sb, dir, S_IFLNK | S_IRWXG);
+	if (!inode)
+		return -ENOSPC;
+
+	inode->i_link = xstrdup(symname);
+	d_instantiate(dentry, inode);
 
-	file->size = node->size;
-	file->priv = node;
 	return 0;
 }
 
-static int ramfs_close(struct device_d *dev, FILE *f)
+static const char *ramfs_get_link(struct dentry *dentry, struct inode *inode)
 {
-	return 0;
+	return inode->i_link;
 }
 
+static const struct inode_operations ramfs_symlink_inode_operations =
+{
+	.get_link = ramfs_get_link,
+};
+
+static const struct inode_operations ramfs_dir_inode_operations =
+{
+	.lookup = simple_lookup,
+	.symlink = ramfs_symlink,
+	.mkdir = ramfs_mkdir,
+	.rmdir = simple_rmdir,
+	.unlink = simple_unlink,
+	.create = ramfs_create,
+};
+
 static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node, int chunk)
 {
 	struct ramfs_chunk *data;
@@ -351,7 +227,8 @@ static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node, int chunk)
 
 static int ramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
 {
-	struct ramfs_inode *node = f->priv;
+	struct inode *inode = f->f_inode;
+	struct ramfs_inode *node = to_ramfs_inode(inode);
 	int chunk;
 	struct ramfs_chunk *data;
 	int ofs;
@@ -359,12 +236,12 @@ static int ramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
 	int pos = f->pos;
 	int size = insize;
 
-	chunk = f->pos / CHUNK_SIZE;
+	chunk = pos / CHUNK_SIZE;
 	debug("%s: reading from chunk %d\n", __FUNCTION__, chunk);
 
 	/* Position ourself in stream */
 	data = ramfs_find_chunk(node, chunk);
-	ofs = f->pos % CHUNK_SIZE;
+	ofs = pos % CHUNK_SIZE;
 
 	/* Read till end of current chunk */
 	if (ofs) {
@@ -400,7 +277,8 @@ static int ramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
 
 static int ramfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize)
 {
-	struct ramfs_inode *node = f->priv;
+	struct inode *inode = f->f_inode;
+	struct ramfs_inode *node = to_ramfs_inode(inode);
 	int chunk;
 	struct ramfs_chunk *data;
 	int ofs;
@@ -455,7 +333,8 @@ static loff_t ramfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
 
 static int ramfs_truncate(struct device_d *dev, FILE *f, ulong size)
 {
-	struct ramfs_inode *node = f->priv;
+	struct inode *inode = f->f_inode;
+	struct ramfs_inode *node = to_ramfs_inode(inode);
 	int oldchunks, newchunks;
 	struct ramfs_chunk *data = node->data;
 
@@ -502,108 +381,38 @@ static int ramfs_truncate(struct device_d *dev, FILE *f, ulong size)
 	return 0;
 }
 
-static DIR* ramfs_opendir(struct device_d *dev, const char *pathname)
+static struct inode *ramfs_alloc_inode(struct super_block *sb)
 {
-	DIR *dir;
-	struct ramfs_priv *priv = dev->priv;
 	struct ramfs_inode *node;
 
-	debug("opendir: %s\n", pathname);
-
-	node = rlookup(priv, pathname);
-
+	node = xzalloc(sizeof(*node));
 	if (!node)
 		return NULL;
 
-	if (!S_ISDIR(node->mode))
-		return NULL;
-
-	dir = xmalloc(sizeof(DIR));
-
-	dir->priv = node->child;
-
-	return dir;
-}
-
-static struct dirent* ramfs_readdir(struct device_d *dev, DIR *dir)
-{
-	struct ramfs_inode *node = dir->priv;
-
-	if (node) {
-		strcpy(dir->d.d_name, node->name);
-		dir->priv = node->next;
-		return &dir->d;
-	}
-	return NULL;
-}
-
-static int ramfs_closedir(struct device_d *dev, DIR *dir)
-{
-	free(dir);
-	return 0;
-}
-
-static int ramfs_stat(struct device_d *dev, const char *filename, struct stat *s)
-{
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node = rlookup(priv, filename);
-
-	if (!node)
-		return -ENOENT;
-
-	s->st_size = node->symlink ? strlen(node->symlink) : node->size;
-	s->st_mode = node->mode;
-
-	return 0;
-}
-
-static int ramfs_symlink(struct device_d *dev, const char *pathname,
-		       const char *newpath)
-{
-	mode_t mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
-
-	return __ramfs_create(dev, newpath, mode, pathname);
+	return &node->inode;
 }
 
-static int ramfs_readlink(struct device_d *dev, const char *pathname,
-			char *buf, size_t bufsiz)
-{
-	struct ramfs_priv *priv = dev->priv;
-	struct ramfs_inode *node = rlookup(priv, pathname);
-	int len;
-
-	if (!node || !node->symlink)
-		return -ENOENT;
-
-	len = min(bufsiz, strlen(node->symlink));
-
-	memcpy(buf, node->symlink, len);
-
-	return 0;
-}
+static const struct super_operations ramfs_ops = {
+	.alloc_inode = ramfs_alloc_inode,
+};
 
 static int ramfs_probe(struct device_d *dev)
 {
-	struct ramfs_inode *n;
+	struct inode *inode;
 	struct ramfs_priv *priv = xzalloc(sizeof(struct ramfs_priv));
+	struct fs_device_d *fsdev = dev_to_fs_device(dev);
+	struct super_block *sb = &fsdev->sb;
 
 	dev->priv = priv;
 
 	priv->root.name = "/";
 	priv->root.mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
 	priv->root.parent = &priv->root;
-	n = ramfs_get_inode();
-	n->name = strdup(".");
-	n->mode = S_IFDIR;
-	n->parent = &priv->root;
-	n->child = n;
-	priv->root.child = n;
-	n = ramfs_get_inode();
-	n->name = strdup("..");
-	n->mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
-	n->parent = &priv->root;
-	n->child = priv->root.child;
-	priv->root.child->next = n;
+
+	sb->s_op = &ramfs_ops;
+
+	inode = ramfs_get_inode(sb, NULL, S_IFDIR);
+	sb->s_root = d_make_root(inode);
 
 	return 0;
 }
@@ -614,22 +423,10 @@ static void ramfs_remove(struct device_d *dev)
 }
 
 static struct fs_driver_d ramfs_driver = {
-	.create    = ramfs_create,
-	.unlink    = ramfs_unlink,
-	.open      = ramfs_open,
-	.close     = ramfs_close,
-	.truncate  = ramfs_truncate,
 	.read      = ramfs_read,
 	.write     = ramfs_write,
 	.lseek     = ramfs_lseek,
-	.mkdir     = ramfs_mkdir,
-	.rmdir     = ramfs_rmdir,
-	.opendir   = ramfs_opendir,
-	.readdir   = ramfs_readdir,
-	.closedir  = ramfs_closedir,
-	.stat      = ramfs_stat,
-	.symlink   = ramfs_symlink,
-	.readlink  = ramfs_readlink,
+	.truncate  = ramfs_truncate,
 	.flags     = FS_DRIVER_NO_DEV,
 	.drv = {
 		.probe  = ramfs_probe,
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 03/10] fs: devfs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
  2018-05-31 15:04 ` [PATCH 01/10] fs: dentry cache implementation Sascha Hauer
  2018-05-31 15:04 ` [PATCH 02/10] fs: ramfs: Switch to " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 04/10] fs: ext4: " Sascha Hauer
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig |   1 -
 fs/devfs.c | 150 ++++++++++++++++++++++++++++++-----------------------
 2 files changed, 85 insertions(+), 66 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 2428a44766..195a5cb060 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -36,7 +36,6 @@ config FS_RAMFS
 config FS_DEVFS
 	bool
 	default y
-	select FS_LEGACY
 	prompt "devfs support"
 
 config FS_TFTP
diff --git a/fs/devfs.c b/fs/devfs.c
index 2a7b1b3466..5d0bb2c674 100644
--- a/fs/devfs.c
+++ b/fs/devfs.c
@@ -33,6 +33,11 @@
 #include <linux/mtd/mtd-abi.h>
 #include <partition.h>
 
+struct devfs_inode {
+	struct inode inode;
+	struct cdev *cdev;
+};
+
 extern struct list_head cdev_list;
 
 static int devfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
@@ -110,14 +115,11 @@ static int devfs_memmap(struct device_d *_dev, FILE *f, void **map, int flags)
 
 static int devfs_open(struct device_d *_dev, FILE *f, const char *filename)
 {
-	struct cdev *cdev;
+	struct inode *inode = f->f_inode;
+	struct devfs_inode *node = container_of(inode, struct devfs_inode, inode);
+	struct cdev *cdev = node->cdev;
 	int ret;
 
-	cdev = cdev_by_name(filename + 1);
-
-	if (!cdev)
-		return -ENOENT;
-
 	f->size = cdev->flags & DEVFS_IS_CHARACTER_DEV ?
 			FILE_SIZE_STREAM : cdev->size;
 	f->priv = cdev;
@@ -180,71 +182,112 @@ static int devfs_truncate(struct device_d *dev, FILE *f, ulong size)
 	return 0;
 }
 
-static DIR* devfs_opendir(struct device_d *dev, const char *pathname)
+static struct inode *devfs_alloc_inode(struct super_block *sb)
 {
-	DIR *dir;
-
-	dir = xzalloc(sizeof(DIR));
+	struct devfs_inode *node;
 
-	if (!list_empty(&cdev_list))
-		dir->priv = list_first_entry(&cdev_list, struct cdev, list);
+	node = xzalloc(sizeof(*node));
+	if (!node)
+		return NULL;
 
-	return dir;
+	return &node->inode;
 }
 
-static struct dirent* devfs_readdir(struct device_d *_dev, DIR *dir)
+int devfs_iterate(struct file *file, struct dir_context *ctx)
 {
-	struct cdev *cdev = dir->priv;
+	struct cdev *cdev;
 
-	if (!cdev)
-		return NULL;
+	dir_emit_dots(file, ctx);
 
-	list_for_each_entry_from(cdev, &cdev_list, list) {
-		strcpy(dir->d.d_name, cdev->name);
-		dir->priv = list_entry(cdev->list.next, struct cdev, list);
-		return &dir->d;
+	list_for_each_entry(cdev, &cdev_list, list) {
+		dir_emit(ctx, cdev->name, strlen(cdev->name),
+				1 /* FIXME */, DT_REG);
 	}
-	return NULL;
+
+        return 0;
 }
 
-static int devfs_closedir(struct device_d *dev, DIR *dir)
+static const struct inode_operations devfs_file_inode_operations;
+static const struct file_operations devfs_dir_operations;
+static const struct inode_operations devfs_dir_inode_operations;
+static const struct file_operations devfs_file_operations;
+
+static struct inode *devfs_get_inode(struct super_block *sb, const struct inode *dir,
+                                     umode_t mode)
 {
-	free(dir);
-	return 0;
+	struct inode *inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+
+	switch (mode & S_IFMT) {
+	default:
+		return NULL;
+	case S_IFREG:
+		inode->i_op = &devfs_file_inode_operations;
+		inode->i_fop = &devfs_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &devfs_dir_inode_operations;
+		inode->i_fop = &devfs_dir_operations;
+		inc_nlink(inode);
+		break;
+	}
+
+	return inode;
 }
 
-static int devfs_stat(struct device_d *_dev, const char *filename, struct stat *s)
+static struct dentry *devfs_lookup(struct inode *dir, struct dentry *dentry,
+				   unsigned int flags)
 {
+	struct devfs_inode *dinode;
+	struct inode *inode;
 	struct cdev *cdev;
 
-	cdev = lcdev_by_name(filename + 1);
+	cdev = cdev_by_name(dentry->name);
 	if (!cdev)
-		return -ENOENT;
+		return ERR_PTR(-ENOENT);
 
-	s->st_mode = S_IFCHR;
-	s->st_size = cdev->size;
+	inode = devfs_get_inode(dir->i_sb, dir, S_IFREG | S_IRWXUGO);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
 
-	if (cdev->link)
-		s->st_mode |= S_IFLNK;
+	dinode = container_of(inode, struct devfs_inode, inode);
 
-	cdev = cdev_readlink(cdev);
+	inode->i_size = cdev->size;
+	dinode->cdev = cdev;
 
-	if (cdev->ops->write)
-		s->st_mode |= S_IWUSR;
-	if (cdev->ops->read)
-		s->st_mode |= S_IRUSR;
+	d_add(dentry, inode);
 
-	return 0;
+	return NULL;
 }
 
+static const struct file_operations devfs_dir_operations = {
+	.iterate = devfs_iterate,
+};
+
+static const struct inode_operations devfs_dir_inode_operations =
+{
+	.lookup = devfs_lookup,
+};
+
+static const struct super_operations devfs_ops = {
+	.alloc_inode = devfs_alloc_inode,
+};
+
 static int devfs_probe(struct device_d *dev)
 {
+	struct inode *inode;
 	struct fs_device_d *fsdev = dev_to_fs_device(dev);
+	struct super_block *sb = &fsdev->sb;
 
-	if (strcmp(fsdev->path, "/dev")) {
-		dev_err(dev, "devfs can only be mounted on /dev/\n");
-		return -EINVAL;
-	}
+	sb->s_op = &devfs_ops;
+
+	inode = devfs_get_inode(sb, NULL, S_IFDIR);
+	sb->s_root = d_make_root(inode);
 
 	return 0;
 }
@@ -253,24 +296,6 @@ static void devfs_delete(struct device_d *dev)
 {
 }
 
-static int devfs_readlink(struct device_d *dev, const char *pathname,
-			  char *buf, size_t bufsz)
-{
-	struct cdev *cdev;
-
-	cdev = cdev_by_name(pathname + 1);
-	if (!cdev)
-		return -ENOENT;
-
-	while (cdev->link)
-		cdev = cdev->link;
-
-	bufsz = min(bufsz, strlen(cdev->name));
-	memcpy(buf, cdev->name, bufsz);
-
-	return 0;
-}
-
 static struct fs_driver_d devfs_driver = {
 	.read      = devfs_read,
 	.write     = devfs_write,
@@ -279,15 +304,10 @@ static struct fs_driver_d devfs_driver = {
 	.close     = devfs_close,
 	.flush     = devfs_flush,
 	.ioctl     = devfs_ioctl,
-	.opendir   = devfs_opendir,
-	.readdir   = devfs_readdir,
 	.truncate  = devfs_truncate,
-	.closedir  = devfs_closedir,
-	.stat      = devfs_stat,
 	.erase     = devfs_erase,
 	.protect   = devfs_protect,
 	.memmap    = devfs_memmap,
-	.readlink  = devfs_readlink,
 	.flags     = FS_DRIVER_NO_DEV,
 	.drv = {
 		.probe  = devfs_probe,
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 04/10] fs: ext4: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (2 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 03/10] fs: devfs: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 05/10] fs: ubifs: " Sascha Hauer
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ext4/Kconfig       |   1 -
 fs/ext4/ext_barebox.c | 279 ++++++++++++++++++++++++------------------
 fs/ext4/ext_common.h  |   3 +
 3 files changed, 160 insertions(+), 123 deletions(-)

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 8643e9d859..f36043d9a7 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -1,4 +1,3 @@
 config FS_EXT4
 	bool
-	select FS_LEGACY
 	prompt "ext4 filesystem support"
diff --git a/fs/ext4/ext_barebox.c b/fs/ext4/ext_barebox.c
index e40278a5bd..1e7da2a4b4 100644
--- a/fs/ext4/ext_barebox.c
+++ b/fs/ext4/ext_barebox.c
@@ -46,34 +46,17 @@ int ext4fs_devread(struct ext_filesystem *fs, int __sector, int byte_offset,
 	return 0;
 }
 
-static int ext_open(struct device_d *dev, FILE *file, const char *filename)
+static inline struct ext2fs_node *to_ext2_node(struct inode *inode)
 {
-	struct ext_filesystem *fs = dev->priv;
-	struct ext2fs_node *inode;
-	int ret;
-
-	ret = ext4fs_open(fs->data, filename, &inode);
-	if (ret)
-		return ret;
-
-	file->size = le32_to_cpu(inode->inode.size);
-	file->priv = inode;
-
-	return 0;
-}
-
-static int ext_close(struct device_d *dev, FILE *f)
-{
-	struct ext_filesystem *fs = dev->priv;
-
-	ext4fs_free_node(f->priv, &fs->data->diropen);
-
-	return 0;
+	return container_of(inode, struct ext2fs_node, i);
 }
 
 static int ext_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
 {
-	return ext4fs_read_file(f->priv, f->pos, insize, buf);
+	struct inode *inode = f->f_inode;
+	struct ext2fs_node *node = to_ext2_node(inode);
+
+	return ext4fs_read_file(node, f->pos, insize, buf);
 }
 
 static loff_t ext_lseek(struct device_d *dev, FILE *f, loff_t pos)
@@ -83,143 +66,195 @@ static loff_t ext_lseek(struct device_d *dev, FILE *f, loff_t pos)
 	return f->pos;
 }
 
-struct ext4fs_dir {
-	struct ext2fs_node *dirnode;
-	int fpos;
-	DIR dir;
-};
-
-static DIR *ext_opendir(struct device_d *dev, const char *pathname)
+static struct inode *ext_alloc_inode(struct super_block *sb)
 {
-	struct ext_filesystem *fs = dev->priv;
-	struct ext4fs_dir *ext4_dir;
-	int type, ret;
-
-	ext4_dir = xzalloc(sizeof(*ext4_dir));
-
-	ret = ext4fs_find_file(pathname, &fs->data->diropen, &ext4_dir->dirnode,
-				  &type);
-	if (ret) {
-		free(ext4_dir);
-		return NULL;
-	}
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct ext_filesystem *fs = fsdev->dev.priv;
+	struct ext2fs_node *node;
 
-	if (type != FILETYPE_DIRECTORY)
+	node = xzalloc(sizeof(*node));
+	if (!node)
 		return NULL;
 
-	ext4_dir->dir.priv = ext4_dir;
+	node->data = fs->data;
 
-	ret = ext4fs_read_inode(ext4_dir->dirnode->data, ext4_dir->dirnode->ino,
-			&ext4_dir->dirnode->inode);
-	if (ret) {
-		ext4fs_free_node(ext4_dir->dirnode, &fs->data->diropen);
-		free(ext4_dir);
+	return &node->i;
+}
 
-		return NULL;
-	}
+static const struct super_operations ext_ops = {
+	.alloc_inode = ext_alloc_inode,
+};
 
-	return &ext4_dir->dir;
-}
+struct inode *ext_get_inode(struct super_block *sb, int ino);
 
-static struct dirent *ext_readdir(struct device_d *dev, DIR *dir)
+static int ext4fs_get_ino(struct ext2fs_node *dir, struct qstr *name, int *inum)
 {
-	struct ext4fs_dir *ext4_dir = dir->priv;
-	struct ext2_dirent dirent;
-	struct ext2fs_node *diro = ext4_dir->dirnode;
+	unsigned int fpos = 0;
 	int ret;
-	char *filename;
 
-	if (ext4_dir->fpos >= le32_to_cpu(diro->inode.size))
-		return NULL;
+	while (fpos < le32_to_cpu(dir->inode.size)) {
+		struct ext2_dirent dirent;
 
-	ret = ext4fs_read_file(diro, ext4_dir->fpos, sizeof(struct ext2_dirent),
-			(char *) &dirent);
-	if (ret < 0)
-		return NULL;
+		ret = ext4fs_read_file(dir, fpos, sizeof(dirent), (char *)&dirent);
+		if (ret < 1)
+			return -EINVAL;
 
-	if (dirent.namelen == 0)
-		return NULL;
+		if (dirent.namelen != 0) {
+			char filename[dirent.namelen];
+			int ino;
 
-	filename = xzalloc(dirent.namelen + 1);
+			ret = ext4fs_read_file(dir, fpos + sizeof(dirent),
+						  dirent.namelen, filename);
+			if (ret < 1)
+				return -EINVAL;
 
-	ret = ext4fs_read_file(diro, ext4_dir->fpos + sizeof(struct ext2_dirent),
-			dirent.namelen, filename);
-	if (ret < 0) {
-		free(filename);
-		return NULL;
+			ino = le32_to_cpu(dirent.inode);
+
+			if (name->len == dirent.namelen &&
+			    !strncmp(name->name, filename, name->len)) {
+				*inum = ino;
+				return 0;
+			}
+		}
+		fpos += le16_to_cpu(dirent.direntlen);
 	}
 
-	filename[dirent.namelen] = '\0';
+	*inum = 0;
 
-	ext4_dir->fpos += le16_to_cpu(dirent.direntlen);
+	return 0;
+}
 
-	strcpy(dir->d.d_name, filename);
+static struct dentry *ext_lookup(struct inode *dir, struct dentry *dentry,
+				 unsigned int flags)
+{
+	struct ext2fs_node *e2dir = to_ext2_node(dir);
+	int ret, ino;
+	struct inode *inode;
 
-	free(filename);
+	ret = ext4fs_get_ino(e2dir, &dentry->d_name, &ino);
+	if (ret)
+		return ERR_PTR(ret);
 
-	return &dir->d;
+	if (ino) {
+		inode = ext_get_inode(dir->i_sb, ino);
+
+		d_add(dentry, inode);
+	}
+
+	return NULL;
 }
 
-static int ext_closedir(struct device_d *dev, DIR *dir)
+static const struct inode_operations ext_inode_operations = {
+	.lookup = ext_lookup,
+};
+
+static int ext_iterate(struct file *file, struct dir_context *ctx)
 {
-	struct ext_filesystem *fs = dev->priv;
-	struct ext4fs_dir *ext4_dir = dir->priv;
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *dir = d_inode(dentry);
+	unsigned int fpos = 0;
+	int status, ret;
+	struct ext2fs_node *diro = to_ext2_node(dir);
+	void *buf;
 
-	ext4fs_free_node(ext4_dir->dirnode, &fs->data->diropen);
+	buf = malloc(dir->i_size);
+	if (!buf)
+		return -ENOMEM;
 
-	free(ext4_dir);
+	status = ext4fs_read_file(diro, 0, dir->i_size, buf);
+	if (status < 1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	while (fpos < dir->i_size) {
+		const struct ext2_dirent *dirent = buf + fpos;
+		const char *filename = buf + fpos + sizeof(*dirent);
+
+		if (dirent->namelen != 0)
+			dir_emit(ctx, filename, dirent->namelen,
+				 le32_to_cpu(dirent->inode), DT_UNKNOWN);
+
+		fpos += le16_to_cpu(dirent->direntlen);
+	}
+	ret = 0;
+out:
+	free(buf);
+
+	return ret;
 
-	return 0;
 }
 
-static int ext_stat(struct device_d *dev, const char *filename, struct stat *s)
+const struct file_operations ext_dir_operations = {
+	.iterate = ext_iterate,
+};
+
+static const char *ext_get_link(struct dentry *dentry, struct inode *inode)
 {
-	struct ext_filesystem *fs = dev->priv;
-	struct ext2fs_node *node;
-	int status, ret;
+	struct ext2fs_node *node = to_ext2_node(inode);
+	int ret;
 
-	status = ext4fs_find_file(filename, &fs->data->diropen, &node, NULL);
-	if (status)
-		return -ENOENT;
+	if (inode->i_size < sizeof(node->inode.b.symlink))
+		return inode->i_link;
 
-	ret = ext4fs_read_inode(node->data, node->ino, &node->inode);
-	if (ret)
-		return ret;
+	BUG_ON(inode->i_link);
 
-	s->st_size = le32_to_cpu(node->inode.size);
-	s->st_mode = le16_to_cpu(node->inode.mode);
+	inode->i_link = zalloc(inode->i_size + 1);
 
-	ext4fs_free_node(node, &fs->data->diropen);
+	ret = ext4fs_read_file(node, 0, inode->i_size, inode->i_link);
+	if (ret == 0) {
+		free(inode->i_link);
+		inode->i_link = NULL;
+	}
 
-	return 0;
+	return inode->i_link;
 }
 
-static int ext_readlink(struct device_d *dev, const char *pathname,
-		char *buf, size_t bufsiz)
+static const struct inode_operations ext_symlink_inode_operations =
 {
-	struct ext_filesystem *fs = dev->priv;
-	struct ext2fs_node *node;
-	char *symlink;
-	int ret, len, type;
+	.get_link = ext_get_link,
+};
 
-	ret = ext4fs_find_file(pathname, &fs->data->diropen, &node, &type);
-	if (ret)
-		return ret;
+struct inode *ext_get_inode(struct super_block *sb, int ino)
+{
+	struct inode *inode;
+	struct ext2fs_node *node;
+	struct fs_device_d *fsdev = container_of(sb, struct fs_device_d, sb);
+	struct ext_filesystem *fs = fsdev->dev.priv;
+	int ret;
 
-	if (type != FILETYPE_SYMLINK)
-		return -EINVAL;
+	inode = new_inode(sb);
 
-	symlink = ext4fs_read_symlink(node);
-	if (!symlink)
-		return -ENOENT;
+	node = container_of(inode, struct ext2fs_node, i);
 
-	len = min(bufsiz, strlen(symlink));
+	ret = ext4fs_read_inode(fs->data, ino, &node->inode);
 
-	memcpy(buf, symlink, len);
+	inode->i_ino = ino;
+	inode->i_mode = le16_to_cpu(node->inode.mode);
+	inode->i_size = le32_to_cpu(node->inode.size);
 
-	free(symlink);
+	switch (inode->i_mode & S_IFMT) {
+	default:
+		return NULL;
+	case S_IFREG:
+		inode->i_op = &ext_inode_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &ext_inode_operations;
+		inode->i_fop = &ext_dir_operations;
+		inc_nlink(inode);
+		break;
+	case S_IFLNK:
+		inode->i_op = &ext_symlink_inode_operations;
+		if (inode->i_size < sizeof(node->inode.b.symlink)) {
+			inode->i_link = zalloc(inode->i_size + 1);
+			strncpy(inode->i_link, node->inode.b.symlink,
+				inode->i_size);
+		}
+		break;
+	}
 
-	return 0;
+	return inode;
 }
 
 static int ext_probe(struct device_d *dev)
@@ -227,6 +262,8 @@ static int ext_probe(struct device_d *dev)
 	struct fs_device_d *fsdev = dev_to_fs_device(dev);
 	int ret;
 	struct ext_filesystem *fs;
+	struct super_block *sb = &fsdev->sb;
+	struct inode *inode;
 
 	fs = xzalloc(sizeof(*fs));
 
@@ -243,6 +280,11 @@ static int ext_probe(struct device_d *dev)
 	if (ret)
 		goto err_mount;
 
+	sb->s_op = &ext_ops;
+
+	inode = ext_get_inode(sb, 2);
+	sb->s_root = d_make_root(inode);
+
 	return 0;
 
 err_mount:
@@ -261,15 +303,8 @@ static void ext_remove(struct device_d *dev)
 }
 
 static struct fs_driver_d ext_driver = {
-	.open      = ext_open,
-	.close     = ext_close,
 	.read      = ext_read,
 	.lseek     = ext_lseek,
-	.opendir   = ext_opendir,
-	.readdir   = ext_readdir,
-	.closedir  = ext_closedir,
-	.stat      = ext_stat,
-	.readlink  = ext_readlink,
 	.type      = filetype_ext,
 	.flags     = 0,
 	.drv = {
diff --git a/fs/ext4/ext_common.h b/fs/ext4/ext_common.h
index e82b56b86a..c084cf9a32 100644
--- a/fs/ext4/ext_common.h
+++ b/fs/ext4/ext_common.h
@@ -30,6 +30,8 @@
 #ifndef __EXT_COMMON__
 #define __EXT_COMMON__
 
+#include <linux/fs.h>
+
 #define SECTOR_SIZE		0x200
 #define SECTOR_BITS		9
 
@@ -208,6 +210,7 @@ struct ext2_dirent {
 };
 
 struct ext2fs_node {
+	struct inode i;
 	struct ext2_data *data;
 	struct ext2_inode inode;
 	int ino;
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 05/10] fs: ubifs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (3 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 04/10] fs: ext4: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 06/10] fs: nfs: " Sascha Hauer
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/ubifs/Kconfig  |   3 -
 fs/ubifs/Makefile |   2 +-
 fs/ubifs/dir.c    | 410 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/ubifs/super.c  | 153 ++---------------
 fs/ubifs/ubifs.c  | 341 +-------------------------------------
 fs/ubifs/ubifs.h  |   7 +-
 6 files changed, 432 insertions(+), 484 deletions(-)
 create mode 100644 fs/ubifs/dir.c

diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 9aa0172289..889a2be97a 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -1,9 +1,6 @@
 menuconfig FS_UBIFS
 	bool
 	depends on MTD_UBI
-	select FS_LEGACY
-# Due to duplicate definition of iput
-	depends on BROKEN
 	prompt "ubifs support"
 
 if FS_UBIFS
diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile
index e39ae3b0fd..44ef1b561c 100644
--- a/fs/ubifs/Makefile
+++ b/fs/ubifs/Makefile
@@ -1,4 +1,4 @@
 obj-y += ubifs.o io.o super.o sb.o master.o lpt.o
-obj-y += lpt_commit.o scan.o lprops.o
+obj-y += lpt_commit.o scan.o lprops.o dir.o
 obj-y += tnc.o tnc_misc.o debug.o crc16.o budget.o
 obj-y += log.o orphan.o recovery.o replay.o gc.o
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
new file mode 100644
index 0000000000..e90bdb8348
--- /dev/null
+++ b/fs/ubifs/dir.c
@@ -0,0 +1,410 @@
+/* * This file is part of UBIFS.
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation.
+ * Copyright (C) 2006, 2007 University of Szeged, Hungary
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём)
+ *          Adrian Hunter
+ *          Zoltan Sogor
+ */
+
+/*
+ * This file implements directory operations.
+ *
+ * All FS operations in this file allocate budget before writing anything to the
+ * media. If they fail to allocate it, the error is returned. The only
+ * exceptions are 'ubifs_unlink()' and 'ubifs_rmdir()' which keep working even
+ * if they unable to allocate the budget, because deletion %-ENOSPC failure is
+ * not what users are usually ready to get. UBIFS budgeting subsystem has some
+ * space reserved for these purposes.
+ *
+ * All operations in this file write all inodes which they change straight
+ * away, instead of marking them dirty. For example, 'ubifs_link()' changes
+ * @i_size of the parent inode and writes the parent inode together with the
+ * target inode. This was done to simplify file-system recovery which would
+ * otherwise be very difficult to do. The only exception is rename which marks
+ * the re-named inode dirty (because its @i_ctime is updated) but does not
+ * write it, but just marks it as dirty.
+ */
+
+#include "ubifs.h"
+
+/**
+ * inherit_flags - inherit flags of the parent inode.
+ * @dir: parent inode
+ * @mode: new inode mode flags
+ *
+ * This is a helper function for 'ubifs_new_inode()' which inherits flag of the
+ * parent directory inode @dir. UBIFS inodes inherit the following flags:
+ * o %UBIFS_COMPR_FL, which is useful to switch compression on/of on
+ *   sub-directory basis;
+ * o %UBIFS_SYNC_FL - useful for the same reasons;
+ * o %UBIFS_DIRSYNC_FL - similar, but relevant only to directories.
+ *
+ * This function returns the inherited flags.
+ */
+static int inherit_flags(const struct inode *dir, umode_t mode)
+{
+	int flags;
+	const struct ubifs_inode *ui = ubifs_inode(dir);
+
+	if (!S_ISDIR(dir->i_mode))
+		/*
+		 * The parent is not a directory, which means that an extended
+		 * attribute inode is being created. No flags.
+		 */
+		return 0;
+
+	flags = ui->flags & (UBIFS_COMPR_FL | UBIFS_SYNC_FL | UBIFS_DIRSYNC_FL);
+	if (!S_ISDIR(mode))
+		/* The "DIRSYNC" flag only applies to directories */
+		flags &= ~UBIFS_DIRSYNC_FL;
+	return flags;
+}
+
+/**
+ * ubifs_new_inode - allocate new UBIFS inode object.
+ * @c: UBIFS file-system description object
+ * @dir: parent directory inode
+ * @mode: inode mode flags
+ *
+ * This function finds an unused inode number, allocates new inode and
+ * initializes it. Returns new inode in case of success and an error code in
+ * case of failure.
+ */
+struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+			      umode_t mode)
+{
+	struct inode *inode;
+	struct ubifs_inode *ui;
+
+	inode = new_inode(c->vfs_sb);
+	ui = ubifs_inode(inode);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and
+	 * marking them dirty in file write path (see 'file_update_time()').
+	 * UBIFS has to fully control "clean <-> dirty" transitions of inodes
+	 * to make budgeting work.
+	 */
+	inode->i_flags |= S_NOCMTIME;
+
+	switch (mode & S_IFMT) {
+	case S_IFREG:
+		inode->i_op = &ubifs_file_inode_operations;
+		inode->i_fop = &ubifs_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op  = &ubifs_dir_inode_operations;
+		inode->i_fop = &ubifs_dir_operations;
+		inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;
+		break;
+	case S_IFLNK:
+		inode->i_op = &ubifs_symlink_inode_operations;
+		break;
+	case S_IFSOCK:
+	case S_IFIFO:
+	case S_IFBLK:
+	case S_IFCHR:
+		inode->i_op  = &ubifs_file_inode_operations;
+		break;
+	default:
+		BUG();
+	}
+
+	ui->flags = inherit_flags(dir, mode);
+	ubifs_set_inode_flags(inode);
+	if (S_ISREG(mode))
+		ui->compr_type = c->default_compr;
+	else
+		ui->compr_type = UBIFS_COMPR_NONE;
+	ui->synced_i_size = 0;
+
+	spin_lock(&c->cnt_lock);
+	/* Inode number overflow is currently not supported */
+	if (c->highest_inum >= INUM_WARN_WATERMARK) {
+		if (c->highest_inum >= INUM_WATERMARK) {
+			spin_unlock(&c->cnt_lock);
+			ubifs_err(c, "out of inode numbers");
+			iput(inode);
+			return ERR_PTR(-EINVAL);
+		}
+		ubifs_warn(c, "running out of inode numbers (current %lu, max %u)",
+			   (unsigned long)c->highest_inum, INUM_WATERMARK);
+	}
+
+	inode->i_ino = ++c->highest_inum;
+	/*
+	 * The creation sequence number remains with this inode for its
+	 * lifetime. All nodes for this inode have a greater sequence number,
+	 * and so it is possible to distinguish obsolete nodes belonging to a
+	 * previous incarnation of the same inode number - for example, for the
+	 * purpose of rebuilding the index.
+	 */
+	ui->creat_sqnum = ++c->max_sqnum;
+	spin_unlock(&c->cnt_lock);
+	return inode;
+}
+
+static int dbg_check_name(const struct ubifs_info *c,
+			  const struct ubifs_dent_node *dent,
+			  const struct qstr *nm)
+{
+	if (!dbg_is_chk_gen(c))
+		return 0;
+	if (le16_to_cpu(dent->nlen) != nm->len)
+		return -EINVAL;
+	if (memcmp(dent->name, nm->name, nm->len))
+		return -EINVAL;
+	return 0;
+}
+
+static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
+				   unsigned int flags)
+{
+	int err;
+	union ubifs_key key;
+	struct inode *inode = NULL;
+	struct ubifs_dent_node *dent;
+	struct ubifs_info *c = dir->i_sb->s_fs_info;
+
+	dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
+
+	if (dentry->d_name.len > UBIFS_MAX_NLEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
+	if (!dent)
+		return ERR_PTR(-ENOMEM);
+
+	dent_key_init(c, &key, dir->i_ino, &dentry->d_name);
+
+	err = ubifs_tnc_lookup_nm(c, &key, dent, &dentry->d_name);
+	if (err) {
+		if (err == -ENOENT) {
+			dbg_gen("not found");
+			goto done;
+		}
+		goto out;
+	}
+
+	if (dbg_check_name(c, dent, &dentry->d_name)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum));
+	if (IS_ERR(inode)) {
+		/*
+		 * This should not happen. Probably the file-system needs
+		 * checking.
+		 */
+		err = PTR_ERR(inode);
+		ubifs_err(c, "dead directory entry '%pd', error %d",
+			dentry, err);
+		ubifs_ro_mode(c, err);
+		goto out;
+	}
+
+done:
+	kfree(dent);
+	/*
+	 * Note, d_splice_alias() would be required instead if we supported
+	 * NFS.
+	 */
+	d_add(dentry, inode);
+	return NULL;
+
+out:
+	kfree(dent);
+	return ERR_PTR(err);
+}
+
+/**
+ * vfs_dent_type - get VFS directory entry type.
+ * @type: UBIFS directory entry type
+ *
+ * This function converts UBIFS directory entry type into VFS directory entry
+ * type.
+ */
+static unsigned int vfs_dent_type(uint8_t type)
+{
+	switch (type) {
+	case UBIFS_ITYPE_REG:
+		return DT_REG;
+	case UBIFS_ITYPE_DIR:
+		return DT_DIR;
+	case UBIFS_ITYPE_LNK:
+		return DT_LNK;
+	case UBIFS_ITYPE_BLK:
+		return DT_BLK;
+	case UBIFS_ITYPE_CHR:
+		return DT_CHR;
+	case UBIFS_ITYPE_FIFO:
+		return DT_FIFO;
+	case UBIFS_ITYPE_SOCK:
+		return DT_SOCK;
+	default:
+		BUG();
+	}
+	return 0;
+}
+
+/*
+ * The classical Unix view for directory is that it is a linear array of
+ * (name, inode number) entries. Linux/VFS assumes this model as well.
+ * Particularly, 'readdir()' call wants us to return a directory entry offset
+ * which later may be used to continue 'readdir()'ing the directory or to
+ * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this
+ * model because directory entries are identified by keys, which may collide.
+ *
+ * UBIFS uses directory entry hash value for directory offsets, so
+ * 'seekdir()'/'telldir()' may not always work because of possible key
+ * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work
+ * properly by means of saving full directory entry name in the private field
+ * of the file description object.
+ *
+ * This means that UBIFS cannot support NFS which requires full
+ * 'seekdir()'/'telldir()' support.
+ */
+static int ubifs_readdir(struct file *file, struct dir_context *ctx)
+{
+	int err = 0;
+	struct qstr nm;
+	union ubifs_key key;
+	struct ubifs_dent_node *dent;
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *dir = d_inode(dentry);
+	struct ubifs_info *c = dir->i_sb->s_fs_info;
+
+	dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos);
+
+	if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2)
+		/*
+		 * The directory was seek'ed to a senseless position or there
+		 * are no more entries.
+		 */
+		return 0;
+
+	if (file->f_version == 0) {
+		/*
+		 * The file was seek'ed, which means that @file->private_data
+		 * is now invalid. This may also be just the first
+		 * 'ubifs_readdir()' invocation, in which case
+		 * @file->private_data is NULL, and the below code is
+		 * basically a no-op.
+		 */
+		kfree(file->private_data);
+		file->private_data = NULL;
+	}
+
+	/*
+	 * 'generic_file_llseek()' unconditionally sets @file->f_version to
+	 * zero, and we use this for detecting whether the file was seek'ed.
+	 */
+	file->f_version = 1;
+
+	/* File positions 0 and 1 correspond to "." and ".." */
+	if (ctx->pos < 2) {
+		ubifs_assert(!file->private_data);
+		dir_emit_dots(file, ctx);
+
+		/* Find the first entry in TNC and save it */
+		lowest_dent_key(c, &key, dir->i_ino);
+		nm.name = NULL;
+		dent = ubifs_tnc_next_ent(c, &key, &nm);
+		if (IS_ERR(dent)) {
+			err = PTR_ERR(dent);
+			goto out;
+		}
+
+		ctx->pos = key_hash_flash(c, &dent->key);
+		file->private_data = dent;
+	}
+
+	dent = file->private_data;
+	if (!dent) {
+		/*
+		 * The directory was seek'ed to and is now readdir'ed.
+		 * Find the entry corresponding to @ctx->pos or the closest one.
+		 */
+		dent_key_init_hash(c, &key, dir->i_ino, ctx->pos);
+		nm.name = NULL;
+		dent = ubifs_tnc_next_ent(c, &key, &nm);
+		if (IS_ERR(dent)) {
+			err = PTR_ERR(dent);
+			goto out;
+		}
+		ctx->pos = key_hash_flash(c, &dent->key);
+		file->private_data = dent;
+	}
+
+	while (1) {
+		dbg_gen("feed '%s', ino %llu, new f_pos %#x",
+			dent->name, (unsigned long long)le64_to_cpu(dent->inum),
+			key_hash_flash(c, &dent->key));
+		ubifs_assert(le64_to_cpu(dent->ch.sqnum) >
+			     ubifs_inode(dir)->creat_sqnum);
+
+		nm.len = le16_to_cpu(dent->nlen);
+		dir_emit(ctx, dent->name, nm.len,
+			       le64_to_cpu(dent->inum),
+			       vfs_dent_type(dent->type));
+
+		/* Switch to the next entry */
+		key_read(c, &dent->key, &key);
+		nm.name = dent->name;
+		dent = ubifs_tnc_next_ent(c, &key, &nm);
+		if (IS_ERR(dent)) {
+			err = PTR_ERR(dent);
+			goto out;
+		}
+
+		kfree(file->private_data);
+		ctx->pos = key_hash_flash(c, &dent->key);
+		file->private_data = dent;
+		cond_resched();
+	}
+
+out:
+	kfree(file->private_data);
+	file->private_data = NULL;
+
+	if (err != -ENOENT)
+		ubifs_err(c, "cannot find next direntry, error %d", err);
+	else
+		/*
+		 * -ENOENT is a non-fatal error in this context, the TNC uses
+		 * it to indicate that the cursor moved past the current directory
+		 * and readdir() has to stop.
+		 */
+		err = 0;
+
+
+	/* 2 is a special value indicating that there are no more direntries */
+	ctx->pos = 2;
+	return err;
+}
+
+const struct inode_operations ubifs_dir_inode_operations = {
+	.lookup      = ubifs_lookup,
+};
+
+const struct file_operations ubifs_dir_operations = {
+	.iterate        = ubifs_readdir,
+};
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index b4eb76202b..abf8ef63c9 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -30,6 +30,7 @@
 
 #include <common.h>
 #include <init.h>
+#include <fs.h>
 #include <malloc.h>
 #include <linux/bug.h>
 #include <linux/log2.h>
@@ -49,8 +50,6 @@ struct vfsmount;
 struct super_block *ubifs_sb;
 LIST_HEAD(super_blocks);
 
-static struct inode *inodes_locked_down[INODE_LOCKED_MAX];
-
 int set_anon_super(struct super_block *s, void *data)
 {
 	return 0;
@@ -84,39 +83,6 @@ int ubifs_iput(struct inode *inode)
 	return 0;
 }
 
-/*
- * Lock (save) inode in inode array for readback after recovery
- */
-void iput(struct inode *inode)
-{
-	int i;
-	struct inode *ino;
-
-	/*
-	 * Search end of list
-	 */
-	for (i = 0; i < INODE_LOCKED_MAX; i++) {
-		if (inodes_locked_down[i] == NULL)
-			break;
-	}
-
-	if (i >= INODE_LOCKED_MAX) {
-		dbg_gen("Error, can't lock (save) more inodes while recovery!!!");
-		return;
-	}
-
-	/*
-	 * Allocate and use new inode
-	 */
-	ino = (struct inode *)kzalloc(sizeof(struct ubifs_inode), 0);
-	memcpy(ino, inode, sizeof(struct ubifs_inode));
-
-	/*
-	 * Finally save inode in array
-	 */
-	inodes_locked_down[i] = ino;
-}
-
 /* from fs/inode.c */
 /**
  * clear_nlink - directly zero an inode's link count
@@ -231,6 +197,9 @@ static int validate_inode(struct ubifs_info *c, const struct inode *inode)
 	return err;
 }
 
+const struct inode_operations ubifs_file_inode_operations;
+const struct file_operations ubifs_file_operations;
+
 struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
 {
 	int err;
@@ -239,35 +208,9 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
 	struct ubifs_info *c = sb->s_fs_info;
 	struct inode *inode;
 	struct ubifs_inode *ui;
-#ifdef __BAREBOX__
-	int i;
-#endif
 
 	dbg_gen("inode %lu", inum);
 
-#ifdef __BAREBOX__
-	/*
-	 * U-Boot special handling of locked down inodes via recovery
-	 * e.g. ubifs_recover_size()
-	 */
-	for (i = 0; i < INODE_LOCKED_MAX; i++) {
-		/*
-		 * Exit on last entry (NULL), inode not found in list
-		 */
-		if (inodes_locked_down[i] == NULL)
-			break;
-
-		if (inodes_locked_down[i]->i_ino == inum) {
-			/*
-			 * We found the locked down inode in our array,
-			 * so just return this pointer instead of creating
-			 * a new one.
-			 */
-			return inodes_locked_down[i];
-		}
-	}
-#endif
-
 	inode = iget_locked(sb, inum);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
@@ -315,10 +258,8 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
 	if (err)
 		goto out_invalid;
 
-#ifndef __BAREBOX__
 	switch (inode->i_mode & S_IFMT) {
 	case S_IFREG:
-		inode->i_mapping->a_ops = &ubifs_file_address_operations;
 		inode->i_op = &ubifs_file_inode_operations;
 		inode->i_fop = &ubifs_file_operations;
 		if (ui->xattr) {
@@ -343,7 +284,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
 		}
 		break;
 	case S_IFLNK:
-		inode->i_op = &ubifs_symlink_inode_operations;
+		inode->i_op = &simple_symlink_inode_operations;
 		if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
 			err = 12;
 			goto out_invalid;
@@ -357,60 +298,10 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
 		((char *)ui->data)[ui->data_len] = '\0';
 		inode->i_link = ui->data;
 		break;
-	case S_IFBLK:
-	case S_IFCHR:
-	{
-		dev_t rdev;
-		union ubifs_dev_desc *dev;
-
-		ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);
-		if (!ui->data) {
-			err = -ENOMEM;
-			goto out_ino;
-		}
-
-		dev = (union ubifs_dev_desc *)ino->data;
-		if (ui->data_len == sizeof(dev->new))
-			rdev = new_decode_dev(le32_to_cpu(dev->new));
-		else if (ui->data_len == sizeof(dev->huge))
-			rdev = huge_decode_dev(le64_to_cpu(dev->huge));
-		else {
-			err = 13;
-			goto out_invalid;
-		}
-		memcpy(ui->data, ino->data, ui->data_len);
-		inode->i_op = &ubifs_file_inode_operations;
-		init_special_inode(inode, inode->i_mode, rdev);
-		break;
-	}
-	case S_IFSOCK:
-	case S_IFIFO:
-		inode->i_op = &ubifs_file_inode_operations;
-		init_special_inode(inode, inode->i_mode, 0);
-		if (ui->data_len != 0) {
-			err = 14;
-			goto out_invalid;
-		}
-		break;
 	default:
 		err = 15;
 		goto out_invalid;
 	}
-#else
-	if ((inode->i_mode & S_IFMT) == S_IFLNK) {
-		if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {
-			err = 12;
-			goto out_invalid;
-		}
-		ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);
-		if (!ui->data) {
-			err = -ENOMEM;
-			goto out_ino;
-		}
-		memcpy(ui->data, ino->data, ui->data_len);
-		((char *)ui->data)[ui->data_len] = '\0';
-	}
-#endif
 
 	kfree(ino);
 #ifndef __BAREBOX__
@@ -447,22 +338,15 @@ static struct inode *ubifs_alloc_inode(struct super_block *sb)
 	return &ui->vfs_inode;
 };
 
-#ifndef __BAREBOX__
-static void ubifs_i_callback(struct rcu_head *head)
-{
-	struct inode *inode = container_of(head, struct inode, i_rcu);
-	struct ubifs_inode *ui = ubifs_inode(inode);
-	kmem_cache_free(ubifs_inode_slab, ui);
-}
-
 static void ubifs_destroy_inode(struct inode *inode)
 {
 	struct ubifs_inode *ui = ubifs_inode(inode);
 
 	kfree(ui->data);
-	call_rcu(&inode->i_rcu, ubifs_i_callback);
+	kfree(ui);
 }
 
+#ifndef __BAREBOX__
 /*
  * Note, Linux write-back code calls this without 'i_mutex'.
  */
@@ -1330,15 +1214,9 @@ static int mount_ubifs(struct ubifs_info *c)
 	long long x, y;
 	size_t sz;
 
-	c->ro_mount = !!(c->vfs_sb->s_flags & MS_RDONLY);
+	c->ro_mount = true;
 	/* Suppress error messages while probing if MS_SILENT is set */
 	c->probing = !!(c->vfs_sb->s_flags & MS_SILENT);
-#ifdef __BAREBOX__
-	if (!c->ro_mount) {
-		printf("UBIFS: only ro mode in Barebox allowed.\n");
-		return -EACCES;
-	}
-#endif
 
 	err = init_constants_early(c);
 	if (err)
@@ -2099,8 +1977,8 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
 
 const struct super_operations ubifs_super_operations = {
 	.alloc_inode   = ubifs_alloc_inode,
-#ifndef __BAREBOX__
 	.destroy_inode = ubifs_destroy_inode,
+#ifndef __BAREBOX__
 	.put_super     = ubifs_put_super,
 	.write_inode   = ubifs_write_inode,
 	.evict_inode   = ubifs_evict_inode,
@@ -2298,15 +2176,11 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
 		goto out_umount;
 	}
 
-#ifndef __BAREBOX__
 	sb->s_root = d_make_root(root);
 	if (!sb->s_root) {
 		err = -ENOMEM;
 		goto out_umount;
 	}
-#else
-	sb->s_root = NULL;
-#endif
 
 	mutex_unlock(&c->umount_mutex);
 	return 0;
@@ -2680,13 +2554,14 @@ MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter");
 MODULE_DESCRIPTION("UBIFS - UBI File System");
 #endif
 
-struct super_block *ubifs_get_super(struct device_d *dev, struct ubi_volume_desc *ubi, int silent)
+int ubifs_get_super(struct device_d *dev, struct ubi_volume_desc *ubi, int silent)
 {
+	struct fs_device_d *fsdev = dev_to_fs_device(dev);
 	struct super_block *sb;
 	struct ubifs_info *c;
 	int err;
 
-	sb = alloc_super(NULL, MS_RDONLY | MS_ACTIVE | MS_NOATIME);
+	sb = &fsdev->sb;
 	c = alloc_ubifs_info(ubi);
 
 	c->dev = dev;
@@ -2712,9 +2587,9 @@ struct super_block *ubifs_get_super(struct device_d *dev, struct ubi_volume_desc
 		goto out;
 	}
 
-	return sb;
+	return 0;
 out:
 	kfree(c);
 	kfree(sb);
-	return ERR_PTR(err);
+	return err;
 }
diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c
index a525b044b8..f9b4f4babc 100644
--- a/fs/ubifs/ubifs.c
+++ b/fs/ubifs/ubifs.c
@@ -269,161 +269,6 @@ int __init ubifs_compressors_init(void)
 	return 0;
 }
 
-/*
- * ubifsls...
- */
-
-static int ubifs_finddir(struct super_block *sb, char *dirname,
-			 unsigned long root_inum, unsigned long *inum)
-{
-	int err;
-	struct qstr nm;
-	union ubifs_key key;
-	struct ubifs_dent_node *dent;
-	struct ubifs_info *c;
-	struct file *file;
-	struct dentry *dentry;
-	struct inode *dir;
-	int ret = 0;
-
-	file = kzalloc(sizeof(struct file), 0);
-	dentry = kzalloc(sizeof(struct dentry), 0);
-	dir = kzalloc(sizeof(struct inode), 0);
-	if (!file || !dentry || !dir) {
-		printf("%s: Error, no memory for malloc!\n", __func__);
-		err = -ENOMEM;
-		goto out;
-	}
-
-	dir->i_sb = sb;
-	file->f_path.dentry = dentry;
-	file->f_path.dentry->d_parent = dentry;
-	file->f_path.dentry->d_inode = dir;
-	file->f_path.dentry->d_inode->i_ino = root_inum;
-	c = sb->s_fs_info;
-
-	dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
-
-	/* Find the first entry in TNC and save it */
-	lowest_dent_key(c, &key, dir->i_ino);
-	nm.name = NULL;
-	dent = ubifs_tnc_next_ent(c, &key, &nm);
-	if (IS_ERR(dent)) {
-		err = PTR_ERR(dent);
-		goto out;
-	}
-
-	file->f_pos = key_hash_flash(c, &dent->key);
-	file->private_data = dent;
-
-	while (1) {
-		dbg_gen("feed '%s', ino %llu, new f_pos %#x",
-			dent->name, (unsigned long long)le64_to_cpu(dent->inum),
-			key_hash_flash(c, &dent->key));
-		ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum);
-
-		nm.len = le16_to_cpu(dent->nlen);
-		if ((strncmp(dirname, (char *)dent->name, nm.len) == 0) &&
-		    (strlen(dirname) == nm.len)) {
-			*inum = le64_to_cpu(dent->inum);
-			ret = 1;
-			goto out_free;
-		}
-
-		/* Switch to the next entry */
-		key_read(c, &dent->key, &key);
-		nm.name = (char *)dent->name;
-		dent = ubifs_tnc_next_ent(c, &key, &nm);
-		if (IS_ERR(dent)) {
-			err = PTR_ERR(dent);
-			goto out;
-		}
-
-		kfree(file->private_data);
-		file->f_pos = key_hash_flash(c, &dent->key);
-		file->private_data = dent;
-		cond_resched();
-	}
-
-out:
-	if (err != -ENOENT)
-		dbg_gen("cannot find next direntry, error %d", err);
-
-out_free:
-	if (file->private_data)
-		kfree(file->private_data);
-	if (file)
-		free(file);
-	if (dentry)
-		free(dentry);
-	if (dir)
-		free(dir);
-
-	return ret;
-}
-
-static unsigned long ubifs_findfile(struct super_block *sb, const char *filename)
-{
-	int ret;
-	char *next;
-	char fpath[128];
-	char *name = fpath;
-	unsigned long root_inum = 1;
-	unsigned long inum;
-
-	strcpy(fpath, filename);
-
-	/* Remove all leading slashes */
-	while (*name == '/')
-		name++;
-
-	/*
-	 * Handle root-direcoty ('/')
-	 */
-	inum = root_inum;
-	if (!name || *name == '\0')
-		return inum;
-
-	for (;;) {
-		struct inode *inode;
-		struct ubifs_inode *ui;
-
-		/* Extract the actual part from the pathname.  */
-		next = strchr(name, '/');
-		if (next) {
-			/* Remove all leading slashes.  */
-			while (*next == '/')
-				*(next++) = '\0';
-		}
-
-		ret = ubifs_finddir(sb, name, root_inum, &inum);
-		if (!ret)
-			return 0;
-		inode = ubifs_iget(sb, inum);
-
-		if (IS_ERR(inode))
-			return 0;
-		ui = ubifs_inode(inode);
-
-		/*
-		 * Check if directory with this name exists
-		 */
-
-		/* Found the node!  */
-		if (!next || *next == '\0')
-			return inum;
-
-		root_inum = inum;
-		name = next;
-	}
-
-	return 0;
-}
-
-/*
- * ubifsload...
- */
-
 /* file.c */
 
 static inline void *kmap(struct page *page)
@@ -487,18 +332,8 @@ struct ubifs_file {
 
 static int ubifs_open(struct device_d *dev, FILE *file, const char *filename)
 {
-	struct ubifs_priv *priv = dev->priv;
-	struct inode *inode;
+	struct inode *inode = file->f_inode;
 	struct ubifs_file *uf;
-	unsigned long inum;
-
-	inum = ubifs_findfile(priv->sb, filename);
-	if (!inum)
-		return -ENOENT;
-
-	inode = ubifs_iget(priv->sb, inum);
-	if (IS_ERR(inode))
-		return -ENOENT;
 
 	uf = xzalloc(sizeof(*uf));
 
@@ -516,9 +351,6 @@ static int ubifs_open(struct device_d *dev, FILE *file, const char *filename)
 static int ubifs_close(struct device_d *dev, FILE *f)
 {
 	struct ubifs_file *uf = f->priv;
-	struct inode *inode = uf->inode;
-
-	ubifs_iput(inode);
 
 	free(uf->buf);
 	free(uf->dn);
@@ -596,163 +428,6 @@ static loff_t ubifs_lseek(struct device_d *dev, FILE *f, loff_t pos)
 	return pos;
 }
 
-struct ubifs_dir {
-	struct file file;
-	struct dentry dentry;
-	struct inode inode;
-	DIR dir;
-	union ubifs_key key;
-	struct ubifs_dent_node *dent;
-	struct ubifs_priv *priv;
-	struct qstr nm;
-};
-
-static DIR *ubifs_opendir(struct device_d *dev, const char *pathname)
-{
-	struct ubifs_priv *priv = dev->priv;
-	struct ubifs_dir *dir;
-	struct file *file;
-	struct dentry *dentry;
-	struct inode *inode;
-	unsigned long inum;
-	struct ubifs_info *c = priv->sb->s_fs_info;
-
-	inum = ubifs_findfile(priv->sb, pathname);
-	if (!inum)
-		return NULL;
-
-	inode = ubifs_iget(priv->sb, inum);
-	if (IS_ERR(inode))
-		return NULL;
-
-	ubifs_iput(inode);
-
-	dir = xzalloc(sizeof(*dir));
-
-	dir->priv = priv;
-
-	file = &dir->file;
-	dentry = &dir->dentry;
-	inode = &dir->inode;
-
-	inode->i_sb = priv->sb;
-	file->f_path.dentry = dentry;
-	file->f_path.dentry->d_parent = dentry;
-	file->f_path.dentry->d_inode = inode;
-	file->f_path.dentry->d_inode->i_ino = inum;
-	file->f_pos = 1;
-
-	/* Find the first entry in TNC and save it */
-	lowest_dent_key(c, &dir->key, inode->i_ino);
-
-	return &dir->dir;
-}
-
-static struct dirent *ubifs_readdir(struct device_d *dev, DIR *_dir)
-{
-	struct ubifs_dir *dir = container_of(_dir, struct ubifs_dir, dir);
-	struct ubifs_info *c = dir->priv->sb->s_fs_info;
-	struct ubifs_dent_node *dent;
-	struct qstr *nm = &dir->nm;
-	struct file *file = &dir->file;
-
-	dent = ubifs_tnc_next_ent(c, &dir->key, nm);
-	if (IS_ERR(dent))
-		return NULL;
-
-	debug("feed '%s', ino %llu, new f_pos %#x\n",
-		dent->name, (unsigned long long)le64_to_cpu(dent->inum),
-		key_hash_flash(c, &dent->key));
-
-	ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(&dir->inode)->creat_sqnum);
-
-	key_read(c, &dent->key, &dir->key);
-	file->f_pos = key_hash_flash(c, &dent->key);
-	file->private_data = dent;
-
-	nm->len = le16_to_cpu(dent->nlen);
-	nm->name = dent->name;
-
-	strcpy(_dir->d.d_name, dent->name);
-
-	free(dir->dent);
-	dir->dent = dent;
-
-	return &_dir->d;
-}
-
-static int ubifs_closedir(struct device_d *dev, DIR *_dir)
-{
-	struct ubifs_dir *dir = container_of(_dir, struct ubifs_dir, dir);
-
-	free(dir->dent);
-	free(dir);
-
-	return 0;
-}
-
-static int ubifs_stat(struct device_d *dev, const char *filename, struct stat *s)
-{
-	struct ubifs_priv *priv = dev->priv;
-	struct inode *inode;
-	unsigned long inum;
-
-	inum = ubifs_findfile(priv->sb, filename);
-	if (!inum)
-		return -ENOENT;
-
-	inode = ubifs_iget(priv->sb, inum);
-	if (IS_ERR(inode))
-		return -ENOENT;
-
-	s->st_size = inode->i_size;
-	s->st_mode = inode->i_mode;
-
-	ubifs_iput(inode);
-
-	return 0;
-}
-
-static char *ubifs_symlink(struct inode *inode)
-{
-	struct ubifs_inode *ui;
-	char *symlink;
-
-	ui = ubifs_inode(inode);
-	symlink = malloc(ui->data_len + 1);
-
-	memcpy(symlink, ui->data, ui->data_len);
-	symlink[ui->data_len] = '\0';
-
-	return symlink;
-}
-
-static int ubifs_readlink(struct device_d *dev, const char *pathname, char *buf,
-			size_t bufsz)
-{
-	struct ubifs_priv *priv = dev->priv;
-	struct inode *inode;
-	char *symlink;
-	int len;
-	unsigned long inum;
-
-	inum = ubifs_findfile(priv->sb, pathname);
-	if (!inum)
-		return -ENOENT;
-
-	inode = ubifs_iget(priv->sb, inum);
-	if (!inode)
-		return -ENOENT;
-
-	symlink = ubifs_symlink(inode);
-
-	len = min(bufsz, strlen(symlink));
-	memcpy(buf, symlink, len);
-	free(symlink);
-
-	return 0;
-}
-
 void ubifs_set_rootarg(struct ubifs_priv *priv, struct fs_device_d *fsdev)
 {
 	struct ubi_volume_info vi = {};
@@ -795,11 +470,11 @@ static int ubifs_probe(struct device_d *dev)
 		goto err_free;
 	}
 
-	priv->sb = ubifs_get_super(dev, priv->ubi, 0);
-	if (IS_ERR(priv->sb)) {
-		ret = PTR_ERR(priv->sb);
+	ret = ubifs_get_super(dev, priv->ubi, 0);
+	if (ret)
 		goto err;
-	}
+
+	priv->sb = &fsdev->sb;
 
 	ubifs_set_rootarg(priv, fsdev);
 
@@ -821,7 +496,6 @@ static void ubifs_remove(struct device_d *dev)
 	ubi_close_volume(priv->ubi);
 
 	free(c);
-	free(sb);
 
 	free(priv);
 }
@@ -831,11 +505,6 @@ static struct fs_driver_d ubifs_driver = {
 	.close     = ubifs_close,
 	.read      = ubifs_read,
 	.lseek     = ubifs_lseek,
-	.opendir   = ubifs_opendir,
-	.readdir   = ubifs_readdir,
-	.closedir  = ubifs_closedir,
-	.stat      = ubifs_stat,
-	.readlink  = ubifs_readlink,
 	.type = filetype_ubifs,
 	.flags     = 0,
 	.drv = {
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 22b24a1161..4c4c927de9 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -36,6 +36,7 @@
 #include <lzo.h>
 #include <crc.h>
 #include <linux/fs.h>
+#include <linux/stat.h>
 #include <linux/sched.h>
 #include <linux/ctype.h>
 #include <linux/time.h>
@@ -49,11 +50,9 @@
 
 #define crc32(seed, data, length)  crc32_no_comp(seed, (unsigned char const *)data, length)
 
-struct dentry;
 struct file;
 struct iattr;
 struct kstat;
-struct vfsmount;
 
 extern struct super_block *ubifs_sb;
 
@@ -72,8 +71,6 @@ struct page {
 	struct inode *inode;
 };
 
-void iput(struct inode *inode);
-
 struct kmem_cache { int sz; };
 
 struct kmem_cache *get_mem(int element_sz);
@@ -1901,7 +1898,7 @@ int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
 
 #ifdef __BAREBOX__
 void ubifs_umount(struct ubifs_info *c);
-struct super_block *ubifs_get_super(struct device_d *dev, struct ubi_volume_desc *ubi, int silent);
+int ubifs_get_super(struct device_d *dev, struct ubi_volume_desc *ubi, int silent);
 #endif
 
 #endif /* !__UBIFS_H__ */
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 06/10] fs: nfs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (4 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 05/10] fs: ubifs: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 07/10] fs: tftp: " Sascha Hauer
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig |   1 -
 fs/nfs.c   | 542 +++++++++++++++++++++++------------------------------
 2 files changed, 234 insertions(+), 309 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 195a5cb060..4d0e2787af 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -52,7 +52,6 @@ config FS_OMAP4_USBBOOT
 
 config FS_NFS
 	depends on NET
-	select FS_LEGACY
 	bool
 	prompt "nfs support"
 
diff --git a/fs/nfs.c b/fs/nfs.c
index 75cd127eeb..eb5db344db 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -20,6 +20,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+#define pr_fmt(fmt) "NFS: " fmt
 
 #include <common.h>
 #include <net.h>
@@ -127,6 +128,11 @@ struct rpc_reply {
 #define NFS_TIMEOUT	(2 * SECOND)
 #define NFS_MAX_RESEND	5
 
+struct nfs_fh {
+	unsigned short size;
+	unsigned char data[NFS3_FHSIZE];
+};
+
 struct nfs_priv {
 	struct net_connection *con;
 	IPaddr_t server;
@@ -136,18 +142,34 @@ struct nfs_priv {
 	uint16_t nfs_port;
 	unsigned manual_nfs_port:1;
 	uint32_t rpc_id;
-	uint32_t rootfh_len;
-	char rootfh[NFS3_FHSIZE];
+	struct nfs_fh rootfh;
 };
 
 struct file_priv {
 	struct kfifo *fifo;
 	void *buf;
-	uint32_t filefh_len;
-	char filefh[NFS3_FHSIZE];
+	struct nfs_priv *npriv;
+	struct nfs_fh fh;
+};
+
+struct nfs_inode {
+	struct inode inode;
+	struct nfs_fh fh;
 	struct nfs_priv *npriv;
 };
 
+static inline struct nfs_inode *nfsi(struct inode *inode)
+{
+	return container_of(inode, struct nfs_inode, inode);
+}
+
+static void nfs_set_fh(struct inode *inode, struct nfs_fh *fh)
+{
+	struct nfs_inode *ninode = nfsi(inode);
+
+	ninode->fh = *fh;
+}
+
 static uint64_t nfs_timer_start;
 
 static int nfs_state;
@@ -234,17 +256,14 @@ struct xdr_stream {
 #define XDR_QUADLEN(l)		(((l) + 3) >> 2)
 
 struct nfs_dir {
-	DIR dir;
-
 	/*
 	 * stream points to the next entry3 in the reply member of READDIR3res
 	 * (if any, to the end indicator otherwise).
 	 */
 	struct xdr_stream stream;
-	struct dirent ent;
-	struct file_priv *priv;
 	uint64_t cookie;
 	char cookieverf[NFS3_COOKIEVERFSIZE];
+	struct nfs_fh fh;
 };
 
 static void xdr_init(struct xdr_stream *stream, void *buf, int len)
@@ -300,11 +319,11 @@ static int decode_filename(struct xdr_stream *xdr, char *name, u32 *length)
 	return 0;
 
 out_nametoolong:
-	printf("%s: returned a too long filename: %u\n", __func__, count);
+	pr_err("%s: returned a too long filename: %u\n", __func__, count);
 	return -ENAMETOOLONG;
 
 out_overflow:
-	printf("%s: premature end of packet\n", __func__);
+	pr_err("%s: premature end of packet\n", __func__);
 	return -EIO;
 }
 
@@ -486,16 +505,16 @@ static uint32_t *nfs_add_uint64(uint32_t *p, uint64_t val)
 	return p + 2;
 }
 
-static uint32_t *nfs_add_fh3(uint32_t *p, unsigned fh_len, const char *fh)
+static uint32_t *nfs_add_fh3(uint32_t *p, struct nfs_fh *fh)
 {
-	*p++ = hton32(fh_len);
+	*p++ = hton32(fh->size);
 
 	/* zero padding */
-	if (fh_len & 3)
-		p[fh_len / 4] = 0;
+	if (fh->size & 3)
+		p[fh->size / 4] = 0;
 
-	memcpy(p, fh, fh_len);
-	p += DIV_ROUND_UP(fh_len, 4);
+	memcpy(p, fh->data, fh->size);
+	p += DIV_ROUND_UP(fh->size, 4);
 	return p;
 }
 
@@ -532,33 +551,36 @@ static const struct {
 	{ 0x00800, S_ISUID },
 };
 
-static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s)
+static int nfs_fattr3_to_stat(uint32_t *p, struct inode *inode)
 {
 	uint32_t mode;
 	size_t i;
 
+	if (!inode)
+		return 0;
+
 	/* offsetof(struct fattr3, type) = 0 */
 	switch (ntoh32(net_read_uint32(p + 0))) {
 	case NF3REG:
-		s->st_mode = S_IFREG;
+		inode->i_mode = S_IFREG;
 		break;
 	case NF3DIR:
-		s->st_mode = S_IFDIR;
+		inode->i_mode = S_IFDIR;
 		break;
 	case NF3BLK:
-		s->st_mode = S_IFBLK;
+		inode->i_mode = S_IFBLK;
 		break;
 	case NF3CHR:
-		s->st_mode = S_IFCHR;
+		inode->i_mode = S_IFCHR;
 		break;
 	case NF3LNK:
-		s->st_mode = S_IFLNK;
+		inode->i_mode = S_IFLNK;
 		break;
 	case NF3SOCK:
-		s->st_mode = S_IFSOCK;
+		inode->i_mode = S_IFSOCK;
 		break;
 	case NF3FIFO:
-		s->st_mode = S_IFIFO;
+		inode->i_mode = S_IFIFO;
 		break;
 	default:
 		printf("%s: invalid mode %x\n",
@@ -570,18 +592,17 @@ static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s)
 	mode = ntoh32(net_read_uint32(p + 1));
 	for (i = 0; i < ARRAY_SIZE(nfs3_mode_bits); ++i) {
 		if (mode & nfs3_mode_bits[i].nfsmode)
-			s->st_mode |= nfs3_mode_bits[i].statmode;
+			inode->i_mode |= nfs3_mode_bits[i].statmode;
 	}
 
 	/* offsetof(struct fattr3, size) = 20 */
-	s->st_size = ntoh64(net_read_uint64(p + 5));
+	inode->i_size = ntoh64(net_read_uint64(p + 5));
 
 	return 0;
 }
 
-static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s)
+static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct inode *inode)
 {
-	struct stat dummy;
 	/*
 	 * union post_op_attr switch (bool attributes_follow) {
 	 * case TRUE:
@@ -592,11 +613,8 @@ static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s)
 	 */
 
 	if (ntoh32(net_read_uint32(p++))) {
-		nfs_fattr3_to_stat(p, s ? *s : &dummy);
+		nfs_fattr3_to_stat(p, inode);
 		p += 21;
-	} else if (s) {
-		/* no attributes available */
-		*s = NULL;
 	}
 
 	return p;
@@ -635,14 +653,14 @@ static int nfs_mount_req(struct nfs_priv *npriv)
 
 	p = nfs_packet + sizeof(struct rpc_reply) + 4;
 
-	npriv->rootfh_len = ntoh32(net_read_uint32(p++));
-	if (npriv->rootfh_len > NFS3_FHSIZE) {
+	npriv->rootfh.size = ntoh32(net_read_uint32(p++));
+	if (npriv->rootfh.size > NFS3_FHSIZE) {
 		printf("%s: file handle too big: %lu\n", __func__,
-				(unsigned long)npriv->rootfh_len);
+				(unsigned long)npriv->rootfh.size);
 		return -EIO;
 	}
-	memcpy(npriv->rootfh, p, npriv->rootfh_len);
-	p += DIV_ROUND_UP(npriv->rootfh_len, 4);
+	memcpy(npriv->rootfh.data, p, npriv->rootfh.size);
+	p += DIV_ROUND_UP(npriv->rootfh.size, 4);
 
 	return 0;
 }
@@ -674,9 +692,10 @@ static void nfs_umount_req(struct nfs_priv *npriv)
  *
  * *s is set to NULL if LOOKUP3resok doesn't contain obj_attributes.
  */
-static int nfs_lookup_req(struct file_priv *priv,
-		uint32_t filename_len, const char *filename, struct stat **s)
+static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh,
+			  const char *filename, struct inode *inode)
 {
+	struct nfs_inode *ninode = nfsi(inode);
 	uint32_t data[1024];
 	uint32_t *p;
 	int len;
@@ -709,73 +728,29 @@ static int nfs_lookup_req(struct file_priv *priv,
 	p = rpc_add_credentials(p);
 
 	/* what.dir */
-	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_fh3(p, fh);
 
 	/* what.name */
-	p = nfs_add_filename(p, filename_len, filename);
+	p = nfs_add_filename(p, strlen(filename), filename);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len);
+	ret = rpc_req(npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len);
 	if (ret)
 		return ret;
 
 	p = nfs_packet + sizeof(struct rpc_reply) + 4;
 
-	priv->filefh_len = ntoh32(net_read_uint32(p++));
-	if (priv->filefh_len > NFS3_FHSIZE) {
-		debug("%s: file handle too big: %lu\n", __func__,
-				(unsigned long)priv->filefh_len);
+	ninode->fh.size = ntoh32(net_read_uint32(p++));
+	if (ninode->fh.size > NFS3_FHSIZE) {
+		debug("%s: file handle too big: %u\n", __func__,
+		      ninode->fh.size);
 		return -EIO;
 	}
-	memcpy(priv->filefh, p, priv->filefh_len);
-	p += DIV_ROUND_UP(priv->filefh_len, 4);
-
-	if (s)
-		nfs_read_post_op_attr(p, s);
-
-	return 0;
-}
-
-static int nfs_attr_req(struct file_priv *priv, struct stat *s)
-{
-	uint32_t data[1024];
-	uint32_t *p;
-	int len;
-	int ret;
-
-	/*
-	 * struct GETATTR3args {
-	 * 	nfs_fh3 object;
-	 * }
-	 *
-	 * struct GETATTR3resok {
-	 * 	fattr3 obj_attributes;
-	 * };
-	 *
-	 * union GETATTR3res switch (nfsstat3 status) {
-	 * case NFS3_OK:
-	 * 	GETATTR3resok resok;
-	 * default:
-	 * 	void;
-	 * }
-	 */
-
-	p = &(data[0]);
-	p = rpc_add_credentials(p);
-
-	/* object */
-	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
-
-	len = p - &(data[0]);
-
-	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_GETATTR, data, len);
-	if (ret)
-		return ret;
+	memcpy(ninode->fh.data, p, ninode->fh.size);
+	p += DIV_ROUND_UP(ninode->fh.size, 4);
 
-	p = nfs_packet + sizeof(struct rpc_reply) + 4;
-
-	nfs_fattr3_to_stat(p, s);
+	nfs_read_post_op_attr(p, inode);
 
 	return 0;
 }
@@ -784,7 +759,7 @@ static int nfs_attr_req(struct file_priv *priv, struct stat *s)
  * returns with dir->stream pointing to the first entry
  * of dirlist3 res.resok.reply
  */
-static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir)
+static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir)
 {
 	uint32_t data[1024];
 	uint32_t *p;
@@ -833,7 +808,7 @@ static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir)
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_fh3(p, &dir->fh);
 	p = nfs_add_uint64(p, dir->cookie);
 
 	memcpy(p, dir->cookieverf, NFS3_COOKIEVERFSIZE);
@@ -841,7 +816,7 @@ static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir)
 
 	p = nfs_add_uint32(p, 1024); /* count */
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data);
+	ret = rpc_req(npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data);
 	if (ret)
 		return NULL;
 
@@ -909,7 +884,7 @@ static int nfs_read_req(struct file_priv *priv, uint64_t offset,
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_fh3(p, &priv->fh);
 	p = nfs_add_uint64(p, offset);
 	p = nfs_add_uint32(p, readlen);
 
@@ -953,82 +928,11 @@ static void nfs_handler(void *ctx, char *packet, unsigned len)
 	nfs_len = len;
 }
 
-static int nfs_create(struct device_d *dev, const char *pathname, mode_t mode)
-{
-	return -ENOSYS;
-}
-
-static int nfs_unlink(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
-static int nfs_mkdir(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
-static int nfs_rmdir(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
 static int nfs_truncate(struct device_d *dev, FILE *f, ulong size)
 {
 	return -ENOSYS;
 }
 
-static struct file_priv *nfs_do_open(struct device_d *dev,
-		const char *filename, struct stat **s)
-{
-	struct file_priv *priv;
-	struct nfs_priv *npriv = dev->priv;
-	int ret;
-	const char *tok;
-
-	debug("%s: filename = %s\n", __func__, filename);
-	priv = xzalloc(sizeof(*priv));
-
-	priv->npriv = npriv;
-
-	if (!*filename) {
-		priv->filefh_len = npriv->rootfh_len;
-		memcpy(priv->filefh, npriv->rootfh, npriv->rootfh_len);
-		return priv;
-	}
-
-	filename++;
-
-	priv->filefh_len = npriv->rootfh_len;
-	memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE);
-
-	while (*filename) {
-		size_t flen;
-
-		tok = strchr(filename, '/');
-		if (tok)
-			flen = tok - filename;
-		else
-			flen = strlen(filename);
-
-		ret = nfs_lookup_req(priv, flen, filename, s);
-		if (ret)
-			goto out;
-
-		if (tok)
-			filename += flen + 1;
-		else
-			break;
-	}
-
-	return priv;
-
-out:
-	free(priv);
-
-	return ERR_PTR(ret);
-}
-
 static void nfs_do_close(struct file_priv *priv)
 {
 	if (priv->fifo)
@@ -1037,34 +941,8 @@ static void nfs_do_close(struct file_priv *priv)
 	free(priv);
 }
 
-static struct file_priv *nfs_do_stat(struct device_d *dev,
-		const char *filename, struct stat *s)
-{
-	struct file_priv *priv;
-	int ret;
-	struct stat **sptr = &s;
-
-	debug("%s: filename = %s\n", __func__, filename);
-	priv = nfs_do_open(dev, filename, sptr);
-	if (IS_ERR(priv))
-		return priv;
-
-	if (!*sptr) {
-		/*
-		 * The nfs server didn't provide obj_attributes in the lookup
-		 * reply, so ask for them explicitly.
-		 */
-		ret = nfs_attr_req(priv, s);
-		if (ret) {
-			nfs_do_close(priv);
-			return ERR_PTR(ret);
-		}
-	}
-
-	return priv;
-}
-
-static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
+static int nfs_readlink_req(struct nfs_priv *npriv, struct nfs_fh *fh,
+			    char **target)
 {
 	uint32_t data[1024];
 	uint32_t *p;
@@ -1095,11 +973,11 @@ static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
 	p = &(data[0]);
 	p = rpc_add_credentials(p);
 
-	p = nfs_add_fh3(p, priv->filefh_len, priv->filefh);
+	p = nfs_add_fh3(p, fh);
 
 	len = p - &(data[0]);
 
-	ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READLINK, data, len);
+	ret = rpc_req(npriv, PROG_NFS, NFSPROC3_READLINK, data, len);
 	if (ret)
 		return ret;
 
@@ -1110,44 +988,37 @@ static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
 	len = ntoh32(net_read_uint32(p)); /* new path length */
 	p++;
 
-	if (len > size)
-		return -ENOMEM;
-
-	memcpy(buf, p, len);
+	*target = xzalloc(len + 1);
+	memcpy(*target, p, len);
 
 	return 0;
 }
 
-static int nfs_readlink(struct device_d *dev, const char *filename,
-			char *realname, size_t size)
+static const char *nfs_get_link(struct dentry *dentry, struct inode *inode)
 {
-	struct file_priv *priv;
+	struct nfs_inode *ninode = nfsi(inode);
+	struct nfs_priv *npriv = ninode->npriv;
 	int ret;
 
-	priv = nfs_do_open(dev, filename, NULL);
-	if (IS_ERR(priv))
-		return PTR_ERR(priv);
-
-	ret = nfs_readlink_req(priv, realname, size);
-	if (ret) {
-		nfs_do_close(priv);
-		return ret;
-	}
+	ret = nfs_readlink_req(npriv, &ninode->fh, &inode->i_link);
+	if (ret)
+		return ERR_PTR(ret);
 
-	return 0;
+	return inode->i_link;
 }
 
 static int nfs_open(struct device_d *dev, FILE *file, const char *filename)
 {
+	struct inode *inode = file->f_inode;
+	struct nfs_inode *ninode = nfsi(inode);
+	struct nfs_priv *npriv = ninode->npriv;
 	struct file_priv *priv;
-	struct stat s;
-
-	priv = nfs_do_stat(dev, filename, &s);
-	if (IS_ERR(priv))
-		return PTR_ERR(priv);
 
+	priv = xzalloc(sizeof(*priv));
+	priv->fh = ninode->fh;
+	priv->npriv = npriv;
 	file->priv = priv;
-	file->size = s.st_size;
+	file->size = inode->i_size;
 
 	priv->fifo = kfifo_alloc(1024);
 	if (!priv->fifo) {
@@ -1199,115 +1070,170 @@ static loff_t nfs_lseek(struct device_d *dev, FILE *file, loff_t pos)
 	return file->pos;
 }
 
-static DIR *nfs_opendir(struct device_d *dev, const char *pathname)
+static int nfs_iterate(struct file *file, struct dir_context *ctx)
 {
-	struct file_priv *priv;
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *dir = d_inode(dentry);
+	struct nfs_priv *npriv = nfsi(dir)->npriv;
 	void *buf = NULL;
-	struct nfs_dir *dir;
+	struct nfs_dir *ndir;
+	struct xdr_stream *xdr;
+	int ret;
+	uint32_t *p, len;
 
-	priv = nfs_do_open(dev, pathname, NULL);
-	if (IS_ERR(priv))
-		return NULL;
+	ndir = xzalloc(sizeof(*ndir));
+	ndir->fh = nfsi(dir)->fh;
 
-	dir = xzalloc(sizeof(*dir));
-	dir->priv = priv;
+	while (1) {
+		/* cookie == 0 and cookieverf == 0 means start of dir */
+		buf = nfs_readdirattr_req(npriv, ndir);
+		if (!buf) {
+			pr_err("%s: nfs_readdirattr_req failed\n", __func__);
+			ret = -EINVAL;
+			goto out;
+		}
 
-	/* cookie == 0 and cookieverf == 0 means start of dir */
-	buf = nfs_readdirattr_req(priv, dir);
-	if (!buf) {
-		debug("%s: nfs_readdirattr_req failed\n", __func__);
-		goto err;
-	}
+		xdr = &ndir->stream;
 
-	return &dir->dir;
+		while (1) {
+			char name[256];
 
-err:
-	free(buf);
-	free(dir);
-	nfs_do_close(priv);
-	return NULL;
-}
+			p = xdr_inline_decode(xdr, 4);
+			if (!p)
+				goto err_eop;
 
-static struct dirent *nfs_readdir(struct device_d *dev, DIR *dir)
-{
-	struct nfs_dir *ndir = container_of(dir, struct nfs_dir, dir);
-	uint32_t *p;
-	int ret;
-	int len;
-	struct xdr_stream *xdr = &ndir->stream;
+			if (!net_read_uint32(p)) {
+				/* eof? */
+				p = xdr_inline_decode(xdr, 4);
+				if (!p)
+					goto err_eop;
 
-again:
-	p = xdr_inline_decode(xdr, 4);
-	if (!p) {
-		printf("%s: premature end of packet\n", __func__);
-		return NULL;
-	}
+				if (net_read_uint32(p)) {
+					ret = 0;
+					goto out;
+				}
 
-	if (!net_read_uint32(p)) {
-		/* eof? */
-		p = xdr_inline_decode(xdr, 4);
-		if (!p) {
-			printf("%s: premature end of packet\n", __func__);
-			return NULL;
-		}
-		if (net_read_uint32(p))
-			return NULL;
+				break;
+			}
 
-		if (!nfs_readdirattr_req(ndir->priv, ndir)) {
-			printf("%s: nfs_readdirattr_req failed\n", __func__);
-			return NULL;
-		}
+			/* skip over fileid */
+			p = xdr_inline_decode(xdr, 8);
+			if (!p)
+				goto err_eop;
 
-		goto again;
-	}
+			ret = decode_filename(xdr, name, &len);
+			if (ret)
+				goto out;
 
-	/* there is another entry available in the last reply */
+			dir_emit(ctx, name, len, 0, DT_UNKNOWN);
 
-	/* skip over fileid */
-	p = xdr_inline_decode(xdr, 8);
-	if (!p) {
-		printf("%s: premature end of packet\n", __func__);
-		return NULL;
+			p = xdr_inline_decode(xdr, 8);
+			if (!p)
+				goto err_eop;
+
+			ndir->cookie = ntoh64(net_read_uint64(p));
+		}
+		free(buf);
 	}
 
-	ret = decode_filename(xdr, ndir->ent.d_name, &len);
-	if (ret)
-		return NULL;
+	ret = 0;
 
-	p = xdr_inline_decode(xdr, 8);
-	if (!p) {
-		printf("%s: premature end of packet\n", __func__);
+out:
+	free(ndir->stream.buf);
+	free(ndir);
+
+	return ret;
+
+err_eop:
+	pr_err("Unexpected end of packet\n");
+
+	return -EIO;
+}
+
+static struct inode *nfs_alloc_inode(struct super_block *sb)
+{
+	struct nfs_inode *node;
+
+	node = xzalloc(sizeof(*node));
+	if (!node)
 		return NULL;
-	}
-	ndir->cookie = ntoh64(net_read_uint64(p));
 
-	return &ndir->ent;
+	return &node->inode;
 }
 
-static int nfs_closedir(struct device_d *dev, DIR *dir)
+static const struct inode_operations nfs_file_inode_operations;
+static const struct file_operations nfs_dir_operations;
+static const struct inode_operations nfs_dir_inode_operations;
+static const struct file_operations nfs_file_operations;
+static const struct inode_operations nfs_symlink_inode_operations = {
+	.get_link = nfs_get_link,
+};
+
+static int nfs_init_inode(struct nfs_priv *npriv, struct inode *inode,
+			  unsigned int mode)
 {
-	struct nfs_dir *ndir = (void *)dir;
+	struct nfs_inode *ninode = nfsi(inode);
 
-	nfs_do_close(ndir->priv);
-	free(ndir->stream.buf);
-	free(ndir);
+	ninode->npriv = npriv;
+
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+
+	switch (inode->i_mode & S_IFMT) {
+	default:
+		return -EINVAL;
+	case S_IFREG:
+		inode->i_op = &nfs_file_inode_operations;
+		inode->i_fop = &nfs_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &nfs_dir_inode_operations;
+		inode->i_fop = &nfs_dir_operations;
+		inc_nlink(inode);
+		break;
+	case S_IFLNK:
+		inode->i_op = &nfs_symlink_inode_operations;
+		break;
+	}
 
 	return 0;
 }
 
-static int nfs_stat(struct device_d *dev, const char *filename, struct stat *s)
+static struct dentry *nfs_lookup(struct inode *dir, struct dentry *dentry,
+				   unsigned int flags)
 {
-	struct file_priv *priv;
+	struct nfs_inode *ndir = nfsi(dir);
+	struct inode *inode = new_inode(dir->i_sb);
+	struct nfs_priv *npriv = ndir->npriv;
+	int ret;
 
-	priv = nfs_do_stat(dev, filename, s);
-	if (IS_ERR(priv)) {
-		return PTR_ERR(priv);
-	} else {
-		nfs_do_close(priv);
-		return 0;
-	}
+	if (!inode)
+		return NULL;
+
+	ret = nfs_lookup_req(npriv, &ndir->fh, dentry->name, inode);
+	if (ret)
+		return NULL;
+
+	nfs_init_inode(npriv, inode, inode->i_mode);
+
+	d_add(dentry, inode);
+
+	return NULL;
 }
 
+static const struct file_operations nfs_dir_operations = {
+	.iterate = nfs_iterate,
+};
+
+static const struct inode_operations nfs_dir_inode_operations =
+{
+	.lookup = nfs_lookup,
+};
+
+static const struct super_operations nfs_ops = {
+	.alloc_inode = nfs_alloc_inode,
+};
+
 static char *rootnfsopts;
 
 static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device_d *fsdev)
@@ -1347,8 +1273,10 @@ static int nfs_probe(struct device_d *dev)
 {
 	struct fs_device_d *fsdev = dev_to_fs_device(dev);
 	struct nfs_priv *npriv = xzalloc(sizeof(struct nfs_priv));
+	struct super_block *sb = &fsdev->sb;
 	char *tmp = xstrdup(fsdev->backingstore);
 	char *path;
+	struct inode *inode;
 	int ret;
 
 	dev->priv = npriv;
@@ -1414,6 +1342,13 @@ static int nfs_probe(struct device_d *dev)
 
 	free(tmp);
 
+	sb->s_op = &nfs_ops;
+
+	inode = new_inode(sb);
+	nfs_set_fh(inode, &npriv->rootfh);
+	nfs_init_inode(npriv, inode, S_IFDIR);
+	sb->s_root = d_make_root(inode);
+
 	return 0;
 
 err2:
@@ -1443,17 +1378,8 @@ static struct fs_driver_d nfs_driver = {
 	.close     = nfs_close,
 	.read      = nfs_read,
 	.lseek     = nfs_lseek,
-	.opendir   = nfs_opendir,
-	.readdir   = nfs_readdir,
-	.closedir  = nfs_closedir,
-	.stat      = nfs_stat,
-	.create    = nfs_create,
-	.unlink    = nfs_unlink,
-	.mkdir     = nfs_mkdir,
-	.rmdir     = nfs_rmdir,
 	.write     = nfs_write,
 	.truncate  = nfs_truncate,
-	.readlink  = nfs_readlink,
 	.flags     = 0,
 	.drv = {
 		.probe  = nfs_probe,
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 07/10] fs: tftp: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (5 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 06/10] fs: nfs: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 08/10] fs: cramfs: " Sascha Hauer
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig |  1 -
 fs/tftp.c  | 97 +++++++++++++++++++++++++++++++-----------------------
 2 files changed, 55 insertions(+), 43 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 4d0e2787af..4cde73e7e8 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -40,7 +40,6 @@ config FS_DEVFS
 
 config FS_TFTP
 	bool
-	select FS_LEGACY
 	prompt "tftp support"
 	depends on NET
 
diff --git a/fs/tftp.c b/fs/tftp.c
index 847921aa56..cc30c5eb8f 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -89,26 +89,6 @@ struct tftp_priv {
 	IPaddr_t server;
 };
 
-static int tftp_create(struct device_d *dev, const char *pathname, mode_t mode)
-{
-	return 0;
-}
-
-static int tftp_unlink(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
-static int tftp_mkdir(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
-static int tftp_rmdir(struct device_d *dev, const char *pathname)
-{
-	return -ENOSYS;
-}
-
 static int tftp_truncate(struct device_d *dev, FILE *f, ulong size)
 {
 	return 0;
@@ -466,6 +446,9 @@ out:
 static int tftp_open(struct device_d *dev, FILE *file, const char *filename)
 {
 	struct file_priv *priv;
+	struct fs_device_d *fsdev = dev_to_fs_device(dev);
+
+	filename = dpath(file->dentry, fsdev->vfsmount.mnt_root);
 
 	priv = tftp_do_open(dev, file->flags, filename);
 	if (IS_ERR(priv))
@@ -618,40 +601,76 @@ out_free:
 	return -ENOSYS;
 }
 
-static DIR* tftp_opendir(struct device_d *dev, const char *pathname)
+static const struct inode_operations tftp_file_inode_operations;
+static const struct inode_operations tftp_dir_inode_operations;
+static const struct file_operations tftp_file_operations;
+
+static struct inode *tftp_get_inode(struct super_block *sb, const struct inode *dir,
+                                     umode_t mode)
 {
-	/* not implemented in tftp protocol */
-	return NULL;
+	struct inode *inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+	inode->i_size = FILE_SIZE_STREAM;
+
+	switch (mode & S_IFMT) {
+	default:
+		return NULL;
+	case S_IFREG:
+		inode->i_op = &tftp_file_inode_operations;
+		inode->i_fop = &tftp_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &tftp_dir_inode_operations;
+		inode->i_fop = &simple_dir_operations;
+		inc_nlink(inode);
+		break;
+	}
+
+	return inode;
 }
 
-static int tftp_stat(struct device_d *dev, const char *filename, struct stat *s)
+static struct dentry *tftp_lookup(struct inode *dir, struct dentry *dentry,
+			    unsigned int flags)
 {
-	struct file_priv *priv;
-
-	priv = tftp_do_open(dev, O_RDONLY, filename);
-	if (IS_ERR(priv))
-		return PTR_ERR(priv);
+	struct inode *inode;
 
-	s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
-	if (priv->filesize)
-		s->st_size = priv->filesize;
-	else
-		s->st_size = FILESIZE_MAX;
+	inode = tftp_get_inode(dir->i_sb, dir, S_IFREG | S_IRWXUGO);
+	if (!inode)
+		return ERR_PTR(-ENOSPC);
 
-	tftp_do_close(priv);
+	d_add(dentry, inode);
 
-	return 0;
+	return NULL;
 }
 
+static const struct inode_operations tftp_dir_inode_operations =
+{
+	.lookup = tftp_lookup,
+};
+
+static const struct super_operations tftp_ops;
+
 static int tftp_probe(struct device_d *dev)
 {
 	struct fs_device_d *fsdev = dev_to_fs_device(dev);
 	struct tftp_priv *priv = xzalloc(sizeof(struct tftp_priv));
+	struct super_block *sb = &fsdev->sb;
+	struct inode *inode;
 
 	dev->priv = priv;
 
 	priv->server = resolv(fsdev->backingstore);
 
+	sb->s_op = &tftp_ops;
+
+	inode = tftp_get_inode(sb, NULL, S_IFDIR);
+	sb->s_root = d_make_root(inode);
+
 	return 0;
 }
 
@@ -667,12 +686,6 @@ static struct fs_driver_d tftp_driver = {
 	.close     = tftp_close,
 	.read      = tftp_read,
 	.lseek     = tftp_lseek,
-	.opendir   = tftp_opendir,
-	.stat      = tftp_stat,
-	.create    = tftp_create,
-	.unlink    = tftp_unlink,
-	.mkdir     = tftp_mkdir,
-	.rmdir     = tftp_rmdir,
 	.write     = tftp_write,
 	.truncate  = tftp_truncate,
 	.flags     = 0,
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 08/10] fs: cramfs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (6 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 07/10] fs: tftp: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 09/10] fs: squashfs: " Sascha Hauer
  2018-05-31 15:04 ` [PATCH 10/10] block: Adjust cache sizes Sascha Hauer
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/Kconfig         |   1 -
 fs/cramfs/cramfs.c | 523 +++++++++++++++++++++++----------------------
 2 files changed, 273 insertions(+), 251 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 4cde73e7e8..76a3846929 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -23,7 +23,6 @@ config FS_AUTOMOUNT
 config FS_CRAMFS
 	bool
 	select ZLIB
-	select FS_LEGACY
 	prompt "cramfs support"
 
 source fs/ext4/Kconfig
diff --git a/fs/cramfs/cramfs.c b/fs/cramfs/cramfs.c
index a02c253841..a3ce354c92 100644
--- a/fs/cramfs/cramfs.c
+++ b/fs/cramfs/cramfs.c
@@ -51,10 +51,16 @@ struct cramfs_priv {
 };
 
 struct cramfs_inode_info {
+	struct inode i_inode;
 	struct cramfs_inode inode;
 	unsigned long *block_ptrs;
 };
 
+static inline struct cramfs_inode_info* to_cramfs_inode_info(struct inode *inode)
+{
+	return container_of(inode, struct cramfs_inode_info, i_inode);
+}
+
 static int cramfs_read_super(struct cramfs_priv *priv)
 {
 	unsigned long root_offset;
@@ -107,233 +113,29 @@ static int cramfs_read_super(struct cramfs_priv *priv)
 	return 0;
 }
 
-static struct cramfs_inode_info *cramfs_get_inode(struct cramfs_priv *priv, unsigned long offset)
-{
-	struct cramfs_inode_info *inodei = xmalloc(sizeof(*inodei));
-
-	if (cdev_read(priv->cdev, &inodei->inode, sizeof(struct cramfs_inode), offset, 0) < 0) {
-		free(inodei);
-		return NULL;
-	}
-
-	return inodei;
-}
-
-static struct cramfs_inode_info *cramfs_resolve (struct cramfs_priv *priv, unsigned long offset,
-				     unsigned long size, int raw,
-				     char *filename)
-{
-	unsigned long inodeoffset = 0, nextoffset;
-	struct cramfs_inode_info *inodei = NULL, *ret;
-	char *name = xmalloc(256);
-
-	while (inodeoffset < size) {
-		int namelen;
-		inodei = cramfs_get_inode(priv, offset + inodeoffset);
-
-		/*
-		 * Namelengths on disk are shifted by two
-		 * and the name padded out to 4-byte boundaries
-		 * with zeroes.
-		 */
-		namelen = CRAMFS_GET_NAMELEN (&inodei->inode) << 2;
-		cdev_read(priv->cdev, name, namelen, offset + inodeoffset + sizeof (struct cramfs_inode), 0);
-
-		nextoffset =
-			inodeoffset + sizeof (struct cramfs_inode) + namelen;
-
-		if (!strncmp (filename, name, namelen)) {
-			char *p = strtok (NULL, "/");
-
-			if (raw && (p == NULL || *p == '\0'))
-				goto out1;
-
-			if (S_ISDIR (CRAMFS_16 (inodei->inode.mode))) {
-				ret = cramfs_resolve(priv,
-					CRAMFS_GET_OFFSET(&inodei->inode) << 2,
-					CRAMFS_24 (inodei->inode.size),
-					raw, p);
-				goto out;
-			} else if (S_ISREG (CRAMFS_16 (inodei->inode.mode))) {
-				goto out1;
-			} else {
-				printf ("%*.*s: unsupported file type (%x)\n",
-					namelen, namelen, name,
-					CRAMFS_16 (inodei->inode.mode));
-				ret = NULL;
-				goto out;
-			}
-		}
-
-		free(inodei);
-		inodeoffset = nextoffset;
-	}
-
-	free(name);
-	return NULL;
-
-out1:
-	ret = cramfs_get_inode(priv, offset + inodeoffset);
-out:
-	free(inodei);
-	free(name);
-	return ret;
-}
-
-static int cramfs_fill_dirent (struct cramfs_priv *priv, unsigned long offset, struct dirent *d)
-{
-	struct cramfs_inode_info *inodei = cramfs_get_inode(priv, offset);
-	int namelen;
-
-	if (!inodei)
-		return -EINVAL;
-
-	memset(d->d_name, 0, 256);
-
-	/*
-	 * Namelengths on disk are shifted by two
-	 * and the name padded out to 4-byte boundaries
-	 * with zeroes.
-	 */
-
-	namelen = CRAMFS_GET_NAMELEN (&inodei->inode) << 2;
-	cdev_read(priv->cdev, d->d_name, namelen, offset + sizeof(struct cramfs_inode), 0);
-	free(inodei);
-	return namelen;
-}
-
-struct cramfs_dir {
-	unsigned long offset, size;
-	unsigned long inodeoffset;
-	DIR dir;
-};
-
-static DIR* cramfs_opendir(struct device_d *_dev, const char *filename)
+static int cramfs_read_file(struct inode *inode, unsigned long offset,
+			    void *buf, size_t size)
 {
-	struct cramfs_priv *priv = _dev->priv;
-	char *f;
-
-	struct cramfs_dir *dir = xzalloc(sizeof(struct cramfs_dir));
-	dir->dir.priv = dir;
-
-	if (strlen (filename) == 0 || !strcmp (filename, "/")) {
-		/* Root directory. Use root inode in super block */
-		dir->offset = CRAMFS_GET_OFFSET (&(priv->super.root)) << 2;
-		dir->size = CRAMFS_24 (priv->super.root.size);
-	} else {
-		struct cramfs_inode_info *inodei;
-
-		f = strdup(filename);
-		/* Resolve the path */
-		inodei = cramfs_resolve(priv,
-					 CRAMFS_GET_OFFSET (&(priv->super.root)) <<
-					 2, CRAMFS_24 (priv->super.root.size), 1,
-					 strtok (f, "/"));
-		free(f);
-		if (!inodei)
-			goto err_free;
-
-		/* Resolving was successful. Examine the inode */
-		if (!S_ISDIR (CRAMFS_16 (inodei->inode.mode))) {
-			/* It's not a directory */
-			free(inodei);
-			goto err_free;
-		}
-
-		dir->offset = CRAMFS_GET_OFFSET (&inodei->inode) << 2;
-		dir->size = CRAMFS_24 (inodei->inode.size);
-		free(inodei);
-	}
-
-	return &dir->dir;
-
-err_free:
-	free(dir);
-	return NULL;
-}
-
-static struct dirent* cramfs_readdir(struct device_d *_dev, DIR *_dir)
-{
-	struct cramfs_priv *priv = _dev->priv;
-	struct cramfs_dir *dir = _dir->priv;
-	unsigned long nextoffset;
-
-	/* List the given directory */
-	if (dir->inodeoffset < dir->size) {
-		nextoffset = cramfs_fill_dirent (priv, dir->offset + dir->inodeoffset, &_dir->d);
-
-		dir->inodeoffset += sizeof (struct cramfs_inode) + nextoffset;
-		return &_dir->d;
-	}
-	return NULL;
-}
-
-static int cramfs_closedir(struct device_d *dev, DIR *_dir)
-{
-	struct cramfs_dir *dir = _dir->priv;
-	free(dir);
-	return 0;
-}
-
-static int cramfs_open(struct device_d *_dev, FILE *file, const char *filename)
-{
-	struct cramfs_priv *priv = _dev->priv;
-	struct cramfs_inode_info *inodei;
-	char *f;
-
-	f = strdup(filename);
-	inodei = cramfs_resolve (priv,
-				 CRAMFS_GET_OFFSET (&(priv->super.root)) << 2,
-				 CRAMFS_24 (priv->super.root.size), 0,
-				 strtok (f, "/"));
-	free(f);
-
-	if (!inodei)
-		return -ENOENT;
-
-	file->priv = inodei;
-	file->size = inodei->inode.size;
-
-	inodei->block_ptrs = xzalloc(4096);
-	cdev_read(priv->cdev, inodei->block_ptrs, 4096, CRAMFS_GET_OFFSET(&inodei->inode) << 2, 0);
-
-	return 0;
-}
-
-static int cramfs_close(struct device_d *dev, FILE *file)
-{
-	struct cramfs_inode_info *inodei = file->priv;
-
-	free(inodei->block_ptrs);
-	free(inodei);
-
-	return 0;
-}
-
-static int cramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
-{
-	struct cramfs_priv *priv = _dev->priv;
-	struct cramfs_inode_info *inodei = f->priv;
-	struct cramfs_inode *inode = &inodei->inode;
+	struct cramfs_inode_info *info = to_cramfs_inode_info(inode);
+	struct cramfs_inode *cramfs_inode = &info->inode;
+	struct fs_device_d *fsdev = container_of(inode->i_sb, struct fs_device_d, sb);
+	struct cramfs_priv *priv = fsdev->dev.priv;
 	unsigned int blocknr;
 	int outsize = 0;
-	unsigned long *block_ptrs = inodei->block_ptrs;
-	int ofs = f->pos % 4096;
+	int ofs = offset % 4096;
 	static char cramfs_read_buf[4096];
 
-	if (f->pos + size > inode->size)
-		size = inode->size - f->pos;
-
 	while (size) {
-		unsigned long base;
+		uint32_t base;
 		int copy;
 
-		blocknr = (f->pos + outsize) >> 12;
-
+		blocknr = (offset + outsize) >> 12;
 		if (blocknr)
-			base = CRAMFS_32 (block_ptrs[blocknr - 1]);
+			cdev_read(priv->cdev, &base, 4,
+				  OFFSET(inode) + (blocknr - 1) * 4, 0);
 		else
-			base = (CRAMFS_GET_OFFSET(inode) + (((CRAMFS_24 (inode->size)) + 4095) >> 12)) << 2;
+			base = (CRAMFS_GET_OFFSET(cramfs_inode) +
+				(((CRAMFS_24 (cramfs_inode->size)) + 4095) >> 12)) << 2;
 
 		if (priv->curr_base < 0 || priv->curr_base != base) {
 
@@ -359,38 +161,17 @@ static int cramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
 	return outsize;
 }
 
+static int cramfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size)
+{
+	return cramfs_read_file(f->f_inode, f->pos, buf, size);
+}
+
 static loff_t cramfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
 {
 	f->pos = pos;
 	return f->pos;
 }
 
-static int cramfs_stat(struct device_d *_dev, const char *filename, struct stat *stat)
-{
-	struct cramfs_priv *priv = _dev->priv;
-	struct cramfs_inode_info *inodei;
-	struct cramfs_inode *inode;
-	char *f;
-
-	f = strdup(filename);
-
-	inodei = cramfs_resolve (priv,
-			 CRAMFS_GET_OFFSET (&(priv->super.root)) << 2,
-			 CRAMFS_24 (priv->super.root.size), 1,
-			 strtok (f, "/"));
-	free(f);
-
-	if (!inodei)
-		return -ENOENT;
-
-	inode = &inodei->inode;
-	stat->st_mode = CRAMFS_16 (inode->mode);
-	stat->st_size = CRAMFS_24 (inode->size);
-
-	free(inodei);
-
-	return 0;
-}
 #if 0
 static int cramfs_info (struct device_d *dev)
 {
@@ -419,20 +200,260 @@ static int cramfs_info (struct device_d *dev)
 }
 #endif
 
+static const struct file_operations cramfs_dir_operations;
+static const struct inode_operations cramfs_dir_inode_operations;
+static const struct inode_operations cramfs_symlink_inode_operations;
+
+static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset)
+{
+	if (!cino->offset)
+		return offset + 1;
+	if (!cino->size)
+		return offset + 1;
+
+	/*
+	 * The file mode test fixes buggy mkcramfs implementations where
+	 * cramfs_inode->offset is set to a non zero value for entries
+	 * which did not contain data, like devices node and fifos.
+	 */
+	switch (cino->mode & S_IFMT) {
+	case S_IFREG:
+	case S_IFDIR:
+	case S_IFLNK:
+		return cino->offset << 2;
+	default:
+		break;
+	}
+	return offset + 1;
+}
+
+static struct inode *get_cramfs_inode(struct super_block *sb,
+	const struct cramfs_inode *cramfs_inode, unsigned int offset)
+{
+	struct cramfs_inode_info *info;
+	static struct timespec zerotime;
+	struct inode *inode;
+
+	inode = new_inode(sb);
+
+	inode->i_ino = cramino(cramfs_inode, offset);
+
+	info = to_cramfs_inode_info(inode);
+
+	switch (cramfs_inode->mode & S_IFMT) {
+	case S_IFREG:
+		break;
+	case S_IFDIR:
+		inode->i_op = &cramfs_dir_inode_operations;
+		inode->i_fop = &cramfs_dir_operations;
+		break;
+	case S_IFLNK:
+		inode->i_op = &cramfs_symlink_inode_operations;
+		break;
+	default:
+		return NULL;
+	}
+
+	info->inode = *cramfs_inode;
+
+	inode->i_mode = cramfs_inode->mode;
+
+	/* if the lower 2 bits are zero, the inode contains data */
+	if (!(inode->i_ino & 3)) {
+		inode->i_size = cramfs_inode->size;
+		inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
+	}
+
+	/* Struct copy intentional */
+	inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
+	/* inode->i_nlink is left 1 - arguably wrong for directories,
+	   but it's the best we can do without reading the directory
+	   contents.  1 yields the right result in GNU find, even
+	   without -noleaf option. */
+
+	return inode;
+}
+
+static struct dentry *cramfs_lookup(struct inode *dir, struct dentry *dentry,
+				    unsigned int flags)
+{
+	struct cramfs_inode *de;
+	unsigned int offset = 0;
+	struct inode *inode = NULL;
+	struct fs_device_d *fsdev = container_of(dir->i_sb, struct fs_device_d, sb);
+	struct cramfs_priv *priv = fsdev->dev.priv;
+
+	de = xmalloc(sizeof(*de) + CRAMFS_MAXPATHLEN);
+
+	while (offset < dir->i_size) {
+		char *name;
+		int namelen, retval;
+		int dir_off = OFFSET(dir) + offset;
+
+		cdev_read(priv->cdev, de, sizeof(*de) + CRAMFS_MAXPATHLEN, dir_off, 0);
+
+		name = (char *)(de + 1);
+
+		namelen = de->namelen << 2;
+		offset += sizeof(*de) + namelen;
+
+		/* Quick check that the name is roughly the right length */
+		if (((dentry->d_name.len + 3) & ~3) != namelen)
+			continue;
+
+		for (;;) {
+			if (!namelen) {
+				inode = ERR_PTR(-EIO);
+				goto out;
+			}
+			if (name[namelen-1])
+				break;
+			namelen--;
+		}
+		if (namelen != dentry->d_name.len)
+			continue;
+		retval = memcmp(dentry->d_name.name, name, namelen);
+		if (retval > 0)
+			continue;
+		if (!retval) {
+			inode = get_cramfs_inode(dir->i_sb, de, dir_off);
+			break;
+		}
+	}
+out:
+	free(de);
+
+	if (IS_ERR(inode))
+		return ERR_CAST(inode);
+	d_add(dentry, inode);
+
+	return NULL;
+}
+
+static struct inode *cramfs_alloc_inode(struct super_block *sb)
+{
+	struct cramfs_inode_info *info;
+
+	info = xzalloc(sizeof(*info));
+ 
+	return &info->i_inode;
+}
+
+static int cramfs_iterate(struct file *file, struct dir_context *ctx)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *dir = d_inode(dentry);
+	struct fs_device_d *fsdev = container_of(dir->i_sb, struct fs_device_d, sb);
+	struct cramfs_priv *priv = fsdev->dev.priv;
+	char *buf;
+	unsigned int offset;
+	struct cramfs_inode *de;
+	int ret;
+
+	/* Offset within the thing. */
+	if (ctx->pos >= dir->i_size)
+		return 0;
+	offset = ctx->pos;
+	/* Directory entries are always 4-byte aligned */
+	if (offset & 3)
+		return -EINVAL;
+
+	buf = xmalloc(CRAMFS_MAXPATHLEN);
+	de = xmalloc(sizeof(*de) + CRAMFS_MAXPATHLEN);
+
+	while (offset < dir->i_size) {
+		unsigned long nextoffset;
+		char *name;
+		ino_t ino;
+		umode_t mode;
+		int namelen;
+
+		cdev_read(priv->cdev, de, sizeof(*de) + CRAMFS_MAXPATHLEN,
+			  OFFSET(dir) + offset, 0);
+		name = (char *)(de + 1);
+
+		/*
+		 * Namelengths on disk are shifted by two
+		 * and the name padded out to 4-byte boundaries
+		 * with zeroes.
+		 */
+		namelen = de->namelen << 2;
+		memcpy(buf, name, namelen);
+		ino = cramino(de, OFFSET(dir) + offset);
+		mode = de->mode;
+
+		nextoffset = offset + sizeof(*de) + namelen;
+		for (;;) {
+			if (!namelen) {
+				ret = -EIO;
+				goto out;
+			}
+			if (buf[namelen - 1])
+				break;
+			namelen--;
+		}
+
+		dir_emit(ctx, buf, namelen, ino, mode >> 12);
+
+		ctx->pos = offset = nextoffset;
+	}
+	ret = 0;
+out:
+	kfree(buf);
+	free(de);
+	return ret;
+}
+
+static const struct file_operations cramfs_dir_operations = {
+	.iterate = cramfs_iterate,
+};
+
+static const struct inode_operations cramfs_dir_inode_operations =
+{
+	.lookup = cramfs_lookup,
+};
+
+static const char *cramfs_get_link(struct dentry *dentry, struct inode *inode)
+{
+	int ret;
+
+	inode->i_link = xzalloc(inode->i_size + 1);
+
+	ret = cramfs_read_file(inode, 0, inode->i_link, inode->i_size);
+	if (ret < 0)
+		return NULL;
+
+	return inode->i_link;
+}
+
+static const struct inode_operations cramfs_symlink_inode_operations =
+{
+	.get_link = cramfs_get_link,
+};
+
+static const struct super_operations cramfs_ops = {
+	.alloc_inode = cramfs_alloc_inode,
+};
+
 static int cramfs_probe(struct device_d *dev)
 {
 	struct fs_device_d *fsdev;
 	struct cramfs_priv *priv;
 	int ret;
+	struct super_block *sb;
+	struct inode *root;
 
 	fsdev = dev_to_fs_device(dev);
+	sb = &fsdev->sb;
 
 	priv = xmalloc(sizeof(struct cramfs_priv));
 	dev->priv = priv;
 
 	ret = fsdev_open_cdev(fsdev);
-	if (ret)
+	if (ret) {
+		dev_err(dev, "open cdev failed: %d\n", ret);
 		goto err_out;
+	}
 
 	priv->cdev = fsdev->cdev;
 
@@ -444,6 +465,14 @@ static int cramfs_probe(struct device_d *dev)
 	priv->curr_base = -1;
 
 	cramfs_uncompress_init ();
+
+	sb->s_op = &cramfs_ops;
+
+	root = get_cramfs_inode(sb, &priv->super.root, 0);
+	if (IS_ERR(root))
+		return PTR_ERR(root);
+	sb->s_root = d_make_root(root);
+
 	return 0;
 
 err_out:
@@ -461,14 +490,8 @@ static void cramfs_remove(struct device_d *dev)
 }
 
 static struct fs_driver_d cramfs_driver = {
-	.open		= cramfs_open,
-	.close		= cramfs_close,
 	.read		= cramfs_read,
 	.lseek		= cramfs_lseek,
-	.opendir	= cramfs_opendir,
-	.readdir	= cramfs_readdir,
-	.closedir	= cramfs_closedir,
-	.stat		= cramfs_stat,
 	.drv = {
 		.probe = cramfs_probe,
 		.remove = cramfs_remove,
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 09/10] fs: squashfs: Switch to dentry cache implementation
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (7 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 08/10] fs: cramfs: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  2018-05-31 15:04 ` [PATCH 10/10] block: Adjust cache sizes Sascha Hauer
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

While at it implement symlink support.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/squashfs/Kconfig    |   1 -
 fs/squashfs/Makefile   |   2 +
 fs/squashfs/dir.c      | 232 +++++++++++++++++++++++++++++++++++++++++
 fs/squashfs/inode.c    |   9 ++
 fs/squashfs/namei.c    |  17 +--
 fs/squashfs/squashfs.c | 186 +++++----------------------------
 fs/squashfs/squashfs.h |   9 +-
 fs/squashfs/super.c    |   4 +-
 fs/squashfs/symlink.c  |  82 +++++++++++++++
 9 files changed, 365 insertions(+), 177 deletions(-)
 create mode 100644 fs/squashfs/dir.c
 create mode 100644 fs/squashfs/symlink.c

diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index fce05c5730..19b8297af6 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -1,7 +1,6 @@
 menuconfig FS_SQUASHFS
 	bool
 	prompt "squashfs support"
-	select FS_LEGACY
 	help
 	  Saying Y here includes support for SquashFS 4.0 (a Compressed
 	  Read-Only File System).  Squashfs is a highly compressed read-only
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 077114c49c..81fc7e570d 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -10,6 +10,8 @@ obj-y	+= id.o
 obj-y	+= inode.o
 obj-y	+= namei.o
 obj-y	+= super.o
+obj-y	+= symlink.o
+obj-y	+= dir.o
 obj-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
 obj-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
 obj-$(CONFIG_SQUASHFS_LZ4) += lz4_wrapper.o
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c
new file mode 100644
index 0000000000..6275857136
--- /dev/null
+++ b/fs/squashfs/dir.c
@@ -0,0 +1,232 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * dir.c
+ */
+
+/*
+ * This file implements code to read directories from disk.
+ *
+ * See namei.c for a description of directory organisation on disk.
+ */
+
+#include <linux/fs.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static const unsigned char squashfs_filetype_table[] = {
+	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+/*
+ * Lookup offset (f_pos) in the directory index, returning the
+ * metadata block containing it.
+ *
+ * If we get an error reading the index then return the part of the index
+ * (if any) we have managed to read - the index isn't essential, just
+ * quicker.
+ */
+static int get_dir_index_using_offset(struct super_block *sb,
+	u64 *next_block, int *next_offset, u64 index_start, int index_offset,
+	int i_count, u64 f_pos)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	int err, i, index, length = 0;
+	unsigned int size;
+	struct squashfs_dir_index dir_index;
+
+	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
+					i_count, f_pos);
+
+	/*
+	 * Translate from external f_pos to the internal f_pos.  This
+	 * is offset by 3 because we invent "." and ".." entries which are
+	 * not actually stored in the directory.
+	 */
+	if (f_pos <= 3)
+		return f_pos;
+	f_pos -= 3;
+
+	for (i = 0; i < i_count; i++) {
+		err = squashfs_read_metadata(sb, &dir_index, &index_start,
+				&index_offset, sizeof(dir_index));
+		if (err < 0)
+			break;
+
+		index = le32_to_cpu(dir_index.index);
+		if (index > f_pos)
+			/*
+			 * Found the index we're looking for.
+			 */
+			break;
+
+		size = le32_to_cpu(dir_index.size) + 1;
+
+		/* size should never be larger than SQUASHFS_NAME_LEN */
+		if (size > SQUASHFS_NAME_LEN)
+			break;
+
+		err = squashfs_read_metadata(sb, NULL, &index_start,
+				&index_offset, size);
+		if (err < 0)
+			break;
+
+		length = index;
+		*next_block = le32_to_cpu(dir_index.start_block) +
+					msblk->directory_table;
+	}
+
+	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+	/*
+	 * Translate back from internal f_pos to external f_pos.
+	 */
+	return length + 3;
+}
+
+
+static int squashfs_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *inode = d_inode(dentry);
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	u64 block = squashfs_i(inode)->start + msblk->directory_table;
+	int offset = squashfs_i(inode)->offset, length, err;
+	unsigned int inode_number, dir_count, size, type;
+	struct squashfs_dir_header dirh;
+	struct squashfs_dir_entry *dire;
+
+	TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
+
+	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
+	if (dire == NULL) {
+		ERROR("Failed to allocate squashfs_dir_entry\n");
+		goto finish;
+	}
+
+	/*
+	 * Return "." and  ".." entries as the first two filenames in the
+	 * directory.  To maximise compression these two entries are not
+	 * stored in the directory, and so we invent them here.
+	 *
+	 * It also means that the external f_pos is offset by 3 from the
+	 * on-disk directory f_pos.
+	 */
+	while (ctx->pos < 3) {
+		char *name;
+		int i_ino;
+
+		if (ctx->pos == 0) {
+			name = ".";
+			size = 1;
+			i_ino = inode->i_ino;
+		} else {
+			name = "..";
+			size = 2;
+			i_ino = squashfs_i(inode)->parent;
+		}
+
+		if (!dir_emit(ctx, name, size, i_ino,
+				squashfs_filetype_table[1]))
+			goto finish;
+
+		ctx->pos += size;
+	}
+
+	length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
+				squashfs_i(inode)->dir_idx_start,
+				squashfs_i(inode)->dir_idx_offset,
+				squashfs_i(inode)->dir_idx_cnt,
+				ctx->pos);
+
+	while (length < i_size_read(inode)) {
+		/*
+		 * Read directory header
+		 */
+		err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
+					&offset, sizeof(dirh));
+		if (err < 0)
+			goto failed_read;
+
+		length += sizeof(dirh);
+
+		dir_count = le32_to_cpu(dirh.count) + 1;
+
+		if (dir_count > SQUASHFS_DIR_COUNT)
+			goto failed_read;
+
+		while (dir_count--) {
+			/*
+			 * Read directory entry.
+			 */
+			err = squashfs_read_metadata(inode->i_sb, dire, &block,
+					&offset, sizeof(*dire));
+			if (err < 0)
+				goto failed_read;
+
+			size = le16_to_cpu(dire->size) + 1;
+
+			/* size should never be larger than SQUASHFS_NAME_LEN */
+			if (size > SQUASHFS_NAME_LEN)
+				goto failed_read;
+
+			err = squashfs_read_metadata(inode->i_sb, dire->name,
+					&block, &offset, size);
+			if (err < 0)
+				goto failed_read;
+
+			length += sizeof(*dire) + size;
+
+			if (ctx->pos >= length)
+				continue;
+
+			dire->name[size] = '\0';
+			inode_number = le32_to_cpu(dirh.inode_number) +
+				((short) le16_to_cpu(dire->inode_number));
+			type = le16_to_cpu(dire->type);
+
+			if (type > SQUASHFS_MAX_DIR_TYPE)
+				goto failed_read;
+
+			if (!dir_emit(ctx, dire->name, size,
+					inode_number,
+					squashfs_filetype_table[type]))
+				goto finish;
+
+			ctx->pos = length;
+		}
+	}
+
+finish:
+	kfree(dire);
+	return 0;
+
+failed_read:
+	ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
+	kfree(dire);
+	return 0;
+}
+
+const struct file_operations squashfs_dir_ops = {
+	.iterate = squashfs_readdir,
+};
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
index 923c397fa8..470536e589 100644
--- a/fs/squashfs/inode.c
+++ b/fs/squashfs/inode.c
@@ -172,6 +172,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 		}
 
 		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
+		inode->i_op = &squashfs_inode_ops;
 		inode->i_mode |= S_IFREG;
 		inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
 		squashfs_i(inode)->fragment_block = frag_blk;
@@ -214,6 +215,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 
 		xattr_id = le32_to_cpu(sqsh_ino->xattr);
 		inode->i_size = le64_to_cpu(sqsh_ino->file_size);
+		inode->i_op = &squashfs_inode_ops;
 		inode->i_mode |= S_IFREG;
 		inode->i_blocks = (inode->i_size -
 				le64_to_cpu(sqsh_ino->sparse) + 511) >> 9;
@@ -240,6 +242,8 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 			goto failed_read;
 
 		inode->i_size = le16_to_cpu(sqsh_ino->file_size);
+		inode->i_op = &squashfs_dir_inode_ops;
+		inode->i_fop = &squashfs_dir_ops;
 		inode->i_mode |= S_IFDIR;
 		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
 		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
@@ -263,6 +267,8 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 
 		xattr_id = le32_to_cpu(sqsh_ino->xattr);
 		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
+		inode->i_op = &squashfs_dir_inode_ops;
+		inode->i_fop = &squashfs_dir_ops;
 		inode->i_mode |= S_IFDIR;
 		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
 		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
@@ -288,6 +294,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 			goto failed_read;
 
 		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
+		inode->i_op = &squashfs_symlink_inode_ops;
 		inode->i_mode |= S_IFLNK;
 		squashfs_i(inode)->start = block;
 		squashfs_i(inode)->offset = offset;
@@ -400,3 +407,5 @@ failed_read:
 	ERROR("Unable to read inode 0x%llx\n", ino);
 	return err;
 }
+
+const struct inode_operations squashfs_inode_ops;
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
index 482fda5a11..baf1e8b646 100644
--- a/fs/squashfs/namei.c
+++ b/fs/squashfs/namei.c
@@ -48,11 +48,11 @@
  * and doesn't require much extra storage on disk.
  */
 
+#include <common.h>
 #include <linux/fs.h>
 #include <malloc.h>
 #include <linux/string.h>
 #include <linux/dcache.h>
-#include <common.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -130,11 +130,11 @@ out:
 }
 
 
-struct inode *squashfs_lookup(struct inode *dir, const char *cur_name,
-				 unsigned int flags)
+static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
+				     unsigned int flags)
 {
-	const unsigned char *name = cur_name;
-	int len = strlen(cur_name);
+	const unsigned char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
 	struct inode *inode = NULL;
 	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
 	struct squashfs_dir_header dirh;
@@ -223,7 +223,8 @@ struct inode *squashfs_lookup(struct inode *dir, const char *cur_name,
 
 exit_lookup:
 	kfree(dire);
-	return inode;
+	d_add(dentry, inode);
+	return NULL;
 
 data_error:
 	err = -EIO;
@@ -344,3 +345,7 @@ failed:
 	kfree(dire);
 	return 1;
 }
+
+const struct inode_operations squashfs_dir_inode_ops = {
+	.lookup = squashfs_lookup,
+};
diff --git a/fs/squashfs/squashfs.c b/fs/squashfs/squashfs.c
index cf7431ee04..d9049b7523 100644
--- a/fs/squashfs/squashfs.c
+++ b/fs/squashfs/squashfs.c
@@ -39,81 +39,7 @@ char *squashfs_devread(struct squashfs_sb_info *fs, int byte_offset,
 	return buf;
 }
 
-static struct inode *duplicate_inode(struct inode *inode)
-{
-	struct squashfs_inode_info *ei;
-	ei = malloc(sizeof(struct squashfs_inode_info));
-	if (ei == NULL) {
-		ERROR("Error allocating memory for inode\n");
-		return NULL;
-	}
-	memcpy(ei, squashfs_i(inode),
-		sizeof(struct squashfs_inode_info));
-
-	return &ei->vfs_inode;
-}
-
-static struct inode *squashfs_findfile(struct super_block *sb,
-		const char *filename, char *buf)
-{
-	char *next;
-	char fpath[128];
-	char *name = fpath;
-	struct inode *inode;
-	struct inode *t_inode = NULL;
-
-	strcpy(fpath, filename);
-
-	/* Remove all leading slashes */
-	while (*name == '/')
-		name++;
-
-	inode = duplicate_inode(sb->s_root->d_inode);
-
-	/*
-	 * Handle root-directory ('/')
-	 */
-	if (!name || *name == '\0')
-		return inode;
-
-	for (;;) {
-		/* Extract the actual part from the pathname.  */
-		next = strchr(name, '/');
-		if (next) {
-			/* Remove all leading slashes.  */
-			while (*next == '/')
-				*(next++) = '\0';
-		}
-
-		t_inode = squashfs_lookup(inode, name, 0);
-		if (t_inode == NULL)
-			break;
-
-		/*
-		 * Check if directory with this name exists
-		 */
-
-		/* Found the node!  */
-		if (!next || *next == '\0') {
-			if (buf != NULL)
-				sprintf(buf, "%s", name);
-
-			free(squashfs_i(inode));
-			return t_inode;
-		}
-
-		name = next;
-
-		free(squashfs_i(inode));
-		inode = t_inode;
-	}
-
-	free(squashfs_i(inode));
-	return NULL;
-}
-
-static void squashfs_set_rootarg(struct squashfs_priv *priv,
-					struct fs_device_d *fsdev)
+static void squashfs_set_rootarg(struct fs_device_d *fsdev)
 {
 	struct ubi_volume_desc *ubi_vol;
 	struct ubi_volume_info vi = {};
@@ -141,16 +67,27 @@ static void squashfs_set_rootarg(struct squashfs_priv *priv,
 	free(str);
 }
 
+static struct inode *squashfs_alloc_inode(struct super_block *sb)
+{
+	struct squashfs_inode_info *node;
+
+	node = xzalloc(sizeof(*node));
+
+	return &node->vfs_inode;
+}
+
+static const struct super_operations squashfs_super_ops = {
+        .alloc_inode = squashfs_alloc_inode,
+};
+
 static int squashfs_probe(struct device_d *dev)
 {
 	struct fs_device_d *fsdev;
-	struct squashfs_priv *priv;
 	int ret;
+	struct super_block *sb;
 
 	fsdev = dev_to_fs_device(dev);
-
-	priv = xzalloc(sizeof(struct squashfs_priv));
-	dev->priv = priv;
+	sb = &fsdev->sb;
 
 	ret = fsdev_open_cdev(fsdev);
 	if (ret)
@@ -163,35 +100,34 @@ static int squashfs_probe(struct device_d *dev)
 		goto err_out;
 	}
 
-	squashfs_set_rootarg(priv, fsdev);
+	squashfs_set_rootarg(fsdev);
+
+	sb->s_op = &squashfs_super_ops;
 
 	return 0;
 
 err_out:
-	free(priv);
 
 	return ret;
 }
 
 static void squashfs_remove(struct device_d *dev)
 {
-	struct squashfs_priv *priv = dev->priv;
+	struct fs_device_d *fsdev;
+	struct super_block *sb;
+
+	fsdev = dev_to_fs_device(dev);
+	sb = &fsdev->sb;
 
-	squashfs_put_super(&priv->sb);
-	free(priv);
+	squashfs_put_super(sb);
 }
 
 static int squashfs_open(struct device_d *dev, FILE *file, const char *filename)
 {
-	struct squashfs_priv *priv = dev->priv;
-	struct inode *inode;
+	struct inode *inode = file->f_inode;
 	struct squashfs_page *page;
 	int i;
 
-	inode = squashfs_findfile(&priv->sb, filename, NULL);
-	if (!inode)
-		return -ENOENT;
-
 	page = malloc(sizeof(struct squashfs_page));
 	page->buf = calloc(32, sizeof(*page->buf));
 	for (i = 0; i < 32; i++) {
@@ -229,7 +165,6 @@ static int squashfs_close(struct device_d *dev, FILE *f)
 		free(page->buf[i]);
 
 	free(page->buf);
-	free(squashfs_i(page->real_page.inode));
 	free(page);
 
 	return 0;
@@ -314,80 +249,11 @@ struct squashfs_dir {
 	char root_d_name[256];
 };
 
-static DIR *squashfs_opendir(struct device_d *dev, const char *pathname)
-{
-	struct squashfs_priv *priv = dev->priv;
-	struct inode *inode;
-	struct squashfs_dir *dir;
-	char buf[256];
-
-	inode = squashfs_findfile(&priv->sb, pathname, buf);
-	if (!inode)
-		return NULL;
-
-	dir = xzalloc(sizeof(struct squashfs_dir));
-	dir->dir.priv = dir;
-
-	dir->root_dentry.d_inode = inode;
-
-	sprintf(dir->d_name, "%s", buf);
-	sprintf(dir->root_d_name, "%s", buf);
-
-	return &dir->dir;
-}
-
-static struct dirent *squashfs_readdir(struct device_d *dev, DIR *_dir)
-{
-	struct squashfs_dir *dir = _dir->priv;
-	struct dentry *root_dentry = &dir->root_dentry;
-
-	if (squashfs_lookup_next(root_dentry->d_inode,
-				 dir->root_d_name,
-				 dir->d_name))
-		return NULL;
-
-	strcpy(_dir->d.d_name, dir->d_name);
-
-	return &_dir->d;
-}
-
-static int squashfs_closedir(struct device_d *dev, DIR *_dir)
-{
-	struct squashfs_dir *dir = _dir->priv;
-
-	free(squashfs_i(dir->root_dentry.d_inode));
-	free(dir);
-
-	return 0;
-}
-
-static int squashfs_stat(struct device_d *dev, const char *filename,
-		struct stat *s)
-{
-	struct squashfs_priv *priv = dev->priv;
-	struct inode *inode;
-
-	inode = squashfs_findfile(&priv->sb, filename, NULL);
-	if (!inode)
-		return -ENOENT;
-
-	s->st_size = inode->i_size;
-	s->st_mode = inode->i_mode;
-
-	free(squashfs_i(inode));
-
-	return 0;
-}
-
 static struct fs_driver_d squashfs_driver = {
 	.open		= squashfs_open,
 	.close		= squashfs_close,
 	.read		= squashfs_read,
 	.lseek		= squashfs_lseek,
-	.opendir	= squashfs_opendir,
-	.readdir	= squashfs_readdir,
-	.closedir	= squashfs_closedir,
-	.stat		= squashfs_stat,
 	.type		= filetype_squashfs,
 	.drv = {
 		.probe = squashfs_probe,
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 9ad6534e46..31c9bc454e 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -25,10 +25,6 @@
 #define DEBUG
 #define pgoff_t		unsigned long
 
-struct squashfs_priv {
-	struct super_block sb;
-};
-
 /*
  * We "simulate" the Linux page struct much simpler here
  */
@@ -132,10 +128,7 @@ extern const struct address_space_operations squashfs_aops;
 extern const struct inode_operations squashfs_inode_ops;
 
 /* namei.c */
-extern struct inode *squashfs_lookup(struct inode *dir, const char *cur_name,
-				 unsigned int flags);
-extern int squashfs_lookup_next(struct inode *dir,
-		char *root_name, char *cur_name);
+extern const struct inode_operations squashfs_dir_inode_ops;
 
 /* symlink.c */
 extern const struct address_space_operations squashfs_symlink_aops;
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 34e92e3c1d..e2b7b8d5a1 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -324,11 +324,11 @@ failed_mount:
 
 int squashfs_mount(struct fs_device_d *fsdev, int silent)
 {
-	struct squashfs_priv *priv = fsdev->dev.priv;
+	struct super_block *sb = &fsdev->sb;
 
 	dev_dbg(&fsdev->dev, "squashfs_mount\n");
 
-	if (squashfs_fill_super(&priv->sb, fsdev, silent))
+	if (squashfs_fill_super(sb, fsdev, silent))
 		return -EINVAL;
 
 	return 0;
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c
new file mode 100644
index 0000000000..40b9bdcc8b
--- /dev/null
+++ b/fs/squashfs/symlink.c
@@ -0,0 +1,82 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * symlink.c
+ */
+
+/*
+ * This file implements code to handle symbolic links.
+ *
+ * The data contents of symbolic links are stored inside the symbolic
+ * link inode within the inode table.  This allows the normally small symbolic
+ * link to be compressed as part of the inode table, achieving much greater
+ * compression than if the symbolic link was compressed individually.
+ */
+
+#include <malloc.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/pagemap.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static const char *squashfs_get_link(struct dentry *dentry, struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	int index = 0;
+	u64 block = squashfs_i(inode)->start;
+	int offset = squashfs_i(inode)->offset;
+	int length = min_t(int, i_size_read(inode) - index, PAGE_SIZE);
+	int bytes;
+	unsigned char *symlink;
+
+	TRACE("Entered squashfs_symlink_readpage, start block "
+			"%llx, offset %x\n", block, offset);
+
+	symlink = malloc(length + 1);
+	if (!symlink)
+		return NULL;
+
+	symlink[length] = 0;
+
+	bytes = squashfs_read_metadata(sb, symlink, &block, &offset, length);
+	if (bytes < 0) {
+		ERROR("Unable to read symlink [%llx:%x]\n",
+			squashfs_i(inode)->start,
+			squashfs_i(inode)->offset);
+		goto error_out;
+	}
+
+	inode->i_link = symlink;
+
+	return inode->i_link;
+
+error_out:
+	free(symlink);
+
+	return NULL;
+}
+
+const struct inode_operations squashfs_symlink_inode_ops = {
+	.get_link = squashfs_get_link,
+};
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 10/10] block: Adjust cache sizes
  2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
                   ` (8 preceding siblings ...)
  2018-05-31 15:04 ` [PATCH 09/10] fs: squashfs: " Sascha Hauer
@ 2018-05-31 15:04 ` Sascha Hauer
  9 siblings, 0 replies; 11+ messages in thread
From: Sascha Hauer @ 2018-05-31 15:04 UTC (permalink / raw)
  To: Barebox List

Use four times more cache entries and divide the memory for each entry
by four. This lowers the linear read throughput somewhat but increases
the access speed for filesystems.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/block.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/common/block.c b/common/block.c
index 55d8d1637e..219b943afc 100644
--- a/common/block.c
+++ b/common/block.c
@@ -36,7 +36,7 @@ struct chunk {
 	struct list_head list;
 };
 
-#define BUFSIZE (PAGE_SIZE * 16)
+#define BUFSIZE (PAGE_SIZE * 4)
 
 /*
  * Write all dirty chunks back to the device
@@ -361,7 +361,7 @@ int blockdevice_register(struct block_device *blk)
 	debug("%s: rdbufsize: %d blockbits: %d blkmask: 0x%08x\n", __func__, blk->rdbufsize, blk->blockbits,
 			blk->blkmask);
 
-	for (i = 0; i < 8; i++) {
+	for (i = 0; i < 32; i++) {
 		struct chunk *chunk = xzalloc(sizeof(*chunk));
 		chunk->data = dma_alloc(BUFSIZE);
 		chunk->num = i;
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2018-05-31 15:07 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-31 15:04 [PATCH v2 00/10] dentry cache support Sascha Hauer
2018-05-31 15:04 ` [PATCH 01/10] fs: dentry cache implementation Sascha Hauer
2018-05-31 15:04 ` [PATCH 02/10] fs: ramfs: Switch to " Sascha Hauer
2018-05-31 15:04 ` [PATCH 03/10] fs: devfs: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 04/10] fs: ext4: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 05/10] fs: ubifs: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 06/10] fs: nfs: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 07/10] fs: tftp: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 08/10] fs: cramfs: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 09/10] fs: squashfs: " Sascha Hauer
2018-05-31 15:04 ` [PATCH 10/10] block: Adjust cache sizes Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox