Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / fs / nfsd / nfs4xdr.c
index 091114d..67d6800 100644 (file)
 
 #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)
 {
@@ -188,7 +73,7 @@ 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;
 }
 
 /*
@@ -252,7 +137,7 @@ xdr_error:                                  \
        }                                       \
 } 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
@@ -267,8 +152,7 @@ u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
        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;
@@ -306,7 +190,7 @@ defer_free(struct nfsd4_compoundargs *argp,
        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) {
@@ -404,8 +288,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                        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)
@@ -418,11 +300,10 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                                                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;
@@ -440,8 +321,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                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;
@@ -453,8 +332,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                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;
@@ -586,8 +463,6 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
                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:
@@ -653,7 +528,7 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
 {
        DECODE_HEAD;
 
-       lock->lk_stateowner = NULL;
+       lock->lk_replay_owner = NULL;
        /*
        * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
        */
@@ -1117,7 +992,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        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;
@@ -1334,16 +1209,15 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        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)); \
@@ -1455,7 +1329,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                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;
                }
        }
@@ -1487,7 +1364,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        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) {
                /*
@@ -1888,10 +1768,11 @@ nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
                 */
                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;
@@ -2019,7 +1900,6 @@ nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denie
 static void
 nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
 {
-
        ENCODE_SEQID_OP_HEAD;
 
        if (!nfserr) {
@@ -2030,7 +1910,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock
        } 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
@@ -2094,7 +1974,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
        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
@@ -2196,10 +2076,10 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read
        }
        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)
@@ -2209,27 +2089,20 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read
        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;
 }
@@ -2266,21 +2139,20 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r
 
        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;
 }
@@ -2290,7 +2162,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
 {
        int maxcount;
        loff_t offset;
-       u32 *page, *savep;
+       u32 *page, *savep, *tailbase;
        ENCODE_HEAD;
 
        if (nfserr)
@@ -2306,6 +2178,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
        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)
@@ -2350,14 +2223,12 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
        *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:
@@ -2599,10 +2470,8 @@ void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
                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;