From 08c2618551234d444f04e6882bffb50ccd6a54cc Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Thu, 23 Sep 2010 12:48:02 -0400 Subject: [PATCH] symlink: FSCTL_GET_REPARSE_POINT driver handles FSCTL_GET_REPARSE_POINT by sending a symlink query upcall daemon handles symlink query upcall by calling nfs41_readlink() Signed-off-by: Casey Bodley --- daemon/symlink.c | 34 +++++++++++++- sys/nfs41_driver.c | 112 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/daemon/symlink.c b/daemon/symlink.c index 14f6b2c..4b4e9d8 100644 --- a/daemon/symlink.c +++ b/daemon/symlink.c @@ -25,6 +25,7 @@ #include #include +#include "nfs41_ops.h" #include "upcall.h" #include "util.h" #include "daemon_debug.h" @@ -170,9 +171,40 @@ out: return status; } +static int map_symlink_errors(int status) +{ + switch (status) { + case NFS4ERR_BADCHAR: + case NFS4ERR_BADNAME: return ERROR_INVALID_REPARSE_DATA; + case NFS4ERR_WRONG_TYPE: return ERROR_NOT_A_REPARSE_POINT; + default: return nfs_to_windows_error(status, ERROR_BAD_NET_RESP); + } +} + int handle_symlink(nfs41_upcall *upcall) { - return NO_ERROR; + symlink_upcall_args *args = &upcall->args.symlink; + nfs41_open_state *state = args->state; + int status = NO_ERROR; + + if (args->set) { + } else { + uint32_t len; + + /* read the link */ + status = nfs41_readlink(state->session, &state->file, + NFS41_MAX_PATH_LEN, args->target_get.path, &len); + if (status) { + eprintf("nfs41_readlink() failed with %s\n", + nfs_error_string(status)); + status = map_symlink_errors(status); + goto out; + } + args->target_get.len = (unsigned short)len; + dprintf(2, "returning symlink target '%s'\n", args->target_get.path); + } +out: + return status; } int marshall_symlink(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall) diff --git a/sys/nfs41_driver.c b/sys/nfs41_driver.c index 61d0119..c588a32 100644 --- a/sys/nfs41_driver.c +++ b/sys/nfs41_driver.c @@ -4228,14 +4228,124 @@ out: return status; } +static NTSTATUS map_symlink_errors(NTSTATUS status) +{ + switch (status) { + case NO_ERROR: return STATUS_SUCCESS; + case ERROR_INVALID_REPARSE_DATA: return STATUS_IO_REPARSE_DATA_INVALID; + case ERROR_NOT_A_REPARSE_POINT: return STATUS_NOT_A_REPARSE_POINT; + case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES; + case ERROR_INSUFFICIENT_BUFFER: return STATUS_BUFFER_TOO_SMALL; + case STATUS_BUFFER_TOO_SMALL: + case ERROR_BUFFER_OVERFLOW: return STATUS_BUFFER_OVERFLOW; + default: + DbgP("failed to map windows error %d to NTSTATUS; " + "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status); + case ERROR_BAD_NET_RESP: return STATUS_INVALID_NETWORK_RESPONSE; + } +} + +static void print_reparse_buffer(PREPARSE_DATA_BUFFER Reparse) +{ + UNICODE_STRING name; + DbgP("ReparseTag: %08X\n", Reparse->ReparseTag); + DbgP("ReparseDataLength: %8u\n", Reparse->ReparseDataLength); + DbgP("Reserved: %8u\n", Reparse->Reserved); + DbgP("SubstituteNameOffset: %8u\n", Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset); + DbgP("SubstituteNameLength: %8u\n", Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength); + DbgP("PrintNameOffset: %8u\n", Reparse->SymbolicLinkReparseBuffer.PrintNameOffset); + DbgP("PrintNameLength: %8u\n", Reparse->SymbolicLinkReparseBuffer.PrintNameLength); + DbgP("Flags: %08X\n", Reparse->SymbolicLinkReparseBuffer.Flags); + + name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[ + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)]; + name.MaximumLength = name.Length = + Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength; + DbgP("SubstituteName: %wZ\n", &name); + + name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[ + Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)]; + name.MaximumLength = name.Length = + Reparse->SymbolicLinkReparseBuffer.PrintNameLength; + DbgP("PrintName: %wZ\n", &name); +} + +static NTSTATUS nfs41_GetReparsePoint( + IN OUT PRX_CONTEXT RxContext) +{ + UNICODE_STRING TargetName; + XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl; + PNFS41_FOBX Fobx = NFS41GetFileObjectExtension(RxContext->pFobx); + PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen; + PNFS41_V_NET_ROOT_EXTENSION VNetRoot = NFS41GetVNetRootExtension(SrvOpen->pVNetRoot); + nfs41_updowncall_entry *entry; + const USHORT HeaderLen = FIELD_OFFSET(REPARSE_DATA_BUFFER, + SymbolicLinkReparseBuffer.PathBuffer); + NTSTATUS status; + + if (FsCtl->OutputBufferLength < HeaderLen) { + RxContext->InformationToReturn = HeaderLen; + status = STATUS_BUFFER_TOO_SMALL; + goto out; + } + + TargetName.Buffer = (PWCH)((PBYTE)FsCtl->pOutputBuffer + HeaderLen); + TargetName.MaximumLength = (USHORT)min(FsCtl->OutputBufferLength - HeaderLen, 0xFFFF); + + status = nfs41_UpcallCreate(NFS41_SYMLINK, &entry); + if (status) + goto out; + + entry->u.Symlink.session = VNetRoot->session; + entry->u.Symlink.open_state = Fobx->nfs41_open_state; + entry->u.Symlink.filename = SrvOpen->pAlreadyPrefixedName; + entry->u.Symlink.target = &TargetName; + entry->u.Symlink.set = FALSE; + + if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) { + status = STATUS_INTERNAL_ERROR; + goto out; + } + + status = map_symlink_errors(entry->status); + if (status == STATUS_SUCCESS) { + /* fill in the output buffer */ + PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pOutputBuffer; + Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK; + Reparse->ReparseDataLength = HeaderLen + TargetName.Length - + REPARSE_DATA_BUFFER_HEADER_SIZE; + Reparse->Reserved = 0; + Reparse->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE; + /* PrintName and SubstituteName point to the same string */ + Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; + Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength = TargetName.Length; + Reparse->SymbolicLinkReparseBuffer.PrintNameOffset = 0; + Reparse->SymbolicLinkReparseBuffer.PrintNameLength = TargetName.Length; + print_reparse_buffer(Reparse); + + RxContext->IoStatusBlock.Information = HeaderLen + TargetName.Length; + } else if (status == STATUS_BUFFER_TOO_SMALL) { + RxContext->InformationToReturn = HeaderLen + TargetName.Length; + } + RxFreePool(entry); +out: + return status; +} + NTSTATUS nfs41_FsCtl( IN OUT PRX_CONTEXT RxContext) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; DbgEn(); + DbgP("FileName: %wZ\n", &RxContext->CurrentIrpSp->FileObject->FileName); + switch (RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode) { + case FSCTL_GET_REPARSE_POINT: + DbgP("FSCTL_GET_REPARSE_POINT\n"); + status = nfs41_GetReparsePoint(RxContext); + break; + } DbgEx(); return status; - } NTSTATUS nfs41_IoCtl(