overlayfs update for 5.15
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYTDKKAAKCRDh3BK/laaZ PG9PAQCUF0fdBlCKudwSEt5PV5xemycL9OCAlYCd7d4XbBIe9wEA6sVJL9J+OwV2 aF0NomiXtJccE+S9+byjVCyqSzQJGQQ= =6L2Y -----END PGP SIGNATURE----- Merge tag 'ovl-update-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs 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
This commit is contained in:
commit
815409a12c
@ -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);
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -16,13 +16,16 @@
|
||||
#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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 *,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
|
@ -14,13 +14,16 @@
|
||||
#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;
|
||||
|
@ -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 *);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 *);
|
||||
|
@ -10,12 +10,15 @@
|
||||
#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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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++) {
|
||||
|
@ -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.
|
||||
|
@ -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)) {
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
18
fs/stat.c
18
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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user