Merge tag 'ovl-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 16:21:27 +0000 (09:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 16:21:27 +0000 (09:21 -0700)
Pull overlayfs update from Miklos Szeredi:

 - Copy up immutable/append/sync/noatime attributes (Amir Goldstein)

 - Improve performance by enabling RCU lookup.

 - Misc fixes and improvements

The reason this touches so many files is that the ->get_acl() method now
gets a "bool rcu" argument.  The ->get_acl() API was updated based on
comments from Al and Linus:

Link: https://lore.kernel.org/linux-fsdevel/CAJfpeguQxpd6Wgc0Jd3ks77zcsAv_bn0q17L3VNnnmPKu11t8A@mail.gmail.com/
* tag 'ovl-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: enable RCU'd ->get_acl()
  vfs: add rcu argument to ->get_acl() callback
  ovl: fix BUG_ON() in may_delete() when called from ovl_cleanup()
  ovl: use kvalloc in xattr copy-up
  ovl: update ctime when changing fileattr
  ovl: skip checking lower file's i_writecount on truncate
  ovl: relax lookup error on mismatch origin ftype
  ovl: do not set overlay.opaque for new directories
  ovl: add ovl_allow_offline_changes() helper
  ovl: disable decoding null uuid with redirect_dir
  ovl: consistent behavior for immutable/append-only inodes
  ovl: copy up sync/noatime fileattr flags
  ovl: pass ovl_fs to ovl_check_setxattr()
  fs: add generic helper for filling statx attribute flags

49 files changed:
Documentation/filesystems/locking.rst
Documentation/filesystems/overlayfs.rst
Documentation/filesystems/vfs.rst
fs/9p/acl.c
fs/9p/acl.h
fs/bad_inode.c
fs/btrfs/acl.c
fs/btrfs/ctree.h
fs/ceph/acl.c
fs/ceph/super.h
fs/erofs/xattr.c
fs/erofs/xattr.h
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext4/acl.c
fs/ext4/acl.h
fs/f2fs/acl.c
fs/f2fs/acl.h
fs/fuse/acl.c
fs/fuse/fuse_i.h
fs/gfs2/acl.c
fs/gfs2/acl.h
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jfs/acl.c
fs/jfs/jfs_acl.h
fs/nfs/nfs3_fs.h
fs/nfs/nfs3acl.c
fs/ocfs2/acl.c
fs/ocfs2/acl.h
fs/orangefs/acl.c
fs/orangefs/inode.c
fs/orangefs/orangefs-kernel.h
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c
fs/overlayfs/util.c
fs/posix_acl.c
fs/reiserfs/acl.h
fs/reiserfs/xattr_acl.c
fs/stat.c
fs/xfs/xfs_acl.c
fs/xfs/xfs_acl.h
include/linux/fs.h
include/linux/posix_acl.h
include/linux/stat.h

index 2a75dd5..d36fe79 100644 (file)
@@ -70,7 +70,7 @@ prototypes::
        const char *(*get_link) (struct dentry *, struct inode *, struct delayed_call *);
        void (*truncate) (struct inode *);
        int (*permission) (struct inode *, int, unsigned int);
-       int (*get_acl)(struct inode *, int);
+       struct posix_acl * (*get_acl)(struct inode *, int, bool);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
index 455ca86..7da6c30 100644 (file)
@@ -427,6 +427,9 @@ b) If a file residing on a lower layer is opened for read-only and then
 memory mapped with MAP_SHARED, then subsequent changes to the file are not
 reflected in the memory mapping.
 
+c) If a file residing on a lower layer is being executed, then opening that
+file for write or truncating the file will not be denied with ETXTBSY.
+
 The following options allow overlayfs to act more like a standards
 compliant filesystem:
 
index 14c31ec..bf5c480 100644 (file)
@@ -432,7 +432,7 @@ As of kernel 2.6.22, the following members are defined:
                const char *(*get_link) (struct dentry *, struct inode *,
                                         struct delayed_call *);
                int (*permission) (struct user_namespace *, struct inode *, int);
-               int (*get_acl)(struct inode *, int);
+               struct posix_acl * (*get_acl)(struct inode *, int, bool);
                int (*setattr) (struct user_namespace *, struct dentry *, struct iattr *);
                int (*getattr) (struct user_namespace *, const struct path *, struct kstat *, u32, unsigned int);
                ssize_t (*listxattr) (struct dentry *, char *, size_t);
index bb1b286..c381499 100644 (file)
@@ -97,10 +97,13 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
        return acl;
 }
 
-struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type)
+struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct v9fs_session_info *v9ses;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        v9ses = v9fs_inode2v9ses(inode);
        if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) ||
                        ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) {
index e4f7e88..d43c894 100644 (file)
@@ -16,7 +16,7 @@
 
 #ifdef CONFIG_9P_FS_POSIX_ACL
 extern int v9fs_get_acl(struct inode *, struct p9_fid *);
-extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
+extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu);
 extern int v9fs_acl_chmod(struct inode *, struct p9_fid *);
 extern int v9fs_set_create_acl(struct inode *, struct p9_fid *,
                               struct posix_acl *, struct posix_acl *);
index 48e1614..12b8fdc 100644 (file)
@@ -121,7 +121,7 @@ static const char *bad_inode_get_link(struct dentry *dentry,
        return ERR_PTR(-EIO);
 }
 
-static struct posix_acl *bad_inode_get_acl(struct inode *inode, int type)
+static struct posix_acl *bad_inode_get_acl(struct inode *inode, int type, bool rcu)
 {
        return ERR_PTR(-EIO);
 }
index c9f9789..0a0d0ec 100644 (file)
 #include "btrfs_inode.h"
 #include "xattr.h"
 
-struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
+struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu)
 {
        int size;
        const char *name;
        char *value = NULL;
        struct posix_acl *acl;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                name = XATTR_NAME_POSIX_ACL_ACCESS;
index f07c82f..dff2c8a 100644 (file)
@@ -3706,7 +3706,7 @@ static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
 
 /* acl.c */
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
-struct posix_acl *btrfs_get_acl(struct inode *inode, int type);
+struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu);
 int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                  struct posix_acl *acl, int type);
 int btrfs_init_acl(struct btrfs_trans_handle *trans,
index 529af59..f4fc8e0 100644 (file)
@@ -29,7 +29,7 @@ static inline void ceph_set_cached_acl(struct inode *inode,
        spin_unlock(&ci->i_ceph_lock);
 }
 
-struct posix_acl *ceph_get_acl(struct inode *inode, int type)
+struct posix_acl *ceph_get_acl(struct inode *inode, int type, bool rcu)
 {
        int size;
        unsigned int retry_cnt = 0;
@@ -37,6 +37,9 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
        char *value = NULL;
        struct posix_acl *acl;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                name = XATTR_NAME_POSIX_ACL_ACCESS;
index b1a3636..c30258f 100644 (file)
@@ -1088,7 +1088,7 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx);
 /* acl.c */
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 
-struct posix_acl *ceph_get_acl(struct inode *, int);
+struct posix_acl *ceph_get_acl(struct inode *, int, bool);
 int ceph_set_acl(struct user_namespace *mnt_userns,
                 struct inode *inode, struct posix_acl *acl, int type);
 int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
index 8dd54b4..778f2c5 100644 (file)
@@ -673,12 +673,15 @@ ssize_t erofs_listxattr(struct dentry *dentry,
 }
 
 #ifdef CONFIG_EROFS_FS_POSIX_ACL
-struct posix_acl *erofs_get_acl(struct inode *inode, int type)
+struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct posix_acl *acl;
        int prefix, rc;
        char *value = NULL;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                prefix = EROFS_XATTR_INDEX_POSIX_ACL_ACCESS;
index 366dcb4..94090c7 100644 (file)
@@ -80,7 +80,7 @@ static inline int erofs_getxattr(struct inode *inode, int index,
 #endif /* !CONFIG_EROFS_FS_XATTR */
 
 #ifdef CONFIG_EROFS_FS_POSIX_ACL
-struct posix_acl *erofs_get_acl(struct inode *inode, int type);
+struct posix_acl *erofs_get_acl(struct inode *inode, int type, bool rcu);
 #else
 #define erofs_get_acl  (NULL)
 #endif
index b9a9db9..bf29896 100644 (file)
@@ -141,13 +141,16 @@ fail:
  * inode->i_mutex: don't care
  */
 struct posix_acl *
-ext2_get_acl(struct inode *inode, int type)
+ext2_get_acl(struct inode *inode, int type, bool rcu)
 {
        int name_index;
        char *value = NULL;
        struct posix_acl *acl;
        int retval;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
index 917db5f..925ab62 100644 (file)
@@ -55,7 +55,7 @@ static inline int ext2_acl_count(size_t size)
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
 
 /* acl.c */
-extern struct posix_acl *ext2_get_acl(struct inode *inode, int type);
+extern struct posix_acl *ext2_get_acl(struct inode *inode, int type, bool rcu);
 extern int ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                        struct posix_acl *acl, int type);
 extern int ext2_init_acl (struct inode *, struct inode *);
index c5eaffc..0613dfc 100644 (file)
@@ -142,13 +142,16 @@ fail:
  * inode->i_mutex: don't care
  */
 struct posix_acl *
-ext4_get_acl(struct inode *inode, int type)
+ext4_get_acl(struct inode *inode, int type, bool rcu)
 {
        int name_index;
        char *value = NULL;
        struct posix_acl *acl;
        int retval;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
index 84b8942..3219669 100644 (file)
@@ -55,7 +55,7 @@ static inline int ext4_acl_count(size_t size)
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 
 /* acl.c */
-struct posix_acl *ext4_get_acl(struct inode *inode, int type);
+struct posix_acl *ext4_get_acl(struct inode *inode, int type, bool rcu);
 int ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                 struct posix_acl *acl, int type);
 extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
index 239ad94..16e826e 100644 (file)
@@ -196,8 +196,11 @@ static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
        return acl;
 }
 
-struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
+struct posix_acl *f2fs_get_acl(struct inode *inode, int type, bool rcu)
 {
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        return __f2fs_get_acl(inode, type, NULL);
 }
 
index 986fd1b..a26e33c 100644 (file)
@@ -33,7 +33,7 @@ struct f2fs_acl_header {
 
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
 
-extern struct posix_acl *f2fs_get_acl(struct inode *, int);
+extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
 extern int f2fs_set_acl(struct user_namespace *, struct inode *,
                        struct posix_acl *, int);
 extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
index 52b1653..337cb29 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/posix_acl.h>
 #include <linux/posix_acl_xattr.h>
 
-struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        int size;
@@ -19,6 +19,9 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type)
        void *value = NULL;
        struct posix_acl *acl;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        if (fuse_is_bad(inode))
                return ERR_PTR(-EIO);
 
index 6fb639b..3d18556 100644 (file)
@@ -1209,7 +1209,7 @@ extern const struct xattr_handler *fuse_acl_xattr_handlers[];
 extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
 
 struct posix_acl;
-struct posix_acl *fuse_get_acl(struct inode *inode, int type);
+struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu);
 int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                 struct posix_acl *acl, int type);
 
index 9165d70..734d1f0 100644 (file)
@@ -57,13 +57,16 @@ static struct posix_acl *__gfs2_get_acl(struct inode *inode, int type)
        return acl;
 }
 
-struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
+struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_holder gh;
        bool need_unlock = false;
        struct posix_acl *acl;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        if (!gfs2_glock_is_locked_by_me(ip->i_gl)) {
                int ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
                                             LM_FLAG_ANY, &gh);
index eccc6a4..cd180ca 100644 (file)
@@ -11,7 +11,7 @@
 
 #define GFS2_ACL_MAX_ENTRIES(sdp) ((300 << (sdp)->sd_sb.sb_bsize_shift) >> 12)
 
-extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type);
+extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu);
 extern int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 extern int gfs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                        struct posix_acl *acl, int type);
index 55a79df..e945e34 100644 (file)
@@ -173,12 +173,15 @@ static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
        return ERR_PTR(-EINVAL);
 }
 
-struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct posix_acl *acl;
        char *value = NULL;
        int rc, xprefix;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                xprefix = JFFS2_XPREFIX_ACL_ACCESS;
index 62c50da..9d9fb7c 100644 (file)
@@ -27,7 +27,7 @@ struct jffs2_acl_header {
 
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 
-struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
+struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu);
 int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                  struct posix_acl *acl, int type);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
index 43c285c..a653f34 100644 (file)
 #include "jfs_xattr.h"
 #include "jfs_acl.h"
 
-struct posix_acl *jfs_get_acl(struct inode *inode, int type)
+struct posix_acl *jfs_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct posix_acl *acl;
        char *ea_name;
        int size;
        char *value = NULL;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch(type) {
                case ACL_TYPE_ACCESS:
                        ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
index 7ae389a..3de4028 100644 (file)
@@ -7,7 +7,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-struct posix_acl *jfs_get_acl(struct inode *inode, int type);
+struct posix_acl *jfs_get_acl(struct inode *inode, int type, bool rcu);
 int jfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                struct posix_acl *acl, int type);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
index c8a1928..03a4e67 100644 (file)
@@ -11,7 +11,7 @@
  * nfs3acl.c
  */
 #ifdef CONFIG_NFS_V3_ACL
-extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type);
+extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu);
 extern int nfs3_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                        struct posix_acl *acl, int type);
 extern int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
index 9ec560a..93de0b5 100644 (file)
@@ -44,7 +44,7 @@ static void nfs3_abort_get_acl(struct posix_acl **p)
        cmpxchg(p, sentinel, ACL_NOT_CACHED);
 }
 
-struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
+struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct page *pages[NFSACL_MAXPAGES] = { };
@@ -62,6 +62,9 @@ struct posix_acl *nfs3_get_acl(struct inode *inode, int type)
        };
        int status, count;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        if (!nfs_server_capable(inode, NFS_CAP_ACLS))
                return ERR_PTR(-EOPNOTSUPP);
 
index 5c72a7e..23a72a4 100644 (file)
@@ -289,7 +289,7 @@ unlock:
        return status;
 }
 
-struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
+struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct ocfs2_super *osb;
        struct buffer_head *di_bh = NULL;
@@ -297,6 +297,9 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type)
        int had_lock;
        struct ocfs2_lock_holder oh;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        osb = OCFS2_SB(inode->i_sb);
        if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
                return NULL;
index f59d8d0..95a57c8 100644 (file)
@@ -16,7 +16,7 @@ struct ocfs2_acl_entry {
        __le32 e_id;
 };
 
-struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type);
+struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu);
 int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                      struct posix_acl *acl, int type);
 extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
index 18852b9..605e5a3 100644 (file)
 #include "orangefs-bufmap.h"
 #include <linux/posix_acl_xattr.h>
 
-struct posix_acl *orangefs_get_acl(struct inode *inode, int type)
+struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct posix_acl *acl;
        int ret;
        char *key = NULL, *value = NULL;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                key = XATTR_NAME_POSIX_ACL_ACCESS;
index 16ac617..c1bb4c4 100644 (file)
@@ -882,12 +882,7 @@ int orangefs_getattr(struct user_namespace *mnt_userns, const struct path *path,
                if (!(request_mask & STATX_SIZE))
                        stat->result_mask &= ~STATX_SIZE;
 
-               stat->attributes_mask = STATX_ATTR_IMMUTABLE |
-                   STATX_ATTR_APPEND;
-               if (inode->i_flags & S_IMMUTABLE)
-                       stat->attributes |= STATX_ATTR_IMMUTABLE;
-               if (inode->i_flags & S_APPEND)
-                       stat->attributes |= STATX_ATTR_APPEND;
+               generic_fill_statx_attr(inode, stat);
        }
        return ret;
 }
index 0e6b976..b5940ec 100644 (file)
@@ -106,7 +106,7 @@ enum orangefs_vfs_op_states {
 extern int orangefs_init_acl(struct inode *inode, struct inode *dir);
 extern const struct xattr_handler *orangefs_xattr_handlers[];
 
-extern struct posix_acl *orangefs_get_acl(struct inode *inode, int type);
+extern struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu);
 extern int orangefs_set_acl(struct user_namespace *mnt_userns,
                            struct inode *inode, struct posix_acl *acl,
                            int type);
index 2846b94..4e7d5bf 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/file.h>
+#include <linux/fileattr.h>
 #include <linux/splice.h>
 #include <linux/xattr.h>
 #include <linux/security.h>
@@ -62,7 +63,7 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
                return list_size;
        }
 
-       buf = kzalloc(list_size, GFP_KERNEL);
+       buf = kvzalloc(list_size, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
@@ -105,11 +106,12 @@ retry:
                if (size > value_size) {
                        void *new;
 
-                       new = krealloc(value, size, GFP_KERNEL);
+                       new = kvmalloc(size, GFP_KERNEL);
                        if (!new) {
                                error = -ENOMEM;
                                break;
                        }
+                       kvfree(value);
                        value = new;
                        value_size = size;
                        goto retry;
@@ -124,12 +126,50 @@ retry:
                        error = 0;
                }
        }
-       kfree(value);
+       kvfree(value);
 out:
-       kfree(buf);
+       kvfree(buf);
        return error;
 }
 
