1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or (at
10 * your option) any later version.
12 * This library is distributed in the hope that it will be useful, but
13 * without any warranty; without even the implied warranty of merchantability
14 * or fitness for a particular purpose. See the GNU Lesser General Public
15 * License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include "nfs41_compound.h"
26 #include "nfs41_xdr.h"
27 #include "nfs41_ops.h"
29 #include "name_cache.h"
30 #include "daemon_debug.h"
32 #include "rpc/auth_sspi.h"
34 int compound_error(int status
)
36 if (status
!= NFS4_OK
)
37 dprintf(1, "COMPOUND failed with status %d.\n", status
);
42 nfs41_compound
*compound
,
48 compound
->args
.tag_len
= (uint32_t)strlen(tag
);
49 memcpy(compound
->args
.tag
, tag
, compound
->args
.tag_len
);
50 compound
->args
.minorversion
= 1;
51 compound
->args
.argarray_count
= 0;
52 compound
->args
.argarray
= argops
;
54 /* initialize results */
55 ZeroMemory(&compound
->res
, sizeof(nfs41_compound_res
));
56 compound
->res
.tag_len
= NFS4_OPAQUE_LIMIT
;
57 compound
->res
.resarray_count
= 0;
58 compound
->res
.resarray
= resops
;
62 nfs41_compound
*compound
,
67 const uint32_t i
= compound
->args
.argarray_count
++;
68 const uint32_t j
= compound
->res
.resarray_count
++;
69 compound
->args
.argarray
[i
].op
= opnum
;
70 compound
->args
.argarray
[i
].arg
= arg
;
71 compound
->res
.resarray
[j
].op
= opnum
;
72 compound
->res
.resarray
[j
].res
= res
;
75 /* Due to the possibility of replays, we might get a response to a different
76 * call than the one we're expecting. If we don't have a way to check for
77 * this, we'll likely crash trying to decode into the wrong structures.
78 * This function copies the number of operations and all of the operation
79 * numbers from the compound arguments into the response, so we can verify
80 * them on decode and fail before doing any damage. */
81 static void set_expected_res(
82 nfs41_compound
*compound
)
85 compound
->res
.resarray_count
= compound
->args
.argarray_count
;
86 for (i
= 0; i
< compound
->res
.resarray_count
; i
++)
87 compound
->res
.resarray
[i
].op
= compound
->args
.argarray
[i
].op
;
91 static int create_new_rpc_auth(nfs41_session
*session
, uint32_t op
,
92 nfs41_secinfo_info
*secinfo
)
95 int status
= ERROR_NETWORK_UNREACHABLE
, i
;
98 for (i
= 0; i
< MAX_SECINFOS
; i
++) {
99 if (!secinfo
[i
].sec_flavor
&& !secinfo
[i
].type
)
101 if (secinfo
[i
].sec_flavor
== RPCSEC_GSS
) {
102 auth
= authsspi_create_default(session
->client
->rpc
->rpc
,
103 session
->client
->rpc
->server_name
, secinfo
[i
].type
);
105 eprintf("handle_wrongsecinfo_noname: authsspi_create_default for "
106 "gsstype %s failed\n", gssauth_string(secinfo
[i
].type
));
109 sec_flavor
= secinfo
[i
].type
;
111 char machname
[MAXHOSTNAMELEN
+ 1];
113 if (gethostname(machname
, sizeof(machname
)) == -1) {
114 eprintf("nfs41_rpc_clnt_create: gethostname failed\n");
117 machname
[sizeof(machname
) - 1] = '\0';
118 auth
= authsys_create(machname
, session
->client
->rpc
->uid
,
119 session
->client
->rpc
->gid
, 0, gids
);
121 eprintf("handle_wrongsecinfo_noname: authsys_create failed\n");
124 sec_flavor
= AUTH_SYS
;
126 AcquireSRWLockExclusive(&session
->client
->rpc
->lock
);
127 session
->client
->rpc
->sec_flavor
= sec_flavor
;
128 session
->client
->rpc
->rpc
->cl_auth
= auth
;
129 ReleaseSRWLockExclusive(&session
->client
->rpc
->lock
);
137 int compound_encode_send_decode(
138 nfs41_session
*session
,
139 nfs41_compound
*compound
,
142 int status
, retry_count
= 0, delayby
= 0, secinfo_status
;
143 nfs41_sequence_args
*args
= (nfs41_sequence_args
*)
144 compound
->args
.argarray
[0].arg
;
145 uint32_t saved_sec_flavor
;
147 int op1
= compound
->args
.argarray
[0].op
;
152 set_expected_res(compound
);
153 status
= nfs41_send_compound(session
->client
->rpc
,
154 (char *)&compound
->args
, (char *)&compound
->res
);
155 // bump sequence number if sequence op succeeded.
156 if (compound
->res
.resarray_count
> 0 &&
157 compound
->res
.resarray
[0].op
== OP_SEQUENCE
) {
158 nfs41_sequence_res
*seq
=
159 (nfs41_sequence_res
*)compound
->res
.resarray
[0].res
;
160 if (seq
->sr_status
== NFS4_OK
) {
161 // returned slotid must be the same we sent
162 if (seq
->sr_resok4
.sr_slotid
!= args
->sa_slotid
) {
163 eprintf("[session] sr_slotid=%d != sa_slotid=%d\n",
164 seq
->sr_resok4
.sr_slotid
, args
->sa_slotid
);
168 // returned sessionid must be the same we sent
169 if (memcmp(seq
->sr_resok4
.sr_sessionid
, args
->sa_sessionid
,
170 NFS4_SESSIONID_SIZE
)) {
171 eprintf("[session] sr_sessionid != sa_sessionid\n");
172 print_hexbuf(1, (unsigned char *)"sr_sessionid",
173 seq
->sr_resok4
.sr_sessionid
, NFS4_SESSIONID_SIZE
);
174 print_hexbuf(1, (unsigned char *)"sa_sessionid",
175 args
->sa_sessionid
, NFS4_SESSIONID_SIZE
);
179 if (seq
->sr_resok4
.sr_status_flags
)
180 print_sr_status_flags(1, seq
->sr_resok4
.sr_status_flags
);
182 nfs41_session_bump_seq(session
, args
->sa_slotid
,
183 seq
->sr_resok4
.sr_target_highest_slotid
);
185 /* check sequence status flags for state revocation */
186 if (try_recovery
&& seq
->sr_resok4
.sr_status_flags
)
187 nfs41_recover_sequence_flags(session
,
188 seq
->sr_resok4
.sr_status_flags
);
193 eprintf("nfs41_send_compound failed %d for seqid=%d, slotid=%d\n",
194 status
, args
->sa_sequenceid
, args
->sa_slotid
);
199 if (compound
->res
.status
!= NFS4_OK
)
200 dprintf(1, "\n################ %s ################\n\n",
201 nfs_error_string(compound
->res
.status
));
203 switch (compound
->res
.status
) {
207 case NFS4ERR_STALE_CLIENTID
:
210 if (!nfs41_recovery_start_or_wait(session
->client
))
212 // try to create a new client
213 status
= nfs41_client_renew(session
->client
);
215 nfs41_recovery_finish(session
->client
);
217 eprintf("nfs41_client_renew() failed with %d\n", status
);
218 status
= ERROR_BAD_NET_RESP
;
221 if (op1
== OP_CREATE_SESSION
) {
222 nfs41_create_session_args
*csa
= (nfs41_create_session_args
*)
223 compound
->args
.argarray
[0].arg
;
224 AcquireSRWLockShared(&session
->client
->exid_lock
);
225 csa
->csa_clientid
= session
->client
->clnt_id
;
226 csa
->csa_sequence
= session
->client
->seq_id
;
227 AcquireSRWLockShared(&session
->client
->exid_lock
);
231 case NFS4ERR_BADSESSION
:
234 if (!nfs41_recovery_start_or_wait(session
->client
))
236 // try to create a new session
237 status
= nfs41_recover_session(session
, FALSE
);
239 nfs41_recovery_finish(session
->client
);
241 eprintf("nfs41_recover_session() failed with %d\n", status
);
242 status
= ERROR_BAD_NET_RESP
;
247 case NFS4ERR_EXPIRED
: /* revoked by lease expiration */
248 case NFS4ERR_BAD_STATEID
:
249 case NFS4ERR_STALE_STATEID
: /* server reboot */
250 if (op1
== OP_SEQUENCE
)
251 nfs41_session_free_slot(session
, args
->sa_slotid
);
252 if (try_recovery
&& nfs41_recover_stateid(session
,
253 &compound
->args
.argarray
[compound
->res
.resarray_count
-1]))
257 case NFS4ERR_BADSLOT
:
258 /* free the slot and retry with a new one */
259 if (op1
!= OP_SEQUENCE
|| nfs41_session_bad_slot(session
, args
))
265 #define RETRY_INDEFINITELY
266 #ifndef RETRY_INDEFINITELY
267 #define NUMBER_2_RETRY 19
270 #ifndef RETRY_INDEFINITELY
271 if (retry_count
< NUMBER_2_RETRY
) {
273 if (op1
== OP_SEQUENCE
)
274 nfs41_session_free_slot(session
, args
->sa_slotid
);
275 if (compound
->res
.status
== NFS4ERR_GRACE
)
278 delayby
= 500*retry_count
;
279 dprintf(1, "Compound returned %s: sleeping for %ums..\n",
280 (compound
->res
.status
==NFS4ERR_GRACE
)?"NFS4ERR_GRACE":"NFS4ERR_DELAY",
283 dprintf(1, "Attempting to resend compound.\n");
285 #ifndef RETRY_INDEFINITELY
290 case NFS4ERR_FHEXPIRED
: /* TODO: recover expired volatile filehandles */
291 status
= NFS4ERR_STALE
; /* for now, treat them as ERR_STALE */
295 nfs_argop4
*argarray
= compound
->args
.argarray
;
296 struct nfs41_name_cache
*name_cache
=
297 session_name_cache(session
);
298 nfs41_putfh_args
*putfh
;
299 uint32_t i
, start
= 0;
301 /* NFS4ERR_STALE generally comes from a PUTFH operation. in
302 * this case, remove its filehandle from the name cache. but
303 * because COMPOUNDs are not atomic, a file can be removed
304 * between PUTFH and the operation that uses it. in this
305 * case, we can't tell which PUTFH operation is to blame, so
306 * we must invalidate filehandles of all PUTFH operations in
309 if (argarray
[compound
->res
.resarray_count
-1].op
== OP_PUTFH
)
310 start
= compound
->res
.resarray_count
-1;
312 for (i
= start
; i
< compound
->res
.resarray_count
; i
++) {
313 if (argarray
[i
].op
== OP_PUTFH
) {
314 putfh
= (nfs41_putfh_args
*)argarray
[i
].arg
;
316 if (!putfh
->in_recovery
&& putfh
->file
->path
)
317 nfs41_name_cache_remove_stale(name_cache
,
318 session
, putfh
->file
->path
);
323 case NFS4ERR_WRONGSEC
:
325 nfs41_secinfo_info secinfo
[MAX_SECINFOS
] = { 0 };
326 uint32_t rcount
= compound
->res
.resarray_count
;
327 nfs_argop4
*argarray
= compound
->args
.argarray
;
328 uint32_t op
= argarray
[rcount
-1].op
;
329 nfs41_putfh_args
*putfh
;
330 nfs41_path_fh
*file
= NULL
;
339 case OP_SECINFO_NO_NAME
:
341 if (op1
== OP_SEQUENCE
)
342 nfs41_session_free_slot(session
, args
->sa_slotid
);
343 /* from: 2.6.3.1.1.5. Put Filehandle Operation + SECINFO/SECINFO_NO_NAME
344 * The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC to a put
345 * filehandle operation that is immediately followed by SECINFO or
346 * SECINFO_NO_NAME. The NFSv4.1 server MUST NOT return NFS4ERR_WRONGSEC
347 * from SECINFO or SECINFO_NO_NAME.
349 if (op1
== OP_SEQUENCE
&&
350 (argarray
[1].op
== OP_PUTFH
||
351 argarray
[1].op
== OP_PUTROOTFH
) &&
352 (argarray
[2].op
== OP_SECINFO_NO_NAME
||
353 argarray
[2].op
== OP_SECINFO
)) {
354 dprintf(1, "SECINFO: BROKEN SERVER\n");
359 if (!nfs41_recovery_start_or_wait(session
->client
))
362 saved_sec_flavor
= session
->client
->rpc
->sec_flavor
;
363 saved_auth
= session
->client
->rpc
->rpc
->cl_auth
;
364 if (op
== OP_LOOKUP
|| op
== OP_OPEN
) {
365 const nfs41_component
*name
;
366 nfs41_path_fh tmp
= { 0 };
367 nfs41_getfh_res
*getfh
;
368 nfs41_lookup_args
*largs
;
369 nfs41_op_open_args
*oargs
;
370 if (argarray
[rcount
-2].op
== OP_PUTFH
) {
371 putfh
= (nfs41_putfh_args
*)argarray
[rcount
-2].arg
;
373 } else if (argarray
[rcount
-2].op
== OP_GETATTR
&&
374 argarray
[rcount
-3].op
== OP_GETFH
) {
375 getfh
= (nfs41_getfh_res
*)compound
->res
.resarray
[rcount
-3].res
;
376 memcpy(&tmp
.fh
, getfh
->fh
, sizeof(nfs41_fh
));
380 nfs41_recovery_finish(session
->client
);
384 if (op
== OP_LOOKUP
) {
385 largs
= (nfs41_lookup_args
*)argarray
[rcount
-1].arg
;
387 } else if (op
== OP_OPEN
) {
388 oargs
= (nfs41_op_open_args
*)argarray
[rcount
-1].arg
;
389 name
= oargs
->claim
->u
.null
.filename
;
391 secinfo_status
= nfs41_secinfo(session
, file
, name
, secinfo
);
392 if (secinfo_status
) {
393 eprintf("nfs41_secinfo failed with %d\n", secinfo_status
);
394 nfs41_recovery_finish(session
->client
);
395 if (secinfo_status
== NFS4ERR_BADSESSION
) {
396 if (op1
== OP_SEQUENCE
)
397 nfs41_session_free_slot(session
, args
->sa_slotid
);
404 if (op
== OP_PUTFH
) {
405 putfh
= (nfs41_putfh_args
*)argarray
[rcount
-1].arg
;
408 secinfo_status
= nfs41_secinfo_noname(session
, file
, secinfo
);
409 if (secinfo_status
) {
410 eprintf("nfs41_secinfo_noname failed with %d\n",
412 nfs41_recovery_finish(session
->client
);
413 if (op1
== OP_SEQUENCE
)
414 nfs41_session_free_slot(session
, args
->sa_slotid
);
418 secinfo_status
= create_new_rpc_auth(session
, op
, secinfo
);
419 if (!secinfo_status
) {
420 auth_destroy(saved_auth
);
421 nfs41_recovery_finish(session
->client
);
422 // Need to retry only
425 AcquireSRWLockExclusive(&session
->client
->rpc
->lock
);
426 session
->client
->rpc
->sec_flavor
= saved_sec_flavor
;
427 session
->client
->rpc
->rpc
->cl_auth
= saved_auth
;
428 ReleaseSRWLockExclusive(&session
->client
->rpc
->lock
);
429 nfs41_recovery_finish(session
->client
);
435 if (compound
->res
.resarray
[0].op
== OP_SEQUENCE
) {
436 nfs41_sequence_res
*seq
=
437 (nfs41_sequence_res
*)compound
->res
.resarray
[0].res
;
438 if (seq
->sr_status
== NFS4_OK
&& session
->client
->rpc
->needcb
&&
439 (seq
->sr_resok4
.sr_status_flags
& SEQ4_STATUS_CB_PATH_DOWN
)) {
440 nfs41_session_free_slot(session
, args
->sa_slotid
);
441 nfs41_bind_conn_to_session(session
->client
->rpc
,
442 session
->session_id
, CDFC4_BACK_OR_BOTH
);
447 if (op1
== OP_SEQUENCE
)
448 nfs41_session_free_slot(session
, args
->sa_slotid
);
453 if (compound
->res
.resarray
[0].op
== OP_SEQUENCE
)
454 nfs41_session_get_slot(session
, &args
->sa_slotid
,
455 &args
->sa_sequenceid
, &args
->sa_highest_slotid
);