blob: be8bf8af9917aa61f296e34e772f2bd23a8c79e5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * XDR support for nfsd/protocol version 3.
3 *
4 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
5 *
6 * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
7 */
8
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/namei.h>
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +030010#include <linux/sunrpc/svc_xprt.h>
Boaz Harrosh9a74af22009-12-03 20:30:56 +020011#include "xdr3.h"
J. Bruce Fields2e8138a2007-11-15 17:05:43 -050012#include "auth.h"
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +030013#include "netns.h"
Al Viro3dadecc2013-01-24 02:18:08 -050014#include "vfs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
16#define NFSDDBG_FACILITY NFSDDBG_XDR
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
19/*
20 * Mapping of S_IF* types to NFS file types
21 */
22static u32 nfs3_ftypes[] = {
23 NF3NON, NF3FIFO, NF3CHR, NF3BAD,
24 NF3DIR, NF3BAD, NF3BLK, NF3BAD,
25 NF3REG, NF3BAD, NF3LNK, NF3BAD,
26 NF3SOCK, NF3BAD, NF3LNK, NF3BAD,
27};
28
29/*
30 * XDR functions for basic NFS types
31 */
Adrian Bunk3ee6f612006-12-06 20:40:23 -080032static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070033encode_time3(__be32 *p, struct timespec *time)
Linus Torvalds1da177e2005-04-16 15:20:36 -070034{
35 *p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
36 return p;
37}
38
Adrian Bunk3ee6f612006-12-06 20:40:23 -080039static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070040decode_time3(__be32 *p, struct timespec *time)
Linus Torvalds1da177e2005-04-16 15:20:36 -070041{
42 time->tv_sec = ntohl(*p++);
43 time->tv_nsec = ntohl(*p++);
44 return p;
45}
46
Adrian Bunk3ee6f612006-12-06 20:40:23 -080047static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070048decode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
50 unsigned int size;
51 fh_init(fhp, NFS3_FHSIZE);
52 size = ntohl(*p++);
53 if (size > NFS3_FHSIZE)
54 return NULL;
55
56 memcpy(&fhp->fh_handle.fh_base, p, size);
57 fhp->fh_handle.fh_size = size;
58 return p + XDR_QUADLEN(size);
59}
60
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +000061/* Helper function for NFSv3 ACL code */
Al Viro91f07162006-10-19 23:28:57 -070062__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp)
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +000063{
64 return decode_fh(p, fhp);
65}
66
Adrian Bunk3ee6f612006-12-06 20:40:23 -080067static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070068encode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070069{
70 unsigned int size = fhp->fh_handle.fh_size;
71 *p++ = htonl(size);
72 if (size) p[XDR_QUADLEN(size)-1]=0;
73 memcpy(p, &fhp->fh_handle.fh_base, size);
74 return p + XDR_QUADLEN(size);
75}
76
77/*
78 * Decode a file name and make sure that the path contains
79 * no slashes or null bytes.
80 */
Adrian Bunk3ee6f612006-12-06 20:40:23 -080081static __be32 *
Chuck Leveree1a95b2007-11-01 16:56:58 -040082decode_filename(__be32 *p, char **namp, unsigned int *lenp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083{
84 char *name;
Chuck Leveree1a95b2007-11-01 16:56:58 -040085 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87 if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
88 for (i = 0, name = *namp; i < *lenp; i++, name++) {
89 if (*name == '\0' || *name == '/')
90 return NULL;
91 }
92 }
93
94 return p;
95}
96
Adrian Bunk3ee6f612006-12-06 20:40:23 -080097static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070098decode_sattr3(__be32 *p, struct iattr *iap)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099{
100 u32 tmp;
101
102 iap->ia_valid = 0;
103
104 if (*p++) {
105 iap->ia_valid |= ATTR_MODE;
106 iap->ia_mode = ntohl(*p++);
107 }
108 if (*p++) {
Eric W. Biederman458878a2013-02-02 04:16:08 -0800109 iap->ia_uid = make_kuid(&init_user_ns, ntohl(*p++));
110 if (uid_valid(iap->ia_uid))
111 iap->ia_valid |= ATTR_UID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 }
113 if (*p++) {
Eric W. Biederman458878a2013-02-02 04:16:08 -0800114 iap->ia_gid = make_kgid(&init_user_ns, ntohl(*p++));
115 if (gid_valid(iap->ia_gid))
116 iap->ia_valid |= ATTR_GID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 }
118 if (*p++) {
119 u64 newsize;
120
121 iap->ia_valid |= ATTR_SIZE;
122 p = xdr_decode_hyper(p, &newsize);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800123 iap->ia_size = min_t(u64, newsize, NFS_OFFSET_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 }
125 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
126 iap->ia_valid |= ATTR_ATIME;
127 } else if (tmp == 2) { /* set to client time */
128 iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
129 iap->ia_atime.tv_sec = ntohl(*p++);
130 iap->ia_atime.tv_nsec = ntohl(*p++);
131 }
132 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
133 iap->ia_valid |= ATTR_MTIME;
134 } else if (tmp == 2) { /* set to client time */
135 iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
136 iap->ia_mtime.tv_sec = ntohl(*p++);
137 iap->ia_mtime.tv_nsec = ntohl(*p++);
138 }
139 return p;
140}
141
NeilBrownaf6a4e22007-02-14 00:33:12 -0800142static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
143{
144 u64 f;
145 switch(fsid_source(fhp)) {
146 default:
147 case FSIDSOURCE_DEV:
148 p = xdr_encode_hyper(p, (u64)huge_encode_dev
Al Virofc640052016-04-10 01:33:30 -0400149 (fhp->fh_dentry->d_sb->s_dev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800150 break;
151 case FSIDSOURCE_FSID:
152 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
153 break;
154 case FSIDSOURCE_UUID:
155 f = ((u64*)fhp->fh_export->ex_uuid)[0];
156 f ^= ((u64*)fhp->fh_export->ex_uuid)[1];
157 p = xdr_encode_hyper(p, f);
158 break;
159 }
160 return p;
161}
162
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800163static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700164encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
David Shawa334de22006-01-06 00:19:58 -0800165 struct kstat *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
David Shawa334de22006-01-06 00:19:58 -0800167 *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
Albert Fluegel6e14b462013-11-18 12:18:01 -0500168 *p++ = htonl((u32) (stat->mode & S_IALLUGO));
David Shawa334de22006-01-06 00:19:58 -0800169 *p++ = htonl((u32) stat->nlink);
Eric W. Biederman458878a2013-02-02 04:16:08 -0800170 *p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid));
171 *p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid));
David Shawa334de22006-01-06 00:19:58 -0800172 if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
174 } else {
David Shawa334de22006-01-06 00:19:58 -0800175 p = xdr_encode_hyper(p, (u64) stat->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 }
David Shawa334de22006-01-06 00:19:58 -0800177 p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
178 *p++ = htonl((u32) MAJOR(stat->rdev));
179 *p++ = htonl((u32) MINOR(stat->rdev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800180 p = encode_fsid(p, fhp);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400181 p = xdr_encode_hyper(p, stat->ino);
David Shawa334de22006-01-06 00:19:58 -0800182 p = encode_time3(p, &stat->atime);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400183 p = encode_time3(p, &stat->mtime);
David Shawa334de22006-01-06 00:19:58 -0800184 p = encode_time3(p, &stat->ctime);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186 return p;
187}
188
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800189static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700190encode_saved_post_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 /* Attributes to follow */
193 *p++ = xdr_one;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400194 return encode_fattr3(rqstp, p, fhp, &fhp->fh_post_attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195}
196
197/*
198 * Encode post-operation attributes.
199 * The inode may be NULL if the call failed because of a stale file
200 * handle. In this case, no attributes are returned.
201 */
Al Viro91f07162006-10-19 23:28:57 -0700202static __be32 *
203encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
205 struct dentry *dentry = fhp->fh_dentry;
David Howells2b0143b2015-03-17 22:25:59 +0000206 if (dentry && d_really_is_positive(dentry)) {
Al Viro3dadecc2013-01-24 02:18:08 -0500207 __be32 err;
David Shawa334de22006-01-06 00:19:58 -0800208 struct kstat stat;
209
Al Viro3dadecc2013-01-24 02:18:08 -0500210 err = fh_getattr(fhp, &stat);
David Shawa334de22006-01-06 00:19:58 -0800211 if (!err) {
212 *p++ = xdr_one; /* attributes follow */
David Howells2b0143b2015-03-17 22:25:59 +0000213 lease_get_mtime(d_inode(dentry), &stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800214 return encode_fattr3(rqstp, p, fhp, &stat);
215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 }
217 *p++ = xdr_zero;
218 return p;
219}
220
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000221/* Helper for NFSv3 ACLs */
Al Viro91f07162006-10-19 23:28:57 -0700222__be32 *
223nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000224{
225 return encode_post_op_attr(rqstp, p, fhp);
226}
227
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228/*
229 * Enocde weak cache consistency data
230 */
Al Viro91f07162006-10-19 23:28:57 -0700231static __be32 *
232encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
234 struct dentry *dentry = fhp->fh_dentry;
235
David Howells2b0143b2015-03-17 22:25:59 +0000236 if (dentry && d_really_is_positive(dentry) && fhp->fh_post_saved) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 if (fhp->fh_pre_saved) {
238 *p++ = xdr_one;
239 p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
240 p = encode_time3(p, &fhp->fh_pre_mtime);
241 p = encode_time3(p, &fhp->fh_pre_ctime);
242 } else {
243 *p++ = xdr_zero;
244 }
245 return encode_saved_post_attr(rqstp, p, fhp);
246 }
247 /* no pre- or post-attrs */
248 *p++ = xdr_zero;
249 return encode_post_op_attr(rqstp, p, fhp);
250}
251
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400252/*
253 * Fill in the post_op attr for the wcc data
254 */
255void fill_post_wcc(struct svc_fh *fhp)
256{
Al Viro3dadecc2013-01-24 02:18:08 -0500257 __be32 err;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400258
259 if (fhp->fh_post_saved)
260 printk("nfsd: inode locked twice during operation.\n");
261
Al Viro3dadecc2013-01-24 02:18:08 -0500262 err = fh_getattr(fhp, &fhp->fh_post_attr);
David Howells2b0143b2015-03-17 22:25:59 +0000263 fhp->fh_post_change = d_inode(fhp->fh_dentry)->i_version;
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100264 if (err) {
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400265 fhp->fh_post_saved = false;
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100266 /* Grab the ctime anyway - set_change_info might use it */
David Howells2b0143b2015-03-17 22:25:59 +0000267 fhp->fh_post_attr.ctime = d_inode(fhp->fh_dentry)->i_ctime;
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100268 } else
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400269 fhp->fh_post_saved = true;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400270}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272/*
273 * XDR decode functions
274 */
275int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200276nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200278 struct nfsd_fhandle *args = rqstp->rq_argp;
279
Benoit Tained40aa332014-05-22 16:32:30 +0200280 p = decode_fh(p, &args->fh);
281 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 return 0;
283 return xdr_argsize_check(rqstp, p);
284}
285
286int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200287nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200289 struct nfsd3_sattrargs *args = rqstp->rq_argp;
290
Benoit Tained40aa332014-05-22 16:32:30 +0200291 p = decode_fh(p, &args->fh);
292 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700294 p = decode_sattr3(p, &args->attrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 if ((args->check_guard = ntohl(*p++)) != 0) {
297 struct timespec time;
298 p = decode_time3(p, &time);
299 args->guardtime = time.tv_sec;
300 }
301
302 return xdr_argsize_check(rqstp, p);
303}
304
305int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200306nfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200308 struct nfsd3_diropargs *args = rqstp->rq_argp;
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 if (!(p = decode_fh(p, &args->fh))
311 || !(p = decode_filename(p, &args->name, &args->len)))
312 return 0;
313
314 return xdr_argsize_check(rqstp, p);
315}
316
317int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200318nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200320 struct nfsd3_accessargs *args = rqstp->rq_argp;
321
Benoit Tained40aa332014-05-22 16:32:30 +0200322 p = decode_fh(p, &args->fh);
323 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 return 0;
325 args->access = ntohl(*p++);
326
327 return xdr_argsize_check(rqstp, p);
328}
329
330int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200331nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200333 struct nfsd3_readargs *args = rqstp->rq_argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 unsigned int len;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500335 int v;
Greg Banks7adae482006-10-04 02:15:47 -0700336 u32 max_blocksize = svc_max_payload(rqstp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Benoit Tained40aa332014-05-22 16:32:30 +0200338 p = decode_fh(p, &args->fh);
339 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700341 p = xdr_decode_hyper(p, &args->offset);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800342 args->count = ntohl(*p++);
J. Bruce Fields51f56772017-04-06 22:36:31 -0400343
344 if (!xdr_argsize_check(rqstp, p))
345 return 0;
346
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800347 len = min(args->count, max_blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 /* set up the kvec */
350 v=0;
351 while (len > 0) {
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500352 struct page *p = *(rqstp->rq_next_page++);
353
354 rqstp->rq_vec[v].iov_base = page_address(p);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800355 rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
NeilBrown3cc03b12006-10-04 02:15:47 -0700356 len -= rqstp->rq_vec[v].iov_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 v++;
358 }
359 args->vlen = v;
J. Bruce Fields51f56772017-04-06 22:36:31 -0400360 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
362
363int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200364nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200366 struct nfsd3_writeargs *args = rqstp->rq_argp;
Peter Staubachf34b9562007-05-09 02:34:48 -0700367 unsigned int len, v, hdr, dlen;
Greg Banks7adae482006-10-04 02:15:47 -0700368 u32 max_blocksize = svc_max_payload(rqstp);
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400369 struct kvec *head = rqstp->rq_arg.head;
370 struct kvec *tail = rqstp->rq_arg.tail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Benoit Tained40aa332014-05-22 16:32:30 +0200372 p = decode_fh(p, &args->fh);
373 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700375 p = xdr_decode_hyper(p, &args->offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 args->count = ntohl(*p++);
378 args->stable = ntohl(*p++);
379 len = args->len = ntohl(*p++);
J. Bruce Fields13bf9fb2017-04-21 15:26:30 -0400380 if ((void *)p > head->iov_base + head->iov_len)
381 return 0;
Peter Staubachf34b9562007-05-09 02:34:48 -0700382 /*
383 * The count must equal the amount of data passed.
384 */
385 if (args->count != args->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 return 0;
387
Peter Staubachf34b9562007-05-09 02:34:48 -0700388 /*
389 * Check to make sure that we got the right number of
390 * bytes.
Peter Staubachf34b9562007-05-09 02:34:48 -0700391 */
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400392 hdr = (void*)p - head->iov_base;
393 dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr;
Peter Staubachf34b9562007-05-09 02:34:48 -0700394 /*
395 * Round the length of the data which was specified up to
396 * the next multiple of XDR units and then compare that
397 * against the length which was actually received.
NeilBrownba67a392008-01-11 17:06:52 -0500398 * Note that when RPCSEC/GSS (for example) is used, the
399 * data buffer can be padded so dlen might be larger
400 * than required. It must never be smaller.
Peter Staubachf34b9562007-05-09 02:34:48 -0700401 */
NeilBrownba67a392008-01-11 17:06:52 -0500402 if (dlen < XDR_QUADLEN(len)*4)
Peter Staubachf34b9562007-05-09 02:34:48 -0700403 return 0;
404
405 if (args->count > max_blocksize) {
406 args->count = max_blocksize;
407 len = args->len = max_blocksize;
408 }
NeilBrown3cc03b12006-10-04 02:15:47 -0700409 rqstp->rq_vec[0].iov_base = (void*)p;
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400410 rqstp->rq_vec[0].iov_len = head->iov_len - hdr;
Peter Staubachf34b9562007-05-09 02:34:48 -0700411 v = 0;
NeilBrown3cc03b12006-10-04 02:15:47 -0700412 while (len > rqstp->rq_vec[v].iov_len) {
413 len -= rqstp->rq_vec[v].iov_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 v++;
NeilBrown3cc03b12006-10-04 02:15:47 -0700415 rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
416 rqstp->rq_vec[v].iov_len = PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 }
NeilBrown3cc03b12006-10-04 02:15:47 -0700418 rqstp->rq_vec[v].iov_len = len;
Peter Staubachf34b9562007-05-09 02:34:48 -0700419 args->vlen = v + 1;
420 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
423int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200424nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200426 struct nfsd3_createargs *args = rqstp->rq_argp;
427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 if (!(p = decode_fh(p, &args->fh))
429 || !(p = decode_filename(p, &args->name, &args->len)))
430 return 0;
431
432 switch (args->createmode = ntohl(*p++)) {
433 case NFS3_CREATE_UNCHECKED:
434 case NFS3_CREATE_GUARDED:
NeilBrown072f62e2007-05-09 02:34:57 -0700435 p = decode_sattr3(p, &args->attrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 break;
437 case NFS3_CREATE_EXCLUSIVE:
438 args->verf = p;
439 p += 2;
440 break;
441 default:
442 return 0;
443 }
444
445 return xdr_argsize_check(rqstp, p);
446}
Christoph Hellwig026fec72017-05-08 19:01:48 +0200447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200449nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200451 struct nfsd3_createargs *args = rqstp->rq_argp;
452
NeilBrown072f62e2007-05-09 02:34:57 -0700453 if (!(p = decode_fh(p, &args->fh)) ||
454 !(p = decode_filename(p, &args->name, &args->len)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700456 p = decode_sattr3(p, &args->attrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 return xdr_argsize_check(rqstp, p);
459}
460
461int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200462nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200464 struct nfsd3_symlinkargs *args = rqstp->rq_argp;
Chuck Levera628f662007-11-01 16:57:20 -0400465 unsigned int len, avail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 char *old, *new;
467 struct kvec *vec;
468
NeilBrown072f62e2007-05-09 02:34:57 -0700469 if (!(p = decode_fh(p, &args->ffh)) ||
470 !(p = decode_filename(p, &args->fname, &args->flen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 )
472 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700473 p = decode_sattr3(p, &args->attrs);
474
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 /* now decode the pathname, which might be larger than the first page.
476 * As we have to check for nul's anyway, we copy it into a new page
477 * This page appears in the rq_res.pages list, but as pages_len is always
478 * 0, it won't get in the way
479 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 len = ntohl(*p++);
481 if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
482 return 0;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500483 args->tname = new = page_address(*(rqstp->rq_next_page++));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 args->tlen = len;
485 /* first copy and check from the first page */
486 old = (char*)p;
487 vec = &rqstp->rq_arg.head[0];
J. Bruce Fields13bf9fb2017-04-21 15:26:30 -0400488 if ((void *)old > vec->iov_base + vec->iov_len)
489 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 avail = vec->iov_len - (old - (char*)vec->iov_base);
491 while (len && avail && *old) {
492 *new++ = *old++;
493 len--;
494 avail--;
495 }
496 /* now copy next page if there is one */
497 if (len && !avail && rqstp->rq_arg.page_len) {
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800498 avail = min_t(unsigned int, rqstp->rq_arg.page_len, PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 old = page_address(rqstp->rq_arg.pages[0]);
500 }
501 while (len && avail && *old) {
502 *new++ = *old++;
503 len--;
504 avail--;
505 }
506 *new = '\0';
507 if (len)
508 return 0;
509
510 return 1;
511}
512
513int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200514nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200516 struct nfsd3_mknodargs *args = rqstp->rq_argp;
517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 if (!(p = decode_fh(p, &args->fh))
519 || !(p = decode_filename(p, &args->name, &args->len)))
520 return 0;
521
522 args->ftype = ntohl(*p++);
523
524 if (args->ftype == NF3BLK || args->ftype == NF3CHR
NeilBrown072f62e2007-05-09 02:34:57 -0700525 || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
526 p = decode_sattr3(p, &args->attrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
528 if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
529 args->major = ntohl(*p++);
530 args->minor = ntohl(*p++);
531 }
532
533 return xdr_argsize_check(rqstp, p);
534}
535
536int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200537nfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200539 struct nfsd3_renameargs *args = rqstp->rq_argp;
540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 if (!(p = decode_fh(p, &args->ffh))
542 || !(p = decode_filename(p, &args->fname, &args->flen))
543 || !(p = decode_fh(p, &args->tfh))
544 || !(p = decode_filename(p, &args->tname, &args->tlen)))
545 return 0;
546
547 return xdr_argsize_check(rqstp, p);
548}
549
550int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200551nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200553 struct nfsd3_readlinkargs *args = rqstp->rq_argp;
554
Benoit Tained40aa332014-05-22 16:32:30 +0200555 p = decode_fh(p, &args->fh);
556 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 return 0;
J. Bruce Fields51f56772017-04-06 22:36:31 -0400558 if (!xdr_argsize_check(rqstp, p))
559 return 0;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500560 args->buffer = page_address(*(rqstp->rq_next_page++));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
J. Bruce Fields51f56772017-04-06 22:36:31 -0400562 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563}
564
565int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200566nfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200568 struct nfsd3_linkargs *args = rqstp->rq_argp;
569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 if (!(p = decode_fh(p, &args->ffh))
571 || !(p = decode_fh(p, &args->tfh))
572 || !(p = decode_filename(p, &args->tname, &args->tlen)))
573 return 0;
574
575 return xdr_argsize_check(rqstp, p);
576}
577
578int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200579nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200581 struct nfsd3_readdirargs *args = rqstp->rq_argp;
Benoit Tained40aa332014-05-22 16:32:30 +0200582 p = decode_fh(p, &args->fh);
583 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 return 0;
585 p = xdr_decode_hyper(p, &args->cookie);
586 args->verf = p; p += 2;
587 args->dircount = ~0;
588 args->count = ntohl(*p++);
J. Bruce Fields51f56772017-04-06 22:36:31 -0400589
590 if (!xdr_argsize_check(rqstp, p))
591 return 0;
592
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800593 args->count = min_t(u32, args->count, PAGE_SIZE);
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500594 args->buffer = page_address(*(rqstp->rq_next_page++));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
J. Bruce Fields51f56772017-04-06 22:36:31 -0400596 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597}
598
599int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200600nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200602 struct nfsd3_readdirargs *args = rqstp->rq_argp;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500603 int len;
Greg Banks7adae482006-10-04 02:15:47 -0700604 u32 max_blocksize = svc_max_payload(rqstp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Benoit Tained40aa332014-05-22 16:32:30 +0200606 p = decode_fh(p, &args->fh);
607 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return 0;
609 p = xdr_decode_hyper(p, &args->cookie);
610 args->verf = p; p += 2;
611 args->dircount = ntohl(*p++);
612 args->count = ntohl(*p++);
613
J. Bruce Fields51f56772017-04-06 22:36:31 -0400614 if (!xdr_argsize_check(rqstp, p))
615 return 0;
616
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800617 len = args->count = min(args->count, max_blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 while (len > 0) {
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500619 struct page *p = *(rqstp->rq_next_page++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 if (!args->buffer)
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500621 args->buffer = page_address(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 len -= PAGE_SIZE;
623 }
J. Bruce Fields51f56772017-04-06 22:36:31 -0400624 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
627int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200628nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200630 struct nfsd3_commitargs *args = rqstp->rq_argp;
Benoit Tained40aa332014-05-22 16:32:30 +0200631 p = decode_fh(p, &args->fh);
632 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return 0;
634 p = xdr_decode_hyper(p, &args->offset);
635 args->count = ntohl(*p++);
636
637 return xdr_argsize_check(rqstp, p);
638}
639
640/*
641 * XDR encode functions
642 */
643/*
644 * There must be an encoding function for void results so svc_process
645 * will work properly.
646 */
647int
Al Viro91f07162006-10-19 23:28:57 -0700648nfs3svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
650 return xdr_ressize_check(rqstp, p);
651}
652
653/* GETATTR */
654int
Al Viro91f07162006-10-19 23:28:57 -0700655nfs3svc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 struct nfsd3_attrstat *resp)
657{
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400658 if (resp->status == 0) {
David Howells2b0143b2015-03-17 22:25:59 +0000659 lease_get_mtime(d_inode(resp->fh.fh_dentry),
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400660 &resp->stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800661 p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400662 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 return xdr_ressize_check(rqstp, p);
664}
665
666/* SETATTR, REMOVE, RMDIR */
667int
Al Viro91f07162006-10-19 23:28:57 -0700668nfs3svc_encode_wccstat(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 struct nfsd3_attrstat *resp)
670{
671 p = encode_wcc_data(rqstp, p, &resp->fh);
672 return xdr_ressize_check(rqstp, p);
673}
674
675/* LOOKUP */
676int
Al Viro91f07162006-10-19 23:28:57 -0700677nfs3svc_encode_diropres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 struct nfsd3_diropres *resp)
679{
680 if (resp->status == 0) {
681 p = encode_fh(p, &resp->fh);
682 p = encode_post_op_attr(rqstp, p, &resp->fh);
683 }
684 p = encode_post_op_attr(rqstp, p, &resp->dirfh);
685 return xdr_ressize_check(rqstp, p);
686}
687
688/* ACCESS */
689int
Al Viro91f07162006-10-19 23:28:57 -0700690nfs3svc_encode_accessres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 struct nfsd3_accessres *resp)
692{
693 p = encode_post_op_attr(rqstp, p, &resp->fh);
694 if (resp->status == 0)
695 *p++ = htonl(resp->access);
696 return xdr_ressize_check(rqstp, p);
697}
698
699/* READLINK */
700int
Al Viro91f07162006-10-19 23:28:57 -0700701nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 struct nfsd3_readlinkres *resp)
703{
704 p = encode_post_op_attr(rqstp, p, &resp->fh);
705 if (resp->status == 0) {
706 *p++ = htonl(resp->len);
707 xdr_ressize_check(rqstp, p);
708 rqstp->rq_res.page_len = resp->len;
709 if (resp->len & 3) {
710 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 rqstp->rq_res.tail[0].iov_base = p;
712 *p = 0;
713 rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
714 }
715 return 1;
716 } else
717 return xdr_ressize_check(rqstp, p);
718}
719
720/* READ */
721int
Al Viro91f07162006-10-19 23:28:57 -0700722nfs3svc_encode_readres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 struct nfsd3_readres *resp)
724{
725 p = encode_post_op_attr(rqstp, p, &resp->fh);
726 if (resp->status == 0) {
727 *p++ = htonl(resp->count);
728 *p++ = htonl(resp->eof);
729 *p++ = htonl(resp->count); /* xdr opaque count */
730 xdr_ressize_check(rqstp, p);
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300731 /* now update rqstp->rq_res to reflect data as well */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 rqstp->rq_res.page_len = resp->count;
733 if (resp->count & 3) {
734 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 rqstp->rq_res.tail[0].iov_base = p;
736 *p = 0;
737 rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
738 }
739 return 1;
740 } else
741 return xdr_ressize_check(rqstp, p);
742}
743
744/* WRITE */
745int
Al Viro91f07162006-10-19 23:28:57 -0700746nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 struct nfsd3_writeres *resp)
748{
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +0300749 struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 p = encode_wcc_data(rqstp, p, &resp->fh);
752 if (resp->status == 0) {
753 *p++ = htonl(resp->count);
754 *p++ = htonl(resp->committed);
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +0300755 *p++ = htonl(nn->nfssvc_boot.tv_sec);
756 *p++ = htonl(nn->nfssvc_boot.tv_usec);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 }
758 return xdr_ressize_check(rqstp, p);
759}
760
761/* CREATE, MKDIR, SYMLINK, MKNOD */
762int
Al Viro91f07162006-10-19 23:28:57 -0700763nfs3svc_encode_createres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 struct nfsd3_diropres *resp)
765{
766 if (resp->status == 0) {
767 *p++ = xdr_one;
768 p = encode_fh(p, &resp->fh);
769 p = encode_post_op_attr(rqstp, p, &resp->fh);
770 }
771 p = encode_wcc_data(rqstp, p, &resp->dirfh);
772 return xdr_ressize_check(rqstp, p);
773}
774
775/* RENAME */
776int
Al Viro91f07162006-10-19 23:28:57 -0700777nfs3svc_encode_renameres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 struct nfsd3_renameres *resp)
779{
780 p = encode_wcc_data(rqstp, p, &resp->ffh);
781 p = encode_wcc_data(rqstp, p, &resp->tfh);
782 return xdr_ressize_check(rqstp, p);
783}
784
785/* LINK */
786int
Al Viro91f07162006-10-19 23:28:57 -0700787nfs3svc_encode_linkres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 struct nfsd3_linkres *resp)
789{
790 p = encode_post_op_attr(rqstp, p, &resp->fh);
791 p = encode_wcc_data(rqstp, p, &resp->tfh);
792 return xdr_ressize_check(rqstp, p);
793}
794
795/* READDIR */
796int
Al Viro91f07162006-10-19 23:28:57 -0700797nfs3svc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 struct nfsd3_readdirres *resp)
799{
800 p = encode_post_op_attr(rqstp, p, &resp->fh);
801
802 if (resp->status == 0) {
803 /* stupid readdir cookie */
804 memcpy(p, resp->verf, 8); p += 2;
805 xdr_ressize_check(rqstp, p);
806 if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
807 return 1; /*No room for trailer */
808 rqstp->rq_res.page_len = (resp->count) << 2;
809
810 /* add the 'tail' to the end of the 'head' page - page 0. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 rqstp->rq_res.tail[0].iov_base = p;
812 *p++ = 0; /* no more entries */
813 *p++ = htonl(resp->common.err == nfserr_eof);
814 rqstp->rq_res.tail[0].iov_len = 2<<2;
815 return 1;
816 } else
817 return xdr_ressize_check(rqstp, p);
818}
819
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800820static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700821encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400822 int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
824 *p++ = xdr_one; /* mark entry present */
825 p = xdr_encode_hyper(p, ino); /* file id */
826 p = xdr_encode_array(p, name, namlen);/* name length & name */
827
828 cd->offset = p; /* remember pointer */
829 p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
830
831 return p;
832}
833
Al Viroefe39652012-04-13 00:32:14 -0400834static __be32
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
NeilBrown43b0e7e2015-05-03 09:16:53 +1000836 const char *name, int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837{
838 struct svc_export *exp;
839 struct dentry *dparent, *dchild;
Al Viroefe39652012-04-13 00:32:14 -0400840 __be32 rv = nfserr_noent;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 dparent = cd->fh.fh_dentry;
843 exp = cd->fh.fh_export;
844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 if (isdotent(name, namlen)) {
846 if (namlen == 2) {
847 dchild = dget_parent(dparent);
Al Viroefe39652012-04-13 00:32:14 -0400848 /* filesystem root - cannot return filehandle for ".." */
849 if (dchild == dparent)
850 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 } else
852 dchild = dget(dparent);
853 } else
NeilBrownbbddca82016-01-07 16:08:20 -0500854 dchild = lookup_one_len_unlocked(name, dparent, namlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (IS_ERR(dchild))
Al Viroefe39652012-04-13 00:32:14 -0400856 return rv;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400857 if (d_mountpoint(dchild))
858 goto out;
David Howells2b0143b2015-03-17 22:25:59 +0000859 if (d_really_is_negative(dchild))
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400860 goto out;
NeilBrown43b0e7e2015-05-03 09:16:53 +1000861 if (dchild->d_inode->i_ino != ino)
862 goto out;
Al Viroefe39652012-04-13 00:32:14 -0400863 rv = fh_compose(fhp, exp, dchild, &cd->fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400864out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 dput(dchild);
866 return rv;
867}
868
NeilBrown43b0e7e2015-05-03 09:16:53 +1000869static __be32 *encode_entryplus_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name, int namlen, u64 ino)
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400870{
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500871 struct svc_fh *fh = &cd->scratch;
Al Viroefe39652012-04-13 00:32:14 -0400872 __be32 err;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400873
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500874 fh_init(fh, NFS3_FHSIZE);
NeilBrown43b0e7e2015-05-03 09:16:53 +1000875 err = compose_entry_fh(cd, fh, name, namlen, ino);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400876 if (err) {
877 *p++ = 0;
878 *p++ = 0;
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400879 goto out;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400880 }
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500881 p = encode_post_op_attr(cd->rqstp, p, fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400882 *p++ = xdr_one; /* yes, a file handle follows */
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500883 p = encode_fh(p, fh);
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400884out:
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500885 fh_put(fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400886 return p;
887}
888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889/*
890 * Encode a directory entry. This one works for both normal readdir
891 * and readdirplus.
892 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
893 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
894 *
895 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
896 * file handle.
897 */
898
899#define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
900#define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
901static int
NeilBrown598b9a52007-03-26 21:32:08 -0800902encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400903 loff_t offset, u64 ino, unsigned int d_type, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
905 struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
906 common);
Al Viro91f07162006-10-19 23:28:57 -0700907 __be32 *p = cd->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 caddr_t curr_page_addr = NULL;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500909 struct page ** page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 int slen; /* string (name) length */
911 int elen; /* estimated entry length in words */
912 int num_entry_words = 0; /* actual number of words */
913
914 if (cd->offset) {
915 u64 offset64 = offset;
916
917 if (unlikely(cd->offset1)) {
918 /* we ended up with offset on a page boundary */
919 *cd->offset = htonl(offset64 >> 32);
920 *cd->offset1 = htonl(offset64 & 0xffffffff);
921 cd->offset1 = NULL;
922 } else {
NeilBrown598b9a52007-03-26 21:32:08 -0800923 xdr_encode_hyper(cd->offset, offset64);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 }
925 }
926
927 /*
928 dprintk("encode_entry(%.*s @%ld%s)\n",
929 namlen, name, (long) offset, plus? " plus" : "");
930 */
931
932 /* truncate filename if too long */
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800933 namlen = min(namlen, NFS3_MAXNAMLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 slen = XDR_QUADLEN(namlen);
936 elen = slen + NFS3_ENTRY_BAGGAGE
937 + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
938
939 if (cd->buflen < elen) {
940 cd->common.err = nfserr_toosmall;
941 return -EINVAL;
942 }
943
944 /* determine which page in rq_respages[] we are currently filling */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500945 for (page = cd->rqstp->rq_respages + 1;
946 page < cd->rqstp->rq_next_page; page++) {
947 curr_page_addr = page_address(*page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 if (((caddr_t)cd->buffer >= curr_page_addr) &&
950 ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE))
951 break;
952 }
953
954 if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
955 /* encode entry in current page */
956
957 p = encode_entry_baggage(cd, p, name, namlen, ino);
958
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400959 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +1000960 p = encode_entryplus_baggage(cd, p, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 num_entry_words = p - cd->buffer;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500962 } else if (*(page+1) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 /* temporarily encode entry into next page, then move back to
964 * current and next page in rq_respages[] */
Al Viro91f07162006-10-19 23:28:57 -0700965 __be32 *p1, *tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 int len1, len2;
967
968 /* grab next page for temporary storage of entry */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500969 p1 = tmp = page_address(*(page+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
972
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400973 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +1000974 p1 = encode_entryplus_baggage(cd, p1, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 /* determine entry word length and lengths to go in pages */
977 num_entry_words = p1 - tmp;
978 len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
979 if ((num_entry_words << 2) < len1) {
980 /* the actual number of words in the entry is less
981 * than elen and can still fit in the current page
982 */
983 memmove(p, tmp, num_entry_words << 2);
984 p += num_entry_words;
985
986 /* update offset */
987 cd->offset = cd->buffer + (cd->offset - tmp);
988 } else {
989 unsigned int offset_r = (cd->offset - tmp) << 2;
990
991 /* update pointer to offset location.
992 * This is a 64bit quantity, so we need to
993 * deal with 3 cases:
994 * - entirely in first page
995 * - entirely in second page
996 * - 4 bytes in each page
997 */
998 if (offset_r + 8 <= len1) {
999 cd->offset = p + (cd->offset - tmp);
1000 } else if (offset_r >= len1) {
1001 cd->offset -= len1 >> 2;
1002 } else {
1003 /* sitting on the fence */
1004 BUG_ON(offset_r != len1 - 4);
1005 cd->offset = p + (cd->offset - tmp);
1006 cd->offset1 = tmp;
1007 }
1008
1009 len2 = (num_entry_words << 2) - len1;
1010
1011 /* move from temp page to current and next pages */
1012 memmove(p, tmp, len1);
1013 memmove(tmp, (caddr_t)tmp+len1, len2);
1014
1015 p = tmp + (len2 >> 2);
1016 }
1017 }
1018 else {
1019 cd->common.err = nfserr_toosmall;
1020 return -EINVAL;
1021 }
1022
1023 cd->buflen -= num_entry_words;
1024 cd->buffer = p;
1025 cd->common.err = nfs_ok;
1026 return 0;
1027
1028}
1029
1030int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001031nfs3svc_encode_entry(void *cd, const char *name,
1032 int namlen, loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
1034 return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
1035}
1036
1037int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001038nfs3svc_encode_entry_plus(void *cd, const char *name,
1039 int namlen, loff_t offset, u64 ino,
1040 unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041{
1042 return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
1043}
1044
1045/* FSSTAT */
1046int
Al Viro91f07162006-10-19 23:28:57 -07001047nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 struct nfsd3_fsstatres *resp)
1049{
1050 struct kstatfs *s = &resp->stats;
1051 u64 bs = s->f_bsize;
1052
1053 *p++ = xdr_zero; /* no post_op_attr */
1054
1055 if (resp->status == 0) {
1056 p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
1057 p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
1058 p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
1059 p = xdr_encode_hyper(p, s->f_files); /* total inodes */
1060 p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
1061 p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
1062 *p++ = htonl(resp->invarsec); /* mean unchanged time */
1063 }
1064 return xdr_ressize_check(rqstp, p);
1065}
1066
1067/* FSINFO */
1068int
Al Viro91f07162006-10-19 23:28:57 -07001069nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 struct nfsd3_fsinfores *resp)
1071{
1072 *p++ = xdr_zero; /* no post_op_attr */
1073
1074 if (resp->status == 0) {
1075 *p++ = htonl(resp->f_rtmax);
1076 *p++ = htonl(resp->f_rtpref);
1077 *p++ = htonl(resp->f_rtmult);
1078 *p++ = htonl(resp->f_wtmax);
1079 *p++ = htonl(resp->f_wtpref);
1080 *p++ = htonl(resp->f_wtmult);
1081 *p++ = htonl(resp->f_dtpref);
1082 p = xdr_encode_hyper(p, resp->f_maxfilesize);
1083 *p++ = xdr_one;
1084 *p++ = xdr_zero;
1085 *p++ = htonl(resp->f_properties);
1086 }
1087
1088 return xdr_ressize_check(rqstp, p);
1089}
1090
1091/* PATHCONF */
1092int
Al Viro91f07162006-10-19 23:28:57 -07001093nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 struct nfsd3_pathconfres *resp)
1095{
1096 *p++ = xdr_zero; /* no post_op_attr */
1097
1098 if (resp->status == 0) {
1099 *p++ = htonl(resp->p_link_max);
1100 *p++ = htonl(resp->p_name_max);
1101 *p++ = htonl(resp->p_no_trunc);
1102 *p++ = htonl(resp->p_chown_restricted);
1103 *p++ = htonl(resp->p_case_insensitive);
1104 *p++ = htonl(resp->p_case_preserving);
1105 }
1106
1107 return xdr_ressize_check(rqstp, p);
1108}
1109
1110/* COMMIT */
1111int
Al Viro91f07162006-10-19 23:28:57 -07001112nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 struct nfsd3_commitres *resp)
1114{
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +03001115 struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
1116
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 p = encode_wcc_data(rqstp, p, &resp->fh);
1118 /* Write verifier */
1119 if (resp->status == 0) {
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +03001120 *p++ = htonl(nn->nfssvc_boot.tv_sec);
1121 *p++ = htonl(nn->nfssvc_boot.tv_usec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 }
1123 return xdr_ressize_check(rqstp, p);
1124}
1125
1126/*
1127 * XDR release functions
1128 */
Christoph Hellwig85374882017-05-08 18:48:24 +02001129void
1130nfs3svc_release_fhandle(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131{
Christoph Hellwig85374882017-05-08 18:48:24 +02001132 struct nfsd3_attrstat *resp = rqstp->rq_resp;
1133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 fh_put(&resp->fh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135}
1136
Christoph Hellwig85374882017-05-08 18:48:24 +02001137void
1138nfs3svc_release_fhandle2(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139{
Christoph Hellwig85374882017-05-08 18:48:24 +02001140 struct nfsd3_fhandle_pair *resp = rqstp->rq_resp;
1141
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 fh_put(&resp->fh1);
1143 fh_put(&resp->fh2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144}