@@ -106,6 +106,20 @@ config NFSD_V4_2_WRITE_PLUS
If unsure, say N.
+config NFSD_V4_2_READ_PLUS
+ bool "Enable READ_PLUS support for the NFS v4.2 server"
+ depends on NFSD_V4
+ help
+ Say Y here if you want to enable support for the NFS v4.2 operation
+ READ_PLUS, which is used for reading a file that may contain data
+ holes (sparse files)
+
+ WARNING: there is still a chance of backwards-incompatible protocol
+ changes. This feature is targeted at developers and testers only.
+
+ If unsure, say N.
+
+
config NFSD_V4_SECURITY_LABEL
bool "Provide Security Label support for NFSv4 server"
depends on NFSD_V4 && SECURITY
@@ -1943,6 +1943,15 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_get_currentstateid = (stateid_getter)nfsd4_get_writestateid,
},
#endif /* CONFIG_NFSD_V4_2_WRITE_PLUS */
+#ifdef CONFIG_NFSD_V4_2_READ_PLUS
+ [OP_READ_PLUS] = {
+ .op_func = (nfsd4op_func)nfsd4_read,
+ .op_flags = OP_MODIFIES_SOMETHING,
+ .op_name = "OP_READ_PLUS",
+ .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
+ .op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid,
+ },
+#endif /* CONFIG_NFSD_V4_2_READ_PLUS */
#ifdef CONFIG_NFSD_V4_2_SEEK
[OP_SEEK] = {
.op_func = (nfsd4op_func)nfsd4_seek,
@@ -1636,7 +1636,11 @@ static nfsd4_dec nfsd4_dec_ops[] = {
#else
[OP_WRITE_PLUS] = (nfsd4_dec)nfsd4_decode_notsupp,
#endif /* CONFIG_NFSD_V4_2_WRITE_PLUS */
+#ifdef CONFIG_NFSD_V4_2_READ_PLUS
+ [OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_read,
+#else
[OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_notsupp,
+#endif /* CONFIG_NFSD_V4_2_READ_PLUS */
#ifdef CONFIG_NFSD_V4_2_SEEK
[OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek,
#else
@@ -3038,29 +3042,14 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struc
return nfserr;
}
-static __be32
-nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
- struct nfsd4_read *read)
+static void nfsd4_encode_read_setup_pages(struct nfsd4_compoundres *resp,
+ struct nfsd4_read *read,
+ unsigned long maxcount)
{
- u32 eof;
int v;
struct page *page;
- unsigned long maxcount;
- long len;
- __be32 *p;
-
- if (nfserr)
- return nfserr;
- if (resp->xbuf->page_len)
- return nfserr_resource;
-
- RESERVE_SPACE(8); /* eof flag and byte count */
-
- maxcount = svc_max_payload(resp->rqstp);
- if (maxcount > read->rd_length)
- maxcount = read->rd_length;
+ long len = maxcount;
- len = maxcount;
v = 0;
while (len > 0) {
page = *(resp->rqstp->rq_next_page);
@@ -3076,7 +3065,28 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
len -= PAGE_SIZE;
}
read->rd_vlen = v;
+}
+
+static __be32
+nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
+ struct nfsd4_read *read)
+{
+ u32 eof;
+ unsigned long maxcount;
+ __be32 *p;
+ if (nfserr)
+ return nfserr;
+ if (resp->xbuf->page_len)
+ return nfserr_resource;
+
+ RESERVE_SPACE(8); /* eof flag and byte count */
+
+ maxcount = svc_max_payload(resp->rqstp);
+ if (maxcount > read->rd_length)
+ maxcount = read->rd_length;
+
+ nfsd4_encode_read_setup_pages(resp, read, maxcount);
nfserr = nfsd_read_file(read->rd_rqstp, read->rd_fhp, read->rd_filp,
read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
&maxcount);
@@ -3631,6 +3641,131 @@ nfsd4_encode_write_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
}
#endif /* CONFIG_NFSD_V4_2_WRITE_PLUS */
+#ifdef CONFIG_NFSD_V4_2_READ_PLUS
+static __be32
+nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
+ struct nfsd4_read *read, u32 *eof)
+{
+ __be32 *p, nfserr;
+ unsigned long maxcount;
+
+ maxcount = svc_max_payload(resp->rqstp);
+ if (maxcount > read->rd_length)
+ maxcount = read->rd_length;
+
+ nfsd4_encode_read_setup_pages(resp, read, maxcount);
+ nfserr = nfsd_read_file(read->rd_rqstp, read->rd_fhp, read->rd_filp,
+ read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
+ &maxcount);
+ if (nfserr)
+ return nfserr;
+
+ RESERVE_SPACE(20);
+ WRITE32(NFS4_CONTENT_DATA);
+ WRITE64(read->rd_offset);
+ WRITE32(true); /* allocated flag */
+ WRITE32(maxcount);
+ ADJUST_ARGS();
+
+ *eof = (read->rd_offset + maxcount >=
+ read->rd_fhp->fh_dentry->d_inode->i_size);
+
+ resp->xbuf->head[0].iov_len = (char*)p
+ - (char*)resp->xbuf->head[0].iov_base;
+ resp->xbuf->page_len = maxcount;
+
+ /* Use rest of head for padding and remaining ops: */
+ resp->xbuf->tail[0].iov_base = p;
+ resp->xbuf->tail[0].iov_len = 0;
+ if (maxcount&3) {
+ 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;
+}
+
+static __be32
+nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp,
+ struct nfsd4_read *read, u32 *eof)
+{
+ __be32 *p;
+ u64 data_pos, max_pos, count;
+
+ /*
+ * Sometimes the file doesn't have any more data
+ */
+ data_pos = vfs_llseek(read->rd_filp, read->rd_offset, SEEK_DATA);
+ max_pos = read->rd_fhp->fh_dentry->d_inode->i_size;
+ if (data_pos > max_pos)
+ data_pos = max_pos;
+ count = data_pos - read->rd_offset;
+
+ RESERVE_SPACE(24);
+ WRITE32(NFS4_CONTENT_HOLE);
+ WRITE64(read->rd_offset);
+ WRITE64(count);
+ WRITE32(false);
+ ADJUST_ARGS();
+
+ *eof = (read->rd_offset + count >= max_pos);
+
+ read->rd_offset += count;
+ if (count > read->rd_length)
+ read->rd_length = 0;
+ else
+ read->rd_length -= count;
+
+ return 0;
+}
+
+static __be32
+nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
+ struct nfsd4_read *read)
+{
+ u32 eof, count = 0;
+ __be32 *p, *p_saved;
+
+ if (nfserr)
+ return nfserr;
+ if (resp->xbuf->page_len)
+ return nfserr_resource;
+
+ RESERVE_SPACE(8); /* eof flag and contents count */
+ p_saved = p;
+ WRITE32(0); /* dummy write, we'll revisit this point later */
+ WRITE32(0); /* transmit one giant data chunk */
+ ADJUST_ARGS();
+
+ /**
+ * Encode a hole only if we begin reading from one
+ */
+ if (read->rd_offset == vfs_llseek(read->rd_filp, read->rd_offset, SEEK_HOLE)) {
+ nfserr = nfsd4_encode_read_plus_hole(resp, read, &eof);
+ if (nfserr)
+ return nfserr;
+ count++;
+ if ((read->rd_length == 0) || (eof == true))
+ goto out_done;
+ }
+
+ /**
+ * Encode the rest as data
+ */
+ nfserr = nfsd4_encode_read_plus_data(resp, read, &eof);
+ if (nfserr)
+ return nfserr;
+ count++;
+
+out_done:
+ *p_saved++ = htonl(eof);
+ *p_saved = htonl(count);
+ return 0;
+}
+#endif /* CONFIG_NFSD_V4_2_READ_PLUS */
+
#ifdef CONFIG_NFSD_V4_2_SEEK
static __be32
nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
@@ -3737,7 +3872,11 @@ static nfsd4_enc nfsd4_enc_ops[] = {
#else
[OP_WRITE_PLUS] = (nfsd4_enc)nfsd4_encode_noop,
#endif /* CONFIG_NFSD_V4_2_WRITE_PLUS */
+#ifdef CONFIG_NFSD_V4_2_READ_PLUS
+ [OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_read_plus,
+#else
[OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_noop,
+#endif /* CONFIG_NFSD_V4_2_READ_PLUS */
#ifdef CONFIG_NFSD_V4_2_SEEK
[OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek,
#else