aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVivek Goyal <vgoyal@redhat.com>2018-05-11 11:49:32 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-07-20 03:56:16 -0400
commit0a2d0d3f2f291e3080721888a986ea52e43e1086 (patch)
tree5095284d9d7579f2c39b4c70e4e6ae4d7850c74d
parent4120fe64dce4f73d1a595253568d9f27674f2071 (diff)
ovl: Check redirect on index as well
Right now we seem to check redirect only if upperdentry is found. But it is possible that there is no upperdentry but later we found an index. We need to check redirect on index as well and set it in ovl_inode->redirect. Otherwise link code can assume that dentry does not have redirect and place a new one which breaks things. In my testing overlay/033 test started failing in xfstests. Following are the details. For example do following. $ mkdir lower upper work merged - Make lower dir with 4 links. $ echo "foo" > lower/l0.txt $ ln lower/l0.txt lower/l1.txt $ ln lower/l0.txt lower/l2.txt $ ln lower/l0.txt lower/l3.txt - Mount with index on and metacopy on. $ mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work,\ index=on,metacopy=on none merged - Link lower $ ln merged/l0.txt merged/l4.txt (This will metadata copy up of l0.txt and put an absolute redirect /l0.txt) $ echo 2 > /proc/sys/vm/drop/caches $ ls merged/l1.txt (Now l1.txt will be looked up. There is no upper dentry but there is lower dentry and index will be found. We don't check for redirect on index, hence ovl_inode->redirect will be NULL.) - Link Upper $ ln merged/l4.txt merged/l5.txt (Lookup of l4.txt will use inode from l1.txt lookup which is still in cache. It has ovl_inode->redirect NULL, hence link will put a new redirect and replace /l0.txt with /l4.txt - Drop caches. echo 2 > /proc/sys/vm/drop_caches - List l1.txt and it returns -ESTALE $ ls merged/l0.txt (It returns stale because, we found a metacopy of l0.txt in upper and it has redirect l4.txt but there is no file named l4.txt in lower layer. So lower data copy is not found and -ESTALE is returned.) So problem here is that we did not process redirect on index. Check redirect on index as well and then problem is fixed. Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/namei.c50
-rw-r--r--fs/overlayfs/overlayfs.h1
-rw-r--r--fs/overlayfs/util.c50
3 files changed, 64 insertions, 37 deletions
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index eddb80dd0766..f28711846dd6 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -31,32 +31,13 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
31 size_t prelen, const char *post) 31 size_t prelen, const char *post)
32{ 32{
33 int res; 33 int res;
34 char *s, *next, *buf = NULL; 34 char *buf;
35 35
36 res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0); 36 buf = ovl_get_redirect_xattr(dentry, prelen + strlen(post));
37 if (res < 0) { 37 if (IS_ERR_OR_NULL(buf))
38 if (res == -ENODATA || res == -EOPNOTSUPP) 38 return PTR_ERR(buf);
39 return 0;
40 goto fail;
41 }
42 buf = kzalloc(prelen + res + strlen(post) + 1, GFP_KERNEL);
43 if (!buf)
44 return -ENOMEM;
45 39
46 if (res == 0)
47 goto invalid;
48
49 res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
50 if (res < 0)
51 goto fail;
52 if (res == 0)
53 goto invalid;
54 if (buf[0] == '/') { 40 if (buf[0] == '/') {
55 for (s = buf; *s++ == '/'; s = next) {
56 next = strchrnul(s, '/');
57 if (s == next)
58 goto invalid;
59 }
60 /* 41 /*
61 * One of the ancestor path elements in an absolute path 42 * One of the ancestor path elements in an absolute path
62 * lookup in ovl_lookup_layer() could have been opaque and 43 * lookup in ovl_lookup_layer() could have been opaque and
@@ -67,9 +48,7 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
67 */ 48 */
68 d->stop = false; 49 d->stop = false;
69 } else { 50 } else {
70 if (strchr(buf, '/') != NULL) 51 res = strlen(buf) + 1;
71 goto invalid;
72
73 memmove(buf + prelen, buf, res); 52 memmove(buf + prelen, buf, res);
74 memcpy(buf, d->name.name, prelen); 53 memcpy(buf, d->name.name, prelen);
75 } 54 }
@@ -81,16 +60,6 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
81 d->name.len = strlen(d->redirect); 60 d->name.len = strlen(d->redirect);
82 61
83 return 0; 62 return 0;
84
85err_free:
86 kfree(buf);
87 return 0;
88fail:
89 pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
90 goto err_free;
91invalid:
92 pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
93 goto err_free;
94} 63}
95 64
96static int ovl_acceptable(void *ctx, struct dentry *dentry) 65static int ovl_acceptable(void *ctx, struct dentry *dentry)
@@ -1071,8 +1040,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
1071 1040
1072 if (upperdentry) 1041 if (upperdentry)
1073 ovl_dentry_set_upper_alias(dentry); 1042 ovl_dentry_set_upper_alias(dentry);
1074 else if (index) 1043 else if (index) {
1075 upperdentry = dget(index); 1044 upperdentry = dget(index);
1045 upperredirect = ovl_get_redirect_xattr(upperdentry, 0);
1046 if (IS_ERR(upperredirect)) {
1047 err = PTR_ERR(upperredirect);
1048 upperredirect = NULL;
1049 goto out_free_oe;
1050 }
1051 }
1076 1052
1077 if (upperdentry || ctr) { 1053 if (upperdentry || ctr) {
1078 struct ovl_inode_params oip = { 1054 struct ovl_inode_params oip = {
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index a6b466b30f2b..c85aa438cc8f 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -276,6 +276,7 @@ void ovl_nlink_end(struct dentry *dentry, bool locked);
276int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); 276int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
277int ovl_check_metacopy_xattr(struct dentry *dentry); 277int ovl_check_metacopy_xattr(struct dentry *dentry);
278bool ovl_is_metacopy_dentry(struct dentry *dentry); 278bool ovl_is_metacopy_dentry(struct dentry *dentry);
279char *ovl_get_redirect_xattr(struct dentry *dentry, int padding);
279 280
280static inline bool ovl_is_impuredir(struct dentry *dentry) 281static inline bool ovl_is_impuredir(struct dentry *dentry)
281{ 282{
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 1aa9e0c5a327..8cfb62cc8672 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -865,3 +865,53 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
865 865
866 return (oe->numlower > 1); 866 return (oe->numlower > 1);
867} 867}
868
869char *ovl_get_redirect_xattr(struct dentry *dentry, int padding)
870{
871 int res;
872 char *s, *next, *buf = NULL;
873
874 res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0);
875 if (res < 0) {
876 if (res == -ENODATA || res == -EOPNOTSUPP)
877 return NULL;
878 goto fail;
879 }
880
881 buf = kzalloc(res + padding + 1, GFP_KERNEL);
882 if (!buf)
883 return ERR_PTR(-ENOMEM);
884
885 if (res == 0)
886 goto invalid;
887
888 res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
889 if (res < 0)
890 goto fail;
891 if (res == 0)
892 goto invalid;
893
894 if (buf[0] == '/') {
895 for (s = buf; *s++ == '/'; s = next) {
896 next = strchrnul(s, '/');
897 if (s == next)
898 goto invalid;
899 }
900 } else {
901 if (strchr(buf, '/') != NULL)
902 goto invalid;
903 }
904
905 return buf;
906
907err_free:
908 kfree(buf);
909 return ERR_PTR(res);
910fail:
911 pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
912 goto err_free;
913invalid:
914 pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
915 res = -EINVAL;
916 goto err_free;
917}