diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/Kconfig | 18 | ||||
-rw-r--r-- | fs/Makefile | 3 | ||||
-rw-r--r-- | fs/cramfs/cramfs.c | 523 | ||||
-rw-r--r-- | fs/devfs.c | 150 | ||||
-rw-r--r-- | fs/ext4/ext_barebox.c | 279 | ||||
-rw-r--r-- | fs/ext4/ext_common.h | 3 | ||||
-rw-r--r-- | fs/fat/Kconfig | 1 | ||||
-rw-r--r-- | fs/fs.c | 3186 | ||||
-rw-r--r-- | fs/legacy.c | 315 | ||||
-rw-r--r-- | fs/libfs.c | 97 | ||||
-rw-r--r-- | fs/nfs.c | 542 | ||||
-rw-r--r-- | fs/pstore/Kconfig | 1 | ||||
-rw-r--r-- | fs/ramfs.c | 401 | ||||
-rw-r--r-- | fs/squashfs/Makefile | 2 | ||||
-rw-r--r-- | fs/squashfs/dir.c | 232 | ||||
-rw-r--r-- | fs/squashfs/inode.c | 9 | ||||
-rw-r--r-- | fs/squashfs/namei.c | 17 | ||||
-rw-r--r-- | fs/squashfs/squashfs.c | 186 | ||||
-rw-r--r-- | fs/squashfs/squashfs.h | 9 | ||||
-rw-r--r-- | fs/squashfs/super.c | 13 | ||||
-rw-r--r-- | fs/squashfs/symlink.c | 82 | ||||
-rw-r--r-- | fs/tftp.c | 97 | ||||
-rw-r--r-- | fs/ubifs/Makefile | 2 | ||||
-rw-r--r-- | fs/ubifs/dir.c | 291 | ||||
-rw-r--r-- | fs/ubifs/super.c | 153 | ||||
-rw-r--r-- | fs/ubifs/ubifs.c | 341 | ||||
-rw-r--r-- | fs/ubifs/ubifs.h | 7 |
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 @@ -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, +}; @@ -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, +}; @@ -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__ */ |