From 493267b6ec40026be65e3564fd24b879be3c06d1 Mon Sep 17 00:00:00 2001 From: William Lee Irwin III Date: Mon, 18 Oct 2004 18:00:17 -0700 Subject: [PATCH] eliminate inode waitqueue hashtable Eliminate the inode waitqueue hashtable using bit_waitqueue() via wait_on_bit() and wake_up_bit() to locate the waitqueue head associated with a bit. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux/fs.h') diff --git a/include/linux/fs.h b/include/linux/fs.h index 4f6fe6b575a8..2a13baa4250b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -981,7 +981,8 @@ struct super_operations { #define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */ #define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */ #define I_DIRTY_PAGES 4 /* Data-related inode changes pending */ -#define I_LOCK 8 +#define __I_LOCK 3 +#define I_LOCK (1 << __I_LOCK) #define I_FREEING 16 #define I_CLEAR 32 #define I_NEW 64 -- cgit v1.2.3 From 1cd05eadec387f138424603a1b34761506443707 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 Oct 2004 18:02:05 -0700 Subject: [PATCH] don't export blkdev_open and def_blk_ops Already since 2.4 all block devices use block_device_operations and shouldn't deal with file operations directly. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/block_dev.c | 6 +----- include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/block_dev.c b/fs/block_dev.c index 5c3f09b86172..28aac88114de 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -666,7 +666,7 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags) EXPORT_SYMBOL(blkdev_get); -int blkdev_open(struct inode * inode, struct file * filp) +static int blkdev_open(struct inode * inode, struct file * filp) { struct block_device *bdev; int res; @@ -695,8 +695,6 @@ int blkdev_open(struct inode * inode, struct file * filp) return res; } -EXPORT_SYMBOL(blkdev_open); - int blkdev_put(struct block_device *bdev) { int ret = 0; @@ -798,8 +796,6 @@ struct file_operations def_blk_fops = { .sendfile = generic_file_sendfile, }; -EXPORT_SYMBOL(def_blk_fops); - int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg) { int res; diff --git a/include/linux/fs.h b/include/linux/fs.h index 2a13baa4250b..dc457c3fede2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1255,7 +1255,6 @@ extern struct block_device *bdget(dev_t); extern void bd_set_size(struct block_device *, loff_t size); extern void bd_forget(struct inode *inode); extern void bdput(struct block_device *); -extern int blkdev_open(struct inode *, struct file *); extern struct block_device *open_by_devnum(dev_t, unsigned); extern struct file_operations def_blk_fops; extern struct address_space_operations def_blk_aops; -- cgit v1.2.3 From 42017c2e0ae391565af3c894020379194f668d54 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 Oct 2004 18:05:13 -0700 Subject: [PATCH] generic acl support for ->permission Currently we every filesystem with Posix ACLs has it's own reimplemtation of the generic permission checking code with additonal ACL support. This patch - adds an optional callback to vfs_permission that filesystems can use for ACL support (and renames it to generic_permission because the old name was wrong - it wasn't like the other vfs_* functions at all) - uses it in ext2, ext3 and jfs. XFS will follow a little later as it's permission checking is burried under several layers of abstraction. From: Dave Kleikamp jfs doesn't currently set MS_POSIXACL (it doesn't require the acl mount option), so this test would fail here. The patch below will set it. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/cifs/CHANGES | 4 +-- fs/cifs/cifsfs.c | 2 +- fs/exec.c | 2 +- fs/ext2/acl.c | 66 +++++++++------------------------------- fs/ext3/acl.c | 66 +++++++++------------------------------- fs/hfs/inode.c | 2 +- fs/hfsplus/inode.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/jfs/acl.c | 81 ++++++------------------------------------------- fs/jfs/super.c | 4 +++ fs/namei.c | 33 ++++++++++++++------ fs/nfs/dir.c | 2 +- fs/proc/base.c | 2 +- include/linux/fs.h | 4 ++- 14 files changed, 79 insertions(+), 193 deletions(-) (limited to 'include/linux/fs.h') diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 087be36211eb..2a1924078059 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -7,8 +7,8 @@ or out of order NULL pointer checks in little used error paths). Version 1.21 ------------ -Add new mount parm to control whether mode check (vfs_permission) is done on -the client. If Unix extensions are enabled and the uids on the client +Add new mount parm to control whether mode check (generic_permission) is done +on the client. If Unix extensions are enabled and the uids on the client and server do not match, client permission checks are meaningless on server uids that do not exist on the client (this does not affect the normal ACL check which occurs on the server). Fix default uid diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 729cdba71530..996fc298a306 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -200,7 +200,7 @@ static int cifs_permission(struct inode * inode, int mask, struct nameidata *nd) on the client (above and beyond ACL on servers) for servers which do not support setting and viewing mode bits, so allowing client to check permissions is useful */ - return vfs_permission(inode, mask); + return generic_permission(inode, mask, NULL); } static kmem_cache_t *cifs_inode_cachep; diff --git a/fs/exec.c b/fs/exec.c index 4124b39bdd27..4915bffb045d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -886,7 +886,7 @@ int prepare_binprm(struct linux_binprm *bprm) mode = inode->i_mode; /* * Check execute perms again - if the caller has CAP_DAC_OVERRIDE, - * vfs_permission lets a non-executable through + * generic_permission lets a non-executable through */ if (!(mode & 0111)) /* with at least _one_ execute bit set */ return -EACCES; diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index 89d1df91411f..0d961d5e00ed 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -280,60 +280,24 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) return error; } -/* - * Inode operation permission(). - * - * inode->i_sem: don't care - */ -int -ext2_permission(struct inode *inode, int mask, struct nameidata *nd) +static int +ext2_check_acl(struct inode *inode, int mask) { - int mode = inode->i_mode; - - /* Nobody gets write access to a read-only fs */ - if ((mask & MAY_WRITE) && IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; - /* Nobody gets write access to an immutable file */ - if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) - return -EACCES; - if (current->fsuid == inode->i_uid) { - mode >>= 6; - } else if (test_opt(inode->i_sb, POSIX_ACL)) { - struct posix_acl *acl; - - /* The access ACL cannot grant access if the group class - permission bits don't contain all requested permissions. */ - if (((mode >> 3) & mask & S_IRWXO) != mask) - goto check_groups; - acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); - if (acl) { - int error = posix_acl_permission(inode, acl, mask); - posix_acl_release(acl); - if (error == -EACCES) - goto check_capabilities; - return error; - } else - goto check_groups; - } else { -check_groups: - if (in_group_p(inode->i_gid)) - mode >>= 3; + struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); + + if (acl) { + int error = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return error; } - if ((mode & mask & S_IRWXO) == mask) - return 0; -check_capabilities: - /* Allowed to override Discretionary Access Control? */ - if (!(mask & MAY_EXEC) || - (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) - if (capable(CAP_DAC_OVERRIDE)) - return 0; - /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ - if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || - (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) - return 0; - return -EACCES; + return -EAGAIN; +} + +int +ext2_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, ext2_check_acl); } /* diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index a3cf77de0e43..a335fd134352 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -285,60 +285,24 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, return error; } -/* - * Inode operation permission(). - * - * inode->i_sem: don't care - */ -int -ext3_permission(struct inode *inode, int mask, struct nameidata *nd) +static int +ext3_check_acl(struct inode *inode, int mask) { - int mode = inode->i_mode; - - /* Nobody gets write access to a read-only fs */ - if ((mask & MAY_WRITE) && IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; - /* Nobody gets write access to an immutable file */ - if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) - return -EACCES; - if (current->fsuid == inode->i_uid) { - mode >>= 6; - } else if (test_opt(inode->i_sb, POSIX_ACL)) { - struct posix_acl *acl; - - /* The access ACL cannot grant access if the group class - permission bits don't contain all requested permissions. */ - if (((mode >> 3) & mask & S_IRWXO) != mask) - goto check_groups; - acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); - if (acl) { - int error = posix_acl_permission(inode, acl, mask); - posix_acl_release(acl); - if (error == -EACCES) - goto check_capabilities; - return error; - } else - goto check_groups; - } else { -check_groups: - if (in_group_p(inode->i_gid)) - mode >>= 3; + struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS); + + if (acl) { + int error = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return error; } - if ((mode & mask & S_IRWXO) == mask) - return 0; -check_capabilities: - /* Allowed to override Discretionary Access Control? */ - if (!(mask & MAY_EXEC) || - (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) - if (capable(CAP_DAC_OVERRIDE)) - return 0; - /* Read and search granted if capable(CAP_DAC_READ_SEARCH) */ - if (capable(CAP_DAC_READ_SEARCH) && ((mask == MAY_READ) || - (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))) - return 0; - return -EACCES; + return -EAGAIN; +} + +int +ext3_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, ext3_check_acl); } /* diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 6c869f377964..ae55ce1a5405 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -517,7 +517,7 @@ static int hfs_permission(struct inode *inode, int mask, { if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) return 0; - return vfs_permission(inode, mask); + return generic_permission(inode, mask, NULL); } static int hfs_file_open(struct inode *inode, struct file *file) diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index eff1c987b6fb..f58025cb38af 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -260,7 +260,7 @@ static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *n */ if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111)) return 0; - return vfs_permission(inode, mask); + return generic_permission(inode, mask, NULL); } diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 7d42da0a304d..937fac26b34a 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -808,7 +808,7 @@ int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) if(name == NULL) return(-ENOMEM); err = access_file(name, r, w, x); kfree(name); - if(!err) err = vfs_permission(ino, desired); + if(!err) err = generic_permission(ino, desired, NULL); return(err); } diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index fc584f2194f4..8d2a9ab981d4 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -123,88 +123,25 @@ out: return rc; } -/* - * jfs_permission() - * - * modified vfs_permission to check posix acl - */ -int jfs_permission(struct inode * inode, int mask, struct nameidata *nd) +static int jfs_check_acl(struct inode *inode, int mask) { - umode_t mode = inode->i_mode; struct jfs_inode_info *ji = JFS_IP(inode); - if (mask & MAY_WRITE) { - /* - * Nobody gets write access to a read-only fs. - */ - if (IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; - - /* - * Nobody gets write access to an immutable file. - */ - if (IS_IMMUTABLE(inode)) - return -EACCES; - } - - if (current->fsuid == inode->i_uid) { - mode >>= 6; - goto check_mode; - } - /* - * ACL can't contain additional permissions if the ACL_MASK entry - * is zero. - */ - if (!(mode & S_IRWXG)) - goto check_groups; - if (ji->i_acl == JFS_ACL_NOT_CACHED) { - struct posix_acl *acl; - - acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); - + struct posix_acl *acl = jfs_get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl)) return PTR_ERR(acl); posix_acl_release(acl); } - if (ji->i_acl) { - int rc = posix_acl_permission(inode, ji->i_acl, mask); - if (rc == -EACCES) - goto check_capabilities; - return rc; - } - -check_groups: - if (in_group_p(inode->i_gid)) - mode >>= 3; - -check_mode: - /* - * If the DACs are ok we don't need any capability check. - */ - if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) - return 0; + if (ji->i_acl) + return posix_acl_permission(inode, ji->i_acl, mask); + return -EAGAIN; +} -check_capabilities: - /* - * Read/write DACs are always overridable. - * Executable DACs are overridable if at least one exec bit is set. - */ - if (!(mask & MAY_EXEC) || - (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) - if (capable(CAP_DAC_OVERRIDE)) - return 0; - - /* - * Searching includes executable on directories, else just read. - */ - if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) - if (capable(CAP_DAC_READ_SEARCH)) - return 0; - - return -EACCES; +int jfs_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, jfs_check_acl); } int jfs_init_acl(struct inode *inode, struct inode *dir) diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 7c91ccfe382f..ecf1bca73050 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -403,6 +403,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) } sbi->flag = flag; +#ifdef CONFIG_JFS_POSIX_ACL + sb->s_flags |= MS_POSIXACL; +#endif + if (newLVSize) { printk(KERN_ERR "resize option for remount only\n"); return -EINVAL; diff --git a/fs/namei.c b/fs/namei.c index bf259a9c5b21..287339bb7b7c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -152,15 +152,19 @@ char * getname(const char __user * filename) return result; } -/* - * vfs_permission() +/** + * generic_permission - check for access rights on a Posix-like filesystem + * @inode: inode to check access rights for + * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) + * @check_acl: optional callback to check for Posix ACLs * - * is used to check for read/write/execute permissions on a file. + * Used to check for read/write/execute permissions on a file. * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things.. */ -int vfs_permission(struct inode * inode, int mask) +int generic_permission(struct inode *inode, int mask, + int (*check_acl)(struct inode *inode, int mask)) { umode_t mode = inode->i_mode; @@ -181,8 +185,18 @@ int vfs_permission(struct inode * inode, int mask) if (current->fsuid == inode->i_uid) mode >>= 6; - else if (in_group_p(inode->i_gid)) - mode >>= 3; + else { + if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { + int error = check_acl(inode, mask); + if (error == -EACCES) + goto check_capabilities; + else if (error != -EAGAIN) + return error; + } + + if (in_group_p(inode->i_gid)) + mode >>= 3; + } /* * If the DACs are ok we don't need any capability check. @@ -190,6 +204,7 @@ int vfs_permission(struct inode * inode, int mask) if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) return 0; + check_capabilities: /* * Read/write DACs are always overridable. * Executable DACs are overridable if at least one exec bit is set. @@ -220,7 +235,7 @@ int permission(struct inode * inode,int mask, struct nameidata *nd) if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission(inode, submask, nd); else - retval = vfs_permission(inode, submask); + retval = generic_permission(inode, submask, NULL); if (retval) return retval; @@ -315,7 +330,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, /* * Short-cut version of permission(), for calling by * path_walk(), when dcache lock is held. Combines parts - * of permission() and vfs_permission(), and tests ONLY for + * of permission() and generic_permission(), and tests ONLY for * MAY_EXEC permission. * * If appropriate, check DAC only. If not appropriate, or @@ -2456,7 +2471,7 @@ EXPORT_SYMBOL(vfs_follow_link); EXPORT_SYMBOL(vfs_link); EXPORT_SYMBOL(vfs_mkdir); EXPORT_SYMBOL(vfs_mknod); -EXPORT_SYMBOL(vfs_permission); +EXPORT_SYMBOL(generic_permission); EXPORT_SYMBOL(vfs_readlink); EXPORT_SYMBOL(vfs_rename); EXPORT_SYMBOL(vfs_rmdir); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 626252aec95a..eac30582ff9e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1600,7 +1600,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) return res; out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); - res = vfs_permission(inode, mask); + res = generic_permission(inode, mask, NULL); unlock_kernel(); return res; } diff --git a/fs/proc/base.c b/fs/proc/base.c index 987240398c4a..91060b9921cc 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -473,7 +473,7 @@ out: static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) { - if (vfs_permission(inode, mask) != 0) + if (generic_permission(inode, mask, NULL) != 0) return -EACCES; return proc_check_root(inode); } diff --git a/include/linux/fs.h b/include/linux/fs.h index dc457c3fede2..13e1b4cb7c74 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1339,7 +1339,9 @@ extern sector_t bmap(struct inode *, sector_t); extern int setattr_mask(unsigned int); extern int notify_change(struct dentry *, struct iattr *); extern int permission(struct inode *, int, struct nameidata *); -extern int vfs_permission(struct inode *, int); +extern int generic_permission(struct inode *, int, + int (*check_acl)(struct inode *, int)); + extern int get_write_access(struct inode *); extern int deny_write_access(struct file *); static inline void put_write_access(struct inode * inode) -- cgit v1.2.3 From dbcd7a5fb9d12336fb4a758ff9f3fe57dd21772c Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 18 Oct 2004 18:15:51 -0700 Subject: [PATCH] xattr consolidation v3 - generic xattr API This patch consolidates common xattr handling logic into the core fs code, with modifications suggested by Christoph Hellwig (hang off superblock, remove locking, use generic code as methods), for use by ext2, ext3 and devpts, as well as upcoming tmpfs xattr code. Signed-off-by: James Morris Signed-off-by: Stephen Smalley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/xattr.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 1 + include/linux/xattr.h | 16 +++++++ 3 files changed, 146 insertions(+) (limited to 'include/linux/fs.h') diff --git a/fs/xattr.c b/fs/xattr.c index a71900b5349a..dd8031011212 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -5,6 +5,7 @@ Copyright (C) 2001 by Andreas Gruenbacher Copyright (C) 2001 SGI - Silicon Graphics, Inc + Copyright (c) 2004 Red Hat, Inc., James Morris */ #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include /* @@ -348,3 +350,130 @@ sys_fremovexattr(int fd, char __user *name) fput(f); 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) \ + for ((handler) = *(handlers)++; \ + (handler) != NULL; \ + (handler) = *(handlers)++) + +/* + * Find the xattr_handler with the matching prefix. + */ +static struct xattr_handler * +xattr_resolve_name(struct xattr_handler **handlers, const char **name) +{ + struct xattr_handler *handler; + + if (!*name) + return NULL; + + for_each_xattr_handler(handlers, handler) { + const char *n = strcmp_prefix(*name, handler->prefix); + if (n) { + *name = n; + break; + } + } + return handler; +} + +/* + * Find the handler for the prefix and dispatch its get() operation. + */ +ssize_t +generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->get(inode, name, buffer, size); +} + +/* + * Combine the results of the list() operation from every xattr_handler in the + * list. + */ +ssize_t +generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) +{ + struct inode *inode = dentry->d_inode; + struct xattr_handler *handler, **handlers = inode->i_sb->s_xattr; + unsigned int size = 0; + + if (!buffer) { + for_each_xattr_handler(handlers, handler) + size += handler->list(inode, NULL, 0, NULL, 0); + } else { + char *buf = buffer; + + for_each_xattr_handler(handlers, handler) { + size = handler->list(inode, buf, buffer_size, NULL, 0); + if (size > buffer_size) + return -ERANGE; + buf += size; + buffer_size -= size; + } + size = buf - buffer; + } + return size; +} + +/* + * Find the handler for the prefix and dispatch its set() operation. + */ +int +generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + if (size == 0) + value = ""; /* empty EA, do not remove */ + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(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) +{ + struct xattr_handler *handler; + struct inode *inode = dentry->d_inode; + + handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); + if (!handler) + return -EOPNOTSUPP; + return handler->set(inode, name, NULL, 0, XATTR_REPLACE); +} + +EXPORT_SYMBOL(generic_getxattr); +EXPORT_SYMBOL(generic_listxattr); +EXPORT_SYMBOL(generic_setxattr); +EXPORT_SYMBOL(generic_removexattr); diff --git a/include/linux/fs.h b/include/linux/fs.h index 13e1b4cb7c74..6768655fd11d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -758,6 +758,7 @@ struct super_block { int s_need_sync_fs; atomic_t s_active; void *s_security; + struct xattr_handler **s_xattr; struct list_head s_dirty; /* dirty inodes */ struct list_head s_io; /* parked for writeback */ diff --git a/include/linux/xattr.h b/include/linux/xattr.h index d9c5d5c83d49..23f9c61d9546 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -5,6 +5,7 @@ Copyright (C) 2001 by Andreas Gruenbacher Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. + Copyright (c) 2004 Red Hat, Inc., James Morris */ #ifndef _LINUX_XATTR_H #define _LINUX_XATTR_H @@ -14,4 +15,19 @@ #define XATTR_SECURITY_PREFIX "security." +struct xattr_handler { + char *prefix; + size_t (*list)(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len); + int (*get)(struct inode *inode, const char *name, void *buffer, + size_t size); + int (*set)(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags); +}; + +ssize_t generic_getxattr(struct dentry *dentry, 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, const char *name, const void *value, size_t size, int flags); +int generic_removexattr(struct dentry *dentry, const char *name); + #endif /* _LINUX_XATTR_H */ -- cgit v1.2.3