From 284950a3c5a9c26a2ab481e4d0d7e56cd25db09d Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 22 Feb 2019 22:40:24 -0800 Subject: [PATCH 1/4] checkpoint --- ZFSin/ZFSin.vcxproj | 2 +- ZFSin/zfs/module/zfs/zfs_vnops.c | 23 ++ ZFSin/zfs/module/zfs/zfs_vnops_windows.c | 258 +++++++++++++++++++---- 3 files changed, 246 insertions(+), 37 deletions(-) diff --git a/ZFSin/ZFSin.vcxproj b/ZFSin/ZFSin.vcxproj index a2325193..892fa85b 100644 --- a/ZFSin/ZFSin.vcxproj +++ b/ZFSin/ZFSin.vcxproj @@ -42,7 +42,7 @@ Debug Win32 ZFSin - 10.0.16299.0 + 10.0.17763.0 diff --git a/ZFSin/zfs/module/zfs/zfs_vnops.c b/ZFSin/zfs/module/zfs/zfs_vnops.c index 8bf92686..5b5be9c9 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops.c @@ -2643,6 +2643,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, int flags, switch (dirlisttype) { case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: case FileBothDirectoryInformation: case FileDirectoryInformation: case FileNamesInformation: @@ -2946,6 +2947,28 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, int flags, break; + case FileIdFullDirectoryInformation: + // Full is the same as Both, but no Shortname + structsize = FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION, FileName[0]); + if (outcount + structsize + namelenholder > bufsize) break; + + eodp = (FILE_FULL_DIR_INFORMATION *)bufptr; + FILE_ID_FULL_DIR_INFORMATION *fifdi = (FILE_ID_FULL_DIR_INFORMATION *)bufptr; + fifdi->AllocationSize.QuadPart = S_ISDIR(tzp->z_mode) ? 0 : P2ROUNDUP(tzp->z_size, zfs_blksz(tzp)); + fifdi->EndOfFile.QuadPart = S_ISDIR(tzp->z_mode) ? 0 : tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, fifdi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, fifdi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, fifdi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, fifdi->LastAccessTime.QuadPart); + fifdi->EaSize = tzp->z_pflags & ZFS_REPARSEPOINT ? 0xa0000003 : xattr_getsize(ZTOV(tzp)); + fifdi->FileAttributes = zfs_getwinflags(tzp); + fifdi->FileId.QuadPart = objnum; + fifdi->FileIndex = offset; + nameptr = fifdi->FileName; + fifdi->FileNameLength = namelenholder; + + break; + case FileBothDirectoryInformation: structsize = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[0]); if (outcount + structsize + namelenholder > bufsize) break; diff --git a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c index 6932f78e..7958fa29 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c @@ -535,6 +535,7 @@ int zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, return 0; } +NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset); /* * In POSIX, the vnop_lookup() would return with iocount still held * for the caller to issue VN_RELE() on when done. @@ -1231,6 +1232,35 @@ int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) &vp->share_access); vnode_unlock(vp); + if (Irp->AssociatedIrp.SystemBuffer) { + vnode_apply_eas(vp, (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, NULL); + } + + PECP_LIST ecp; + FsRtlGetEcpListFromIrp(Irp, &ecp); + if (ecp) { + GUID ecpType; + VOID *ecpContext = NULL; + ULONG ecpContextSize; + while (STATUS_NOT_FOUND != FsRtlGetNextExtraCreateParameter(ecp, ecpContext, &ecpType, &ecpContext, &ecpContextSize)) { + if (IsEqualGUID(&ecpType, &GUID_ECP_ATOMIC_CREATE)) { + ATOMIC_CREATE_ECP_CONTEXT *atomic = (ATOMIC_CREATE_ECP_CONTEXT*)(ecpContext); + if (atomic->ReparseBuffer != NULL) { + ULONG t = atomic->ReparseBuffer->ReparseTag; + if (t == IO_REPARSE_TAG_LX_BLK || t == IO_REPARSE_TAG_LX_CHR || t == IO_REPARSE_TAG_LX_FIFO) { + switch (t) { + } + atomic->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET; + FsRtlAcknowledgeEcp(ecpContext); + } + } + } else if (IsEqualGUID(&ecpType, &GUID_ECP_QUERY_ON_CREATE)) { + QUERY_ON_CREATE_ECP_CONTEXT *qocContext = (QUERY_ON_CREATE_ECP_CONTEXT*)(ecpContext); + FsRtlAcknowledgeEcp(ecpContext); + } + } + } + if (stream_name == NULL) zfs_send_notify(zfsvfs, zp->z_name_cache, zp->z_name_offset, FILE_NOTIFY_CHANGE_FILE_NAME, @@ -2765,6 +2795,105 @@ NTSTATUS file_id_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LO return STATUS_SUCCESS; } +static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER pCtime, PLARGE_INTEGER pCrtime, PLARGE_INTEGER pAtime) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + sa_bulk_attr_t bulk[3]; + int count = 0; + uint64_t mtime[2]; + uint64_t ctime[2]; + uint64_t crtime[2]; + + if (pMtime) + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16); + if (pCtime) + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16); + if (pCrtime) + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, &crtime, 16); + if (count > 0) + sa_bulk_lookup(zp->z_sa_hdl, bulk, count); + + if (pMtime) + TIME_UNIX_TO_WINDOWS(mtime, pMtime->QuadPart); + if (pCtime) + TIME_UNIX_TO_WINDOWS(ctime, pCtime->QuadPart); + if (pCrtime) + TIME_UNIX_TO_WINDOWS(crtime, pCrtime->QuadPart); + if (pAtime) + TIME_UNIX_TO_WINDOWS(zp->z_atime, pAtime->QuadPart); +} + +static ULONG zp_get_reparse_tag(znode_t *zp) +{ + ULONG reparseTag = 0; + if (zp->z_pflags & ZFS_REPARSEPOINT) { + /* get reparse tag */ + int err; + uio_t *uio; + REPARSE_DATA_BUFFER tagdata; + uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); + uio_addiov(uio, &tagdata, sizeof(tagdata)); + err = zfs_readlink(zp->z_vnode, uio, NULL, NULL); + reparseTag = tagdata.ReparseTag; + uio_free(uio); + } + return reparseTag; +} + +NTSTATUS file_stat_lx_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FILE_STAT_LX_INFORMATION *flx) +{ + dprintf(" %s\n", __func__); + if (IrpSp->FileObject && IrpSp->FileObject->FsContext) { + struct vnode *vp = IrpSp->FileObject->FsContext; + if (VN_HOLD(vp) == 0) { + znode_t *zp = VTOZ(vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + flx->FileId.QuadPart = zp->z_id; + zp_unpack_times(zp, &flx->LastWriteTime, &flx->ChangeTime, &flx->CreationTime, &flx->LastAccessTime); + + flx->FileAttributes = zfs_getwinflags(zp); + flx->AllocationSize.QuadPart = P2ROUNDUP(zp->z_size, zfs_blksz(zp)); + flx->EndOfFile.QuadPart = vnode_isdir(vp) ? 0 : zp->z_size; + flx->ReparseTag = zp_get_reparse_tag(zp); + flx->NumberOfLinks = zp->z_links; + flx->EffectiveAccess = 0; /***************************************/ + + // LX_INFORMATION specific + flx->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE; + + if (vnode_ischr(vp) || vnode_isblk(vp)) { + uint64_t rdev; + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), + &rdev, sizeof (rdev)) == 0); + + flx->LxFlags |= LX_FILE_METADATA_HAS_DEVICE_ID; + flx->LxDeviceIdMajor = (rdev >> 20) & 0xFFFULL; + flx->LxDeviceIdMinor = rdev & 0xFFFFFULL; + + flx->ReparseTag = vnode_ischr(vp) ? IO_REPARSE_TAG_LX_CHR : IO_REPARSE_TAG_LX_BLK; + } else if ((zp->z_mode & S_IFIFO) == S_IFIFO) { + flx->ReparseTag = IO_REPARSE_TAG_LX_FIFO; + } + + flx->LxMode = zp->z_mode; + flx->LxUid = zp->z_uid; + flx->LxGid = zp->z_gid; + + if (flx->ReparseTag != 0) + flx->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + + if (vnode_isdir(vp) && zfsvfs->z_case == ZFS_CASE_SENSITIVE) + { + flx->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR; + } + + VN_RELE(vp); + } + return STATUS_SUCCESS; + } + return STATUS_OBJECT_NAME_NOT_FOUND; +} + // // If overflow, set Information to input_size and NameLength to required size. // @@ -2776,7 +2905,7 @@ NTSTATUS file_name_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_ return STATUS_INVALID_PARAMETER; - if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_NAME_INFORMATION)) { + if (IrpSp->Parameters.QueryFile.Length < 0x6/*sizeof(FILE_NAME_INFORMATION)*/) { Irp->IoStatus.Information = sizeof(FILE_NAME_INFORMATION); return STATUS_BUFFER_TOO_SMALL; } @@ -3159,7 +3288,7 @@ NTSTATUS query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA // If overflow, set Information to input_size and NameLength to required size. // dprintf("* %s: FileNameInformation (normalize %d)\n", __func__, normalize); - if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_NAME_INFORMATION)) { + if (IrpSp->Parameters.QueryFile.Length < 0x6/*sizeof(FILE_NAME_INFORMATION)*/) { Irp->IoStatus.Information = sizeof(FILE_NAME_INFORMATION); Status = STATUS_BUFFER_TOO_SMALL; break; @@ -3238,6 +3367,19 @@ NTSTATUS query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA Irp->IoStatus.Information = sizeof(FILE_ID_INFORMATION); } break; + case FileStatLxInformation: + dprintf("* %s: FileStatLxInformation\n", __func__); + if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) { + Irp->IoStatus.Information = sizeof(FILE_STAT_LX_INFORMATION); + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + FILE_STAT_LX_INFORMATION *flx = Irp->AssociatedIrp.SystemBuffer; + if (vp) { + Status = file_stat_lx_information(DeviceObject, Irp, IrpSp, flx); + Irp->IoStatus.Information = sizeof(FILE_STAT_LX_INFORMATION); + } + break; default: dprintf("* %s: unknown class 0x%x NOT IMPLEMENTED\n", __func__, IrpSp->Parameters.QueryFile.FileInformationClass); break; @@ -3546,7 +3688,31 @@ NTSTATUS query_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpS return Status; } -int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION *ea) +// WSL uses special EAs to interact with uid/gid/mode/device major/minor +static BOOLEAN xattr_process_special(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, vattr_t *vap) +{ + if (ea->EaNameLength < 6 || strncmp(ea->EaName, "$LX", 3) != 0) + return FALSE; + + BOOLEAN wasSpecial = FALSE; + void *eaValue = &ea->EaName[0] + ea->EaNameLength + 1; + if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXUID", 6) == 0) { + vap->va_uid = *(PUINT32)eaValue; + vap->va_active |= AT_UID; + wasSpecial = TRUE; + } else if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXGID", 6) == 0) { + vap->va_gid = *(PUINT32)eaValue; + vap->va_active |= AT_GID; + wasSpecial = TRUE; + } else if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXMOD", 6) == 0) { + vap->va_mode = *(PUINT32)eaValue; + vap->va_active |= AT_MODE; + wasSpecial = TRUE; + } + return wasSpecial; +} + +static int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION *ea) { int error; struct vnode *xvp = NULL; @@ -3585,6 +3751,53 @@ int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION return error; } +NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset) +{ + if (vp == NULL || ea == NULL) return STATUS_INVALID_PARAMETER; + + struct vnode *xdvp = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + // Optional: Check for validity if the caller wants it. + if (eaErrorOffset != NULL) { + Status = IoCheckEaBufferValidity(ea, eaLength, eaErrorOffset); + if (!NT_SUCCESS(Status)) { + dprintf("%s: failed validity: 0x%x\n", __func__, Status); + return Status; + } + } + + // Open (or Create) the xattr directory + if (zfs_get_xattrdir(VTOZ(vp), &xdvp, NULL, CREATE_XATTR_DIR) != 0) { + Status = STATUS_EA_CORRUPT_ERROR; + goto out; + } + + vattr_t va = { 0 }; + int error; + for (PFILE_FULL_EA_INFORMATION i = ea; ; i = (PFILE_FULL_EA_INFORMATION)((uint8_t*)i + i->NextEntryOffset)) { + if (xattr_process_special(vp, i, &va)) { + dprintf(" encountered special EA '%.*s'\n", i->EaNameLength, i->EaName); + } else { + error = xattr_process(vp, xdvp, i); + if (error != 0) dprintf(" failed to process xattr: %d\n", error); + } + + if (i->NextEntryOffset == 0) + break; + } + + if (va.va_active != 0) + zfs_setattr(vp, &va, 0, NULL, NULL); + +out: + if (xdvp != NULL) { + VN_RELE(xdvp); + } + + return Status; +} + /* * Receive an array of structs to set EAs, iterate until Next is null. */ @@ -3593,11 +3806,10 @@ NTSTATUS set_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) uint32_t input_len = IrpSp->Parameters.SetEa.Length; uint8_t *buffer = NULL, *UserBuffer = NULL; NTSTATUS Status = STATUS_SUCCESS; - struct vnode *vp = NULL, *xdvp = NULL; if (IrpSp->FileObject == NULL) return STATUS_INVALID_PARAMETER; - vp = IrpSp->FileObject->FsContext; + struct vnode *vp = IrpSp->FileObject->FsContext; if (vp == NULL) return STATUS_INVALID_PARAMETER; dprintf("%s\n", __func__); @@ -3607,42 +3819,16 @@ NTSTATUS set_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) // This magic is straight out of fastfat buffer = BufferUserBuffer(Irp, input_len); - Status = IoCheckEaBufferValidity((PFILE_FULL_EA_INFORMATION)buffer, - input_len, - (PULONG)&Irp->IoStatus.Information); - + ULONG eaErrorOffset = 0; + Status = vnode_apply_eas(vp, (PFILE_FULL_EA_INFORMATION)buffer, input_len, &eaErrorOffset); + // (Information is ULONG_PTR; as win64 is a LLP64 platform, ULONG isn't the right length.) + Irp->IoStatus.Information = eaErrorOffset; if (!NT_SUCCESS(Status)) { - dprintf("%s: failed Validity: 0x%x\n", __func__, Status); + dprintf("%s: failed vnode_apply_eas: 0x%x\n", __func__, Status); return Status; } - // Iterate "buffer", to get xattr name, and value. - FILE_FULL_EA_INFORMATION *FullEa; - - // Open (or Create) the xattr directory - if (zfs_get_xattrdir(VTOZ(vp), &xdvp, NULL, CREATE_XATTR_DIR) != 0) { - Status = STATUS_EA_CORRUPT_ERROR; - goto out; - } - - uint64_t offset = 0; - int error; - do { - FullEa = (FILE_FULL_EA_INFORMATION *)&buffer[offset]; - - error = xattr_process(vp, xdvp, FullEa); - if (error != 0) dprintf(" failed to process xattr: %d\n", error); - - offset = FullEa->NextEntryOffset; - } while (offset != 0); - -out: - if (xdvp != NULL) { - VN_RELE(xdvp); - } - return Status; - return STATUS_EAS_NOT_SUPPORTED; } NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) From 27d6a0aa701197d6f884de95540d27bb83ece581 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sat, 23 Feb 2019 01:24:08 -0800 Subject: [PATCH 2/4] Checkpoint II: better ECP, better EA, better QOC --- ZFSin/spl/include/sys/sysmacros.h | 9 + ZFSin/spl/include/sys/types.h | 1 - ZFSin/spl/module/spl/spl-vnode.c | 2 +- ZFSin/zfs/module/zfs/zfs_vnops.c | 4 +- ZFSin/zfs/module/zfs/zfs_vnops_windows.c | 274 ++++++++++++++--------- ZFSin/zfs/module/zfs/zfs_znode.c | 4 +- 6 files changed, 183 insertions(+), 111 deletions(-) diff --git a/ZFSin/spl/include/sys/sysmacros.h b/ZFSin/spl/include/sys/sysmacros.h index 5dc33d77..f309afcc 100644 --- a/ZFSin/spl/include/sys/sysmacros.h +++ b/ZFSin/spl/include/sys/sysmacros.h @@ -161,6 +161,15 @@ extern uint32_t zone_get_hostid(void *zone); extern void spl_setup(void); extern void spl_cleanup(void); +#define major(x) \ + ((unsigned)((((x) >> 31 >> 1) & 0xfffff000) | \ + (((x) >> 8) & 0x00000fff))) +#define minor(x) ((unsigned)((((x) >> 12) & 0xffffff00) | ((x)&0x000000ff))) + +#define makedev(x, y) \ + ((((x)&0xfffff000ULL) << 32) | (((x)&0x00000fffULL) << 8) | \ + (((y)&0xffffff00ULL) << 12) | (((y)&0x000000ffULL))) + #define makedevice(maj,min) makedev(maj,min) /* common macros */ diff --git a/ZFSin/spl/include/sys/types.h b/ZFSin/spl/include/sys/types.h index e9fea8eb..c3a0a26f 100644 --- a/ZFSin/spl/include/sys/types.h +++ b/ZFSin/spl/include/sys/types.h @@ -211,7 +211,6 @@ typedef struct { unsigned char g_guid[KAUTH_GUID_SIZE]; } guid_t; -#define minor(x) (x) #pragma warning( disable: 4296 ) // expression is always true #pragma error( disable: 4296 ) // expression is always true #pragma warning( disable: 4703 ) // potentially uninitialized local pointer variable diff --git a/ZFSin/spl/module/spl/spl-vnode.c b/ZFSin/spl/module/spl/spl-vnode.c index 1a7d99ac..03c5be94 100644 --- a/ZFSin/spl/module/spl/spl-vnode.c +++ b/ZFSin/spl/module/spl/spl-vnode.c @@ -784,7 +784,7 @@ int vnode_isswap(vnode_t *vp) int vnode_isfifo(vnode_t *vp) { - return 0; + return vp->v_type == VFIFO; } int vnode_islnk(vnode_t *vp) diff --git a/ZFSin/zfs/module/zfs/zfs_vnops.c b/ZFSin/zfs/module/zfs/zfs_vnops.c index 5b5be9c9..72a72eed 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops.c @@ -3262,8 +3262,8 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, #ifdef sun vap->va_rdev = vp->v_rdev; #else -// if (vnode_isblk(vp) || vnode_ischr(vp)) -// vap->va_rdev = zfs_cmpldev(rdev); + if (vnode_isblk(vp) || vnode_ischr(vp)) + vap->va_rdev = zfs_cmpldev(rdev); #endif //vap->va_seq = zp->z_seq; vap->va_flags = 0; /* FreeBSD: Reset chflags(2) flags. */ diff --git a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c index 7958fa29..5df4dffd 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -535,6 +536,62 @@ int zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, return 0; } +static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER pCtime, PLARGE_INTEGER pCrtime, PLARGE_INTEGER pAtime); +static ULONG zp_get_reparse_tag(znode_t *zp); +#define ZFSWIN_FILL_STAT_INFORMATION(fst, vp) \ + do { \ + znode_t *zp = VTOZ((vp)); \ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; \ + (fst)->FileId.QuadPart = zp->z_id; \ + zp_unpack_times(zp, &(fst)->LastWriteTime, &(fst)->ChangeTime, \ + &(fst)->CreationTime, &(fst)->LastAccessTime); \ + \ + (fst)->FileAttributes = zfs_getwinflags(zp); \ + (fst)->AllocationSize.QuadPart = \ + P2ROUNDUP(zp->z_size, zfs_blksz(zp)); \ + (fst)->EndOfFile.QuadPart = \ + vnode_isdir((vp)) ? 0 : zp->z_size; \ + (fst)->ReparseTag = zp_get_reparse_tag(zp); \ + (fst)->NumberOfLinks = zp->z_links; \ + \ + if ((fst)->ReparseTag != 0) \ + (fst)->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; \ + \ + } while (0) + +#define ZFSWIN_FILL_LX_INFORMATION(flx, vp) \ + do { \ + znode_t *zp = VTOZ((vp)); \ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; \ + \ + (flx)->EffectiveAccess = \ + 0; /***************************************/ \ + \ + (flx)->LxFlags = LX_FILE_METADATA_HAS_UID | \ + LX_FILE_METADATA_HAS_GID | \ + LX_FILE_METADATA_HAS_MODE; \ + \ + if (vnode_ischr((vp)) || vnode_isblk((vp))) { \ + uint64_t rdev; \ + VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), \ + &rdev, sizeof(rdev)) == 0); \ + dev_t cmpl = zfs_cmpldev(rdev); \ + \ + (flx)->LxFlags |= LX_FILE_METADATA_HAS_DEVICE_ID; \ + (flx)->LxDeviceIdMajor = major(cmpl); \ + (flx)->LxDeviceIdMinor = minor(cmpl); \ + } \ + \ + (flx)->LxMode = zp->z_mode; \ + (flx)->LxUid = zp->z_uid; \ + (flx)->LxGid = zp->z_gid; \ + \ + if (vnode_isdir((vp)) && \ + zfsvfs->z_case == ZFS_CASE_SENSITIVE) { \ + (flx)->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR; \ + } \ + } while (0) + NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset); /* * In POSIX, the vnop_lookup() would return with iocount still held @@ -1187,6 +1244,60 @@ int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) vap.va_type = VREG; vap.va_mode = 0644; + PQUERY_ON_CREATE_ECP_CONTEXT qocContext = NULL; + PECP_LIST ecp = NULL; + FsRtlGetEcpListFromIrp(Irp, &ecp); + if (ecp) { + GUID ecpType; + VOID *ecpContext = NULL; + ULONG ecpContextSize; + while (NT_SUCCESS(FsRtlGetNextExtraCreateParameter(ecp, ecpContext, &ecpType, &ecpContext, &ecpContextSize))) { + if (IsEqualGUID(&ecpType, &GUID_ECP_ATOMIC_CREATE)) { + ATOMIC_CREATE_ECP_CONTEXT *atomic = (PATOMIC_CREATE_ECP_CONTEXT)ecpContext; + if (BooleanFlagOn(atomic->InFlags, ATOMIC_CREATE_ECP_IN_FLAG_REPARSE_POINT_SPECIFIED) + && atomic->ReparseBuffer != NULL) { + ULONG tag = atomic->ReparseBuffer->ReparseTag; + enum vtype newType = VNON; + switch (tag) { + case IO_REPARSE_TAG_LX_BLK: + newType = VBLK; + break; + case IO_REPARSE_TAG_LX_CHR: + newType = VCHR; + break; + case IO_REPARSE_TAG_LX_FIFO: + newType = VFIFO; + break; + } + + if (newType != VNON) { + // Type was overridden by the reparse point value. + vap.va_type = newType; + + SetFlag(atomic->OutFlags, ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET); + FsRtlAcknowledgeEcp(ecpContext); + } + } + } else if (IsEqualGUID(&ecpType, &GUID_ECP_QUERY_ON_CREATE)) { + qocContext = (PQUERY_ON_CREATE_ECP_CONTEXT)ecpContext; + } + } + } + + // The associated buffer on a CreateFile is an EA buffer. + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + static BOOLEAN vattr_apply_single_ea(vattr_t *vap, PFILE_FULL_EA_INFORMATION ea); + for (PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ; ea = (PFILE_FULL_EA_INFORMATION)((uint8_t*)ea + ea->NextEntryOffset)) { + // only parse $LX attrs right now -- things we can store before the file + // gets created. + if (vattr_apply_single_ea(&vap, ea)) { + dprintf(" encountered special attrs EA '%.*s'\n", ea->EaNameLength, ea->EaName); + } + if (ea->NextEntryOffset == 0) + break; + } + } + // If O_TRUNC: switch (CreateDisposition) { case FILE_SUPERSEDE: @@ -1233,32 +1344,18 @@ int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) vnode_unlock(vp); if (Irp->AssociatedIrp.SystemBuffer) { + // Second pass: this will apply all EAs that are not only vattr EAs vnode_apply_eas(vp, (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.Create.EaLength, NULL); } - PECP_LIST ecp; - FsRtlGetEcpListFromIrp(Irp, &ecp); - if (ecp) { - GUID ecpType; - VOID *ecpContext = NULL; - ULONG ecpContextSize; - while (STATUS_NOT_FOUND != FsRtlGetNextExtraCreateParameter(ecp, ecpContext, &ecpType, &ecpContext, &ecpContextSize)) { - if (IsEqualGUID(&ecpType, &GUID_ECP_ATOMIC_CREATE)) { - ATOMIC_CREATE_ECP_CONTEXT *atomic = (ATOMIC_CREATE_ECP_CONTEXT*)(ecpContext); - if (atomic->ReparseBuffer != NULL) { - ULONG t = atomic->ReparseBuffer->ReparseTag; - if (t == IO_REPARSE_TAG_LX_BLK || t == IO_REPARSE_TAG_LX_CHR || t == IO_REPARSE_TAG_LX_FIFO) { - switch (t) { - } - atomic->OutFlags |= ATOMIC_CREATE_ECP_OUT_FLAG_REPARSE_POINT_SET; - FsRtlAcknowledgeEcp(ecpContext); - } - } - } else if (IsEqualGUID(&ecpType, &GUID_ECP_QUERY_ON_CREATE)) { - QUERY_ON_CREATE_ECP_CONTEXT *qocContext = (QUERY_ON_CREATE_ECP_CONTEXT*)(ecpContext); - FsRtlAcknowledgeEcp(ecpContext); - } + if (qocContext) { + if (BooleanFlagOn(qocContext->Flags, QoCFileStatInformation)) { + ZFSWIN_FILL_STAT_INFORMATION(&qocContext->StatInformation, vp); + } + if (BooleanFlagOn(qocContext->Flags, QoCFileLxInformation)) { + ZFSWIN_FILL_LX_INFORMATION(&qocContext->LxInformation, vp); } + FsRtlAcknowledgeEcp(qocContext); } if (stream_name == NULL) @@ -2836,7 +2933,17 @@ static ULONG zp_get_reparse_tag(znode_t *zp) err = zfs_readlink(zp->z_vnode, uio, NULL, NULL); reparseTag = tagdata.ReparseTag; uio_free(uio); + return reparseTag; } + + struct vnode *vp = ZTOV(zp); + if (vnode_ischr(vp)) + reparseTag = IO_REPARSE_TAG_LX_CHR; + else if (vnode_isblk(vp)) + reparseTag = IO_REPARSE_TAG_LX_BLK; + else if (vnode_isfifo(vp)) + reparseTag = IO_REPARSE_TAG_LX_FIFO; + return reparseTag; } @@ -2846,47 +2953,8 @@ NTSTATUS file_stat_lx_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STA if (IrpSp->FileObject && IrpSp->FileObject->FsContext) { struct vnode *vp = IrpSp->FileObject->FsContext; if (VN_HOLD(vp) == 0) { - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; - flx->FileId.QuadPart = zp->z_id; - zp_unpack_times(zp, &flx->LastWriteTime, &flx->ChangeTime, &flx->CreationTime, &flx->LastAccessTime); - - flx->FileAttributes = zfs_getwinflags(zp); - flx->AllocationSize.QuadPart = P2ROUNDUP(zp->z_size, zfs_blksz(zp)); - flx->EndOfFile.QuadPart = vnode_isdir(vp) ? 0 : zp->z_size; - flx->ReparseTag = zp_get_reparse_tag(zp); - flx->NumberOfLinks = zp->z_links; - flx->EffectiveAccess = 0; /***************************************/ - - // LX_INFORMATION specific - flx->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE; - - if (vnode_ischr(vp) || vnode_isblk(vp)) { - uint64_t rdev; - VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), - &rdev, sizeof (rdev)) == 0); - - flx->LxFlags |= LX_FILE_METADATA_HAS_DEVICE_ID; - flx->LxDeviceIdMajor = (rdev >> 20) & 0xFFFULL; - flx->LxDeviceIdMinor = rdev & 0xFFFFFULL; - - flx->ReparseTag = vnode_ischr(vp) ? IO_REPARSE_TAG_LX_CHR : IO_REPARSE_TAG_LX_BLK; - } else if ((zp->z_mode & S_IFIFO) == S_IFIFO) { - flx->ReparseTag = IO_REPARSE_TAG_LX_FIFO; - } - - flx->LxMode = zp->z_mode; - flx->LxUid = zp->z_uid; - flx->LxGid = zp->z_gid; - - if (flx->ReparseTag != 0) - flx->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; - - if (vnode_isdir(vp) && zfsvfs->z_case == ZFS_CASE_SENSITIVE) - { - flx->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR; - } - + ZFSWIN_FILL_STAT_INFORMATION(flx, vp); + ZFSWIN_FILL_LX_INFORMATION(flx, vp); VN_RELE(vp); } return STATUS_SUCCESS; @@ -3220,17 +3288,7 @@ NTSTATUS query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA if (vp) { znode_t *zp = VTOZ(vp); tag->FileAttributes = zfs_getwinflags(zp); - if (zp->z_pflags & ZFS_REPARSEPOINT) { - int err; - uio_t *uio; - REPARSE_DATA_BUFFER tagdata; - uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); - uio_addiov(uio, &tagdata, sizeof(tagdata)); - err = zfs_readlink(vp, uio, NULL, NULL); - tag->ReparseTag = tagdata.ReparseTag; - dprintf("Returning tag 0x%x\n", tag->ReparseTag); - uio_free(uio); - } + tag->ReparseTag = zp_get_reparse_tag(zp); Irp->IoStatus.Information = sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); Status = STATUS_SUCCESS; } @@ -3689,30 +3747,36 @@ NTSTATUS query_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpS } // WSL uses special EAs to interact with uid/gid/mode/device major/minor -static BOOLEAN xattr_process_special(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, vattr_t *vap) +// Returns: TRUE if the EA was stored in the vattr. +static BOOLEAN vattr_apply_single_ea(vattr_t *vap, PFILE_FULL_EA_INFORMATION ea) { - if (ea->EaNameLength < 6 || strncmp(ea->EaName, "$LX", 3) != 0) + if (ea->EaNameLength != 6 || strncmp(ea->EaName, "$LX", 3) != 0) return FALSE; - BOOLEAN wasSpecial = FALSE; + BOOLEAN setVap = FALSE; void *eaValue = &ea->EaName[0] + ea->EaNameLength + 1; - if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXUID", 6) == 0) { + if (strncmp(ea->EaName, LX_FILE_METADATA_UID_EA_NAME, ea->EaNameLength) == 0) { vap->va_uid = *(PUINT32)eaValue; vap->va_active |= AT_UID; - wasSpecial = TRUE; - } else if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXGID", 6) == 0) { + setVap = TRUE; + } else if (strncmp(ea->EaName, LX_FILE_METADATA_GID_EA_NAME, ea->EaNameLength) == 0) { vap->va_gid = *(PUINT32)eaValue; vap->va_active |= AT_GID; - wasSpecial = TRUE; - } else if (ea->EaNameLength == 6 && strncmp(ea->EaName, "$LXMOD", 6) == 0) { + setVap = TRUE; + } else if (strncmp(ea->EaName, LX_FILE_METADATA_MODE_EA_NAME, ea->EaNameLength) == 0) { vap->va_mode = *(PUINT32)eaValue; vap->va_active |= AT_MODE; - wasSpecial = TRUE; - } - return wasSpecial; + setVap = TRUE; + } else if (strncmp(ea->EaName, LX_FILE_METADATA_DEVICE_ID_EA_NAME, ea->EaNameLength) == 0) { + UINT32 *vu32 = (UINT32*)eaValue; + vap->va_rdev = makedev(vu32[0], vu32[1]); + vap->va_active |= VNODE_ATTR_va_rdev; + setVap = TRUE; + } + return setVap; } -static int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION *ea) +static int vnode_apply_single_ea(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION *ea) { int error; struct vnode *xvp = NULL; @@ -3751,44 +3815,46 @@ static int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFO return error; } -NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset) +NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, ULONG eaLength, PULONG eaErrorOffset) { - if (vp == NULL || ea == NULL) return STATUS_INVALID_PARAMETER; + if (vp == NULL || eas == NULL) return STATUS_INVALID_PARAMETER; - struct vnode *xdvp = NULL; NTSTATUS Status = STATUS_SUCCESS; // Optional: Check for validity if the caller wants it. if (eaErrorOffset != NULL) { - Status = IoCheckEaBufferValidity(ea, eaLength, eaErrorOffset); + Status = IoCheckEaBufferValidity(eas, eaLength, eaErrorOffset); if (!NT_SUCCESS(Status)) { dprintf("%s: failed validity: 0x%x\n", __func__, Status); return Status; } } - // Open (or Create) the xattr directory - if (zfs_get_xattrdir(VTOZ(vp), &xdvp, NULL, CREATE_XATTR_DIR) != 0) { - Status = STATUS_EA_CORRUPT_ERROR; - goto out; - } - - vattr_t va = { 0 }; + struct vnode *xdvp = NULL; + vattr_t vap = { 0 }; int error; - for (PFILE_FULL_EA_INFORMATION i = ea; ; i = (PFILE_FULL_EA_INFORMATION)((uint8_t*)i + i->NextEntryOffset)) { - if (xattr_process_special(vp, i, &va)) { - dprintf(" encountered special EA '%.*s'\n", i->EaNameLength, i->EaName); + for (PFILE_FULL_EA_INFORMATION ea = eas; ; ea = (PFILE_FULL_EA_INFORMATION)((uint8_t*)ea + ea->NextEntryOffset)) { + if (vattr_apply_single_ea(&vap, ea)) { + dprintf(" encountered special attrs EA '%.*s'\n", ea->EaNameLength, ea->EaName); } else { - error = xattr_process(vp, xdvp, i); + // optimization: do not create an xattr dir if we only had special vattr EAs + if (xdvp == NULL) { + // Open (or Create) the xattr directory + if (zfs_get_xattrdir(VTOZ(vp), &xdvp, NULL, CREATE_XATTR_DIR) != 0) { + Status = STATUS_EA_CORRUPT_ERROR; + goto out; + } + } + error = vnode_apply_single_ea(vp, xdvp, ea); if (error != 0) dprintf(" failed to process xattr: %d\n", error); } - if (i->NextEntryOffset == 0) + if (ea->NextEntryOffset == 0) break; } - if (va.va_active != 0) - zfs_setattr(vp, &va, 0, NULL, NULL); + if (vap.va_active != 0) + zfs_setattr(vp, &vap, 0, NULL, NULL); out: if (xdvp != NULL) { diff --git a/ZFSin/zfs/module/zfs/zfs_znode.c b/ZFSin/zfs/module/zfs/zfs_znode.c index aabfe161..84211952 100644 --- a/ZFSin/zfs/module/zfs/zfs_znode.c +++ b/ZFSin/zfs/module/zfs/zfs_znode.c @@ -576,7 +576,6 @@ zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx) #ifndef MAXMIN64 #define MAXMIN64 0xffffffffUL #endif -#if 0 /* * Create special expldev for ZFS private use. * Can't use standard expldev since it doesn't do @@ -602,7 +601,6 @@ zfs_cmpldev(uint64_t dev) { return (makedev((dev >> NBITSMINOR64), (dev & MAXMIN64))); } -#endif static void zfs_znode_sa_init(zfsvfs_t *zfsvfs, znode_t *zp, @@ -1015,7 +1013,7 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, } if (vap->va_type == VBLK || vap->va_type == VCHR) { - // rdev = zfs_expldev(vap->va_rdev); + rdev = zfs_expldev(vap->va_rdev); } parent = dzp->z_id; From ee7ad9c6e4f41ec80958aaa730a4cf13141c358c Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sun, 24 Feb 2019 00:34:46 -0800 Subject: [PATCH 3/4] Comments and prototypes --- ZFSin/zfs/module/zfs/zfs_vnops_windows.c | 27 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c index 5df4dffd..9139eb4d 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c @@ -119,6 +119,12 @@ uint64_t vnop_num_reclaims = 0; uint64_t vnop_num_vnodes = 0; #endif +#pragma region Prototypes +static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER pCtime, PLARGE_INTEGER pCrtime, PLARGE_INTEGER pAtime); +static NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset); +static BOOLEAN vattr_apply_single_ea(vattr_t *vap, PFILE_FULL_EA_INFORMATION ea); +#pragma endregion + BOOLEAN zfs_AcquireForLazyWrite(void *Context, BOOLEAN Wait) { struct vnode *vp = Context; @@ -536,8 +542,14 @@ int zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, return 0; } -static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER pCtime, PLARGE_INTEGER pCrtime, PLARGE_INTEGER pAtime); static ULONG zp_get_reparse_tag(znode_t *zp); +// There are multiple struct types with "stat" style members. +// In the absence of templating (C++!), a macro will serve as a type-unsafe way +// to fill them all out. +// Non-comprehensively, they are: +// - FILE_STAT_LX_INFORMATION +// - FILE_STAT_INFORMATION +// - QUERY_ON_CREATE_FILE_STAT_INFORMATION #define ZFSWIN_FILL_STAT_INFORMATION(fst, vp) \ do { \ znode_t *zp = VTOZ((vp)); \ @@ -559,6 +571,10 @@ static ULONG zp_get_reparse_tag(znode_t *zp); \ } while (0) +// There are multiple struct types with "lx" style members. +// Non-comprehensively, they are: +// - FILE_STAT_LX_INFORMATION +// - QUERY_ON_CREATE_FILE_LX_INFORMATION #define ZFSWIN_FILL_LX_INFORMATION(flx, vp) \ do { \ znode_t *zp = VTOZ((vp)); \ @@ -592,7 +608,6 @@ static ULONG zp_get_reparse_tag(znode_t *zp); } \ } while (0) -NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset); /* * In POSIX, the vnop_lookup() would return with iocount still held * for the caller to issue VN_RELE() on when done. @@ -1286,7 +1301,6 @@ int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) // The associated buffer on a CreateFile is an EA buffer. if (Irp->AssociatedIrp.SystemBuffer != NULL) { - static BOOLEAN vattr_apply_single_ea(vattr_t *vap, PFILE_FULL_EA_INFORMATION ea); for (PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ; ea = (PFILE_FULL_EA_INFORMATION)((uint8_t*)ea + ea->NextEntryOffset)) { // only parse $LX attrs right now -- things we can store before the file // gets created. @@ -3815,6 +3829,9 @@ static int vnode_apply_single_ea(struct vnode *vp, struct vnode *xdvp, FILE_FULL return error; } +/* + * Apply a set of EAs to a vnode, while handling special Windows EAs that set UID/GID/Mode/rdev. + */ NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, ULONG eaLength, PULONG eaErrorOffset) { if (vp == NULL || eas == NULL) return STATUS_INVALID_PARAMETER; @@ -3837,7 +3854,7 @@ NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, ULONG if (vattr_apply_single_ea(&vap, ea)) { dprintf(" encountered special attrs EA '%.*s'\n", ea->EaNameLength, ea->EaName); } else { - // optimization: do not create an xattr dir if we only had special vattr EAs + // optimization: defer creating an xattr dir until the first standard EA if (xdvp == NULL) { // Open (or Create) the xattr directory if (zfs_get_xattrdir(VTOZ(vp), &xdvp, NULL, CREATE_XATTR_DIR) != 0) { @@ -3865,7 +3882,7 @@ NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, ULONG } /* - * Receive an array of structs to set EAs, iterate until Next is null. + * Receive an array of structs to set EAs, check basic facts (len, file presence) and apply */ NTSTATUS set_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { From aa505c702632db89dd3470df1a1530e9ade3d147 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sun, 24 Feb 2019 15:55:04 -0800 Subject: [PATCH 4/4] Reparse point synthesis --- ZFSin/zfs/module/zfs/zfs_vnops_windows.c | 96 ++++++++++++++---------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c index 9139eb4d..0049e7fb 100644 --- a/ZFSin/zfs/module/zfs/zfs_vnops_windows.c +++ b/ZFSin/zfs/module/zfs/zfs_vnops_windows.c @@ -121,6 +121,7 @@ uint64_t vnop_num_vnodes = 0; #pragma region Prototypes static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER pCtime, PLARGE_INTEGER pCrtime, PLARGE_INTEGER pAtime); +static NTSTATUS zp_get_or_synthesize_reparse(znode_t *zp, PREPARSE_DATA_BUFFER pTagData, ULONG tagDataLen, PULONG pTagDataRequiredLen); static NTSTATUS vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION ea, ULONG eaLength, PULONG eaErrorOffset); static BOOLEAN vattr_apply_single_ea(vattr_t *vap, PFILE_FULL_EA_INFORMATION ea); #pragma endregion @@ -542,8 +543,7 @@ int zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, return 0; } -static ULONG zp_get_reparse_tag(znode_t *zp); -// There are multiple struct types with "stat" style members. +// There are multiple sturct types with "stat" style members. // In the absence of templating (C++!), a macro will serve as a type-unsafe way // to fill them all out. // Non-comprehensively, they are: @@ -563,12 +563,16 @@ static ULONG zp_get_reparse_tag(znode_t *zp); P2ROUNDUP(zp->z_size, zfs_blksz(zp)); \ (fst)->EndOfFile.QuadPart = \ vnode_isdir((vp)) ? 0 : zp->z_size; \ - (fst)->ReparseTag = zp_get_reparse_tag(zp); \ + (fst)->ReparseTag = 0; \ (fst)->NumberOfLinks = zp->z_links; \ \ - if ((fst)->ReparseTag != 0) \ + REPARSE_DATA_BUFFER reparse = {0}; \ + if (STATUS_NOT_A_REPARSE_POINT != \ + (zp_get_or_synthesize_reparse(zp, &reparse, \ + sizeof(reparse), NULL))) { \ + (fst)->ReparseTag = reparse.ReparseTag; \ (fst)->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; \ - \ + } \ } while (0) // There are multiple struct types with "lx" style members. @@ -2934,31 +2938,48 @@ static void zp_unpack_times(znode_t *zp, PLARGE_INTEGER pMtime, PLARGE_INTEGER p TIME_UNIX_TO_WINDOWS(zp->z_atime, pAtime->QuadPart); } -static ULONG zp_get_reparse_tag(znode_t *zp) +static NTSTATUS zp_get_or_synthesize_reparse(znode_t *zp, PREPARSE_DATA_BUFFER pTagData, ULONG tagDataLen, PULONG pTagDataRequiredLen) { - ULONG reparseTag = 0; + if (tagDataLen < REPARSE_DATA_BUFFER_HEADER_SIZE) + return STATUS_INVALID_PARAMETER; + + NTSTATUS Status = STATUS_NOT_A_REPARSE_POINT; + if (zp->z_pflags & ZFS_REPARSEPOINT) { /* get reparse tag */ int err; uio_t *uio; - REPARSE_DATA_BUFFER tagdata; uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); - uio_addiov(uio, &tagdata, sizeof(tagdata)); + uio_addiov(uio, pTagData, tagDataLen); err = zfs_readlink(zp->z_vnode, uio, NULL, NULL); - reparseTag = tagdata.ReparseTag; uio_free(uio); - return reparseTag; - } - struct vnode *vp = ZTOV(zp); - if (vnode_ischr(vp)) - reparseTag = IO_REPARSE_TAG_LX_CHR; - else if (vnode_isblk(vp)) - reparseTag = IO_REPARSE_TAG_LX_BLK; - else if (vnode_isfifo(vp)) - reparseTag = IO_REPARSE_TAG_LX_FIFO; + if (pTagDataRequiredLen) + *pTagDataRequiredLen = zp->z_size; + Status = tagDataLen < zp->z_size ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; + } else { + struct vnode *vp = ZTOV(zp); + ULONG synthesizedTag = 0; + + if (vnode_ischr(vp)) { + synthesizedTag = IO_REPARSE_TAG_LX_CHR; + } else if (vnode_isblk(vp)) { + synthesizedTag = IO_REPARSE_TAG_LX_BLK; + } else if (vnode_isfifo(vp)) { + synthesizedTag = IO_REPARSE_TAG_LX_FIFO; + } - return reparseTag; + if (synthesizedTag) { + if (pTagDataRequiredLen) + *pTagDataRequiredLen = sizeof(REPARSE_DATA_BUFFER); + + pTagData->ReparseTag = synthesizedTag; + pTagData->ReparseDataLength = 0; + Status = STATUS_SUCCESS; + } + } + + return Status; } NTSTATUS file_stat_lx_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, FILE_STAT_LX_INFORMATION *flx) @@ -3302,7 +3323,13 @@ NTSTATUS query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA if (vp) { znode_t *zp = VTOZ(vp); tag->FileAttributes = zfs_getwinflags(zp); - tag->ReparseTag = zp_get_reparse_tag(zp); + tag->ReparseTag = 0; + REPARSE_DATA_BUFFER reparse; + if (STATUS_NOT_A_REPARSE_POINT != + (zp_get_or_synthesize_reparse( + zp, &reparse, sizeof(reparse), NULL))) { + tag->ReparseTag = reparse.ReparseTag; + } Irp->IoStatus.Information = sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); Status = STATUS_SUCCESS; } @@ -3919,7 +3946,7 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA NTSTATUS Status = STATUS_NOT_A_REPARSE_POINT; PFILE_OBJECT FileObject = IrpSp->FileObject; DWORD outlen = IrpSp->Parameters.FileSystemControl.OutputBufferLength; - void *buffer = Irp->AssociatedIrp.SystemBuffer; + PREPARSE_DATA_BUFFER reparseBuffer = (PREPARSE_DATA_BUFFER)Irp->AssociatedIrp.SystemBuffer; struct vnode *vp; if (FileObject == NULL) return STATUS_INVALID_PARAMETER; @@ -3929,25 +3956,12 @@ NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCA if (vp) { VN_HOLD(vp); znode_t *zp = VTOZ(vp); - - if (zp->z_pflags & ZFS_REPARSEPOINT) { - int err; - int size = MIN(zp->z_size, outlen); - uio_t *uio; - uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); - uio_addiov(uio, buffer, size); - err = zfs_readlink(vp, uio, NULL, NULL); - uio_free(uio); - - if (outlen < zp->z_size) - Status = STATUS_BUFFER_OVERFLOW; - else - Status = STATUS_SUCCESS; - - Irp->IoStatus.Information = size; - - REPARSE_DATA_BUFFER *rdb = buffer; - dprintf("Returning tag 0x%x\n", rdb->ReparseTag); + ULONG requiredLen = 0; + Status = zp_get_or_synthesize_reparse(zp, (PREPARSE_DATA_BUFFER)reparseBuffer, outlen, &requiredLen); + if (Status != STATUS_NOT_A_REPARSE_POINT) { + // Buffer overflow and success can also come out of zp_get_or_synthesize_reparse. + Irp->IoStatus.Information = requiredLen; + dprintf("Returning tag 0x%x\n", reparseBuffer->ReparseTag); } VN_RELE(vp); }