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;