Skip to content

Commit

Permalink
CreateFile of newfile:newstream should create both
Browse files Browse the repository at this point in the history
In addition, many more stream fixes, illegal chars, and names

Signed-off-by: Jorgen Lundman <[email protected]>
  • Loading branch information
lundman committed Jun 20, 2023
1 parent 894d512 commit 94f1f52
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 22 deletions.
90 changes: 70 additions & 20 deletions module/os/windows/zfs/zfs_vnops_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ stream_parse(char *filename, char **streamname)
return (0);

// Regular file, with "::$DATA" end?
if (strcmp(colon, "::$DATA") == 0) {
if (strcasecmp(colon, "::$DATA") == 0) {
*colon = 0; // Terminate before colon
return (0);
}
Expand All @@ -356,24 +356,39 @@ stream_parse(char *filename, char **streamname)
// We now ADD ":$DATA" to the stream name.
strcat(*streamname, ":$DATA");

return (0);
goto checkname;
}

// Have second colon, better be ":$DATA".
if (strcmp(second, ":$DATA") == 0) {
if (strcasecmp(second, ":$DATA") == 0) {

// Terminate at second colon, set streamname
// We now keep the ":$DATA" extension in the xattr name
// *second = 0;

*streamname = &colon[1];
*colon = 0; // Cut of streamname from filename
return (0);

goto checkname;
}

// Not $DATA
dprintf("%s: Not handling StreamType '%s'\n", __func__, second);
return (EINVAL);

checkname:
if (strlen(*streamname) >= 512)
return (STATUS_OBJECT_NAME_INVALID);

if (strchr(*streamname, '/') ||
/* strchr(&colon[2], ':') || there is one at ":$DATA" */
!strcasecmp("DOSATTRIB:$DATA", *streamname) ||
!strcasecmp("EA:$DATA", *streamname) ||
!strcasecmp("reparse:$DATA", *streamname) ||
!strcasecmp("casesensitive:$DATA", *streamname))
return (STATUS_OBJECT_NAME_INVALID);

return (0);
}

/*
Expand Down Expand Up @@ -537,6 +552,9 @@ zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist,
if (!word && finalpartmustnotexist && dvp && !vp) {
dprintf("CREATE with existing dir exit?\n");
VN_RELE(dvp);

if (zp && ZTOV(zp) && !vnode_isdir(ZTOV(zp)))
return (ENOTDIR);
return (EEXIST);
}

Expand Down Expand Up @@ -855,7 +873,7 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
error = stream_parse(filename, &stream_name);
if (error) {
Irp->IoStatus.Information = 0;
return (STATUS_INVALID_PARAMETER);
return (error);
}
if (stream_name != NULL)
dprintf("%s: Parsed out streamname '%s'\n",
Expand Down Expand Up @@ -998,7 +1016,14 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
if (error == EEXIST) {
dprintf("%s: dir exists, wont create\n", __func__);
Irp->IoStatus.Information = FILE_EXISTS;
return (STATUS_OBJECT_NAME_COLLISION);
if (OpenTargetDirectory)
return (STATUS_NOT_A_DIRECTORY);
return (STATUS_FILE_IS_A_DIRECTORY); // 2
}
if (error == ENOTDIR) {
dprintf("%s: file exists, wont create\n", __func__);
Irp->IoStatus.Information = FILE_EXISTS;
return (STATUS_OBJECT_NAME_COLLISION); // 3
}
// A directory component did not exist, or was a file
if ((dvp == NULL) || (error == ENOTDIR)) {
Expand Down Expand Up @@ -1036,7 +1061,7 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
vp = NULL;
dvp = ZTOV(dzp);
int direntflags = 0; // To detect ED_CASE_CONFLICT
error = zfs_dirlook(dzp, stream_name, &zp, 0 /* FIGNORECASE */,
error = zfs_dirlook(dzp, stream_name, &zp, FIGNORECASE,
&direntflags, NULL);
if (!CreateFile && error) {
Irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
Expand Down Expand Up @@ -1096,6 +1121,8 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
VN_RELE(vp);
VN_RELE(dvp);
Irp->IoStatus.Information = FILE_EXISTS;
if (CreateDirectory && !vnode_isdir(vp))
return (STATUS_NOT_A_DIRECTORY);
return (STATUS_OBJECT_NAME_COLLISION); // create file error
}

Expand Down Expand Up @@ -1249,7 +1276,7 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
// NTSTATUS Status;

