diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c index 727087a..07dd030 100644 --- a/daemon/daemon_debug.c +++ b/daemon/daemon_debug.c @@ -298,6 +298,7 @@ const char* opcode2string(DWORD opcode) case NFS41_FILE_QUERY: return "NFS41_FILE_QUERY"; case NFS41_FILE_SET: return "NFS41_FILE_SET"; case NFS41_EA_SET: return "NFS41_EA_SET"; + case NFS41_EA_GET: return "NFS41_EA_GET"; case NFS41_SYMLINK: return "NFS41_SYMLINK"; case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY"; case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY"; diff --git a/daemon/from_kernel.h b/daemon/from_kernel.h index 549024b..4608072 100644 --- a/daemon/from_kernel.h +++ b/daemon/from_kernel.h @@ -195,6 +195,20 @@ typedef struct _FILE_LINK_INFORMATION { WCHAR FileName[1]; } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; +typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + +typedef struct _FILE_GET_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + /* wdm.h */ typedef enum _FSINFOCLASS { FileFsVolumeInformation = 1, diff --git a/daemon/getattr.c b/daemon/getattr.c index 7defe85..8f81006 100644 --- a/daemon/getattr.c +++ b/daemon/getattr.c @@ -26,7 +26,9 @@ #include #include +#include #include "from_kernel.h" +#include "delegation.h" #include "daemon_debug.h" #include "nfs41_ops.h" #include "name_cache.h" @@ -167,8 +169,189 @@ out: } +static int parse_getexattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) +{ + int status; + getexattr_upcall_args *args = &upcall->args.getexattr; + + status = get_name(&buffer, &length, &args->path); + if (status) goto out; + status = safe_read(&buffer, &length, &args->eaindex, sizeof(args->eaindex)); + if (status) goto out; + status = safe_read(&buffer, &length, &args->restart, sizeof(args->restart)); + if (status) goto out; + status = safe_read(&buffer, &length, &args->single, sizeof(args->single)); + if (status) goto out; + status = safe_read(&buffer, &length, &args->ealist_len, sizeof(args->ealist_len)); + if (status) goto out; + args->ealist = buffer; + + dprintf(1, "parsing NFS41_EA_QUERY: buf_len=%d Initial %d Restart %d " + "Single %d\n", args->buf_len,args->eaindex, args->restart, args->single); +out: + return status; +} + +static int marshall_getexattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall) +{ + int status = NO_ERROR; + getexattr_upcall_args *args = &upcall->args.getexattr; + uint32_t len = args->buf_len; + + status = safe_write(&buffer, length, &len, sizeof(len)); + if (status) goto out; + status = safe_write(&buffer, length, args->buf, len); + if (status) goto out; +out: + free(args->buf); + return status; +} + + +static int handle_getexattr(nfs41_upcall *upcall) +{ + int status = 0; + getexattr_upcall_args *args = &upcall->args.getexattr; + PFILE_GET_EA_INFORMATION gea = + (PFILE_GET_EA_INFORMATION)args->ealist, prev = NULL; + PFILE_FULL_EA_INFORMATION eainfo, entry_pos; + unsigned char *entry_buf, buf[NFS4_EASIZE] = { 0 }; + nfs41_open_state *state = upcall->state_ref; + nfs41_path_fh parent, file; + open_claim4 claim; + stateid4 open_stateid; + stateid_arg stateid; + nfs41_component dst_name; + open_delegation4 delegation = { 0 }; + bool_t eof; + uint32_t bytes_read = 0; + ULONG buflen = 0, needed = 0; + + status = nfs41_rpc_openattr(state->session, &state->file, FALSE, &parent.fh); + if (status){ + dprintf(1, "nfs41_rpc_openattr() failed with error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out; + } + + entry_buf = malloc(UPCALL_BUF_SIZE); + if (entry_buf == NULL) { + status = GetLastError(); + goto out; + } + + entry_pos = eainfo = (PFILE_FULL_EA_INFORMATION)entry_buf; + + while (gea != prev) { + dst_name.name = gea->EaName; + dst_name.len = gea->EaNameLength; + claim.claim = CLAIM_NULL; + claim.u.null.filename = &dst_name; + status = nfs41_open(state->session, &parent, &file, &state->owner, + &claim, OPEN4_SHARE_ACCESS_READ, OPEN4_SHARE_DENY_BOTH, + OPEN4_NOCREATE, UNCHECKED4, 0, TRUE, &open_stateid, + &delegation, NULL); + if (status) { + dprintf(1, "nfs41_open() failed with error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out_free; + } + + stateid.stateid = open_stateid; + stateid.stateid.seqid = 0; + status = nfs41_read(state->session, &file, &stateid, 0, NFS4_EASIZE, + buf, &bytes_read, &eof); + if (status) { + dprintf(2, "nfs41_rpc_read EA attribute failed\n"); + status = nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT); + nfs41_close(state->session, &file, &stateid); + goto out_free; + } + + if (eof) { + dprintf(1, "read thread reached eof: bytes_read %d\n", bytes_read); + eainfo->EaNameLength = gea->EaNameLength; + if (FAILED(StringCchCopy((LPSTR)eainfo->EaName, gea->EaNameLength + 1, + (LPCSTR)gea->EaName))) { + status = ERROR_BUFFER_OVERFLOW; + nfs41_close(state->session, &file, &stateid); + goto out_free; + } + + if (FAILED(StringCchCopy((LPSTR)eainfo->EaName + + eainfo->EaNameLength + 1, bytes_read + 1, (LPCSTR)buf))) { + status = ERROR_BUFFER_OVERFLOW; + nfs41_close(state->session, &file, &stateid); + goto out_free; + } + + memset(buf, 0, NFS4_EASIZE); + eainfo->EaValueLength = (USHORT) bytes_read; + needed = (eainfo->EaNameLength + eainfo->EaValueLength) + + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName); + + if (needed % 4) + needed = needed + (4 - (needed % 4)); + + eainfo->NextEntryOffset = needed; + eainfo->Flags = 0; + + buflen = buflen + needed; + prev = gea; + + if (gea->NextEntryOffset != 0) { + gea = (PFILE_GET_EA_INFORMATION) + ((PBYTE) gea + gea->NextEntryOffset); + eainfo = (PFILE_FULL_EA_INFORMATION) + ((PBYTE) eainfo + eainfo->NextEntryOffset); + } + + status = nfs41_close(state->session, &file, &stateid); + if (status) { + dprintf(1, "nfs41_close() failed with error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out_free; + } + } else { + dprintf(2, "Size of the EA value is greater than %d\n", NFS4_EASIZE); + status = nfs41_close(state->session, &file, &stateid); + if (status) { + dprintf(1, "nfs41_rpc_openattr() failed with error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + } + /* treating extended attribute values larger than NFS4_EASIZE as failure */ + status = ERROR_INVALID_DATA; + goto out_free; + + } + } + + eainfo->NextEntryOffset = 0; + args->buf = (unsigned char *)entry_pos; + args->buf_len = buflen; + goto out; + +out_free: + free(entry_buf); +out: + return status; + +} + + + const nfs41_upcall_op nfs41_op_getattr = { parse_getattr, handle_getattr, marshall_getattr }; + +const nfs41_upcall_op nfs41_op_getexattr = { + parse_getexattr, + handle_getexattr, + marshall_getexattr + }; \ No newline at end of file diff --git a/daemon/nfs41_const.h b/daemon/nfs41_const.h index b522679..a3ad017 100644 --- a/daemon/nfs41_const.h +++ b/daemon/nfs41_const.h @@ -36,6 +36,9 @@ #define NFS4_OPAQUE_LIMIT 1024 #define NFS4_SESSIONID_SIZE 16 #define NFS4_STATEID_OTHER 12 +#define NFS4_EASIZE 256 +#define NFS4_EANAME_SIZE 128 + #define NFS41_MAX_FILEIO_SIZE (1024 * 1024) #define NFS41_MAX_SERVER_CACHE 1024 diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c index 055562d..9d4e624 100644 --- a/daemon/nfs41_ops.c +++ b/daemon/nfs41_ops.c @@ -507,6 +507,10 @@ int nfs41_open( if (compound_error(status = compound.res.status)) goto out; + if (dir_info.type == NF4ATTRDIR) + goto out; + + /* fill in the file handle's fileid and superblock */ file->fh.fileid = info->fileid; status = nfs41_superblock_for_fh(session, &info->fsid, &parent->fh, file); @@ -761,6 +765,10 @@ int nfs41_write( eprintf("WRITE succeeded with count=0; returning %s\n", nfs_error_string(status)); } + + if (info.type == NF4NAMEDATTR) + goto out; + nfs41_superblock_space_changed(file->fh.superblock); out: return status; diff --git a/daemon/setattr.c b/daemon/setattr.c index 103d4f4..9252e7d 100644 --- a/daemon/setattr.c +++ b/daemon/setattr.c @@ -497,8 +497,13 @@ static int parse_setexattr(unsigned char *buffer, uint32_t length, nfs41_upcall int status; setexattr_upcall_args *args = &upcall->args.setexattr; + status = get_name(&buffer, &length, &args->path); + if (status) goto out; status = safe_read(&buffer, &length, &args->mode, sizeof(args->mode)); if (status) goto out; + status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len)); + if (status) goto out; + args->buf = buffer; dprintf(1, "parsing NFS41_EA_SET: mode=%o\n", args->mode); out: @@ -512,24 +517,97 @@ static int handle_setexattr(nfs41_upcall *upcall) nfs41_open_state *state = upcall->state_ref; stateid_arg stateid; nfs41_file_info info = { 0 }; + PFILE_FULL_EA_INFORMATION eainfo = + (PFILE_FULL_EA_INFORMATION)args->buf, prev = NULL; + nfs41_path_fh parent, file; + open_claim4 claim; + stateid4 open_stateid; + nfs41_component dst_name; + nfs41_write_verf verf; + uint32_t bytes_written; + UCHAR *buf; + open_delegation4 delegation = { 0 }; /* break read delegations before SETATTR */ nfs41_delegation_return(state->session, &state->file, OPEN_DELEGATE_READ, FALSE); nfs41_open_stateid_arg(state, &stateid); + + if ((strncmp("NfsV3Attributes", eainfo->EaName, eainfo->EaNameLength) == 0 && + strlen("NfsV3Attributes") == eainfo->EaNameLength) || + (strncmp("NfsActOnLink", eainfo->EaName, eainfo->EaNameLength)== 0 && + strlen("NfsActOnLink") == eainfo->EaNameLength)) { + info.mode = args->mode; + info.attrmask.arr[1] |= FATTR4_WORD1_MODE; + info.attrmask.count = 2; + status = nfs41_setattr(state->session, &state->file, &stateid, &info); + if (status) { + dprintf(1, "nfs41_setattr() failed with error %s.\n", + nfs_error_string(status)); + return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + } + } else { + status = nfs41_rpc_openattr(state->session, &state->file, TRUE, &parent.fh); + if (status) { + dprintf(1, "handle_setexattr: nfs41_rpc_openattr() failed with error %s.\n", + nfs_error_string(status)); + return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + } - /* mode */ - info.mode = args->mode; - info.attrmask.arr[1] |= FATTR4_WORD1_MODE; - info.attrmask.count = 2; + while (eainfo != prev) { + /* we don't allow for extended attribute values to be larger than NFS4_EASIZE. + * thus, let's not allow setting such. + */ + if (eainfo->EaValueLength > NFS4_EASIZE) { + dprintf(1, "trying to write extended attribute value of size %d" + "max allowed %d\n", eainfo->EaValueLength, NFS4_EASIZE); + status = ERROR_INVALID_DATA; + goto out; + } + dst_name.name = eainfo->EaName; + dst_name.len = eainfo->EaNameLength; + claim.claim = CLAIM_NULL; + claim.u.null.filename = &dst_name; + status = nfs41_open(state->session, &parent, &file, &state->owner, &claim, + OPEN4_SHARE_ACCESS_WRITE, OPEN4_SHARE_DENY_BOTH, OPEN4_CREATE, + UNCHECKED4, 0664, TRUE, &open_stateid, &delegation, NULL); + if (status) { + dprintf(1, "handle_setexattr: nfs41_rpc_open() failed with error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out; + } - status = nfs41_setattr(state->session, &state->file, &stateid, &info); - if (status) - dprintf(1, "nfs41_setattr() failed with error %s.\n", - nfs_error_string(status)); + stateid.stateid = open_stateid; + stateid.stateid.seqid = 0; + buf = (UCHAR *) eainfo->EaName + eainfo->EaNameLength + 1; + status = nfs41_write(state->session, &file, &stateid, buf, + eainfo->EaValueLength, 0, FILE_SYNC4, &bytes_written, &verf); + if (status) { + dprintf(1, "handle_setexattr: nfs41_write() failed w/error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + nfs41_close(state->session, &file, &stateid); + goto out; + } - return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + status = nfs41_close(state->session, &file, &stateid); + if (status) { + dprintf(1, "handle_setexattr: nfs41_close() failed w/error %s.\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out; + } + + bytes_written = 0; + prev = eainfo; + eainfo = (FILE_FULL_EA_INFORMATION *) ((ULONG_PTR) eainfo + + eainfo->NextEntryOffset); + } + } +out: + return status; } diff --git a/daemon/upcall.c b/daemon/upcall.c index b08ff96..5f7eb8b 100644 --- a/daemon/upcall.c +++ b/daemon/upcall.c @@ -42,6 +42,7 @@ extern const nfs41_upcall_op nfs41_op_unlock; extern const nfs41_upcall_op nfs41_op_readdir; extern const nfs41_upcall_op nfs41_op_getattr; extern const nfs41_upcall_op nfs41_op_setattr; +extern const nfs41_upcall_op nfs41_op_getexattr; extern const nfs41_upcall_op nfs41_op_setexattr; extern const nfs41_upcall_op nfs41_op_symlink; extern const nfs41_upcall_op nfs41_op_volume; @@ -60,6 +61,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = { &nfs41_op_readdir, &nfs41_op_getattr, &nfs41_op_setattr, + &nfs41_op_getexattr, &nfs41_op_setexattr, &nfs41_op_symlink, &nfs41_op_volume, diff --git a/daemon/upcall.h b/daemon/upcall.h index 443c1cd..3606307 100644 --- a/daemon/upcall.h +++ b/daemon/upcall.h @@ -102,7 +102,22 @@ typedef struct __setattr_upcall_args { int set_class; } setattr_upcall_args; +typedef struct __getexattr_upcall_args { + const char *path; + unsigned char *buf; + uint32_t buf_len; + ULONG eaindex; + unsigned char *ealist; + uint32_t ealist_len; + BOOLEAN single; + BOOLEAN restart; +} getexattr_upcall_args; + + typedef struct __setexattr_upcall_args { + const char *path; + unsigned char *buf; + uint32_t buf_len; uint32_t mode; } setexattr_upcall_args; @@ -155,6 +170,7 @@ typedef union __upcall_args { lock_upcall_args lock; unlock_upcall_args unlock; getattr_upcall_args getattr; + getexattr_upcall_args getexattr; setattr_upcall_args setattr; setexattr_upcall_args setexattr; readdir_upcall_args readdir; diff --git a/sys/nfs41_debug.c b/sys/nfs41_debug.c index d2d2515..baf8088 100644 --- a/sys/nfs41_debug.c +++ b/sys/nfs41_debug.c @@ -609,6 +609,7 @@ const char *opcode2string(int opcode) case NFS41_FILE_QUERY: return "NFS41_FILE_QUERY"; case NFS41_FILE_SET: return "NFS41_FILE_SET"; case NFS41_EA_SET: return "NFS41_EA_SET"; + case NFS41_EA_GET: return "NFS41_EA_GET"; case NFS41_SYMLINK: return "NFS41_SYMLINK"; case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY"; case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY"; diff --git a/sys/nfs41_driver.c b/sys/nfs41_driver.c index e51f7df..2bd077d 100644 --- a/sys/nfs41_driver.c +++ b/sys/nfs41_driver.c @@ -45,7 +45,7 @@ typedef struct __nfs41_timings { } nfs41_timings; nfs41_timings lookup, readdir, open, close, getattr, setattr, getacl, setacl, volume, - read, write, lock, unlock; + read, write, lock, unlock, setexattr, getexattr; #endif DRIVER_INITIALIZE DriverEntry; DRIVER_UNLOAD nfs41_driver_unload; @@ -194,8 +194,21 @@ typedef struct _updowncall_entry { FILE_INFORMATION_CLASS InfoClass; } SetFile; struct { + PUNICODE_STRING filename; + PVOID buf; + ULONG buf_len; DWORD mode; } SetEa; + struct { + PUNICODE_STRING filename; + PVOID buf; + ULONG buf_len; + PVOID EaList; + ULONG EaListLength; + ULONG EaIndex; + BOOLEAN ReturnSingleEntry; + BOOLEAN RestartScan; + } QueryEa; struct { PUNICODE_STRING filename; PUNICODE_STRING target; @@ -940,22 +953,77 @@ NTSTATUS marshal_nfs41_easet(nfs41_updowncall_entry *entry, goto out; else tmp += *len; - header_len = *len + sizeof(DWORD); + header_len = *len + length_as_ansi(entry->u.SetEa.filename) + + sizeof(ULONG) + entry->u.SetEa.buf_len + sizeof(DWORD); if (header_len > buf_len) { status = STATUS_INSUFFICIENT_RESOURCES; goto out; } + status = marshall_unicode_as_ansi(&tmp, entry->u.SetEa.filename); + if (status) goto out; RtlCopyMemory(tmp, &entry->u.SetEa.mode, sizeof(DWORD)); - + tmp += sizeof(DWORD); + RtlCopyMemory(tmp, &entry->u.SetEa.buf_len, sizeof(ULONG)); + tmp += sizeof(ULONG); + RtlCopyMemory(tmp, entry->u.SetEa.buf, entry->u.SetEa.buf_len); + *len = header_len; - DbgP("marshal_nfs41_easet: mode=0x%x\n", entry->u.SetEa.mode); + DbgP("marshal_nfs41_easet: filename=%wZ, buflen=%d mode=0x%x\n", + entry->u.SetEa.filename, entry->u.SetEa.buf_len, entry->u.SetEa.mode); out: DbgEx(); return status; } +NTSTATUS marshal_nfs41_eaget(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.QueryEa.filename) + + 2 * sizeof(ULONG) + entry->u.QueryEa.EaListLength + 2 * sizeof(BOOLEAN); + + if (header_len > buf_len) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto out; + } + + status = marshall_unicode_as_ansi(&tmp, entry->u.QueryEa.filename); + if (status) goto out; + RtlCopyMemory(tmp, &entry->u.QueryEa.EaIndex, sizeof(ULONG)); + tmp += sizeof(ULONG); + RtlCopyMemory(tmp, &entry->u.QueryEa.RestartScan, sizeof(BOOLEAN)); + tmp += sizeof(BOOLEAN); + RtlCopyMemory(tmp, &entry->u.QueryEa.ReturnSingleEntry, sizeof(BOOLEAN)); + tmp += sizeof(BOOLEAN); + RtlCopyMemory(tmp, &entry->u.QueryEa.EaListLength, sizeof(ULONG)); + tmp += sizeof(ULONG); + RtlCopyMemory(tmp, entry->u.QueryEa.EaList, entry->u.QueryEa.EaListLength); + + *len = header_len; + + DbgP("marshal_nfs41_eaget: filename=%wZ, index=%d list_len=%d " + "rescan=%d single=%d\n", entry->u.QueryEa.filename, + entry->u.QueryEa.EaIndex, entry->u.QueryEa.EaListLength, + entry->u.QueryEa.RestartScan, entry->u.QueryEa.ReturnSingleEntry); +out: + DbgEx(); + return status; +} + + NTSTATUS marshal_nfs41_symlink(nfs41_updowncall_entry *entry, unsigned char *buf, ULONG buf_len, @@ -1178,6 +1246,9 @@ handle_upcall( case NFS41_EA_SET: status = marshal_nfs41_easet(entry, pbOut, cbOut, len); break; + case NFS41_EA_GET: + status = marshal_nfs41_eaget(entry, pbOut, cbOut, len); + break; case NFS41_SYMLINK: status = marshal_nfs41_symlink(entry, pbOut, cbOut, len); break; @@ -1508,6 +1579,17 @@ nfs41_downcall ( cur->u.QueryFile.buf_len = tmp->u.QueryFile.buf_len; RtlCopyMemory(cur->u.QueryFile.buf, buf, tmp->u.QueryFile.buf_len); break; + case NFS41_EA_GET: + RtlCopyMemory(&tmp->u.QueryEa.buf_len, buf, sizeof(ULONG)); + buf += sizeof(ULONG); + if (tmp->u.QueryEa.buf_len > cur->u.QueryEa.buf_len) { + cur->status = STATUS_BUFFER_TOO_SMALL; + cur->u.QueryEa.buf_len = tmp->u.QueryEa.buf_len; + break; + } + cur->u.QueryEa.buf_len = tmp->u.QueryEa.buf_len; + RtlCopyMemory(cur->u.QueryEa.buf, buf, tmp->u.QueryEa.buf_len); + break; case NFS41_SYMLINK: if (cur->u.Symlink.set) break; @@ -1826,7 +1908,7 @@ out: #ifdef ENABLE_TIMINGS static void print_op_stat(const char *op_str, nfs41_timings *time, BOOLEAN clear) { - DbgP("%-7s: num_ops=%-10d delta_ticks=%-10d size=%-10d\n", op_str, + DbgP("%-9s: num_ops=%-10d delta_ticks=%-10d size=%-10d\n", op_str, time->tops, time->tops ? time->ticks/time->tops : 0, time->sops ? time->size/time->sops : 0); if (clear) { @@ -1862,6 +1944,8 @@ out: print_op_stat("volume", &volume, 1); print_op_stat("getattr", &getattr, 1); print_op_stat("setattr", &setattr, 1); + print_op_stat("getexattr", &getexattr, 1); + print_op_stat("setexattr", &setexattr, 1); print_op_stat("readdir", &readdir, 1); print_op_stat("getacl", &getacl, 1); print_op_stat("setacl", &setacl, 1); @@ -3611,72 +3695,6 @@ void create_nfs3_attrs(nfs3_attrs *attrs, PNFS41_FCB nfs41_fcb) 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) { @@ -3708,42 +3726,200 @@ NTSTATUS nfs41_SetEaInformation ( PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext = NFS41GetVNetRootExtension(SrvOpen->pVNetRoot); PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context; + PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext); PFILE_FULL_EA_INFORMATION eainfo = (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer; nfs3_attrs *attrs = NULL; PNFS41_NETROOT_EXTENSION pNetRootContext = NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot); + ULONG buflen = RxContext->CurrentIrpSp->Parameters.SetEa.Length, error_offset; +#ifdef ENABLE_TIMINGS + LARGE_INTEGER t1, t2; + t1 = KeQueryPerformanceCounter(NULL); +#endif 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 (AnsiStrEq(&NfsV3Attributes, eainfo->EaName, eainfo->EaNameLength)) { + attrs = (nfs3_attrs *)(eainfo->EaName + eainfo->EaNameLength + 1); + print_nfs3_attrs(attrs); + DbgP("old mode is %o new mode is %o\n", nfs41_fcb->mode, attrs->mode); + entry->u.SetEa.mode = nfs41_fcb->mode = attrs->mode; + } else { + entry->u.SetEa.mode = 0; + status = IoCheckEaBufferValidity(eainfo, buflen, &error_offset); + if (status) { + RxFreePool(entry); + goto out; + } + } + entry->u.SetEa.buf = eainfo; + entry->u.SetEa.buf_len = buflen; + entry->u.SetEa.filename = FileName; + DbgP("FULL_EA_INFO: FileName=%wZ total_EA_len=%d EaNameLen=%d ExValueLen=%d\n", + entry->u.SetEa.filename, buflen, eainfo->EaNameLength, + eainfo->EaValueLength); + if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) { status = STATUS_INTERNAL_ERROR; goto out; } - +#ifdef ENABLE_TIMINGS + if (entry->status == STATUS_SUCCESS) { + InterlockedIncrement(&setexattr.sops); + InterlockedAdd64(&setexattr.size, entry->u.SetEa.buf_len); + } +#endif status = map_setea_error(entry->status); RxFreePool(entry); out: +#ifdef ENABLE_TIMINGS + t2 = KeQueryPerformanceCounter(NULL); + InterlockedIncrement(&setexattr.tops); + InterlockedAdd64(&setexattr.ticks, t2.QuadPart - t1.QuadPart); +#ifdef ENABLE_INDV_TIMINGS + DbgP("nfs41_SetEaInformation delta = %d op=%d sum=%d\n", + t2.QuadPart - t1.QuadPart, setexattr.tops, setexattr.ticks); +#endif +#endif DbgEx(); return status; } +NTSTATUS nfs41_QueryEaInformation ( + IN OUT PRX_CONTEXT RxContext) +{ + NTSTATUS status = STATUS_EAS_NOT_SUPPORTED; + PNFS41_FCB nfs41_fcb = (PNFS41_FCB)(RxContext->pFcb)->Context; + 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); + PFILE_GET_EA_INFORMATION query = (PFILE_GET_EA_INFORMATION) + RxContext->CurrentIrpSp->Parameters.QueryEa.EaList; + PFILE_FULL_EA_INFORMATION info; + PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext); + nfs41_updowncall_entry *entry; + ULONG buflen = RxContext->CurrentIrpSp->Parameters.QueryEa.Length; +#ifdef ENABLE_TIMINGS + LARGE_INTEGER t1, t2; + t1 = KeQueryPerformanceCounter(NULL); +#endif + + 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; + goto out; + } + + 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; + goto out; + } + + status = nfs41_UpcallCreate(NFS41_EA_GET, &nfs41_fobx->sec_ctx, + pVNetRootContext->session, nfs41_fobx->nfs41_open_state, + pNetRootContext->nfs41d_version, &entry); + if (status) + goto out; + entry->u.QueryEa.filename = FileName; + entry->u.QueryEa.buf_len = buflen; + entry->u.QueryEa.buf = RxContext->Info.Buffer; + entry->u.QueryEa.EaList = query; + entry->u.QueryEa.EaListLength = RxContext->QueryEa.UserEaListLength; + entry->u.QueryEa.EaIndex = RxContext->QueryEa.UserEaIndex; + entry->u.QueryEa.RestartScan = RxContext->QueryEa.RestartScan; + entry->u.QueryEa.ReturnSingleEntry = RxContext->QueryEa.ReturnSingleEntry; + + if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) { + status = STATUS_INTERNAL_ERROR; + goto out; + } + + if (entry->status == STATUS_BUFFER_TOO_SMALL) { + RxContext->InformationToReturn = entry->u.QueryEa.buf_len; + status = STATUS_BUFFER_TOO_SMALL; + } else if (entry->status == STATUS_SUCCESS) { + RxContext->Info.LengthRemaining = entry->u.QueryEa.buf_len; + RxContext->IoStatusBlock.Status = STATUS_SUCCESS; +#ifdef ENABLE_TIMINGS + InterlockedIncrement(&getexattr.sops); + InterlockedAdd64(&getexattr.size, entry->u.QueryEa.buf_len); +#endif + } else { + status = map_setea_error(entry->status); + } + RxFreePool(entry); + } +out: +#ifdef ENABLE_TIMINGS + t2 = KeQueryPerformanceCounter(NULL); + InterlockedIncrement(&getexattr.tops); + InterlockedAdd64(&getexattr.ticks, t2.QuadPart - t1.QuadPart); +#ifdef ENABLE_INDV_TIMINGS + DbgP("nfs41_QueryEaInformation delta = %d op=%d sum=%d\n", + t2.QuadPart - t1.QuadPart, getexattr.tops, getexattr.ticks); +#endif +#endif + DbgEx(); + return status; +} static void print_acl_args(SECURITY_INFORMATION info) { DbgP("Security query: %s %s %s\n", diff --git a/sys/nfs41_driver.h b/sys/nfs41_driver.h index 2ca182f..e1a70cb 100644 --- a/sys/nfs41_driver.h +++ b/sys/nfs41_driver.h @@ -66,6 +66,7 @@ typedef enum _nfs41_opcodes { NFS41_DIR_QUERY, NFS41_FILE_QUERY, NFS41_FILE_SET, + NFS41_EA_GET, NFS41_EA_SET, NFS41_SYMLINK, NFS41_VOLUME_QUERY,