Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libnfs/nfs4/nfs4.c
Line
Count
Source
1
/* -*-  mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil;  -*- */
2
/*
3
   Copyright (C) 2016 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
4
5
   This program is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU Lesser General Public License as published by
7
   the Free Software Foundation; either version 2.1 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU Lesser General Public License for more details.
14
15
   You should have received a copy of the GNU Lesser General Public License
16
   along with this program; if not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifdef HAVE_CONFIG_H
20
#include "config.h"
21
#endif
22
23
#ifdef PS2_EE
24
#include "ps2_compat.h"
25
#endif
26
27
#ifdef WIN32
28
#include <win32/win32_compat.h>
29
#else
30
#include <sys/stat.h>
31
#endif/*WIN32*/
32
33
#include <stdio.h>
34
#include <assert.h>
35
#include <errno.h>
36
#include <string.h>
37
#include <stdlib.h>
38
#include "libnfs-zdr.h"
39
#include "libnfs.h"
40
#include "libnfs-raw.h"
41
#include "libnfs-private.h"
42
#include "libnfs-raw-nfs4.h"
43
44
char *
45
nfsstat4_to_str(int error)
46
0
{
47
0
  switch (error) {
48
0
        case NFS4_OK: return "NFS4_OK"; break;
49
0
        case NFS4ERR_PERM: return "NFS4ERR_PERM"; break;
50
0
        case NFS4ERR_NOENT: return "NFS4ERR_NOENT"; break;
51
0
        case NFS4ERR_IO: return "NFS4ERR_IO"; break;
52
0
        case NFS4ERR_NXIO: return "NFS4ERR_NXIO"; break;
53
0
        case NFS4ERR_ACCESS: return "NFS4ERR_ACCESS"; break;
54
0
        case NFS4ERR_EXIST: return "NFS4ERR_EXIST"; break;
55
0
        case NFS4ERR_XDEV: return "NFS4ERR_XDEV"; break;
56
0
        case NFS4ERR_NOTDIR: return "NFS4ERR_NOTDIR"; break;
57
0
        case NFS4ERR_ISDIR: return "NFS4ERR_ISDIR"; break;
58
0
        case NFS4ERR_INVAL: return "NFS4ERR_INVAL"; break;
59
0
        case NFS4ERR_FBIG: return "NFS4ERR_FBIG"; break;
60
0
        case NFS4ERR_NOSPC: return "NFS4ERR_NOSPC"; break;
61
0
        case NFS4ERR_ROFS: return"NFS4ERR_ROFS"; break;
62
0
        case NFS4ERR_MLINK: return "NFS4ERR_MLINK"; break;
63
0
        case NFS4ERR_NAMETOOLONG: return "NFS4ERR_NAMETOOLONG"; break;
64
0
        case NFS4ERR_NOTEMPTY: return "NFS4ERR_NOTEMPTY"; break;
65
0
        case NFS4ERR_DQUOT: return "NFS4ERR_DQUOT"; break;
66
0
        case NFS4ERR_STALE: return "NFS4ERR_STALE"; break;
67
0
        case NFS4ERR_BADHANDLE: return "NFS4ERR_BADHANDLE"; break;
68
0
        case NFS4ERR_BAD_COOKIE: return "NFS4ERR_BAD_COOKIE"; break;
69
0
        case NFS4ERR_NOTSUPP: return "NFS4ERR_NOTSUPP"; break;
70
0
        case NFS4ERR_TOOSMALL: return "NFS4ERR_TOOSMALL"; break;
71
0
        case NFS4ERR_SERVERFAULT: return "NFS4ERR_SERVERFAULT"; break;
72
0
        case NFS4ERR_BADTYPE: return "NFS4ERR_BADTYPE"; break;
73
0
        case NFS4ERR_DELAY: return "NFS4ERR_DELAY"; break;
74
0
        case NFS4ERR_SAME: return "NFS4ERR_SAME"; break;
75
0
        case NFS4ERR_DENIED: return "NFS4ERR_DENIED"; break;
76
0
        case NFS4ERR_EXPIRED: return "NFS4ERR_EXPIRED"; break;
77
0
        case NFS4ERR_LOCKED: return "NFS4ERR_LOCKED"; break;
78
0
        case NFS4ERR_GRACE: return "NFS4ERR_GRACE"; break;
79
0
        case NFS4ERR_FHEXPIRED: return "NFS4ERR_FHEXPIRED"; break;
80
0
        case NFS4ERR_SHARE_DENIED: return "NFS4ERR_SHARE_DENIED"; break;
81
0
        case NFS4ERR_WRONGSEC: return "NFS4ERR_WRONGSEC"; break;
82
0
        case NFS4ERR_CLID_INUSE: return "NFS4ERR_CLID_INUSE"; break;
83
0
        case NFS4ERR_RESOURCE: return "NFS4ERR_RESOURCE"; break;
84
0
        case NFS4ERR_MOVED: return "NFS4ERR_MOVED"; break;
85
0
        case NFS4ERR_NOFILEHANDLE: return "NFS4ERR_NOFILEHANDLE"; break;
86
0
        case NFS4ERR_MINOR_VERS_MISMATCH:
87
0
                return "NFS4ERR_MINOR_VERS_MISMATCH"; break;
88
0
        case NFS4ERR_STALE_CLIENTID: return "NFS4ERR_STALE_CLIENTID"; break;
89
0
        case NFS4ERR_STALE_STATEID: return "NFS4ERR_STALE_STATEID"; break;
90
0
        case NFS4ERR_OLD_STATEID: return "NFS4ERR_OLD_STATEID"; break;
91
0
        case NFS4ERR_BAD_STATEID: return "NFS4ERR_BAD_STATEID"; break;
92
0
        case NFS4ERR_BAD_SEQID: return "NFS4ERR_BAD_SEQID"; break;
93
0
        case NFS4ERR_NOT_SAME: return "NFS4ERR_NOT_SAME"; break;
94
0
        case NFS4ERR_LOCK_RANGE: return "NFS4ERR_LOCK_RANGE"; break;
95
0
        case NFS4ERR_SYMLINK: return "NFS4ERR_SYMLINK"; break;
96
0
        case NFS4ERR_RESTOREFH: return "NFS4ERR_RESTOREFH"; break;
97
0
        case NFS4ERR_LEASE_MOVED: return "NFS4ERR_LEASE_MOVED"; break;
98
0
        case NFS4ERR_ATTRNOTSUPP: return "NFS4ERR_ATTRNOTSUPP"; break;
99
0
        case NFS4ERR_NO_GRACE: return "NFS4ERR_NO_GRACE"; break;
100
0
        case NFS4ERR_RECLAIM_BAD: return "NFS4ERR_RECLAIM_BAD"; break;
101
0
        case NFS4ERR_RECLAIM_CONFLICT: return "NFS4ERR_RECLAIM_CONFLICT"; break;
102
0
        case NFS4ERR_BADZDR: return "NFS4ERR_BADZDR"; break;
103
0
        case NFS4ERR_LOCKS_HELD: return "NFS4ERR_LOCKS_HELD"; break;
104
0
        case NFS4ERR_OPENMODE: return "NFS4ERR_OPENMODE"; break;
105
0
        case NFS4ERR_BADOWNER: return "NFS4ERR_BADOWNER"; break;
106
0
        case NFS4ERR_BADCHAR: return "NFS4ERR_BADCHAR"; break;
107
0
        case NFS4ERR_BADNAME: return "NFS4ERR_BADNAME"; break;
108
0
        case NFS4ERR_BAD_RANGE: return "NFS4ERR_BAD_RANGE"; break;
109
0
        case NFS4ERR_LOCK_NOTSUPP: return "NFS4ERR_LOCK_NOTSUPP"; break;
110
0
        case NFS4ERR_OP_ILLEGAL: return "NFS4ERR_OP_ILLEGAL"; break;
111
0
        case NFS4ERR_DEADLOCK: return "NFS4ERR_DEADLOCK"; break;
112
0
        case NFS4ERR_FILE_OPEN: return "NFS4ERR_FILE_OPEN"; break;
113
0
        case NFS4ERR_ADMIN_REVOKED: return "NFS4ERR_ADMIN_REVOKED"; break;
114
0
        case NFS4ERR_CB_PATH_DOWN: return "NFS4ERR_CB_PATH_DOWN"; break;
115
        /* NFSv4.1 (RFC 8881) and NFSv4.2 (RFC 7862) */
116
0
        case NFS4ERR_BADIOMODE: return "NFS4ERR_BADIOMODE"; break;
117
0
        case NFS4ERR_BADLAYOUT: return "NFS4ERR_BADLAYOUT"; break;
118
0
        case NFS4ERR_BAD_SESSION_DIGEST:
119
0
                return "NFS4ERR_BAD_SESSION_DIGEST"; break;
120
0
        case NFS4ERR_BADSESSION: return "NFS4ERR_BADSESSION"; break;
121
0
        case NFS4ERR_BADSLOT: return "NFS4ERR_BADSLOT"; break;
122
0
        case NFS4ERR_COMPLETE_ALREADY:
123
0
                return "NFS4ERR_COMPLETE_ALREADY"; break;
124
0
        case NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
125
0
                return "NFS4ERR_CONN_NOT_BOUND_TO_SESSION"; break;
126
0
        case NFS4ERR_DELEG_ALREADY_WANTED:
127
0
                return "NFS4ERR_DELEG_ALREADY_WANTED"; break;
128
0
        case NFS4ERR_BACK_CHAN_BUSY: return "NFS4ERR_BACK_CHAN_BUSY"; break;
129
0
        case NFS4ERR_LAYOUTTRYLATER: return "NFS4ERR_LAYOUTTRYLATER"; break;
130
0
        case NFS4ERR_LAYOUTUNAVAILABLE:
131
0
                return "NFS4ERR_LAYOUTUNAVAILABLE"; break;
132
0
        case NFS4ERR_NOMATCHING_LAYOUT:
133
0
                return "NFS4ERR_NOMATCHING_LAYOUT"; break;
134
0
        case NFS4ERR_RECALLCONFLICT: return "NFS4ERR_RECALLCONFLICT"; break;
135
0
        case NFS4ERR_UNKNOWN_LAYOUTTYPE:
136
0
                return "NFS4ERR_UNKNOWN_LAYOUTTYPE"; break;
137
0
        case NFS4ERR_SEQ_MISORDERED: return "NFS4ERR_SEQ_MISORDERED"; break;
138
0
        case NFS4ERR_SEQUENCE_POS: return "NFS4ERR_SEQUENCE_POS"; break;
139
0
        case NFS4ERR_REQ_TOO_BIG: return "NFS4ERR_REQ_TOO_BIG"; break;
140
0
        case NFS4ERR_REP_TOO_BIG: return "NFS4ERR_REP_TOO_BIG"; break;
141
0
        case NFS4ERR_REP_TOO_BIG_TO_CACHE:
142
0
                return "NFS4ERR_REP_TOO_BIG_TO_CACHE"; break;
143
0
        case NFS4ERR_RETRY_UNCACHED_REP:
144
0
                return "NFS4ERR_RETRY_UNCACHED_REP"; break;
145
0
        case NFS4ERR_UNSAFE_COMPOUND: return "NFS4ERR_UNSAFE_COMPOUND"; break;
146
0
        case NFS4ERR_TOO_MANY_OPS: return "NFS4ERR_TOO_MANY_OPS"; break;
147
0
        case NFS4ERR_OP_NOT_IN_SESSION:
148
0
                return "NFS4ERR_OP_NOT_IN_SESSION"; break;
149
0
        case NFS4ERR_HASH_ALG_UNSUPP: return "NFS4ERR_HASH_ALG_UNSUPP"; break;
150
0
        case NFS4ERR_CLIENTID_BUSY: return "NFS4ERR_CLIENTID_BUSY"; break;
151
0
        case NFS4ERR_PNFS_IO_HOLE: return "NFS4ERR_PNFS_IO_HOLE"; break;
152
0
        case NFS4ERR_SEQ_FALSE_RETRY: return "NFS4ERR_SEQ_FALSE_RETRY"; break;
153
0
        case NFS4ERR_BAD_HIGH_SLOT: return "NFS4ERR_BAD_HIGH_SLOT"; break;
154
0
        case NFS4ERR_DEADSESSION: return "NFS4ERR_DEADSESSION"; break;
155
0
        case NFS4ERR_ENCR_ALG_UNSUPP: return "NFS4ERR_ENCR_ALG_UNSUPP"; break;
156
0
        case NFS4ERR_PNFS_NO_LAYOUT: return "NFS4ERR_PNFS_NO_LAYOUT"; break;
157
0
        case NFS4ERR_NOT_ONLY_OP: return "NFS4ERR_NOT_ONLY_OP"; break;
158
0
        case NFS4ERR_WRONG_CRED: return "NFS4ERR_WRONG_CRED"; break;
159
0
        case NFS4ERR_WRONG_TYPE: return "NFS4ERR_WRONG_TYPE"; break;
160
0
        case NFS4ERR_DIRDELEG_UNAVAIL:
161
0
                return "NFS4ERR_DIRDELEG_UNAVAIL"; break;
162
0
        case NFS4ERR_REJECT_DELEG: return "NFS4ERR_REJECT_DELEG"; break;
163
0
        case NFS4ERR_RETURNCONFLICT: return "NFS4ERR_RETURNCONFLICT"; break;
164
0
        case NFS4ERR_DELEG_REVOKED: return "NFS4ERR_DELEG_REVOKED"; break;
165
0
        case NFS4ERR_PARTNER_NOTSUPP: return "NFS4ERR_PARTNER_NOTSUPP"; break;
166
0
        case NFS4ERR_PARTNER_NO_AUTH: return "NFS4ERR_PARTNER_NO_AUTH"; break;
167
0
        case NFS4ERR_UNION_NOTSUPP: return "NFS4ERR_UNION_NOTSUPP"; break;
168
0
        case NFS4ERR_OFFLOAD_DENIED: return "NFS4ERR_OFFLOAD_DENIED"; break;
169
0
        case NFS4ERR_WRONG_LFS: return "NFS4ERR_WRONG_LFS"; break;
170
0
        case NFS4ERR_BADLABEL: return "NFS4ERR_BADLABEL"; break;
171
0
        case NFS4ERR_OFFLOAD_NO_REQS: return "NFS4ERR_OFFLOAD_NO_REQS"; break;
172
0
  };
173
0
  return "unknown nfsv4 error";
174
0
}
175
176
int
177
nfsstat4_to_errno(int error)
178
0
{
179
0
  switch (error) {
180
0
        case NFS4_OK: return 0;
181
0
        case NFS4ERR_PERM: return -EPERM;
182
0
        case NFS4ERR_NOENT: return -ENOENT ;
183
0
        case NFS4ERR_IO: return -EIO;
184
0
        case NFS4ERR_NXIO: return -ENXIO;
185
0
        case NFS4ERR_ACCESS: return -EACCES ;
186
0
        case NFS4ERR_EXIST: return -EEXIST;
187
0
        case NFS4ERR_XDEV: return -EXDEV;
188
0
        case NFS4ERR_NOTDIR: return -ENOTDIR ;
189
0
        case NFS4ERR_ISDIR: return -EISDIR ;
190
0
        case NFS4ERR_INVAL: return -EINVAL;
191
0
        case NFS4ERR_FBIG: return -EFBIG;
192
0
        case NFS4ERR_NOSPC: return -ENOSPC;
193
0
        case NFS4ERR_ROFS: return -EROFS;
194
0
        case NFS4ERR_MLINK: return -EMLINK;
195
0
        case NFS4ERR_NAMETOOLONG: return -ENAMETOOLONG;
196
0
        case NFS4ERR_NOTEMPTY: return -ENOTEMPTY;
197
0
        case NFS4ERR_DQUOT: return -ERANGE;
198
0
        case NFS4ERR_STALE: return -ESTALE;
199
0
        case NFS4ERR_BADHANDLE: return -EINVAL;
200
0
        case NFS4ERR_BAD_COOKIE: return -EINVAL;
201
        /*
202
         * The server understood the operation and does not implement
203
         * it, which is exactly ENOTSUP. EINVAL said the arguments were
204
         * wrong instead, and contradicted what the NFSv4.2 and named
205
         * attribute calls document they return for an unsupporting
206
         * server.
207
         */
208
0
        case NFS4ERR_NOTSUPP: return -ENOTSUP;
209
0
        case NFS4ERR_TOOSMALL: return -EIO;
210
0
        case NFS4ERR_SERVERFAULT: return -EIO;
211
0
        case NFS4ERR_BADTYPE: return -EINVAL;
212
0
        case NFS4ERR_DELAY: return -EIO;
213
0
        case NFS4ERR_SAME: return -EIO;
214
0
        case NFS4ERR_DENIED: return -EIO;
215
0
        case NFS4ERR_EXPIRED: return -EAGAIN;
216
0
        case NFS4ERR_LOCKED: return -EIO;
217
0
        case NFS4ERR_GRACE: return -EIO;
218
0
        case NFS4ERR_FHEXPIRED: return -EAGAIN;
219
0
        case NFS4ERR_SHARE_DENIED: return -EIO;
220
0
        case NFS4ERR_WRONGSEC: return -EIO;
221
0
        case NFS4ERR_CLID_INUSE: return -EIO;
222
0
        case NFS4ERR_RESOURCE: return -EIO;
223
0
        case NFS4ERR_MOVED: return -EIO;
224
0
        case NFS4ERR_NOFILEHANDLE: return -EIO;
225
0
        case NFS4ERR_MINOR_VERS_MISMATCH: return -EIO;
226
0
        case NFS4ERR_STALE_CLIENTID: return -EIO;
227
0
        case NFS4ERR_STALE_STATEID: return -EIO;
228
0
        case NFS4ERR_OLD_STATEID: return -EIO;
229
0
        case NFS4ERR_BAD_STATEID: return -EINVAL;
230
0
        case NFS4ERR_BAD_SEQID: return -EINVAL;
231
0
        case NFS4ERR_NOT_SAME: return -EIO;
232
0
        case NFS4ERR_LOCK_RANGE: return -EIO;
233
0
        case NFS4ERR_SYMLINK: return -EIO;
234
0
        case NFS4ERR_RESTOREFH: return -EIO;
235
0
        case NFS4ERR_ATTRNOTSUPP: return -EINVAL;
236
0
        case NFS4ERR_NO_GRACE: return -EIO;
237
0
        case NFS4ERR_RECLAIM_BAD: return -EIO;
238
0
        case NFS4ERR_RECLAIM_CONFLICT: return -EIO;
239
0
        case NFS4ERR_BADZDR: return -EINVAL;
240
0
        case NFS4ERR_LOCKS_HELD: return -EIO;
241
0
        case NFS4ERR_OPENMODE: return -EIO;
242
0
        case NFS4ERR_BADOWNER: return -EINVAL;
243
0
        case NFS4ERR_BADCHAR: return -EINVAL;
244
0
        case NFS4ERR_BADNAME: return -EINVAL;
245
0
        case NFS4ERR_BAD_RANGE: return -EINVAL;
246
0
        case NFS4ERR_LOCK_NOTSUPP: return -EINVAL;
247
0
        case NFS4ERR_OP_ILLEGAL: return -EIO;
248
0
        case NFS4ERR_DEADLOCK: return -EIO;
249
0
        case NFS4ERR_FILE_OPEN: return -EIO;
250
0
        case NFS4ERR_ADMIN_REVOKED: return -EIO;
251
0
        case NFS4ERR_CB_PATH_DOWN: return -EIO;
252
        /* NFSv4.1 (RFC 8881) and NFSv4.2 (RFC 7862) */
253
0
        case NFS4ERR_BADIOMODE: return -EINVAL;
254
0
        case NFS4ERR_BADLAYOUT: return -EINVAL;
255
0
        case NFS4ERR_BAD_SESSION_DIGEST: return -ENOTCONN;
256
0
        case NFS4ERR_BADSESSION: return -ENOTCONN;
257
0
        case NFS4ERR_BADSLOT: return -EIO;
258
0
        case NFS4ERR_COMPLETE_ALREADY: return -EIO;
259
0
        case NFS4ERR_CONN_NOT_BOUND_TO_SESSION: return -ENOTCONN;
260
0
        case NFS4ERR_DELEG_ALREADY_WANTED: return -EALREADY;
261
0
        case NFS4ERR_BACK_CHAN_BUSY: return -EBUSY;
262
0
        case NFS4ERR_LAYOUTTRYLATER: return -EAGAIN;
263
0
        case NFS4ERR_LAYOUTUNAVAILABLE: return -ENOTSUP;
264
0
        case NFS4ERR_NOMATCHING_LAYOUT: return -ENOENT;
265
0
        case NFS4ERR_RECALLCONFLICT: return -EAGAIN;
266
0
        case NFS4ERR_UNKNOWN_LAYOUTTYPE: return -ENOTSUP;
267
0
        case NFS4ERR_SEQ_MISORDERED: return -EIO;
268
0
        case NFS4ERR_SEQUENCE_POS: return -EIO;
269
0
        case NFS4ERR_REQ_TOO_BIG: return -EFBIG;
270
0
        case NFS4ERR_REP_TOO_BIG: return -EFBIG;
271
0
        case NFS4ERR_REP_TOO_BIG_TO_CACHE: return -EFBIG;
272
0
        case NFS4ERR_RETRY_UNCACHED_REP: return -EIO;
273
0
        case NFS4ERR_UNSAFE_COMPOUND: return -EIO;
274
0
        case NFS4ERR_TOO_MANY_OPS: return -E2BIG;
275
0
        case NFS4ERR_OP_NOT_IN_SESSION: return -EIO;
276
0
        case NFS4ERR_HASH_ALG_UNSUPP: return -ENOTSUP;
277
0
        case NFS4ERR_CLIENTID_BUSY: return -EBUSY;
278
0
        case NFS4ERR_PNFS_IO_HOLE: return -EIO;
279
0
        case NFS4ERR_SEQ_FALSE_RETRY: return -EIO;
280
0
        case NFS4ERR_BAD_HIGH_SLOT: return -EIO;
281
0
        case NFS4ERR_DEADSESSION: return -ENOTCONN;
282
0
        case NFS4ERR_ENCR_ALG_UNSUPP: return -ENOTSUP;
283
0
        case NFS4ERR_PNFS_NO_LAYOUT: return -EIO;
284
0
        case NFS4ERR_NOT_ONLY_OP: return -EIO;
285
0
        case NFS4ERR_WRONG_CRED: return -EPERM;
286
0
        case NFS4ERR_WRONG_TYPE: return -EINVAL;
287
0
        case NFS4ERR_DIRDELEG_UNAVAIL: return -ENOTSUP;
288
0
        case NFS4ERR_REJECT_DELEG: return -EIO;
289
0
        case NFS4ERR_RETURNCONFLICT: return -EAGAIN;
290
0
        case NFS4ERR_DELEG_REVOKED: return -EIO;
291
0
        case NFS4ERR_PARTNER_NOTSUPP: return -ENOTSUP;
292
0
        case NFS4ERR_PARTNER_NO_AUTH: return -EPERM;
293
0
        case NFS4ERR_UNION_NOTSUPP: return -ENOTSUP;
294
0
        case NFS4ERR_OFFLOAD_DENIED: return -EPERM;
295
0
        case NFS4ERR_WRONG_LFS: return -EINVAL;
296
0
        case NFS4ERR_BADLABEL: return -EINVAL;
297
0
        case NFS4ERR_OFFLOAD_NO_REQS: return -ENOTSUP;
298
0
  };
299
0
  return -ERANGE;
300
0
}
301
302
#ifdef HAVE_NFS4_2
303
/*
304
 * NFSv4.1+ session slots.
305
 *
306
 * Every COMPOUND past the bootstrap leads with SEQUENCE, which names a slot
307
 * and a sequence id within that slot. RFC 8881 2.10.6.1 allows only one
308
 * request at a time per slot: the server keys its reply cache on the pair, so
309
 * a second request sent on a busy slot is either taken for a retransmit of
310
 * the first or rejected with NFS4ERR_SEQ_MISORDERED. The number of slots the
311
 * session was granted is therefore also the number of COMPOUNDs that may be
312
 * in flight at once.
313
 *
314
 * A slot is taken as the COMPOUND is written to the socket, not when it was
315
 * built, so that an application can queue up as many requests as it likes and
316
 * only the number on the wire is bounded. It is given back by rpc_free_pdu()
317
 * when that COMPOUND is finished with, however it finished, that being the
318
 * one place every pdu passes through.
319
 */
