Index: 2.4.x-xfs/fs/xattr.c =================================================================== --- 2.4.x-xfs.orig/fs/xattr.c 2004-10-19 10:26:55.000000000 +1000 +++ 2.4.x-xfs/fs/xattr.c 2006-01-18 13:21:52.889829629 +1100 @@ -15,45 +15,112 @@ #include /* - * Extended attribute memory allocation wrappers, originally - * based on the Intermezzo PRESTO_ALLOC/PRESTO_FREE macros. - * The vmalloc use here is very uncommon - extended attributes - * are supposed to be small chunks of metadata, and it is quite - * unusual to have very many extended attributes, so lists tend - * to be quite short as well. The 64K upper limit is derived - * from the extended attribute size limit used by XFS. - * Intentionally allow zero @size for value/list size requests. + * Check permissions for extended attribute access. This is a bit complicated + * because different namespaces have very different rules. */ -static void * -xattr_alloc(size_t size, size_t limit) +static int +xattr_permission(struct inode *inode, const char *name, int mask) { - void *ptr; + /* + * We can never set or remove an extended attribute on a read-only + * filesystem or on an immutable / append-only inode. + */ + if (mask & MAY_WRITE) { + if (IS_RDONLY(inode)) + return -EROFS; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EPERM; + } + + /* + * No restriction for security.* and system.* from the VFS. Decision + * on these is left to the underlying filesystem / security module. + */ + if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) || + !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) + return 0; + + /* + * The trusted.* namespace can only accessed by a privilegued user. + */ + if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) + return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM); + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) { + if (!S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + } + + return permission(inode, mask); +} + +int +vfs_setxattr(struct dentry *dentry, char *name, void *value, + size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error; - if (size > limit) - return ERR_PTR(-E2BIG); + if (!inode->i_op->setxattr) + return -EOPNOTSUPP; - if (!size) /* size request, no buffer is needed */ - return NULL; - else if (size <= PAGE_SIZE) - ptr = kmalloc((unsigned long) size, GFP_KERNEL); - else - ptr = vmalloc((unsigned long) size); - if (!ptr) - return ERR_PTR(-ENOMEM); - return ptr; -} - -static void -xattr_free(void *ptr, size_t size) -{ - if (!size) /* size request, no buffer was needed */ - return; - else if (size <= PAGE_SIZE) - kfree(ptr); - else - vfree(ptr); + error = xattr_permission(inode, name, MAY_WRITE); + if (error) + return error; + + down(&inode->i_sem); + lock_kernel(); + error = inode->i_op->setxattr(dentry, name, value, size, flags); + unlock_kernel(); + up(&inode->i_sem); + return error; } +ssize_t +vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error; + + if (!inode->i_op->getxattr) + return -EOPNOTSUPP; + + error = xattr_permission(inode, name, MAY_READ); + if (error) + return error; + + down(&inode->i_sem); + lock_kernel(); + error = inode->i_op->getxattr(dentry, name, value, size); + unlock_kernel(); + up(&inode->i_sem); + return error; +} + +int +vfs_removexattr(struct dentry *dentry, 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; + + down(&inode->i_sem); + lock_kernel(); + error = inode->i_op->removexattr(dentry, name); + unlock_kernel(); + up(&inode->i_sem); + + return error; +} + + /* * Extended attribute SET operations */ @@ -61,7 +128,7 @@ setxattr(struct dentry *d, char *name, void *value, size_t size, int flags) { int error; - void *kvalue; + void *kvalue = NULL; char kname[XATTR_NAME_MAX + 1]; if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) @@ -73,25 +140,20 @@ if (error < 0) return error; - kvalue = xattr_alloc(size, XATTR_SIZE_MAX); - if (IS_ERR(kvalue)) - return PTR_ERR(kvalue); - - if (size > 0 && copy_from_user(kvalue, value, size)) { - xattr_free(kvalue, size); - return -EFAULT; + if (size) { + if (size > XATTR_SIZE_MAX) + return -E2BIG; + kvalue = kmalloc(size, GFP_KERNEL); + if (!kvalue) + return -ENOMEM; + if (copy_from_user(kvalue, value, size)) { + kfree(kvalue); + return -EFAULT; + } } - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->setxattr) { - down(&d->d_inode->i_sem); - lock_kernel(); - error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags); - unlock_kernel(); - up(&d->d_inode->i_sem); - } - - xattr_free(kvalue, size); + error = vfs_setxattr(d, kname, kvalue, size, flags); + kfree(kvalue); return error; } @@ -144,7 +206,7 @@ getxattr(struct dentry *d, char *name, void *value, size_t size) { ssize_t error; - void *kvalue; + void *kvalue = NULL; char kname[XATTR_NAME_MAX + 1]; error = strncpy_from_user(kname, name, sizeof(kname)); @@ -153,23 +215,25 @@ if (error < 0) return error; - kvalue = xattr_alloc(size, XATTR_SIZE_MAX); - if (IS_ERR(kvalue)) - return PTR_ERR(kvalue); - - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->getxattr) { - down(&d->d_inode->i_sem); - lock_kernel(); - error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); - unlock_kernel(); - up(&d->d_inode->i_sem); + if (size) { + if (size > XATTR_SIZE_MAX) + size = XATTR_SIZE_MAX; + kvalue = kmalloc(size, GFP_KERNEL); + if (!kvalue) + return -ENOMEM; + memset(kvalue, 0, size); } - if (kvalue && error > 0) - if (copy_to_user(value, kvalue, error)) + error = vfs_getxattr(d, kname, kvalue, size); + if (error > 0) { + if (size && copy_to_user(value, kvalue, error)) error = -EFAULT; - xattr_free(kvalue, size); + } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { + /* The file system tried to returned a value bigger + than XATTR_SIZE_MAX bytes. Not possible. */ + error = -E2BIG; + } + kfree(kvalue); return error; } @@ -222,25 +286,34 @@ listxattr(struct dentry *d, char *list, size_t size) { ssize_t error; - char *klist; + char *klist = NULL; - klist = (char *)xattr_alloc(size, XATTR_LIST_MAX); - if (IS_ERR(klist)) - return PTR_ERR(klist); + if (size) { + if (size > XATTR_LIST_MAX) + size = XATTR_LIST_MAX; + klist = kmalloc(size, GFP_KERNEL); + if (!klist) + return -ENOMEM; + } error = -EOPNOTSUPP; if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { - down(&d->d_inode->i_sem); lock_kernel(); + down(&d->d_inode->i_sem); error = d->d_inode->i_op->listxattr(d, klist, size); unlock_kernel(); up(&d->d_inode->i_sem); } - - if (klist && error > 0) - if (copy_to_user(list, klist, error)) + if (error > 0) { + if (size && copy_to_user(list, klist, error)) error = -EFAULT; - xattr_free(klist, size); + } else if (error == -ERANGE && size >= XATTR_LIST_MAX) { + /* The file system tried to returned a list bigger + than XATTR_LIST_MAX bytes. Not possible. */ + error = -E2BIG; + } + + kfree(klist); return error; } @@ -301,15 +374,7 @@ if (error < 0) return error; - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->removexattr) { - down(&d->d_inode->i_sem); - lock_kernel(); - error = d->d_inode->i_op->removexattr(d, kname); - unlock_kernel(); - up(&d->d_inode->i_sem); - } - return error; + return vfs_removexattr(d, kname); } asmlinkage long Index: 2.4.x-xfs/include/linux/xattr.h =================================================================== --- 2.4.x-xfs.orig/include/linux/xattr.h 2004-10-19 10:28:04.000000000 +1000 +++ 2.4.x-xfs/include/linux/xattr.h 2006-01-18 13:21:41.819097121 +1100 @@ -11,5 +11,17 @@ #define XATTR_CREATE 0x1 /* set the value, fail if attr already exists */ #define XATTR_REPLACE 0x2 /* set the value, fail if attr does not exist */ +/* Namespaces */ +#define XATTR_SECURITY_PREFIX "security." +#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1) + +#define XATTR_SYSTEM_PREFIX "system." +#define XATTR_SYSTEM_PREFIX_LEN (sizeof (XATTR_SYSTEM_PREFIX) - 1) + +#define XATTR_TRUSTED_PREFIX "trusted." +#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_PREFIX) - 1) + +#define XATTR_USER_PREFIX "user." +#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) #endif /* _LINUX_XATTR_H */