Skip to content

Commit bdc0102

Browse files
committed
Merge tag 'ovl-fixes-6.8-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs
Pull overlayfs fix from Amir Goldstein: "Change the on-disk format for the new "xwhiteouts" feature introduced in v6.7 The change reduces unneeded overhead of an extra getxattr per readdir. The only user of the "xwhiteout" feature is the external composefs tool, which has been updated to support the new on-disk format. This change is also designated for 6.7.y" * tag 'ovl-fixes-6.8-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs: ovl: mark xwhiteouts directory with overlay.opaque='x'
2 parents a658e0e + 420332b commit bdc0102

File tree

7 files changed

+110
-51
lines changed

7 files changed

+110
-51
lines changed

Documentation/filesystems/overlayfs.rst

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ filesystem, an overlay filesystem needs to record in the upper filesystem
145145
that files have been removed. This is done using whiteouts and opaque
146146
directories (non-directories are always opaque).
147147

148-
A whiteout is created as a character device with 0/0 device number.
148+
A whiteout is created as a character device with 0/0 device number or
149+
as a zero-size regular file with the xattr "trusted.overlay.whiteout".
150+
149151
When a whiteout is found in the upper level of a merged directory, any
150152
matching name in the lower level is ignored, and the whiteout itself
151153
is also hidden.
@@ -154,6 +156,13 @@ A directory is made opaque by setting the xattr "trusted.overlay.opaque"
154156
to "y". Where the upper filesystem contains an opaque directory, any
155157
directory in the lower filesystem with the same name is ignored.
156158

159+
An opaque directory should not conntain any whiteouts, because they do not
160+
serve any purpose. A merge directory containing regular files with the xattr
161+
"trusted.overlay.whiteout", should be additionally marked by setting the xattr
162+
"trusted.overlay.opaque" to "x" on the merge directory itself.
163+
This is needed to avoid the overhead of checking the "trusted.overlay.whiteout"
164+
on all entries during readdir in the common case.
165+
157166
readdir
158167
-------
159168

@@ -534,8 +543,9 @@ A lower dir with a regular whiteout will always be handled by the overlayfs
534543
mount, so to support storing an effective whiteout file in an overlayfs mount an
535544
alternative form of whiteout is supported. This form is a regular, zero-size
536545
file with the "overlay.whiteout" xattr set, inside a directory with the
537-
"overlay.whiteouts" xattr set. Such whiteouts are never created by overlayfs,
538-
but can be used by userspace tools (like containers) that generate lower layers.
546+
"overlay.opaque" xattr set to "x" (see `whiteouts and opaque directories`_).
547+
These alternative whiteouts are never created by overlayfs, but can be used by
548+
userspace tools (like containers) that generate lower layers.
539549
These alternative whiteouts can be escaped using the standard xattr escape
540550
mechanism in order to properly nest to any depth.
541551

fs/overlayfs/namei.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818

1919
struct ovl_lookup_data {
2020
struct super_block *sb;
21-
struct vfsmount *mnt;
21+
const struct ovl_layer *layer;
2222
struct qstr name;
2323
bool is_dir;
2424
bool opaque;
25+
bool xwhiteouts;
2526
bool stop;
2627
bool last;
2728
char *redirect;
@@ -201,17 +202,13 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
201202
return real;
202203
}
203204

