ms-nfs41-client/sys/nfs41_driver.c

5108 lines
179 KiB
C
Raw Normal View History

/* Copyright (c) 2010
* The Regents of the University of Michigan
* All Rights Reserved
*
* Permission is granted to use, copy and redistribute this software
* for noncommercial education and research purposes, so long as no
* fee is charged, and so long as the name of the University of Michigan
* is not used in any advertising or publicity pertaining to the use
* or distribution of this software without specific, written prior
* authorization. Permission to modify or otherwise create derivative
* works of this software is not granted.
*
* This software is provided as is, without representation or warranty
* of any kind either express or implied, including without limitation
* the implied warranties of merchantability, fitness for a particular
* purpose, or noninfringement. The Regents of the University of
* Michigan shall not be liable for any damages, including special,
* indirect, incidental, or consequential damages, with respect to any
* claim arising out of or in connection with the use of the software,
* even if it has been or is hereafter advised of the possibility of
* such damages.
*/
#define MINIRDR__NAME "Value is ignored, only fact of definition"
#include <rx.h>
#include <windef.h>
#include <winerror.h>
#include <Ntstrsafe.h>
#include "nfs41_driver.h"
#include "nfs41_np.h"
#include "nfs41_debug.h"
//#define DEBUG_CLOSE
DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD nfs41_driver_unload;
DRIVER_DISPATCH ( nfs41_FsdDispatch );
struct _MINIRDR_DISPATCH nfs41_ops;
PRDBSS_DEVICE_OBJECT nfs41_dev;
#define FCB_BASIC_INFO_CACHED 0x0001
#define FCB_STANDARD_INFO_CACHED 0x0010
#define DISABLE_CACHING 0
#define ENABLE_READ_CACHING 1
#define ENABLE_WRITE_CACHING 2
#define ENABLE_READWRITE_CACHING 3
#define NFS41_MM_POOLTAG ('nfs4')
KEVENT upcallEvent;
FAST_MUTEX upcallLock, downcallLock;
FAST_MUTEX xidLock;
FAST_MUTEX openOwnerLock;
ULONG xid = 0;
ULONG open_owner_id = 1;
#define DECLARE_CONST_ANSI_STRING(_var, _string) \
const CHAR _var ## _buffer[] = _string; \
const ANSI_STRING _var = { sizeof(_string) - sizeof(CHAR), \
sizeof(_string), (PCH) _var ## _buffer }
DECLARE_CONST_ANSI_STRING(NfsV3Attributes, "NfsV3Attributes");
DECLARE_CONST_ANSI_STRING(NfsSymlinkTargetName, "NfsSymlinkTargetName");
DECLARE_CONST_ANSI_STRING(NfsActOnLink, "NfsActOnLink");
static INLINE BOOL AnsiStrEq(
IN const ANSI_STRING *lhs,
IN const CHAR *rhs,
IN const UCHAR rhs_len)
{
return lhs->Length == rhs_len &&
RtlCompareMemory(lhs->Buffer, rhs, rhs_len) == rhs_len;
}
typedef struct _nfs3_attrs {
DWORD type, mode, nlink, uid, gid, filler1;
LARGE_INTEGER size, used;
struct {
DWORD specdata1;
DWORD specdata2;
} rdev;
LONGLONG fsid, fileid;
2010-10-27 14:58:35 -04:00
LONGLONG atime, mtime, ctime;
} nfs3_attrs;
2010-10-27 14:58:35 -04:00
LARGE_INTEGER unix_time_diff; //needed to convert windows time to unix
enum ftype3 {
NF3REG = 1,
NF3DIR,
NF3BLK,
NF3CHR,
NF3LNK,
NF3SOCK,
NF3FIFO
};
typedef enum _nfs41_updowncall_state {
NFS41_WAITING_FOR_UPCALL,
NFS41_WAITING_FOR_DOWNCALL,
NFS41_DONE_PROCESSING,
NFS41_NOT_WAITING
} nfs41_updowncall_state;
typedef struct _updowncall_entry {
DWORD version;
DWORD xid;
DWORD opcode;
NTSTATUS status;
nfs41_updowncall_state state;
FAST_MUTEX lock;
LIST_ENTRY next;
KEVENT cond;
DWORD errno;
BOOLEAN async_op;
2010-10-12 10:02:20 -04:00
SECURITY_CLIENT_CONTEXT sec_ctx;
PSECURITY_CLIENT_CONTEXT psec_ctx;
HANDLE open_state;
HANDLE session;
union {
struct {
PUNICODE_STRING srv_name;
PUNICODE_STRING root;
2010-12-01 19:11:47 -05:00
DWORD sec_flavor;
} Mount;
struct {
PMDL MdlAddress;
PVOID buf;
LONGLONG offset;
ULONG len;
PRX_CONTEXT rxcontext;
} ReadWrite;
struct {
HANDLE handle;
LONGLONG offset;
LONGLONG length;
BOOLEAN exclusive;
BOOLEAN blocking;
} Lock;
struct {
ULONG count;
LOWIO_LOCK_LIST locks;
} Unlock;
struct {
FILE_BASIC_INFORMATION binfo;
FILE_STANDARD_INFORMATION sinfo;
PUNICODE_STRING filename;
UNICODE_STRING symlink;
ULONG access_mask;
ULONG access_mode;
ULONG attrs;
ULONG copts;
ULONG disp;
ULONG cattrs;
ULONG open_owner_id;
DWORD mode;
LONGLONG changeattr;
BOOLEAN symlink_embedded;
} Open;
struct {
PUNICODE_STRING filename;
BOOLEAN remove;
BOOLEAN renamed;
} Close;
struct {
PUNICODE_STRING filter;
PVOID buf;
ULONG buf_len;
FILE_INFORMATION_CLASS InfoClass;
BOOLEAN restart_scan;
BOOLEAN return_single;
BOOLEAN initial_query;
} QueryFile;
struct {
PUNICODE_STRING filename;
PVOID buf;
ULONG buf_len;
FILE_INFORMATION_CLASS InfoClass;
ULONG open_owner_id;
ULONG access_mask;
ULONG access_mode;
} SetFile;
struct {
DWORD mode;
} SetEa;
struct {
PUNICODE_STRING filename;
PUNICODE_STRING target;
BOOLEAN set;
} Symlink;
struct {
FS_INFORMATION_CLASS query;
PVOID buf;
LONG buf_len;
} Volume;
2011-04-12 15:56:20 -04:00
struct {
SECURITY_INFORMATION query;
PVOID buf;
DWORD buf_len;
} Acl;
} u;
} nfs41_updowncall_entry;
typedef struct _updowncall_list {
LIST_ENTRY head;
} nfs41_updowncall_list;
nfs41_updowncall_list *upcall = NULL, *downcall = NULL;
#define nfs41_AddEntry(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
InsertTailList(&pList->head, &(pEntry)->next); \
ExReleaseFastMutex(&lock);
#define nfs41_RemoveFirst(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
pEntry = (IsListEmpty(&pList->head) \
? NULL \
: RemoveHeadList(&pList->head)); \
ExReleaseFastMutex(&lock);
#define nfs41_RemoveLast(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
pEntry = RemoveTailList(&pList->head); \
ExReleaseFastMutex(&lock);
#define nfs41_RemoveEntry(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
RemoveEntryList(&pEntry->next); \
ExReleaseFastMutex(&lock);
#define nfs41_IsListEmpty(lock,pList,flag) \
ExAcquireFastMutex(&lock); \
*flag = IsListEmpty(&pList->head); \
ExReleaseFastMutex(&lock);
#define nfs41_GetFirstEntry(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
pEntry = (IsListEmpty(&pList->head) \
? NULL \
: (nfs41_updowncall_entry *) \
(CONTAINING_RECORD(pList->head.Flink, \
nfs41_updowncall_entry, \
next))); \
ExReleaseFastMutex(&lock);
#define nfs41_GetNextEntry(pList,pEntry) \
((pEntry->next.Flink == &pList->head) \
? NULL \
: (nfs41_updowncall_entry *) \
(CONTAINING_RECORD(pEntry->next.Flink, \
nfs41_updowncall_entry, \
next)));
/* In order to cooperate with other network providers,
* we only claim paths of the format '\\server\nfs4\path' */
DECLARE_CONST_UNICODE_STRING(NfsPrefix, L"\\nfs4");
2010-12-01 19:11:47 -05:00
DECLARE_CONST_UNICODE_STRING(AUTH_SYS_NAME, L"sys");
DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5_NAME, L"krb5");
DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5I_NAME, L"krb5i");
DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5P_NAME, L"krb5p");
#define SERVER_NAME_BUFFER_SIZE 1024
#define MOUNT_CONFIG_RW_SIZE_MIN 1024
#define MOUNT_CONFIG_RW_SIZE_DEFAULT 32768
#define MOUNT_CONFIG_RW_SIZE_MAX 65536
2010-12-01 19:11:47 -05:00
#define MAX_SEC_FLAVOR_LEN 12
typedef struct _NFS41_MOUNT_CONFIG {
DWORD ReadSize;
DWORD WriteSize;
BOOLEAN ReadOnly;
WCHAR srv_buffer[SERVER_NAME_BUFFER_SIZE];
UNICODE_STRING SrvName;
WCHAR mntpt_buffer[MAX_PATH];
UNICODE_STRING MntPt;
2010-12-01 19:11:47 -05:00
WCHAR sec_flavor[MAX_SEC_FLAVOR_LEN];
UNICODE_STRING SecFlavor;
} NFS41_MOUNT_CONFIG, *PNFS41_MOUNT_CONFIG;
typedef struct _NFS41_NETROOT_EXTENSION {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
HANDLE auth_sys_session;
HANDLE gss_session;
DWORD nfs41d_version;
} NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION;
#define NFS41GetNetRootExtension(pNetRoot) \
(((pNetRoot) == NULL) ? NULL : (PNFS41_NETROOT_EXTENSION)((pNetRoot)->Context))
/* FileSystemName as reported by FileFsAttributeInfo query */
#define FS_NAME L"NFS"
#define FS_NAME_LEN (sizeof(FS_NAME) - sizeof(WCHAR))
#define FS_ATTR_LEN (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + FS_NAME_LEN)
typedef struct _NFS41_V_NET_ROOT_EXTENSION {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
HANDLE session;
BYTE FsAttrs[FS_ATTR_LEN];
LONG FsAttrsLen;
2010-12-01 19:11:47 -05:00
DWORD sec_flavor;
} NFS41_V_NET_ROOT_EXTENSION, *PNFS41_V_NET_ROOT_EXTENSION;
#define NFS41GetVNetRootExtension(pVNetRoot) \
(((pVNetRoot) == NULL) ? NULL : \
(PNFS41_V_NET_ROOT_EXTENSION)((pVNetRoot)->Context))
typedef struct _NFS41_FCB {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
ULONG Flags;
FILE_BASIC_INFORMATION BasicInfo;
FILE_STANDARD_INFORMATION StandardInfo;
BOOLEAN Renamed;
DWORD mode;
LONGLONG changeattr;
} NFS41_FCB, *PNFS41_FCB;
#define NFS41GetFcbExtension(pFcb) \
(((pFcb) == NULL) ? NULL : (PNFS41_FCB)((pFcb)->Context))
typedef struct _NFS41_SRV_OPEN {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
} NFS41_SRV_OPEN, *PNFS41_SRV_OPEN;
#define NFS41GetSrvOpenExtension(pSrvOpen) \
(((pSrvOpen) == NULL) ? NULL : (PNFS41_SRV_OPEN)((pSrvOpen)->Context))
typedef struct _NFS41_FOBX {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
HANDLE nfs41_open_state;
SECURITY_CLIENT_CONTEXT sec_ctx;
} NFS41_FOBX, *PNFS41_FOBX;
#define NFS41GetFileObjectExtension(pFobx) \
(((pFobx) == NULL) ? NULL : (PNFS41_FOBX)((pFobx)->Context))
typedef struct _NFS41_SERVER_ENTRY {
PMRX_SRV_CALL pRdbssSrvCall;
WCHAR NameBuffer[SERVER_NAME_BUFFER_SIZE];
UNICODE_STRING Name; // the server name.
} NFS41_SERVER_ENTRY, *PNFS41_SERVER_ENTRY;
typedef struct _NFS41_DEVICE_EXTENSION {
NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize;
PRDBSS_DEVICE_OBJECT DeviceObject;
ULONG ActiveNodes;
HANDLE SharedMemorySection;
DWORD nfs41d_version;
} NFS41_DEVICE_EXTENSION, *PNFS41_DEVICE_EXTENSION;
#define NFS41GetDeviceExtension(RxContext,pExt) \
PNFS41_DEVICE_EXTENSION pExt = (PNFS41_DEVICE_EXTENSION) \
((PBYTE)(RxContext->RxDeviceObject) + sizeof(RDBSS_DEVICE_OBJECT))
typedef enum _NULMRX_STORAGE_TYPE_CODES {
NTC_NFS41_DEVICE_EXTENSION = (NODE_TYPE_CODE)0xFC00,
} NFS41_STORAGE_TYPE_CODES;
#define RxDefineNode( node, type ) \
node->NodeTypeCode = NTC_##type; \
node->NodeByteSize = sizeof(type);
#define RDR_NULL_STATE 0
#define RDR_UNLOADED 1
#define RDR_UNLOADING 2
#define RDR_LOADING 3
#define RDR_LOADED 4
#define RDR_STOPPED 5
#define RDR_STOPPING 6
#define RDR_STARTING 7
#define RDR_STARTED 8
nfs41_init_driver_state nfs41_init_state = NFS41_INIT_DRIVER_STARTABLE;
nfs41_start_driver_state nfs41_start_state = NFS41_START_DRIVER_STARTABLE;
static NTSTATUS map_readwrite_errors(DWORD status);
ULONG get_next_xid() {
ULONG x;
ExAcquireFastMutex(&xidLock);
x = ++xid;
ExReleaseFastMutex(&xidLock);
return x;
}
ULONG get_next_open_owner() {
ULONG x;
ExAcquireFastMutex(&openOwnerLock);
x = open_owner_id++;
ExReleaseFastMutex(&openOwnerLock);
return x;
}
void print_debug_header(PRX_CONTEXT RxContext)
{
PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
if (IrpSp) {
DbgP("FileOject %p Filename %wZ %wZ\n", IrpSp->FileObject,
&IrpSp->FileObject->FileName, SrvOpen->pAlreadyPrefixedName);
print_file_object(0, IrpSp->FileObject);
} else
DbgP("Couldn't print FileObject IrpSp is NULL\n");
print_fcb(1, RxContext->pFcb);
print_srv_open(1, RxContext->pRelevantSrvOpen);
print_fobx(1, RxContext->pFobx);
if (RxContext->pFobx) {
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
DbgP("Session=0x%x OpenState=0x%x\n", pVNetRootContext->session,
nfs41_fobx->nfs41_open_state);
}
}
/* convert strings from unicode -> ansi during marshalling to
* save space in the upcall buffers and avoid extra copies */
static INLINE ULONG length_as_ansi(
PCUNICODE_STRING str)
{
return sizeof(str->MaximumLength) + RtlUnicodeStringToAnsiSize(str);
}
static NTSTATUS marshall_unicode_as_ansi(
unsigned char **pos,
PCUNICODE_STRING str)
{
ANSI_STRING ansi;
NTSTATUS status;
/* convert the string directly into the upcall buffer */
ansi.Buffer = (PCHAR)*pos + sizeof(ansi.MaximumLength);
ansi.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(str);
status = RtlUnicodeStringToAnsiString(&ansi, str, FALSE);
if (status)
goto out;
RtlCopyMemory(*pos, &ansi.MaximumLength, sizeof(ansi.MaximumLength));
*pos += sizeof(ansi.MaximumLength);
(*pos)[ansi.Length] = '\0';
*pos += ansi.MaximumLength;
out:
return status;
}
NTSTATUS marshal_nfs41_header(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
header_len = sizeof(entry->version) + sizeof(entry->xid) +
sizeof(entry->opcode) + 2 * sizeof(HANDLE);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
else
*len = header_len;
RtlCopyMemory(tmp, &entry->version, sizeof(entry->version));
tmp += sizeof(entry->version);
RtlCopyMemory(tmp, &entry->xid, sizeof(entry->xid));
tmp += sizeof(entry->xid);
RtlCopyMemory(tmp, &entry->opcode, sizeof(entry->opcode));
tmp += sizeof(entry->opcode);
RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
tmp += sizeof(HANDLE);
RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
tmp += sizeof(HANDLE);
2010-10-12 10:02:20 -04:00
DbgP("[upcall] entry=%p xid=%d opcode=%d version=%d session=0x%x "
"open_state=0x%x\n", entry, entry->xid, entry->opcode, entry->version,
entry->session, entry->open_state);
out:
return status;
}
2010-12-01 19:11:47 -05:00
const char* secflavorop2name(DWORD sec_flavor)
{
switch(sec_flavor) {
case RPCSEC_AUTH_SYS: return "AUTH_SYS";
case RPCSEC_AUTHGSS_KRB5: return "AUTHGSS_KRB5";
case RPCSEC_AUTHGSS_KRB5I: return "AUTHGSS_KRB5I";
case RPCSEC_AUTHGSS_KRB5P: return "AUTHGSS_KRB5P";
}
return "UNKNOWN FLAVOR";
}
NTSTATUS marshal_nfs41_mount(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
/* 03/25/2011: Kernel crash to nfsd not running but mount upcall cued up */
if (!MmIsAddressValid(entry->u.Mount.srv_name) ||
!MmIsAddressValid(entry->u.Mount.root)) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
header_len = *len + length_as_ansi(entry->u.Mount.srv_name) +
2010-12-01 19:11:47 -05:00
length_as_ansi(entry->u.Mount.root) + sizeof(entry->u.Mount.sec_flavor);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
status = marshall_unicode_as_ansi(&tmp, entry->u.Mount.srv_name);
if (status) goto out;
status = marshall_unicode_as_ansi(&tmp, entry->u.Mount.root);
if (status) goto out;
2010-12-01 19:11:47 -05:00
RtlCopyMemory(tmp, &entry->u.Mount.sec_flavor, sizeof(entry->u.Mount.sec_flavor));
*len = header_len;
2010-12-01 19:11:47 -05:00
DbgP("server name=%wZ mount point=%wZ sec_flavor=%s\n",
entry->u.Mount.srv_name, entry->u.Mount.root,
secflavorop2name(entry->u.Mount.sec_flavor));
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_unmount(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
status = marshal_nfs41_header(entry, buf, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
DbgP("session=0x%x\n", entry->session);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_open(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + length_as_ansi(entry->u.Open.filename) +
6 * sizeof(ULONG) + sizeof(DWORD);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
status = marshall_unicode_as_ansi(&tmp, entry->u.Open.filename);
if (status) goto out;
RtlCopyMemory(tmp, &entry->u.Open.access_mask, sizeof(entry->u.Open.access_mask));
tmp += sizeof(entry->u.Open.access_mask);
RtlCopyMemory(tmp, &entry->u.Open.access_mode, sizeof(entry->u.Open.access_mode));
tmp += sizeof(entry->u.Open.access_mode);
RtlCopyMemory(tmp, &entry->u.Open.attrs, sizeof(entry->u.Open.attrs));
tmp += sizeof(entry->u.Open.attrs);
RtlCopyMemory(tmp, &entry->u.Open.copts, sizeof(entry->u.Open.copts));
tmp += sizeof(entry->u.Open.copts);
RtlCopyMemory(tmp, &entry->u.Open.disp, sizeof(entry->u.Open.disp));
tmp += sizeof(entry->u.Open.disp);
RtlCopyMemory(tmp, &entry->u.Open.open_owner_id,
sizeof(entry->u.Open.open_owner_id));
tmp += sizeof(entry->u.Open.open_owner_id);
RtlCopyMemory(tmp, &entry->u.Open.mode, sizeof(DWORD));
*len = header_len;
DbgP("mask=0x%x mode=0x%x attrs=0x%x opts=0x%x dispo=0x%x "
"open_owner_id=0x%x mode=%o\n", entry->u.Open.access_mask,
entry->u.Open.access_mode, entry->u.Open.attrs,
entry->u.Open.copts, entry->u.Open.disp,
entry->u.Open.open_owner_id, entry->u.Open.mode);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_rw(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(entry->u.ReadWrite.len) +
sizeof(entry->u.ReadWrite.offset) + sizeof(HANDLE);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.ReadWrite.len, sizeof(entry->u.ReadWrite.len));
tmp += sizeof(entry->u.ReadWrite.len);
RtlCopyMemory(tmp, &entry->u.ReadWrite.offset, sizeof(entry->u.ReadWrite.offset));
tmp += sizeof(entry->u.ReadWrite.offset);
__try {
entry->u.ReadWrite.buf =
MmMapLockedPagesSpecifyCache(entry->u.ReadWrite.MdlAddress,
UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
DbgP("MdlAddress=%p Userspace=%p\n", entry->u.ReadWrite.MdlAddress,
entry->u.ReadWrite.buf);
if (entry->u.ReadWrite.buf == NULL) {
print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
NTSTATUS code;
code = GetExceptionCode();
print_error("Call to MmMapLocked failed due to exception 0x%x\n", code);
status = STATUS_ACCESS_DENIED;
goto out;
}
RtlCopyMemory(tmp, &entry->u.ReadWrite.buf, sizeof(HANDLE));
*len = header_len;
DbgP("len=%u offset=%lu\n", entry->u.ReadWrite.len, entry->u.ReadWrite.offset);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_lock(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
PULONG len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + 2 * sizeof(LONGLONG) + 2 * sizeof(BOOLEAN);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Lock.offset, sizeof(LONGLONG));
tmp += sizeof(LONGLONG);
RtlCopyMemory(tmp, &entry->u.Lock.length, sizeof(LONGLONG));
tmp += sizeof(LONGLONG);
RtlCopyMemory(tmp, &entry->u.Lock.exclusive, sizeof(BOOLEAN));
tmp += sizeof(BOOLEAN);
RtlCopyMemory(tmp, &entry->u.Lock.blocking, sizeof(BOOLEAN));
tmp += sizeof(BOOLEAN);
*len = header_len;
DbgP("offset=%llx length=%llx exclusive=%u blocking=%u\n",
entry->u.Lock.offset, entry->u.Lock.length,
entry->u.Lock.exclusive, entry->u.Lock.blocking);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_unlock(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
PULONG len)
{
NTSTATUS status;
ULONG header_len = 0;
unsigned char *tmp = buf;
PLOWIO_LOCK_LIST lock;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(ULONG) +
entry->u.Unlock.count * 2 * sizeof(LONGLONG);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Unlock.count, sizeof(ULONG));
tmp += sizeof(ULONG);
lock = &entry->u.Unlock.locks;
while (lock) {
RtlCopyMemory(tmp, &lock->ByteOffset, sizeof(LONGLONG));
tmp += sizeof(LONGLONG);
RtlCopyMemory(tmp, &lock->Length, sizeof(LONGLONG));
tmp += sizeof(LONGLONG);
lock = lock->Next;
}
*len = header_len;
DbgP("count=%u\n", entry->u.Unlock.count);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_close(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(BOOLEAN);
if (entry->u.Close.remove)
header_len += length_as_ansi(entry->u.Close.filename) +
sizeof(BOOLEAN);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Close.remove, sizeof(BOOLEAN));
if (entry->u.Close.remove) {
tmp += sizeof(BOOLEAN);
status = marshall_unicode_as_ansi(&tmp, entry->u.Close.filename);
if (status) goto out;
RtlCopyMemory(tmp, &entry->u.Close.renamed, sizeof(BOOLEAN));
}
*len = header_len;
DbgP("remove=%d renamed=%d filename=%wZ\n", entry->u.Close.remove,
entry->u.Close.renamed, entry->u.Close.filename);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_dirquery(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + 2 * sizeof(ULONG) +
length_as_ansi(entry->u.QueryFile.filter) + 3 * sizeof(BOOLEAN);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->u.QueryFile.buf_len, sizeof(ULONG));
tmp += sizeof(ULONG);
status = marshall_unicode_as_ansi(&tmp, entry->u.QueryFile.filter);
if (status) goto out;
RtlCopyMemory(tmp, &entry->u.QueryFile.initial_query, sizeof(BOOLEAN));
tmp += sizeof(BOOLEAN);
RtlCopyMemory(tmp, &entry->u.QueryFile.restart_scan, sizeof(BOOLEAN));
tmp += sizeof(BOOLEAN);
RtlCopyMemory(tmp, &entry->u.QueryFile.return_single, sizeof(BOOLEAN));
*len = header_len;
DbgP("filter='%wZ' class=%d\n\t1st\\restart\\single=%d\\%d\\%d\n",
entry->u.QueryFile.filter, entry->u.QueryFile.InfoClass,
entry->u.QueryFile.initial_query, entry->u.QueryFile.restart_scan,
entry->u.QueryFile.return_single);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_filequery(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + 2 * sizeof(ULONG);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->u.QueryFile.buf_len, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
tmp += sizeof(HANDLE);
RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
*len = header_len;
DbgP("class=%d\n", entry->u.QueryFile.InfoClass);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_fileset(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + length_as_ansi(entry->u.SetFile.filename) +
5 * sizeof(ULONG) + entry->u.SetFile.buf_len;
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
status = marshall_unicode_as_ansi(&tmp, entry->u.SetFile.filename);
if (status) goto out;
RtlCopyMemory(tmp, &entry->u.SetFile.InfoClass, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->u.SetFile.buf_len, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, entry->u.SetFile.buf, entry->u.SetFile.buf_len);
tmp += entry->u.SetFile.buf_len;
RtlCopyMemory(tmp, &entry->u.SetFile.open_owner_id, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->u.SetFile.access_mask, sizeof(ULONG));
tmp += sizeof(ULONG);
RtlCopyMemory(tmp, &entry->u.SetFile.access_mode, sizeof(ULONG));
*len = header_len;
DbgP("filename='%wZ' class=%d open_owner_id=0x%x access_mask=0x%x "
"access_mode=0x%x\n", entry->u.SetFile.filename,
entry->u.SetFile.InfoClass, entry->u.SetFile.open_owner_id,
entry->u.SetFile.access_mask, entry->u.SetFile.access_mode);
print_hexbuf(0, (unsigned char *)"setfile buffer", entry->u.SetFile.buf,
entry->u.SetFile.buf_len);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_easet(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(DWORD);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.SetEa.mode, sizeof(DWORD));
*len = header_len;
DbgP("mode=0x%x\n", entry->u.SetEa.mode);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_symlink(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(BOOLEAN) +
length_as_ansi(entry->u.Symlink.filename);
if (entry->u.Symlink.set)
header_len += length_as_ansi(entry->u.Symlink.target);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
marshall_unicode_as_ansi(&tmp, entry->u.Symlink.filename);
RtlCopyMemory(tmp, &entry->u.Symlink.set, sizeof(BOOLEAN));
tmp += sizeof(BOOLEAN);
if (entry->u.Symlink.set)
marshall_unicode_as_ansi(&tmp, entry->u.Symlink.target);
*len = header_len;
DbgP("symlink name %wZ symlink target %wZ\n", entry->u.Symlink.filename,
entry->u.Symlink.set?entry->u.Symlink.target : NULL);
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_volume(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(FS_INFORMATION_CLASS);
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Volume.query, sizeof(FS_INFORMATION_CLASS));
*len = header_len;
DbgP("query=0x%x\n", entry->u.Volume.query);
out:
DbgEx();
return status;
}
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
NTSTATUS marshal_nfs41_getacl(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(SECURITY_INFORMATION);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
*len = header_len;
DbgP("query=%d\n", entry->u.Acl.query);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
out:
DbgEx();
return status;
}
2011-04-12 15:56:20 -04:00
NTSTATUS marshal_nfs41_setacl(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG header_len = 0;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
if (status == STATUS_INSUFFICIENT_RESOURCES)
goto out;
else
tmp += *len;
header_len = *len + sizeof(SECURITY_INFORMATION) +
sizeof(ULONG) + entry->u.Acl.buf_len;
2011-04-12 15:56:20 -04:00
if (header_len > buf_len) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
2011-04-12 15:56:20 -04:00
tmp += sizeof(SECURITY_INFORMATION);
RtlCopyMemory(tmp, &entry->u.Acl.buf_len, sizeof(DWORD));
tmp += sizeof(DWORD);
RtlCopyMemory(tmp, entry->u.Acl.buf, entry->u.Acl.buf_len);
2011-04-12 15:56:20 -04:00
*len = header_len;
DbgP("query=%d sec_desc_len=%d\n", entry->u.Acl.query, entry->u.Acl.buf_len);
2011-04-12 15:56:20 -04:00
out:
DbgEx();
return status;
}
NTSTATUS marshal_nfs41_shutdown(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
ULONG *len)
{
NTSTATUS status = STATUS_SUCCESS;
unsigned char *tmp = buf;
DbgEn();
status = marshal_nfs41_header(entry, tmp, buf_len, len);
DbgEx();
return status;
}
NTSTATUS
nfs41_invalidate_cache (
IN PRX_CONTEXT RxContext
)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS
handle_upcall(
IN PRX_CONTEXT RxContext,
IN nfs41_updowncall_entry *entry,
OUT ULONG *len
)
{
NTSTATUS status = STATUS_SUCCESS;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
ULONG cbOut = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
unsigned char *pbOut = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
status = SeImpersonateClientEx(entry->psec_ctx, NULL);
2010-10-12 10:02:20 -04:00
if (status != STATUS_SUCCESS)
print_error("SeImpersonateClientEx failed %x\n", status);
2010-10-12 10:02:20 -04:00
switch(entry->opcode) {
case NFS41_SHUTDOWN:
status = marshal_nfs41_shutdown(entry, pbOut, cbOut, len);
DbgP("[upcall] About to signal waiting IO thread\n");
KeSetEvent(&entry->cond, 0, FALSE);
break;
case NFS41_MOUNT:
status = marshal_nfs41_mount(entry, pbOut, cbOut, len);
break;
case NFS41_UNMOUNT:
status = marshal_nfs41_unmount(entry, pbOut, cbOut, len);
break;
case NFS41_OPEN:
status = marshal_nfs41_open(entry, pbOut, cbOut, len);
break;
case NFS41_READ:
status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
break;
case NFS41_WRITE:
status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
break;
case NFS41_LOCK:
status = marshal_nfs41_lock(entry, pbOut, cbOut, len);
break;
case NFS41_UNLOCK:
status = marshal_nfs41_unlock(entry, pbOut, cbOut, len);
break;
case NFS41_CLOSE:
status = marshal_nfs41_close(entry, pbOut, cbOut, len);
break;
case NFS41_DIR_QUERY:
status = marshal_nfs41_dirquery(entry, pbOut, cbOut, len);
break;
case NFS41_FILE_QUERY:
status = marshal_nfs41_filequery(entry, pbOut, cbOut, len);
break;
case NFS41_FILE_SET:
status = marshal_nfs41_fileset(entry, pbOut, cbOut, len);
break;
case NFS41_EA_SET:
status = marshal_nfs41_easet(entry, pbOut, cbOut, len);
break;
case NFS41_SYMLINK:
status = marshal_nfs41_symlink(entry, pbOut, cbOut, len);
break;
case NFS41_VOLUME_QUERY:
status = marshal_nfs41_volume(entry, pbOut, cbOut, len);
break;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
case NFS41_ACL_QUERY:
status = marshal_nfs41_getacl(entry, pbOut, cbOut, len);
break;
2011-04-12 15:56:20 -04:00
case NFS41_ACL_SET:
status = marshal_nfs41_setacl(entry, pbOut, cbOut, len);
break;
default:
status = STATUS_INVALID_PARAMETER;
print_error("Unknown nfs41 ops %d\n", entry->opcode);
}
if (status == STATUS_SUCCESS)
print_hexbuf(0, (unsigned char *)"upcall buffer", pbOut, *len);
return status;
}
2010-10-12 09:55:32 -04:00
NTSTATUS nfs41_UpcallCreate(
IN DWORD opcode,
IN PSECURITY_CLIENT_CONTEXT clnt_sec_ctx,
IN HANDLE session,
IN HANDLE open_state,
IN DWORD version,
OUT nfs41_updowncall_entry **entry_out)
{
NTSTATUS status = STATUS_SUCCESS;
nfs41_updowncall_entry *entry;
2010-10-12 09:55:32 -04:00
SECURITY_SUBJECT_CONTEXT sec_ctx;
2010-10-12 10:02:20 -04:00
SECURITY_QUALITY_OF_SERVICE sec_qos;
entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
NFS41_MM_POOLTAG);
if (entry == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlZeroMemory(entry, sizeof(nfs41_updowncall_entry));
entry->xid = get_next_xid();
entry->opcode = opcode;
entry->state = NFS41_WAITING_FOR_UPCALL;
entry->session = session;
entry->open_state = open_state;
entry->version = version;
/*XXX KeInitializeEvent will bugcheck under verifier if allocated from PagedPool? */
KeInitializeEvent(&entry->cond, SynchronizationEvent, FALSE);
ExInitializeFastMutex(&entry->lock);
2010-10-12 09:55:32 -04:00
if (clnt_sec_ctx == NULL) {
SeCaptureSubjectContext(&sec_ctx);
sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
sec_qos.ImpersonationLevel = SecurityImpersonation;
sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sec_qos.EffectiveOnly = 0;
status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos, 1, &entry->sec_ctx);
if (status != STATUS_SUCCESS) {
print_error("SeCreateClientSecurityFromSubjectContext "
"failed with %x\n", status);
RxFreePool(entry);
}
entry->psec_ctx = &entry->sec_ctx;
SeReleaseSubjectContext(&sec_ctx);
} else
entry->psec_ctx = clnt_sec_ctx;
2010-10-12 09:55:32 -04:00
*entry_out = entry;
out:
return status;
}
NTSTATUS nfs41_UpcallWaitForReply(
IN nfs41_updowncall_entry *entry)
{
NTSTATUS status = STATUS_SUCCESS;
const char *opstring = opcode2string(entry->opcode);
nfs41_AddEntry(upcallLock, upcall, entry);
KeSetEvent(&upcallEvent, 0, FALSE);
DbgP("@@@ Creating %s upcall entry=%p xid=%d\n", opstring, entry, entry->xid);
if (!entry->async_op) {
/* 02/03/2011 AGLO: it is not clear what the "right" waiting design should be.
* Having non-interruptable waiting seems to be the right approach. However,
* when things go wrong, the only wait to proceed is a reboot (since "waits"
* are not interruptable we can't stop a hung task.
* Having interruptable wait causes issues with security context.
* For now, I'm making CLOSE non-interruptable but keeping the rest interruptable
* so that we don't have to reboot all the time
*/
/* 02/15/2011 cbodley: added NFS41_UNLOCK for the same reason. locking
* tests were triggering an interrupted unlock, which led to a bugcheck
* in CloseSrvOpen() */
#define MAKE_WAITONCLOSE_NONITERRUPTABLE
#ifdef MAKE_WAITONCLOSE_NONITERRUPTABLE
if (entry->opcode == NFS41_CLOSE || entry->opcode == NFS41_UNLOCK)
status = KeWaitForSingleObject(&entry->cond, Executive,
KernelMode, FALSE, NULL);
else
status = KeWaitForSingleObject(&entry->cond, Executive,
UserMode, TRUE, NULL);
#else
status = KeWaitForSingleObject(&entry->cond, Executive, KernelMode, FALSE, NULL);
#endif
print_wait_status(1, "[downcall]", status, opcode2string(entry->opcode),
entry, entry->xid);
} else
goto out;
switch(status) {
case STATUS_SUCCESS:
break;
case STATUS_USER_APC:
case STATUS_ALERTED:
default:
ExAcquireFastMutex(&entry->lock);
if (entry->state == NFS41_DONE_PROCESSING) {
DbgP("[downcall] finish processing %s entry=%p xid=%d\n",
opcode2string(entry->opcode), entry, entry->xid);
ExReleaseFastMutex(&entry->lock);
break;
}
DbgP("[upcall] abandoning %s entry=%p xid=%d\n",
opcode2string(entry->opcode), entry, entry->xid);
entry->state = NFS41_NOT_WAITING;
ExReleaseFastMutex(&entry->lock);
goto out;
}
nfs41_RemoveEntry(downcallLock, downcall, entry);
out:
return status;
}
NTSTATUS
nfs41_upcall (
IN PRX_CONTEXT RxContext
)
{
NTSTATUS status = STATUS_SUCCESS; /* XXX */
nfs41_updowncall_entry *entry = NULL;
ULONG len = 0;
PLIST_ENTRY pEntry;
DbgEn();
process_upcall:
nfs41_RemoveFirst(upcallLock, upcall, pEntry);
if (pEntry) {
entry = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
nfs41_updowncall_entry, next);
ExAcquireFastMutex(&entry->lock);
nfs41_AddEntry(downcallLock, downcall, entry);
status = handle_upcall(RxContext, entry, &len);
if (status == STATUS_SUCCESS &&
entry->state == NFS41_WAITING_FOR_UPCALL)
entry->state = NFS41_WAITING_FOR_DOWNCALL;
ExReleaseFastMutex(&entry->lock);
if (status == STATUS_INSUFFICIENT_RESOURCES) {
print_error("upcall buffer is too small\n");
entry->status = status;
KeSetEvent(&entry->cond, 0, FALSE);
RxContext->InformationToReturn = 0;
} else
RxContext->InformationToReturn = len;
}
else {
DbgP("[upcall] About to go to sleep\n");
status = KeWaitForSingleObject(&upcallEvent, Executive, UserMode, TRUE,
(PLARGE_INTEGER) NULL);
print_wait_status(1, "[upcall]", status, NULL, NULL, 0);
switch (status) {
case STATUS_SUCCESS:
goto process_upcall;
case STATUS_USER_APC:
case STATUS_ALERTED:
default:
goto out;
}
}
out:
DbgEx();
return status;
}
NTSTATUS
nfs41_downcall (
IN PRX_CONTEXT RxContext
)
{
NTSTATUS status = STATUS_SUCCESS; /* XXX */
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
ULONG in_len = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
PLIST_ENTRY pEntry;
nfs41_updowncall_entry *tmp;
nfs41_updowncall_entry *cur= NULL;
DWORD found = 0;
DbgEn();
print_hexbuf(0, (unsigned char *)"downcall buffer", buf, in_len);
tmp = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
NFS41_MM_POOLTAG);
if (tmp == NULL) {
goto out;
}
RtlZeroMemory(tmp, sizeof(nfs41_updowncall_entry));
RtlCopyMemory(&tmp->xid, buf, sizeof(tmp->xid));
buf += sizeof(tmp->xid);
RtlCopyMemory(&tmp->opcode, buf, sizeof(tmp->opcode));
buf += sizeof(tmp->opcode);
RtlCopyMemory(&tmp->status, buf, sizeof(tmp->status));
buf += sizeof(tmp->status);
RtlCopyMemory(&tmp->errno, buf, sizeof(tmp->errno));
buf += sizeof(tmp->errno);
DbgP("[downcall] xid=%d opcode=%d status=%d errno=%d\n", tmp->xid, tmp->opcode,
tmp->status, tmp->errno);
ExAcquireFastMutex(&downcallLock);
pEntry = &downcall->head;
while (pEntry != NULL) {
cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
nfs41_updowncall_entry, next);
if (cur->xid == tmp->xid) {
found = 1;
break;
}
if (pEntry->Flink == &downcall->head) {
DbgP("reached end of the list\n");
break;
}
pEntry = pEntry->Flink;
}
ExReleaseFastMutex(&downcallLock);
if (!found) {
print_error("Didn't find xid=%d entry\n", tmp->xid);
goto out_free;
}
ExAcquireFastMutex(&cur->lock);
SeStopImpersonatingClient();
if (cur->state == NFS41_NOT_WAITING) {
print_error("[downcall] Nobody is waiting for this request!!!\n");
ExReleaseFastMutex(&cur->lock);
nfs41_RemoveEntry(downcallLock, downcall, cur);
SeDeleteClientSecurity(cur->psec_ctx);
RxFreePool(cur);
status = STATUS_UNSUCCESSFUL;
goto out_free;
}
cur->state = NFS41_DONE_PROCESSING;
cur->status = tmp->status;
cur->errno = tmp->errno;
status = STATUS_SUCCESS;
if (!tmp->status) {
switch (tmp->opcode) {
case NFS41_MOUNT:
RtlCopyMemory(&cur->session, buf, sizeof(HANDLE));
buf += sizeof(HANDLE);
RtlCopyMemory(&cur->version, buf, sizeof(DWORD));
DbgP("[mount] session pointer 0x%x version %d\n", cur->session, cur->version);
break;
case NFS41_WRITE:
case NFS41_READ:
RtlCopyMemory(&cur->u.ReadWrite.len, buf, sizeof(cur->u.ReadWrite.len));
DbgP("[read/write] returned len %ld\n", cur->u.ReadWrite.len);
#if 1
/* 08/27/2010: it looks like we really don't need to call MmUnmapLockedPages()
* eventhough we called MmMapLockedPagesSpecifyCache() as the MDL passed to us
* is already locked.
*/
__try {
MmUnmapLockedPages(cur->u.ReadWrite.buf, cur->u.ReadWrite.MdlAddress);
} __except(EXCEPTION_EXECUTE_HANDLER) {
NTSTATUS code;
code = GetExceptionCode();
print_error("Call to MmUnmapLockedPages failed due to"
" exception 0x%0x\n", code);
status = STATUS_ACCESS_DENIED;
}
#endif
break;
case NFS41_OPEN:
RtlCopyMemory(&cur->u.Open.binfo, buf, sizeof(FILE_BASIC_INFORMATION));
buf += sizeof(FILE_BASIC_INFORMATION);
RtlCopyMemory(&cur->u.Open.sinfo, buf, sizeof(FILE_STANDARD_INFORMATION));
buf += sizeof(FILE_STANDARD_INFORMATION);
RtlCopyMemory(&cur->open_state, buf, sizeof(HANDLE));
buf += sizeof(HANDLE);
RtlCopyMemory(&cur->u.Open.mode, buf, sizeof(DWORD));
buf += sizeof(DWORD);
RtlCopyMemory(&cur->u.Open.changeattr, buf, sizeof(LONGLONG));
buf += sizeof(LONGLONG);
if (tmp->errno == ERROR_REPARSE) {
RtlCopyMemory(&cur->u.Open.symlink_embedded, buf, sizeof(BOOLEAN));
buf += sizeof(BOOLEAN);
RtlCopyMemory(&cur->u.Open.symlink.MaximumLength, buf, sizeof(USHORT));
buf += sizeof(USHORT);
cur->u.Open.symlink.Length = cur->u.Open.symlink.MaximumLength - sizeof(WCHAR);
cur->u.Open.symlink.Buffer = RxAllocatePoolWithTag(NonPagedPool,
cur->u.Open.symlink.MaximumLength, NFS41_MM_POOLTAG);
if (cur->u.Open.symlink.Buffer == NULL) {
cur->status = STATUS_INSUFFICIENT_RESOURCES;
status = STATUS_UNSUCCESSFUL;
break;
}
RtlCopyMemory(cur->u.Open.symlink.Buffer, buf, cur->u.Open.symlink.MaximumLength);
DbgP("[open] ERROR_REPARSE -> '%wZ'\n", &cur->u.Open.symlink);
}
DbgP("[open] open_state 0x%x mode %o changeattr 0x%x\n",
cur->open_state, cur->u.Open.mode, cur->u.Open.changeattr);
break;
case NFS41_DIR_QUERY:
case NFS41_FILE_QUERY:
RtlCopyMemory(&tmp->u.QueryFile.buf_len, buf, sizeof(ULONG));
buf += sizeof(ULONG);
if (tmp->u.QueryFile.buf_len > cur->u.QueryFile.buf_len) {
cur->status = STATUS_BUFFER_TOO_SMALL;
cur->u.QueryFile.buf_len = tmp->u.QueryFile.buf_len;
break;
}
cur->u.QueryFile.buf_len = tmp->u.QueryFile.buf_len;
RtlCopyMemory(cur->u.QueryFile.buf, buf, tmp->u.QueryFile.buf_len);
break;
case NFS41_SYMLINK:
if (cur->u.Symlink.set)
break;
RtlCopyMemory(&cur->u.Symlink.target->Length, buf, sizeof(USHORT));
buf += sizeof(USHORT);
if (cur->u.Symlink.target->Length > cur->u.Symlink.target->MaximumLength) {
cur->status = STATUS_BUFFER_TOO_SMALL;
break;
}
RtlCopyMemory(cur->u.Symlink.target->Buffer, buf,
cur->u.Symlink.target->Length);
cur->u.Symlink.target->Length -= sizeof(UNICODE_NULL);
break;
case NFS41_VOLUME_QUERY:
RtlCopyMemory(&tmp->u.Volume.buf_len, buf, sizeof(LONG));
buf += sizeof(LONG);
if (tmp->u.Volume.buf_len > cur->u.Volume.buf_len) {
cur->status = STATUS_BUFFER_TOO_SMALL;
cur->u.Volume.buf_len = tmp->u.Volume.buf_len;
break;
}
cur->u.Volume.buf_len = tmp->u.Volume.buf_len;
RtlCopyMemory(cur->u.Volume.buf, buf, tmp->u.Volume.buf_len);
break;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
case NFS41_ACL_QUERY:
RtlCopyMemory(&tmp->u.Acl.buf_len, buf, sizeof(DWORD));
buf += sizeof(DWORD);
if (tmp->u.Acl.buf_len > cur->u.Acl.buf_len) {
cur->status = STATUS_BUFFER_TOO_SMALL;
cur->u.Acl.buf_len = tmp->u.Acl.buf_len;
break;
} else {
cur->u.Acl.buf = RxAllocatePoolWithTag(NonPagedPool,
tmp->u.Acl.buf_len, NFS41_MM_POOLTAG);
if (cur->u.Acl.buf == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out_free;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
}
RtlCopyMemory(cur->u.Acl.buf, buf, tmp->u.Acl.buf_len);
cur->u.Acl.buf_len = tmp->u.Acl.buf_len;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
}
break;
}
}
DbgP("[downcall] About to signal waiting IO thread\n");
ExReleaseFastMutex(&cur->lock);
if (cur->async_op) {
if (cur->status == STATUS_SUCCESS) {
cur->u.ReadWrite.rxcontext->StoredStatus = STATUS_SUCCESS;
cur->u.ReadWrite.rxcontext->InformationToReturn = cur->u.ReadWrite.len;
} else {
cur->u.ReadWrite.rxcontext->StoredStatus = map_readwrite_errors(cur->status);
cur->u.ReadWrite.rxcontext->InformationToReturn = 0;
}
nfs41_RemoveEntry(downcallLock, downcall, cur);
RxLowIoCompletion(cur->u.ReadWrite.rxcontext);
} else
KeSetEvent(&cur->cond, 0, FALSE);
out_free:
RxFreePool(tmp);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_shutdown_daemon(DWORD version)
{
NTSTATUS status = STATUS_SUCCESS;
nfs41_updowncall_entry *entry = NULL;
DbgEn();
status = nfs41_UpcallCreate(NFS41_SHUTDOWN, NULL, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE, version, &entry);
if (status)
goto out;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
SeDeleteClientSecurity(&entry->sec_ctx);
RxFreePool(entry);
out:
DbgEx();
return status;
}
static NTSTATUS SharedMemoryInit(
OUT PHANDLE phSection)
{
NTSTATUS status;
HANDLE hSection;
UNICODE_STRING SectionName;
SECURITY_DESCRIPTOR SecurityDesc;
OBJECT_ATTRIBUTES SectionAttrs;
LARGE_INTEGER nSectionSize;
DbgEn();
RtlInitUnicodeString(&SectionName, NFS41_SHARED_MEMORY_NAME);
/* XXX: setting dacl=NULL grants access to everyone */
status = RtlCreateSecurityDescriptor(&SecurityDesc,
SECURITY_DESCRIPTOR_REVISION);
if (status) {
print_error("RtlCreateSecurityDescriptor() failed with %08X\n", status);
goto out;
}
status = RtlSetDaclSecurityDescriptor(&SecurityDesc, TRUE, NULL, FALSE);
if (status) {
print_error("RtlSetDaclSecurityDescriptor() failed with %08X\n", status);
goto out;
}
InitializeObjectAttributes(&SectionAttrs, &SectionName,
0, NULL, &SecurityDesc);
nSectionSize.QuadPart = sizeof(NFS41NP_SHARED_MEMORY);
status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE,
&SectionAttrs, &nSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
switch (status) {
case STATUS_SUCCESS:
break;
case STATUS_OBJECT_NAME_COLLISION:
DbgP("section already created; returning success\n");
status = STATUS_SUCCESS;
goto out;
default:
DbgP("ZwCreateSection failed with %08X\n", status);
goto out;
}
out:
DbgEx();
return status;
}
static NTSTATUS SharedMemoryFree(
IN HANDLE hSection)
{
NTSTATUS status;
DbgEn();
status = ZwClose(hSection);
DbgEx();
return status;
}
NTSTATUS nfs41_Start(
IN OUT struct _RX_CONTEXT *RxContext,
IN OUT PRDBSS_DEVICE_OBJECT dev)
{
NTSTATUS status;
NFS41GetDeviceExtension(RxContext, DevExt);
DbgEn();
status = SharedMemoryInit(&DevExt->SharedMemorySection);
if (status) {
print_error("InitSharedMemory failed with %08X\n", status);
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
InterlockedCompareExchange((PLONG)&nfs41_start_state,
NFS41_START_DRIVER_STARTED,
NFS41_START_DRIVER_START_IN_PROGRESS);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_Stop(
IN OUT struct _RX_CONTEXT * RxContext,
IN OUT PRDBSS_DEVICE_OBJECT dev)
{
NTSTATUS status;
NFS41GetDeviceExtension(RxContext, DevExt);
DbgEn();
status = SharedMemoryFree(DevExt->SharedMemorySection);
DbgEx();
return status;
}
HANDLE
GetConnectionHandle(
IN PUNICODE_STRING ConnectionName,
IN PVOID EaBuffer,
IN ULONG EaLength
)
{
NTSTATUS status;
HANDLE Handle = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
DbgEn();
InitializeObjectAttributes(&ObjectAttributes, ConnectionName,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwCreateFile(&Handle, SYNCHRONIZE, &ObjectAttributes,
&IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
EaBuffer, EaLength);
if (!NT_SUCCESS(status))
Handle = INVALID_HANDLE_VALUE;
else
DbgP("created handle %p\n", &Handle);
DbgEx();
return Handle;
}
NTSTATUS nfs41_GetConnectionInfoFromBuffer(
IN PVOID Buffer,
IN ULONG BufferLen,
OUT PUNICODE_STRING pConnectionName,
OUT PVOID *ppEaBuffer,
OUT PULONG pEaLength)
{
NTSTATUS status = STATUS_SUCCESS;
USHORT NameLength;
USHORT EaPadding;
ULONG EaLength;
ULONG BufferLenExpected;
PBYTE ptr;
DbgEn();
/* make sure buffer is at least big enough for header */
if (BufferLen < sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG))
{
status = STATUS_BAD_NETWORK_NAME;
print_error("Invalid input buffer.\n");
pConnectionName->Length = pConnectionName->MaximumLength = 0;
*ppEaBuffer = NULL;
*pEaLength = 0;
goto out;
}
ptr = Buffer;
NameLength = *(PUSHORT)ptr;
ptr += sizeof(USHORT);
EaPadding = *(PUSHORT)ptr;
ptr += sizeof(USHORT);
EaLength = *(PULONG)ptr;
ptr += sizeof(ULONG);
/* validate buffer length */
BufferLenExpected = sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG) +
NameLength + EaPadding + EaLength;
if (BufferLen != BufferLenExpected)
{
status = STATUS_BAD_NETWORK_NAME;
print_error("Received buffer of length %lu, but expected %lu bytes.\n",
BufferLen, BufferLenExpected);
pConnectionName->Length = pConnectionName->MaximumLength = 0;
*ppEaBuffer = NULL;
*pEaLength = 0;
goto out;
}
pConnectionName->Buffer = (PWCH)ptr;
pConnectionName->Length = NameLength - sizeof(WCHAR);
pConnectionName->MaximumLength = NameLength;
DbgP("connection name %wZ\n", pConnectionName);
if (EaLength)
*ppEaBuffer = ptr + NameLength + EaPadding;
else
*ppEaBuffer = NULL;
*pEaLength = EaLength;
out:
DbgEx();
return status;
}
NTSTATUS
nfs41_CreateConnection (
IN PRX_CONTEXT RxContext,
OUT PBOOLEAN PostToFsp
)
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE Handle;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PVOID Buffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
ULONG BufferLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
UNICODE_STRING FileName;
PVOID EaBuffer;
ULONG EaLength;
BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
DbgEn();
if (!Wait) {
//just post right now!
DbgP("returning STATUS_PENDING\n");
*PostToFsp = TRUE;
status = STATUS_PENDING;
goto out;
}
status = nfs41_GetConnectionInfoFromBuffer(Buffer, BufferLen,
&FileName, &EaBuffer, &EaLength);
if (status != STATUS_SUCCESS)
goto out;
Handle = GetConnectionHandle(&FileName, EaBuffer, EaLength);
if (Handle == INVALID_HANDLE_VALUE)
status = STATUS_BAD_NETWORK_NAME;
out:
DbgEx();
return status;
}
NTSTATUS nfs41_unmount(HANDLE session, DWORD version)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
nfs41_updowncall_entry *entry;
DbgEn();
status = nfs41_UpcallCreate(NFS41_UNMOUNT, NULL, session,
INVALID_HANDLE_VALUE, version, &entry);
if (status)
goto out;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
SeDeleteClientSecurity(&entry->sec_ctx);
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS
nfs41_DeleteConnection (
IN PRX_CONTEXT RxContext,
OUT PBOOLEAN PostToFsp
)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PWCHAR ConnectName = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
ULONG ConnectNameLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
HANDLE Handle;
UNICODE_STRING FileName;
PFILE_OBJECT pFileObject;
BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
DbgEn();
if (!Wait) {
//just post right now!
*PostToFsp = TRUE;
DbgP("returning STATUS_PENDING\n");
status = STATUS_PENDING;
goto out;
}
FileName.Buffer = ConnectName;
FileName.Length = (USHORT) ConnectNameLen - sizeof(WCHAR);
FileName.MaximumLength = (USHORT) ConnectNameLen;
Handle = GetConnectionHandle(&FileName, NULL, 0);
if (Handle == INVALID_HANDLE_VALUE)
goto out;
DbgP("GetConnectionHandle returned success\n");
status = ObReferenceObjectByHandle(Handle, 0L, NULL, KernelMode,
(PVOID *)&pFileObject, NULL);
if (NT_SUCCESS(status)) {
PV_NET_ROOT VNetRoot;
DbgP("ObReferenceObjectByHandle worked ok %p\n", pFileObject);
// VNetRoot exists as FOBx in the FsContext2
VNetRoot = (PV_NET_ROOT) pFileObject->FsContext2;
// make sure the node looks right
if (NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT)
{
DbgP("Calling RxFinalizeConnection for NetRoot %p from VNetRoot %p\n",
VNetRoot->NetRoot, VNetRoot);
status = RxFinalizeConnection(VNetRoot->NetRoot, VNetRoot, TRUE);
}
else
status = STATUS_BAD_NETWORK_NAME;
ObDereferenceObject(pFileObject);
}
ZwClose(Handle);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_DevFcbXXXControlFile(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
UCHAR op = RxContext->MajorFunction;
PLOWIO_CONTEXT io_ctx = &RxContext->LowIoContext;
ULONG fsop = io_ctx->ParamsFor.FsCtl.FsControlCode;
ULONG state;
ULONG in_len = io_ctx->ParamsFor.IoCtl.InputBufferLength;
DWORD *buf = io_ctx->ParamsFor.IoCtl.pInputBuffer;
NFS41GetDeviceExtension(RxContext, DevExt);
DWORD nfs41d_version = 0;
//DbgEn();
print_ioctl(0, op);
switch(op) {
case IRP_MJ_FILE_SYSTEM_CONTROL:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
print_fs_ioctl(0, fsop);
switch (fsop) {
case IOCTL_NFS41_INVALCACHE:
status = nfs41_invalidate_cache(RxContext);
break;
case IOCTL_NFS41_READ:
status = nfs41_upcall(RxContext);
break;
case IOCTL_NFS41_WRITE:
status = nfs41_downcall(RxContext);
break;
case IOCTL_NFS41_ADDCONN:
status = nfs41_CreateConnection(RxContext, &RxContext->PostRequest);
break;
case IOCTL_NFS41_DELCONN:
if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
DbgP("device has open handles %d\n",
RxContext->RxDeviceObject->NumberOfActiveFcbs);
status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
break;
}
status = nfs41_DeleteConnection(RxContext, &RxContext->PostRequest);
break;
case IOCTL_NFS41_GETSTATE:
state = RDR_NULL_STATE;
if (io_ctx->ParamsFor.IoCtl.OutputBufferLength >=
sizeof(ULONG) ) {
// map the states to control app's equivalents
print_driver_state(nfs41_start_state);
switch (nfs41_start_state) {
case NFS41_START_DRIVER_STARTABLE:
case NFS41_START_DRIVER_STOPPED:
state = RDR_STOPPED;
break;
case NFS41_START_DRIVER_START_IN_PROGRESS:
state = RDR_STARTING;
break;
case NFS41_START_DRIVER_STARTED:
state = RDR_STARTED;
break;
}
*(ULONG *)io_ctx->ParamsFor.IoCtl.pOutputBuffer = state;
RxContext->InformationToReturn = sizeof(ULONG);
status = STATUS_SUCCESS;
} else
status = STATUS_INVALID_PARAMETER;
break;
case IOCTL_NFS41_START:
print_driver_state(nfs41_start_state);
if (in_len >= sizeof(DWORD)) {
RtlCopyMemory(&nfs41d_version, buf, sizeof(DWORD));
DbgP("NFS41 Daemon sent start request with version %d\n", nfs41d_version);
DbgP("Currently used NFS41 Daemon version is %d\n", DevExt->nfs41d_version);
DevExt->nfs41d_version = nfs41d_version;
}
switch(nfs41_start_state) {
case NFS41_START_DRIVER_STARTABLE:
(nfs41_start_driver_state)InterlockedCompareExchange(
(PLONG)&nfs41_start_state,
NFS41_START_DRIVER_START_IN_PROGRESS,
NFS41_START_DRIVER_STARTABLE);
//lack of break is intentional
case NFS41_START_DRIVER_START_IN_PROGRESS:
status = RxStartMinirdr(RxContext, &RxContext->PostRequest);
if (status == STATUS_REDIRECTOR_STARTED) {
DbgP("redirector started\n");
status = STATUS_SUCCESS;
} else if (status == STATUS_PENDING &&
RxContext->PostRequest == TRUE) {
DbgP("RxStartMinirdr pending %08lx\n", status);
status = STATUS_MORE_PROCESSING_REQUIRED;
}
break;
case NFS41_START_DRIVER_STARTED:
status = STATUS_SUCCESS;
break;
default:
status = STATUS_INVALID_PARAMETER;
}
break;
case IOCTL_NFS41_STOP:
if (nfs41_start_state == NFS41_START_DRIVER_STARTED)
nfs41_shutdown_daemon(DevExt->nfs41d_version);
if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
DbgP("device has open handles %d\n",
RxContext->RxDeviceObject->NumberOfActiveFcbs);
status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
break;
}
state = (nfs41_start_driver_state)InterlockedCompareExchange(
(PLONG)&nfs41_start_state,
NFS41_START_DRIVER_STARTABLE,
NFS41_START_DRIVER_STARTED);
status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
DbgP("RxStopMinirdr status %08lx\n", status);
if (status == STATUS_PENDING && RxContext->PostRequest == TRUE )
status = STATUS_MORE_PROCESSING_REQUIRED;
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
};
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
};
//DbgEx();
return status;
}
NTSTATUS
_nfs41_CreateSrvCall(
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
{
NTSTATUS status = STATUS_SUCCESS;
PMRX_SRVCALL_CALLBACK_CONTEXT SCCBC = pCallbackContext;
PMRX_SRV_CALL pSrvCall;
PMRX_SRVCALLDOWN_STRUCTURE SrvCalldownStructure =
(PMRX_SRVCALLDOWN_STRUCTURE)(SCCBC->SrvCalldownStructure);
PNFS41_SERVER_ENTRY pServerEntry = NULL;
DbgEn();
pSrvCall = SrvCalldownStructure->SrvCall;
ASSERT( pSrvCall );
ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
print_srv_call(0, pSrvCall);
// validate the server name with the test name of 'pnfs'
DbgP("SrvCall: Connection Name Length: %d %wZ\n",
pSrvCall->pSrvCallName->Length, pSrvCall->pSrvCallName);
if (pSrvCall->pSrvCallName->Length > SERVER_NAME_BUFFER_SIZE) {
print_error("Server name '%wZ' too long for server entry (max %u)\n",
pSrvCall->pSrvCallName, SERVER_NAME_BUFFER_SIZE);
status = STATUS_NAME_TOO_LONG;
goto out;
}
/* Let's create our own representation of the server */
pServerEntry = (PNFS41_SERVER_ENTRY)RxAllocatePoolWithTag(PagedPool,
sizeof(NFS41_SERVER_ENTRY), NFS41_MM_POOLTAG);
if (pServerEntry == NULL) {
print_error("failed to allocate memory for pServerEntry\n");
status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
RtlZeroMemory(pServerEntry, sizeof(NFS41_SERVER_ENTRY));
pServerEntry->Name.Buffer = pServerEntry->NameBuffer;
pServerEntry->Name.Length = pSrvCall->pSrvCallName->Length;
pServerEntry->Name.MaximumLength = SERVER_NAME_BUFFER_SIZE;
RtlCopyMemory(pServerEntry->Name.Buffer, pSrvCall->pSrvCallName->Buffer,
pServerEntry->Name.Length);
DbgP("copying server name %wZ into server entry %p\n",
&pServerEntry->Name, pServerEntry);
pCallbackContext->RecommunicateContext = pServerEntry;
DbgP("saving pServerEntry %p in RecommunicateContext\n", pServerEntry);
InterlockedExchangePointer(&pServerEntry->pRdbssSrvCall, pSrvCall);
DbgP("saving pSrvCall %p in pServerEntry's pRdbssSrvCall\n", pSrvCall);
out:
SCCBC->Status = status;
SrvCalldownStructure->CallBack(SCCBC);
DbgEx();
return status;
}
NTSTATUS nfs41_CreateSrvCall(
PMRX_SRV_CALL pSrvCall,
PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
{
NTSTATUS status;
DbgEn();
ASSERT( pSrvCall );
ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
DbgP("pCallbackContext %p\n", pCallbackContext);
if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
DbgP("executing with RDBSS context\n");
status = _nfs41_CreateSrvCall(pCallbackContext);
} else {
DbgP("dispatching CreateSrvCall to a system thread\n");
status = RxDispatchToWorkerThread(nfs41_dev, DelayedWorkQueue,
_nfs41_CreateSrvCall, pCallbackContext);
if (status != STATUS_SUCCESS) {
print_error("RxDispatchToWorkerThread returned status %08lx\n", status);
pCallbackContext->Status = status;
pCallbackContext->SrvCalldownStructure->CallBack(pCallbackContext);
status = STATUS_PENDING;
}
}
/* RDBSS expects MRxCreateSrvCall to return STATUS_PENDING */
if (status == STATUS_SUCCESS) {
DbgP("mapping SUCCESS returned status to PENDING\n");
status = STATUS_PENDING;
}
DbgEx();
return status;
}
NTSTATUS nfs41_SrvCallWinnerNotify(
IN OUT PMRX_SRV_CALL pSrvCall,
IN BOOLEAN ThisMinirdrIsTheWinner,
IN OUT PVOID pSrvCallContext)
{
NTSTATUS status = STATUS_SUCCESS;
PNFS41_SERVER_ENTRY pServerEntry;
DbgEn();
pServerEntry = (PNFS41_SERVER_ENTRY)pSrvCallContext;
if (!ThisMinirdrIsTheWinner) {
ASSERT(1);
goto out;
}
pSrvCall->Context = pServerEntry;
DbgP("This minirdr is the winner SrvCall context %p points to server entry %p\n",
pSrvCall->Context, pServerEntry);
out:
DbgEx();
return status;
}
static NTSTATUS map_mount_errors(DWORD status)
{
switch (status) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NETWORK_UNREACHABLE:
case ERROR_BAD_NET_RESP: return STATUS_UNEXPECTED_NETWORK_ERROR;
case ERROR_BAD_NETPATH: return STATUS_BAD_NETWORK_PATH;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INSUFFICIENT_RESOURCES\n", status);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
NTSTATUS nfs41_mount(PUNICODE_STRING srv_name, PUNICODE_STRING root,
2010-12-01 19:11:47 -05:00
DWORD sec_flavor, PHANDLE session, DWORD *version)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
nfs41_updowncall_entry *entry;
DbgEn();
status = nfs41_UpcallCreate(NFS41_MOUNT, NULL, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE, *version, &entry);
if (status)
goto out;
entry->u.Mount.srv_name = srv_name;
entry->u.Mount.root = root;
2010-12-01 19:11:47 -05:00
entry->u.Mount.sec_flavor = sec_flavor;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
SeDeleteClientSecurity(&entry->sec_ctx);
*session = entry->session;
/* map windows ERRORs to NTSTATUS */
status = map_mount_errors(entry->status);
if (status == STATUS_SUCCESS)
*version = entry->version;
RxFreePool(entry);
out:
DbgEx();
return status;
}
/* TODO: move mount config stuff to another file -cbodley */
void nfs41_MountConfig_InitDefaults(
OUT PNFS41_MOUNT_CONFIG Config)
{
RtlZeroMemory(Config, sizeof(NFS41_MOUNT_CONFIG));
Config->ReadSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
Config->WriteSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
Config->ReadOnly = FALSE;
Config->SrvName.Length = 0;
Config->SrvName.MaximumLength = SERVER_NAME_BUFFER_SIZE;
Config->SrvName.Buffer = Config->srv_buffer;
Config->MntPt.Length = 0;
Config->MntPt.MaximumLength = MAX_PATH;
Config->MntPt.Buffer = Config->mntpt_buffer;
2010-12-01 19:11:47 -05:00
Config->SecFlavor.Length = 0;
Config->SecFlavor.MaximumLength = MAX_SEC_FLAVOR_LEN;
Config->SecFlavor.Buffer = Config->sec_flavor;
RtlCopyUnicodeString(&Config->SecFlavor, &AUTH_SYS_NAME);
}
static NTSTATUS nfs41_MountConfig_ParseBoolean(
IN PFILE_FULL_EA_INFORMATION Option,
IN PUNICODE_STRING usValue,
OUT PBOOLEAN Value)
{
NTSTATUS status = STATUS_SUCCESS;
/* if no value is specified, assume TRUE
* if a value is specified, it must be a '1' */
if (Option->EaValueLength == 0 || *usValue->Buffer == L'1')
*Value = TRUE;
else
*Value = FALSE;
DbgP(" '%ls' -> '%wZ' -> %u\n",
(LPWSTR)Option->EaName, *usValue, *Value);
return status;
}
static NTSTATUS nfs41_MountConfig_ParseDword(
IN PFILE_FULL_EA_INFORMATION Option,
IN PUNICODE_STRING usValue,
OUT PDWORD Value,
IN DWORD Minimum,
IN DWORD Maximum)
{
NTSTATUS status;
LPWSTR Name = (LPWSTR)Option->EaName;
if (Option->EaValueLength)
{
status = RtlUnicodeStringToInteger(usValue, 0, Value);
if (status == STATUS_SUCCESS)
{
if (*Value < Minimum)
*Value = Minimum;
if (*Value > Maximum)
*Value = Maximum;
DbgP(" '%ls' -> '%wZ' -> %lu\n", Name, *usValue, *Value);
}
else
print_error("Failed to convert %s='%wZ' to unsigned long.\n",
Name, *usValue);
}
else
status = STATUS_INVALID_PARAMETER;
return status;
}
NTSTATUS nfs41_MountConfig_ParseOptions(
IN PFILE_FULL_EA_INFORMATION EaBuffer,
IN ULONG EaLength,
IN OUT PNFS41_MOUNT_CONFIG Config)
{
NTSTATUS status = STATUS_SUCCESS;
PFILE_FULL_EA_INFORMATION Option;
LPWSTR Name;
size_t NameLen;
UNICODE_STRING usValue;
DbgEn();
Option = EaBuffer;
while (status == STATUS_SUCCESS)
{
Name = (LPWSTR)Option->EaName;
NameLen = Option->EaNameLength/sizeof(WCHAR);
usValue.Length = usValue.MaximumLength = Option->EaValueLength;
usValue.Buffer = (PWCH)(Option->EaName +
Option->EaNameLength + sizeof(WCHAR));
if (wcsncmp(L"ro", Name, NameLen) == 0)
{
status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
&Config->ReadOnly);
}
else if (wcsncmp(L"rsize", Name, NameLen) == 0)
{
status = nfs41_MountConfig_ParseDword(Option, &usValue,
&Config->ReadSize, MOUNT_CONFIG_RW_SIZE_MIN,
MOUNT_CONFIG_RW_SIZE_MAX);
}
else if (wcsncmp(L"wsize", Name, NameLen) == 0)
{
status = nfs41_MountConfig_ParseDword(Option, &usValue,
&Config->WriteSize, MOUNT_CONFIG_RW_SIZE_MIN,
MOUNT_CONFIG_RW_SIZE_MAX);
}
else if (wcsncmp(L"srvname", Name, NameLen) == 0)
{
if (usValue.Length > Config->SrvName.MaximumLength)
status = STATUS_NAME_TOO_LONG;
else
RtlCopyUnicodeString(&Config->SrvName, &usValue);
}
else if (wcsncmp(L"mntpt", Name, NameLen) == 0)
{
if (usValue.Length > Config->MntPt.MaximumLength)
status = STATUS_NAME_TOO_LONG;
else
RtlCopyUnicodeString(&Config->MntPt, &usValue);
}
2010-12-01 19:11:47 -05:00
else if (wcsncmp(L"sec", Name, NameLen) == 0)
{
if (usValue.Length > Config->SecFlavor.MaximumLength)
status = STATUS_NAME_TOO_LONG;
else
RtlCopyUnicodeString(&Config->SecFlavor, &usValue);
}
else
{
status = STATUS_INVALID_PARAMETER;
print_error("Unrecognized option '%ls' -> '%wZ'\n",
Name, usValue);
}
if (Option->NextEntryOffset == 0)
break;
Option = (PFILE_FULL_EA_INFORMATION)
((PBYTE)Option + Option->NextEntryOffset);
}
DbgEx();
return status;
}
static NTSTATUS has_nfs_prefix(
IN PUNICODE_STRING SrvCallName,
IN PUNICODE_STRING NetRootName)
{
NTSTATUS status = STATUS_BAD_NETWORK_NAME;
if (NetRootName->Length >= SrvCallName->Length + NfsPrefix.Length) {
const UNICODE_STRING NetRootPrefix = {
NfsPrefix.Length,
NetRootName->MaximumLength - SrvCallName->Length,
&NetRootName->Buffer[SrvCallName->Length/2]
};
if (RtlCompareUnicodeString(&NetRootPrefix, &NfsPrefix, FALSE) == 0)
status = STATUS_SUCCESS;
}
return status;
}
2010-12-01 19:11:47 -05:00
static NTSTATUS map_sec_flavor(
IN PUNICODE_STRING sec_flavor_name,
OUT PDWORD sec_flavor)
{
if (RtlCompareUnicodeString(sec_flavor_name, &AUTH_SYS_NAME, FALSE) == 0)
*sec_flavor = RPCSEC_AUTH_SYS;
else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5_NAME, FALSE) == 0)
*sec_flavor = RPCSEC_AUTHGSS_KRB5;
else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5I_NAME, FALSE) == 0)
*sec_flavor = RPCSEC_AUTHGSS_KRB5I;
else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5P_NAME, FALSE) == 0)
*sec_flavor = RPCSEC_AUTHGSS_KRB5P;
else return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
NTSTATUS nfs41_CreateVNetRoot(
IN OUT PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
{
NTSTATUS status = STATUS_SUCCESS;
NFS41_MOUNT_CONFIG Config;
PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)pCreateNetRootContext->pVNetRoot;
PMRX_NET_ROOT pNetRoot = pVNetRoot->pNetRoot;
PMRX_SRV_CALL pSrvCall = pNetRoot->pSrvCall;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(pVNetRoot);
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(pNetRoot);
NFS41GetDeviceExtension(pCreateNetRootContext->RxContext,DevExt);
DWORD nfs41d_version = DevExt->nfs41d_version;
ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) &&
(NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL));
DbgEn();
print_srv_call(0, pSrvCall);
print_net_root(0, pNetRoot);
print_v_net_root(1, pVNetRoot);
DbgP("pVNetRoot=%p pNetRoot=%p\n", pVNetRoot, pNetRoot);
DbgP("pNetRoot=%wZ pSrvCallName=%wZ VirtualNetRootStatus=0x%x "
"NetRootStatus=0x%x\n", pNetRoot->pNetRootName, pSrvCall->pSrvCallName,
pCreateNetRootContext->VirtualNetRootStatus,
pCreateNetRootContext->NetRootStatus);
if (pNetRoot->Type != NET_ROOT_DISK) {
print_error("pNetRoot->Type %u != NET_ROOT_DISK\n", pNetRoot->Type);
status = STATUS_NOT_SUPPORTED;
goto out;
}
/* In order to cooperate with other network providers, we must
* only claim paths of the form '\\server\nfs4\path' */
status = has_nfs_prefix(pSrvCall->pSrvCallName, pNetRoot->pNetRootName);
if (status) {
print_error("NetRootName %wZ doesn't match '\\nfs4'!\n",
pNetRoot->pNetRootName);
goto out;
}
pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
pNetRoot->DeviceType = FILE_DEVICE_DISK;
nfs41_MountConfig_InitDefaults(&Config);
if (pCreateNetRootContext->RxContext->Create.EaLength) {
/* parse the extended attributes for mount options */
status = nfs41_MountConfig_ParseOptions(
pCreateNetRootContext->RxContext->Create.EaBuffer,
pCreateNetRootContext->RxContext->Create.EaLength,
&Config);
if (status != STATUS_SUCCESS)
goto out;
} else {
/* use the SRV_CALL name (without leading \) as the hostname */
Config.SrvName.Buffer = pSrvCall->pSrvCallName->Buffer + 1;
Config.SrvName.Length =
pSrvCall->pSrvCallName->Length - sizeof(WCHAR);
Config.SrvName.MaximumLength =
pSrvCall->pSrvCallName->MaximumLength - sizeof(WCHAR);
}
2010-12-01 19:11:47 -05:00
status = map_sec_flavor(&Config.SecFlavor, &pVNetRootContext->sec_flavor);
if (status != STATUS_SUCCESS) {
DbgP("Invalid rpcsec security flavor %wZ\n", &Config.SecFlavor);
goto out;
}
if (pVNetRootContext->sec_flavor == RPCSEC_AUTH_SYS &&
pNetRootContext->auth_sys_session) {
pVNetRootContext->session = pNetRootContext->auth_sys_session;
DbgP("Using existing AUTH_SYS session 0x%x\n", pVNetRootContext->session);
goto out;
} else if (pVNetRootContext->sec_flavor != RPCSEC_AUTH_SYS &&
pNetRootContext->gss_session) {
pVNetRootContext->session = pNetRootContext->gss_session;
DbgP("Using existing AUTHGSS session 0x%x\n", pVNetRootContext->session);
goto out;
}
/* send the mount upcall */
DbgP("Server Name %wZ Mount Point %wZ SecFlavor %wZ\n",
&Config.SrvName, &Config.MntPt, &Config.SecFlavor);
2010-12-01 19:11:47 -05:00
status = nfs41_mount(&Config.SrvName, &Config.MntPt, pVNetRootContext->sec_flavor,
&pVNetRootContext->session, &nfs41d_version);
if (status != STATUS_SUCCESS)
goto out;
pNetRootContext->nfs41d_version = nfs41d_version;
if (pVNetRootContext->sec_flavor == RPCSEC_AUTH_SYS)
pNetRootContext->auth_sys_session = pVNetRootContext->session;
else
pNetRootContext->gss_session = pVNetRootContext->session;
DbgP("Saving new session 0x%x\n", pVNetRootContext->session);
out:
/* AGLO do we need to worry about handling new netroot vs using existing one */
pCreateNetRootContext->VirtualNetRootStatus = status;
pCreateNetRootContext->NetRootStatus = status;
DbgP("initiating net root callback with status %08lx\n", status);
pCreateNetRootContext->Callback(pCreateNetRootContext);
/* RDBSS expects that MRxCreateVNetRoot returns STATUS_PENDING
* on success or failure */
status = STATUS_PENDING;
DbgEx();
return status;
}
VOID nfs41_ExtractNetRootName(
IN PUNICODE_STRING FilePathName,
IN PMRX_SRV_CALL SrvCall,
OUT PUNICODE_STRING NetRootName,
OUT PUNICODE_STRING RestOfName OPTIONAL)
{
ULONG length = FilePathName->Length;
PWCH w = FilePathName->Buffer;
PWCH wlimit = (PWCH)(((PCHAR)w)+length);
PWCH wlow;
DbgEn();
DbgP("Input: pSrvCall %p\n", SrvCall);
DbgP("Input: FilePathName=%wZ SrvCallName=%wZ\n",
FilePathName, SrvCall->pSrvCallName);
w += (SrvCall->pSrvCallName->Length/sizeof(WCHAR));
NetRootName->Buffer = wlow = w;
/* parse the entire path into NetRootName */
#if USE_ENTIRE_PATH
w = wlimit;
#else
for (;;) {
if (w >= wlimit)
break;
if ((*w == OBJ_NAME_PATH_SEPARATOR) && (w != wlow))
break;
w++;
}
#endif
NetRootName->Length = NetRootName->MaximumLength
= (USHORT)((PCHAR)w - (PCHAR)wlow);
DbgP("Output: NetRootName=%wZ\n", NetRootName);
DbgR();
return;
}
NTSTATUS nfs41_FinalizeSrvCall(
PMRX_SRV_CALL pSrvCall,
BOOLEAN Force)
{
NTSTATUS status = STATUS_SUCCESS;
PNFS41_SERVER_ENTRY pServerEntry = (PNFS41_SERVER_ENTRY)(pSrvCall->Context);
DbgEn();
print_srv_call(0, pSrvCall);
if (pSrvCall->Context == NULL)
goto out;
InterlockedCompareExchangePointer(&pServerEntry->pRdbssSrvCall, NULL, pSrvCall);
DbgP("freeing server name %wZ and server entry %p\n",
&pServerEntry->Name, pServerEntry);
RxFreePool(pServerEntry);
pSrvCall->Context = NULL;
out:
DbgEx();
return status;
}
NTSTATUS nfs41_FinalizeNetRoot(
IN OUT PMRX_NET_ROOT pNetRoot,
IN PBOOLEAN ForceDisconnect)
{
NTSTATUS status = STATUS_SUCCESS;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension((PMRX_NET_ROOT)pNetRoot);
nfs41_updowncall_entry *tmp;
DbgEn();
print_net_root(1, pNetRoot);
if (pNetRoot->Type != NET_ROOT_DISK) {
status = STATUS_NOT_SUPPORTED;
goto out;
}
if (pNetRootContext == NULL || (pNetRootContext->auth_sys_session == NULL &&
pNetRootContext->gss_session == NULL)) {
print_error("No valid session has been established\n");
goto out;
}
if (pNetRoot->NumberOfFcbs > 0 || pNetRoot->NumberOfSrvOpens > 0) {
print_error("%d open Fcbs %d open SrvOpens\n", pNetRoot->NumberOfFcbs,
pNetRoot->NumberOfSrvOpens);
goto out;
}
if (pNetRootContext->auth_sys_session) {
status = nfs41_unmount(pNetRootContext->auth_sys_session, pNetRootContext->nfs41d_version);
if (status) {
print_error("nfs41_mount AUTH_SYS failed with %d\n", status);
goto out;
}
}
if (pNetRootContext->gss_session) {
status = nfs41_unmount(pNetRootContext->gss_session, pNetRootContext->nfs41d_version);
if (status) {
print_error("nfs41_mount AUTHGSS failed with %d\n", status);
goto out;
}
}
// check if there is anything waiting in the upcall or downcall queue
do {
nfs41_GetFirstEntry(upcallLock, upcall, tmp);
if (tmp != NULL) {
DbgP("Removing entry from upcall list\n");
nfs41_RemoveEntry(upcallLock, upcall, tmp);
tmp->status = STATUS_INSUFFICIENT_RESOURCES;
KeSetEvent(&tmp->cond, 0, FALSE);
} else
break;
} while (1);
do {
nfs41_GetFirstEntry(downcallLock, downcall, tmp);
if (tmp != NULL) {
DbgP("Removing entry from downcall list\n");
nfs41_RemoveEntry(downcallLock, downcall, tmp);
tmp->status = STATUS_INSUFFICIENT_RESOURCES;
KeSetEvent(&tmp->cond, 0, FALSE);
} else
break;
} while (1);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_FinalizeVNetRoot(
IN OUT PMRX_V_NET_ROOT pVNetRoot,
IN PBOOLEAN ForceDisconnect)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
print_v_net_root(1, pVNetRoot);
if (pVNetRoot->pNetRoot->Type != NET_ROOT_DISK)
status = STATUS_NOT_SUPPORTED;
DbgEx();
return status;
}
BOOLEAN isDataAccess(ACCESS_MASK mask)
{
if ((mask & FILE_READ_DATA) ||
(mask & FILE_WRITE_DATA) ||
(mask & FILE_APPEND_DATA))
return TRUE;
return FALSE;
}
BOOLEAN has_file_changed(
IN LONGLONG new_changeattr,
IN PFILE_BASIC_INFORMATION new_binfo,
IN PNFS41_FCB nfs41_fcb)
{
if (new_changeattr != nfs41_fcb->changeattr && nfs41_fcb->changeattr)
return TRUE;
if (new_binfo->ChangeTime.QuadPart != nfs41_fcb->BasicInfo.ChangeTime.QuadPart)
return TRUE;
return FALSE;
}
void print_open_args(PRX_CONTEXT RxContext)
{
print_debug_header(RxContext);
print_net_root(0, RxContext->pFcb->pNetRoot);
print_v_net_root(0, RxContext->pRelevantSrvOpen->pVNetRoot);
//DbgP("RxContext->FsdUid %ld\n", RxContext->FsdUid);
//DbgP("RxInferFileType returns %d\n", RxInferFileType(RxContext));
print_irps_flags(0, RxContext->CurrentIrpSp);
print_irp_flags(0, RxContext->CurrentIrp);
print_nt_create_params(1, RxContext->Create.NtCreateParameters);
}
static NTSTATUS map_open_errors(DWORD status, int len)
{
switch (status) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_ACCESS_DENIED:
if (len > 0) return STATUS_NETWORK_ACCESS_DENIED;
else return STATUS_SUCCESS;
case ERROR_INVALID_NAME: return STATUS_OBJECT_NAME_INVALID;
case ERROR_FILE_EXISTS: return STATUS_OBJECT_NAME_COLLISION;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_FILENAME_EXCED_RANGE: return STATUS_NAME_TOO_LONG;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;
case ERROR_PATH_NOT_FOUND: return STATUS_OBJECT_PATH_NOT_FOUND;
case ERROR_BAD_NETPATH: return STATUS_BAD_NETWORK_PATH;
case ERROR_SHARING_VIOLATION: return STATUS_SHARING_VIOLATION;
case ERROR_REPARSE: return STATUS_REPARSE;
case ERROR_TOO_MANY_LINKS: return STATUS_TOO_MANY_LINKS;
default:
print_error("[ERROR] nfs41_Create: upcall returned %d returning "
"STATUS_INSUFFICIENT_RESOURCES\n", status);
case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES;
}
}
static DWORD map_disposition_to_create_retval(DWORD disposition, DWORD errno)
{
switch(disposition) {
case FILE_SUPERSEDE:
if (errno == ERROR_FILE_NOT_FOUND) return FILE_CREATED;
else return FILE_SUPERSEDED;
case FILE_CREATE: return FILE_CREATED;
case FILE_OPEN: return FILE_OPENED;
case FILE_OPEN_IF:
if (errno == ERROR_FILE_NOT_FOUND) return FILE_CREATED;
else return FILE_OPENED;
case FILE_OVERWRITE: return FILE_OVERWRITTEN;
case FILE_OVERWRITE_IF:
if (errno == ERROR_FILE_NOT_FOUND) return FILE_CREATED;
else return FILE_OVERWRITTEN;
default:
print_error("unknown disposition %d\n", disposition);
return FILE_OPENED;
}
}
NTSTATUS nfs41_Create(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
__notnull PMRX_FCB Fcb = RxContext->pFcb;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
FCB_INIT_PACKET InitPacket;
RX_FILE_TYPE StorageType = 0;
NT_CREATE_PARAMETERS params = RxContext->Create.NtCreateParameters;
nfs41_updowncall_entry *entry = NULL;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FOBX nfs41_fobx = NULL;
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)Fcb->Context;
PFILE_FULL_EA_INFORMATION eainfo = NULL;
nfs3_attrs *attrs = NULL;
BOOLEAN file_changed = FALSE;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
DbgEn();
print_open_args(RxContext);
if (RxContext->CurrentIrp->AssociatedIrp.SystemBuffer)
print_ea_info(1, RxContext->CurrentIrp->AssociatedIrp.SystemBuffer);
if (Fcb->pNetRoot->Type != NET_ROOT_DISK) {
print_error("unknown netroot type %d\n", Fcb->pNetRoot->Type);
status = STATUS_NOT_IMPLEMENTED;
goto out;
}
if (FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
print_error("FCB_STATE_PAGING_FILE not implemented\n");
status = STATUS_NOT_IMPLEMENTED;
goto out;
}
if (pNetRootContext->auth_sys_session == NULL && pNetRootContext->gss_session == NULL) {
print_error("No valid session established\n");
goto out;
}
status = nfs41_UpcallCreate(NFS41_OPEN, NULL, pVNetRootContext->session,
INVALID_HANDLE_VALUE, pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.Open.filename = SrvOpen->pAlreadyPrefixedName;
entry->u.Open.access_mask = params.DesiredAccess;
entry->u.Open.access_mode = params.ShareAccess;
entry->u.Open.attrs = params.FileAttributes;
entry->u.Open.disp = params.Disposition;
entry->u.Open.copts = params.CreateOptions;
if (isDataAccess(params.DesiredAccess))
entry->u.Open.open_owner_id = get_next_open_owner();
// if we are creating a file check if nfsv3attributes were passed in
if (params.Disposition != FILE_OPEN && params.Disposition != FILE_OVERWRITE) {
if (RxContext->CurrentIrp->AssociatedIrp.SystemBuffer) {
eainfo = (PFILE_FULL_EA_INFORMATION)
RxContext->CurrentIrp->AssociatedIrp.SystemBuffer;
if (AnsiStrEq(&NfsV3Attributes, eainfo->EaName, eainfo->EaNameLength)) {
attrs = (nfs3_attrs *)(eainfo->EaName + eainfo->EaNameLength + 1);
DbgP("creating file with mode %o\n", attrs->mode);
entry->u.Open.mode = attrs->mode;
}
}
if (!entry->u.Open.mode)
entry->u.Open.mode = 0777;
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
SeDeleteClientSecurity(&entry->sec_ctx);
if (entry->status == NO_ERROR && entry->errno == ERROR_REPARSE) {
/* symbolic link handling. when attempting to open a symlink when the
* FILE_OPEN_REPARSE_POINT flag is not set, replace the filename with
* the symlink target's by calling RxPrepareToReparseSymbolicLink()
* and returning STATUS_REPARSE. the object manager will attempt to
* open the new path, and return its handle for the original open */
PRDBSS_DEVICE_OBJECT DeviceObject = RxContext->RxDeviceObject;
PV_NET_ROOT VNetRoot = (PV_NET_ROOT)RxContext->pRelevantSrvOpen->pVNetRoot;
PUNICODE_STRING VNetRootPrefix = &VNetRoot->PrefixEntry.Prefix;
UNICODE_STRING AbsPath;
PCHAR buf;
BOOLEAN ReparseRequired;
/* allocate the string for RxPrepareToReparseSymbolicLink(), and
* format an absolute path "DeviceName+VNetRootName+symlink" */
AbsPath.Length = DeviceObject->DeviceName.Length +
VNetRootPrefix->Length + entry->u.Open.symlink.Length;
AbsPath.MaximumLength = AbsPath.Length + sizeof(UNICODE_NULL);
AbsPath.Buffer = RxAllocatePoolWithTag(NonPagedPool,
AbsPath.MaximumLength, NFS41_MM_POOLTAG);
if (AbsPath.Buffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out_free;
}
buf = (PCHAR)AbsPath.Buffer;
RtlCopyMemory(buf, DeviceObject->DeviceName.Buffer, DeviceObject->DeviceName.Length);
buf += DeviceObject->DeviceName.Length;
RtlCopyMemory(buf, VNetRootPrefix->Buffer, VNetRootPrefix->Length);
buf += VNetRootPrefix->Length;
RtlCopyMemory(buf, entry->u.Open.symlink.Buffer, entry->u.Open.symlink.Length);
RxFreePool(entry->u.Open.symlink.Buffer);
buf += entry->u.Open.symlink.Length;
*(PWCHAR)buf = UNICODE_NULL;
status = RxPrepareToReparseSymbolicLink(RxContext,
entry->u.Open.symlink_embedded, &AbsPath, TRUE, &ReparseRequired);
DbgP("RxPrepareToReparseSymbolicLink(%u, '%wZ') returned %08lX, "
"FileName is '%wZ'\n", entry->u.Open.symlink_embedded,
&AbsPath, status, &RxContext->CurrentIrpSp->FileObject->FileName);
if (status == STATUS_SUCCESS)
status = ReparseRequired ? STATUS_REPARSE :
STATUS_OBJECT_PATH_NOT_FOUND;
goto out_free;
}
status = map_open_errors(entry->status, SrvOpen->pAlreadyPrefixedName->Length);
if (status != STATUS_SUCCESS) {
print_open_error(1, status);
goto out_free;
}
if (!RxIsFcbAcquiredExclusive(Fcb)) {
ASSERT(!RxIsFcbAcquiredShared(Fcb));
RxAcquireExclusiveFcbResourceInMRx(Fcb);
}
RxContext->pFobx = RxCreateNetFobx(RxContext, SrvOpen);
if( RxContext->pFobx == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto out_free;
}
print_fobx(1, RxContext->pFobx);
nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
nfs41_fobx->nfs41_open_state = entry->open_state;
{
SECURITY_SUBJECT_CONTEXT sec_ctx;
SECURITY_QUALITY_OF_SERVICE sec_qos;
SeCaptureSubjectContext(&sec_ctx);
sec_qos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
sec_qos.ImpersonationLevel = SecurityImpersonation;
sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sec_qos.EffectiveOnly = 0;
status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos, 1, &nfs41_fobx->sec_ctx);
if (status != STATUS_SUCCESS) {
print_error("SeCreateClientSecurityFromSubjectContext "
"failed with %x\n", status);
RxFreePool(entry);
}
DbgP("Created client security token %p\n", nfs41_fobx->sec_ctx.ClientToken);
SeReleaseSubjectContext(&sec_ctx);
}
// we get attributes only for data access and file (not directories)
if (Fcb->OpenCount > 0)
file_changed = has_file_changed(entry->u.Open.changeattr,
&entry->u.Open.binfo, nfs41_fcb);
if (Fcb->OpenCount == 0 || file_changed) {
print_basic_info(1, &entry->u.Open.binfo);
print_std_info(1, &entry->u.Open.sinfo);
RtlCopyMemory(&nfs41_fcb->BasicInfo, &entry->u.Open.binfo,
sizeof(entry->u.Open.binfo));
RtlCopyMemory(&nfs41_fcb->StandardInfo, &entry->u.Open.sinfo,
sizeof(entry->u.Open.sinfo));
nfs41_fcb->mode = entry->u.Open.mode;
nfs41_fcb->changeattr = entry->u.Open.changeattr;
nfs41_fcb->Flags = FCB_BASIC_INFO_CACHED | FCB_STANDARD_INFO_CACHED;
RxFormInitPacket(InitPacket,
&entry->u.Open.binfo.FileAttributes,
&entry->u.Open.sinfo.NumberOfLinks,
&entry->u.Open.binfo.CreationTime,
&entry->u.Open.binfo.LastAccessTime,
&entry->u.Open.binfo.LastWriteTime,
&entry->u.Open.binfo.ChangeTime,
&entry->u.Open.sinfo.AllocationSize,
&entry->u.Open.sinfo.EndOfFile,
&entry->u.Open.sinfo.EndOfFile);
if (entry->u.Open.sinfo.Directory)
StorageType = FileTypeDirectory;
else
StorageType = FileTypeFile;
RxFinishFcbInitialization(Fcb, RDBSS_STORAGE_NTC(StorageType),
&InitPacket);
}
else {
DbgP("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
if (nfs41_fcb->Flags) {
print_basic_info(1, &nfs41_fcb->BasicInfo);
print_std_info(1, &nfs41_fcb->StandardInfo);
}
}
if (file_changed && !nfs41_fcb->StandardInfo.Directory) {
ULONG flag = DISABLE_CACHING;
DbgP("file object %wZ changed\n", SrvOpen->pAlreadyPrefixedName);
RxIndicateChangeOfBufferingStateForSrvOpen(SrvOpen->pVNetRoot->pNetRoot->pSrvCall,
SrvOpen, SrvOpen->Key, ULongToPtr(flag));
} else if (!file_changed && !nfs41_fcb->StandardInfo.Directory) {
#if 0
SrvOpen->BufferingFlags |= FCB_STATE_DISABLE_LOCAL_BUFFERING;
#else
// turn on read caching
if (params.DesiredAccess & FILE_READ_DATA)
SrvOpen->BufferingFlags |=
(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED);
// turn on write caching only if the file opened for both reading and writing
// we current CANT turn on write-only caching because RDBSS translates a write
// into a read first which leads to a NFS4ERR_IO error from the server because
// the file was opened read-only.
if (/*(params.DesiredAccess & FILE_READ_DATA) && */
(params.DesiredAccess & FILE_WRITE_DATA ||
params.DesiredAccess & FILE_APPEND_DATA))
SrvOpen->BufferingFlags |=
(FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
#endif
}
if (params.CreateOptions & FILE_DELETE_ON_CLOSE) {
DbgP("We need to delete this file on close\n");
nfs41_fcb->StandardInfo.DeletePending = TRUE;
}
RxContext->Create.ReturnedCreateInformation =
map_disposition_to_create_retval(params.Disposition, entry->errno);
RxContext->pFobx->OffsetOfNextEaToReturn = 1;
RxContext->CurrentIrp->IoStatus.Information =
RxContext->Create.ReturnedCreateInformation;
status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
out_free:
if (entry)
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_CollapseOpen(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_ShouldTryToCollapseThisOpen(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
DbgEn();
if (RxContext->pRelevantSrvOpen == NULL)
status = STATUS_SUCCESS;
else
print_debug_header(RxContext);
DbgEx();
return status;
}
ULONG nfs41_ExtendForCache(
IN OUT PRX_CONTEXT RxContext,
IN PLARGE_INTEGER pNewFileSize,
OUT PLARGE_INTEGER pNewAllocationSize
)
{
NTSTATUS status = STATUS_SUCCESS;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
DbgEn();
print_debug_header(RxContext);
DbgP("input: byte count 0x%x filesize 0x%x alloc size 0x%x\n",
LowIoContext->ParamsFor.ReadWrite.ByteCount, *pNewFileSize, *pNewAllocationSize);
pNewAllocationSize->QuadPart = pNewFileSize->QuadPart + 8192;
nfs41_fcb->StandardInfo.AllocationSize.QuadPart = pNewAllocationSize->QuadPart;
nfs41_fcb->StandardInfo.EndOfFile.QuadPart = pNewFileSize->QuadPart;
DbgP("new filesize 0x%x new allocation size 0x%x\n", *pNewFileSize,
*pNewAllocationSize);
DbgEx();
return status;
}
ULONG nfs41_ExtendForNonCache(
IN OUT PRX_CONTEXT RxContext,
IN PLARGE_INTEGER pNewFileSize,
OUT PLARGE_INTEGER pNewAllocationSize
)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_Truncate (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_ZeroExtend(
IN PRX_CONTEXT RxContext
)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_CleanupFobx (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_SUCCESS;
#ifdef DEBUG_CLOSE
RxCaptureFcb;
RxCaptureFobx;
DbgEn();
print_fcb(1, capFcb);
print_fobx(1, capFobx);
DbgEx();
#endif
return status;
}
void print_close_args(PRX_CONTEXT RxContext)
{
print_debug_header(RxContext);
}
static NTSTATUS map_close_errors(DWORD status)
{
switch (status) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_NOT_EMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INTERNAL_ERROR\n", status);
case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR;
}
}
NTSTATUS nfs41_CloseSrvOpen (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_close_args(RxContext);
status = nfs41_UpcallCreate(NFS41_CLOSE, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
if (!RxContext->pFcb->OpenCount) {
entry->u.Close.remove = nfs41_fcb->StandardInfo.DeletePending;
entry->u.Close.renamed = nfs41_fcb->Renamed;
entry->u.Close.filename = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
} else if (nfs41_fcb->StandardInfo.DeletePending && nfs41_fcb->StandardInfo.Directory) {
entry->u.Close.remove = nfs41_fcb->StandardInfo.DeletePending;
entry->u.Close.filename = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
/* map windows ERRORs to NTSTATUS */
status = map_close_errors(entry->status);
RxFreePool(entry);
SeDeleteClientSecurity(&nfs41_fobx->sec_ctx);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_Flush(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_ForcedClose (
IN OUT PMRX_SRV_OPEN SrvOpen)
{
NTSTATUS status = STATUS_SUCCESS;
#ifdef DEBUG_CLOSE
DbgEn();
print_srv_open(1, SrvOpen);
print_fcb(1, SrvOpen->pFcb);
print_v_net_root(1, SrvOpen->pVNetRoot);
#ifdef TESTING_CACHE_INVALIDATIO
if (SrvOpen == saved_srv_open) {
DbgP("closing saved SRV_OPEN\n");
saved_srv_open = NULL;
saved_srv_call = NULL;
}
#endif
DbgEx();
#endif
return status;
}
NTSTATUS nfs41_DeallocateForFcb (
IN OUT PMRX_FCB pFcb)
{
NTSTATUS status = STATUS_SUCCESS;
#ifdef DEBUG_CLOSE
DbgEn();
print_fcb(1, pFcb);
print_net_root(1, pFcb->pNetRoot);
DbgEx();
#endif
return status;
}
NTSTATUS nfs41_DeallocateForFobx (
IN OUT PMRX_FOBX pFobx)
{
NTSTATUS status = STATUS_SUCCESS;
#ifdef DEBUG_CLOSE
DbgEn();
print_fobx(1, pFobx);
print_srv_open(1, pFobx->pSrvOpen);
DbgEx();
#endif
return status;
}
void print_debug_filedirquery_header(PRX_CONTEXT RxContext)
{
print_debug_header(RxContext);
DbgP("FileName='%wZ', InfoClass = %s\n",
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
print_file_information_class(RxContext->Info.FileInformationClass));
}
void print_querydir_args(PRX_CONTEXT RxContext)
{
print_debug_filedirquery_header(RxContext);
DbgP("Filter='%wZ', Index=%d, Restart/Single/Specified/Init=%d/%d/%d/%d\n",
&RxContext->pFobx->UnicodeQueryTemplate, RxContext->QueryDirectory.FileIndex,
RxContext->QueryDirectory.RestartScan,
RxContext->QueryDirectory.ReturnSingleEntry,
RxContext->QueryDirectory.IndexSpecified,
RxContext->QueryDirectory.InitialQuery);
}
static NTSTATUS map_querydir_errors(DWORD status)
{
switch (status) {
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_BUFFER_OVERFLOW: return STATUS_BUFFER_OVERFLOW;
case ERROR_FILE_NOT_FOUND: return STATUS_NO_SUCH_FILE;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
case ERROR_NO_MORE_FILES: return STATUS_NO_MORE_FILES;
case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES;
default:
print_error("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;
}
}
NTSTATUS nfs41_QueryDirectory (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
nfs41_updowncall_entry *entry;
FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
PUNICODE_STRING Filter = &RxContext->pFobx->UnicodeQueryTemplate;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_querydir_args(RxContext);
switch (InfoClass) {
/* classes handled in readdir_copy_entry() and readdir_size_for_entry() */
case FileNamesInformation:
case FileDirectoryInformation:
case FileFullDirectoryInformation:
case FileIdFullDirectoryInformation:
case FileBothDirectoryInformation:
case FileIdBothDirectoryInformation:
break;
default:
print_error("unhandled dir query class %d\n", InfoClass);
status = STATUS_INVALID_PARAMETER;
goto out;
}
status = nfs41_UpcallCreate(NFS41_DIR_QUERY, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.QueryFile.InfoClass = InfoClass;
entry->u.QueryFile.buf_len = RxContext->Info.LengthRemaining;
entry->u.QueryFile.buf = RxContext->Info.Buffer;
entry->u.QueryFile.filter = Filter;
entry->u.QueryFile.initial_query = RxContext->QueryDirectory.InitialQuery;
entry->u.QueryFile.restart_scan = RxContext->QueryDirectory.RestartScan;
entry->u.QueryFile.return_single = RxContext->QueryDirectory.ReturnSingleEntry;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (entry->status == STATUS_BUFFER_TOO_SMALL) {
print_error("ERROR: buffer too small provided %d need %d\n",
RxContext->Info.LengthRemaining, entry->u.QueryFile.buf_len);
RxContext->InformationToReturn = entry->u.QueryFile.buf_len;
status = STATUS_BUFFER_TOO_SMALL;
} else if (entry->status == STATUS_SUCCESS) {
RtlCopyMemory(RxContext->Info.Buffer, entry->u.QueryFile.buf,
entry->u.QueryFile.buf_len);
RxContext->Info.LengthRemaining -= entry->u.QueryFile.buf_len;
status = STATUS_SUCCESS;
} else {
/* map windows ERRORs to NTSTATUS */
status = map_querydir_errors(entry->status);
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
void print_queryvolume_args(PRX_CONTEXT RxContext)
{
print_debug_header(RxContext);
DbgP("FileName='%wZ', InfoClass = %s BufferLen = %d\n",
GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
print_fs_information_class(RxContext->Info.FileInformationClass),
RxContext->Info.LengthRemaining);
}
static NTSTATUS map_volume_errors(DWORD status)
{
switch (status) {
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_VC_DISCONNECTED: return STATUS_CONNECTION_DISCONNECTED;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES;
default:
print_error("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;
}
}
NTSTATUS nfs41_QueryVolumeInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
ULONG RemainingLength = RxContext->Info.LengthRemaining;
FS_INFORMATION_CLASS InfoClass = RxContext->Info.FsInformationClass;
ULONG SizeUsed;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
nfs41_updowncall_entry *entry;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_queryvolume_args(RxContext);
switch (InfoClass) {
case FileFsVolumeInformation:
{
PFILE_FS_VOLUME_INFORMATION pVolInfo = RxContext->Info.Buffer;
DECLARE_CONST_UNICODE_STRING(Label, L"PnfsLabel");
SizeUsed = sizeof(FILE_FS_VOLUME_INFORMATION) + Label.Length;
if (RemainingLength < SizeUsed) {
#if 0
status = STATUS_BUFFER_TOO_SMALL;
RxContext->InformationToReturn = SizeUsed;
#else
/* Have to have status success for Notepad to be happy */
status = STATUS_SUCCESS;
#endif
goto out;
}
RtlZeroMemory(pVolInfo, sizeof(FILE_FS_VOLUME_INFORMATION));
pVolInfo->VolumeCreationTime.QuadPart = 0;
pVolInfo->VolumeSerialNumber = 0xBABAFACE;
pVolInfo->SupportsObjects = FALSE;
RtlCopyMemory(&pVolInfo->VolumeLabel[0], (PVOID)Label.Buffer, Label.Length);
RxContext->Info.LengthRemaining -= SizeUsed;
status = STATUS_SUCCESS;
goto out;
}
case FileFsDeviceInformation:
{
PFILE_FS_DEVICE_INFORMATION pDevInfo = RxContext->Info.Buffer;
SizeUsed = sizeof(FILE_FS_DEVICE_INFORMATION);
if (RemainingLength < SizeUsed) {
status = STATUS_BUFFER_TOO_SMALL;
RxContext->InformationToReturn = SizeUsed;
goto out;
}
RtlZeroMemory(pDevInfo, SizeUsed);
pDevInfo->DeviceType = RxContext->pFcb->pNetRoot->DeviceType;
pDevInfo->Characteristics = FILE_REMOTE_DEVICE; // | FILE_READ_ONLY_DEVICE;
RxContext->Info.LengthRemaining -= SizeUsed;
status = STATUS_SUCCESS;
goto out;
}
case FileFsAttributeInformation:
/* used cached fs attributes if available */
if (pVNetRootContext->FsAttrsLen) {
const LONG len = pVNetRootContext->FsAttrsLen;
if (RxContext->Info.LengthRemaining < len) {
RxContext->InformationToReturn = len;
status = STATUS_BUFFER_TOO_SMALL;
goto out;
}
RtlCopyMemory(RxContext->Info.Buffer,
pVNetRootContext->FsAttrs, len);
RxContext->Info.LengthRemaining -= len;
status = STATUS_SUCCESS;
goto out;
}
/* else fall through and send the upcall */
case FileFsSizeInformation:
case FileFsFullSizeInformation:
break;
default:
print_error("unhandled fs query class %d\n", InfoClass);
status = STATUS_INVALID_PARAMETER;
goto out;
}
status = nfs41_UpcallCreate(NFS41_VOLUME_QUERY, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.Volume.query = InfoClass;
entry->u.Volume.buf = RxContext->Info.Buffer;
entry->u.Volume.buf_len = RxContext->Info.LengthRemaining;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (entry->status == STATUS_BUFFER_TOO_SMALL) {
RxContext->InformationToReturn = entry->u.Volume.buf_len;
status = STATUS_BUFFER_TOO_SMALL;
} else if (entry->status == STATUS_SUCCESS) {
if (InfoClass == FileFsAttributeInformation) {
/* fill in the FileSystemName */
PFILE_FS_ATTRIBUTE_INFORMATION attrs =
(PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer;
DECLARE_CONST_UNICODE_STRING(FsName, FS_NAME);
entry->u.Volume.buf_len += FsName.Length;
if (entry->u.Volume.buf_len > RxContext->Info.LengthRemaining) {
RxContext->InformationToReturn = entry->u.Volume.buf_len;
status = STATUS_BUFFER_TOO_SMALL;
goto out;
}
RtlCopyMemory(attrs->FileSystemName, FsName.Buffer,
FsName.MaximumLength); /* 'MaximumLength' to include null */
attrs->FileSystemNameLength = FsName.Length;
/* save fs attributes with the vnetroot */
if (entry->u.Volume.buf_len <= FS_ATTR_LEN) {
RtlCopyMemory(&pVNetRootContext->FsAttrs,
RxContext->Info.Buffer, entry->u.Volume.buf_len);
pVNetRootContext->FsAttrsLen = entry->u.Volume.buf_len;
}
}
RxContext->Info.LengthRemaining -= entry->u.Volume.buf_len;
status = STATUS_SUCCESS;
} else {
status = map_volume_errors(entry->status);
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_SetVolumeInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
void print_nfs3_attrs(nfs3_attrs *attrs)
{
DbgP("type=%d mode=%o nlink=%d size=%d atime=%x mtime=%x ctime=%x\n",
attrs->type, attrs->mode, attrs->nlink, attrs->size, attrs->atime,
attrs->mtime, attrs->ctime);
}
2010-10-27 14:58:35 -04:00
void file_time_to_nfs_time(
IN const PLARGE_INTEGER file_time,
OUT LONGLONG *nfs_time)
{
LARGE_INTEGER diff = unix_time_diff;
diff.QuadPart = file_time->QuadPart - diff.QuadPart;
*nfs_time = diff.QuadPart / 10000000;
}
void create_nfs3_attrs(nfs3_attrs *attrs, PNFS41_FCB nfs41_fcb)
{
RtlZeroMemory(attrs, sizeof(nfs3_attrs));
if (nfs41_fcb->BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
attrs->type = NF3LNK;
else if (nfs41_fcb->StandardInfo.Directory)
attrs->type = NF3DIR;
else
attrs->type = NF3REG;
attrs->mode = nfs41_fcb->mode;
attrs->nlink = nfs41_fcb->StandardInfo.NumberOfLinks;
attrs->size.QuadPart = attrs->used.QuadPart =
nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
2010-10-27 14:58:35 -04:00
file_time_to_nfs_time(&nfs41_fcb->BasicInfo.LastAccessTime, &attrs->atime);
file_time_to_nfs_time(&nfs41_fcb->BasicInfo.ChangeTime, &attrs->mtime);
file_time_to_nfs_time(&nfs41_fcb->BasicInfo.CreationTime, &attrs->ctime);
}
NTSTATUS nfs41_QueryEaInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
PFILE_GET_EA_INFORMATION query = (PFILE_GET_EA_INFORMATION)
RxContext->CurrentIrpSp->Parameters.QueryEa.EaList;
PFILE_FULL_EA_INFORMATION info;
DbgEn();
print_debug_header(RxContext);
if (RxContext->CurrentIrpSp->Parameters.QueryEa.EaList) {
DbgP("Looking for a specific EA?\n");
print_get_ea(1, query);
if (AnsiStrEq(&NfsV3Attributes, query->EaName, query->EaNameLength)) {
nfs3_attrs attrs;
const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
NfsV3Attributes.Length + sizeof(nfs3_attrs) - sizeof(CHAR);
if (LengthRequired > RxContext->Info.LengthRemaining) {
status = STATUS_BUFFER_TOO_SMALL;
RxContext->InformationToReturn = LengthRequired;
goto out;
}
create_nfs3_attrs(&attrs, nfs41_fcb);
DbgP("returning fake v3attrs EA\n");
print_nfs3_attrs(&attrs);
info = RxContext->Info.Buffer;
info->NextEntryOffset = 0;
info->Flags = 0;
info->EaNameLength = (UCHAR)NfsV3Attributes.Length;
info->EaValueLength = sizeof(nfs3_attrs);
RtlCopyMemory(info->EaName, NfsV3Attributes.Buffer, NfsV3Attributes.Length);
RtlCopyMemory(info->EaName + info->EaNameLength + 1, &attrs,
sizeof(nfs3_attrs));
RxContext->Info.LengthRemaining = LengthRequired;
status = STATUS_SUCCESS;
} else if (AnsiStrEq(&NfsActOnLink, query->EaName, query->EaNameLength)
|| AnsiStrEq(&NfsSymlinkTargetName, query->EaName, query->EaNameLength)) {
const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
NfsActOnLink.Length - sizeof(CHAR);
if (LengthRequired > RxContext->Info.LengthRemaining) {
status = STATUS_BUFFER_TOO_SMALL;
RxContext->InformationToReturn = LengthRequired;
goto out;
}
DbgP("returning fake link EA\n");
info = RxContext->Info.Buffer;
info->NextEntryOffset = 0;
info->Flags = 0;
info->EaNameLength = (UCHAR)NfsActOnLink.Length;
info->EaValueLength = 0;
RtlCopyMemory(info->EaName, NfsActOnLink.Buffer, NfsActOnLink.Length);
RxContext->Info.LengthRemaining = LengthRequired;
status = STATUS_SUCCESS;
} else
print_error("Couldn't match %s\n", query->EaName);
}
out:
DbgEx();
return status;
}
static NTSTATUS map_setea_error(DWORD error)
{
switch (error) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NOT_EMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
case ERROR_FILE_EXISTS: return STATUS_OBJECT_NAME_COLLISION;
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return STATUS_OBJECT_PATH_NOT_FOUND;
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_NOT_SUPPORTED: return STATUS_NOT_IMPLEMENTED;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_BUFFER_OVERFLOW: return STATUS_INSUFFICIENT_RESOURCES;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INVALID_PARAMETER\n", error);
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
}
}
NTSTATUS nfs41_SetEaInformation (
IN OUT struct _RX_CONTEXT *RxContext)
{
NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
PFILE_FULL_EA_INFORMATION eainfo =
(PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer;
nfs3_attrs *attrs = NULL;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_debug_header(RxContext);
print_ea_info(1, eainfo);
if (AnsiStrEq(&NfsV3Attributes, eainfo->EaName, eainfo->EaNameLength)) {
attrs = (nfs3_attrs *)(eainfo->EaName + eainfo->EaNameLength + 1);
print_nfs3_attrs(attrs);
DbgP("old mode is %x new mode is %x\n", nfs41_fcb->mode, attrs->mode);
nfs41_fcb->mode = attrs->mode;
} else
goto out;
status = nfs41_UpcallCreate(NFS41_EA_SET, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.SetEa.mode = attrs->mode;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
status = map_setea_error(entry->status);
RxFreePool(entry);
out:
DbgEx();
return status;
}
2011-04-12 15:56:20 -04:00
static void print_acl_args(SECURITY_INFORMATION info)
{
if (info & OWNER_SECURITY_INFORMATION)
DbgP("OWNER_SECURITY_INFORMATION\n");
if (info & GROUP_SECURITY_INFORMATION)
DbgP("GROUP_SECURITY_INFORMATION\n");
if (info & DACL_SECURITY_INFORMATION)
DbgP("DACL_SECURITY_INFORMATION\n");
if (info & SACL_SECURITY_INFORMATION)
DbgP("SACL_SECURITY_INFORMATION\n");
}
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
static NTSTATUS map_query_acl_error(DWORD error)
{
switch (error) {
2011-04-12 15:56:20 -04:00
case NO_ERROR: return STATUS_SUCCESS;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
case ERROR_NOT_SUPPORTED: return STATUS_NOT_SUPPORTED;
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
case ERROR_BAD_NET_RESP: return STATUS_INVALID_NETWORK_RESPONSE;
}
}
2011-04-12 15:56:20 -04:00
NTSTATUS nfs41_QuerySecurityInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
2011-04-12 15:56:20 -04:00
SECURITY_INFORMATION info_class =
RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
DbgEn();
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
print_debug_header(RxContext);
2011-04-12 15:56:20 -04:00
print_acl_args(info_class);
/* we don't support sacls */
if (info_class == SACL_SECURITY_INFORMATION)
goto out;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
status = nfs41_UpcallCreate(NFS41_ACL_QUERY, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
if (status)
goto out;
entry->u.Acl.query = info_class;
/* we can't provide RxContext->CurrentIrp->UserBuffer to the upcall thread
* because it becomes an invalid pointer with that execution context
*/
entry->u.Acl.buf_len = RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length;
DbgP("security buffer len %d\n", entry->u.Acl.buf_len);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (entry->status == STATUS_BUFFER_TOO_SMALL) {
DbgP("nfs41_QuerySecurityInformation: provided buffer size=%d but we need %d\n",
RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length,
entry->u.Acl.buf_len);
status = STATUS_BUFFER_TOO_SMALL;
RxContext->InformationToReturn = entry->u.Acl.buf_len;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
} else if (entry->status == STATUS_SUCCESS) {
if (RtlValidSecurityDescriptor(entry->u.Acl.buf)) {
DbgP("Received a valid security descriptor\n");
if (MmIsAddressValid(RxContext->CurrentIrp->UserBuffer)) {
PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
RxContext->CurrentIrp->UserBuffer;
DbgP("Received a valid user pointer\n");
RtlCopyMemory(sec_desc, entry->u.Acl.buf, entry->u.Acl.buf_len);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
} else {
DbgP("Received invalid user pointer\n");
status = STATUS_INTERNAL_ERROR;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
}
} else {
DbgP("Received invalid security descriptor\n");
status = STATUS_INTERNAL_ERROR;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
}
RxFreePool(entry->u.Acl.buf);
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
RxContext->IoStatusBlock.Information = RxContext->InformationToReturn =
entry->u.Acl.buf_len;
if (!status)
RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
first stab at handling security irp Basic handling of owner and group security query (no dacl). Added new upcall for NFS41_ACL_QUERY (driver and daemon code). Daemon, upon getting NFS41_ACL_QUERY first places a getattr that has owner, group attribute request. We currently don't cache them!!! Then, we parse nfs4name format (ie user@domain or group@domain) into user and domain. We currently ignore domain part!!! Then, we assume that whatever we are mapping is "known" locally (ie LookupAccountName() api which retrieves a SID for a given name). Mapping from name to SID can only be done in the userland. We then copy the bytes via the upcall pipe to the kernel. If the received user or group cant be mapped via LookupAccoundName(), we create a well known null SID as the reply. Kernel creates a security descriptor in the absolute-format and adds owner and group sids to it. Important: RtlSetOwner/Group functions only work with absolute-format security descriptor, however the reply to the user needs to be in the self-relative format. The way security query works is that it passes us a buffer to be filled with the security context. However the user doesn't know how big the buffer should be so, the user is allowed to pass a null buffer and have the kernel return how much memory is needed. This leads to 2 security queries => 2 NFS41_ACL_QUERY upcalls => 2 getattr rpcs... It should be improved. TODO: - need to add caching of owner/group attributes for a file? - need to add calls to LDAP for more general mapping? - need to cache reply of the ACL if supplied length is 0?
2011-03-15 16:31:52 -04:00
} else {
status = map_query_acl_error(entry->status);
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_SetSecurityInformation (
IN OUT struct _RX_CONTEXT *RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
2011-04-12 15:56:20 -04:00
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
PSECURITY_DESCRIPTOR sec_desc = RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityDescriptor;
SECURITY_INFORMATION info_class =
RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityInformation;
DbgEn();
2011-04-12 15:56:20 -04:00
print_debug_header(RxContext);
print_acl_args(info_class);
/* check that ACL is present */
if (info_class & DACL_SECURITY_INFORMATION) {
PACL acl;
BOOLEAN present, dacl_default;
status = RtlGetDaclSecurityDescriptor(sec_desc, &present, &acl, &dacl_default);
if (status) {
DbgP("RtlGetDaclSecurityDescriptor failed %x\n", status);
goto out;
}
if (present == FALSE) {
DbgP("NO ACL present\n");
goto out;
}
}
/* we don't support sacls */
if (info_class == SACL_SECURITY_INFORMATION)
goto out;
status = nfs41_UpcallCreate(NFS41_ACL_SET, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
2011-04-12 15:56:20 -04:00
if (status)
goto out;
entry->u.Acl.query = info_class;
entry->u.Acl.buf = sec_desc;
entry->u.Acl.buf_len = RtlLengthSecurityDescriptor(sec_desc);
2011-04-12 15:56:20 -04:00
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
status = map_query_acl_error(entry->status);
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_QueryQuotaInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_SetQuotaInformation (
IN OUT struct _RX_CONTEXT *RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_SetVolumeInfo (
IN OUT struct _RX_CONTEXT *RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
void print_queryfile_args(PRX_CONTEXT RxContext)
{
print_debug_filedirquery_header(RxContext);
}
static NTSTATUS map_queryfile_error(DWORD error)
{
switch (error) {
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
case ERROR_BAD_NET_RESP: return STATUS_INVALID_NETWORK_RESPONSE;
}
}
NTSTATUS nfs41_QueryFileInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
switch (InfoClass) {
case FileEaInformation:
{
PFILE_EA_INFORMATION info =
(PFILE_EA_INFORMATION)RxContext->Info.Buffer;
info->EaSize = 0;
RxContext->Info.LengthRemaining -= sizeof(FILE_EA_INFORMATION);
status = STATUS_SUCCESS;
goto out;
}
#ifdef FCB_ATTR_CACHING
case FileBasicInformation:
if(nfs41_fcb->Flags & FCB_BASIC_INFO_CACHED) {
RtlCopyMemory(RxContext->Info.Buffer, &nfs41_fcb->BasicInfo,
sizeof(nfs41_fcb->BasicInfo));
RxContext->Info.LengthRemaining -= sizeof(nfs41_fcb->BasicInfo);
status = STATUS_SUCCESS;
goto out;
}
break;
case FileStandardInformation:
if(nfs41_fcb->Flags & FCB_STANDARD_INFO_CACHED) {
RtlCopyMemory(RxContext->Info.Buffer, &nfs41_fcb->StandardInfo,
sizeof(nfs41_fcb->StandardInfo));
RxContext->Info.LengthRemaining -= sizeof(nfs41_fcb->StandardInfo);
status = STATUS_SUCCESS;
goto out;
}
break;
#else
case FileBasicInformation:
case FileStandardInformation:
#endif
case FileInternalInformation:
case FileAttributeTagInformation:
break;
default:
print_error("unhandled file query class %d\n", InfoClass);
status = STATUS_INVALID_PARAMETER;
goto out;
}
print_queryfile_args(RxContext);
status = nfs41_UpcallCreate(NFS41_FILE_QUERY, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.QueryFile.InfoClass = InfoClass;
entry->u.QueryFile.buf = RxContext->Info.Buffer;
entry->u.QueryFile.buf_len = RxContext->Info.LengthRemaining;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (entry->status == STATUS_BUFFER_TOO_SMALL) {
RxContext->InformationToReturn = entry->u.QueryFile.buf_len;
status = STATUS_BUFFER_TOO_SMALL;
} else if (entry->status == STATUS_SUCCESS) {
BOOLEAN DeletePending = FALSE;
RxContext->Info.LengthRemaining -= entry->u.QueryFile.buf_len;
status = STATUS_SUCCESS;
switch (InfoClass) {
case FileBasicInformation:
RtlCopyMemory(&nfs41_fcb->BasicInfo, RxContext->Info.Buffer,
sizeof(nfs41_fcb->BasicInfo));
nfs41_fcb->Flags |= FCB_BASIC_INFO_CACHED;
print_basic_info(1, &nfs41_fcb->BasicInfo);
break;
case FileStandardInformation:
#ifndef FCB_ATTR_CACHING
/* this a fix for RDBSS behaviour when it first calls ExtendForCache,
* then it sends a file query irp for standard attributes and
* expects to receive EndOfFile of value set by the ExtendForCache.
* It seems to cache the filesize based on that instead of sending
* a file size query for after doing the write.
*/
{
PFILE_STANDARD_INFORMATION std_info;
std_info = (PFILE_STANDARD_INFORMATION)RxContext->Info.Buffer;
if (nfs41_fcb->StandardInfo.AllocationSize.QuadPart >
std_info->AllocationSize.QuadPart) {
DbgP("Old AllocationSize is bigger: saving %x\n",
nfs41_fcb->StandardInfo.AllocationSize.QuadPart);
std_info->AllocationSize.QuadPart =
nfs41_fcb->StandardInfo.AllocationSize.QuadPart;
}
if (nfs41_fcb->StandardInfo.EndOfFile.QuadPart >
std_info->EndOfFile.QuadPart) {
DbgP("Old EndOfFile is bigger: saving %x\n",
nfs41_fcb->StandardInfo.EndOfFile);
std_info->EndOfFile.QuadPart =
nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
}
}
#endif
if (nfs41_fcb->StandardInfo.DeletePending)
DeletePending = TRUE;
RtlCopyMemory(&nfs41_fcb->StandardInfo, RxContext->Info.Buffer,
sizeof(nfs41_fcb->StandardInfo));
nfs41_fcb->StandardInfo.DeletePending = DeletePending;
nfs41_fcb->Flags |= FCB_STANDARD_INFO_CACHED;
print_std_info(1, &nfs41_fcb->StandardInfo);
break;
}
} else {
status = map_queryfile_error(entry->status);
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
void print_setfile_args(PRX_CONTEXT RxContext)
{
print_debug_filedirquery_header(RxContext);
}
static NTSTATUS map_setfile_error(DWORD error)
{
switch (error) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NOT_EMPTY: return STATUS_DIRECTORY_NOT_EMPTY;
case ERROR_FILE_EXISTS: return STATUS_OBJECT_NAME_COLLISION;
case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return STATUS_OBJECT_PATH_NOT_FOUND;
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
case ERROR_NOT_SAME_DEVICE: return STATUS_NOT_SAME_DEVICE;
case ERROR_NOT_SUPPORTED: return STATUS_NOT_IMPLEMENTED;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_BUFFER_OVERFLOW: return STATUS_INSUFFICIENT_RESOURCES;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_INVALID_PARAMETER\n", error);
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
}
}
NTSTATUS nfs41_SetFileInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
FILE_RENAME_INFORMATION rinfo;
PFILE_OBJECT fo = RxContext->CurrentIrpSp->FileObject;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_setfile_args(RxContext);
switch (InfoClass) {
case FileRenameInformation:
{
PFILE_RENAME_INFORMATION rinfo =
(PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
UNICODE_STRING dst = { (USHORT)rinfo->FileNameLength,
(USHORT)rinfo->FileNameLength, rinfo->FileName };
if (rinfo->RootDirectory) {
status = STATUS_NOT_SUPPORTED;
goto out;
}
DbgP("Attempting to rename to '%wZ'\n", dst);
nfs41_fcb->Flags = 0;
}
break;
case FileLinkInformation:
{
PFILE_LINK_INFORMATION linfo =
(PFILE_LINK_INFORMATION)RxContext->Info.Buffer;
UNICODE_STRING dst = { (USHORT)linfo->FileNameLength,
(USHORT)linfo->FileNameLength, linfo->FileName };
if (linfo->RootDirectory) {
status = STATUS_NOT_SUPPORTED;
goto out;
}
DbgP("Attempting to add link as '%wZ'\n", dst);
nfs41_fcb->Flags = 0;
}
break;
case FileDispositionInformation:
{
PFILE_DISPOSITION_INFORMATION dinfo =
(PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
if (dinfo->DeleteFile) {
// we can delete directories right away
if (nfs41_fcb->StandardInfo.Directory)
break;
nfs41_fcb->Flags = 0;
nfs41_fcb->StandardInfo.DeletePending = TRUE;
if (RxContext->pFcb->OpenCount > 1) {
rinfo.ReplaceIfExists = 0;
rinfo.RootDirectory = INVALID_HANDLE_VALUE;
rinfo.FileNameLength = 0;
rinfo.FileName[0] = L'\0';
InfoClass = FileRenameInformation;
nfs41_fcb->Renamed = TRUE;
break;
}
}
status = STATUS_SUCCESS;
goto out;
}
case FileBasicInformation:
case FileAllocationInformation:
nfs41_fcb->Flags = 0;
break;
case FileEndOfFileInformation:
{
PFILE_END_OF_FILE_INFORMATION info =
(PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
nfs41_fcb->StandardInfo.AllocationSize =
nfs41_fcb->StandardInfo.EndOfFile = info->EndOfFile;
nfs41_fcb->Flags = 0;
break;
}
default:
print_error("unknown set_file information class %d\n", InfoClass);
status = STATUS_NOT_SUPPORTED;
goto out;
}
status = nfs41_UpcallCreate(NFS41_FILE_SET, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.SetFile.filename = FileName;
entry->u.SetFile.InfoClass = InfoClass;
switch(InfoClass) {
case FileAllocationInformation:
case FileEndOfFileInformation:
entry->u.SetFile.open_owner_id = get_next_open_owner();
if (fo->ReadAccess)
entry->u.SetFile.access_mask = FILE_READ_DATA;
if (fo->WriteAccess)
entry->u.SetFile.access_mask |= FILE_WRITE_DATA;
if (fo->SharedRead)
entry->u.SetFile.access_mode = FILE_SHARE_READ;
if (fo->SharedWrite)
entry->u.SetFile.access_mode |= FILE_SHARE_WRITE;
}
if (RxContext->Info.FileInformationClass == FileDispositionInformation &&
InfoClass == FileRenameInformation) {
entry->u.SetFile.buf = &rinfo;
entry->u.SetFile.buf_len = sizeof(rinfo);
} else {
entry->u.SetFile.buf = RxContext->Info.Buffer;
entry->u.SetFile.buf_len = RxContext->Info.Length;
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
status = map_setfile_error(entry->status);
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_SetFileInformationAtCleanup(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status;
DbgEn();
status = nfs41_SetFileInformation(RxContext);
DbgEx();
return status;
}
NTSTATUS nfs41_IsValidDirectory (
IN OUT PRX_CONTEXT RxContext,
IN PUNICODE_STRING DirectoryName)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_PreparseName(
IN OUT PRX_CONTEXT RxContext,
IN PUNICODE_STRING Name
)
{
NTSTATUS status = STATUS_SUCCESS;
//DbgEn();
//DbgEx();
return status;
}
NTSTATUS nfs41_ComputeNewBufferingState(
IN OUT PMRX_SRV_OPEN pSrvOpen,
IN PVOID pMRxContext,
OUT ULONG *pNewBufferingState)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG flag;
DbgEn();
flag = PtrToUlong(pMRxContext);
DbgP("pSrvOpen %p Flags %08x\n", pSrvOpen, pSrvOpen->BufferingFlags);
switch(flag) {
case DISABLE_CACHING:
if (pSrvOpen->BufferingFlags &
(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED))
pSrvOpen->BufferingFlags &=
~(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED);
if (pSrvOpen->BufferingFlags &
(FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED))
pSrvOpen->BufferingFlags &=
~(FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
pSrvOpen->BufferingFlags |= FCB_STATE_DISABLE_LOCAL_BUFFERING;
break;
case ENABLE_READ_CACHING:
pSrvOpen->BufferingFlags |=
(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED);
break;
case ENABLE_WRITE_CACHING:
pSrvOpen->BufferingFlags |=
(FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
break;
case ENABLE_READWRITE_CACHING:
pSrvOpen->BufferingFlags =
(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED |
FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
}
DbgP("new Flags %08x\n", pSrvOpen->BufferingFlags);
*pNewBufferingState = pSrvOpen->BufferingFlags;
DbgEx();
return status;
}
void print_readwrite_args(PRX_CONTEXT RxContext)
{
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
PIRP Irp = RxContext->CurrentIrp;
print_debug_header(RxContext);
DbgP("Irp flags: paging io %ld noncachedio %ld syncio %ld\n",
FlagOn(Irp->Flags, IRP_PAGING_IO),
FlagOn(Irp->Flags, IRP_NOCACHE),
FlagOn(IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO));
DbgP("Bytecount 0x%x Byteoffset 0x%x Buffer %p\n",
LowIoContext->ParamsFor.ReadWrite.ByteCount,
LowIoContext->ParamsFor.ReadWrite.ByteOffset,
LowIoContext->ParamsFor.ReadWrite.Buffer);
}
void enable_caching(PMRX_SRV_OPEN SrvOpen)
{
ULONG flag = 0;
if (SrvOpen->DesiredAccess & FILE_READ_DATA)
flag = ENABLE_READ_CACHING;
if (SrvOpen->DesiredAccess & FILE_WRITE_DATA)
flag = ENABLE_WRITE_CACHING;
if ((SrvOpen->DesiredAccess & FILE_READ_DATA) &&
(SrvOpen->DesiredAccess & FILE_WRITE_DATA))
flag = ENABLE_READWRITE_CACHING;
print_caching_level(1, flag);
if (!flag)
return;
RxIndicateChangeOfBufferingStateForSrvOpen(SrvOpen->pVNetRoot->pNetRoot->pSrvCall,
SrvOpen, SrvOpen->Key, ULongToPtr(flag));
}
static NTSTATUS map_readwrite_errors(DWORD status)
{
switch (status) {
case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
case ERROR_LOCK_VIOLATION: return STATUS_FILE_LOCK_CONFLICT;
case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
default:
print_error("failed to map windows error %d to NTSTATUS; "
"defaulting to STATUS_NET_WRITE_FAULT\n", status);
case ERROR_NET_WRITE_FAULT: return STATUS_NET_WRITE_FAULT;
}
}
NTSTATUS nfs41_Read (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
BOOLEAN async = FALSE;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_readwrite_args(RxContext);
status = nfs41_UpcallCreate(NFS41_READ, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
entry->u.ReadWrite.len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) == FALSE) {
entry->u.ReadWrite.rxcontext = RxContext;
async = entry->async_op = TRUE;
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (async) {
DbgP("This is asynchronous read, returning control back to the user\n");
status = STATUS_PENDING;
goto out;
}
if (entry->status == NO_ERROR) {
status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
RxContext->IoStatusBlock.Information = entry->u.ReadWrite.len;
nfs41_fcb->Flags = 0;
if (!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
LOWIO_READWRITEFLAG_PAGING_IO) &&
(SrvOpen->DesiredAccess & FILE_READ_DATA) &&
!(SrvOpen->BufferingFlags &
(FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED)))
enable_caching(SrvOpen);
} else {
status = map_readwrite_errors(entry->status);
RxContext->CurrentIrp->IoStatus.Status = status;
RxContext->IoStatusBlock.Information = 0;
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_Write (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context;
BOOLEAN async = FALSE;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_readwrite_args(RxContext);
status = nfs41_UpcallCreate(NFS41_WRITE, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
entry->u.ReadWrite.len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) == FALSE) {
entry->u.ReadWrite.rxcontext = RxContext;
async = entry->async_op = TRUE;
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
if (async) {
DbgP("This is asynchronous write, returning control back to the user\n");
status = STATUS_PENDING;
goto out;
}
if (entry->status == NO_ERROR) {
//update cached file attributes
nfs41_fcb->StandardInfo.EndOfFile.QuadPart = entry->u.ReadWrite.len +
entry->u.ReadWrite.offset;
status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
RxContext->IoStatusBlock.Information = entry->u.ReadWrite.len;
nfs41_fcb->Flags = 0;
if (!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
LOWIO_READWRITEFLAG_PAGING_IO) &&
(SrvOpen->DesiredAccess & FILE_WRITE_DATA) &&
(SrvOpen->DesiredAccess & FILE_READ_DATA) &&
!(SrvOpen->BufferingFlags &
(FCB_STATE_WRITEBUFFERING_ENABLED | FCB_STATE_WRITECACHING_ENABLED)))
enable_caching(SrvOpen);
} else {
status = map_readwrite_errors(entry->status);
RxContext->CurrentIrp->IoStatus.Status = status;
RxContext->IoStatusBlock.Information = 0;
}
RxFreePool(entry);
out:
DbgEx();
return status;
}
NTSTATUS nfs41_IsLockRealizable (
IN OUT PMRX_FCB pFcb,
IN PLARGE_INTEGER ByteOffset,
IN PLARGE_INTEGER Length,
IN ULONG LowIoLockFlags)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
ByteOffset->QuadPart,Length->QuadPart,
BooleanFlagOn(LowIoLockFlags, SL_EXCLUSIVE_LOCK),
!BooleanFlagOn(LowIoLockFlags, SL_FAIL_IMMEDIATELY));
DbgEx();
return status;
}
static NTSTATUS map_lock_errors(DWORD status)
{
switch (status) {
case NO_ERROR: return STATUS_SUCCESS;
case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
case ERROR_LOCK_FAILED: return STATUS_LOCK_NOT_GRANTED;
case ERROR_NOT_LOCKED: return STATUS_RANGE_NOT_LOCKED;
case ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: return STATUS_UNSUCCESSFUL;
case ERROR_OUTOFMEMORY: return STATUS_INSUFFICIENT_RESOURCES;
case ERROR_SHARING_VIOLATION: return STATUS_SHARING_VIOLATION;
case ERROR_FILE_INVALID: return STATUS_FILE_INVALID;
/* if we return ERROR_INVALID_PARAMETER, Windows translates that to
* success!! */
case ERROR_INVALID_PARAMETER: return STATUS_LOCK_NOT_GRANTED;
default:
print_error("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_lock_args(PRX_CONTEXT RxContext)
{
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
print_debug_header(RxContext);
DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
LowIoContext->ParamsFor.Locks.ByteOffset,
LowIoContext->ParamsFor.Locks.Length,
BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK),
!BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY));
}
/* use exponential backoff between polls for blocking locks */
#define MSEC_TO_RELATIVE_WAIT (-10000)
#define MIN_LOCK_POLL_WAIT (500 * MSEC_TO_RELATIVE_WAIT) /* 500ms */
#define MAX_LOCK_POLL_WAIT (30000 * MSEC_TO_RELATIVE_WAIT) /* 30s */
static void denied_lock_backoff(
IN OUT PLARGE_INTEGER delay)
{
if (delay->QuadPart == 0)
delay->QuadPart = MIN_LOCK_POLL_WAIT;
else
delay->QuadPart <<= 1;
if (delay->QuadPart < MAX_LOCK_POLL_WAIT)
delay->QuadPart = MAX_LOCK_POLL_WAIT;
}
NTSTATUS nfs41_Lock(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_SUCCESS;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
LARGE_INTEGER poll_delay = {0};
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_lock_args(RxContext);
/* RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
LowIoContext->ResourceThreadId); */
status = nfs41_UpcallCreate(NFS41_LOCK, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.Lock.offset = LowIoContext->ParamsFor.Locks.ByteOffset;
entry->u.Lock.length = LowIoContext->ParamsFor.Locks.Length;
entry->u.Lock.exclusive = BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK);
entry->u.Lock.blocking = !BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY);
retry_upcall:
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
/* blocking locks keep trying until it succeeds */
if (entry->status == ERROR_LOCK_FAILED && entry->u.Lock.blocking) {
denied_lock_backoff(&poll_delay);
DbgP("returned ERROR_LOCK_FAILED; retrying in %llums\n",
poll_delay.QuadPart / MSEC_TO_RELATIVE_WAIT);
KeDelayExecutionThread(KernelMode, FALSE, &poll_delay);
entry->state = NFS41_WAITING_FOR_UPCALL;
goto retry_upcall;
}
status = map_lock_errors(entry->status);
RxContext->CurrentIrp->IoStatus.Status = status;
RxFreePool(entry);
out:
DbgEx();
return status;
}
static void print_unlock_args(PRX_CONTEXT RxContext)
{
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
print_debug_header(RxContext);
if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
PLOWIO_LOCK_LIST lock = LowIoContext->ParamsFor.Locks.LockList;
DbgP("LOWIO_OP_UNLOCK_MULTIPLE:");
while (lock) {
DbgP(" (offset=%llu, length=%llu)", lock->ByteOffset, lock->Length);
lock = lock->Next;
}
DbgP("\n");
} else {
DbgP("LOWIO_OP_UNLOCK: offset=%llu, length=%llu\n",
LowIoContext->ParamsFor.Locks.ByteOffset,
LowIoContext->ParamsFor.Locks.Length);
}
}
static __inline ULONG unlock_list_count(PLOWIO_LOCK_LIST lock)
{
ULONG count = 0;
while (lock) {
count++;
lock = lock->Next;
}
return count;
}
NTSTATUS nfs41_Unlock(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_SUCCESS;
nfs41_updowncall_entry *entry;
PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
__notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_lock_args(RxContext);
/* RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
LowIoContext->ResourceThreadId); */
status = nfs41_UpcallCreate(NFS41_UNLOCK, &nfs41_fobx->sec_ctx,
pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
entry->u.Unlock.count = unlock_list_count(
LowIoContext->ParamsFor.Locks.LockList);
RtlCopyMemory(&entry->u.Unlock.locks,
LowIoContext->ParamsFor.Locks.LockList,
sizeof(LOWIO_LOCK_LIST));
} else {
entry->u.Unlock.count = 1;
entry->u.Unlock.locks.ByteOffset =
LowIoContext->ParamsFor.Locks.ByteOffset;
entry->u.Unlock.locks.Length =
LowIoContext->ParamsFor.Locks.Length;
}
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
status = map_lock_errors(entry->status);
RxContext->CurrentIrp->IoStatus.Status = status;
RxFreePool(entry);
out:
DbgEx();
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:
print_error("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_SetReparsePoint(
IN OUT PRX_CONTEXT RxContext)
{
UNICODE_STRING TargetName;
XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pInputBuffer;
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;
NTSTATUS status;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
print_reparse_buffer(Reparse);
if (Reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
status = STATUS_IO_REPARSE_TAG_MISMATCH;
goto out;
}
TargetName.MaximumLength = TargetName.Length =
Reparse->SymbolicLinkReparseBuffer.PrintNameLength;
TargetName.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
VNetRoot->session, Fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
entry->u.Symlink.filename = SrvOpen->pAlreadyPrefixedName;
entry->u.Symlink.target = &TargetName;
entry->u.Symlink.set = TRUE;
if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
status = STATUS_INTERNAL_ERROR;
goto out;
}
status = map_symlink_errors(entry->status);
RxFreePool(entry);
out:
DbgEx();
return status;
}
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;
PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
DbgEn();
if (!BooleanFlagOn(RxContext->pFcb->Attributes,
FILE_ATTRIBUTE_REPARSE_POINT)) {
status = STATUS_NOT_A_REPARSE_POINT;
DbgP("FILE_ATTRIBUTE_REPARSE_POINT is not set!\n");
goto out;
}
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, &Fobx->sec_ctx,
VNetRoot->session, Fobx->nfs41_open_state,
pNetRootContext->nfs41d_version, &entry);
if (status)
goto out;
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:
DbgEx();
return status;
}
NTSTATUS nfs41_FsCtl(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
DbgEn();
print_debug_header(RxContext);
DbgP("FileName: %wZ\n", &RxContext->CurrentIrpSp->FileObject->FileName);
switch (RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode) {
case FSCTL_SET_REPARSE_POINT:
DbgP("FSCTL_SET_REPARSE_POINT\n");
status = nfs41_SetReparsePoint(RxContext);
break;
case FSCTL_GET_REPARSE_POINT:
DbgP("FSCTL_GET_REPARSE_POINT\n");
status = nfs41_GetReparsePoint(RxContext);
break;
}
DbgEx();
return status;
}
NTSTATUS nfs41_IoCtl(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_NotifyChangeDirectory(
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_CompleteBufferingStateChangeRequest (
IN OUT PRX_CONTEXT RxContext,
IN OUT PMRX_SRV_OPEN SrvOpen,
IN PVOID pContext)
{
NTSTATUS status = STATUS_SUCCESS;
DbgEn();
DbgEx();
return status;
}
NTSTATUS nfs41_AreFilesAliased(
PFCB Fcb1,
PFCB Fcb2)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
PMRX_SRV_OPEN srv1 = (PMRX_SRV_OPEN)Fcb1->InternalSrvOpen,
srv2 = (PMRX_SRV_OPEN)Fcb2->InternalSrvOpen;
DbgEn();
DbgP("fcb1 %p fcb2 %p srv1 %p srv2 %p\n", Fcb1, Fcb2, srv1, srv2);
DbgP("file1 %wZ file2 %wZ\n", srv1->pAlreadyPrefixedName,
srv2->pAlreadyPrefixedName);
DbgEx();
return status;
}
NTSTATUS nfs41_GetConnectionId(
IN OUT PRX_CONTEXT RxContext,
IN OUT PRX_CONNECTION_ID UniqueId)
{
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
//DbgEn();
//DbgEx();
return status;
}
NTSTATUS nfs41_FsdDispatch (
IN PDEVICE_OBJECT dev,
IN PIRP Irp
)
{
#ifdef DEBUG_FSDDISPATCH
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
#endif
NTSTATUS status;
#ifdef DEBUG_FSDDISPATCH
DbgEn();
DbgP("CURRENT IRP = %d.%d\n", IrpSp->MajorFunction, IrpSp->MinorFunction);
if(IrpSp->FileObject)
DbgP("FileOject %p Filename %wZ\n", IrpSp->FileObject,
&IrpSp->FileObject->FileName);
switch(IrpSp->MajorFunction) {
case 0:
DbgP("Create: share access %d\n", IrpSp->Parameters.Create.ShareAccess);
break;
case 5:
DbgP("InfoClass %d\n", IrpSp->Parameters.QueryFile.FileInformationClass);
break;
}
#endif
if (dev != (PDEVICE_OBJECT)nfs41_dev) {
print_error("*** not ours ***\n");
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT );
status = STATUS_INVALID_DEVICE_REQUEST;
goto out;
}
status = RxFsdDispatch((PRDBSS_DEVICE_OBJECT)dev,Irp);
/* AGLO: 08/05/2009 - looks like RxFsdDispatch frees IrpSp */
out:
#ifdef DEBUG_FSDDISPATCH
DbgEx();
#endif
return status;
}
NTSTATUS nfs41_init_ops()
{
DbgEn();
ZeroAndInitializeNodeType(&nfs41_ops, RDBSS_NTC_MINIRDR_DISPATCH,
sizeof(MINIRDR_DISPATCH));
nfs41_ops.MRxFlags = (RDBSS_MANAGE_NET_ROOT_EXTENSION |
RDBSS_MANAGE_V_NET_ROOT_EXTENSION |
RDBSS_MANAGE_FCB_EXTENSION |
RDBSS_MANAGE_SRV_OPEN_EXTENSION |
RDBSS_MANAGE_FOBX_EXTENSION);
nfs41_ops.MRxSrvCallSize = 0;
nfs41_ops.MRxNetRootSize = sizeof(NFS41_NETROOT_EXTENSION);
nfs41_ops.MRxVNetRootSize = sizeof(NFS41_V_NET_ROOT_EXTENSION);
nfs41_ops.MRxFcbSize = sizeof(NFS41_FCB);
nfs41_ops.MRxSrvOpenSize = sizeof(NFS41_SRV_OPEN);
nfs41_ops.MRxFobxSize = sizeof(NFS41_FOBX);
// Mini redirector cancel routine ..
nfs41_ops.MRxCancel = NULL;
//
// Mini redirector Start/Stop. Each mini-rdr can be started or stopped
// while the others continue to operate.
//
nfs41_ops.MRxStart = nfs41_Start;
nfs41_ops.MRxStop = nfs41_Stop;
nfs41_ops.MRxDevFcbXXXControlFile = nfs41_DevFcbXXXControlFile;
//
// Mini redirector name resolution.
//
nfs41_ops.MRxCreateSrvCall = nfs41_CreateSrvCall;
nfs41_ops.MRxSrvCallWinnerNotify = nfs41_SrvCallWinnerNotify;
nfs41_ops.MRxCreateVNetRoot = nfs41_CreateVNetRoot;
nfs41_ops.MRxExtractNetRootName = nfs41_ExtractNetRootName;
nfs41_ops.MRxFinalizeSrvCall = nfs41_FinalizeSrvCall;
nfs41_ops.MRxFinalizeNetRoot = nfs41_FinalizeNetRoot;
nfs41_ops.MRxFinalizeVNetRoot = nfs41_FinalizeVNetRoot;
//
// File System Object Creation/Deletion.
//
nfs41_ops.MRxCreate = nfs41_Create;
nfs41_ops.MRxCollapseOpen = nfs41_CollapseOpen;
nfs41_ops.MRxShouldTryToCollapseThisOpen = nfs41_ShouldTryToCollapseThisOpen;
nfs41_ops.MRxExtendForCache = nfs41_ExtendForCache;
nfs41_ops.MRxExtendForNonCache = nfs41_ExtendForCache;
nfs41_ops.MRxZeroExtend = nfs41_ZeroExtend;
nfs41_ops.MRxTruncate = nfs41_Truncate;
nfs41_ops.MRxCleanupFobx = nfs41_CleanupFobx;
nfs41_ops.MRxCloseSrvOpen = nfs41_CloseSrvOpen;
nfs41_ops.MRxFlush = nfs41_Flush;
nfs41_ops.MRxForceClosed = nfs41_ForcedClose;
nfs41_ops.MRxDeallocateForFcb = nfs41_DeallocateForFcb;
nfs41_ops.MRxDeallocateForFobx = nfs41_DeallocateForFobx;
nfs41_ops.MRxIsLockRealizable = nfs41_IsLockRealizable;
//
// File System Objects query/Set
//
nfs41_ops.MRxQueryDirectory = nfs41_QueryDirectory;
nfs41_ops.MRxQueryVolumeInfo = nfs41_QueryVolumeInformation;
nfs41_ops.MRxSetVolumeInfo = nfs41_SetVolumeInfo;
nfs41_ops.MRxQueryEaInfo = nfs41_QueryEaInformation;
nfs41_ops.MRxSetEaInfo = nfs41_SetEaInformation;
nfs41_ops.MRxQuerySdInfo = nfs41_QuerySecurityInformation;
nfs41_ops.MRxSetSdInfo = nfs41_SetSecurityInformation;
nfs41_ops.MRxQueryQuotaInfo = nfs41_QueryQuotaInformation;
nfs41_ops.MRxSetQuotaInfo = nfs41_SetQuotaInformation;
nfs41_ops.MRxQueryFileInfo = nfs41_QueryFileInformation;
nfs41_ops.MRxSetFileInfo = nfs41_SetFileInformation;
nfs41_ops.MRxSetFileInfoAtCleanup = nfs41_SetFileInformationAtCleanup ;
//
// Buffering state change
//
nfs41_ops.MRxComputeNewBufferingState = nfs41_ComputeNewBufferingState;
//
// File System Object I/O
//
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_READ] = nfs41_Read;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_WRITE] = nfs41_Write;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK] = nfs41_Lock;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK] = nfs41_Lock;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK] = nfs41_Unlock;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = nfs41_Unlock;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_FSCTL] = nfs41_FsCtl;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_IOCTL] = nfs41_IoCtl;
nfs41_ops.MRxLowIOSubmit[LOWIO_OP_NOTIFY_CHANGE_DIRECTORY] =
nfs41_NotifyChangeDirectory;
//
// Miscellanous
//
nfs41_ops.MRxCompleteBufferingStateChangeRequest =
nfs41_CompleteBufferingStateChangeRequest;
nfs41_ops.MRxIsValidDirectory = nfs41_IsValidDirectory;
nfs41_ops.MRxPreparseName = nfs41_PreparseName;
nfs41_ops.MRxAreFilesAliased = nfs41_AreFilesAliased;
nfs41_ops.MRxGetConnectionId = nfs41_GetConnectionId;
DbgR();
return(STATUS_SUCCESS);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT drv, IN PUNICODE_STRING path)
{
NTSTATUS status;
ULONG flags = 0, i;
UNICODE_STRING dev_name, user_dev_name;
PNFS41_DEVICE_EXTENSION dev_exts;
2010-11-02 13:45:08 -04:00
TIME_FIELDS jan_1_1970 = {1970, 1, 1, 0, 0, 0, 0, 0};
DbgEn();
status = RxDriverEntry(drv, path);
if (status != STATUS_SUCCESS) {
print_error("RxDriverEntry failed: %08lx\n", status);
goto out;
}
RtlInitUnicodeString(&dev_name, NFS41_DEVICE_NAME);
SetFlag(flags, RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS);
status = nfs41_init_ops();
if (status != STATUS_SUCCESS) {
print_error("nfs41_init_ops failed to initialize dispatch table\n");
goto out;
}
DbgP("calling RxRegisterMinirdr\n");
status = RxRegisterMinirdr(&nfs41_dev, drv, &nfs41_ops, flags, &dev_name,
sizeof(NFS41_DEVICE_EXTENSION),
FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_REMOTE_DEVICE);
if (status != STATUS_SUCCESS) {
print_error("RxRegisterMinirdr failed: %08lx\n", status);
goto out;
}
nfs41_dev->Flags |= DO_BUFFERED_IO;
dev_exts = (PNFS41_DEVICE_EXTENSION)
((PBYTE)(nfs41_dev) + sizeof(RDBSS_DEVICE_OBJECT));
RxDefineNode(dev_exts, NFS41_DEVICE_EXTENSION);
dev_exts->DeviceObject = nfs41_dev;
RtlInitUnicodeString(&user_dev_name, NFS41_SHADOW_DEVICE_NAME);
DbgP("calling IoCreateSymbolicLink %wZ %wZ\n", &user_dev_name, &dev_name);
status = IoCreateSymbolicLink(&user_dev_name, &dev_name);
if (status != STATUS_SUCCESS) {
print_error("Device name IoCreateSymbolicLink failed: %08lx\n", status);
goto out_unregister;
}
KeInitializeEvent(&upcallEvent, SynchronizationEvent, FALSE );
ExInitializeFastMutex(&upcallLock);
ExInitializeFastMutex(&downcallLock);
ExInitializeFastMutex(&xidLock);
ExInitializeFastMutex(&openOwnerLock);
upcall = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_list),
NFS41_MM_POOLTAG);
if (upcall == NULL)
goto out_unregister;
InitializeListHead(&upcall->head);
downcall = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_list),
NFS41_MM_POOLTAG);
if (downcall == NULL) {
RxFreePool(upcall);
goto out_unregister;
}
InitializeListHead(&downcall->head);
drv->DriverUnload = nfs41_driver_unload;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
drv->MajorFunction[i] = (PDRIVER_DISPATCH)nfs41_FsdDispatch;
2010-10-27 14:58:35 -04:00
RtlTimeFieldsToTime(&jan_1_1970, &unix_time_diff);
out_unregister:
if (status != STATUS_SUCCESS)
RxUnregisterMinirdr(nfs41_dev);
out:
DbgEx();
return status;
}
VOID nfs41_driver_unload(IN PDRIVER_OBJECT drv)
{
PRX_CONTEXT RxContext;
NTSTATUS status;
UNICODE_STRING dev_name, pipe_name;
DbgEn();
RxContext = RxCreateRxContext(NULL, nfs41_dev, RX_CONTEXT_FLAG_IN_FSP);
if (RxContext == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto unload;
}
status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
RxDereferenceAndDeleteRxContext(RxContext);
unload:
RtlInitUnicodeString(&dev_name, NFS41_SHADOW_DEVICE_NAME);
status = IoDeleteSymbolicLink(&dev_name);
if (status != STATUS_SUCCESS) {
print_error("couldn't delete device symbolic link\n");
}
RtlInitUnicodeString(&pipe_name, NFS41_SHADOW_PIPE_NAME);
status = IoDeleteSymbolicLink(&pipe_name);
if (status != STATUS_SUCCESS) {
print_error("couldn't delete pipe symbolic link\n");
}
if (upcall)
RxFreePool(upcall);
if (downcall)
RxFreePool(downcall);
RxUnload(drv);
DbgP("driver unloaded %p\n", drv);
DbgR();
}