Skip to content

Commit b79e05a

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: no direct iteration for dir with origin xattr
If a non-merge dir in an overlay mount has an overlay.origin xattr, it means it was once an upper merge dir, which may contain whiteouts and then the lower dir was removed under it. Do not iterate real dir directly in this case to avoid exposing whiteouts. [SzM] Set OVL_WHITEOUT for all merge directories as well. [amir] A directory that was just copied up does not have the OVL_WHITEOUTS flag. We need to set it to fix merge dir iteration. Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 4eae06d commit b79e05a

File tree

6 files changed

+55
-5
lines changed

6 files changed

+55
-5
lines changed

fs/overlayfs/copy_up.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
486486
static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
487487
{
488488
struct inode *udir = c->destdir->d_inode;
489+
struct inode *inode;
489490
struct dentry *newdentry = NULL;
490491
struct dentry *temp = NULL;
491492
int err;
@@ -508,7 +509,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
508509
if (err)
509510
goto out_cleanup;
510511

511-
ovl_inode_update(d_inode(c->dentry), newdentry);
512+
inode = d_inode(c->dentry);
513+
ovl_inode_update(inode, newdentry);
514+
if (S_ISDIR(inode->i_mode))
515+
ovl_set_flag(OVL_WHITEOUTS, inode);
516+
512517
out:
513518
dput(temp);
514519
return err;

fs/overlayfs/inode.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,16 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
661661
if (upperdentry && ovl_is_impuredir(upperdentry))
662662
ovl_set_flag(OVL_IMPURE, inode);
663663

664+
/* Check for non-merge dir that may have whiteouts */
665+
if (S_ISDIR(realinode->i_mode)) {
666+
struct ovl_entry *oe = dentry->d_fsdata;
667+
668+
if (((upperdentry && lowerdentry) || oe->numlower > 1) ||
669+
ovl_check_origin_xattr(upperdentry ?: lowerdentry)) {
670+
ovl_set_flag(OVL_WHITEOUTS, inode);
671+
}
672+
}
673+
664674
if (inode->i_state & I_NEW)
665675
unlock_new_inode(inode);
666676
out:

fs/overlayfs/overlayfs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ enum ovl_path_type {
2828
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
2929

3030
enum ovl_flag {
31+
/* Pure upper dir that may contain non pure upper entries */
3132
OVL_IMPURE,
33+
/* Non-merge dir that may contain whiteout entries */
34+
OVL_WHITEOUTS,
3235
OVL_INDEX,
3336
};
3437

@@ -223,6 +226,7 @@ bool ovl_is_whiteout(struct dentry *dentry);
223226
struct file *ovl_path_open(struct path *path, int flags);
224227
int ovl_copy_up_start(struct dentry *dentry);
225228
void ovl_copy_up_end(struct dentry *dentry);
229+
bool ovl_check_origin_xattr(struct dentry *dentry);
226230
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name);
227231
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
228232
const char *name, const void *value, size_t size,

fs/overlayfs/readdir.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,21 +316,37 @@ static inline int ovl_dir_read(struct path *realpath,
316316
return err;
317317
}
318318

319+
/*
320+
* Can we iterate real dir directly?
321+
*
322+
* Non-merge dir may contain whiteouts from a time it was a merge upper, before
323+
* lower dir was removed under it and possibly before it was rotated from upper
324+
* to lower layer.
325+
*/
326+
static bool ovl_dir_is_real(struct dentry *dir)
327+
{
328+
return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir));
329+
}
330+
319331
static void ovl_dir_reset(struct file *file)
320332
{
321333
struct ovl_dir_file *od = file->private_data;
322334
struct ovl_dir_cache *cache = od->cache;
323335
struct dentry *dentry = file->f_path.dentry;
324-
enum ovl_path_type type = ovl_path_type(dentry);
336+
bool is_real;
325337

326338
if (cache && ovl_dentry_version_get(dentry) != cache->version) {
327339
ovl_cache_put(od, dentry);
328340
od->cache = NULL;
329341
od->cursor = NULL;
330342
}
331-
WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type));
332-
if (od->is_real && OVL_TYPE_MERGE(type))
343+
is_real = ovl_dir_is_real(dentry);
344+
if (od->is_real != is_real) {
345+
/* is_real can only become false when dir is copied up */
346+
if (WARN_ON(is_real))
347+
return;
333348
od->is_real = false;
349+
}
334350
}
335351

336352
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
@@ -816,7 +832,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
816832
return PTR_ERR(realfile);
817833
}
818834
od->realfile = realfile;
819-
od->is_real = !OVL_TYPE_MERGE(type);
835+
od->is_real = ovl_dir_is_real(file->f_path.dentry);
820836
od->is_upper = OVL_TYPE_UPPER(type);
821837
file->private_data = od;
822838

fs/overlayfs/super.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
11411141

11421142
root_dentry->d_fsdata = oe;
11431143

1144+
/* Root is always merge -> can have whiteouts */
1145+
ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
11441146
ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
11451147
ovl_dentry_lower(root_dentry));
11461148

fs/overlayfs/util.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,19 @@ void ovl_copy_up_end(struct dentry *dentry)
329329
mutex_unlock(&OVL_I(d_inode(dentry))->lock);
330330
}
331331

332+
bool ovl_check_origin_xattr(struct dentry *dentry)
333+
{
334+
int res;
335+
336+
res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
337+
338+
/* Zero size value means "copied up but origin unknown" */
339+
if (res >= 0)
340+
return true;
341+
342+
return false;
343+
}
344+
332345
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
333346
{
334347
int res;

0 commit comments

Comments
 (0)