Skip to content

Commit

Permalink
ovl: whiteout index when union nlink drops to zero
Browse files Browse the repository at this point in the history
With NFS export feature enabled, when overlay inode nlink drops to
zero, instead of removing the index entry, replace it with a whiteout
index entry.

This is needed for NFS export in order to prevent future open by handle
from opening the lower file directly.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
  • Loading branch information
Amir Goldstein authored and Miklos Szeredi committed Jan 24, 2018
1 parent 89a1755 commit e7dd0e7
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 29 deletions.
58 changes: 35 additions & 23 deletions fs/overlayfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
}

/* caller holds i_mutex on workdir */
static struct dentry *ovl_whiteout(struct dentry *workdir,
struct dentry *dentry)
static struct dentry *ovl_whiteout(struct dentry *workdir)
{
int err;
struct dentry *whiteout;
Expand All @@ -83,6 +82,38 @@ static struct dentry *ovl_whiteout(struct dentry *workdir,
return whiteout;
}

/* Caller must hold i_mutex on both workdir and dir */
int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
struct dentry *dentry)
{
struct inode *wdir = workdir->d_inode;
struct dentry *whiteout;
int err;
int flags = 0;

whiteout = ovl_whiteout(workdir);
err = PTR_ERR(whiteout);
if (IS_ERR(whiteout))
return err;

if (d_is_dir(dentry))
flags = RENAME_EXCHANGE;

err = ovl_do_rename(wdir, whiteout, dir, dentry, flags);
if (err)
goto kill_whiteout;
if (flags)
ovl_cleanup(wdir, dentry);

out:
dput(whiteout);
return err;

kill_whiteout:
ovl_cleanup(wdir, whiteout);
goto out;
}

int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr, struct dentry *hardlink, bool debug)
{
Expand Down Expand Up @@ -591,14 +622,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
struct list_head *list)
{
struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode;
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *udir = upperdir->d_inode;
struct dentry *whiteout;
struct dentry *upper;
struct dentry *opaquedir = NULL;
int err;
int flags = 0;

if (WARN_ON(!workdir))
return -EROFS;
Expand Down Expand Up @@ -627,24 +654,13 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
goto out_dput_upper;
}

whiteout = ovl_whiteout(workdir, dentry);
err = PTR_ERR(whiteout);
if (IS_ERR(whiteout))
goto out_dput_upper;

if (d_is_dir(upper))
flags = RENAME_EXCHANGE;

err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
err = ovl_cleanup_and_whiteout(workdir, d_inode(upperdir), upper);
if (err)
goto kill_whiteout;
if (flags)
ovl_cleanup(wdir, upper);
goto out_d_drop;

ovl_dentry_version_inc(dentry->d_parent, true);
out_d_drop:
d_drop(dentry);
dput(whiteout);
out_dput_upper:
dput(upper);
out_unlock:
Expand All @@ -653,10 +669,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
dput(opaquedir);
out:
return err;

kill_whiteout:
ovl_cleanup(wdir, whiteout);
goto out_d_drop;
}

static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
Expand Down
2 changes: 2 additions & 0 deletions fs/overlayfs/overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
/* dir.c */
extern const struct inode_operations ovl_dir_inode_operations;
struct dentry *ovl_lookup_temp(struct dentry *workdir);
int ovl_cleanup_and_whiteout(struct dentry *workdir, struct inode *dir,
struct dentry *dentry);
struct cattr {
dev_t rdev;
umode_t mode;
Expand Down
17 changes: 11 additions & 6 deletions fs/overlayfs/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,8 @@ bool ovl_need_index(struct dentry *dentry)
/* Caller must hold OVL_I(inode)->lock */
static void ovl_cleanup_index(struct dentry *dentry)
{
struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode;
struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
struct inode *dir = indexdir->d_inode;
struct dentry *lowerdentry = ovl_dentry_lower(dentry);
struct dentry *upperdentry = ovl_dentry_upper(dentry);
struct dentry *index = NULL;
Expand Down Expand Up @@ -518,13 +519,17 @@ static void ovl_cleanup_index(struct dentry *dentry)
}

inode_lock_nested(dir, I_MUTEX_PARENT);
/* TODO: whiteout instead of cleanup to block future open by handle */
index = lookup_one_len(name.name, ovl_indexdir(dentry->d_sb), name.len);
index = lookup_one_len(name.name, indexdir, name.len);
err = PTR_ERR(index);
if (!IS_ERR(index))
err = ovl_cleanup(dir, index);
else
if (IS_ERR(index)) {
index = NULL;
} else if (ovl_index_all(dentry->d_sb)) {
/* Whiteout orphan index to block future open by handle */
err = ovl_cleanup_and_whiteout(indexdir, dir, index);
} else {
/* Cleanup orphan index entries */
err = ovl_cleanup(dir, index);
}

inode_unlock(dir);
if (err)
Expand Down

0 comments on commit e7dd0e7

Please sign in to comment.