summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig18
-rw-r--r--fs/Makefile3
-rw-r--r--fs/cramfs/cramfs.c523
-rw-r--r--fs/devfs.c150
-rw-r--r--fs/ext4/ext_barebox.c279
-rw-r--r--fs/ext4/ext_common.h3
-rw-r--r--fs/fat/Kconfig1
-rw-r--r--fs/fs.c3186
-rw-r--r--fs/legacy.c315
-rw-r--r--fs/libfs.c97
-rw-r--r--fs/nfs.c542
-rw-r--r--fs/pstore/Kconfig1
-rw-r--r--fs/ramfs.c401
-rw-r--r--fs/squashfs/Makefile2
-rw-r--r--fs/squashfs/dir.c232
-rw-r--r--fs/squashfs/inode.c9
-rw-r--r--fs/squashfs/namei.c17
-rw-r--r--fs/squashfs/squashfs.c186
-rw-r--r--fs/squashfs/squashfs.h9
-rw-r--r--fs/squashfs/super.c13
-rw-r--r--fs/squashfs/symlink.c82
-rw-r--r--fs/tftp.c97
-rw-r--r--fs/ubifs/Makefile2
-rw-r--r--fs/ubifs/dir.c291
-rw-r--r--fs/ubifs/super.c153
-rw-r--r--fs/ubifs/ubifs.c341
-rw-r--r--fs/ubifs/ubifs.h7
27 files changed, 4209 insertions, 2751 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 3512000556..76a3846929 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -6,6 +6,17 @@ 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
@@ -35,6 +46,7 @@ config FS_OMAP4_USBBOOT
bool
prompt "Filesystem over usb boot"
depends on OMAP4_USBBOOT
+ select FS_LEGACY
config FS_NFS
depends on NET
@@ -43,6 +55,7 @@ config FS_NFS
config FS_EFI
depends on EFI_BOOTUP
+ select FS_LEGACY
bool
prompt "EFI filesystem support"
help
@@ -51,6 +64,7 @@ config FS_EFI
config FS_EFIVARFS
depends on EFI_BOOTUP
+ select FS_LEGACY
bool
prompt "EFI variable filesystem support (efivarfs)"
help
@@ -62,6 +76,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 +93,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 +112,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/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,
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,
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;
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 8a49e32b5c..41818ea811 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,380 +181,20 @@ static int check_fd(int fd)
return 0;
}
-#ifdef CONFIG_FS_AUTOMOUNT
-
-#define AUTOMOUNT_IS_FILE (1 << 0)
-
-struct automount {
- char *path;
- char *cmd;
- struct list_head list;
- unsigned int flags;
-};
-
-static LIST_HEAD(automount_list);
-
-void automount_remove(const char *_path)
-{
- char *path = normalise_path(_path);
- struct automount *am;
-
- list_for_each_entry(am, &automount_list, list) {
- if (!strcmp(path, am->path))
- goto found;
- }
-
- return;
-found:
- list_del(&am->list);
- free(am->path);
- free(am->cmd);
- free(am);
-}
-EXPORT_SYMBOL(automount_remove);
-
-int automount_add(const char *path, const char *cmd)
-{
- struct automount *am = xzalloc(sizeof(*am));
- struct stat s;
- int ret;
-
- am->path = normalise_path(path);
- am->cmd = xstrdup(cmd);
-
- automount_remove(am->path);
-
- 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;
- }
-
- list_add_tail(&am->list, &automount_list);
-
- return 0;
-}
-EXPORT_SYMBOL(automount_add);
-
-void cdev_create_default_automount(struct cdev *cdev)
-{
- char *path, *cmd;
-
- path = basprintf("/mnt/%s", cdev->name);
- cmd = basprintf("mount %s", cdev->name);
-
- make_directory(path);
- automount_add(path, cmd);
-
- free(cmd);
- free(path);
-}
-
-void automount_print(void)
-{
- struct automount *am;
-
- list_for_each_entry(am, &automount_list, list)
- printf("%-20s %s\n", am->path, am->cmd);
-}
-EXPORT_SYMBOL(automount_print);
-
-static void automount_mount(const char *path, int instat)
-{
- struct automount *am;
- int ret;
- static int in_automount;
-
- if (in_automount)
- return;
-
- in_automount++;
-
- if (fs_dev_root != get_fsdevice_by_path(path))
- 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;
- }
-
- if (len_path < len_am_path)
- continue;
-
- if (strncmp(path, am->path, len_am_path))
- continue;
-
- if (*(path + len_am_path) != 0 && *(path + len_am_path) != '/')
- continue;
-
- setenv("automount_path", am->path);
- export("automount_path");
- ret = run_command(am->cmd);
- setenv("automount_path", NULL);
-
- if (ret)
- printf("running automount command '%s' failed\n",
- am->cmd);
-
- break;
- }
-out:
- in_automount--;
-}
-
-BAREBOX_MAGICVAR(automount_path, "mountpath passed to automount scripts");
-
-#else
-static void automount_mount(const char *path, int instat)
+int create(struct dentry *dir, struct dentry *dentry)
{
-}
-#endif /* CONFIG_FS_AUTOMOUNT */
+ struct inode *inode;
-static struct fs_device_d *get_fs_device_and_root_path(char **path)
-{
- struct fs_device_d *fsdev;
-
- automount_mount(*path, 0);
-
- fsdev = get_fsdevice_by_path(*path);
- if (!fsdev)
- return NULL;
- if (fsdev != fs_dev_root)
- *path += strlen(fsdev->path);
-
- return fsdev;
-}
-
-static int dir_is_empty(const char *pathname)
-{
- DIR *dir;
- struct dirent *d;
- int ret = 1;
-
- dir = opendir(pathname);
- if (!dir) {
- errno = ENOENT;
- return -ENOENT;
- }
-
- while ((d = readdir(dir))) {
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
- continue;
- ret = 0;
- break;
- }
-
- closedir(dir);
- return ret;
-}
-
-static int parent_check_directory(const char *path)
-{
- struct stat s;
- int ret;
- char *dir = dirname(xstrdup(path));
-
- ret = lstat(dir, &s);
-
- free(dir);
-
- if (ret)
+ if (d_is_negative(dir))
return -ENOENT;
- if (!S_ISDIR(s.st_mode))
- return -ENOTDIR;
-
- return 0;
-}
-
-const char *getcwd(void)
-{
- return cwd;
-}
-EXPORT_SYMBOL(getcwd);
-
-int chdir(const char *pathname)
-{
- char *p = normalise_path(pathname);
- 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);
-
-out:
- free(p);
-
- if (ret)
- errno = -ret;
-
- return ret;
-}
-EXPORT_SYMBOL(chdir);
-
-int unlink(const char *pathname)
-{
- struct fs_device_d *fsdev;
- struct fs_driver_d *fsdrv;
- char *p = canonicalize_dir(pathname);
- char *freep = p;
- int ret;
- struct stat s;
-
- ret = lstat(p, &s);
- if (ret)
- goto out;
-
- if (S_ISDIR(s.st_mode)) {
- ret = -EISDIR;
- goto out;
- }
-
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENOENT;
- goto out;
- }
- fsdrv = fsdev->driver;
+ inode = d_inode(dir);
- if (!fsdrv->unlink) {
- ret = -ENOSYS;
- goto out;
- }
+ if (!inode->i_op->create)
+ return -EROFS;
- ret = fsdrv->unlink(&fsdev->dev, p);
- if (ret)
- errno = -ret;
-out:
- free(freep);
- if (ret)
- errno = -ret;
- return ret;
+ return inode->i_op->create(inode, dentry, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);
}
-EXPORT_SYMBOL(unlink);
-
-int open(const char *pathname, int flags, ...)
-{
- struct fs_device_d *fsdev;
- struct fs_driver_d *fsdrv;
- FILE *f;
- int exist_err = 0;
- struct stat s;
- char *path;
- char *freep;
- 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;
- }
-
- fsdev = get_fs_device_and_root_path(&path);
- if (!fsdev) {
- ret = -ENOENT;
- goto out;
- }
-
- fsdrv = fsdev->driver;
-
- f->fsdev = fsdev;
- f->flags = flags;
-
- if ((flags & O_ACCMODE) && !fsdrv->write) {
- ret = -EROFS;
- goto out;
- }
-
- 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)
- 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)
- goto out;
- }
-
- if (flags & O_APPEND)
- f->pos = f->size;
-
- free(freep);
- return f->no;
-
-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)
{
@@ -869,6 +330,7 @@ static ssize_t __write(FILE *f, const void *buf, size_t count)
goto out;
} else {
f->size = f->pos + count;
+ f->f_inode->i_size = f->size;
}
}
ret = fsdrv->write(&f->fsdev->dev, f, buf, count);
@@ -1097,7 +559,7 @@ int close(int fd)
{
struct fs_driver_d *fsdrv;
FILE *f;
- int ret;
+ int ret = 0;
if (check_fd(fd))
return -errno;
@@ -1105,7 +567,9 @@ int close(int fd)
f = &files[fd];
fsdrv = f->fsdev->driver;
- ret = fsdrv->close(&f->fsdev->dev, f);
+
+ if (fsdrv->close)
+ ret = fsdrv->close(&f->fsdev->dev, f);
put_file(f);
@@ -1116,91 +580,6 @@ int close(int fd)
}
EXPORT_SYMBOL(close);
-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;
-
- ret = lstat(pathname, &s);
- if (ret)
- goto out;
-
- if (!S_ISLNK(s.st_mode)) {
- ret = -EINVAL;
- goto out;
- }
-
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENODEV;
- goto out;
- }
- fsdrv = fsdev->driver;
-
- if (fsdrv->readlink)
- ret = fsdrv->readlink(&fsdev->dev, p, buf, bufsiz);
- else
- ret = -ENOSYS;
-
- if (ret)
- goto out;
-
-out:
- free(freep);
-
- if (ret)
- errno = -ret;
-
- return ret;
-}
-EXPORT_SYMBOL(readlink);
-
-int symlink(const char *pathname, const char *newpath)
-{
- struct fs_driver_d *fsdrv;
- struct fs_device_d *fsdev;
- char *p;
- int ret;
- struct stat s;
-
- p = canonicalize_path(newpath);
- if (IS_ERR(p)) {
- ret = PTR_ERR(p);
- goto out;
- }
-
- ret = lstat(p, &s);
- if (!ret) {
- ret = -EEXIST;
- goto out;
- }
-
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENODEV;
- goto out;
- }
- fsdrv = fsdev->driver;
-
- if (fsdrv->symlink) {
- ret = fsdrv->symlink(&fsdev->dev, pathname, p);
- } else {
- ret = -EPERM;
- }
-
-out:
- free(p);
- if (ret)
- errno = -ret;
-
- return ret;
-}
-EXPORT_SYMBOL(symlink);
-
static int fs_match(struct device_d *dev, struct driver_d *drv)
{
return strcmp(dev->name, drv->name) ? -1 : 0;
@@ -1221,15 +600,56 @@ static int fs_probe(struct device_d *dev)
list_add_tail(&fsdev->list, &fs_device_list);
- if (!fs_dev_root)
- fs_dev_root = fsdev;
+ if (IS_ENABLED(CONFIG_FS_LEGACY) && !fsdev->sb.s_root) {
+ ret = fs_init_legacy(fsdev);
+ if (ret)
+ return ret;
+ }
+
+ 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);
@@ -1239,15 +659,23 @@ static void fs_remove(struct device_d *dev)
free(fsdev->path);
free(fsdev->options);
- if (fsdev == fs_dev_root)
- fs_dev_root = NULL;
-
if (fsdev->cdev)
cdev_close(fsdev->cdev);
- if (fsdev->loop)
+ 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);
}
@@ -1322,551 +750,2319 @@ int fsdev_open_cdev(struct fs_device_d *fsdev)
return 0;
}
+static void init_super(struct super_block *sb)
+{
+ INIT_LIST_HEAD(&sb->s_inodes);
+}
+
+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);
+
/*
- * Mount a device to a directory.
- * We do this by registering a new device on which the filesystem
- * driver will match.
+ * 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.
*/
-int mount(const char *device, const char *fsname, const char *_path,
- const char *fsoptions)
+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;
- char *path = normalise_path(_path);
- if (!fsoptions)
- fsoptions = "";
+ /*
+ * 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;
- debug("mount: %s on %s type %s, options=%s\n",
- device, path, fsname, fsoptions);
+ newpath = basprintf("/mnt/%s", cdev->name);
+ make_directory(newpath);
- if (fs_dev_root) {
- struct stat s;
+ devpath = basprintf("/dev/%s", cdev->name);
- fsdev = get_fsdevice_by_path(path);
- if (fsdev != fs_dev_root) {
- printf("sorry, no nested mounts\n");
- 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;
+ 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;
+ }
}
- } else {
- /* no mtab, so we only allow to mount on '/' */
- if (*path != '/' || *(path + 1)) {
- ret = -ENOTDIR;
- goto err_free_path;
+
+ /* 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 (!fsname)
- fsname = detect_fs(device, fsoptions);
+ 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;
+}
- if (!fsname)
+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;
+ }
- 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);
+ path->dentry = dentry;
+ path->mnt = nd->path.mnt;
- ret = register_device(&fsdev->dev);
- if (ret)
- goto err_register;
+ return follow_managed(path, nd);
+}
- if (!fsdev->dev.driver) {
- /*
- * Driver didn't accept the device or no driver for this
- * device. Bail out
- */
- ret = -EINVAL;
- goto err_no_driver;
+/*
+ * 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);
}
+}
- if (!fsdev->linux_rootarg && fsdev->cdev && fsdev->cdev->partuuid[0] != 0) {
- char *str = basprintf("root=PARTUUID=%s",
- fsdev->cdev->partuuid);
+static int path_parent_directory(struct path *path)
+{
+ struct dentry *old = path->dentry;
- fsdev_set_linux_rootarg(fsdev, str);
+ 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;
}
- free(path);
+ follow_mount(&nd->path);
+
+ nd->inode = nd->path.dentry->d_inode;
return 0;
+}
-err_no_driver:
- unregister_device(&fsdev->dev);
-err_register:
- fs_remove(&fsdev->dev);
-err_free_path:
- free(path);
+static inline int handle_dots(struct nameidata *nd, int type)
+{
+ if (type == LAST_DOTDOT) {
+ return follow_dotdot(nd);
+ }
+ return 0;
+}
- errno = -ret;
+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;
+}
- return ret;
+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;
}
-EXPORT_SYMBOL(mount);
-static int fsdev_umount(struct fs_device_d *fsdev)
+static int pick_link(struct nameidata *nd, struct path *link,
+ struct inode *inode)
{
- return unregister_device(&fsdev->dev);
+ 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;
}
-/**
- * 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
+/*
+ * 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.
*/
-int umount_by_cdev(struct cdev *cdev)
+static inline int step_into(struct nameidata *nd, struct path *path,
+ int flags, struct inode *inode)
{
- struct fs_device_d *fs;
- struct fs_device_d *fs_tmp;
- int first_error = 0;
+ if (!(flags & WALK_MORE) && nd->depth)
+ put_link(nd);
- for_each_fs_device_safe(fs_tmp, fs) {
- int ret;
+ 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;
+ }
- 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 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 first_error;
+ return step_into(nd, &path, flags, d_inode(path.dentry));
}
-EXPORT_SYMBOL(umount_by_cdev);
-int umount(const char *pathname)
+static int component_len(const char *name, char separator)
{
- struct fs_device_d *fsdev = NULL, *f;
- char *p = normalise_path(pathname);
+ int len = 0;
- for_each_fs_device(f) {
- if (!strcmp(p, f->path)) {
- fsdev = f;
- break;
- }
+ 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;
}
- if (!fsdev) {
- struct cdev *cdev = cdev_open(p, O_RDWR);
+ result->refcnt = 1;
- if (cdev) {
- free(p);
- cdev_close(cdev);
- return umount_by_cdev(cdev);
+ 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;
}
+}
- free(p);
+static const char *path_init(struct nameidata *nd, unsigned flags)
+{
+ const char *s = nd->name->name;
- if (f == fs_dev_root && !list_is_singular(&fs_device_list)) {
- errno = EBUSY;
- return -EBUSY;
+ 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;
}
- if (!fsdev) {
- errno = EFAULT;
- return -EFAULT;
+ 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;
+}
- return fsdev_umount(fsdev);
+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;
}
-EXPORT_SYMBOL(umount);
-DIR *opendir(const char *pathname)
+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)
{
- DIR *dir = NULL;
struct fs_device_d *fsdev;
- struct fs_driver_d *fsdrv;
- char *p = canonicalize_path(pathname);
- char *freep = p;
+ struct path path;
int ret;
- struct stat s;
- ret = stat(pathname, &s);
+ 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;
- if (!S_ISDIR(s.st_mode)) {
- ret = -ENOTDIR;
+ 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;
}
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENOENT;
+ 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;
}
- fsdrv = fsdev->driver;
- debug("opendir: fsdrv: %p\n",fsdrv);
+ 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;
+ }
- dir = fsdrv->opendir(&fsdev->dev, p);
- if (dir) {
- dir->dev = &fsdev->dev;
- dir->fsdrv = fsdrv;
+ 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;
+
+ dput(dentry);
+
+ error = lookup_last(&nd);
+ if (error <= 0)
+ break;
+
+ s = trailing_symlink(&nd);
+ if (IS_ERR(s)) {
+ error = PTR_ERR(s);
+ break;
+ }
+ }
+
+ terminate_walk(&nd);
+ putname(nd.name);
+
+ if (error)
+ return error;
+
+ 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 {
- /*
- * FIXME: The fs drivers should return ERR_PTR here so that
- * we are able to forward the error
- */
- ret = -EINVAL;
+ if (d_is_dir(dentry) && !dentry_is_tftp(dentry)) {
+ error = -EISDIR;
+ goto out1;
+ }
+ }
+
+ inode = d_inode(dentry);
+
+ f = get_file();
+ if (!f) {
+ error = -EMFILE;
+ goto out1;
}
+ f->path = xstrdup(pathname);
+ f->dentry = dentry;
+ f->f_inode = iget(inode);
+ f->flags = flags;
+ f->size = inode->i_size;
+
+ 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;
+ }
+
+ if (flags & O_TRUNC) {
+ error = fsdrv->truncate(&fsdev->dev, f, 0);
+ f->size = 0;
+ inode->i_size = 0;
+ if (error)
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ f->pos = f->size;
+
+ return f->no;
+
out:
- free(freep);
+ put_file(f);
+out1:
+
+ if (error)
+ errno = -error;
+ return error;
+}
+EXPORT_SYMBOL(open);
+int unlink(const char *pathname)
+{
+ int ret;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct path path;
+
+ ret = filename_lookup(AT_FDCWD, getname(pathname), 0, &path);
+ if (ret)
+ goto out;
+
+ dentry = path.dentry;
+
+ if (d_is_dir(dentry)) {
+ ret = -EISDIR;
+ goto out_put;
+ }
+
+ inode = d_inode(dentry->d_parent);
+
+ if (!inode->i_op->unlink) {
+ ret = -EPERM;
+ goto out_put;
+ }
+
+ ret = inode->i_op->unlink(inode, dentry);
+ if (ret)
+ goto out_put;
+
+ d_delete(dentry);
+
+out_put:
+ path_put(&path);
+out:
if (ret)
errno = -ret;
+ return ret;
+}
+EXPORT_SYMBOL(unlink);
- return dir;
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ if (!dir->i_op->symlink)
+ return -EPERM;
+
+ return dir->i_op->symlink(dir, dentry, oldname);
}
-EXPORT_SYMBOL(opendir);
-struct dirent *readdir(DIR *dir)
+int symlink(const char *pathname, const char *newpath)
{
- struct dirent *ent;
+ struct dentry *dentry;
+ struct path path;
+ int error;
+ unsigned int lookup_flags = LOOKUP_DIRECTORY;
- if (!dir)
- return NULL;
+ dentry = filename_create(AT_FDCWD, getname(newpath), &path, lookup_flags);
+ if (IS_ERR(dentry)) {
+ error = PTR_ERR(dentry);
+ goto out;
+ }
- ent = dir->fsdrv->readdir(dir->dev, dir);
+ error = vfs_symlink(path.dentry->d_inode, dentry, pathname);
+out:
+ if (error)
+ errno = -error;
- if (!ent)
- errno = EBADF;
+ return error;
+}
+EXPORT_SYMBOL(symlink);
+
+static void release_dir(DIR *d)
+{
+ struct readdir_entry *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &d->entries, list) {
+ free(entry);
+ }
- return ent;
+ free(d);
}
-EXPORT_SYMBOL(readdir);
-int closedir(DIR *dir)
+DIR *opendir(const char *pathname)
{
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 (!dir) {
- errno = EBADF;
- return -EBADF;
+ dir = path.dentry;
+
+ if (d_is_negative(dir)) {
+ ret = -ENOENT;
+ goto out_put;
}
- ret = dir->fsdrv->closedir(dir->dev, dir);
+ inode = d_inode(dir);
+
+ 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 ret;
+ path_put(&path);
+
+ return d;
+
+out_release:
+ release_dir(d);
+out_put:
+ path_put(&path);
+out:
+ errno = -ret;
+
+ return NULL;
}
-EXPORT_SYMBOL(closedir);
+EXPORT_SYMBOL(opendir);
-int stat(const char *filename, struct stat *s)
+int closedir(DIR *dir)
{
- char *path = canonicalize_path(filename);
int ret;
- if (IS_ERR(path))
- return PTR_ERR(path);
-
- ret = lstat(path, s);
+ if (!dir) {
+ errno = EBADF;
+ return -EBADF;
+ }
- free(path);
+ release_dir(dir);
return ret;
}
-EXPORT_SYMBOL(stat);
+EXPORT_SYMBOL(closedir);
-static int __lstat(const char *filename, struct stat *s)
+int readlink(const char *pathname, char *buf, size_t bufsiz)
{
- struct fs_driver_d *fsdrv;
- struct fs_device_d *fsdev;
- char *f = normalise_path(filename);
- char *freep = f;
int ret;
+ struct dentry *dentry;
+ struct inode *inode;
+ const char *link;
+ struct path path = {};
- automount_mount(f, 1);
+ ret = filename_lookup(AT_FDCWD, getname(pathname), 0, &path);
+ if (ret)
+ goto out;
- memset(s, 0, sizeof(struct stat));
+ dentry = path.dentry;
- fsdev = get_fsdevice_by_path(f);
- if (!fsdev) {
- ret = -ENOENT;
- goto out;
+ if (!d_is_symlink(dentry)) {
+ ret = -EINVAL;
+ goto out_put;
}
- if (fsdev != fs_dev_root && strcmp(f, fsdev->path))
- f += strlen(fsdev->path);
- else
- fsdev = fs_dev_root;
+ inode = d_inode(dentry);
- fsdrv = fsdev->driver;
+ if (!inode->i_op->get_link) {
+ ret = -EPERM;
+ goto out_put;
+ }
- if (*f == 0)
- f = "/";
+ link = inode->i_op->get_link(dentry, inode);
+ if (IS_ERR(link)) {
+ ret = PTR_ERR(link);
+ goto out_put;
+ }
- ret = fsdrv->stat(&fsdev->dev, f, s);
-out:
- free(freep);
+ strncpy(buf, link, bufsiz);
+ ret = 0;
+out_put:
+ path_put(&path);
+out:
if (ret)
errno = -ret;
return ret;
}
+EXPORT_SYMBOL(readlink);
-int lstat(const char *filename, struct stat *s)
+static int stat_filename(const char *filename, struct stat *s, unsigned int flags)
{
- char *f = canonicalize_dir(filename);
int ret;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct path path = {};
+
+ ret = filename_lookup(AT_FDCWD, getname(filename), flags, &path);
+ if (ret)
+ goto out;
+
+ dentry = path.dentry;
- if (IS_ERR(f))
- return PTR_ERR(f);
+ if (d_is_negative(dentry)) {
+ ret = -ENOENT;
+ goto out_put;
+ }
- ret = __lstat(f, s);
+ inode = d_inode(dentry);
- free(f);
+ stat_inode(inode, s);
+ ret = 0;
+out_put:
+ path_put(&path);
+out:
return ret;
}
+
+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 stat_filename(filename, s, 0);
+}
EXPORT_SYMBOL(lstat);
-int fstat(int fd, struct stat *s)
+static char *__dpath(struct dentry *dentry, struct dentry *root)
{
- FILE *f;
- struct fs_device_d *fsdev;
+ char *res, *ppath;
- if (check_fd(fd))
- return -errno;
+ if (dentry == root)
+ return NULL;
+ if (dentry == d_root)
+ return NULL;
- f = &files[fd];
+ while (IS_ROOT(dentry)) {
+ struct fs_device_d *fsdev;
- fsdev = f->fsdev;
+ for_each_fs_device(fsdev) {
+ if (dentry == fsdev->vfsmount.mnt_root) {
+ dentry = fsdev->vfsmount.mountpoint;
+ break;
+ }
+ }
+ }
+
+ ppath = __dpath(dentry->d_parent, root);
+ if (ppath)
+ res = basprintf("%s/%s", ppath, dentry->name);
+ else
+ res = basprintf("/%s", dentry->name);
+ free(ppath);
- return fsdev->driver->stat(&fsdev->dev, f->path, s);
+ return res;
}
-EXPORT_SYMBOL(fstat);
-int mkdir (const char *pathname, mode_t mode)
+/**
+ * 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_driver_d *fsdrv;
- struct fs_device_d *fsdev;
- char *p = canonicalize_path(pathname);
- char *freep = p;
- int ret;
- struct stat s;
+ char *res;
- ret = parent_check_directory(p);
- if (ret)
- goto out;
+ if (dentry == root)
+ return strdup("/");
- ret = stat(pathname, &s);
- if (!ret) {
- ret = -EEXIST;
- goto out;
- }
+ res = __dpath(dentry, root);
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENOENT;
+ return res;
+}
+
+/**
+ * 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;
+
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+ if (ret)
goto out;
- }
- fsdrv = fsdev->driver;
- if (fsdrv->mkdir)
- ret = fsdrv->mkdir(&fsdev->dev, p);
- else
- ret = -EROFS;
+ res = dpath(path.dentry, d_root);
out:
- free(freep);
-
if (ret)
errno = -ret;
- return ret;
+ return res;
}
-EXPORT_SYMBOL(mkdir);
-int rmdir (const char *pathname)
+const char *getcwd(void)
{
- struct fs_driver_d *fsdrv;
- struct fs_device_d *fsdev;
- char *p = canonicalize_path(pathname);
- char *freep = p;
+ return cwd;
+}
+EXPORT_SYMBOL(getcwd);
+
+int chdir(const char *pathname)
+{
+ char *realpath;
+ struct path path;
int ret;
- struct stat s;
- ret = lstat(pathname, &s);
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
if (ret)
goto out;
- if (!S_ISDIR(s.st_mode)) {
+
+ if (!d_is_dir(path.dentry)) {
ret = -ENOTDIR;
goto out;
}
- if (!dir_is_empty(pathname)) {
- ret = -ENOTEMPTY;
- goto out;
- }
+ realpath = dpath(path.dentry, d_root);
+ strcpy(cwd, realpath);
+ free(realpath);
+ cwd_dentry = path.dentry;
+ cwd_mnt = path.mnt;
- fsdev = get_fs_device_and_root_path(&p);
- if (!fsdev) {
- ret = -ENODEV;
- goto out;
- }
- fsdrv = fsdev->driver;
+ ret = 0;
- if (fsdrv->rmdir)
- ret = fsdrv->rmdir(&fsdev->dev, p);
- else
- ret = -EROFS;
out:
- free(freep);
-
if (ret)
errno = -ret;
return ret;
}
-EXPORT_SYMBOL(rmdir);
+EXPORT_SYMBOL(chdir);
/*
- * 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.
+ * Mount a device to a directory.
+ * We do this by registering a new device on which the filesystem
+ * driver will match.
*/
-const char *cdev_get_mount_path(struct cdev *cdev)
+int mount(const char *device, const char *fsname, const char *pathname,
+ const char *fsoptions)
{
struct fs_device_d *fsdev;
+ int ret;
+ struct path path = {};
- for_each_fs_device(fsdev) {
- if (fsdev->cdev && fsdev->cdev == cdev)
- return fsdev->path;
+ if (d_root) {
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+ if (ret)
+ goto out;
+
+ if (!d_is_dir(path.dentry)) {
+ ret = -ENOTDIR;
+ goto out;
+ }
+
+ if (IS_ROOT(path.dentry) || d_mountpoint(path.dentry)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ if (pathname[0] != '/' || pathname[1])
+ return -EINVAL;
}
- return NULL;
+ 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) {
+ 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->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;
+
+ if (!fsdev->dev.driver) {
+ /*
+ * Driver didn't accept the device or no driver for this
+ * device. Bail out
+ */
+ ret = -EINVAL;
+ 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);
+
+ fsdev_set_linux_rootarg(fsdev, str);
+ }
+
+ path_put(&path);
+
+ return 0;
+
+err_no_driver:
+ unregister_device(&fsdev->dev);
+err_register:
+ fs_remove(&fsdev->dev);
+out:
+ path_put(&path);
+
+ errno = -ret;
+
+ return ret;
}
+EXPORT_SYMBOL(mount);
-/*
- * 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)
+int umount(const char *pathname)
{
- const char *path;
- char *newpath, *devpath;
+ struct fs_device_d *fsdev = NULL, *f;
+ struct path path = {};
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;
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+ if (ret)
+ return ret;
- newpath = basprintf("/mnt/%s", cdev->name);
- make_directory(newpath);
+ if (path.dentry == d_root) {
+ path_put(&path);
+ return -EBUSY;
+ }
- devpath = basprintf("/dev/%s", cdev->name);
+ for_each_fs_device(f) {
+ if (path.dentry == f->vfsmount.mnt_root) {
+ fsdev = f;
+ break;
+ }
+ }
- ret = mount(devpath, NULL, newpath, fsoptions);
+ path_put(&path);
- free(devpath);
+ if (!fsdev) {
+ struct cdev *cdev = cdev_open(pathname, O_RDWR);
- if (ret) {
- free(newpath);
- return ERR_PTR(ret);
+ if (cdev) {
+ cdev_close(cdev);
+ return umount_by_cdev(cdev);
+ }
}
- return cdev_get_mount_path(cdev);
+ if (!fsdev) {
+ errno = EFAULT;
+ return -EFAULT;
+ }
+
+ return fsdev_umount(fsdev);
}
+EXPORT_SYMBOL(umount);
-/*
- * mount_all - iterate over block devices and mount all devices we are able to
- */
-void mount_all(void)
+#ifdef CONFIG_FS_AUTOMOUNT
+
+#define AUTOMOUNT_IS_FILE (1 << 0)
+
+struct automount {
+ char *path;
+ struct dentry *dentry;
+ char *cmd;
+ struct list_head list;
+ unsigned int flags;
+};
+
+static LIST_HEAD(automount_list);
+
+static void automount_remove_dentry(struct dentry *dentry)
{
- struct device_d *dev;
- struct block_device *bdev;
+ struct automount *am;
- if (!IS_ENABLED(CONFIG_BLOCK))
+ list_for_each_entry(am, &automount_list, list) {
+ if (dentry == am->dentry)
+ goto found;
+ }
+
+ return;
+found:
+ list_del(&am->list);
+ dput(am->dentry);
+ free(am->path);
+ free(am->cmd);
+ free(am);
+}
+
+void automount_remove(const char *pathname)
+{
+ struct path path;
+ int ret;
+
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+ if (ret)
return;
- for_each_device(dev)
- device_detect(dev);
+ automount_remove_dentry(path.dentry);
- for_each_block_device(bdev) {
- struct cdev *cdev = &bdev->cdev;
+ path_put(&path);
+}
+EXPORT_SYMBOL(automount_remove);
- list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list)
- cdev_mount_default(cdev, NULL);
+int automount_add(const char *pathname, const char *cmd)
+{
+ struct automount *am = xzalloc(sizeof(*am));
+ struct path path;
+ int ret;
+
+ ret = filename_lookup(AT_FDCWD, getname(pathname), LOOKUP_FOLLOW, &path);
+ if (ret)
+ return ret;
+
+ if (!d_is_dir(path.dentry)) {
+ ret = -ENOTDIR;
+ goto out;
}
+
+ am->path = dpath(path.dentry, d_root);
+ am->dentry = dget(path.dentry);
+ am->dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+ am->cmd = xstrdup(cmd);
+
+ automount_remove_dentry(am->dentry);
+
+ list_add_tail(&am->list, &automount_list);
+
+ ret = 0;
+out:
+ path_put(&path);
+
+ return ret;
}
+EXPORT_SYMBOL(automount_add);
-void fsdev_set_linux_rootarg(struct fs_device_d *fsdev, const char *str)
+void cdev_create_default_automount(struct cdev *cdev)
{
- fsdev->linux_rootarg = xstrdup(str);
+ char *path, *cmd;
- dev_add_param_fixed(&fsdev->dev, "linux.bootargs", fsdev->linux_rootarg);
+ path = basprintf("/mnt/%s", cdev->name);
+ cmd = basprintf("mount %s", cdev->name);
+
+ make_directory(path);
+ automount_add(path, cmd);
+
+ free(cmd);
+ free(path);
}
-/**
- * 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)
+void automount_print(void)
{
- struct fs_device_d *fsdev;
- const char *str;
+ struct automount *am;
- fsdev = get_fsdevice_by_path(path);
- if (!fsdev)
- return ERR_PTR(-EINVAL);
+ list_for_each_entry(am, &automount_list, list)
+ printf("%-20s %s\n", am->path, am->cmd);
+}
+EXPORT_SYMBOL(automount_print);
- str = dev_get_param(&fsdev->dev, "linux.bootargs");
- if (!str)
- return ERR_PTR(-ENOSYS);
+static int automount_mount(struct dentry *dentry)
+{
+ struct automount *am;
+ int ret = -ENOENT;
+ static int in_automount;
- return xstrdup(str);
+ if (in_automount)
+ return -EINVAL;
+
+ in_automount++;
+
+ list_for_each_entry(am, &automount_list, list) {
+ if (am->dentry != dentry)
+ continue;
+
+ setenv("automount_path", am->path);
+ export("automount_path");
+ ret = run_command(am->cmd);
+ setenv("automount_path", NULL);
+
+ if (ret) {
+ printf("running automount command '%s' failed\n",
+ am->cmd);
+ ret = -ENODEV;
+ }
+
+ break;
+ }
+
+ in_automount--;
+
+ return ret;
}
-/**
- * __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
+BAREBOX_MAGICVAR(automount_path, "mountpath passed to automount scripts");
+
+#else
+static int automount_mount(struct dentry *dentry)
+{
+ return 0;
+}
+#endif /* CONFIG_FS_AUTOMOUNT */
+
+#ifdef DEBUG
+
+/*
+ * Some debug commands, helpful to debug the dcache implementation
*/
-bool __is_tftp_fs(const char *path)
+#include <command.h>
+
+static int do_lookup_dentry(int argc, char *argv[])
+{
+ struct path path;
+ int ret;
+ char *canon;
+ char mode[16];
+
+ if (argc < 2)
+ return COMMAND_ERROR_USAGE;
+
+ ret = filename_lookup(AT_FDCWD, getname(argv[1]), 0, &path);
+ if (ret) {
+ printf("Cannot lookup path \"%s\": %s\n",
+ argv[1], strerror(-ret));
+ return 1;
+ }
+
+ canon = canonicalize_path(argv[1]);
+
+ 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);
+
+ path_put(&path);
+ free(canon);
+
+ return 0;
+}
+
+BAREBOX_CMD_START(lookup_dentry)
+ .cmd = do_lookup_dentry,
+BAREBOX_CMD_END
+
+static struct dentry *debug_follow_mount(struct dentry *dentry)
{
struct fs_device_d *fsdev;
+ unsigned managed = dentry->d_flags;
- fsdev = get_fsdevice_by_path(path);
- if (!fsdev)
- return false;
+ if (managed & DCACHE_MOUNTED) {
+ for_each_fs_device(fsdev) {
+ if (dentry == fsdev->vfsmount.mountpoint)
+ return fsdev->vfsmount.mnt_root;
+ }
+ return NULL;
+ } else {
+ return dentry;
+ }
+}
- if (strcmp(fsdev->driver->drv.name, "tftp"))
- return false;
+static void debug_dump_dentries(struct dentry *parent, int indent)
+{
+ int i;
+ struct dentry *dentry, *mp;
- return true;
+ 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);
+
+ mp = debug_follow_mount(parent);
+ if (mp != parent) {
+ for (i = 0; i < indent; i++)
+ printf("\t");
+ printf("MOUNT: ");
+
+ parent = mp;
+
+ goto again;
+ }
+
+ list_for_each_entry(dentry, &parent->d_subdirs, d_child)
+ debug_dump_dentries(dentry, indent + 1);
+}
+
+static int do_debug_fs_dump(int argc, char *argv[])
+{
+ debug_dump_dentries(d_root, 0);
+
+ 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/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,
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/ramfs.c b/fs/ramfs.c
index 7af885b324..7548bdac9f 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,
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 4c730e09e4..e2b7b8d5a1 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)
{
@@ -333,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,
+};
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,
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..8c230da8f3
--- /dev/null
+++ b/fs/ubifs/dir.c
@@ -0,0 +1,291 @@
+/* * 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"
+
+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__ */