Skip to content

Commit bc8df7a

Browse files
alexlarssonamir73il
authored andcommitted
ovl: Add an alternative type of whiteout
An xattr whiteout (called "xwhiteout" in the code) is a reguar file of zero size with the "overlay.whiteout" xattr set. A file like this in a directory with the "overlay.whiteouts" xattrs set will be treated the same way as a regular whiteout. The "overlay.whiteouts" directory xattr is used in order to efficiently handle overlay checks in readdir(), as we only need to checks xattrs in affected directories. The advantage of this kind of whiteout is that they can be escaped using the standard overlay xattr escaping mechanism. So, a file with a "overlay.overlay.whiteout" xattr would be unescaped to "overlay.whiteout", 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. To work as a whiteout for both regular overlayfs mounts as well as userxattr mounts both the "user.overlay.whiteout*" and the "trusted.overlay.whiteout*" xattrs will need to be created. Signed-off-by: Alexander Larsson <[email protected]> Reviewed-by: Amir Goldstein <[email protected]> Signed-off-by: Amir Goldstein <[email protected]>
1 parent dad02fa commit bc8df7a

File tree

6 files changed

+88
-15
lines changed

6 files changed

+88
-15
lines changed

fs/overlayfs/dir.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
477477
goto out_unlock;
478478

479479
err = -ESTALE;
480-
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
480+
if (d_is_negative(upper) || !ovl_upper_is_whiteout(ofs, upper))
481481
goto out_dput;
482482

483483
newdentry = ovl_create_temp(ofs, workdir, cattr);
@@ -1211,7 +1211,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
12111211
}
12121212
} else {
12131213
if (!d_is_negative(newdentry)) {
1214-
if (!new_opaque || !ovl_is_whiteout(newdentry))
1214+
if (!new_opaque || !ovl_upper_is_whiteout(ofs, newdentry))
12151215
goto out_dput;
12161216
} else {
12171217
if (flags & RENAME_EXCHANGE)

fs/overlayfs/namei.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
251251
err = -EREMOTE;
252252
goto out_err;
253253
}
254-
if (ovl_is_whiteout(this)) {
254+
255+
path.dentry = this;
256+
path.mnt = d->mnt;
257+
if (ovl_path_is_whiteout(OVL_FS(d->sb), &path)) {
255258
d->stop = d->opaque = true;
256259
goto put_and_out;
257260
}
@@ -264,8 +267,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
264267
goto put_and_out;
265268
}
266269

267-
path.dentry = this;
268-
path.mnt = d->mnt;
269270
if (!d_can_lookup(this)) {
270271
if (d->is_dir || !last_element) {
271272
d->stop = true;
@@ -438,7 +439,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
438439
else if (IS_ERR(origin))
439440
return PTR_ERR(origin);
440441

441-
if (upperdentry && !ovl_is_whiteout(upperdentry) &&
442+
if (upperdentry && !ovl_upper_is_whiteout(ofs, upperdentry) &&
442443
inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
443444
goto invalid;
444445

@@ -1402,7 +1403,11 @@ bool ovl_lower_positive(struct dentry *dentry)
14021403
break;
14031404
}
14041405
} else {
1405-
positive = !ovl_is_whiteout(this);
1406+
struct path path = {
1407+
.dentry = this,
1408+
.mnt = parentpath->layer->mnt,
1409+
};
1410+
positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path);
14061411
done = true;
14071412
dput(this);
14081413
}

fs/overlayfs/overlayfs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ enum ovl_xattr {
4949
OVL_XATTR_UUID,
5050
OVL_XATTR_METACOPY,
5151
OVL_XATTR_PROTATTR,
52+
OVL_XATTR_XWHITEOUT,
53+
OVL_XATTR_XWHITEOUTS,
5254
};
5355

5456
enum ovl_inode_flag {
@@ -473,16 +475,29 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
473475
void ovl_dir_modified(struct dentry *dentry, bool impurity);
474476
u64 ovl_inode_version_get(struct inode *inode);
475477
bool ovl_is_whiteout(struct dentry *dentry);
478+
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path);
476479
struct file *ovl_path_open(const struct path *path, int flags);
477480
int ovl_copy_up_start(struct dentry *dentry, int flags);
478481
void ovl_copy_up_end(struct dentry *dentry);
479482
bool ovl_already_copied_up(struct dentry *dentry, int flags);
480483
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
481484
enum ovl_xattr ox);
482485
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
486+
bool ovl_path_check_xwhiteout_xattr(struct ovl_fs *ofs, const struct path *path);
487+
bool ovl_path_check_xwhiteouts_xattr(struct ovl_fs *ofs, const struct path *path);
483488
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
484489
const struct path *upperpath);
485490