204-
static bool ovl_is_opaquedir(struct ovl_fs *ofs, const struct path *path)
205-
{
206-
return ovl_path_check_dir_xattr(ofs, path, OVL_XATTR_OPAQUE);
207-
}
208-
209205
static struct dentry *ovl_lookup_positive_unlocked(struct ovl_lookup_data *d,
210206
const char *name,
211207
struct dentry *base, int len,
212208
bool drop_negative)
213209
{
214-
struct dentry *ret = lookup_one_unlocked(mnt_idmap(d->mnt), name, base, len);
210+
struct dentry *ret = lookup_one_unlocked(mnt_idmap(d->layer->mnt), name,
211+
base, len);
215212

216213
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
217214
if (drop_negative && ret->d_lockref.count == 1) {
@@ -232,10 +229,13 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
232229
size_t prelen, const char *post,
233230
struct dentry **ret, bool drop_negative)
234231
{
232+
struct ovl_fs *ofs = OVL_FS(d->sb);
235233
struct dentry *this;
236234
struct path path;
237235
int err;
238236
bool last_element = !post[0];
237+
bool is_upper = d->layer->idx == 0;
238+
char val;
239239

240240
this = ovl_lookup_positive_unlocked(d, name, base, namelen, drop_negative);
241241
if (IS_ERR(this)) {
@@ -253,8 +253,8 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
253253
}
254254

255255
path.dentry = this;
256-
path.mnt = d->mnt;
257-
if (ovl_path_is_whiteout(OVL_FS(d->sb), &path)) {
256+
path.mnt = d->layer->mnt;
257+
if (ovl_path_is_whiteout(ofs, &path)) {
258258
d->stop = d->opaque = true;
259259
goto put_and_out;
260260
}
@@ -272,7 +272,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
272272
d->stop = true;
273273
goto put_and_out;
274274
}
275-
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path, NULL);
275+
err = ovl_check_metacopy_xattr(ofs, &path, NULL);
276276
if (err < 0)
277277
goto out_err;
278278

@@ -292,7 +292,12 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
292292
if (d->last)
293293
goto out;
294294

295-
if (ovl_is_opaquedir(OVL_FS(d->sb), &path)) {
295+
/* overlay.opaque=x means xwhiteouts directory */
296+
val = ovl_get_opaquedir_val(ofs, &path);
297+
if (last_element && !is_upper && val == 'x') {
298+
d->xwhiteouts = true;
299+
ovl_layer_set_xwhiteouts(ofs, d->layer);
300+
} else if (val == 'y') {
296301
d->stop = true;
297302
if (last_element)
298303
d->opaque = true;
@@ -863,21 +868,25 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
863868
* Returns next layer in stack starting from top.
864869
* Returns -1 if this is the last layer.
865870
*/
866-
int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
871+
int ovl_path_next(int idx, struct dentry *dentry, struct path *path,
872+
const struct ovl_layer **layer)
867873
{
868874
struct ovl_entry *oe = OVL_E(dentry);
869875
struct ovl_path *lowerstack = ovl_lowerstack(oe);
870876

871877
BUG_ON(idx < 0);
872878
if (idx == 0) {
873879
ovl_path_upper(dentry, path);
874-
if (path->dentry)
880+
if (path->dentry) {
881+
*layer = &OVL_FS(dentry->d_sb)->layers[0];
875882
return ovl_numlower(oe) ? 1 : -1;
883+
}
876884
idx++;
877885
}
878886
BUG_ON(idx > ovl_numlower(oe));
879887
path->dentry = lowerstack[idx - 1].dentry;
880-
path->mnt = lowerstack[idx - 1].layer->mnt;
888+
*layer = lowerstack[idx - 1].layer;
889+
path->mnt = (*layer)->mnt;
881890

882891
return (idx < ovl_numlower(oe)) ? idx + 1 : -1;
883892
}
@@ -1055,7 +1064,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10551064
old_cred = ovl_override_creds(dentry->d_sb);
10561065
upperdir = ovl_dentry_upper(dentry->d_parent);
10571066
if (upperdir) {
1058-
d.mnt = ovl_upper_mnt(ofs);
1067+
d.layer = &ofs->layers[0];
10591068
err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
10601069
if (err)
10611070
goto out;
@@ -1111,7 +1120,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
11111120
else if (d.is_dir || !ofs->numdatalayer)
11121121
d.last = lower.layer->idx == ovl_numlower(roe);
11131122

1114-
d.mnt = lower.layer->mnt;
1123+
d.layer = lower.layer;
11151124
err = ovl_lookup_layer(lower.dentry, &d, &this, false);
11161125
if (err)
11171126
goto out_put;
@@ -1278,6 +1287,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
12781287

12791288
if (upperopaque)
12801289
ovl_dentry_set_opaque(dentry);
1290+
if (d.xwhiteouts)
1291+
ovl_dentry_set_xwhiteouts(dentry);
12811292

12821293
if (upperdentry)
12831294
ovl_dentry_set_upper_alias(dentry);

fs/overlayfs/overlayfs.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ enum ovl_xattr {
5050
OVL_XATTR_METACOPY,
5151
OVL_XATTR_PROTATTR,
5252
OVL_XATTR_XWHITEOUT,
53-
OVL_XATTR_XWHITEOUTS,
5453
};
5554

5655
enum ovl_inode_flag {
@@ -70,6 +69,8 @@ enum ovl_entry_flag {
7069
OVL_E_UPPER_ALIAS,
7170
OVL_E_OPAQUE,
7271
OVL_E_CONNECTED,
72+
/* Lower stack may contain xwhiteout entries */
73+
OVL_E_XWHITEOUTS,
7374
};
7475

7576
enum {
@@ -477,6 +478,10 @@ bool ovl_dentry_test_flag(unsigned long flag, struct dentry *dentry);
477478
bool ovl_dentry_is_opaque(struct dentry *dentry);
478479
bool ovl_dentry_is_whiteout(struct dentry *dentry);
479480
void ovl_dentry_set_opaque(struct dentry *dentry);
481+
bool ovl_dentry_has_xwhiteouts(struct dentry *dentry);
482+
void ovl_dentry_set_xwhiteouts(struct dentry *dentry);
483+
void ovl_layer_set_xwhiteouts(struct ovl_fs *ofs,
484+
const struct ovl_layer *layer);
480485
bool ovl_dentry_has_upper_alias(struct dentry *dentry);
481486
void ovl_dentry_set_upper_alias(struct dentry *dentry);
482487
bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags);
@@ -494,11 +499,10 @@ struct file *ovl_path_open(const struct path *path, int flags);
494499
int ovl_copy_up_start(struct dentry *dentry, int flags);
495500
void ovl_copy_up_end(struct dentry *dentry);
496501
bool ovl_already_copied_up(struct dentry *dentry, int flags);
497-
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
498-
enum ovl_xattr ox);
502+
char ovl_get_dir_xattr_val(struct ovl_fs *ofs, const struct path *path,
503+
enum ovl_xattr ox);
499504
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
500505
bool ovl_path_check_xwhiteout_xattr(struct ovl_fs *ofs, const struct path *path);
501-
bool ovl_path_check_xwhiteouts_xattr(struct ovl_fs *ofs, const struct path *path);
502506
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
503507
const struct path *upperpath);
504508

@@ -573,7 +577,13 @@ static inline bool ovl_is_impuredir(struct super_block *sb,
573577
.mnt = ovl_upper_mnt(ofs),
574578
};
575579

576-
return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE);
580+
return ovl_get_dir_xattr_val(ofs, &upperpath, OVL_XATTR_IMPURE) == 'y';
581+
}
582+
583+
static inline char ovl_get_opaquedir_val(struct ovl_fs *ofs,
584+
const struct path *path)
585+
{
586+
return ovl_get_dir_xattr_val(ofs, path, OVL_XATTR_OPAQUE);
577587
}
578588

579589
static inline bool ovl_redirect_follow(struct ovl_fs *ofs)
@@ -680,7 +690,8 @@ int ovl_get_index_name(struct ovl_fs *ofs, struct dentry *origin,
680690
struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
681691
struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
682692
struct dentry *origin, bool verify);
683-
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
693+
int ovl_path_next(int idx, struct dentry *dentry, struct path *path,
694+
const struct ovl_layer **layer);
684695
int ovl_verify_lowerdata(struct dentry *dentry);
685696
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
686697
unsigned int flags);

