[PATCH V12 04/12] famfs: Introduce inode_operations and super_operations

John Groves <[email protected]> Mon, 3 Aug 2026 02:28:56 +0000
Newsgroups dev.linux.lists.nvdimm,dev.linux.lists.fuse-devel,org.kernel.vger.linux-cxl,org.kernel.vger.linux-doc,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <0100019fc573edf6-93c159df-6197-4bdf-9f1d-74b77a72ce7e-000000@email.amazonses.com>
From: John Groves <[email protected]>=0D=0A=0D=0AThe famfs inode and super =
operations are generic other than=0D=0Ashow_options, evict_inode and seta=
ttr (which prevents truncation..=0D=0A=0D=0AThis commit builds but is sti=
ll too incomplete to run=0D=0A=0D=0ASigned-off-by: John Groves <john@grov=
es.net>=0D=0A---=0D=0A fs/famfs/famfs_inode.c    | 249 ++++++++++++++++++=
+++++++++++++++++++-=0D=0A fs/famfs/famfs_internal.h |   6 +=0D=0A 2 file=
s changed, 252 insertions(+), 3 deletions(-)=0D=0A=0D=0Adiff --git a/fs/f=
amfs/famfs_inode.c b/fs/famfs/famfs_inode.c=0D=0Aindex ad71e5e7a8e3..efc6=
b852eca0 100644=0D=0A--- a/fs/famfs/famfs_inode.c=0D=0A+++ b/fs/famfs/fam=
fs_inode.c=0D=0A@@ -29,6 +29,9 @@=0D=0A=20=0D=0A #define FAMFS_DEFAULT_MO=
DE=090755=0D=0A=20=0D=0A+static const struct inode_operations famfs_file_=
inode_operations;=0D=0A+static const struct inode_operations famfs_dir_in=
ode_operations;=0D=0A+=0D=0A static struct inode *famfs_get_inode(=0D=0A =
=09=09=09struct super_block *sb,=0D=0A =09=09=09const struct inode *dir,=0D=
=0A@@ -54,11 +57,11 @@ static struct inode *famfs_get_inode(=0D=0A =09=09=
init_special_inode(inode, mode, dev);=0D=0A =09=09break;=0D=0A =09case S_=
IFREG:=0D=0A-=09=09inode->i_op =3D NULL /* famfs_file_inode_operations */=
;=0D=0A+=09=09inode->i_op =3D &famfs_file_inode_operations;=0D=0A =09=09i=
node->i_fop =3D NULL /* &famfs_file_operations */;=0D=0A =09=09break;=0D=0A=
 =09case S_IFDIR:=0D=0A-=09=09inode->i_op =3D NULL /* famfs_dir_inode_ope=
rations */;=0D=0A+=09=09inode->i_op =3D &famfs_dir_inode_operations;=0D=0A=
 =09=09inode->i_fop =3D &simple_dir_operations;=0D=0A=20=0D=0A =09=09/* D=
irectory inodes start off with i_nlink =3D=3D 2 (for ".") */=0D=0A@@ -72,=
6 +75,246 @@ static struct inode *famfs_get_inode(=0D=0A =09return inode;=
=0D=0A }=0D=0A=20=0D=0A+/************************************************=
***************************=0D=0A+ * famfs inode_operations=0D=0A+ */=0D=0A=
+=0D=0A+static int=0D=0A+famfs_setattr(=0D=0A+=09struct mnt_idmap *idmap,=
=0D=0A+=09struct dentry *dentry,=0D=0A+=09struct iattr *iattr)=0D=0A+{=0D=
=0A+=09struct inode *inode =3D d_inode(dentry);=0D=0A+=09struct famfs_fs_=
info *fsi =3D inode->i_sb->s_fs_info;=0D=0A+=0D=0A+=09/* Resizing a famfs=
 file (its size is pinned to the fmap) */=0D=0A+=09if ((iattr->ia_valid &=
 ATTR_SIZE) &&=0D=0A+=09    !famfs_opt_enabled(fsi, FAMFS_OPT_TRUNCATE) &=
&=0D=0A+=09    iattr->ia_size !=3D i_size_read(inode))=0D=0A+=09=09return=
 -EPERM;=0D=0A+=09if ((iattr->ia_valid & ATTR_MODE) &&=0D=0A+=09    !famf=
s_opt_enabled(fsi, FAMFS_OPT_CHMOD))=0D=0A+=09=09return -EPERM;=0D=0A+=09=
if ((iattr->ia_valid & (ATTR_UID | ATTR_GID)) &&=0D=0A+=09    !famfs_opt_=
enabled(fsi, FAMFS_OPT_CHOWN))=0D=0A+=09=09return -EPERM;=0D=0A+=09if ((i=
attr->ia_valid & (ATTR_ATIME | ATTR_MTIME)) &&=0D=0A+=09    !famfs_opt_en=
abled(fsi, FAMFS_OPT_UTIMES))=0D=0A+=09=09return -EPERM;=0D=0A+=0D=0A+=09=
return simple_setattr(idmap, dentry, iattr);=0D=0A+}=0D=0A+=0D=0A+static =
const struct inode_operations famfs_file_inode_operations =3D {=0D=0A+=09=
/* All generic */=0D=0A+=09.setattr=09   =3D famfs_setattr,=0D=0A+=09.get=
attr=09   =3D simple_getattr,=0D=0A+};=0D=0A+=0D=0A+/*=0D=0A+ * Internal =
inode creation helper, shared by ->create, ->mkdir, ->mknod and=0D=0A+ * =
->symlink. Each of those callers is responsible for its own FAMFS_OPT_*=0D=
=0A+ * permission check before getting here.=0D=0A+ */=0D=0A+static int=0D=
=0A+famfs_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry=
 *dentry,=0D=0A+=09    umode_t mode, dev_t dev)=0D=0A+{=0D=0A+=09struct f=
amfs_fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=09struct timespec64 tv=
;=0D=0A+=09struct inode *inode;=0D=0A+=0D=0A+=09if (fsi->deverror)=0D=0A+=
=09=09return -ENODEV;=0D=0A+=0D=0A+=09inode =3D famfs_get_inode(dir->i_sb=
, dir, mode, dev);=0D=0A+=09if (!inode)=0D=0A+=09=09return -ENOSPC;=0D=0A=
+=0D=0A+=09d_make_persistent(dentry, inode);=0D=0A+=09tv =3D inode_set_ct=
ime_current(inode);=0D=0A+=09inode_set_mtime_to_ts(inode, tv);=0D=0A+=09i=
node_set_atime_to_ts(inode, tv);=0D=0A+=0D=0A+=09return 0;=0D=0A+}=0D=0A+=
=0D=0A+static struct dentry *famfs_mkdir(struct mnt_idmap *idmap, struct =
inode *dir,=0D=0A+=09=09struct dentry *dentry, umode_t mode)=0D=0A+{=0D=0A=
+=09struct famfs_fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=09int rc;=0D=
=0A+=0D=0A+=09if (fsi->deverror)=0D=0A+=09=09return ERR_PTR(-ENODEV);=0D=0A=
+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_MKDIR))=0D=0A+=09=09return ERR_=
PTR(-EPERM);=0D=0A+=0D=0A+=09rc =3D famfs_mknod(&nop_mnt_idmap, dir, dent=
ry, mode | S_IFDIR, 0);=0D=0A+=09if (rc)=0D=0A+=09=09return ERR_PTR(rc);=0D=
=0A+=0D=0A+=09inc_nlink(dir);=0D=0A+=0D=0A+=09return ERR_PTR(0);=0D=0A+}=0D=
=0A+=0D=0A+static int famfs_create(struct mnt_idmap *idmap, struct inode =
*dir,=0D=0A+=09=09=09struct dentry *dentry, umode_t mode, bool excl)=0D=0A=
+{=0D=0A+=09struct famfs_fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=0D=
=0A+=09if (fsi->deverror)=0D=0A+=09=09return -ENODEV;=0D=0A+=09if (!famfs=
_opt_enabled(fsi, FAMFS_OPT_CREATE))=0D=0A+=09=09return -EPERM;=0D=0A+=0D=
=0A+=09return famfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFREG, 0)=
;=0D=0A+}=0D=0A+=0D=0A+static int=0D=0A+famfs_mknod_op(struct mnt_idmap *=
idmap, struct inode *dir,=0D=0A+=09       struct dentry *dentry, umode_t =
mode, dev_t dev)=0D=0A+{=0D=0A+=09struct famfs_fs_info *fsi =3D dir->i_sb=
->s_fs_info;=0D=0A+=0D=0A+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_MKNOD)=
)=0D=0A+=09=09return -EPERM;=0D=0A+=0D=0A+=09return famfs_mknod(idmap, di=
r, dentry, mode, dev);=0D=0A+}=0D=0A+=0D=0A+static int=0D=0A+famfs_symlin=
k(struct mnt_idmap *idmap, struct inode *dir,=0D=0A+=09      struct dentr=
y *dentry, const char *symname)=0D=0A+{=0D=0A+=09struct famfs_fs_info *fs=
i =3D dir->i_sb->s_fs_info;=0D=0A+=09struct inode *inode;=0D=0A+=09int le=
n, rc;=0D=0A+=0D=0A+=09if (fsi->deverror)=0D=0A+=09=09return -ENODEV;=0D=0A=
+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_SYMLINK))=0D=0A+=09=09return -E=
PERM;=0D=0A+=0D=0A+=09inode =3D famfs_get_inode(dir->i_sb, dir, S_IFLNK |=
 0777, 0);=0D=0A+=09if (!inode)=0D=0A+=09=09return -ENOSPC;=0D=0A+=0D=0A+=