491+
static inline bool ovl_upper_is_whiteout(struct ovl_fs *ofs,
492+
struct dentry *upperdentry)
493+
{
494+
struct path upperpath = {
495+
.dentry = upperdentry,
496+
.mnt = ovl_upper_mnt(ofs),
497+
};
498+
return ovl_path_is_whiteout(ofs, &upperpath);
499+
}
500+
486501
static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
487502
struct dentry *upperdentry)
488503
{

fs/overlayfs/readdir.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct ovl_cache_entry {
2525
struct ovl_cache_entry *next_maybe_whiteout;
2626
bool is_upper;
2727
bool is_whiteout;
28+
bool check_xwhiteout;
2829
char name[];
2930
};
3031

@@ -47,6 +48,7 @@ struct ovl_readdir_data {
4748
int err;
4849
bool is_upper;
4950
bool d_type_supported;
51+
bool in_xwhiteouts_dir;
5052
};
5153

5254
struct ovl_dir_file {
@@ -162,6 +164,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
162164
p->ino = 0;
163165
p->is_upper = rdd->is_upper;
164166
p->is_whiteout = false;
167+
/* Defer check for overlay.whiteout to ovl_iterate() */
168+
p->check_xwhiteout = rdd->in_xwhiteouts_dir && d_type == DT_REG;
165169

166170
if (d_type == DT_CHR) {
167171
p->next_maybe_whiteout = rdd->first_maybe_whiteout;
@@ -301,6 +305,8 @@ static inline int ovl_dir_read(const struct path *realpath,
301305
if (IS_ERR(realfile))
302306
return PTR_ERR(realfile);
303307

308+
rdd->in_xwhiteouts_dir = rdd->dentry &&
309+
ovl_path_check_xwhiteouts_xattr(OVL_FS(rdd->dentry->d_sb), realpath);
304310
rdd->first_maybe_whiteout = NULL;
305311
rdd->ctx.pos = 0;
306312
do {
@@ -447,16 +453,19 @@ static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid,
447453
}
448454

449455
/*
450-
* Set d_ino for upper entries. Non-upper entries should always report
456+
* Set d_ino for upper entries if needed. Non-upper entries should always report
451457
* the uppermost real inode ino and should not call this function.
452458
*
453459
* When not all layer are on same fs, report real ino also for upper.
454460
*
455461
* When all layers are on the same fs, and upper has a reference to
456462
* copy up origin, call vfs_getattr() on the overlay entry to make
457463
* sure that d_ino will be consistent with st_ino from stat(2).
464+
*
465+
* Also checks the overlay.whiteout xattr by doing a full lookup which will return
466+
* negative in this case.
458467
*/
459-
static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry *p)
468+
static int ovl_cache_update(const struct path *path, struct ovl_cache_entry *p, bool update_ino)
460469

461470
{
462471
struct dentry *dir = path->dentry;
@@ -467,7 +476,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
467476
int xinobits = ovl_xino_bits(ofs);
468477
int err = 0;
469478

470-
if (!ovl_same_dev(ofs))
479+
if (!ovl_same_dev(ofs) && !p->check_xwhiteout)
471480
goto out;
472481

473482
if (p->name[0] == '.') {
@@ -481,6 +490,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
481490
goto get;
482491
}
483492
}
493+
/* This checks also for xwhiteouts */
484494
this = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
485495
if (IS_ERR_OR_NULL(this) || !this->d_inode) {
486496
/* Mark a stale entry */
@@ -494,6 +504,9 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
494504
}
495505

496506
get:
507+
if (!ovl_same_dev(ofs) || !update_ino)
508+
goto out;
509+
497510
type = ovl_path_type(this);
498511
if (OVL_TYPE_ORIGIN(type)) {
499512
struct kstat stat;
@@ -572,7 +585,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
572585
list_for_each_entry_safe(p, n, list, l_node) {
573586
if (strcmp(p->name, ".") != 0 &&
574587
strcmp(p->name, "..") != 0) {
575-
err = ovl_cache_update_ino(path, p);
588+
err = ovl_cache_update(path, p, true);
576589
if (err)
577590
return err;
578591
}
@@ -778,13 +791,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
778791
while (od->cursor != &od->cache->entries) {
779792
p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
780793
if (!p->is_whiteout) {
781-
if (!p->ino) {
782-
err = ovl_cache_update_ino(&file->f_path, p);
794+
if (!p->ino || p->check_xwhiteout) {
795+
err = ovl_cache_update(&file->f_path, p, !p->ino);
783796
if (err)
784797
goto out;
785798
}
786799
}
787-
/* ovl_cache_update_ino() sets is_whiteout on stale entry */
800+
/* ovl_cache_update() sets is_whiteout on stale entry */
788801
if (!p->is_whiteout) {
789802
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
790803
break;

fs/overlayfs/super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
585585
if (IS_ERR(whiteout))
586586
goto cleanup_temp;
587587

588-
err = ovl_is_whiteout(whiteout);
588+
err = ovl_upper_is_whiteout(ofs, whiteout);
589589

590590
/* Best effort cleanup of whiteout and temp file */
591591
if (err)

fs/overlayfs/util.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,16 @@ bool ovl_is_whiteout(struct dentry *dentry)
601601
return inode && IS_WHITEOUT(inode);
602602
}
603603

604+
/*
605+
* Use this over ovl_is_whiteout for upper and lower files, as it also
606+
* handles overlay.whiteout xattr whiteout files.
607+
*/
608+
bool ovl_path_is_whiteout(struct ovl_fs *ofs, const struct path *path)
609+
{
610+
return ovl_is_whiteout(path->dentry) ||
611+
ovl_path_check_xwhiteout_xattr(ofs, path);
612+
}
613+
604614
struct file *ovl_path_open(const struct path *path, int flags)
605615
{
606616
struct inode *inode = d_inode(path->dentry);
@@ -716,6 +726,32 @@ bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path)
716726
return false;
717727
}
718728

729+
bool ovl_path_check_xwhiteout_xattr(struct ovl_fs *ofs, const struct path *path)
730+
{
731+
struct dentry *dentry = path->dentry;
732+
int res;
733+
734+
/* xattr.whiteout must be a zero size regular file */
735+
if (!d_is_reg(dentry) || i_size_read(d_inode(dentry)) != 0)
736+
return false;
737+
738+
res = ovl_path_getxattr(ofs, path, OVL_XATTR_XWHITEOUT, NULL, 0);
739+
return res >= 0;
740+
}
741+
742+
bool ovl_path_check_xwhiteouts_xattr(struct ovl_fs *ofs, const struct path *path)
743+
{
744+
struct dentry *dentry = path->dentry;
745+
int res;
746+
747+
/* xattr.whiteouts must be a directory */
748+
if (!d_is_dir(dentry))
749+
return false;
750+
751+
res = ovl_path_getxattr(ofs, path, OVL_XATTR_XWHITEOUTS, NULL, 0);
752+
return res >= 0;
753+
}
754+
719755
/*
720756
* Load persistent uuid from xattr into s_uuid if found, or store a new
721757
* random generated value in s_uuid and in xattr.
@@ -800,6 +836,8 @@ bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
800836
#define OVL_XATTR_UUID_POSTFIX "uuid"
801837
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
802838
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
839+
#define OVL_XATTR_XWHITEOUT_POSTFIX "whiteout"
840+
#define OVL_XATTR_XWHITEOUTS_POSTFIX "whiteouts"
803841

804842
#define OVL_XATTR_TAB_ENTRY(x) \
805843
[x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
@@ -815,6 +853,8 @@ const char *const ovl_xattr_table[][2] = {
815853
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID),
816854
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
817855
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
856+
OVL_XATTR_TAB_ENTRY(OVL_XATTR_XWHITEOUT),
857+
OVL_XATTR_TAB_ENTRY(OVL_XATTR_XWHITEOUTS),
818858
};
819859

820860
int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,

0 commit comments

Comments
 (0)