320
void
321
nfs4_session_mutex_init(struct rpc_context *rpc)
322
0
{
323
#ifdef HAVE_MULTITHREADING
324
        nfs_mt_mutex_init(&rpc->nfs4_slot_mutex);
325
#endif /* HAVE_MULTITHREADING */
326
0
        (void)rpc;
327
0
}
328
329
void
330
nfs4_session_mutex_destroy(struct rpc_context *rpc)
331
0
{
332
#ifdef HAVE_MULTITHREADING
333
        nfs_mt_mutex_destroy(&rpc->nfs4_slot_mutex);
334
#endif /* HAVE_MULTITHREADING */
335
0
        (void)rpc;
336
0
}
337
338
/*
339
 * The slot table has a lock of its own rather than sharing rpc_mutex, because
340
 * slots are taken and released from inside regions that already hold
341
 * rpc_mutex and that mutex is not recursive. Nothing under this lock reaches
342
 * for rpc_mutex, so the two cannot deadlock against each other.
343
 */
344
static void
345
nfs4_session_lock(struct rpc_context *rpc)
346
0
{
347
#ifdef HAVE_MULTITHREADING
348
        if (rpc->multithreading_enabled) {
349
                nfs_mt_mutex_lock(&rpc->nfs4_slot_mutex);
350
        }
351
#endif /* HAVE_MULTITHREADING */
352
0
        (void)rpc;
353
0
}
354
355
static void
356
nfs4_session_unlock(struct rpc_context *rpc)
357
0
{
358
#ifdef HAVE_MULTITHREADING
359
        if (rpc->multithreading_enabled) {
360
                nfs_mt_mutex_unlock(&rpc->nfs4_slot_mutex);
361
        }
362
#endif /* HAVE_MULTITHREADING */
363
0
        (void)rpc;
364
0
}
365
366
int
367
nfs4_session_init(struct rpc_context *rpc, const char *sessionid,
368
                  uint32_t slot_count)
