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 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 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 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 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 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 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 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 */ 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 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 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 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 */ 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