=09len =3D strlen(symname) + 1;=0D=0A+=09rc =3D page_symlink(inode, symna=
me, len);=0D=0A+=09if (rc) {=0D=0A+=09=09iput(inode);=0D=0A+=09=09return =
rc;=0D=0A+=09}=0D=0A+=0D=0A+=09d_make_persistent(dentry, inode);=0D=0A+=09=
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));=0D=0A+=0D=0A+=09=
return 0;=0D=0A+}=0D=0A+=0D=0A+static int=0D=0A+famfs_link(struct dentry =
*old_dentry, struct inode *dir, struct dentry *dentry)=0D=0A+{=0D=0A+=09s=
truct famfs_fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=0D=0A+=09if (!f=
amfs_opt_enabled(fsi, FAMFS_OPT_LINK))=0D=0A+=09=09return -EPERM;=0D=0A+=0D=
=0A+=09return simple_link(old_dentry, dir, dentry);=0D=0A+}=0D=0A+=0D=0A+=
static int famfs_unlink(struct inode *dir, struct dentry *dentry)=0D=0A+{=
=0D=0A+=09struct inode *inode =3D d_inode(dentry);=0D=0A+=09struct famfs_=
fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=0D=0A+=09/* A file with an =
fmap may only be unlinked when explicitly enabled */=0D=0A+=09if (inode->=
i_private && !famfs_opt_enabled(fsi, FAMFS_OPT_UNLINK))=0D=0A+=09=09retur=
n -EPERM;=0D=0A+=0D=0A+=09return simple_unlink(dir, dentry);=0D=0A+}=0D=0A=
+=0D=0A+static int famfs_rmdir(struct inode *dir, struct dentry *dentry)=0D=
=0A+{=0D=0A+=09struct famfs_fs_info *fsi =3D dir->i_sb->s_fs_info;=0D=0A+=
=0D=0A+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_RMDIR))=0D=0A+=09=09retur=
n -EPERM;=0D=0A+=0D=0A+=09return simple_rmdir(dir, dentry);=0D=0A+}=0D=0A=
+=0D=0A+static int=0D=0A+famfs_rename(=0D=0A+=09struct mnt_idmap *idmap,=0D=
=0A+=09struct inode *old_dir,=0D=0A+=09struct dentry *old_dentry,=0D=0A+=09=
struct inode *new_dir,=0D=0A+=09struct dentry *new_dentry,=0D=0A+=09unsig=
ned int flags)=0D=0A+{=0D=0A+=09struct famfs_fs_info *fsi =3D old_dir->i_=
sb->s_fs_info;=0D=0A+=0D=0A+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_RENA=
ME))=0D=0A+=09=09return -EPERM;=0D=0A+=0D=0A+=09return simple_rename(idma=
p, old_dir, old_dentry, new_dir, new_dentry,=0D=0A+=09=09=09     flags);=0D=
=0A+}=0D=0A+=0D=0A+static const struct inode_operations famfs_dir_inode_o=
perations =3D {=0D=0A+=09.create=09=09=3D famfs_create,=0D=0A+=09.lookup=09=
=09=3D simple_lookup,=0D=0A+=09.link=09=09=3D famfs_link,=0D=0A+=09.unlin=
k=09=09=3D famfs_unlink,=0D=0A+=09.symlink=09=3D famfs_symlink,=0D=0A+=09=
=2Emkdir=09=09=3D famfs_mkdir,=0D=0A+=09.mknod=09=09=3D famfs_mknod_op,=0D=
=0A+=09.rmdir=09=09=3D famfs_rmdir,=0D=0A+=09.rename=09=09=3D famfs_renam=
e,=0D=0A+};=0D=0A+=0D=0A+/***********************************************=
******************************=0D=0A+ * famfs super_operations=0D=0A+ *=0D=
=0A+ * TODO: implement a famfs_statfs() that shows size, free and availab=
le space,=0D=0A+ * etc.=0D=0A+ */=0D=0A+=0D=0A+/*=0D=0A+ * famfs_show_opt=
ions() - Display the mount options in /proc/mounts.=0D=0A+ */=0D=0A+stati=
c int famfs_show_options(struct seq_file *m, struct dentry *root)=0D=0A+{=
=0D=0A+=09struct famfs_fs_info *fsi =3D root->d_sb->s_fs_info;=0D=0A+=0D=0A=
+=09if (fsi->mount_opts.mode !=3D FAMFS_DEFAULT_MODE)=0D=0A+=09=09seq_pri=
ntf(m, ",mode=3D%o", fsi->mount_opts.mode);=0D=0A+=0D=0A+=09return 0;=0D=0A=
+}=0D=0A+=0D=0A+static void famfs_evict_inode(struct inode *inode)=0D=0A+=
{=0D=0A+=09inode->i_private =3D NULL;=0D=0A+=09dax_break_layout_final(ino=
de);=0D=0A+=09truncate_inode_pages_final(&inode->i_data);=0D=0A+=09clear_=
inode(inode);=0D=0A+}=0D=0A+=0D=0A+static const struct super_operations f=
amfs_super_ops =3D {=0D=0A+=09.statfs=09=09=3D simple_statfs,=0D=0A+=09.d=
rop_inode=09=3D inode_just_drop,=0D=0A+=09.show_options=09=3D famfs_show_=
options,=0D=0A+=09.evict_inode    =3D famfs_evict_inode,=0D=0A+};=0D=0A+=0D=
=0A+/********************************************************************=
*********/=0D=0A+=0D=0A /*=0D=0A  * famfs dax_operations (for famfs-mode =
dax)=0D=0A  */=0D=0A@@ -305,7 +548,7 @@ famfs_fill_super(struct super_blo=
ck *sb, struct fs_context *fc)=0D=0A =09sb->s_blocksize=09=09=3D PAGE_SIZ=
E;=0D=0A =09sb->s_blocksize_bits=09=3D PAGE_SHIFT;=0D=0A =09sb->s_magic=09=
=09=3D FAMFS_SUPER_MAGIC;=0D=0A-=09sb->s_op=09=09=3D NULL /* famfs_super_=
ops */;=0D=0A+=09sb->s_op=09=09=3D &famfs_super_ops;=0D=0A =09sb->s_time_=
gran=09=09=3D 1;=0D=0A }=0D=0A=20=0D=0Adiff --git a/fs/famfs/famfs_intern=
al.h b/fs/famfs/famfs_internal.h=0D=0Aindex ebb9c499cf69..485087588a11 10=
0644=0D=0A--- a/fs/famfs/famfs_internal.h=0D=0A+++ b/fs/famfs/famfs_inter=
nal.h=0D=0A@@ -70,6 +70,12 @@ struct famfs_fs_info {=0D=0A =09struct rw_s=
emaphore       devlist_sem;=0D=0A };=0D=0A=20=0D=0A+/* This stub will be =
replaced in a later commit=20=0D=0A+ * Note: the opt parameter is intenti=
onally unused, and will be used by=0D=0A+ * the replacement function when=
 that commit lands=0D=0A+ */=0D=0A+#define famfs_opt_enabled(fsi, opt) (f=
si !=3D 0)=0D=0A+=0D=0A int lookup_daxdev(const char *pathname, dev_t *de=
vno);=0D=0A int famfs_devlist_alloc(struct famfs_fs_info *fsi);=0D=0A int=
 famfs_install_daxdev(struct famfs_fs_info *fsi, struct super_block *sb,=0D=
=0A--=20=0D=0A2.53.0=0D=0A=0D=0A