diff --git a/daemon/getattr.c b/daemon/getattr.c index 30ee53e..5e558e6 100644 --- a/daemon/getattr.c +++ b/daemon/getattr.c @@ -69,6 +69,7 @@ static int parse_getattr(unsigned char *buffer, uint32_t length, nfs41_upcall *u if (status) goto out; status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(args->state)); if (status) goto out; upcall_open_state_ref(upcall, args->state); diff --git a/daemon/lock.c b/daemon/lock.c index c3476bf..c7461fc 100644 --- a/daemon/lock.c +++ b/daemon/lock.c @@ -97,6 +97,7 @@ static int parse_lock(unsigned char *buffer, uint32_t length, nfs41_upcall *upca upcall_open_state_ref(upcall, args->state); status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->offset, sizeof(LONGLONG)); if (status) goto out; status = safe_read(&buffer, &length, &args->length, sizeof(LONGLONG)); @@ -185,6 +186,7 @@ static int parse_unlock(unsigned char *buffer, uint32_t length, nfs41_upcall *up upcall_open_state_ref(upcall, args->state); status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->count, sizeof(ULONG)); if (status) goto out; diff --git a/daemon/mount.c b/daemon/mount.c index d8a0ce5..3f9bd40 100644 --- a/daemon/mount.c +++ b/daemon/mount.c @@ -100,7 +100,7 @@ out: return status; out_err: - nfs41_root_free(root); + nfs41_root_deref(root); goto out; } @@ -136,7 +136,8 @@ static int handle_unmount(nfs41_upcall *upcall) { int status = NO_ERROR; unmount_upcall_args *args = &upcall->args.unmount; - nfs41_root_free(args->root); + /* release the original reference from nfs41_root_create() */ + nfs41_root_deref(args->root); return status; } diff --git a/daemon/namespace.c b/daemon/namespace.c index 203f5a7..2e672ef 100644 --- a/daemon/namespace.c +++ b/daemon/namespace.c @@ -57,6 +57,7 @@ int nfs41_root_create( root->wsize = wsize; root->rsize = rsize; InitializeCriticalSection(&root->lock); + root->ref_count = 1; /* generate a unique client_owner */ status = nfs41_client_owner(name, &root->client_owner); @@ -71,7 +72,7 @@ out: return status; } -void nfs41_root_free( +static void root_free( IN nfs41_root *root) { struct list_entry *entry, *tmp; @@ -86,6 +87,25 @@ void nfs41_root_free( dprintf(NSLVL, "<-- nfs41_root_free()\n"); } +void nfs41_root_ref( + IN nfs41_root *root) +{ + const LONG count = InterlockedIncrement(&root->ref_count); + + dprintf(2, "nfs41_root_ref() count %d\n", count); +} + +void nfs41_root_deref( + IN nfs41_root *root) +{ + const LONG count = InterlockedDecrement(&root->ref_count); + + dprintf(2, "nfs41_root_deref() count %d\n", count); + if (count == 0) + root_free(root); +} + + /* root_client_find_addrs() */ struct cl_addr_info { const multi_addr4 *addrs; diff --git a/daemon/nfs41.h b/daemon/nfs41.h index 930079f..b18891d 100644 --- a/daemon/nfs41.h +++ b/daemon/nfs41.h @@ -168,12 +168,18 @@ typedef struct __nfs41_session { nfs41_cb_session cb_session; } nfs41_session; +/* nfs41_root reference counting: + * similar to nfs41_open_state, the driver holds an implicit reference + * between MOUNT and UNMOUNT. all other upcalls use upcall_root_ref() on + * upcall_parse(), which prevents the root/clients from being freed and + * guarantees a matching deref on upcall_cleanup() */ typedef struct __nfs41_root { client_owner4 client_owner; CRITICAL_SECTION lock; struct list_entry clients; uint32_t wsize; uint32_t rsize; + LONG ref_count; } nfs41_root; @@ -184,7 +190,10 @@ int nfs41_root_create( IN uint32_t rsize, OUT nfs41_root **root_out); -void nfs41_root_free( +void nfs41_root_ref( + IN nfs41_root *root); + +void nfs41_root_deref( IN nfs41_root *root); int nfs41_root_mount_addrs( diff --git a/daemon/open.c b/daemon/open.c index e0bf833..b943e7e 100644 --- a/daemon/open.c +++ b/daemon/open.c @@ -121,6 +121,7 @@ static int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upca if (status) goto out; status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->open_owner_id, sizeof(ULONG)); if (status) goto out; status = safe_read(&buffer, &length, &args->mode, sizeof(DWORD)); @@ -497,6 +498,7 @@ static int parse_close(unsigned char *buffer, uint32_t length, nfs41_upcall *upc status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(nfs41_open_state *)); if (status) goto out; status = safe_read(&buffer, &length, &args->remove, sizeof(BOOLEAN)); diff --git a/daemon/readdir.c b/daemon/readdir.c index 9e81474..7ef8a33 100644 --- a/daemon/readdir.c +++ b/daemon/readdir.c @@ -62,6 +62,7 @@ static int parse_readdir(unsigned char *buffer, uint32_t length, nfs41_upcall *u if (status) goto out; status = safe_read(&buffer, &length, &args->root, sizeof(args->root)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(args->state)); if (status) goto out; upcall_open_state_ref(upcall, args->state); diff --git a/daemon/readwrite.c b/daemon/readwrite.c index 615931b..efde36f 100644 --- a/daemon/readwrite.c +++ b/daemon/readwrite.c @@ -47,6 +47,7 @@ static int parse_rw(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall if (status) goto out; status = safe_read(&buffer, &length, &args->root, sizeof(args->root)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(args->state)); if (status) goto out; upcall_open_state_ref(upcall, args->state); diff --git a/daemon/setattr.c b/daemon/setattr.c index b2ab812..3e6a2c4 100644 --- a/daemon/setattr.c +++ b/daemon/setattr.c @@ -500,6 +500,7 @@ static int parse_setexattr(unsigned char *buffer, uint32_t length, nfs41_upcall status = safe_read(&buffer, &length, &args->root, sizeof(args->root)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(args->state)); if (status) goto out; upcall_open_state_ref(upcall, args->state); diff --git a/daemon/symlink.c b/daemon/symlink.c index 94240d8..0bfd825 100644 --- a/daemon/symlink.c +++ b/daemon/symlink.c @@ -197,6 +197,7 @@ static int parse_symlink(unsigned char *buffer, uint32_t length, nfs41_upcall *u status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->state, sizeof(nfs41_open_state *)); if (status) goto out; upcall_open_state_ref(upcall, args->state); diff --git a/daemon/upcall.c b/daemon/upcall.c index fc16d83..9823b60 100644 --- a/daemon/upcall.c +++ b/daemon/upcall.c @@ -186,4 +186,8 @@ void upcall_cleanup( nfs41_open_state_deref(upcall->state_ref); upcall->state_ref = NULL; } + if (upcall->root_ref) { + nfs41_root_deref(upcall->root_ref); + upcall->root_ref = NULL; + } } diff --git a/daemon/upcall.h b/daemon/upcall.h index b071fa6..c05dfa2 100644 --- a/daemon/upcall.h +++ b/daemon/upcall.h @@ -180,6 +180,7 @@ typedef struct __nfs41_upcall { /* store referenced pointers with the upcall for * automatic dereferencing on upcall_cleanup(); * see upcall_root_ref() and upcall_open_state_ref() */ + nfs41_root *root_ref; nfs41_open_state *state_ref; } nfs41_upcall; @@ -222,6 +223,14 @@ void upcall_cleanup( IN nfs41_upcall *upcall); +static __inline void upcall_root_ref( + IN nfs41_upcall *upcall, + IN nfs41_root *root) +{ + nfs41_root_ref(root); + upcall->root_ref = root; +} + static __inline void upcall_open_state_ref( IN nfs41_upcall *upcall, IN nfs41_open_state *state) diff --git a/daemon/volume.c b/daemon/volume.c index f9e528c..c232331 100644 --- a/daemon/volume.c +++ b/daemon/volume.c @@ -48,6 +48,7 @@ static int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *up volume_upcall_args *args = &upcall->args.volume; status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); if (status) goto out; + upcall_root_ref(upcall, args->root); status = safe_read(&buffer, &length, &args->query, sizeof(FS_INFORMATION_CLASS)); if (status) goto out;