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 #include <stdio.h>
25
26 #include "wintirpc.h"
27 #include "rpc/rpc.h"
28
29 #include "name_cache.h"
30 #include "daemon_debug.h"
31 #include "nfs41.h"
32 #include "util.h"
33
34
35 #define SRVLVL 2 /* dprintf level for server logging */
36
37
38 /* nfs41_server_list */
39 struct server_list {
40 struct list_entry head;
41 CRITICAL_SECTION lock;
42 };
43 static struct server_list g_server_list;
44
45 #define server_entry(pos) list_container(pos, nfs41_server, entry)
46
47
nfs41_server_list_init()48 void nfs41_server_list_init()
49 {
50 list_init(&g_server_list.head);
51 InitializeCriticalSection(&g_server_list.lock);
52 }
53
54 /* http://tools.ietf.org/html/rfc5661#section-1.6
55 * 1.6. General Definitions: Server Owner:
56 * "When the client has two connections each to a peer with the same major
57 * identifier, the client assumes that both peers are the same server (the
58 * server namespace is the same via each connection)" */
59
60 /* http://tools.ietf.org/html/rfc5661#section-2.10.4
61 * 2.10.4. Server Scope
62 * "When the server scope values are the same, server owner value may be
63 * validly compared. In cases where the server scope values are different,
64 * server owner values are treated as different even if they contain all
65 * identical bytes." */
66
67 /* given these definitions, we require that both the server_owner.major_id
68 * and server_scope are identical when matching instances of nfs41_server */
69
70 struct server_info {
71 const char *scope;
72 const char *owner;
73 };
74
server_compare(const struct list_entry * entry,const void * value)75 static int server_compare(
76 const struct list_entry *entry,
77 const void *value)
78 {
79 const nfs41_server *server = server_entry(entry);
80 const struct server_info *info = (const struct server_info*)value;
81 const int diff = strncmp(server->scope, info->scope, NFS4_OPAQUE_LIMIT);
82 return diff ? diff : strncmp(server->owner, info->owner, NFS4_OPAQUE_LIMIT);
83 }
84
server_entry_find(IN struct server_list * servers,IN const struct server_info * info,OUT struct list_entry ** entry_out)85 static int server_entry_find(
86 IN struct server_list *servers,
87 IN const struct server_info *info,
88 OUT struct list_entry **entry_out)
89 {
90 *entry_out = list_search(&servers->head, info, server_compare);
91 return *entry_out ? NO_ERROR : ERROR_FILE_NOT_FOUND;
92 }
93
server_create(IN const struct server_info * info,OUT nfs41_server ** server_out)94 static int server_create(
95 IN const struct server_info *info,
96 OUT nfs41_server **server_out)
97 {
98 int status = NO_ERROR;
99 nfs41_server *server;
100
101 server = calloc(1, sizeof(nfs41_server));
102 if (server == NULL) {
103 status = GetLastError();
104 eprintf("failed to allocate server %s\n", info->owner);
105 goto out;
106 }
107
108 StringCchCopyA(server->scope, NFS4_OPAQUE_LIMIT, info->scope);
109 StringCchCopyA(server->owner, NFS4_OPAQUE_LIMIT, info->owner);
110 InitializeSRWLock(&server->addrs.lock);
111 nfs41_superblock_list_init(&server->superblocks);
112
113 status = nfs41_name_cache_create(&server->name_cache);
114 if (status) {
115 eprintf("nfs41_name_cache_create() failed with %d\n", status);
116 goto out_free;
117 }
118 out:
119 *server_out = server;
120 return status;
121
122 out_free:
123 free(server);
124 server = NULL;
125 goto out;
126 }
127
server_free(IN nfs41_server * server)128 static void server_free(
129 IN nfs41_server *server)
130 {
131 dprintf(SRVLVL, "server_free(%s)\n", server->owner);
132 nfs41_superblock_list_free(&server->superblocks);
133 nfs41_name_cache_free(&server->name_cache);
134 free(server);
135 }
136
server_ref_locked(IN nfs41_server * server)137 static __inline void server_ref_locked(
138 IN nfs41_server *server)
139 {
140 server->ref_count++;
141 dprintf(SRVLVL, "nfs41_server_ref(%s) count %d\n",
142 server->owner, server->ref_count);
143 }
144
nfs41_server_ref(IN nfs41_server * server)145 void nfs41_server_ref(
146 IN nfs41_server *server)
147 {
148 EnterCriticalSection(&g_server_list.lock);
149
150 server_ref_locked(server);
151
152 LeaveCriticalSection(&g_server_list.lock);
153 }
154
nfs41_server_deref(IN nfs41_server * server)155 void nfs41_server_deref(
156 IN nfs41_server *server)
157 {
158 EnterCriticalSection(&g_server_list.lock);
159
160 server->ref_count--;
161 dprintf(SRVLVL, "nfs41_server_deref(%s) count %d\n",
162 server->owner, server->ref_count);
163 if (server->ref_count == 0) {
164 list_remove(&server->entry);
165 server_free(server);
166 }
167
168 LeaveCriticalSection(&g_server_list.lock);
169 }
170
server_addrs_add(IN OUT struct server_addrs * addrs,IN const netaddr4 * addr)171 static void server_addrs_add(
172 IN OUT struct server_addrs *addrs,
173 IN const netaddr4 *addr)
174 {
175 /* we keep a list of addrs used to connect to each server. once it gets
176 * bigger than NFS41_ADDRS_PER_SERVER, overwrite the oldest addrs. use
177 * server_addrs.next_index to implement a circular array */
178
179 AcquireSRWLockExclusive(&addrs->lock);
180
181 if (multi_addr_find(&addrs->addrs, addr, NULL)) {
182 dprintf(SRVLVL, "server_addrs_add() found existing addr '%s'.\n",
183 addr->uaddr);
184 } else {
185 /* overwrite the address at 'next_index' */
186 StringCchCopyA(addrs->addrs.arr[addrs->next_index].netid,
187 NFS41_NETWORK_ID_LEN+1, addr->netid);
188 StringCchCopyA(addrs->addrs.arr[addrs->next_index].uaddr,
189 NFS41_UNIVERSAL_ADDR_LEN+1, addr->uaddr);
190
191 /* increment/wrap next_index */
192 addrs->next_index = (addrs->next_index + 1) % NFS41_ADDRS_PER_SERVER;
193 /* update addrs.count if necessary */
194 if (addrs->addrs.count < addrs->next_index)
195 addrs->addrs.count = addrs->next_index;
196
197 dprintf(SRVLVL, "server_addrs_add() added new addr '%s'.\n",
198 addr->uaddr);
199 }
200 ReleaseSRWLockExclusive(&addrs->lock);
201 }
202
nfs41_server_addrs(IN nfs41_server * server,OUT multi_addr4 * addrs)203 void nfs41_server_addrs(
204 IN nfs41_server *server,
205 OUT multi_addr4 *addrs)
206 {
207 struct server_addrs *saddrs = &server->addrs;
208 uint32_t i, j;
209
210 /* make a copy of the server's addrs, with most recent first */
211 AcquireSRWLockShared(&saddrs->lock);
212 j = saddrs->next_index;
213 for (i = 0; i < saddrs->addrs.count; i++) {
214 /* decrement/wrap j */
215 j = (NFS41_ADDRS_PER_SERVER + j - 1) % NFS41_ADDRS_PER_SERVER;
216 memcpy(&addrs->arr[i], &saddrs->addrs.arr[j], sizeof(netaddr4));
217 }
218 ReleaseSRWLockShared(&saddrs->lock);
219 }
220
nfs41_server_find_or_create(IN const char * server_owner_major_id,IN const char * server_scope,IN const netaddr4 * addr,OUT nfs41_server ** server_out)221 int nfs41_server_find_or_create(
222 IN const char *server_owner_major_id,
223 IN const char *server_scope,
224 IN const netaddr4 *addr,
225 OUT nfs41_server **server_out)
226 {
227 struct server_info info;
228 struct list_entry *entry;
229 nfs41_server *server;
230 int status;
231
232 info.owner = server_owner_major_id;
233 info.scope = server_scope;
234
235 dprintf(SRVLVL, "--> nfs41_server_find_or_create(%s)\n", info.owner);
236
237 EnterCriticalSection(&g_server_list.lock);
238
239 /* search for an existing server */
240 entry = list_search(&g_server_list.head, &info, server_compare);
241 if (entry == NULL) {
242 /* create a new server */
243 status = server_create(&info, &server);
244 if (status == NO_ERROR) {
245 /* add it to the list */
246 list_add_tail(&g_server_list.head, &server->entry);
247 *server_out = server;
248
249 dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
250 "returning new server %p\n", server);
251 } else {
252 dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
253 "returning %d\n", status);
254 }
255 } else {
256 server = server_entry(entry);
257 status = NO_ERROR;
258
259 dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
260 "returning existing server %p\n", server);
261 }
262
263 if (server) {
264 /* register the address used to connect */
265 server_addrs_add(&server->addrs, addr);
266
267 server_ref_locked(server);
268 }
269
270 *server_out = server;
271 LeaveCriticalSection(&g_server_list.lock);
272 return status;
273 }
274
nfs41_server_resolve(IN const char * hostname,IN unsigned short port,OUT multi_addr4 * addrs)275 int nfs41_server_resolve(
276 IN const char *hostname,
277 IN unsigned short port,
278 OUT multi_addr4 *addrs)
279 {
280 int status = ERROR_BAD_NET_NAME;
281 char service[16];
282 struct addrinfo hints = { 0 }, *res, *info;
283 struct netconfig *nconf;
284 struct netbuf addr;
285 char *netid, *uaddr;
286
287 dprintf(SRVLVL, "--> nfs41_server_resolve(%s:%u)\n",
288 hostname, port);
289
290 addrs->count = 0;
291
292 StringCchPrintfA(service, 16, "%u", port);
293
294 /* request a list of tcp addrs for the given hostname,port */
295 hints.ai_family = AF_UNSPEC;
296 hints.ai_socktype = SOCK_STREAM;
297 hints.ai_protocol = IPPROTO_TCP;
298
299 if (getaddrinfo(hostname, service, &hints, &res) != 0)
300 goto out;
301
302 for (info = res; info != NULL; info = info->ai_next) {
303 /* find the appropriate entry in /etc/netconfig */
304 switch (info->ai_family) {
305 case AF_INET: netid = "tcp"; break;
306 case AF_INET6: netid = "tcp6"; break;
307 default: continue;
308 }
309
310 nconf = getnetconfigent(netid);
311 if (nconf == NULL)
312 continue;
313
314 /* convert to a transport-independent universal address */
315 addr.buf = info->ai_addr;
316 addr.maxlen = addr.len = (unsigned int)info->ai_addrlen;
317
318 uaddr = taddr2uaddr(nconf, &addr);
319 freenetconfigent(nconf);
320
321 if (uaddr == NULL)
322 continue;
323
324 StringCchCopyA(addrs->arr[addrs->count].netid,
325 NFS41_NETWORK_ID_LEN+1, netid);
326 StringCchCopyA(addrs->arr[addrs->count].uaddr,
327 NFS41_UNIVERSAL_ADDR_LEN+1, uaddr);
328 freeuaddr(uaddr);
329
330 status = NO_ERROR;
331 if (++addrs->count >= NFS41_ADDRS_PER_SERVER)
332 break;
333 }
334 freeaddrinfo(res);
335 out:
336 if (status)
337 dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
338 "error %d\n", hostname, port, status);
339 else
340 dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
341 "%s\n", hostname, port, addrs->arr[0].uaddr);
342 return status;
343 }
344