overlayfs: Delayed copy up of data
Subsystems (2)
v13 cover overlayfs: Delayed copy up of data
Hi,
Here are the V13 of metadata only copy up patches.
These apply on top of 4.16.0-rc7 kernel + "ovl: Bunch of ovl_lookup() path fixes" patch series.
Patches are also available here.
https://github.com/rhvgoyal/linux/commits/metacopy-v13
Changes from V12:
- Primarily took care of Amir's comments.
- Redirects are now placed on regular file also and that opened bunch of races. Fixed those issues.
- Posted few ovl_lookup() fixes and rebased these patches on top of the fixes. ("ovl: Bunch of ovl_lookup() path fixes")
- Now metacopy file with upper is also marked as OVL_TYPE_MERGED and rename code makes use of that assumption. (Suggested by Amir).
- Fixed an issue where OVL_TYPE_ORIGIN was being set even for broken hardlinks with metacopy.
- Fixed races w.r.t remove redirect path.
- During rename, set relative redirect if nlink=1, otherwise set absolute redirect on metacopy files.
Any comments and feedback is welcome.
Thanks
Vivek
Vivek Goyal (28):
ovl: Set OVL_INDEX flag in ovl_get_inode()
ovl: Initialize ovl_inode->redirect in ovl_get_inode()
ovl: Rename local variable locked to new_locked
ovl: Provide a mount option metacopy=on/off for metadata copyup
ovl: During copy up, first copy up metadata and then data
ovl: Move the copy up helpers to copy_up.c
ovl: Copy up only metadata during copy up where it makes sense
ovl: Add helper ovl_already_copied_up()
ovl: A new xattr OVL_XATTR_METACOPY for file on upper
ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
ovl: Copy up meta inode data from lowest data inode
ovl: Fix ovl_getattr() to get number of blocks from lower
ovl: Add helper ovl_dentry_lowerdata() to get lower data dentry
ovl: Do not expose metacopy only dentry from d_real()
ovl: Move some of ovl_nlink_start() functionality in ovl_nlink_prep()
ovl: Create locked version of ovl_nlink_start() and ovl_nlink_end()
ovl: During rename lock both source and target ovl_inode
ovl: Check redirects for metacopy files
ovl: Treat metacopy dentries as type OVL_PATH_MERGE
ovl: Do not set dentry type ORIGIN for broken hardlinks
ovl: Set redirect on metacopy files upon rename
ovl: Set redirect on upper inode when it is linked
ovl: Remove redirect when data of a metacopy file is copied up
ovl: Do not error if REDIRECT XATTR is missing
ovl: Use out_err insteada of out_nomem
ovl: Re-check redirect xattr during inode initialization
ovl: Verify a data dentry has been found for metacopy inode
ovl: Enable metadata only feature
Documentation/filesystems/overlayfs.txt | 30 +++- fs/overlayfs/Kconfig | 19 +++ fs/overlayfs/copy_up.c | 165 +++++++++++++++---- fs/overlayfs/dir.c | 166 ++++++++++++++++--- fs/overlayfs/export.c | 7 +- fs/overlayfs/inode.c | 140 +++++++++++----- fs/overlayfs/namei.c | 121 ++++++++++---- fs/overlayfs/overlayfs.h | 30 +++- fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 62 ++++++- fs/overlayfs/util.c | 281 +++++++++++++++++++++++++++++--- 11 files changed, 861 insertions(+), 161 deletions(-)
Signature · 1 line
2.13.6
PATCH v13 1/28 ovl: Set OVL_INDEX flag in ovl_get_inode()
All the inode/ovl_inode initialization should happen in ovl_get_inode(). This is especially useful when multiple dentries are pointing to same inode and inode is already in the cash. In that case, we don't have to initialize the OVL_INDEX. We don't even take ovl_inode->lock in ovl_lookup() and that can run into issues for the case of shared inode.
So move OVL_INDEX flag setting in ovl_get_inode().
--- fs/overlayfs/export.c | 3 --- fs/overlayfs/inode.c | 3 +++ fs/overlayfs/namei.c | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-)
fs/overlayfs/export.c +0 -3
@@ -311,9 +311,6 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,return ERR_CAST (inode); } -if (index) -ovl_set_flag (OVL_INDEX , inode); - dentry =d_find_any_alias (inode);if (!dentry) { dentry =d_alloc_anon (inode->i_sb);
fs/overlayfs/inode.c +3 -0
@@ -760,6 +760,9 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (upperdentry &&ovl_is_impuredir (upperdentry))ovl_set_flag (OVL_IMPURE , inode); +if (index) +ovl_set_flag (OVL_INDEX , inode); +/* Check for non-merge dir that may have whiteouts */ if (is_dir) { if (((upperdentry && lowerdentry) || numlower > 1) ||
fs/overlayfs/namei.c +0 -2
@@ -1005,8 +1005,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_free_oe;OVL_I (inode)->redirect = upperredirect; -if (index) -ovl_set_flag (OVL_INDEX , inode); }revert_creds (old_cred);
Signature · 1 line
2.13.6
Re: [PATCH v13 01/28] ovl: Set OVL_INDEX flag in ovl_get_inode()
Quoted 3 lines · Vivek Goyal
All the inode/ovl_inode initialization should happen in
ovl_get_inode(). This is especially useful when multiple dentries are pointing to same inode and inode is already in the cash. In that case, we don't have to
s/in the cash/in cache/
Quoted 6 lines
initialize the
OVL_INDEX. We don't even takeovl_inode->lockinovl_lookup()and that can run into issues for the case of shared inode.So move
OVL_INDEXflag setting inovl_get_inode().Signed-off-by Vivek Goyal <[email protected]>
Quoted 50 lines
--- fs/overlayfs/export.c | 3 --- fs/overlayfs/inode.c | 3 +++ fs/overlayfs/namei.c | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-)fs/overlayfs/export.c +0
-3@@ -311,9 +311,6 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,return ERR_CAST (inode); } -if (index) -ovl_set_flag (OVL_INDEX , inode); - dentry =d_find_any_alias (inode);if (!dentry) { dentry =d_alloc_anon (inode->i_sb);fs/overlayfs/inode.c +3
-0@@ -760,6 +760,9 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (upperdentry &&ovl_is_impuredir (upperdentry))ovl_set_flag (OVL_IMPURE , inode); +if (index) +ovl_set_flag (OVL_INDEX , inode); +/* Check for non-merge dir that may have whiteouts */ if (is_dir) { if (((upperdentry && lowerdentry) || numlower > 1) ||fs/overlayfs/namei.c +0
-3@@ -1005,8 +1005,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_free_oe;OVL_I (inode)->redirect = upperredirect; -if (index) -ovl_set_flag (OVL_INDEX , inode); }revert_creds (old_cred); --2.13.6
PATCH v13 2/28 ovl: Initialize ovl_inode->redirect in ovl_get_inode()
In reply to Vivek GoyalAll the inode/ovl_inode properties ideally should be initialized in ovl_get_inode(). Otherwise this can be a problem for the cases where multiple dentries point to same inode (hardlinks, disconnected dentries).
As of now this is not a problem as redirects are used only for directories which don't share inode. But soon I want to use redirects for regular files also and there it will become an issue.
Hence, move ->redirect initialization in ovl_get_inode().
--- fs/overlayfs/export.c | 2 +- fs/overlayfs/inode.c | 5 ++++- fs/overlayfs/namei.c | 4 +--- fs/overlayfs/overlayfs.h | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-)
fs/overlayfs/export.c +1 -1
@@ -305,7 +305,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,if (d_is_dir (upper ?: lower))return ERR_PTR (-EIO ); - inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower); + inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL );if (IS_ERR (inode)) {dput (upper);return ERR_CAST (inode);
fs/overlayfs/inode.c +4 -1
@@ -704,7 +704,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,struct inode *ovl_get_inode (struct super_block *sb,struct dentry *upperdentry,struct dentry *lowerdentry,struct dentry *index, -unsigned int numlower) +unsigned int numlower,char *redirect) {struct inode *realinode = upperdentry ?d_inode (upperdentry) :NULL ;struct inode *inode;
@@ -741,6 +741,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, }dput (upperdentry); +kfree (redirect); goto out; }
@@ -763,6 +764,8 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (index)ovl_set_flag (OVL_INDEX , inode); +OVL_I (inode)->redirect = redirect; +/* Check for non-merge dir that may have whiteouts */ if (is_dir) { if (((upperdentry && lowerdentry) || numlower > 1) ||
fs/overlayfs/namei.c +1 -3
@@ -999,12 +999,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,if (ctr) origin = stack[0 ].dentry; inode =ovl_get_inode (dentry->d_sb, upperdentry, origin, index, - ctr); + ctr, upperredirect); err =PTR_ERR (inode);if (IS_ERR (inode)) goto out_free_oe; - -OVL_I (inode)->redirect = upperredirect; }revert_creds (old_cred);
fs/overlayfs/overlayfs.h +1 -1
@@ -330,7 +330,7 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, bool is_upper);struct inode *ovl_get_inode (struct super_block *sb,struct dentry *upperdentry,struct dentry *lowerdentry,struct dentry *index, -unsigned int numlower); +unsigned int numlower,char *redirect);static inline void ovl_copyattr (struct inode *from,struct inode *to) { to->i_uid = from->i_uid;
Signature · 1 line
2.13.6
Re: [PATCH v13 02/28] ovl: Initialize ovl_inode->redirect in ovl_get_inode()
Quoted 11 lines · Vivek Goyal
All the inode/ovl_inode properties ideally should be initialized in
ovl_get_inode(). Otherwise this can be a problem for the cases where multiple dentries point to same inode (hardlinks, disconnected dentries).As of now this is not a problem as redirects are used only for directories which don't share inode. But soon I want to use redirects for regular files also and there it will become an issue.
Hence, move ->redirect initialization in
ovl_get_inode().Signed-off-by Vivek Goyal <[email protected]>
Quoted 12 lines
--- fs/overlayfs/export.c | 2 +- fs/overlayfs/inode.c | 5 ++++- fs/overlayfs/namei.c | 4 +--- fs/overlayfs/overlayfs.h | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-)fs/overlayfs/export.c +0
-0
fs/overlayfs/export.c static struct dentry *ovl_obtain_alias(struct super_block *sb, · line 308
Quoted 4 lines
fs/overlayfs/export.c +0
-1@@ -305,7 +305,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,if (d_is_dir (upper ?: lower))return ERR_PTR (-EIO ); - inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower);
I'd leave a TODO comment here for fixing metacopy+NFS export support You may add the comment in later patch, just don't forget.
Quoted 68 lines
fs/overlayfs/export.c +1
-0+ inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL );if (IS_ERR (inode)) {dput (upper);return ERR_CAST (inode);fs/overlayfs/inode.c +4
-1@@ -704,7 +704,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,struct inode *ovl_get_inode (struct super_block *sb,struct dentry *upperdentry,struct dentry *lowerdentry,struct dentry *index, -unsigned int numlower) +unsigned int numlower,char *redirect) {struct inode *realinode = upperdentry ?d_inode (upperdentry) :NULL ;struct inode *inode;@@ -741,6 +741,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, }dput (upperdentry); +kfree (redirect); goto out; }@@ -763,6 +764,8 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (index)ovl_set_flag (OVL_INDEX , inode); +OVL_I (inode)->redirect = redirect; +/* Check for non-merge dir that may have whiteouts */ if (is_dir) { if (((upperdentry && lowerdentry) || numlower > 1) ||fs/overlayfs/namei.c +1
-3@@ -999,12 +999,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,if (ctr) origin = stack[0 ].dentry; inode =ovl_get_inode (dentry->d_sb, upperdentry, origin, index, - ctr); + ctr, upperredirect); err =PTR_ERR (inode);if (IS_ERR (inode)) goto out_free_oe; - -OVL_I (inode)->redirect = upperredirect; }revert_creds (old_cred);fs/overlayfs/overlayfs.h +1
-2@@ -330,7 +330,7 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, bool is_upper);struct inode *ovl_get_inode (struct super_block *sb,struct dentry *upperdentry,struct dentry *lowerdentry,struct dentry *index, -unsigned int numlower); +unsigned int numlower,char *redirect);static inline void ovl_copyattr (struct inode *from,struct inode *to) { to->i_uid = from->i_uid; --2.13.6
PATCH v13 3/28 ovl: Rename local variable locked to new_locked
In reply to Vivek GoyalAs "locked" tracks the mutex lock state of "new" dentry inode instead of "old" dentry inode, name the variable "new_locked" instead.
Soon I will need to lock old dentry inode lock as well and will track that state in variable "locked".
--- fs/overlayfs/dir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/overlayfs/dir.c +3 -3
@@ -892,7 +892,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, unsigned int flags) {int err; -bool locked = false; +bool new_locked = false;struct dentry *old_upperdir;struct dentry *new_upperdir;struct dentry *olddentry;
@@ -959,7 +959,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,if (err) goto out_drop_write; }else { - err =ovl_nlink_start (new, &locked); + err =ovl_nlink_start (new, &new_locked);if (err) goto out_drop_write; }
@@ -1087,7 +1087,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,unlock_rename (new_upperdir, old_upperdir); out_revert_creds:revert_creds (old_cred); -ovl_nlink_end (new, locked); +ovl_nlink_end (new, new_locked); out_drop_write:ovl_drop_write (old); out:
Signature · 1 line
2.13.6
Re: [PATCH v13 03/28] ovl: Rename local variable locked to new_locked
Quoted 7 lines · Vivek Goyal
As "locked" tracks the mutex lock state of "new" dentry inode instead of "old" dentry inode, name the variable "
new_locked" instead.Soon I will need to lock old dentry inode lock as well and will track that state in variable "locked".
Signed-off-by Vivek Goyal <[email protected]>
Quoted 38 lines
--- fs/overlayfs/dir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)fs/overlayfs/dir.c +3
-4@@ -892,7 +892,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, unsigned int flags) {int err; -bool locked = false; +bool new_locked = false;struct dentry *old_upperdir;struct dentry *new_upperdir;struct dentry *olddentry;@@ -959,7 +959,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,if (err) goto out_drop_write; }else { - err =ovl_nlink_start (new, &locked); + err =ovl_nlink_start (new, &new_locked);if (err) goto out_drop_write; }@@ -1087,7 +1087,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,unlock_rename (new_upperdir, old_upperdir); out_revert_creds:revert_creds (old_cred); -ovl_nlink_end (new, locked); +ovl_nlink_end (new, new_locked); out_drop_write:ovl_drop_write (old); out: --2.13.6
PATCH v13 4/28 ovl: Provide a mount option metacopy=on/off for metadata copyup
In reply to Vivek GoyalBy default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast.
Like index feature, we verify on mount that upper root is not being reused with a different lower root. This hopes to get the configuration right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.
--- Documentation/filesystems/overlayfs.txt | 30 ++++++++++++++++++++++++- fs/overlayfs/Kconfig | 19 ++++++++++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-)
Documentation/filesystems/overlayfs.txt +29 -1
@@ -249,6 +249,30 @@ rightmost one and going left. In the above example lower1 will be the top, lower2 the middle and lower3 the bottom layer. +Metadata only copyup +-------------------- + +When metadata only copy up feature is enabled, overlayfs will only copy +up metadata (as opposed to whole file), when a metadata specific operation +like chown/chmod is performed. Full file will be copied up later when +file is opened for WRITE operation. + +IOW, this is delayed data copy up operation and data is copied up when +there is a need to actually modify data. + +There are multiple ways to enable/disable this feature. A config option +CONFIG_OVERLAY_FS_METACOPY can be set/unset to enable/disable this feature +by default. Or one can enable/disable it at module load time with module +parameter metacopy=on/off. Lastly, there is also a per mount option +metacopy=on/off to enable/disable this feature per mount. + +Do not use metacopy=on with untrusted upper/lower directories. Otherwise +it is possible that an attacker can create an handcrafted file with +appropriate REDIRECT and METACOPY xattrs, and gain access to file on lower +pointed by REDIRECT. This should not be possible on local system as setting +"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible +for untrusted layers like from a pen drive. + Sharing and copying layers --------------------------
@@ -267,7 +291,7 @@ though it will not result in a crash or deadlock. Mounting an overlay using an upper layer path, where the upper layer path was previously used by another mounted overlay in combination with a different lower layer path, is allowed, unless the "inodes index" feature -is enabled. +or "metadata only copyup" feature is enabled. With the "inodes index" feature, on the first time mount, an NFS file handle of the lower layer root directory, along with the UUID of the lower
@@ -280,6 +304,10 @@ lower root origin, mount will fail with ESTALE. An overlayfs mount with does not support NFS export, lower filesystem does not have a valid UUID or if the upper filesystem does not support extended attributes. +For "metadata only copyup" feature there is no verification mechanism at +mount time. So if same upper is mouted with different set of lower, mount +probably will succeed but expect the unexpected later on. So don't do it. + It is quite a common practice to copy overlay layers to a different directory tree on the same or different underlying filesystem, and even to a different machine. With the "inodes index" feature, trying to mount
fs/overlayfs/Kconfig +19 -0
@@ -86,3 +86,22 @@ config OVERLAY_FS_NFS_EXPORT case basis with the "nfs_export=on" mount option. Say N unless you fully understand the consequences. + +config OVERLAY_FS_METACOPY + bool "Overlayfs: turn on metadata only copy up feature by default" + depends on OVERLAY_FS + depends on !OVERLAY_FS_NFS_EXPORT + select OVERLAY_FS_REDIRECT_DIR + help + If this config option is enabled then overlay filesystems will + copy up only metadata where appropriate and data copy up will + happen when a file is opended for WRITE operation. It is still + possible to turn off this feature globally with the "metacopy=off" + module option or on a filesystem instance basis with the + "metacopy=off" mount option. + + Note, that this feature is not backward compatible. That is, + mounting an overlay which has metacopy only inodes on a kernel + that doesn't support this feature will have unexpected results. + + If unsure, say N.
fs/overlayfs/ovl_entry.h +1 -0
@@ -18,6 +18,7 @@ struct ovl_config {const char *redirect_mode;bool index;bool nfs_export; +bool metacopy; };struct ovl_layer {
fs/overlayfs/super.c +39 -1
@@ -58,6 +58,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)dput (oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def =IS_ENABLED (CONFIG_OVERLAY_FS_METACOPY ); +module_param_named (metacopy, ovl_metacopy_def, bool,0644 ); +MODULE_PARM_DESC (ovl_metacopy_def, +"Default to on or off for the metadata only copy up feature" ); +static void ovl_dentry_release (struct dentry *dentry) {struct ovl_entry *oe = dentry->d_fsdata;
@@ -350,6 +355,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)if (ofs->config.nfs_export != ovl_nfs_export_def)seq_printf (m,",nfs_export=%s" , ofs->config.nfs_export ?"on" :"off" ); +if (ofs->config.metacopy != ovl_metacopy_def) +seq_printf (m,",metacopy=%s" , + ofs->config.metacopy ?"on" :"off" );return 0 ; }
@@ -384,6 +392,8 @@ enum {OPT_INDEX_OFF ,OPT_NFS_EXPORT_ON ,OPT_NFS_EXPORT_OFF , +OPT_METACOPY_ON , +OPT_METACOPY_OFF , OPT_ERR, };
@@ -397,6 +407,8 @@ static const match_table_t ovl_tokens = { {OPT_INDEX_OFF, "index=off"}, {OPT_NFS_EXPORT_ON, "nfs_export=on"}, {OPT_NFS_EXPORT_OFF, "nfs_export=off"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} };
@@ -511,6 +523,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->nfs_export = false;break ; +case OPT_METACOPY_ON : + config->metacopy = true; +break ; + +case OPT_METACOPY_OFF : + config->metacopy = false; +break ; +default :pr_err ("overlayfs: unrecognized mount option \"%s\" or missing value\n" , p);return -EINVAL ;
@@ -993,7 +1013,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)if (err) { ofs->noxattr = true; ofs->config.index = false; -pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off.\n" ); + ofs->config.metacopy = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off and metacopy=off.\n" ); err =0 ; }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE );
@@ -1012,6 +1033,11 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ofs->config.nfs_export = false; } +/* metacopy feature with upper requires redirect_dir=on */ +if (ofs->config.metacopy && !ofs->config.redirect_dir) { +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=on\", falling back to metacopy=off.\n" ); + ofs->config.metacopy = false; + } out:mnt_drop_write (mnt);return err;
@@ -1188,6 +1214,12 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ofs->config.nfs_export = false; } +if (!ofs->config.upperdir && ofs->config.metacopy && + !ofs->config.redirect_follow) { + ofs->config.metacopy = false; +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n" ); + } + err = -ENOMEM ; stack =kcalloc (stacklen,sizeof (struct path ),GFP_KERNEL );if (!stack)
@@ -1263,6 +1295,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.index = ovl_index_def; ofs->config.nfs_export = ovl_nfs_export_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;
@@ -1331,6 +1364,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } } +if (ofs->config.metacopy && ofs->config.nfs_export) { +pr_warn ("overlayfs: Metadata copy up requires NFS export disabled, falling back to nfs_export=off.\n" ); + ofs->config.nfs_export = false; + } +if (ofs->config.nfs_export) sb->s_export_op = &ovl_export_operations;
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 04/28] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 11 lines · Vivek Goyal
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast.
Like index feature, we verify on mount that upper root is not being reused with a different lower root.
I don't see that in the patch
Quoted 74 lines
This hopes to get the configuration
right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- Documentation/filesystems/overlayfs.txt | 30 ++++++++++++++++++++++++- fs/overlayfs/Kconfig | 19 ++++++++++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-)Documentation/filesystems/overlayfs.txt +29
-1@@ -249,6 +249,30 @@ rightmost one and going left. In the above example lower1 will be the top, lower2 the middle and lower3 the bottom layer. +Metadata only copyup +-------------------- + +When metadata only copy up feature is enabled, overlayfs will only copy +up metadata (as opposed to whole file), when a metadata specific operation +like chown/chmod is performed. Full file will be copied up later when +file is opened for WRITE operation. + +IOW, this is delayed data copy up operation and data is copied up when +there is a need to actually modify data. + +There are multiple ways to enable/disable this feature. A config option +CONFIG_OVERLAY_FS_METACOPY can be set/unset to enable/disable this feature +by default. Or one can enable/disable it at module load time with module +parameter metacopy=on/off. Lastly, there is also a per mount option +metacopy=on/off to enable/disable this feature per mount. + +Do not use metacopy=on with untrusted upper/lower directories. Otherwise +it is possible that an attacker can create an handcrafted file with +appropriate REDIRECT and METACOPY xattrs, and gain access to file on lower +pointed by REDIRECT. This should not be possible on local system as setting +"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible +for untrusted layers like from a pen drive. + Sharing and copying layers --------------------------@@ -267,7 +291,7 @@ though it will not result in a crash or deadlock. Mounting an overlay using an upper layer path, where the upper layer path was previously used by another mounted overlay in combination with a different lower layer path, is allowed, unless the "inodes index" feature -is enabled. +or "metadata only copyup" feature is enabled. With the "inodes index" feature, on the first time mount, an NFS file handle of the lower layer root directory, along with the UUID of the lower@@ -280,6 +304,10 @@ lower root origin, mount will fail with ESTALE. An overlayfs mount with does not support NFS export, lower filesystem does not have a valid UUID or if the upper filesystem does not support extended attributes. +For "metadata only copyup" feature there is no verification mechanism at +mount time. So if same upper is mouted with different set of lower, mount +probably will succeed but expect the unexpected later on. So don't do it. + It is quite a common practice to copy overlay layers to a different directory tree on the same or different underlying filesystem, and even to a different machine. With the "inodes index" feature, trying to mountfs/overlayfs/Kconfig +0
-0
fs/overlayfs/Kconfig config OVERLAY_FS_NFS_EXPORT · line 93
Quoted 8 lines
fs/overlayfs/Kconfig +5
-0@@ -86,3 +86,22 @@ config OVERLAY_FS_NFS_EXPORT case basis with the "nfs_export=on" mount option. Say N unless you fully understand the consequences. + +config OVERLAY_FS_METACOPY + bool "Overlayfs: turn on metadata only copy up feature by default" + depends on OVERLAY_FS + depends on !OVERLAY_FS_NFS_EXPORT
Like the test in the code, the dependency should be OVERLAY_FS_NFS_EXPORT depends on !OVERLAY_FS_METACOPY
fs/overlayfs/Kconfig config OVERLAY_FS_NFS_EXPORT · line 94
Quoted 1 line
fs/overlayfs/Kconfig +1
-0+ select OVERLAY_FS_REDIRECT_DIR
At first glance, I thought this should be
depends on OVERLAY_FS_REDIRECT_DIR,
like in the code
But I see why select makes sense in the context of config options. Makes me wonder if NFS_EXPORT should also select INDEX
I know why I didn't do this logic in the code, because we do not distinguish in the code between explicit mount option "index=off" and no mount option at all when default is "index=off". The former should disable nfs_export but the latter should enable index.
Quoted 95 lines
fs/overlayfs/Kconfig +13
-0+ help + If this config option is enabled then overlay filesystems will + copy up only metadata where appropriate and data copy up will + happen when a file is opended for WRITE operation. It is still + possible to turn off this feature globally with the "metacopy=off" + module option or on a filesystem instance basis with the + "metacopy=off" mount option. + + Note, that this feature is not backward compatible. That is, + mounting an overlay which has metacopy only inodes on a kernel + that doesn't support this feature will have unexpected results. + + If unsure, say N.fs/overlayfs/ovl_entry.h +1
-0@@ -18,6 +18,7 @@ struct ovl_config {const char *redirect_mode;bool index;bool nfs_export; +bool metacopy; };struct ovl_layer {fs/overlayfs/super.c +22
-1@@ -58,6 +58,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)dput (oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def =IS_ENABLED (CONFIG_OVERLAY_FS_METACOPY ); +module_param_named (metacopy, ovl_metacopy_def, bool,0644 ); +MODULE_PARM_DESC (ovl_metacopy_def, +"Default to on or off for the metadata only copy up feature" ); +static void ovl_dentry_release (struct dentry *dentry) {struct ovl_entry *oe = dentry->d_fsdata;@@ -350,6 +355,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)if (ofs->config.nfs_export != ovl_nfs_export_def)seq_printf (m,",nfs_export=%s" , ofs->config.nfs_export ?"on" :"off" ); +if (ofs->config.metacopy != ovl_metacopy_def) +seq_printf (m,",metacopy=%s" , + ofs->config.metacopy ?"on" :"off" );return 0 ; }@@ -384,6 +392,8 @@ enum {OPT_INDEX_OFF ,OPT_NFS_EXPORT_ON ,OPT_NFS_EXPORT_OFF , +OPT_METACOPY_ON , +OPT_METACOPY_OFF , OPT_ERR, };@@ -397,6 +407,8 @@ static const match_table_t ovl_tokens = { {OPT_INDEX_OFF, "index=off"}, {OPT_NFS_EXPORT_ON, "nfs_export=on"}, {OPT_NFS_EXPORT_OFF, "nfs_export=off"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} };@@ -511,6 +523,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->nfs_export = false;break ; +case OPT_METACOPY_ON : + config->metacopy = true; +break ; + +case OPT_METACOPY_OFF : + config->metacopy = false; +break ; +default :pr_err ("overlayfs: unrecognized mount option \"%s\" or missing value\n" , p);return -EINVAL ;@@ -993,7 +1013,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)if (err) { ofs->noxattr = true; ofs->config.index = false; -pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off.\n" ); + ofs->config.metacopy = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off and metacopy=off.\n" ); err =0 ; }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE );
fs/overlayfs/super.c static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) · line 1040
Quoted 8 lines
fs/overlayfs/super.c +5
-0@@ -1012,6 +1033,11 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ofs->config.nfs_export = false; } +/* metacopy feature with upper requires redirect_dir=on */ +if (ofs->config.metacopy && !ofs->config.redirect_dir) { +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=on\", falling back to metacopy=off.\n" ); + ofs->config.metacopy = false; + }
Please move all these scattered tests that depend only on parsed config values at the end of ovl_parse_redirect_mode().
Quoted 25 lines
fs/overlayfs/super.c +7
-0out:mnt_drop_write (mnt);return err;@@ -1188,6 +1214,12 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ofs->config.nfs_export = false; } +if (!ofs->config.upperdir && ofs->config.metacopy && + !ofs->config.redirect_follow) { + ofs->config.metacopy = false; +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n" ); + } + err = -ENOMEM ; stack =kcalloc (stacklen,sizeof (struct path ),GFP_KERNEL );if (!stack)@@ -1263,6 +1295,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.index = ovl_index_def; ofs->config.nfs_export = ovl_nfs_export_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent) · line 1368
Quoted 5 lines
fs/overlayfs/super.c +2
-0@@ -1331,6 +1364,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } } +if (ofs->config.metacopy && ofs->config.nfs_export) { +pr_warn ("overlayfs: Metadata copy up requires NFS export disabled, falling back to nfs_export=off.\n" );
"NFS export is not supported with metadata only copy up, ..."
Thanks,
Amir.
Re: [PATCH v13 04/28] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 7 lines · Amir Goldstein
Quoted 6 lines · Vivek Goyal
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
Quoted 7 lines
Quoted 5 lines
metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast.
Like index feature, we verify on mount that upper root is not being reused with a different lower root.
I don't see that in the patch
Oh.., this is leftover from previous patches. Will remove this comment. I have completely got rid of dealing with ORIGIN when moving to REDIRECT based lookup.
Quoted 77 lines
Quoted 77 lines
This hopes to get the configuration
right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- Documentation/filesystems/overlayfs.txt | 30 ++++++++++++++++++++++++- fs/overlayfs/Kconfig | 19 ++++++++++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-)Documentation/filesystems/overlayfs.txt +29
-1@@ -249,6 +249,30 @@ rightmost one and going left. In the above example lower1 will be the top, lower2 the middle and lower3 the bottom layer. +Metadata only copyup +-------------------- + +When metadata only copy up feature is enabled, overlayfs will only copy +up metadata (as opposed to whole file), when a metadata specific operation +like chown/chmod is performed. Full file will be copied up later when +file is opened for WRITE operation. + +IOW, this is delayed data copy up operation and data is copied up when +there is a need to actually modify data. + +There are multiple ways to enable/disable this feature. A config option +CONFIG_OVERLAY_FS_METACOPY can be set/unset to enable/disable this feature +by default. Or one can enable/disable it at module load time with module +parameter metacopy=on/off. Lastly, there is also a per mount option +metacopy=on/off to enable/disable this feature per mount. + +Do not use metacopy=on with untrusted upper/lower directories. Otherwise +it is possible that an attacker can create an handcrafted file with +appropriate REDIRECT and METACOPY xattrs, and gain access to file on lower +pointed by REDIRECT. This should not be possible on local system as setting +"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible +for untrusted layers like from a pen drive. + Sharing and copying layers --------------------------@@ -267,7 +291,7 @@ though it will not result in a crash or deadlock. Mounting an overlay using an upper layer path, where the upper layer path was previously used by another mounted overlay in combination with a different lower layer path, is allowed, unless the "inodes index" feature -is enabled. +or "metadata only copyup" feature is enabled. With the "inodes index" feature, on the first time mount, an NFS file handle of the lower layer root directory, along with the UUID of the lower@@ -280,6 +304,10 @@ lower root origin, mount will fail with ESTALE. An overlayfs mount with does not support NFS export, lower filesystem does not have a valid UUID or if the upper filesystem does not support extended attributes. +For "metadata only copyup" feature there is no verification mechanism at +mount time. So if same upper is mouted with different set of lower, mount +probably will succeed but expect the unexpected later on. So don't do it. + It is quite a common practice to copy overlay layers to a different directory tree on the same or different underlying filesystem, and even to a different machine. With the "inodes index" feature, trying to mountfs/overlayfs/Kconfig +0
-0@@ -86,3 +86,22 @@ config OVERLAY_FS_NFS_EXPORT case basis with the "nfs_export=on" mount option.
Quoted 9 lines
fs/overlayfs/Kconfig config OVERLAY_FS_NFS_EXPORT · line 93
Quoted 6 lines
fs/overlayfs/Kconfig +5
-0Say N unless you fully understand the consequences. + +config OVERLAY_FS_METACOPY + bool "Overlayfs: turn on metadata only copy up feature by default" + depends on OVERLAY_FS + depends on !OVERLAY_FS_NFS_EXPORTLike the test in the code, the dependency should be
OVERLAY_FS_NFS_EXPORTdepends on !OVERLAY_FS_METACOPY
Ok, makes sense. Will change.
Quoted 8 lines
fs/overlayfs/Kconfig config OVERLAY_FS_NFS_EXPORT · line 94
Quoted 2 lines
fs/overlayfs/Kconfig +1
-0+ select OVERLAY_FS_REDIRECT_DIRAt first glance, I thought this should be
depends onOVERLAY_FS_REDIRECT_DIR,
like in the code
But I see why select makes sense in the context of config options. Makes me wonder ifNFS_EXPORTshould also select INDEX
I think it makes sense to select INDEX if user enables NFS_EXPORT.
Quoted 104 lines
I know why I didn't do this logic in the code, because we do not distinguish in the code between explicit mount option "index=off" and no mount option at all when default is "index=off". The former should disable
nfs_exportbut the latter should enable index.Quoted 98 lines
fs/overlayfs/Kconfig +13
-0+ help + If this config option is enabled then overlay filesystems will + copy up only metadata where appropriate and data copy up will + happen when a file is opended for WRITE operation. It is still + possible to turn off this feature globally with the "metacopy=off" + module option or on a filesystem instance basis with the + "metacopy=off" mount option. + + Note, that this feature is not backward compatible. That is, + mounting an overlay which has metacopy only inodes on a kernel + that doesn't support this feature will have unexpected results. + + If unsure, say N.fs/overlayfs/ovl_entry.h +1
-0@@ -18,6 +18,7 @@ struct ovl_config {const char *redirect_mode;bool index;bool nfs_export; +bool metacopy; };struct ovl_layer {fs/overlayfs/super.c +22
-1@@ -58,6 +58,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)dput (oe->lowerstack[i].dentry); } +static bool ovl_metacopy_def =IS_ENABLED (CONFIG_OVERLAY_FS_METACOPY ); +module_param_named (metacopy, ovl_metacopy_def, bool,0644 ); +MODULE_PARM_DESC (ovl_metacopy_def, +"Default to on or off for the metadata only copy up feature" ); +static void ovl_dentry_release (struct dentry *dentry) {struct ovl_entry *oe = dentry->d_fsdata;@@ -350,6 +355,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)if (ofs->config.nfs_export != ovl_nfs_export_def)seq_printf (m,",nfs_export=%s" , ofs->config.nfs_export ?"on" :"off" ); +if (ofs->config.metacopy != ovl_metacopy_def) +seq_printf (m,",metacopy=%s" , + ofs->config.metacopy ?"on" :"off" );return 0 ; }@@ -384,6 +392,8 @@ enum {OPT_INDEX_OFF ,OPT_NFS_EXPORT_ON ,OPT_NFS_EXPORT_OFF , +OPT_METACOPY_ON , +OPT_METACOPY_OFF , OPT_ERR, };@@ -397,6 +407,8 @@ static const match_table_t ovl_tokens = { {OPT_INDEX_OFF, "index=off"}, {OPT_NFS_EXPORT_ON, "nfs_export=on"}, {OPT_NFS_EXPORT_OFF, "nfs_export=off"}, + {OPT_METACOPY_ON, "metacopy=on"}, + {OPT_METACOPY_OFF, "metacopy=off"}, {OPT_ERR, NULL} };@@ -511,6 +523,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->nfs_export = false;break ; +case OPT_METACOPY_ON : + config->metacopy = true; +break ; + +case OPT_METACOPY_OFF : + config->metacopy = false; +break ; +default :pr_err ("overlayfs: unrecognized mount option \"%s\" or missing value\n" , p);return -EINVAL ;@@ -993,7 +1013,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)if (err) { ofs->noxattr = true; ofs->config.index = false; -pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off.\n" ); + ofs->config.metacopy = false; +pr_warn ("overlayfs: upper fs does not support xattr, falling back to index=off and metacopy=off.\n" ); err =0 ; }else {vfs_removexattr (ofs->workdir,OVL_XATTR_OPAQUE );@@ -1012,6 +1033,11 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ofs->config.nfs_export = false; }
Quoted 8 lines
fs/overlayfs/super.c static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) · line 1040
Quoted 5 lines
fs/overlayfs/super.c +5
-0+/* metacopy feature with upper requires redirect_dir=on */ +if (ofs->config.metacopy && !ofs->config.redirect_dir) { +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=on\", falling back to metacopy=off.\n" ); + ofs->config.metacopy = false; + }Please move all these scattered tests that depend only on parsed config values at the end of
ovl_parse_redirect_mode().
Will do.
Quoted 29 lines
Quoted 29 lines
fs/overlayfs/super.c +7
-0out:mnt_drop_write (mnt);return err;@@ -1188,6 +1214,12 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ofs->config.nfs_export = false; } +if (!ofs->config.upperdir && ofs->config.metacopy && + !ofs->config.redirect_follow) { + ofs->config.metacopy = false; +pr_warn ("overlayfs: metadata only copyup requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n" ); + } + err = -ENOMEM ; stack =kcalloc (stacklen,sizeof (struct path ),GFP_KERNEL );if (!stack)@@ -1263,6 +1295,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ofs->config.index = ovl_index_def; ofs->config.nfs_export = ovl_nfs_export_def; + ofs->config.metacopy = ovl_metacopy_def; err =ovl_parse_opt ((char *) data, &ofs->config);if (err) goto out_err;@@ -1331,6 +1364,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } }
Quoted 4 lines
fs/overlayfs/super.c static int ovl_fill_super(struct super_block *sb, void *data, int silent) · line 1368
Quoted 2 lines
fs/overlayfs/super.c +2
-0+if (ofs->config.metacopy && ofs->config.nfs_export) { +pr_warn ("overlayfs: Metadata copy up requires NFS export disabled, falling back to nfs_export=off.\n" );"NFS export is not supported with metadata only copy up, ..."
Will change.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 04/28] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 43 lines · Vivek Goyal
Quoted 7 lines · Amir Goldstein
Quoted 6 lines · Vivek Goyal
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
Quoted 7 lines
Quoted 5 lines
metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast.
Like index feature, we verify on mount that upper root is not being reused with a different lower root.
I don't see that in the patch
Oh.., this is leftover from previous patches. Will remove this comment. I have completely got rid of dealing with ORIGIN when moving to REDIRECT based lookup.
Quoted 23 lines
Quoted 23 lines
This hopes to get the configuration
right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- Documentation/filesystems/overlayfs.txt | 30 ++++++++++++++++++++++++- fs/overlayfs/Kconfig | 19 ++++++++++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-)Documentation/filesystems/overlayfs.txt +0
-0@@ -249,6 +249,30 @@ rightmost one and going left. In the above example lower1 will be the top, lower2 the middle and lower3 the bottom layer.
Quoted 11 lines
Quoted 11 lines
Documentation/filesystems/overlayfs.txt rightmost one and going left. In the above example lower1 will be the · line 262
Quoted 11 lines
Documentation/filesystems/overlayfs.txt +11
-0+Metadata only copyup +-------------------- + +When metadata only copy up feature is enabled, overlayfs will only copy +up metadata (as opposed to whole file), when a metadata specific operation +like chown/chmod is performed. Full file will be copied up later when +file is opened for WRITE operation. + +IOW, this is delayed data copy up operation and data is copied up when +there is a need to actually modify data. +
Vivek,
Some nick picking here.
This is document is about the only decent documentation for overlayfs. I always try to live up to the standards set by Neil who started it. Abbreviations (IOW) doesn't seem right to me in this context. Also, you'll notice that the form "copyup" does not appear in this document. It may sound petty, but if people wish to read a coherent document about overlayfs, we would be wise to maintain coherency in the phrases we use throughout the document.
Thanks,
Amir.
Re: [PATCH v13 04/28] ovl: Provide a mount option metacopy=on/off for metadata copyup
Quoted 58 lines · Amir Goldstein
Quoted 43 lines · Vivek Goyal
Quoted 7 lines · Amir Goldstein
Quoted 6 lines · Vivek Goyal
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other.
Also provide a kernel config and module option to enable/disable metacopy feature.
Quoted 7 lines
Quoted 5 lines
metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast.
Like index feature, we verify on mount that upper root is not being reused with a different lower root.
I don't see that in the patch
Oh.., this is leftover from previous patches. Will remove this comment. I have completely got rid of dealing with ORIGIN when moving to REDIRECT based lookup.
Quoted 23 lines
Quoted 23 lines
This hopes to get the configuration
right and detect the copied layers use case. But this does only so much as we don't verify all the lowers. So it is possible that a lower is missing and later data copy up fails.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- Documentation/filesystems/overlayfs.txt | 30 ++++++++++++++++++++++++- fs/overlayfs/Kconfig | 19 ++++++++++++++++ fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 40 ++++++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 2 deletions(-)Documentation/filesystems/overlayfs.txt +0
-0@@ -249,6 +249,30 @@ rightmost one and going left. In the above example lower1 will be the top, lower2 the middle and lower3 the bottom layer.Quoted 11 lines
Quoted 11 lines
Documentation/filesystems/overlayfs.txt rightmost one and going left. In the above example lower1 will be the · line 262
Quoted 11 lines
Documentation/filesystems/overlayfs.txt +11
-0+Metadata only copyup +-------------------- + +When metadata only copy up feature is enabled, overlayfs will only copy +up metadata (as opposed to whole file), when a metadata specific operation +like chown/chmod is performed. Full file will be copied up later when +file is opened for WRITE operation. + +IOW, this is delayed data copy up operation and data is copied up when +there is a need to actually modify data. +Vivek,
Quoted 8 lines
Some nick picking here.
This is document is about the only decent documentation for overlayfs. I always try to live up to the standards set by Neil who started it. Abbreviations (IOW) doesn't seem right to me in this context. Also, you'll notice that the form "copyup" does not appear in this document. It may sound petty, but if people wish to read a coherent document about overlayfs, we would be wise to maintain coherency in the phrases we use throughout the document.
Ok, I will remove IOW. Also will replace "copyup" with "copy up".
Vivek
PATCH v13 5/28 ovl: During copy up, first copy up metadata and then data
In reply to Vivek GoyalJust a little re-ordering of code. This helps with next patch where after copying up metadata, we skip data copying step, if needed.
--- fs/overlayfs/copy_up.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-)
fs/overlayfs/copy_up.c +17 -19
@@ -536,28 +536,10 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) {int err; -if (S_ISREG (c->stat.mode)) { -struct path upperpath; - -ovl_path_upper (c->dentry, &upperpath); -BUG_ON (upperpath.dentry !=NULL ); - upperpath.dentry = temp; - - err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); -if (err) -return err; - } - err =ovl_copy_xattr (c->lowerpath.dentry, temp);if (err)return err; -inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); -inode_unlock (temp->d_inode); -if (err) -return err; -/* * Store identifier of lower inode in upper inode xattr to * allow lookup of the copy up origin inode.
@@ -571,7 +553,23 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } -return 0 ; +if (S_ISREG (c->stat.mode)) { +struct path upperpath; + +ovl_path_upper (c->dentry, &upperpath); +BUG_ON (upperpath.dentry !=NULL ); + upperpath.dentry = temp; + + err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +if (err) +return err; + } + +inode_lock (temp->d_inode); + err =ovl_set_attr (temp, &c->stat); +inode_unlock (temp->d_inode); + +return err; }static int ovl_copy_up_locked (struct ovl_copy_up_ctx *c)
Signature · 1 line
2.13.6
PATCH v13 6/28 ovl: Move the copy up helpers to copy_up.c
In reply to Vivek GoyalRight now two copy up helpers are in inode.c. Amir suggested it might be better to move these to copy_up.c.
There will one more related function which will come in later patch.
--- fs/overlayfs/copy_up.c | 32 ++++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 32 -------------------------------- fs/overlayfs/overlayfs.h | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-)
fs/overlayfs/copy_up.c +32 -0
@@ -826,6 +826,38 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)return err; } +static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) +{ +/* Copy up of disconnected dentry does not set upper alias */ +if (ovl_dentry_upper (dentry) && + (ovl_dentry_has_upper_alias (dentry) || + (dentry->d_flags &DCACHE_DISCONNECTED ))) +return false; + +if (special_file (d_inode (dentry)->i_mode)) +return false; + +if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +return false; + +return true; +} + +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) +{ +int err =0 ; + +if (ovl_open_need_copy_up (dentry, file_flags)) { + err =ovl_want_write (dentry); +if (!err) { + err =ovl_copy_up_flags (dentry, file_flags); +ovl_drop_write (dentry); + } + } + +return err; +} +int ovl_copy_up (struct dentry *dentry) {return ovl_copy_up_flags (dentry,0 );
fs/overlayfs/inode.c +0 -32
@@ -349,38 +349,6 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)return acl; } -static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) -{ -/* Copy up of disconnected dentry does not set upper alias */ -if (ovl_dentry_upper (dentry) && - (ovl_dentry_has_upper_alias (dentry) || - (dentry->d_flags &DCACHE_DISCONNECTED ))) -return false; - -if (special_file (d_inode (dentry)->i_mode)) -return false; - -if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) -return false; - -return true; -} - -int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags) -{ -int err =0 ; - -if (ovl_open_need_copy_up (dentry, file_flags)) { - err =ovl_want_write (dentry); -if (!err) { - err =ovl_copy_up_flags (dentry, file_flags); -ovl_drop_write (dentry); - } - } - -return err; -} -int ovl_update_time (struct inode *inode,struct timespec *ts,int flags) {struct dentry *alias;
fs/overlayfs/overlayfs.h +1 -1
@@ -321,7 +321,6 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size);ssize_t ovl_listxattr (struct dentry *dentry,char *list,size_t size);struct posix_acl *ovl_get_acl (struct inode *inode,int type); -int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags);int ovl_update_time (struct inode *inode,struct timespec *ts,int flags);bool ovl_is_private_xattr (const char *name);
@@ -359,6 +358,7 @@ int ovl_cleanup(struct inode *dir, struct dentry *dentry);/* copy_up.c */ int ovl_copy_up (struct dentry *dentry);int ovl_copy_up_flags (struct dentry *dentry,int flags); +int ovl_open_maybe_copy_up (struct dentry *dentry,unsigned int file_flags);int ovl_copy_xattr (struct dentry *old,struct dentry *new);int ovl_set_attr (struct dentry *upper,struct kstat *stat);struct ovl_fh *ovl_encode_fh (struct dentry *real,bool is_upper);
Signature · 1 line
2.13.6
PATCH v13 7/28 ovl: Copy up only metadata during copy up where it makes sense
In reply to Vivek GoyalIf it makes sense to copy up only metadata during copy up, do it. This is done for regular files which are not opened for WRITE and have origin being saved.
Right now ->metacopy is set to 0 always. Last patch in the series will remove the hard coded statement and enable metacopy feature.
Currently ovl_set_origin() returns success even if specified xattr could not be set due to -EOPNOTSUPP returned by upper. IOW, ovl_set_origin() operation is optional and copy up operation continues.
With metadata copy up, ovl_set_origin() can't be optional. We need to know if origin could be set or not. If it could not be set, then either disable metacopy or abort copy up operation. I have take then later approach and disable metacopy for this inode.
Normally we should not run into this path as metacopy will not be enabled if upper does not support xattr. This check is done during mount. This path can only hit if checks during mount pass but at some point of time later upper still returns -EOPNOTSUPP. So its a saftey mechanism to handle that unexpected -EOPNOTSUPP.
--- fs/overlayfs/copy_up.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-)
fs/overlayfs/copy_up.c +24 -1
@@ -232,6 +232,26 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)return err; } +static bool ovl_need_meta_copy_up (struct dentry *dentry,umode_t mode, +int flags) +{ +struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + +/* TODO: Will enable metacopy in last patch of series */ +return false; + +if (!ofs->config.metacopy) +return false; + +if (!S_ISREG (mode)) +return false; + +if (flags && ((OPEN_FMODE (flags) &FMODE_WRITE ) || (flags &O_TRUNC ))) +return false; + +return true; +} +struct ovl_fh *ovl_encode_fh (struct dentry *real,bool is_upper) {struct ovl_fh *fh;
@@ -416,6 +436,7 @@ struct ovl_copy_up_ctx {bool tmpfile;bool origin;bool indexed; +bool metacopy; };static int ovl_link_up (struct ovl_copy_up_ctx *c)
@@ -553,7 +574,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } -if (S_ISREG (c->stat.mode)) { +if (S_ISREG (c->stat.mode) && !c->metacopy) {struct path upperpath;ovl_path_upper (c->dentry, &upperpath);
@@ -729,6 +750,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,if (err)return err; + ctx.metacopy =ovl_need_meta_copy_up (dentry, ctx.stat.mode, flags); +if (parent) {ovl_path_upper (parent, &parentpath); ctx.destdir = parentpath.dentry;
Signature · 1 line
2.13.6
PATCH v13 8/28 ovl: Add helper ovl_already_copied_up()
In reply to Vivek GoyalThere are couple of places where we need to know if file is already copied up (in lockless manner). Right now its open coded and there are only two conditions to check. Soon this patch series will introduce another condition to check and Amir wants to introduce one more. So introduce a helper instead to check this so that code is easier to read.
--- fs/overlayfs/copy_up.c | 20 ++------------------ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 26 +++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 19 deletions(-)
fs/overlayfs/copy_up.c +2 -18
@@ -810,21 +810,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)struct dentry *next;struct dentry *parent =NULL ; -/* -* Check if copy-up has happened as well as for upper alias (in -* case of hard links) is there. -* -* Both checks are lockless: -* - false negatives: will recheck under oi->lock -* - false positives: -* + ovl_dentry_upper() uses memory barriers to ensure the -* upper dentry is up-to-date -* + ovl_dentry_has_upper_alias() relies on locking of -* upper parent i_rwsem to prevent reordering copy-up -* with rename. -*/ -if (ovl_dentry_upper (dentry) && - (ovl_dentry_has_upper_alias (dentry) || disconnected)) +if (ovl_already_copied_up (dentry))break ; next =dget (dentry);
@@ -852,9 +838,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) {/* Copy up of disconnected dentry does not set upper alias */ -if (ovl_dentry_upper (dentry) && - (ovl_dentry_has_upper_alias (dentry) || - (dentry->d_flags &DCACHE_DISCONNECTED ))) +if (ovl_already_copied_up (dentry))return false;if (special_file (d_inode (dentry)->i_mode))
fs/overlayfs/overlayfs.h +1 -0
@@ -242,6 +242,7 @@ bool ovl_is_whiteout(struct dentry *dentry);struct file *ovl_path_open (struct path *path,int flags);int ovl_copy_up_start (struct dentry *dentry);void ovl_copy_up_end (struct dentry *dentry); +bool ovl_already_copied_up (struct dentry *dentry);bool ovl_check_origin_xattr (struct dentry *dentry);bool ovl_check_dir_xattr (struct dentry *dentry,const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
fs/overlayfs/util.c +25 -1
@@ -339,13 +339,37 @@ struct file *ovl_path_open(struct path *path, int flags)return dentry_open (path, flags |O_NOATIME ,current_cred ()); } +bool ovl_already_copied_up (struct dentry *dentry) +{ +bool disconnected = dentry->d_flags &DCACHE_DISCONNECTED ; + +/* +* Check if copy-up has happened as well as for upper alias (in +* case of hard links) is there. +* +* Both checks are lockless: +* - false negatives: will recheck under oi->lock +* - false positives: +* + ovl_dentry_upper() uses memory barriers to ensure the +* upper dentry is up-to-date +* + ovl_dentry_has_upper_alias() relies on locking of +* upper parent i_rwsem to prevent reordering copy-up +* with rename. +*/ +if (ovl_dentry_upper (dentry) && + (ovl_dentry_has_upper_alias (dentry) || disconnected)) +return true; + +return false; +} +int ovl_copy_up_start (struct dentry *dentry) {struct ovl_inode *oi =OVL_I (d_inode (dentry));int err; err =mutex_lock_interruptible (&oi->lock); -if (!err &&ovl_dentry_has_upper_alias (dentry)) { +if (!err &&ovl_already_copied_up (dentry)) { err =1 ;/* Already copied up */ mutex_unlock (&oi->lock); }
Signature · 1 line
2.13.6
PATCH v13 9/28 ovl: A new xattr OVL_XATTR_METACOPY for file on upper
In reply to Vivek GoyalNow we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr OVL_XATTR_METACOPY to distinguish between two cases.
Presence of OVL_XATTR_METACOPY reflects that file has been copied up metadata only and and data will be copied up later from lower origin. So this xattr is set when a metadata copy takes place and cleared when data copy takes place.
We also use a bit in ovl_inode->flags to cache OVL_UPPERDATA which reflects whether ovl inode has data or not (as opposed to metadata only copy up).
If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in ovl_inode->flags. While all these operations happen with oi->lock held, read side of oi->flags can be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.
So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real() acquire(oi->lock)
ovl_open_maybe_copy_up() ovl_copy_up_data()
open_open_need_copy_up() vfs_removexattr()
ovl_already_copied_up()
ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA)
ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Hence this patch introduces smp_wmb() on setting UPPERDATA flag operation and smp_rmb() on UPPERDATA flag test operation.
May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
--- fs/overlayfs/copy_up.c | 56 ++++++++++++++++++++++++++++++---- fs/overlayfs/dir.c | 1 + fs/overlayfs/overlayfs.h | 18 +++++++++-- fs/overlayfs/super.c | 1 + fs/overlayfs/util.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 143 insertions(+), 11 deletions(-)
fs/overlayfs/copy_up.c +51 -5
@@ -195,6 +195,16 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)return error; } +static int ovl_set_size (struct dentry *upperdentry,struct kstat *stat) +{ +struct iattr attr = { + .ia_valid =ATTR_SIZE , + .ia_size = stat->size, + }; + +return notify_change (upperdentry, &attr,NULL ); +} +static int ovl_set_timestamps (struct dentry *upperdentry,struct kstat *stat) {struct iattr attr = {
@@ -586,8 +596,18 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } +if (c->metacopy) { + err =ovl_check_setxattr (c->dentry, temp,OVL_XATTR_METACOPY , +NULL ,0 , -EOPNOTSUPP ); +if (err) +return err; + } +inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); +if (c->metacopy) + err =ovl_set_size (temp, &c->stat); +if (!err) + err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode);return err;
@@ -625,6 +645,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)if (err) goto out_cleanup; +if (!c->metacopy) +ovl_set_upperdata (d_inode (c->dentry)); inode =d_inode (c->dentry);ovl_inode_update (inode, newdentry);if (S_ISDIR (inode->i_mode))
@@ -729,6 +751,28 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)return err; } +/* Copy up data of an inode which was copied up metadata only in the past. */ +static int ovl_copy_up_meta_inode_data (struct ovl_copy_up_ctx *c) +{ +struct path upperpath; +int err; + +ovl_path_upper (c->dentry, &upperpath); +if (WARN_ON (upperpath.dentry ==NULL )) +return -EIO ; + + err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +if (err) +return err; + + err =vfs_removexattr (upperpath.dentry,OVL_XATTR_METACOPY ); +if (err) +return err; + +ovl_set_upperdata (d_inode (c->dentry)); +return err; +} +static int ovl_copy_up_one (struct dentry *parent,struct dentry *dentry,int flags) {
@@ -775,7 +819,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, }ovl_do_check_copy_up (ctx.lowerpath.dentry); - err =ovl_copy_up_start (dentry); + err =ovl_copy_up_start (dentry, flags);/* err < 0: interrupted, err > 0: raced with another copy-up */ if (unlikely (err)) {if (err >0 )
@@ -785,6 +829,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, err =ovl_do_copy_up (&ctx);if (!err && parent && !ovl_dentry_has_upper_alias (dentry)) err =ovl_link_up (&ctx); +if (!err &&ovl_dentry_needs_data_copy_up_locked (dentry, flags)) + err =ovl_copy_up_meta_inode_data (&ctx);ovl_copy_up_end (dentry); }do_delayed_call (&done);
@@ -810,7 +856,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)struct dentry *next;struct dentry *parent =NULL ; -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))break ; next =dget (dentry);
@@ -838,13 +884,13 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)static bool ovl_open_need_copy_up (struct dentry *dentry,int flags) {/* Copy up of disconnected dentry does not set upper alias */ -if (ovl_already_copied_up (dentry)) +if (ovl_already_copied_up (dentry, flags))return false;if (special_file (d_inode (dentry)->i_mode))return false; -if (!(OPEN_FMODE (flags) &FMODE_WRITE ) && !(flags &O_TRUNC )) +if (!ovl_open_flags_need_copy_up (flags))return false;return true;
fs/overlayfs/dir.c +1 -0
@@ -189,6 +189,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode,ovl_dentry_version_inc (dentry->d_parent, false);ovl_dentry_set_upper_alias (dentry);if (!hardlink) { +ovl_set_upperdata (inode);ovl_inode_update (inode, newdentry);ovl_copyattr (newdentry->d_inode, inode); }else {
fs/overlayfs/overlayfs.h +16 -2
@@ -28,6 +28,7 @@ enum ovl_path_type {#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"#define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper" +#define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"enum ovl_inode_flag {/* Pure upper dir that may contain non pure upper entries */
@@ -35,6 +36,7 @@ enum ovl_inode_flag {/* Non-merge dir that may contain whiteout entries */ OVL_WHITEOUTS ,OVL_INDEX , + OVL_UPPERDATA, }; enum ovl_entry_flag {
@@ -196,6 +198,14 @@ static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)return ret; } +static inline bool ovl_open_flags_need_copy_up (int flags) +{ +if (!flags) +return false; + +return ((OPEN_FMODE (flags) &FMODE_WRITE ) || (flags &O_TRUNC )); +} +/* util.c */ int ovl_want_write (struct dentry *dentry);void ovl_drop_write (struct dentry *dentry);
@@ -230,6 +240,10 @@ bool ovl_dentry_is_whiteout(struct dentry *dentry);void ovl_dentry_set_opaque (struct dentry *dentry);bool ovl_dentry_has_upper_alias (struct dentry *dentry);void ovl_dentry_set_upper_alias (struct dentry *dentry); +bool ovl_dentry_needs_data_copy_up (struct dentry *dentry,int flags); +bool ovl_dentry_needs_data_copy_up_locked (struct dentry *dentry,int flags); +bool ovl_has_upperdata (struct dentry *dentry); +void ovl_set_upperdata (struct inode *inode);bool ovl_redirect_dir (struct super_block *sb);const char *ovl_dentry_get_redirect (struct dentry *dentry);void ovl_dentry_set_redirect (struct dentry *dentry,const char *redirect);
@@ -240,9 +254,9 @@ void ovl_dentry_version_inc(struct dentry *dentry, bool impurity);u64 ovl_dentry_version_get (struct dentry *dentry);bool ovl_is_whiteout (struct dentry *dentry);struct file *ovl_path_open (struct path *path,int flags); -int ovl_copy_up_start (struct dentry *dentry); +int ovl_copy_up_start (struct dentry *dentry,int flags);void ovl_copy_up_end (struct dentry *dentry); -bool ovl_already_copied_up (struct dentry *dentry); +bool ovl_already_copied_up (struct dentry *dentry,int flags);bool ovl_check_origin_xattr (struct dentry *dentry);bool ovl_check_dir_xattr (struct dentry *dentry,const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
fs/overlayfs/super.c +1 -0
@@ -1398,6 +1398,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)/* Root is always merge -> can have whiteouts */ ovl_set_flag (OVL_WHITEOUTS ,d_inode (root_dentry));ovl_dentry_set_flag (OVL_E_CONNECTED , root_dentry); +ovl_set_upperdata (d_inode (root_dentry));ovl_inode_init (d_inode (root_dentry), upperpath.dentry,ovl_dentry_lower (root_dentry));
fs/overlayfs/util.c +74 -4
106 lines not on this page · raw message
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 09/28] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 99 lines · Vivek Goyal
Now we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr
OVL_XATTR_METACOPYto distinguish between two cases.Presence of
OVL_XATTR_METACOPYreflects that file has been copied up metadata only and and data will be copied up later from lower origin. So this xattr is set when a metadata copy takes place and cleared when data copy takes place.We also use a bit in
ovl_inode->flagsto cacheOVL_UPPERDATAwhich reflects whether ovl inode has data or not (as opposed to metadata only copy up).If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in
ovl_inode->flags. While all these operations happen withoi->lockheld, read side ofoi->flagscan be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real()acquire(oi->lock)
ovl_open_maybe_copy_up()ovl_copy_up_data()
open_open_need_copy_up()vfs_removexattr()
ovl_already_copied_up()ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA) ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Hence this patch introduces
smp_wmb()on setting UPPERDATA flag operation andsmp_rmb()on UPPERDATA flag test operation.May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 56 ++++++++++++++++++++++++++++++---- fs/overlayfs/dir.c | 1 + fs/overlayfs/overlayfs.h | 18 +++++++++-- fs/overlayfs/super.c | 1 + fs/overlayfs/util.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 143 insertions(+), 11 deletions(-)fs/overlayfs/copy_up.c +21
-1@@ -195,6 +195,16 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)return error; } +static int ovl_set_size (struct dentry *upperdentry,struct kstat *stat) +{ +struct iattr attr = { + .ia_valid =ATTR_SIZE , + .ia_size = stat->size, + }; + +return notify_change (upperdentry, &attr,NULL ); +} +static int ovl_set_timestamps (struct dentry *upperdentry,struct kstat *stat) {struct iattr attr = {@@ -586,8 +596,18 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } +if (c->metacopy) { + err =ovl_check_setxattr (c->dentry, temp,OVL_XATTR_METACOPY , +NULL ,0 , -EOPNOTSUPP ); +if (err) +return err; + } +inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); +if (c->metacopy) + err =ovl_set_size (temp, &c->stat); +if (!err) + err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode);return err;
fs/overlayfs/copy_up.c static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) · line 651
Quoted 7 lines
fs/overlayfs/copy_up.c +2
-0@@ -625,6 +645,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)if (err) goto out_cleanup; +if (!c->metacopy) +ovl_set_upperdata (d_inode (c->dentry)); inode =d_inode (c->dentry);ovl_inode_update (inode, newdentry);
Following discussion on patch 20/28, I think this should be
if (!c->metacopy)
ovl_set_flag(OVL_UPPERDATA, inode);without the memory barrier, because all the places that check ovl_has_upperdata check upperdentry first, so the smp_wmb() in ovl_inode_update() is sufficient and the extra wmb is really only needed in ovl_copy_up_meta_inode_data().
Right?
Amir.
Re: [PATCH v13 09/28] ovl: A new xattr OVL_XATTR_METACOPY for file on upper
Quoted 112 lines · Amir Goldstein
Quoted 99 lines · Vivek Goyal
Now we will have the capability to have upper inodes which might be only metadata copy up and data is still on lower inode. So add a new xattr
OVL_XATTR_METACOPYto distinguish between two cases.Presence of
OVL_XATTR_METACOPYreflects that file has been copied up metadata only and and data will be copied up later from lower origin. So this xattr is set when a metadata copy takes place and cleared when data copy takes place.We also use a bit in
ovl_inode->flagsto cacheOVL_UPPERDATAwhich reflects whether ovl inode has data or not (as opposed to metadata only copy up).If a file is copied up metadata only and later when same file is opened for WRITE, then data copy up takes place. We copy up data, remove METACOPY xattr and then set the UPPERDATA flag in
ovl_inode->flags. While all these operations happen withoi->lockheld, read side ofoi->flagscan be lockless. That is another thread on another cpu can check if UPPERDATA flag is set or not.So this gives us an ordering requirement w.r.t UPPERDATA flag. That is, if another cpu sees UPPERDATA flag set, then it should be guaranteed that effects of data copy up and remove xattr operations are also visible.
For example.
CPU1 CPU2
ovl_d_real()acquire(oi->lock)
ovl_open_maybe_copy_up()ovl_copy_up_data()
open_open_need_copy_up()vfs_removexattr()
ovl_already_copied_up()ovl_dentry_needs_data_copy_up() ovl_set_flag(OVL_UPPERDATA) ovl_test_flag(OVL_UPPERDATA) release(oi->lock)Say CPU2 is copying up data and in the end sets UPPERDATA flag. But if CPU1 perceives the effects of setting UPPERDATA flag but not the effects of preceeding operations (ex. upper that is not fully copied up), it will be a problem.
Hence this patch introduces
smp_wmb()on setting UPPERDATA flag operation andsmp_rmb()on UPPERDATA flag test operation.May be some other lock or barrier is already covering it. But I am not sure what that is and is it obvious enough that we will not break it in future.
So hence trying to be safe here and introducing barriers explicitly for UPPERDATA flag/bit.
Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/copy_up.c | 56 ++++++++++++++++++++++++++++++---- fs/overlayfs/dir.c | 1 + fs/overlayfs/overlayfs.h | 18 +++++++++-- fs/overlayfs/super.c | 1 + fs/overlayfs/util.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 143 insertions(+), 11 deletions(-)fs/overlayfs/copy_up.c +21
-1@@ -195,6 +195,16 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)return error; } +static int ovl_set_size (struct dentry *upperdentry,struct kstat *stat) +{ +struct iattr attr = { + .ia_valid =ATTR_SIZE , + .ia_size = stat->size, + }; + +return notify_change (upperdentry, &attr,NULL ); +} +static int ovl_set_timestamps (struct dentry *upperdentry,struct kstat *stat) {struct iattr attr = {@@ -586,8 +596,18 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)return err; } +if (c->metacopy) { + err =ovl_check_setxattr (c->dentry, temp,OVL_XATTR_METACOPY , +NULL ,0 , -EOPNOTSUPP ); +if (err) +return err; + } +inode_lock (temp->d_inode); - err =ovl_set_attr (temp, &c->stat); +if (c->metacopy) + err =ovl_set_size (temp, &c->stat); +if (!err) + err =ovl_set_attr (temp, &c->stat);inode_unlock (temp->d_inode);return err;fs/overlayfs/copy_up.c static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) · line 651
Quoted 7 lines
fs/overlayfs/copy_up.c +2
-0@@ -625,6 +645,8 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)if (err) goto out_cleanup; +if (!c->metacopy) +ovl_set_upperdata (d_inode (c->dentry)); inode =d_inode (c->dentry);ovl_inode_update (inode, newdentry);Following discussion on patch 20/28, I think this should be
if (!c->metacopy) ovl_set_flag(OVL_UPPERDATA, inode);
Quoted 6 lines
without the memory barrier, because all the places that check
ovl_has_upperdatacheck upperdentry first, so thesmp_wmb()inovl_inode_update()is sufficient and the extra wmb is really only needed inovl_copy_up_meta_inode_data().Right?
May be. I am not sure. We will need help of a barrier expert to say that we are understanding it right. :-) I did not see this pattern directly mentioned in memory-barrier.txt though.
For now, I would like to stick to exisiting implementation and look at all barrier related optimizations in a separate much smaller patch series.
Vivek
PATCH v13 10/28 ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
In reply to Vivek GoyalThis patch modifies ovl_lookup() and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.
During lookup, check for presence of OVL_XATTR_METACOPY and if not present, set OVL_UPPERDATA bit in flags.
We don't support metacopy feature with nfs_export. So in nfs_export code, we set OVL_UPPERDATA flag set unconditionally if upper inode exists.
Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
--- fs/overlayfs/export.c | 3 ++ fs/overlayfs/inode.c | 6 +++- fs/overlayfs/namei.c | 90 ++++++++++++++++++++++++++++++++++++++++++------ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 22 ++++++++++++ 5 files changed, 110 insertions(+), 12 deletions(-)
fs/overlayfs/export.c +3 -0
@@ -311,6 +311,9 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,return ERR_CAST (inode); } +if (upper) +ovl_set_flag (OVL_UPPERDATA , inode); + dentry =d_find_any_alias (inode);if (!dentry) { dentry =d_alloc_anon (inode->i_sb);
fs/overlayfs/inode.c +5 -1
@@ -677,7 +677,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,struct inode *realinode = upperdentry ?d_inode (upperdentry) :NULL ;struct inode *inode;bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index); -bool is_dir; +bool is_dir, metacopy = false;if (!realinode) realinode =d_inode (lowerdentry);
@@ -732,6 +732,10 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (index)ovl_set_flag (OVL_INDEX , inode); + metacopy =ovl_check_metacopy_xattr (upperdentry ?: lowerdentry); +if (upperdentry && !metacopy) +ovl_set_flag (OVL_UPPERDATA , inode); +OVL_I (inode)->redirect = redirect;/* Check for non-merge dir that may have whiteouts */
fs/overlayfs/namei.c +79 -11
184 lines not on this page · raw message
fs/overlayfs/overlayfs.h +1 -0
@@ -272,6 +272,7 @@ bool ovl_need_index(struct dentry *dentry);int ovl_nlink_start (struct dentry *dentry,bool *locked);void ovl_nlink_end (struct dentry *dentry,bool locked);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir); +int ovl_check_metacopy_xattr (struct dentry *dentry);static inline bool ovl_is_impuredir (struct dentry *dentry) {
fs/overlayfs/util.c +22 -0
@@ -740,3 +740,25 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)pr_err ("overlayfs: failed to lock workdir+upperdir\n" );return -EIO ; } + +/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */ +int ovl_check_metacopy_xattr (struct dentry *dentry) +{ +int res; + +/* Only regular files can have metacopy xattr */ +if (!S_ISREG (d_inode (dentry)->i_mode)) +return 0 ; + + res =vfs_getxattr (dentry,OVL_XATTR_METACOPY ,NULL ,0 ); +if (res <0 ) { +if (res == -ENODATA || res == -EOPNOTSUPP ) +return 0 ; + goto out; + } + +return 1 ; +out: +pr_warn_ratelimited ("overlayfs: failed to get metacopy (%i)\n" , res); +return res; +}
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 10/28] ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
Quoted 51 lines · Vivek Goyal
This patch modifies
ovl_lookup()and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags.We don't support metacopy feature with
nfs_export. So innfs_exportcode, we setOVL_UPPERDATAflag set unconditionally if upper inode exists.Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/export.c | 3 ++ fs/overlayfs/inode.c | 6 +++- fs/overlayfs/namei.c | 90 ++++++++++++++++++++++++++++++++++++++++++------ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 22 ++++++++++++ 5 files changed, 110 insertions(+), 12 deletions(-)fs/overlayfs/export.c +3
-0@@ -311,6 +311,9 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,return ERR_CAST (inode); } +if (upper) +ovl_set_flag (OVL_UPPERDATA , inode); + dentry =d_find_any_alias (inode);if (!dentry) { dentry =d_alloc_anon (inode->i_sb);fs/overlayfs/inode.c +1
-1@@ -677,7 +677,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,struct inode *realinode = upperdentry ?d_inode (upperdentry) :NULL ;struct inode *inode;bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index); -bool is_dir; +bool is_dir, metacopy = false;if (!realinode) realinode =d_inode (lowerdentry);
fs/overlayfs/inode.c struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, · line 735
Quoted 4 lines
fs/overlayfs/inode.c +1
-0@@ -732,6 +732,10 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (index)ovl_set_flag (OVL_INDEX , inode); + metacopy =ovl_check_metacopy_xattr (upperdentry ?: lowerdentry);
No reason to check metacopy on lowerdentry.
Quoted 19 lines
fs/overlayfs/inode.c +3
-0+if (upperdentry && !metacopy) +ovl_set_flag (OVL_UPPERDATA , inode); +OVL_I (inode)->redirect = redirect;/* Check for non-merge dir that may have whiteouts */ fs/overlayfs/namei.c +1
-0@@ -24,6 +24,7 @@ struct ovl_lookup_data {bool stop;bool last;char *redirect; +bool metacopy; }; static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 255
Quoted 4 lines
fs/overlayfs/namei.c +0
-1@@ -252,9 +253,13 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto put_and_out; }if (!d_can_lookup (this)) { - d->stop = true;
upper was a dir. You look in lower and find a non-dir. you need to stop going to next layer. goto put_and_out won't do that.
Similarly, you need to handle the case where dir is found below non-dir with metacopy.
Quoted 102 lines
fs/overlayfs/namei.c +25
-7if (d->is_dir) goto put_and_out; + err =ovl_check_metacopy_xattr (this); +if (err <0 ) + goto out_err; + d->stop = !err; + d->metacopy = !!err; goto out; }if (last_element)@@ -815,7 +820,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct ovl_fs *ofs = dentry->d_sb->s_fs_info;struct ovl_entry *poe = dentry->d_parent->d_fsdata;struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; -struct ovl_path *stack =NULL ; +struct ovl_path *stack =NULL , *origin_path =NULL ;struct dentry *upperdir, *upperdentry =NULL ;struct dentry *origin =NULL ;struct dentry *index =NULL ;@@ -826,6 +831,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct dentry *this;unsigned int i;int err; +bool metacopy = false;struct ovl_lookup_data d = { .name = dentry->d_name, .is_dir = false,@@ -833,6 +839,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .stop = false, .last = ofs->config.redirect_follow ? false : !poe->numlower, .redirect = NULL, + .metacopy = false, };if (dentry->d_name.len > ofs->namelen)@@ -851,7 +858,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out; }if (upperdentry && !d.is_dir) { -BUG_ON (!d.stop || d.redirect); +unsigned int origin_ctr =0 ; +BUG_ON (d.redirect);/* * Lookup copy up origin by decoding origin file handle. * We may get a disconnected dentry, which is fine, @@ -862,16 +870,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* number - it's the same as if we held a reference * to a dentry in lower layer that was moved under us. */ - err =ovl_check_origin (ofs, upperdentry, &stack, &ctr); + err =ovl_check_origin (ofs, upperdentry, &origin_path, + &origin_ctr);if (err) goto out_put_upper; + +if (d.metacopy) + metacopy = true; }if (d.redirect) { err = -ENOMEM ; upperredirect =kstrdup (d.redirect,GFP_KERNEL );if (!upperredirect) - goto out_put_upper; + goto out_put_origin;if (d.redirect[0 ] =='/' ) poe = roe; }@@ -883,7 +895,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, stack =kcalloc (ofs->numlower,sizeof (struct ovl_path ),GFP_KERNEL );if (!stack) - goto out_put_upper; + goto out_put_origin; }for (i =0 ; !d.stop && i < poe->numlower; i++) {@@ -905,7 +917,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* If no origin fh is stored in upper of a merge dir, store fh * of lower dir and set upper parent "impure". */ -if (upperdentry && !ctr && !ofs->noxattr) { +if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) { err =ovl_fix_origin (dentry, this, upperdentry);if (err) {dput (this);@@ -917,16 +929,35 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* When "verify_lower" feature is enabled, do not merge with a * lower dir that does not match a stored origin xattr. In any * case, only verified origin is used for index lookup. +* +* For non-dir dentry, make sure dentry found by lookup +* matches the origin stored in upper. Otherwise its an +* error. */ -if (upperdentry && !ctr &&ovl_verify_lower (dentry->d_sb)) { +if (upperdentry && !ctr && + ((d.is_dir &&ovl_verify_lower (dentry->d_sb)) || + (!d.is_dir && origin_path))) { err =ovl_verify_origin (upperdentry, this, false);if (err) {dput (this);
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 949
Quoted 10 lines
fs/overlayfs/namei.c +5
-3-break ; +if (d.is_dir) +break ; + goto out_put; } -/* Bless lower dir as verified origin */ - origin = this; +if (d.is_dir) + origin = this;
It's ok to bless verified non-dir as well.
It is going to be blesses anyway, just above index lookup if ctr > 0.
Quoted 15 lines
fs/overlayfs/namei.c +11
-0+ } + +if (d.metacopy) + metacopy = true; +/* +* Do not store intermediate metacopy dentries in chain, +* except top most lower metacopy dentry +*/ +if (d.metacopy && ctr) { +dput (this); +continue ; } stack[ctr].dentry = this;
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 995
Quoted 5 lines
fs/overlayfs/namei.c +2
-0@@ -960,6 +991,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } +if (metacopy) { +BUG_ON (d.is_dir);
Yeh, I think that is really a bug, because you need to detect the case of dir in lower layer under metacopy in upper layer and do something about it.
Quoted 41 lines
fs/overlayfs/namei.c +30
-0+/* +* Found a metacopy dentry but did not find corresponding +* data dentry +*/ +if (d.metacopy) { + err = -ESTALE ; + goto out_put; + } + + err = -EPERM ; +if (!ofs->config.metacopy) { +pr_warn_ratelimited ("overlay: refusing to follow" +" metacopy origin for (%pd2)\n" , + dentry); + goto out_put; + } + }else if (!d.is_dir && upperdentry && !ctr && origin_path) { +if (WARN_ON (stack !=NULL )) { + err = -EIO ; + goto out_put; + } + stack = origin_path; + ctr =1 ; + origin_path =NULL ; + } +/* * Lookup index by lower inode and verify it matches upper inode. * We only trust dir index if we verified that lower dir matches @@ -1006,6 +1065,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, }revert_creds (old_cred); +if (origin_path) { +dput (origin_path->dentry); +kfree (origin_path); + }dput (index);kfree (stack);kfree (d.redirect);
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 1089
Quoted 8 lines
fs/overlayfs/namei.c +5
-0@@ -1019,6 +1082,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,for (i =0 ; i < ctr; i++)dput (stack[i].dentry);kfree (stack); +out_put_origin: +if (origin_path) { +dput (origin_path->dentry); +kfree (origin_path); + }
There is no need for the new goto label.
Just add this in existing out_put_upper label.
Thanks,
Amir,
Amir Goldstein
maintainerRe: [PATCH v13 10/28] ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
Quoted 17 lines · Amir Goldstein
Quoted 16 lines · Vivek Goyal
This patch modifies
ovl_lookup()and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags.We don't support metacopy feature with
nfs_export. So innfs_exportcode, we setOVL_UPPERDATAflag set unconditionally if upper inode exists.Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
Signed-off-by Vivek Goyal <[email protected]>---
[...]
Quoted 49 lines
Quoted 16 lines
fs/overlayfs/namei.c +7
-1@@ -917,16 +929,35 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* When "verify_lower" feature is enabled, do not merge with a * lower dir that does not match a stored origin xattr. In any * case, only verified origin is used for index lookup. +* +* For non-dir dentry, make sure dentry found by lookup +* matches the origin stored in upper. Otherwise its an +* error. */ -if (upperdentry && !ctr &&ovl_verify_lower (dentry->d_sb)) { +if (upperdentry && !ctr && + ((d.is_dir &&ovl_verify_lower (dentry->d_sb)) || + (!d.is_dir && origin_path))) { err =ovl_verify_origin (upperdentry, this, false);if (err) {dput (this);fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 949
Quoted 10 lines
fs/overlayfs/namei.c +5
-3-break ; +if (d.is_dir) +break ; + goto out_put; } -/* Bless lower dir as verified origin */ - origin = this; +if (d.is_dir) + origin = this;It's ok to bless verified non-dir as well.
It is going to be blesses anyway, just above index lookup if ctr > 0.Quoted 18 lines
fs/overlayfs/namei.c +11
-0+ } + +if (d.metacopy) + metacopy = true; +/* +* Do not store intermediate metacopy dentries in chain, +* except top most lower metacopy dentry +*/ +if (d.metacopy && ctr) { +dput (this); +continue ; } stack[ctr].dentry = this;@@ -960,6 +991,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } }
Quoted 23 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 995
Quoted 2 lines
fs/overlayfs/namei.c +2
-0+if (metacopy) { +BUG_ON (d.is_dir);Yeh, I think that is really a bug, because you need to detect the case of dir in lower layer under metacopy in upper layer and do something about it.
Quoted 16 lines
fs/overlayfs/namei.c +16
-0+/* +* Found a metacopy dentry but did not find corresponding +* data dentry +*/ +if (d.metacopy) { + err = -ESTALE ; + goto out_put; + } + + err = -EPERM ; +if (!ofs->config.metacopy) { +pr_warn_ratelimited ("overlay: refusing to follow" +" metacopy origin for (%pd2)\n" , + dentry); + goto out_put; + }
in (!origin_path) lower was followed by name/redirect and not verified by origin, we should not lookup index of non-dir below. Right now non-dir index entries assume that origin xattr exists and matches the entry name. We may be able to relax that in the future using non-dir redirect, but we are not there yet.
Quoted 5 lines
Quoted 5 lines
fs/overlayfs/namei.c +5
-0+ }else if (!d.is_dir && upperdentry && !ctr && origin_path) { +if (WARN_ON (stack !=NULL )) { + err = -EIO ; + goto out_put; + }
Quoted 8 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 1024
Quoted 8 lines
fs/overlayfs/namei.c +5
-0+ stack = origin_path; + ctr =1 ; + origin_path =NULL ; + } +/* * Lookup index by lower inode and verify it matches upper inode. * We only trust dir index if we verified that lower dir matches
* origin, otherwise dir index entries may be inconsistent and we
* ignore them. Always lookup index of non-dir and non-upper.
*/
if (ctr && (!upperdentry || !d.is_dir))
origin = stack[0].dentry;So this condition needs to be fixed.
Thanks,
Amir.
Re: [PATCH v13 10/28] ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
Quoted 93 lines · Amir Goldstein
Quoted 17 lines · Amir Goldstein
Quoted 16 lines · Vivek Goyal
This patch modifies
ovl_lookup()and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags.We don't support metacopy feature with
nfs_export. So innfs_exportcode, we setOVL_UPPERDATAflag set unconditionally if upper inode exists.Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
Signed-off-by Vivek Goyal <[email protected]>---
[...]
Quoted 49 lines
Quoted 16 lines
fs/overlayfs/namei.c +7
-1@@ -917,16 +929,35 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* When "verify_lower" feature is enabled, do not merge with a * lower dir that does not match a stored origin xattr. In any * case, only verified origin is used for index lookup. +* +* For non-dir dentry, make sure dentry found by lookup +* matches the origin stored in upper. Otherwise its an +* error. */ -if (upperdentry && !ctr &&ovl_verify_lower (dentry->d_sb)) { +if (upperdentry && !ctr && + ((d.is_dir &&ovl_verify_lower (dentry->d_sb)) || + (!d.is_dir && origin_path))) { err =ovl_verify_origin (upperdentry, this, false);if (err) {dput (this);fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 949
Quoted 10 lines
fs/overlayfs/namei.c +5
-3-break ; +if (d.is_dir) +break ; + goto out_put; } -/* Bless lower dir as verified origin */ - origin = this; +if (d.is_dir) + origin = this;It's ok to bless verified non-dir as well.
It is going to be blesses anyway, just above index lookup if ctr > 0.Quoted 18 lines
fs/overlayfs/namei.c +11
-0+ } + +if (d.metacopy) + metacopy = true; +/* +* Do not store intermediate metacopy dentries in chain, +* except top most lower metacopy dentry +*/ +if (d.metacopy && ctr) { +dput (this); +continue ; } stack[ctr].dentry = this;@@ -960,6 +991,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } }Quoted 23 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 995
Quoted 2 lines
fs/overlayfs/namei.c +2
-0+if (metacopy) { +BUG_ON (d.is_dir);Yeh, I think that is really a bug, because you need to detect the case of dir in lower layer under metacopy in upper layer and do something about it.
Quoted 16 lines
fs/overlayfs/namei.c +16
-0+/* +* Found a metacopy dentry but did not find corresponding +* data dentry +*/ +if (d.metacopy) { + err = -ESTALE ; + goto out_put; + } + + err = -EPERM ; +if (!ofs->config.metacopy) { +pr_warn_ratelimited ("overlay: refusing to follow" +" metacopy origin for (%pd2)\n" , + dentry); + goto out_put; + }
Quoted 5 lines
in (!origin_path) lower was followed by name/redirect and not verified by origin, we should not lookup index of non-dir below. Right now non-dir index entries assume that origin xattr exists and matches the entry name. We may be able to relax that in the future using non-dir redirect, but we are not there yet.
Ok.
Quoted 15 lines
Quoted 15 lines
Quoted 5 lines
fs/overlayfs/namei.c +3
-0+ }else if (!d.is_dir && upperdentry && !ctr && origin_path) { +if (WARN_ON (stack !=NULL )) { + err = -EIO ;fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 1024
Quoted 10 lines
fs/overlayfs/namei.c +7
-0+ goto out_put; + } + stack = origin_path; + ctr =1 ; + origin_path =NULL ; + } +/* * Lookup index by lower inode and verify it matches upper inode. * We only trust dir index if we verified that lower dir matches
Quoted 7 lines
* origin, otherwise dir index entries may be inconsistent and we * ignore them. Always lookup index of non-dir and non-upper. */ if (ctr && (!upperdentry || !d.is_dir)) origin = stack[0].dentry;So this condition needs to be fixed.
Ok, So I could make it like the case of directory. That is for metacopy files bless origin only if it was verified and modify above condiiton as follows.
if (ctr && (!upperdentry || (!d.is_dir && !metacopy)))
origin = stack[0].dentry;That way for a metcopy dentry we will search for index only if origin was verified.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 10/28] ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
Quoted 129 lines · Vivek Goyal
Quoted 93 lines · Amir Goldstein
Quoted 17 lines · Amir Goldstein
Quoted 16 lines · Vivek Goyal
This patch modifies
ovl_lookup()and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags.We don't support metacopy feature with
nfs_export. So innfs_exportcode, we setOVL_UPPERDATAflag set unconditionally if upper inode exists.Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
Signed-off-by Vivek Goyal <[email protected]>---
[...]
Quoted 49 lines
Quoted 16 lines
fs/overlayfs/namei.c +7
-1@@ -917,16 +929,35 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* When "verify_lower" feature is enabled, do not merge with a * lower dir that does not match a stored origin xattr. In any * case, only verified origin is used for index lookup. +* +* For non-dir dentry, make sure dentry found by lookup +* matches the origin stored in upper. Otherwise its an +* error. */ -if (upperdentry && !ctr &&ovl_verify_lower (dentry->d_sb)) { +if (upperdentry && !ctr && + ((d.is_dir &&ovl_verify_lower (dentry->d_sb)) || + (!d.is_dir && origin_path))) { err =ovl_verify_origin (upperdentry, this, false);if (err) {dput (this);fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 949
Quoted 10 lines
fs/overlayfs/namei.c +5
-3-break ; +if (d.is_dir) +break ; + goto out_put; } -/* Bless lower dir as verified origin */ - origin = this; +if (d.is_dir) + origin = this;It's ok to bless verified non-dir as well.
It is going to be blesses anyway, just above index lookup if ctr > 0.Quoted 18 lines
fs/overlayfs/namei.c +11
-0+ } + +if (d.metacopy) + metacopy = true; +/* +* Do not store intermediate metacopy dentries in chain, +* except top most lower metacopy dentry +*/ +if (d.metacopy && ctr) { +dput (this); +continue ; } stack[ctr].dentry = this;@@ -960,6 +991,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } }Quoted 23 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 995
Quoted 2 lines
fs/overlayfs/namei.c +2
-0+if (metacopy) { +BUG_ON (d.is_dir);Yeh, I think that is really a bug, because you need to detect the case of dir in lower layer under metacopy in upper layer and do something about it.
Quoted 16 lines
fs/overlayfs/namei.c +16
-0+/* +* Found a metacopy dentry but did not find corresponding +* data dentry +*/ +if (d.metacopy) { + err = -ESTALE ; + goto out_put; + } + + err = -EPERM ; +if (!ofs->config.metacopy) { +pr_warn_ratelimited ("overlay: refusing to follow" +" metacopy origin for (%pd2)\n" , + dentry); + goto out_put; + }Quoted 5 lines
in (!origin_path) lower was followed by name/redirect and not verified by origin, we should not lookup index of non-dir below. Right now non-dir index entries assume that origin xattr exists and matches the entry name. We may be able to relax that in the future using non-dir redirect, but we are not there yet.
Ok.
Quoted 15 lines
Quoted 15 lines
Quoted 5 lines
fs/overlayfs/namei.c +3
-0+ }else if (!d.is_dir && upperdentry && !ctr && origin_path) { +if (WARN_ON (stack !=NULL )) { + err = -EIO ;fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 1024
Quoted 10 lines
fs/overlayfs/namei.c +7
-0+ goto out_put; + } + stack = origin_path; + ctr =1 ; + origin_path =NULL ; + } +/* * Lookup index by lower inode and verify it matches upper inode. * We only trust dir index if we verified that lower dir matches Quoted 7 lines
* origin, otherwise dir index entries may be inconsistent and we * ignore them. Always lookup index of non-dir and non-upper. */ if (ctr && (!upperdentry || !d.is_dir)) origin = stack[0].dentry;So this condition needs to be fixed.
Ok, So I could make it like the case of directory. That is for metacopy files bless origin only if it was verified and modify above condiiton as follows.
Quoted 6 lines
if (ctr && (!upperdentry || (!d.is_dir && !metacopy))) origin = stack[0].dentry;That way for a metcopy dentry we will search for index only if origin was verified.
Correct.
Thanks,
Amir.
Re: [PATCH v13 10/28] ovl: Modify ovl_lookup() and friends to lookup metacopy dentry
In reply to Amir GoldsteinQuoted 55 lines · Amir Goldstein
Quoted 54 lines · Vivek Goyal
This patch modifies
ovl_lookup()and friends to lookup metacopy dentries. It also allows for presence of metacopy dentries in lower layer.During lookup, check for presence of
OVL_XATTR_METACOPYand if not present, setOVL_UPPERDATAbit in flags.We don't support metacopy feature with
nfs_export. So innfs_exportcode, we setOVL_UPPERDATAflag set unconditionally if upper inode exists.Do not follow metacopy origin if we find a metacopy only inode and metacopy feature is not enabled for that mount. Like redirect, this can have security implications where an attacker could hand craft upper and try to gain access to file on lower which it should not have to begin with.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/export.c | 3 ++ fs/overlayfs/inode.c | 6 +++- fs/overlayfs/namei.c | 90 ++++++++++++++++++++++++++++++++++++++++++------ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 22 ++++++++++++ 5 files changed, 110 insertions(+), 12 deletions(-)fs/overlayfs/export.c +3
-0@@ -311,6 +311,9 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,return ERR_CAST (inode); } +if (upper) +ovl_set_flag (OVL_UPPERDATA , inode); + dentry =d_find_any_alias (inode);if (!dentry) { dentry =d_alloc_anon (inode->i_sb);fs/overlayfs/inode.c +1
-1@@ -677,7 +677,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,struct inode *realinode = upperdentry ?d_inode (upperdentry) :NULL ;struct inode *inode;bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index); -bool is_dir; +bool is_dir, metacopy = false;if (!realinode) realinode =d_inode (lowerdentry);@@ -732,6 +732,10 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (index)ovl_set_flag (OVL_INDEX , inode);
Quoted 3 lines
fs/overlayfs/inode.c struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, · line 735
Quoted 1 line
fs/overlayfs/inode.c +1
-0+ metacopy =ovl_check_metacopy_xattr (upperdentry ?: lowerdentry);No reason to check metacopy on lowerdentry.
Right. Will change it.
Quoted 18 lines
Quoted 18 lines
fs/overlayfs/inode.c +3
-0+if (upperdentry && !metacopy) +ovl_set_flag (OVL_UPPERDATA , inode); +OVL_I (inode)->redirect = redirect;/* Check for non-merge dir that may have whiteouts */ fs/overlayfs/namei.c +1
-0@@ -24,6 +24,7 @@ struct ovl_lookup_data {bool stop;bool last;char *redirect; +bool metacopy; };
Quoted 9 lines
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 255
Quoted 6 lines
fs/overlayfs/namei.c +0
-1static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,@@ -252,9 +253,13 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto put_and_out; }if (!d_can_lookup (this)) { - d->stop = true;upper was a dir. You look in lower and find a non-dir. you need to stop going to next layer. goto
put_and_outwon't do that.
Ok, will set d->stop = true before "goto put_and_out" to handle this case.
Quoted 3 lines
Similarly, you need to handle the case where dir is found below non-dir with metacopy.
Ok, will look into it.
Quoted 108 lines
Quoted 108 lines
fs/overlayfs/namei.c +28
-8if (d->is_dir) goto put_and_out; + err =ovl_check_metacopy_xattr (this); +if (err <0 ) + goto out_err; + d->stop = !err; + d->metacopy = !!err; goto out; }if (last_element)@@ -815,7 +820,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct ovl_fs *ofs = dentry->d_sb->s_fs_info;struct ovl_entry *poe = dentry->d_parent->d_fsdata;struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; -struct ovl_path *stack =NULL ; +struct ovl_path *stack =NULL , *origin_path =NULL ;struct dentry *upperdir, *upperdentry =NULL ;struct dentry *origin =NULL ;struct dentry *index =NULL ;@@ -826,6 +831,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct dentry *this;unsigned int i;int err; +bool metacopy = false;struct ovl_lookup_data d = { .name = dentry->d_name, .is_dir = false,@@ -833,6 +839,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .stop = false, .last = ofs->config.redirect_follow ? false : !poe->numlower, .redirect = NULL, + .metacopy = false, };if (dentry->d_name.len > ofs->namelen)@@ -851,7 +858,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out; }if (upperdentry && !d.is_dir) { -BUG_ON (!d.stop || d.redirect); +unsigned int origin_ctr =0 ; +BUG_ON (d.redirect);/* * Lookup copy up origin by decoding origin file handle. * We may get a disconnected dentry, which is fine, @@ -862,16 +870,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* number - it's the same as if we held a reference * to a dentry in lower layer that was moved under us. */ - err =ovl_check_origin (ofs, upperdentry, &stack, &ctr); + err =ovl_check_origin (ofs, upperdentry, &origin_path, + &origin_ctr);if (err) goto out_put_upper; + +if (d.metacopy) + metacopy = true; }if (d.redirect) { err = -ENOMEM ; upperredirect =kstrdup (d.redirect,GFP_KERNEL );if (!upperredirect) - goto out_put_upper; + goto out_put_origin;if (d.redirect[0 ] =='/' ) poe = roe; }@@ -883,7 +895,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, stack =kcalloc (ofs->numlower,sizeof (struct ovl_path ),GFP_KERNEL );if (!stack) - goto out_put_upper; + goto out_put_origin; }for (i =0 ; !d.stop && i < poe->numlower; i++) {@@ -905,7 +917,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* If no origin fh is stored in upper of a merge dir, store fh * of lower dir and set upper parent "impure". */ -if (upperdentry && !ctr && !ofs->noxattr) { +if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) { err =ovl_fix_origin (dentry, this, upperdentry);if (err) {dput (this);@@ -917,16 +929,35 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,* When "verify_lower" feature is enabled, do not merge with a * lower dir that does not match a stored origin xattr. In any * case, only verified origin is used for index lookup. +* +* For non-dir dentry, make sure dentry found by lookup +* matches the origin stored in upper. Otherwise its an +* error. */ -if (upperdentry && !ctr &&ovl_verify_lower (dentry->d_sb)) { +if (upperdentry && !ctr && + ((d.is_dir &&ovl_verify_lower (dentry->d_sb)) || + (!d.is_dir && origin_path))) { err =ovl_verify_origin (upperdentry, this, false);if (err) {dput (this); -break ; +if (d.is_dir) +break ; + goto out_put; }
Quoted 8 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 949
Quoted 5 lines
fs/overlayfs/namei.c +2
-2-/* Bless lower dir as verified origin */ - origin = this; +if (d.is_dir) + origin = this;It's ok to bless verified non-dir as well.
It is going to be blesses anyway, just above index lookup if ctr > 0.
Hmm..., make sense to trust origin once we have verified origin both for dir and non-dir. Will do.
Quoted 70 lines
Quoted 16 lines
fs/overlayfs/namei.c +11
-0+ } + +if (d.metacopy) + metacopy = true; +/* +* Do not store intermediate metacopy dentries in chain, +* except top most lower metacopy dentry +*/ +if (d.metacopy && ctr) { +dput (this); +continue ; } stack[ctr].dentry = this;fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 995
Quoted 5 lines
fs/overlayfs/namei.c +2
-0@@ -960,6 +991,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } +if (metacopy) { +BUG_ON (d.is_dir);Yeh, I think that is really a bug, because you need to detect the case of dir in lower layer under metacopy in upper layer and do something about it.
Quoted 44 lines
fs/overlayfs/namei.c +30
-0+/* +* Found a metacopy dentry but did not find corresponding +* data dentry +*/ +if (d.metacopy) { + err = -ESTALE ; + goto out_put; + } + + err = -EPERM ; +if (!ofs->config.metacopy) { +pr_warn_ratelimited ("overlay: refusing to follow" +" metacopy origin for (%pd2)\n" , + dentry); + goto out_put; + } + }else if (!d.is_dir && upperdentry && !ctr && origin_path) { +if (WARN_ON (stack !=NULL )) { + err = -EIO ; + goto out_put; + } + stack = origin_path; + ctr =1 ; + origin_path =NULL ; + } +/* * Lookup index by lower inode and verify it matches upper inode. * We only trust dir index if we verified that lower dir matches @@ -1006,6 +1065,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, }revert_creds (old_cred); +if (origin_path) { +dput (origin_path->dentry); +kfree (origin_path); + }dput (index);kfree (stack);kfree (d.redirect);@@ -1019,6 +1082,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,for (i =0 ; i < ctr; i++)dput (stack[i].dentry);kfree (stack);
Quoted 8 lines
fs/overlayfs/namei.c struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, · line 1089
Quoted 5 lines
fs/overlayfs/namei.c +5
-0+out_put_origin: +if (origin_path) { +dput (origin_path->dentry); +kfree (origin_path); + }There is no need for the new goto label.
Just add this in existingout_put_upperlabel.
Ok, I should be able to club this with out_put_upper label.
Vivek
Quoted 3 lines
Thanks,
Amir,
PATCH v13 11/28 ovl: Copy up meta inode data from lowest data inode
In reply to Vivek GoyalSo far lower could not be a meta inode. So whenever it was time to copy up data of a meta inode, we could copy it up from top most lower dentry.
But now lower itself can be a metacopy inode. That means data copy up needs to take place from a data inode in metacopy inode chain. Find lower data inode in the chain and use that for data copy up.
Introduced a helper called ovl_path_lowerdata() to find the lower data inode chain.
--- fs/overlayfs/copy_up.c | 14 ++++++++++---- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-)
fs/overlayfs/copy_up.c +10 -4
@@ -585,13 +585,15 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) }if (S_ISREG (c->stat.mode) && !c->metacopy) { -struct path upperpath; +struct path upperpath, datapath;ovl_path_upper (c->dentry, &upperpath);BUG_ON (upperpath.dentry !=NULL ); upperpath.dentry = temp; - err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +ovl_path_lowerdata (c->dentry, &datapath); +BUG_ON (datapath.dentry ==NULL ); + err =ovl_copy_up_data (&datapath, &upperpath, c->stat.size);if (err)return err; }
@@ -754,14 +756,18 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)/* Copy up data of an inode which was copied up metadata only in the past. */ static int ovl_copy_up_meta_inode_data (struct ovl_copy_up_ctx *c) { -struct path upperpath; +struct path upperpath, datapath;int err;ovl_path_upper (c->dentry, &upperpath);if (WARN_ON (upperpath.dentry ==NULL ))return -EIO ; - err =ovl_copy_up_data (&c->lowerpath, &upperpath, c->stat.size); +ovl_path_lowerdata (c->dentry, &datapath); +if (WARN_ON (datapath.dentry ==NULL )) +return -EIO ; + + err =ovl_copy_up_data (&datapath, &upperpath, c->stat.size);if (err)return err;
fs/overlayfs/overlayfs.h +1 -0
@@ -222,6 +222,7 @@ bool ovl_dentry_weird(struct dentry *dentry);enum ovl_path_type ovl_path_type (struct dentry *dentry);void ovl_path_upper (struct dentry *dentry,struct path *path);void ovl_path_lower (struct dentry *dentry,struct path *path); +void ovl_path_lowerdata (struct dentry *dentry,struct path *path);enum ovl_path_type ovl_path_real (struct dentry *dentry,struct path *path);struct dentry *ovl_dentry_upper (struct dentry *dentry);struct dentry *ovl_dentry_lower (struct dentry *dentry);
fs/overlayfs/util.c +14 -0
@@ -148,6 +148,20 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) } } +void ovl_path_lowerdata (struct dentry *dentry,struct path *path) +{ +struct ovl_entry *oe = dentry->d_fsdata; +int idx = oe->numlower -1 ; + +if (!oe->numlower) { + *path = (struct path ) { }; +return ; + } + + path->mnt = oe->lowerstack[idx].layer->mnt; + path->dentry = oe->lowerstack[idx].dentry; +} +enum ovl_path_type ovl_path_real (struct dentry *dentry,struct path *path) {enum ovl_path_type type =ovl_path_type (dentry);
Signature · 1 line
2.13.6
PATCH v13 12/28 ovl: Fix ovl_getattr() to get number of blocks from lower
In reply to Vivek GoyalIf an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the stat->st_blocks.
We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return stat->st_blocks either from lower or stable upper and not something in between. Hence, ovl_has_upperdata() is called first to figure out whether block reporting will take place from lower or upper.
--- fs/overlayfs/inode.c | 17 ++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-)
fs/overlayfs/inode.c +16 -1
@@ -76,6 +76,9 @@ int ovl_getattr(const struct path *path, struct kstat *stat,bool is_dir =S_ISDIR (dentry->d_inode->i_mode);bool samefs =ovl_same_sb (dentry->d_sb);int err; +bool metacopy = false; + + metacopy =ovl_is_metacopy_dentry (dentry); type =ovl_path_real (dentry, &realpath); old_cred =ovl_override_creds (dentry->d_sb);
@@ -93,7 +96,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,if (!is_dir || samefs) {if (OVL_TYPE_ORIGIN (type)) {struct kstat lowerstat; -u32 lowermask =STATX_INO | (!is_dir ?STATX_NLINK :0 ); +u32 lowermask =STATX_INO |STATX_BLOCKS | + (!is_dir ?STATX_NLINK :0 );ovl_path_lower (dentry, &realpath); err = vfs_getattr(&realpath, &lowerstat,
@@ -126,6 +130,17 @@ int ovl_getattr(const struct path *path, struct kstat *stat,else stat->dev =ovl_get_pseudo_dev (dentry); } +if (metacopy) { +struct kstat lowerdatastat; +u32 lowermask =STATX_BLOCKS ; + +ovl_path_lowerdata (dentry, &realpath); + err =vfs_getattr (&realpath, &lowerdatastat, + lowermask, flags); +if (err) + goto out; + stat->blocks = lowerdatastat.blocks; + }if (samefs) {/* * When all layers are on the same fs, all real inode
fs/overlayfs/overlayfs.h +1 -0
@@ -274,6 +274,7 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked);void ovl_nlink_end (struct dentry *dentry,bool locked);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry); +bool ovl_is_metacopy_dentry (struct dentry *dentry);static inline bool ovl_is_impuredir (struct dentry *dentry) {
fs/overlayfs/util.c +16 -0
@@ -776,3 +776,19 @@ int ovl_check_metacopy_xattr(struct dentry *dentry)pr_warn_ratelimited ("overlayfs: failed to get metacopy (%i)\n" , res);return res; } + +bool ovl_is_metacopy_dentry (struct dentry *dentry) +{ +struct ovl_entry *oe = dentry->d_fsdata; + +if (!d_is_reg (dentry)) +return false; + +if (ovl_dentry_upper (dentry)) { +if (!ovl_has_upperdata (dentry)) +return true; +return false; + } + +return (oe->numlower >1 ); +}
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 12/28] ovl: Fix ovl_getattr() to get number of blocks from lower
Quoted 32 lines · Vivek Goyal
If an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the
stat->st_blocks.We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return
stat->st_blockseither from lower or stable upper and not something in between. Hence,ovl_has_upperdata()is called first to figure out whether block reporting will take place from lower or upper.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/inode.c | 17 ++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +3
-0@@ -76,6 +76,9 @@ int ovl_getattr(const struct path *path, struct kstat *stat,bool is_dir =S_ISDIR (dentry->d_inode->i_mode);bool samefs =ovl_same_sb (dentry->d_sb);int err; +bool metacopy = false; + + metacopy =ovl_is_metacopy_dentry (dentry); type =ovl_path_real (dentry, &realpath); old_cred =ovl_override_creds (dentry->d_sb);
fs/overlayfs/inode.c int ovl_getattr(const struct path *path, struct kstat *stat, · line 100
Quoted 6 lines
fs/overlayfs/inode.c +2
-1@@ -93,7 +96,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,if (!is_dir || samefs) {if (OVL_TYPE_ORIGIN (type)) {struct kstat lowerstat; -u32 lowermask =STATX_INO | (!is_dir ?STATX_NLINK :0 ); +u32 lowermask =STATX_INO |STATX_BLOCKS | + (!is_dir ?STATX_NLINK :0 );
Leftover.
But I think you could do with one vfs_getattr():
if (!is_dir || samefs) {
u32 lowermask = metacopy ? STATX_BLOCKS : 0;
if (OVL_TYPE_ORIGIN(type))
lowermask |= STATX_INO | (!is_dir ? STATX_NLINK : 0);
if (lowermask) {
ovl_path_lower(dentry, &realpath);
err = vfs_getattr(&realpath, &lowerstat,...
Thanks,
Amir.
Re: [PATCH v13 12/28] ovl: Fix ovl_getattr() to get number of blocks from lower
Quoted 44 lines · Amir Goldstein
Quoted 32 lines · Vivek Goyal
If an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the
stat->st_blocks.We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return
stat->st_blockseither from lower or stable upper and not something in between. Hence,ovl_has_upperdata()is called first to figure out whether block reporting will take place from lower or upper.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/inode.c | 17 ++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +3
-0@@ -76,6 +76,9 @@ int ovl_getattr(const struct path *path, struct kstat *stat,bool is_dir =S_ISDIR (dentry->d_inode->i_mode);bool samefs =ovl_same_sb (dentry->d_sb);int err; +bool metacopy = false; + + metacopy =ovl_is_metacopy_dentry (dentry); type =ovl_path_real (dentry, &realpath); old_cred =ovl_override_creds (dentry->d_sb);fs/overlayfs/inode.c int ovl_getattr(const struct path *path, struct kstat *stat, · line 100
Quoted 6 lines
fs/overlayfs/inode.c +2
-1@@ -93,7 +96,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,if (!is_dir || samefs) {if (OVL_TYPE_ORIGIN (type)) {struct kstat lowerstat; -u32 lowermask =STATX_INO | (!is_dir ?STATX_NLINK :0 ); +u32 lowermask =STATX_INO |STATX_BLOCKS | + (!is_dir ?STATX_NLINK :0 );Leftover.
But I think you could do with one
vfs_getattr():
Quoted 7 lines
if (!is_dir || samefs) {
u32 lowermask = metacopy ? STATX_BLOCKS : 0;
if (OVL_TYPE_ORIGIN(type))
lowermask |= STATX_INO | (!is_dir ? STATX_NLINK : 0);
if (lowermask) {
ovl_path_lower(dentry, &realpath);
err = vfs_getattr(&realpath, &lowerstat,We now support mid layer metacopy dentries. So we will need to do vfs_getattr() on lowest data dentry for data blocks. While rest of the info (inode number and nlink) comes from top most lower dentry.
So I don't think we can get rid of extra vfs_getattr()?
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 12/28] ovl: Fix ovl_getattr() to get number of blocks from lower
Quoted 53 lines · Vivek Goyal
Quoted 44 lines · Amir Goldstein
Quoted 32 lines · Vivek Goyal
If an inode has been copied up metadata only, then we need to query the number of blocks from lower and fill up the
stat->st_blocks.We need to be careful about races where we are doing stat on one cpu and data copy up is taking place on other cpu. We want to return
stat->st_blockseither from lower or stable upper and not something in between. Hence,ovl_has_upperdata()is called first to figure out whether block reporting will take place from lower or upper.Reviewed-by Amir Goldstein <[email protected]>Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/inode.c | 17 ++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +3
-0@@ -76,6 +76,9 @@ int ovl_getattr(const struct path *path, struct kstat *stat,bool is_dir =S_ISDIR (dentry->d_inode->i_mode);bool samefs =ovl_same_sb (dentry->d_sb);int err; +bool metacopy = false; + + metacopy =ovl_is_metacopy_dentry (dentry); type =ovl_path_real (dentry, &realpath); old_cred =ovl_override_creds (dentry->d_sb);fs/overlayfs/inode.c int ovl_getattr(const struct path *path, struct kstat *stat, · line 100
Quoted 6 lines
fs/overlayfs/inode.c +2
-1@@ -93,7 +96,8 @@ int ovl_getattr(const struct path *path, struct kstat *stat,if (!is_dir || samefs) {if (OVL_TYPE_ORIGIN (type)) {struct kstat lowerstat; -u32 lowermask =STATX_INO | (!is_dir ?STATX_NLINK :0 ); +u32 lowermask =STATX_INO |STATX_BLOCKS | + (!is_dir ?STATX_NLINK :0 );Leftover.
But I think you could do with one
vfs_getattr():Quoted 7 lines
if (!is_dir || samefs) { u32 lowermask = metacopy ? STATX_BLOCKS : 0; if (OVL_TYPE_ORIGIN(type)) lowermask |= STATX_INO | (!is_dir ? STATX_NLINK : 0); if (lowermask) { ovl_path_lower(dentry, &realpath); err = vfs_getattr(&realpath, &lowerstat,
Quoted 6 lines
We now support mid layer metacopy dentries. So we will need to do
vfs_getattr()on lowest data dentry for data blocks. While rest of the info (inode number and nlink) comes from top most lower dentry.So I don't think we can get rid of extra
vfs_getattr()?
Right. didn't notice that subtle detail, better amend commit message to mention that.
Thanks,
Amir.
PATCH v13 13/28 ovl: Add helper ovl_dentry_lowerdata() to get lower data dentry
In reply to Vivek GoyalNow we have the notion of data dentry and metacopy dentry. ovl_dentry_lower() will return lower dentry at idx 0, but it could be either data or metacopy dentry. Now we support metacopy dentries in lower layers so it is possible that lowerstack[0] is metacopy dentry while lowerstack[1] is actual data dentry.
So add an helper which returns lowest most dentry which is supposed to be data dentry.
--- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)
fs/overlayfs/overlayfs.h +1 -0
@@ -226,6 +226,7 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path);enum ovl_path_type ovl_path_real (struct dentry *dentry,struct path *path);struct dentry *ovl_dentry_upper (struct dentry *dentry);struct dentry *ovl_dentry_lower (struct dentry *dentry); +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry);struct dentry *ovl_dentry_real (struct dentry *dentry);struct dentry *ovl_i_dentry_upper (struct inode *inode);struct inode *ovl_inode_upper (struct inode *inode);
fs/overlayfs/util.c +14 -0
@@ -186,6 +186,20 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry)return oe->numlower ? oe->lowerstack[0 ].dentry :NULL ; } +/* +* ovl_dentry_lower() could return either a data dentry or metacopy dentry +* dependig on what is stored in lowerstack[0]. At times we need to find +* lower dentry which has data (and not metacopy dntry). This helper +* returns the lower data dentry. +*/ +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry) +{ +struct ovl_entry *oe = dentry->d_fsdata; +int idx = oe->numlower -1 ; + +return idx >=0 ? oe->lowerstack[idx].dentry :NULL ; +} +struct dentry *ovl_dentry_real (struct dentry *dentry) {return ovl_dentry_upper (dentry) ?:ovl_dentry_lower (dentry);
Signature · 1 line
2.13.6
Re: [PATCH v13 13/28] ovl: Add helper ovl_dentry_lowerdata() to get lower data dentry
Quoted 2 lines · Vivek Goyal
Now we have the notion of data dentry and metacopy dentry.
ovl_dentry_lower()will return lower dentry at idx 0, but it could be either data or metacopy
will return the upper most lower dentry. (idx 0 means upper).
Quoted 8 lines
dentry. Now we support metacopy dentries in lower layers so it is possible that lowerstack[0] is metacopy dentry while lowerstack[1] is actual data dentry.
So add an helper which returns lowest most dentry which is supposed to be data dentry.
Signed-off-by Vivek Goyal <[email protected]>
Quoted 25 lines
--- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)fs/overlayfs/overlayfs.h +1
-0@@ -226,6 +226,7 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path);enum ovl_path_type ovl_path_real (struct dentry *dentry,struct path *path);struct dentry *ovl_dentry_upper (struct dentry *dentry);struct dentry *ovl_dentry_lower (struct dentry *dentry); +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry);struct dentry *ovl_dentry_real (struct dentry *dentry);struct dentry *ovl_i_dentry_upper (struct inode *inode);struct inode *ovl_inode_upper (struct inode *inode);fs/overlayfs/util.c +0
-0@@ -186,6 +186,20 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry)return oe->numlower ? oe->lowerstack[0 ].dentry :NULL ; }
fs/overlayfs/util.c struct dentry *ovl_dentry_lower(struct dentry *dentry) · line 198
Quoted 10 lines
fs/overlayfs/util.c +10
-0+/* +* ovl_dentry_lower() could return either a data dentry or metacopy dentry +* dependig on what is stored in lowerstack[0]. At times we need to find +* lower dentry which has data (and not metacopy dntry). This helper +* returns the lower data dentry. +*/ +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry) +{ +struct ovl_entry *oe = dentry->d_fsdata; +int idx = oe->numlower -1 ;
Please stick with convention of layer->idx that idx 0 is upper.
Quoted 10 lines
fs/overlayfs/util.c +4
-1+ +return idx >=0 ? oe->lowerstack[idx].dentry :NULL ; +} +struct dentry *ovl_dentry_real (struct dentry *dentry) {return ovl_dentry_upper (dentry) ?:ovl_dentry_lower (dentry); --2.13.6
Re: [PATCH v13 13/28] ovl: Add helper ovl_dentry_lowerdata() to get lower data dentry
Quoted 5 lines · Amir Goldstein
Quoted 2 lines · Vivek Goyal
Now we have the notion of data dentry and metacopy dentry.
ovl_dentry_lower()will return lower dentry at idx 0, but it could be either data or metacopywill return the upper most lower dentry. (idx 0 means upper).
I am explaining the behavior of current ovl_dentry_lower() helper here. (And not of new helper).
Quoted 59 lines
Quoted 9 lines
dentry. Now we support metacopy dentries in lower layers so it is possible that lowerstack[0] is metacopy dentry while lowerstack[1] is actual data dentry.
So add an helper which returns lowest most dentry which is supposed to be data dentry.
Signed-off-by Vivek Goyal <[email protected]>Reviewed-by Amir Goldstein <[email protected]>Quoted 25 lines
--- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+)fs/overlayfs/overlayfs.h +1
-0@@ -226,6 +226,7 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path);enum ovl_path_type ovl_path_real (struct dentry *dentry,struct path *path);struct dentry *ovl_dentry_upper (struct dentry *dentry);struct dentry *ovl_dentry_lower (struct dentry *dentry); +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry);struct dentry *ovl_dentry_real (struct dentry *dentry);struct dentry *ovl_i_dentry_upper (struct inode *inode);struct inode *ovl_inode_upper (struct inode *inode);fs/overlayfs/util.c +0
-0@@ -186,6 +186,20 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry)return oe->numlower ? oe->lowerstack[0 ].dentry :NULL ; }fs/overlayfs/util.c struct dentry *ovl_dentry_lower(struct dentry *dentry) · line 198
Quoted 10 lines
fs/overlayfs/util.c +10
-0+/* +* ovl_dentry_lower() could return either a data dentry or metacopy dentry +* dependig on what is stored in lowerstack[0]. At times we need to find +* lower dentry which has data (and not metacopy dntry). This helper +* returns the lower data dentry. +*/ +struct dentry *ovl_dentry_lowerdata (struct dentry *dentry) +{ +struct ovl_entry *oe = dentry->d_fsdata; +int idx = oe->numlower -1 ;Please stick with convention of
layer->idxthat idx 0 is upper.Quoted 10 lines
fs/overlayfs/util.c +4
-1+ +return idx >=0 ? oe->lowerstack[idx].dentry :NULL ; +} +struct dentry *ovl_dentry_real (struct dentry *dentry) {return ovl_dentry_upper (dentry) ?:ovl_dentry_lower (dentry); --2.13.6
PATCH v13 14/28 ovl: Do not expose metacopy only dentry from d_real()
In reply to Vivek Goyald_real() can make a upper or lower metacopy dentry/inode visible to the vfs layer. This is something new and vfs layer does not know that this inode contains only metadata and not data. And this could break things.
So to be safe, do not expose metacopy only dentry/inode to vfs using d_real().
IOW, d_real() will not reuturn metacopy dentry. Instead, it will return dentry corresponding lower data dentry/inode which has file data.
For regular d_real() call (inode == NULL, D_REAL_UPPER not set), if upper dentry inode is metacopy only and does not have data, return lower dentry.
If d_real() is called with flag D_REAL_UPPER, return upper dentry only if it has data (flag OVL_UPPERDATA is set).
Similiarly, if d_real(inode=X) is called, a warning is emitted if returned dentry/inode does not have OVL_UPPERDATA set. This should not happen as we never made this metacopy inode visible to vfs so nobody should be calling overlayfs back with inode=metacopy_inode.
I scanned the code and I don't think it breaks any of the existing code. There are two users of D_REAL_UPPER. may_write_real() and update_ovl_inode_times().
may_write_real(), will get an NULL dentry if upper inode is metacopy only and it will return -EPERM. Effectively, we are disallowing modifications to metacopy only inode from this interface. Though there is opportunity to improve it. (Allow chattr on metacopy inodes).
update_ovl_inode_times() gets inode mtime and ctime from real inode. It should not be broken for metacopy inode as well for following reasons.
- For any metadata operations (setattr, acl etc), overlay always calls ovl_copyattr() and updates ovl inode mtime and ctime. So there is no need to update mtime and ctime in this case. Its already updated, hence even if d_real(D_REAL_UPPER) returns nil, it should be fine.
- For metadata inode, mtime should be same as lower and not change. (data can't be modified on metadata inode without copyup). IOW, mtime of ovl dentry should be same as mtime of underlying metadata inode on upper always. So there is no need to update it.
- For file writes, ctime and mtime will be updated. But in that case first data will be copied up and this will not be a metadata inode anymore. And furthr call to d_real(D_REAL_UPPER) will return upper inode and new mtime and ctime will be obtainable.
So atime updates should work just fine for metacopy inodes. I think only corner case is if somehow underlying filesystem changes ctime of upper metadata inode without overlay knowing about it. Not sure how that can happen. If somehow is affected by that, then we probably can implement another flag which will allow caller to get metacopy inode as well. Something like d_real(D_REAL_UPPER | D_METACOPY). And that should solve this issue.
--- fs/overlayfs/super.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
fs/overlayfs/super.c +17 -4
@@ -96,8 +96,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry,struct dentry *real;int err; -if (flags &D_REAL_UPPER ) -return ovl_dentry_upper (dentry); +if (flags &D_REAL_UPPER ) { + real =ovl_dentry_upper (dentry); +if (!real) +return NULL ; +if (!ovl_has_upperdata (dentry)) +return NULL ; +return real; + }if (!d_is_reg (dentry)) {if (!inode || inode ==d_inode (dentry))
@@ -113,15 +119,22 @@ static struct dentry *ovl_d_real(struct dentry *dentry, real =ovl_dentry_upper (dentry);if (real && (!inode || inode ==d_inode (real))) { +bool metacopy = !ovl_has_upperdata (dentry);if (!inode) { err =ovl_check_append_only (d_inode (real), open_flags);if (err)return ERR_PTR (err); - } + +if (unlikely (metacopy)) + goto lower; + }else if (unlikely (metacopy)) + goto bug; +return real; } - real =ovl_dentry_lower (dentry); +lower: + real =ovl_dentry_lowerdata (dentry);if (!real) goto bug;
Signature · 1 line
2.13.6
PATCH v13 15/28 ovl: Move some of ovl_nlink_start() functionality in ovl_nlink_prep()
In reply to Vivek GoyalSoon I want to write patches to enable redirects on non-dir files. That means it is possible that we have to deal with the case where multiple dentries might be sharing inode and ovl_inode->redirect field setting/resetting will need to be protected by taking ovl_inode->lock. Current dentry based locking alone will not be sufficient for this case.
As of now, nlink based code takes ovl_inode->lock in some cases. For redirect case, during ovl_rename() I might have to take ovl_inode->lock both on old as well as new ovl_inode. And that means that I need to make sure there are no deadlocks.
I want to separate out logic for taking lock in a new function. Hence will need to break down ovl_nlink_start() a bit.
ovl_nlink_start() does the copy up and then takes ovl_inode->lock. Move copy up related portions in a separate function called ovl_nlink_prep().
This patch is just code reorganization and no funcitonal change.
--- fs/overlayfs/dir.c | 14 ++++++++++++++ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 26 ++++++++++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-)
fs/overlayfs/dir.c +14 -0
@@ -595,6 +595,10 @@ static int ovl_link(struct dentry *old, struct inode *newdir,if (err) goto out_drop_write; + err =ovl_nlink_prep (old); +if (err) + goto out_drop_write; + err =ovl_nlink_start (old, &locked);if (err) goto out_drop_write;
@@ -752,6 +756,10 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)if (err) goto out_drop_write; + err =ovl_nlink_prep (dentry); +if (err) + goto out_drop_write; + err =ovl_nlink_start (dentry, &locked);if (err) goto out_drop_write;
@@ -960,6 +968,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,if (err) goto out_drop_write; }else { + err =ovl_nlink_prep (new); +if (err) + goto out_drop_write; + } + +if (overwrite) { err =ovl_nlink_start (new, &new_locked);if (err) goto out_drop_write;
fs/overlayfs/overlayfs.h +1 -0
@@ -271,6 +271,7 @@ bool ovl_test_flag(unsigned long flag, struct inode *inode);bool ovl_inuse_trylock (struct dentry *dentry);void ovl_inuse_unlock (struct dentry *dentry);bool ovl_need_index (struct dentry *dentry); +int ovl_nlink_prep (struct dentry *dentry);int ovl_nlink_start (struct dentry *dentry,bool *locked);void ovl_nlink_end (struct dentry *dentry,bool locked);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);
fs/overlayfs/util.c +18 -8
@@ -675,15 +675,9 @@ static void ovl_cleanup_index(struct dentry *dentry) goto out; } -/* -* Operations that change overlay inode and upper inode nlink need to be -* synchronized with copy up for persistent nlink accounting. -*/ -int ovl_nlink_start (struct dentry *dentry,bool *locked) +int ovl_nlink_prep (struct dentry *dentry) { -struct ovl_inode *oi =OVL_I (d_inode (dentry)); -const struct cred *old_cred; -int err; +int err =0 ;if (!d_inode (dentry))return 0 ;
@@ -708,6 +702,22 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)return err; } +return err; +} + +/* +* Operations that change overlay inode and upper inode nlink need to be +* synchronized with copy up for persistent nlink accounting. +*/ +int ovl_nlink_start (struct dentry *dentry,bool *locked) +{ +struct ovl_inode *oi =OVL_I (d_inode (dentry)); +const struct cred *old_cred; +int err; + +if (!d_inode (dentry)) +return 0 ; + err =mutex_lock_interruptible (&oi->lock);if (err)return err;
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 15/28] ovl: Move some of ovl_nlink_start() functionality in ovl_nlink_prep()
Quoted 11 lines · Vivek Goyal
Soon I want to write patches to enable redirects on non-dir files. That means it is possible that we have to deal with the case where multiple dentries might be sharing inode and
ovl_inode->redirectfield setting/resetting will need to be protected by takingovl_inode->lock. Current dentry based locking alone will not be sufficient for this case.As of now, nlink based code takes
ovl_inode->lockin some cases. For redirect case, duringovl_rename()I might have to takeovl_inode->lockboth on old as well as newovl_inode. And that means that I need to make sure there are no deadlocks.
Quoted 5 lines
I want to separate out logic for taking lock in a new function. Hence will need to break down
ovl_nlink_start()a bit.
ovl_nlink_start()does the copy up and then takesovl_inode->lock. Move copy up related portions in a separate function calledovl_nlink_prep().
The sensible thing to do is to call 2 helpers from ovl_nlink_start() ovl_nlink_prep() and ovl_nlink_???() and then you can use helpers directly in rename, but don't need to change all other places. The changes to other places do not make the code better, they make it worse.
Quoted 113 lines
This patch is just code reorganization and no funcitonal change.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 14 ++++++++++++++ fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 26 ++++++++++++++++++-------- 3 files changed, 33 insertions(+), 8 deletions(-)fs/overlayfs/dir.c +14
-0@@ -595,6 +595,10 @@ static int ovl_link(struct dentry *old, struct inode *newdir,if (err) goto out_drop_write; + err =ovl_nlink_prep (old); +if (err) + goto out_drop_write; + err =ovl_nlink_start (old, &locked);if (err) goto out_drop_write;@@ -752,6 +756,10 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)if (err) goto out_drop_write; + err =ovl_nlink_prep (dentry); +if (err) + goto out_drop_write; + err =ovl_nlink_start (dentry, &locked);if (err) goto out_drop_write;@@ -960,6 +968,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,if (err) goto out_drop_write; }else { + err =ovl_nlink_prep (new); +if (err) + goto out_drop_write; + } + +if (overwrite) { err =ovl_nlink_start (new, &new_locked);if (err) goto out_drop_write;fs/overlayfs/overlayfs.h +1
-0@@ -271,6 +271,7 @@ bool ovl_test_flag(unsigned long flag, struct inode *inode);bool ovl_inuse_trylock (struct dentry *dentry);void ovl_inuse_unlock (struct dentry *dentry);bool ovl_need_index (struct dentry *dentry); +int ovl_nlink_prep (struct dentry *dentry);int ovl_nlink_start (struct dentry *dentry,bool *locked);void ovl_nlink_end (struct dentry *dentry,bool locked);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);fs/overlayfs/util.c +18
-9@@ -675,15 +675,9 @@ static void ovl_cleanup_index(struct dentry *dentry) goto out; } -/* -* Operations that change overlay inode and upper inode nlink need to be -* synchronized with copy up for persistent nlink accounting. -*/ -int ovl_nlink_start (struct dentry *dentry,bool *locked) +int ovl_nlink_prep (struct dentry *dentry) { -struct ovl_inode *oi =OVL_I (d_inode (dentry)); -const struct cred *old_cred; -int err; +int err =0 ;if (!d_inode (dentry))return 0 ;@@ -708,6 +702,22 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)return err; } +return err; +} + +/* +* Operations that change overlay inode and upper inode nlink need to be +* synchronized with copy up for persistent nlink accounting. +*/ +int ovl_nlink_start (struct dentry *dentry,bool *locked) +{ +struct ovl_inode *oi =OVL_I (d_inode (dentry)); +const struct cred *old_cred; +int err; + +if (!d_inode (dentry)) +return 0 ; + err =mutex_lock_interruptible (&oi->lock);if (err)return err; --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 16/28 ovl: Create locked version of ovl_nlink_start() and ovl_nlink_end()
In reply to Vivek GoyalSoon in ovl_rename() I will introduce a function to take lock both on old and new ovl_inode. That means I don't want ovl_nlink_start() to take lock and ovl_nlink_end() to drop lock. It will be done by other functions.
So create ovl_nlink_start_locked() and ovl_nlink_end_locked() which assume that by the time they are called, lock has already been taken and these don't deal with taking/releasing locks.
These will be used in following patches.
--- fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/util.c | 50 +++++++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 20 deletions(-)
fs/overlayfs/overlayfs.h +2 -0
@@ -273,7 +273,9 @@ void ovl_inuse_unlock(struct dentry *dentry);bool ovl_need_index (struct dentry *dentry);int ovl_nlink_prep (struct dentry *dentry);int ovl_nlink_start (struct dentry *dentry,bool *locked); +int ovl_nlink_start_locked (struct dentry *dentry);void ovl_nlink_end (struct dentry *dentry,bool locked); +void ovl_nlink_end_locked (struct dentry *dentry);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry);bool ovl_is_metacopy_dentry (struct dentry *dentry);
fs/overlayfs/util.c +30 -20
77 lines not on this page · raw message
Signature · 1 line
2.13.6
Re: [PATCH v13 16/28] ovl: Create locked version of ovl_nlink_start() and ovl_nlink_end()
Quoted 11 lines · Vivek Goyal
Soon in
ovl_rename()I will introduce a function to take lock both on old and newovl_inode. That means I don't wantovl_nlink_start()to take lock andovl_nlink_end()to drop lock. It will be done by other functions.So create
ovl_nlink_start_locked()andovl_nlink_end_locked()which assume that by the time they are called, lock has already been taken and these don't deal with taking/releasing locks.These will be used in following patches.
Signed-off-by Vivek Goyal <[email protected]>
Quoted 110 lines
--- fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/util.c | 50 +++++++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 20 deletions(-)fs/overlayfs/overlayfs.h +2
-0@@ -273,7 +273,9 @@ void ovl_inuse_unlock(struct dentry *dentry);bool ovl_need_index (struct dentry *dentry);int ovl_nlink_prep (struct dentry *dentry);int ovl_nlink_start (struct dentry *dentry,bool *locked); +int ovl_nlink_start_locked (struct dentry *dentry);void ovl_nlink_end (struct dentry *dentry,bool locked); +void ovl_nlink_end_locked (struct dentry *dentry);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry);bool ovl_is_metacopy_dentry (struct dentry *dentry);fs/overlayfs/util.c +30
-21@@ -709,21 +709,13 @@ int ovl_nlink_prep(struct dentry *dentry)* Operations that change overlay inode and upper inode nlink need to be * synchronized with copy up for persistent nlink accounting. */ -int ovl_nlink_start (struct dentry *dentry,bool *locked) +int ovl_nlink_start_locked (struct dentry *dentry) { -struct ovl_inode *oi =OVL_I (d_inode (dentry));const struct cred *old_cred;int err; -if (!d_inode (dentry)) -return 0 ; - - err =mutex_lock_interruptible (&oi->lock); -if (err) -return err; -if (d_is_dir (dentry) || !ovl_test_flag (OVL_INDEX ,d_inode (dentry))) - goto out; +return 0 ; old_cred =ovl_override_creds (dentry->d_sb);/* @@ -734,8 +726,22 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)*/ err =ovl_set_nlink_upper (dentry);revert_creds (old_cred); +return err; +} -out: +int ovl_nlink_start (struct dentry *dentry,bool *locked) +{ +struct ovl_inode *oi =OVL_I (d_inode (dentry)); +int err; + +if (!d_inode (dentry)) +return 0 ; + + err =mutex_lock_interruptible (&oi->lock); +if (err) +return err; + + err =ovl_nlink_start_locked (dentry);if (err)mutex_unlock (&oi->lock);else @@ -744,18 +750,22 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked)return err; } -void ovl_nlink_end (struct dentry *dentry,bool locked) +void ovl_nlink_end_locked (struct dentry *dentry) { -if (locked) { -if (ovl_test_flag (OVL_INDEX ,d_inode (dentry)) && -d_inode (dentry)->i_nlink ==0 ) { -const struct cred *old_cred; +if (ovl_test_flag (OVL_INDEX ,d_inode (dentry)) && +d_inode (dentry)->i_nlink ==0 ) { +const struct cred *old_cred; - old_cred =ovl_override_creds (dentry->d_sb); -ovl_cleanup_index (dentry); -revert_creds (old_cred); - } + old_cred =ovl_override_creds (dentry->d_sb); +ovl_cleanup_index (dentry); +revert_creds (old_cred); + } +} +void ovl_nlink_end (struct dentry *dentry,bool locked) +{ +if (locked) { +ovl_nlink_end_locked (dentry);mutex_unlock (&OVL_I (d_inode (dentry))->lock); } } --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 17/28 ovl: During rename lock both source and target ovl_inode
In reply to Vivek GoyalIn some cases we need to take both source (old) and target(new) dentry ovl_inode->lock. This patch adds support for that. Locks are taken in order of increasing inode address to avoid deadlock. This code has been taken from lock_two_nondirectories().
As of now, metacopy needs this lock if we are planning to update redirect information on source/target ovl_inode. nlink related accounting takes this lock on target for the case of overwrite.
One change w.r.t existing code is mutex_lock() vs mutex_lock_interruptible(). Now ovl_rename() code path has switched to mutex_lock() variant for ovl_inode->lock. There is no particular reason, I just found it easier to reuse lock_two_nondirectories(), that's why. Hoping its not a concern.
--- fs/overlayfs/dir.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 5 deletions(-)
fs/overlayfs/dir.c +82 -5
@@ -896,12 +896,84 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir)return err; } +static void ovl_lock_two_nondirectories (struct ovl_inode *oi1, +struct ovl_inode *oi2) +{ +if (oi1 > oi2) +swap (oi1, oi2); + +if (oi1) +mutex_lock (&oi1->lock); +if (oi2 && oi2 != oi1) +mutex_lock_nested (&oi2->lock,I_MUTEX_NONDIR2 ); +} + +static void ovl_rename_lock_ovl_inodes (struct dentry *old,struct dentry *new, +bool overwrite,bool *old_locked, +bool *new_locked) +{ +bool lock_old = false, lock_new = false; +struct ovl_inode * oi1 =NULL , *oi2 =NULL ; + +/* +* First determine which inodes need to be locked. +* +* If "old" is metacopy, oi->redirect will be set. So we need lock +* on old ovl_inode. If "new" is metacopy, and it is not being +* overwritten, then oi->redirect will be set and we will need +* lock on new ovl_inode. +*/ + +if (ovl_is_metacopy_dentry (old)) + lock_old = true; + +if (!overwrite &&ovl_is_metacopy_dentry (new)) + lock_new = true; +/* +* For nlink, if we are overwriting and new->inode is not empty, +* then we need to take lock on new ovl_inode. +*/ +if (overwrite &&d_inode (new)) + lock_new = true; + + oi1 =OVL_I (d_inode (old)); +if (lock_new) + oi2 =OVL_I (d_inode (new)); + +if (lock_old && lock_new) { +ovl_lock_two_nondirectories (oi1, oi2); + *old_locked = *new_locked = true; +return ; + } + +if (lock_old) { +mutex_lock (&oi1->lock); + *old_locked = true; +return ; + } + +if (lock_new) { +mutex_lock (&oi2->lock); + *new_locked = true; + } +} + +static void ovl_rename_unlock_ovl_inodes (struct dentry *old,struct dentry *new, +bool old_locked,bool new_locked) +{ +if (old_locked) +mutex_unlock (&OVL_I (d_inode (old))->lock); + +if (new_locked &&d_inode (old) !=d_inode (new)) +mutex_unlock (&OVL_I (d_inode (new))->lock); +} +static int ovl_rename (struct inode *olddir,struct dentry *old,struct inode *newdir,struct dentry *new,unsigned int flags) {int err; -bool new_locked = false; +bool old_locked = false, new_locked = false;struct dentry *old_upperdir;struct dentry *new_upperdir;struct dentry *olddentry;
@@ -973,10 +1045,12 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, goto out_drop_write; } -if (overwrite) { - err =ovl_nlink_start (new, &new_locked); +ovl_rename_lock_ovl_inodes (old, new, overwrite, &old_locked, + &new_locked); +if (overwrite && new_locked) { + err =ovl_nlink_start_locked (new);if (err) - goto out_drop_write; + goto out_unlock_inodes; } old_cred =ovl_override_creds (old->d_sb);
@@ -1102,7 +1176,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,unlock_rename (new_upperdir, old_upperdir); out_revert_creds:revert_creds (old_cred); -ovl_nlink_end (new, new_locked); +if (new_locked) +ovl_nlink_end_locked (new); +out_unlock_inodes: +ovl_rename_unlock_ovl_inodes (old, new, old_locked, new_locked); out_drop_write:ovl_drop_write (old); out:
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 17/28] ovl: During rename lock both source and target ovl_inode
Quoted 9 lines · Vivek Goyal
In some cases we need to take both source (old) and target(new) dentry
ovl_inode->lock. This patch adds support for that. Locks are taken in order of increasing inode address to avoid deadlock. This code has been taken fromlock_two_nondirectories().As of now, metacopy needs this lock if we are planning to update redirect information on source/target
ovl_inode. nlink related accounting takes this lock on target for the case of overwrite.
This is very over complicated.
Here is how this could be simplified.
Setting redirects does NOT need to happen under all the complicated rename locks. Settings redirects can happen completely before everything else because it is harmless to set redirect and not rename afterwards.
I would split out a helper ovl_rename_prep() it can also take care of copy ups and check ovl_can_rename() and then set redirect if needed on old and/or new locking them one at a time.
You can take the ovl_inode->lock inside ovl_set_redirect() and you can also override credentials or whatever is needed due to moving set redirect earlier.
You probably don't need all the nlink_start re-factoring with this change.
Thanks,
Amir.
Re: [PATCH v13 17/28] ovl: During rename lock both source and target ovl_inode
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
In some cases we need to take both source (old) and target(new) dentry
ovl_inode->lock. This patch adds support for that. Locks are taken in order of increasing inode address to avoid deadlock. This code has been taken fromlock_two_nondirectories().As of now, metacopy needs this lock if we are planning to update redirect information on source/target
ovl_inode. nlink related accounting takes this lock on target for the case of overwrite.
Quoted 7 lines
This is very over complicated.
Here is how this could be simplified.
Setting redirects does NOT need to happen under all the complicated rename locks. Settings redirects can happen completely before everything else because it is harmless to set redirect and not rename afterwards.
hmm..., So basically set redirects before doing lock_rename(new_upperdir, old_upperdir). I guess even before ovl_set_nlink_start() takes inode lock. That way we don't have to hold two inode locks together and we should be able to take one inode lock at a time, set redirect and release lock.
I spent some time thinking if I can spot anything obviously wrong but could not find anything. So I will try to implement this. If this works, I like the idea of simplifying the locking a bit. Right now trying to take two inode locks makes it complicated.
Vivek
Quoted 13 lines
I would split out a helper
ovl_rename_prep()it can also take care of copy ups and checkovl_can_rename()and then set redirect if needed on old and/or new locking them one at a time.
You can take theovl_inode->lockinsideovl_set_redirect()and you can also override credentials or whatever is needed due to moving set redirect earlier.You probably don't need all the
nlink_startre-factoring with this change.Thanks,
Amir.
PATCH v13 18/28 ovl: Check redirects for metacopy files
In reply to Vivek GoyalRight now we rely on path based lookup for data origin of metacopy upper. This will work only if upper has not been renamed. We solved this problem already for merged directories using redirect. Use same logic for metacopy files.
This patch just goes on to check redirects for metacopy files.
--- fs/overlayfs/namei.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
fs/overlayfs/namei.c +12 -12
@@ -260,18 +260,19 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto out_err; d->stop = !err; d->metacopy = !!err; - goto out; - } -if (last_element) - d->is_dir = true; -if (d->last) - goto out; - -if (ovl_is_opaquedir (this)) { - d->stop = true; +if (!d->metacopy) + goto out; + }else {if (last_element) - d->opaque = true; - goto out; + d->is_dir = true; +if (d->last) + goto out; +if (ovl_is_opaquedir (this)) { + d->stop = true; +if (last_element) + d->opaque = true; + goto out; + } } err =ovl_check_redirect (this, d, prelen, post);if (err)
@@ -859,7 +860,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, }if (upperdentry && !d.is_dir) {unsigned int origin_ctr =0 ; -BUG_ON (d.redirect);/* * Lookup copy up origin by decoding origin file handle. * We may get a disconnected dentry, which is fine,
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 18/28] ovl: Check redirects for metacopy files
Quoted 7 lines · Vivek Goyal
Right now we rely on path based lookup for data origin of metacopy upper. This will work only if upper has not been renamed. We solved this problem already for merged directories using redirect. Use same logic for metacopy files.
This patch just goes on to check redirects for metacopy files.
The patch changes the logic very subtly, but it is really hard to follow because of convoluted diff. Please make changes that don't change logic in a separate patch.
Quoted 21 lines
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/namei.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)fs/overlayfs/namei.c +0
-8@@ -260,18 +260,19 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto out_err; d->stop = !err; d->metacopy = !!err; - goto out; - } -if (last_element) - d->is_dir = true; -if (d->last) - goto out; - -if (ovl_is_opaquedir (this)) {
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 269
Quoted 10 lines
fs/overlayfs/namei.c +6
-3- d->stop = true; +if (!d->metacopy) + goto out; + }else {if (last_element) - d->opaque = true; - goto out; + d->is_dir = true; +if (d->last) + goto out;
If I am not mistaken, d->last test is relevant to both did and non-dir, because it also prevents the unneeded check redirect.
Thanks,
Amir.
Re: [PATCH v13 18/28] ovl: Check redirects for metacopy files
Quoted 39 lines · Amir Goldstein
Quoted 7 lines · Vivek Goyal
Right now we rely on path based lookup for data origin of metacopy upper. This will work only if upper has not been renamed. We solved this problem already for merged directories using redirect. Use same logic for metacopy files.
This patch just goes on to check redirects for metacopy files.
The patch changes the logic very subtly, but it is really hard to follow because of convoluted diff. Please make changes that don't change logic in a separate patch.
Quoted 26 lines
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/namei.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)fs/overlayfs/namei.c +3
-9@@ -260,18 +260,19 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto out_err; d->stop = !err; d->metacopy = !!err; - goto out; - } -if (last_element) - d->is_dir = true; -if (d->last) - goto out; - -if (ovl_is_opaquedir (this)) { - d->stop = true; +if (!d->metacopy) + goto out; + }else {if (last_element)
Quoted 8 lines
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 269
Quoted 5 lines
fs/overlayfs/namei.c +3
-2- d->opaque = true; - goto out; + d->is_dir = true; +if (d->last) + goto out;If I am not mistaken,
d->lasttest is relevant to both did and non-dir, because it also prevents the unneeded check redirect.
It is just an optimization which we do only for directories as of now and this patch does not change that behavior. For non-dirs we never check redirects anyway as of now.
One could argue that this optimization should be introduced for metacopy files as well. I could do that. Just that I did not focus too much on optimizations a lot. This series has been growing and dragging enough, that I want to focus on correctness first. And worry about optimizations later.
Vivek
Quoted 3 lines
Thanks,
Amir.
Amir Goldstein
maintainerRe: [PATCH v13 18/28] ovl: Check redirects for metacopy files
Quoted 49 lines · Vivek Goyal
Quoted 39 lines · Amir Goldstein
Quoted 7 lines · Vivek Goyal
Right now we rely on path based lookup for data origin of metacopy upper. This will work only if upper has not been renamed. We solved this problem already for merged directories using redirect. Use same logic for metacopy files.
This patch just goes on to check redirects for metacopy files.
The patch changes the logic very subtly, but it is really hard to follow because of convoluted diff. Please make changes that don't change logic in a separate patch.
Quoted 26 lines
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/namei.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)fs/overlayfs/namei.c +3
-9@@ -260,18 +260,19 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto out_err; d->stop = !err; d->metacopy = !!err; - goto out; - } -if (last_element) - d->is_dir = true; -if (d->last) - goto out; - -if (ovl_is_opaquedir (this)) { - d->stop = true; +if (!d->metacopy) + goto out; + }else {if (last_element)Quoted 8 lines
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 269
Quoted 5 lines
fs/overlayfs/namei.c +3
-2- d->opaque = true; - goto out; + d->is_dir = true; +if (d->last) + goto out;If I am not mistaken,
d->lasttest is relevant to both did and non-dir, because it also prevents the unneeded check redirect.
Quoted 3 lines
It is just an optimization which we do only for directories as of now and this patch does not change that behavior. For non-dirs we never check redirects anyway as of now.
This is a moot argument.
Of course this patch changes behavior.
If adds the ability to redirect non-dir and it should add the same logic as for dir.
Quoted 6 lines
One could argue that this optimization should be introduced for metacopy files as well. I could do that. Just that I did not focus too much on optimizations a lot. This series has been growing and dragging enough, that I want to focus on correctness first. And worry about optimizations later.
I hear your frustration, but I think the patch set is progressing very nicely (and got a few prep patches already in overlayfs-next), so this isn't going to break you ;-)
if (!d->metacopy || d->last)
goto out;
Thanks,
Amir.
Re: [PATCH v13 18/28] ovl: Check redirects for metacopy files
Quoted 66 lines · Amir Goldstein
Quoted 49 lines · Vivek Goyal
Quoted 39 lines · Amir Goldstein
Quoted 7 lines · Vivek Goyal
Right now we rely on path based lookup for data origin of metacopy upper. This will work only if upper has not been renamed. We solved this problem already for merged directories using redirect. Use same logic for metacopy files.
This patch just goes on to check redirects for metacopy files.
The patch changes the logic very subtly, but it is really hard to follow because of convoluted diff. Please make changes that don't change logic in a separate patch.
Quoted 26 lines
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/namei.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)fs/overlayfs/namei.c +3
-9@@ -260,18 +260,19 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, goto out_err; d->stop = !err; d->metacopy = !!err; - goto out; - } -if (last_element) - d->is_dir = true; -if (d->last) - goto out; - -if (ovl_is_opaquedir (this)) { - d->stop = true; +if (!d->metacopy) + goto out; + }else {if (last_element)Quoted 8 lines
fs/overlayfs/namei.c static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, · line 269
Quoted 5 lines
fs/overlayfs/namei.c +3
-2- d->opaque = true; - goto out; + d->is_dir = true; +if (d->last) + goto out;If I am not mistaken,
d->lasttest is relevant to both did and non-dir, because it also prevents the unneeded check redirect.Quoted 3 lines
It is just an optimization which we do only for directories as of now and this patch does not change that behavior. For non-dirs we never check redirects anyway as of now.
This is a moot argument.
Of course this patch changes behavior.
If adds the ability to redirect non-dir and it should add the same logic as for dir.Quoted 6 lines
One could argue that this optimization should be introduced for metacopy files as well. I could do that. Just that I did not focus too much on optimizations a lot. This series has been growing and dragging enough, that I want to focus on correctness first. And worry about optimizations later.
Quoted 6 lines
I hear your frustration, but I think the patch set is progressing very nicely (and got a few prep patches already in overlayfs-next), so this isn't going to break you ;-)
if (!d->metacopy ||
d->last)goto out;
Ok, I will add this.
Vivek
PATCH v13 19/28 ovl: Treat metacopy dentries as type OVL_PATH_MERGE
In reply to Vivek GoyalRight now OVL_PATH_MERGE is used only for merged directories. But conceptually, a metacopy dentry (backed by a lower data dentry) is a merged entity as well.
So mark metacopy dentries as OVL_PATH_MERGE and ovl_rename() makes use of this property later to set redirect on a metacopy file.
--- fs/overlayfs/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/overlayfs/util.c +1 -1
@@ -118,7 +118,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)*/ if (oe->numlower) { type |= __OVL_PATH_ORIGIN; -if (d_is_dir (dentry)) +if (d_is_dir (dentry) || !ovl_has_upperdata (dentry)) type |= __OVL_PATH_MERGE; } }else {
Signature · 1 line
2.13.6
Re: [PATCH v13 19/28] ovl: Treat metacopy dentries as type OVL_PATH_MERGE
Quoted 8 lines · Vivek Goyal
Right now
OVL_PATH_MERGEis used only for merged directories. But conceptually, a metacopy dentry (backed by a lower data dentry) is a merged entity as well.So mark metacopy dentries as
OVL_PATH_MERGEandovl_rename()makes use of this property later to set redirect on a metacopy file.Signed-off-by Vivek Goyal <[email protected]>
Quoted 24 lines
--- fs/overlayfs/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/overlayfs/util.c +1
-2@@ -118,7 +118,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)*/ if (oe->numlower) { type |= __OVL_PATH_ORIGIN; -if (d_is_dir (dentry)) +if (d_is_dir (dentry) || !ovl_has_upperdata (dentry)) type |= __OVL_PATH_MERGE; } }else { --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 20/28 ovl: Do not set dentry type ORIGIN for broken hardlinks
In reply to Vivek GoyalIf a dentry has copy up origin, we set flag OVL_PATH_ORIGIN. So far this decision was easy that we had to check only for oe->numlower and if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).
But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set OVL_PATH_ORIGIN if we have a broken hardlink.
--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/overlayfs/util.c +8 -1
@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. */ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +if (d_is_dir (dentry) || !ovl_has_upperdata (dentry)) type |= __OVL_PATH_MERGE; }
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin.
This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store OVL_ORIGIN inode flag during lookup and then use ovl_test_flag(OVL_ORIGIN) in place of OVL_TYPE_ORIGIN(type).
If I am not mistaken, you could set flag OVL_ORIGIN in ovl_get_inode() IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helper ovl_hash_bylower(), so I hope that helps.
Thanks,
Amir.
Re: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 40 lines · Amir Goldstein
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +
Quoted 2 lines
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
Directory should be fine because for directories ->i_nlink == 1 and this condition will become true.
Quoted 11 lines
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store
OVL_ORIGINinode flag during lookup and then useovl_test_flag(OVL_ORIGIN)in place ofOVL_TYPE_ORIGIN(type).If I am not mistaken, you could set flag
OVL_ORIGINinovl_get_inode()IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helperovl_hash_bylower(), so I hope that helps.
Ok, I can put another ovl-inode flag say OVL_ORIGIN. But that also means that I need to set the flag during copy up. And that means it brings back ordering requirements for lockless access. For example, in ovl_getattr(), if we replace OVL_TYPE_ORIGIN() with ovl_test_flag(ORIGIN), without explicit barriers, what's the guarantee a user will see OVL_ORIGIN flag yet. In fact it can happen that user might see upperdentry but not OVL_ORIGIN.
In fact I see OVL_INDEX being used in ovl_getattr(). If a parallel copy up took place it is possible that ovl_getattr() saw upperdentry but not the OVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.
Thanks
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 59 lines · Vivek Goyal
Quoted 40 lines · Amir Goldstein
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +Quoted 2 lines
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
Directory should be fine because for directories ->i_nlink == 1 and this condition will become true.
Quoted 11 lines
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store
OVL_ORIGINinode flag during lookup and then useovl_test_flag(OVL_ORIGIN)in place ofOVL_TYPE_ORIGIN(type).If I am not mistaken, you could set flag
OVL_ORIGINinovl_get_inode()IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helperovl_hash_bylower(), so I hope that helps.
Quoted 7 lines
Ok, I can put another ovl-inode flag say
OVL_ORIGIN. But that also means that I need to set the flag during copy up. And that means it brings back ordering requirements for lockless access. For example, inovl_getattr(), if we replaceOVL_TYPE_ORIGIN()withovl_test_flag(ORIGIN), without explicit barriers, what's the guarantee a user will seeOVL_ORIGINflag yet. In fact it can happen that user might see upperdentry but notOVL_ORIGIN.
OK, so instead set OVL_BYLOWER in ovl_get_inode() IFF (bylower). that property doesn't change on copy up.
and set __OVL_PATH_ORIGIN in ovl_path_type() if upper AND numlower AND OVL_BYLOWER.
Quoted 6 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.
Yes. and that is not the only problem I see with ovl_getattr().
stat->dev could be set to ovl_get_pseudo_dev(dentry), even if stat->ino is not set to lowerstat.ino; that is not good.
If I am not mistaken, all the special conditions in ovl_getattr() to use lower st_ino are already encoded in ovl_hash_bylower(), so you may completely remove those extra tests if OVL_TYPE_ORIGIN(type) already checks the OVL_BYLOWER flag.
It may be hard to see my claim above is true, because, for example, sb->s_export_op in ovl_hash_bylower() is equivalent to ovl_verify_lower(dentry->d_sb) in ovl_getattr().
Please double check my claims, and if you agree, you can submit a patch that Fixes: ("a0c5ad307ac0 ovl: relax same fs constraint for constant st_ino"), which already does the right thing for metacopy.
Thanks,
Amir.
Amir Goldstein
maintainerRe: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 1 line · Amir Goldstein
On Tue, Apr 10, 2018 at 5:00 PM, Vivek Goyal <[email protected]> wrote:
[...]
Quoted 18 lines
Quoted 6 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.Yes. and that is not the only problem I see with
ovl_getattr().
stat->devcould be set toovl_get_pseudo_dev(dentry), even ifstat->inois not set to lowerstat.ino; that is not good.If I am not mistaken, all the special conditions in
ovl_getattr()to use lowerst_inoare already encoded inovl_hash_bylower(), so you may completely remove those extra tests ifOVL_TYPE_ORIGIN(type)already checks theOVL_BYLOWERflag.
Quoted 8 lines
It may be hard to see my claim above is true, because, for example,
sb->s_export_opinovl_hash_bylower()is equivalent toovl_verify_lower(dentry->d_sb)inovl_getattr().Please double check my claims, and if you agree, you can submit a patch that Fixes: ("
a0c5ad307ac0ovl: relax same fs constraint for constantst_ino"), which already does the right thing for metacopy.
On second thought, it would be better to submit the simple fix patch first, so it could be applied to stable kernels that don't have ovl_hash_bylower() and only then submit the patch that adds OVL_BYLOWER and removes the extra tests in ovl_getattr(). Something like this (untested) should do.
Let me know if you would like me to submit the fix patch.
Thanks,
Amir.
fs/overlayfs/inode.c +0 -0
@@ -118,13 +118,10 @@ int ovl_getattr(const struct path *path, struct
kstat *stat,
*/
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
(!ovl_verify_lower(dentry->d_sb) &&
- (is_dir || lowerstat.nlink == 1)))
+ (is_dir || lowerstat.nlink == 1))) {
stat->ino = lowerstat.ino;-
- if (samefs)
- WARN_ON_ONCE(stat->dev != lowerstat.dev);
- else
stat->dev = ovl_get_pseudo_dev(dentry);
+ }
}
if (samefs) {
/*Re: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 30 lines · Amir Goldstein
Quoted 1 line · Amir Goldstein
On Tue, Apr 10, 2018 at 5:00 PM, Vivek Goyal <[email protected]> wrote:
[...]
Quoted 18 lines
Quoted 6 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.Yes. and that is not the only problem I see with
ovl_getattr().
stat->devcould be set toovl_get_pseudo_dev(dentry), even ifstat->inois not set to lowerstat.ino; that is not good.If I am not mistaken, all the special conditions in
ovl_getattr()to use lowerst_inoare already encoded inovl_hash_bylower(), so you may completely remove those extra tests ifOVL_TYPE_ORIGIN(type)already checks theOVL_BYLOWERflag.Quoted 8 lines
It may be hard to see my claim above is true, because, for example,
sb->s_export_opinovl_hash_bylower()is equivalent toovl_verify_lower(dentry->d_sb)inovl_getattr().Please double check my claims, and if you agree, you can submit a patch that Fixes: ("
a0c5ad307ac0ovl: relax same fs constraint for constantst_ino"), which already does the right thing for metacopy.
Quoted 6 lines
On second thought, it would be better to submit the simple fix patch first, so it could be applied to stable kernels that don't have
ovl_hash_bylower()and only then submit the patch that addsOVL_BYLOWERand removes the extra tests inovl_getattr(). Something like this (untested) should do.
Let me know if you would like me to submit the fix patch.
Frankly speaking I don't understand the fix. So I would prefer that you submit it with a proper commit message.
Vivek
Quoted 25 lines
Thanks,
Amir.fs/overlayfs/inode.c +2
-5@@ -118,13 +118,10 @@ int ovl_getattr(const struct path *path, structkstat *stat, */ if (ovl_test_flag (OVL_INDEX ,d_inode (dentry)) || (!ovl_verify_lower (dentry->d_sb) && - (is_dir || lowerstat.nlink ==1 ))) + (is_dir || lowerstat.nlink ==1 ))) { stat->ino = lowerstat.ino; - -if (samefs) -WARN_ON_ONCE (stat->dev != lowerstat.dev); -else stat->dev =ovl_get_pseudo_dev (dentry); + } }if (samefs) {/*
Re: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
In reply to Amir GoldsteinQuoted 68 lines · Amir Goldstein
Quoted 59 lines · Vivek Goyal
Quoted 40 lines · Amir Goldstein
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +Quoted 2 lines
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
Directory should be fine because for directories ->i_nlink == 1 and this condition will become true.
Quoted 11 lines
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store
OVL_ORIGINinode flag during lookup and then useovl_test_flag(OVL_ORIGIN)in place ofOVL_TYPE_ORIGIN(type).If I am not mistaken, you could set flag
OVL_ORIGINinovl_get_inode()IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helperovl_hash_bylower(), so I hope that helps.Quoted 7 lines
Ok, I can put another ovl-inode flag say
OVL_ORIGIN. But that also means that I need to set the flag during copy up. And that means it brings back ordering requirements for lockless access. For example, inovl_getattr(), if we replaceOVL_TYPE_ORIGIN()withovl_test_flag(ORIGIN), without explicit barriers, what's the guarantee a user will seeOVL_ORIGINflag yet. In fact it can happen that user might see upperdentry but notOVL_ORIGIN.
Quoted 4 lines
OK, so instead set
OVL_BYLOWERinovl_get_inode()IFF (bylower). that property doesn't change on copy up.
and set__OVL_PATH_ORIGINinovl_path_type()if upper AND numlower ANDOVL_BYLOWER.
Ok, so at the time of lookup, we know if hardlink will be broken or not and that property does not change over copy up. So yes that should solve the issue.
Having said that, OVL_BYLOWER is very broad in my opinion. Setting OVL_ORIGIN in inode appeals more to me. This means this inode either has copy up origin or will have copy up origin when copy up happens.
What do you think?
I have another proposal. All these problems seem to stem from the fact that copy up changes some of the fields in ovl_inode and we don't have any strict ordering for the updates.
I am wondering can we replace data dependency barrier (smp_read_barrier_depends) with a full smp_read() and use that for ovl_dentry_upper(). And do all the flag updates before ovl_inode_update(). This should make sure that if caller sees upperdentry, then not only upper dentry is table, at the same time any updates to OVL_INDEX, OVL_ORIGIN and any other flags have been done.
So in copy up path, we will first update OVL_INDEX, OVL_ORIGIN etc and then call ovl_inode_update(). And on read side fetch upperdentry first and put smp_rmb() after that. And if upperdentry is visible, that means INDEX and ORIGIN update must be visible as well.
Copy up side
-----------
store OVL_INDEX store OVL_ORIGIN store any other relevant flag smp_wmb() store ovl_inode->__upperdentry
Read side (ovl_getxattr and other users)
-----------------------------------------
load ovl_inode->__upperdentry smp_rmb() load OVL_INDEX load OVL_ORIGIN
Now if ovl_inode->__upperdentry is set, that means not only upper dentry is stable, it should also mean that ovl_inode->flags are stable too.
If this theory of barrier operations is correct, I feel this is a better solution. It is easy to understand at the same time it allows for easy code extension in future.
Right now, these lockless assumptions are so difficult to understand and so error prone, that all the new users will invariably end up introducing races.
Quoted 19 lines
Quoted 7 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.Yes. and that is not the only problem I see with
ovl_getattr().
stat->devcould be set toovl_get_pseudo_dev(dentry), even ifstat->inois not set to lowerstat.ino; that is not good.If I am not mistaken, all the special conditions in
ovl_getattr()to use lowerst_inoare already encoded inovl_hash_bylower(), so you may completely remove those extra tests ifOVL_TYPE_ORIGIN(type)already checks theOVL_BYLOWERflag.
Quoted 7 lines
It may be hard to see my claim above is true, because, for example,
sb->s_export_opinovl_hash_bylower()is equivalent toovl_verify_lower(dentry->d_sb)inovl_getattr().Please double check my claims, and if you agree, you can submit a patch that Fixes: ("
a0c5ad307ac0ovl: relax same fs constraint for constantst_ino"), which already does the right thing for metacopy.
I will need to spend some time to better understand it. I feel that changing barrier semantics around __upperdentry, will solve this issue with existing code as well.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 78 lines · Vivek Goyal
Quoted 68 lines · Amir Goldstein
Quoted 59 lines · Vivek Goyal
Quoted 40 lines · Amir Goldstein
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +Quoted 2 lines
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
Directory should be fine because for directories ->i_nlink == 1 and this condition will become true.
Quoted 11 lines
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store
OVL_ORIGINinode flag during lookup and then useovl_test_flag(OVL_ORIGIN)in place ofOVL_TYPE_ORIGIN(type).If I am not mistaken, you could set flag
OVL_ORIGINinovl_get_inode()IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helperovl_hash_bylower(), so I hope that helps.Quoted 7 lines
Ok, I can put another ovl-inode flag say
OVL_ORIGIN. But that also means that I need to set the flag during copy up. And that means it brings back ordering requirements for lockless access. For example, inovl_getattr(), if we replaceOVL_TYPE_ORIGIN()withovl_test_flag(ORIGIN), without explicit barriers, what's the guarantee a user will seeOVL_ORIGINflag yet. In fact it can happen that user might see upperdentry but notOVL_ORIGIN.Quoted 4 lines
OK, so instead set
OVL_BYLOWERinovl_get_inode()IFF (bylower). that property doesn't change on copy up.
and set__OVL_PATH_ORIGINinovl_path_type()if upper AND numlower ANDOVL_BYLOWER.Ok, so at the time of lookup, we know if hardlink will be broken or not and that property does not change over copy up. So yes that should solve the issue.
Quoted 6 lines
Having said that,
OVL_BYLOWERis very broad in my opinion. SettingOVL_ORIGINin inode appeals more to me. This means this inode either has copy up origin or will have copy up origin when copy up happens.What do you think?
I think its better to choose a good flag name than to add complexity. How about OVL_CONST_INO?
Quoted 36 lines
I have another proposal. All these problems seem to stem from the fact that copy up changes some of the fields in
ovl_inodeand we don't have any strict ordering for the updates.I am wondering can we replace data dependency barrier (
smp_read_barrier_depends) with a fullsmp_read()and use that forovl_dentry_upper(). And do all the flag updates beforeovl_inode_update(). This should make sure that if caller sees upperdentry, then not only upper dentry is table, at the same time any updates toOVL_INDEX,OVL_ORIGINand any other flags have been done.So in copy up path, we will first update
OVL_INDEX,OVL_ORIGINetc and then callovl_inode_update(). And on read side fetch upperdentry first and putsmp_rmb()after that. And if upperdentry is visible, that means INDEX and ORIGIN update must be visible as well.Copy up side
-----------store OVL_INDEX store OVL_ORIGIN store any other relevant flag smp_wmb() store ovl_inode->__upperdentryRead side (
ovl_getxattrand other users)
-----------------------------------------load ovl_inode->__upperdentry smp_rmb() load OVL_INDEX load OVL_ORIGINNow if
ovl_inode->__upperdentryis set, that means not only upper dentry is stable, it should also mean thatovl_inode->flagsare stable too.
Quoted 8 lines
If this theory of barrier operations is correct, I feel this is a better solution. It is easy to understand at the same time it allows for easy code extension in future.
Right now, these lockless assumptions are so difficult to understand and so error prone, that all the new users will invariably end up introducing races.
I see the value in your proposal, but:
1. I do not know how to measure the performance impact of this change.
2. patch 20/28 introduces another smp_wmb() in ovl_copy_up_locked() and it is not conditional to metacopy being enabled. Is that right? Can your proposal make that any better?
Quoted 23 lines
Quoted 23 lines
Quoted 7 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.Yes. and that is not the only problem I see with
ovl_getattr().
stat->devcould be set toovl_get_pseudo_dev(dentry), even ifstat->inois not set to lowerstat.ino; that is not good.If I am not mistaken, all the special conditions in
ovl_getattr()to use lowerst_inoare already encoded inovl_hash_bylower(), so you may completely remove those extra tests ifOVL_TYPE_ORIGIN(type)already checks theOVL_BYLOWERflag.It may be hard to see my claim above is true, because, for example,
sb->s_export_opinovl_hash_bylower()is equivalent toovl_verify_lower(dentry->d_sb)inovl_getattr().
Quoted 8 lines
Quoted 3 lines
Please double check my claims, and if you agree, you can submit a patch that Fixes: ("
a0c5ad307ac0ovl: relax same fs constraint for constantst_ino"), which already does the right thing for metacopy.I will need to spend some time to better understand it. I feel that changing barrier semantics around
__upperdentry, will solve this issue with existing code as well.
Its a simple bug not related to barriers semantics. I will post a fix patch.
Thanks,
Amir.
Re: [PATCH v13 20/28] ovl: Do not set dentry type ORIGIN for broken hardlinks
Quoted 84 lines · Amir Goldstein
Quoted 83 lines · Vivek Goyal
Quoted 68 lines · Amir Goldstein
Quoted 59 lines · Vivek Goyal
Quoted 40 lines · Amir Goldstein
Quoted 23 lines · Vivek Goyal
If a dentry has copy up origin, we set flag
OVL_PATH_ORIGIN. So far this decision was easy that we had to check only foroe->numlowerand if it is non-zero, we knew there is copy up origin. (For non-dir we installed origin dentry in lowerstack[0]).But we don't create ORGIN xattr for broken hardlinks (index=off). And with metacopy feature it is possible that we will still install lowerstack[0]. But that's lower data dentry of metacopy upper of broken hardlink and not ORIGIN XATTR is not set.
So two differentiate between two cases, do not set
OVL_PATH_ORIGINif we have a broken hardlink.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/util.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/util.c +0
-0fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 117
Quoted 1 line
fs/overlayfs/util.c +0
-0@@ -117,7 +117,14 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)* Non-dir dentry can hold lower dentry of its copy up origin. This comment needs updating with metacopy.
fs/overlayfs/util.c enum ovl_path_type ovl_path_type(struct dentry *dentry) · line 127
Quoted 11 lines
fs/overlayfs/util.c +8
-1*/ if (oe->numlower) { - type |= __OVL_PATH_ORIGIN; +/* +* ORIGIN is created for everyting except broken +* hardlinks +*/ +if (!(d_inode (dentry)->i_nlink >1 && + !ovl_test_flag (OVL_INDEX ,d_inode (dentry)))) + type |= __OVL_PATH_ORIGIN; +Quoted 2 lines
I don't like relying on overlay nlink. it is not reliable. And I think you missed the directory case.
Directory should be fine because for directories ->i_nlink == 1 and this condition will become true.
Quoted 11 lines
The information you need was available during lookup and we did not keep it (was lower verified by origin fh). Most likely what we need to do it store
OVL_ORIGINinode flag during lookup and then useovl_test_flag(OVL_ORIGIN)in place ofOVL_TYPE_ORIGIN(type).If I am not mistaken, you could set flag
OVL_ORIGINinovl_get_inode()IFF (upperdentry && bylower), but to be honest the rules became so complicated that I can't say for sure. At least I concentrated all the rules in one helperovl_hash_bylower(), so I hope that helps.Quoted 7 lines
Ok, I can put another ovl-inode flag say
OVL_ORIGIN. But that also means that I need to set the flag during copy up. And that means it brings back ordering requirements for lockless access. For example, inovl_getattr(), if we replaceOVL_TYPE_ORIGIN()withovl_test_flag(ORIGIN), without explicit barriers, what's the guarantee a user will seeOVL_ORIGINflag yet. In fact it can happen that user might see upperdentry but notOVL_ORIGIN.Quoted 4 lines
OK, so instead set
OVL_BYLOWERinovl_get_inode()IFF (bylower). that property doesn't change on copy up.
and set__OVL_PATH_ORIGINinovl_path_type()if upper AND numlower ANDOVL_BYLOWER.Ok, so at the time of lookup, we know if hardlink will be broken or not and that property does not change over copy up. So yes that should solve the issue.
Having said that,
OVL_BYLOWERis very broad in my opinion. SettingOVL_ORIGINin inode appeals more to me. This means this inode either has copy up origin or will have copy up origin when copy up happens.
Quoted 4 lines
Quoted 1 line
What do you think?
I think its better to choose a good flag name than to add complexity. How about
OVL_CONST_INO?
OVL_CONST_INO sounds better in terms of naming. So it will translate to ORIGIN.
OVL_ORIGIN = (upperdentry && OVL_CONST_INO)
Quoted 46 lines
Quoted 37 lines
I have another proposal. All these problems seem to stem from the fact that copy up changes some of the fields in
ovl_inodeand we don't have any strict ordering for the updates.I am wondering can we replace data dependency barrier (
smp_read_barrier_depends) with a fullsmp_read()and use that forovl_dentry_upper(). And do all the flag updates beforeovl_inode_update(). This should make sure that if caller sees upperdentry, then not only upper dentry is table, at the same time any updates toOVL_INDEX,OVL_ORIGINand any other flags have been done.So in copy up path, we will first update
OVL_INDEX,OVL_ORIGINetc and then callovl_inode_update(). And on read side fetch upperdentry first and putsmp_rmb()after that. And if upperdentry is visible, that means INDEX and ORIGIN update must be visible as well.Copy up side
-----------store OVL_INDEX store OVL_ORIGIN store any other relevant flag smp_wmb() store ovl_inode->__upperdentryRead side (
ovl_getxattrand other users)
-----------------------------------------load ovl_inode->__upperdentry smp_rmb() load OVL_INDEX load OVL_ORIGINNow if
ovl_inode->__upperdentryis set, that means not only upper dentry is stable, it should also mean thatovl_inode->flagsare stable too.Quoted 8 lines
If this theory of barrier operations is correct, I feel this is a better solution. It is easy to understand at the same time it allows for easy code extension in future.
Right now, these lockless assumptions are so difficult to understand and so error prone, that all the new users will invariably end up introducing races.
Quoted 5 lines
I see the value in your proposal, but:
1. I do not know how to measure the performance impact of this change.
2. patch 20/28 introduces anothersmp_wmb()inovl_copy_up_locked()and it is not conditional to metacopy being enabled. Is that right? Can your proposal make that any better?
I am thinking that for now I will leave it as it is because OVL_CONST_INO probably will solve the immediate issue. I will probably take up this proposal in a separate patch series once we are done merging a basic metacopy patch series.
Thanks
Vivek
Quoted 38 lines
Quoted 24 lines
Quoted 24 lines
Quoted 8 lines
In fact I see
OVL_INDEXbeing used inovl_getattr(). If a parallel copy up took place it is possible thatovl_getattr()saw upperdentry but not theOVL_INDEX. And then nlink count will be off. IOW, what barrier/lock guarantees that this does not happen.Yes. and that is not the only problem I see with
ovl_getattr().
stat->devcould be set toovl_get_pseudo_dev(dentry), even ifstat->inois not set to lowerstat.ino; that is not good.If I am not mistaken, all the special conditions in
ovl_getattr()to use lowerst_inoare already encoded inovl_hash_bylower(), so you may completely remove those extra tests ifOVL_TYPE_ORIGIN(type)already checks theOVL_BYLOWERflag.It may be hard to see my claim above is true, because, for example,
sb->s_export_opinovl_hash_bylower()is equivalent toovl_verify_lower(dentry->d_sb)inovl_getattr().Quoted 8 lines
Quoted 3 lines
Please double check my claims, and if you agree, you can submit a patch that Fixes: ("
a0c5ad307ac0ovl: relax same fs constraint for constantst_ino"), which already does the right thing for metacopy.I will need to spend some time to better understand it. I feel that changing barrier semantics around
__upperdentry, will solve this issue with existing code as well.Its a simple bug not related to barriers semantics. I will post a fix patch.
Thanks,
Amir.
PATCH v13 21/28 ovl: Set redirect on metacopy files upon rename
In reply to Vivek GoyalSet redirect on metacopy files upon rename. This will help find data dentry in lower dirs.
--- fs/overlayfs/dir.c | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-)
fs/overlayfs/dir.c +37 -13
@@ -968,6 +968,27 @@ static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new,mutex_unlock (&OVL_I (d_inode (new))->lock); } +static bool ovl_relative_redirect (struct dentry *dentry,bool samedir) +{ +if (d_is_dir (dentry)) +return samedir; + +/* +* For non-dir hardlinked files, we need absolute redirects +* in general as two upper hardlinks could be in different +* dirs. We could put a relative redirect now and convert +* it to absolute redirect later. But when nlink > 1 and +* indexing is on, that means relative redirect needs to be +* converted to absolute during copy up of another lower +* hardllink as well. +* +* So without optimizing too much, just check if non-dir +* has nlink > 1 or not. If yes, set absolute redirect to +* begin with. +*/ +return (d_inode (dentry)->i_nlink >1 ? false : samedir); +} +static int ovl_rename (struct inode *olddir,struct dentry *old,struct inode *newdir,struct dentry *new,unsigned int flags)
@@ -1131,22 +1152,25 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, goto out_dput; err =0 ; -if (is_dir) { -if (ovl_type_merge_or_lower (old)) - err =ovl_set_redirect (old, samedir); -else if (!old_opaque &&ovl_type_merge (new->d_parent)) - err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); -if (err) - goto out_dput; - } -if (!overwrite && new_is_dir) { +if (ovl_type_merge_or_lower (old)) + err =ovl_set_redirect (old, +ovl_relative_redirect (old, samedir)); +else if (is_dir && !old_opaque &&ovl_type_merge (new->d_parent)) + err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); + +if (err) + goto out_dput; + +if (!overwrite) {if (ovl_type_merge_or_lower (new)) - err =ovl_set_redirect (new, samedir); -else if (!new_opaque &&ovl_type_merge (old->d_parent)) + err =ovl_set_redirect (new,ovl_relative_redirect (new, + samedir)); +else if (new_is_dir && !new_opaque && +ovl_type_merge (old->d_parent)) err =ovl_set_opaque_xerr (new, newdentry, -EXDEV ); -if (err) - goto out_dput; } +if (err) + goto out_dput; err =ovl_do_rename (old_upperdir->d_inode, olddentry, new_upperdir->d_inode, newdentry, flags);
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 21/28] ovl: Set redirect on metacopy files upon rename
Quoted 25 lines · Vivek Goyal
Set redirect on metacopy files upon rename. This will help find data dentry in lower dirs.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-)fs/overlayfs/dir.c +9
-0@@ -968,6 +968,27 @@ static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new,mutex_unlock (&OVL_I (d_inode (new))->lock); } +static bool ovl_relative_redirect (struct dentry *dentry,bool samedir) +{ +if (d_is_dir (dentry)) +return samedir; + +/* +* For non-dir hardlinked files, we need absolute redirects +* in general as two upper hardlinks could be in different +* dirs. We could put a relative redirect now and convert
fs/overlayfs/dir.c static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new, · line 989
Quoted 10 lines
fs/overlayfs/dir.c +10
-0+* it to absolute redirect later. But when nlink > 1 and +* indexing is on, that means relative redirect needs to be +* converted to absolute during copy up of another lower +* hardllink as well. +* +* So without optimizing too much, just check if non-dir +* has nlink > 1 or not. If yes, set absolute redirect to +* begin with. +*/ +return (d_inode (dentry)->i_nlink >1 ? false : samedir);
I don't think this is wrong, but I don't like relying on the overlay inode nlink. I wonder if you should separate the case of indexed and non-indexed inode.
Quoted 51 lines
fs/overlayfs/dir.c +18
-14+} +static int ovl_rename (struct inode *olddir,struct dentry *old,struct inode *newdir,struct dentry *new,unsigned int flags)@@ -1131,22 +1152,25 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, goto out_dput; err =0 ; -if (is_dir) { -if (ovl_type_merge_or_lower (old)) - err =ovl_set_redirect (old, samedir); -else if (!old_opaque &&ovl_type_merge (new->d_parent)) - err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); -if (err) - goto out_dput; - } -if (!overwrite && new_is_dir) { +if (ovl_type_merge_or_lower (old)) + err =ovl_set_redirect (old, +ovl_relative_redirect (old, samedir)); +else if (is_dir && !old_opaque &&ovl_type_merge (new->d_parent)) + err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); + +if (err) + goto out_dput; + +if (!overwrite) {if (ovl_type_merge_or_lower (new)) - err =ovl_set_redirect (new, samedir); -else if (!new_opaque &&ovl_type_merge (old->d_parent)) + err =ovl_set_redirect (new,ovl_relative_redirect (new, + samedir)); +else if (new_is_dir && !new_opaque && +ovl_type_merge (old->d_parent)) err =ovl_set_opaque_xerr (new, newdentry, -EXDEV ); -if (err) - goto out_dput; } +if (err) + goto out_dput; err =ovl_do_rename (old_upperdir->d_inode, olddentry, new_upperdir->d_inode, newdentry, flags); --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v13 21/28] ovl: Set redirect on metacopy files upon rename
Quoted 32 lines · Amir Goldstein
Quoted 31 lines · Vivek Goyal
Set redirect on metacopy files upon rename. This will help find data dentry in lower dirs.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-)fs/overlayfs/dir.c +15
-0@@ -968,6 +968,27 @@ static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new,mutex_unlock (&OVL_I (d_inode (new))->lock); } +static bool ovl_relative_redirect (struct dentry *dentry,bool samedir) +{ +if (d_is_dir (dentry)) +return samedir; + +/* +* For non-dir hardlinked files, we need absolute redirects +* in general as two upper hardlinks could be in different +* dirs. We could put a relative redirect now and convert +* it to absolute redirect later. But when nlink > 1 and +* indexing is on, that means relative redirect needs to be +* converted to absolute during copy up of another lower +* hardllink as well. +* +* So without optimizing too much, just check if non-dir
Quoted 8 lines
fs/overlayfs/dir.c static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new, · line 989
Quoted 4 lines
fs/overlayfs/dir.c +4
-0+* has nlink > 1 or not. If yes, set absolute redirect to +* begin with. +*/ +return (d_inode (dentry)->i_nlink >1 ? false : samedir);I don't think this is wrong, but I don't like relying on the overlay inode nlink. I wonder if you should separate the case of indexed and non-indexed inode.
Hi Amir,
So if inode is indexed, use absolute redirect, otherwise use, relative redirect? But how do we get reliably the informaton if inode is indexed or not. I mean, OVL_INDEX has memory ordering issues, w.r.t copy up, so that's untrusted as well.
How about if I check for nlink of lowerdentry (instead of ovl dentry). Will that be fine?
Vivek
Quoted 52 lines
Quoted 52 lines
fs/overlayfs/dir.c +18
-14+} +static int ovl_rename (struct inode *olddir,struct dentry *old,struct inode *newdir,struct dentry *new,unsigned int flags)@@ -1131,22 +1152,25 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, goto out_dput; err =0 ; -if (is_dir) { -if (ovl_type_merge_or_lower (old)) - err =ovl_set_redirect (old, samedir); -else if (!old_opaque &&ovl_type_merge (new->d_parent)) - err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); -if (err) - goto out_dput; - } -if (!overwrite && new_is_dir) { +if (ovl_type_merge_or_lower (old)) + err =ovl_set_redirect (old, +ovl_relative_redirect (old, samedir)); +else if (is_dir && !old_opaque &&ovl_type_merge (new->d_parent)) + err =ovl_set_opaque_xerr (old, olddentry, -EXDEV ); + +if (err) + goto out_dput; + +if (!overwrite) {if (ovl_type_merge_or_lower (new)) - err =ovl_set_redirect (new, samedir); -else if (!new_opaque &&ovl_type_merge (old->d_parent)) + err =ovl_set_redirect (new,ovl_relative_redirect (new, + samedir)); +else if (new_is_dir && !new_opaque && +ovl_type_merge (old->d_parent)) err =ovl_set_opaque_xerr (new, newdentry, -EXDEV ); -if (err) - goto out_dput; } +if (err) + goto out_dput; err =ovl_do_rename (old_upperdir->d_inode, olddentry, new_upperdir->d_inode, newdentry, flags); --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Amir Goldstein
maintainerRe: [PATCH v13 21/28] ovl: Set redirect on metacopy files upon rename
Quoted 44 lines · Vivek Goyal
Quoted 32 lines · Amir Goldstein
Quoted 31 lines · Vivek Goyal
Set redirect on metacopy files upon rename. This will help find data dentry in lower dirs.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-)fs/overlayfs/dir.c +15
-0@@ -968,6 +968,27 @@ static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new,mutex_unlock (&OVL_I (d_inode (new))->lock); } +static bool ovl_relative_redirect (struct dentry *dentry,bool samedir) +{ +if (d_is_dir (dentry)) +return samedir; + +/* +* For non-dir hardlinked files, we need absolute redirects +* in general as two upper hardlinks could be in different +* dirs. We could put a relative redirect now and convert +* it to absolute redirect later. But when nlink > 1 and +* indexing is on, that means relative redirect needs to be +* converted to absolute during copy up of another lower +* hardllink as well. +* +* So without optimizing too much, just check if non-dir Quoted 8 lines
fs/overlayfs/dir.c static void ovl_rename_unlock_ovl_inodes(struct dentry *old, struct dentry *new, · line 989
Quoted 4 lines
fs/overlayfs/dir.c +4
-0+* has nlink > 1 or not. If yes, set absolute redirect to +* begin with. +*/ +return (d_inode (dentry)->i_nlink >1 ? false : samedir);I don't think this is wrong, but I don't like relying on the overlay inode nlink. I wonder if you should separate the case of indexed and non-indexed inode.
Hi Amir,
Quoted 8 lines
So if inode is indexed, use absolute redirect, otherwise use, relative redirect? But how do we get reliably the informaton if inode is indexed or not. I mean,
OVL_INDEXhas memory ordering issues, w.r.t copy up, so that's untrusted as well.How about if I check for nlink of lowerdentry (instead of ovl dentry). Will that be fine?
I guess that would be fine.
Maybe nicer if we set OVL_LOWER_HARDLINK in lookup.
But for now, let's just say I did not NACK your patch
and maybe there is no good reason to doubt the reliability of overlay inode nlink, so unless Miklos feels otherwise, let's postpone this discussion for a later time.
Cheers,
Amir.
PATCH v13 22/28 ovl: Set redirect on upper inode when it is linked
In reply to Vivek GoyalWhen we create a hardlink to a metacopy upper file, first the redirect on that inode. Path based lookup will not work with newly created link and redirect will solve that issue.
Also use absolute redirect as two hardlinks could be in different directores and relative redirect will not work.
--- fs/overlayfs/dir.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
fs/overlayfs/dir.c +14 -4
@@ -24,6 +24,8 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);MODULE_PARM_DESC (ovl_redirect_max,"Maximum length of absolute redirect xattr value" ); +static int ovl_set_redirect (struct dentry *dentry,bool samedir); +int ovl_cleanup (struct inode *wdir,struct dentry *wdentry) {int err;
@@ -468,6 +470,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,const struct cred *old_cred;struct cred *override_cred;struct dentry *parent = dentry->d_parent; +struct dentry *hardlink_upper; + + hardlink_upper = hardlink ?ovl_dentry_upper (hardlink) :NULL ; err =ovl_copy_up (parent);if (err)
@@ -502,12 +507,18 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,put_cred (override_creds (override_cred));put_cred (override_cred); +if (hardlink &&ovl_is_metacopy_dentry (hardlink)) { + err =ovl_set_redirect (hardlink, false); +if (err) + goto out_revert_creds; + } +if (!ovl_dentry_is_whiteout (dentry)) err =ovl_create_upper (dentry, inode, attr, - hardlink); + hardlink_upper);else err =ovl_create_over_whiteout (dentry, inode, attr, - hardlink); + hardlink_upper); } out_revert_creds:revert_creds (old_cred);
@@ -606,8 +617,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, inode =d_inode (old);ihold (inode); - err =ovl_create_or_link (new, inode,NULL ,ovl_dentry_upper (old), -ovl_type_origin (old)); + err =ovl_create_or_link (new, inode,NULL , old,ovl_type_origin (old));if (err)iput (inode);
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 22/28] ovl: Set redirect on upper inode when it is linked
Quoted 36 lines · Vivek Goyal
When we create a hardlink to a metacopy upper file, first the redirect on that inode. Path based lookup will not work with newly created link and redirect will solve that issue.
Also use absolute redirect as two hardlinks could be in different directores and relative redirect will not work.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)fs/overlayfs/dir.c +5
-0@@ -24,6 +24,8 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);MODULE_PARM_DESC (ovl_redirect_max,"Maximum length of absolute redirect xattr value" ); +static int ovl_set_redirect (struct dentry *dentry,bool samedir); +int ovl_cleanup (struct inode *wdir,struct dentry *wdentry) {int err;@@ -468,6 +470,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,const struct cred *old_cred;struct cred *override_cred;struct dentry *parent = dentry->d_parent; +struct dentry *hardlink_upper; + + hardlink_upper = hardlink ?ovl_dentry_upper (hardlink) :NULL ; err =ovl_copy_up (parent);if (err)
fs/overlayfs/dir.c static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, · line 511
Quoted 5 lines
fs/overlayfs/dir.c +2
-0@@ -502,12 +507,18 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,put_cred (override_creds (override_cred));put_cred (override_cred); +if (hardlink &&ovl_is_metacopy_dentry (hardlink)) { + err =ovl_set_redirect (hardlink, false);
Like with rename, redirect could be set much sooner in ovl_link() if all the locks were contained within ovl_set_redirect(). I think code will be simpler overall, but can't say for sure...
Quoted 32 lines
fs/overlayfs/dir.c +7
-5+if (err) + goto out_revert_creds; + } +if (!ovl_dentry_is_whiteout (dentry)) err =ovl_create_upper (dentry, inode, attr, - hardlink); + hardlink_upper);else err =ovl_create_over_whiteout (dentry, inode, attr, - hardlink); + hardlink_upper); } out_revert_creds:revert_creds (old_cred);@@ -606,8 +617,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, inode =d_inode (old);ihold (inode); - err =ovl_create_or_link (new, inode,NULL ,ovl_dentry_upper (old), -ovl_type_origin (old)); + err =ovl_create_or_link (new, inode,NULL , old,ovl_type_origin (old));if (err)iput (inode); --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v13 22/28] ovl: Set redirect on upper inode when it is linked
Quoted 40 lines · Amir Goldstein
Quoted 39 lines · Vivek Goyal
When we create a hardlink to a metacopy upper file, first the redirect on that inode. Path based lookup will not work with newly created link and redirect will solve that issue.
Also use absolute redirect as two hardlinks could be in different directores and relative redirect will not work.
Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/dir.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)fs/overlayfs/dir.c +5
-0@@ -24,6 +24,8 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);MODULE_PARM_DESC (ovl_redirect_max,"Maximum length of absolute redirect xattr value" ); +static int ovl_set_redirect (struct dentry *dentry,bool samedir); +int ovl_cleanup (struct inode *wdir,struct dentry *wdentry) {int err;@@ -468,6 +470,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,const struct cred *old_cred;struct cred *override_cred;struct dentry *parent = dentry->d_parent; +struct dentry *hardlink_upper; + + hardlink_upper = hardlink ?ovl_dentry_upper (hardlink) :NULL ; err =ovl_copy_up (parent);if (err)@@ -502,12 +507,18 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,put_cred (override_creds (override_cred));put_cred (override_cred);
Quoted 6 lines
fs/overlayfs/dir.c static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, · line 511
Quoted 2 lines
fs/overlayfs/dir.c +2
-0+if (hardlink &&ovl_is_metacopy_dentry (hardlink)) { + err =ovl_set_redirect (hardlink, false);Like with rename, redirect could be set much sooner in
ovl_link()if all the locks were contained withinovl_set_redirect(). I think code will be simpler overall, but can't say for sure...
For now, I am not taking any locks in ovl_set_redirect(). I think VFS locking along with d_lock seems sufficient for the current use case.
So leaving it untouched for now.
Vivek
Quoted 34 lines
Quoted 34 lines
fs/overlayfs/dir.c +7
-5+if (err) + goto out_revert_creds; + } +if (!ovl_dentry_is_whiteout (dentry)) err =ovl_create_upper (dentry, inode, attr, - hardlink); + hardlink_upper);else err =ovl_create_over_whiteout (dentry, inode, attr, - hardlink); + hardlink_upper); } out_revert_creds:revert_creds (old_cred);@@ -606,8 +617,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, inode =d_inode (old);ihold (inode); - err =ovl_create_or_link (new, inode,NULL ,ovl_dentry_upper (old), -ovl_type_origin (old)); + err =ovl_create_or_link (new, inode,NULL , old,ovl_type_origin (old));if (err)iput (inode); --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 23/28 ovl: Remove redirect when data of a metacopy file is copied up
In reply to Vivek GoyalWhen a metacopy file is no longer a metacopy and data has been copied up, remove REDIRECT xattr. Its not needed anymore.
--- fs/overlayfs/copy_up.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
fs/overlayfs/copy_up.c +10 -1
@@ -774,9 +774,18 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) err =vfs_removexattr (upperpath.dentry,OVL_XATTR_METACOPY );if (err)return err; +/* +* A metacopy files does not need redirect xattr once data has +* been copied up. +*/ + err =vfs_removexattr (upperpath.dentry,OVL_XATTR_REDIRECT ); +if (err && err != -ENODATA && err != -EOPNOTSUPP ) +return err; +/* We must be holding ovl_inode->lock if we are here */ +ovl_dentry_set_redirect (c->dentry,NULL );ovl_set_upperdata (d_inode (c->dentry)); -return err; +return 0 ; } static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
Signature · 1 line
2.13.6
PATCH v13 24/28 ovl: Do not error if REDIRECT XATTR is missing
In reply to Vivek GoyalCurrently we first call vfs_getxattr() to get size of REDIRECT xatttr and then make another call vfs_getxattr() to read the xattr in buffer.
This is all part of ovl_lookup() and we do not have ovl_inode->lock. That means it is possible that inode got copied up on another cpu and is not a metacopy inode anymore. And that also means REDIRECT xattr got removed. And that means that while first call to vfs_getxattr() succeeded, second call can fail with -ENODATA.
Do not error out if -ENODATA is received from second call. With metacopy enabled, it is not an error case anymore.
--- fs/overlayfs/namei.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/overlayfs/namei.c +8 -1
@@ -47,8 +47,15 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, goto invalid; res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT , buf, res); -if (res <0 ) +if (res <0 ) { +/* +* Redirect xattr can be removed if a parallel data +* copy up took place. +*/ +if (res == -ENODATA ) + goto err_free; goto fail; + }if (res ==0 ) goto invalid;if (buf[0 ] =='/' ) {
Signature · 1 line
2.13.6
Re: [PATCH v13 24/28] ovl: Do not error if REDIRECT XATTR is missing
Quoted 9 lines · Vivek Goyal
Currently we first call
vfs_getxattr()to get size of REDIRECT xatttr and then make another callvfs_getxattr()to read the xattr in buffer.This is all part of
ovl_lookup()and we do not haveovl_inode->lock. That means it is possible that inode got copied up on another cpu and is not a metacopy inode anymore. And that also means REDIRECT xattr got removed. And that means that while first call tovfs_getxattr()succeeded, second call can fail with-ENODATA.
Quoted 4 lines
Do not error out if
-ENODATAis received from second call. With metacopy enabled, it is not an error case anymore.Signed-off-by Vivek Goyal <[email protected]>
Quoted 32 lines
--- fs/overlayfs/namei.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/overlayfs/namei.c +8
-2@@ -47,8 +47,15 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, goto invalid; res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT , buf, res); -if (res <0 ) +if (res <0 ) { +/* +* Redirect xattr can be removed if a parallel data +* copy up took place. +*/ +if (res == -ENODATA ) + goto err_free; goto fail; + }if (res ==0 ) goto invalid;if (buf[0 ] =='/' ) { --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 25/28 ovl: Use out_err insteada of out_nomem
In reply to Vivek GoyalRight now we use goto out_nomem which assumes error code is -ENOMEM. But there are other errors returned like -ESTALE as well. So instead of out_nomem, use out_err which will do ERR_PTR(err). That way one can putt error code in err and jump to out_err.
This just code reorganization and no change of functionality.
I am about to add more code and this organization helps laying more code and error paths on top of it.
--- fs/overlayfs/inode.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
fs/overlayfs/inode.c +10 -7
@@ -693,6 +693,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,struct inode *inode;bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index);bool is_dir, metacopy = false; +int err = -ENOMEM ;if (!realinode) realinode =d_inode (lowerdentry);
@@ -710,7 +711,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, inode =iget5_locked (sb, (unsigned long ) key, ovl_inode_test, ovl_inode_set, key);if (!inode) - goto out_nomem; + goto out_err;if (!(inode->i_state &I_NEW )) {/* * Verify that the underlying files stored in the inode
@@ -719,8 +720,8 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (!ovl_verify_inode (inode, lowerdentry, upperdentry, true)) {iput (inode); - inode =ERR_PTR (-ESTALE ); - goto out; + err = -ESTALE ; + goto out_err; }dput (upperdentry);
@@ -735,8 +736,10 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, }else {/* Lower hardlink that will be broken on copy up */ inode =new_inode (sb); -if (!inode) - goto out_nomem; +if (!inode) { + err = -ENOMEM ; + goto out_err; + } }ovl_fill_inode (inode, realinode->i_mode, realinode->i_rdev);ovl_inode_init (inode, upperdentry, lowerdentry);
@@ -766,7 +769,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, out:return inode; -out_nomem: - inode =ERR_PTR (-ENOMEM ); +out_err: + inode =ERR_PTR (err); goto out; }
Signature · 1 line
2.13.6
Re: [PATCH v13 25/28] ovl: Use out_err insteada of out_nomem
Quoted 11 lines · Vivek Goyal
Right now we use goto
out_nomemwhich assumes error code is-ENOMEM. But there are other errors returned like-ESTALEas well. So instead ofout_nomem, useout_errwhich will doERR_PTR(err). That way one can putt error code in err and jump toout_err.This just code reorganization and no change of functionality.
I am about to add more code and this organization helps laying more code and error paths on top of it.
Signed-off-by Vivek Goyal <[email protected]>
Quoted 37 lines
--- fs/overlayfs/inode.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)fs/overlayfs/inode.c +4
-3@@ -693,6 +693,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,struct inode *inode;bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index);bool is_dir, metacopy = false; +int err = -ENOMEM ;if (!realinode) realinode =d_inode (lowerdentry);@@ -710,7 +711,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, inode =iget5_locked (sb, (unsigned long ) key, ovl_inode_test, ovl_inode_set, key);if (!inode) - goto out_nomem; + goto out_err;if (!(inode->i_state &I_NEW )) {/* * Verify that the underlying files stored in the inode @@ -719,8 +720,8 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (!ovl_verify_inode (inode, lowerdentry, upperdentry, true)) {iput (inode); - inode =ERR_PTR (-ESTALE ); - goto out; + err = -ESTALE ; + goto out_err; }dput (upperdentry);
fs/overlayfs/inode.c struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, · line 740
Quoted 7 lines
fs/overlayfs/inode.c +2
-2@@ -735,8 +736,10 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, }else {/* Lower hardlink that will be broken on copy up */ inode =new_inode (sb); -if (!inode) - goto out_nomem; +if (!inode) { + err = -ENOMEM ;
I recon Miklos prefers setting err value before condition. same with ESTALE above
Quoted 22 lines
fs/overlayfs/inode.c +4
-3+ goto out_err; + } }ovl_fill_inode (inode, realinode->i_mode, realinode->i_rdev);ovl_inode_init (inode, upperdentry, lowerdentry);@@ -766,7 +769,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, out:return inode; -out_nomem: - inode =ERR_PTR (-ENOMEM ); +out_err: + inode =ERR_PTR (err); goto out; } --2.13.6
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v13 26/28 ovl: Re-check redirect xattr during inode initialization
In reply to Vivek GoyalSo far redirect could be placed on directories only and now it can be placed on regular files as well. Also it could be completely removed when a metacopy copy up file's data is copied up. That means if a redirect is present during ovl_lookup(), it could be gone by the time ovl_get_inode() happens.
Or it is possible that ovl_lookup() does not see a redirect and a rename is taking place on a hard link and that places a redirect. And by the time ovl_lookup() calls ovl_get_inode(), it sets ovl_inode->redirect = NULL (Assume inode got flushed out of cache and was allocated new).
IOW, because we check and process redirect without locks in ovl_lookup(), many possibilities open up for regular files. So for such cases, do not use the redirect provided by the caller. Instead query it and install in ovl_inode->redirect.
--- fs/overlayfs/inode.c | 19 ++++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-)
fs/overlayfs/inode.c +18 -1
@@ -694,6 +694,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index);bool is_dir, metacopy = false;int err = -ENOMEM ; +char *new_redirect =NULL ;if (!realinode) realinode =d_inode (lowerdentry);
@@ -754,7 +755,18 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (upperdentry && !metacopy)ovl_set_flag (OVL_UPPERDATA , inode); -OVL_I (inode)->redirect = redirect; +if (!metacopy) { +OVL_I (inode)->redirect = redirect; + redirect =NULL ; + }else if (upperdentry) { + new_redirect =ovl_get_redirect_xattr (upperdentry); +if (IS_ERR (new_redirect)) { + err =PTR_ERR (new_redirect); + goto out_err_inode; + } +OVL_I (inode)->redirect = new_redirect; + new_redirect =NULL ; + }/* Check for non-merge dir that may have whiteouts */ if (is_dir) {
@@ -764,11 +776,16 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, } } +kfree (redirect);if (inode->i_state &I_NEW )unlock_new_inode (inode); out:return inode; +out_err_inode: +if (inode->i_state &I_NEW ) +unlock_new_inode (inode); +iput (inode); out_err: inode =ERR_PTR (err); goto out;
fs/overlayfs/overlayfs.h +1 -0
@@ -279,6 +279,7 @@ void ovl_nlink_end_locked(struct dentry *dentry);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry);bool ovl_is_metacopy_dentry (struct dentry *dentry); +char *ovl_get_redirect_xattr (struct dentry *dentry);static inline bool ovl_is_impuredir (struct dentry *dentry) {
fs/overlayfs/util.c +42 -0
45 lines not on this page · raw message
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 26/28] ovl: Re-check redirect xattr during inode initialization
Quoted 6 lines · Vivek Goyal
So far redirect could be placed on directories only and now it can be placed on regular files as well. Also it could be completely removed when a metacopy copy up file's data is copied up. That means if a redirect is present during
ovl_lookup(), it could be gone by the timeovl_get_inode()happens.
There is a bit of a mess in the assumptions.
If the inode is pure upper or indexed origin, than the alleged race ends up in !(inode->i_state & I_NEW) and you discard redirect anyway.
If the inode is non-indexed copyup, then it is a different inode on disk and different struct ovl_inode in memory than the inode of the copy up we are allegedly racing with (they are broken hardlinks), so there is no issue.
Quoted 4 lines
Or it is possible that
ovl_lookup()does not see a redirect and a rename is taking place on a hard link and that places a redirect. And by the timeovl_lookup()callsovl_get_inode(), it setsovl_inode->redirect= NULL (Assume inode got flushed out of cache and was allocated new).
Same as above.
I am not saying there are no races between lookup and rename/link, but IMO the text above does not describe them or proves that they exist.
Quoted 125 lines
IOW, because we check and process redirect without locks in
ovl_lookup(), many possibilities open up for regular files. So for such cases, do not use the redirect provided by the caller. Instead query it and install inovl_inode->redirect.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/inode.c | 19 ++++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +18
-1@@ -694,6 +694,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index);bool is_dir, metacopy = false;int err = -ENOMEM ; +char *new_redirect =NULL ;if (!realinode) realinode =d_inode (lowerdentry);@@ -754,7 +755,18 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (upperdentry && !metacopy)ovl_set_flag (OVL_UPPERDATA , inode); -OVL_I (inode)->redirect = redirect; +if (!metacopy) { +OVL_I (inode)->redirect = redirect; + redirect =NULL ; + }else if (upperdentry) { + new_redirect =ovl_get_redirect_xattr (upperdentry); +if (IS_ERR (new_redirect)) { + err =PTR_ERR (new_redirect); + goto out_err_inode; + } +OVL_I (inode)->redirect = new_redirect; + new_redirect =NULL ; + }/* Check for non-merge dir that may have whiteouts */ if (is_dir) {@@ -764,11 +776,16 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, } } +kfree (redirect);if (inode->i_state &I_NEW )unlock_new_inode (inode); out:return inode; +out_err_inode: +if (inode->i_state &I_NEW ) +unlock_new_inode (inode); +iput (inode); out_err: inode =ERR_PTR (err); goto out;fs/overlayfs/overlayfs.h +1
-0@@ -279,6 +279,7 @@ void ovl_nlink_end_locked(struct dentry *dentry);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry);bool ovl_is_metacopy_dentry (struct dentry *dentry); +char *ovl_get_redirect_xattr (struct dentry *dentry);static inline bool ovl_is_impuredir (struct dentry *dentry) {fs/overlayfs/util.c +42
-1@@ -833,3 +833,45 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)return (oe->numlower >1 ); } + +char *ovl_get_redirect_xattr (struct dentry *dentry) +{ +int res; +char *s, *next, *buf =NULL ; + + res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT ,NULL ,0 ); +if (res <0 ) { +if (res == -ENODATA || res == -EOPNOTSUPP ) +return NULL ; +return ERR_PTR (res); + } + + buf =kzalloc (res +1 ,GFP_KERNEL ); +if (!buf) +return ERR_PTR (-ENOMEM ); + + res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT , buf, res); +if (res <0 ) { +kfree (buf); +return ERR_PTR (res); + } +if (res ==0 ) + goto invalid; + +if (buf[0 ] =='/' ) { +for (s = buf; *s++ =='/' ; s = next) { + next =strchrnul (s,'/' ); +if (s == next) + goto invalid; + } + }else { +if (strchr (buf,'/' ) !=NULL ) + goto invalid; + } + +return buf; +invalid: +pr_warn_ratelimited ("overlayfs: invalid redirect (%s)\n" , buf); +kfree (buf); +return ERR_PTR (-EINVAL ); +} --
Quoted 2 lines
2.13.6
If you really end up needing this helper, you should use it from lookup as well.
Thanks,
Amir.
Re: [PATCH v13 26/28] ovl: Re-check redirect xattr during inode initialization
Quoted 8 lines · Amir Goldstein
Quoted 6 lines · Vivek Goyal
So far redirect could be placed on directories only and now it can be placed on regular files as well. Also it could be completely removed when a metacopy copy up file's data is copied up. That means if a redirect is present during
ovl_lookup(), it could be gone by the timeovl_get_inode()happens.
Quoted 4 lines
There is a bit of a mess in the assumptions.
If the inode is pure upper or indexed origin, than the alleged race ends up in !(inode->i_state &
I_NEW) and you discard redirect anyway.
Can't these also happen when I_NEW=true. I mean inode could be flushed out of cache. Say one cpu is doing ovl_lookup() and thread got blocked while other cpu did copy up of file on other cpu, removed redirect and inode got flushed out of cache. Now cpu1 resumes execuction, creates a new inode but it needs to re-check if redirect is still present or not?
Quoted 5 lines
If the inode is non-indexed copyup, then it is a different inode on disk and different struct
ovl_inodein memory than the inode of the copy up we are allegedly racing with (they are broken hardlinks), so there is no issue.
Agreed that in case of broken hardlinks this race does not exist. But do we really want to optimize it here?
Quoted 11 lines
Quoted 5 lines
Or it is possible that
ovl_lookup()does not see a redirect and a rename is taking place on a hard link and that places a redirect. And by the timeovl_lookup()callsovl_get_inode(), it setsovl_inode->redirect= NULL (Assume inode got flushed out of cache and was allocated new).Same as above.
I am not saying there are no races between lookup and rename/link, but IMO the text above does not describe them or proves that they exist.
I can try to give more details. But I think if inode gets flushed out of cache, then we need to query redirect info again.
Vivek
Quoted 132 lines
Quoted 125 lines
IOW, because we check and process redirect without locks in
ovl_lookup(), many possibilities open up for regular files. So for such cases, do not use the redirect provided by the caller. Instead query it and install inovl_inode->redirect.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/inode.c | 19 ++++++++++++++++++- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-)fs/overlayfs/inode.c +18
-1@@ -694,6 +694,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,bool bylower =ovl_hash_bylower (sb, upperdentry, lowerdentry, index);bool is_dir, metacopy = false;int err = -ENOMEM ; +char *new_redirect =NULL ;if (!realinode) realinode =d_inode (lowerdentry);@@ -754,7 +755,18 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,if (upperdentry && !metacopy)ovl_set_flag (OVL_UPPERDATA , inode); -OVL_I (inode)->redirect = redirect; +if (!metacopy) { +OVL_I (inode)->redirect = redirect; + redirect =NULL ; + }else if (upperdentry) { + new_redirect =ovl_get_redirect_xattr (upperdentry); +if (IS_ERR (new_redirect)) { + err =PTR_ERR (new_redirect); + goto out_err_inode; + } +OVL_I (inode)->redirect = new_redirect; + new_redirect =NULL ; + }/* Check for non-merge dir that may have whiteouts */ if (is_dir) {@@ -764,11 +776,16 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, } } +kfree (redirect);if (inode->i_state &I_NEW )unlock_new_inode (inode); out:return inode; +out_err_inode: +if (inode->i_state &I_NEW ) +unlock_new_inode (inode); +iput (inode); out_err: inode =ERR_PTR (err); goto out;fs/overlayfs/overlayfs.h +1
-0@@ -279,6 +279,7 @@ void ovl_nlink_end_locked(struct dentry *dentry);int ovl_lock_rename_workdir (struct dentry *workdir,struct dentry *upperdir);int ovl_check_metacopy_xattr (struct dentry *dentry);bool ovl_is_metacopy_dentry (struct dentry *dentry); +char *ovl_get_redirect_xattr (struct dentry *dentry);static inline bool ovl_is_impuredir (struct dentry *dentry) {fs/overlayfs/util.c +42
-1@@ -833,3 +833,45 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)return (oe->numlower >1 ); } + +char *ovl_get_redirect_xattr (struct dentry *dentry) +{ +int res; +char *s, *next, *buf =NULL ; + + res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT ,NULL ,0 ); +if (res <0 ) { +if (res == -ENODATA || res == -EOPNOTSUPP ) +return NULL ; +return ERR_PTR (res); + } + + buf =kzalloc (res +1 ,GFP_KERNEL ); +if (!buf) +return ERR_PTR (-ENOMEM ); + + res =vfs_getxattr (dentry,OVL_XATTR_REDIRECT , buf, res); +if (res <0 ) { +kfree (buf); +return ERR_PTR (res); + } +if (res ==0 ) + goto invalid; + +if (buf[0 ] =='/' ) { +for (s = buf; *s++ =='/' ; s = next) { + next =strchrnul (s,'/' ); +if (s == next) + goto invalid; + } + }else { +if (strchr (buf,'/' ) !=NULL ) + goto invalid; + } + +return buf; +invalid: +pr_warn_ratelimited ("overlayfs: invalid redirect (%s)\n" , buf); +kfree (buf); +return ERR_PTR (-EINVAL ); +} --Quoted 2 lines
2.13.6
If you really end up needing this helper, you should use it from lookup as well.
Thanks,
Amir.
Amir Goldstein
maintainerRe: [PATCH v13 26/28] ovl: Re-check redirect xattr during inode initialization
Quoted 14 lines · Vivek Goyal
Quoted 8 lines · Amir Goldstein
Quoted 6 lines · Vivek Goyal
So far redirect could be placed on directories only and now it can be placed on regular files as well. Also it could be completely removed when a metacopy copy up file's data is copied up. That means if a redirect is present during
ovl_lookup(), it could be gone by the timeovl_get_inode()happens.Quoted 4 lines
There is a bit of a mess in the assumptions.
If the inode is pure upper or indexed origin, than the alleged race ends up in !(inode->i_state &
I_NEW) and you discard redirect anyway.
Quoted 7 lines
Can't these also happen when I_NEW=true. I mean inode could be flushed out of cache. Say one cpu is doing
ovl_lookup()and thread got blocked while other cpu did copy up of file on other cpu, removed redirect and inode got flushed out of cache. Now cpu1 resumes execuction, creates a new inode but it needs to re-check if redirect is still present or not?
Not sure. Best to drop the idea of removing REDIRECT on copy up and be done with it.
Quoted 9 lines
Quoted 5 lines
If the inode is non-indexed copyup, then it is a different inode on disk and different struct
ovl_inodein memory than the inode of the copy up we are allegedly racing with (they are broken hardlinks), so there is no issue.Agreed that in case of broken hardlinks this race does not exist. But do we really want to optimize it here?
Well, if you are going to cleanup REDIRECT only on I_NEW, then no need to special case broken hardlinks.
Thanks,
Amir.
PATCH v13 27/28 ovl: Verify a data dentry has been found for metacopy inode
In reply to Vivek GoyalIf we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence ovl_lookup() could not find the correct dentry.
First case is an error while second case is not error. If file has been copied up, then it does not matter if data dentry was found or not.
Redirect removal is protected using ovl_inode->lock and ovl_lookup() does not have access to that lock. So to differentiate between these two cases, take appropriate inode lock in ovl_get_inode() and make sure a data dentry has been found for metacopy inode. Otherwise lookup failed and its an error.
For example, say two files are hardlinked, foo.txt and bar.txt. Say foo.txt is renamed to foo-renamed.txt gets copied up metadata only. This will also put a redirect "/foo.txt" on hardlnk inode. Now assume foo-renamed.txt is opened for write and is undergoing data copy up on one cpu and bar.txt is under going ovl_lookup() on other cpu. Data copy up path will remove REDIRECT and METACOPY xattr. It is possible that METACOPY xattr is visible to ovl_lookup() but by the REDIRECT xattr was gone by the time. That means no data dentry will be found but at the same time now inode is not metacopy inode. So data dentry is not required. So this is not error case. But if inode was still metacopy but data dentry was not found this is error case. (May be due to underlying layer changed). Fix it by returning -ESTALE.
If inode was found in cache, then take ovl_inode->lock before checking status of inode. If inode has been allocated, then it is returned with inode lock anyway and other threads will block on that lock, so no need to take ovl_inode->lock.
--- fs/overlayfs/export.c | 3 ++- fs/overlayfs/inode.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++- fs/overlayfs/namei.c | 14 ++++---------- fs/overlayfs/overlayfs.h | 3 ++- 4 files changed, 56 insertions(+), 13 deletions(-)
fs/overlayfs/export.c +2 -1
@@ -305,7 +305,8 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,if (d_is_dir (upper ?: lower))return ERR_PTR (-EIO ); - inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL ); + inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL , + false);if (IS_ERR (inode)) {dput (upper);return ERR_CAST (inode);
fs/overlayfs/inode.c +48 -1
69 lines not on this page · raw message
fs/overlayfs/namei.c +4 -10
@@ -839,7 +839,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,struct dentry *this;unsigned int i;int err; -bool metacopy = false; +bool metacopy = false, metacopydata = false;struct ovl_lookup_data d = { .name = dentry->d_name, .is_dir = false,
@@ -958,6 +958,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,if (d.metacopy) metacopy = true; +else + metacopydata = true;/* * Do not store intermediate metacopy dentries in chain, * except top most lower metacopy dentry
@@ -1000,14 +1002,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,if (metacopy) {BUG_ON (d.is_dir); -/* -* Found a metacopy dentry but did not find corresponding -* data dentry -*/ -if (d.metacopy) { - err = -ESTALE ; - goto out_put; - } err = -EPERM ;if (!ofs->config.metacopy) {
@@ -1065,7 +1059,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,if (ctr) origin = stack[0 ].dentry; inode =ovl_get_inode (dentry->d_sb, upperdentry, origin, index, - ctr, upperredirect); + ctr, upperredirect, metacopydata); err =PTR_ERR (inode);if (IS_ERR (inode)) goto out_free_oe;
fs/overlayfs/overlayfs.h +2 -1
@@ -352,7 +352,8 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, bool is_upper);struct inode *ovl_get_inode (struct super_block *sb,struct dentry *upperdentry,struct dentry *lowerdentry,struct dentry *index, -unsigned int numlower,char *redirect); +unsigned int numlower,char *redirect, +bool metacopydata);static inline void ovl_copyattr (struct inode *from,struct inode *to) { to->i_uid = from->i_uid;
Signature · 1 line
2.13.6
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding a I_NEW inode.
Quoted 76 lines
First case is an error while second case is not error. If file has been copied up, then it does not matter if data dentry was found or not.
Redirect removal is protected using
ovl_inode->lockandovl_lookup()does not have access to that lock. So to differentiate between these two cases, take appropriate inode lock inovl_get_inode()and make sure a data dentry has been found for metacopy inode. Otherwise lookup failed and its an error.For example, say two files are hardlinked, foo.txt and bar.txt. Say foo.txt is renamed to foo-renamed.txt gets copied up metadata only. This will also put a redirect "/foo.txt" on hardlnk inode. Now assume foo-renamed.txt is opened for write and is undergoing data copy up on one cpu and bar.txt is under going
ovl_lookup()on other cpu. Data copy up path will remove REDIRECT and METACOPY xattr. It is possible that METACOPY xattr is visible toovl_lookup()but by the REDIRECT xattr was gone by the time. That means no data dentry will be found but at the same time now inode is not metacopy inode. So data dentry is not required. So this is not error case. But if inode was still metacopy but data dentry was not found this is error case. (May be due to underlying layer changed). Fix it by returning-ESTALE.If inode was found in cache, then take
ovl_inode->lockbefore checking status of inode. If inode has been allocated, then it is returned with inode lock anyway and other threads will block on that lock, so no need to takeovl_inode->lock.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/export.c | 3 ++- fs/overlayfs/inode.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++- fs/overlayfs/namei.c | 14 ++++---------- fs/overlayfs/overlayfs.h | 3 ++- 4 files changed, 56 insertions(+), 13 deletions(-)fs/overlayfs/export.c +2
-1@@ -305,7 +305,8 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,if (d_is_dir (upper ?: lower))return ERR_PTR (-EIO ); - inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL ); + inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL , + false);if (IS_ERR (inode)) {dput (upper);return ERR_CAST (inode);fs/overlayfs/inode.c +19
-0@@ -685,9 +685,42 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,return true; } +static bool ovl_verify_metacopy_data (struct super_block *sb, +struct inode *inode,bool metacopydata) +{ +struct ovl_fs *ofs = sb->s_fs_info; +struct ovl_inode *oi =OVL_I (inode); +bool metacopy = false; + +/* A metacopy data dentry was found. So no need to do further checks */ +if (metacopydata) +return true; + +/* +* Metacopy feature not enabled. No metadata data copy up should take +* place. So no further checks needed. +*/ +if (!ofs->config.metacopy) +return true; + +if (!S_ISREG (inode->i_mode))
fs/overlayfs/inode.c static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, · line 716
Quoted 10 lines
fs/overlayfs/inode.c +10
-0+return true; + +/* +* Metacopy feature is enabled and we have not found metacopy data +* dentry. Make sure this inode is not metacopy inode. +*/ +mutex_lock (&oi->lock); + metacopy = !ovl_test_flag (OVL_UPPERDATA , inode); +mutex_unlock (&oi->lock); +
FYI, the reason you need mutex_lock_interruptible is so if other CPU is busy with large copy up, the process that does lookup() can
be interrupted by user.
This is not likley to be a problem here, because race condition suggests that copy up of data is complete by now, but nevertheless.
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.
Quoted 2 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts?
Cleaning REDIRECT is not must for the functionality. It is just more about cleanup as that redirect is not needed anymore.
Quoted 3 lines
I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding a
I_NEWinode.
Hmm.., ok, that sounds reasonable too. Can look into it.
Quoted 86 lines
Quoted 86 lines
First case is an error while second case is not error. If file has been copied up, then it does not matter if data dentry was found or not.
Redirect removal is protected using
ovl_inode->lockandovl_lookup()does not have access to that lock. So to differentiate between these two cases, take appropriate inode lock inovl_get_inode()and make sure a data dentry has been found for metacopy inode. Otherwise lookup failed and its an error.For example, say two files are hardlinked, foo.txt and bar.txt. Say foo.txt is renamed to foo-renamed.txt gets copied up metadata only. This will also put a redirect "/foo.txt" on hardlnk inode. Now assume foo-renamed.txt is opened for write and is undergoing data copy up on one cpu and bar.txt is under going
ovl_lookup()on other cpu. Data copy up path will remove REDIRECT and METACOPY xattr. It is possible that METACOPY xattr is visible toovl_lookup()but by the REDIRECT xattr was gone by the time. That means no data dentry will be found but at the same time now inode is not metacopy inode. So data dentry is not required. So this is not error case. But if inode was still metacopy but data dentry was not found this is error case. (May be due to underlying layer changed). Fix it by returning-ESTALE.If inode was found in cache, then take
ovl_inode->lockbefore checking status of inode. If inode has been allocated, then it is returned with inode lock anyway and other threads will block on that lock, so no need to takeovl_inode->lock.Signed-off-by Vivek Goyal <[email protected]>--- fs/overlayfs/export.c | 3 ++- fs/overlayfs/inode.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++- fs/overlayfs/namei.c | 14 ++++---------- fs/overlayfs/overlayfs.h | 3 ++- 4 files changed, 56 insertions(+), 13 deletions(-)fs/overlayfs/export.c +2
-1@@ -305,7 +305,8 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,if (d_is_dir (upper ?: lower))return ERR_PTR (-EIO ); - inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL ); + inode =ovl_get_inode (sb,dget (upper), lower, index, !!lower,NULL , + false);if (IS_ERR (inode)) {dput (upper);return ERR_CAST (inode);fs/overlayfs/inode.c +27
-0@@ -685,9 +685,42 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,return true; } +static bool ovl_verify_metacopy_data (struct super_block *sb, +struct inode *inode,bool metacopydata) +{ +struct ovl_fs *ofs = sb->s_fs_info; +struct ovl_inode *oi =OVL_I (inode); +bool metacopy = false; + +/* A metacopy data dentry was found. So no need to do further checks */ +if (metacopydata) +return true; + +/* +* Metacopy feature not enabled. No metadata data copy up should take +* place. So no further checks needed. +*/ +if (!ofs->config.metacopy) +return true; + +if (!S_ISREG (inode->i_mode)) +return true; + +/* +* Metacopy feature is enabled and we have not found metacopy data +* dentry. Make sure this inode is not metacopy inode. +*/ +mutex_lock (&oi->lock); + metacopy = !ovl_test_flag (OVL_UPPERDATA , inode);
Quoted 8 lines
fs/overlayfs/inode.c static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, · line 716
Quoted 2 lines
fs/overlayfs/inode.c +2
-0+mutex_unlock (&oi->lock); +FYI, the reason you need
mutex_lock_interruptibleis so if other CPU is busy with large copy up, the process that doeslookup()can
be interrupted by user.
This is not likley to be a problem here, because race condition suggests that copy up of data is complete by now, but nevertheless.
Ok, good to know.
Vivek
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
In reply to Amir GoldsteinQuoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.
Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.
Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding I_NEW inode, that means we will have to always do vfs_removexattr(OVL_XATTR_REDIRECT) on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.
IOW, I think removing REDIRECT and doing appropriate locking around ovl_inode->redirect is probably better.
Thanks
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 18 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.
Quoted 9 lines
Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.
Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then call ovl_get_inode() with upper redirect and upper metacopy information.
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.
Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.
We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 3 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in ovl_get_inode() and add the unnecessary overhead (To all non-dir files).
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 49 lines · Vivek Goyal
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 1 line
Quoted 7 lines
Quoted 2 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in
ovl_get_inode()and add the unnecessary overhead (To all non-dir files).
I see.
Quoted 8 lines
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.
Quoted 7 lines
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Look. I have no objection to cleaning REDIRECT, but I am saying it is tricky, so I think it is going to cost you a few more patches and maybe a few more review cycles, so I advised against it.
But here is another idea: store the redirect string in the METACOPY xattr, this way, removal of METACOPY xattr atomically cleans up REDIRECT and in lookup, only need to check METACOPY xattr (exists but empty means no redirect).
Cheers,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 76 lines · Amir Goldstein
Quoted 49 lines · Vivek Goyal
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 1 line
Quoted 7 lines
Quoted 2 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in
ovl_get_inode()and add the unnecessary overhead (To all non-dir files).I see.
Quoted 8 lines
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.Quoted 7 lines
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Quoted 3 lines
Look. I have no objection to cleaning REDIRECT, but I am saying it is tricky, so I think it is going to cost you a few more patches and maybe a few more review cycles, so I advised against it.
Hi Amir,
Anyway, I doubt these patches are going to be merged for 4.17. So I am fine if it takes few more revisions to properly review it. Doing it properly is more important. (Despite the fact that I am little exhausted now. :-))
Quoted 5 lines
But here is another idea: store the redirect string in the METACOPY xattr, this way, removal of METACOPY xattr atomically cleans up REDIRECT and in lookup, only need to check METACOPY xattr (exists but empty means no redirect).
I don't like this much. I had thought about it but did not pursue it.
- First of all, I don't like that REDIRECT for dir and non-dir will be stored differently.
- Secondly, xattr is just one pience. We also need to protet ov_inode->redirect field and this will not solve that issue. That issue can be solved only if we provide proper locking so that readers and writers of ovl_inode->redirect don't race and run into unexpected surprises.
Given VFS locking does not protect against copy up path races with rename(), I think that right solution for this problem is to protect against this race by taking ovl_inode->lock. I think this is something future readers can understand and build more functionality on top.
If we are primarily worried about races against copy up for redirect, then we probably don't have to double lock both ovl_inodes. As you suggested, I should be able to move out set_redirect() earlier in rename path and take one lock at a time. That should simplify the locking logic a bit. How about this instead?
Vivek
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 110 lines · Vivek Goyal
Quoted 76 lines · Amir Goldstein
Quoted 49 lines · Vivek Goyal
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 1 line
Quoted 7 lines
Quoted 2 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in
ovl_get_inode()and add the unnecessary overhead (To all non-dir files).I see.
Quoted 8 lines
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.Quoted 7 lines
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Quoted 3 lines
Look. I have no objection to cleaning REDIRECT, but I am saying it is tricky, so I think it is going to cost you a few more patches and maybe a few more review cycles, so I advised against it.
Hi Amir,
Anyway, I doubt these patches are going to be merged for 4.17. So I am fine if it takes few more revisions to properly review it. Doing it properly is more important. (Despite the fact that I am little exhausted now. :-))
Quoted 5 lines
But here is another idea: store the redirect string in the METACOPY xattr, this way, removal of METACOPY xattr atomically cleans up REDIRECT and in lookup, only need to check METACOPY xattr (exists but empty means no redirect).
I don't like this much. I had thought about it but did not pursue it.
- First of all, I don't like that REDIRECT for dir and non-dir will be stored differently.
- Secondly, xattr is just one pience. We also need to protet
ov_inode->redirectfield and this will not solve that issue. That issue can be solved only if we provide proper locking so that readers and writers ofovl_inode->redirectdon't race and run into unexpected surprises.Given VFS locking does not protect against copy up path races with
rename(), I think that right solution for this problem is to protect against this race by takingovl_inode->lock. I think this is something future readers can understand and build more functionality on top.
Quoted 5 lines
If we are primarily worried about races against copy up for redirect, then we probably don't have to double lock both
ovl_inodes. As you suggested, I should be able to move outset_redirect()earlier in rename path and take one lock at a time. That should simplify the locking logic a bit. How about this instead?
If you don't like this locking ovl_inode->lock model, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 117 lines · Vivek Goyal
Quoted 110 lines · Vivek Goyal
Quoted 76 lines · Amir Goldstein
Quoted 49 lines · Vivek Goyal
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 1 line
Quoted 7 lines
Quoted 2 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in
ovl_get_inode()and add the unnecessary overhead (To all non-dir files).I see.
Quoted 8 lines
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.Quoted 7 lines
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Quoted 3 lines
Look. I have no objection to cleaning REDIRECT, but I am saying it is tricky, so I think it is going to cost you a few more patches and maybe a few more review cycles, so I advised against it.
Hi Amir,
Anyway, I doubt these patches are going to be merged for 4.17. So I am fine if it takes few more revisions to properly review it. Doing it properly is more important. (Despite the fact that I am little exhausted now. :-))
Quoted 5 lines
But here is another idea: store the redirect string in the METACOPY xattr, this way, removal of METACOPY xattr atomically cleans up REDIRECT and in lookup, only need to check METACOPY xattr (exists but empty means no redirect).
I don't like this much. I had thought about it but did not pursue it.
- First of all, I don't like that REDIRECT for dir and non-dir will be stored differently.
- Secondly, xattr is just one pience. We also need to protet
ov_inode->redirectfield and this will not solve that issue. That issue can be solved only if we provide proper locking so that readers and writers ofovl_inode->redirectdon't race and run into unexpected surprises.Given VFS locking does not protect against copy up path races with
rename(), I think that right solution for this problem is to protect against this race by takingovl_inode->lock. I think this is something future readers can understand and build more functionality on top.Quoted 5 lines
If we are primarily worried about races against copy up for redirect, then we probably don't have to double lock both
ovl_inodes. As you suggested, I should be able to move outset_redirect()earlier in rename path and take one lock at a time. That should simplify the locking logic a bit. How about this instead?
Quoted 5 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.
I think we are confusing two different things in this discussion. Locking ovl_inode->lock for changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.
I was asking about removal of REDIRECT because this patch (ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.
My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 140 lines · Amir Goldstein
Quoted 117 lines · Vivek Goyal
Quoted 110 lines · Vivek Goyal
Quoted 76 lines · Amir Goldstein
Quoted 49 lines · Vivek Goyal
Quoted 25 lines · Amir Goldstein
Quoted 24 lines · Vivek Goyal
Quoted 11 lines · Amir Goldstein
Quoted 9 lines · Vivek Goyal
If we find a upper metacopy inode, make sure we also found associated data dentry in lower. Otherwise copy up operation later will fail.
There are two cases where this can happen. First case is that somehow data file was removed from lower layer. Other case is that REDIRECT xattr was removed due to copy up of file on another cpu (when inode is shared between two dentries) and hence
ovl_lookup()could not find the correct dentry.Quoted 5 lines
Remind me again why we remove REDIRECT xattr?
Is it a must for functionality or just for being boy scouts? I would prefer to avoid having to deal with races of this sort. You can cleanup REDIRECT for non-dir that is not metacopy on lookup when finding aI_NEWinode.Ok, thinking more about it. If we were to clean REDIRECT on lookup when finding
I_NEWinode, that means we will have to always dovfs_removexattr(OVL_XATTR_REDIRECT)on all non-metacopy non-dir files. That does not sound like a very good idea. Its unnecessary overhead in lookup path.Quoted 8 lines
Quoted 3 lines
IOW, I think removing REDIRECT and doing appropriate locking around
ovl_inode->redirectis probably better.Here is what I propose.
During lookup, you anyway check REDIRECT and check METACOPY on upper and then callovl_get_inode()with upper redirect and upper metacopy information.We check for upperredirect only if metacopy xattr is found. Otherwise we skip checking for redirect.
https://github.com/rhvgoyal/linux/blob/metacopy-v13/fs/overlayfs/namei.c#L270
Quoted 3 lines
IF this is a new inode AND both REDIRECT and METACOPY were found on upper THEN it is safe to remove REDIRECT xattr.
If both METACOPY and REDIRECT were found, then we should not remove REDIRECT. That REDIRECT is still useful. REDIRECT should be removed only if METACOPY is not found and REDIRECT is found (on a non-dir file).
Quoted 1 line
Quoted 7 lines
Quoted 2 lines
Maybe I am missing something, but I don't see where the extra overhead is, beyond the overhead already there for metacopy enabled lookup.
Given we don't check for REDIRECT if upper is not METACOPY, that means we will have to always check for REDIRECT in
ovl_get_inode()and add the unnecessary overhead (To all non-dir files).I see.
Quoted 8 lines
Quoted 7 lines
OTOH, I don't see what cleaning REDIRECT gets us in the first place. During lookup, REDIRECT does not affect non metacopy non-dir, because we skip
ovl_check_redirect().
REDIRECT could actually be useful for reconstructing ORIGIN xattr and index after copying layers, so not sure its a good thing to remove it at all. After all, redirects are pretty rare as it is.Quoted 7 lines
I see it as unnecessary xattr present and feel that cleaning it is a good idea. Given we are thinking of packing REDIRECT xattr in tar file for layer backup and restore case, it makes even more sense to clean it up otherwise it shows up every where unnecessarily. I personally think it is always a good idea to cleanup information you don't need anymore, instead of letting it sit around.
Quoted 3 lines
Look. I have no objection to cleaning REDIRECT, but I am saying it is tricky, so I think it is going to cost you a few more patches and maybe a few more review cycles, so I advised against it.
Hi Amir,
Anyway, I doubt these patches are going to be merged for 4.17. So I am fine if it takes few more revisions to properly review it. Doing it properly is more important. (Despite the fact that I am little exhausted now. :-))
Quoted 5 lines
But here is another idea: store the redirect string in the METACOPY xattr, this way, removal of METACOPY xattr atomically cleans up REDIRECT and in lookup, only need to check METACOPY xattr (exists but empty means no redirect).
I don't like this much. I had thought about it but did not pursue it.
- First of all, I don't like that REDIRECT for dir and non-dir will be stored differently.
- Secondly, xattr is just one pience. We also need to protet
ov_inode->redirectfield and this will not solve that issue. That issue can be solved only if we provide proper locking so that readers and writers ofovl_inode->redirectdon't race and run into unexpected surprises.Given VFS locking does not protect against copy up path races with
rename(), I think that right solution for this problem is to protect against this race by takingovl_inode->lock. I think this is something future readers can understand and build more functionality on top.Quoted 5 lines
If we are primarily worried about races against copy up for redirect, then we probably don't have to double lock both
ovl_inodes. As you suggested, I should be able to move outset_redirect()earlier in rename path and take one lock at a time. That should simplify the locking logic a bit. How about this instead?Quoted 5 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.I think we are confusing two different things in this discussion. Locking
ovl_inode->lockfor changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.I was asking about removal of REDIRECT because this patch (
ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.
Quoted 5 lines
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Hi Amir,
Ok, let me drop removing REDIRECT from the patchset to reduce complexity. Lets first try to get in a basic version in.
Now coming to the question of locking ovl_inode->lock. If REDIRECTS are never removed and only upgraded (from relative to absolute), my understanding is that current VFS locking is sufficient to prevent races. Only rename and link path set redirects and both the paths take inode locks and that means two set redirects can't make progress in parallel on an inode.
When it comes to ovl_lookup(), we will set ovl_inode->redirect only for the case of I_NEW. So no races should be there as well.
So far we had to add locking due to copy up path and now copy up path will not touch redirect xattr. I probably will not even clear ovl_inode->inode redirect as we are not removing REDIRECT.
Do you see any other path racing and still needed ovl_inode->lock?
Thanks
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
On Thu, Apr 5, 2018 at 11:45 PM, Vivek Goyal <[email protected]> wrote:
[...]
Quoted 41 lines
Quoted 23 lines
Quoted 6 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.I think we are confusing two different things in this discussion. Locking
ovl_inode->lockfor changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.I was asking about removal of REDIRECT because this patch (
ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.Quoted 5 lines
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Hi Amir,
Ok, let me drop removing REDIRECT from the patchset to reduce complexity. Lets first try to get in a basic version in.
Now coming to the question of locking
ovl_inode->lock. If REDIRECTS are never removed and only upgraded (from relative to absolute), my understanding is that current VFS locking is sufficient to prevent races. Only rename and link path set redirects and both the paths take inode locks and that means two set redirects can't make progress in parallel on an inode.
Quoted 9 lines
When it comes to
ovl_lookup(), we will setovl_inode->redirectonly for the case ofI_NEW. So no races should be there as well.So far we had to add locking due to copy up path and now copy up path will not touch redirect xattr. I probably will not even clear
ovl_inode->inoderedirect as we are not removing REDIRECT.Do you see any other path racing and still needed
ovl_inode->lock?
Let's see.
Current code is taking A->d_lock in ovl_set_redirect(A) to protect against racing with redirect path traversal in ovl_get_redirect(A/B/c).
For the purpose of protecting ancestors redirect path, d_lock is sufficient and is needed anyway to access d_name.
Also any rename in the filesystem is *also* serialized with ovl_sb->s_vfs_rename_mutex. That would make the change I suggested to move ovl_set_redirect() before lock_rename() a bit tricky. You may say that taking d_lock in ovl_set_redirect() is not strictly needed, but I guess it is better coding (or maybe something I am missing).
Now when adding ovl_set_redirect() for non-dir and in ovl_link() the plot thickens, because:
1. link is not serialized with ovl_sb->s_vfs_rename_mutex lock, so path traversal in ovl_get_redirect() in unstable
2. 'old' dentry in ovl_link() can be disconnected, so there is no path for ovl_get_redirect() at all.
The only way to get a disconnected dentry is with nfs export, so for now, it is enough to WARN_ON and return error in ovl_set_redirect() for (dentry->d_flags & DCACHE_DISCONNECTED)
with a comment referring to nfs_export+metacopy support.
Maybe the simplest solution w.r.t stabilizing path traversal is to move ovl_set_redirect() above lock_rename() as I suggested and use a new lock ofs->ovl_rename_mutex inside ovl_get_redirect() to protect the !samedir traversal and also take that lock before lock_rename() in ovl_rename().
Back to the original question: how should ovl_inode->redirect be protected if at all it needs protection?
I think setting redirect need the protection of ofs->ovl_rename_mutex. Can either take it just around ovl_dentry_set_redirect(), probably instead of d_lock, or take the ovl_rename_mutex inside ovl_set_redirect() and around ovl_get_redirect() instead of inside ovl_get_redirect().
Hmm, I hope I got this right. I advise to get feedback from Miklos to this design before you proceed.
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 75 lines · Amir Goldstein
On Thu, Apr 5, 2018 at 11:45 PM, Vivek Goyal <[email protected]> wrote:
[...]Quoted 41 lines
Quoted 23 lines
Quoted 6 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.I think we are confusing two different things in this discussion. Locking
ovl_inode->lockfor changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.I was asking about removal of REDIRECT because this patch (
ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.Quoted 5 lines
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Hi Amir,
Ok, let me drop removing REDIRECT from the patchset to reduce complexity. Lets first try to get in a basic version in.
Now coming to the question of locking
ovl_inode->lock. If REDIRECTS are never removed and only upgraded (from relative to absolute), my understanding is that current VFS locking is sufficient to prevent races. Only rename and link path set redirects and both the paths take inode locks and that means two set redirects can't make progress in parallel on an inode.Quoted 9 lines
When it comes to
ovl_lookup(), we will setovl_inode->redirectonly for the case ofI_NEW. So no races should be there as well.So far we had to add locking due to copy up path and now copy up path will not touch redirect xattr. I probably will not even clear
ovl_inode->inoderedirect as we are not removing REDIRECT.Do you see any other path racing and still needed
ovl_inode->lock?Let's see.
Current code is taking
A->d_lockinovl_set_redirect(A)to protect against racing with redirect path traversal inovl_get_redirect(A/B/c).For the purpose of protecting ancestors redirect path,
d_lockis sufficient and is needed anyway to accessd_name.Also any rename in the filesystem is *also* serialized with
ovl_sb->s_vfs_rename_mutex. That would make the change I suggested to moveovl_set_redirect()beforelock_rename()a bit tricky. You may say that takingd_lockinovl_set_redirect()is not strictly needed, but I guess it is better coding (or maybe something I am missing).Now when adding
ovl_set_redirect()for non-dir and inovl_link()the plot thickens, because:
1. link is not serialized withovl_sb->s_vfs_rename_mutexlock, so path traversal inovl_get_redirect()in unstable
2. 'old' dentry inovl_link()can be disconnected, so there is no path forovl_get_redirect()at all.
Quoted 4 lines
The only way to get a disconnected dentry is with nfs export, so for now, it is enough to
WARN_ONand return error inovl_set_redirect()for (dentry->d_flags&DCACHE_DISCONNECTED)
with a comment referring to nfs_export+metacopy support.
Hi Amir,
I can put this warning for DCACHE_DISCONNECTED dentries.
Now, most interesting part for me here is that why do we need to stop/synchronize other renames in the system while some set_redirect/get _redirect operation is taking place. That's the part I don't understand. When I am looking at current code, I feel d_lock, seems to be good enough to make sure that ovl_get_redirect() works fine when parallel renames progressing on other cpu.
So a simple example, is say I am creating a link bar/foo-link.txt to a file foo/foo.txt and that triggers setting absolute redirect on foo.txt and we will call ovl_get_direct() and traverse the tree up till root. Now say a part of the tree is also being renamed. Say foo/ is being renamed to alpha/. I am wondering is d_lock is not enough to make sure this is not a problem.
We always set redirect first and then do rename. That means d_parent should be changed only after redirect has been set on a dentry. And that should guarantee that if ovl_get_redirect() sees new parent, then parent_dentry->redirect has been already set. If it sees dentry before rename, then redirect might be there if not, dentry name would be used and it will also see the old parent and continue traversal up.
Is there anything I am missing?
Quoted 6 lines
Maybe the simplest solution w.r.t stabilizing path traversal is to move
ovl_set_redirect()abovelock_rename()as I suggested and use a new lockofs->ovl_rename_mutexinsideovl_get_redirect()to protect the !samedir traversal and also take that lock beforelock_rename()inovl_rename().
I already made changes to move ovl_set_redirect() above lock_rename() in my copy. I still can't see the need of ofs->ovl_rename_mutex. Please help me understand the need with an example.
Quoted 3 lines
Back to the original question: how should
ovl_inode->redirectbe protected if at all it needs protection?
At this point I am thinking at max we need to just take ovl_inode->lock to protect ovl_inode->redirect. It just makes it little safer and relatively easier to understand.
Thanks
Vivek
Quoted 11 lines
I think setting redirect need the protection of
ofs->ovl_rename_mutex. Can either take it just aroundovl_dentry_set_redirect(), probably instead ofd_lock, or take theovl_rename_mutexinsideovl_set_redirect()and aroundovl_get_redirect()instead of insideovl_get_redirect().Hmm, I hope I got this right. I advise to get feedback from Miklos to this design before you proceed.
Thanks,
Amir.
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 83 lines · Vivek Goyal
Quoted 75 lines · Amir Goldstein
On Thu, Apr 5, 2018 at 11:45 PM, Vivek Goyal <[email protected]> wrote:
[...]Quoted 41 lines
Quoted 23 lines
Quoted 6 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.I think we are confusing two different things in this discussion. Locking
ovl_inode->lockfor changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.I was asking about removal of REDIRECT because this patch (
ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.Quoted 5 lines
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Hi Amir,
Ok, let me drop removing REDIRECT from the patchset to reduce complexity. Lets first try to get in a basic version in.
Now coming to the question of locking
ovl_inode->lock. If REDIRECTS are never removed and only upgraded (from relative to absolute), my understanding is that current VFS locking is sufficient to prevent races. Only rename and link path set redirects and both the paths take inode locks and that means two set redirects can't make progress in parallel on an inode.Quoted 9 lines
When it comes to
ovl_lookup(), we will setovl_inode->redirectonly for the case ofI_NEW. So no races should be there as well.So far we had to add locking due to copy up path and now copy up path will not touch redirect xattr. I probably will not even clear
ovl_inode->inoderedirect as we are not removing REDIRECT.Do you see any other path racing and still needed
ovl_inode->lock?Let's see.
Current code is taking
A->d_lockinovl_set_redirect(A)to protect against racing with redirect path traversal inovl_get_redirect(A/B/c).For the purpose of protecting ancestors redirect path,
d_lockis sufficient and is needed anyway to accessd_name.Also any rename in the filesystem is *also* serialized with
ovl_sb->s_vfs_rename_mutex. That would make the change I suggested to moveovl_set_redirect()beforelock_rename()a bit tricky. You may say that takingd_lockinovl_set_redirect()is not strictly needed, but I guess it is better coding (or maybe something I am missing).Now when adding
ovl_set_redirect()for non-dir and inovl_link()the plot thickens, because:
1. link is not serialized withovl_sb->s_vfs_rename_mutexlock, so path traversal inovl_get_redirect()in unstable
2. 'old' dentry inovl_link()can be disconnected, so there is no path forovl_get_redirect()at all.Quoted 4 lines
The only way to get a disconnected dentry is with nfs export, so for now, it is enough to
WARN_ONand return error inovl_set_redirect()for (dentry->d_flags&DCACHE_DISCONNECTED)
with a comment referring to nfs_export+metacopy support.Hi Amir,
Quoted 8 lines
I can put this warning for
DCACHE_DISCONNECTEDdentries.Now, most interesting part for me here is that why do we need to stop/synchronize other renames in the system while some set_redirect/get
_redirectoperation is taking place. That's the part I don't understand. When I am looking at current code, I feeld_lock, seems to be good enough to make sure thatovl_get_redirect()works fine when parallel renames progressing on other cpu.
Right.
Quoted 16 lines
So a simple example, is say I am creating a link
bar/foo-link.txtto a filefoo/foo.txtand that triggers setting absolute redirect on foo.txt and we will callovl_get_direct()and traverse the tree up till root. Now say a part of the tree is also being renamed. Say foo/ is being renamed to alpha/. I am wondering isd_lockis not enough to make sure this is not a problem.We always set redirect first and then do rename. That means
d_parentshould be changed only after redirect has been set on a dentry. And that should guarantee that ifovl_get_redirect()sees new parent, thenparent_dentry->redirecthas been already set. If it sees dentry before rename, then redirect might be there if not, dentry name would be used and it will also see the old parent and continue traversal up.
Quoted 1 line
Is there anything I am missing?
No. I think you got it right.
I was confused.
Quoted 8 lines
Quoted 7 lines
Maybe the simplest solution w.r.t stabilizing path traversal is to move
ovl_set_redirect()abovelock_rename()as I suggested and use a new lockofs->ovl_rename_mutexinsideovl_get_redirect()to protect the !samedir traversal and also take that lock beforelock_rename()inovl_rename().
Quoted 4 lines
I already made changes to move
ovl_set_redirect()abovelock_rename()in my copy. I still can't see the need ofofs->ovl_rename_mutex. Please help me understand the need with an example.
No need.
Quoted 8 lines
Quoted 3 lines
Back to the original question: how should
ovl_inode->redirectbe protected if at all it needs protection?At this point I am thinking at max we need to just take
ovl_inode->lockto protectovl_inode->redirect. It just makes it little safer and relatively easier to understand.
I agree. I don't think ovl_inode->lock is needed, but I would add a comment explaining why (VFS inode lock).
The problem is that we cannot get rid of d_lock for path traversal and I don't see how we can easily take both ovl dentry d_lock with ovl_inode->lock, because the latter is a sleeping lock, but layering wise it is logically below the overlay dentry lock.
So better not add ovl_inode->lock
Sorry for the noise.
I hope moving set redirect before lock_rename simplified your patches, so this whole exercise served a point.
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 140 lines · Amir Goldstein
Quoted 83 lines · Vivek Goyal
Quoted 75 lines · Amir Goldstein
On Thu, Apr 5, 2018 at 11:45 PM, Vivek Goyal <[email protected]> wrote:
[...]Quoted 41 lines
Quoted 23 lines
Quoted 6 lines
If you don't like this locking
ovl_inode->lockmodel, I guess for now I could live with not removing REDIRECT after copy up. Once that gets merged, I could do one patch series just to clean up REDIRECT after copy up and do proper locking.I think we are confusing two different things in this discussion. Locking
ovl_inode->lockfor changing redirect is something that should stay in the patch set IMO and should be simplified by moving set redirect before taking rename locks on two inodes.I was asking about removal of REDIRECT because this patch (
ovl_verify_metacopy_data) is a bit tricky for me to review and I still don't feel confident about it.My intuition says we could go other ways as well:
- unite METACOPY/REDIRECT xattr (we can call the unite xattr REDIRECT and not METACOPY)
- memory barriers between setting/clearing/checking METACOPY/REDIRECT (there is already a barrier for setting upperdata flag, so that's half the work already.Quoted 5 lines
We can either have this discussion now or, as you suggested leave it to a following patch set. Rule of thumb - if this is v13 with 28 patches, might not be a bad idea to deffer 2 patches and reduce complexity...
Hi Amir,
Ok, let me drop removing REDIRECT from the patchset to reduce complexity. Lets first try to get in a basic version in.
Now coming to the question of locking
ovl_inode->lock. If REDIRECTS are never removed and only upgraded (from relative to absolute), my understanding is that current VFS locking is sufficient to prevent races. Only rename and link path set redirects and both the paths take inode locks and that means two set redirects can't make progress in parallel on an inode.Quoted 9 lines
When it comes to
ovl_lookup(), we will setovl_inode->redirectonly for the case ofI_NEW. So no races should be there as well.So far we had to add locking due to copy up path and now copy up path will not touch redirect xattr. I probably will not even clear
ovl_inode->inoderedirect as we are not removing REDIRECT.Do you see any other path racing and still needed
ovl_inode->lock?Let's see.
Current code is taking
A->d_lockinovl_set_redirect(A)to protect against racing with redirect path traversal inovl_get_redirect(A/B/c).For the purpose of protecting ancestors redirect path,
d_lockis sufficient and is needed anyway to accessd_name.Also any rename in the filesystem is *also* serialized with
ovl_sb->s_vfs_rename_mutex. That would make the change I suggested to moveovl_set_redirect()beforelock_rename()a bit tricky. You may say that takingd_lockinovl_set_redirect()is not strictly needed, but I guess it is better coding (or maybe something I am missing).Now when adding
ovl_set_redirect()for non-dir and inovl_link()the plot thickens, because:
1. link is not serialized withovl_sb->s_vfs_rename_mutexlock, so path traversal inovl_get_redirect()in unstable
2. 'old' dentry inovl_link()can be disconnected, so there is no path forovl_get_redirect()at all.Quoted 4 lines
The only way to get a disconnected dentry is with nfs export, so for now, it is enough to
WARN_ONand return error inovl_set_redirect()for (dentry->d_flags&DCACHE_DISCONNECTED)
with a comment referring to nfs_export+metacopy support.Hi Amir,
Quoted 8 lines
I can put this warning for
DCACHE_DISCONNECTEDdentries.Now, most interesting part for me here is that why do we need to stop/synchronize other renames in the system while some set_redirect/get
_redirectoperation is taking place. That's the part I don't understand. When I am looking at current code, I feeld_lock, seems to be good enough to make sure thatovl_get_redirect()works fine when parallel renames progressing on other cpu.Right.
Quoted 16 lines
So a simple example, is say I am creating a link
bar/foo-link.txtto a filefoo/foo.txtand that triggers setting absolute redirect on foo.txt and we will callovl_get_direct()and traverse the tree up till root. Now say a part of the tree is also being renamed. Say foo/ is being renamed to alpha/. I am wondering isd_lockis not enough to make sure this is not a problem.We always set redirect first and then do rename. That means
d_parentshould be changed only after redirect has been set on a dentry. And that should guarantee that ifovl_get_redirect()sees new parent, thenparent_dentry->redirecthas been already set. If it sees dentry before rename, then redirect might be there if not, dentry name would be used and it will also see the old parent and continue traversal up.Quoted 1 line
Is there anything I am missing?
No. I think you got it right.
I was confused.Quoted 8 lines
Quoted 7 lines
Maybe the simplest solution w.r.t stabilizing path traversal is to move
ovl_set_redirect()abovelock_rename()as I suggested and use a new lockofs->ovl_rename_mutexinsideovl_get_redirect()to protect the !samedir traversal and also take that lock beforelock_rename()inovl_rename().Quoted 4 lines
I already made changes to move
ovl_set_redirect()abovelock_rename()in my copy. I still can't see the need ofofs->ovl_rename_mutex. Please help me understand the need with an example.No need.
Quoted 8 lines
Quoted 3 lines
Back to the original question: how should
ovl_inode->redirectbe protected if at all it needs protection?At this point I am thinking at max we need to just take
ovl_inode->lockto protectovl_inode->redirect. It just makes it little safer and relatively easier to understand.
Quoted 7 lines
I agree. I don't think
ovl_inode->lockis needed, but I would add a comment explaining why (VFS inode lock).The problem is that we cannot get rid of
d_lockfor path traversal and I don't see how we can easily take both ovl dentryd_lockwithovl_inode->lock, because the latter is a sleeping lock, but layering wise it is logically below the overlay dentry lock.
Actually, I think ovl_inode->lock is at same layer as inode->i_rwsem. So VFS first takes i_rwsem and then d_lock. And we probably should do the same thing. In fact we are already taking ovl_inode->lock in ovl_nlink_start() first and then calling ovl_set_redirect() which takes d_lock.
Quoted 3 lines
So better not add
ovl_inode->lock
Sorry for the noise.
I will not add ovl_inode->lock if I can explain everything. I have a feeling that redirect upgrade path will have some funny interactions with ovl_lookup() path. Let me write the new patches and see if ovl_inode->lock is still required.
Quoted 3 lines
I hope moving set redirect before
lock_renamesimplified your patches, so this whole exercise served a point.
It definitely does. Thanks for that idea.
Vivek
Amir Goldstein
maintainerRe: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
On Fri, Apr 6, 2018 at 8:32 PM, Vivek Goyal <[email protected]> wrote:
[...]
Quoted 17 lines
Quoted 9 lines
Quoted 8 lines
Quoted 3 lines
Back to the original question: how should
ovl_inode->redirectbe protected if at all it needs protection?At this point I am thinking at max we need to just take
ovl_inode->lockto protectovl_inode->redirect. It just makes it little safer and relatively easier to understand.Quoted 7 lines
I agree. I don't think
ovl_inode->lockis needed, but I would add a comment explaining why (VFS inode lock).The problem is that we cannot get rid of
d_lockfor path traversal and I don't see how we can easily take both ovl dentryd_lockwithovl_inode->lock, because the latter is a sleeping lock, but layering wise it is logically below the overlay dentry lock.
Quoted 6 lines
Actually, I think
ovl_inode->lockis at same layer asinode->i_rwsem. So VFS first takesi_rwsemand thend_lock. And we probably should do the same thing. In fact we are already takingovl_inode->lockinovl_nlink_start()first and then callingovl_set_redirect()which takesd_lock.
It's not completely ok that we do that.
If we can always abide to the rules of locking order:
VFS overlay layer locks -> internal overlay locks -> VFS underlying fs locks
We will be safer.
So if ovl_set_redirect() can happen without taking ovl_inode->lock and completely before underlying fs VFS locks I think that would be better.
Quoted 9 lines
Quoted 3 lines
So better not add
ovl_inode->lock
Sorry for the noise.I will not add
ovl_inode->lockif I can explain everything. I have a feeling that redirect upgrade path will have some funny interactions withovl_lookup()path. Let me write the new patches and see ifovl_inode->lockis still required.
OK.
Thanks,
Amir.
Re: [PATCH v13 27/28] ovl: Verify a data dentry has been found for metacopy inode
Quoted 26 lines · Amir Goldstein
On Fri, Apr 6, 2018 at 8:32 PM, Vivek Goyal <[email protected]> wrote:
[...]Quoted 17 lines
Quoted 9 lines
Quoted 8 lines
Quoted 3 lines
Back to the original question: how should
ovl_inode->redirectbe protected if at all it needs protection?At this point I am thinking at max we need to just take
ovl_inode->lockto protectovl_inode->redirect. It just makes it little safer and relatively easier to understand.Quoted 7 lines
I agree. I don't think
ovl_inode->lockis needed, but I would add a comment explaining why (VFS inode lock).The problem is that we cannot get rid of
d_lockfor path traversal and I don't see how we can easily take both ovl dentryd_lockwithovl_inode->lock, because the latter is a sleeping lock, but layering wise it is logically below the overlay dentry lock.Quoted 6 lines
Actually, I think
ovl_inode->lockis at same layer asinode->i_rwsem. So VFS first takesi_rwsemand thend_lock. And we probably should do the same thing. In fact we are already takingovl_inode->lockinovl_nlink_start()first and then callingovl_set_redirect()which takesd_lock.
Quoted 4 lines
It's not completely ok that we do that.
If we can always abide to the rules of locking order:
VFS overlay layer locks -> internal overlay locks -> VFS underlying fs locks
Sure, and that's what will happen when we move ovl_set_redirect() above lock_rename().
- When ovl_rename() is called, VFS is holding ovl layer locks.
- ovl_nlink_start() and ovl_set_redrect() with take internal overlay lock (ovl_inode->lock)
- And then lock_rename() will take VFS underlying fs lock.
So ordering of locks will be exactly as you like it.
Vivek
PATCH v13 28/28 ovl: Enable metadata only feature
In reply to Vivek GoyalAll the bits are in patches before this. So it is time to enable the metadata only copy up feature.
--- fs/overlayfs/copy_up.c | 3 --- 1 file changed, 3 deletions(-)
fs/overlayfs/copy_up.c +0 -3
@@ -247,9 +247,6 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode, {struct ovl_fs *ofs = dentry->d_sb->s_fs_info; -/* TODO: Will enable metacopy in last patch of series */ -return false; -if (!ofs->config.metacopy)return false;
Signature · 1 line
2.13.6