From: John Groves <[email protected]>

famfs denies most namespace, attribute and data operations by default
because the userspace log, not the kernel, is authoritative for a famfs
instance. Earlier commits already guard each such operation with a
famfs_opt_enabled(fsi, FAMFS_OPT_x) check backed by a permissive stub. This
commit defines the permission bitmap and makes those checks live.

Add:
 - FAMFS_OPT_* (uapi): a u64 permission bitmap, one bit per gated operation
   (create, mkdir, mknod, symlink, link, unlink, rmdir, rename, the four
   setattr components, data write, and MAP_CREATE), plus FAMFS_OPT_ALL. The
   FAMFS_OPT_XATTR bit is reserved - famfs has no xattr ops yet.
 - fsi->opts: a per-mount atomic64 bitmap initialized to FAMFS_OPT_DEFAULT,
   which sets famfs's default policy: create, mkdir, chmod, chown, utimes,
   write and MAP_CREATE are permitted; unlink of mapped files, link,
   symlink, mknod, rmdir, rename and truncate are denied.
 - the real famfs_opt_enabled() (replacing the stub), so every planted gate
   now consults fsi->opts.
 - FAMFSIOC_{GET,SET,CLEAR}_OPTS: read the bitmap, or enable/disable the
   bits set in a caller-supplied mask, returning the resulting bitmap.
   SET/CLEAR require CAP_SYS_ADMIN and reject unknown bits with -EINVAL;
   the bitmap is updated with atomic RMW so the checks stay lockless.
Signed-off-by: John Groves <[email protected]>
---
 fs/famfs/famfs_file.c            | 55 ++++++++++++++++++++++++++++++++
 fs/famfs/famfs_inode.c           |  1 +
 fs/famfs/famfs_internal.h        | 30 ++++++++++++++---
 include/uapi/linux/famfs_ioctl.h | 45 ++++++++++++++++++++++++++
 4 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c
index e11a55ecf8d7..abf049b32a4b 100644
--- a/fs/famfs/famfs_file.c
+++ b/fs/famfs/famfs_file.c
@@ -357,6 +357,49 @@ famfs_daxdev_open(struct file *file, void __user *arg)
        return rc;
 }
 
