summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-10 17:11:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-10 17:11:50 -0700
commit97d2116708ca0fd6ad8b00811ee4349b7e19e96f (patch)
tree81f73fc1a6daee60737b591cf1be73cc4f79de37
parent30066ce675d3af350bc5a53858991c0b518dda00 (diff)
parentfd50ecaddf8372a1d96e0daeaac0f93cf04e4d42 (diff)
Merge branch 'work.xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs xattr updates from Al Viro: "xattr stuff from Andreas This completes the switch to xattr_handler ->get()/->set() from ->getxattr/->setxattr/->removexattr" * 'work.xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: vfs: Remove {get,set,remove}xattr inode operations xattr: Stop calling {get,set,remove}xattr inode operations vfs: Check for the IOP_XATTR flag in listxattr xattr: Add __vfs_{get,set,remove}xattr helpers libfs: Use IOP_XATTR flag for empty directory handling vfs: Use IOP_XATTR flag for bad-inode handling vfs: Add IOP_XATTR inode operations flag vfs: Move xattr_resolve_name to the front of fs/xattr.c ecryptfs: Switch to generic xattr handlers sockfs: Get rid of getxattr iop sockfs: getxattr: Fail with -EOPNOTSUPP for invalid attribute names kernfs: Switch to generic xattr handlers hfs: Switch to generic xattr handlers jffs2: Remove jffs2_{get,set,remove}xattr macros xattr: Remove unnecessary NULL attribute name check
-rw-r--r--Documentation/filesystems/Locking24
-rw-r--r--Documentation/filesystems/vfs.txt45
-rw-r--r--drivers/staging/lustre/lustre/llite/file.c3
-rw-r--r--drivers/staging/lustre/lustre/llite/namei.c6
-rw-r--r--drivers/staging/lustre/lustre/llite/symlink.c3
-rw-r--r--fs/9p/vfs_inode_dotl.c9
-rw-r--r--fs/bad_inode.c21
-rw-r--r--fs/btrfs/inode.c12
-rw-r--r--fs/cachefiles/bind.c4
-rw-r--r--fs/cachefiles/namei.c4
-rw-r--r--fs/ceph/dir.c3
-rw-r--r--fs/ceph/inode.c6
-rw-r--r--fs/cifs/cifsfs.c9
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h2
-rw-r--r--fs/ecryptfs/inode.c67
-rw-r--r--fs/ecryptfs/main.c1
-rw-r--r--fs/ecryptfs/mmap.c13
-rw-r--r--fs/ext2/file.c3
-rw-r--r--fs/ext2/namei.c6
-rw-r--r--fs/ext2/symlink.c6
-rw-r--r--fs/ext4/file.c3
-rw-r--r--fs/ext4/namei.c6
-rw-r--r--fs/ext4/symlink.c9
-rw-r--r--fs/f2fs/file.c3
-rw-r--r--fs/f2fs/namei.c12
-rw-r--r--fs/fuse/dir.c9
-rw-r--r--fs/gfs2/inode.c9
-rw-r--r--fs/hfs/attr.c83
-rw-r--r--fs/hfs/hfs_fs.h6
-rw-r--r--fs/hfs/inode.c5
-rw-r--r--fs/hfs/super.c1
-rw-r--r--fs/hfsplus/dir.c3
-rw-r--r--fs/hfsplus/inode.c3
-rw-r--r--fs/inode.c2
-rw-r--r--fs/jffs2/dir.c3
-rw-r--r--fs/jffs2/file.c3
-rw-r--r--fs/jffs2/symlink.c3
-rw-r--r--fs/jffs2/xattr.h6
-rw-r--r--fs/jfs/file.c3
-rw-r--r--fs/jfs/namei.c3
-rw-r--r--fs/jfs/symlink.c6
-rw-r--r--fs/kernfs/dir.c3
-rw-r--r--fs/kernfs/inode.c155
-rw-r--r--fs/kernfs/kernfs-internal.h7
-rw-r--r--fs/kernfs/mount.c1
-rw-r--r--fs/kernfs/symlink.c3
-rw-r--r--fs/libfs.c29
-rw-r--r--fs/nfs/nfs3proc.c6
-rw-r--r--fs/nfs/nfs4proc.c6
-rw-r--r--fs/ocfs2/file.c3
-rw-r--r--fs/ocfs2/namei.c3
-rw-r--r--fs/ocfs2/symlink.c3
-rw-r--r--fs/orangefs/inode.c3
-rw-r--r--fs/orangefs/namei.c3
-rw-r--r--fs/orangefs/symlink.c1
-rw-r--r--fs/orangefs/xattr.c3
-rw-r--r--fs/overlayfs/copy_up.c4
-rw-r--r--fs/overlayfs/dir.c3
-rw-r--r--fs/overlayfs/inode.c6
-rw-r--r--fs/overlayfs/super.c4
-rw-r--r--fs/reiserfs/file.c3
-rw-r--r--fs/reiserfs/namei.c9
-rw-r--r--fs/squashfs/inode.c1
-rw-r--r--fs/squashfs/namei.c1
-rw-r--r--fs/squashfs/symlink.c1
-rw-r--r--fs/squashfs/xattr.h1
-rw-r--r--fs/ubifs/dir.c3
-rw-r--r--fs/ubifs/file.c6
-rw-r--r--fs/xattr.c250
-rw-r--r--fs/xfs/xfs_iops.c15
-rw-r--r--include/linux/fs.h23
-rw-r--r--include/linux/xattr.h7
-rw-r--r--mm/shmem.c15
-rw-r--r--net/socket.c61
-rw-r--r--security/commoncap.c25
-rw-r--r--security/integrity/evm/evm_crypto.c7
-rw-r--r--security/integrity/evm/evm_main.c4
-rw-r--r--security/integrity/ima/ima_appraise.c21
-rw-r--r--security/selinux/hooks.c19
-rw-r--r--security/smack/smack_lsm.c12
80 files changed, 471 insertions, 687 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index d30fb2cb5066..f56b39ee2e54 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -61,10 +61,7 @@ prototypes:
int (*get_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
- int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
- ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
- int (*removexattr) (struct dentry *, const char *);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
void (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *,
@@ -91,15 +88,13 @@ setattr: yes
permission: no (may not block if called in rcu-walk mode)
get_acl: no
getattr: no
-setxattr: yes
-getxattr: no
listxattr: no
-removexattr: yes
fiemap: no
update_time: no
atomic_open: yes
tmpfile: no
+
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
victim.
cross-directory ->rename() and rename2() has (per-superblock)
@@ -108,6 +103,23 @@ victim.
See Documentation/filesystems/directory-locking for more detailed discussion
of the locking scheme for directory operations.
+----------------------- xattr_handler operations -----------------------
+prototypes:
+ bool (*list)(struct dentry *dentry);
+ int (*get)(const struct xattr_handler *handler, struct dentry *dentry,
+ struct inode *inode, const char *name, void *buffer,
+ size_t size);
+ int (*set)(const struct xattr_handler *handler, struct dentry *dentry,
+ struct inode *inode, const char *name, const void *buffer,
+ size_t size, int flags);
+
+locking rules:
+ all may block
+ i_mutex(inode)
+list: no
+get: no
+set: yes
+
--------------------------- super_operations ---------------------------
prototypes:
struct inode *(*alloc_inode)(struct super_block *sb);
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index cbec006e10e4..43ef86c266ec 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -323,6 +323,35 @@ Whoever sets up the inode is responsible for filling in the "i_op" field. This
is a pointer to a "struct inode_operations" which describes the methods that
can be performed on individual inodes.
+struct xattr_handlers
+---------------------
+
+On filesystems that support extended attributes (xattrs), the s_xattr
+superblock field points to a NULL-terminated array of xattr handlers. Extended
+attributes are name:value pairs.
+
+ name: Indicates that the handler matches attributes with the specified name
+ (such as "system.posix_acl_access"); the prefix field must be NULL.
+
+ prefix: Indicates that the handler matches all attributes with the specified
+ name prefix (such as "user."); the name field must be NULL.
+
+ list: Determine if attributes matching this xattr handler should be listed
+ for a particular dentry. Used by some listxattr implementations like
+ generic_listxattr.
+
+ get: Called by the VFS to get the value of a particular extended attribute.
+ This method is called by the getxattr(2) system call.
+
+ set: Called by the VFS to set the value of a particular extended attribute.
+ When the new value is NULL, called to remove a particular extended
+ attribute. This method is called by the the setxattr(2) and
+ removexattr(2) system calls.
+
+When none of the xattr handlers of a filesystem match the specified attribute
+name or when a filesystem doesn't support extended attributes, the various
+*xattr(2) system calls return -EOPNOTSUPP.
+
The Inode Object
================
@@ -356,10 +385,7 @@ struct inode_operations {
int (*get_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
- int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
- ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
- int (*removexattr) (struct dentry *, const char *);
void (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode, int *opened);
@@ -463,19 +489,8 @@ otherwise noted.
getattr: called by the VFS to get attributes of a file. This method
is called by stat(2) and related system calls.
- setxattr: called by the VFS to set an extended attribute for a file.
- Extended attribute is a name:value pair associated with an
- inode. This method is called by setxattr(2) system call.
-
- getxattr: called by the VFS to retrieve the value of an extended
- attribute name. This method is called by getxattr(2) function
- call.
-
listxattr: called by the VFS to list all extended attributes for a
- given file. This method is called by listxattr(2) system call.
-
- removexattr: called by the VFS to remove an extended attribute from
- a file. This method is called by removexattr(2) system call.
+ given file. This method is called by the listxattr(2) system call.
update_time: called by the VFS to update a specific time or the i_version of
an inode. If this is not defined the VFS will update the inode itself
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
index 65c945d8bae2..e1d784bae064 100644
--- a/drivers/staging/lustre/lustre/llite/file.c
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -3268,10 +3268,7 @@ const struct inode_operations ll_file_inode_operations = {
.setattr = ll_setattr,
.getattr = ll_getattr,
.permission = ll_inode_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ll_listxattr,
- .removexattr = generic_removexattr,
.fiemap = ll_fiemap,
.get_acl = ll_get_acl,
};
diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c
index dfa36d34c645..ff3ce7486b29 100644
--- a/drivers/staging/lustre/lustre/llite/namei.c
+++ b/drivers/staging/lustre/lustre/llite/namei.c
@@ -1152,10 +1152,7 @@ const struct inode_operations ll_dir_inode_operations = {
.setattr = ll_setattr,
.getattr = ll_getattr,
.permission = ll_inode_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ll_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ll_get_acl,
};
@@ -1163,9 +1160,6 @@ const struct inode_operations ll_special_inode_operations = {
.setattr = ll_setattr,
.getattr = ll_getattr,
.permission = ll_inode_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ll_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ll_get_acl,
};
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index f8bc7ed59646..82c7c48aa619 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -154,8 +154,5 @@ const struct inode_operations ll_fast_symlink_inode_operations = {
.get_link = ll_get_link,
.getattr = ll_getattr,
.permission = ll_inode_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ll_listxattr,
- .removexattr = generic_removexattr,
};
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 0e6ad3019711..afaa4b6de801 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -967,9 +967,6 @@ const struct inode_operations v9fs_dir_inode_operations_dotl = {
.rename = v9fs_vfs_rename,
.getattr = v9fs_vfs_getattr_dotl,
.setattr = v9fs_vfs_setattr_dotl,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = v9fs_listxattr,
.get_acl = v9fs_iop_get_acl,
};
@@ -977,9 +974,6 @@ const struct inode_operations v9fs_dir_inode_operations_dotl = {
const struct inode_operations v9fs_file_inode_operations_dotl = {
.getattr = v9fs_vfs_getattr_dotl,
.setattr = v9fs_vfs_setattr_dotl,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = v9fs_listxattr,
.get_acl = v9fs_iop_get_acl,
};
@@ -989,8 +983,5 @@ const struct inode_operations v9fs_symlink_inode_operations_dotl = {
.get_link = v9fs_vfs_get_link_dotl,
.getattr = v9fs_vfs_getattr_dotl,
.setattr = v9fs_vfs_setattr_dotl,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = v9fs_listxattr,
};
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/btrfs/inode.c b/fs/btrfs/inode.c
index a0d3016f6c26..994fe5af160b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -10556,10 +10556,7 @@ static const struct inode_operations btrfs_dir_inode_operations = {
.symlink = btrfs_symlink,
.setattr = btrfs_setattr,
.mknod = btrfs_mknod,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
- .removexattr = generic_removexattr,
.permission = btrfs_permission,
.get_acl = btrfs_get_acl,
.set_acl = btrfs_set_acl,
@@ -10633,10 +10630,7 @@ static const struct address_space_operations btrfs_symlink_aops = {
static const struct inode_operations btrfs_file_inode_operations = {
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
- .removexattr = generic_removexattr,
.permission = btrfs_permission,
.fiemap = btrfs_fiemap,
.get_acl = btrfs_get_acl,
@@ -10647,10 +10641,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
.permission = btrfs_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
- .removexattr = generic_removexattr,
.get_acl = btrfs_get_acl,
.set_acl = btrfs_set_acl,
.update_time = btrfs_update_time,
@@ -10661,10 +10652,7 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
.permission = btrfs_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
- .removexattr = generic_removexattr,
.update_time = btrfs_update_time,
};
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 c6ee4b5fb7e6..339c910da916 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/ceph/dir.c b/fs/ceph/dir.c
index df4b3e6fa563..e33bd0933396 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1486,10 +1486,7 @@ const struct inode_operations ceph_dir_iops = {
.permission = ceph_permission,
.getattr = ceph_getattr,
.setattr = ceph_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ceph_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ceph_get_acl,
.set_acl = ceph_set_acl,
.mknod = ceph_mknod,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 082e82dcbaa4..da00b11d4a7a 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -94,10 +94,7 @@ const struct inode_operations ceph_file_iops = {
.permission = ceph_permission,
.setattr = ceph_setattr,
.getattr = ceph_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ceph_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ceph_get_acl,
.set_acl = ceph_set_acl,
};
@@ -1885,10 +1882,7 @@ static const struct inode_operations ceph_symlink_iops = {
.get_link = simple_get_link,
.setattr = ceph_setattr,
.getattr = ceph_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ceph_listxattr,
- .removexattr = generic_removexattr,
};
int __ceph_setattr(struct inode *inode, struct iattr *attr)
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 14ae4b8e1a3c..34aac1c73ee1 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -901,30 +901,21 @@ const struct inode_operations cifs_dir_inode_ops = {
.setattr = cifs_setattr,
.symlink = cifs_symlink,
.mknod = cifs_mknod,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = cifs_listxattr,
- .removexattr = generic_removexattr,
};
const struct inode_operations cifs_file_inode_ops = {
.setattr = cifs_setattr,
.getattr = cifs_getattr,
.permission = cifs_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = cifs_listxattr,
- .removexattr = generic_removexattr,
};
const struct inode_operations cifs_symlink_inode_ops = {
.readlink = generic_readlink,
.get_link = cifs_get_link,
.permission = cifs_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = cifs_listxattr,
- .removexattr = generic_removexattr,
};
static int cifs_clone_file_range(struct file *src_file, loff_t off,
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 5ffba186f352..ddccec3124d7 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,7 @@ const struct inode_operations ecryptfs_symlink_iops = {
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
.getattr = ecryptfs_getattr_link,
- .setxattr = ecryptfs_setxattr,
- .getxattr = ecryptfs_getxattr,
.listxattr = ecryptfs_listxattr,
- .removexattr = ecryptfs_removexattr
};
const struct inode_operations ecryptfs_dir_iops = {
@@ -1107,18 +1105,43 @@ const struct inode_operations ecryptfs_dir_iops = {
.rename = ecryptfs_rename,
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
- .setxattr = ecryptfs_setxattr,
- .getxattr = ecryptfs_getxattr,
.listxattr = ecryptfs_listxattr,
- .removexattr = ecryptfs_removexattr
};
const struct inode_operations ecryptfs_main_iops = {
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
.getattr = ecryptfs_getattr,
- .setxattr = ecryptfs_setxattr,
- .getxattr = ecryptfs_getxattr,
.listxattr = ecryptfs_listxattr,
- .removexattr = ecryptfs_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/ext2/file.c b/fs/ext2/file.c
index 0ca363d1341c..a0e1478dfd04 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -241,10 +241,7 @@ const struct file_operations ext2_file_operations = {
const struct inode_operations ext2_file_inode_operations = {
#ifdef CONFIG_EXT2_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
- .removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index d446203127fc..ff32ea799496 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -428,10 +428,7 @@ const struct inode_operations ext2_dir_inode_operations = {
.mknod = ext2_mknod,
.rename = ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
- .removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
@@ -441,10 +438,7 @@ const struct inode_operations ext2_dir_inode_operations = {
const struct inode_operations ext2_special_inode_operations = {
#ifdef CONFIG_EXT2_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
- .removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.get_acl = ext2_get_acl,
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 3495d8ae4b33..8437b191bf5d 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -25,10 +25,7 @@ const struct inode_operations ext2_symlink_inode_operations = {
.get_link = page_get_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -37,9 +34,6 @@ const struct inode_operations ext2_fast_symlink_inode_operations = {
.get_link = simple_get_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
- .removexattr = generic_removexattr,
#endif
};
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 36d49cfbf2dc..2a822d30e73f 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -706,10 +706,7 @@ const struct file_operations ext4_file_operations = {
const struct inode_operations ext4_file_inode_operations = {
.setattr = ext4_setattr,
.getattr = ext4_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index c344b819cffa..a73a9196b929 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3880,10 +3880,7 @@ const struct inode_operations ext4_dir_inode_operations = {
.tmpfile = ext4_tmpfile,
.rename2 = ext4_rename2,
.setattr = ext4_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
@@ -3891,10 +3888,7 @@ const struct inode_operations ext4_dir_inode_operations = {
const struct inode_operations ext4_special_inode_operations = {
.setattr = ext4_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
};
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index fdf1c6154745..557b3b0d668c 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -86,28 +86,19 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = ext4_encrypted_get_link,
.setattr = ext4_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
};
const struct inode_operations ext4_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = page_get_link,
.setattr = ext4_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
};
const struct inode_operations ext4_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = simple_get_link,
.setattr = ext4_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
- .removexattr = generic_removexattr,
};
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 90455974c2ae..acdf4b929f97 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -732,10 +732,7 @@ const struct inode_operations f2fs_file_inode_operations = {
.get_acl = f2fs_get_acl,
.set_acl = f2fs_set_acl,
#ifdef CONFIG_F2FS_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
- .removexattr = generic_removexattr,
#endif
.fiemap = f2fs_fiemap,
};
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 5625b879c98a..e80ed0302c22 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -1080,10 +1080,7 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
#ifdef CONFIG_F2FS_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -1103,10 +1100,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
.get_acl = f2fs_get_acl,
.set_acl = f2fs_set_acl,
#ifdef CONFIG_F2FS_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -1116,10 +1110,7 @@ const struct inode_operations f2fs_symlink_inode_operations = {
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
#ifdef CONFIG_F2FS_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -1129,9 +1120,6 @@ const struct inode_operations f2fs_special_inode_operations = {
.get_acl = f2fs_get_acl,
.set_acl = f2fs_set_acl,
#ifdef CONFIG_F2FS_FS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
- .removexattr = generic_removexattr,
#endif
};
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index a430c19607f4..572d12410c7c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1801,10 +1801,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
.mknod = fuse_mknod,
.permission = fuse_permission,
.getattr = fuse_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
- .removexattr = generic_removexattr,
.get_acl = fuse_get_acl,
.set_acl = fuse_set_acl,
};
@@ -1824,10 +1821,7 @@ static const struct inode_operations fuse_common_inode_operations = {
.setattr = fuse_setattr,
.permission = fuse_permission,
.getattr = fuse_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
- .removexattr = generic_removexattr,
.get_acl = fuse_get_acl,
.set_acl = fuse_set_acl,
};
@@ -1837,10 +1831,7 @@ static const struct inode_operations fuse_symlink_inode_operations = {
.get_link = fuse_get_link,
.readlink = generic_readlink,
.getattr = fuse_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = fuse_listxattr,
- .removexattr = generic_removexattr,
};
void fuse_init_common(struct inode *inode)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 7efd1d19d325..f6c4f0058899 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -2040,10 +2040,7 @@ const struct inode_operations gfs2_file_iops = {
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = gfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = gfs2_fiemap,
.get_acl = gfs2_get_acl,
.set_acl = gfs2_set_acl,
@@ -2062,10 +2059,7 @@ const struct inode_operations gfs2_dir_iops = {
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = gfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = gfs2_fiemap,
.get_acl = gfs2_get_acl,
.set_acl = gfs2_set_acl,
@@ -2078,10 +2072,7 @@ const struct inode_operations gfs2_symlink_iops = {
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = gfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = gfs2_fiemap,
};
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 09cce23864da..ed373261f26d 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,5 @@ 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,
+ .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/hfsplus/dir.c b/fs/hfsplus/dir.c
index 42e128661dc1..9cbe43075de5 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -562,10 +562,7 @@ const struct inode_operations hfsplus_dir_inode_operations = {
.symlink = hfsplus_symlink,
.mknod = hfsplus_mknod,
.rename = hfsplus_rename,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = hfsplus_listxattr,
- .removexattr = generic_removexattr,
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
.get_acl = hfsplus_get_posix_acl,
.set_acl = hfsplus_set_posix_acl,
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index c43ef397a3aa..10827c912c4d 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -333,10 +333,7 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
static const struct inode_operations hfsplus_file_inode_operations = {
.setattr = hfsplus_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = hfsplus_listxattr,
- .removexattr = generic_removexattr,
#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
.get_acl = hfsplus_get_posix_acl,
.set_acl = hfsplus_set_posix_acl,
diff --git a/fs/inode.c b/fs/inode.c
index a3c7ba7f6b59..7d037591259d 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..9b242434adf2 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -61,10 +61,7 @@ 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,
.listxattr = jffs2_listxattr,
- .removexattr = jffs2_removexattr
};
/***********************************************************************/
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index 0e62dec3effc..c12476e309c6 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -66,10 +66,7 @@ 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,
.listxattr = jffs2_listxattr,
- .removexattr = jffs2_removexattr
};
const struct address_space_operations jffs2_file_address_operations =
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
index 2cabd649d4fb..8f3f0855fcd2 100644
--- a/fs/jffs2/symlink.c
+++ b/fs/jffs2/symlink.c
@@ -16,8 +16,5 @@ 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,
.listxattr = jffs2_listxattr,
- .removexattr = jffs2_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/jfs/file.c b/fs/jfs/file.c
index cf62037b8a04..739492c7a3fd 100644
--- a/fs/jfs/file.c
+++ b/fs/jfs/file.c
@@ -140,10 +140,7 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
}
const struct inode_operations jfs_file_inode_operations = {
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = jfs_listxattr,
- .removexattr = generic_removexattr,
.setattr = jfs_setattr,
#ifdef CONFIG_JFS_POSIX_ACL
.get_acl = jfs_get_acl,
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 814b0c58016c..e420c6088336 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -1537,10 +1537,7 @@ const struct inode_operations jfs_dir_inode_operations = {
.rmdir = jfs_rmdir,
.mknod = jfs_mknod,
.rename = jfs_rename,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = jfs_listxattr,
- .removexattr = generic_removexattr,
.setattr = jfs_setattr,
#ifdef CONFIG_JFS_POSIX_ACL
.get_acl = jfs_get_acl,
diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c
index c94c7e4a1323..c82404fee6cd 100644
--- a/fs/jfs/symlink.c
+++ b/fs/jfs/symlink.c
@@ -25,19 +25,13 @@ const struct inode_operations jfs_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = simple_get_link,
.setattr = jfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = jfs_listxattr,
- .removexattr = generic_removexattr,
};
const struct inode_operations jfs_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = page_get_link,
.setattr = jfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = jfs_listxattr,
- .removexattr = generic_removexattr,
};
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index e57174d43683..2b23ad91a464 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1126,9 +1126,6 @@ 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,
.listxattr = kernfs_iop_listxattr,
.mkdir = kernfs_iop_mkdir,
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index df21f5b75549..102b6f0bc7af 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -28,9 +28,6 @@ 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,
.listxattr = kernfs_iop_listxattr,
};
@@ -138,17 +135,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 +152,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 +303,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..9b43ca02b7ab 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -134,9 +134,6 @@ 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,
.listxattr = kernfs_iop_listxattr,
.readlink = generic_readlink,
.get_link = kernfs_iop_get_link,
diff --git a/fs/libfs.c b/fs/libfs.c
index 2b3c3ae70153..a6d89f151771 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/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 698be9361280..dc925b531f32 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -899,9 +899,6 @@ static const struct inode_operations nfs3_dir_inode_operations = {
.setattr = nfs_setattr,
#ifdef CONFIG_NFS_V3_ACL
.listxattr = nfs3_listxattr,
- .getxattr = generic_getxattr,
- .setxattr = generic_setxattr,
- .removexattr = generic_removexattr,
.get_acl = nfs3_get_acl,
.set_acl = nfs3_set_acl,
#endif
@@ -913,9 +910,6 @@ static const struct inode_operations nfs3_file_inode_operations = {
.setattr = nfs_setattr,
#ifdef CONFIG_NFS_V3_ACL
.listxattr = nfs3_listxattr,
- .getxattr = generic_getxattr,
- .setxattr = generic_setxattr,
- .removexattr = generic_removexattr,
.get_acl = nfs3_get_acl,
.set_acl = nfs3_set_acl,
#endif
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a9dec32ba9ba..0e327528a3ce 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -8941,20 +8941,14 @@ static const struct inode_operations nfs4_dir_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
- .getxattr = generic_getxattr,
- .setxattr = generic_setxattr,
.listxattr = nfs4_listxattr,
- .removexattr = generic_removexattr,
};
static const struct inode_operations nfs4_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
- .getxattr = generic_getxattr,
- .setxattr = generic_setxattr,
.listxattr = nfs4_listxattr,
- .removexattr = generic_removexattr,
};
const struct nfs_rpc_ops nfs_v4_clientops = {
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 5e1901546868..ba5c177d0ed6 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2444,10 +2444,7 @@ const struct inode_operations ocfs2_file_iops = {
.setattr = ocfs2_setattr,
.getattr = ocfs2_getattr,
.permission = ocfs2_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ocfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = ocfs2_fiemap,
.get_acl = ocfs2_iop_get_acl,
.set_acl = ocfs2_iop_set_acl,
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index a8f1225e6d9b..6cc043ebb9fa 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -2913,10 +2913,7 @@ const struct inode_operations ocfs2_dir_iops = {
.setattr = ocfs2_setattr,
.getattr = ocfs2_getattr,
.permission = ocfs2_permission,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ocfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = ocfs2_fiemap,
.get_acl = ocfs2_iop_get_acl,
.set_acl = ocfs2_iop_set_acl,
diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c
index 6c2a3e3c521c..6ad8eecefe21 100644
--- a/fs/ocfs2/symlink.c
+++ b/fs/ocfs2/symlink.c
@@ -91,9 +91,6 @@ const struct inode_operations ocfs2_symlink_inode_operations = {
.get_link = page_get_link,
.getattr = ocfs2_getattr,
.setattr = ocfs2_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ocfs2_listxattr,
- .removexattr = generic_removexattr,
.fiemap = ocfs2_fiemap,
};
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index c83846fb9b14..0e3bd7e07f88 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -296,10 +296,7 @@ const struct inode_operations orangefs_file_inode_operations = {
.set_acl = orangefs_set_acl,
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = orangefs_listxattr,
- .removexattr = generic_removexattr,
.permission = orangefs_permission,
};
diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c
index 0e34fcfa4d51..4d5576a21c82 100644
--- a/fs/orangefs/namei.c
+++ b/fs/orangefs/namei.c
@@ -462,9 +462,6 @@ const struct inode_operations orangefs_dir_inode_operations = {
.rename = orangefs_rename,
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = orangefs_listxattr,
.permission = orangefs_permission,
};
diff --git a/fs/orangefs/symlink.c b/fs/orangefs/symlink.c
index 8fecf823f5ba..10b0b06e075e 100644
--- a/fs/orangefs/symlink.c
+++ b/fs/orangefs/symlink.c
@@ -14,6 +14,5 @@ const struct inode_operations orangefs_symlink_inode_operations = {
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
.listxattr = orangefs_listxattr,
- .setxattr = generic_setxattr,
.permission = orangefs_permission,
};
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 db37a0e02d32..3f803b3a1f82 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/dir.c b/fs/overlayfs/dir.c
index b0ffa1d1677e..3a60e68ec965 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -1013,10 +1013,7 @@ const struct inode_operations ovl_dir_inode_operations = {
.mknod = ovl_mknod,
.permission = ovl_permission,
.getattr = ovl_dir_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ovl_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ovl_get_acl,
.update_time = ovl_update_time,
};
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 251e5253f2c1..c18d6a4ff456 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -367,10 +367,7 @@ static const struct inode_operations ovl_file_inode_operations = {
.setattr = ovl_setattr,
.permission = ovl_permission,
.getattr = ovl_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ovl_listxattr,
- .removexattr = generic_removexattr,
.get_acl = ovl_get_acl,
.update_time = ovl_update_time,
};
@@ -380,10 +377,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
.get_link = ovl_get_link,
.readlink = ovl_readlink,
.getattr = ovl_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ovl_listxattr,
- .removexattr = generic_removexattr,
.update_time = ovl_update_time,
};
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 3d0b9dee2b76..7e3f0127fc1a 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/reiserfs/file.c b/fs/reiserfs/file.c
index 90f815bdfa8a..2f8c5c9bdaf6 100644
--- a/fs/reiserfs/file.c
+++ b/fs/reiserfs/file.c
@@ -260,10 +260,7 @@ const struct file_operations reiserfs_file_operations = {
const struct inode_operations reiserfs_file_inode_operations = {
.setattr = reiserfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 8a36696d6df9..fd7d0606aa96 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1650,10 +1650,7 @@ const struct inode_operations reiserfs_dir_inode_operations = {
.mknod = reiserfs_mknod,
.rename = reiserfs_rename,
.setattr = reiserfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
@@ -1667,10 +1664,7 @@ const struct inode_operations reiserfs_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = page_get_link,
.setattr = reiserfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = generic_removexattr,
.permission = reiserfs_permission,
};
@@ -1679,10 +1673,7 @@ const struct inode_operations reiserfs_symlink_inode_operations = {
*/
const struct inode_operations reiserfs_special_inode_operations = {
.setattr = reiserfs_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
index 0927b1e80ab6..e9793b1e49a5 100644
--- a/fs/squashfs/inode.c
+++ b/fs/squashfs/inode.c
@@ -425,7 +425,6 @@ failed_read:
const struct inode_operations squashfs_inode_ops = {
- .getxattr = generic_getxattr,
.listxattr = squashfs_listxattr
};
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
index 67cad77fefb4..40c10d9974c9 100644
--- a/fs/squashfs/namei.c
+++ b/fs/squashfs/namei.c
@@ -247,6 +247,5 @@ failed:
const struct inode_operations squashfs_dir_inode_ops = {
.lookup = squashfs_lookup,
- .getxattr = generic_getxattr,
.listxattr = squashfs_listxattr
};
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c
index d688ef42a6a1..79b9c31a0c8f 100644
--- a/fs/squashfs/symlink.c
+++ b/fs/squashfs/symlink.c
@@ -120,7 +120,6 @@ const struct address_space_operations squashfs_symlink_aops = {
const struct inode_operations squashfs_symlink_inode_ops = {
.readlink = generic_readlink,
.get_link = page_get_link,
- .getxattr = generic_getxattr,
.listxattr = squashfs_listxattr
};
diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h
index c83f5d9ec125..afe70f815e3d 100644
--- a/fs/squashfs/xattr.h
+++ b/fs/squashfs/xattr.h
@@ -42,6 +42,5 @@ static inline int squashfs_xattr_lookup(struct super_block *sb,
return 0;
}
#define squashfs_listxattr NULL
-#define generic_getxattr NULL
#define squashfs_xattr_handlers NULL
#endif
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 4b86d3a738e1..1d55aeaebf23 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -1182,10 +1182,7 @@ const struct inode_operations ubifs_dir_inode_operations = {
.rename = ubifs_rename,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ubifs_listxattr,
- .removexattr = generic_removexattr,
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index b0a6a53263f3..a746982fbcda 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1621,10 +1621,7 @@ const struct address_space_operations ubifs_file_address_operations = {
const struct inode_operations ubifs_file_inode_operations = {
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ubifs_listxattr,
- .removexattr = generic_removexattr,
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
@@ -1635,10 +1632,7 @@ const struct inode_operations ubifs_symlink_inode_operations = {
.get_link = simple_get_link,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = ubifs_listxattr,
- .removexattr = generic_removexattr,
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
diff --git a/fs/xattr.c b/fs/xattr.c
index c243905835ab..3368659c471e 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,76 +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.
- */
-ssize_t
-generic_getxattr(struct dentry *dentry, struct inode *inode,
- const char *name, void *buffer, size_t size)
-{
- const struct xattr_handler *handler;
-
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (IS_ERR(handler))
- return PTR_ERR(handler);
- return handler->get(handler, dentry, inode,
- name, buffer, size);
-}
-
/*
* Combine the results of the list() operation from every xattr_handler in the
* list.
@@ -747,44 +776,7 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
}
return size;
}
-
-/*
- * Find the handler for the prefix and dispatch its set() operation.
- */
-int
-generic_setxattr(struct dentry *dentry, struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
-{
- const struct xattr_handler *handler;
-
- if (size == 0)
- value = ""; /* empty EA, do not remove */
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (IS_ERR(handler))
- return PTR_ERR(handler);
- return handler->set(handler, dentry, inode, name, value, size, flags);
-}
-
-/*
- * Find the handler for the prefix and dispatch its set() operation to remove
- * any associated extended attribute.
- */
-int
-generic_removexattr(struct dentry *dentry, const char *name)
-{
- const struct xattr_handler *handler;
-
- handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (IS_ERR(handler))
- return PTR_ERR(handler);
- return handler->set(handler, dentry, d_inode(dentry), name, NULL,
- 0, XATTR_REPLACE);
-}
-
-EXPORT_SYMBOL(generic_getxattr);
EXPORT_SYMBOL(generic_listxattr);
-EXPORT_SYMBOL(generic_setxattr);
-EXPORT_SYMBOL(generic_removexattr);
/**
* xattr_full_name - Compute full attribute name from suffix
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index ba99803eba98..a7404c5aafe2 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -1066,9 +1066,6 @@ static const struct inode_operations xfs_inode_operations = {
.set_acl = xfs_set_acl,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.fiemap = xfs_vn_fiemap,
.update_time = xfs_vn_update_time,
@@ -1094,9 +1091,6 @@ static const struct inode_operations xfs_dir_inode_operations = {
.set_acl = xfs_set_acl,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
.tmpfile = xfs_vn_tmpfile,
@@ -1122,9 +1116,6 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
.set_acl = xfs_set_acl,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
.tmpfile = xfs_vn_tmpfile,
@@ -1135,9 +1126,6 @@ static const struct inode_operations xfs_symlink_inode_operations = {
.get_link = xfs_vn_get_link,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
};
@@ -1147,9 +1135,6 @@ static const struct inode_operations xfs_inline_symlink_inode_operations = {
.get_link = xfs_vn_get_link_inline,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
- .removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
};
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4ba5957ae089..7e09f5926b3b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -592,6 +592,7 @@ is_uncached_acl(struct posix_acl *acl)
#define IOP_FASTPERM 0x0001
#define IOP_LOOKUP 0x0002
#define IOP_NOFOLLOW 0x0004
+#define IOP_XATTR 0x0008
/*
* Keep mostly read-only and often accessed (especially for
@@ -1751,12 +1752,7 @@ struct inode_operations {
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
- int (*setxattr) (struct dentry *, struct inode *,
- const char *, const void *, size_t, int);
- ssize_t (*getxattr) (struct dentry *, struct inode *,
- const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
- int (*removexattr) (struct dentry *, const char *);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, struct timespec *, int);
@@ -2087,10 +2083,19 @@ struct super_block *sget(struct file_system_type *type,
int (*test)(struct super_block *,void *),
int (*set)(struct super_block *,void *),
int flags, void *data);
-extern struct dentry *mount_pseudo(struct file_system_type *, char *,
- const struct super_operations *ops,
- const struct dentry_operations *dops,
- unsigned long);
+extern struct dentry *mount_pseudo_xattr(struct file_system_type *, char *,
+ const struct super_operations *ops,
+ const struct xattr_handler **xattr,
+ const struct dentry_operations *dops,
+ unsigned long);
+
+static inline struct dentry *
+mount_pseudo(struct file_system_type *fs_type, char *name,
+ const struct super_operations *ops,
+ const struct dentry_operations *dops, unsigned long magic)
+{
+ return mount_pseudo_xattr(fs_type, name, ops, NULL, dops, magic);
+}
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
#define fops_get(fops) \
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 94079bab9243..e77605a0c8da 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -46,17 +46,16 @@ struct xattr {
};
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
+ssize_t __vfs_getxattr(struct dentry *, struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int);
int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
+int __vfs_removexattr(struct dentry *, const char *);
int vfs_removexattr(struct dentry *, const char *);
-ssize_t generic_getxattr(struct dentry *dentry, struct inode *inode, const char *name, void *buffer, size_t size);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
-int generic_setxattr(struct dentry *dentry, struct inode *inode,
- const char *name, const void *value, size_t size, int flags);
-int generic_removexattr(struct dentry *dentry, const char *name);
ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
char **xattr_value, size_t size, gfp_t flags);
diff --git a/mm/shmem.c b/mm/shmem.c
index 828253ab772c..8596217b5e26 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3175,10 +3175,7 @@ static const struct inode_operations shmem_short_symlink_operations = {
.readlink = generic_readlink,
.get_link = simple_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -3186,10 +3183,7 @@ static const struct inode_operations shmem_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = shmem_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -3683,10 +3677,7 @@ static const struct inode_operations shmem_inode_operations = {
.getattr = shmem_getattr,
.setattr = shmem_setattr,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
.set_acl = simple_set_acl,
#endif
};
@@ -3705,10 +3696,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
.tmpfile = shmem_tmpfile,
#endif
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,
@@ -3718,10 +3706,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
static const struct inode_operations shmem_special_inode_operations = {
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,
diff --git a/net/socket.c b/net/socket.c
index a1bd16106625..5a9bf5ee2464 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -320,11 +320,38 @@ static const struct dentry_operations sockfs_dentry_operations = {
.d_dname = sockfs_dname,
};
+static int sockfs_xattr_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *inode,
+ const char *suffix, void *value, size_t size)
+{
+ if (value) {
+ if (dentry->d_name.len + 1 > size)
+ return -ERANGE;
+ memcpy(value, dentry->d_name.name, dentry->d_name.len + 1);
+ }
+ return dentry->d_name.len + 1;
+}
+
+#define XATTR_SOCKPROTONAME_SUFFIX "sockprotoname"
+#define XATTR_NAME_SOCKPROTONAME (XATTR_SYSTEM_PREFIX XATTR_SOCKPROTONAME_SUFFIX)
+#define XATTR_NAME_SOCKPROTONAME_LEN (sizeof(XATTR_NAME_SOCKPROTONAME)-1)
+
+static const struct xattr_handler sockfs_xattr_handler = {
+ .name = XATTR_NAME_SOCKPROTONAME,
+ .get = sockfs_xattr_get,
+};
+
+static const struct xattr_handler *sockfs_xattr_handlers[] = {
+ &sockfs_xattr_handler,
+ NULL
+};
+
static struct dentry *sockfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- return mount_pseudo(fs_type, "socket:", &sockfs_ops,
- &sockfs_dentry_operations, SOCKFS_MAGIC);
+ return mount_pseudo_xattr(fs_type, "socket:", &sockfs_ops,
+ sockfs_xattr_handlers,
+ &sockfs_dentry_operations, SOCKFS_MAGIC);
}
static struct vfsmount *sock_mnt __read_mostly;
@@ -463,35 +490,6 @@ static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)
return NULL;
}
-#define XATTR_SOCKPROTONAME_SUFFIX "sockprotoname"
-#define XATTR_NAME_SOCKPROTONAME (XATTR_SYSTEM_PREFIX XATTR_SOCKPROTONAME_SUFFIX)
-#define XATTR_NAME_SOCKPROTONAME_LEN (sizeof(XATTR_NAME_SOCKPROTONAME)-1)
-static ssize_t sockfs_getxattr(struct dentry *dentry, struct inode *inode,
- const char *name, void *value, size_t size)
-{
- const char *proto_name;
- size_t proto_size;
- int error;
-
- error = -ENODATA;
- if (!strncmp(name, XATTR_NAME_SOCKPROTONAME, XATTR_NAME_SOCKPROTONAME_LEN)) {
- proto_name = dentry->d_name.name;
- proto_size = strlen(proto_name);
-
- if (value) {
- error = -ERANGE;
- if (proto_size + 1 > size)
- goto out;
-
- strncpy(value, proto_name, proto_size + 1);
- }
- error = proto_size + 1;
- }
-
-out:
- return error;
-}
-
static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer,
size_t size)
{
@@ -521,7 +519,6 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer,
}
static const struct inode_operations sockfs_inode_ops = {
- .getxattr = sockfs_getxattr,
.listxattr = sockfs_listxattr,
};
diff --git a/security/commoncap.c b/security/commoncap.c
index 14540bd78561..8df676fbd393 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -310,13 +310,8 @@ int cap_inode_need_killpriv(struct dentry *dentry)
struct inode *inode = d_backing_inode(dentry);
int error;
- if (!inode->i_op->getxattr)
- return 0;
-
- error = inode->i_op->getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0);
- if (error <= 0)
- return 0;
- return 1;
+ error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0);
+ return error > 0;
}
/**
@@ -329,12 +324,12 @@ int cap_inode_need_killpriv(struct dentry *dentry)
*/
int cap_inode_killpriv(struct dentry *dentry)
{
- struct inode *inode = d_backing_inode(dentry);
-
- if (!inode->i_op->removexattr)
- return 0;
+ int error;
- return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
+ error = __vfs_removexattr(dentry, XATTR_NAME_CAPS);
+ if (error == -EOPNOTSUPP)
+ error = 0;
+ return error;
}
/*
@@ -394,11 +389,11 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
- if (!inode || !inode->i_op->getxattr)
+ if (!inode)
return -ENODATA;
- size = inode->i_op->getxattr((struct dentry *)dentry, inode,
- XATTR_NAME_CAPS, &caps, XATTR_CAPS_SZ);
+ size = __vfs_getxattr((struct dentry *)dentry, inode,
+ XATTR_NAME_CAPS, &caps, XATTR_CAPS_SZ);
if (size == -ENODATA || size == -EOPNOTSUPP)
/* no data, that's ok */
return -ENODATA;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 11c1d30bd705..bf663915412e 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -182,8 +182,9 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int error;
int size;
- if (!inode->i_op->getxattr)
+ if (!(inode->i_opflags & IOP_XATTR))
return -EOPNOTSUPP;
+
desc = init_desc(type);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -253,8 +254,8 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
&xattr_data,
sizeof(xattr_data), 0);
- } else if (rc == -ENODATA && inode->i_op->removexattr) {
- rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+ } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) {
+ rc = __vfs_removexattr(dentry, XATTR_NAME_EVM);
}
return rc;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index b9e26288d30c..ba8615576d4d 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -78,11 +78,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
int error;
int count = 0;
- if (!inode->i_op->getxattr)
+ if (!(inode->i_opflags & IOP_XATTR))
return -EOPNOTSUPP;
for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
- error = inode->i_op->getxattr(dentry, inode, *xattr, NULL, 0);
+ error = __vfs_getxattr(dentry, inode, *xattr, NULL, 0);
if (error < 0) {
if (error == -ENODATA)
continue;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index ef1e4e701780..389325ac6067 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -165,13 +165,13 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value)
{
- struct inode *inode = d_backing_inode(dentry);
-
- if (!inode->i_op->getxattr)
- return 0;
+ ssize_t ret;
- return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
- 0, GFP_NOFS);
+ ret = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
+ 0, GFP_NOFS);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+ return ret;
}
/*
@@ -195,7 +195,7 @@ int ima_appraise_measurement(enum ima_hooks func,
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len, hash_start = 0;
- if (!inode->i_op->getxattr)
+ if (!(inode->i_opflags & IOP_XATTR))
return INTEGRITY_UNKNOWN;
if (rc <= 0) {
@@ -322,10 +322,10 @@ void ima_inode_post_setattr(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
- int must_appraise, rc;
+ int must_appraise;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
- || !inode->i_op->removexattr)
+ || !(inode->i_opflags & IOP_XATTR))
return;
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
@@ -338,8 +338,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
iint->flags |= IMA_APPRAISE;
}
if (!must_appraise)
- rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
- return;
+ __vfs_removexattr(dentry, XATTR_NAME_IMA);
}
/*
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 2205ea27aa0a..085057936287 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -507,14 +507,14 @@ static int sb_finish_set_opts(struct super_block *sb)
the root directory. -ENODATA is ok, as this may be
the first boot of the SELinux kernel before we have
assigned xattr values to the filesystem. */
- if (!root_inode->i_op->getxattr) {
+ if (!(root_inode->i_opflags & IOP_XATTR)) {
printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
"xattr support\n", sb->s_id, sb->s_type->name);
rc = -EOPNOTSUPP;
goto out;
}
- rc = root_inode->i_op->getxattr(root, root_inode,
- XATTR_NAME_SELINUX, NULL, 0);
+
+ rc = __vfs_getxattr(root, root_inode, XATTR_NAME_SELINUX, NULL, 0);
if (rc < 0 && rc != -ENODATA) {
if (rc == -EOPNOTSUPP)
printk(KERN_WARNING "SELinux: (dev %s, type "
@@ -1410,11 +1410,10 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
case SECURITY_FS_USE_NATIVE:
break;
case SECURITY_FS_USE_XATTR:
- if (!inode->i_op->getxattr) {
+ if (!(inode->i_opflags & IOP_XATTR)) {
isec->sid = sbsec->def_sid;
break;
}
-
/* Need a dentry, since the xattr API requires one.
Life would be simpler if we could just pass the inode. */
if (opt_dentry) {
@@ -1445,14 +1444,12 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
}
context[len] = '\0';
- rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,
- context, len);
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
if (rc == -ERANGE) {
kfree(context);
/* Need a larger buffer. Query for the right size. */
- rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,
- NULL, 0);
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
if (rc < 0) {
dput(dentry);
goto out_unlock;
@@ -1465,9 +1462,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
}
context[len] = '\0';
- rc = inode->i_op->getxattr(dentry, inode,
- XATTR_NAME_SELINUX,
- context, len);
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
}
dput(dentry);
if (rc < 0) {
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index caec2256ab22..1cb060293505 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -265,14 +265,14 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
char *buffer;
struct smack_known *skp = NULL;
- if (ip->i_op->getxattr == NULL)
+ if (!(ip->i_opflags & IOP_XATTR))
return ERR_PTR(-EOPNOTSUPP);
buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
if (buffer == NULL)
return ERR_PTR(-ENOMEM);
- rc = ip->i_op->getxattr(dp, ip, name, buffer, SMK_LONGLABEL);
+ rc = __vfs_getxattr(dp, ip, name, buffer, SMK_LONGLABEL);
if (rc < 0)
skp = ERR_PTR(rc);
else if (rc == 0)
@@ -3520,8 +3520,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* It would be curious if the label of the task
* does not match that assigned.
*/
- if (inode->i_op->getxattr == NULL)
- break;
+ if (!(inode->i_opflags & IOP_XATTR))
+ break;
/*
* Get the dentry for xattr.
*/
@@ -3545,12 +3545,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
if (isp->smk_flags & SMK_INODE_CHANGED) {
isp->smk_flags &= ~SMK_INODE_CHANGED;
- rc = inode->i_op->setxattr(dp, inode,
+ rc = __vfs_setxattr(dp, inode,
XATTR_NAME_SMACKTRANSMUTE,
TRANS_TRUE, TRANS_TRUE_SIZE,
0);
} else {
- rc = inode->i_op->getxattr(dp, inode,
+ rc = __vfs_getxattr(dp, inode,
XATTR_NAME_SMACKTRANSMUTE, trattr,
TRANS_TRUE_SIZE);
if (rc >= 0 && strncmp(trattr, TRANS_TRUE,