diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index bb36b4304491..eef0576a7785 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -48,8 +48,8 @@ #include #include #include -#ifdef CONFIG_NFSD_V4 #include +#ifdef CONFIG_NFSD_V4 #include #include #include @@ -365,8 +365,30 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, goto out; } -#if defined(CONFIG_NFSD_V4) +#if defined(CONFIG_NFSD_V2_ACL) || \ + defined(CONFIG_NFSD_V3_ACL) || \ + defined(CONFIG_NFSD_V4) +static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf) +{ + ssize_t buflen; + int error; + buflen = vfs_getxattr(dentry, key, NULL, 0); + if (buflen <= 0) + return buflen; + + *buf = kmalloc(buflen, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + + error = vfs_getxattr(dentry, key, *buf, buflen); + if (error < 0) + return error; + return buflen; +} +#endif + +#if defined(CONFIG_NFSD_V4) static int set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) { @@ -374,7 +396,6 @@ set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) size_t buflen; char *buf = NULL; int error = 0; - struct inode *inode = dentry->d_inode; buflen = posix_acl_xattr_size(pacl->a_count); buf = kmalloc(buflen, GFP_KERNEL); @@ -388,15 +409,7 @@ set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) goto out; } - error = -EOPNOTSUPP; - if (inode->i_op && inode->i_op->setxattr) { - mutex_lock(&inode->i_mutex); - security_inode_setxattr(dentry, key, buf, len, 0); - error = inode->i_op->setxattr(dentry, key, buf, len, 0); - if (!error) - security_inode_post_setxattr(dentry, key, buf, len, 0); - mutex_unlock(&inode->i_mutex); - } + error = vfs_setxattr(dentry, key, buf, len, 0); out: kfree(buf); return error; @@ -455,44 +468,19 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, static struct posix_acl * _get_posix_acl(struct dentry *dentry, char *key) { - struct inode *inode = dentry->d_inode; - char *buf = NULL; - int buflen, error = 0; + void *buf = NULL; struct posix_acl *pacl = NULL; + int buflen; - error = -EOPNOTSUPP; - if (inode->i_op == NULL) - goto out_err; - if (inode->i_op->getxattr == NULL) - goto out_err; - - error = security_inode_getxattr(dentry, key); - if (error) - goto out_err; - - buflen = inode->i_op->getxattr(dentry, key, NULL, 0); - if (buflen <= 0) { - error = buflen < 0 ? buflen : -ENODATA; - goto out_err; - } - - buf = kmalloc(buflen, GFP_KERNEL); - if (buf == NULL) { - error = -ENOMEM; - goto out_err; - } - - error = inode->i_op->getxattr(dentry, key, buf, buflen); - if (error < 0) - goto out_err; + buflen = nfsd_getxattr(dentry, key, &buf); + if (!buflen) + buflen = -ENODATA; + if (buflen <= 0) + return ERR_PTR(buflen); pacl = posix_acl_from_xattr(buf, buflen); - out: kfree(buf); return pacl; - out_err: - pacl = ERR_PTR(error); - goto out; } int @@ -1884,39 +1872,25 @@ nfsd_get_posix_acl(struct svc_fh *fhp, int type) ssize_t size; struct posix_acl *acl; - if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->getxattr) + if (!IS_POSIXACL(inode)) + return ERR_PTR(-EOPNOTSUPP); + + switch (type) { + case ACL_TYPE_ACCESS: + name = POSIX_ACL_XATTR_ACCESS; + break; + case ACL_TYPE_DEFAULT: + name = POSIX_ACL_XATTR_DEFAULT; + break; + default: return ERR_PTR(-EOPNOTSUPP); - switch(type) { - case ACL_TYPE_ACCESS: - name = POSIX_ACL_XATTR_ACCESS; - break; - case ACL_TYPE_DEFAULT: - name = POSIX_ACL_XATTR_DEFAULT; - break; - default: - return ERR_PTR(-EOPNOTSUPP); } - size = inode->i_op->getxattr(fhp->fh_dentry, name, NULL, 0); + size = nfsd_getxattr(fhp->fh_dentry, name, &value); + if (size < 0) + return ERR_PTR(size); - if (size < 0) { - acl = ERR_PTR(size); - goto getout; - } else if (size > 0) { - value = kmalloc(size, GFP_KERNEL); - if (!value) { - acl = ERR_PTR(-ENOMEM); - goto getout; - } - size = inode->i_op->getxattr(fhp->fh_dentry, name, value, size); - if (size < 0) { - acl = ERR_PTR(size); - goto getout; - } - } acl = posix_acl_from_xattr(value, size); - -getout: kfree(value); return acl; } @@ -1957,16 +1931,13 @@ nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) } else size = 0; - if (!fhp->fh_locked) - fh_lock(fhp); /* unlocking is done automatically */ if (size) - error = inode->i_op->setxattr(fhp->fh_dentry, name, - value, size, 0); + error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0); else { if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT) error = 0; else { - error = inode->i_op->removexattr(fhp->fh_dentry, name); + error = vfs_removexattr(fhp->fh_dentry, name); if (error == -ENODATA) error = 0; } diff --git a/fs/xattr.c b/fs/xattr.c index 386a532ee5a9..fee804e69a9a 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -19,6 +19,96 @@ #include #include + +int +vfs_setxattr(struct dentry *dentry, char *name, void *value, + size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + int error; + + mutex_lock(&inode->i_mutex); + error = security_inode_setxattr(dentry, name, value, size, flags); + if (error) + goto out; + error = -EOPNOTSUPP; + if (inode->i_op->setxattr) { + error = inode->i_op->setxattr(dentry, name, value, size, flags); + if (!error) { + fsnotify_xattr(dentry); + security_inode_post_setxattr(dentry, name, value, + size, flags); + } + } else if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof XATTR_SECURITY_PREFIX - 1)) { + const char *suffix = name + sizeof XATTR_SECURITY_PREFIX - 1; + error = security_inode_setsecurity(inode, suffix, value, + size, flags); + if (!error) + fsnotify_xattr(dentry); + } +out: + mutex_unlock(&inode->i_mutex); + return error; +} +EXPORT_SYMBOL_GPL(vfs_setxattr); + +ssize_t +vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = security_inode_getxattr(dentry, name); + if (error) + return error; + + if (inode->i_op->getxattr) + error = inode->i_op->getxattr(dentry, name, value, size); + else + error = -EOPNOTSUPP; + + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof XATTR_SECURITY_PREFIX - 1)) { + const char *suffix = name + sizeof XATTR_SECURITY_PREFIX - 1; + int ret = security_inode_getsecurity(inode, suffix, value, + size, error); + /* + * Only overwrite the return value if a security module + * is actually active. + */ + if (ret != -EOPNOTSUPP) + error = ret; + } + + return error; +} +EXPORT_SYMBOL_GPL(vfs_getxattr); + +int +vfs_removexattr(struct dentry *dentry, char *name) +{ + struct inode *inode = dentry->d_inode; + int error; + + if (!inode->i_op->removexattr) + return -EOPNOTSUPP; + + error = security_inode_removexattr(dentry, name); + if (error) + return error; + + mutex_lock(&inode->i_mutex); + error = inode->i_op->removexattr(dentry, name); + mutex_unlock(&inode->i_mutex); + + if (!error) + fsnotify_xattr(dentry); + return error; +} +EXPORT_SYMBOL_GPL(vfs_removexattr); + + /* * Extended attribute SET operations */ @@ -51,29 +141,7 @@ setxattr(struct dentry *d, char __user *name, void __user *value, } } - mutex_lock(&d->d_inode->i_mutex); - error = security_inode_setxattr(d, kname, kvalue, size, flags); - if (error) - goto out; - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->setxattr) { - error = d->d_inode->i_op->setxattr(d, kname, kvalue, - size, flags); - if (!error) { - fsnotify_xattr(d); - security_inode_post_setxattr(d, kname, kvalue, - size, flags); - } - } else if (!strncmp(kname, XATTR_SECURITY_PREFIX, - sizeof XATTR_SECURITY_PREFIX - 1)) { - const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1; - error = security_inode_setsecurity(d->d_inode, suffix, kvalue, - size, flags); - if (!error) - fsnotify_xattr(d); - } -out: - mutex_unlock(&d->d_inode->i_mutex); + error = vfs_setxattr(d, kname, kvalue, size, flags); kfree(kvalue); return error; } @@ -147,22 +215,7 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) return -ENOMEM; } - error = security_inode_getxattr(d, kname); - if (error) - goto out; - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->getxattr) - error = d->d_inode->i_op->getxattr(d, kname, kvalue, size); - - if (!strncmp(kname, XATTR_SECURITY_PREFIX, - sizeof XATTR_SECURITY_PREFIX - 1)) { - const char *suffix = kname + sizeof XATTR_SECURITY_PREFIX - 1; - int rv = security_inode_getsecurity(d->d_inode, suffix, kvalue, - size, error); - /* Security module active: overwrite error value */ - if (rv != -EOPNOTSUPP) - error = rv; - } + error = vfs_getxattr(d, kname, kvalue, size); if (error > 0) { if (size && copy_to_user(value, kvalue, error)) error = -EFAULT; @@ -171,7 +224,6 @@ getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) than XATTR_SIZE_MAX bytes. Not possible. */ error = -E2BIG; } -out: kfree(kvalue); return error; } @@ -318,19 +370,7 @@ removexattr(struct dentry *d, char __user *name) if (error < 0) return error; - error = -EOPNOTSUPP; - if (d->d_inode->i_op && d->d_inode->i_op->removexattr) { - error = security_inode_removexattr(d, kname); - if (error) - goto out; - mutex_lock(&d->d_inode->i_mutex); - error = d->d_inode->i_op->removexattr(d, kname); - mutex_unlock(&d->d_inode->i_mutex); - if (!error) - fsnotify_xattr(d); - } -out: - return error; + return vfs_removexattr(d, kname); } asmlinkage long diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 23f9c61d9546..366f0ab4219f 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -25,6 +25,10 @@ struct xattr_handler { size_t size, int flags); }; +ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t); +int vfs_setxattr(struct dentry *, char *, void *, size_t, int); +int vfs_removexattr(struct dentry *, char *); + 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);