369
0
{
370
0
        struct nfs4_slot *slots;
371
372
0
        if (slot_count < 1) {
373
0
                return -1;
374
0
        }
375
0
        if (slot_count > NFS4_MAX_SLOTS) {
376
0
                slot_count = NFS4_MAX_SLOTS;
377
0
        }
378
379
0
        slots = calloc(slot_count, sizeof(struct nfs4_slot));
380
0
        if (slots == NULL) {
381
0
                return -1;
382
0
        }
383
384
0
        nfs4_session_lock(rpc);
385
0
        free(rpc->nfs4_slots);
386
0
        memcpy(rpc->nfs4_sessionid, sessionid, sizeof(sessionid4));
387
0
        rpc->nfs4_slots = slots;
388
0
        rpc->nfs4_slot_count = slot_count;
389
0
        rpc->nfs4_slots_in_use = 0;
390
0
        rpc->nfs4_slot_hint = 0;
391
0
        rpc->nfs4_renew_due = rpc_current_time() + NFS4_SESSION_RENEW_MSECS;
392
0
        rpc->nfs4_session_valid = 1;
393
0
        nfs4_session_unlock(rpc);
394
395
0
        return 0;
396
0
}
397
398
void
399
nfs4_session_destroy(struct rpc_context *rpc)
400
0
{
401
0
        nfs4_session_lock(rpc);
402
0
        rpc->nfs4_session_valid = 0;
403
0
        rpc->nfs4_slot_count = 0;
404
0
        rpc->nfs4_slots_in_use = 0;
405
0
        free(rpc->nfs4_slots);
406
0
        rpc->nfs4_slots = NULL;
407
0
        nfs4_session_unlock(rpc);
408
0
}
409
410
/*
411
 * Take a free slot and advance its sequence id. Returns -1 when every slot is
412
 * busy, i.e. when the application already has as many COMPOUNDs outstanding
413
 * as the session can carry.
414
 */