+static int ovl_copy_fileattr(struct inode *inode, struct path *old,
+                            struct path *new)
+{
+       struct fileattr oldfa = { .flags_valid = true };
+       struct fileattr newfa = { .flags_valid = true };
+       int err;
+
+       err = ovl_real_fileattr_get(old, &oldfa);
+       if (err)
+               return err;
+
+       err = ovl_real_fileattr_get(new, &newfa);
+       if (err)
+               return err;
+
+       /*
+        * We cannot set immutable and append-only flags on upper inode,
+        * because we would not be able to link upper inode to upper dir
+        * not set overlay private xattr on upper inode.
+        * Store these flags in overlay.protattr xattr instead.
+        */
+       if (oldfa.flags & OVL_PROT_FS_FLAGS_MASK) {
+               err = ovl_set_protattr(inode, new->dentry, &oldfa);
+               if (err)
+                       return err;
+       }
+
+       BUILD_BUG_ON(OVL_COPY_FS_FLAGS_MASK & ~FS_COMMON_FL);
+       newfa.flags &= ~OVL_COPY_FS_FLAGS_MASK;
+       newfa.flags |= (oldfa.flags & OVL_COPY_FS_FLAGS_MASK);
+
+       BUILD_BUG_ON(OVL_COPY_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
+       newfa.fsx_xflags &= ~OVL_COPY_FSX_FLAGS_MASK;
+       newfa.fsx_xflags |= (oldfa.fsx_xflags & OVL_COPY_FSX_FLAGS_MASK);
+
+       return ovl_real_fileattr_set(new, &newfa);
+}
+
 static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
                            struct path *new, loff_t len)
 {
@@ -331,8 +371,8 @@ out_err:
        return ERR_PTR(err);
 }
 
