diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2016-12-16 05:02:56 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-12-16 05:02:56 -0500 |
commit | e28edc46b8e29d2a4c10263cd7769e657582fff4 (patch) | |
tree | 1daec9814456bc7f3842d68201c27c2f593b256b | |
parent | 48fab5d7c750ff70aa77c36a44c01211020bbc98 (diff) |
ovl: consolidate lookup for underlying layers
Use a common helper for lookup of upper and lower layers. This paves the
way for looking up directory redirects.
No functional change.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | fs/overlayfs/namei.c | 156 |
1 files changed, 86 insertions, 70 deletions
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 2e0b84c68ef6..f213297d187e 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c | |||
@@ -13,26 +13,13 @@ | |||
13 | #include "overlayfs.h" | 13 | #include "overlayfs.h" |
14 | #include "ovl_entry.h" | 14 | #include "ovl_entry.h" |
15 | 15 | ||
16 | static struct dentry *ovl_lookup_real(struct dentry *dir, | 16 | struct ovl_lookup_data { |
17 | const struct qstr *name) | 17 | struct qstr name; |
18 | { | 18 | bool is_dir; |
19 | struct dentry *dentry; | 19 | bool opaque; |
20 | 20 | bool stop; | |
21 | dentry = lookup_one_len_unlocked(name->name, dir, name->len); | 21 | bool last; |
22 | if (IS_ERR(dentry)) { | 22 | }; |
23 | if (PTR_ERR(dentry) == -ENOENT || | ||
24 | PTR_ERR(dentry) == -ENAMETOOLONG) | ||
25 | dentry = NULL; | ||
26 | } else if (!dentry->d_inode) { | ||
27 | dput(dentry); | ||
28 | dentry = NULL; | ||
29 | } else if (ovl_dentry_weird(dentry)) { | ||
30 | dput(dentry); | ||
31 | /* Don't support traversing automounts and other weirdness */ | ||
32 | dentry = ERR_PTR(-EREMOTE); | ||
33 | } | ||
34 | return dentry; | ||
35 | } | ||
36 | 23 | ||
37 | static bool ovl_is_opaquedir(struct dentry *dentry) | 24 | static bool ovl_is_opaquedir(struct dentry *dentry) |
38 | { | 25 | { |
@@ -49,6 +36,64 @@ static bool ovl_is_opaquedir(struct dentry *dentry) | |||
49 | return false; | 36 | return false; |
50 | } | 37 | } |
51 | 38 | ||
39 | static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, | ||
40 | const char *name, unsigned int namelen, | ||
41 | struct dentry **ret) | ||
42 | { | ||
43 | struct dentry *this; | ||
44 | int err; | ||
45 | |||
46 | this = lookup_one_len_unlocked(name, base, namelen); | ||
47 | if (IS_ERR(this)) { | ||
48 | err = PTR_ERR(this); | ||
49 | this = NULL; | ||
50 | if (err == -ENOENT || err == -ENAMETOOLONG) | ||
51 | goto out; | ||
52 | goto out_err; | ||
53 | } | ||
54 | if (!this->d_inode) | ||
55 | goto put_and_out; | ||
56 | |||
57 | if (ovl_dentry_weird(this)) { | ||
58 | /* Don't support traversing automounts and other weirdness */ | ||
59 | err = -EREMOTE; | ||
60 | goto out_err; | ||
61 | } | ||
62 | if (ovl_is_whiteout(this)) { | ||
63 | d->stop = d->opaque = true; | ||
64 | goto put_and_out; | ||
65 | } | ||
66 | if (!d_can_lookup(this)) { | ||
67 | d->stop = true; | ||
68 | if (d->is_dir) | ||
69 | goto put_and_out; | ||
70 | goto out; | ||
71 | } | ||
72 | d->is_dir = true; | ||
73 | if (!d->last && ovl_is_opaquedir(this)) { | ||
74 | d->stop = d->opaque = true; | ||
75 | goto out; | ||
76 | } | ||
77 | out: | ||
78 | *ret = this; | ||
79 | return 0; | ||
80 | |||
81 | put_and_out: | ||
82 | dput(this); | ||
83 | this = NULL; | ||
84 | goto out; | ||
85 | |||
86 | out_err: | ||
87 | dput(this); | ||
88 | return err; | ||
89 | } | ||
90 | |||
91 | static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, | ||
92 | struct dentry **ret) | ||
93 | { | ||
94 | return ovl_lookup_single(base, d, d->name.name, d->name.len, ret); | ||
95 | } | ||
96 | |||
52 | /* | 97 | /* |
53 | * Returns next layer in stack starting from top. | 98 | * Returns next layer in stack starting from top. |
54 | * Returns -1 if this is the last layer. | 99 | * Returns -1 if this is the last layer. |
@@ -82,11 +127,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
82 | unsigned int ctr = 0; | 127 | unsigned int ctr = 0; |
83 | struct inode *inode = NULL; | 128 | struct inode *inode = NULL; |
84 | bool upperopaque = false; | 129 | bool upperopaque = false; |
85 | bool stop = false; | ||
86 | bool isdir = false; | ||
87 | struct dentry *this; | 130 | struct dentry *this; |
88 | unsigned int i; | 131 | unsigned int i; |
89 | int err; | 132 | int err; |
133 | struct ovl_lookup_data d = { | ||
134 | .name = dentry->d_name, | ||
135 | .is_dir = false, | ||
136 | .opaque = false, | ||
137 | .stop = false, | ||
138 | .last = !poe->numlower, | ||
139 | }; | ||
90 | 140 | ||
91 | if (dentry->d_name.len > ofs->namelen) | 141 | if (dentry->d_name.len > ofs->namelen) |
92 | return ERR_PTR(-ENAMETOOLONG); | 142 | return ERR_PTR(-ENAMETOOLONG); |
@@ -94,70 +144,36 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
94 | old_cred = ovl_override_creds(dentry->d_sb); | 144 | old_cred = ovl_override_creds(dentry->d_sb); |
95 | upperdir = ovl_upperdentry_dereference(poe); | 145 | upperdir = ovl_upperdentry_dereference(poe); |
96 | if (upperdir) { | 146 | if (upperdir) { |
97 | this = ovl_lookup_real(upperdir, &dentry->d_name); | 147 | err = ovl_lookup_layer(upperdir, &d, &upperdentry); |
98 | err = PTR_ERR(this); | 148 | if (err) |
99 | if (IS_ERR(this)) | ||
100 | goto out; | 149 | goto out; |
101 | 150 | ||
102 | if (this) { | 151 | if (upperdentry && unlikely(ovl_dentry_remote(upperdentry))) { |
103 | if (unlikely(ovl_dentry_remote(this))) { | 152 | dput(upperdentry); |
104 | dput(this); | 153 | err = -EREMOTE; |
105 | err = -EREMOTE; | 154 | goto out; |
106 | goto out; | ||
107 | } | ||
108 | if (ovl_is_whiteout(this)) { | ||
109 | dput(this); | ||
110 | this = NULL; | ||
111 | stop = upperopaque = true; | ||
112 | } else if (!d_is_dir(this)) { | ||
113 | stop = true; | ||
114 | } else { | ||
115 | isdir = true; | ||
116 | if (poe->numlower && ovl_is_opaquedir(this)) | ||
117 | stop = upperopaque = true; | ||
118 | } | ||
119 | } | 155 | } |
120 | upperdentry = this; | 156 | upperopaque = d.opaque; |
121 | } | 157 | } |
122 | 158 | ||
123 | if (!stop && poe->numlower) { | 159 | if (!d.stop && poe->numlower) { |
124 | err = -ENOMEM; | 160 | err = -ENOMEM; |
125 | stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL); | 161 | stack = kcalloc(poe->numlower, sizeof(struct path), |
162 | GFP_TEMPORARY); | ||
126 | if (!stack) | 163 | if (!stack) |
127 | goto out_put_upper; | 164 | goto out_put_upper; |
128 | } | 165 | } |
129 | 166 | ||
130 | for (i = 0; !stop && i < poe->numlower; i++) { | 167 | for (i = 0; !d.stop && i < poe->numlower; i++) { |
131 | struct path lowerpath = poe->lowerstack[i]; | 168 | struct path lowerpath = poe->lowerstack[i]; |
132 | 169 | ||
133 | this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name); | 170 | d.last = i == poe->numlower - 1; |
134 | err = PTR_ERR(this); | 171 | err = ovl_lookup_layer(lowerpath.dentry, &d, &this); |
135 | if (IS_ERR(this)) | 172 | if (err) |
136 | goto out_put; | 173 | goto out_put; |
137 | 174 | ||
138 | if (!this) | 175 | if (!this) |
139 | continue; | 176 | continue; |
140 | if (ovl_is_whiteout(this)) { | ||
141 | dput(this); | ||
142 | break; | ||
143 | } | ||
144 | /* | ||
145 | * If this is a non-directory then stop here. | ||
146 | */ | ||
147 | if (!d_is_dir(this)) { | ||
148 | if (isdir) { | ||
149 | dput(this); | ||
150 | break; | ||
151 | } | ||
152 | stop = true; | ||
153 | } else { | ||
154 | /* | ||
155 | * Only makes sense to check opaque dir if this is not | ||
156 | * the lowermost layer. | ||
157 | */ | ||
158 | if (i < poe->numlower - 1 && ovl_is_opaquedir(this)) | ||
159 | stop = true; | ||
160 | } | ||
161 | 177 | ||
162 | stack[ctr].dentry = this; | 178 | stack[ctr].dentry = this; |
163 | stack[ctr].mnt = lowerpath.mnt; | 179 | stack[ctr].mnt = lowerpath.mnt; |