415
static int
416
nfs4_session_get_slot(struct rpc_context *rpc, SEQUENCE4args *sargs,
417
                      uint32_t *slotid)
418
0
{
419
0
        uint32_t i = 0, n;
420
421
0
        nfs4_session_lock(rpc);
422
0
        if (!rpc->nfs4_session_valid) {
423
0
                nfs4_session_unlock(rpc);
424
0
                return -1;
425
0
        }
426
0
        if (rpc->nfs4_slots_in_use >= rpc->nfs4_slot_count) {
427
0
                nfs4_session_unlock(rpc);
428
0
                return -1;
429
0
        }
430
        /*
431
         * Start where the last search left off. Slots are taken and released
432
         * in roughly the order requests are issued, so the next free one is
433
         * almost always the next one along and this finds it immediately
434
         * rather than rescanning the busy run from the start every time.
435
         */
436
0
        for (n = 0; n < rpc->nfs4_slot_count; n++) {
437
0
                i = (rpc->nfs4_slot_hint + n) % rpc->nfs4_slot_count;
438
0
                if (!rpc->nfs4_slots[i].in_use) {
439
0
                        break;
440
0
                }
441
0
        }
442
0
        assert(n < rpc->nfs4_slot_count);
443
0
        rpc->nfs4_slot_hint = (i + 1) % rpc->nfs4_slot_count;
444
0
        rpc->nfs4_slots[i].in_use = 1;
445
0
        rpc->nfs4_slots_in_use++;
446
        /*
447
         * The first request on a slot uses sequence id 1 and each new request
448
         * on it advances by one, so that the server can tell a fresh request
449
         * from a retransmit of the previous one.
450
         */
451
0
        rpc->nfs4_slots[i].seqid++;
452
453
0
        memcpy(sargs->sa_sessionid, rpc->nfs4_sessionid, sizeof(sessionid4));
454
0
        sargs->sa_sequenceid = rpc->nfs4_slots[i].seqid;
455
0
        sargs->sa_slotid = i;
456
0
        sargs->sa_highest_slotid = rpc->nfs4_slot_count - 1;
457
0
        sargs->sa_cachethis = 0;
458
0
        nfs4_session_unlock(rpc);
459
460
0
        *slotid = i;
461
0
        return 0;
462
0
}
463
464
/*
465
 * Give a slot back. rollback is for a COMPOUND that was abandoned before it
466
 * could be queued for sending: the server never saw that sequence id, so
467
 * leaving it consumed would put a gap in the slot that the next request there
468
 * is rejected for.
469
 */
470
void
471
nfs4_session_put_slot(struct rpc_context *rpc, uint32_t slotid, int rollback)
472
0
{
473
0
        nfs4_session_lock(rpc);
474
0
        if (rpc->nfs4_slots && slotid < rpc->nfs4_slot_count) {
475
0
                if (rollback && rpc->nfs4_slots[slotid].seqid > 0) {
476
0
                        rpc->nfs4_slots[slotid].seqid--;
477
0
                }
478
0
                if (rpc->nfs4_slots[slotid].in_use) {
479
0
                        rpc->nfs4_slots[slotid].in_use = 0;
480
0
                        rpc->nfs4_slots_in_use--;
481
0
                }
482
0
        }
483
0
        nfs4_session_unlock(rpc);
484
0
}
485
486
/*
487
 * Note where the leading SEQUENCE of a COMPOUND is going to land in the
488
 * encoded buffer, and mark the pdu as needing a slot. Called before the args
489
 * are encoded. A COMPOUND without a leading SEQUENCE, which is any of the
490
 * bootstrap ones and every NFSv4.0 COMPOUND, is left alone.
491
 *
492
 * The slot itself is not taken here. Taking one at encode time would cap how
493
 * many requests an application may have outstanding at the number of slots
494
 * the session was granted, which for libnfs is the wrong trade: submission
495
 * should stay unbounded and the slot count should only bound how many
496
 * COMPOUNDs are on the wire at once. So the slot is taken later, by
497
 * nfs4_pdu_take_slot() as the pdu is written, and everything behind it simply
498
 * waits in the send queue.
499
 */
500
static int
501
nfs4_pdu_note_sequence(struct rpc_context *rpc, struct rpc_pdu *pdu,
502
                       struct COMPOUND4args *args, uint32_t start)
503
0
{
504
0
        if (args->argarray.argarray_len < 1 ||
505
0
            args->argarray.argarray_val[0].argop != OP_SEQUENCE) {
506
0
                return 0;
507
0
        }
508
509
        /*
510
         * With Kerberos integrity or privacy the payload is signed or wrapped
511
         * once it has been marshalled, so it cannot be touched again
512
         * afterwards. There the slot has to be taken now and stamped into the
513
         * args before they are encoded, which does put a request back under
514
         * the old ceiling of one outstanding COMPOUND per slot. Plain AUTH_SYS
515
         * and AUTH_TLS, which is everything else, take theirs at send time.
516
         */
517
#ifdef HAVE_LIBKRB5
518
        if (rpc->sec == RPC_SEC_KRB5I || rpc->sec == RPC_SEC_KRB5P) {
519
                uint32_t slotid;
520
521
                if (nfs4_session_get_slot(rpc,
522
                        &args->argarray.argarray_val[0].nfs_argop4_u.opsequence,
523
                        &slotid) < 0) {
524
                        rpc_set_error(rpc, "NFSv4.2 session has no free slot. "
525
                                      "All %d are in use by requests that have "
526
                                      "not completed yet.",
527
                                      rpc->nfs4_slot_count);
528
                        return -1;
529
                }
530
                pdu->nfs4_slot = slotid;
531
                pdu->nfs4_slot_held = 1;
532
                rpc->nfs4_renew_due = rpc_current_time() +
533
                        NFS4_SESSION_RENEW_MSECS;
534
                return 0;
535
        }
536
#endif /* HAVE_LIBKRB5 */
537
538
        /*
539
         * COMPOUND4args is a tag, a minor version and the operation array,
540
         * and the first operation here is the SEQUENCE whose args we want to
541
         * come back to. Everything ahead of those args is fixed width, so
542
         * their offset can be counted out: the tag's length word and its
543
         * padded contents, the minor version, the operation count and the
544
         * opcode itself.
545
         */
546
0
        pdu->nfs4_seq_pos = start + 4 +
547
0
                ((args->tag.utf8string_len + 3) & ~3u) + 4 + 4 + 4;
548
0
        pdu->nfs4_needs_slot = 1;
549
550
0
        return 0;
551
0
}
552
553
/*
554
 * Take a slot for a pdu that is about to be written, and stamp the slot id
555
 * and its sequence id into the SEQUENCE that was encoded earlier. Returns -1
556
 * when every slot is busy, which means the caller should leave this pdu
557
 * queued and come back to it once one frees up.
558
 */