// Streams do not call SeAccessCheck?
if (stream_name != NULL) {
if (stream_name != NULL && vp != NULL) {
IoSetShareAccess(
IrpSp->Parameters.Create.SecurityContext->
DesiredAccess,
Expand Down Expand Up @@ -1384,18 +1411,22 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
CreateDisposition == FILE_CREATE, vap->va_mode,
&zp, NULL, 0, NULL, NULL);
if (error == 0) {
boolean_t reenter_for_xattr = B_FALSE;

vp = ZTOV(zp);
// Creating two things? Don't attach until 2nd item.
if (!(zp->z_pflags & ZFS_XATTR) && stream_name != NULL)
reenter_for_xattr = B_TRUE;

zfs_couplefileobject(vp, FileObject,
zp ? zp->z_size : 0ULL);
vnode_ref(vp); // Hold open reference, until CLOSE
vp = ZTOV(zp);

check_and_set_stream_parent(stream_name, FileObject,
VTOZ(dvp)->z_xattr_parent);
if (!reenter_for_xattr) {
zfs_couplefileobject(vp, FileObject,
zp ? zp->z_size : 0ULL);
vnode_ref(vp);

if (DeleteOnClose)
Status = zfs_setunlink(FileObject, dvp);
if (DeleteOnClose)
Status = zfs_setunlink(FileObject, dvp);
}

if (Status == STATUS_SUCCESS) {

Expand Down Expand Up @@ -1426,13 +1457,17 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
&vp->share_access);
vnode_unlock(vp);

if (stream_name == NULL) {
// Did we create file, or stream?
if (!(zp->z_pflags & ZFS_XATTR)) {
zfs_send_notify(zfsvfs,
zp->z_name_cache,
zp->z_name_offset,
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED);
} else {
check_and_set_stream_parent(stream_name,
FileObject,
VTOZ(dvp)->z_xattr_parent);

zfs_build_path_stream(zp, NULL, NULL,
NULL, NULL, stream_name);
Expand All @@ -1444,9 +1479,17 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo,
FILE_ACTION_ADDED_STREAM,
stream_name);
}

/* Windows lets you create a file, and stream, in one. */
/* Call this function again, lets hope, only once */
if (reenter_for_xattr) {
Status = EAGAIN;
}

}
VN_RELE(vp);
VN_RELE(dvp);

return (Status);
}
if (error == EEXIST)
Expand Down Expand Up @@ -1628,11 +1671,12 @@ zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo)
}


do {

// Call ZFS
status = zfs_vnop_lookup_impl(Irp, IrpSp, zmo, filename, &vap);

// Call ZFS
status = zfs_vnop_lookup_impl(Irp, IrpSp, zmo, filename, &vap);

} while (status == EAGAIN);

