ACL support for the NFSv4 server fs/nfsd/nfs4proc.c | 11 ++ fs/nfsd/nfs4xdr.c | 95 ++++++++++++++++++++++--- fs/nfsd/vfs.c | 174 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/nfsd/nfsd.h | 11 ++ include/linux/nfsd/xdr4.h | 54 +++++++------- 5 files changed, 305 insertions(+), 40 deletions(-) diff -puN fs/nfsd/nfs4xdr.c~acl_server fs/nfsd/nfs4xdr.c --- current/fs/nfsd/nfs4xdr.c~acl_server Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfsd/nfs4xdr.c Mon Jun 2 18:00:56 2003 @@ -54,6 +54,8 @@ #include #include #include +#include +#include #define NFSDDBG_FACILITY NFSDDBG_XDR @@ -327,7 +329,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoun } static int -nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr) +nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr, + struct nfs4_acl **acl) { int expected_len, len = 0; u32 dummy32; @@ -356,6 +359,33 @@ nfsd4_decode_fattr(struct nfsd4_compound READ64(iattr->ia_size); iattr->ia_valid |= ATTR_SIZE; } + if (bmval[0] & FATTR4_WORD0_ACL) { + int nace, i; + struct nfs4_ace ace; + + READ_BUF(4); len += 4; + READ32(nace); + + *acl = nfs4_acl_new(); + if (*acl == NULL) { + status = -ENOMEM; + goto out_nfserr; + } + + for (i = 0; i < nace; i++) { + READ_BUF(16); len += 16; + READ32(ace.type); + READ32(ace.flag); + READ32(ace.access_mask); + READ32(ace.wholen); + READ_BUF(ace.wholen); + len += XDR_QUADLEN(ace.wholen) << 2; + nfs4_acl_add_ace(*acl, ace.type, ace.flag, + ace.access_mask, (char *)p, ace.wholen); + p += XDR_QUADLEN(ace.wholen); + } + } else + *acl = NULL; if (bmval[1] & FATTR4_WORD1_MODE) { READ_BUF(4); len += 4; @@ -539,7 +569,7 @@ nfsd4_decode_create(struct nfsd4_compoun if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) return status; - if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr))) + if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl))) goto out; DECODE_TAIL; @@ -610,7 +640,7 @@ nfsd4_decode_open(struct nfsd4_compounda switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: case NFS4_CREATE_GUARDED: - if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr))) + if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl))) goto out; break; case NFS4_CREATE_EXCLUSIVE: @@ -757,7 +787,7 @@ nfsd4_decode_setattr(struct nfsd4_compou READ_BUF(sizeof(stateid_t)); READ32(setattr->sa_stateid.st_generation); COPYMEM(&setattr->sa_stateid.st_other, sizeof(stateid_other_t)); - if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr))) + if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl))) goto out; DECODE_TAIL; @@ -1086,17 +1116,20 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s u64 dummy64; u32 *p = buffer; int status; + struct nfs4_acl *acl = NULL; BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0); BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1); + /* XXX GETACL here */ + status = vfs_getattr(exp->ex_mnt, dentry, &stat); if (status) goto out_nfserr; if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) || (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | - FATTR4_WORD1_SPACE_TOTAL))) { + FATTR4_WORD1_SPACE_TOTAL))) { status = vfs_statfs(dentry->d_inode->i_sb, &statfs); if (status) goto out_nfserr; @@ -1118,6 +1151,11 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s if (status) goto out_nfserr; } + if (bmval0 & FATTR4_WORD0_ACL) { + status = nfsd4_get_nfs4_acl(dentry, &acl); + if (status && status != -ENODATA) + goto out_nfserr; + } if ((buflen -= 16) < 0) goto out_resource; @@ -1219,10 +1257,47 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s goto out_resource; WRITE32(0); } + if (bmval0 & FATTR4_WORD0_ACL) { + struct nfs4_ace *ace; + struct list_head *h; + int alen; + + if (acl == NULL) { + if ((buflen -= 4) < 0) + goto out_resource; + + WRITE32(0); + goto out_acl; + } + + alen = acl->naces * 16 + 4; + + list_for_each(h, &acl->ace_head) { + ace = list_entry(h, struct nfs4_ace, l_ace); + alen += XDR_QUADLEN(ace->wholen) << 2; + } + + if ((buflen -= alen) < 0) + goto out_resource; + + WRITE32(acl->naces); + + list_for_each(h, &acl->ace_head) { + ace = list_entry(h, struct nfs4_ace, l_ace); + + WRITE32(ace->type); + WRITE32(ace->flag); + WRITE32(ace->access_mask); + WRITE32(ace->wholen); + WRITEMEM(ace->who, ace->wholen); + } + + out_acl: + } if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32(0); + WRITE32(1); } if (bmval0 & FATTR4_WORD0_CANSETTIME) { if ((buflen -= 4) < 0) @@ -1395,7 +1470,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s *countp = p - buffer; status = nfs_ok; -out: + out: if (fhp == &tempfh) fh_put(&tempfh); if (owner) @@ -1403,14 +1478,14 @@ out: if (group) name_put(group); return status; -out_nfserr: + out_nfserr: status = nfserrno(status); goto out; -out_resource: + out_resource: *countp = 0; status = nfserr_resource; goto out; -out_serverfault: + out_serverfault: status = nfserr_serverfault; goto out; } diff -puN include/linux/nfsd/xdr4.h~acl_server include/linux/nfsd/xdr4.h --- current/include/linux/nfsd/xdr4.h~acl_server Mon Jun 2 12:54:10 2003 +++ current-marius/include/linux/nfsd/xdr4.h Mon Jun 2 12:54:10 2003 @@ -39,6 +39,8 @@ #ifndef _LINUX_NFSD_XDR4_H #define _LINUX_NFSD_XDR4_H +#include + #define NFSD4_MAX_TAGLEN 128 typedef struct { @@ -98,9 +100,9 @@ struct nfsd4_commit { }; struct nfsd4_create { - u32 cr_namelen; /* request */ - char * cr_name; /* request */ - u32 cr_type; /* request */ + u32 cr_namelen; /* request */ + char * cr_name; /* request */ + u32 cr_type; /* request */ union { /* request */ struct { u32 namelen; @@ -111,9 +113,10 @@ struct nfsd4_create { u32 specdata2; } dev; /* NF4BLK, NF4CHR */ } u; - u32 cr_bmval[2]; /* request */ - struct iattr cr_iattr; /* request */ + u32 cr_bmval[2]; /* request */ + struct iattr cr_iattr; /* request */ struct nfsd4_change_info cr_cinfo; /* response */ + struct nfs4_acl *cr_acl; }; #define cr_linklen u.link.namelen #define cr_linkname u.link.name @@ -142,29 +145,29 @@ struct nfsd4_putfh { }; struct nfsd4_open { - u32 op_claim_type; /* request */ - u32 op_namelen; /* request - everything but CLAIM_PREV */ - char * op_name; /* request - everything but CLAIM_PREV */ - u32 op_delegate_type; /* request - CLAIM_PREV only */ + u32 op_claim_type; /* request */ + u32 op_namelen; /* request - everything but CLAIM_PREV */ + char * op_name; /* request - everything but CLAIM_PREV */ + u32 op_delegate_type; /* request - CLAIM_PREV only */ delegation_stateid_t op_delegate_stateid; /* request - CLAIM_DELEGATE_CUR only */ - u32 op_create; /* request */ - u32 op_createmode; /* request */ - u32 op_bmval[2]; /* request */ + u32 op_create; /* request */ + u32 op_createmode; /* request */ + u32 op_bmval[2]; /* request */ union { /* request */ struct iattr iattr; /* UNCHECKED4,GUARDED4 */ nfs4_verifier verf; /* EXCLUSIVE4 */ } u; - clientid_t op_clientid; /* request */ - u32 op_ownerlen; /* request */ - char * op_owner; /* request */ - u32 op_seqid; /* request */ - u32 op_share_access; /* request */ - u32 op_share_deny; /* request */ - stateid_t op_stateid; /* response */ + clientid_t op_clientid; /* request */ + u32 op_ownerlen; /* request */ + char * op_owner; /* request */ + u32 op_seqid; /* request */ + u32 op_share_access; /* request */ + u32 op_share_deny; /* request */ + stateid_t op_stateid; /* response */ struct nfsd4_change_info op_cinfo; /* response */ - u32 op_rflags; /* response */ - int op_truncate; /* used during processing */ - + u32 op_rflags; /* response */ + int op_truncate; /* used during processing */ + struct nfs4_acl *op_acl; }; #define op_iattr u.iattr #define op_verf u.verf @@ -216,9 +219,10 @@ struct nfsd4_rename { }; struct nfsd4_setattr { - stateid_t sa_stateid; /* request */ - u32 sa_bmval[2]; /* request */ - struct iattr sa_iattr; /* request */ + stateid_t sa_stateid; /* request */ + u32 sa_bmval[2]; /* request */ + struct iattr sa_iattr; /* request */ + struct nfs4_acl *sa_acl; }; struct nfsd4_setclientid { diff -puN include/linux/nfsd/nfsd.h~acl_server include/linux/nfsd/nfsd.h --- current/include/linux/nfsd/nfsd.h~acl_server Mon Jun 2 12:54:10 2003 +++ current-marius/include/linux/nfsd/nfsd.h Mon Jun 2 12:54:10 2003 @@ -73,6 +73,11 @@ int nfsd_lookup(struct svc_rqst *, stru const char *, int, struct svc_fh *); int nfsd_setattr(struct svc_rqst *, struct svc_fh *, struct iattr *, int, time_t); +#ifdef CONFIG_NFSD_V4 +int nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *, + struct nfs4_acl *); +int nfsd4_get_nfs4_acl(struct dentry *, struct nfs4_acl **); +#endif /* CONFIG_NFSD_V4 */ int nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, int type, dev_t rdev, struct svc_fh *res); @@ -223,7 +228,6 @@ extern struct timeval nfssvc_boot; /* * The following attributes are currently not supported by the NFSv4 server: - * ACL (will be supported in a forthcoming patch) * ARCHIVE (deprecated anyway) * FS_LOCATIONS (will be supported eventually) * HIDDEN (unlikely to be supported any time soon) @@ -243,7 +247,7 @@ extern struct timeval nfssvc_boot; | FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \ | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_HOMOGENEOUS \ | FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \ - | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE) + | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL) #define NFSD_SUPPORTED_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \ @@ -258,7 +262,8 @@ extern struct timeval nfssvc_boot; (FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) /* These are the only attrs allowed in CREATE/OPEN/SETATTR. */ -#define NFSD_WRITEABLE_ATTRS_WORD0 FATTR4_WORD0_SIZE +#define NFSD_WRITEABLE_ATTRS_WORD0 \ +(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL ) #define NFSD_WRITEABLE_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET) diff -puN fs/nfsd/nfs4proc.c~acl_server fs/nfsd/nfs4proc.c --- current/fs/nfsd/nfs4proc.c~acl_server Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfsd/nfs4proc.c Mon Jun 2 12:54:10 2003 @@ -450,7 +450,16 @@ nfsd4_rename(struct svc_rqst *rqstp, str static inline int nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr) { - return nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, 0, (time_t)0); + int error = 0; + + if (setattr->sa_acl != NULL) + error = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl); + + if (error == nfs_ok) + error = nfsd_setattr(rqstp, current_fh, + &setattr->sa_iattr, 0, (time_t)0); + + return error; } static inline int diff -puN fs/nfsd/vfs.c~acl_server fs/nfsd/vfs.c --- current/fs/nfsd/vfs.c~acl_server Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfsd/vfs.c Mon Jun 2 18:00:56 2003 @@ -43,6 +43,16 @@ #endif /* CONFIG_NFSD_V3 */ #include #include +#ifdef CONFIG_NFSD_V4 +#include +#include +#include +#include +#include +#include +#endif /* CONFIG_NFSD_V4 */ + + #include @@ -247,7 +257,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str /* Looks probable. Now just make sure time is in the right ballpark. * Solaris, at least, doesn't seem to care what the time request is. * We require it be within 30 minutes of now. - */ + 5A */ time_t delta = iap->ia_atime.tv_sec - get_seconds(); if (delta<0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR && @@ -324,6 +334,168 @@ out_nfserr: goto out; } +#ifdef CONFIG_NFSD_V4 +static int +_set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) +{ + int len, flag; + size_t buflen; + char *buf; + int error = 0; + struct inode *inode = dentry->d_inode; + + buflen = posix_acl_xattr_size(pacl->a_count); + buf = kmalloc(buflen, GFP_KERNEL); + if (buf == NULL) { + error = -ENOMEM; + goto out; + } + + len = posix_acl_to_xattr(pacl, buf, buflen); + if (len < 0) { + error = len; + goto out_buf; + } + + /* XXX MARIUS security_inode_setxattr() */ + down(&inode->i_sem); + /* XXX MARIUS is not allowing flag = 0 ext3 specific? */ + flag = (*inode->i_op->getxattr)(dentry, key, NULL, 0) > 0 ? + XATTR_REPLACE : XATTR_CREATE; + + error = (*inode->i_op->setxattr)(dentry, key, buf, len, flag); + up(&inode->i_sem); + + out_buf: + kfree(buf); + out: + return (error); +} + +int +nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct nfs4_acl *acl) +{ + int error; + struct dentry *dentry; + struct inode *inode; + struct posix_acl *pacl, *dpacl; + + /* Get inode */ + error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); + if (error) + goto out; + + dentry = fhp->fh_dentry; + inode = dentry->d_inode; + + if (inode->i_op == NULL || inode->i_op->setxattr == NULL || + inode->i_op->getxattr == NULL) { + error = -EIO; + goto out_nfserr; + } + + error = nfs4_acl_nfsv4_to_posix(NULL, acl, &pacl, &dpacl); + if (error < 0) + goto out_nfserr; + + if (pacl != NULL) { + error = _set_nfsv4_acl_one(dentry, pacl, + XATTR_NAME_ACL_ACCESS); + if (error < 0) + goto out_nfserr; + } + + if (dpacl != NULL) { + error = _set_nfsv4_acl_one(dentry, dpacl, + XATTR_NAME_ACL_DEFAULT); + if (error < 0) + goto out_nfserr; + } + + /* XXX MARIUS: notify change? */ + + error = nfs_ok; + + out: + return (error); + out_nfserr: + error = nfserrno(error); + goto out; +} + +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; + struct posix_acl *pacl = NULL; + + down(&inode->i_sem); + + buflen = (*inode->i_op->getxattr)(dentry, key, NULL, 0); + if (buflen <= 0) { + error = buflen < 0 ? buflen : -ENODATA; + goto out_sem; + } + + buf = kmalloc(buflen, GFP_KERNEL); + if (buf == NULL) { + error = -ENOMEM; + goto out_sem; + } + + error = (*inode->i_op->getxattr)(dentry, key, buf, buflen); + if (error < 0) + goto out_sem; + + error = 0; + up(&inode->i_sem); + + pacl = posix_acl_from_xattr(buf, buflen); + out: + if (buf != NULL) + kfree(buf); + return (pacl); + out_sem: + up(&inode->i_sem); + pacl = ERR_PTR(error); + goto out; +} + +int +nfsd4_get_nfs4_acl(struct dentry *dentry, struct nfs4_acl **acl) +{ + struct inode *inode = dentry->d_inode; + int error = 0; + struct posix_acl *pacl, *dpacl = NULL; + + pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS); + if (IS_ERR(pacl)) { + error = PTR_ERR(pacl); + goto out; + } + + if (S_ISDIR(inode->i_mode)) { + dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT); + if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA) + dpacl = NULL; + else if (IS_ERR(dpacl)) { + error = PTR_ERR(dpacl); + goto out; + } + } + + *acl = nfs4_acl_posix_to_nfsv4(NULL, pacl, dpacl); + if (IS_ERR(*acl)) + error = PTR_ERR(*acl); + out: + return (error); +} + +#endif /* CONFIG_NFSD_V4 */ + #ifdef CONFIG_NFSD_V3 /* * Check server access rights to a file system object _