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/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 8bf92686..72a72eed 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; @@ -3239,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 6932f78e..0049e7fb 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 @@ -118,6 +119,13 @@ 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 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 + BOOLEAN zfs_AcquireForLazyWrite(void *Context, BOOLEAN Wait) { struct vnode *vp = Context; @@ -535,6 +543,75 @@ int zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, return 0; } +// 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: +// - 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)); \ + 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 = 0; \ + (fst)->NumberOfLinks = zp->z_links; \ + \ + 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. +// 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)); \ + 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) + /* * In POSIX, the vnop_lookup() would return with iocount still held * for the caller to issue VN_RELE() on when done. @@ -1186,6 +1263,59 @@ 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) { + 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: @@ -1231,6 +1361,21 @@ int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) &vp->share_access); 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); + } + + 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) zfs_send_notify(zfsvfs, zp->z_name_cache, zp->z_name_offset, FILE_NOTIFY_CHANGE_FILE_NAME, @@ -2765,6 +2910,93 @@ 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 NTSTATUS zp_get_or_synthesize_reparse(znode_t *zp, PREPARSE_DATA_BUFFER pTagData, ULONG tagDataLen, PULONG pTagDataRequiredLen) +{ + 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; + uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); + uio_addiov(uio, pTagData, tagDataLen); + err = zfs_readlink(zp->z_vnode, uio, NULL, NULL); + uio_free(uio); + + 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; + } + + 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) +{ + dprintf(" %s\n", __func__); + if (IrpSp->FileObject && IrpSp->FileObject->FsContext) { + struct vnode *vp = IrpSp->FileObject->FsContext; + if (VN_HOLD(vp) == 0) { + ZFSWIN_FILL_STAT_INFORMATION(flx, vp); + ZFSWIN_FILL_LX_INFORMATION(flx, vp); + 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 +3008,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; } @@ -3091,16 +3323,12 @@ 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 = 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; @@ -3159,7 +3387,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 +3466,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 +3787,37 @@ 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 +// 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) + return FALSE; + + BOOLEAN setVap = FALSE; + void *eaValue = &ea->EaName[0] + ea->EaNameLength + 1; + if (strncmp(ea->EaName, LX_FILE_METADATA_UID_EA_NAME, ea->EaNameLength) == 0) { + vap->va_uid = *(PUINT32)eaValue; + vap->va_active |= AT_UID; + 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; + 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; + 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 vnode_apply_single_ea(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION *ea) { int error; struct vnode *xvp = NULL; @@ -3586,18 +3857,69 @@ int xattr_process(struct vnode *vp, struct vnode *xdvp, FILE_FULL_EA_INFORMATION } /* - * Receive an array of structs to set EAs, iterate until Next is null. + * 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; + + NTSTATUS Status = STATUS_SUCCESS; + + // Optional: Check for validity if the caller wants it. + if (eaErrorOffset != NULL) { + Status = IoCheckEaBufferValidity(eas, eaLength, eaErrorOffset); + if (!NT_SUCCESS(Status)) { + dprintf("%s: failed validity: 0x%x\n", __func__, Status); + return Status; + } + } + + struct vnode *xdvp = NULL; + vattr_t vap = { 0 }; + int error; + 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 { + // 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) { + 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 (ea->NextEntryOffset == 0) + break; + } + + if (vap.va_active != 0) + zfs_setattr(vp, &vap, 0, NULL, NULL); + +out: + if (xdvp != NULL) { + VN_RELE(xdvp); + } + + return Status; +} + +/* + * 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) { 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 +3929,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) @@ -3650,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; @@ -3660,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); } 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;