-int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
-                  struct dentry *lower, struct dentry *upper)
+int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
+                  struct dentry *upper)
 {
        const struct ovl_fh *fh = NULL;
        int err;
@@ -351,7 +391,7 @@ int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
        /*
         * Do not fail when upper doesn't support xattrs.
         */
-       err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh->buf,
+       err = ovl_check_setxattr(ofs, upper, OVL_XATTR_ORIGIN, fh->buf,
                                 fh ? fh->fb.len : 0, 0);
        kfree(fh);
 
@@ -493,20 +533,21 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
 static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
 {
        struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
+       struct inode *inode = d_inode(c->dentry);
+       struct path upperpath, datapath;
        int err;
 
+       ovl_path_upper(c->dentry, &upperpath);
+       if (WARN_ON(upperpath.dentry != NULL))
+               return -EIO;
+
+       upperpath.dentry = temp;
+
        /*
         * Copy up data first and then xattrs. Writing data after
         * xattrs will remove security.capability xattr automatically.
         */
        if (S_ISREG(c->stat.mode) && !c->metacopy) {
-               struct path upperpath, datapath;
-
-               ovl_path_upper(c->dentry, &upperpath);
-               if (WARN_ON(upperpath.dentry != NULL))
-                       return -EIO;
-               upperpath.dentry = temp;
-
                ovl_path_lowerdata(c->dentry, &datapath);
                err = ovl_copy_up_data(ofs, &datapath, &upperpath,
                                       c->stat.size);
@@ -518,6 +559,16 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
        if (err)
                return err;
 
+       if (inode->i_flags & OVL_COPY_I_FLAGS_MASK) {
+               /*
+                * Copy the fileattr inode flags that are the source of already
+                * copied i_flags
+                */
+               err = ovl_copy_fileattr(inode, &c->lowerpath, &upperpath);
+               if (err)
+                       return err;
+       }
+
        /*
         * Store identifier of lower inode in upper inode xattr to
         * allow lookup of the copy up origin inode.
@@ -526,13 +577,13 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
         * hard link.
         */
        if (c->origin) {
-               err = ovl_set_origin(ofs, c->dentry, c->lowerpath.dentry, temp);
+               err = ovl_set_origin(ofs, c->lowerpath.dentry, temp);
                if (err)
                        return err;
        }
 
        if (c->metacopy) {
-               err = ovl_check_setxattr(c->dentry, temp, OVL_XATTR_METACOPY,
+               err = ovl_check_setxattr(ofs, temp, OVL_XATTR_METACOPY,
                                         NULL, 0, -EOPNOTSUPP);
                if (err)
                        return err;
index 93efe70..1fefb2b 100644 (file)
@@ -233,9 +233,10 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr)
 static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
                               int xerr)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        int err;
 
-       err = ovl_check_setxattr(dentry, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
+       err = ovl_check_setxattr(ofs, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
        if (!err)
                ovl_dentry_set_opaque(dentry);
 
@@ -320,6 +321,7 @@ static bool ovl_type_origin(struct dentry *dentry)
 static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
                            struct ovl_cattr *attr)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
        struct inode *udir = upperdir->d_inode;
        struct dentry *newdentry;
@@ -338,7 +340,8 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
        if (IS_ERR(newdentry))
                goto out_unlock;
 
-       if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry)) {
+       if (ovl_type_merge(dentry->d_parent) && d_is_dir(newdentry) &&
+           !ovl_allow_offline_changes(ofs)) {
                /* Setting opaque here is just an optimization, allow to fail */
                ovl_set_opaque(dentry, newdentry);
        }
@@ -542,8 +545,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
                        goto out_cleanup;
        }
        err = ovl_instantiate(dentry, inode, newdentry, hardlink);
-       if (err)
-               goto out_cleanup;
+       if (err) {
+               ovl_cleanup(udir, newdentry);
+               dput(newdentry);
+       }
 out_dput:
        dput(upper);
 out_unlock:
@@ -1043,6 +1048,7 @@ static bool ovl_need_absolute_redirect(struct dentry *dentry, bool samedir)
 static int ovl_set_redirect(struct dentry *dentry, bool samedir)
 {
        int err;
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        const char *redirect = ovl_dentry_get_redirect(dentry);
        bool absolute_redirect = ovl_need_absolute_redirect(dentry, samedir);
 
@@ -1053,7 +1059,7 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir)
        if (IS_ERR(redirect))
                return PTR_ERR(redirect);
 
-       err = ovl_check_setxattr(dentry, ovl_dentry_upper(dentry),
+       err = ovl_check_setxattr(ofs, ovl_dentry_upper(dentry),
                                 OVL_XATTR_REDIRECT,
                                 redirect, strlen(redirect), -EXDEV);
        if (!err) {
index 5e828a1..832b175 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/fiemap.h>
 #include <linux/fileattr.h>
 #include <linux/security.h>
+#include <linux/namei.h>
 #include "overlayfs.h"
 
 
@@ -33,12 +34,6 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                goto out;
 
        if (attr->ia_valid & ATTR_SIZE) {
-               struct inode *realinode = d_inode(ovl_dentry_real(dentry));
-
-               err = -ETXTBSY;
-               if (atomic_read(&realinode->i_writecount) < 0)
-                       goto out_drop_write;
-
                /* Truncate should trigger data copy up as well */
                full_copy_up = true;
        }
@@ -162,7 +157,8 @@ int ovl_getattr(struct user_namespace *mnt_userns, const struct path *path,
        enum ovl_path_type type;
        struct path realpath;
        const struct cred *old_cred;
-       bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
+       struct inode *inode = d_inode(dentry);
+       bool is_dir = S_ISDIR(inode->i_mode);
        int fsid = 0;
        int err;
        bool metacopy_blocks = false;
@@ -175,6 +171,9 @@ int ovl_getattr(struct user_namespace *mnt_userns, const struct path *path,
        if (err)
                goto out;
 
+       /* Report the effective immutable/append-only STATX flags */
+       generic_fill_statx_attr(inode, stat);
+
        /*
         * For non-dir or same fs, we use st_ino of the copy up origin.
         * This guaranties constant st_dev/st_ino across copy up.
@@ -448,7 +447,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
        return res;
 }
 
-struct posix_acl *ovl_get_acl(struct inode *inode, int type)
+struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct inode *realinode = ovl_inode_real(inode);
        const struct cred *old_cred;
@@ -457,6 +456,9 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
        if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
                return NULL;
 
+       if (rcu)
+               return get_cached_acl_rcu(realinode, type);
+
        old_cred = ovl_override_creds(inode->i_sb);
        acl = get_acl(realinode, type);
        revert_creds(old_cred);
@@ -503,16 +505,14 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
  * Introducing security_inode_fileattr_get/set() hooks would solve this issue
  * properly.
  */
-static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa,
+static int ovl_security_fileattr(struct path *realpath, struct fileattr *fa,
                                 bool set)
 {
-       struct path realpath;
        struct file *file;
        unsigned int cmd;
        int err;
 
-       ovl_path_real(dentry, &realpath);
-       file = dentry_open(&realpath, O_RDONLY, current_cred());
+       file = dentry_open(realpath, O_RDONLY, current_cred());
        if (IS_ERR(file))
                return PTR_ERR(file);
 
@@ -527,12 +527,24 @@ static int ovl_security_fileattr(struct dentry *dentry, struct fileattr *fa,
        return err;
 }
 
+int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa)
+{
+       int err;
+
+       err = ovl_security_fileattr(realpath, fa, true);
+       if (err)
+               return err;
+
+       return vfs_fileattr_set(&init_user_ns, realpath->dentry, fa);
+}
+
 int ovl_fileattr_set(struct user_namespace *mnt_userns,
                     struct dentry *dentry, struct fileattr *fa)
 {
        struct inode *inode = d_inode(dentry);
-       struct dentry *upperdentry;
+       struct path upperpath;
        const struct cred *old_cred;
+       unsigned int flags;
        int err;
 
        err = ovl_want_write(dentry);
@@ -541,31 +553,78 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
 
        err = ovl_copy_up(dentry);
        if (!err) {
-               upperdentry = ovl_dentry_upper(dentry);
+               ovl_path_real(dentry, &upperpath);
 
                old_cred = ovl_override_creds(inode->i_sb);
-               err = ovl_security_fileattr(dentry, fa, true);
+               /*
+                * Store immutable/append-only flags in xattr and clear them
+                * in upper fileattr (in case they were set by older kernel)
+                * so children of "ovl-immutable" directories lower aliases of
+                * "ovl-immutable" hardlinks could be copied up.
+                * Clear xattr when flags are cleared.
+                */
+               err = ovl_set_protattr(inode, upperpath.dentry, fa);
                if (!err)
-                       err = vfs_fileattr_set(&init_user_ns, upperdentry, fa);
+                       err = ovl_real_fileattr_set(&upperpath, fa);
                revert_creds(old_cred);
-               ovl_copyflags(ovl_inode_real(inode), inode);
+
+               /*
+                * Merge real inode flags with inode flags read from
+                * overlay.protattr xattr
+                */
+               flags = ovl_inode_real(inode)->i_flags & OVL_COPY_I_FLAGS_MASK;
+
+               BUILD_BUG_ON(OVL_PROT_I_FLAGS_MASK & ~OVL_COPY_I_FLAGS_MASK);
+               flags |= inode->i_flags & OVL_PROT_I_FLAGS_MASK;
+               inode_set_flags(inode, flags, OVL_COPY_I_FLAGS_MASK);
+
+               /* Update ctime */
+               ovl_copyattr(ovl_inode_real(inode), inode);
        }
        ovl_drop_write(dentry);
 out:
        return err;
 }
 
+/* Convert inode protection flags to fileattr flags */
+static void ovl_fileattr_prot_flags(struct inode *inode, struct fileattr *fa)
+{
+       BUILD_BUG_ON(OVL_PROT_FS_FLAGS_MASK & ~FS_COMMON_FL);
+       BUILD_BUG_ON(OVL_PROT_FSX_FLAGS_MASK & ~FS_XFLAG_COMMON);
+
+       if (inode->i_flags & S_APPEND) {
+               fa->flags |= FS_APPEND_FL;
+               fa->fsx_xflags |= FS_XFLAG_APPEND;
+       }
+       if (inode->i_flags & S_IMMUTABLE) {
+               fa->flags |= FS_IMMUTABLE_FL;
+               fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
+       }
+}
+
+int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa)
+{
+       int err;
+
+       err = ovl_security_fileattr(realpath, fa, false);
+       if (err)
+               return err;
+
+       return vfs_fileattr_get(realpath->dentry, fa);
+}
+
 int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
        struct inode *inode = d_inode(dentry);
-       struct dentry *realdentry = ovl_dentry_real(dentry);
+       struct path realpath;
        const struct cred *old_cred;
        int err;
 
+       ovl_path_real(dentry, &realpath);
+
        old_cred = ovl_override_creds(inode->i_sb);
-       err = ovl_security_fileattr(dentry, fa, false);
-       if (!err)
-               err = vfs_fileattr_get(realdentry, fa);
+       err = ovl_real_fileattr_get(&realpath, fa);
+       ovl_fileattr_prot_flags(inode, fa);
        revert_creds(old_cred);
 
        return err;
@@ -1118,6 +1177,10 @@ struct inode *ovl_get_inode(struct super_block *sb,
                }
        }
 
+       /* Check for immutable/append-only inode flags in xattr */
+       if (upperdentry)
+               ovl_check_protattr(inode, upperdentry);
+
        if (inode->i_state & I_NEW)
                unlock_new_inode(inode);
 out:
index 210cd6f..1a9b515 100644 (file)
@@ -392,7 +392,7 @@ invalid:
                            upperdentry, d_inode(upperdentry)->i_mode & S_IFMT,
                            d_inode(origin)->i_mode & S_IFMT);
        dput(origin);
-       return -EIO;
+       return -ESTALE;
 }
 
 static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
@@ -811,7 +811,7 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
        if (err)
                return err;
 
-       err = ovl_set_origin(ofs, dentry, lower, upper);
+       err = ovl_set_origin(ofs, lower, upper);
        if (!err)
                err = ovl_set_impure(dentry->d_parent, upper->d_parent);
 
index 6ec73db..3894f33 100644 (file)
@@ -34,6 +34,7 @@ enum ovl_xattr {
        OVL_XATTR_NLINK,
        OVL_XATTR_UPPER,
        OVL_XATTR_METACOPY,
+       OVL_XATTR_PROTATTR,
 };
 
 enum ovl_inode_flag {
@@ -262,6 +263,18 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
        return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC));
 }
 
