From: John Groves <[email protected]> The famfs inode and super operations are generic other than show_options, evict_inode and setattr (which prevents truncation..
This commit builds but is still too incomplete to run Signed-off-by: John Groves <[email protected]> --- fs/famfs/famfs_inode.c | 249 +++++++++++++++++++++++++++++++++++++- fs/famfs/famfs_internal.h | 6 + 2 files changed, 252 insertions(+), 3 deletions(-) diff --git a/fs/famfs/famfs_inode.c b/fs/famfs/famfs_inode.c index ad71e5e7a8e3..efc6b852eca0 100644 --- a/fs/famfs/famfs_inode.c +++ b/fs/famfs/famfs_inode.c @@ -29,6 +29,9 @@ #define FAMFS_DEFAULT_MODE 0755 +static const struct inode_operations famfs_file_inode_operations; +static const struct inode_operations famfs_dir_inode_operations; + static struct inode *famfs_get_inode( struct super_block *sb, const struct inode *dir, @@ -54,11 +57,11 @@ static struct inode *famfs_get_inode( init_special_inode(inode, mode, dev); break; case S_IFREG: - inode->i_op = NULL /* famfs_file_inode_operations */; + inode->i_op = &famfs_file_inode_operations; inode->i_fop = NULL /* &famfs_file_operations */; break; case S_IFDIR: - inode->i_op = NULL /* famfs_dir_inode_operations */; + inode->i_op = &famfs_dir_inode_operations; inode->i_fop = &simple_dir_operations; /* Directory inodes start off with i_nlink == 2 (for ".") */ @@ -72,6 +75,246 @@ static struct inode *famfs_get_inode( return inode; } +/*************************************************************************** + * famfs inode_operations + */ + +static int +famfs_setattr( + struct mnt_idmap *idmap, + struct dentry *dentry, + struct iattr *iattr) +{ + struct inode *inode = d_inode(dentry); + struct famfs_fs_info *fsi = inode->i_sb->s_fs_info; + + /* Resizing a famfs file (its size is pinned to the fmap) */ + if ((iattr->ia_valid & ATTR_SIZE) && + !famfs_opt_enabled(fsi, FAMFS_OPT_TRUNCATE) && + iattr->ia_size != i_size_read(inode)) + return -EPERM; + if ((iattr->ia_valid & ATTR_MODE) && + !famfs_opt_enabled(fsi, FAMFS_OPT_CHMOD)) + return -EPERM; + if ((iattr->ia_valid & (ATTR_UID | ATTR_GID)) && + !famfs_opt_enabled(fsi, FAMFS_OPT_CHOWN)) + return -EPERM; + if ((iattr->ia_valid & (ATTR_ATIME | ATTR_MTIME)) && + !famfs_opt_enabled(fsi, FAMFS_OPT_UTIMES)) + return -EPERM; + + return simple_setattr(idmap, dentry, iattr); +} + +static const struct inode_operations famfs_file_inode_operations = { + /* All generic */ + .setattr = famfs_setattr, + .getattr = simple_getattr, +}; + +/* + * Internal inode creation helper, shared by ->create, ->mkdir, ->mknod and + * ->symlink. Each of those callers is responsible for its own FAMFS_OPT_* + * permission check before getting here. + */ +static int +famfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, + umode_t mode, dev_t dev) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + struct timespec64 tv; + struct inode *inode; + + if (fsi->deverror) + return -ENODEV; + + inode = famfs_get_inode(dir->i_sb, dir, mode, dev); + if (!inode) + return -ENOSPC; + + d_make_persistent(dentry, inode); + tv = inode_set_ctime_current(inode); + inode_set_mtime_to_ts(inode, tv); + inode_set_atime_to_ts(inode, tv); + + return 0; +} + +static struct dentry *famfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + int rc; + + if (fsi->deverror) + return ERR_PTR(-ENODEV); + if (!famfs_opt_enabled(fsi, FAMFS_OPT_MKDIR)) + return ERR_PTR(-EPERM); + + rc = famfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); + if (rc) + return ERR_PTR(rc); + + inc_nlink(dir); + + return ERR_PTR(0); +} + +static int famfs_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + + if (fsi->deverror) + return -ENODEV; + if (!famfs_opt_enabled(fsi, FAMFS_OPT_CREATE)) + return -EPERM; + + return famfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFREG, 0); +} + +static int +famfs_mknod_op(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t dev) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + + if (!famfs_opt_enabled(fsi, FAMFS_OPT_MKNOD)) + return -EPERM; + + return famfs_mknod(idmap, dir, dentry, mode, dev); +} + +static int +famfs_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *symname) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + struct inode *inode; + int len, rc; + + if (fsi->deverror) + return -ENODEV; + if (!famfs_opt_enabled(fsi, FAMFS_OPT_SYMLINK)) + return -EPERM; + + inode = famfs_get_inode(dir->i_sb, dir, S_IFLNK | 0777, 0); + if (!inode) + return -ENOSPC; + + len = strlen(symname) + 1; + rc = page_symlink(inode, symname, len); + if (rc) { + iput(inode); + return rc; + } + + d_make_persistent(dentry, inode); + inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); + + return 0; +} + +static int +famfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + + if (!famfs_opt_enabled(fsi, FAMFS_OPT_LINK)) + return -EPERM; + + return simple_link(old_dentry, dir, dentry); +} + +static int famfs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + + /* A file with an fmap may only be unlinked when explicitly enabled */ + if (inode->i_private && !famfs_opt_enabled(fsi, FAMFS_OPT_UNLINK)) + return -EPERM; + + return simple_unlink(dir, dentry); +} + +static int famfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct famfs_fs_info *fsi = dir->i_sb->s_fs_info; + + if (!famfs_opt_enabled(fsi, FAMFS_OPT_RMDIR)) + return -EPERM; + + return simple_rmdir(dir, dentry); +} + +static int +famfs_rename( + struct mnt_idmap *idmap, + struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + struct famfs_fs_info *fsi = old_dir->i_sb->s_fs_info; + + if (!famfs_opt_enabled(fsi, FAMFS_OPT_RENAME)) + return -EPERM; + + return simple_rename(idmap, old_dir, old_dentry, new_dir, new_dentry, + flags); +} + +static const struct inode_operations famfs_dir_inode_operations = { + .create = famfs_create, + .lookup = simple_lookup, + .link = famfs_link, + .unlink = famfs_unlink, + .symlink = famfs_symlink, + .mkdir = famfs_mkdir, + .mknod = famfs_mknod_op, + .rmdir = famfs_rmdir, + .rename = famfs_rename, +}; + +/***************************************************************************** + * famfs super_operations + * + * TODO: implement a famfs_statfs() that shows size, free and available space, + * etc. + */ + +/* + * famfs_show_options() - Display the mount options in /proc/mounts. + */ +static int famfs_show_options(struct seq_file *m, struct dentry *root) +{ + struct famfs_fs_info *fsi = root->d_sb->s_fs_info; + + if (fsi->mount_opts.mode != FAMFS_DEFAULT_MODE) + seq_printf(m, ",mode=%o", fsi->mount_opts.mode); + + return 0; +} + +static void famfs_evict_inode(struct inode *inode) +{ + inode->i_private = NULL; + dax_break_layout_final(inode); + truncate_inode_pages_final(&inode->i_data); + clear_inode(inode); +} + +static const struct super_operations famfs_super_ops = { + .statfs = simple_statfs, + .drop_inode = inode_just_drop, + .show_options = famfs_show_options, + .evict_inode = famfs_evict_inode, +}; + +/*****************************************************************************/ + /* * famfs dax_operations (for famfs-mode dax) */ @@ -305,7 +548,7 @@ famfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = FAMFS_SUPER_MAGIC; - sb->s_op = NULL /* famfs_super_ops */; + sb->s_op = &famfs_super_ops; sb->s_time_gran = 1; } diff --git a/fs/famfs/famfs_internal.h b/fs/famfs/famfs_internal.h index ebb9c499cf69..485087588a11 100644 --- a/fs/famfs/famfs_internal.h +++ b/fs/famfs/famfs_internal.h @@ -70,6 +70,12 @@ struct famfs_fs_info { struct rw_semaphore devlist_sem; }; +/* This stub will be replaced in a later commit + * Note: the opt parameter is intentionally unused, and will be used by + * the replacement function when that commit lands + */ +#define famfs_opt_enabled(fsi, opt) (fsi != 0) + int lookup_daxdev(const char *pathname, dev_t *devno); int famfs_devlist_alloc(struct famfs_fs_info *fsi); int famfs_install_daxdev(struct famfs_fs_info *fsi, struct super_block *sb, -- 2.53.0

