blob: 3a2b4abea1a42d896d62dd8d89794ebc391c4cb1 [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
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800120static __be32 *
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400121decode_sattr3(__be32 *p, struct iattr *iap, struct user_namespace *userns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
123 u32 tmp;
124
125 iap->ia_valid = 0;
126
127 if (*p++) {
128 iap->ia_valid |= ATTR_MODE;
129 iap->ia_mode = ntohl(*p++);
130 }
131 if (*p++) {
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400132 iap->ia_uid = make_kuid(userns, ntohl(*p++));
Eric W. Biederman458878a2013-02-02 04:16:08 -0800133 if (uid_valid(iap->ia_uid))
134 iap->ia_valid |= ATTR_UID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 }
136 if (*p++) {
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400137 iap->ia_gid = make_kgid(userns, ntohl(*p++));
Eric W. Biederman458878a2013-02-02 04:16:08 -0800138 if (gid_valid(iap->ia_gid))
139 iap->ia_valid |= ATTR_GID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 }
141 if (*p++) {
142 u64 newsize;
143
144 iap->ia_valid |= ATTR_SIZE;
145 p = xdr_decode_hyper(p, &newsize);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800146 iap->ia_size = min_t(u64, newsize, NFS_OFFSET_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
148 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
149 iap->ia_valid |= ATTR_ATIME;
150 } else if (tmp == 2) { /* set to client time */
151 iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
152 iap->ia_atime.tv_sec = ntohl(*p++);
153 iap->ia_atime.tv_nsec = ntohl(*p++);
154 }
155 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
156 iap->ia_valid |= ATTR_MTIME;
157 } else if (tmp == 2) { /* set to client time */
158 iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
159 iap->ia_mtime.tv_sec = ntohl(*p++);
160 iap->ia_mtime.tv_nsec = ntohl(*p++);
161 }
162 return p;
163}
164
NeilBrownaf6a4e22007-02-14 00:33:12 -0800165static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
166{
167 u64 f;
168 switch(fsid_source(fhp)) {
169 default:
170 case FSIDSOURCE_DEV:
171 p = xdr_encode_hyper(p, (u64)huge_encode_dev
Al Virofc640052016-04-10 01:33:30 -0400172 (fhp->fh_dentry->d_sb->s_dev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800173 break;
174 case FSIDSOURCE_FSID:
175 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
176 break;
177 case FSIDSOURCE_UUID:
178 f = ((u64*)fhp->fh_export->ex_uuid)[0];
179 f ^= ((u64*)fhp->fh_export->ex_uuid)[1];
180 p = xdr_encode_hyper(p, f);
181 break;
182 }
183 return p;
184}
185
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800186static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700187encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
David Shawa334de22006-01-06 00:19:58 -0800188 struct kstat *stat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400190 struct user_namespace *userns = nfsd_user_namespace(rqstp);
David Shawa334de22006-01-06 00:19:58 -0800191 *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
Albert Fluegel6e14b462013-11-18 12:18:01 -0500192 *p++ = htonl((u32) (stat->mode & S_IALLUGO));
David Shawa334de22006-01-06 00:19:58 -0800193 *p++ = htonl((u32) stat->nlink);
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400194 *p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
195 *p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
David Shawa334de22006-01-06 00:19:58 -0800196 if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
198 } else {
David Shawa334de22006-01-06 00:19:58 -0800199 p = xdr_encode_hyper(p, (u64) stat->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
David Shawa334de22006-01-06 00:19:58 -0800201 p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
202 *p++ = htonl((u32) MAJOR(stat->rdev));
203 *p++ = htonl((u32) MINOR(stat->rdev));
NeilBrownaf6a4e22007-02-14 00:33:12 -0800204 p = encode_fsid(p, fhp);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400205 p = xdr_encode_hyper(p, stat->ino);
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100206 p = encode_time3(p, &stat->atime);
207 p = encode_time3(p, &stat->mtime);
208 p = encode_time3(p, &stat->ctime);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 return p;
211}
212
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800213static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700214encode_saved_post_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 /* Attributes to follow */
217 *p++ = xdr_one;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400218 return encode_fattr3(rqstp, p, fhp, &fhp->fh_post_attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219}
220
221/*
222 * Encode post-operation attributes.
223 * The inode may be NULL if the call failed because of a stale file
224 * handle. In this case, no attributes are returned.
225 */
Al Viro91f07162006-10-19 23:28:57 -0700226static __be32 *
227encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 struct dentry *dentry = fhp->fh_dentry;
Jeff Laytondaab1102020-11-30 17:03:14 -0500230 if (!fhp->fh_no_wcc && dentry && d_really_is_positive(dentry)) {
Al Viro3dadecc2013-01-24 02:18:08 -0500231 __be32 err;
David Shawa334de22006-01-06 00:19:58 -0800232 struct kstat stat;
233
Al Viro3dadecc2013-01-24 02:18:08 -0500234 err = fh_getattr(fhp, &stat);
David Shawa334de22006-01-06 00:19:58 -0800235 if (!err) {
236 *p++ = xdr_one; /* attributes follow */
David Howells2b0143b2015-03-17 22:25:59 +0000237 lease_get_mtime(d_inode(dentry), &stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800238 return encode_fattr3(rqstp, p, fhp, &stat);
239 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 }
241 *p++ = xdr_zero;
242 return p;
243}
244
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000245/* Helper for NFSv3 ACLs */
Al Viro91f07162006-10-19 23:28:57 -0700246__be32 *
247nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Andreas Gruenbachera257cdd2005-06-22 17:16:26 +0000248{
249 return encode_post_op_attr(rqstp, p, fhp);
250}
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252/*
253 * Enocde weak cache consistency data
254 */
Al Viro91f07162006-10-19 23:28:57 -0700255static __be32 *
256encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 struct dentry *dentry = fhp->fh_dentry;
259
David Howells2b0143b2015-03-17 22:25:59 +0000260 if (dentry && d_really_is_positive(dentry) && fhp->fh_post_saved) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 if (fhp->fh_pre_saved) {
262 *p++ = xdr_one;
263 p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
264 p = encode_time3(p, &fhp->fh_pre_mtime);
265 p = encode_time3(p, &fhp->fh_pre_ctime);
266 } else {
267 *p++ = xdr_zero;
268 }
269 return encode_saved_post_attr(rqstp, p, fhp);
270 }
271 /* no pre- or post-attrs */
272 *p++ = xdr_zero;
273 return encode_post_op_attr(rqstp, p, fhp);
274}
275
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400276/*
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200277 * Fill in the pre_op attr for the wcc data
278 */
279void fill_pre_wcc(struct svc_fh *fhp)
280{
281 struct inode *inode;
282 struct kstat stat;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500283 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200284 __be32 err;
285
Jeff Laytondaab1102020-11-30 17:03:14 -0500286 if (fhp->fh_no_wcc || fhp->fh_pre_saved)
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200287 return;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200288 inode = d_inode(fhp->fh_dentry);
289 err = fh_getattr(fhp, &stat);
290 if (err) {
291 /* Grab the times from inode anyway */
292 stat.mtime = inode->i_mtime;
293 stat.ctime = inode->i_ctime;
294 stat.size = inode->i_size;
295 }
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500296 if (v4)
297 fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200298
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100299 fhp->fh_pre_mtime = stat.mtime;
300 fhp->fh_pre_ctime = stat.ctime;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200301 fhp->fh_pre_size = stat.size;
Amir Goldstein39ca1bf2018-01-03 17:14:35 +0200302 fhp->fh_pre_saved = true;
303}
304
305/*
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400306 * Fill in the post_op attr for the wcc data
307 */
308void fill_post_wcc(struct svc_fh *fhp)
309{
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500310 bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
311 struct inode *inode = d_inode(fhp->fh_dentry);
Al Viro3dadecc2013-01-24 02:18:08 -0500312 __be32 err;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400313
Jeff Laytondaab1102020-11-30 17:03:14 -0500314 if (fhp->fh_no_wcc)
315 return;
316
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400317 if (fhp->fh_post_saved)
318 printk("nfsd: inode locked twice during operation.\n");
319
Al Viro3dadecc2013-01-24 02:18:08 -0500320 err = fh_getattr(fhp, &fhp->fh_post_attr);
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100321 if (err) {
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400322 fhp->fh_post_saved = false;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500323 fhp->fh_post_attr.ctime = inode->i_ctime;
Neil Brownc1ac3ff2010-12-02 11:14:30 +1100324 } else
Jeff Laytonaaf91ec2015-09-17 08:28:39 -0400325 fhp->fh_post_saved = true;
J. Bruce Fields942b20dc2020-11-30 17:46:17 -0500326 if (v4)
327 fhp->fh_post_change =
328 nfsd4_change_attribute(&fhp->fh_post_attr, inode);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400329}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
331/*
332 * XDR decode functions
333 */
Chuck Leverdcc46992020-10-01 18:59:12 -0400334
335int
Chuck Lever95753632020-10-20 14:30:02 -0400336nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Chuck Lever95753632020-10-20 14:30:02 -0400338 struct xdr_stream *xdr = &rqstp->rq_arg_stream;
Christoph Hellwig026fec72017-05-08 19:01:48 +0200339 struct nfsd_fhandle *args = rqstp->rq_argp;
340
Chuck Lever95753632020-10-20 14:30:02 -0400341 return svcxdr_decode_nfs_fh3(xdr, &args->fh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342}
343
344int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200345nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200347 struct nfsd3_sattrargs *args = rqstp->rq_argp;
348
Benoit Tained40aa332014-05-22 16:32:30 +0200349 p = decode_fh(p, &args->fh);
350 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400352 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 if ((args->check_guard = ntohl(*p++)) != 0) {
Arnd Bergmann92c5e462019-10-31 14:55:32 +0100355 struct timespec64 time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 p = decode_time3(p, &time);
357 args->guardtime = time.tv_sec;
358 }
359
360 return xdr_argsize_check(rqstp, p);
361}
362
363int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200364nfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200366 struct nfsd3_diropargs *args = rqstp->rq_argp;
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if (!(p = decode_fh(p, &args->fh))
369 || !(p = decode_filename(p, &args->name, &args->len)))
370 return 0;
371
372 return xdr_argsize_check(rqstp, p);
373}
374
375int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200376nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200378 struct nfsd3_accessargs *args = rqstp->rq_argp;
379
Benoit Tained40aa332014-05-22 16:32:30 +0200380 p = decode_fh(p, &args->fh);
381 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 return 0;
383 args->access = ntohl(*p++);
384
385 return xdr_argsize_check(rqstp, p);
386}
387
388int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200389nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200391 struct nfsd3_readargs *args = rqstp->rq_argp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 unsigned int len;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500393 int v;
Greg Banks7adae482006-10-04 02:15:47 -0700394 u32 max_blocksize = svc_max_payload(rqstp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
Benoit Tained40aa332014-05-22 16:32:30 +0200396 p = decode_fh(p, &args->fh);
397 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700399 p = xdr_decode_hyper(p, &args->offset);
J. Bruce Fields9512a162017-05-16 15:57:42 -0400400
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800401 args->count = ntohl(*p++);
402 len = min(args->count, max_blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 /* set up the kvec */
405 v=0;
406 while (len > 0) {
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500407 struct page *p = *(rqstp->rq_next_page++);
408
409 rqstp->rq_vec[v].iov_base = page_address(p);
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800410 rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
NeilBrown3cc03b12006-10-04 02:15:47 -0700411 len -= rqstp->rq_vec[v].iov_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 v++;
413 }
414 args->vlen = v;
J. Bruce Fields9512a162017-05-16 15:57:42 -0400415 return xdr_argsize_check(rqstp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
418int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200419nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200421 struct nfsd3_writeargs *args = rqstp->rq_argp;
Chuck Lever8154ef22018-03-27 10:54:07 -0400422 unsigned int len, hdr, dlen;
Greg Banks7adae482006-10-04 02:15:47 -0700423 u32 max_blocksize = svc_max_payload(rqstp);
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400424 struct kvec *head = rqstp->rq_arg.head;
425 struct kvec *tail = rqstp->rq_arg.tail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Benoit Tained40aa332014-05-22 16:32:30 +0200427 p = decode_fh(p, &args->fh);
428 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 return 0;
NeilBrown072f62e2007-05-09 02:34:57 -0700430 p = xdr_decode_hyper(p, &args->offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
432 args->count = ntohl(*p++);
433 args->stable = ntohl(*p++);
434 len = args->len = ntohl(*p++);
J. Bruce Fields13bf9fb2017-04-21 15:26:30 -0400435 if ((void *)p > head->iov_base + head->iov_len)
436 return 0;
Peter Staubachf34b95682007-05-09 02:34:48 -0700437 /*
438 * The count must equal the amount of data passed.
439 */
440 if (args->count != args->len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return 0;
442
Peter Staubachf34b95682007-05-09 02:34:48 -0700443 /*
444 * Check to make sure that we got the right number of
445 * bytes.
Peter Staubachf34b95682007-05-09 02:34:48 -0700446 */
J. Bruce Fieldsdb44bac2017-04-25 16:21:34 -0400447 hdr = (void*)p - head->iov_base;
448 dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr;
Peter Staubachf34b95682007-05-09 02:34:48 -0700449 /*
450 * Round the length of the data which was specified up to
451 * the next multiple of XDR units and then compare that
452 * against the length which was actually received.
NeilBrownba67a392008-01-11 17:06:52 -0500453 * Note that when RPCSEC/GSS (for example) is used, the
454 * data buffer can be padded so dlen might be larger
455 * than required. It must never be smaller.
Peter Staubachf34b95682007-05-09 02:34:48 -0700456 */
NeilBrownba67a392008-01-11 17:06:52 -0500457 if (dlen < XDR_QUADLEN(len)*4)
Peter Staubachf34b95682007-05-09 02:34:48 -0700458 return 0;
459
460 if (args->count > max_blocksize) {
461 args->count = max_blocksize;
462 len = args->len = max_blocksize;
463 }
Chuck Lever8154ef22018-03-27 10:54:07 -0400464
465 args->first.iov_base = (void *)p;
466 args->first.iov_len = head->iov_len - hdr;
Peter Staubachf34b95682007-05-09 02:34:48 -0700467 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
470int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200471nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200473 struct nfsd3_createargs *args = rqstp->rq_argp;
474
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 if (!(p = decode_fh(p, &args->fh))
476 || !(p = decode_filename(p, &args->name, &args->len)))
477 return 0;
478
479 switch (args->createmode = ntohl(*p++)) {
480 case NFS3_CREATE_UNCHECKED:
481 case NFS3_CREATE_GUARDED:
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400482 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 break;
484 case NFS3_CREATE_EXCLUSIVE:
485 args->verf = p;
486 p += 2;
487 break;
488 default:
489 return 0;
490 }
491
492 return xdr_argsize_check(rqstp, p);
493}
Christoph Hellwig026fec72017-05-08 19:01:48 +0200494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200496nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200498 struct nfsd3_createargs *args = rqstp->rq_argp;
499
NeilBrown072f62e2007-05-09 02:34:57 -0700500 if (!(p = decode_fh(p, &args->fh)) ||
501 !(p = decode_filename(p, &args->name, &args->len)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400503 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 return xdr_argsize_check(rqstp, p);
506}
507
508int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200509nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200511 struct nfsd3_symlinkargs *args = rqstp->rq_argp;
Chuck Lever38a70312018-03-27 10:54:21 -0400512 char *base = (char *)p;
513 size_t dlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
NeilBrown072f62e2007-05-09 02:34:57 -0700515 if (!(p = decode_fh(p, &args->ffh)) ||
Chuck Lever38a70312018-03-27 10:54:21 -0400516 !(p = decode_filename(p, &args->fname, &args->flen)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 return 0;
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400518 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
NeilBrown072f62e2007-05-09 02:34:57 -0700519
Chuck Lever38a70312018-03-27 10:54:21 -0400520 args->tlen = ntohl(*p++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Chuck Lever38a70312018-03-27 10:54:21 -0400522 args->first.iov_base = p;
523 args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
524 args->first.iov_len -= (char *)p - base;
525
526 dlen = args->first.iov_len + rqstp->rq_arg.page_len +
527 rqstp->rq_arg.tail[0].iov_len;
528 if (dlen < XDR_QUADLEN(args->tlen) << 2)
529 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 return 1;
531}
532
533int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200534nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200536 struct nfsd3_mknodargs *args = rqstp->rq_argp;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (!(p = decode_fh(p, &args->fh))
539 || !(p = decode_filename(p, &args->name, &args->len)))
540 return 0;
541
542 args->ftype = ntohl(*p++);
543
544 if (args->ftype == NF3BLK || args->ftype == NF3CHR
NeilBrown072f62e2007-05-09 02:34:57 -0700545 || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
Trond Myklebuste45d1a12019-04-09 12:13:42 -0400546 p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
549 args->major = ntohl(*p++);
550 args->minor = ntohl(*p++);
551 }
552
553 return xdr_argsize_check(rqstp, p);
554}
555
556int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200557nfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200559 struct nfsd3_renameargs *args = rqstp->rq_argp;
560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 if (!(p = decode_fh(p, &args->ffh))
562 || !(p = decode_filename(p, &args->fname, &args->flen))
563 || !(p = decode_fh(p, &args->tfh))
564 || !(p = decode_filename(p, &args->tname, &args->tlen)))
565 return 0;
566
567 return xdr_argsize_check(rqstp, p);
568}
569
570int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200571nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200573 struct nfsd3_readlinkargs *args = rqstp->rq_argp;
574
Benoit Tained40aa332014-05-22 16:32:30 +0200575 p = decode_fh(p, &args->fh);
576 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 return 0;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500578 args->buffer = page_address(*(rqstp->rq_next_page++));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
J. Bruce Fields9512a162017-05-16 15:57:42 -0400580 return xdr_argsize_check(rqstp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
583int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200584nfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200586 struct nfsd3_linkargs *args = rqstp->rq_argp;
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 if (!(p = decode_fh(p, &args->ffh))
589 || !(p = decode_fh(p, &args->tfh))
590 || !(p = decode_filename(p, &args->tname, &args->tlen)))
591 return 0;
592
593 return xdr_argsize_check(rqstp, p);
594}
595
596int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200597nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200599 struct nfsd3_readdirargs *args = rqstp->rq_argp;
Murphy Zhou3c867942019-04-04 14:57:11 +0800600 int len;
NeilBrownf875a792019-03-07 09:49:46 +1100601 u32 max_blocksize = svc_max_payload(rqstp);
602
Benoit Tained40aa332014-05-22 16:32:30 +0200603 p = decode_fh(p, &args->fh);
604 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 return 0;
606 p = xdr_decode_hyper(p, &args->cookie);
607 args->verf = p; p += 2;
608 args->dircount = ~0;
609 args->count = ntohl(*p++);
Murphy Zhou3c867942019-04-04 14:57:11 +0800610 len = args->count = min_t(u32, args->count, max_blocksize);
611
612 while (len > 0) {
613 struct page *p = *(rqstp->rq_next_page++);
614 if (!args->buffer)
615 args->buffer = page_address(p);
616 len -= PAGE_SIZE;
617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
J. Bruce Fields9512a162017-05-16 15:57:42 -0400619 return xdr_argsize_check(rqstp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620}
621
622int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200623nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200625 struct nfsd3_readdirargs *args = rqstp->rq_argp;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500626 int len;
Greg Banks7adae482006-10-04 02:15:47 -0700627 u32 max_blocksize = svc_max_payload(rqstp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Benoit Tained40aa332014-05-22 16:32:30 +0200629 p = decode_fh(p, &args->fh);
630 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return 0;
632 p = xdr_decode_hyper(p, &args->cookie);
633 args->verf = p; p += 2;
634 args->dircount = ntohl(*p++);
635 args->count = ntohl(*p++);
636
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800637 len = args->count = min(args->count, max_blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 while (len > 0) {
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500639 struct page *p = *(rqstp->rq_next_page++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 if (!args->buffer)
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500641 args->buffer = page_address(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 len -= PAGE_SIZE;
643 }
J. Bruce Fields9512a162017-05-16 15:57:42 -0400644
645 return xdr_argsize_check(rqstp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646}
647
648int
Christoph Hellwig026fec72017-05-08 19:01:48 +0200649nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Christoph Hellwig026fec72017-05-08 19:01:48 +0200651 struct nfsd3_commitargs *args = rqstp->rq_argp;
Benoit Tained40aa332014-05-22 16:32:30 +0200652 p = decode_fh(p, &args->fh);
653 if (!p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 return 0;
655 p = xdr_decode_hyper(p, &args->offset);
656 args->count = ntohl(*p++);
657
658 return xdr_argsize_check(rqstp, p);
659}
660
661/*
662 * XDR encode functions
663 */
Chuck Levercc028a12020-10-02 15:52:44 -0400664
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665/* GETATTR */
666int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200667nfs3svc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200669 struct nfsd3_attrstat *resp = rqstp->rq_resp;
670
Chuck Levercc028a12020-10-02 15:52:44 -0400671 *p++ = resp->status;
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400672 if (resp->status == 0) {
David Howells2b0143b2015-03-17 22:25:59 +0000673 lease_get_mtime(d_inode(resp->fh.fh_dentry),
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400674 &resp->stat.mtime);
David Shawa334de22006-01-06 00:19:58 -0800675 p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400676 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return xdr_ressize_check(rqstp, p);
678}
679
680/* SETATTR, REMOVE, RMDIR */
681int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200682nfs3svc_encode_wccstat(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200684 struct nfsd3_attrstat *resp = rqstp->rq_resp;
685
Chuck Levercc028a12020-10-02 15:52:44 -0400686 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 p = encode_wcc_data(rqstp, p, &resp->fh);
688 return xdr_ressize_check(rqstp, p);
689}
690
691/* LOOKUP */
692int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200693nfs3svc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200695 struct nfsd3_diropres *resp = rqstp->rq_resp;
696
Chuck Levercc028a12020-10-02 15:52:44 -0400697 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if (resp->status == 0) {
699 p = encode_fh(p, &resp->fh);
700 p = encode_post_op_attr(rqstp, p, &resp->fh);
701 }
702 p = encode_post_op_attr(rqstp, p, &resp->dirfh);
703 return xdr_ressize_check(rqstp, p);
704}
705
706/* ACCESS */
707int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200708nfs3svc_encode_accessres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200710 struct nfsd3_accessres *resp = rqstp->rq_resp;
711
Chuck Levercc028a12020-10-02 15:52:44 -0400712 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 p = encode_post_op_attr(rqstp, p, &resp->fh);
714 if (resp->status == 0)
715 *p++ = htonl(resp->access);
716 return xdr_ressize_check(rqstp, p);
717}
718
719/* READLINK */
720int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200721nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200723 struct nfsd3_readlinkres *resp = rqstp->rq_resp;
Chuck Lever76e54922020-11-05 10:24:19 -0500724 struct kvec *head = rqstp->rq_res.head;
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200725
Chuck Levercc028a12020-10-02 15:52:44 -0400726 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 p = encode_post_op_attr(rqstp, p, &resp->fh);
728 if (resp->status == 0) {
729 *p++ = htonl(resp->len);
730 xdr_ressize_check(rqstp, p);
731 rqstp->rq_res.page_len = resp->len;
732 if (resp->len & 3) {
733 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 rqstp->rq_res.tail[0].iov_base = p;
735 *p = 0;
736 rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
737 }
Chuck Lever76e54922020-11-05 10:24:19 -0500738 if (svc_encode_result_payload(rqstp, head->iov_len, resp->len))
739 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 return 1;
741 } else
742 return xdr_ressize_check(rqstp, p);
743}
744
745/* READ */
746int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200747nfs3svc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200749 struct nfsd3_readres *resp = rqstp->rq_resp;
Chuck Lever76e54922020-11-05 10:24:19 -0500750 struct kvec *head = rqstp->rq_res.head;
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200751
Chuck Levercc028a12020-10-02 15:52:44 -0400752 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 p = encode_post_op_attr(rqstp, p, &resp->fh);
754 if (resp->status == 0) {
755 *p++ = htonl(resp->count);
756 *p++ = htonl(resp->eof);
757 *p++ = htonl(resp->count); /* xdr opaque count */
758 xdr_ressize_check(rqstp, p);
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300759 /* now update rqstp->rq_res to reflect data as well */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 rqstp->rq_res.page_len = resp->count;
761 if (resp->count & 3) {
762 /* need to pad the tail */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 rqstp->rq_res.tail[0].iov_base = p;
764 *p = 0;
765 rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
766 }
Chuck Lever76e54922020-11-05 10:24:19 -0500767 if (svc_encode_result_payload(rqstp, head->iov_len,
768 resp->count))
769 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 return 1;
771 } else
772 return xdr_ressize_check(rqstp, p);
773}
774
775/* WRITE */
776int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200777nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200779 struct nfsd3_writeres *resp = rqstp->rq_resp;
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +0300780
Chuck Levercc028a12020-10-02 15:52:44 -0400781 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 p = encode_wcc_data(rqstp, p, &resp->fh);
783 if (resp->status == 0) {
784 *p++ = htonl(resp->count);
785 *p++ = htonl(resp->committed);
Trond Myklebust19e06632020-01-06 13:40:37 -0500786 *p++ = resp->verf[0];
787 *p++ = resp->verf[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
789 return xdr_ressize_check(rqstp, p);
790}
791
792/* CREATE, MKDIR, SYMLINK, MKNOD */
793int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200794nfs3svc_encode_createres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200796 struct nfsd3_diropres *resp = rqstp->rq_resp;
797
Chuck Levercc028a12020-10-02 15:52:44 -0400798 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 if (resp->status == 0) {
800 *p++ = xdr_one;
801 p = encode_fh(p, &resp->fh);
802 p = encode_post_op_attr(rqstp, p, &resp->fh);
803 }
804 p = encode_wcc_data(rqstp, p, &resp->dirfh);
805 return xdr_ressize_check(rqstp, p);
806}
807
808/* RENAME */
809int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200810nfs3svc_encode_renameres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200812 struct nfsd3_renameres *resp = rqstp->rq_resp;
813
Chuck Levercc028a12020-10-02 15:52:44 -0400814 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 p = encode_wcc_data(rqstp, p, &resp->ffh);
816 p = encode_wcc_data(rqstp, p, &resp->tfh);
817 return xdr_ressize_check(rqstp, p);
818}
819
820/* LINK */
821int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200822nfs3svc_encode_linkres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200824 struct nfsd3_linkres *resp = rqstp->rq_resp;
825
Chuck Levercc028a12020-10-02 15:52:44 -0400826 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 p = encode_post_op_attr(rqstp, p, &resp->fh);
828 p = encode_wcc_data(rqstp, p, &resp->tfh);
829 return xdr_ressize_check(rqstp, p);
830}
831
832/* READDIR */
833int
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200834nfs3svc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
Christoph Hellwig63f8de32017-05-08 19:42:02 +0200836 struct nfsd3_readdirres *resp = rqstp->rq_resp;
837
Chuck Levercc028a12020-10-02 15:52:44 -0400838 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 p = encode_post_op_attr(rqstp, p, &resp->fh);
840
841 if (resp->status == 0) {
842 /* stupid readdir cookie */
843 memcpy(p, resp->verf, 8); p += 2;
844 xdr_ressize_check(rqstp, p);
845 if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
846 return 1; /*No room for trailer */
847 rqstp->rq_res.page_len = (resp->count) << 2;
848
849 /* add the 'tail' to the end of the 'head' page - page 0. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 rqstp->rq_res.tail[0].iov_base = p;
851 *p++ = 0; /* no more entries */
852 *p++ = htonl(resp->common.err == nfserr_eof);
853 rqstp->rq_res.tail[0].iov_len = 2<<2;
854 return 1;
855 } else
856 return xdr_ressize_check(rqstp, p);
857}
858
Adrian Bunk3ee6f612006-12-06 20:40:23 -0800859static __be32 *
Al Viro91f07162006-10-19 23:28:57 -0700860encode_entry_baggage(struct nfsd3_readdirres *cd, __be32 *p, const char *name,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400861 int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
863 *p++ = xdr_one; /* mark entry present */
864 p = xdr_encode_hyper(p, ino); /* file id */
865 p = xdr_encode_array(p, name, namlen);/* name length & name */
866
867 cd->offset = p; /* remember pointer */
868 p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
869
870 return p;
871}
872
Al Viroefe39652012-04-13 00:32:14 -0400873static __be32
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
NeilBrown43b0e7e2015-05-03 09:16:53 +1000875 const char *name, int namlen, u64 ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876{
877 struct svc_export *exp;
878 struct dentry *dparent, *dchild;
Al Viroefe39652012-04-13 00:32:14 -0400879 __be32 rv = nfserr_noent;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
881 dparent = cd->fh.fh_dentry;
882 exp = cd->fh.fh_export;
883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 if (isdotent(name, namlen)) {
885 if (namlen == 2) {
886 dchild = dget_parent(dparent);
J. Bruce Fields51b2ee72021-01-11 16:01:29 -0500887 /*
888 * Don't return filehandle for ".." if we're at
889 * the filesystem or export root:
890 */
Al Viroefe39652012-04-13 00:32:14 -0400891 if (dchild == dparent)
892 goto out;
J. Bruce Fields51b2ee72021-01-11 16:01:29 -0500893 if (dparent == exp->ex_path.dentry)
894 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 } else
896 dchild = dget(dparent);
897 } else
Al Viro6c2d47982019-10-31 01:21:58 -0400898 dchild = lookup_positive_unlocked(name, dparent, namlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 if (IS_ERR(dchild))
Al Viroefe39652012-04-13 00:32:14 -0400900 return rv;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400901 if (d_mountpoint(dchild))
902 goto out;
NeilBrown43b0e7e2015-05-03 09:16:53 +1000903 if (dchild->d_inode->i_ino != ino)
904 goto out;
Al Viroefe39652012-04-13 00:32:14 -0400905 rv = fh_compose(fhp, exp, dchild, &cd->fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400906out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 dput(dchild);
908 return rv;
909}
910
NeilBrown43b0e7e2015-05-03 09:16:53 +1000911static __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 -0400912{
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500913 struct svc_fh *fh = &cd->scratch;
Al Viroefe39652012-04-13 00:32:14 -0400914 __be32 err;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400915
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500916 fh_init(fh, NFS3_FHSIZE);
NeilBrown43b0e7e2015-05-03 09:16:53 +1000917 err = compose_entry_fh(cd, fh, name, namlen, ino);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400918 if (err) {
919 *p++ = 0;
920 *p++ = 0;
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400921 goto out;
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400922 }
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500923 p = encode_post_op_attr(cd->rqstp, p, fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400924 *p++ = xdr_one; /* yes, a file handle follows */
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500925 p = encode_fh(p, fh);
J. Bruce Fieldsaed100f2009-09-04 14:40:36 -0400926out:
J. Bruce Fields068c34c2014-01-09 16:24:35 -0500927 fh_put(fh);
J. Bruce Fields8177e6d2009-09-04 14:13:09 -0400928 return p;
929}
930
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931/*
932 * Encode a directory entry. This one works for both normal readdir
933 * and readdirplus.
934 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
935 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
936 *
937 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
938 * file handle.
939 */
940
941#define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
942#define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
943static int
NeilBrown598b9a52007-03-26 21:32:08 -0800944encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
Peter Staubach40ee5dc2007-08-16 12:10:07 -0400945 loff_t offset, u64 ino, unsigned int d_type, int plus)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946{
947 struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
948 common);
Al Viro91f07162006-10-19 23:28:57 -0700949 __be32 *p = cd->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 caddr_t curr_page_addr = NULL;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500951 struct page ** page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 int slen; /* string (name) length */
953 int elen; /* estimated entry length in words */
954 int num_entry_words = 0; /* actual number of words */
955
956 if (cd->offset) {
957 u64 offset64 = offset;
958
959 if (unlikely(cd->offset1)) {
960 /* we ended up with offset on a page boundary */
961 *cd->offset = htonl(offset64 >> 32);
962 *cd->offset1 = htonl(offset64 & 0xffffffff);
963 cd->offset1 = NULL;
964 } else {
NeilBrown598b9a52007-03-26 21:32:08 -0800965 xdr_encode_hyper(cd->offset, offset64);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 }
NeilBrownb6023452019-03-04 14:08:22 +1100967 cd->offset = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969
970 /*
971 dprintk("encode_entry(%.*s @%ld%s)\n",
972 namlen, name, (long) offset, plus? " plus" : "");
973 */
974
975 /* truncate filename if too long */
Kinglong Mee3c7aa152014-06-10 18:08:19 +0800976 namlen = min(namlen, NFS3_MAXNAMLEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 slen = XDR_QUADLEN(namlen);
979 elen = slen + NFS3_ENTRY_BAGGAGE
980 + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
981
982 if (cd->buflen < elen) {
983 cd->common.err = nfserr_toosmall;
984 return -EINVAL;
985 }
986
987 /* determine which page in rq_respages[] we are currently filling */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -0500988 for (page = cd->rqstp->rq_respages + 1;
989 page < cd->rqstp->rq_next_page; page++) {
990 curr_page_addr = page_address(*page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
992 if (((caddr_t)cd->buffer >= curr_page_addr) &&
993 ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE))
994 break;
995 }
996
997 if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
998 /* encode entry in current page */
999
1000 p = encode_entry_baggage(cd, p, name, namlen, ino);
1001
J. Bruce Fields8177e6d2009-09-04 14:13:09 -04001002 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +10001003 p = encode_entryplus_baggage(cd, p, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 num_entry_words = p - cd->buffer;
J. Bruce Fieldsafc59402012-12-10 18:01:37 -05001005 } else if (*(page+1) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 /* temporarily encode entry into next page, then move back to
1007 * current and next page in rq_respages[] */
Al Viro91f07162006-10-19 23:28:57 -07001008 __be32 *p1, *tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 int len1, len2;
1010
1011 /* grab next page for temporary storage of entry */
J. Bruce Fieldsafc59402012-12-10 18:01:37 -05001012 p1 = tmp = page_address(*(page+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
1015
J. Bruce Fields8177e6d2009-09-04 14:13:09 -04001016 if (plus)
NeilBrown43b0e7e2015-05-03 09:16:53 +10001017 p1 = encode_entryplus_baggage(cd, p1, name, namlen, ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
1019 /* determine entry word length and lengths to go in pages */
1020 num_entry_words = p1 - tmp;
1021 len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
1022 if ((num_entry_words << 2) < len1) {
1023 /* the actual number of words in the entry is less
1024 * than elen and can still fit in the current page
1025 */
1026 memmove(p, tmp, num_entry_words << 2);
1027 p += num_entry_words;
1028
1029 /* update offset */
1030 cd->offset = cd->buffer + (cd->offset - tmp);
1031 } else {
1032 unsigned int offset_r = (cd->offset - tmp) << 2;
1033
1034 /* update pointer to offset location.
1035 * This is a 64bit quantity, so we need to
1036 * deal with 3 cases:
1037 * - entirely in first page
1038 * - entirely in second page
1039 * - 4 bytes in each page
1040 */
1041 if (offset_r + 8 <= len1) {
1042 cd->offset = p + (cd->offset - tmp);
1043 } else if (offset_r >= len1) {
1044 cd->offset -= len1 >> 2;
1045 } else {
1046 /* sitting on the fence */
1047 BUG_ON(offset_r != len1 - 4);
1048 cd->offset = p + (cd->offset - tmp);
1049 cd->offset1 = tmp;
1050 }
1051
1052 len2 = (num_entry_words << 2) - len1;
1053
1054 /* move from temp page to current and next pages */
1055 memmove(p, tmp, len1);
1056 memmove(tmp, (caddr_t)tmp+len1, len2);
1057
1058 p = tmp + (len2 >> 2);
1059 }
1060 }
1061 else {
1062 cd->common.err = nfserr_toosmall;
1063 return -EINVAL;
1064 }
1065
1066 cd->buflen -= num_entry_words;
1067 cd->buffer = p;
1068 cd->common.err = nfs_ok;
1069 return 0;
1070
1071}
1072
1073int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001074nfs3svc_encode_entry(void *cd, const char *name,
1075 int namlen, loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076{
1077 return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
1078}
1079
1080int
NeilBrowna0ad13e2007-01-26 00:57:10 -08001081nfs3svc_encode_entry_plus(void *cd, const char *name,
1082 int namlen, loff_t offset, u64 ino,
1083 unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084{
1085 return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
1086}
1087
1088/* FSSTAT */
1089int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001090nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001092 struct nfsd3_fsstatres *resp = rqstp->rq_resp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 struct kstatfs *s = &resp->stats;
1094 u64 bs = s->f_bsize;
1095
Chuck Levercc028a12020-10-02 15:52:44 -04001096 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 *p++ = xdr_zero; /* no post_op_attr */
1098
1099 if (resp->status == 0) {
1100 p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
1101 p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
1102 p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
1103 p = xdr_encode_hyper(p, s->f_files); /* total inodes */
1104 p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
1105 p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
1106 *p++ = htonl(resp->invarsec); /* mean unchanged time */
1107 }
1108 return xdr_ressize_check(rqstp, p);
1109}
1110
1111/* FSINFO */
1112int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001113nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001115 struct nfsd3_fsinfores *resp = rqstp->rq_resp;
1116
Chuck Levercc028a12020-10-02 15:52:44 -04001117 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 *p++ = xdr_zero; /* no post_op_attr */
1119
1120 if (resp->status == 0) {
1121 *p++ = htonl(resp->f_rtmax);
1122 *p++ = htonl(resp->f_rtpref);
1123 *p++ = htonl(resp->f_rtmult);
1124 *p++ = htonl(resp->f_wtmax);
1125 *p++ = htonl(resp->f_wtpref);
1126 *p++ = htonl(resp->f_wtmult);
1127 *p++ = htonl(resp->f_dtpref);
1128 p = xdr_encode_hyper(p, resp->f_maxfilesize);
1129 *p++ = xdr_one;
1130 *p++ = xdr_zero;
1131 *p++ = htonl(resp->f_properties);
1132 }
1133
1134 return xdr_ressize_check(rqstp, p);
1135}
1136
1137/* PATHCONF */
1138int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001139nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001141 struct nfsd3_pathconfres *resp = rqstp->rq_resp;
1142
Chuck Lever1905cac2020-10-23 10:41:01 -04001143 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 *p++ = xdr_zero; /* no post_op_attr */
1145
1146 if (resp->status == 0) {
1147 *p++ = htonl(resp->p_link_max);
1148 *p++ = htonl(resp->p_name_max);
1149 *p++ = htonl(resp->p_no_trunc);
1150 *p++ = htonl(resp->p_chown_restricted);
1151 *p++ = htonl(resp->p_case_insensitive);
1152 *p++ = htonl(resp->p_case_preserving);
1153 }
1154
1155 return xdr_ressize_check(rqstp, p);
1156}
1157
1158/* COMMIT */
1159int
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001160nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161{
Christoph Hellwig63f8de32017-05-08 19:42:02 +02001162 struct nfsd3_commitres *resp = rqstp->rq_resp;
Stanislav Kinsburskyb9c0ef82012-12-06 14:23:19 +03001163
Chuck Levercc028a12020-10-02 15:52:44 -04001164 *p++ = resp->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 p = encode_wcc_data(rqstp, p, &resp->fh);
1166 /* Write verifier */
1167 if (resp->status == 0) {
Trond Myklebust524ff1a2020-01-06 13:40:36 -05001168 *p++ = resp->verf[0];
1169 *p++ = resp->verf[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 }
1171 return xdr_ressize_check(rqstp, p);
1172}
1173
1174/*
1175 * XDR release functions
1176 */
Christoph Hellwig85374882017-05-08 18:48:24 +02001177void
1178nfs3svc_release_fhandle(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179{
Christoph Hellwig85374882017-05-08 18:48:24 +02001180 struct nfsd3_attrstat *resp = rqstp->rq_resp;
1181
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 fh_put(&resp->fh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183}
1184
Christoph Hellwig85374882017-05-08 18:48:24 +02001185void
1186nfs3svc_release_fhandle2(struct svc_rqst *rqstp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187{
Christoph Hellwig85374882017-05-08 18:48:24 +02001188 struct nfsd3_fhandle_pair *resp = rqstp->rq_resp;
1189
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 fh_put(&resp->fh1);
1191 fh_put(&resp->fh2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192}