diff --git a/daemon/from_kernel.h b/daemon/from_kernel.h index 9658dd4..fbd5c85 100644 --- a/daemon/from_kernel.h +++ b/daemon/from_kernel.h @@ -192,4 +192,35 @@ typedef struct _FILE_LINK_INFORMATION { WCHAR FileName[1]; } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; +/* wdm.h */ +typedef enum _FSINFOCLASS { + FileFsVolumeInformation = 1, + FileFsLabelInformation, // 2 + FileFsSizeInformation, // 3 + FileFsDeviceInformation, // 4 + FileFsAttributeInformation, // 5 + FileFsControlInformation, // 6 + FileFsFullSizeInformation, // 7 + FileFsObjectIdInformation, // 8 + FileFsDriverPathInformation, // 9 + FileFsVolumeFlagsInformation,// 10 + FileFsMaximumInformation +} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; + +/* ntddk.h */ +typedef struct _FILE_FS_SIZE_INFORMATION { + LARGE_INTEGER TotalAllocationUnits; + LARGE_INTEGER AvailableAllocationUnits; + ULONG SectorsPerAllocationUnit; + ULONG BytesPerSector; +} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION; + +typedef struct _FILE_FS_FULL_SIZE_INFORMATION { + LARGE_INTEGER TotalAllocationUnits; + LARGE_INTEGER CallerAvailableAllocationUnits; + LARGE_INTEGER ActualAvailableAllocationUnits; + ULONG SectorsPerAllocationUnit; + ULONG BytesPerSector; +} FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION; + #endif diff --git a/daemon/upcall.h b/daemon/upcall.h index 884ad76..1118394 100644 --- a/daemon/upcall.h +++ b/daemon/upcall.h @@ -25,6 +25,7 @@ #define __NFS41_DAEMON_UPCALL_H__ #include "nfs41_ops.h" +#include "from_kernel.h" /* structures for upcall arguments */ @@ -134,9 +135,12 @@ typedef struct __readdir_upcall_args { typedef struct __volume_upcall_args { nfs41_root *root; - ULONGLONG total; - ULONGLONG user; - ULONGLONG avail; + FS_INFORMATION_CLASS query; + int len; + union { + FILE_FS_SIZE_INFORMATION size; + FILE_FS_FULL_SIZE_INFORMATION fullsize; + } info; } volume_upcall_args; typedef union __upcall_args { diff --git a/daemon/volume.c b/daemon/volume.c index 1e639a3..9a9e8e6 100644 --- a/daemon/volume.c +++ b/daemon/volume.c @@ -31,30 +31,46 @@ #include "daemon_debug.h" +/* windows volume queries want size in 'units', so we have to + * convert the nfs space_* attributes from bytes to units */ +#define SECTORS_PER_UNIT 8 +#define BYTES_PER_SECTOR 512 +#define BYTES_PER_UNIT (SECTORS_PER_UNIT * BYTES_PER_SECTOR) + +#define TO_UNITS(bytes) (bytes / BYTES_PER_UNIT) + + int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) { int status; volume_upcall_args *args = &upcall->args.volume; status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE)); + if (status) goto out; + status = safe_read(&buffer, &length, &args->query, sizeof(FS_INFORMATION_CLASS)); +out: if (status) eprintf("parsing NFS41_VOLUME_QUERY failed with %d\n", status); else - dprintf(1, "parsing NFS41_VOLUME_QUERY: root=0x%p\n", args->root); + dprintf(1, "parsing NFS41_VOLUME_QUERY: root=0x%p, query=%d\n", + args->root, args->query); return status; } -int handle_volume(nfs41_upcall *upcall) +static int get_volume_size_info( + IN nfs41_session *session, + IN const char *query, + OUT OPTIONAL PLONGLONG total_out, + OUT OPTIONAL PLONGLONG user_out, + OUT OPTIONAL PLONGLONG avail_out) { nfs41_file_info info = { 0 }; bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } }; - volume_upcall_args *args = &upcall->args.volume; int status; /* query the space_ attributes of the root filesystem */ - status = nfs41_getattr(nfs41_root_session(args->root), - NULL, &attr_request, &info); + status = nfs41_getattr(session, NULL, &attr_request, &info); if (status) { eprintf("nfs41_getattr() failed with %s\n", nfs_error_string(status)); @@ -62,25 +78,61 @@ int handle_volume(nfs41_upcall *upcall) goto out; } - args->total = info.space_total; /* total disk space in bytes */ - args->user = info.space_avail; /* bytes available to this user */ - args->avail = info.space_free; /* free disk space in bytes */ - dprintf(2, "Volume: %llu user, %llu free of %llu total\n", - args->user, args->avail, args->total); + dprintf(2, "%s: %llu user, %llu free of %llu total\n", query, + info.space_avail, info.space_free, info.space_total); + + if (total_out) *total_out = TO_UNITS(info.space_total); + if (user_out) *user_out = TO_UNITS(info.space_avail); + if (avail_out) *avail_out = TO_UNITS(info.space_free); out: return status; } +int handle_volume(nfs41_upcall *upcall) +{ + volume_upcall_args *args = &upcall->args.volume; + nfs41_session *session = nfs41_root_session(args->root); + int status; + + switch (args->query) { + case FileFsSizeInformation: + args->len = sizeof(args->info.size); + args->info.size.SectorsPerAllocationUnit = SECTORS_PER_UNIT; + args->info.size.BytesPerSector = BYTES_PER_SECTOR; + + status = get_volume_size_info(session, "FileFsSizeInformation", + &args->info.size.TotalAllocationUnits.QuadPart, + &args->info.size.AvailableAllocationUnits.QuadPart, + NULL); + break; + + case FileFsFullSizeInformation: + args->len = sizeof(args->info.fullsize); + args->info.fullsize.SectorsPerAllocationUnit = SECTORS_PER_UNIT; + args->info.fullsize.BytesPerSector = BYTES_PER_SECTOR; + + status = get_volume_size_info(session, "FileFsFullSizeInformation", + &args->info.fullsize.TotalAllocationUnits.QuadPart, + &args->info.fullsize.CallerAvailableAllocationUnits.QuadPart, + &args->info.fullsize.ActualAvailableAllocationUnits.QuadPart); + break; + + default: + eprintf("unhandled fs query class %d\n", args->query); + status = ERROR_INVALID_PARAMETER; + break; + } + return status; +} + int marshall_volume(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall) { int status; volume_upcall_args *args = &upcall->args.volume; - status = safe_write(&buffer, length, &args->total, sizeof(args->total)); + status = safe_write(&buffer, length, &args->len, sizeof(args->len)); if (status) goto out; - status = safe_write(&buffer, length, &args->user, sizeof(args->user)); - if (status) goto out; - status = safe_write(&buffer, length, &args->avail, sizeof(args->avail)); + status = safe_write(&buffer, length, &args->info, args->len); out: return status; }