Skip to content

Commit 335d3fc

Browse files
sargunMiklos Szeredi
authored andcommitted
ovl: implement volatile-specific fsync error behaviour
Overlayfs's volatile option allows the user to bypass all forced sync calls to the upperdir filesystem. This comes at the cost of safety. We can never ensure that the user's data is intact, but we can make a best effort to expose whether or not the data is likely to be in a bad state. The best way to handle this in the time being is that if an overlayfs's upperdir experiences an error after a volatile mount occurs, that error will be returned on fsync, fdatasync, sync, and syncfs. This is contradictory to the traditional behaviour of VFS which fails the call once, and only raises an error if a subsequent fsync error has occurred, and been raised by the filesystem. One awkward aspect of the patch is that we have to manually set the superblock's errseq_t after the sync_fs callback as opposed to just returning an error from syncfs. This is because the call chain looks something like this: sys_syncfs -> sync_filesystem -> __sync_filesystem -> /* The return value is ignored here sb->s_op->sync_fs(sb) _sync_blockdev /* Where the VFS fetches the error to raise to userspace */ errseq_check_and_advance Because of this we call errseq_set every time the sync_fs callback occurs. Due to the nature of this seen / unseen dichotomy, if the upperdir is an inconsistent state at the initial mount time, overlayfs will refuse to mount, as overlayfs cannot get a snapshot of the upperdir's errseq that will increment on error until the user calls syncfs. Signed-off-by: Sargun Dhillon <[email protected]> Suggested-by: Amir Goldstein <[email protected]> Reviewed-by: Amir Goldstein <[email protected]> Fixes: c86243b ("ovl: provide a mount option "volatile"") Cc: [email protected] Reviewed-by: Vivek Goyal <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 03fedf9 commit 335d3fc

File tree

7 files changed

+71
-11
lines changed

7 files changed

+71
-11
lines changed

Documentation/filesystems/overlayfs.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,14 @@ without significant effort.
586586
The advantage of mounting with the "volatile" option is that all forms of
587587
sync calls to the upper filesystem are omitted.
588588

589+
In order to avoid a giving a false sense of safety, the syncfs (and fsync)
590+
semantics of volatile mounts are slightly different than that of the rest of
591+
VFS. If any writeback error occurs on the upperdir's filesystem after a
592+
volatile mount takes place, all sync functions will return an error. Once this
593+
condition is reached, the filesystem will not recover, and every subsequent sync
594+
call will return an error, even if the upperdir has not experience a new error
595+
since the last sync call.
596+
589597
When overlay is mounted with "volatile" option, the directory
590598
"$workdir/work/incompat/volatile" is created. During next mount, overlay
591599
checks for this directory and refuses to mount if present. This is a strong

fs/overlayfs/file.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,9 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
398398
const struct cred *old_cred;
399399
int ret;
400400

401-
if (!ovl_should_sync(OVL_FS(file_inode(file)->i_sb)))
402-
return 0;
401+
ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
402+
if (ret <= 0)
403+
return ret;
403404

404405
ret = ovl_real_fdget_meta(file, &real, !datasync);
405406
if (ret)

fs/overlayfs/overlayfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry);
324324
bool ovl_is_metacopy_dentry(struct dentry *dentry);
325325
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
326326
int padding);
327+
int ovl_sync_status(struct ovl_fs *ofs);
327328

328329
static inline bool ovl_is_impuredir(struct super_block *sb,
329330
struct dentry *dentry)

fs/overlayfs/ovl_entry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ struct ovl_fs {
8181
atomic_long_t last_ino;
8282
/* Whiteout dentry cache */
8383
struct dentry *whiteout;
84+
/* r/o snapshot of upperdir sb's only taken on volatile mounts */
85+
errseq_t errseq;
8486
};
8587

8688
static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs)

fs/overlayfs/readdir.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -900,8 +900,9 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
900900
struct file *realfile;
901901
int err;
902902

