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_ops.h"
27 #include "daemon_debug.h"
30 #define NSLVL 2 /* dprintf level for namespace logging */
33 #define client_entry(pos) list_container(pos, nfs41_client, root_entry)
37 int nfs41_root_create(
39 IN
uint32_t sec_flavor
,
42 OUT nfs41_root
**root_out
)
44 int status
= NO_ERROR
;
47 dprintf(NSLVL
, "--> nfs41_root_create()\n");
49 root
= calloc(1, sizeof(nfs41_root
));
51 status
= GetLastError();
55 list_init(&root
->clients
);
58 InitializeCriticalSection(&root
->lock
);
60 root
->sec_flavor
= sec_flavor
;
62 /* generate a unique client_owner */
63 status
= nfs41_client_owner(name
, sec_flavor
, &root
->client_owner
);
65 eprintf("nfs41_client_owner() failed with %d\n", status
);
72 dprintf(NSLVL
, "<-- nfs41_root_create() returning %d\n", status
);
76 static void root_free(
79 struct list_entry
*entry
, *tmp
;
81 dprintf(NSLVL
, "--> nfs41_root_free()\n");
84 list_for_each_tmp(entry
, tmp
, &root
->clients
)
85 nfs41_client_free(client_entry(entry
));
86 DeleteCriticalSection(&root
->lock
);
89 dprintf(NSLVL
, "<-- nfs41_root_free()\n");
95 const LONG count
= InterlockedIncrement(&root
->ref_count
);
97 dprintf(NSLVL
, "nfs41_root_ref() count %d\n", count
);
100 void nfs41_root_deref(
103 const LONG count
= InterlockedDecrement(&root
->ref_count
);
105 dprintf(NSLVL
, "nfs41_root_deref() count %d\n", count
);
111 /* root_client_find_addrs() */
112 struct cl_addr_info
{
113 const multi_addr4
*addrs
;
117 static int cl_addr_compare(
118 IN
const struct list_entry
*entry
,
119 IN
const void *value
)
121 nfs41_client
*client
= client_entry(entry
);
122 const struct cl_addr_info
*info
= (const struct cl_addr_info
*)value
;
125 /* match any of the desired roles */
126 AcquireSRWLockShared(&client
->exid_lock
);
127 roles
= info
->roles
& client
->roles
;
128 ReleaseSRWLockShared(&client
->exid_lock
);
131 return ERROR_FILE_NOT_FOUND
;
133 /* match any address in 'addrs' with any address in client->rpc->addrs */
134 for (i
= 0; i
< info
->addrs
->count
; i
++)
135 if (multi_addr_find(&client
->rpc
->addrs
, &info
->addrs
->arr
[i
], NULL
))
138 return ERROR_FILE_NOT_FOUND
;
141 static int root_client_find_addrs(
143 IN
const multi_addr4
*addrs
,
145 OUT nfs41_client
**client_out
)
147 struct cl_addr_info info
;
148 struct list_entry
*entry
;
151 dprintf(NSLVL
, "--> root_client_find_addrs()\n");
154 info
.roles
= nfs41_exchange_id_flags(is_data
) & EXCHGID4_FLAG_MASK_PNFS
;
156 entry
= list_search(&root
->clients
, &info
, cl_addr_compare
);
158 *client_out
= client_entry(entry
);
160 dprintf(NSLVL
, "<-- root_client_find_addrs() returning 0x%p\n",
163 status
= ERROR_FILE_NOT_FOUND
;
164 dprintf(NSLVL
, "<-- root_client_find_addrs() failed with %d\n",
170 /* root_client_find() */
171 struct cl_exid_info
{
172 const nfs41_exchange_id_res
*exchangeid
;
176 static int cl_exid_compare(
177 IN
const struct list_entry
*entry
,
178 IN
const void *value
)
180 nfs41_client
*client
= client_entry(entry
);
181 const struct cl_exid_info
*info
= (const struct cl_exid_info
*)value
;
182 int status
= ERROR_FILE_NOT_FOUND
;
184 AcquireSRWLockShared(&client
->exid_lock
);
186 /* match any of the desired roles */
187 if ((info
->roles
& client
->roles
) == 0)
189 /* match server_owner.major_id */
190 if (strncmp(info
->exchangeid
->server_owner
.so_major_id
,
191 client
->server
->owner
, NFS4_OPAQUE_LIMIT
) != 0)
193 /* match server_scope */
194 if (strncmp(info
->exchangeid
->server_scope
,
195 client
->server
->scope
, NFS4_OPAQUE_LIMIT
) != 0)
198 if (info
->exchangeid
->clientid
!= client
->clnt_id
)
203 ReleaseSRWLockShared(&client
->exid_lock
);
207 static int root_client_find(
209 IN
const nfs41_exchange_id_res
*exchangeid
,
211 OUT nfs41_client
**client_out
)
213 struct cl_exid_info info
;
214 struct list_entry
*entry
;
217 dprintf(NSLVL
, "--> root_client_find()\n");
219 info
.exchangeid
= exchangeid
;
220 info
.roles
= nfs41_exchange_id_flags(is_data
) & EXCHGID4_FLAG_MASK_PNFS
;
222 entry
= list_search(&root
->clients
, &info
, cl_exid_compare
);
224 *client_out
= client_entry(entry
);
226 dprintf(NSLVL
, "<-- root_client_find() returning 0x%p\n",
229 status
= ERROR_FILE_NOT_FOUND
;
230 dprintf(NSLVL
, "<-- root_client_find() failed with %d\n",
236 static int session_get_lease(
237 IN nfs41_session
*session
,
238 IN OPTIONAL
uint32_t lease_time
)
240 bool_t use_mds_lease
;
243 /* http://tools.ietf.org/html/rfc5661#section-13.1.1
244 * 13.1.1. Sessions Considerations for Data Servers:
245 * If the reply to EXCHANGE_ID has just the EXCHGID4_FLAG_USE_PNFS_DS role
246 * set, then (as noted in Section 13.6) the client will not be able to
247 * determine the data server's lease_time attribute because GETATTR will
248 * not be permitted. Instead, the rule is that any time a client
249 * receives a layout referring it to a data server that returns just the
250 * EXCHGID4_FLAG_USE_PNFS_DS role, the client MAY assume that the
251 * lease_time attribute from the metadata server that returned the
252 * layout applies to the data server. */
253 AcquireSRWLockShared(&session
->client
->exid_lock
);
254 use_mds_lease
= session
->client
->roles
== EXCHGID4_FLAG_USE_PNFS_DS
;
255 ReleaseSRWLockShared(&session
->client
->exid_lock
);
257 if (!use_mds_lease
) {
258 /* the client is allowed to GETATTR, so query the lease_time */
259 nfs41_file_info info
= { 0 };
260 bitmap4 attr_request
= { 1, { FATTR4_WORD0_LEASE_TIME
, 0, 0 } };
262 status
= nfs41_getattr(session
, NULL
, &attr_request
, &info
);
264 eprintf("nfs41_getattr() failed with %s\n",
265 nfs_error_string(status
));
266 status
= nfs_to_windows_error(status
, ERROR_BAD_NET_RESP
);
269 lease_time
= info
.lease_time
;
272 status
= nfs41_session_set_lease(session
, lease_time
);
274 eprintf("nfs41_session_set_lease() failed %d\n", status
);
281 static int root_client_create(
283 IN nfs41_rpc_clnt
*rpc
,
285 IN OPTIONAL
uint32_t lease_time
,
286 IN
const nfs41_exchange_id_res
*exchangeid
,
287 OUT nfs41_client
**client_out
)
289 nfs41_client
*client
;
290 nfs41_session
*session
;
293 /* create client (transfers ownership of rpc to client) */
294 status
= nfs41_client_create(rpc
, &root
->client_owner
,
295 is_data
, exchangeid
, &client
);
297 eprintf("nfs41_client_create() failed with %d\n", status
);
301 rpc
->client
= client
;
303 /* create session (and client takes ownership) */
304 status
= nfs41_session_create(client
, &session
);
306 eprintf("nfs41_session_create failed %d\n", status
);
311 /* send RECLAIM_COMPLETE, but don't fail on ERR_NOTSUPP */
312 status
= nfs41_reclaim_complete(session
);
313 if (status
&& status
!= NFS4ERR_NOTSUPP
) {
314 eprintf("nfs41_reclaim_complete() failed with %s\n",
315 nfs_error_string(status
));
316 status
= ERROR_BAD_NETPATH
;
321 /* get least time and start session renewal thread */
322 status
= session_get_lease(session
, lease_time
);
326 *client_out
= client
;
331 nfs41_client_free(client
);
335 int nfs41_root_mount_addrs(
337 IN
const multi_addr4
*addrs
,
339 IN OPTIONAL
uint32_t lease_time
,
340 OUT nfs41_client
**client_out
)
342 nfs41_exchange_id_res exchangeid
= { 0 };
344 nfs41_client
*client
, *existing
;
347 dprintf(NSLVL
, "--> nfs41_root_mount_addrs()\n");
349 /* look for an existing client that matches the address and role */
350 EnterCriticalSection(&root
->lock
);
351 status
= root_client_find_addrs(root
, addrs
, is_data
, &client
);
352 LeaveCriticalSection(&root
->lock
);
354 if (status
== NO_ERROR
)
357 /* create an rpc client */
358 status
= nfs41_rpc_clnt_create(addrs
, root
->wsize
, root
->rsize
,
359 root
->uid
, root
->gid
, root
->sec_flavor
, &rpc
);
361 eprintf("nfs41_rpc_clnt_create() failed %d\n", status
);
365 /* get a clientid with exchangeid */
366 status
= nfs41_exchange_id(rpc
, &root
->client_owner
,
367 nfs41_exchange_id_flags(is_data
), &exchangeid
);
369 eprintf("nfs41_exchange_id() failed %s\n", nfs_error_string(status
));
370 status
= ERROR_BAD_NET_RESP
;
374 /* attempt to match existing clients by the exchangeid response */
375 EnterCriticalSection(&root
->lock
);
376 status
= root_client_find(root
, &exchangeid
, is_data
, &client
);
377 LeaveCriticalSection(&root
->lock
);
379 if (status
== NO_ERROR
)
382 /* create a client for this clientid */
383 status
= root_client_create(root
, rpc
, is_data
,
384 lease_time
, &exchangeid
, &client
);
386 eprintf("nfs41_client_create() failed %d\n", status
);
387 /* root_client_create takes care of cleaning up
388 * thus don't go to out_free_rpc */
392 /* because we don't hold the root's lock over session creation,
393 * we could end up creating multiple clients with the same
394 * server and roles */
395 EnterCriticalSection(&root
->lock
);
396 status
= root_client_find(root
, &exchangeid
, is_data
, &existing
);
399 dprintf(NSLVL
, "caching new client 0x%p\n", client
);
401 /* the client is not a duplicate, so add it to the list */
402 list_add_tail(&root
->clients
, &client
->root_entry
);
405 dprintf(NSLVL
, "created a duplicate client 0x%p! using "
406 "existing client 0x%p instead\n", client
, existing
);
408 /* a matching client has been created in parallel, so free
409 * the one we created and use the existing client instead */
410 nfs41_client_free(client
);
413 LeaveCriticalSection(&root
->lock
);
416 if (status
== NO_ERROR
)
417 *client_out
= client
;
418 dprintf(NSLVL
, "<-- nfs41_root_mount_addrs() returning %d\n", status
);
422 nfs41_rpc_clnt_free(rpc
);
427 /* http://tools.ietf.org/html/rfc5661#section-11.9
428 * 11.9. The Attribute fs_locations
429 * An entry in the server array is a UTF-8 string and represents one of a
430 * traditional DNS host name, IPv4 address, IPv6 address, or a zero-length
431 * string. An IPv4 or IPv6 address is represented as a universal address
432 * (see Section 3.3.9 and [15]), minus the netid, and either with or without
433 * the trailing ".p1.p2" suffix that represents the port number. If the
434 * suffix is omitted, then the default port, 2049, SHOULD be assumed. A
435 * zero-length string SHOULD be used to indicate the current address being
436 * used for the RPC call. */
437 static int referral_mount_location(
439 IN
const fs_location4
*loc
,
440 OUT nfs41_client
**client_out
)
443 int status
= ERROR_BAD_NET_NAME
;
446 /* create a client and session for the first available server */
447 for (i
= 0; i
< loc
->server_count
; i
++) {
448 /* XXX: only deals with 'address' as a hostname with default port */
449 status
= nfs41_server_resolve(loc
->servers
[i
].address
, 2049, &addrs
);
450 if (status
) continue;
452 status
= nfs41_root_mount_addrs(root
, &addrs
, 0, 0, client_out
);
453 if (status
== NO_ERROR
)
459 int nfs41_root_mount_referral(
461 IN
const fs_locations4
*locations
,
462 OUT
const fs_location4
**loc_out
,
463 OUT nfs41_client
**client_out
)
465 int status
= ERROR_BAD_NET_NAME
;
468 /* establish a mount to the first available location */
469 for (i
= 0; i
< locations
->location_count
; i
++) {
470 status
= referral_mount_location(root
,
471 &locations
->locations
[i
], client_out
);
472 if (status
== NO_ERROR
) {
473 *loc_out
= &locations
->locations
[i
];