+static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs)
+{
+       /*
+        * To avoid regressions in existing setups with overlay lower offline
+        * changes, we allow lower changes only if none of the new features
+        * are used.
+        */
+       return (!ofs->config.index && !ofs->config.metacopy &&
+               !ofs->config.redirect_dir && ofs->config.xino != OVL_XINO_ON);
+}
+
+
 /* util.c */
 int ovl_want_write(struct dentry *dentry);
 void ovl_drop_write(struct dentry *dentry);
@@ -320,7 +333,7 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags);
 bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry);
 bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
                         enum ovl_xattr ox);
-int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
+int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
                       enum ovl_xattr ox, const void *value, size_t size,
                       int xerr);
 int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
@@ -485,7 +498,7 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
 int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
                  void *value, size_t size);
 ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
-struct posix_acl *ovl_get_acl(struct inode *inode, int type);
+struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu);
 int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
 bool ovl_is_private_xattr(struct super_block *sb, const char *name);
 
@@ -518,9 +531,28 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
        i_size_write(to, i_size_read(from));
 }
 
+/* vfs inode flags copied from real to ovl inode */
+#define OVL_COPY_I_FLAGS_MASK  (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE)
+/* vfs inode flags read from overlay.protattr xattr to ovl inode */
+#define OVL_PROT_I_FLAGS_MASK  (S_APPEND | S_IMMUTABLE)
+
+/*
+ * fileattr flags copied from lower to upper inode on copy up.
+ * We cannot copy up immutable/append-only flags, because that would prevent
+ * linking temp inode to upper dir, so we store them in xattr instead.
+ */
+#define OVL_COPY_FS_FLAGS_MASK (FS_SYNC_FL | FS_NOATIME_FL)
+#define OVL_COPY_FSX_FLAGS_MASK        (FS_XFLAG_SYNC | FS_XFLAG_NOATIME)
+#define OVL_PROT_FS_FLAGS_MASK  (FS_APPEND_FL | FS_IMMUTABLE_FL)
+#define OVL_PROT_FSX_FLAGS_MASK (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE)
+
+void ovl_check_protattr(struct inode *inode, struct dentry *upper);
+int ovl_set_protattr(struct inode *inode, struct dentry *upper,
+                     struct fileattr *fa);
+
 static inline void ovl_copyflags(struct inode *from, struct inode *to)
 {
-       unsigned int mask = S_SYNC | S_IMMUTABLE | S_APPEND | S_NOATIME;
+       unsigned int mask = OVL_COPY_I_FLAGS_MASK;
 
        inode_set_flags(to, from->i_flags & mask, mask);
 }
@@ -548,6 +580,8 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
 extern const struct file_operations ovl_file_operations;
 int __init ovl_aio_request_cache_init(void);
 void ovl_aio_request_cache_destroy(void);
+int ovl_real_fileattr_get(struct path *realpath, struct fileattr *fa);
+int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa);
 int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa);
 int ovl_fileattr_set(struct user_namespace *mnt_userns,
                     struct dentry *dentry, struct fileattr *fa);
@@ -561,8 +595,8 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
 int ovl_set_attr(struct dentry *upper, struct kstat *stat);
 struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
                                  bool is_upper);
-int ovl_set_origin(struct ovl_fs *ofs, struct dentry *dentry,
-                  struct dentry *lower, struct dentry *upper);
+int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
+                  struct dentry *upper);
 
 /* export.c */
 extern const struct export_operations ovl_export_operations;
index b01d414..178daa5 100644 (file)
@@ -1599,9 +1599,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
         * user opted-in to one of the new features that require following the
         * lower inode of non-dir upper.
         */
