To sync only what was written through an overlay, we have to know which upper inodes were modified through it. Mark the overlay inode dirty when its upper inode is modified, which puts it on the dirty list of the overlay's bdi. Nearly every modification ends in ovl_copyattr(), so do it there. These do not, so they mark the inode themselves: - copy up, for the inode and for its parent - ovl_instantiate(), for a new inode and for a new hard link - atime updates - shared writable mappings - the removal of a stale "impure" xattr in readdir Do not mark an unhashed inode. __mark_inode_dirty() would set its dirty flags without queueing it, and every later call would return early. The inode of a lower hardlink is unhashed until ovl_inode_update() hashes it. The root inode is never hashed, a later patch takes care of it. Based on work by Chengguang Xu. Link: https://lore.kernel.org/r/20211122030038.1938875-1-cgxu519@mykernel.net Suggested-by: Amir Goldstein Suggested-by: Miklos Szeredi Signed-off-by: Josef Bacik --- fs/overlayfs/copy_up.c | 9 +++++++++ fs/overlayfs/dir.c | 3 +++ fs/overlayfs/file.c | 7 ++++++- fs/overlayfs/inode.c | 4 +++- fs/overlayfs/overlayfs.h | 20 ++++++++++++++++++++ fs/overlayfs/readdir.c | 1 + fs/overlayfs/util.c | 12 +++++++++++- 7 files changed, 53 insertions(+), 3 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index e963701b4c87..44bc96aa3d5d 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -1199,6 +1199,15 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, err = ovl_link_up(&ctx); if (!err && ovl_dentry_needs_data_copy_up_locked(dentry, flags)) err = ovl_copy_up_meta_inode_data(&ctx); + /* + * All three write to the upper inode, and there may be no later + * write that would mark the overlay inode dirty. The first two + * also add an entry to the upper inode of the parent, set its + * "impure" xattr and restore its timestamps. + */ + ovl_mark_inode_dirty(d_inode(dentry)); + if (parent) + ovl_mark_inode_dirty(d_inode(parent)); ovl_copy_up_end(dentry); } do_delayed_call(&done); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 1194ccf981c3..a24326446659 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -331,6 +331,9 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, inc_nlink(inode); } + /* The upper inode was just created, or has got a new link */ + ovl_mark_inode_dirty(inode); + if (tmpfile) d_mark_tmpfile(tmpfile, inode); diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 7433220d4ad6..53c5131a0482 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -472,8 +472,13 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma) .cred = ovl_creds(file_inode(file)->i_sb), .accessed = ovl_file_accessed, }; + int err; - return backing_file_mmap(of->realfile, vma, &ctx); + err = backing_file_mmap(of->realfile, vma, &ctx); + /* Stores through the mapping go straight to the upper inode */ + if (!err && vma_is_shared_maywrite(vma)) + ovl_mark_inode_dirty(file_inode(file)); + return err; } static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 63906b3dafcb..9a4bf7c2502b 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -587,6 +587,7 @@ int ovl_update_time(struct inode *inode, enum fs_update_time type, touch_atime(&upperpath); inode_set_atime_to_ts(inode, inode_get_atime(d_inode(upperpath.dentry))); + ovl_mark_inode_dirty(inode); } } return 0; @@ -898,7 +899,8 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, oi->lowerdata_redirect = oip->lowerdata_redirect; realinode = ovl_inode_real(inode); - ovl_copyattr(inode); + /* Nothing was changed, the inode is only being looked up */ + __ovl_copyattr(inode); ovl_copyflags(realinode, inode); ovl_map_ino(inode, ino, fsid); } diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 5b1a097ce543..d82c4bed5af2 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -691,6 +691,25 @@ static inline bool ovl_should_sync_upper_fs(struct ovl_fs *ofs) return ovl_should_sync(ofs) && !ovl_tracks_dirty(ofs); } +/* + * The upper inode was modified through this overlay. Put the overlay inode on + * the dirty list of the overlay, where syncfs() and unmount find it. + * + * Do not look at the dirty flags of the upper inode here. They are cleared + * when the writeback of the upper inode starts, not when it completes. + * + * Do not mark an inode that is not hashed. __mark_inode_dirty() would set its + * dirty flags without putting it on a dirty list, and once the flags are set, + * later calls return early. The overlay inode of a lower hardlink is unhashed + * until ovl_inode_update() hashes it, and ovl_copy_up_one() marks it after + * that. The root inode is never hashed, so it is not tracked. + */ +static inline void ovl_mark_inode_dirty(struct inode *inode) +{ + if (ovl_tracks_dirty(OVL_FS(inode->i_sb)) && !inode_unhashed(inode)) + mark_inode_dirty(inode); +} + /* * To avoid regressions in existing setups with overlay lower offline changes, * we allow lower changes only if none of the new features are used. @@ -878,6 +897,7 @@ bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir); struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir); struct inode *ovl_get_inode(struct super_block *sb, struct ovl_inode_params *oip); +void __ovl_copyattr(struct inode *to); void ovl_copyattr(struct inode *to); /* vfs fileattr flags read from overlay.protattr xattr to ovl inode */ diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 7d6f7f6022eb..f02c37600df6 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -724,6 +724,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path) if (!ovl_want_write(dentry)) { ovl_removexattr(ofs, ovl_dentry_upper(dentry), OVL_XATTR_IMPURE); + ovl_mark_inode_dirty(inode); ovl_drop_write(dentry); } ovl_clear_flag(OVL_IMPURE, inode); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 521717209b2e..a260c5a350e2 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1492,7 +1492,7 @@ int ovl_sync_status(struct ovl_fs *ofs) * map any lower or upper inode owned by id 1001 to id 1000. These mapping * helpers are nops when the relevant layer isn't idmapped. */ -void ovl_copyattr(struct inode *inode) +void __ovl_copyattr(struct inode *inode) { struct path realpath; struct inode *realinode; @@ -1516,3 +1516,13 @@ void ovl_copyattr(struct inode *inode) i_size_write(inode, i_size_read(realinode)); spin_unlock(&inode->i_lock); } + +/* + * The attributes are copied because the real inode was modified through the + * overlay, so this is also where the overlay inode is marked dirty. + */ +void ovl_copyattr(struct inode *inode) +{ + __ovl_copyattr(inode); + ovl_mark_inode_dirty(inode); +} -- 2.55.0