#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
#include <linux/nfsd_idmap.h>
+#include <linux/nfs4.h>
+#include <linux/nfs4_acl.h>
+#include <linux/vserver/xid.h>
#define NFSDDBG_FACILITY NFSDDBG_XDR
-static const char utf8_byte_len[256] = {
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0
-};
-
-static inline int
-is_legal_utf8_sequence(unsigned char *source, int length)
-{
- unsigned char *ptr;
- unsigned char c;
-
- if (length==1) return 1;
-
- /* Check for overlong sequence, and check second byte */
- c = *(source + 1);
- switch (*source) {
- case 0xE0: /* 3 bytes */
- if ( c < 0xA0 ) return 0;
- break;
- case 0xF0: /* 4 bytes */
- if ( c < 0x90 ) return 0;
- break;
- case 0xF8: /* 5 bytes */
- if ( c < 0xC8 ) return 0;
- break;
- case 0xFC: /* 6 bytes */
- if ( c < 0x84 ) return 0;
- break;
- default:
- if ( (c & 0xC0) != 0x80) return 0;
- }
-
- /* Check that trailing bytes look like 10xxxxxx */
- for (ptr = source++ + length - 1; ptr>source; ptr--)
- if ( ((*ptr) & 0xC0) != 0x80 ) return 0;
- return 1;
-}
-
-/* This does some screening on disallowed unicode characters. It is NOT
- * comprehensive.
- */
-static int
-is_allowed_utf8_char(unsigned char *source, int length)
-{
- /* We assume length and source point to a valid utf8 sequence */
- unsigned char c;
-
- /* Disallow F0000 and up (in utf8, F3B08080) */
- if (*source > 0xF3 ) return 0;
- c = *(source + 1);
- switch (*source) {
- case 0xF3:
- if (c >= 0xB0) return 0;
- break;
- /* Disallow D800-F8FF (in utf8, EDA080-EFA3BF */
- case 0xED:
- if (c >= 0xA0) return 0;
- break;
- case 0xEE:
- return 0;
- break;
- case 0xEF:
- if (c <= 0xA3) return 0;
- /* Disallow FFF9-FFFF (EFBFB9-EFBFBF) */
- if (c==0xBF)
- /* Don't need to check <=0xBF, since valid utf8 */
- if ( *(source+2) >= 0xB9) return 0;
- break;
- }
- return 1;
-}
-
-/* This routine should really check to see that the proper stringprep
- * mappings have been applied. Instead, we do a simple screen of some
- * of the more obvious illegal values by calling is_allowed_utf8_char.
- * This will allow many illegal strings through, but if a client behaves,
- * it will get full functionality. The other option (apart from full
- * stringprep checking) is to limit everything to an easily handled subset,
- * such as 7-bit ascii.
- *
- * Note - currently calling routines ignore return value except as boolean.
- */
-static int
-check_utf8(char *str, int len)
-{
- unsigned char *chunk, *sourceend;
- int chunklen;
-
- chunk = str;
- sourceend = str + len;
-
- while (chunk < sourceend) {
- chunklen = utf8_byte_len[*chunk];
- if (!chunklen)
- return nfserr_inval;
- if (chunk + chunklen > sourceend)
- return nfserr_inval;
- if (!is_legal_utf8_sequence(chunk, chunklen))
- return nfserr_inval;
- if (!is_allowed_utf8_char(chunk, chunklen))
- return nfserr_inval;
- if ( (chunklen==1) && (!*chunk) )
- return nfserr_inval; /* Disallow embedded nulls */
- chunk += chunklen;
- }
-
- return 0;
-}
-
static int
check_filename(char *str, int len, int err)
{
for (i = 0; i < len; i++)
if (str[i] == '/')
return err;
- return check_utf8(str, len);
+ return 0;
}
/*
} \
} while (0)
-u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
+static u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
{
/* We want more bytes than seem to be available.
* Maybe we need a new page, maybe we have just run out
if (nbytes <= sizeof(argp->tmp))
p = argp->tmp;
else {
- if (argp->tmpp)
- kfree(argp->tmpp);
+ kfree(argp->tmpp);
p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
if (!p)
return NULL;
return p;
}
-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+static int
+defer_free(struct nfsd4_compoundargs *argp,
+ void (*release)(const void *), void *p)
{
struct tmpbuf *tb;
+
+ tb = kmalloc(sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+ tb->buf = p;
+ tb->release = release;
+ tb->next = argp->to_free;
+ argp->to_free = tb;
+ return 0;
+}
+
+static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+{
+ void *new = NULL;
if (p == argp->tmp) {
- p = kmalloc(nbytes, GFP_KERNEL);
- if (!p) return NULL;
+ new = kmalloc(nbytes, GFP_KERNEL);
+ if (!new) return NULL;
+ p = new;
memcpy(p, argp->tmp, nbytes);
} else {
if (p != argp->tmpp)
BUG();
argp->tmpp = NULL;
}
- tb = kmalloc(sizeof(*tb), GFP_KERNEL);
- if (!tb) {
- kfree(p);
+ if (defer_free(argp, kfree, p)) {
+ kfree(new);
return NULL;
- }
- tb->buf = p;
- tb->next = argp->to_free;
- argp->to_free = tb;
- return (char*)p;
+ } else
+ return (char *)p;
}
}
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;
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;
+ }
+ defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
+
+ for (i = 0; i < nace; i++) {
+ READ_BUF(16); len += 16;
+ READ32(ace.type);
+ READ32(ace.flag);
+ READ32(ace.access_mask);
+ READ32(dummy32);
+ READ_BUF(dummy32);
+ len += XDR_QUADLEN(dummy32) << 2;
+ READMEM(buf, dummy32);
+ ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
+ status = 0;
+ if (ace.whotype != NFS4_ACL_WHO_NAMED)
+ ace.who = 0;
+ else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)
+ status = nfsd_map_name_to_gid(argp->rqstp,
+ buf, dummy32, &ace.who);
+ else
+ status = nfsd_map_name_to_uid(argp->rqstp,
+ buf, dummy32, &ace.who);
+ if (status)
+ goto out_nfserr;
+ status = nfs4_acl_add_ace(*acl, ace.type, ace.flag,
+ ace.access_mask, ace.whotype, ace.who);
+ if (status)
+ goto out_nfserr;
+ }
+ } else
+ *acl = NULL;
if (bmval[1] & FATTR4_WORD1_MODE) {
READ_BUF(4);
len += 4;
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
READMEM(buf, dummy32);
- if (check_utf8(buf, dummy32))
- return nfserr_inval;
if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
goto out_nfserr;
iattr->ia_valid |= ATTR_UID;
READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2);
READMEM(buf, dummy32);
- if (check_utf8(buf, dummy32))
- return nfserr_inval;
if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
goto out_nfserr;
iattr->ia_valid |= ATTR_GID;
DECODE_TAIL;
}
-#define NFS4_STATE_NOT_LOCKED ((void *)-1)
-
static int
nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
{
DECODE_HEAD;
- close->cl_stateowner = NFS4_STATE_NOT_LOCKED;
+ close->cl_stateowner = NULL;
READ_BUF(4 + sizeof(stateid_t));
READ32(close->cl_seqid);
READ32(close->cl_stateid.si_generation);
READ32(create->cr_linklen);
READ_BUF(create->cr_linklen);
SAVEMEM(create->cr_linkname, create->cr_linklen);
- if (check_utf8(create->cr_linkname, create->cr_linklen))
- return nfserr_inval;
break;
case NF4BLK:
case NF4CHR:
case NF4SOCK:
case NF4FIFO:
case NF4DIR:
- break;
default:
- goto xdr_error;
+ break;
}
READ_BUF(4);
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;
}
+static inline int
+nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
+{
+ DECODE_HEAD;
+
+ READ_BUF(sizeof(stateid_t));
+ READ32(dr->dr_stateid.si_generation);
+ COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+ DECODE_TAIL;
+}
+
static inline int
nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
{
{
DECODE_HEAD;
- lock->lk_stateowner = NFS4_STATE_NOT_LOCKED;
+ lock->lk_replay_owner = NULL;
/*
* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
*/
{
DECODE_HEAD;
- locku->lu_stateowner = NFS4_STATE_NOT_LOCKED;
+ locku->lu_stateowner = NULL;
READ_BUF(24 + sizeof(stateid_t));
READ32(locku->lu_type);
if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
memset(open->op_bmval, 0, sizeof(open->op_bmval));
open->op_iattr.ia_valid = 0;
- open->op_stateowner = NFS4_STATE_NOT_LOCKED;
+ open->op_stateowner = NULL;
/* seqid, share_access, share_deny, clientid, ownerlen */
READ_BUF(16 + sizeof(clientid_t));
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:
READ32(open->op_delegate_type);
break;
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
- READ_BUF(sizeof(delegation_stateid_t) + 4);
- COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
+ READ_BUF(sizeof(stateid_t) + 4);
+ COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t));
READ32(open->op_fname.len);
READ_BUF(open->op_fname.len);
SAVEMEM(open->op_fname.data, open->op_fname.len);
{
DECODE_HEAD;
- open_conf->oc_stateowner = NFS4_STATE_NOT_LOCKED;
+ open_conf->oc_stateowner = NULL;
READ_BUF(4 + sizeof(stateid_t));
READ32(open_conf->oc_req_stateid.si_generation);
COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t));
{
DECODE_HEAD;
- open_down->od_stateowner = NFS4_STATE_NOT_LOCKED;
- READ_BUF(4 + sizeof(stateid_t));
+ open_down->od_stateowner = NULL;
+ READ_BUF(12 + sizeof(stateid_t));
READ32(open_down->od_stateid.si_generation);
COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t));
READ32(open_down->od_seqid);
READ_BUF(sizeof(stateid_t));
READ32(setattr->sa_stateid.si_generation);
COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_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;
if (argp->opcnt > 100)
goto xdr_error;
- if (argp->opcnt > sizeof(argp->iops)/sizeof(argp->iops[0])) {
+ if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
if (!argp->ops) {
argp->ops = argp->iops;
case OP_CREATE:
op->status = nfsd4_decode_create(argp, &op->u.create);
break;
+ case OP_DELEGRETURN:
+ op->status = nfsd4_decode_delegreturn(argp, &op->u.delegreturn);
+ break;
case OP_GETATTR:
op->status = nfsd4_decode_getattr(argp, &op->u.getattr);
break;
save = resp->p;
/*
- * Routine for encoding the result of a
- * "seqid-mutating" NFSv4 operation. This is
- * where seqids are incremented, and the
- * replay cache is filled.
+ * Routine for encoding the result of a "seqid-mutating" NFSv4 operation. This
+ * is where sequence id's are incremented, and the replay cache is filled.
+ * Note that we increment sequence id's here, at the last moment, so we're sure
+ * we know whether the error to be returned is a sequence id mutating error.
*/
#define ENCODE_SEQID_OP_TAIL(stateowner) do { \
- if (seqid_mutating_err(nfserr) && stateowner \
- && (stateowner != NFS4_STATE_NOT_LOCKED)) { \
- if (stateowner->so_confirmed) \
- stateowner->so_seqid++; \
+ if (seqid_mutating_err(nfserr) && stateowner) { \
+ stateowner->so_seqid++; \
stateowner->so_replay.rp_status = nfserr; \
stateowner->so_replay.rp_buflen = \
(((char *)(resp)->p - (char *)save)); \
memcpy(stateowner->so_replay.rp_buf, save, \
stateowner->so_replay.rp_buflen); \
- } \
- if (stateowner != NFS4_STATE_NOT_LOCKED) \
- nfs4_unlock_state(); \
- } while (0);
+ } } while (0);
static u32 nfs4_ftypes[16] = {
NF4SOCK, NF4BAD, NF4LNK, NF4BAD,
};
+static int
+nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+ u32 **p, int *buflen)
+{
+ int status;
+
+ if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
+ return nfserr_resource;
+ if (whotype != NFS4_ACL_WHO_NAMED)
+ status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
+ else if (group)
+ status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
+ else
+ status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
+ if (status < 0)
+ return nfserrno(status);
+ *p = xdr_encode_opaque(*p, NULL, status);
+ *buflen -= (XDR_QUADLEN(status) << 2) + 4;
+ BUG_ON(*buflen < 0);
+ return 0;
+}
+
+static inline int
+nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
+{
+ return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
+}
+
+static inline int
+nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen)
+{
+ return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
+}
+
+static inline int
+nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+ u32 **p, int *buflen)
+{
+ return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
+}
+
+
/*
* Note: @fhp can be NULL; in this case, we might have to compose the filehandle
* ourselves.
u32 bmval0 = bmval[0];
u32 bmval1 = bmval[1];
struct kstat stat;
- char owner[IDMAP_NAMESZ];
- u32 ownerlen = 0;
- char group[IDMAP_NAMESZ];
- u32 grouplen = 0;
struct svc_fh tempfh;
struct kstatfs statfs;
int buflen = *countp << 2;
u64 dummy64;
u32 *p = buffer;
int status;
+ int aclsupport = 0;
+ struct nfs4_acl *acl = NULL;
BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
if (status)
goto out_nfserr;
}
- if ((bmval0 & FATTR4_WORD0_FILEHANDLE) && !fhp) {
+ if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
fh_init(&tempfh, NFS4_FHSIZE);
status = fh_compose(&tempfh, exp, dentry, NULL);
if (status)
goto out;
fhp = &tempfh;
}
- if (bmval1 & FATTR4_WORD1_OWNER) {
- int temp = nfsd_map_uid_to_name(rqstp, stat.uid, owner);
- if (temp < 0) {
- status = temp;
- goto out_nfserr;
- }
- ownerlen = (unsigned) temp;
- }
- if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
- int temp = nfsd_map_gid_to_name(rqstp, stat.gid, group);
- if (temp < 0) {
- status = temp;
- goto out_nfserr;
+ if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
+ | FATTR4_WORD0_SUPPORTED_ATTRS)) {
+ status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+ aclsupport = (status == 0);
+ if (bmval0 & FATTR4_WORD0_ACL) {
+ if (status == -EOPNOTSUPP)
+ bmval0 &= ~FATTR4_WORD0_ACL;
+ else if (status == -EINVAL) {
+ status = nfserr_attrnotsupp;
+ goto out;
+ } else if (status != 0)
+ goto out_nfserr;
}
- grouplen = (unsigned) temp;
}
-
if ((buflen -= 16) < 0)
goto out_resource;
if ((buflen -= 12) < 0)
goto out_resource;
WRITE32(2);
- WRITE32(NFSD_SUPPORTED_ATTRS_WORD0);
+ WRITE32(aclsupport ?
+ NFSD_SUPPORTED_ATTRS_WORD0 :
+ NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
}
if (bmval0 & FATTR4_WORD0_TYPE) {
if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
if ((buflen -= 4) < 0)
goto out_resource;
- WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME );
+ if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
+ WRITE32(NFS4_FH_PERSISTENT);
+ else
+ WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
}
if (bmval0 & FATTR4_WORD0_CHANGE) {
/*
if (bmval0 & FATTR4_WORD0_FSID) {
if ((buflen -= 16) < 0)
goto out_resource;
- WRITE32(0);
- WRITE32(MAJOR(stat.dev));
- WRITE32(0);
- WRITE32(MINOR(stat.dev));
+ if (is_fsid(fhp, rqstp->rq_reffh)) {
+ WRITE64((u64)exp->ex_fsid);
+ WRITE64((u64)0);
+ } else {
+ WRITE32(0);
+ WRITE32(MAJOR(stat.dev));
+ WRITE32(0);
+ WRITE32(MINOR(stat.dev));
+ }
}
if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
if ((buflen -= 4) < 0)
goto out_resource;
WRITE32(0);
}
+ if (bmval0 & FATTR4_WORD0_ACL) {
+ struct nfs4_ace *ace;
+ struct list_head *h;
+
+ if (acl == NULL) {
+ if ((buflen -= 4) < 0)
+ goto out_resource;
+
+ WRITE32(0);
+ goto out_acl;
+ }
+ if ((buflen -= 4) < 0)
+ goto out_resource;
+ WRITE32(acl->naces);
+
+ list_for_each(h, &acl->ace_head) {
+ ace = list_entry(h, struct nfs4_ace, l_ace);
+
+ if ((buflen -= 4*3) < 0)
+ goto out_resource;
+ WRITE32(ace->type);
+ WRITE32(ace->flag);
+ WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
+ status = nfsd4_encode_aclname(rqstp, ace->whotype,
+ ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
+ &p, &buflen);
+ if (status == nfserr_resource)
+ goto out_resource;
+ if (status)
+ goto out;
+ }
+ }
+out_acl:
if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
if ((buflen -= 4) < 0)
goto out_resource;
- WRITE32(0);
+ WRITE32(aclsupport ?
+ ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
}
if (bmval0 & FATTR4_WORD0_CANSETTIME) {
if ((buflen -= 4) < 0)
WRITE32(stat.nlink);
}
if (bmval1 & FATTR4_WORD1_OWNER) {
- buflen -= (XDR_QUADLEN(ownerlen) << 2) + 4;
- if (buflen < 0)
+ status = nfsd4_encode_user(rqstp,
+ XIDINO_UID(XID_TAG(dentry->d_inode),
+ stat.uid, stat.xid), &p, &buflen);
+ if (status == nfserr_resource)
goto out_resource;
- WRITE32(ownerlen);
- WRITEMEM(owner, ownerlen);
+ if (status)
+ goto out;
}
if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
- buflen -= (XDR_QUADLEN(grouplen) << 2) + 4;
- if (buflen < 0)
+ status = nfsd4_encode_group(rqstp,
+ XIDINO_GID(XID_TAG(dentry->d_inode),
+ stat.gid, stat.xid), &p, &buflen);
+ if (status == nfserr_resource)
goto out_resource;
- WRITE32(grouplen);
- WRITEMEM(group, grouplen);
+ if (status)
+ goto out;
}
if (bmval1 & FATTR4_WORD1_RAWDEV) {
if ((buflen -= 8) < 0)
status = nfs_ok;
out:
+ nfs4_acl_free(acl);
if (fhp == &tempfh)
fh_put(&tempfh);
return status;
goto out;
}
+static int
+nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
+ const char *name, int namlen, u32 *p, int *buflen)
+{
+ struct svc_export *exp = cd->rd_fhp->fh_export;
+ struct dentry *dentry;
+ int nfserr;
+
+ dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
+ if (IS_ERR(dentry))
+ return nfserrno(PTR_ERR(dentry));
+
+ exp_get(exp);
+ if (d_mountpoint(dentry)) {
+ if (nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp)) {
+ /*
+ * -EAGAIN is the only error returned from
+ * nfsd_cross_mnt() and it indicates that an
+ * up-call has been initiated to fill in the export
+ * options on exp. When the answer comes back,
+ * this call will be retried.
+ */
+ nfserr = nfserr_dropit;
+ goto out_put;
+ }
+
+ }
+ nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
+ cd->rd_rqstp);
+out_put:
+ dput(dentry);
+ exp_put(exp);
+ return nfserr;
+}
+
+static u32 *
+nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr)
+{
+ u32 *attrlenp;
+
+ if (buflen < 6)
+ return NULL;
+ *p++ = htonl(2);
+ *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
+ *p++ = htonl(0); /* bmval1 */
+
+ attrlenp = p++;
+ *p++ = nfserr; /* no htonl */
+ *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
+ return p;
+}
+
static int
nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
loff_t offset, ino_t ino, unsigned int d_type)
struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
int buflen;
u32 *p = cd->buffer;
- u32 *attrlenp;
- struct dentry *dentry;
- struct svc_export *exp = cd->rd_fhp->fh_export;
- u32 bmval0, bmval1;
- int nfserr = 0;
+ int nfserr = nfserr_toosmall;
/* In nfsv4, "." and ".." never make it onto the wire.. */
if (name && isdotent(name, namlen)) {
buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
if (buflen < 0)
- goto nospc;
+ goto fail;
*p++ = xdr_one; /* mark entry present */
cd->offset = p; /* remember pointer */
p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
p = xdr_encode_array(p, name, namlen); /* name length & name */
- /*
- * Now we come to the ugly part: writing the fattr for this entry.
- */
- bmval0 = cd->rd_bmval[0];
- bmval1 = cd->rd_bmval[1];
- if ((bmval0 & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_FILEID)) || bmval1) {
- /*
- * "Heavyweight" case: we have no choice except to
- * call nfsd4_encode_fattr().
- */
- dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
- if (IS_ERR(dentry)) {
- nfserr = nfserrno(PTR_ERR(dentry));
- goto error;
- }
-
- if (d_mountpoint(dentry)) {
- if ((nfserr = nfsd_cross_mnt(cd->rd_rqstp, &dentry,
- &exp))) {
- /*
- * -EAGAIN is the only error returned from
- * nfsd_cross_mnt() and it indicates that an
- * up-call has been initiated to fill in the export
- * options on exp. When the answer comes back,
- * this call will be retried.
- */
- dput(dentry);
- nfserr = nfserr_dropit;
- goto error;
- }
-
- }
-
- nfserr = nfsd4_encode_fattr(NULL, exp,
- dentry, p, &buflen, cd->rd_bmval,
- cd->rd_rqstp);
- if (!nfserr) {
- p += buflen;
- goto out;
- }
- if (nfserr == nfserr_resource)
- goto nospc;
-
-error:
+ nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen);
+ switch (nfserr) {
+ case nfs_ok:
+ p += buflen;
+ break;
+ case nfserr_resource:
+ nfserr = nfserr_toosmall;
+ goto fail;
+ case nfserr_dropit:
+ goto fail;
+ default:
/*
- * If we get here, we experienced a miscellaneous
- * failure while writing the attributes. If the
- * client requested the RDATTR_ERROR attribute,
+ * If the client requested the RDATTR_ERROR attribute,
* we stuff the error code into this attribute
* and continue. If this attribute was not requested,
* then in accordance with the spec, we fail the
* entire READDIR operation(!)
*/
- if (!(bmval0 & FATTR4_WORD0_RDATTR_ERROR)) {
- cd->common.err = nfserr;
- return -EINVAL;
+ if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
+ goto fail;
+ p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
+ if (p == NULL) {
+ nfserr = nfserr_toosmall;
+ goto fail;
}
-
- bmval0 = FATTR4_WORD0_RDATTR_ERROR;
- bmval1 = 0;
- /* falling through here will do the right thing... */
}
-
- /*
- * In the common "lightweight" case, we avoid
- * the overhead of nfsd4_encode_fattr() by assembling
- * a small fattr by hand.
- */
- if (buflen < 6)
- goto nospc;
- *p++ = htonl(2);
- *p++ = htonl(bmval0);
- *p++ = htonl(bmval1);
-
- attrlenp = p++;
- if (bmval0 & FATTR4_WORD0_RDATTR_ERROR)
- *p++ = nfserr; /* no htonl */
- if (bmval0 & FATTR4_WORD0_FILEID)
- p = xdr_encode_hyper(p, (u64)ino);
- *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
-
-out:
cd->buflen -= (p - cd->buffer);
cd->buffer = p;
cd->common.err = nfs_ok;
return 0;
-
-nospc:
- cd->common.err = nfserr_toosmall;
+fail:
+ cd->common.err = nfserr;
return -EINVAL;
}
{
ENCODE_HEAD;
- RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop->so_owner.len));
+ RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
WRITE64(ld->ld_start);
WRITE64(ld->ld_length);
WRITE32(ld->ld_type);
- WRITEMEM(&ld->ld_sop->so_client->cl_clientid, 8);
- WRITE32(ld->ld_sop->so_owner.len);
- WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
+ if (ld->ld_sop) {
+ WRITEMEM(&ld->ld_clientid, 8);
+ WRITE32(ld->ld_sop->so_owner.len);
+ WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
+ kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
+ } else { /* non - nfsv4 lock in conflict, no clientid nor owner */
+ WRITE64((u64)0); /* clientid */
+ WRITE32(0); /* length of owner name */
+ }
ADJUST_ARGS();
}
static void
nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
{
-
ENCODE_SEQID_OP_HEAD;
if (!nfserr) {
} else if (nfserr == nfserr_denied)
nfsd4_encode_lock_denied(resp, &lock->lk_denied);
- ENCODE_SEQID_OP_TAIL(lock->lk_stateowner);
+ ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
}
static void
case NFS4_OPEN_DELEGATE_NONE:
break;
case NFS4_OPEN_DELEGATE_READ:
- RESERVE_SPACE(20 + sizeof(delegation_stateid_t));
- WRITEMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
- WRITE32(0);
+ RESERVE_SPACE(20 + sizeof(stateid_t));
+ WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+ WRITE32(open->op_recall);
/*
* TODO: ACE's in delegations
ADJUST_ARGS();
break;
case NFS4_OPEN_DELEGATE_WRITE:
- RESERVE_SPACE(32 + sizeof(delegation_stateid_t));
- WRITEMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
+ RESERVE_SPACE(32 + sizeof(stateid_t));
+ WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
WRITE32(0);
/*
}
read->rd_vlen = v;
- nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp,
- read->rd_offset,
- read->rd_iov, read->rd_vlen,
- &maxcount);
+ nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp,
+ read->rd_offset, read->rd_iov, read->rd_vlen,
+ &maxcount);
+
if (nfserr == nfserr_symlink)
nfserr = nfserr_inval;
if (nfserr)
WRITE32(eof);
WRITE32(maxcount);
ADJUST_ARGS();
- resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
-
+ resp->xbuf->head[0].iov_len = (char*)p
+ - (char*)resp->xbuf->head[0].iov_base;
resp->xbuf->page_len = maxcount;
- /* read zero bytes -> don't set up tail */
- if(!maxcount)
- return 0;
-
- /* set up page for remaining responses */
- svc_take_page(resp->rqstp);
- resp->xbuf->tail[0].iov_base =
- page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
- resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+ /* Use rest of head for padding and remaining ops: */
+ resp->rqstp->rq_restailpage = 0;
+ resp->xbuf->tail[0].iov_base = p;
resp->xbuf->tail[0].iov_len = 0;
- resp->p = resp->xbuf->tail[0].iov_base;
- resp->end = resp->p + PAGE_SIZE/4;
-
if (maxcount&3) {
- *(resp->p)++ = 0;
+ RESERVE_SPACE(4);
+ WRITE32(0);
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
+ ADJUST_ARGS();
}
return 0;
}
WRITE32(maxcount);
ADJUST_ARGS();
- resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
+ resp->xbuf->head[0].iov_len = (char*)p
+ - (char*)resp->xbuf->head[0].iov_base;
+ resp->xbuf->page_len = maxcount;
- svc_take_page(resp->rqstp);
- resp->xbuf->tail[0].iov_base =
- page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
- resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+ /* Use rest of head for padding and remaining ops: */
+ resp->rqstp->rq_restailpage = 0;
+ resp->xbuf->tail[0].iov_base = p;
resp->xbuf->tail[0].iov_len = 0;
- resp->p = resp->xbuf->tail[0].iov_base;
- resp->end = resp->p + PAGE_SIZE/4;
-
- resp->xbuf->page_len = maxcount;
if (maxcount&3) {
- *(resp->p)++ = 0;
+ RESERVE_SPACE(4);
+ WRITE32(0);
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
+ ADJUST_ARGS();
}
return 0;
}
{
int maxcount;
loff_t offset;
- u32 *page, *savep;
+ u32 *page, *savep, *tailbase;
ENCODE_HEAD;
if (nfserr)
WRITE32(0);
ADJUST_ARGS();
resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
+ tailbase = p;
maxcount = PAGE_SIZE;
if (maxcount > readdir->rd_maxcount)
*p++ = htonl(readdir->common.err == nfserr_eof);
resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
- /* allocate a page for the tail */
- svc_take_page(resp->rqstp);
- resp->xbuf->tail[0].iov_base =
- page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
- resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+ /* Use rest of head for padding and remaining ops: */
+ resp->rqstp->rq_restailpage = 0;
+ resp->xbuf->tail[0].iov_base = tailbase;
resp->xbuf->tail[0].iov_len = 0;
resp->p = resp->xbuf->tail[0].iov_base;
- resp->end = resp->p + PAGE_SIZE/4;
+ resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
return 0;
err_no_verf:
case OP_CREATE:
nfsd4_encode_create(resp, op->status, &op->u.create);
break;
+ case OP_DELEGRETURN:
+ break;
case OP_GETATTR:
op->status = nfsd4_encode_getattr(resp, op->status, &op->u.getattr);
break;
RESERVE_SPACE(rp->rp_buflen);
WRITEMEM(rp->rp_buf, rp->rp_buflen);
ADJUST_ARGS();
- nfs4_unlock_state();
}
/*
return xdr_ressize_check(rqstp, p);
}
+void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
+{
+ if (args->ops != args->iops) {
+ kfree(args->ops);
+ args->ops = args->iops;
+ }
+ kfree(args->tmpp);
+ args->tmpp = NULL;
+ while (args->to_free) {
+ struct tmpbuf *tb = args->to_free;
+ args->to_free = tb->next;
+ tb->release(tb->buf);
+ kfree(tb);
+ }
+}
+
int
nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
{
status = nfsd4_decode_compound(args);
if (status) {
- if (args->ops != args->iops) {
- kfree(args->ops);
- args->ops = args->iops;
- }
- if (args->tmpp) {
- kfree(args->tmpp);
- args->tmpp = NULL;
- }
- while (args->to_free) {
- struct tmpbuf *tb = args->to_free;
- args->to_free = tb->next;
- kfree(tb->buf);
- kfree(tb);
- }
+ nfsd4_release_compoundargs(args);
}
return !status;
}
/*
* All that remains is to write the tag and operation count...
*/
- struct iovec *iov;
+ struct kvec *iov;
p = resp->tagp;
*p++ = htonl(resp->taglen);
memcpy(p, resp->tag, resp->taglen);