-       if (!ofs->config.index && !ofs->config.metacopy &&
-           ofs->config.xino != OVL_XINO_ON &&
-           uuid_is_null(uuid))
+       if (ovl_allow_offline_changes(ofs) && uuid_is_null(uuid))
                return false;
 
        for (i = 0; i < ofs->numfs; i++) {
index b9d0362..f48284a 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/cred.h>
 #include <linux/xattr.h>
 #include <linux/exportfs.h>
+#include <linux/fileattr.h>
 #include <linux/uuid.h>
 #include <linux/namei.h>
 #include <linux/ratelimit.h>
@@ -585,6 +586,7 @@ bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
 #define OVL_XATTR_NLINK_POSTFIX                "nlink"
 #define OVL_XATTR_UPPER_POSTFIX                "upper"
 #define OVL_XATTR_METACOPY_POSTFIX     "metacopy"
+#define OVL_XATTR_PROTATTR_POSTFIX     "protattr"
 
 #define OVL_XATTR_TAB_ENTRY(x) \
        [x] = { [false] = OVL_XATTR_TRUSTED_PREFIX x ## _POSTFIX, \
@@ -598,14 +600,14 @@ const char *const ovl_xattr_table[][2] = {
        OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
        OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
        OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
+       OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
 };
 
-int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
+int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
                       enum ovl_xattr ox, const void *value, size_t size,
                       int xerr)
 {
        int err;
-       struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
 
        if (ofs->noxattr)
                return xerr;
@@ -623,6 +625,7 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
 
 int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry)
 {
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
        int err;
 
        if (ovl_test_flag(OVL_IMPURE, d_inode(dentry)))
@@ -632,14 +635,95 @@ int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry)
         * Do not fail when upper doesn't support xattrs.
         * Upper inodes won't have origin nor redirect xattr anyway.
         */
-       err = ovl_check_setxattr(dentry, upperdentry, OVL_XATTR_IMPURE,
-                                "y", 1, 0);
+       err = ovl_check_setxattr(ofs, upperdentry, OVL_XATTR_IMPURE, "y", 1, 0);
        if (!err)
                ovl_set_flag(OVL_IMPURE, d_inode(dentry));
 
        return err;
 }
 
+
+#define OVL_PROTATTR_MAX 32 /* Reserved for future flags */
+
+void ovl_check_protattr(struct inode *inode, struct dentry *upper)
+{
+       struct ovl_fs *ofs = OVL_FS(inode->i_sb);
+       u32 iflags = inode->i_flags & OVL_PROT_I_FLAGS_MASK;
+       char buf[OVL_PROTATTR_MAX+1];
+       int res, n;
+
+       res = ovl_do_getxattr(ofs, upper, OVL_XATTR_PROTATTR, buf,
+                             OVL_PROTATTR_MAX);
+       if (res < 0)
+               return;
+
+       /*
+        * Initialize inode flags from overlay.protattr xattr and upper inode
+        * flags.  If upper inode has those fileattr flags set (i.e. from old
+        * kernel), we do not clear them on ovl_get_inode(), but we will clear
+        * them on next fileattr_set().
+        */
+       for (n = 0; n < res; n++) {
+               if (buf[n] == 'a')
+                       iflags |= S_APPEND;
+               else if (buf[n] == 'i')
+                       iflags |= S_IMMUTABLE;
+               else
+                       break;
+       }
+
+       if (!res || n < res) {
+               pr_warn_ratelimited("incompatible overlay.protattr format (%pd2, len=%d)\n",
+                                   upper, res);
+       } else {
+               inode_set_flags(inode, iflags, OVL_PROT_I_FLAGS_MASK);
+       }
+}
+
+int ovl_set_protattr(struct inode *inode, struct dentry *upper,
+                     struct fileattr *fa)
+{
+       struct ovl_fs *ofs = OVL_FS(inode->i_sb);
+       char buf[OVL_PROTATTR_MAX];
+       int len = 0, err = 0;
+       u32 iflags = 0;
+
+       BUILD_BUG_ON(HWEIGHT32(OVL_PROT_FS_FLAGS_MASK) > OVL_PROTATTR_MAX);
+
+       if (fa->flags & FS_APPEND_FL) {
+               buf[len++] = 'a';
+               iflags |= S_APPEND;
+       }
+       if (fa->flags & FS_IMMUTABLE_FL) {
+               buf[len++] = 'i';
+               iflags |= S_IMMUTABLE;
+       }
+
+       /*
+        * Do not allow to set protection flags when upper doesn't support
+        * xattrs, because we do not set those fileattr flags on upper inode.
+        * Remove xattr if it exist and all protection flags are cleared.
+        */
+       if (len) {
+               err = ovl_check_setxattr(ofs, upper, OVL_XATTR_PROTATTR,
+                                        buf, len, -EPERM);
+       } else if (inode->i_flags & OVL_PROT_I_FLAGS_MASK) {
+               err = ovl_do_removexattr(ofs, upper, OVL_XATTR_PROTATTR);
+               if (err == -EOPNOTSUPP || err == -ENODATA)
+                       err = 0;
+       }
+       if (err)
+               return err;
+
+       inode_set_flags(inode, iflags, OVL_PROT_I_FLAGS_MASK);
+
+       /* Mask out the fileattr flags that should not be set in upper inode */
+       fa->flags &= ~OVL_PROT_FS_FLAGS_MASK;
+       fa->fsx_xflags &= ~OVL_PROT_FSX_FLAGS_MASK;
+
+       return 0;
+}
+
 /**
  * Caller must hold a reference to inode to prevent it from being freed while
  * it is marked inuse.
index f3309a7..f5c25f5 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/xattr.h>
 #include <linux/export.h>
 #include <linux/user_namespace.h>
+#include <linux/namei.h>
 
 static struct posix_acl **acl_by_type(struct inode *inode, int type)
 {
@@ -56,7 +57,17 @@ EXPORT_SYMBOL(get_cached_acl);
 
 struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
 {
-       return rcu_dereference(*acl_by_type(inode, type));
+       struct posix_acl *acl = rcu_dereference(*acl_by_type(inode, type));
+
+       if (acl == ACL_DONT_CACHE) {
+               struct posix_acl *ret;
+
+               ret = inode->i_op->get_acl(inode, type, LOOKUP_RCU);
+               if (!IS_ERR(ret))
+                       acl = ret;
+       }
+
+       return acl;
 }
 EXPORT_SYMBOL(get_cached_acl_rcu);
 
@@ -138,7 +149,7 @@ struct posix_acl *get_acl(struct inode *inode, int type)
                set_cached_acl(inode, type, NULL);
                return NULL;
        }
-       acl = inode->i_op->get_acl(inode, type);
+       acl = inode->i_op->get_acl(inode, type, false);
 
        if (IS_ERR(acl)) {
                /*
index fd58618..d9052b8 100644 (file)
@@ -48,7 +48,7 @@ static inline int reiserfs_acl_count(size_t size)
 }
 
 #ifdef CONFIG_REISERFS_FS_POSIX_ACL
-struct posix_acl *reiserfs_get_acl(struct inode *inode, int type);
+struct posix_acl *reiserfs_get_acl(struct inode *inode, int type, bool rcu);
 int reiserfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                     struct posix_acl *acl, int type);
 int reiserfs_acl_chmod(struct inode *inode);
index a954714..d6fcddc 100644 (file)
@@ -190,13 +190,16 @@ fail:
  * inode->i_mutex: down
  * BKL held [before 2.5.x]
  */
-struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
+struct posix_acl *reiserfs_get_acl(struct inode *inode, int type, bool rcu)
 {
        char *name, *value;
        struct posix_acl *acl;
        int size;
        int retval;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        switch (type) {
        case ACL_TYPE_ACCESS:
                name = XATTR_NAME_POSIX_ACL_ACCESS;
index 1fa38bd..28d2020 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -59,6 +59,24 @@ void generic_fillattr(struct user_namespace *mnt_userns, struct inode *inode,
 }
 EXPORT_SYMBOL(generic_fillattr);
 
+/**
+ * generic_fill_statx_attr - Fill in the statx attributes from the inode flags
+ * @inode:     Inode to use as the source
+ * @stat:      Where to fill in the attribute flags
+ *
+ * Fill in the STATX_ATTR_* flags in the kstat structure for properties of the
+ * inode that are published on i_flags and enforced by the VFS.
+ */
+void generic_fill_statx_attr(struct inode *inode, struct kstat *stat)
+{
+       if (inode->i_flags & S_IMMUTABLE)
+               stat->attributes |= STATX_ATTR_IMMUTABLE;
+       if (inode->i_flags & S_APPEND)
+               stat->attributes |= STATX_ATTR_APPEND;
+       stat->attributes_mask |= KSTAT_ATTR_VFS_FLAGS;
+}
+EXPORT_SYMBOL(generic_fill_statx_attr);
+
 /**
  * vfs_getattr_nosec - getattr without security checks
  * @path: file to get attributes from
index f7fc1d2..5c52ee8 100644 (file)
@@ -125,7 +125,7 @@ xfs_acl_to_disk(struct xfs_acl *aclp, const struct posix_acl *acl)
 }
 
 struct posix_acl *
-xfs_get_acl(struct inode *inode, int type)
+xfs_get_acl(struct inode *inode, int type, bool rcu)
 {
        struct xfs_inode        *ip = XFS_I(inode);
        struct xfs_mount        *mp = ip->i_mount;
@@ -137,6 +137,9 @@ xfs_get_acl(struct inode *inode, int type)
        };
        int                     error;
 
+       if (rcu)
+               return ERR_PTR(-ECHILD);
+
        trace_xfs_get_acl(ip);
 
        switch (type) {
index 7bdb3a4..bb6abdc 100644 (file)
@@ -10,13 +10,13 @@ struct inode;
 struct posix_acl;
 
 #ifdef CONFIG_XFS_POSIX_ACL
-extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
+extern struct posix_acl *xfs_get_acl(struct inode *inode, int type, bool rcu);
 extern int xfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
                       struct posix_acl *acl, int type);
 extern int __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 void xfs_forget_acl(struct inode *inode, const char *name);
 #else
-static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type)
+static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type, bool rcu)
 {
        return NULL;
 }
index 1c01f9f..a6074cd 100644 (file)
@@ -588,6 +588,11 @@ static inline void mapping_allow_writable(struct address_space *mapping)
 
 struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
+/*
+ * ACL_DONT_CACHE is for stacked filesystems, that rely on underlying fs to
+ * cache the ACL.  This also means that ->get_acl() can be called in RCU mode
+ * with the LOOKUP_RCU flag.
+ */
 #define ACL_DONT_CACHE ((void *)(-3))
 
 static inline struct posix_acl *
@@ -2109,7 +2114,7 @@ struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
        const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
        int (*permission) (struct user_namespace *, struct inode *, int);
-       struct posix_acl * (*get_acl)(struct inode *, int);
+       struct posix_acl * (*get_acl)(struct inode *, int, bool);
 
        int (*readlink) (struct dentry *, char __user *,int);
 
@@ -3314,6 +3319,7 @@ extern int page_symlink(struct inode *inode, const char *symname, int len);
 extern const struct inode_operations page_symlink_inode_operations;
 extern void kfree_link(void *);
 void generic_fillattr(struct user_namespace *, struct inode *, struct kstat *);
+void generic_fill_statx_attr(struct inode *inode, struct kstat *stat);
 extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int);
 extern int vfs_getattr(const struct path *, struct kstat *, u32, unsigned int);
 void __inode_add_bytes(struct inode *inode, loff_t bytes);
index 307094e..b65c877 100644 (file)
@@ -72,6 +72,8 @@ extern struct posix_acl *get_posix_acl(struct inode *, int);
 extern int set_posix_acl(struct user_namespace *, struct inode *, int,
                         struct posix_acl *);
 
+struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
+
 #ifdef CONFIG_FS_POSIX_ACL
 int posix_acl_chmod(struct user_namespace *, struct inode *, umode_t);
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
@@ -84,7 +86,6 @@ extern int simple_set_acl(struct user_namespace *, struct inode *,
 extern int simple_acl_create(struct inode *, struct inode *);
 
 struct posix_acl *get_cached_acl(struct inode *inode, int type);
-struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
 void set_cached_acl(struct inode *inode, int type, struct posix_acl *acl);
 void forget_cached_acl(struct inode *inode, int type);
 void forget_all_cached_acls(struct inode *inode);
index fff27e6..7df0693 100644 (file)
@@ -34,6 +34,10 @@ struct kstat {
         STATX_ATTR_ENCRYPTED |                         \
         STATX_ATTR_VERITY                              \
         )/* Attrs corresponding to FS_*_FL flags */
+#define KSTAT_ATTR_VFS_FLAGS                           \
+       (STATX_ATTR_IMMUTABLE |                         \
+        STATX_ATTR_APPEND                              \
+        ) /* Attrs corresponding to S_* flags that are enforced by the VFS */
        u64             ino;
        dev_t           dev;
        dev_t           rdev;