559
int
560
nfs4_pdu_take_slot(struct rpc_context *rpc, struct rpc_pdu *pdu)
561
0
{
562
0
        SEQUENCE4args sargs;
563
0
        uint32_t slotid, pos;
564
565
0
        if (!pdu->nfs4_needs_slot || pdu->nfs4_slot_held) {
566
0
                return 0;
567
0
        }
568
569
0
        memset(&sargs, 0, sizeof(sargs));
570
0
        if (nfs4_session_get_slot(rpc, &sargs, &slotid) < 0) {
571
0
                return -1;
572
0
        }
573
574
        /*
575
         * Rewrite SEQUENCE4args where it already sits. The encode buffer is
576
         * what the socket writes from and the record is fixed width up to
577
         * this point, so seeking back and re-encoding just these args
578
         * disturbs nothing else.
579
         */
580
0
        pos = zdr_getpos(&pdu->zdr);
581
0
        assert(pdu->nfs4_seq_pos < pos);
582
0
        zdr_setpos(&pdu->zdr, pdu->nfs4_seq_pos);
583
        /*
584
         * Cannot fail: these are fixed width fields being written back over
585
         * the ones already encoded at this position, so there is nothing to
586
         * grow the buffer for.
587
         */
588
0
        (void)zdr_SEQUENCE4args(&pdu->zdr, &sargs);
589
0
        assert(zdr_getpos(&pdu->zdr) <= pos);
590
0
        zdr_setpos(&pdu->zdr, pos);
591
592
0
        pdu->nfs4_slot = slotid;
593
0
        pdu->nfs4_slot_held = 1;
594
595
        /*
596
         * Any SEQUENCE renews the lease, so a connection that is being used
597
         * never needs a renewal of its own.
598
         */
599
0
        rpc->nfs4_renew_due = rpc_current_time() + NFS4_SESSION_RENEW_MSECS;
600
601
0
        return 0;
602
0
}
603
604
/*
605
 * Whether a COMPOUND that needs a slot could be sent right now. The send path
606
 * asks this before requesting POLLOUT, so that a full slot table parks the
607
 * writer instead of spinning on a socket it must not write to yet.
608
 */
609
int
610
nfs4_session_has_free_slot(struct rpc_context *rpc)
611
0
{
612
0
        int free_slot;
613
614
0
        nfs4_session_lock(rpc);
615
0
        free_slot = !rpc->nfs4_session_valid ||
616
0
                rpc->nfs4_slots_in_use < rpc->nfs4_slot_count;
617
0
        nfs4_session_unlock(rpc);
618
619
0
        return free_slot;
620
0
}
621
622
/* RFC 8881 15.1.1.3: replay only if no state-changing op has succeeded. */
623
static void
624
nfs4_pdu_note_delay(struct rpc_pdu *pdu, const COMPOUND4args *args)
625
0
{
626
0
        uint32_t i;
627
628
0
        if (!pdu->nfs4_needs_slot) {
629
0
                return;
630
0
        }
631
0
        for (i = 0; i < args->argarray.argarray_len; i++) {
632
0
                pdu->nfs4_delay_maxres = i + 1;
633
0
                switch (args->argarray.argarray_val[i].argop) {
634
0
                case OP_SEQUENCE:
635
0
                case OP_PUTFH:
636
0
                case OP_PUTROOTFH:
637
0
                case OP_SAVEFH:
638
0
                case OP_RESTOREFH:
639
0
                case OP_LOOKUP:
640
0
                case OP_LOOKUPP:
641
0
                case OP_GETFH:
642
0
                case OP_GETATTR:
643
0
                case OP_ACCESS:
644
0
                case OP_READLINK:
645
0
                        break;
646
0
                default:
647
0
                        return;
648
0
                }
649
0
        }
650
0
}
651
652
0
#define NFS4_DELAY_MAX_ATTEMPTS 8
653
0
#define NFS4_DELAY_BASE_MSECS   100u
654
0
#define NFS4_DELAY_MAX_MSECS    1000u
655
656
int
657
nfs4_pdu_retry_delay(struct rpc_context *rpc, struct rpc_pdu *pdu,
658
                     const COMPOUND4res *res)
