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 "nfs41_ops.h"
27 #include "nfs41_callback.h"
28 #include "daemon_debug.h"
29
30
31 #define FDLVL 2 /* dprintf level for file device logging */
32
33
34 /* pnfs_file_device_list */
35 struct pnfs_file_device_list {
36 struct list_entry head;
37 CRITICAL_SECTION lock;
38 };
39
40 #define device_entry(pos) list_container(pos, pnfs_file_device, entry)
41
42
file_device_create(IN const unsigned char * deviceid,IN struct pnfs_file_device_list * devices,OUT pnfs_file_device ** device_out)43 static enum pnfs_status file_device_create(
44 IN const unsigned char *deviceid,
45 IN struct pnfs_file_device_list *devices,
46 OUT pnfs_file_device **device_out)
47 {
48 enum pnfs_status status = PNFS_SUCCESS;
49 pnfs_file_device *device;
50
51 device = calloc(1, sizeof(pnfs_file_device));
52 if (device == NULL) {
53 status = PNFSERR_RESOURCES;
54 goto out;
55 }
56
57 memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
58 device->devices = devices;
59 InitializeCriticalSection(&device->device.lock);
60 *device_out = device;
61 out:
62 return status;
63 }
64
file_device_free(IN pnfs_file_device * device)65 static void file_device_free(
66 IN pnfs_file_device *device)
67 {
68 free(device->servers.arr);
69 free(device->stripes.arr);
70 DeleteCriticalSection(&device->device.lock);
71 free(device);
72 }
73
deviceid_compare(const struct list_entry * entry,const void * deviceid)74 static int deviceid_compare(
75 const struct list_entry *entry,
76 const void *deviceid)
77 {
78 const pnfs_file_device *device = device_entry(entry);
79 return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
80 }
81
file_device_find_or_create(IN const unsigned char * deviceid,IN struct pnfs_file_device_list * devices,OUT pnfs_file_device ** device_out)82 static enum pnfs_status file_device_find_or_create(
83 IN const unsigned char *deviceid,
84 IN struct pnfs_file_device_list *devices,
85 OUT pnfs_file_device **device_out)
86 {
87 struct list_entry *entry;
88 enum pnfs_status status;
89
90 dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n");
91
92 EnterCriticalSection(&devices->lock);
93
94 /* search for an existing device */
95 entry = list_search(&devices->head, deviceid, deviceid_compare);
96 if (entry == NULL) {
97 /* create a new device */
98 pnfs_file_device *device;
99 status = file_device_create(deviceid, devices, &device);
100 if (status == PNFS_SUCCESS) {
101 /* add it to the list */
102 list_add_tail(&devices->head, &device->entry);
103 *device_out = device;
104
105 dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
106 "returning new device %p\n", device);
107 } else {
108 dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
109 "returning %s\n", pnfs_error_string(status));
110 }
111 } else {
112 *device_out = device_entry(entry);
113 status = PNFS_SUCCESS;
114
115 dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
116 "returning existing device %p\n", *device_out);
117 }
118
119 LeaveCriticalSection(&devices->lock);
120 return status;
121 }
122
123
pnfs_file_device_list_create(OUT struct pnfs_file_device_list ** devices_out)124 enum pnfs_status pnfs_file_device_list_create(
125 OUT struct pnfs_file_device_list **devices_out)
126 {
127 enum pnfs_status status = PNFS_SUCCESS;
128 struct pnfs_file_device_list *devices;
129
130 devices = calloc(1, sizeof(struct pnfs_file_device_list));
131 if (devices == NULL) {
132 status = PNFSERR_RESOURCES;
133 goto out;
134 }
135
136 list_init(&devices->head);
137 InitializeCriticalSection(&devices->lock);
138
139 *devices_out = devices;
140 out:
141 return status;
142 }
143
pnfs_file_device_list_free(IN struct pnfs_file_device_list * devices)144 void pnfs_file_device_list_free(
145 IN struct pnfs_file_device_list *devices)
146 {
147 struct list_entry *entry, *tmp;
148
149 EnterCriticalSection(&devices->lock);
150
151 list_for_each_tmp(entry, tmp, &devices->head)
152 file_device_free(device_entry(entry));
153
154 LeaveCriticalSection(&devices->lock);
155 DeleteCriticalSection(&devices->lock);
156 free(devices);
157 }
158
pnfs_file_device_list_invalidate(IN struct pnfs_file_device_list * devices)159 void pnfs_file_device_list_invalidate(
160 IN struct pnfs_file_device_list *devices)
161 {
162 struct list_entry *entry, *tmp;
163 pnfs_file_device *device;
164
165 dprintf(FDLVL, "--> pnfs_file_device_list_invalidate()\n");
166
167 EnterCriticalSection(&devices->lock);
168
169 list_for_each_tmp(entry, tmp, &devices->head) {
170 device = device_entry(entry);
171 EnterCriticalSection(&device->device.lock);
172 /* if there are layouts still using the device, flag it
173 * as revoked and clean up on last reference */
174 if (device->device.layout_count) {
175 device->device.status |= PNFS_DEVICE_REVOKED;
176 LeaveCriticalSection(&device->device.lock);
177 } else {
178 LeaveCriticalSection(&device->device.lock);
179 /* no layouts are using it, so it's safe to free */
180 list_remove(entry);
181 file_device_free(device);
182 }
183 }
184
185 LeaveCriticalSection(&devices->lock);
186
187 dprintf(FDLVL, "<-- pnfs_file_device_list_invalidate()\n");
188 }
189
190
191 /* pnfs_file_device */
pnfs_file_device_get(IN nfs41_session * session,IN struct pnfs_file_device_list * devices,IN unsigned char * deviceid,OUT pnfs_file_device ** device_out)192 enum pnfs_status pnfs_file_device_get(
193 IN nfs41_session *session,
194 IN struct pnfs_file_device_list *devices,
195 IN unsigned char *deviceid,
196 OUT pnfs_file_device **device_out)
197 {
198 pnfs_file_device *device;
199 enum pnfs_status status;
200 enum nfsstat4 nfsstat;
201
202 dprintf(FDLVL, "--> pnfs_file_device_get()\n");
203
204 status = file_device_find_or_create(deviceid, devices, &device);
205 if (status)
206 goto out;
207
208 EnterCriticalSection(&device->device.lock);
209
210 /* don't give out a device that's been revoked */
211 if (device->device.status & PNFS_DEVICE_REVOKED)
212 status = PNFSERR_NO_DEVICE;
213 else if (device->device.status & PNFS_DEVICE_GRANTED)
214 status = PNFS_SUCCESS;
215 else {
216 nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device);
217 if (nfsstat == NFS4_OK) {
218 device->device.status = PNFS_DEVICE_GRANTED;
219 status = PNFS_SUCCESS;
220
221 dprintf(FDLVL, "Received device info:\n");
222 dprint_device(FDLVL, device);
223 } else {
224 status = PNFSERR_NO_DEVICE;
225
226 eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n",
227 nfs_error_string(nfsstat));
228 }
229 }
230
231 if (status == PNFS_SUCCESS) {
232 device->device.layout_count++;
233 dprintf(FDLVL, "pnfs_file_device_get() -> %u\n",
234 device->device.layout_count);
235 *device_out = device;
236 }
237
238 LeaveCriticalSection(&device->device.lock);
239 out:
240 dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n",
241 pnfs_error_string(status));
242 return status;
243 }
244
pnfs_file_device_put(IN pnfs_file_device * device)245 void pnfs_file_device_put(
246 IN pnfs_file_device *device)
247 {
248 uint32_t count;
249 EnterCriticalSection(&device->device.lock);
250 count = --device->device.layout_count;
251 dprintf(FDLVL, "pnfs_file_device_put() -> %u\n", count);
252
253 /* if the device was revoked, remove/free the device on last reference */
254 if (count == 0 && device->device.status & PNFS_DEVICE_REVOKED) {
255 EnterCriticalSection(&device->devices->lock);
256 list_remove(&device->entry);
257 LeaveCriticalSection(&device->devices->lock);
258
259 LeaveCriticalSection(&device->device.lock);
260
261 file_device_free(device);
262 dprintf(FDLVL, "revoked file device freed after last reference\n");
263 } else {
264 LeaveCriticalSection(&device->device.lock);
265 }
266 }
267
data_client_status(IN pnfs_data_server * server,OUT nfs41_client ** client_out)268 static enum pnfs_status data_client_status(
269 IN pnfs_data_server *server,
270 OUT nfs41_client **client_out)
271 {
272 enum pnfs_status status = PNFSERR_NOT_CONNECTED;
273
274 if (server->client) {
275 dprintf(FDLVL, "pnfs_data_server_client() returning "
276 "existing client %llu\n", server->client->clnt_id);
277 *client_out = server->client;
278 status = PNFS_SUCCESS;
279 }
280 return status;
281 }
282
pnfs_data_server_client(IN nfs41_root * root,IN pnfs_data_server * server,IN uint32_t default_lease,OUT nfs41_client ** client_out)283 enum pnfs_status pnfs_data_server_client(
284 IN nfs41_root *root,
285 IN pnfs_data_server *server,
286 IN uint32_t default_lease,
287 OUT nfs41_client **client_out)
288 {
289 int status;
290 enum pnfs_status pnfsstat;
291
292 dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n",
293 server->addrs.arr[0].uaddr);
294
295 /* if we've already created the client, return it */
296 AcquireSRWLockShared(&server->lock);
297 pnfsstat = data_client_status(server, client_out);
298 ReleaseSRWLockShared(&server->lock);
299
300 if (!pnfsstat)
301 goto out;
302
303 AcquireSRWLockExclusive(&server->lock);
304
305 pnfsstat = data_client_status(server, client_out);
306 if (pnfsstat) {
307 status = nfs41_root_mount_addrs(root, &server->addrs, 1, default_lease,
308 &server->client);
309 if (status) {
310 dprintf(FDLVL, "data_client_create('%s') failed with %d\n",
311 server->addrs.arr[0].uaddr, status);
312 } else {
313 *client_out = server->client;
314 pnfsstat = PNFS_SUCCESS;
315
316 dprintf(FDLVL, "pnfs_data_server_client() returning new client "
317 "%llu\n", server->client->clnt_id);
318 }
319 }
320
321 ReleaseSRWLockExclusive(&server->lock);
322 out:
323 return pnfsstat;
324 }
325
326
327 /* CB_NOTIFY_DEVICEID */
pnfs_file_device_notify(IN struct pnfs_file_device_list * devices,IN const struct notify_deviceid4 * change)328 enum pnfs_status pnfs_file_device_notify(
329 IN struct pnfs_file_device_list *devices,
330 IN const struct notify_deviceid4 *change)
331 {
332 struct list_entry *entry;
333 enum pnfs_status status = PNFSERR_NO_DEVICE;
334
335 dprintf(FDLVL, "--> pnfs_file_device_notify(%u, %0llX:%0llX)\n",
336 change->type, change->deviceid);
337
338 if (change->layouttype != PNFS_LAYOUTTYPE_FILE) {
339 status = PNFSERR_NOT_SUPPORTED;
340 goto out;
341 }
342
343 EnterCriticalSection(&devices->lock);
344
345 entry = list_search(&devices->head, change->deviceid, deviceid_compare);
346 if (entry) {
347 dprintf(FDLVL, "found file device %p\n", device_entry(entry));
348
349 if (change->type == NOTIFY_DEVICEID4_CHANGE) {
350 /* if (change->immediate) ... */
351 dprintf(FDLVL, "CHANGE (%u)\n", change->immediate);
352 } else if (change->type == NOTIFY_DEVICEID4_DELETE) {
353 /* This notification MUST NOT be sent if the client
354 * has a layout that refers to the device ID. */
355 dprintf(FDLVL, "DELETE\n");
356 }
357 status = PNFS_SUCCESS;
358 }
359
360 LeaveCriticalSection(&devices->lock);
361 out:
362 dprintf(FDLVL, "<-- pnfs_file_device_notify() returning %s\n",
363 pnfs_error_string(status));
364 return status;
365 }
366