]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ovl: support encoding non-decodable file handles
authorAmir Goldstein <amir73il@gmail.com>
Sun, 23 Apr 2023 16:02:04 +0000 (19:02 +0300)
committerAmir Goldstein <amir73il@gmail.com>
Sat, 12 Aug 2023 16:02:47 +0000 (19:02 +0300)
When all layers support file handles, we support encoding non-decodable
file handles (a.k.a. fid) even with nfs_export=off.

When file handles do not need to be decoded, we do not need to copy up
redirected lower directories on encode, and we encode also non-indexed
upper with lower file handle, so fid will not change on copy up.

This enables reporting fanotify events with file handles on overlayfs
with default config/mount options.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
fs/overlayfs/export.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/ovl_entry.h
fs/overlayfs/super.c

index 35680b6e175b86892b40666c2607dded2907113e..6d54f3fc24c55c40e3aa90e5736e61f0cc58c915 100644 (file)
@@ -174,28 +174,37 @@ static int ovl_connect_layer(struct dentry *dentry)
  * U = upper file handle
  * L = lower file handle
  *
- * (*) Connecting an overlay dir from real lower dentry is not always
+ * (*) Decoding a connected overlay dir from real lower dentry is not always
  * possible when there are redirects in lower layers and non-indexed merge dirs.
  * To mitigate those case, we may copy up the lower dir ancestor before encode
- * a lower dir file handle.
+ * of a decodable file handle for non-upper dir.
  *
  * Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
  */
 static int ovl_check_encode_origin(struct dentry *dentry)
 {
        struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
+       bool decodable = ofs->config.nfs_export;
+
+       /* Lower file handle for non-upper non-decodable */
+       if (!ovl_dentry_upper(dentry) && !decodable)
+               return 0;
 
        /* Upper file handle for pure upper */
        if (!ovl_dentry_lower(dentry))
                return 0;
 
        /*
-        * Upper file handle for non-indexed upper.
-        *
         * Root is never indexed, so if there's an upper layer, encode upper for
         * root.
         */
-       if (ovl_dentry_upper(dentry) &&
+       if (dentry == dentry->d_sb->s_root)
+               return 0;
+
+       /*
+        * Upper decodable file handle for non-indexed upper.
+        */
+       if (ovl_dentry_upper(dentry) && decodable &&
            !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
                return 0;
 
@@ -205,7 +214,7 @@ static int ovl_check_encode_origin(struct dentry *dentry)
         * ovl_connect_layer() will try to make origin's layer "connected" by
         * copying up a "connectable" ancestor.
         */
-       if (d_is_dir(dentry) && ovl_upper_mnt(ofs))
+       if (d_is_dir(dentry) && ovl_upper_mnt(ofs) && decodable)
                return ovl_connect_layer(dentry);
 
        /* Lower file handle for indexed and non-upper dir/non-dir */
@@ -876,3 +885,8 @@ const struct export_operations ovl_export_operations = {
        .get_name       = ovl_get_name,
        .get_parent     = ovl_get_parent,
 };
+
+/* encode_fh() encodes non-decodable file handles with nfs_export=off */
+const struct export_operations ovl_export_fid_operations = {
+       .encode_fh      = ovl_encode_fh,
+};
index a63e57447be975cd9031128553c279568486a182..c1c9ff62caad6952c019fabec4416056c0a1d7f8 100644 (file)
@@ -1311,7 +1311,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
                return false;
 
        /* No, if non-indexed upper with NFS export */
-       if (sb->s_export_op && upper)
+       if (ofs->config.nfs_export && upper)
                return false;
 
        /* Otherwise, hash by lower inode for fsnotify */
index 488bd14c2ed822a56c0548bd911c8f835a8b73e3..453610fb9bf960251d96929919610338d1efa1ad 100644 (file)
@@ -799,6 +799,7 @@ int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
 
 /* export.c */
 extern const struct export_operations ovl_export_operations;
+extern const struct export_operations ovl_export_fid_operations;
 
 /* super.c */
 int ovl_fill_super(struct super_block *sb, struct fs_context *fc);
index e999c73fb0c39387db73633b56e0ff87a6151747..7a5196c94d7527c2827e0456e07f840e23ce473b 100644 (file)
@@ -82,6 +82,7 @@ struct ovl_fs {
        const struct cred *creator_cred;
        bool tmpfile;
        bool noxattr;
+       bool nofh;
        /* Did we take the inuse lock? */
        bool upperdir_locked;
        bool workdir_locked;
index 9ed21f335adf78f8b323a839dfb428fc98284ea3..e56108ffe8aa5cebb5b99e5d99bdf5549d133156 100644 (file)
@@ -400,6 +400,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
                pr_warn("fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
                        name);
        }
+       ofs->nofh |= !fh_type;
        /*
         * Decoding origin file handle is required for persistent st_ino.
         * Without persistent st_ino, xino=auto falls back to xino=off.
@@ -818,6 +819,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
                ofs->config.index = false;
                pr_warn("upper fs does not support file handles, falling back to index=off.\n");
        }
+       ofs->nofh |= !fh_type;
 
        /* Check if upper fs has 32bit inode numbers */
        if (fh_type != FILEID_INO32_GEN)
@@ -1452,8 +1454,15 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
                ofs->config.nfs_export = false;
        }
 
+       /*
+        * Support encoding decodable file handles with nfs_export=on
+        * and encoding non-decodable file handles with nfs_export=off
+        * if all layers support file handles.
+        */
        if (ofs->config.nfs_export)
                sb->s_export_op = &ovl_export_operations;
+       else if (!ofs->nofh)
+               sb->s_export_op = &ovl_export_fid_operations;
 
        /* Never override disk quota limits or use reserved space */
        cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);