#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 0;
}
-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
{
void *new = NULL;
if (p == argp->tmp) {
READ_BUF(dummy32);
len += XDR_QUADLEN(dummy32) << 2;
READMEM(buf, dummy32);
- if (check_utf8(buf, dummy32))
- return nfserr_inval;
ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
status = 0;
if (ace.whotype != NFS4_ACL_WHO_NAMED)
buf, dummy32, &ace.who);
if (status)
goto out_nfserr;
- if (nfs4_acl_add_ace(*acl, ace.type, ace.flag,
- ace.access_mask, ace.whotype, ace.who) != 0) {
- status = -ENOMEM;
+ 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;
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;
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:
{
DECODE_HEAD;
- lock->lk_stateowner = NULL;
+ lock->lk_replay_owner = NULL;
/*
* type, reclaim(boolean), offset, length, new_lock_owner(boolean)
*/
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;
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) { \
- if (stateowner->so_confirmed) \
- stateowner->so_seqid++; \
+ stateowner->so_seqid++; \
stateowner->so_replay.rp_status = nfserr; \
stateowner->so_replay.rp_buflen = \
(((char *)(resp)->p - (char *)save)); \
if (bmval0 & FATTR4_WORD0_ACL) {
if (status == -EOPNOTSUPP)
bmval0 &= ~FATTR4_WORD0_ACL;
- else if (status != 0)
+ else if (status == -EINVAL) {
+ status = nfserr_attrnotsupp;
+ goto out;
+ } else if (status != 0)
goto out_nfserr;
}
}
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 (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
goto fail;
- nfserr = nfserr_toosmall;
p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
- if (p == NULL)
+ if (p == NULL) {
+ nfserr = nfserr_toosmall;
goto fail;
+ }
}
cd->buflen -= (p - cd->buffer);
cd->buffer = p;
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_READ:
RESERVE_SPACE(20 + sizeof(stateid_t));
WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
- WRITE32(0);
+ WRITE32(open->op_recall);
/*
* TODO: ACE's in delegations
}
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:
kfree(args->ops);
args->ops = args->iops;
}
- if (args->tmpp) {
- kfree(args->tmpp);
- args->tmpp = NULL;
- }
+ kfree(args->tmpp);
+ args->tmpp = NULL;
while (args->to_free) {
struct tmpbuf *tb = args->to_free;
args->to_free = tb->next;