903-
if (!ovl_should_sync(OVL_FS(file->f_path.dentry->d_sb)))
904-
return 0;
903+
err = ovl_sync_status(OVL_FS(file->f_path.dentry->d_sb));
904+
if (err <= 0)
905+
return err;
905906

906907
realfile = ovl_dir_real_file(file, true);
907908
err = PTR_ERR_OR_ZERO(realfile);

fs/overlayfs/super.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,20 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
264264
struct super_block *upper_sb;
265265
int ret;
266266

267-
if (!ovl_upper_mnt(ofs))
268-
return 0;
267+
ret = ovl_sync_status(ofs);
268+
/*
269+
* We have to always set the err, because the return value isn't
270+
* checked in syncfs, and instead indirectly return an error via
271+
* the sb's writeback errseq, which VFS inspects after this call.
272+
*/
273+
if (ret < 0) {
274+
errseq_set(&sb->s_wb_err, -EIO);
275+
return -EIO;
276+
}
277+
278+
if (!ret)
279+
return ret;
269280

270-
if (!ovl_should_sync(ofs))
271-
return 0;
272281
/*
273282
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
274283
* All the super blocks will be iterated, including upper_sb.
@@ -1993,6 +2002,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
19932002
sb->s_op = &ovl_super_operations;
19942003

19952004
if (ofs->config.upperdir) {
2005+
struct super_block *upper_sb;
2006+
19962007
if (!ofs->config.workdir) {
19972008
pr_err("missing 'workdir'\n");
19982009
goto out_err;
@@ -2002,16 +2013,25 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
20022013
if (err)
20032014
goto out_err;
20042015

2016+
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
2017+
if (!ovl_should_sync(ofs)) {
2018+
ofs->errseq = errseq_sample(&upper_sb->s_wb_err);
2019+
if (errseq_check(&upper_sb->s_wb_err, ofs->errseq)) {
2020+
err = -EIO;
2021+
pr_err("Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.\n");
2022+
goto out_err;
2023+
}
2024+
}
2025+
20052026
err = ovl_get_workdir(sb, ofs, &upperpath);
20062027
if (err)
20072028
goto out_err;
20082029

20092030
if (!ofs->workdir)
20102031
sb->s_flags |= SB_RDONLY;
20112032

2012-
sb->s_stack_depth = ovl_upper_mnt(ofs)->mnt_sb->s_stack_depth;
2013-
sb->s_time_gran = ovl_upper_mnt(ofs)->mnt_sb->s_time_gran;
2014-
2033+
sb->s_stack_depth = upper_sb->s_stack_depth;
2034+
sb->s_time_gran = upper_sb->s_time_gran;
20152035
}
20162036
oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers);
20172037
err = PTR_ERR(oe);

fs/overlayfs/util.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,30 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
962962
kfree(buf);
963963
return ERR_PTR(res);
964964
}
965+
966+
/*
967+
* ovl_sync_status() - Check fs sync status for volatile mounts
968+
*
969+
* Returns 1 if this is not a volatile mount and a real sync is required.
970+
*
971+
* Returns 0 if syncing can be skipped because mount is volatile, and no errors
972+
* have occurred on the upperdir since the mount.
973+
*
974+
* Returns -errno if it is a volatile mount, and the error that occurred since
975+
* the last mount. If the error code changes, it'll return the latest error
976+
* code.
977+
*/
978+
979+
int ovl_sync_status(struct ovl_fs *ofs)
980+
{
981+
struct vfsmount *mnt;
982+
983+
if (ovl_should_sync(ofs))
984+
return 1;
985+
986+
mnt = ovl_upper_mnt(ofs);
987+
if (!mnt)
988+
return 0;
989+
990+
return errseq_check(&mnt->mnt_sb->s_wb_err, ofs->errseq);
991+
}

0 commit comments

Comments
 (0)