[NFSD] [TELNET] Remove the hardcoded directory paths C:\ReactOS
[reactos.git] / base / services / nfsd / nfs41_compound.c
1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
3 *
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "nfs41_compound.h"
26 #include "nfs41_xdr.h"
27 #include "nfs41_ops.h"
28 #include "recovery.h"
29 #include "name_cache.h"
30 #include "daemon_debug.h"
31 #include "rpc/rpc.h"
32 #include "rpc/auth_sspi.h"
33
34 int compound_error(int status)
35 {
36 if (status != NFS4_OK)
37 dprintf(1, "COMPOUND failed with status %d.\n", status);
38 return status;
39 }
40
41 void compound_init(
42 nfs41_compound *compound,
43 nfs_argop4 *argops,
44 nfs_resop4 *resops,
45 const char *tag)
46 {
47 /* initialize args */
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;
53
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;
59 }
60
61 void compound_add_op(
62 nfs41_compound *compound,
63 uint32_t opnum,
64 void *arg,
65 void *res)
66 {
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;
73 }
74
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)
83 {
84 uint32_t i;
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;
88 }
89
90
91 static int create_new_rpc_auth(nfs41_session *session, uint32_t op,
92 nfs41_secinfo_info *secinfo)
93 {
94 AUTH *auth = NULL;
95 int status = ERROR_NETWORK_UNREACHABLE, i;
96 uint32_t sec_flavor;
97
98 for (i = 0; i < MAX_SECINFOS; i++) {
99 if (!secinfo[i].sec_flavor && !secinfo[i].type)
100 goto out;
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);
104 if (auth == NULL) {
105 eprintf("handle_wrongsecinfo_noname: authsspi_create_default for "
106 "gsstype %s failed\n", gssauth_string(secinfo[i].type));
107 continue;
108 }
109 sec_flavor = secinfo[i].type;
110 } else {
111 char machname[MAXHOSTNAMELEN + 1];
112 gid_t gids[1];
113 if (gethostname(machname, sizeof(machname)) == -1) {
114 eprintf("nfs41_rpc_clnt_create: gethostname failed\n");
115 continue;
116 }
117 machname[sizeof(machname) - 1] = '\0';
118 auth = authsys_create(machname, session->client->rpc->uid,
119 session->client->rpc->gid, 0, gids);
120 if (auth == NULL) {
121 eprintf("handle_wrongsecinfo_noname: authsys_create failed\n");
122 continue;
123 }
124 sec_flavor = AUTH_SYS;
125 }
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);
130 status = 0;
131 break;
132 }
133 out:
134 return status;
135 }
136
137 int compound_encode_send_decode(
138 nfs41_session *session,
139 nfs41_compound *compound,
140 bool_t try_recovery)
141 {
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;
146 AUTH *saved_auth;
147 int op1 = compound->args.argarray[0].op;
148
149 retry:
150 /* send compound */
151 retry_count++;
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);
165 status = NFS4ERR_IO;
166 goto out_free_slot;
167 }
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);
176 status = NFS4ERR_IO;
177 goto out_free_slot;
178 }
179 if (seq->sr_resok4.sr_status_flags)
180 print_sr_status_flags(1, seq->sr_resok4.sr_status_flags);
181
182 nfs41_session_bump_seq(session, args->sa_slotid,
183 seq->sr_resok4.sr_target_highest_slotid);
184
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);
189 }
190 }
191
192 if (status) {
193 eprintf("nfs41_send_compound failed %d for seqid=%d, slotid=%d\n",
194 status, args->sa_sequenceid, args->sa_slotid);
195 status = NFS4ERR_IO;
196 goto out_free_slot;
197 }
198
199 if (compound->res.status != NFS4_OK)
200 dprintf(1, "\n################ %s ################\n\n",
201 nfs_error_string(compound->res.status));
202
203 switch (compound->res.status) {
204 case NFS4_OK:
205 break;
206
207 case NFS4ERR_STALE_CLIENTID:
208 if (!try_recovery)
209 goto out;
210 if (!nfs41_recovery_start_or_wait(session->client))
211 goto do_retry;
212 // try to create a new client
213 status = nfs41_client_renew(session->client);
214
215 nfs41_recovery_finish(session->client);
216 if (status) {
217 eprintf("nfs41_client_renew() failed with %d\n", status);
218 status = ERROR_BAD_NET_RESP;
219 goto out;
220 }
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);
228 }
229 goto do_retry;
230
231 case NFS4ERR_BADSESSION:
232 if (!try_recovery)
233 goto out;
234 if (!nfs41_recovery_start_or_wait(session->client))
235 goto do_retry;
236 // try to create a new session
237 status = nfs41_recover_session(session, FALSE);
238
239 nfs41_recovery_finish(session->client);
240 if (status) {
241 eprintf("nfs41_recover_session() failed with %d\n", status);
242 status = ERROR_BAD_NET_RESP;
243 goto out;
244 }
245 goto do_retry;
246
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]))
254 goto do_retry;
255 goto out;
256
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))
260 goto out;
261 goto retry;
262
263 case NFS4ERR_GRACE:
264 case NFS4ERR_DELAY:
265 #define RETRY_INDEFINITELY
266 #ifndef RETRY_INDEFINITELY
267 #define NUMBER_2_RETRY 19
268 #endif
269
270 #ifndef RETRY_INDEFINITELY
271 if (retry_count < NUMBER_2_RETRY) {
272 #endif
273 if (op1 == OP_SEQUENCE)
274 nfs41_session_free_slot(session, args->sa_slotid);
275 if (compound->res.status == NFS4ERR_GRACE)
276 delayby = 5000;
277 else
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",
281 delayby);
282 Sleep(delayby);
283 dprintf(1, "Attempting to resend compound.\n");
284 goto do_retry;
285 #ifndef RETRY_INDEFINITELY
286 }
287 #endif
288 break;
289
290 case NFS4ERR_FHEXPIRED: /* TODO: recover expired volatile filehandles */
291 status = NFS4ERR_STALE; /* for now, treat them as ERR_STALE */
292 /* no break */
293 case NFS4ERR_STALE:
294 {
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;
300
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
307 * the COMPOUND */
308
309 if (argarray[compound->res.resarray_count-1].op == OP_PUTFH)
310 start = compound->res.resarray_count-1;
311
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;
315
316 if (!putfh->in_recovery && putfh->file->path)
317 nfs41_name_cache_remove_stale(name_cache,
318 session, putfh->file->path);
319 }
320 }
321 }
322 break;
323 case NFS4ERR_WRONGSEC:
324 {
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;
331 switch(op) {
332 case OP_PUTFH:
333 case OP_RESTOREFH:
334 case OP_LINK:
335 case OP_RENAME:
336 case OP_PUTROOTFH:
337 case OP_LOOKUP:
338 case OP_OPEN:
339 case OP_SECINFO_NO_NAME:
340 case OP_SECINFO:
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.
348 */
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");
355 goto out;
356 }
357 if (!try_recovery)
358 goto out;
359 if (!nfs41_recovery_start_or_wait(session->client))
360 goto do_retry;
361
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;
372 file = putfh->file;
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));
377 file = &tmp;
378 }
379 else {
380 nfs41_recovery_finish(session->client);
381 goto out;
382 }
383
384 if (op == OP_LOOKUP) {
385 largs = (nfs41_lookup_args *)argarray[rcount-1].arg;
386 name = largs->name;
387 } else if (op == OP_OPEN) {
388 oargs = (nfs41_op_open_args *)argarray[rcount-1].arg;
389 name = oargs->claim->u.null.filename;
390 }
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);
398 goto do_retry;
399 }
400 goto out_free_slot;
401 }
402 }
403 else {
404 if (op == OP_PUTFH) {
405 putfh = (nfs41_putfh_args *)argarray[rcount-1].arg;
406 file = putfh->file;
407 }
408 secinfo_status = nfs41_secinfo_noname(session, file, secinfo);
409 if (secinfo_status) {
410 eprintf("nfs41_secinfo_noname failed with %d\n",
411 secinfo_status);
412 nfs41_recovery_finish(session->client);
413 if (op1 == OP_SEQUENCE)
414 nfs41_session_free_slot(session, args->sa_slotid);
415 goto out_free_slot;
416 }
417 }
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
423 goto do_retry;
424 } else {
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);
430 }
431 break;
432 }
433 }
434 }
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);
443 goto out;
444 }
445 }
446 out_free_slot:
447 if (op1 == OP_SEQUENCE)
448 nfs41_session_free_slot(session, args->sa_slotid);
449 out:
450 return status;
451
452 do_retry:
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);
456 goto retry;
457 }