diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/bad_inode.c | 21 | ||||
-rw-r--r-- | fs/cachefiles/bind.c | 4 | ||||
-rw-r--r-- | fs/cachefiles/namei.c | 4 | ||||
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 2 | ||||
-rw-r--r-- | fs/ecryptfs/inode.c | 76 | ||||
-rw-r--r-- | fs/ecryptfs/main.c | 1 | ||||
-rw-r--r-- | fs/ecryptfs/mmap.c | 13 | ||||
-rw-r--r-- | fs/hfs/attr.c | 83 | ||||
-rw-r--r-- | fs/hfs/hfs_fs.h | 6 | ||||
-rw-r--r-- | fs/hfs/inode.c | 7 | ||||
-rw-r--r-- | fs/hfs/super.c | 1 | ||||
-rw-r--r-- | fs/inode.c | 2 | ||||
-rw-r--r-- | fs/jffs2/dir.c | 6 | ||||
-rw-r--r-- | fs/jffs2/file.c | 6 | ||||
-rw-r--r-- | fs/jffs2/symlink.c | 6 | ||||
-rw-r--r-- | fs/jffs2/xattr.h | 6 | ||||
-rw-r--r-- | fs/kernfs/dir.c | 6 | ||||
-rw-r--r-- | fs/kernfs/inode.c | 158 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 7 | ||||
-rw-r--r-- | fs/kernfs/mount.c | 1 | ||||
-rw-r--r-- | fs/kernfs/symlink.c | 6 | ||||
-rw-r--r-- | fs/libfs.c | 29 | ||||
-rw-r--r-- | fs/orangefs/xattr.c | 3 | ||||
-rw-r--r-- | fs/overlayfs/copy_up.c | 4 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 4 | ||||
-rw-r--r-- | fs/xattr.c | 203 |
26 files changed, 370 insertions, 295 deletions
diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 3ba385eaa26e..7bb153c33459 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -100,29 +100,12 @@ static int bad_inode_setattr(struct dentry *direntry, struct iattr *attrs) return -EIO; } -static int bad_inode_setxattr(struct dentry *dentry, struct inode *inode, - const char *name, const void *value, size_t size, int flags) -{ - return -EIO; -} - -static ssize_t bad_inode_getxattr(struct dentry *dentry, struct inode *inode, - const char *name, void *buffer, size_t size) -{ - return -EIO; -} - static ssize_t bad_inode_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { return -EIO; } -static int bad_inode_removexattr(struct dentry *dentry, const char *name) -{ - return -EIO; -} - static const struct inode_operations bad_inode_ops = { .create = bad_inode_create, @@ -142,10 +125,7 @@ static const struct inode_operations bad_inode_ops = .permission = bad_inode_permission, .getattr = bad_inode_getattr, .setattr = bad_inode_setattr, - .setxattr = bad_inode_setxattr, - .getxattr = bad_inode_getxattr, .listxattr = bad_inode_listxattr, - .removexattr = bad_inode_removexattr, }; @@ -175,6 +155,7 @@ void make_bad_inode(struct inode *inode) inode->i_atime = inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); inode->i_op = &bad_inode_ops; + inode->i_opflags &= ~IOP_XATTR; inode->i_fop = &bad_file_ops; } EXPORT_SYMBOL(make_bad_inode); diff --git a/fs/cachefiles/bind.c b/fs/cachefiles/bind.c index 6af790fc3df8..3ff867f87d73 100644 --- a/fs/cachefiles/bind.c +++ b/fs/cachefiles/bind.c @@ -20,6 +20,7 @@ #include <linux/mount.h> #include <linux/statfs.h> #include <linux/ctype.h> +#include <linux/xattr.h> #include "internal.h" static int cachefiles_daemon_add_cache(struct cachefiles_cache *caches); @@ -126,8 +127,7 @@ static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) if (d_is_negative(root) || !d_backing_inode(root)->i_op->lookup || !d_backing_inode(root)->i_op->mkdir || - !d_backing_inode(root)->i_op->setxattr || - !d_backing_inode(root)->i_op->getxattr || + !(d_backing_inode(root)->i_opflags & IOP_XATTR) || !root->d_sb->s_op->statfs || !root->d_sb->s_op->sync_fs) goto error_unsupported; diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 3f7c2cd41f8f..6eb3dec2adbb 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -20,6 +20,7 @@ #include <linux/namei.h> #include <linux/security.h> #include <linux/slab.h> +#include <linux/xattr.h> #include "internal.h" #define CACHEFILES_KEYBUF_SIZE 512 @@ -799,8 +800,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, } ret = -EPERM; - if (!d_backing_inode(subdir)->i_op->setxattr || - !d_backing_inode(subdir)->i_op->getxattr || + if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) || !d_backing_inode(subdir)->i_op->lookup || !d_backing_inode(subdir)->i_op->mkdir || !d_backing_inode(subdir)->i_op->create || diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 4ba1547bb9ad..599a29237cfe 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -715,4 +715,6 @@ int ecryptfs_set_f_namelen(long *namelen, long lower_namelen, int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, loff_t offset); +extern const struct xattr_handler *ecryptfs_xattr_handlers[]; + #endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 9d153b6a1d72..32fee255d7b5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -1005,15 +1005,14 @@ ecryptfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, const void *value, size_t size, int flags) { - int rc = 0; + int rc; struct dentry *lower_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!d_inode(lower_dentry)->i_op->setxattr) { + if (!(d_inode(lower_dentry)->i_opflags & IOP_XATTR)) { rc = -EOPNOTSUPP; goto out; } - rc = vfs_setxattr(lower_dentry, name, value, size, flags); if (!rc && inode) fsstack_copy_attr_all(inode, d_inode(lower_dentry)); @@ -1025,15 +1024,14 @@ ssize_t ecryptfs_getxattr_lower(struct dentry *lower_dentry, struct inode *lower_inode, const char *name, void *value, size_t size) { - int rc = 0; + int rc; - if (!lower_inode->i_op->getxattr) { + if (!(lower_inode->i_opflags & IOP_XATTR)) { rc = -EOPNOTSUPP; goto out; } inode_lock(lower_inode); - rc = lower_inode->i_op->getxattr(lower_dentry, lower_inode, - name, value, size); + rc = __vfs_getxattr(lower_dentry, lower_inode, name, value, size); inode_unlock(lower_inode); out: return rc; @@ -1066,19 +1064,22 @@ out: return rc; } -static int ecryptfs_removexattr(struct dentry *dentry, const char *name) +static int ecryptfs_removexattr(struct dentry *dentry, struct inode *inode, + const char *name) { - int rc = 0; + int rc; struct dentry *lower_dentry; + struct inode *lower_inode; lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!d_inode(lower_dentry)->i_op->removexattr) { + lower_inode = ecryptfs_inode_to_lower(inode); + if (!(lower_inode->i_opflags & IOP_XATTR)) { rc = -EOPNOTSUPP; goto out; } - inode_lock(d_inode(lower_dentry)); - rc = d_inode(lower_dentry)->i_op->removexattr(lower_dentry, name); - inode_unlock(d_inode(lower_dentry)); + inode_lock(lower_inode); + rc = __vfs_removexattr(lower_dentry, name); + inode_unlock(lower_inode); out: return rc; } @@ -1089,10 +1090,10 @@ const struct inode_operations ecryptfs_symlink_iops = { .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .getattr = ecryptfs_getattr_link, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr + .removexattr = generic_removexattr }; const struct inode_operations ecryptfs_dir_iops = { @@ -1107,18 +1108,49 @@ const struct inode_operations ecryptfs_dir_iops = { .rename = ecryptfs_rename, .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr + .removexattr = generic_removexattr }; const struct inode_operations ecryptfs_main_iops = { .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .getattr = ecryptfs_getattr, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr + .removexattr = generic_removexattr +}; + +static int ecryptfs_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return ecryptfs_getxattr(dentry, inode, name, buffer, size); +} + +static int ecryptfs_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + if (value) + return ecryptfs_setxattr(dentry, inode, name, value, size, flags); + else { + BUG_ON(flags != XATTR_REPLACE); + return ecryptfs_removexattr(dentry, inode, name); + } +} + +const struct xattr_handler ecryptfs_xattr_handler = { + .prefix = "", /* match anything */ + .get = ecryptfs_xattr_get, + .set = ecryptfs_xattr_set, +}; + +const struct xattr_handler *ecryptfs_xattr_handlers[] = { + &ecryptfs_xattr_handler, + NULL }; diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 612004495141..151872dcc1f4 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -529,6 +529,7 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags /* ->kill_sb() will take care of sbi after that point */ sbi = NULL; s->s_op = &ecryptfs_sops; + s->s_xattr = ecryptfs_xattr_handlers; s->s_d_op = &ecryptfs_dops; err = "Reading sb failed"; diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index 9c3437c8a5b1..1f0c471b4ba3 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -32,6 +32,7 @@ #include <linux/file.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/xattr.h> #include <asm/unaligned.h> #include "ecryptfs_kernel.h" @@ -422,7 +423,7 @@ static int ecryptfs_write_inode_size_to_xattr(struct inode *ecryptfs_inode) struct inode *lower_inode = d_inode(lower_dentry); int rc; - if (!lower_inode->i_op->getxattr || !lower_inode->i_op->setxattr) { + if (!(lower_inode->i_opflags & IOP_XATTR)) { printk(KERN_WARNING "No support for setting xattr in lower filesystem\n"); rc = -ENOSYS; @@ -436,15 +437,13 @@ static int ecryptfs_write_inode_size_to_xattr(struct inode *ecryptfs_inode) goto out; } inode_lock(lower_inode); - size = lower_inode->i_op->getxattr(lower_dentry, lower_inode, - ECRYPTFS_XATTR_NAME, - xattr_virt, PAGE_SIZE); + size = __vfs_getxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME, + xattr_virt, PAGE_SIZE); if (size < 0) size = 8; put_unaligned_be64(i_size_read(ecryptfs_inode), xattr_virt); - rc = lower_inode->i_op->setxattr(lower_dentry, lower_inode, - ECRYPTFS_XATTR_NAME, - xattr_virt, size, 0); + rc = __vfs_setxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME, + xattr_virt, size, 0); inode_unlock(lower_inode); if (rc) printk(KERN_ERR "Error whilst attempting to write inode size " diff --git a/fs/hfs/attr.c b/fs/hfs/attr.c index d9a86919fdf6..0933600e11c8 100644 --- a/fs/hfs/attr.c +++ b/fs/hfs/attr.c @@ -13,9 +13,13 @@ #include "hfs_fs.h" #include "btree.h" -int hfs_setxattr(struct dentry *unused, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) +enum hfs_xattr_type { + HFS_TYPE, + HFS_CREATOR, +}; + +static int __hfs_setxattr(struct inode *inode, enum hfs_xattr_type type, + const void *value, size_t size, int flags) { struct hfs_find_data fd; hfs_cat_rec rec; @@ -36,18 +40,22 @@ int hfs_setxattr(struct dentry *unused, struct inode *inode, sizeof(struct hfs_cat_file)); file = &rec.file; - if (!strcmp(name, "hfs.type")) { + switch (type) { + case HFS_TYPE: if (size == 4) memcpy(&file->UsrWds.fdType, value, 4); else res = -ERANGE; - } else if (!strcmp(name, "hfs.creator")) { + break; + + case HFS_CREATOR: if (size == 4) memcpy(&file->UsrWds.fdCreator, value, 4); else res = -ERANGE; - } else - res = -EOPNOTSUPP; + break; + } + if (!res) hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); @@ -56,8 +64,8 @@ out: return res; } -ssize_t hfs_getxattr(struct dentry *unused, struct inode *inode, - const char *name, void *value, size_t size) +static ssize_t __hfs_getxattr(struct inode *inode, enum hfs_xattr_type type, + void *value, size_t size) { struct hfs_find_data fd; hfs_cat_rec rec; @@ -80,41 +88,64 @@ ssize_t hfs_getxattr(struct dentry *unused, struct inode *inode, } file = &rec.file; - if (!strcmp(name, "hfs.type")) { + switch (type) { + case HFS_TYPE: if (size >= 4) { memcpy(value, &file->UsrWds.fdType, 4); res = 4; } else res = size ? -ERANGE : 4; - } else if (!strcmp(name, "hfs.creator")) { + break; + + case HFS_CREATOR: if (size >= 4) { memcpy(value, &file->UsrWds.fdCreator, 4); res = 4; } else res = size ? -ERANGE : 4; - } else - res = -ENODATA; + break; + } + out: if (size) hfs_find_exit(&fd); return res; } -#define HFS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type")) - -ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +static int hfs_xattr_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, void *value, size_t size) { - struct inode *inode = d_inode(dentry); + return __hfs_getxattr(inode, handler->flags, value, size); +} - if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) +static int hfs_xattr_set(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + if (!value) return -EOPNOTSUPP; - if (!buffer || !size) - return HFS_ATTRLIST_SIZE; - if (size < HFS_ATTRLIST_SIZE) - return -ERANGE; - strcpy(buffer, "hfs.type"); - strcpy(buffer + sizeof("hfs.type"), "hfs.creator"); - - return HFS_ATTRLIST_SIZE; + return __hfs_setxattr(inode, handler->flags, value, size, flags); } + +static const struct xattr_handler hfs_creator_handler = { + .name = "hfs.creator", + .flags = HFS_CREATOR, + .get = hfs_xattr_get, + .set = hfs_xattr_set, +}; + +static const struct xattr_handler hfs_type_handler = { + .name = "hfs.type", + .flags = HFS_TYPE, + .get = hfs_xattr_get, + .set = hfs_xattr_set, +}; + +const struct xattr_handler *hfs_xattr_handlers[] = { + &hfs_creator_handler, + &hfs_type_handler, + NULL +}; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 16f5172ee40d..4cdec5a19347 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -212,11 +212,7 @@ extern void hfs_evict_inode(struct inode *); extern void hfs_delete_inode(struct inode *); /* attr.c */ -extern int hfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, - const void *value, size_t size, int flags); -extern ssize_t hfs_getxattr(struct dentry *dentry, struct inode *inode, - const char *name, void *value, size_t size); -extern ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size); +extern const struct xattr_handler *hfs_xattr_handlers[]; /* mdb.c */ extern int hfs_mdb_get(struct super_block *); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index c6a32415735b..7f44f1a4e4c0 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -15,6 +15,7 @@ #include <linux/mpage.h> #include <linux/sched.h> #include <linux/uio.h> +#include <linux/xattr.h> #include "hfs_fs.h" #include "btree.h" @@ -687,7 +688,7 @@ static const struct file_operations hfs_file_operations = { static const struct inode_operations hfs_file_inode_operations = { .lookup = hfs_file_lookup, .setattr = hfs_inode_setattr, - .setxattr = hfs_setxattr, - .getxattr = hfs_getxattr, - .listxattr = hfs_listxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = generic_listxattr, }; diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 1ca95c232bb5..bf6304a350a6 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -406,6 +406,7 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent) } sb->s_op = &hfs_super_operations; + sb->s_xattr = hfs_xattr_handlers; sb->s_flags |= MS_NODIRATIME; mutex_init(&sbi->bitmap_lock); diff --git a/fs/inode.c b/fs/inode.c index 7e3ef3af3db9..705f8609fdb8 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -140,6 +140,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_fop = &no_open_fops; inode->__i_nlink = 1; inode->i_opflags = 0; + if (sb->s_xattr) + inode->i_opflags |= IOP_XATTR; i_uid_write(inode, 0); i_gid_write(inode, 0); atomic_set(&inode->i_writecount, 0); diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 30eb33ff8189..8aa2f8936c3a 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -61,10 +61,10 @@ const struct inode_operations jffs2_dir_inode_operations = .get_acl = jffs2_get_acl, .set_acl = jffs2_set_acl, .setattr = jffs2_setattr, - .setxattr = jffs2_setxattr, - .getxattr = jffs2_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = jffs2_listxattr, - .removexattr = jffs2_removexattr + .removexattr = generic_removexattr }; /***********************************************************************/ diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 0e62dec3effc..fdf9e1cb2e48 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -66,10 +66,10 @@ const struct inode_operations jffs2_file_inode_operations = .get_acl = jffs2_get_acl, .set_acl = jffs2_set_acl, .setattr = jffs2_setattr, - .setxattr = jffs2_setxattr, - .getxattr = jffs2_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = jffs2_listxattr, - .removexattr = jffs2_removexattr + .removexattr = generic_removexattr }; const struct address_space_operations jffs2_file_address_operations = diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 2cabd649d4fb..afe2d75ae395 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -16,8 +16,8 @@ const struct inode_operations jffs2_symlink_inode_operations = .readlink = generic_readlink, .get_link = simple_get_link, .setattr = jffs2_setattr, - .setxattr = jffs2_setxattr, - .getxattr = jffs2_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = jffs2_listxattr, - .removexattr = jffs2_removexattr + .removexattr = generic_removexattr }; diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 467ff376ee26..720007b2fd65 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -99,9 +99,6 @@ extern const struct xattr_handler jffs2_user_xattr_handler; extern const struct xattr_handler jffs2_trusted_xattr_handler; extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); -#define jffs2_getxattr generic_getxattr -#define jffs2_setxattr generic_setxattr -#define jffs2_removexattr generic_removexattr #else @@ -116,9 +113,6 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); #define jffs2_xattr_handlers NULL #define jffs2_listxattr NULL -#define jffs2_getxattr NULL -#define jffs2_setxattr NULL -#define jffs2_removexattr NULL #endif /* CONFIG_JFFS2_FS_XATTR */ diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e57174d43683..a6e430adf67d 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -1126,9 +1126,9 @@ const struct inode_operations kernfs_dir_iops = { .permission = kernfs_iop_permission, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, - .setxattr = kernfs_iop_setxattr, - .removexattr = kernfs_iop_removexattr, - .getxattr = kernfs_iop_getxattr, + .setxattr = generic_setxattr, + .removexattr = generic_removexattr, + .getxattr = generic_getxattr, .listxattr = kernfs_iop_listxattr, .mkdir = kernfs_iop_mkdir, diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 63b925d5ba1e..6bc87547dede 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -28,9 +28,9 @@ static const struct inode_operations kernfs_iops = { .permission = kernfs_iop_permission, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, - .setxattr = kernfs_iop_setxattr, - .removexattr = kernfs_iop_removexattr, - .getxattr = kernfs_iop_getxattr, + .setxattr = generic_setxattr, + .removexattr = generic_removexattr, + .getxattr = generic_getxattr, .listxattr = kernfs_iop_listxattr, }; @@ -138,17 +138,12 @@ out: return error; } -static int kernfs_node_setsecdata(struct kernfs_node *kn, void **secdata, +static int kernfs_node_setsecdata(struct kernfs_iattrs *attrs, void **secdata, u32 *secdata_len) { - struct kernfs_iattrs *attrs; void *old_secdata; size_t old_secdata_len; - attrs = kernfs_iattrs(kn); - if (!attrs) - return -ENOMEM; - old_secdata = attrs->ia_secdata; old_secdata_len = attrs->ia_secdata_len; @@ -160,71 +155,6 @@ static int kernfs_node_setsecdata(struct kernfs_node *kn, void **secdata, return 0; } -int kernfs_iop_setxattr(struct dentry *unused, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) -{ - struct kernfs_node *kn = inode->i_private; - struct kernfs_iattrs *attrs; - void *secdata; - int error; - u32 secdata_len = 0; - - attrs = kernfs_iattrs(kn); - if (!attrs) - return -ENOMEM; - - if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { - const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; - error = security_inode_setsecurity(inode, suffix, - value, size, flags); - if (error) - return error; - error = security_inode_getsecctx(inode, - &secdata, &secdata_len); - if (error) - return error; - - mutex_lock(&kernfs_mutex); - error = kernfs_node_setsecdata(kn, &secdata, &secdata_len); - mutex_unlock(&kernfs_mutex); - - if (secdata) - security_release_secctx(secdata, secdata_len); - return error; - } else if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) { - return simple_xattr_set(&attrs->xattrs, name, value, size, - flags); - } - - return -EINVAL; -} - -int kernfs_iop_removexattr(struct dentry *dentry, const char *name) -{ - struct kernfs_node *kn = dentry->d_fsdata; - struct kernfs_iattrs *attrs; - - attrs = kernfs_iattrs(kn); - if (!attrs) - return -ENOMEM; - - return simple_xattr_set(&attrs->xattrs, name, NULL, 0, XATTR_REPLACE); -} - -ssize_t kernfs_iop_getxattr(struct dentry *unused, struct inode *inode, - const char *name, void *buf, size_t size) -{ - struct kernfs_node *kn = inode->i_private; - struct kernfs_iattrs *attrs; - - attrs = kernfs_iattrs(kn); - if (!attrs) - return -ENOMEM; - - return simple_xattr_get(&attrs->xattrs, name, buf, size); -} - ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size) { struct kernfs_node *kn = dentry->d_fsdata; @@ -376,3 +306,83 @@ int kernfs_iop_permission(struct inode *inode, int mask) return generic_permission(inode, mask); } + +static int kernfs_xattr_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *suffix, void *value, size_t size) +{ + const char *name = xattr_full_name(handler, suffix); + struct kernfs_node *kn = inode->i_private; + struct kernfs_iattrs *attrs; + + attrs = kernfs_iattrs(kn); + if (!attrs) + return -ENOMEM; + + return simple_xattr_get(&attrs->xattrs, name, value, size); +} + +static int kernfs_xattr_set(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *suffix, const void *value, + size_t size, int flags) +{ + const char *name = xattr_full_name(handler, suffix); + struct kernfs_node *kn = inode->i_private; + struct kernfs_iattrs *attrs; + + attrs = kernfs_iattrs(kn); + if (!attrs) + return -ENOMEM; + + return simple_xattr_set(&attrs->xattrs, name, value, size, flags); +} + +const struct xattr_handler kernfs_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .get = kernfs_xattr_get, + .set = kernfs_xattr_set, +}; + +static int kernfs_security_xattr_set(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *suffix, const void *value, + size_t size, int flags) +{ + struct kernfs_node *kn = inode->i_private; + struct kernfs_iattrs *attrs; + void *secdata; + u32 secdata_len = 0; + int error; + + attrs = kernfs_iattrs(kn); + if (!attrs) + return -ENOMEM; + + error = security_inode_setsecurity(inode, suffix, value, size, flags); + if (error) + return error; + error = security_inode_getsecctx(inode, &secdata, &secdata_len); + if (error) + return error; + + mutex_lock(&kernfs_mutex); + error = kernfs_node_setsecdata(attrs, &secdata, &secdata_len); + mutex_unlock(&kernfs_mutex); + + if (secdata) + security_release_secctx(secdata, secdata_len); + return error; +} + +const struct xattr_handler kernfs_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .get = kernfs_xattr_get, + .set = kernfs_security_xattr_set, +}; + +const struct xattr_handler *kernfs_xattr_handlers[] = { + &kernfs_trusted_xattr_handler, + &kernfs_security_xattr_handler, + NULL +}; diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 37159235ac10..bfd551bbf231 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -76,17 +76,12 @@ extern struct kmem_cache *kernfs_node_cache; /* * inode.c */ +extern const struct xattr_handler *kernfs_xattr_handlers[]; void kernfs_evict_inode(struct inode *inode); int kernfs_iop_permission(struct inode *inode, int mask); int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr); int kernfs_iop_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -int kernfs_iop_setxattr(struct dentry *dentry, struct inode *inode, - const char *name, const void *value, - size_t size, int flags); -int kernfs_iop_removexattr(struct dentry *dentry, const char *name); -ssize_t kernfs_iop_getxattr(struct dentry *dentry, struct inode *inode, - const char *name, void *buf, size_t size); ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size); /* diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index b3d73ad52b22..d5b149a45be1 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -158,6 +158,7 @@ static int kernfs_fill_super(struct super_block *sb, unsigned long magic) sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = magic; sb->s_op = &kernfs_sops; + sb->s_xattr = kernfs_xattr_handlers; sb->s_time_gran = 1; /* get root inode, initialize and unlock it */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 117b8b3416f9..549a14c7c50a 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -134,9 +134,9 @@ static const char *kernfs_iop_get_link(struct dentry *dentry, } const struct inode_operations kernfs_symlink_iops = { - .setxattr = kernfs_iop_setxattr, - .removexattr = kernfs_iop_removexattr, - .getxattr = kernfs_iop_getxattr, + .setxattr = generic_setxattr, + .removexattr = generic_removexattr, + .getxattr = generic_getxattr, .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .get_link = kernfs_iop_get_link, diff --git a/fs/libfs.c b/fs/libfs.c index 74dc8b9e7f53..b322d756b20d 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -236,8 +236,8 @@ static const struct super_operations simple_super_operations = { * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that * will never be mountable) */ -struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name, - const struct super_operations *ops, +struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name, + const struct super_operations *ops, const struct xattr_handler **xattr, const struct dentry_operations *dops, unsigned long magic) { struct super_block *s; @@ -254,6 +254,7 @@ struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name, s->s_blocksize_bits = PAGE_SHIFT; s->s_magic = magic; s->s_op = ops ? ops : &simple_super_operations; + s->s_xattr = xattr; s->s_time_gran = 1; root = new_inode(s); if (!root) @@ -281,7 +282,7 @@ Enomem: deactivate_locked_super(s); return ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL(mount_pseudo); +EXPORT_SYMBOL(mount_pseudo_xattr); int simple_open(struct inode *inode, struct file *file) { @@ -1149,24 +1150,6 @@ static int empty_dir_setattr(struct dentry *dentry, struct iattr *attr) return -EPERM; } -static int empty_dir_setxattr(struct dentry *dentry, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -static ssize_t empty_dir_getxattr(struct dentry *dentry, struct inode *inode, - const char *name, void *value, size_t size) -{ - return -EOPNOTSUPP; -} - -static int empty_dir_removexattr(struct dentry *dentry, const char *name) -{ - return -EOPNOTSUPP; -} - static ssize_t empty_dir_listxattr(struct dentry *dentry, char *list, size_t size) { return -EOPNOTSUPP; @@ -1177,9 +1160,6 @@ static const struct inode_operations empty_dir_inode_operations = { .permission = generic_permission, .setattr = empty_dir_setattr, .getattr = empty_dir_getattr, - .setxattr = empty_dir_setxattr, - .getxattr = empty_dir_getxattr, - .removexattr = empty_dir_removexattr, .listxattr = empty_dir_listxattr, }; @@ -1215,6 +1195,7 @@ void make_empty_dir_inode(struct inode *inode) inode->i_blocks = 0; inode->i_op = &empty_dir_inode_operations; + inode->i_opflags &= ~IOP_XATTR; inode->i_fop = &empty_dir_operations; } diff --git a/fs/orangefs/xattr.c b/fs/orangefs/xattr.c index 2a9f07f06d10..74a81b1daaac 100644 --- a/fs/orangefs/xattr.c +++ b/fs/orangefs/xattr.c @@ -73,6 +73,9 @@ ssize_t orangefs_inode_getxattr(struct inode *inode, const char *name, "%s: name %s, buffer_size %zd\n", __func__, name, size); + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + if (strlen(name) >= ORANGEFS_MAX_XATTR_NAMELEN) { gossip_err("Invalid key length (%d)\n", (int)strlen(name)); diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 43fdc2765aea..807951cb438c 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -58,8 +58,8 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) char *buf, *name, *value = NULL; int uninitialized_var(error); - if (!old->d_inode->i_op->getxattr || - !new->d_inode->i_op->getxattr) + if (!(old->d_inode->i_opflags & IOP_XATTR) || + !(new->d_inode->i_opflags & IOP_XATTR)) return 0; list_size = vfs_listxattr(old, NULL, 0); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e2a94a26767b..f170114481f7 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -275,10 +275,10 @@ static bool ovl_is_opaquedir(struct dentry *dentry) char val; struct inode *inode = dentry->d_inode; - if (!S_ISDIR(inode->i_mode) || !inode->i_op->getxattr) + if (!S_ISDIR(inode->i_mode) || !(inode->i_opflags & IOP_XATTR)) return false; - res = inode->i_op->getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1); + res = __vfs_getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1); if (res == 1 && val == 'y') return true; diff --git a/fs/xattr.c b/fs/xattr.c index c243905835ab..2432442656a2 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -24,6 +24,59 @@ #include <asm/uaccess.h> +static const char * +strcmp_prefix(const char *a, const char *a_prefix) +{ + while (*a_prefix && *a == *a_prefix) { + a++; + a_prefix++; + } + return *a_prefix ? NULL : a; +} + +/* + * In order to implement different sets of xattr operations for each xattr + * prefix, a filesystem should create a null-terminated array of struct + * xattr_handler (one for each prefix) and hang a pointer to it off of the + * s_xattr field of the superblock. + */ +#define for_each_xattr_handler(handlers, handler) \ + if (handlers) \ + for ((handler) = *(handlers)++; \ + (handler) != NULL; \ + (handler) = *(handlers)++) + +/* + * Find the xattr_handler with the matching prefix. + */ +static const struct xattr_handler * +xattr_resolve_name(struct inode *inode, const char **name) +{ + const struct xattr_handler **handlers = inode->i_sb->s_xattr; + const struct xattr_handler *handler; + + if (!(inode->i_opflags & IOP_XATTR)) { + if (unlikely(is_bad_inode(inode))) + return ERR_PTR(-EIO); + return ERR_PTR(-EOPNOTSUPP); + } + for_each_xattr_handler(handlers, handler) { + const char *n; + + n = strcmp_prefix(*name, xattr_prefix(handler)); + if (n) { + if (!handler->prefix ^ !*n) { + if (*n) + continue; + return ERR_PTR(-EINVAL); + } + *name = n; + return handler; + } + } + return ERR_PTR(-EOPNOTSUPP); +} + /* * Check permissions for extended attribute access. This is a bit complicated * because different namespaces have very different rules. @@ -80,6 +133,23 @@ xattr_permission(struct inode *inode, const char *name, int mask) return inode_permission(inode, mask); } +int +__vfs_setxattr(struct dentry *dentry, struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + const struct xattr_handler *handler; + + handler = xattr_resolve_name(inode, &name); + if (IS_ERR(handler)) + return PTR_ERR(handler); + if (!handler->set) + return -EOPNOTSUPP; + if (size == 0) + value = ""; /* empty EA, do not remove */ + return handler->set(handler, dentry, inode, name, value, size, flags); +} +EXPORT_SYMBOL(__vfs_setxattr); + /** * __vfs_setxattr_noperm - perform setxattr operation without performing * permission checks. @@ -106,8 +176,8 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, if (issec) inode->i_flags &= ~S_NOSEC; - if (inode->i_op->setxattr) { - error = inode->i_op->setxattr(dentry, inode, name, value, size, flags); + if (inode->i_opflags & IOP_XATTR) { + error = __vfs_setxattr(dentry, inode, name, value, size, flags); if (!error) { fsnotify_xattr(dentry); security_inode_post_setxattr(dentry, name, value, @@ -115,6 +185,9 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, } } else if (issec) { const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; + + if (unlikely(is_bad_inode(inode))) + return -EIO; error = security_inode_setsecurity(inode, suffix, value, size, flags); if (!error) @@ -188,6 +261,7 @@ ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, size_t xattr_size, gfp_t flags) { + const struct xattr_handler *handler; struct inode *inode = dentry->d_inode; char *value = *xattr_value; int error; @@ -196,10 +270,12 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, if (error) return error; - if (!inode->i_op->getxattr) + handler = xattr_resolve_name(inode, &name); + if (IS_ERR(handler)) + return PTR_ERR(handler); + if (!handler->get) return -EOPNOTSUPP; - - error = inode->i_op->getxattr(dentry, inode, name, NULL, 0); + error = handler->get(handler, dentry, inode, name, NULL, 0); if (error < 0) return error; @@ -210,12 +286,27 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, memset(value, 0, error + 1); } - error = inode->i_op->getxattr(dentry, inode, name, value, error); + error = handler->get(handler, dentry, inode, name, value, error); *xattr_value = value; return error; } ssize_t +__vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, + void *value, size_t size) +{ + const struct xattr_handler *handler; + + handler = xattr_resolve_name(inode, &name); + if (IS_ERR(handler)) + return PTR_ERR(handler); + if (!handler->get) + return -EOPNOTSUPP; + return handler->get(handler, dentry, inode, name, value, size); +} +EXPORT_SYMBOL(__vfs_getxattr); + +ssize_t vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct inode *inode = dentry->d_inode; @@ -242,28 +333,24 @@ vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) return ret; } nolsm: - if (inode->i_op->getxattr) - error = inode->i_op->getxattr(dentry, inode, name, value, size); - else - error = -EOPNOTSUPP; - - return error; + return __vfs_getxattr(dentry, inode, name, value, size); } EXPORT_SYMBOL_GPL(vfs_getxattr); ssize_t -vfs_listxattr(struct dentry *d, char *list, size_t size) +vfs_listxattr(struct dentry *dentry, char *list, size_t size) { + struct inode *inode = d_inode(dentry); ssize_t error; - error = security_inode_listxattr(d); + error = security_inode_listxattr(dentry); if (error) return error; - error = -EOPNOTSUPP; - if (d->d_inode->i_op->listxattr) { - error = d->d_inode->i_op->listxattr(d, list, size); + if (inode->i_op->listxattr && (inode->i_opflags & IOP_XATTR)) { + error = -EOPNOTSUPP; + error = inode->i_op->listxattr(dentry, list, size); } else { - error = security_inode_listsecurity(d->d_inode, list, size); + error = security_inode_listsecurity(inode, list, size); if (size && error > size) error = -ERANGE; } @@ -272,14 +359,26 @@ vfs_listxattr(struct dentry *d, char *list, size_t size) EXPORT_SYMBOL_GPL(vfs_listxattr); int +__vfs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = d_inode(dentry); + const struct xattr_handler *handler; + + handler = xattr_resolve_name(inode, &name); + if (IS_ERR(handler)) + return PTR_ERR(handler); + if (!handler->set) + return -EOPNOTSUPP; + return handler->set(handler, dentry, inode, name, NULL, 0, XATTR_REPLACE); +} +EXPORT_SYMBOL(__vfs_removexattr); + +int vfs_removexattr(struct dentry *dentry, const char *name) { struct inode *inode = dentry->d_inode; int error; - if (!inode->i_op->removexattr) - return -EOPNOTSUPP; - error = xattr_permission(inode, name, MAY_WRITE); if (error) return error; @@ -289,7 +388,7 @@ vfs_removexattr(struct dentry *dentry, const char *name) if (error) goto out; - error = inode->i_op->removexattr(dentry, name); + error = __vfs_removexattr(dentry, name); if (!error) { fsnotify_xattr(dentry); @@ -641,60 +740,6 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) return error; } - -static const char * -strcmp_prefix(const char *a, const char *a_prefix) -{ - while (*a_prefix && *a == *a_prefix) { - a++; - a_prefix++; - } - return *a_prefix ? NULL : a; -} - -/* - * In order to implement different sets of xattr operations for each xattr - * prefix with the generic xattr API, a filesystem should create a - * null-terminated array of struct xattr_handler (one for each prefix) and - * hang a pointer to it off of the s_xattr field of the superblock. - * - * The generic_fooxattr() functions will use this list to dispatch xattr - * operations to the correct xattr_handler. - */ -#define for_each_xattr_handler(handlers, handler) \ - if (handlers) \ - for ((handler) = *(handlers)++; \ - (handler) != NULL; \ - (handler) = *(handlers)++) - -/* - * Find the xattr_handler with the matching prefix. - */ -static const struct xattr_handler * -xattr_resolve_name(const struct xattr_handler **handlers, const char **name) -{ - const struct xattr_handler *handler; - - if (!*name) - return ERR_PTR(-EINVAL); - - for_each_xattr_handler(handlers, handler) { - const char *n; - - n = strcmp_prefix(*name, xattr_prefix(handler)); - if (n) { - if (!handler->prefix ^ !*n) { - if (*n) - continue; - return ERR_PTR(-EINVAL); - } - *name = n; - return handler; - } - } - return ERR_PTR(-EOPNOTSUPP); -} - /* * Find the handler for the prefix and dispatch its get() operation. */ @@ -704,7 +749,7 @@ generic_getxattr(struct dentry *dentry, struct inode *inode, { const struct xattr_handler *handler; - handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); + handler = xattr_resolve_name(inode, &name); if (IS_ERR(handler)) return PTR_ERR(handler); return handler->get(handler, dentry, inode, @@ -759,7 +804,7 @@ generic_setxattr(struct dentry *dentry, struct inode *inode, const char *name, if (size == 0) value = ""; /* empty EA, do not remove */ - handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); + handler = xattr_resolve_name(inode, &name); if (IS_ERR(handler)) return PTR_ERR(handler); return handler->set(handler, dentry, inode, name, value, size, flags); @@ -774,7 +819,7 @@ generic_removexattr(struct dentry *dentry, const char *name) { const struct xattr_handler *handler; - handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); + handler = xattr_resolve_name(d_inode(dentry), &name); if (IS_ERR(handler)) return PTR_ERR(handler); return handler->set(handler, dentry, d_inode(dentry), name, NULL, |