Skip to content

Commit 7d6899f

Browse files
committed
ovl: fsync after metadata copy-up
For upper filesystems which do not use strict ordering of persisting metadata changes (e.g. ubifs), when overlayfs file is modified for the first time, copy up will create a copy of the lower file and its parent directories in the upper layer. Permission lost of the new upper parent directory was observed during power-cut stress test. Fix by moving the fsync call to after metadata copy to make sure that the metadata copied up directory and files persists to disk before renaming from tmp to final destination. With metacopy enabled, this change will hurt performance of workloads such as chown -R, so we keep the legacy behavior of fsync only on copyup of data. Link: https://lore.kernel.org/linux-unionfs/CAOQ4uxj-pOvmw1-uXR3qVdqtLjSkwcR9nVKcNU_vC10Zyf2miQ@mail.gmail.com/ Reported-and-tested-by: Fei Lv <[email protected]> Signed-off-by: Amir Goldstein <[email protected]>
1 parent 34b4540 commit 7d6899f

File tree

1 file changed

+39
-4
lines changed

1 file changed

+39
-4
lines changed

fs/overlayfs/copy_up.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,24 @@ static int ovl_verify_area(loff_t pos, loff_t pos2, loff_t len, loff_t totlen)
243243
return 0;
244244
}
245245

246+
static int ovl_sync_file(struct path *path)
247+
{
248+
struct file *new_file;
249+
int err;
250+
251+
new_file = ovl_path_open(path, O_LARGEFILE | O_RDONLY);
252+
if (IS_ERR(new_file))
253+
return PTR_ERR(new_file);
254+
255+
err = vfs_fsync(new_file, 0);
256+
fput(new_file);
257+
258+
return err;
259+
}
260+
246261
static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
247-
struct file *new_file, loff_t len)
262+
struct file *new_file, loff_t len,
263+
bool datasync)
248264
{
249265
struct path datapath;
250266
struct file *old_file;
@@ -342,7 +358,8 @@ static int ovl_copy_up_file(struct ovl_fs *ofs, struct dentry *dentry,
342358

343359
len -= bytes;
344360
}
345-
if (!error && ovl_should_sync(ofs))
361+
/* call fsync once, either now or later along with metadata */
362+
if (!error && ovl_should_sync(ofs) && datasync)
346363
error = vfs_fsync(new_file, 0);
347364
out_fput:
348365
fput(old_file);
@@ -574,6 +591,7 @@ struct ovl_copy_up_ctx {
574591
bool indexed;
575592
bool metacopy;
576593
bool metacopy_digest;
594+
bool metadata_fsync;
577595
};
578596

579597
static int ovl_link_up(struct ovl_copy_up_ctx *c)
@@ -634,7 +652,8 @@ static int ovl_copy_up_data(struct ovl_copy_up_ctx *c, const struct path *temp)
634652
if (IS_ERR(new_file))
635653
return PTR_ERR(new_file);
636654

637-
err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size);
655+
err = ovl_copy_up_file(ofs, c->dentry, new_file, c->stat.size,
656+
!c->metadata_fsync);
638657
fput(new_file);
639658

640659
return err;
@@ -701,6 +720,10 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
701720
err = ovl_set_attr(ofs, temp, &c->stat);
702721
inode_unlock(temp->d_inode);
703722

723+
/* fsync metadata before moving it into upper dir */
724+
if (!err && ovl_should_sync(ofs) && c->metadata_fsync)
725+
err = ovl_sync_file(&upperpath);
726+
704727
return err;
705728
}
706729

@@ -860,7 +883,8 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
860883

861884
temp = tmpfile->f_path.dentry;
862885
if (!c->metacopy && c->stat.size) {
863-
err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size);
886+
err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size,
887+
!c->metadata_fsync);
864888
if (err)
865889
goto out_fput;
866890
}
@@ -1135,6 +1159,17 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
11351159
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
11361160
return -EOVERFLOW;
11371161

1162+
/*
1163+
* With metacopy disabled, we fsync after final metadata copyup, for
1164+
* both regular files and directories to get atomic copyup semantics
1165+
* on filesystems that do not use strict metadata ordering (e.g. ubifs).
1166+
*
1167+
* With metacopy enabled we want to avoid fsync on all meta copyup
1168+
* that will hurt performance of workloads such as chown -R, so we
1169+
* only fsync on data copyup as legacy behavior.
1170+
*/
1171+
ctx.metadata_fsync = !OVL_FS(dentry->d_sb)->config.metacopy &&
1172+
(S_ISREG(ctx.stat.mode) || S_ISDIR(ctx.stat.mode));
11381173
ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags);
11391174

11401175
if (parent) {

0 commit comments

Comments
 (0)