blob: f870a068aad8585feccaf9a0e0e875b975447a01 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * XDR support for nfsd/protocol version 3.
4 *
5 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
6 *
7 * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
8 */
9
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/namei.h>
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +030011#include <linux/sunrpc/svc_xprt.h>
Boaz Harrosh9a74af22009-12-03 20:30:56 +020012#include "xdr3.h"
J. Bruce Fields2e8138a2007-11-15 17:05:43 -050013#include "auth.h"
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +030014#include "netns.h"
Al Viro3dadecc2013-01-24 02:18:08 -050015#include "vfs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17#define NFSDDBG_FACILITY NFSDDBG_XDR
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20/*
21 * Mapping of S_IF* types to NFS file types
22 */
23static u32 nfs3_ftypes[] = {
24 NF3NON, NF3FIFO, NF3CHR, NF3BAD,
25 NF3DIR, NF3BAD, NF3BLK, NF3BAD,
26 NF3REG, NF3BAD, NF3LNK, NF3BAD,
27 NF3SOCK, NF3BAD, NF3LNK, NF3BAD,
28};
29
Trond Myklebust27c438f2019-09-02 13:02:56 -040030
Linus Torvalds1da177e2005-04-16 15:20:36 -070031/*
Chuck Lever95753632020-10-20 14:30:02 -040032 * Basic NFSv3 data types (RFC 1813 Sections 2.5 and 2.6)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 */
Chuck Lever95753632020-10-20 14:30:02 -040034
Adrian Bunk3ee6f612006-12-06 20:40:23 -080035static __be32 *
Arnd Bergmann92c5e462019-10-31 14:55:32 +010036encode_time3(__be32 *p, struct timespec64 *time)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037{
38 *p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
39 return p;
40}
41
Adrian Bunk3ee6f612006-12-06 20:40:23 -080042static __be32 *
Arnd Bergmann92c5e462019-10-31 14:55:32 +010043decode_time3(__be32 *p, struct timespec64 *time)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
45 time->tv_sec = ntohl(*p++);
46 time->tv_nsec = ntohl(*p++);
47 return p;
48}
49
Chuck Lever95753632020-10-20 14:30:02 -040050static bool
51svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp)
52{
53 __be32 *p;
54 u32 size;
55
56 if (xdr_stream_decode_u32(xdr, &size) < 0)
57 return false;
58 if (size == 0 || size > NFS3_FHSIZE)
59 return false;
60 p = xdr_inline_decode(xdr, size);
61 if (!p)
62 return false;
63 fh_init(fhp, NFS3_FHSIZE);
64 fhp->fh_handle.fh_size = size;
65 memcpy(&fhp->fh_handle.fh_base, p, size);
66
67 return true;
68}
69
Adrian Bunk3ee6f612006-12-06 20:40:23 -080070static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070071decode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070072{
73 unsigned int size;
74 fh_init(fhp, NFS3_FHSIZE);
75 size = ntohl(*p++);
76 if (size > NFS3_FHSIZE)
77 return NULL;
78
79 memcpy(&fhp->fh_handle.fh_base, p, size);
80 fhp->fh_handle.fh_size = size;
81 return p + XDR_QUADLEN(size);
82}
83
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +000084/* Helper function for NFSv3 ACL code */
Al Viro91f07162006-10-19 23:28:57 -070085__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp)
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +000086{
87 return decode_fh(p, fhp);
88}
89
Adrian Bunk3ee6f612006-12-06 20:40:23 -080090static __be32 *
Al Viro91f07162006-10-19 23:28:57 -070091encode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
93 unsigned int size = fhp->fh_handle.fh_size;
94 *p++ = htonl(size);
95 if (size) p[XDR_QUADLEN(size)-1]=0;
96 memcpy(p, &fhp->fh_handle.fh_base, size);
97 return p + XDR_QUADLEN(size);
98}
99
100/*
101 * Decode a file name and make sure that the path contains
102 * no slashes or null bytes.
103 */
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800104static __be32 *
Chuck Leveree1a95b2007-11-01 16:56:58 -0400105decode_filename(__be32 *p, char **namp, unsigned int *lenp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106{
107 char *name;
Chuck Leveree1a95b2007-11-01 16:56:58 -0400108 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
110 if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
111 for (i = 0, name = *namp; i < *lenp; i++, name++) {
112 if (*name == '\0' || *name == '/')
113 return NULL;
114 }
115 }
116
117 return p;
118}
119
Chuck Lever54d1d432020-10-20 15:42:33 -0400120static bool
121svcxdr_decode_filename3(struct xdr_stream *xdr, char **name, unsigned int *len)
122{
123 u32 size, i;
124 __be32 *p;
125 char *c;
126
127 if (xdr_stream_decode_u32(xdr, &size) < 0)
128 return false;
129 if (size == 0 || size > NFS3_MAXNAMLEN)
130 return false;
131 p = xdr_inline_decode(xdr, size);
132 if (!p)
133 return false;
134
135 *len = size;
136 *name = (char *)p;
137 for (i = 0, c = *name; i < size; i++, c++) {
138 if (*c == '\0' || *c == '/')
139 return false;
140 }
141
142 return true;
143}
144
145static bool
146svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp,
147 char **name, unsigned int *len)
148{
149 return svcxdr_decode_nfs_fh3(xdr, fhp) &&
150 svcxdr_decode_filename3(xdr, name, len);
151}
152
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800153static __be32 *
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400154decode_sattr3(__be32 *p, struct iattr *iap, struct user_namespace *userns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155{
156 u32 tmp;
157
158 iap->ia_valid = 0;
159
160 if (*p++) {
161 iap->ia_valid |= ATTR_MODE;
162 iap->ia_mode = ntohl(*p++);
163 }
164 if (*p++) {
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400165 iap->ia_uid = make_kuid(userns, ntohl(*p++));
Eric W. Biederman458878a2013-02-02 04:16:08 -0800166 if (uid_valid(iap->ia_uid))
167 iap->ia_valid |= ATTR_UID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 }
169 if (*p++) {
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400170 iap->ia_gid = make_kgid(userns, ntohl(*p++));
Eric W. Biederman458878a2013-02-02 04:16:08 -0800171 if (gid_valid(iap->ia_gid))
172 iap->ia_valid |= ATTR_GID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
174 if (*p++) {
175 u64 newsize;
176
177 iap->ia_valid |= ATTR_SIZE;
178 p = xdr_decode_hyper(p, &newsize);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800179 iap->ia_size = min_t(u64, newsize, NFS_OFFSET_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 }
181 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
182 iap->ia_valid |= ATTR_ATIME;
183 } else if (tmp == 2) { /* set to client time */
184 iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
185 iap->ia_atime.tv_sec = ntohl(*p++);
186 iap->ia_atime.tv_nsec = ntohl(*p++);
187 }
188 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
189 iap->ia_valid |= ATTR_MTIME;
190 } else if (tmp == 2) { /* set to client time */
191 iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
192 iap->ia_mtime.tv_sec = ntohl(*p++);
193 iap->ia_mtime.tv_nsec = ntohl(*p++);
194 }
195 return p;
196}
197
NeilBrownaf6a4e22007-02-14 00:33:12 -0800198static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
199{
200 u64 f;
201 switch(fsid_source(fhp)) {
202 default:
203 case FSIDSOURCE_DEV:
204 p = xdr_encode_hyper(p, (u64)huge_encode_dev
Al Virofc640052016-04-10 01:33:30 -0400205 (fhp->fh_dentry->d_sb->s_dev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800206 break;
207 case FSIDSOURCE_FSID:
208 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
209 break;
210 case FSIDSOURCE_UUID:
211 f = ((u64*)fhp->fh_export->ex_uuid)[0];
212 f ^= ((u64*)fhp->fh_export->ex_uuid)[1];
213 p = xdr_encode_hyper(p, f);
214 break;
215 }
216 return p;
217}
218
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800219static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700220encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
David Shawa334de22006-01-06 00:19:58 -0800221 struct kstat *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400223 struct user_namespace *userns = nfsd_user_namespace(rqstp);
David Shawa334de22006-01-06 00:19:58 -0800224 *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
Albert Fluegel6e14b462013-11-18 12:18:01 -0500225 *p++ = htonl((u32) (stat->mode & S_IALLUGO));
David Shawa334de22006-01-06 00:19:58 -0800226 *p++ = htonl((u32) stat->nlink);
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400227 *p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
228 *p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
David Shawa334de22006-01-06 00:19:58 -0800229 if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
231 } else {
David Shawa334de22006-01-06 00:19:58 -0800232 p = xdr_encode_hyper(p, (u64) stat->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 }
David Shawa334de22006-01-06 00:19:58 -0800234 p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
235 *p++ = htonl((u32) MAJOR(stat->rdev));
236 *p++ = htonl((u32) MINOR(stat->rdev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800237 p = encode_fsid(p, fhp);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400238 p = xdr_encode_hyper(p, stat->ino);
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100239 p = encode_time3(p, &stat->atime);
240 p = encode_time3(p, &stat->mtime);
241 p = encode_time3(p, &stat->ctime);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
243 return p;
244}
245
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800246static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700247encode_saved_post_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 /* Attributes to follow */
250 *p++ = xdr_one;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400251 return encode_fattr3(rqstp, p, fhp, &fhp->fh_post_attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252}
253
254/*
255 * Encode post-operation attributes.
256 * The inode may be NULL if the call failed because of a stale file
257 * handle. In this case, no attributes are returned.
258 */
Al Viro91f07162006-10-19 23:28:57 -0700259static __be32 *
260encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261{
262 struct dentry *dentry = fhp->fh_dentry;
Jeff Laytondaab1102020-11-30 17:03:14 -0500263 if (!fhp->fh_no_wcc && dentry && d_really_is_positive(dentry)) {
Al Viro3dadecc2013-01-24 02:18:08 -0500264 __be32 err;
David Shawa334de22006-01-06 00:19:58 -0800265 struct kstat stat;
266
Al Viro3dadecc2013-01-24 02:18:08 -0500267 err = fh_getattr(fhp, &stat);
David Shawa334de22006-01-06 00:19:58 -0800268 if (!err) {
269 *p++ = xdr_one; /* attributes follow */
David Howells2b0143b2015-03-17 22:25:59 +0000270 lease_get_mtime(d_inode(dentry), &stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800271 return encode_fattr3(rqstp, p, fhp, &stat);
272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 }
274 *p++ = xdr_zero;
275 return p;
276}
277
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000278/* Helper for NFSv3 ACLs */
Al Viro91f07162006-10-19 23:28:57 -0700279__be32 *
280nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000281{
282 return encode_post_op_attr(rqstp, p, fhp);
283}
284
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285/*
286 * Enocde weak cache consistency data
287 */
Al Viro91f07162006-10-19 23:28:57 -0700288static __be32 *
289encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
291 struct dentry *dentry = fhp->fh_dentry;
292
David Howells2b0143b2015-03-17 22:25:59 +0000293 if (dentry && d_really_is_positive(dentry) && fhp->fh_post_saved) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 if (fhp->fh_pre_saved) {
295 *p++ = xdr_one;
296 p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
297 p = encode_time3(p, &fhp->fh_pre_mtime);
298 p = encode_time3(p, &fhp->fh_pre_ctime);
299 } else {
300 *p++ = xdr_zero;
301 }
302 return encode_saved_post_attr(rqstp, p, fhp);
303 }
304 /* no pre- or post-attrs */
305 *p++ = xdr_zero;
306 return encode_post_op_attr(rqstp, p, fhp);
307}
308
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400309/*
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200310 * Fill in the pre_op attr for the wcc data
311 */
312void fill_pre_wcc(struct svc_fh *fhp)
313{
314 struct inode *inode;
315 struct kstat stat;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500316 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200317 __be32 err;
318
Jeff Laytondaab1102020-11-30 17:03:14 -0500319 if (fhp->fh_no_wcc || fhp->fh_pre_saved)
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200320 return;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200321 inode = d_inode(fhp->fh_dentry);
322 err = fh_getattr(fhp, &stat);
323 if (err) {
324 /* Grab the times from inode anyway */
325 stat.mtime = inode->i_mtime;
326 stat.ctime = inode->i_ctime;
327 stat.size = inode->i_size;
328 }
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500329 if (v4)
330 fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200331
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100332 fhp->fh_pre_mtime = stat.mtime;
333 fhp->fh_pre_ctime = stat.ctime;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200334 fhp->fh_pre_size = stat.size;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200335 fhp->fh_pre_saved = true;
336}
337
338/*
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400339 * Fill in the post_op attr for the wcc data
340 */
341void fill_post_wcc(struct svc_fh *fhp)
342{
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500343 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
344 struct inode *inode = d_inode(fhp->fh_dentry);
Al Viro3dadecc2013-01-24 02:18:08 -0500345 __be32 err;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400346
Jeff Laytondaab1102020-11-30 17:03:14 -0500347 if (fhp->fh_no_wcc)
348 return;
349
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400350 if (fhp->fh_post_saved)
351 printk("nfsd: inode locked twice during operation.\n");
352
Al Viro3dadecc2013-01-24 02:18:08 -0500353 err = fh_getattr(fhp, &fhp->fh_post_attr);
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100354 if (err) {
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400355 fhp->fh_post_saved = false;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500356 fhp->fh_post_attr.ctime = inode->i_ctime;
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100357 } else
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400358 fhp->fh_post_saved = true;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500359 if (v4)
360 fhp->fh_post_change =
361 nfsd4_change_attribute(&fhp->fh_post_attr, inode);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400362}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
364/*
365 * XDR decode functions
366 */
Chuck Leverdcc46992020-10-01 18:59:12 -0400367
368int
Chuck Lever95753632020-10-20 14:30:02 -0400369nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370{
Chuck Lever95753632020-10-20 14:30:02 -0400371 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200372 struct nfsd_fhandle *args = rqstp->rq_argp;
373
Chuck Lever95753632020-10-20 14:30:02 -0400374 return svcxdr_decode_nfs_fh3(xdr, &args->fh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
377int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200378nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200380 struct nfsd3_sattrargs *args = rqstp->rq_argp;
381
Benoit Tained40aa332014-05-22 16:32:30 +0200382 p = decode_fh(p, &args->fh);
383 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400385 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
387 if ((args->check_guard = ntohl(*p++)) != 0) {
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100388 struct timespec64 time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 p = decode_time3(p, &time);
390 args->guardtime = time.tv_sec;
391 }
392
393 return xdr_argsize_check(rqstp, p);
394}
395
396int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200397nfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
Chuck Lever54d1d432020-10-20 15:42:33 -0400399 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200400 struct nfsd3_diropargs *args = rqstp->rq_argp;
401
Chuck Lever54d1d432020-10-20 15:42:33 -0400402 return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403}
404
405int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200406nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407{
Chuck Lever3b921a22020-10-20 14:32:04 -0400408 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200409 struct nfsd3_accessargs *args = rqstp->rq_argp;
410
Chuck Lever3b921a22020-10-20 14:32:04 -0400411 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 return 0;
Chuck Lever3b921a22020-10-20 14:32:04 -0400413 if (xdr_stream_decode_u32(xdr, &args->access) < 0)
414 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Chuck Lever3b921a22020-10-20 14:32:04 -0400416 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
419int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200420nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
Chuck Leverbe63bd22020-10-20 14:34:40 -0400422 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200423 struct nfsd3_readargs *args = rqstp->rq_argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
Chuck Leverbe63bd22020-10-20 14:34:40 -0400425 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 return 0;
Chuck Leverbe63bd22020-10-20 14:34:40 -0400427 if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
428 return 0;
429 if (xdr_stream_decode_u32(xdr, &args->count) < 0)
430 return 0;
J. Bruce Fields9512a162017-05-16 15:57:42 -0400431
Chuck Leverbe63bd22020-10-20 14:34:40 -0400432 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
435int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200436nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
Chuck Leverc43b2f22020-10-22 11:14:55 -0400438 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200439 struct nfsd3_writeargs *args = rqstp->rq_argp;
Greg Banks7adae482006-10-04 02:15:47 -0700440 u32 max_blocksize = svc_max_payload(rqstp);
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400441 struct kvec *head = rqstp->rq_arg.head;
442 struct kvec *tail = rqstp->rq_arg.tail;
Chuck Leverc43b2f22020-10-22 11:14:55 -0400443 size_t remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Chuck Leverc43b2f22020-10-22 11:14:55 -0400445 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 return 0;
Chuck Leverc43b2f22020-10-22 11:14:55 -0400447 if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
448 return 0;
449 if (xdr_stream_decode_u32(xdr, &args->count) < 0)
450 return 0;
451 if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
452 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Chuck Leverc43b2f22020-10-22 11:14:55 -0400454 /* opaque data */
455 if (xdr_stream_decode_u32(xdr, &args->len) < 0)
J. Bruce Fields13bf9fb2017-04-21 15:26:30 -0400456 return 0;
Chuck Leverc43b2f22020-10-22 11:14:55 -0400457
458 /* request sanity */
Peter Staubachf34b95682007-05-09 02:34:48 -0700459 if (args->count != args->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 return 0;
Chuck Leverc43b2f22020-10-22 11:14:55 -0400461 remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
462 remaining -= xdr_stream_pos(xdr);
463 if (remaining < xdr_align_size(args->len))
Peter Staubachf34b95682007-05-09 02:34:48 -0700464 return 0;
Peter Staubachf34b95682007-05-09 02:34:48 -0700465 if (args->count > max_blocksize) {
466 args->count = max_blocksize;
Chuck Leverc43b2f22020-10-22 11:14:55 -0400467 args->len = max_blocksize;
Peter Staubachf34b95682007-05-09 02:34:48 -0700468 }
Chuck Lever8154ef22018-03-27 10:54:07 -0400469
Chuck Leverc43b2f22020-10-22 11:14:55 -0400470 args->first.iov_base = xdr->p;
471 args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
472
Peter Staubachf34b95682007-05-09 02:34:48 -0700473 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474}
475
476int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200477nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200479 struct nfsd3_createargs *args = rqstp->rq_argp;
480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 if (!(p = decode_fh(p, &args->fh))
482 || !(p = decode_filename(p, &args->name, &args->len)))
483 return 0;
484
485 switch (args->createmode = ntohl(*p++)) {
486 case NFS3_CREATE_UNCHECKED:
487 case NFS3_CREATE_GUARDED:
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400488 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 break;
490 case NFS3_CREATE_EXCLUSIVE:
491 args->verf = p;
492 p += 2;
493 break;
494 default:
495 return 0;
496 }
497
498 return xdr_argsize_check(rqstp, p);
499}
Christoph Hellwig026fec72017-05-08 19:01:48 +0200500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200502nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200504 struct nfsd3_createargs *args = rqstp->rq_argp;
505
NeilBrown072f62e2007-05-09 02:34:57 -0700506 if (!(p = decode_fh(p, &args->fh)) ||
507 !(p = decode_filename(p, &args->name, &args->len)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400509 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 return xdr_argsize_check(rqstp, p);
512}
513
514int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200515nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200517 struct nfsd3_symlinkargs *args = rqstp->rq_argp;
Chuck Lever38a70312018-03-27 10:54:21 -0400518 char *base = (char *)p;
519 size_t dlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
NeilBrown072f62e2007-05-09 02:34:57 -0700521 if (!(p = decode_fh(p, &args->ffh)) ||
Chuck Lever38a70312018-03-27 10:54:21 -0400522 !(p = decode_filename(p, &args->fname, &args->flen)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400524 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
NeilBrown072f62e2007-05-09 02:34:57 -0700525
Chuck Lever38a70312018-03-27 10:54:21 -0400526 args->tlen = ntohl(*p++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Chuck Lever38a70312018-03-27 10:54:21 -0400528 args->first.iov_base = p;
529 args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
530 args->first.iov_len -= (char *)p - base;
531
532 dlen = args->first.iov_len + rqstp->rq_arg.page_len +
533 rqstp->rq_arg.tail[0].iov_len;
534 if (dlen < XDR_QUADLEN(args->tlen) << 2)
535 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 return 1;
537}
538
539int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200540nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200542 struct nfsd3_mknodargs *args = rqstp->rq_argp;
543
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if (!(p = decode_fh(p, &args->fh))
545 || !(p = decode_filename(p, &args->name, &args->len)))
546 return 0;
547
548 args->ftype = ntohl(*p++);
549
550 if (args->ftype == NF3BLK || args->ftype == NF3CHR
NeilBrown072f62e2007-05-09 02:34:57 -0700551 || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400552 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
554 if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
555 args->major = ntohl(*p++);
556 args->minor = ntohl(*p++);
557 }
558
559 return xdr_argsize_check(rqstp, p);
560}
561
562int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200563nfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Chuck Leverd181e0a2020-10-20 15:44:12 -0400565 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200566 struct nfsd3_renameargs *args = rqstp->rq_argp;
567
Chuck Leverd181e0a2020-10-20 15:44:12 -0400568 return svcxdr_decode_diropargs3(xdr, &args->ffh,
569 &args->fname, &args->flen) &&
570 svcxdr_decode_diropargs3(xdr, &args->tfh,
571 &args->tname, &args->tlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
574int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200575nfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200577 struct nfsd3_linkargs *args = rqstp->rq_argp;
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 if (!(p = decode_fh(p, &args->ffh))
580 || !(p = decode_fh(p, &args->tfh))
581 || !(p = decode_filename(p, &args->tname, &args->tlen)))
582 return 0;
583
584 return xdr_argsize_check(rqstp, p);
585}
586
587int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200588nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400590 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200591 struct nfsd3_readdirargs *args = rqstp->rq_argp;
NeilBrownf875a792019-03-07 09:49:46 +1100592
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400593 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 return 0;
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400595 if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
596 return 0;
597 args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
598 if (!args->verf)
599 return 0;
600 if (xdr_stream_decode_u32(xdr, &args->count) < 0)
601 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400603 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604}
605
606int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200607nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608{
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400609 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200610 struct nfsd3_readdirargs *args = rqstp->rq_argp;
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400611 u32 dircount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400613 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 return 0;
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400615 if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
616 return 0;
617 args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
618 if (!args->verf)
619 return 0;
620 /* dircount is ignored */
621 if (xdr_stream_decode_u32(xdr, &dircount) < 0)
622 return 0;
623 if (xdr_stream_decode_u32(xdr, &args->count) < 0)
624 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Chuck Lever9cedc2e2020-10-19 13:23:52 -0400626 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627}
628
629int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200630nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
Chuck Leverc8d26a02020-10-20 14:41:56 -0400632 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200633 struct nfsd3_commitargs *args = rqstp->rq_argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
Chuck Leverc8d26a02020-10-20 14:41:56 -0400635 if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
636 return 0;
637 if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
638 return 0;
639 if (xdr_stream_decode_u32(xdr, &args->count) < 0)
640 return 0;
641
642 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
645/*
646 * XDR encode functions
647 */
Chuck Levercc028a12020-10-02 15:52:44 -0400648
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649/* GETATTR */
650int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200651nfs3svc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200653 struct nfsd3_attrstat *resp = rqstp->rq_resp;
654
Chuck Levercc028a12020-10-02 15:52:44 -0400655 *p++ = resp->status;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400656 if (resp->status == 0) {
David Howells2b0143b2015-03-17 22:25:59 +0000657 lease_get_mtime(d_inode(resp->fh.fh_dentry),
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400658 &resp->stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800659 p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400660 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 return xdr_ressize_check(rqstp, p);
662}
663
664/* SETATTR, REMOVE, RMDIR */
665int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200666nfs3svc_encode_wccstat(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200668 struct nfsd3_attrstat *resp = rqstp->rq_resp;
669
Chuck Levercc028a12020-10-02 15:52:44 -0400670 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 p = encode_wcc_data(rqstp, p, &resp->fh);
672 return xdr_ressize_check(rqstp, p);
673}
674
675/* LOOKUP */
676int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200677nfs3svc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200679 struct nfsd3_diropres *resp = rqstp->rq_resp;
680
Chuck Levercc028a12020-10-02 15:52:44 -0400681 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 if (resp->status == 0) {
683 p = encode_fh(p, &resp->fh);
684 p = encode_post_op_attr(rqstp, p, &resp->fh);
685 }
686 p = encode_post_op_attr(rqstp, p, &resp->dirfh);
687 return xdr_ressize_check(rqstp, p);
688}
689
690/* ACCESS */
691int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200692nfs3svc_encode_accessres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200694 struct nfsd3_accessres *resp = rqstp->rq_resp;
695
Chuck Levercc028a12020-10-02 15:52:44 -0400696 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 p = encode_post_op_attr(rqstp, p, &resp->fh);
698 if (resp->status == 0)
699 *p++ = htonl(resp->access);
700 return xdr_ressize_check(rqstp, p);
701}
702
703/* READLINK */
704int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200705nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200707 struct nfsd3_readlinkres *resp = rqstp->rq_resp;
Chuck Lever76e54922020-11-05 10:24:19 -0500708 struct kvec *head = rqstp->rq_res.head;
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200709
Chuck Levercc028a12020-10-02 15:52:44 -0400710 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 p = encode_post_op_attr(rqstp, p, &resp->fh);
712 if (resp->status == 0) {
713 *p++ = htonl(resp->len);
714 xdr_ressize_check(rqstp, p);
715 rqstp->rq_res.page_len = resp->len;
716 if (resp->len & 3) {
717 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 rqstp->rq_res.tail[0].iov_base = p;
719 *p = 0;
720 rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
721 }
Chuck Lever76e54922020-11-05 10:24:19 -0500722 if (svc_encode_result_payload(rqstp, head->iov_len, resp->len))
723 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return 1;
725 } else
726 return xdr_ressize_check(rqstp, p);
727}
728
729/* READ */
730int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200731nfs3svc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200733 struct nfsd3_readres *resp = rqstp->rq_resp;
Chuck Lever76e54922020-11-05 10:24:19 -0500734 struct kvec *head = rqstp->rq_res.head;
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200735
Chuck Levercc028a12020-10-02 15:52:44 -0400736 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 p = encode_post_op_attr(rqstp, p, &resp->fh);
738 if (resp->status == 0) {
739 *p++ = htonl(resp->count);
740 *p++ = htonl(resp->eof);
741 *p++ = htonl(resp->count); /* xdr opaque count */
742 xdr_ressize_check(rqstp, p);
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300743 /* now update rqstp->rq_res to reflect data as well */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 rqstp->rq_res.page_len = resp->count;
745 if (resp->count & 3) {
746 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 rqstp->rq_res.tail[0].iov_base = p;
748 *p = 0;
749 rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
750 }
Chuck Lever76e54922020-11-05 10:24:19 -0500751 if (svc_encode_result_payload(rqstp, head->iov_len,
752 resp->count))
753 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 return 1;
755 } else
756 return xdr_ressize_check(rqstp, p);
757}
758
759/* WRITE */
760int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200761nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200763 struct nfsd3_writeres *resp = rqstp->rq_resp;
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +0300764
Chuck Levercc028a12020-10-02 15:52:44 -0400765 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 p = encode_wcc_data(rqstp, p, &resp->fh);
767 if (resp->status == 0) {
768 *p++ = htonl(resp->count);
769 *p++ = htonl(resp->committed);
Trond Myklebust19e06632020-01-06 13:40:37 -0500770 *p++ = resp->verf[0];
771 *p++ = resp->verf[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 }
773 return xdr_ressize_check(rqstp, p);
774}
775
776/* CREATE, MKDIR, SYMLINK, MKNOD */
777int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200778nfs3svc_encode_createres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200780 struct nfsd3_diropres *resp = rqstp->rq_resp;
781
Chuck Levercc028a12020-10-02 15:52:44 -0400782 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 if (resp->status == 0) {
784 *p++ = xdr_one;
785 p = encode_fh(p, &resp->fh);
786 p = encode_post_op_attr(rqstp, p, &resp->fh);
787 }
788 p = encode_wcc_data(rqstp, p, &resp->dirfh);
789 return xdr_ressize_check(rqstp, p);
790}
791
792/* RENAME */
793int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200794nfs3svc_encode_renameres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200796 struct nfsd3_renameres *resp = rqstp->rq_resp;
797
Chuck Levercc028a12020-10-02 15:52:44 -0400798 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 p = encode_wcc_data(rqstp, p, &resp->ffh);
800 p = encode_wcc_data(rqstp, p, &resp->tfh);
801 return xdr_ressize_check(rqstp, p);
802}
803
804/* LINK */
805int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200806nfs3svc_encode_linkres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200808 struct nfsd3_linkres *resp = rqstp->rq_resp;
809
Chuck Levercc028a12020-10-02 15:52:44 -0400810 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 p = encode_post_op_attr(rqstp, p, &resp->fh);
812 p = encode_wcc_data(rqstp, p, &resp->tfh);
813 return xdr_ressize_check(rqstp, p);
814}
815
816/* READDIR */
817int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200818nfs3svc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200820 struct nfsd3_readdirres *resp = rqstp->rq_resp;
821
Chuck Levercc028a12020-10-02 15:52:44 -0400822 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 p = encode_post_op_attr(rqstp, p, &resp->fh);
824
825 if (resp->status == 0) {
826 /* stupid readdir cookie */
827 memcpy(p, resp->verf, 8); p += 2;
828 xdr_ressize_check(rqstp, p);
829 if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
830 return 1; /*No room for trailer */
831 rqstp->rq_res.page_len = (resp->count) << 2;
832
833 /* add the 'tail' to the end of the 'head' page - page 0. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 rqstp->rq_res.tail[0].iov_base = p;
835 *p++ = 0; /* no more entries */
836 *p++ = htonl(resp->common.err == nfserr_eof);
837 rqstp->rq_res.tail[0].iov_len = 2<<2;
838 return 1;
839 } else
840 return xdr_ressize_check(rqstp, p);
841}
842
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800843static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700844encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400845 int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
847 *p++ = xdr_one; /* mark entry present */
848 p = xdr_encode_hyper(p, ino); /* file id */
849 p = xdr_encode_array(p, name, namlen);/* name length & name */
850
851 cd->offset = p; /* remember pointer */
852 p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
853
854 return p;
855}
856
Al Viroefe39652012-04-13 00:32:14 -0400857static __be32
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
NeilBrown43b0e7e2015-05-03 09:16:53 +1000859 const char *name, int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860{
861 struct svc_export *exp;
862 struct dentry *dparent, *dchild;
Al Viroefe39652012-04-13 00:32:14 -0400863 __be32 rv = nfserr_noent;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
865 dparent = cd->fh.fh_dentry;
866 exp = cd->fh.fh_export;
867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (isdotent(name, namlen)) {
869 if (namlen == 2) {
870 dchild = dget_parent(dparent);
J. Bruce Fields51b2ee72021-01-11 16:01:29 -0500871 /*
872 * Don't return filehandle for ".." if we're at
873 * the filesystem or export root:
874 */
Al Viroefe39652012-04-13 00:32:14 -0400875 if (dchild == dparent)
876 goto out;
J. Bruce Fields51b2ee72021-01-11 16:01:29 -0500877 if (dparent == exp->ex_path.dentry)
878 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 } else
880 dchild = dget(dparent);
881 } else
Al Viro6c2d47982019-10-31 01:21:58 -0400882 dchild = lookup_positive_unlocked(name, dparent, namlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 if (IS_ERR(dchild))
Al Viroefe39652012-04-13 00:32:14 -0400884 return rv;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400885 if (d_mountpoint(dchild))
886 goto out;
NeilBrown43b0e7e2015-05-03 09:16:53 +1000887 if (dchild->d_inode->i_ino != ino)
888 goto out;
Al Viroefe39652012-04-13 00:32:14 -0400889 rv = fh_compose(fhp, exp, dchild, &cd->fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400890out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 dput(dchild);
892 return rv;
893}
894
NeilBrown43b0e7e2015-05-03 09:16:53 +1000895static __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 -0400896{
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500897 struct svc_fh *fh = &cd->scratch;
Al Viroefe39652012-04-13 00:32:14 -0400898 __be32 err;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400899
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500900 fh_init(fh, NFS3_FHSIZE);
NeilBrown43b0e7e2015-05-03 09:16:53 +1000901 err = compose_entry_fh(cd, fh, name, namlen, ino);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400902 if (err) {
903 *p++ = 0;
904 *p++ = 0;
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400905 goto out;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400906 }
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500907 p = encode_post_op_attr(cd->rqstp, p, fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400908 *p++ = xdr_one; /* yes, a file handle follows */
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500909 p = encode_fh(p, fh);
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400910out:
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500911 fh_put(fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400912 return p;
913}
914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915/*
916 * Encode a directory entry. This one works for both normal readdir
917 * and readdirplus.
918 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
919 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
920 *
921 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
922 * file handle.
923 */
924
925#define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
926#define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
927static int
NeilBrown598b9a52007-03-26 21:32:08 -0800928encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400929 loff_t offset, u64 ino, unsigned int d_type, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930{
931 struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
932 common);
Al Viro91f07162006-10-19 23:28:57 -0700933 __be32 *p = cd->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 caddr_t curr_page_addr = NULL;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500935 struct page ** page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 int slen; /* string (name) length */
937 int elen; /* estimated entry length in words */
938 int num_entry_words = 0; /* actual number of words */
939
940 if (cd->offset) {
941 u64 offset64 = offset;
942
943 if (unlikely(cd->offset1)) {
944 /* we ended up with offset on a page boundary */
945 *cd->offset = htonl(offset64 >> 32);
946 *cd->offset1 = htonl(offset64 & 0xffffffff);
947 cd->offset1 = NULL;
948 } else {
NeilBrown598b9a52007-03-26 21:32:08 -0800949 xdr_encode_hyper(cd->offset, offset64);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
NeilBrownb6023452019-03-04 14:08:22 +1100951 cd->offset = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
953
954 /*
955 dprintk("encode_entry(%.*s @%ld%s)\n",
956 namlen, name, (long) offset, plus? " plus" : "");
957 */
958
959 /* truncate filename if too long */
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800960 namlen = min(namlen, NFS3_MAXNAMLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
962 slen = XDR_QUADLEN(namlen);
963 elen = slen + NFS3_ENTRY_BAGGAGE
964 + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
965
966 if (cd->buflen < elen) {
967 cd->common.err = nfserr_toosmall;
968 return -EINVAL;
969 }
970
971 /* determine which page in rq_respages[] we are currently filling */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500972 for (page = cd->rqstp->rq_respages + 1;
973 page < cd->rqstp->rq_next_page; page++) {
974 curr_page_addr = page_address(*page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 if (((caddr_t)cd->buffer >= curr_page_addr) &&
977 ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE))
978 break;
979 }
980
981 if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
982 /* encode entry in current page */
983
984 p = encode_entry_baggage(cd, p, name, namlen, ino);
985
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400986 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +1000987 p = encode_entryplus_baggage(cd, p, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 num_entry_words = p - cd->buffer;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500989 } else if (*(page+1) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 /* temporarily encode entry into next page, then move back to
991 * current and next page in rq_respages[] */
Al Viro91f07162006-10-19 23:28:57 -0700992 __be32 *p1, *tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 int len1, len2;
994
995 /* grab next page for temporary storage of entry */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500996 p1 = tmp = page_address(*(page+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
998 p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
999
J. Bruce Fields8177e6d2009-09-04 14:13:09 -04001000 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +10001001 p1 = encode_entryplus_baggage(cd, p1, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003 /* determine entry word length and lengths to go in pages */
1004 num_entry_words = p1 - tmp;
1005 len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
1006 if ((num_entry_words << 2) < len1) {
1007 /* the actual number of words in the entry is less
1008 * than elen and can still fit in the current page
1009 */
1010 memmove(p, tmp, num_entry_words << 2);
1011 p += num_entry_words;
1012
1013 /* update offset */
1014 cd->offset = cd->buffer + (cd->offset - tmp);
1015 } else {
1016 unsigned int offset_r = (cd->offset - tmp) << 2;
1017
1018 /* update pointer to offset location.
1019 * This is a 64bit quantity, so we need to
1020 * deal with 3 cases:
1021 * - entirely in first page
1022 * - entirely in second page
1023 * - 4 bytes in each page
1024 */
1025 if (offset_r + 8 <= len1) {
1026 cd->offset = p + (cd->offset - tmp);
1027 } else if (offset_r >= len1) {
1028 cd->offset -= len1 >> 2;
1029 } else {
1030 /* sitting on the fence */
1031 BUG_ON(offset_r != len1 - 4);
1032 cd->offset = p + (cd->offset - tmp);
1033 cd->offset1 = tmp;
1034 }
1035
1036 len2 = (num_entry_words << 2) - len1;
1037
1038 /* move from temp page to current and next pages */
1039 memmove(p, tmp, len1);
1040 memmove(tmp, (caddr_t)tmp+len1, len2);
1041
1042 p = tmp + (len2 >> 2);
1043 }
1044 }
1045 else {
1046 cd->common.err = nfserr_toosmall;
1047 return -EINVAL;
1048 }
1049
1050 cd->buflen -= num_entry_words;
1051 cd->buffer = p;
1052 cd->common.err = nfs_ok;
1053 return 0;
1054
1055}
1056
1057int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001058nfs3svc_encode_entry(void *cd, const char *name,
1059 int namlen, loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
1061 return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
1062}
1063
1064int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001065nfs3svc_encode_entry_plus(void *cd, const char *name,
1066 int namlen, loff_t offset, u64 ino,
1067 unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068{
1069 return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
1070}
1071
1072/* FSSTAT */
1073int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001074nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001076 struct nfsd3_fsstatres *resp = rqstp->rq_resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 struct kstatfs *s = &resp->stats;
1078 u64 bs = s->f_bsize;
1079
Chuck Levercc028a12020-10-02 15:52:44 -04001080 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 *p++ = xdr_zero; /* no post_op_attr */
1082
1083 if (resp->status == 0) {
1084 p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
1085 p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
1086 p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
1087 p = xdr_encode_hyper(p, s->f_files); /* total inodes */
1088 p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
1089 p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
1090 *p++ = htonl(resp->invarsec); /* mean unchanged time */
1091 }
1092 return xdr_ressize_check(rqstp, p);
1093}
1094
1095/* FSINFO */
1096int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001097nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001099 struct nfsd3_fsinfores *resp = rqstp->rq_resp;
1100
Chuck Levercc028a12020-10-02 15:52:44 -04001101 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 *p++ = xdr_zero; /* no post_op_attr */
1103
1104 if (resp->status == 0) {
1105 *p++ = htonl(resp->f_rtmax);
1106 *p++ = htonl(resp->f_rtpref);
1107 *p++ = htonl(resp->f_rtmult);
1108 *p++ = htonl(resp->f_wtmax);
1109 *p++ = htonl(resp->f_wtpref);
1110 *p++ = htonl(resp->f_wtmult);
1111 *p++ = htonl(resp->f_dtpref);
1112 p = xdr_encode_hyper(p, resp->f_maxfilesize);
1113 *p++ = xdr_one;
1114 *p++ = xdr_zero;
1115 *p++ = htonl(resp->f_properties);
1116 }
1117
1118 return xdr_ressize_check(rqstp, p);
1119}
1120
1121/* PATHCONF */
1122int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001123nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001125 struct nfsd3_pathconfres *resp = rqstp->rq_resp;
1126
Chuck Lever1905cac2020-10-23 10:41:01 -04001127 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 *p++ = xdr_zero; /* no post_op_attr */
1129
1130 if (resp->status == 0) {
1131 *p++ = htonl(resp->p_link_max);
1132 *p++ = htonl(resp->p_name_max);
1133 *p++ = htonl(resp->p_no_trunc);
1134 *p++ = htonl(resp->p_chown_restricted);
1135 *p++ = htonl(resp->p_case_insensitive);
1136 *p++ = htonl(resp->p_case_preserving);
1137 }
1138
1139 return xdr_ressize_check(rqstp, p);
1140}
1141
1142/* COMMIT */
1143int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001144nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001146 struct nfsd3_commitres *resp = rqstp->rq_resp;
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +03001147
Chuck Levercc028a12020-10-02 15:52:44 -04001148 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 p = encode_wcc_data(rqstp, p, &resp->fh);
1150 /* Write verifier */
1151 if (resp->status == 0) {
Trond Myklebust524ff1a2020-01-06 13:40:36 -05001152 *p++ = resp->verf[0];
1153 *p++ = resp->verf[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 }
1155 return xdr_ressize_check(rqstp, p);
1156}
1157
1158/*
1159 * XDR release functions
1160 */
Christoph Hellwig85374882017-05-08 18:48:24 +02001161void
1162nfs3svc_release_fhandle(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163{
Christoph Hellwig85374882017-05-08 18:48:24 +02001164 struct nfsd3_attrstat *resp = rqstp->rq_resp;
1165
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 fh_put(&resp->fh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167}
1168
Christoph Hellwig85374882017-05-08 18:48:24 +02001169void
1170nfs3svc_release_fhandle2(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171{
Christoph Hellwig85374882017-05-08 18:48:24 +02001172 struct nfsd3_fhandle_pair *resp = rqstp->rq_resp;
1173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 fh_put(&resp->fh1);
1175 fh_put(&resp->fh2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176}