Skip to content

Commit

Permalink
ovl: Add an alternative type of whiteout
Browse files Browse the repository at this point in the history
If a lowerdir contains a fifo with the overlay.whiteout xattr then it
is considered as a whiteout just like a traditional char(0,0)
whiteout.

The advantage of this kind of whiteout is that they can be escaped
using the standard overlay xattrs escaping mechanism. So, a fifo with
a overlay.overlay.whiteout xattr would be unescaped to an
overlay.whiteout fifo, which could then be consumed by another
overlayfs as a whiteout.

Overlayfs itself doesn't create whiteouts like this, but a userspace
mechanism could use this alternative mechanism to convert images
that may contain whiteouts to be used with overlayfs.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
  • Loading branch information
alexlarsson committed Aug 23, 2023
1 parent c303fe8 commit 8392cac
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 10 deletions.
4 changes: 2 additions & 2 deletions fs/overlayfs/dir.c
Expand Up @@ -477,7 +477,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
goto out_unlock;

err = -ESTALE;
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
if (d_is_negative(upper) || !ovl_upper_is_whiteout(ofs, upper))
goto out_dput;

newdentry = ovl_create_temp(ofs, workdir, cattr);
Expand Down Expand Up @@ -1219,7 +1219,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
}
} else {
if (!d_is_negative(newdentry)) {
if (!new_opaque || !ovl_is_whiteout(newdentry))
if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry))
goto out_dput;
} else {
if (flags & RENAME_EXCHANGE)
Expand Down
14 changes: 9 additions & 5 deletions fs/overlayfs/namei.c
Expand Up @@ -251,7 +251,9 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
err = -EREMOTE;
goto out_err;
}
if (ovl_is_whiteout(this)) {
path.dentry = this;
path.mnt = d->mnt;
if (ovl_path_is_whiteout(OVL_FS(d->sb), &path)) {
d->stop = d->opaque = true;
goto put_and_out;
}
Expand All @@ -264,8 +266,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
goto put_and_out;
}

path.dentry = this;
path.mnt = d->mnt;
if (!d_can_lookup(this)) {
if (d->is_dir || !last_element) {
d->stop = true;
Expand Down Expand Up @@ -438,7 +438,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
else if (IS_ERR(origin))
return PTR_ERR(origin);

if (upperdentry && !ovl_is_whiteout(upperdentry) &&
if (upperdentry && !ovl_upper_is_whiteout(ofs, upperdentry) &&
inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
goto invalid;

Expand Down Expand Up @@ -1383,7 +1383,11 @@ bool ovl_lower_positive(struct dentry *dentry)
break;
}
} else {
positive = !ovl_is_whiteout(this);
struct path path = {
.dentry = this,
.mnt = parentpath->layer->mnt,
};
positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path);
done = true;
dput(this);
}
Expand Down
13 changes: 13 additions & 0 deletions fs/overlayfs/overlayfs.h
Expand Up @@ -49,6 +49,7 @@ enum ovl_xattr {
OVL_XATTR_UUID,
OVL_XATTR_METACOPY,
OVL_XATTR_PROTATTR,
OVL_XATTR_WHITEOUT,
};

enum ovl_inode_flag {
Expand Down Expand Up @@ -469,16 +470,28 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
void ovl_dir_modified(struct dentry *dentry, bool impurity);
u64 ovl_inode_version_get(struct inode *inode);
bool ovl_is_whiteout(struct dentry *dentry);
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path);
struct file *ovl_path_open(const struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry);
bool ovl_already_copied_up(struct dentry *dentry, int flags);
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
enum ovl_xattr ox);
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
bool ovl_path_check_whiteout_xattr(struct ovl_fs *ofs, const struct path *path);
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
const struct path *upperpath);

static inline bool ovl_upper_is_whiteout(struct ovl_fs *ofs,
struct dentry *upperdentry)
{
struct path upperpath = {
.dentry = upperdentry,
.mnt = ovl_upper_mnt(ofs),
};
return ovl_path_is_whiteout(ofs, &upperpath);
}

static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
struct dentry *upperdentry)
{
Expand Down
9 changes: 7 additions & 2 deletions fs/overlayfs/readdir.c
Expand Up @@ -163,7 +163,7 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
p->is_upper = rdd->is_upper;
p->is_whiteout = false;

if (d_type == DT_CHR) {
if (d_type == DT_CHR || d_type == DT_FIFO) {
p->next_maybe_whiteout = rdd->first_maybe_whiteout;
rdd->first_maybe_whiteout = p;
}
Expand Down Expand Up @@ -280,7 +280,12 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
rdd->first_maybe_whiteout = p->next_maybe_whiteout;
dentry = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
if (!IS_ERR(dentry)) {
p->is_whiteout = ovl_is_whiteout(dentry);
struct path childpath = {
.dentry = dentry,
.mnt = path->mnt,
};
p->is_whiteout = ovl_path_is_whiteout(OVL_FS(rdd->dentry->d_sb),
&childpath);
dput(dentry);
}
}
Expand Down
2 changes: 1 addition & 1 deletion fs/overlayfs/super.c
Expand Up @@ -579,7 +579,7 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
if (IS_ERR(whiteout))
goto cleanup_temp;

err = ovl_is_whiteout(whiteout);
err = ovl_upper_is_whiteout(ofs, whiteout);

/* Best effort cleanup of whiteout and temp file */
if (err)
Expand Down
25 changes: 25 additions & 0 deletions fs/overlayfs/util.c
Expand Up @@ -430,6 +430,16 @@ bool ovl_dentry_is_whiteout(struct dentry *dentry)
return !dentry->d_inode && ovl_dentry_is_opaque(dentry);
}

/*
* Use this over ovl_is_whiteout for upper and lower files, as it also
* handles OVL_XATTR_WHITEOUT based whiteout files.
*/
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path)
{
return ovl_is_whiteout(path->dentry) ||
ovl_path_check_whiteout_xattr(ofs, path);
}

void ovl_dentry_set_opaque(struct dentry *dentry)
{
ovl_dentry_set_flag(OVL_E_OPAQUE, dentry);
Expand Down Expand Up @@ -676,6 +686,19 @@ bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path)
return false;
}

bool ovl_path_check_whiteout_xattr(struct ovl_fs *ofs, const struct path *path)
{
struct dentry *dentry = path->dentry;
int res;

/* xattr based whiteouts must be fifo */
if (!d_is_special(dentry))
return false;

res = ovl_path_getxattr(ofs, path, OVL_XATTR_WHITEOUT, NULL, 0);
return res >= 0;
}

/*
* Load persistent uuid from xattr into s_uuid if found, or store a new
* random generated value in s_uuid and in xattr.
Expand Down Expand Up @@ -760,6 +783,7 @@ bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
#define OVL_XATTR_UUID_POSTFIX "uuid"
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
#define OVL_XATTR_WHITEOUT_POSTFIX "whiteout"

#define OVL_XATTR_TAB_ENTRY(x) \
[x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
Expand All @@ -775,6 +799,7 @@ const char *const ovl_xattr_table[][2] = {
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
OVL_XATTR_TAB_ENTRY(OVL_XATTR_WHITEOUT),
};

int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
Expand Down

0 comments on commit 8392cac

Please sign in to comment.