xref: /reactos/base/services/nfsd/namespace.c (revision c2c66aff)
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 <windows.h>
23 #include <strsafe.h>
24 
25 #include "nfs41_ops.h"
26 #include "util.h"
27 #include "daemon_debug.h"
28 
29 
30 #define NSLVL 2 /* dprintf level for namespace logging */
31 
32 
33 #define client_entry(pos) list_container(pos, nfs41_client, root_entry)
34 
35 
36 /* nfs41_root */
nfs41_root_create(IN const char * name,IN uint32_t sec_flavor,IN uint32_t wsize,IN uint32_t rsize,OUT nfs41_root ** root_out)37 int nfs41_root_create(
38     IN const char *name,
39     IN uint32_t sec_flavor,
40     IN uint32_t wsize,
41     IN uint32_t rsize,
42     OUT nfs41_root **root_out)
43 {
44     int status = NO_ERROR;
45     nfs41_root *root;
46 
47     dprintf(NSLVL, "--> nfs41_root_create()\n");
48 
49     root = calloc(1, sizeof(nfs41_root));
50     if (root == NULL) {
51         status = GetLastError();
52         goto out;
53     }
54 
55     list_init(&root->clients);
56     root->wsize = wsize;
57     root->rsize = rsize;
58     InitializeCriticalSection(&root->lock);
59     root->ref_count = 1;
60     root->sec_flavor = sec_flavor;
61 
62     /* generate a unique client_owner */
63     status = nfs41_client_owner(name, sec_flavor, &root->client_owner);
64     if (status) {
65         eprintf("nfs41_client_owner() failed with %d\n", status);
66         free(root);
67         goto out;
68     }
69 
70     *root_out = root;
71 out:
72     dprintf(NSLVL, "<-- nfs41_root_create() returning %d\n", status);
73     return status;
74 }
75 
root_free(IN nfs41_root * root)76 static void root_free(
77     IN nfs41_root *root)
78 {
79     struct list_entry *entry, *tmp;
80 
81     dprintf(NSLVL, "--> nfs41_root_free()\n");
82 
83     /* free clients */
84     list_for_each_tmp(entry, tmp, &root->clients)
85         nfs41_client_free(client_entry(entry));
86     DeleteCriticalSection(&root->lock);
87     free(root);
88 
89     dprintf(NSLVL, "<-- nfs41_root_free()\n");
90 }
91 
nfs41_root_ref(IN nfs41_root * root)92 void nfs41_root_ref(
93     IN nfs41_root *root)
94 {
95     const LONG count = InterlockedIncrement(&root->ref_count);
96 
97     dprintf(NSLVL, "nfs41_root_ref() count %d\n", count);
98 }
99 
nfs41_root_deref(IN nfs41_root * root)100 void nfs41_root_deref(
101     IN nfs41_root *root)
102 {
103     const LONG count = InterlockedDecrement(&root->ref_count);
104 
105     dprintf(NSLVL, "nfs41_root_deref() count %d\n", count);
106     if (count == 0)
107         root_free(root);
108 }
109 
110 
111 /* root_client_find_addrs() */
112 struct cl_addr_info {
113     const multi_addr4       *addrs;
114     uint32_t                roles;
115 };
116 
cl_addr_compare(IN const struct list_entry * entry,IN const void * value)117 static int cl_addr_compare(
118     IN const struct list_entry *entry,
119     IN const void *value)
120 {
121     nfs41_client *client = client_entry(entry);
122     const struct cl_addr_info *info = (const struct cl_addr_info*)value;
123     uint32_t i, roles;
124 
125     /* match any of the desired roles */
126     AcquireSRWLockShared(&client->exid_lock);
127     roles = info->roles & client->roles;
128     ReleaseSRWLockShared(&client->exid_lock);
129 
130     if (roles == 0)
131         return ERROR_FILE_NOT_FOUND;
132 
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))
136             return NO_ERROR;
137 
138     return ERROR_FILE_NOT_FOUND;
139 }
140 
root_client_find_addrs(IN nfs41_root * root,IN const multi_addr4 * addrs,IN bool_t is_data,OUT nfs41_client ** client_out)141 static int root_client_find_addrs(
142     IN nfs41_root *root,
143     IN const multi_addr4 *addrs,
144     IN bool_t is_data,
145     OUT nfs41_client **client_out)
146 {
147     struct cl_addr_info info;
148     struct list_entry *entry;
149     int status;
150 
151     dprintf(NSLVL, "--> root_client_find_addrs()\n");
152 
153     info.addrs = addrs;
154     info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
155 
156     entry = list_search(&root->clients, &info, cl_addr_compare);
157     if (entry) {
158         *client_out = client_entry(entry);
159         status = NO_ERROR;
160         dprintf(NSLVL, "<-- root_client_find_addrs() returning 0x%p\n",
161             *client_out);
162     } else {
163         status = ERROR_FILE_NOT_FOUND;
164         dprintf(NSLVL, "<-- root_client_find_addrs() failed with %d\n",
165             status);
166     }
167     return status;
168 }
169 
170 /* root_client_find() */
171 struct cl_exid_info {
172     const nfs41_exchange_id_res *exchangeid;
173     uint32_t                roles;
174 };
175 
cl_exid_compare(IN const struct list_entry * entry,IN const void * value)176 static int cl_exid_compare(
177     IN const struct list_entry *entry,
178     IN const void *value)
179 {
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;
183 
184     AcquireSRWLockShared(&client->exid_lock);
185 
186     /* match any of the desired roles */
187     if ((info->roles & client->roles) == 0)
188         goto out;
189     /* match server_owner.major_id */
190     if (strncmp(info->exchangeid->server_owner.so_major_id,
191         client->server->owner, NFS4_OPAQUE_LIMIT) != 0)
192         goto out;
193     /* match server_scope */
194     if (strncmp(info->exchangeid->server_scope,
195         client->server->scope, NFS4_OPAQUE_LIMIT) != 0)
196         goto out;
197     /* match clientid */
198     if (info->exchangeid->clientid != client->clnt_id)
199         goto out;
200 
201     status = NO_ERROR;
202 out:
203     ReleaseSRWLockShared(&client->exid_lock);
204     return status;
205 }
206 
root_client_find(IN nfs41_root * root,IN const nfs41_exchange_id_res * exchangeid,IN bool_t is_data,OUT nfs41_client ** client_out)207 static int root_client_find(
208     IN nfs41_root *root,
209     IN const nfs41_exchange_id_res *exchangeid,
210     IN bool_t is_data,
211     OUT nfs41_client **client_out)
212 {
213     struct cl_exid_info info;
214     struct list_entry *entry;
215     int status;
216 
217     dprintf(NSLVL, "--> root_client_find()\n");
218 
219     info.exchangeid = exchangeid;
220     info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
221 
222     entry = list_search(&root->clients, &info, cl_exid_compare);
223     if (entry) {
224         *client_out = client_entry(entry);
225         status = NO_ERROR;
226         dprintf(NSLVL, "<-- root_client_find() returning 0x%p\n",
227             *client_out);
228     } else {
229         status = ERROR_FILE_NOT_FOUND;
230         dprintf(NSLVL, "<-- root_client_find() failed with %d\n",
231             status);
232     }
233     return status;
234 }
235 
session_get_lease(IN nfs41_session * session,IN OPTIONAL uint32_t lease_time)236 static int session_get_lease(
237     IN nfs41_session *session,
238     IN OPTIONAL uint32_t lease_time)
239 {
240     bool_t use_mds_lease;
241     int status;
242 
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);
256 
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 } };
261 
262         status = nfs41_getattr(session, NULL, &attr_request, &info);
263         if (status) {
264             eprintf("nfs41_getattr() failed with %s\n",
265                 nfs_error_string(status));
266             status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
267             goto out;
268         }
269         lease_time = info.lease_time;
270     }
271 
272     status = nfs41_session_set_lease(session, lease_time);
273     if (status) {
274         eprintf("nfs41_session_set_lease() failed %d\n", status);
275         goto out;
276     }
277 out:
278     return status;
279 }
280 
root_client_create(IN nfs41_root * root,IN nfs41_rpc_clnt * rpc,IN bool_t is_data,IN OPTIONAL uint32_t lease_time,IN const nfs41_exchange_id_res * exchangeid,OUT nfs41_client ** client_out)281 static int root_client_create(
282     IN nfs41_root *root,
283     IN nfs41_rpc_clnt *rpc,
284     IN bool_t is_data,
285     IN OPTIONAL uint32_t lease_time,
286     IN const nfs41_exchange_id_res *exchangeid,
287     OUT nfs41_client **client_out)
288 {
289     nfs41_client *client;
290     nfs41_session *session;
291     int status;
292 
293     /* create client (transfers ownership of rpc to client) */
294     status = nfs41_client_create(rpc, &root->client_owner,
295         is_data, exchangeid, &client);
296     if (status) {
297         eprintf("nfs41_client_create() failed with %d\n", status);
298         goto out;
299     }
300     client->root = root;
301     rpc->client = client;
302 
303     /* create session (and client takes ownership) */
304     status = nfs41_session_create(client, &session);
305     if (status) {
306         eprintf("nfs41_session_create failed %d\n", status);
307         goto out_err;
308     }
309 
310     if (!is_data) {
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;
317             goto out_err;
318         }
319     }
320 
321     /* get least time and start session renewal thread */
322     status = session_get_lease(session, lease_time);
323     if (status)
324         goto out_err;
325 
326     *client_out = client;
327 out:
328     return status;
329 
330 out_err:
331     nfs41_client_free(client);
332     goto out;
333 }
334 
nfs41_root_mount_addrs(IN nfs41_root * root,IN const multi_addr4 * addrs,IN bool_t is_data,IN OPTIONAL uint32_t lease_time,OUT nfs41_client ** client_out)335 int nfs41_root_mount_addrs(
336     IN nfs41_root *root,
337     IN const multi_addr4 *addrs,
338     IN bool_t is_data,
339     IN OPTIONAL uint32_t lease_time,
340     OUT nfs41_client **client_out)
341 {
342     nfs41_exchange_id_res exchangeid = { 0 };
343     nfs41_rpc_clnt *rpc;
344     nfs41_client *client, *existing;
345     int status;
346 
347     dprintf(NSLVL, "--> nfs41_root_mount_addrs()\n");
348 
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);
353 
354     if (status == NO_ERROR)
355         goto out;
356 
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);
360     if (status) {
361         eprintf("nfs41_rpc_clnt_create() failed %d\n", status);
362         goto out;
363     }
364 
365     /* get a clientid with exchangeid */
366     status = nfs41_exchange_id(rpc, &root->client_owner,
367         nfs41_exchange_id_flags(is_data), &exchangeid);
368     if (status) {
369         eprintf("nfs41_exchange_id() failed %s\n", nfs_error_string(status));
370         status = ERROR_BAD_NET_RESP;
371         goto out_free_rpc;
372     }
373 
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);
378 
379     if (status == NO_ERROR)
380         goto out_free_rpc;
381 
382     /* create a client for this clientid */
383     status = root_client_create(root, rpc, is_data,
384         lease_time, &exchangeid, &client);
385     if (status) {
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 */
389         goto out;
390     }
391 
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);
397 
398     if (status) {
399         dprintf(NSLVL, "caching new client 0x%p\n", client);
400 
401         /* the client is not a duplicate, so add it to the list */
402         list_add_tail(&root->clients, &client->root_entry);
403         status = NO_ERROR;
404     } else {
405         dprintf(NSLVL, "created a duplicate client 0x%p! using "
406             "existing client 0x%p instead\n", client, existing);
407 
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);
411         client = existing;
412     }
413     LeaveCriticalSection(&root->lock);
414 
415 out:
416     if (status == NO_ERROR)
417         *client_out = client;
418     dprintf(NSLVL, "<-- nfs41_root_mount_addrs() returning %d\n", status);
419     return status;
420 
421 out_free_rpc:
422     nfs41_rpc_clnt_free(rpc);
423     goto out;
424 }
425 
426 
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. */
referral_mount_location(IN nfs41_root * root,IN const fs_location4 * loc,OUT nfs41_client ** client_out)437 static int referral_mount_location(
438     IN nfs41_root *root,
439     IN const fs_location4 *loc,
440     OUT nfs41_client **client_out)
441 {
442     multi_addr4 addrs;
443     int status = ERROR_BAD_NET_NAME;
444     uint32_t i;
445 
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;
451 
452         status = nfs41_root_mount_addrs(root, &addrs, 0, 0, client_out);
453         if (status == NO_ERROR)
454             break;
455     }
456     return status;
457 }
458 
nfs41_root_mount_referral(IN nfs41_root * root,IN const fs_locations4 * locations,OUT const fs_location4 ** loc_out,OUT nfs41_client ** client_out)459 int nfs41_root_mount_referral(
460     IN nfs41_root *root,
461     IN const fs_locations4 *locations,
462     OUT const fs_location4 **loc_out,
463     OUT nfs41_client **client_out)
464 {
465     int status = ERROR_BAD_NET_NAME;
466     uint32_t i;
467 
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];
474             break;
475         }
476     }
477     return status;
478 }
479