+/**
+ * famfs_get_opts() - FAMFSIOC_GET_OPTS: return the permission bitmap
+ */
+static long famfs_get_opts(struct famfs_fs_info *fsi, void __user *arg)
+{
+       struct famfs_ioc_opts o = { .opts = atomic64_read(&fsi->opts) };
+
+       if (copy_to_user(arg, &o, sizeof(o)))
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * famfs_modify_opts() - FAMFSIOC_SET_OPTS / FAMFSIOC_CLEAR_OPTS
+ * @set: true to enable (OR in) the requested bits, false to disable (mask out)
+ *
+ * The caller supplies a mask of FAMFS_OPT_* bits; the resulting bitmap is
+ * returned. Requires CAP_SYS_ADMIN since it changes mount-wide policy.
+ */
+static long famfs_modify_opts(struct famfs_fs_info *fsi, void __user *arg,
+                             bool set)
+{
+       struct famfs_ioc_opts o;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (copy_from_user(&o, arg, sizeof(o)))
+               return -EFAULT;
+       if (o.opts & ~FAMFS_OPT_ALL)
+               return -EINVAL;
+
+       if (set)
+               o.opts = atomic64_fetch_or(o.opts, &fsi->opts) | o.opts;
+       else
+               o.opts = atomic64_fetch_and(~o.opts, &fsi->opts) & ~o.opts;
+
+       if (copy_to_user(arg, &o, sizeof(o)))
+               return -EFAULT;
+
+       return 0;
+}
+
 /**
  * famfs_file_ioctl() - Top-level famfs file ioctl handler
  * @file: the file
@@ -378,6 +421,18 @@ famfs_file_ioctl(struct file *file, unsigned int cmd, 
unsigned long arg)
                rc = 0;
                break;
 
+       case FAMFSIOC_GET_OPTS:
+               rc = famfs_get_opts(fsi, (void __user *)arg);
+               break;
+
+       case FAMFSIOC_SET_OPTS:
+               rc = famfs_modify_opts(fsi, (void __user *)arg, true);
+               break;
+
+       case FAMFSIOC_CLEAR_OPTS:
+               rc = famfs_modify_opts(fsi, (void __user *)arg, false);
+               break;
+
        case FAMFSIOC_DAXDEV_OPEN:
                rc = famfs_daxdev_open(file, (void __user *)arg);
                break;
diff --git a/fs/famfs/famfs_inode.c b/fs/famfs/famfs_inode.c
index a6c3b4574e69..6cbd7d657fd8 100644
--- a/fs/famfs/famfs_inode.c
+++ b/fs/famfs/famfs_inode.c
@@ -717,6 +717,7 @@ static int famfs_init_fs_context(struct fs_context *fc)
                return -ENOMEM;
 
        init_rwsem(&fsi->devlist_sem);
+       atomic64_set(&fsi->opts, FAMFS_OPT_DEFAULT);
        fsi->mount_opts.mode = FAMFS_DEFAULT_MODE;
        fc->s_fs_info        = fsi;
        fc->ops              = &famfs_context_ops;
diff --git a/fs/famfs/famfs_internal.h b/fs/famfs/famfs_internal.h
index b5f9c8d0349f..26873162b4a0 100644
--- a/fs/famfs/famfs_internal.h
+++ b/fs/famfs/famfs_internal.h
@@ -12,11 +12,24 @@
 #define FAMFS_INTERNAL_H
 
 #include <linux/rwsem.h>
+#include <linux/atomic.h>
 #include <linux/bits.h>
 #include <linux/build_bug.h>
 
 #include <linux/famfs_ioctl.h>
 
+/*
+ * Default operation-permission bitmap (see FAMFS_OPT_* in the uapi header).
+ * This preserves famfs's historical behavior: file/dir creation, the fmap
+ * ioctl, data writes, and the non-resize setattr components are permitted;
+ * unlink of mapped files, link, symlink, mknod, rmdir, rename and truncate
+ * are denied until enabled via FAMFSIOC_SET_OPTS.
+ */
+#define FAMFS_OPT_DEFAULT      (FAMFS_OPT_CREATE | FAMFS_OPT_MKDIR | \
+                                FAMFS_OPT_CHMOD | FAMFS_OPT_CHOWN | \
+                                FAMFS_OPT_UTIMES | FAMFS_OPT_WRITE | \
+                                FAMFS_OPT_MAP_CREATE)
+
 extern const struct file_operations famfs_file_operations;
 
 /*
@@ -104,6 +117,8 @@ struct famfs_dax_devlist {
  * @famfs_fs_info
  *
  * @mount_opts:  The mount options
+ * @opts:        Operation-permission bitmap (FAMFS_OPT_*), adjusted at runtime
+ *               via the FAMFSIOC_{GET,SET,CLEAR}_OPTS ioctls
  * @deverror:    True if the dax device has called our notify_failure entry
  *               point, or if other "shutdown" conditions exist
  * @dax_devlist: Table of backing daxdevs (slot 0 is the mount primary)
@@ -111,16 +126,23 @@ struct famfs_dax_devlist {
  */
 struct famfs_fs_info {
        struct famfs_mount_opts   mount_opts;
+       atomic64_t                opts;
        bool                      deverror;
        struct famfs_dax_devlist *dax_devlist;
        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
+/*
+ * famfs_opt_enabled() - is operation permission @opt enabled for this mount?
+ *
+ * @opt is a single FAMFS_OPT_* bit; returns true if that operation is
+ * permitted. The bitmap is read locklessly (updated via atomic RMW by the
+ * FAMFSIOC_{SET,CLEAR}_OPTS ioctls).
  */
-#define famfs_opt_enabled(fsi, opt) (fsi != 0)
+static inline bool famfs_opt_enabled(struct famfs_fs_info *fsi, u64 opt)
+{
+       return !!(atomic64_read(&fsi->opts) & opt);
+}
 
 int lookup_daxdev(const char *pathname, dev_t *devno);
 int famfs_devlist_alloc(struct famfs_fs_info *fsi);
diff --git a/include/uapi/linux/famfs_ioctl.h b/include/uapi/linux/famfs_ioctl.h
index 751d8b033c2e..efe6ef263975 100644
--- a/include/uapi/linux/famfs_ioctl.h
+++ b/include/uapi/linux/famfs_ioctl.h
@@ -100,6 +100,48 @@ struct famfs_ioc_daxdev {
        __u32 flags;
 };
 
+/*
+ * Mount-wide operation permissions, queried and modified via the
+ * FAMFSIOC_{GET,SET,CLEAR}_OPTS ioctls. A set bit means the operation is
+ * permitted; a clear bit means it is rejected with -EPERM. famfs denies most
+ * of these by default because the userspace log, not the kernel, is
+ * authoritative for a famfs instance.
+ */
+#define FAMFS_OPT_CREATE       (1ULL << 0)  /* create a regular file        */
+#define FAMFS_OPT_MKDIR                (1ULL << 1)  /* mkdir                   
     */
+#define FAMFS_OPT_MKNOD                (1ULL << 2)  /* mknod a special file    
     */
+#define FAMFS_OPT_SYMLINK      (1ULL << 3)  /* create a symlink             */
+#define FAMFS_OPT_LINK         (1ULL << 4)  /* hard link                    */
+#define FAMFS_OPT_UNLINK       (1ULL << 5)  /* unlink a mapped file         */
+#define FAMFS_OPT_RMDIR                (1ULL << 6)  /* rmdir                   
     */
+#define FAMFS_OPT_RENAME       (1ULL << 7)  /* rename                       */
+#define FAMFS_OPT_CHMOD                (1ULL << 8)  /* setattr ATTR_MODE       
     */
+#define FAMFS_OPT_CHOWN                (1ULL << 9)  /* setattr ATTR_UID / 
ATTR_GID  */
+#define FAMFS_OPT_TRUNCATE     (1ULL << 10) /* setattr ATTR_SIZE (resize)   */
+#define FAMFS_OPT_UTIMES       (1ULL << 11) /* setattr ATTR_ATIME/ATTR_MTIME*/
+#define FAMFS_OPT_WRITE                (1ULL << 12) /* write file data         
     */
+#define FAMFS_OPT_XATTR                (1ULL << 13) /* set/remove xattrs 
(reserved) */
+#define FAMFS_OPT_MAP_CREATE   (1ULL << 14) /* attach an fmap (MAP_CREATE)  */
+
+#define FAMFS_OPT_ALL          (FAMFS_OPT_CREATE | FAMFS_OPT_MKDIR | \
+                                FAMFS_OPT_MKNOD | FAMFS_OPT_SYMLINK | \
+                                FAMFS_OPT_LINK | FAMFS_OPT_UNLINK | \
+                                FAMFS_OPT_RMDIR | FAMFS_OPT_RENAME | \
+                                FAMFS_OPT_CHMOD | FAMFS_OPT_CHOWN | \
+                                FAMFS_OPT_TRUNCATE | FAMFS_OPT_UTIMES | \
+                                FAMFS_OPT_WRITE | FAMFS_OPT_XATTR | \
+                                FAMFS_OPT_MAP_CREATE)
+
+/**
+ * struct famfs_ioc_opts - operation-permission bitmap
+ * @opts: for GET, the current bitmap is returned here. For SET/CLEAR, the
+ *        caller-supplied mask of bits to enable/disable on input, and the
+ *        resulting bitmap on return.
+ */
+struct famfs_ioc_opts {
+       __u64 opts;
+};
+
 #define FAMFSIOC_MAGIC 'u'
 
 /* famfs file ioctl opcodes */
@@ -111,5 +153,8 @@ struct famfs_ioc_daxdev {
  */
 #define FAMFSIOC_MAP_CREATE    _IOW(FAMFSIOC_MAGIC,  0x51, struct 
famfs_ioc_fmap_header)
 #define FAMFSIOC_DAXDEV_OPEN   _IOW(FAMFSIOC_MAGIC,  0x52, struct 
famfs_ioc_daxdev)
+#define FAMFSIOC_GET_OPTS      _IOR(FAMFSIOC_MAGIC,  0x53, struct 
famfs_ioc_opts)
+#define FAMFSIOC_SET_OPTS      _IOWR(FAMFSIOC_MAGIC, 0x54, struct 
famfs_ioc_opts)
+#define FAMFSIOC_CLEAR_OPTS    _IOWR(FAMFSIOC_MAGIC, 0x55, struct 
famfs_ioc_opts)
 
 #endif /* FAMFS_IOCTL_H */
-- 
2.53.0



Reply via email to