fs/overlayfs/ovl_entry.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ struct ovl_layer {
4040
int idx;
4141
/* One fsid per unique underlying sb (upper fsid == 0) */
4242
int fsid;
43+
/* xwhiteouts were found on this layer */
44+
bool has_xwhiteouts;
4345
};
4446

4547
struct ovl_path {
@@ -59,7 +61,7 @@ struct ovl_fs {
5961
unsigned int numfs;
6062
/* Number of data-only lower layers */
6163
unsigned int numdatalayer;
62-
const struct ovl_layer *layers;
64+
struct ovl_layer *layers;
6365
struct ovl_sb *fs;
6466
/* workbasedir is the path at workdir= mount option */
6567
struct dentry *workbasedir;

fs/overlayfs/readdir.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,6 @@ static inline int ovl_dir_read(const struct path *realpath,
305305
if (IS_ERR(realfile))
306306
return PTR_ERR(realfile);
307307

308-
rdd->in_xwhiteouts_dir = rdd->dentry &&
309-
ovl_path_check_xwhiteouts_xattr(OVL_FS(rdd->dentry->d_sb), realpath);
310308
rdd->first_maybe_whiteout = NULL;
311309
rdd->ctx.pos = 0;
312310
do {
@@ -359,10 +357,13 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
359357
.is_lowest = false,
360358
};
361359
int idx, next;
360+
const struct ovl_layer *layer;
362361

363362
for (idx = 0; idx != -1; idx = next) {
364-
next = ovl_path_next(idx, dentry, &realpath);
363+
next = ovl_path_next(idx, dentry, &realpath, &layer);
365364
rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry;
365+
rdd.in_xwhiteouts_dir = layer->has_xwhiteouts &&
366+
ovl_dentry_has_xwhiteouts(dentry);
366367

367368
if (next != -1) {
368369
err = ovl_dir_read(&realpath, &rdd);

fs/overlayfs/super.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,7 @@ static struct dentry *ovl_get_root(struct super_block *sb,
12491249
struct ovl_entry *oe)
12501250
{
12511251
struct dentry *root;
1252+
struct ovl_fs *ofs = OVL_FS(sb);
12521253
struct ovl_path *lowerpath = ovl_lowerstack(oe);
12531254
unsigned long ino = d_inode(lowerpath->dentry)->i_ino;
12541255
int fsid = lowerpath->layer->fsid;
@@ -1270,6 +1271,20 @@ static struct dentry *ovl_get_root(struct super_block *sb,
12701271
ovl_set_flag(OVL_IMPURE, d_inode(root));
12711272
}
12721273

1274+
/* Look for xwhiteouts marker except in the lowermost layer */
1275+
for (int i = 0; i < ovl_numlower(oe) - 1; i++, lowerpath++) {
1276+
struct path path = {
1277+
.mnt = lowerpath->layer->mnt,
1278+
.dentry = lowerpath->dentry,
1279+
};
1280+
1281+
/* overlay.opaque=x means xwhiteouts directory */
1282+
if (ovl_get_opaquedir_val(ofs, &path) == 'x') {
1283+
ovl_layer_set_xwhiteouts(ofs, lowerpath->layer);
1284+
ovl_dentry_set_xwhiteouts(root);
1285+
}
1286+
}
1287+
12731288
/* Root is always merge -> can have whiteouts */
12741289
ovl_set_flag(OVL_WHITEOUTS, d_inode(root));
12751290
ovl_dentry_set_flag(OVL_E_CONNECTED, root);

0 commit comments

Comments
 (0)