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
29 #include "name_cache.h"
30 #include "daemon_debug.h"
35 #define SRVLVL 2 /* dprintf level for server logging */
38 /* nfs41_server_list */
40 struct list_entry head
;
41 CRITICAL_SECTION lock
;
43 static struct server_list g_server_list
;
45 #define server_entry(pos) list_container(pos, nfs41_server, entry)
48 void nfs41_server_list_init()
50 list_init(&g_server_list
.head
);
51 InitializeCriticalSection(&g_server_list
.lock
);
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)" */
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." */
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 */
75 static int server_compare(
76 const struct list_entry
*entry
,
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
);
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
)
90 *entry_out
= list_search(&servers
->head
, info
, server_compare
);
91 return *entry_out
? NO_ERROR
: ERROR_FILE_NOT_FOUND
;
94 static int server_create(
95 IN
const struct server_info
*info
,
96 OUT nfs41_server
**server_out
)
98 int status
= NO_ERROR
;
101 server
= calloc(1, sizeof(nfs41_server
));
102 if (server
== NULL
) {
103 status
= GetLastError();
104 eprintf("failed to allocate server %s\n", info
->owner
);
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
);
113 status
= nfs41_name_cache_create(&server
->name_cache
);
115 eprintf("nfs41_name_cache_create() failed with %d\n", status
);
119 *server_out
= server
;
128 static void server_free(
129 IN nfs41_server
*server
)
131 dprintf(SRVLVL
, "server_free(%s)\n", server
->owner
);
132 nfs41_superblock_list_free(&server
->superblocks
);
133 nfs41_name_cache_free(&server
->name_cache
);
137 static __inline
void server_ref_locked(
138 IN nfs41_server
*server
)
141 dprintf(SRVLVL
, "nfs41_server_ref(%s) count %d\n",
142 server
->owner
, server
->ref_count
);
145 void nfs41_server_ref(
146 IN nfs41_server
*server
)
148 EnterCriticalSection(&g_server_list
.lock
);
150 server_ref_locked(server
);
152 LeaveCriticalSection(&g_server_list
.lock
);
155 void nfs41_server_deref(
156 IN nfs41_server
*server
)
158 EnterCriticalSection(&g_server_list
.lock
);
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
);
168 LeaveCriticalSection(&g_server_list
.lock
);
171 static void server_addrs_add(
172 IN OUT
struct server_addrs
*addrs
,
173 IN
const netaddr4
*addr
)
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 */
179 AcquireSRWLockExclusive(&addrs
->lock
);
181 if (multi_addr_find(&addrs
->addrs
, addr
, NULL
)) {
182 dprintf(SRVLVL
, "server_addrs_add() found existing addr '%s'.\n",
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
);
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
;
197 dprintf(SRVLVL
, "server_addrs_add() added new addr '%s'.\n",
200 ReleaseSRWLockExclusive(&addrs
->lock
);
203 void nfs41_server_addrs(
204 IN nfs41_server
*server
,
205 OUT multi_addr4
*addrs
)
207 struct server_addrs
*saddrs
= &server
->addrs
;
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
));
218 ReleaseSRWLockShared(&saddrs
->lock
);
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
)
227 struct server_info info
;
228 struct list_entry
*entry
;
229 nfs41_server
*server
;
232 info
.owner
= server_owner_major_id
;
233 info
.scope
= server_scope
;
235 dprintf(SRVLVL
, "--> nfs41_server_find_or_create(%s)\n", info
.owner
);
237 EnterCriticalSection(&g_server_list
.lock
);
239 /* search for an existing server */
240 entry
= list_search(&g_server_list
.head
, &info
, server_compare
);
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
;
249 dprintf(SRVLVL
, "<-- nfs41_server_find_or_create() "
250 "returning new server %p\n", server
);
252 dprintf(SRVLVL
, "<-- nfs41_server_find_or_create() "
253 "returning %d\n", status
);
256 server
= server_entry(entry
);
259 dprintf(SRVLVL
, "<-- nfs41_server_find_or_create() "
260 "returning existing server %p\n", server
);
264 /* register the address used to connect */
265 server_addrs_add(&server
->addrs
, addr
);
267 server_ref_locked(server
);
270 *server_out
= server
;
271 LeaveCriticalSection(&g_server_list
.lock
);
275 int nfs41_server_resolve(
276 IN
const char *hostname
,
277 IN
unsigned short port
,
278 OUT multi_addr4
*addrs
)
280 int status
= ERROR_BAD_NET_NAME
;
282 struct addrinfo hints
= { 0 }, *res
, *info
;
283 struct netconfig
*nconf
;
287 dprintf(SRVLVL
, "--> nfs41_server_resolve(%s:%u)\n",
292 StringCchPrintfA(service
, 16, "%u", port
);
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
;
299 if (getaddrinfo(hostname
, service
, &hints
, &res
) != 0)
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;
310 nconf
= getnetconfigent(netid
);
314 /* convert to a transport-independent universal address */
315 addr
.buf
= info
->ai_addr
;
316 addr
.maxlen
= addr
.len
= (unsigned int)info
->ai_addrlen
;
318 uaddr
= taddr2uaddr(nconf
, &addr
);
319 freenetconfigent(nconf
);
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
);
331 if (++addrs
->count
>= NFS41_ADDRS_PER_SERVER
)
337 dprintf(SRVLVL
, "<-- nfs41_server_resolve(%s:%u) returning "
338 "error %d\n", hostname
, port
, status
);
340 dprintf(SRVLVL
, "<-- nfs41_server_resolve(%s:%u) returning "
341 "%s\n", hostname
, port
, addrs
->arr
[0].uaddr
);