From bd4e44245f2f162da37b02dc1e0e7458cd28a5de Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 14 Jun 2020 10:44:09 +0300 Subject: [PATCH 1/2] ovl: inherit supported ops f_mode flags from final real file Mike Kravetz reported that when hugetlbfs is used as overlayfs upper layer, writes do not fail, but result in writing to lower layer. This is surprising because hugeltbfs file does not have write() nor write_iter() method. Regardless of the question whether or not this type of filesystem should be allowed as overlayfs upper layer, overlayfs file should emulate the supported ops of the underlying files, so at least in the case where underlying file ops cannot change as result of copy up, the overlayfs file should inherit the f_mode flags indicating the supported ops of the underlying file. Reported-by: Mike Kravetz Signed-off-by: Amir Goldstein --- fs/overlayfs/copy_up.c | 11 +++++++++++ fs/overlayfs/file.c | 11 +++++++++++ fs/overlayfs/overlayfs.h | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 79dd052c7dbf..424f2a170f11 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -953,6 +953,17 @@ static bool ovl_open_need_copy_up(struct dentry *dentry, int flags) return true; } +/* May need copy up in the future? */ +bool ovl_may_need_copy_up(struct dentry *dentry) +{ + int flags = O_RDONLY; + + if (ovl_upper_mnt(OVL_FS(dentry->d_sb))) + flags = O_RDWR; + + return ovl_open_need_copy_up(dentry, flags); +} + int ovl_maybe_copy_up(struct dentry *dentry, int flags) { int err = 0; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 01820e654a21..01dd3ed723df 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -153,6 +153,17 @@ static int ovl_open(struct inode *inode, struct file *file) if (IS_ERR(realfile)) return PTR_ERR(realfile); + /* + * Overlay file supported ops are a super set of the underlying file + * supported ops and we do not change them when file is copied up. + * But if file cannot be copied up, then there is no need to advertize + * more supported ops than underlying file actually has. + */ + if (!ovl_may_need_copy_up(file_dentry(file))) { + file->f_mode &= ~OVL_UPPER_FMODE_MASK; + file->f_mode |= realfile->f_mode & OVL_UPPER_FMODE_MASK; + } + file->private_data = realfile; return 0; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index b725c7f15ff4..6748c28ff477 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -110,6 +110,10 @@ struct ovl_fh { #define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \ offsetof(struct ovl_fb, fid)) +/* f_mode bits expected to be set on an upper file */ +#define OVL_UPPER_FMODE_MASK (FMODE_CAN_READ | FMODE_CAN_WRITE | \ + FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE) + static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) { int err = vfs_rmdir(dir, dentry); @@ -485,6 +489,7 @@ int ovl_copy_up(struct dentry *dentry); int ovl_copy_up_with_data(struct dentry *dentry); int ovl_copy_up_flags(struct dentry *dentry, int flags); int ovl_maybe_copy_up(struct dentry *dentry, int flags); +bool ovl_may_need_copy_up(struct dentry *dentry); int ovl_copy_xattr(struct dentry *old, struct dentry *new); int ovl_set_attr(struct dentry *upper, struct kstat *stat); struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper); -- 2.17.1