1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * XDR support for nfsd/protocol version 3.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/namei.h>
11b9c0ef85SStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
129a74af21SBoaz Harrosh #include "xdr3.h"
132e8138a2SJ. Bruce Fields #include "auth.h"
14b9c0ef85SStanislav Kinsbursky #include "netns.h"
153dadecceSAl Viro #include "vfs.h"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds /*
188b704498SChuck Lever * Force construction of an empty post-op attr
198b704498SChuck Lever */
208b704498SChuck Lever static const struct svc_fh nfs3svc_null_fh = {
218b704498SChuck Lever .fh_no_wcc = true,
228b704498SChuck Lever };
238b704498SChuck Lever
248b704498SChuck Lever /*
250a139d1bSChuck Lever * time_delta. {1, 0} means the server is accurate only
260a139d1bSChuck Lever * to the nearest second.
270a139d1bSChuck Lever */
280a139d1bSChuck Lever static const struct timespec64 nfs3svc_time_delta = {
290a139d1bSChuck Lever .tv_sec = 1,
300a139d1bSChuck Lever .tv_nsec = 0,
310a139d1bSChuck Lever };
320a139d1bSChuck Lever
330a139d1bSChuck Lever /*
341da177e4SLinus Torvalds * Mapping of S_IF* types to NFS file types
351da177e4SLinus Torvalds */
362c42f804SChuck Lever static const u32 nfs3_ftypes[] = {
371da177e4SLinus Torvalds NF3NON, NF3FIFO, NF3CHR, NF3BAD,
381da177e4SLinus Torvalds NF3DIR, NF3BAD, NF3BLK, NF3BAD,
391da177e4SLinus Torvalds NF3REG, NF3BAD, NF3LNK, NF3BAD,
401da177e4SLinus Torvalds NF3SOCK, NF3BAD, NF3LNK, NF3BAD,
411da177e4SLinus Torvalds };
421da177e4SLinus Torvalds
4327c438f5STrond Myklebust
441da177e4SLinus Torvalds /*
459575363aSChuck Lever * Basic NFSv3 data types (RFC 1813 Sections 2.5 and 2.6)
461da177e4SLinus Torvalds */
479575363aSChuck Lever
483ee6f61cSAdrian Bunk static __be32 *
encode_nfstime3(__be32 * p,const struct timespec64 * time)492c42f804SChuck Lever encode_nfstime3(__be32 *p, const struct timespec64 *time)
502c42f804SChuck Lever {
512c42f804SChuck Lever *p++ = cpu_to_be32((u32)time->tv_sec);
522c42f804SChuck Lever *p++ = cpu_to_be32(time->tv_nsec);
532c42f804SChuck Lever
542c42f804SChuck Lever return p;
552c42f804SChuck Lever }
562c42f804SChuck Lever
579cde9360SChuck Lever static bool
svcxdr_decode_nfstime3(struct xdr_stream * xdr,struct timespec64 * timep)589cde9360SChuck Lever svcxdr_decode_nfstime3(struct xdr_stream *xdr, struct timespec64 *timep)
591da177e4SLinus Torvalds {
609cde9360SChuck Lever __be32 *p;
619cde9360SChuck Lever
629cde9360SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT * 2);
639cde9360SChuck Lever if (!p)
649cde9360SChuck Lever return false;
659cde9360SChuck Lever timep->tv_sec = be32_to_cpup(p++);
669cde9360SChuck Lever timep->tv_nsec = be32_to_cpup(p);
679cde9360SChuck Lever
689cde9360SChuck Lever return true;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds
7105027eafSChuck Lever /**
7205027eafSChuck Lever * svcxdr_decode_nfs_fh3 - Decode an NFSv3 file handle
7305027eafSChuck Lever * @xdr: XDR stream positioned at an undecoded NFSv3 FH
7405027eafSChuck Lever * @fhp: OUT: filled-in server file handle
7505027eafSChuck Lever *
7605027eafSChuck Lever * Return values:
7705027eafSChuck Lever * %false: The encoded file handle was not valid
7805027eafSChuck Lever * %true: @fhp has been initialized
7905027eafSChuck Lever */
8005027eafSChuck Lever bool
svcxdr_decode_nfs_fh3(struct xdr_stream * xdr,struct svc_fh * fhp)819575363aSChuck Lever svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp)
829575363aSChuck Lever {
839575363aSChuck Lever __be32 *p;
849575363aSChuck Lever u32 size;
859575363aSChuck Lever
869575363aSChuck Lever if (xdr_stream_decode_u32(xdr, &size) < 0)
879575363aSChuck Lever return false;
889575363aSChuck Lever if (size == 0 || size > NFS3_FHSIZE)
899575363aSChuck Lever return false;
909575363aSChuck Lever p = xdr_inline_decode(xdr, size);
919575363aSChuck Lever if (!p)
929575363aSChuck Lever return false;
939575363aSChuck Lever fh_init(fhp, NFS3_FHSIZE);
949575363aSChuck Lever fhp->fh_handle.fh_size = size;
95d8b26071SNeilBrown memcpy(&fhp->fh_handle.fh_raw, p, size);
969575363aSChuck Lever
979575363aSChuck Lever return true;
989575363aSChuck Lever }
999575363aSChuck Lever
10020798dfeSChuck Lever /**
10120798dfeSChuck Lever * svcxdr_encode_nfsstat3 - Encode an NFSv3 status code
10220798dfeSChuck Lever * @xdr: XDR stream
10320798dfeSChuck Lever * @status: status value to encode
10420798dfeSChuck Lever *
10520798dfeSChuck Lever * Return values:
10620798dfeSChuck Lever * %false: Send buffer space was exhausted
10720798dfeSChuck Lever * %true: Success
10820798dfeSChuck Lever */
10920798dfeSChuck Lever bool
svcxdr_encode_nfsstat3(struct xdr_stream * xdr,__be32 status)1102c42f804SChuck Lever svcxdr_encode_nfsstat3(struct xdr_stream *xdr, __be32 status)
1112c42f804SChuck Lever {
1122c42f804SChuck Lever __be32 *p;
1132c42f804SChuck Lever
1142c42f804SChuck Lever p = xdr_reserve_space(xdr, sizeof(status));
1152c42f804SChuck Lever if (!p)
1162c42f804SChuck Lever return false;
1172c42f804SChuck Lever *p = status;
1182c42f804SChuck Lever
1192c42f804SChuck Lever return true;
1202c42f804SChuck Lever }
1212c42f804SChuck Lever
1225cf35335SChuck Lever static bool
svcxdr_encode_nfs_fh3(struct xdr_stream * xdr,const struct svc_fh * fhp)1235cf35335SChuck Lever svcxdr_encode_nfs_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp)
1245cf35335SChuck Lever {
1255cf35335SChuck Lever u32 size = fhp->fh_handle.fh_size;
1265cf35335SChuck Lever __be32 *p;
1275cf35335SChuck Lever
1285cf35335SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT + size);
1295cf35335SChuck Lever if (!p)
1305cf35335SChuck Lever return false;
1315cf35335SChuck Lever *p++ = cpu_to_be32(size);
1325cf35335SChuck Lever if (size)
1335cf35335SChuck Lever p[XDR_QUADLEN(size) - 1] = 0;
134d8b26071SNeilBrown memcpy(p, &fhp->fh_handle.fh_raw, size);
1355cf35335SChuck Lever
1365cf35335SChuck Lever return true;
1375cf35335SChuck Lever }
1385cf35335SChuck Lever
13978315b36SChuck Lever static bool
svcxdr_encode_post_op_fh3(struct xdr_stream * xdr,const struct svc_fh * fhp)14078315b36SChuck Lever svcxdr_encode_post_op_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp)
14178315b36SChuck Lever {
14278315b36SChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
14378315b36SChuck Lever return false;
14478315b36SChuck Lever if (!svcxdr_encode_nfs_fh3(xdr, fhp))
14578315b36SChuck Lever return false;
14678315b36SChuck Lever
14778315b36SChuck Lever return true;
14878315b36SChuck Lever }
14978315b36SChuck Lever
15054d1d43dSChuck Lever static bool
svcxdr_encode_cookieverf3(struct xdr_stream * xdr,const __be32 * verf)151e4ccfe30SChuck Lever svcxdr_encode_cookieverf3(struct xdr_stream *xdr, const __be32 *verf)
152e4ccfe30SChuck Lever {
153e4ccfe30SChuck Lever __be32 *p;
154e4ccfe30SChuck Lever
155e4ccfe30SChuck Lever p = xdr_reserve_space(xdr, NFS3_COOKIEVERFSIZE);
156e4ccfe30SChuck Lever if (!p)
157e4ccfe30SChuck Lever return false;
158e4ccfe30SChuck Lever memcpy(p, verf, NFS3_COOKIEVERFSIZE);
159e4ccfe30SChuck Lever
160e4ccfe30SChuck Lever return true;
161e4ccfe30SChuck Lever }
162e4ccfe30SChuck Lever
163e4ccfe30SChuck Lever static bool
svcxdr_encode_writeverf3(struct xdr_stream * xdr,const __be32 * verf)164ecb7a085SChuck Lever svcxdr_encode_writeverf3(struct xdr_stream *xdr, const __be32 *verf)
165ecb7a085SChuck Lever {
166ecb7a085SChuck Lever __be32 *p;
167ecb7a085SChuck Lever
168ecb7a085SChuck Lever p = xdr_reserve_space(xdr, NFS3_WRITEVERFSIZE);
169ecb7a085SChuck Lever if (!p)
170ecb7a085SChuck Lever return false;
171ecb7a085SChuck Lever memcpy(p, verf, NFS3_WRITEVERFSIZE);
172ecb7a085SChuck Lever
173ecb7a085SChuck Lever return true;
174ecb7a085SChuck Lever }
175ecb7a085SChuck Lever
176ecb7a085SChuck Lever static bool
svcxdr_decode_filename3(struct xdr_stream * xdr,char ** name,unsigned int * len)17754d1d43dSChuck Lever svcxdr_decode_filename3(struct xdr_stream *xdr, char **name, unsigned int *len)
17854d1d43dSChuck Lever {
17954d1d43dSChuck Lever u32 size, i;
18054d1d43dSChuck Lever __be32 *p;
18154d1d43dSChuck Lever char *c;
18254d1d43dSChuck Lever
18354d1d43dSChuck Lever if (xdr_stream_decode_u32(xdr, &size) < 0)
18454d1d43dSChuck Lever return false;
18554d1d43dSChuck Lever if (size == 0 || size > NFS3_MAXNAMLEN)
18654d1d43dSChuck Lever return false;
18754d1d43dSChuck Lever p = xdr_inline_decode(xdr, size);
18854d1d43dSChuck Lever if (!p)
18954d1d43dSChuck Lever return false;
19054d1d43dSChuck Lever
19154d1d43dSChuck Lever *len = size;
19254d1d43dSChuck Lever *name = (char *)p;
19354d1d43dSChuck Lever for (i = 0, c = *name; i < size; i++, c++) {
19454d1d43dSChuck Lever if (*c == '\0' || *c == '/')
19554d1d43dSChuck Lever return false;
19654d1d43dSChuck Lever }
19754d1d43dSChuck Lever
19854d1d43dSChuck Lever return true;
19954d1d43dSChuck Lever }
20054d1d43dSChuck Lever
20154d1d43dSChuck Lever static bool
svcxdr_decode_diropargs3(struct xdr_stream * xdr,struct svc_fh * fhp,char ** name,unsigned int * len)20254d1d43dSChuck Lever svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp,
20354d1d43dSChuck Lever char **name, unsigned int *len)
20454d1d43dSChuck Lever {
20554d1d43dSChuck Lever return svcxdr_decode_nfs_fh3(xdr, fhp) &&
20654d1d43dSChuck Lever svcxdr_decode_filename3(xdr, name, len);
20754d1d43dSChuck Lever }
20854d1d43dSChuck Lever
2099cde9360SChuck Lever static bool
svcxdr_decode_sattr3(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct iattr * iap)2109cde9360SChuck Lever svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
2119cde9360SChuck Lever struct iattr *iap)
2129cde9360SChuck Lever {
2139cde9360SChuck Lever u32 set_it;
2149cde9360SChuck Lever
2159cde9360SChuck Lever iap->ia_valid = 0;
2169cde9360SChuck Lever
2179cde9360SChuck Lever if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2189cde9360SChuck Lever return false;
2199cde9360SChuck Lever if (set_it) {
2209cde9360SChuck Lever u32 mode;
2219cde9360SChuck Lever
2229cde9360SChuck Lever if (xdr_stream_decode_u32(xdr, &mode) < 0)
2239cde9360SChuck Lever return false;
2249cde9360SChuck Lever iap->ia_valid |= ATTR_MODE;
2259cde9360SChuck Lever iap->ia_mode = mode;
2269cde9360SChuck Lever }
2279cde9360SChuck Lever if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2289cde9360SChuck Lever return false;
2299cde9360SChuck Lever if (set_it) {
2309cde9360SChuck Lever u32 uid;
2319cde9360SChuck Lever
2329cde9360SChuck Lever if (xdr_stream_decode_u32(xdr, &uid) < 0)
2339cde9360SChuck Lever return false;
2349cde9360SChuck Lever iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), uid);
2359cde9360SChuck Lever if (uid_valid(iap->ia_uid))
2369cde9360SChuck Lever iap->ia_valid |= ATTR_UID;
2379cde9360SChuck Lever }
2389cde9360SChuck Lever if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2399cde9360SChuck Lever return false;
2409cde9360SChuck Lever if (set_it) {
2419cde9360SChuck Lever u32 gid;
2429cde9360SChuck Lever
2439cde9360SChuck Lever if (xdr_stream_decode_u32(xdr, &gid) < 0)
2449cde9360SChuck Lever return false;
2459cde9360SChuck Lever iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), gid);
2469cde9360SChuck Lever if (gid_valid(iap->ia_gid))
2479cde9360SChuck Lever iap->ia_valid |= ATTR_GID;
2489cde9360SChuck Lever }
2499cde9360SChuck Lever if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2509cde9360SChuck Lever return false;
2519cde9360SChuck Lever if (set_it) {
2529cde9360SChuck Lever u64 newsize;
2539cde9360SChuck Lever
2549cde9360SChuck Lever if (xdr_stream_decode_u64(xdr, &newsize) < 0)
2559cde9360SChuck Lever return false;
2569cde9360SChuck Lever iap->ia_valid |= ATTR_SIZE;
257a648fdebSChuck Lever iap->ia_size = newsize;
2589cde9360SChuck Lever }
2599cde9360SChuck Lever if (xdr_stream_decode_u32(xdr, &set_it) < 0)
2609cde9360SChuck Lever return false;
2619cde9360SChuck Lever switch (set_it) {
2629cde9360SChuck Lever case DONT_CHANGE:
2639cde9360SChuck Lever break;
2649cde9360SChuck Lever case SET_TO_SERVER_TIME:
2659cde9360SChuck Lever iap->ia_valid |= ATTR_ATIME;
2669cde9360SChuck Lever break;
2679cde9360SChuck Lever case SET_TO_CLIENT_TIME:
2689cde9360SChuck Lever if (!svcxdr_decode_nfstime3(xdr, &iap->ia_atime))
2699cde9360SChuck Lever return false;
2709cde9360SChuck Lever iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
2719cde9360SChuck Lever break;
2729cde9360SChuck Lever default:
2739cde9360SChuck Lever return false;
2749cde9360SChuck Lever }
2759cde9360SChuck Lever if (xdr_stream_decode_u32(xdr, &set_it) < 0)
2769cde9360SChuck Lever return false;
2779cde9360SChuck Lever switch (set_it) {
2789cde9360SChuck Lever case DONT_CHANGE:
2799cde9360SChuck Lever break;
2809cde9360SChuck Lever case SET_TO_SERVER_TIME:
2819cde9360SChuck Lever iap->ia_valid |= ATTR_MTIME;
2829cde9360SChuck Lever break;
2839cde9360SChuck Lever case SET_TO_CLIENT_TIME:
2849cde9360SChuck Lever if (!svcxdr_decode_nfstime3(xdr, &iap->ia_mtime))
2859cde9360SChuck Lever return false;
2869cde9360SChuck Lever iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
2879cde9360SChuck Lever break;
2889cde9360SChuck Lever default:
2899cde9360SChuck Lever return false;
2909cde9360SChuck Lever }
2919cde9360SChuck Lever
2929cde9360SChuck Lever return true;
2939cde9360SChuck Lever }
2949cde9360SChuck Lever
2959cde9360SChuck Lever static bool
svcxdr_decode_sattrguard3(struct xdr_stream * xdr,struct nfsd3_sattrargs * args)2969cde9360SChuck Lever svcxdr_decode_sattrguard3(struct xdr_stream *xdr, struct nfsd3_sattrargs *args)
2979cde9360SChuck Lever {
2989cde9360SChuck Lever __be32 *p;
2999cde9360SChuck Lever u32 check;
3009cde9360SChuck Lever
3019cde9360SChuck Lever if (xdr_stream_decode_bool(xdr, &check) < 0)
3029cde9360SChuck Lever return false;
3039cde9360SChuck Lever if (check) {
3049cde9360SChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT * 2);
3059cde9360SChuck Lever if (!p)
3069cde9360SChuck Lever return false;
3079cde9360SChuck Lever args->check_guard = 1;
3089cde9360SChuck Lever args->guardtime = be32_to_cpup(p);
3099cde9360SChuck Lever } else
3109cde9360SChuck Lever args->check_guard = 0;
3119cde9360SChuck Lever
3129cde9360SChuck Lever return true;
3139cde9360SChuck Lever }
3149cde9360SChuck Lever
315f8a38e2dSChuck Lever static bool
svcxdr_decode_specdata3(struct xdr_stream * xdr,struct nfsd3_mknodargs * args)316f8a38e2dSChuck Lever svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args)
3171da177e4SLinus Torvalds {
318f8a38e2dSChuck Lever __be32 *p;
3191da177e4SLinus Torvalds
320f8a38e2dSChuck Lever p = xdr_inline_decode(xdr, XDR_UNIT * 2);
321f8a38e2dSChuck Lever if (!p)
322f8a38e2dSChuck Lever return false;
323f8a38e2dSChuck Lever args->major = be32_to_cpup(p++);
324f8a38e2dSChuck Lever args->minor = be32_to_cpup(p);
3251da177e4SLinus Torvalds
326f8a38e2dSChuck Lever return true;
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds
329f8a38e2dSChuck Lever static bool
svcxdr_decode_devicedata3(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct nfsd3_mknodargs * args)330f8a38e2dSChuck Lever svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
331f8a38e2dSChuck Lever struct nfsd3_mknodargs *args)
332f8a38e2dSChuck Lever {
333f8a38e2dSChuck Lever return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
334f8a38e2dSChuck Lever svcxdr_decode_specdata3(xdr, args);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds
3372c42f804SChuck Lever static bool
svcxdr_encode_fattr3(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp,const struct kstat * stat)3382c42f804SChuck Lever svcxdr_encode_fattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
3392c42f804SChuck Lever const struct svc_fh *fhp, const struct kstat *stat)
3402c42f804SChuck Lever {
3412c42f804SChuck Lever struct user_namespace *userns = nfsd_user_namespace(rqstp);
3422c42f804SChuck Lever __be32 *p;
3432c42f804SChuck Lever u64 fsid;
3442c42f804SChuck Lever
3452c42f804SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 21);
3462c42f804SChuck Lever if (!p)
3472c42f804SChuck Lever return false;
3482c42f804SChuck Lever
3492c42f804SChuck Lever *p++ = cpu_to_be32(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
3502c42f804SChuck Lever *p++ = cpu_to_be32((u32)(stat->mode & S_IALLUGO));
3512c42f804SChuck Lever *p++ = cpu_to_be32((u32)stat->nlink);
3522c42f804SChuck Lever *p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
3532c42f804SChuck Lever *p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
3542c42f804SChuck Lever if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN)
3552c42f804SChuck Lever p = xdr_encode_hyper(p, (u64)NFS3_MAXPATHLEN);
3562c42f804SChuck Lever else
3572c42f804SChuck Lever p = xdr_encode_hyper(p, (u64)stat->size);
3582c42f804SChuck Lever
3592c42f804SChuck Lever /* used */
3602c42f804SChuck Lever p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
3612c42f804SChuck Lever
3622c42f804SChuck Lever /* rdev */
3632c42f804SChuck Lever *p++ = cpu_to_be32((u32)MAJOR(stat->rdev));
3642c42f804SChuck Lever *p++ = cpu_to_be32((u32)MINOR(stat->rdev));
3652c42f804SChuck Lever
3662c42f804SChuck Lever switch(fsid_source(fhp)) {
3672c42f804SChuck Lever case FSIDSOURCE_FSID:
3682c42f804SChuck Lever fsid = (u64)fhp->fh_export->ex_fsid;
3692c42f804SChuck Lever break;
3702c42f804SChuck Lever case FSIDSOURCE_UUID:
3712c42f804SChuck Lever fsid = ((u64 *)fhp->fh_export->ex_uuid)[0];
3722c42f804SChuck Lever fsid ^= ((u64 *)fhp->fh_export->ex_uuid)[1];
3732c42f804SChuck Lever break;
3742c42f804SChuck Lever default:
3752c42f804SChuck Lever fsid = (u64)huge_encode_dev(fhp->fh_dentry->d_sb->s_dev);
3762c42f804SChuck Lever }
3772c42f804SChuck Lever p = xdr_encode_hyper(p, fsid);
3782c42f804SChuck Lever
3792c42f804SChuck Lever /* fileid */
3802c42f804SChuck Lever p = xdr_encode_hyper(p, stat->ino);
3812c42f804SChuck Lever
3822c42f804SChuck Lever p = encode_nfstime3(p, &stat->atime);
3832c42f804SChuck Lever p = encode_nfstime3(p, &stat->mtime);
3842c42f804SChuck Lever encode_nfstime3(p, &stat->ctime);
3852c42f804SChuck Lever
3862c42f804SChuck Lever return true;
3872c42f804SChuck Lever }
3882c42f804SChuck Lever
389907c3822SChuck Lever static bool
svcxdr_encode_wcc_attr(struct xdr_stream * xdr,const struct svc_fh * fhp)39070f8e839SChuck Lever svcxdr_encode_wcc_attr(struct xdr_stream *xdr, const struct svc_fh *fhp)
39170f8e839SChuck Lever {
39270f8e839SChuck Lever __be32 *p;
39370f8e839SChuck Lever
39470f8e839SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 6);
39570f8e839SChuck Lever if (!p)
39670f8e839SChuck Lever return false;
39770f8e839SChuck Lever p = xdr_encode_hyper(p, (u64)fhp->fh_pre_size);
39870f8e839SChuck Lever p = encode_nfstime3(p, &fhp->fh_pre_mtime);
39970f8e839SChuck Lever encode_nfstime3(p, &fhp->fh_pre_ctime);
40070f8e839SChuck Lever
40170f8e839SChuck Lever return true;
40270f8e839SChuck Lever }
40370f8e839SChuck Lever
40470f8e839SChuck Lever static bool
svcxdr_encode_pre_op_attr(struct xdr_stream * xdr,const struct svc_fh * fhp)40570f8e839SChuck Lever svcxdr_encode_pre_op_attr(struct xdr_stream *xdr, const struct svc_fh *fhp)
40670f8e839SChuck Lever {
40770f8e839SChuck Lever if (!fhp->fh_pre_saved) {
40870f8e839SChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
40970f8e839SChuck Lever return false;
41070f8e839SChuck Lever return true;
41170f8e839SChuck Lever }
41270f8e839SChuck Lever
41370f8e839SChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
41470f8e839SChuck Lever return false;
41570f8e839SChuck Lever return svcxdr_encode_wcc_attr(xdr, fhp);
41670f8e839SChuck Lever }
41770f8e839SChuck Lever
41820798dfeSChuck Lever /**
41920798dfeSChuck Lever * svcxdr_encode_post_op_attr - Encode NFSv3 post-op attributes
42020798dfeSChuck Lever * @rqstp: Context of a completed RPC transaction
42120798dfeSChuck Lever * @xdr: XDR stream
42220798dfeSChuck Lever * @fhp: File handle to encode
42320798dfeSChuck Lever *
42420798dfeSChuck Lever * Return values:
42520798dfeSChuck Lever * %false: Send buffer space was exhausted
42620798dfeSChuck Lever * %true: Success
42720798dfeSChuck Lever */
42820798dfeSChuck Lever bool
svcxdr_encode_post_op_attr(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp)429907c3822SChuck Lever svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
430907c3822SChuck Lever const struct svc_fh *fhp)
431907c3822SChuck Lever {
432907c3822SChuck Lever struct dentry *dentry = fhp->fh_dentry;
433907c3822SChuck Lever struct kstat stat;
434907c3822SChuck Lever
435907c3822SChuck Lever /*
436907c3822SChuck Lever * The inode may be NULL if the call failed because of a
437907c3822SChuck Lever * stale file handle. In this case, no attributes are
438907c3822SChuck Lever * returned.
439907c3822SChuck Lever */
440907c3822SChuck Lever if (fhp->fh_no_wcc || !dentry || !d_really_is_positive(dentry))
441907c3822SChuck Lever goto no_post_op_attrs;
442907c3822SChuck Lever if (fh_getattr(fhp, &stat) != nfs_ok)
443907c3822SChuck Lever goto no_post_op_attrs;
444907c3822SChuck Lever
445907c3822SChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
446907c3822SChuck Lever return false;
447907c3822SChuck Lever lease_get_mtime(d_inode(dentry), &stat.mtime);
448907c3822SChuck Lever if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, &stat))
449907c3822SChuck Lever return false;
450907c3822SChuck Lever
451907c3822SChuck Lever return true;
452907c3822SChuck Lever
453907c3822SChuck Lever no_post_op_attrs:
454907c3822SChuck Lever return xdr_stream_encode_item_absent(xdr) > 0;
455907c3822SChuck Lever }
456907c3822SChuck Lever
4571da177e4SLinus Torvalds /*
45870f8e839SChuck Lever * Encode weak cache consistency data
45970f8e839SChuck Lever */
46070f8e839SChuck Lever static bool
svcxdr_encode_wcc_data(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp)46170f8e839SChuck Lever svcxdr_encode_wcc_data(struct svc_rqst *rqstp, struct xdr_stream *xdr,
46270f8e839SChuck Lever const struct svc_fh *fhp)
46370f8e839SChuck Lever {
46470f8e839SChuck Lever struct dentry *dentry = fhp->fh_dentry;
46570f8e839SChuck Lever
46670f8e839SChuck Lever if (!dentry || !d_really_is_positive(dentry) || !fhp->fh_post_saved)
46770f8e839SChuck Lever goto neither;
46870f8e839SChuck Lever
46970f8e839SChuck Lever /* before */
47070f8e839SChuck Lever if (!svcxdr_encode_pre_op_attr(xdr, fhp))
47170f8e839SChuck Lever return false;
47270f8e839SChuck Lever
47370f8e839SChuck Lever /* after */
47470f8e839SChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
47570f8e839SChuck Lever return false;
47670f8e839SChuck Lever if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, &fhp->fh_post_attr))
47770f8e839SChuck Lever return false;
47870f8e839SChuck Lever
47970f8e839SChuck Lever return true;
48070f8e839SChuck Lever
48170f8e839SChuck Lever neither:
48270f8e839SChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
48370f8e839SChuck Lever return false;
48470f8e839SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp))
48570f8e839SChuck Lever return false;
48670f8e839SChuck Lever
48770f8e839SChuck Lever return true;
48870f8e839SChuck Lever }
48970f8e839SChuck Lever
49040ee5dc6SPeter Staubach /*
4911da177e4SLinus Torvalds * XDR decode functions
4921da177e4SLinus Torvalds */
493dcc46991SChuck Lever
494c44b31c2SChuck Lever bool
nfs3svc_decode_fhandleargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)49516c66364SChuck Lever nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4961da177e4SLinus Torvalds {
497026fec7eSChristoph Hellwig struct nfsd_fhandle *args = rqstp->rq_argp;
498026fec7eSChristoph Hellwig
4999575363aSChuck Lever return svcxdr_decode_nfs_fh3(xdr, &args->fh);
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds
502c44b31c2SChuck Lever bool
nfs3svc_decode_sattrargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)50316c66364SChuck Lever nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5041da177e4SLinus Torvalds {
505026fec7eSChristoph Hellwig struct nfsd3_sattrargs *args = rqstp->rq_argp;
506026fec7eSChristoph Hellwig
5079cde9360SChuck Lever return svcxdr_decode_nfs_fh3(xdr, &args->fh) &&
5089cde9360SChuck Lever svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
5099cde9360SChuck Lever svcxdr_decode_sattrguard3(xdr, args);
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds
512c44b31c2SChuck Lever bool
nfs3svc_decode_diropargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)51316c66364SChuck Lever nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5141da177e4SLinus Torvalds {
515026fec7eSChristoph Hellwig struct nfsd3_diropargs *args = rqstp->rq_argp;
516026fec7eSChristoph Hellwig
51754d1d43dSChuck Lever return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds
520c44b31c2SChuck Lever bool
nfs3svc_decode_accessargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)52116c66364SChuck Lever nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5221da177e4SLinus Torvalds {
523026fec7eSChristoph Hellwig struct nfsd3_accessargs *args = rqstp->rq_argp;
524026fec7eSChristoph Hellwig
5253b921a2bSChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
526c44b31c2SChuck Lever return false;
5273b921a2bSChuck Lever if (xdr_stream_decode_u32(xdr, &args->access) < 0)
528c44b31c2SChuck Lever return false;
5291da177e4SLinus Torvalds
530c44b31c2SChuck Lever return true;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds
533c44b31c2SChuck Lever bool
nfs3svc_decode_readargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)53416c66364SChuck Lever nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5351da177e4SLinus Torvalds {
536026fec7eSChristoph Hellwig struct nfsd3_readargs *args = rqstp->rq_argp;
5371da177e4SLinus Torvalds
538be63bd2aSChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
539c44b31c2SChuck Lever return false;
540be63bd2aSChuck Lever if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
541c44b31c2SChuck Lever return false;
542be63bd2aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
543c44b31c2SChuck Lever return false;
5449512a16bSJ. Bruce Fields
545c44b31c2SChuck Lever return true;
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds
548c44b31c2SChuck Lever bool
nfs3svc_decode_writeargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)54916c66364SChuck Lever nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5501da177e4SLinus Torvalds {
551026fec7eSChristoph Hellwig struct nfsd3_writeargs *args = rqstp->rq_argp;
5527adae489SGreg Banks u32 max_blocksize = svc_max_payload(rqstp);
5531da177e4SLinus Torvalds
554c43b2f22SChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
555c44b31c2SChuck Lever return false;
556c43b2f22SChuck Lever if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
557c44b31c2SChuck Lever return false;
558c43b2f22SChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
559c44b31c2SChuck Lever return false;
560c43b2f22SChuck Lever if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
561c44b31c2SChuck Lever return false;
5621da177e4SLinus Torvalds
563c43b2f22SChuck Lever /* opaque data */
564c43b2f22SChuck Lever if (xdr_stream_decode_u32(xdr, &args->len) < 0)
565c44b31c2SChuck Lever return false;
566c43b2f22SChuck Lever
567c43b2f22SChuck Lever /* request sanity */
568f34b9568SPeter Staubach if (args->count != args->len)
569c44b31c2SChuck Lever return false;
570f34b9568SPeter Staubach if (args->count > max_blocksize) {
571f34b9568SPeter Staubach args->count = max_blocksize;
572c43b2f22SChuck Lever args->len = max_blocksize;
573f34b9568SPeter Staubach }
574c43b2f22SChuck Lever
575d4da5baaSChuck Lever return xdr_stream_subsegment(xdr, &args->payload, args->count);
5761da177e4SLinus Torvalds }
5771da177e4SLinus Torvalds
578c44b31c2SChuck Lever bool
nfs3svc_decode_createargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)57916c66364SChuck Lever nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5801da177e4SLinus Torvalds {
581026fec7eSChristoph Hellwig struct nfsd3_createargs *args = rqstp->rq_argp;
582026fec7eSChristoph Hellwig
5836b3a1196SChuck Lever if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
584c44b31c2SChuck Lever return false;
5856b3a1196SChuck Lever if (xdr_stream_decode_u32(xdr, &args->createmode) < 0)
586c44b31c2SChuck Lever return false;
5876b3a1196SChuck Lever switch (args->createmode) {
5881da177e4SLinus Torvalds case NFS3_CREATE_UNCHECKED:
5891da177e4SLinus Torvalds case NFS3_CREATE_GUARDED:
5906b3a1196SChuck Lever return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
5911da177e4SLinus Torvalds case NFS3_CREATE_EXCLUSIVE:
5926b3a1196SChuck Lever args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE);
5936b3a1196SChuck Lever if (!args->verf)
594c44b31c2SChuck Lever return false;
5951da177e4SLinus Torvalds break;
5961da177e4SLinus Torvalds default:
597c44b31c2SChuck Lever return false;
5981da177e4SLinus Torvalds }
599c44b31c2SChuck Lever return true;
6001da177e4SLinus Torvalds }
601026fec7eSChristoph Hellwig
602c44b31c2SChuck Lever bool
nfs3svc_decode_mkdirargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)60316c66364SChuck Lever nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6041da177e4SLinus Torvalds {
605026fec7eSChristoph Hellwig struct nfsd3_createargs *args = rqstp->rq_argp;
606026fec7eSChristoph Hellwig
60783374c27SChuck Lever return svcxdr_decode_diropargs3(xdr, &args->fh,
60883374c27SChuck Lever &args->name, &args->len) &&
60983374c27SChuck Lever svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds
612c44b31c2SChuck Lever bool
nfs3svc_decode_symlinkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)61316c66364SChuck Lever nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6141da177e4SLinus Torvalds {
615026fec7eSChristoph Hellwig struct nfsd3_symlinkargs *args = rqstp->rq_argp;
616da392016SChuck Lever struct kvec *head = rqstp->rq_arg.head;
6171da177e4SLinus Torvalds
618da392016SChuck Lever if (!svcxdr_decode_diropargs3(xdr, &args->ffh, &args->fname, &args->flen))
619c44b31c2SChuck Lever return false;
620da392016SChuck Lever if (!svcxdr_decode_sattr3(rqstp, xdr, &args->attrs))
621c44b31c2SChuck Lever return false;
622da392016SChuck Lever if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
623c44b31c2SChuck Lever return false;
624da392016SChuck Lever
625c3d2a04fSChuck Lever /* symlink_data */
626da392016SChuck Lever args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
627c3d2a04fSChuck Lever args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
628c3d2a04fSChuck Lever return args->first.iov_base != NULL;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds
631c44b31c2SChuck Lever bool
nfs3svc_decode_mknodargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)63216c66364SChuck Lever nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6331da177e4SLinus Torvalds {
634026fec7eSChristoph Hellwig struct nfsd3_mknodargs *args = rqstp->rq_argp;
635026fec7eSChristoph Hellwig
636f8a38e2dSChuck Lever if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
637c44b31c2SChuck Lever return false;
638f8a38e2dSChuck Lever if (xdr_stream_decode_u32(xdr, &args->ftype) < 0)
639c44b31c2SChuck Lever return false;
640f8a38e2dSChuck Lever switch (args->ftype) {
641f8a38e2dSChuck Lever case NF3CHR:
642f8a38e2dSChuck Lever case NF3BLK:
643f8a38e2dSChuck Lever return svcxdr_decode_devicedata3(rqstp, xdr, args);
644f8a38e2dSChuck Lever case NF3SOCK:
645f8a38e2dSChuck Lever case NF3FIFO:
646f8a38e2dSChuck Lever return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
647f8a38e2dSChuck Lever case NF3REG:
648f8a38e2dSChuck Lever case NF3DIR:
649f8a38e2dSChuck Lever case NF3LNK:
650f8a38e2dSChuck Lever /* Valid XDR but illegal file types */
651f8a38e2dSChuck Lever break;
652f8a38e2dSChuck Lever default:
653c44b31c2SChuck Lever return false;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds
656c44b31c2SChuck Lever return true;
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds
659c44b31c2SChuck Lever bool
nfs3svc_decode_renameargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)66016c66364SChuck Lever nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6611da177e4SLinus Torvalds {
662026fec7eSChristoph Hellwig struct nfsd3_renameargs *args = rqstp->rq_argp;
663026fec7eSChristoph Hellwig
664d181e0a4SChuck Lever return svcxdr_decode_diropargs3(xdr, &args->ffh,
665d181e0a4SChuck Lever &args->fname, &args->flen) &&
666d181e0a4SChuck Lever svcxdr_decode_diropargs3(xdr, &args->tfh,
667d181e0a4SChuck Lever &args->tname, &args->tlen);
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds
670c44b31c2SChuck Lever bool
nfs3svc_decode_linkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)67116c66364SChuck Lever nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6721da177e4SLinus Torvalds {
673026fec7eSChristoph Hellwig struct nfsd3_linkargs *args = rqstp->rq_argp;
674026fec7eSChristoph Hellwig
675efaa1e7cSChuck Lever return svcxdr_decode_nfs_fh3(xdr, &args->ffh) &&
676efaa1e7cSChuck Lever svcxdr_decode_diropargs3(xdr, &args->tfh,
677efaa1e7cSChuck Lever &args->tname, &args->tlen);
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds
680c44b31c2SChuck Lever bool
nfs3svc_decode_readdirargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)68116c66364SChuck Lever nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6821da177e4SLinus Torvalds {
683026fec7eSChristoph Hellwig struct nfsd3_readdirargs *args = rqstp->rq_argp;
684f875a792SNeilBrown
6859cedc2e6SChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
686c44b31c2SChuck Lever return false;
6879cedc2e6SChuck Lever if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
688c44b31c2SChuck Lever return false;
6899cedc2e6SChuck Lever args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
6909cedc2e6SChuck Lever if (!args->verf)
691c44b31c2SChuck Lever return false;
6929cedc2e6SChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
693c44b31c2SChuck Lever return false;
6941da177e4SLinus Torvalds
695c44b31c2SChuck Lever return true;
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds
698c44b31c2SChuck Lever bool
nfs3svc_decode_readdirplusargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)69916c66364SChuck Lever nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7001da177e4SLinus Torvalds {
701026fec7eSChristoph Hellwig struct nfsd3_readdirargs *args = rqstp->rq_argp;
7029cedc2e6SChuck Lever u32 dircount;
7031da177e4SLinus Torvalds
7049cedc2e6SChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
705c44b31c2SChuck Lever return false;
7069cedc2e6SChuck Lever if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
707c44b31c2SChuck Lever return false;
7089cedc2e6SChuck Lever args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
7099cedc2e6SChuck Lever if (!args->verf)
710c44b31c2SChuck Lever return false;
7119cedc2e6SChuck Lever /* dircount is ignored */
7129cedc2e6SChuck Lever if (xdr_stream_decode_u32(xdr, &dircount) < 0)
713c44b31c2SChuck Lever return false;
7149cedc2e6SChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
715c44b31c2SChuck Lever return false;
7161da177e4SLinus Torvalds
717c44b31c2SChuck Lever return true;
7181da177e4SLinus Torvalds }
7191da177e4SLinus Torvalds
720c44b31c2SChuck Lever bool
nfs3svc_decode_commitargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)72116c66364SChuck Lever nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7221da177e4SLinus Torvalds {
723026fec7eSChristoph Hellwig struct nfsd3_commitargs *args = rqstp->rq_argp;
7241da177e4SLinus Torvalds
725c8d26a0aSChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
726c44b31c2SChuck Lever return false;
727c8d26a0aSChuck Lever if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
728c44b31c2SChuck Lever return false;
729c8d26a0aSChuck Lever if (xdr_stream_decode_u32(xdr, &args->count) < 0)
730c44b31c2SChuck Lever return false;
731c8d26a0aSChuck Lever
732c44b31c2SChuck Lever return true;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds
7351da177e4SLinus Torvalds /*
7361da177e4SLinus Torvalds * XDR encode functions
7371da177e4SLinus Torvalds */
738cc028a10SChuck Lever
7391da177e4SLinus Torvalds /* GETATTR */
740130e2054SChuck Lever bool
nfs3svc_encode_getattrres(struct svc_rqst * rqstp,struct xdr_stream * xdr)741fda49441SChuck Lever nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7421da177e4SLinus Torvalds {
74363f8de37SChristoph Hellwig struct nfsd3_attrstat *resp = rqstp->rq_resp;
74463f8de37SChristoph Hellwig
7452c42f804SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
746130e2054SChuck Lever return false;
7472c42f804SChuck Lever switch (resp->status) {
7482c42f804SChuck Lever case nfs_ok:
7492c42f804SChuck Lever lease_get_mtime(d_inode(resp->fh.fh_dentry), &resp->stat.mtime);
7502c42f804SChuck Lever if (!svcxdr_encode_fattr3(rqstp, xdr, &resp->fh, &resp->stat))
751130e2054SChuck Lever return false;
7522c42f804SChuck Lever break;
75340ee5dc6SPeter Staubach }
7542c42f804SChuck Lever
755130e2054SChuck Lever return true;
7561da177e4SLinus Torvalds }
7571da177e4SLinus Torvalds
7581da177e4SLinus Torvalds /* SETATTR, REMOVE, RMDIR */
759130e2054SChuck Lever bool
nfs3svc_encode_wccstat(struct svc_rqst * rqstp,struct xdr_stream * xdr)760fda49441SChuck Lever nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7611da177e4SLinus Torvalds {
76263f8de37SChristoph Hellwig struct nfsd3_attrstat *resp = rqstp->rq_resp;
76363f8de37SChristoph Hellwig
76470f8e839SChuck Lever return svcxdr_encode_nfsstat3(xdr, resp->status) &&
76570f8e839SChuck Lever svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds
7681da177e4SLinus Torvalds /* LOOKUP */
769130e2054SChuck Lever bool
nfs3svc_encode_lookupres(struct svc_rqst * rqstp,struct xdr_stream * xdr)770fda49441SChuck Lever nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7711da177e4SLinus Torvalds {
77263f8de37SChristoph Hellwig struct nfsd3_diropres *resp = rqstp->rq_resp;
77363f8de37SChristoph Hellwig
7745cf35335SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
775130e2054SChuck Lever return false;
7765cf35335SChuck Lever switch (resp->status) {
7775cf35335SChuck Lever case nfs_ok:
7785cf35335SChuck Lever if (!svcxdr_encode_nfs_fh3(xdr, &resp->fh))
779130e2054SChuck Lever return false;
7805cf35335SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
781130e2054SChuck Lever return false;
7825cf35335SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
783130e2054SChuck Lever return false;
7845cf35335SChuck Lever break;
7855cf35335SChuck Lever default:
7865cf35335SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
787130e2054SChuck Lever return false;
7881da177e4SLinus Torvalds }
7895cf35335SChuck Lever
790130e2054SChuck Lever return true;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds
7931da177e4SLinus Torvalds /* ACCESS */
794130e2054SChuck Lever bool
nfs3svc_encode_accessres(struct svc_rqst * rqstp,struct xdr_stream * xdr)795fda49441SChuck Lever nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7961da177e4SLinus Torvalds {
79763f8de37SChristoph Hellwig struct nfsd3_accessres *resp = rqstp->rq_resp;
79863f8de37SChristoph Hellwig
799907c3822SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
800130e2054SChuck Lever return false;
801907c3822SChuck Lever switch (resp->status) {
802907c3822SChuck Lever case nfs_ok:
803907c3822SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
804130e2054SChuck Lever return false;
805907c3822SChuck Lever if (xdr_stream_encode_u32(xdr, resp->access) < 0)
806130e2054SChuck Lever return false;
807907c3822SChuck Lever break;
808907c3822SChuck Lever default:
809907c3822SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
810130e2054SChuck Lever return false;
811907c3822SChuck Lever }
812907c3822SChuck Lever
813130e2054SChuck Lever return true;
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds
8161da177e4SLinus Torvalds /* READLINK */
817130e2054SChuck Lever bool
nfs3svc_encode_readlinkres(struct svc_rqst * rqstp,struct xdr_stream * xdr)818fda49441SChuck Lever nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8191da177e4SLinus Torvalds {
82063f8de37SChristoph Hellwig struct nfsd3_readlinkres *resp = rqstp->rq_resp;
82176e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head;
82263f8de37SChristoph Hellwig
8239a9c8923SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
824130e2054SChuck Lever return false;
8259a9c8923SChuck Lever switch (resp->status) {
8269a9c8923SChuck Lever case nfs_ok:
8279a9c8923SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
828130e2054SChuck Lever return false;
8299a9c8923SChuck Lever if (xdr_stream_encode_u32(xdr, resp->len) < 0)
830130e2054SChuck Lever return false;
831*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages, 0,
832*82078b98SChuck Lever resp->len);
8339a9c8923SChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
834130e2054SChuck Lever return false;
8359a9c8923SChuck Lever break;
8369a9c8923SChuck Lever default:
8379a9c8923SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
838130e2054SChuck Lever return false;
8399a9c8923SChuck Lever }
8409a9c8923SChuck Lever
841130e2054SChuck Lever return true;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds /* READ */
845130e2054SChuck Lever bool
nfs3svc_encode_readres(struct svc_rqst * rqstp,struct xdr_stream * xdr)846fda49441SChuck Lever nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8471da177e4SLinus Torvalds {
84863f8de37SChristoph Hellwig struct nfsd3_readres *resp = rqstp->rq_resp;
84976e5492bSChuck Lever struct kvec *head = rqstp->rq_res.head;
85063f8de37SChristoph Hellwig
851cc9bcdadSChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
852130e2054SChuck Lever return false;
853cc9bcdadSChuck Lever switch (resp->status) {
854cc9bcdadSChuck Lever case nfs_ok:
855cc9bcdadSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
856130e2054SChuck Lever return false;
857cc9bcdadSChuck Lever if (xdr_stream_encode_u32(xdr, resp->count) < 0)
858130e2054SChuck Lever return false;
859cc9bcdadSChuck Lever if (xdr_stream_encode_bool(xdr, resp->eof) < 0)
860130e2054SChuck Lever return false;
861cc9bcdadSChuck Lever if (xdr_stream_encode_u32(xdr, resp->count) < 0)
862130e2054SChuck Lever return false;
863*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
864*82078b98SChuck Lever rqstp->rq_res.page_base,
865cc9bcdadSChuck Lever resp->count);
866cc9bcdadSChuck Lever if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
867130e2054SChuck Lever return false;
868cc9bcdadSChuck Lever break;
869cc9bcdadSChuck Lever default:
870cc9bcdadSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
871130e2054SChuck Lever return false;
872cc9bcdadSChuck Lever }
873cc9bcdadSChuck Lever
874130e2054SChuck Lever return true;
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds
8771da177e4SLinus Torvalds /* WRITE */
878130e2054SChuck Lever bool
nfs3svc_encode_writeres(struct svc_rqst * rqstp,struct xdr_stream * xdr)879fda49441SChuck Lever nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8801da177e4SLinus Torvalds {
88163f8de37SChristoph Hellwig struct nfsd3_writeres *resp = rqstp->rq_resp;
882b9c0ef85SStanislav Kinsbursky
883ecb7a085SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
884130e2054SChuck Lever return false;
885ecb7a085SChuck Lever switch (resp->status) {
886ecb7a085SChuck Lever case nfs_ok:
887ecb7a085SChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
888130e2054SChuck Lever return false;
889ecb7a085SChuck Lever if (xdr_stream_encode_u32(xdr, resp->count) < 0)
890130e2054SChuck Lever return false;
891ecb7a085SChuck Lever if (xdr_stream_encode_u32(xdr, resp->committed) < 0)
892130e2054SChuck Lever return false;
893ecb7a085SChuck Lever if (!svcxdr_encode_writeverf3(xdr, resp->verf))
894130e2054SChuck Lever return false;
895ecb7a085SChuck Lever break;
896ecb7a085SChuck Lever default:
897ecb7a085SChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
898130e2054SChuck Lever return false;
8991da177e4SLinus Torvalds }
900ecb7a085SChuck Lever
901130e2054SChuck Lever return true;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds
9041da177e4SLinus Torvalds /* CREATE, MKDIR, SYMLINK, MKNOD */
905130e2054SChuck Lever bool
nfs3svc_encode_createres(struct svc_rqst * rqstp,struct xdr_stream * xdr)906fda49441SChuck Lever nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9071da177e4SLinus Torvalds {
90863f8de37SChristoph Hellwig struct nfsd3_diropres *resp = rqstp->rq_resp;
90963f8de37SChristoph Hellwig
91078315b36SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
911130e2054SChuck Lever return false;
91278315b36SChuck Lever switch (resp->status) {
91378315b36SChuck Lever case nfs_ok:
91478315b36SChuck Lever if (!svcxdr_encode_post_op_fh3(xdr, &resp->fh))
915130e2054SChuck Lever return false;
91678315b36SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
917130e2054SChuck Lever return false;
91878315b36SChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
919130e2054SChuck Lever return false;
92078315b36SChuck Lever break;
92178315b36SChuck Lever default:
92278315b36SChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
923130e2054SChuck Lever return false;
9241da177e4SLinus Torvalds }
92578315b36SChuck Lever
926130e2054SChuck Lever return true;
9271da177e4SLinus Torvalds }
9281da177e4SLinus Torvalds
9291da177e4SLinus Torvalds /* RENAME */
930130e2054SChuck Lever bool
nfs3svc_encode_renameres(struct svc_rqst * rqstp,struct xdr_stream * xdr)931fda49441SChuck Lever nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9321da177e4SLinus Torvalds {
93363f8de37SChristoph Hellwig struct nfsd3_renameres *resp = rqstp->rq_resp;
93463f8de37SChristoph Hellwig
93589d79e96SChuck Lever return svcxdr_encode_nfsstat3(xdr, resp->status) &&
93689d79e96SChuck Lever svcxdr_encode_wcc_data(rqstp, xdr, &resp->ffh) &&
93789d79e96SChuck Lever svcxdr_encode_wcc_data(rqstp, xdr, &resp->tfh);
9381da177e4SLinus Torvalds }
9391da177e4SLinus Torvalds
9401da177e4SLinus Torvalds /* LINK */
941130e2054SChuck Lever bool
nfs3svc_encode_linkres(struct svc_rqst * rqstp,struct xdr_stream * xdr)942fda49441SChuck Lever nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9431da177e4SLinus Torvalds {
94463f8de37SChristoph Hellwig struct nfsd3_linkres *resp = rqstp->rq_resp;
94563f8de37SChristoph Hellwig
9464d74380aSChuck Lever return svcxdr_encode_nfsstat3(xdr, resp->status) &&
9474d74380aSChuck Lever svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh) &&
9484d74380aSChuck Lever svcxdr_encode_wcc_data(rqstp, xdr, &resp->tfh);
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds
9511da177e4SLinus Torvalds /* READDIR */
952130e2054SChuck Lever bool
nfs3svc_encode_readdirres(struct svc_rqst * rqstp,struct xdr_stream * xdr)953fda49441SChuck Lever nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9541da177e4SLinus Torvalds {
95563f8de37SChristoph Hellwig struct nfsd3_readdirres *resp = rqstp->rq_resp;
9567f87fc2dSChuck Lever struct xdr_buf *dirlist = &resp->dirlist;
95763f8de37SChristoph Hellwig
958e4ccfe30SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
959130e2054SChuck Lever return false;
960e4ccfe30SChuck Lever switch (resp->status) {
961e4ccfe30SChuck Lever case nfs_ok:
962e4ccfe30SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
963130e2054SChuck Lever return false;
964e4ccfe30SChuck Lever if (!svcxdr_encode_cookieverf3(xdr, resp->verf))
965130e2054SChuck Lever return false;
966*82078b98SChuck Lever svcxdr_encode_opaque_pages(rqstp, xdr, dirlist->pages, 0,
967*82078b98SChuck Lever dirlist->len);
968e4ccfe30SChuck Lever /* no more entries */
969e4ccfe30SChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
970130e2054SChuck Lever return false;
971e4ccfe30SChuck Lever if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
972130e2054SChuck Lever return false;
973e4ccfe30SChuck Lever break;
974e4ccfe30SChuck Lever default:
975e4ccfe30SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
976130e2054SChuck Lever return false;
977e4ccfe30SChuck Lever }
9781da177e4SLinus Torvalds
979130e2054SChuck Lever return true;
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds
982efe39651SAl Viro static __be32
compose_entry_fh(struct nfsd3_readdirres * cd,struct svc_fh * fhp,const char * name,int namlen,u64 ino)9831da177e4SLinus Torvalds compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
98443b0e7eaSNeilBrown const char *name, int namlen, u64 ino)
9851da177e4SLinus Torvalds {
9861da177e4SLinus Torvalds struct svc_export *exp;
9871da177e4SLinus Torvalds struct dentry *dparent, *dchild;
988efe39651SAl Viro __be32 rv = nfserr_noent;
9891da177e4SLinus Torvalds
9901da177e4SLinus Torvalds dparent = cd->fh.fh_dentry;
9911da177e4SLinus Torvalds exp = cd->fh.fh_export;
9921da177e4SLinus Torvalds
9931da177e4SLinus Torvalds if (isdotent(name, namlen)) {
9941da177e4SLinus Torvalds if (namlen == 2) {
9951da177e4SLinus Torvalds dchild = dget_parent(dparent);
99651b2ee7dSJ. Bruce Fields /*
99751b2ee7dSJ. Bruce Fields * Don't return filehandle for ".." if we're at
99851b2ee7dSJ. Bruce Fields * the filesystem or export root:
99951b2ee7dSJ. Bruce Fields */
1000efe39651SAl Viro if (dchild == dparent)
1001efe39651SAl Viro goto out;
100251b2ee7dSJ. Bruce Fields if (dparent == exp->ex_path.dentry)
100351b2ee7dSJ. Bruce Fields goto out;
10041da177e4SLinus Torvalds } else
10051da177e4SLinus Torvalds dchild = dget(dparent);
10061da177e4SLinus Torvalds } else
10076c2d4798SAl Viro dchild = lookup_positive_unlocked(name, dparent, namlen);
10081da177e4SLinus Torvalds if (IS_ERR(dchild))
1009efe39651SAl Viro return rv;
10108177e6d6SJ. Bruce Fields if (d_mountpoint(dchild))
10118177e6d6SJ. Bruce Fields goto out;
101243b0e7eaSNeilBrown if (dchild->d_inode->i_ino != ino)
101343b0e7eaSNeilBrown goto out;
1014efe39651SAl Viro rv = fh_compose(fhp, exp, dchild, &cd->fh);
10158177e6d6SJ. Bruce Fields out:
10161da177e4SLinus Torvalds dput(dchild);
10171da177e4SLinus Torvalds return rv;
10181da177e4SLinus Torvalds }
10191da177e4SLinus Torvalds
1020a161e6c7SChuck Lever /**
1021a161e6c7SChuck Lever * nfs3svc_encode_cookie3 - Encode a directory offset cookie
1022a161e6c7SChuck Lever * @resp: readdir result context
1023a161e6c7SChuck Lever * @offset: offset cookie to encode
1024a161e6c7SChuck Lever *
10257f87fc2dSChuck Lever * The buffer space for the offset cookie has already been reserved
10267f87fc2dSChuck Lever * by svcxdr_encode_entry3_common().
1027a161e6c7SChuck Lever */
nfs3svc_encode_cookie3(struct nfsd3_readdirres * resp,u64 offset)1028a161e6c7SChuck Lever void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset)
1029a161e6c7SChuck Lever {
10307f87fc2dSChuck Lever __be64 cookie = cpu_to_be64(offset);
1031a161e6c7SChuck Lever
10327f87fc2dSChuck Lever if (!resp->cookie_offset)
10337f87fc2dSChuck Lever return;
10347f87fc2dSChuck Lever write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
10357f87fc2dSChuck Lever sizeof(cookie));
10367f87fc2dSChuck Lever resp->cookie_offset = 0;
1037a161e6c7SChuck Lever }
1038a161e6c7SChuck Lever
10398b704498SChuck Lever static bool
svcxdr_encode_entry3_common(struct nfsd3_readdirres * resp,const char * name,int namlen,loff_t offset,u64 ino)10407f87fc2dSChuck Lever svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name,
10417f87fc2dSChuck Lever int namlen, loff_t offset, u64 ino)
10427f87fc2dSChuck Lever {
10437f87fc2dSChuck Lever struct xdr_buf *dirlist = &resp->dirlist;
10447f87fc2dSChuck Lever struct xdr_stream *xdr = &resp->xdr;
10457f87fc2dSChuck Lever
10467f87fc2dSChuck Lever if (xdr_stream_encode_item_present(xdr) < 0)
10477f87fc2dSChuck Lever return false;
10487f87fc2dSChuck Lever /* fileid */
10497f87fc2dSChuck Lever if (xdr_stream_encode_u64(xdr, ino) < 0)
10507f87fc2dSChuck Lever return false;
10517f87fc2dSChuck Lever /* name */
10527f87fc2dSChuck Lever if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS3_MAXNAMLEN)) < 0)
10537f87fc2dSChuck Lever return false;
10547f87fc2dSChuck Lever /* cookie */
10557f87fc2dSChuck Lever resp->cookie_offset = dirlist->len;
1056c306d737SChuck Lever if (xdr_stream_encode_u64(xdr, OFFSET_MAX) < 0)
10577f87fc2dSChuck Lever return false;
10587f87fc2dSChuck Lever
10597f87fc2dSChuck Lever return true;
10607f87fc2dSChuck Lever }
10617f87fc2dSChuck Lever
10627f87fc2dSChuck Lever /**
10637f87fc2dSChuck Lever * nfs3svc_encode_entry3 - encode one NFSv3 READDIR entry
10647f87fc2dSChuck Lever * @data: directory context
10657f87fc2dSChuck Lever * @name: name of the object to be encoded
10667f87fc2dSChuck Lever * @namlen: length of that name, in bytes
10677f87fc2dSChuck Lever * @offset: the offset of the previous entry
10687f87fc2dSChuck Lever * @ino: the fileid of this entry
10697f87fc2dSChuck Lever * @d_type: unused
10707f87fc2dSChuck Lever *
10717f87fc2dSChuck Lever * Return values:
10727f87fc2dSChuck Lever * %0: Entry was successfully encoded.
10737f87fc2dSChuck Lever * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
10747f87fc2dSChuck Lever *
10757f87fc2dSChuck Lever * On exit, the following fields are updated:
10767f87fc2dSChuck Lever * - resp->xdr
10777f87fc2dSChuck Lever * - resp->common.err
10787f87fc2dSChuck Lever * - resp->cookie_offset
10797f87fc2dSChuck Lever */
nfs3svc_encode_entry3(void * data,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)10807f87fc2dSChuck Lever int nfs3svc_encode_entry3(void *data, const char *name, int namlen,
10817f87fc2dSChuck Lever loff_t offset, u64 ino, unsigned int d_type)
10827f87fc2dSChuck Lever {
10837f87fc2dSChuck Lever struct readdir_cd *ccd = data;
10847f87fc2dSChuck Lever struct nfsd3_readdirres *resp = container_of(ccd,
10857f87fc2dSChuck Lever struct nfsd3_readdirres,
10867f87fc2dSChuck Lever common);
10877f87fc2dSChuck Lever unsigned int starting_length = resp->dirlist.len;
10887f87fc2dSChuck Lever
10897f87fc2dSChuck Lever /* The offset cookie for the previous entry */
10907f87fc2dSChuck Lever nfs3svc_encode_cookie3(resp, offset);
10917f87fc2dSChuck Lever
10927f87fc2dSChuck Lever if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino))
10937f87fc2dSChuck Lever goto out_toosmall;
10947f87fc2dSChuck Lever
10957f87fc2dSChuck Lever xdr_commit_encode(&resp->xdr);
10967f87fc2dSChuck Lever resp->common.err = nfs_ok;
10977f87fc2dSChuck Lever return 0;
10987f87fc2dSChuck Lever
10997f87fc2dSChuck Lever out_toosmall:
11007f87fc2dSChuck Lever resp->cookie_offset = 0;
11017f87fc2dSChuck Lever resp->common.err = nfserr_toosmall;
11027f87fc2dSChuck Lever resp->dirlist.len = starting_length;
11037f87fc2dSChuck Lever return -EINVAL;
11047f87fc2dSChuck Lever }
11057f87fc2dSChuck Lever
11067f87fc2dSChuck Lever static bool
svcxdr_encode_entry3_plus(struct nfsd3_readdirres * resp,const char * name,int namlen,u64 ino)11077f87fc2dSChuck Lever svcxdr_encode_entry3_plus(struct nfsd3_readdirres *resp, const char *name,
11087f87fc2dSChuck Lever int namlen, u64 ino)
11097f87fc2dSChuck Lever {
11107f87fc2dSChuck Lever struct xdr_stream *xdr = &resp->xdr;
11117f87fc2dSChuck Lever struct svc_fh *fhp = &resp->scratch;
11127f87fc2dSChuck Lever bool result;
11137f87fc2dSChuck Lever
11147f87fc2dSChuck Lever result = false;
11157f87fc2dSChuck Lever fh_init(fhp, NFS3_FHSIZE);
11167f87fc2dSChuck Lever if (compose_entry_fh(resp, fhp, name, namlen, ino) != nfs_ok)
11177f87fc2dSChuck Lever goto out_noattrs;
11187f87fc2dSChuck Lever
11197f87fc2dSChuck Lever if (!svcxdr_encode_post_op_attr(resp->rqstp, xdr, fhp))
11207f87fc2dSChuck Lever goto out;
11217f87fc2dSChuck Lever if (!svcxdr_encode_post_op_fh3(xdr, fhp))
11227f87fc2dSChuck Lever goto out;
11237f87fc2dSChuck Lever result = true;
11247f87fc2dSChuck Lever
11257f87fc2dSChuck Lever out:
11267f87fc2dSChuck Lever fh_put(fhp);
11277f87fc2dSChuck Lever return result;
11287f87fc2dSChuck Lever
11297f87fc2dSChuck Lever out_noattrs:
11307f87fc2dSChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
11317f87fc2dSChuck Lever return false;
11327f87fc2dSChuck Lever if (xdr_stream_encode_item_absent(xdr) < 0)
11337f87fc2dSChuck Lever return false;
11347f87fc2dSChuck Lever return true;
11357f87fc2dSChuck Lever }
11367f87fc2dSChuck Lever
11377f87fc2dSChuck Lever /**
11387f87fc2dSChuck Lever * nfs3svc_encode_entryplus3 - encode one NFSv3 READDIRPLUS entry
11397f87fc2dSChuck Lever * @data: directory context
11407f87fc2dSChuck Lever * @name: name of the object to be encoded
11417f87fc2dSChuck Lever * @namlen: length of that name, in bytes
11427f87fc2dSChuck Lever * @offset: the offset of the previous entry
11437f87fc2dSChuck Lever * @ino: the fileid of this entry
11447f87fc2dSChuck Lever * @d_type: unused
11457f87fc2dSChuck Lever *
11467f87fc2dSChuck Lever * Return values:
11477f87fc2dSChuck Lever * %0: Entry was successfully encoded.
11487f87fc2dSChuck Lever * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
11497f87fc2dSChuck Lever *
11507f87fc2dSChuck Lever * On exit, the following fields are updated:
11517f87fc2dSChuck Lever * - resp->xdr
11527f87fc2dSChuck Lever * - resp->common.err
11537f87fc2dSChuck Lever * - resp->cookie_offset
11547f87fc2dSChuck Lever */
nfs3svc_encode_entryplus3(void * data,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)11557f87fc2dSChuck Lever int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen,
11567f87fc2dSChuck Lever loff_t offset, u64 ino, unsigned int d_type)
11577f87fc2dSChuck Lever {
11587f87fc2dSChuck Lever struct readdir_cd *ccd = data;
11597f87fc2dSChuck Lever struct nfsd3_readdirres *resp = container_of(ccd,
11607f87fc2dSChuck Lever struct nfsd3_readdirres,
11617f87fc2dSChuck Lever common);
11627f87fc2dSChuck Lever unsigned int starting_length = resp->dirlist.len;
11637f87fc2dSChuck Lever
11647f87fc2dSChuck Lever /* The offset cookie for the previous entry */
11657f87fc2dSChuck Lever nfs3svc_encode_cookie3(resp, offset);
11667f87fc2dSChuck Lever
11677f87fc2dSChuck Lever if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino))
11687f87fc2dSChuck Lever goto out_toosmall;
11697f87fc2dSChuck Lever if (!svcxdr_encode_entry3_plus(resp, name, namlen, ino))
11707f87fc2dSChuck Lever goto out_toosmall;
11717f87fc2dSChuck Lever
11727f87fc2dSChuck Lever xdr_commit_encode(&resp->xdr);
11737f87fc2dSChuck Lever resp->common.err = nfs_ok;
11747f87fc2dSChuck Lever return 0;
11757f87fc2dSChuck Lever
11767f87fc2dSChuck Lever out_toosmall:
11777f87fc2dSChuck Lever resp->cookie_offset = 0;
11787f87fc2dSChuck Lever resp->common.err = nfserr_toosmall;
11797f87fc2dSChuck Lever resp->dirlist.len = starting_length;
11807f87fc2dSChuck Lever return -EINVAL;
11817f87fc2dSChuck Lever }
11827f87fc2dSChuck Lever
11837f87fc2dSChuck Lever static bool
svcxdr_encode_fsstat3resok(struct xdr_stream * xdr,const struct nfsd3_fsstatres * resp)11848b704498SChuck Lever svcxdr_encode_fsstat3resok(struct xdr_stream *xdr,
11858b704498SChuck Lever const struct nfsd3_fsstatres *resp)
11861da177e4SLinus Torvalds {
11878b704498SChuck Lever const struct kstatfs *s = &resp->stats;
11881da177e4SLinus Torvalds u64 bs = s->f_bsize;
11898b704498SChuck Lever __be32 *p;
11901da177e4SLinus Torvalds
11918b704498SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 13);
11928b704498SChuck Lever if (!p)
11938b704498SChuck Lever return false;
11941da177e4SLinus Torvalds p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
11951da177e4SLinus Torvalds p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
11961da177e4SLinus Torvalds p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
11971da177e4SLinus Torvalds p = xdr_encode_hyper(p, s->f_files); /* total inodes */
11981da177e4SLinus Torvalds p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
11991da177e4SLinus Torvalds p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
12008b704498SChuck Lever *p = cpu_to_be32(resp->invarsec); /* mean unchanged time */
12018b704498SChuck Lever
12028b704498SChuck Lever return true;
12031da177e4SLinus Torvalds }
12048b704498SChuck Lever
12058b704498SChuck Lever /* FSSTAT */
1206130e2054SChuck Lever bool
nfs3svc_encode_fsstatres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1207fda49441SChuck Lever nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12088b704498SChuck Lever {
12098b704498SChuck Lever struct nfsd3_fsstatres *resp = rqstp->rq_resp;
12108b704498SChuck Lever
12118b704498SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1212130e2054SChuck Lever return false;
12138b704498SChuck Lever switch (resp->status) {
12148b704498SChuck Lever case nfs_ok:
12158b704498SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1216130e2054SChuck Lever return false;
12178b704498SChuck Lever if (!svcxdr_encode_fsstat3resok(xdr, resp))
1218130e2054SChuck Lever return false;
12198b704498SChuck Lever break;
12208b704498SChuck Lever default:
12218b704498SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1222130e2054SChuck Lever return false;
12238b704498SChuck Lever }
12248b704498SChuck Lever
1225130e2054SChuck Lever return true;
12261da177e4SLinus Torvalds }
12271da177e4SLinus Torvalds
12280a139d1bSChuck Lever static bool
svcxdr_encode_fsinfo3resok(struct xdr_stream * xdr,const struct nfsd3_fsinfores * resp)12290a139d1bSChuck Lever svcxdr_encode_fsinfo3resok(struct xdr_stream *xdr,
12300a139d1bSChuck Lever const struct nfsd3_fsinfores *resp)
12310a139d1bSChuck Lever {
12320a139d1bSChuck Lever __be32 *p;
12330a139d1bSChuck Lever
12340a139d1bSChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 12);
12350a139d1bSChuck Lever if (!p)
12360a139d1bSChuck Lever return false;
12370a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_rtmax);
12380a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_rtpref);
12390a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_rtmult);
12400a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_wtmax);
12410a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_wtpref);
12420a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_wtmult);
12430a139d1bSChuck Lever *p++ = cpu_to_be32(resp->f_dtpref);
12440a139d1bSChuck Lever p = xdr_encode_hyper(p, resp->f_maxfilesize);
12450a139d1bSChuck Lever p = encode_nfstime3(p, &nfs3svc_time_delta);
12460a139d1bSChuck Lever *p = cpu_to_be32(resp->f_properties);
12470a139d1bSChuck Lever
12480a139d1bSChuck Lever return true;
12490a139d1bSChuck Lever }
12500a139d1bSChuck Lever
12511da177e4SLinus Torvalds /* FSINFO */
1252130e2054SChuck Lever bool
nfs3svc_encode_fsinfores(struct svc_rqst * rqstp,struct xdr_stream * xdr)1253fda49441SChuck Lever nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12541da177e4SLinus Torvalds {
125563f8de37SChristoph Hellwig struct nfsd3_fsinfores *resp = rqstp->rq_resp;
125663f8de37SChristoph Hellwig
12570a139d1bSChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1258130e2054SChuck Lever return false;
12590a139d1bSChuck Lever switch (resp->status) {
12600a139d1bSChuck Lever case nfs_ok:
12610a139d1bSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1262130e2054SChuck Lever return false;
12630a139d1bSChuck Lever if (!svcxdr_encode_fsinfo3resok(xdr, resp))
1264130e2054SChuck Lever return false;
12650a139d1bSChuck Lever break;
12660a139d1bSChuck Lever default:
12670a139d1bSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1268130e2054SChuck Lever return false;
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds
1271130e2054SChuck Lever return true;
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds
1274ded04a58SChuck Lever static bool
svcxdr_encode_pathconf3resok(struct xdr_stream * xdr,const struct nfsd3_pathconfres * resp)1275ded04a58SChuck Lever svcxdr_encode_pathconf3resok(struct xdr_stream *xdr,
1276ded04a58SChuck Lever const struct nfsd3_pathconfres *resp)
1277ded04a58SChuck Lever {
1278ded04a58SChuck Lever __be32 *p;
1279ded04a58SChuck Lever
1280ded04a58SChuck Lever p = xdr_reserve_space(xdr, XDR_UNIT * 6);
1281ded04a58SChuck Lever if (!p)
1282ded04a58SChuck Lever return false;
1283ded04a58SChuck Lever *p++ = cpu_to_be32(resp->p_link_max);
1284ded04a58SChuck Lever *p++ = cpu_to_be32(resp->p_name_max);
1285ded04a58SChuck Lever p = xdr_encode_bool(p, resp->p_no_trunc);
1286ded04a58SChuck Lever p = xdr_encode_bool(p, resp->p_chown_restricted);
1287ded04a58SChuck Lever p = xdr_encode_bool(p, resp->p_case_insensitive);
1288ded04a58SChuck Lever xdr_encode_bool(p, resp->p_case_preserving);
1289ded04a58SChuck Lever
1290ded04a58SChuck Lever return true;
1291ded04a58SChuck Lever }
1292ded04a58SChuck Lever
12931da177e4SLinus Torvalds /* PATHCONF */
1294130e2054SChuck Lever bool
nfs3svc_encode_pathconfres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1295fda49441SChuck Lever nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12961da177e4SLinus Torvalds {
129763f8de37SChristoph Hellwig struct nfsd3_pathconfres *resp = rqstp->rq_resp;
129863f8de37SChristoph Hellwig
1299ded04a58SChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1300130e2054SChuck Lever return false;
1301ded04a58SChuck Lever switch (resp->status) {
1302ded04a58SChuck Lever case nfs_ok:
1303ded04a58SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1304130e2054SChuck Lever return false;
1305ded04a58SChuck Lever if (!svcxdr_encode_pathconf3resok(xdr, resp))
1306130e2054SChuck Lever return false;
1307ded04a58SChuck Lever break;
1308ded04a58SChuck Lever default:
1309ded04a58SChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1310130e2054SChuck Lever return false;
13111da177e4SLinus Torvalds }
13121da177e4SLinus Torvalds
1313130e2054SChuck Lever return true;
13141da177e4SLinus Torvalds }
13151da177e4SLinus Torvalds
13161da177e4SLinus Torvalds /* COMMIT */
1317130e2054SChuck Lever bool
nfs3svc_encode_commitres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1318fda49441SChuck Lever nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
13191da177e4SLinus Torvalds {
132063f8de37SChristoph Hellwig struct nfsd3_commitres *resp = rqstp->rq_resp;
1321b9c0ef85SStanislav Kinsbursky
13225ef2826cSChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1323130e2054SChuck Lever return false;
13245ef2826cSChuck Lever switch (resp->status) {
13255ef2826cSChuck Lever case nfs_ok:
13265ef2826cSChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
1327130e2054SChuck Lever return false;
13285ef2826cSChuck Lever if (!svcxdr_encode_writeverf3(xdr, resp->verf))
1329130e2054SChuck Lever return false;
13305ef2826cSChuck Lever break;
13315ef2826cSChuck Lever default:
13325ef2826cSChuck Lever if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
1333130e2054SChuck Lever return false;
13341da177e4SLinus Torvalds }
13355ef2826cSChuck Lever
1336130e2054SChuck Lever return true;
13371da177e4SLinus Torvalds }
13381da177e4SLinus Torvalds
13391da177e4SLinus Torvalds /*
13401da177e4SLinus Torvalds * XDR release functions
13411da177e4SLinus Torvalds */
13428537488bSChristoph Hellwig void
nfs3svc_release_fhandle(struct svc_rqst * rqstp)13438537488bSChristoph Hellwig nfs3svc_release_fhandle(struct svc_rqst *rqstp)
13441da177e4SLinus Torvalds {
13458537488bSChristoph Hellwig struct nfsd3_attrstat *resp = rqstp->rq_resp;
13468537488bSChristoph Hellwig
13471da177e4SLinus Torvalds fh_put(&resp->fh);
13481da177e4SLinus Torvalds }
13491da177e4SLinus Torvalds
13508537488bSChristoph Hellwig void
nfs3svc_release_fhandle2(struct svc_rqst * rqstp)13518537488bSChristoph Hellwig nfs3svc_release_fhandle2(struct svc_rqst *rqstp)
13521da177e4SLinus Torvalds {
13538537488bSChristoph Hellwig struct nfsd3_fhandle_pair *resp = rqstp->rq_resp;
13548537488bSChristoph Hellwig
13551da177e4SLinus Torvalds fh_put(&resp->fh1);
13561da177e4SLinus Torvalds fh_put(&resp->fh2);
13571da177e4SLinus Torvalds }
1358