659
0
{
660
0
        uint32_t count, delay, pos;
661
662
        /* Leave v4.0, GSS, zero-copy I/O and SEQUENCE errors unchanged. */
663
0
        if (!pdu->nfs4_delay_maxres || rpc->nfs4_minorversion != 2 ||
664
0
            rpc->is_udp || pdu->msg.body.cbody.cred.oa_flavor != AUTH_SYS || pdu->in.base ||
665
0
            pdu->do_not_retry || !pdu->nfs4_slot_held || !res ||
666
0
            res->status != NFS4ERR_DELAY ||
667
0
            pdu->nfs4_delay_attempts >= NFS4_DELAY_MAX_ATTEMPTS) {
668
0
                return 0;
669
0
        }
670
0
        count = res->resarray.resarray_len;
671
0
        if (count < 2 || count > pdu->nfs4_delay_maxres ||
672
0
            res->resarray.resarray_val[0].resop != OP_SEQUENCE ||
673
0
            res->resarray.resarray_val[0].nfs_resop4_u.opsequence.sr_status != NFS4_OK) {
674
0
                return 0;
675
0
        }
676
677
        /* A new logical request needs a new sequence, not a transport replay. */
678
0
        nfs4_session_put_slot(rpc, pdu->nfs4_slot, 0);
679
0
        pdu->nfs4_slot_held = 0;
680
0
        pdu->nfs4_slot_sent = 0;
681
0
        delay = pdu->nfs4_delay_attempts < 4 ?
682
0
                NFS4_DELAY_BASE_MSECS << pdu->nfs4_delay_attempts :
683
0
                NFS4_DELAY_MAX_MSECS;
684
0
        pdu->nfs4_delay_attempts++;
685
0
        pdu->nfs4_delay_until = rpc_current_time() + delay;
686
687
#ifdef HAVE_MULTITHREADING
688
        if (rpc->multithreading_enabled) {
689
                nfs_mt_mutex_lock(&rpc->rpc_mutex);
690
        }
691
#endif
692
0
        pdu->xid = rpc->xid++;
693
#ifdef HAVE_MULTITHREADING
694
        if (rpc->multithreading_enabled) {
695
                nfs_mt_mutex_unlock(&rpc->rpc_mutex);
696
        }
697
#endif
698
        /* Preserve the encoded arguments and the original caller's credentials. */
699
0
        pos = zdr_getpos(&pdu->zdr);
700
0
        zdr_setpos(&pdu->zdr, 0);
701
0
        (void)zdr_uint32_t(&pdu->zdr, &pdu->xid);
702
0
        zdr_setpos(&pdu->zdr, pos);
703
0
        pdu->pdu_stats.xid = pdu->xid;
704
0
        pdu->msg.xid = pdu->xid;
705
0
        RPC_LOG(rpc, 2, "NFS4ERR_DELAY: retry %u in %u ms",
706
0
                pdu->nfs4_delay_attempts, delay);
707
0
        return 1;
708
0
}
709
710
void
711
nfs4_defer_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu)
712
0
{
713
0
        pdu->zdr_decode_buf = NULL;
714
0
        pdu->out.num_done = 0;
715
0
        rpc_reset_cursor(rpc, &pdu->in);
716
0
        pdu->timeout = 0;
717
#ifdef HAVE_MULTITHREADING
718
        if (rpc->multithreading_enabled) {
719
                nfs_mt_mutex_lock(&rpc->rpc_mutex);
720
        }
721
#endif
722
0
        rpc_enqueue(&rpc->nfs4_delay_queue, pdu);
723
0
        rpc->nfs4_delay_queue_len++;
724
0
        rpc_wakeup_service_thread(rpc);
725
#ifdef HAVE_MULTITHREADING
726
        if (rpc->multithreading_enabled) {
727
                nfs_mt_mutex_unlock(&rpc->rpc_mutex);
728
        }
729
#endif
730
0
}
731
732
/* Milliseconds until the earliest pending DELAY retry, or -1 if none. */
733
int
734
nfs4_next_delay_msecs(struct rpc_context *rpc)
735
0
{
736
0
        struct rpc_pdu *pdu;
737
0
        uint64_t now, due = 0;
738
0
        int msecs;
739
740
#ifdef HAVE_MULTITHREADING
741
        if (rpc->multithreading_enabled) {
742
                nfs_mt_mutex_lock(&rpc->rpc_mutex);
743
        }
744
#endif
745
0
        for (pdu = rpc->nfs4_delay_queue.head; pdu; pdu = pdu->next) {
746
0
                if (due == 0 || pdu->nfs4_delay_until < due) {
747
0
                        due = pdu->nfs4_delay_until;
748
0
                }
749
0
        }
750
#ifdef HAVE_MULTITHREADING
751
        if (rpc->multithreading_enabled) {
752
                nfs_mt_mutex_unlock(&rpc->rpc_mutex);
753
        }
754
#endif
755
0
        if (due == 0) {
756
0
                return -1;
757
0
        }
758
759
0
        now = rpc_current_time();
760
0
        if (now >= due) {
761
0
                return 0;
762
0
        }
763
0
        msecs = (int)(due - now);
764
765
0
        return msecs;
766
0
}
767
768
void
769
nfs4_service_delayed(struct rpc_context *rpc)
770
0
{
771
0
        struct rpc_pdu *pdu, *next;
772
0
        uint64_t now = rpc_current_time();
773
774
#ifdef HAVE_MULTITHREADING
775
        if (rpc->multithreading_enabled) {
776
                nfs_mt_mutex_lock(&rpc->rpc_mutex);
777
        }
778
#endif
779
0
        for (pdu = rpc->nfs4_delay_queue.head; pdu; pdu = next) {
780
0
                next = pdu->next;
781
0
                if (now < pdu->nfs4_delay_until) {
782
0
                        continue;
783
0
                }
784
0
                rpc_remove_pdu_from_queue(&rpc->nfs4_delay_queue, pdu);
785
0
                rpc->nfs4_delay_queue_len--;
786
0
                pdu->nfs4_delay_until = 0;
787
0
                pdu_set_timeout(rpc, pdu, now);
788
0
                rpc_add_to_outqueue_lowp(rpc, pdu);
789
0
        }
790
#ifdef HAVE_MULTITHREADING
791
        if (rpc->multithreading_enabled) {
792
                nfs_mt_mutex_unlock(&rpc->rpc_mutex);
793
        }
794
#endif
795
0
}
796
797
/* Caller holds rpc_mutex. */
798
void
799
nfs4_requeue_delayed(struct rpc_context *rpc)
800
0
{
801
0
        struct rpc_pdu *pdu, *next;
802
803
0
        for (pdu = rpc->nfs4_delay_queue.head; pdu; pdu = next) {
804
0
                next = pdu->next;
805
0
                rpc_remove_pdu_from_queue(&rpc->nfs4_delay_queue, pdu);
806
0
                rpc->nfs4_delay_queue_len--;
807
0
                pdu->nfs4_delay_until = 0;
808
0
                rpc_return_to_outqueue(rpc, pdu);
809
0
        }
810
0
}
811
812
/*
813
 * Send a COMPOUND holding exactly one session management operation.
814
 *
815
 * BIND_CONN_TO_SESSION and DESTROY_SESSION are both operations that RFC 8881
816
 * requires to travel on their own, without a leading SEQUENCE, so they take
817
 * no slot and go out even when every slot is busy. prio lets the caller put
818
 * one at the head of the send queue, which is what rebinding a session to a
819
 * fresh connection needs: nothing else on that connection may be sent first.
820
 */
821
static struct rpc_pdu *
822
nfs4_session_op_task(struct rpc_context *rpc, rpc_cb cb, nfs_argop4 *op,
823
                     int prio, void *private_data, const char *what)
824
0
{
825
0
        struct rpc_pdu *pdu;
826
0
        COMPOUND4args args;
827
828
0
        pdu = rpc_allocate_pdu(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_COMPOUND,
829
0
                               cb, private_data, (zdrproc_t)zdr_COMPOUND4res,
830
0
                               sizeof(COMPOUND4res));
831
0
        if (pdu == NULL) {
832
0
                rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for "
833
0
                              "NFS4/%s", what);
834
0
                return NULL;
835
0
        }
836
837
0
        memset(&args, 0, sizeof(args));
838
0
        args.minorversion = rpc->nfs4_minorversion;
839
0
        args.argarray.argarray_len = 1;
840
0
        args.argarray.argarray_val = op;
841
842
0
        if (zdr_COMPOUND4args(&pdu->zdr, &args) == 0) {
843
0
                rpc_set_error(rpc, "ZDR error: Failed to encode COMPOUND4args "
844
0
                              "for %s", what);
845
0
                rpc_free_pdu(rpc, pdu);
846
0
                return NULL;
847
0
        }
848
849
0
        if (rpc_queue_pdu2(rpc, pdu, prio) != 0) {
850
0
                rpc_set_error(rpc, "Failed to queue pdu for NFS4/%s", what);
851
0
                return NULL;
852
0
        }
853
854
0
        return pdu;
855
0
}
856
857
/*
858
 * A session is bound to the connection it was created on. After an automatic
859
 * reconnect the session is still alive on the server, but the new connection
860
 * is not associated with it and every COMPOUND on it would be answered with
861
 * NFS4ERR_CONN_NOT_BOUND_TO_SESSION. This reassociates the two, and must be
862
 * the first thing sent on the new connection.
863
 */
864
struct rpc_pdu *
865
rpc_nfs4_bind_conn_to_session_task(struct rpc_context *rpc, rpc_cb cb,
866
                                   void *private_data)
867
0
{
868
0
        nfs_argop4 op;
869
0
        BIND_CONN_TO_SESSION4args *bargs;
870
871
0
        memset(&op, 0, sizeof(op));
872
0
        op.argop = OP_BIND_CONN_TO_SESSION;
873
0
        bargs = &op.nfs_argop4_u.opbindconntosession;
874
875
0
        memcpy(bargs->bctsa_sessid, rpc->nfs4_sessionid, sizeof(sessionid4));
876
        /* We run no back channel, so only the fore channel is being bound. */
877
0
        bargs->bctsa_dir = CDFC4_FORE;
878
0
        bargs->bctsa_use_conn_in_rdma_mode = 0;
879
880
0
        return nfs4_session_op_task(rpc, cb, &op, PDU_Q_PRIO_HI, private_data,
881
0
                                    "BIND_CONN_TO_SESSION");
882
0
}
883
884
/*
885
 * Release the session. Without this the server holds the session, and the
886
 * state under it, until the lease expires.
887
 */
888
struct rpc_pdu *
889
rpc_nfs4_destroy_session_task(struct rpc_context *rpc, rpc_cb cb,
890
                              void *private_data)
891
0
{
892
0
        nfs_argop4 op;
893
894
0
        memset(&op, 0, sizeof(op));
895
0
        op.argop = OP_DESTROY_SESSION;
896
0
        memcpy(op.nfs_argop4_u.opdestroysession.dsa_sessionid,
897
0
               rpc->nfs4_sessionid, sizeof(sessionid4));
898
899
0
        return nfs4_session_op_task(rpc, cb, &op, PDU_Q_PRIO_LOW, private_data,
900
0
                                    "DESTROY_SESSION");
901
0
}
902
903
/*
904
 * A COMPOUND that is nothing but its own SEQUENCE. It does no work; sending
905
 * one renews the session's lease, which is what keeps an otherwise idle
906
 * connection's session from being reaped by the server.
907
 *
908
 * It goes through the ordinary COMPOUND path so that it takes a slot and is
909
 * sequenced like anything else.
910
 */
911
struct rpc_pdu *
912
rpc_nfs4_renew_session_task(struct rpc_context *rpc, rpc_cb cb,
913
                            void *private_data)
914
0
{
915
0
        COMPOUND4args args;
916
0
        nfs_argop4 op;
917
918
0
        memset(&op, 0, sizeof(op));
919
0
        op.argop = OP_SEQUENCE;
920
921
0
        memset(&args, 0, sizeof(args));
922
0
        args.argarray.argarray_len = 1;
923
0
        args.argarray.argarray_val = &op;
924
925
0
        return rpc_nfs4_compound_task(rpc, cb, &args, private_data);
926
0
}
927
928
int
929
rpc_nfs4_session_is_valid(struct rpc_context *rpc)
930
0
{
931
0
        return rpc->nfs4_session_valid;
932
0
}
933
934
#endif /* HAVE_NFS4_2 */
935
936
937
struct rpc_pdu *rpc_nfs4_null_task(struct rpc_context *rpc, rpc_cb cb,
938
                                   void *private_data)