#if defined(NTDDI_WIN10_RS5) && (NTDDI_VERSION >= NTDDI_WIN10_RS5)
// Did ECP ask for getattr to be returned? None, one or both can be set.
Expand Down Expand Up @@ -3794,6 +3838,11 @@ user_fs_request(PDEVICE_OBJECT DeviceObject, PIRP *PIrp,
Status = STATUS_SUCCESS;
break;

case FSCTL_SET_ZERO_DATA:
dprintf(" FSCTL_SET_ZERO_DATA\n");
Status = fsctl_set_zero_data(DeviceObject, Irp, IrpSp);
break;

default:
dprintf("* %s: unknown class 0x%lx\n", __func__,
IrpSp->Parameters.FileSystemControl.FsControlCode);
Expand Down Expand Up @@ -4705,6 +4754,7 @@ delete_entry(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
error != STATUS_SOME_NOT_MAPPED) {
VN_RELE(dvp);
dprintf("%s: some illegal characters\n", __func__);
return (STATUS_INVALID_PARAMETER); // test.exe
return (STATUS_ILLEGAL_CHARACTER);
}
while (outlen > 0 && filename[outlen - 1] == '\\') outlen--;
Expand Down
113 changes: 111 additions & 2 deletions module/os/windows/zfs/zfs_vnops_windows_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -4273,6 +4273,7 @@ file_stream_information(PDEVICE_OBJECT DeviceObject, PIRP Irp,
}

struct vnode *vp = FileObject->FsContext;
zfs_dirlist_t *zccb = FileObject->FsContext2;
znode_t *zp = VTOZ(vp), *xzp = NULL;
znode_t *xdzp = NULL;
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
Expand All @@ -4289,12 +4290,28 @@ file_stream_information(PDEVICE_OBJECT DeviceObject, PIRP Irp,
zap_attribute_t za;

// Iterate the xattrs.
// Windows can call this on a stream zp, in this case, we
// need to go find the real parent, and iterate on that.
if (zccb && zp->z_pflags & ZFS_XATTR) {

error = zfs_zget(zfsvfs, zccb->real_file_id, &zp);
if (error)
goto out;
} else {
VN_HOLD(vp);
}

// Add a record for this name, if there is room. Keep a
// count of how much space would need.
// insert_xattrname adds first ":" and ":$DATA"
overflow = zfswin_insert_streamname(":$DATA", outbuffer,
&previous_stream, availablebytes, &spaceused, zp->z_size);
if (vnode_isdir(vp))
overflow = zfswin_insert_streamname("", outbuffer,
&previous_stream, availablebytes, &spaceused,
vnode_isdir(vp) ? 0 : zp->z_size);
else
overflow = zfswin_insert_streamname(":$DATA", outbuffer,
&previous_stream, availablebytes, &spaceused,
vnode_isdir(vp) ? 0 : zp->z_size);

/* Grab the hidden attribute directory vnode. */
if (zfs_get_xattrdir(zp, &xdzp, cr, 0) != 0) {
Expand Down Expand Up @@ -4332,6 +4349,8 @@ file_stream_information(PDEVICE_OBJECT DeviceObject, PIRP Irp,
VN_RELE(xdvp);
}

zrele(zp);

zfs_exit(zfsvfs, FTAG);

if (overflow > 0)
Expand Down Expand Up @@ -5060,3 +5079,93 @@ fsctl_zfs_volume_mountpoint(PDEVICE_OBJECT DeviceObject, PIRP Irp,
zmo->mountpoint.Length;
return (STATUS_SUCCESS);
}

NTSTATUS
fsctl_set_zero_data(PDEVICE_OBJECT DeviceObject, PIRP Irp,
PIO_STACK_LOCATION IrpSp)
{
FILE_ZERO_DATA_INFORMATION *fzdi = Irp->AssociatedIrp.SystemBuffer;
ULONG length = IrpSp->Parameters.FileSystemControl.InputBufferLength;
PFILE_OBJECT FileObject = IrpSp->FileObject;
NTSTATUS Status;
LARGE_INTEGER time;
uint64_t start, end;
IO_STATUS_BLOCK iosb;
zfs_dirlist_t *zccb;

if (!fzdi || length < sizeof (FILE_ZERO_DATA_INFORMATION))
return (STATUS_INVALID_PARAMETER);

if (!FileObject)
return (STATUS_INVALID_PARAMETER);

if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) {
dprintf("BeyondFinalZero was <= to Offset (%I64x <= %I64x)\n",
fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart);
return (STATUS_INVALID_PARAMETER);
}

struct vnode *vp = FileObject->FsContext;

if (!vp)
return (STATUS_INVALID_PARAMETER);

zccb = FileObject->FsContext2;

if (!zccb)
return (STATUS_INVALID_PARAMETER);

// if (Irp->RequestorMode == UserMode &&
// !(ccb->access & FILE_WRITE_DATA)) {
// WARN("insufficient privileges\n");
// return STATUS_ACCESS_DENIED;
// }

znode_t *zp = VTOZ(vp);

// ExAcquireResourceSharedLite(&zmo->tree_lock, true);
ExAcquireResourceExclusiveLite(vp->FileHeader.Resource, TRUE);

CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb);

if (!vnode_isreg(vp)) {
dprintf("FileObject did not point to a file\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}

/*
* btrfs has this test, but MS "test.exe streams" tests that this
* works, so we will leave it in.
*/
#if 0
if (zp->z_pflags & ZFS_XATTR) {
dprintf("FileObject is stream\n");
Status = STATUS_INVALID_PARAMETER;
goto end;
}
#endif

if ((uint64_t)fzdi->FileOffset.QuadPart >= zp->z_size) {
Status = STATUS_SUCCESS;
goto end;
}

Status = zfs_freesp(zp, fzdi->FileOffset.QuadPart,
fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart,
O_RDWR, TRUE);

CcPurgeCacheSection(FileObject->SectionObjectPointer,
&fzdi->FileOffset,
(ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart),
FALSE);

Status = STATUS_SUCCESS;

end:

ExReleaseResourceLite(vp->FileHeader.Resource);
// ExReleaseResourceLite(&Vcb->tree_lock);

return (Status);
}

0 comments on commit 94f1f52

Please sign in to comment.