aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-11-20 10:40:00 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2014-11-20 10:40:00 -0500
commit521484639ec19a6f1ed56de6993feb255f5f676c (patch)
tree53cad82bc4ae05b1d2d21a22a2ccde9658e5c8c7
parenta105d685a8483985a01776411de191a726b48132 (diff)
ovl: fix race in private xattr checks
Xattr operations can race with copy up. This does not matter as long as we consistently fiter out "trunsted.overlay.opaque" attribute on upper directories. Previously we checked parent against OVL_PATH_MERGE. This is too general, and prone to race with copy-up. I.e. we found the parent to be on the lower layer but ovl_dentry_real() would return the copied-up dentry, possibly with the "opaque" attribute. So instead use ovl_path_real() and decide to filter the attributes based on the actual type of the dentry we'll use. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r--fs/overlayfs/inode.c27
1 files changed, 18 insertions, 9 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index af2d18c9fcee..07d74b24913b 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -235,26 +235,36 @@ out:
235 return err; 235 return err;
236} 236}
237 237
238static bool ovl_need_xattr_filter(struct dentry *dentry,
239 enum ovl_path_type type)
240{
241 return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
242}
243
238ssize_t ovl_getxattr(struct dentry *dentry, const char *name, 244ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
239 void *value, size_t size) 245 void *value, size_t size)
240{ 246{
241 if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && 247 struct path realpath;
242 ovl_is_private_xattr(name)) 248 enum ovl_path_type type = ovl_path_real(dentry, &realpath);
249
250 if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
243 return -ENODATA; 251 return -ENODATA;
244 252
245 return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); 253 return vfs_getxattr(realpath.dentry, name, value, size);
246} 254}
247 255
248ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) 256ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
249{ 257{
258 struct path realpath;
259 enum ovl_path_type type = ovl_path_real(dentry, &realpath);
250 ssize_t res; 260 ssize_t res;
251 int off; 261 int off;
252 262
253 res = vfs_listxattr(ovl_dentry_real(dentry), list, size); 263 res = vfs_listxattr(realpath.dentry, list, size);
254 if (res <= 0 || size == 0) 264 if (res <= 0 || size == 0)
255 return res; 265 return res;
256 266
257 if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) 267 if (!ovl_need_xattr_filter(dentry, type))
258 return res; 268 return res;
259 269
260 /* filter out private xattrs */ 270 /* filter out private xattrs */
@@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
279{ 289{
280 int err; 290 int err;
281 struct path realpath; 291 struct path realpath;
282 enum ovl_path_type type; 292 enum ovl_path_type type = ovl_path_real(dentry, &realpath);
283 293
284 err = ovl_want_write(dentry); 294 err = ovl_want_write(dentry);
285 if (err) 295 if (err)
286 goto out; 296 goto out;
287 297
288 if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && 298 err = -ENODATA;
289 ovl_is_private_xattr(name)) 299 if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
290 goto out_drop_write; 300 goto out_drop_write;
291 301
292 type = ovl_path_real(dentry, &realpath);
293 if (type == OVL_PATH_LOWER) { 302 if (type == OVL_PATH_LOWER) {
294 err = vfs_getxattr(realpath.dentry, name, NULL, 0); 303 err = vfs_getxattr(realpath.dentry, name, NULL, 0);
295 if (err < 0) 304 if (err < 0)