939
0
{
940
0
  struct rpc_pdu *pdu;
941
942
0
  pdu = rpc_allocate_pdu(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_NULL, cb,
943
0
                               private_data, (zdrproc_t)zdr_void, 0);
944
0
  if (pdu == NULL) {
945
0
    rpc_set_error(rpc, "Out of memory. Failed to allocate pdu "
946
0
                              "for NFS4/NULL call");
947
0
    return NULL;
948
0
  }
949
950
0
  if (rpc_queue_pdu(rpc, pdu) != 0) {
951
0
    rpc_set_error(rpc, "Out of memory. Failed to queue pdu for "
952
0
                              "NFS4/NULL call");
953
0
    return NULL;
954
0
  }
955
956
0
  return pdu;
957
0
}
958
959
static struct rpc_pdu *rpc_nfs4_compound_task_auth(struct rpc_context *rpc,
960
                                                   rpc_cb cb,
961
                                                   struct COMPOUND4args *args,
962
                                                   void *private_data,
963
                                                   size_t alloc_hint,
964
                                                   const struct AUTH *auth)
965
0
{
966
0
  struct rpc_pdu *pdu;
967
968
0
  pdu = rpc_allocate_pdu2_auth(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_COMPOUND,
969
0
                               cb, private_data, (zdrproc_t)zdr_COMPOUND4res,
970
0
                               sizeof(COMPOUND4res),
971
0
                               alloc_hint, 0, auth);
972
0
  if (pdu == NULL) {
973
0
    return NULL;
974
0
  }
975
976
0
#ifdef HAVE_NFS4_2
977
  /*
978
   * The minor version and the session both belong to the connection, not
979
   * to each caller, so they are stamped here rather than at every one of
980
   * the ~40 places that build a COMPOUND. The minor version stays 0
981
   * unless the context selected NFSv4.2, and a COMPOUND that does not
982
   * lead with SEQUENCE needs no slot. Without NFSv4.2 there is nothing
983
   * but 0 to stamp, and callers already memset the args, so the whole
984
   * thing compiles out.
985
   */
986
0
  args->minorversion = rpc->nfs4_minorversion;
987
  /*
988
   * Only a COMPOUND that leads with SEQUENCE, i.e. only NFSv4.2 once its
989
   * session exists, is marked as needing a slot. NFSv4.0 builds no
990
   * SEQUENCE and so is never gated on the slot table.
991
   */
992
0
  if (nfs4_pdu_note_sequence(rpc, pdu, args,
993
0
           zdr_getpos(&pdu->zdr)) < 0) {
994
0
    rpc_free_pdu(rpc, pdu);
995
0
    return NULL;
996
0
  }
997
0
#endif /* HAVE_NFS4_2 */
998
999
0
  if (zdr_COMPOUND4args(&pdu->zdr,  args) == 0) {
1000
0
    rpc_set_error(rpc, "ZDR error: Failed to encode COMPOUND4args");
1001
0
    rpc_free_pdu(rpc, pdu);
1002
0
    return NULL;
1003
0
  }
1004
1005
0
#ifdef HAVE_NFS4_2
1006
0
        nfs4_pdu_note_delay(pdu, args);
1007
0
#endif
1008
1009
0
  if (rpc_queue_pdu(rpc, pdu) != 0) {
1010
0
    rpc_set_error(rpc, "Out of memory. Failed to queue pdu for "
1011
0
                              "NFS4/COMPOUND4 call");
1012
0
    return NULL;
1013
0
  }
1014
1015
0
  return pdu;
1016
0
}
1017
1018
struct rpc_pdu *rpc_nfs4_compound_task2(struct rpc_context *rpc, rpc_cb cb,
1019
                                        struct COMPOUND4args *args,
1020
                                        void *private_data,
1021
                                        size_t alloc_hint)
1022
0
{
1023
0
  return rpc_nfs4_compound_task_auth(rpc, cb, args, private_data,
1024
0
                                      alloc_hint, NULL);
1025
0
}
1026
1027
struct rpc_pdu *rpc_nfs40_renew_task(struct rpc_context *rpc, rpc_cb cb,
1028
                                    void *private_data)
1029
0
{
1030
0
  COMPOUND4args args;
1031
0
  nfs_argop4 op;
1032
1033
0
  memset(&op, 0, sizeof(op));
1034
0
  op.argop = OP_RENEW;
1035
0
  op.nfs_argop4_u.oprenew.clientid = rpc->nfs40_clientid;
1036
0
  memset(&args, 0, sizeof(args));
1037
0
  args.argarray.argarray_len = 1;
1038
0
  args.argarray.argarray_val = &op;
1039
0
  return rpc_nfs4_compound_task_auth(rpc, cb, &args, private_data, 0,
1040
0
                                     rpc->nfs40_renew_auth);
1041
0
}
1042
1043
1044
struct rpc_pdu *rpc_nfs4_compound_task(struct rpc_context *rpc, rpc_cb cb,
1045
                                       struct COMPOUND4args *args,
1046
                                       void *private_data)
1047
0
{
1048
0
        return rpc_nfs4_compound_task2(rpc, cb, args, private_data, 0);
1049
0
}
1050
1051
struct rpc_pdu *rpc_nfs4_readv_task(struct rpc_context *rpc, rpc_cb cb,
1052
                                   const struct iovec *iov, int iovcnt,
1053
                                   struct COMPOUND4args *args,
1054
                                   void *private_data)
1055
0
{
1056
0
  struct rpc_pdu *pdu;
1057
0
  int i;
1058
1059
0
        if (iovcnt == 0 || iov == NULL) {
1060
0
    rpc_set_error(rpc, "Invalid arguments: iov and iovcnt must be specified");
1061
0
    return NULL;
1062
0
        }
1063
1064
        /*
1065
         * It's disallowed since it's not tested. It may work.
1066
         */
1067
0
        if (iovcnt > 1 && rpc->is_udp) {
1068
0
    rpc_set_error(rpc, "Invalid arguments: Vectored read not supported for UDP transport");
1069
0
    return NULL;
1070
0
        }
1071
1072
        /*
1073
         * Don't accept more iovecs than what readv() can handle.
1074
         */
1075
0
        if (iovcnt > RPC_MAX_VECTORS) {
1076
0
    rpc_set_error(rpc, "Invalid arguments: iovcnt must be <= %d", RPC_MAX_VECTORS);
1077
0
    return NULL;
1078
0
        }
1079
1080
0
  pdu = rpc_allocate_pdu2(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_COMPOUND,
1081
0
                               cb, private_data, (zdrproc_t)zdr_COMPOUND4res,
1082
0
                                sizeof(COMPOUND4res), 0, 0);
1083
0
  if (pdu == NULL) {
1084
0
    rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for "
1085
0
                              "NFS4/COMPOUND call");
1086
0
    return NULL;
1087
0
  }
1088
1089
0
#ifdef HAVE_NFS4_2
1090
  /*
1091
   * The minor version and the session both belong to the connection, not
1092
   * to each caller, so they are stamped here rather than at every one of
1093
   * the ~40 places that build a COMPOUND. The minor version stays 0
1094
   * unless the context selected NFSv4.2, and a COMPOUND that does not
1095
   * lead with SEQUENCE needs no slot. Without NFSv4.2 there is nothing
1096
   * but 0 to stamp, and callers already memset the args, so the whole
1097
   * thing compiles out.
1098
   */
1099
0
  args->minorversion = rpc->nfs4_minorversion;
1100
  /*
1101
   * Only a COMPOUND that leads with SEQUENCE, i.e. only NFSv4.2 once its
1102
   * session exists, is marked as needing a slot. NFSv4.0 builds no
1103
   * SEQUENCE and so is never gated on the slot table.
1104
   */
1105
0
  if (nfs4_pdu_note_sequence(rpc, pdu, args,
1106
0
           zdr_getpos(&pdu->zdr)) < 0) {
1107
0
    rpc_free_pdu(rpc, pdu);
1108
0
    return NULL;
1109
0
  }
1110
0
#endif /* HAVE_NFS4_2 */
1111
1112
0
  if (zdr_COMPOUND4args(&pdu->zdr,  args) == 0) {
1113
0
    rpc_set_error(rpc, "ZDR error: Failed to encode COMPOUND4args");
1114
0
    rpc_free_pdu(rpc, pdu);
1115
0
    return NULL;
1116
0
  }
