[NFSD]
[reactos.git] / reactos / base / services / nfsd / pnfs_device.c
diff --git a/reactos/base/services/nfsd/pnfs_device.c b/reactos/base/services/nfsd/pnfs_device.c
new file mode 100644 (file)
index 0000000..425da20
--- /dev/null
@@ -0,0 +1,365 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * without any warranty; without even the implied warranty of merchantability
+ * or fitness for a particular purpose.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ */
+
+#include <windows.h>
+#include <strsafe.h>
+#include <stdio.h>
+
+#include "nfs41_ops.h"
+#include "nfs41_callback.h"
+#include "daemon_debug.h"
+
+
+#define FDLVL 2 /* dprintf level for file device logging */
+
+
+/* pnfs_file_device_list */
+struct pnfs_file_device_list {
+    struct list_entry       head;
+    CRITICAL_SECTION        lock;
+};
+
+#define device_entry(pos) list_container(pos, pnfs_file_device, entry)
+
+
+static enum pnfs_status file_device_create(
+    IN const unsigned char *deviceid,
+    IN struct pnfs_file_device_list *devices,
+    OUT pnfs_file_device **device_out)
+{
+    enum pnfs_status status = PNFS_SUCCESS;
+    pnfs_file_device *device;
+
+    device = calloc(1, sizeof(pnfs_file_device));
+    if (device == NULL) {
+        status = PNFSERR_RESOURCES;
+        goto out;
+    }
+
+    memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
+    device->devices = devices;
+    InitializeCriticalSection(&device->device.lock);
+    *device_out = device;
+out:
+    return status;
+}
+
+static void file_device_free(
+    IN pnfs_file_device *device)
+{
+    free(device->servers.arr);
+    free(device->stripes.arr);
+    DeleteCriticalSection(&device->device.lock);
+    free(device);
+}
+
+static int deviceid_compare(
+    const struct list_entry *entry,
+    const void *deviceid)
+{
+    const pnfs_file_device *device = device_entry(entry);
+    return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
+}
+
+static enum pnfs_status file_device_find_or_create(
+    IN const unsigned char *deviceid,
+    IN struct pnfs_file_device_list *devices,
+    OUT pnfs_file_device **device_out)
+{
+    struct list_entry *entry;
+    enum pnfs_status status;
+
+    dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n");
+
+    EnterCriticalSection(&devices->lock);
+
+    /* search for an existing device */
+    entry = list_search(&devices->head, deviceid, deviceid_compare);
+    if (entry == NULL) {
+        /* create a new device */
+        pnfs_file_device *device;
+        status = file_device_create(deviceid, devices, &device);
+        if (status == PNFS_SUCCESS) {
+            /* add it to the list */
+            list_add_tail(&devices->head, &device->entry);
+            *device_out = device;
+
+            dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
+                "returning new device %p\n", device);
+        } else {
+            dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
+                "returning %s\n", pnfs_error_string(status));
+        }
+    } else {
+        *device_out = device_entry(entry);
+        status = PNFS_SUCCESS;
+
+        dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
+            "returning existing device %p\n", *device_out);
+    }
+
+    LeaveCriticalSection(&devices->lock);
+    return status;
+}
+
+
+enum pnfs_status pnfs_file_device_list_create(
+    OUT struct pnfs_file_device_list **devices_out)
+{
+    enum pnfs_status status = PNFS_SUCCESS;
+    struct pnfs_file_device_list *devices;
+
+    devices = calloc(1, sizeof(struct pnfs_file_device_list));
+    if (devices == NULL) {
+        status = PNFSERR_RESOURCES;
+        goto out;
+    }
+
+    list_init(&devices->head);
+    InitializeCriticalSection(&devices->lock);
+
+    *devices_out = devices;
+out:
+    return status;
+}
+
+void pnfs_file_device_list_free(
+    IN struct pnfs_file_device_list *devices)
+{
+    struct list_entry *entry, *tmp;
+
+    EnterCriticalSection(&devices->lock);
+
+    list_for_each_tmp(entry, tmp, &devices->head)
+        file_device_free(device_entry(entry));
+
+    LeaveCriticalSection(&devices->lock);
+    DeleteCriticalSection(&devices->lock);
+    free(devices);
+}
+
+void pnfs_file_device_list_invalidate(
+    IN struct pnfs_file_device_list *devices)
+{
+    struct list_entry *entry, *tmp;
+    pnfs_file_device *device;
+
+    dprintf(FDLVL, "--> pnfs_file_device_list_invalidate()\n");
+
+    EnterCriticalSection(&devices->lock);
+
+    list_for_each_tmp(entry, tmp, &devices->head) {
+        device = device_entry(entry);
+        EnterCriticalSection(&device->device.lock);
+        /* if there are layouts still using the device, flag it
+         * as revoked and clean up on last reference */
+        if (device->device.layout_count) {
+            device->device.status |= PNFS_DEVICE_REVOKED;
+            LeaveCriticalSection(&device->device.lock);
+        } else {
+            LeaveCriticalSection(&device->device.lock);
+            /* no layouts are using it, so it's safe to free */
+            list_remove(entry);
+            file_device_free(device);
+        }
+    }
+
+    LeaveCriticalSection(&devices->lock);
+
+    dprintf(FDLVL, "<-- pnfs_file_device_list_invalidate()\n");
+}
+
+
+/* pnfs_file_device */
+enum pnfs_status 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)
+{
+    pnfs_file_device *device;
+    enum pnfs_status status;
+    enum nfsstat4 nfsstat;
+
+    dprintf(FDLVL, "--> pnfs_file_device_get()\n");
+
+    status = file_device_find_or_create(deviceid, devices, &device);
+    if (status)
+        goto out;
+
+    EnterCriticalSection(&device->device.lock);
+
+    /* don't give out a device that's been revoked */
+    if (device->device.status & PNFS_DEVICE_REVOKED)
+        status = PNFSERR_NO_DEVICE;
+    else if (device->device.status & PNFS_DEVICE_GRANTED)
+        status = PNFS_SUCCESS;
+    else {
+        nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device);
+        if (nfsstat == NFS4_OK) {
+            device->device.status = PNFS_DEVICE_GRANTED;
+            status = PNFS_SUCCESS;
+
+            dprintf(FDLVL, "Received device info:\n");
+            dprint_device(FDLVL, device);
+        } else {
+            status = PNFSERR_NO_DEVICE;
+
+            eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n",
+                nfs_error_string(nfsstat));
+        }
+    }
+
+    if (status == PNFS_SUCCESS) {
+        device->device.layout_count++;
+        dprintf(FDLVL, "pnfs_file_device_get() -> %u\n",
+            device->device.layout_count);
+        *device_out = device;
+    }
+
+    LeaveCriticalSection(&device->device.lock);
+out:
+    dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n",
+        pnfs_error_string(status));
+    return status;
+}
+
+void pnfs_file_device_put(
+    IN pnfs_file_device *device)
+{
+    uint32_t count;
+    EnterCriticalSection(&device->device.lock);
+    count = --device->device.layout_count;
+    dprintf(FDLVL, "pnfs_file_device_put() -> %u\n", count);
+
+    /* if the device was revoked, remove/free the device on last reference */
+    if (count == 0 && device->device.status & PNFS_DEVICE_REVOKED) {
+        EnterCriticalSection(&device->devices->lock);
+        list_remove(&device->entry);
+        LeaveCriticalSection(&device->devices->lock);
+
+        LeaveCriticalSection(&device->device.lock);
+
+        file_device_free(device);
+        dprintf(FDLVL, "revoked file device freed after last reference\n");
+    } else {
+        LeaveCriticalSection(&device->device.lock);
+    }
+}
+
+static enum pnfs_status data_client_status(
+    IN pnfs_data_server *server,
+    OUT nfs41_client **client_out)
+{
+    enum pnfs_status status = PNFSERR_NOT_CONNECTED;
+
+    if (server->client) {
+        dprintf(FDLVL, "pnfs_data_server_client() returning "
+            "existing client %llu\n", server->client->clnt_id);
+        *client_out = server->client;
+        status = PNFS_SUCCESS;
+    }
+    return status;
+}
+
+enum pnfs_status pnfs_data_server_client(
+    IN nfs41_root *root,
+    IN pnfs_data_server *server,
+    IN uint32_t default_lease,
+    OUT nfs41_client **client_out)
+{
+    int status;
+    enum pnfs_status pnfsstat;
+
+    dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n",
+        server->addrs.arr[0].uaddr);
+
+    /* if we've already created the client, return it */
+    AcquireSRWLockShared(&server->lock);
+    pnfsstat = data_client_status(server, client_out);
+    ReleaseSRWLockShared(&server->lock);
+
+    if (!pnfsstat)
+        goto out;
+
+    AcquireSRWLockExclusive(&server->lock);
+
+    pnfsstat = data_client_status(server, client_out);
+    if (pnfsstat) {
+        status = nfs41_root_mount_addrs(root, &server->addrs, 1, default_lease, 
+            &server->client);
+        if (status) {
+            dprintf(FDLVL, "data_client_create('%s') failed with %d\n",
+                server->addrs.arr[0].uaddr, status);
+        } else {
+            *client_out = server->client;
+            pnfsstat = PNFS_SUCCESS;
+
+            dprintf(FDLVL, "pnfs_data_server_client() returning new client "
+                "%llu\n", server->client->clnt_id);
+        }
+    }
+
+    ReleaseSRWLockExclusive(&server->lock);
+out:
+    return pnfsstat;
+}
+
+
+/* CB_NOTIFY_DEVICEID */
+enum pnfs_status pnfs_file_device_notify(
+    IN struct pnfs_file_device_list *devices,
+    IN const struct notify_deviceid4 *change)
+{
+    struct list_entry *entry;
+    enum pnfs_status status = PNFSERR_NO_DEVICE;
+
+    dprintf(FDLVL, "--> pnfs_file_device_notify(%u, %0llX:%0llX)\n",
+        change->type, change->deviceid);
+
+    if (change->layouttype != PNFS_LAYOUTTYPE_FILE) {
+        status = PNFSERR_NOT_SUPPORTED;
+        goto out;
+    }
+
+    EnterCriticalSection(&devices->lock);
+
+    entry = list_search(&devices->head, change->deviceid, deviceid_compare);
+    if (entry) {
+        dprintf(FDLVL, "found file device %p\n", device_entry(entry));
+
+        if (change->type == NOTIFY_DEVICEID4_CHANGE) {
+            /* if (change->immediate) ... */
+            dprintf(FDLVL, "CHANGE (%u)\n", change->immediate);
+        } else if (change->type == NOTIFY_DEVICEID4_DELETE) {
+            /* This notification MUST NOT be sent if the client
+             * has a layout that refers to the device ID. */
+            dprintf(FDLVL, "DELETE\n");
+        }
+        status = PNFS_SUCCESS;
+    }
+
+    LeaveCriticalSection(&devices->lock);
+out:
+    dprintf(FDLVL, "<-- pnfs_file_device_notify() returning %s\n",
+        pnfs_error_string(status));
+    return status;
+}