|
@@ -74,37 +74,6 @@ enum {
|
|
|
cb_sequence_dec_sz + \
|
|
|
op_dec_sz)
|
|
|
|
|
|
-/*
|
|
|
- * Generic decode routines from fs/nfs/nfs4xdr.c
|
|
|
- */
|
|
|
-#define DECODE_TAIL \
|
|
|
- status = 0; \
|
|
|
-out: \
|
|
|
- return status; \
|
|
|
-xdr_error: \
|
|
|
- dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \
|
|
|
- status = -EIO; \
|
|
|
- goto out
|
|
|
-
|
|
|
-#define READ32(x) (x) = ntohl(*p++)
|
|
|
-#define READ64(x) do { \
|
|
|
- (x) = (u64)ntohl(*p++) << 32; \
|
|
|
- (x) |= ntohl(*p++); \
|
|
|
-} while (0)
|
|
|
-#define READTIME(x) do { \
|
|
|
- p++; \
|
|
|
- (x.tv_sec) = ntohl(*p++); \
|
|
|
- (x.tv_nsec) = ntohl(*p++); \
|
|
|
-} while (0)
|
|
|
-#define READ_BUF(nbytes) do { \
|
|
|
- p = xdr_inline_decode(xdr, nbytes); \
|
|
|
- if (!p) { \
|
|
|
- dprintk("NFSD: %s: reply buffer overflowed in line %d.\n", \
|
|
|
- __func__, __LINE__); \
|
|
|
- return -EIO; \
|
|
|
- } \
|
|
|
-} while (0)
|
|
|
-
|
|
|
struct nfs4_cb_compound_hdr {
|
|
|
/* args */
|
|
|
u32 ident; /* minorversion 0 only */
|
|
@@ -115,57 +84,14 @@ struct nfs4_cb_compound_hdr {
|
|
|
int status;
|
|
|
};
|
|
|
|
|
|
-static struct {
|
|
|
-int stat;
|
|
|
-int errno;
|
|
|
-} nfs_cb_errtbl[] = {
|
|
|
- { NFS4_OK, 0 },
|
|
|
- { NFS4ERR_PERM, EPERM },
|
|
|
- { NFS4ERR_NOENT, ENOENT },
|
|
|
- { NFS4ERR_IO, EIO },
|
|
|
- { NFS4ERR_NXIO, ENXIO },
|
|
|
- { NFS4ERR_ACCESS, EACCES },
|
|
|
- { NFS4ERR_EXIST, EEXIST },
|
|
|
- { NFS4ERR_XDEV, EXDEV },
|
|
|
- { NFS4ERR_NOTDIR, ENOTDIR },
|
|
|
- { NFS4ERR_ISDIR, EISDIR },
|
|
|
- { NFS4ERR_INVAL, EINVAL },
|
|
|
- { NFS4ERR_FBIG, EFBIG },
|
|
|
- { NFS4ERR_NOSPC, ENOSPC },
|
|
|
- { NFS4ERR_ROFS, EROFS },
|
|
|
- { NFS4ERR_MLINK, EMLINK },
|
|
|
- { NFS4ERR_NAMETOOLONG, ENAMETOOLONG },
|
|
|
- { NFS4ERR_NOTEMPTY, ENOTEMPTY },
|
|
|
- { NFS4ERR_DQUOT, EDQUOT },
|
|
|
- { NFS4ERR_STALE, ESTALE },
|
|
|
- { NFS4ERR_BADHANDLE, EBADHANDLE },
|
|
|
- { NFS4ERR_BAD_COOKIE, EBADCOOKIE },
|
|
|
- { NFS4ERR_NOTSUPP, ENOTSUPP },
|
|
|
- { NFS4ERR_TOOSMALL, ETOOSMALL },
|
|
|
- { NFS4ERR_SERVERFAULT, ESERVERFAULT },
|
|
|
- { NFS4ERR_BADTYPE, EBADTYPE },
|
|
|
- { NFS4ERR_LOCKED, EAGAIN },
|
|
|
- { NFS4ERR_RESOURCE, EREMOTEIO },
|
|
|
- { NFS4ERR_SYMLINK, ELOOP },
|
|
|
- { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP },
|
|
|
- { NFS4ERR_DEADLOCK, EDEADLK },
|
|
|
- { -1, EIO }
|
|
|
-};
|
|
|
-
|
|
|
-static int
|
|
|
-nfs_cb_stat_to_errno(int stat)
|
|
|
+/*
|
|
|
+ * Handle decode buffer overflows out-of-line.
|
|
|
+ */
|
|
|
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
|
|
|
{
|
|
|
- int i;
|
|
|
- for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
|
|
|
- if (nfs_cb_errtbl[i].stat == stat)
|
|
|
- return nfs_cb_errtbl[i].errno;
|
|
|
- }
|
|
|
- /* If we cannot translate the error, the recovery routines should
|
|
|
- * handle it.
|
|
|
- * Note: remaining NFSv4 error codes have values > 10000, so should
|
|
|
- * not conflict with native Linux error codes.
|
|
|
- */
|
|
|
- return stat;
|
|
|
+ dprintk("NFS: %s prematurely hit the end of our receive buffer. "
|
|
|
+ "Remaining buffer length is %tu words.\n",
|
|
|
+ func, xdr->end - xdr->p);
|
|
|
}
|
|
|
|
|
|
static __be32 *xdr_encode_empty_array(__be32 *p)
|
|
@@ -262,6 +188,89 @@ static void encode_sessionid4(struct xdr_stream *xdr,
|
|
|
NFS4_MAX_SESSIONID_LEN);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * nfsstat4
|
|
|
+ */
|
|
|
+static const struct {
|
|
|
+ int stat;
|
|
|
+ int errno;
|
|
|
+} nfs_cb_errtbl[] = {
|
|
|
+ { NFS4_OK, 0 },
|
|
|
+ { NFS4ERR_PERM, -EPERM },
|
|
|
+ { NFS4ERR_NOENT, -ENOENT },
|
|
|
+ { NFS4ERR_IO, -EIO },
|
|
|
+ { NFS4ERR_NXIO, -ENXIO },
|
|
|
+ { NFS4ERR_ACCESS, -EACCES },
|
|
|
+ { NFS4ERR_EXIST, -EEXIST },
|
|
|
+ { NFS4ERR_XDEV, -EXDEV },
|
|
|
+ { NFS4ERR_NOTDIR, -ENOTDIR },
|
|
|
+ { NFS4ERR_ISDIR, -EISDIR },
|
|
|
+ { NFS4ERR_INVAL, -EINVAL },
|
|
|
+ { NFS4ERR_FBIG, -EFBIG },
|
|
|
+ { NFS4ERR_NOSPC, -ENOSPC },
|
|
|
+ { NFS4ERR_ROFS, -EROFS },
|
|
|
+ { NFS4ERR_MLINK, -EMLINK },
|
|
|
+ { NFS4ERR_NAMETOOLONG, -ENAMETOOLONG },
|
|
|
+ { NFS4ERR_NOTEMPTY, -ENOTEMPTY },
|
|
|
+ { NFS4ERR_DQUOT, -EDQUOT },
|
|
|
+ { NFS4ERR_STALE, -ESTALE },
|
|
|
+ { NFS4ERR_BADHANDLE, -EBADHANDLE },
|
|
|
+ { NFS4ERR_BAD_COOKIE, -EBADCOOKIE },
|
|
|
+ { NFS4ERR_NOTSUPP, -ENOTSUPP },
|
|
|
+ { NFS4ERR_TOOSMALL, -ETOOSMALL },
|
|
|
+ { NFS4ERR_SERVERFAULT, -ESERVERFAULT },
|
|
|
+ { NFS4ERR_BADTYPE, -EBADTYPE },
|
|
|
+ { NFS4ERR_LOCKED, -EAGAIN },
|
|
|
+ { NFS4ERR_RESOURCE, -EREMOTEIO },
|
|
|
+ { NFS4ERR_SYMLINK, -ELOOP },
|
|
|
+ { NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP },
|
|
|
+ { NFS4ERR_DEADLOCK, -EDEADLK },
|
|
|
+ { -1, -EIO }
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * If we cannot translate the error, the recovery routines should
|
|
|
+ * handle it.
|
|
|
+ *
|
|
|
+ * Note: remaining NFSv4 error codes have values > 10000, so should
|
|
|
+ * not conflict with native Linux error codes.
|
|
|
+ */
|
|
|
+static int nfs_cb_stat_to_errno(int status)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
|
|
|
+ if (nfs_cb_errtbl[i].stat == status)
|
|
|
+ return nfs_cb_errtbl[i].errno;
|
|
|
+ }
|
|
|
+
|
|
|
+ dprintk("NFSD: Unrecognized NFS CB status value: %u\n", status);
|
|
|
+ return -status;
|
|
|
+}
|
|
|
+
|
|
|
+static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected,
|
|
|
+ enum nfsstat4 *status)
|
|
|
+{
|
|
|
+ __be32 *p;
|
|
|
+ u32 op;
|
|
|
+
|
|
|
+ p = xdr_inline_decode(xdr, 4 + 4);
|
|
|
+ if (unlikely(p == NULL))
|
|
|
+ goto out_overflow;
|
|
|
+ op = be32_to_cpup(p++);
|
|
|
+ if (unlikely(op != expected))
|
|
|
+ goto out_unexpected;
|
|
|
+ *status = be32_to_cpup(p);
|
|
|
+ return 0;
|
|
|
+out_overflow:
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
+ return -EIO;
|
|
|
+out_unexpected:
|
|
|
+ dprintk("NFSD: Callback server returned operation %d but "
|
|
|
+ "we issued a request for %d\n", op, expected);
|
|
|
+ return -EIO;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* CB_COMPOUND4args
|
|
|
*
|
|
@@ -295,6 +304,37 @@ static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
|
|
|
*hdr->nops_p = cpu_to_be32(hdr->nops);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * CB_COMPOUND4res
|
|
|
+ *
|
|
|
+ * struct CB_COMPOUND4res {
|
|
|
+ * nfsstat4 status;
|
|
|
+ * utf8str_cs tag;
|
|
|
+ * nfs_cb_resop4 resarray<>;
|
|
|
+ * };
|
|
|
+ */
|
|
|
+static int decode_cb_compound4res(struct xdr_stream *xdr,
|
|
|
+ struct nfs4_cb_compound_hdr *hdr)
|
|
|
+{
|
|
|
+ u32 length;
|
|
|
+ __be32 *p;
|
|
|
+
|
|
|
+ p = xdr_inline_decode(xdr, 4 + 4);
|
|
|
+ if (unlikely(p == NULL))
|
|
|
+ goto out_overflow;
|
|
|
+ hdr->status = be32_to_cpup(p++);
|
|
|
+ /* Ignore the tag */
|
|
|
+ length = be32_to_cpup(p++);
|
|
|
+ p = xdr_inline_decode(xdr, length + 4);
|
|
|
+ if (unlikely(p == NULL))
|
|
|
+ goto out_overflow;
|
|
|
+ hdr->nops = be32_to_cpup(p);
|
|
|
+ return 0;
|
|
|
+out_overflow:
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
+ return -EIO;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* CB_RECALL4args
|
|
|
*
|
|
@@ -356,6 +396,97 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
|
|
|
hdr->nops++;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * CB_SEQUENCE4resok
|
|
|
+ *
|
|
|
+ * struct CB_SEQUENCE4resok {
|
|
|
+ * sessionid4 csr_sessionid;
|
|
|
+ * sequenceid4 csr_sequenceid;
|
|
|
+ * slotid4 csr_slotid;
|
|
|
+ * slotid4 csr_highest_slotid;
|
|
|
+ * slotid4 csr_target_highest_slotid;
|
|
|
+ * };
|
|
|
+ *
|
|
|
+ * union CB_SEQUENCE4res switch (nfsstat4 csr_status) {
|
|
|
+ * case NFS4_OK:
|
|
|
+ * CB_SEQUENCE4resok csr_resok4;
|
|
|
+ * default:
|
|
|
+ * void;
|
|
|
+ * };
|
|
|
+ *
|
|
|
+ * Our current back channel implmentation supports a single backchannel
|
|
|
+ * with a single slot.
|
|
|
+ */
|
|
|
+static int decode_cb_sequence4resok(struct xdr_stream *xdr,
|
|
|
+ struct nfsd4_callback *cb)
|
|
|
+{
|
|
|
+ struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
|
|
|
+ struct nfs4_sessionid id;
|
|
|
+ int status;
|
|
|
+ __be32 *p;
|
|
|
+ u32 dummy;
|
|
|
+
|
|
|
+ status = -ESERVERFAULT;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the server returns different values for sessionID, slotID or
|
|
|
+ * sequence number, the server is looney tunes.
|
|
|
+ */
|
|
|
+ p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4);
|
|
|
+ if (unlikely(p == NULL))
|
|
|
+ goto out_overflow;
|
|
|
+ memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
|
|
|
+ if (memcmp(id.data, session->se_sessionid.data,
|
|
|
+ NFS4_MAX_SESSIONID_LEN) != 0) {
|
|
|
+ dprintk("NFS: %s Invalid session id\n", __func__);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
|
|
|
+
|
|
|
+ dummy = be32_to_cpup(p++);
|
|
|
+ if (dummy != session->se_cb_seq_nr) {
|
|
|
+ dprintk("NFS: %s Invalid sequence number\n", __func__);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ dummy = be32_to_cpup(p++);
|
|
|
+ if (dummy != 0) {
|
|
|
+ dprintk("NFS: %s Invalid slotid\n", __func__);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * FIXME: process highest slotid and target highest slotid
|
|
|
+ */
|
|
|
+ status = 0;
|
|
|
+out:
|
|
|
+ return status;
|
|
|
+out_overflow:
|
|
|
+ print_overflow_msg(__func__, xdr);
|
|
|
+ return -EIO;
|
|
|
+}
|
|
|
+
|
|
|
+static int decode_cb_sequence4res(struct xdr_stream *xdr,
|
|
|
+ struct nfsd4_callback *cb)
|
|
|
+{
|
|
|
+ enum nfsstat4 nfserr;
|
|
|
+ int status;
|
|
|
+
|
|
|
+ if (cb->cb_minorversion == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr);
|
|
|
+ if (unlikely(status))
|
|
|
+ goto out;
|
|
|
+ if (unlikely(nfserr != NFS4_OK))
|
|
|
+ goto out_default;
|
|
|
+ status = decode_cb_sequence4resok(xdr, cb);
|
|
|
+out:
|
|
|
+ return status;
|
|
|
+out_default:
|
|
|
+ return nfs_cb_stat_to_errno(status);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* NFSv4.0 and NFSv4.1 XDR encode functions
|
|
|
*
|
|
@@ -399,119 +530,51 @@ static int nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
|
|
|
}
|
|
|
|
|
|
|
|
|
-static int
|
|
|
-decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
|
|
|
- __be32 *p;
|
|
|
- u32 taglen;
|
|
|
-
|
|
|
- READ_BUF(8);
|
|
|
- READ32(hdr->status);
|
|
|
- /* We've got no use for the tag; ignore it: */
|
|
|
- READ32(taglen);
|
|
|
- READ_BUF(taglen + 4);
|
|
|
- p += XDR_QUADLEN(taglen);
|
|
|
- READ32(hdr->nops);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
|
|
|
-{
|
|
|
- __be32 *p;
|
|
|
- u32 op;
|
|
|
- int32_t nfserr;
|
|
|
-
|
|
|
- READ_BUF(8);
|
|
|
- READ32(op);
|
|
|
- if (op != expected) {
|
|
|
- dprintk("NFSD: decode_cb_op_hdr: Callback server returned "
|
|
|
- " operation %d but we issued a request for %d\n",
|
|
|
- op, expected);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
- READ32(nfserr);
|
|
|
- if (nfserr != NFS_OK)
|
|
|
- return -nfs_cb_stat_to_errno(nfserr);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
- * Our current back channel implmentation supports a single backchannel
|
|
|
- * with a single slot.
|
|
|
+ * NFSv4.0 and NFSv4.1 XDR decode functions
|
|
|
+ *
|
|
|
+ * NFSv4.0 callback result types are defined in section 15 of RFC
|
|
|
+ * 3530: "Network File System (NFS) version 4 Protocol" and section 20
|
|
|
+ * of RFC 5661: "Network File System (NFS) Version 4 Minor Version 1
|
|
|
+ * Protocol".
|
|
|
*/
|
|
|
-static int
|
|
|
-decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
|
|
|
- struct rpc_rqst *rqstp)
|
|
|
-{
|
|
|
- struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
|
|
|
- struct nfs4_sessionid id;
|
|
|
- int status;
|
|
|
- u32 dummy;
|
|
|
- __be32 *p;
|
|
|
-
|
|
|
- if (cb->cb_minorversion == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
|
|
|
- if (status)
|
|
|
- return status;
|
|
|
-
|
|
|
- /*
|
|
|
- * If the server returns different values for sessionID, slotID or
|
|
|
- * sequence number, the server is looney tunes.
|
|
|
- */
|
|
|
- status = -ESERVERFAULT;
|
|
|
-
|
|
|
- READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
|
|
|
- memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
|
|
|
- p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
|
|
|
- if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
|
|
|
- dprintk("%s Invalid session id\n", __func__);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- READ32(dummy);
|
|
|
- if (dummy != ses->se_cb_seq_nr) {
|
|
|
- dprintk("%s Invalid sequence number\n", __func__);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- READ32(dummy); /* slotid must be 0 */
|
|
|
- if (dummy != 0) {
|
|
|
- dprintk("%s Invalid slotid\n", __func__);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- /* FIXME: process highest slotid and target highest slotid */
|
|
|
- status = 0;
|
|
|
-out:
|
|
|
- return status;
|
|
|
-}
|
|
|
-
|
|
|
|
|
|
-static int
|
|
|
-nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
|
|
|
+static int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p, void *__unused)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
|
|
|
- struct nfsd4_callback *cb)
|
|
|
+/*
|
|
|
+ * 20.2. Operation 4: CB_RECALL - Recall a Delegation
|
|
|
+ */
|
|
|
+static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
|
|
|
+ struct nfsd4_callback *cb)
|
|
|
{
|
|
|
struct xdr_stream xdr;
|
|
|
struct nfs4_cb_compound_hdr hdr;
|
|
|
+ enum nfsstat4 nfserr;
|
|
|
int status;
|
|
|
|
|
|
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
|
|
|
- status = decode_cb_compound_hdr(&xdr, &hdr);
|
|
|
- if (status)
|
|
|
+ status = decode_cb_compound4res(&xdr, &hdr);
|
|
|
+ if (unlikely(status))
|
|
|
goto out;
|
|
|
- if (cb) {
|
|
|
- status = decode_cb_sequence(&xdr, cb, rqstp);
|
|
|
- if (status)
|
|
|
+
|
|
|
+ if (cb != NULL) {
|
|
|
+ status = decode_cb_sequence4res(&xdr, cb);
|
|
|
+ if (unlikely(status))
|
|
|
goto out;
|
|
|
}
|
|
|
- status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
|
|
|
+
|
|
|
+ status = decode_cb_op_status(&xdr, OP_CB_RECALL, &nfserr);
|
|
|
+ if (unlikely(status))
|
|
|
+ goto out;
|
|
|
+ if (unlikely(nfserr != NFS4_OK))
|
|
|
+ goto out_default;
|
|
|
out:
|
|
|
return status;
|
|
|
+out_default:
|
|
|
+ return nfs_cb_stat_to_errno(status);
|
|
|
}
|
|
|
|
|
|
/*
|