1117
1118
0
  pdu->in.base = (struct iovec *) malloc(sizeof(struct iovec) * iovcnt * 2);
1119
0
  if (!pdu->in.base) {
1120
0
    rpc_set_error(rpc, "error: Failed to allocate memory");
1121
0
    rpc_free_pdu(rpc, pdu);
1122
0
    return NULL;
1123
0
  }
1124
1125
0
        pdu->in.iov = pdu->in.base;
1126
0
        pdu->in.iov_ref = pdu->in.base + iovcnt;
1127
0
  pdu->in.iovcnt = pdu->in.iovcnt_ref = iovcnt;
1128
1129
0
        for (i = 0; i < iovcnt; i++) {
1130
0
                pdu->in.iov[i] = pdu->in.iov_ref[i] = iov[i];
1131
0
                pdu->in.remaining_size += iov[i].iov_len;
1132
0
        }
1133
1134
0
        pdu->requested_read_count = pdu->in.remaining_size;
1135
0
        pdu->zero_copy_iov = 1;
1136
1137
0
  if (rpc_queue_pdu(rpc, pdu) != 0) {
1138
0
    rpc_set_error(rpc, "Out of memory. Failed to queue pdu for "
1139
0
                              "NFS4/COMPOUND4 call");
1140
0
    return NULL;
1141
0
  }
1142
1143
0
  return pdu;
1144
0
}
1145
1146
struct rpc_pdu *rpc_nfs4_read_task(struct rpc_context *rpc, rpc_cb cb,
1147
                                   void *buf, size_t count,
1148
                                   struct COMPOUND4args *args,
1149
                                   void *private_data)
1150
0
{
1151
0
  struct iovec iov;
1152
1153
0
  iov.iov_base = buf;
1154
0
  iov.iov_len = count;
1155
1156
0
  return rpc_nfs4_readv_task(rpc, cb, &iov, 1, args, private_data);
1157
0
}
1158
1159
struct rpc_pdu *rpc_nfs4_writev_task(struct rpc_context *rpc, rpc_cb cb,
1160
                                     const struct iovec *iov, int iovcnt,
1161
                                     struct COMPOUND4args *args,
1162
                                     void *private_data)
1163
0
{
1164
0
  struct rpc_pdu *pdu;
1165
0
        int start;
1166
0
        uint32_t len;
1167
0
        static uint32_t zero_padding;
1168
0
        int i, copy_payload;
1169
1170
0
        if (iovcnt == 0 || iov == NULL) {
1171
0
    rpc_set_error(rpc, "Invalid arguments: iov and iovcnt must be specified");
1172
0
    return NULL;
1173
0
        }
1174
1175
0
        if (iovcnt > 1 && rpc->is_udp) {
1176
0
    rpc_set_error(rpc, "Invalid arguments: Vectored write not supported for UDP transport");
1177
0
    return NULL;
1178
0
        }
1179
1180
0
        len = 0;
1181
0
        for (i = 0; i < iovcnt; i++) {
1182
0
                len += iov[i].iov_len;
1183
0
        }
1184
0
        copy_payload = rpc_sec_covers_payload(rpc);
1185
1186
0
  pdu = rpc_allocate_pdu2(rpc, NFS4_PROGRAM, NFS_V4, NFSPROC4_COMPOUND,
1187
0
                                cb, private_data, (zdrproc_t)zdr_COMPOUND4res,
1188
0
                                sizeof(COMPOUND4res),
1189
0
                                copy_payload ? len + 8 : 0, iovcnt);
1190
0
  if (pdu == NULL) {
1191
0
    rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for "
1192
0
                              "NFS4/COMPOUND call");
1193
0
    return NULL;
1194
0
  }
1195
1196
0
        start = zdr_getpos(&pdu->zdr);
1197
1198
0
#ifdef HAVE_NFS4_2
1199
  /*
1200
   * The minor version and the session both belong to the connection, not
1201
   * to each caller, so they are stamped here rather than at every one of
1202
   * the ~40 places that build a COMPOUND. The minor version stays 0
1203
   * unless the context selected NFSv4.2, and a COMPOUND that does not
1204
   * lead with SEQUENCE needs no slot. Without NFSv4.2 there is nothing
1205
   * but 0 to stamp, and callers already memset the args, so the whole
1206
   * thing compiles out.
1207
   */
1208
0
  args->minorversion = rpc->nfs4_minorversion;
1209
  /*
1210
   * Only a COMPOUND that leads with SEQUENCE, i.e. only NFSv4.2 once its
1211
   * session exists, is marked as needing a slot. NFSv4.0 builds no
1212
   * SEQUENCE and so is never gated on the slot table.
1213
   */
1214
0
  if (nfs4_pdu_note_sequence(rpc, pdu, args,
1215
0
           zdr_getpos(&pdu->zdr)) < 0) {
1216
0
    rpc_free_pdu(rpc, pdu);
1217
0
    return NULL;
1218
0
  }
1219
0
#endif /* HAVE_NFS4_2 */
1220
1221
0
  if (zdr_COMPOUND4args(&pdu->zdr,  args) == 0) {
1222
0
    rpc_set_error(rpc, "ZDR error: Failed to encode COMPOUND4args");
1223
0
    rpc_free_pdu(rpc, pdu);
1224
0
    return NULL;
1225
0
  }
1226
1227
0
        if (copy_payload) {
1228
0
                if (rpc_zdr_put_payload(&pdu->zdr, iov, iovcnt, len) < 0) {
1229
0
                        rpc_set_error(rpc, "ZDR error: Failed to encode WRITE4 data");
1230
0
                        rpc_free_pdu(rpc, pdu);
1231
0
                        return NULL;
1232
0
                }
1233
0
                goto queue;
1234
0
        }
1235
1236
        /* Add an iovector for the COMPOUND4/.../WRITE4 header */
1237
0
        if (rpc_add_iovector(rpc, &pdu->out, &pdu->outdata.data[start + 4],
1238
0
                             zdr_getpos(&pdu->zdr) - start, NULL) < 0) {
1239
0
    rpc_free_pdu(rpc, pdu);
1240
0
    return NULL;
1241
0
        }
1242
1243
        /* Add an iovector for the length of the byte/array blob */
1244
0
        start = zdr_getpos(&pdu->zdr);
1245
0
        zdr_u_int(&pdu->zdr, &len);
1246
0
        if (rpc_add_iovector(rpc, &pdu->out, &pdu->outdata.data[start + 4],
1247
0
                             4, NULL) < 0) {
1248
0
    rpc_free_pdu(rpc, pdu);
1249
0
    return NULL;
1250
0
        }
1251
1252
        /* Add iovector(s) for the data itself */
1253
0
        for (i = 0; i < iovcnt; i++) {
1254
0
                if (rpc_add_iovector(rpc, &pdu->out,
1255
0
                                     iov[i].iov_base,
1256
0
                                     iov[i].iov_len, NULL) < 0) {
1257
0
                        rpc_free_pdu(rpc, pdu);
1258
0
                        return NULL;
1259
0
                }
1260
0
        }
1261
1262
        /* We may need to pad this to 4 byte boundary */
1263
0
        if (len & 0x03) {
1264
0
                if (rpc_add_iovector(rpc, &pdu->out, (char *)&zero_padding,
1265
0
                                     4 - (len & 0x03),
1266
0
                                     NULL) < 0) {
1267
0
                        rpc_free_pdu(rpc, pdu);
1268
0
                        return NULL;
1269
0
                }
1270
0
        }
1271
1272
0
 queue:
1273
0
  if (rpc_queue_pdu(rpc, pdu) != 0) {
1274
0
    rpc_set_error(rpc, "Out of memory. Failed to queue pdu for "
1275
0
                              "NFS4/COMPOUND4 call");
1276
0
    return NULL;
1277
0
  }
1278
1279
0
  return pdu;
1280
0
}
1281
1282
struct rpc_pdu *rpc_nfs4_write_task(struct rpc_context *rpc, rpc_cb cb,
1283
                                    const void *buf, size_t count,
1284
                                    struct COMPOUND4args *args,
1285
                                    void *private_data)
1286
0
{
1287
        /*
1288
         * We don't have an iovec definition with const iov_base pointer, but
1289
         * rpc_nfs4_writev_task() won't modify the contents of *buf, so the
1290
         * following cast is safe.
1291
         */
1292
0
        struct iovec iov;
1293
1294
0
        iov.iov_base = (void *) buf;
1295
0
        iov.iov_len = count;
1296
1297
0
        return rpc_nfs4_writev_task(rpc, cb, &iov, 1, args, private_data);
1298
0
}