From 8c68d8a5ce3b6c98afb394e6476fcebac3b09e5f Mon Sep 17 00:00:00 2001 From: Jorgen Lundman Date: Sun, 18 Dec 2022 14:15:55 +0900 Subject: [PATCH] Windows: allow mounting of snapshots Break readdir emit function up into smaller, so that zfsctl readdir can also call the same emit. If a dataset is mount on E: E:/.zfs/snapshots/helloworld/root ^-real ^-fake ^-real ^-fake ^-fake For a mounted dataset (here, "E:") the root is a real "zp", but ".zfs", "snapshots" and any snapshot names like "helloworld" are fake "zp" nodes - exist only in memory, not on disk. We redirect requests like readdir, and set-reparse-point (mount) to zfsctl instead, and tag the memory nodes only. Kernel work to handle mounting and unmounting of snapshots, and initial support for readonly (which doesn't exist as a mount option) by putting checks for rdonly in many places. * NOTE * Currently, export -a will do the wrong thing, and not unmount mounted snapshots first, so it will *hang* due to this. To fix: Mounting something inside a dataset, should hold an additional rootvp count, so the filesystem is "busy". and userland libzfs needs to know about snapshots, and include them in the sorted filesystems to unmount. This most likely does not work as getmntany() does not return the mountpoint "path" for snapshots, just that they exist. --- CMakeLists.txt | 1 + cmd/zfs/zfs_main.c | 15 +- include/libzfs.h | 2 +- include/os/windows/spl/sys/debug.h | 4 +- include/os/windows/spl/sys/mount.h | 3 + include/os/windows/spl/sys/vnode.h | 7 + include/os/windows/zfs/sys/zfs_ctldir.h | 17 +- include/os/windows/zfs/sys/zfs_vnops_os.h | 24 +- include/os/windows/zfs/sys/zfs_windows.h | 14 +- lib/libspl/os/windows/getmntany.c | 21 +- lib/libspl/os/windows/posix.c | 9 +- lib/libzfs/os/windows/libzfs_mount_os.c | 142 +-- module/os/windows/driver.c | 2 +- module/os/windows/spl/spl-mount.c | 12 + module/os/windows/spl/spl-vnode.c | 43 +- module/os/windows/zfs/zfs_ctldir.c | 469 +++----- module/os/windows/zfs/zfs_dir.c | 2 +- module/os/windows/zfs/zfs_vfsops.c | 12 +- module/os/windows/zfs/zfs_vnops_os.c | 650 +--------- module/os/windows/zfs/zfs_vnops_windows.c | 342 +++--- module/os/windows/zfs/zfs_vnops_windows_lib.c | 1055 ++++++++++++++--- .../os/windows/zfs/zfs_vnops_windows_mount.c | 15 +- 22 files changed, 1570 insertions(+), 1291 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55c2efeb7b16..5e95afd8daee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ find_package(OpenSSL REQUIRED) # Attempt to simulate scripts/make-gitrev.h and zfs_gitrev.h include(GetGitRevisionDescription) +include(GetGitRevisionDescription) git_describe_working_tree(GIT_GITREV) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/zfs_gitrev.h.win" "${CMAKE_CURRENT_SOURCE_DIR}/include/zfs_gitrev.h" @ONLY) # diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index d02242f3ca22..dfe155aeb94d 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -1453,7 +1453,7 @@ destroy_callback(zfs_handle_t *zhp, void *data) if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { cb->cb_snap_count++; fnvlist_add_boolean(cb->cb_batchedsnaps, name); - zfs_snapshot_unmount_os(zhp, cb->cb_force ? MS_FORCE : 0); + zfs_snapshot_unmount(zhp, cb->cb_force ? MS_FORCE : 0); if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy) { error = destroy_batched(cb); if (error != 0) { @@ -7195,9 +7195,16 @@ share_mount(int op, int argc, char **argv) } while (getmntent(mnttab, &entry) == 0) { + +#ifdef _WIN32 + /* No df/mount command on Windows, show snapshots too */ + if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) + continue; +#else if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || strchr(entry.mnt_special, '@') != NULL) continue; +#endif (void) printf("%-30s %s\n", entry.mnt_special, entry.mnt_mountp); @@ -7219,10 +7226,10 @@ share_mount(int op, int argc, char **argv) } else { if (zfs_get_type(zhp) & ZFS_TYPE_SNAPSHOT) { - ret = zfs_snapshot_mount_os(zhp, options, + ret = zfs_snapshot_mount(zhp, options, flags); } else { - ret = share_mount_one(zhp, op, flags, + ret = share_mount_one(zhp, op, flags, SA_NO_PROTOCOL, B_TRUE, options); zfs_commit_shares(NULL); } @@ -7605,7 +7612,7 @@ unshare_unmount(int op, int argc, char **argv) return (1); if (zfs_get_type(zhp) & ZFS_TYPE_SNAPSHOT) { - ret = zfs_snapshot_unmount_os(zhp, flags); + ret = zfs_snapshot_unmount(zhp, flags); zfs_close(zhp); return (ret); } diff --git a/include/libzfs.h b/include/libzfs.h index 3029532c1c64..a36d08e9fc5b 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -687,7 +687,7 @@ _LIBZFS_H int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, _LIBZFS_H int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props); _LIBZFS_H int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t); -_LIBZFS_H int zfs_rollback_os(zfs_handle_t *); +_LIBZFS_H int zfs_snapshot_unmount(zfs_handle_t *, int); typedef struct renameflags { /* recursive rename */ diff --git a/include/os/windows/spl/sys/debug.h b/include/os/windows/spl/sys/debug.h index f3f3dd8992f8..9d0eb8256224 100644 --- a/include/os/windows/spl/sys/debug.h +++ b/include/os/windows/spl/sys/debug.h @@ -60,8 +60,8 @@ #ifdef _MSC_VER -#define unlikely -#define likely +#define unlikely(X) X +#define likely(X) X #define __maybe_unused #define __printflike(X, Y) #define __unused diff --git a/include/os/windows/spl/sys/mount.h b/include/os/windows/spl/sys/mount.h index ea87a3107fa5..65727c273a66 100644 --- a/include/os/windows/spl/sys/mount.h +++ b/include/os/windows/spl/sys/mount.h @@ -122,6 +122,9 @@ typedef struct mount vfsp_t; int vfs_busy(mount_t *mp, int flags); void vfs_unbusy(mount_t *mp); int vfs_isrdonly(mount_t *mp); +void vfs_setrdonly(mount_t *mp); +void vfs_clearrdonly(mount_t *mp); + void *vfs_fsprivate(mount_t *mp); void vfs_setfsprivate(mount_t *mp, void *mntdata); void vfs_clearflags(mount_t *mp, uint64_t flags); diff --git a/include/os/windows/spl/sys/vnode.h b/include/os/windows/spl/sys/vnode.h index 029e2133d2a1..c2f76622ab65 100644 --- a/include/os/windows/spl/sys/vnode.h +++ b/include/os/windows/spl/sys/vnode.h @@ -92,6 +92,8 @@ struct vnode { uint32_t v_usecount; // Long term holds uint32_t v_type; uint32_t v_unlink; + REPARSE_DATA_BUFFER *v_reparse; + size_t v_reparse_size; uint32_t v_unused; void *v_data; uint64_t v_id; @@ -503,6 +505,11 @@ void cache_purge_negatives(vnode_t *vp); int vnode_removefsref(vnode_t *vp); int vnode_iocount(vnode_t *vp); void vnode_pager_setsize(void *fo, vnode_t *vp, uint64_t size, boolean_t delay); +void vnode_set_reparse(struct vnode *vp, REPARSE_DATA_BUFFER *rpp, size_t size); +ULONG vnode_get_reparse_tag(struct vnode *vp); +int vnode_get_reparse_point(struct vnode *vp, REPARSE_DATA_BUFFER **rpp, + size_t *size); + #define VNODE_READDIR_EXTENDED 1 diff --git a/include/os/windows/zfs/sys/zfs_ctldir.h b/include/os/windows/zfs/sys/zfs_ctldir.h index f24b00b873d5..3543e50425a1 100644 --- a/include/os/windows/zfs/sys/zfs_ctldir.h +++ b/include/os/windows/zfs/sys/zfs_ctldir.h @@ -116,14 +116,17 @@ extern void zfsctl_destroy(zfsvfs_t *); extern struct vnode *zfsctl_root(znode_t *); extern void zfsctl_init(void); extern void zfsctl_fini(void); -extern boolean_t zfsctl_is_node(struct vnode *ip); +extern boolean_t zfsctl_is_node(znode_t *zp); +extern boolean_t zfsctl_is_leafnode(znode_t *zp); + extern boolean_t zfsctl_is_snapdir(struct vnode *ip); extern int zfsctl_fid(struct vnode *ip, fid_t *fidp); /* zfsctl '.zfs' functions */ extern int zfsctl_root_lookup(struct vnode *dip, char *name, - struct vnode **ipp, int flags, cred_t *cr, int *direntflags, + znode_t **zpp, int flags, cred_t *cr, int *direntflags, struct componentname *realpnp); +extern struct vnode *zfs_root_dotdot(struct vnode *vp); /* zfsctl '.zfs/snapshot' functions */ extern int zfsctl_snapdir_lookup(struct vnode *dip, char *name, @@ -143,6 +146,14 @@ extern int zfsctl_snapshot_unmount_delay(spa_t *spa, uint64_t objsetid, int delay); extern int zfsctl_snapdir_vget(struct mount *sb, uint64_t objsetid, int gen, struct vnode **ipp); +extern int zfsctl_mkdir(znode_t *dzp, znode_t **zpp, char *dirname); +extern int zfsctl_set_reparse_point(znode_t *zp, REPARSE_DATA_BUFFER *rdb, + size_t size); +extern int zfsctl_delete_reparse_point(znode_t *zp); +extern ULONG zfsctl_get_reparse_tag(znode_t *zp); +extern int zfsctl_get_reparse_point(znode_t *zp, REPARSE_DATA_BUFFER **buffer, + size_t *size); + /* zfsctl '.zfs/shares' functions */ extern int zfsctl_shares_lookup(struct vnode *dip, char *name, @@ -163,7 +174,7 @@ extern int zfsctl_vnop_reclaim(struct vnop_reclaim_args *); extern void zfs_ereport_snapshot_post(const char *subclass, spa_t *spa, const char *name); -extern void zfsctl_mount_signal(char *, boolean_t); +extern void zfsctl_mount_signal(zfsvfs_t *zfsvfs, char *, boolean_t); /* diff --git a/include/os/windows/zfs/sys/zfs_vnops_os.h b/include/os/windows/zfs/sys/zfs_vnops_os.h index c90e06650e90..1d6aeec3063a 100644 --- a/include/os/windows/zfs/sys/zfs_vnops_os.h +++ b/include/os/windows/zfs/sys/zfs_vnops_os.h @@ -43,6 +43,20 @@ extern "C" { #define KAUTH_WKG_NOBODY 3 #define KAUTH_WKG_EVERYBODY 4 +struct emitdir_ptr { + char *alloc_buf; /* output buffer */ + char *bufptr; /* starts at alloc_buf, increments */ + int bufsize; /* total size of alloc_buf */ + int outcount; /* starts at 0, approaches bufsize */ + ULONG *next_offset; /* ptr to previous nextoffset */ + int last_alignment; /* How much was last alignment */ + uint64_t offset; /* dirindex, 0=".", 1="..", 2=".zfs" */ + int numdirent; + int dirlisttype; /* Win struct to use */ +}; + +typedef struct emitdir_ptr emitdir_ptr_t; + extern int zfs_remove(znode_t *dzp, char *name, cred_t *cr, int flags); extern int zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp, cred_t *cr, int flags, vsecattr_t *vsecp, @@ -74,8 +88,11 @@ extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags, cred_t *cr, int *direntflags, struct componentname *realpnp); extern int zfs_ioctl(vnode_t *vp, ulong_t com, intptr_t data, int flag, cred_t *cred, int *rvalp, caller_context_t *ct); -extern int zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, - zfs_dirlist_t *zccb, int flags, int dirlisttype, int *a_numdirent); +extern int zfs_readdir(vnode_t *vp, emitdir_ptr_t *, cred_t *cr, + zfs_dirlist_t *zccb, int flags); +extern int zfs_readdir_emitdir(zfsvfs_t *zfsvfs, const char *name, + emitdir_ptr_t *ctx, zfs_dirlist_t *zccb, ino64_t objnum); +extern void zfs_readdir_complete(emitdir_ptr_t *ctx); extern int zfs_fsync(znode_t *zp, int syncflag, cred_t *cr); extern int zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, @@ -111,6 +128,9 @@ extern int zpl_xattr_set_sa(struct vnode *vp, const char *name, extern int zpl_xattr_get_sa(struct vnode *vp, const char *name, void *value, size_t size); extern void zfs_zrele_async(znode_t *zp); +extern int zfs_obtain_xattr(znode_t *, const char *, mode_t, cred_t *, + vnode_t **, int); + /* * OSX ACL Helper funcions diff --git a/include/os/windows/zfs/sys/zfs_windows.h b/include/os/windows/zfs/sys/zfs_windows.h index 59f9242bd5a4..7c0a0ac9cffc 100644 --- a/include/os/windows/zfs/sys/zfs_windows.h +++ b/include/os/windows/zfs/sys/zfs_windows.h @@ -110,6 +110,7 @@ extern char *create_options(ULONG options); extern char *create_reply(NTSTATUS, ULONG reply); extern void latency_stats(uint64_t *histo, unsigned int buckets, stat_pair *lat); +extern size_t get_reparse_point_impl(znode_t *zp, char *buffer, size_t outlen); /* zfs_vnop_windows_lib.h */ extern int AsciiStringToUnicodeString(char *in, PUNICODE_STRING out); @@ -124,17 +125,20 @@ extern NTSTATUS zfs_setunlink(FILE_OBJECT *fo, vnode_t *dvp); extern int zfs_find_dvp_vp(zfsvfs_t *, char *, int finalpartmaynotexist, int finalpartmustnotexist, char **lastname, struct vnode **dvpp, struct vnode **vpp, int flags, ULONG options); +extern ULONG get_reparse_tag(znode_t *zp); /* IRP_MJ_SET_INFORMATION helpers */ -extern NTSTATUS file_disposition_information(PDEVICE_OBJECT, PIRP, +extern NTSTATUS set_file_basic_information(PDEVICE_OBJECT, PIRP, PIO_STACK_LOCATION); -extern NTSTATUS file_disposition_information_ex(PDEVICE_OBJECT, PIRP, +extern NTSTATUS set_file_disposition_information(PDEVICE_OBJECT, PIRP, PIO_STACK_LOCATION); -extern NTSTATUS file_endoffile_information(PDEVICE_OBJECT, PIRP, +extern NTSTATUS set_file_disposition_information_ex(PDEVICE_OBJECT, PIRP, PIO_STACK_LOCATION); -extern NTSTATUS file_link_information(PDEVICE_OBJECT, PIRP, +extern NTSTATUS set_file_endoffile_information(PDEVICE_OBJECT, PIRP, PIO_STACK_LOCATION); -extern NTSTATUS file_rename_information(PDEVICE_OBJECT, PIRP, +extern NTSTATUS set_file_link_information(PDEVICE_OBJECT, PIRP, + PIO_STACK_LOCATION); +extern NTSTATUS set_file_rename_information(PDEVICE_OBJECT, PIRP, PIO_STACK_LOCATION); /* IRP_MJ_GET_INFORMATION helpers */ diff --git a/lib/libspl/os/windows/getmntany.c b/lib/libspl/os/windows/getmntany.c index 4224d4ffd90d..6d112cb7df41 100644 --- a/lib/libspl/os/windows/getmntany.c +++ b/lib/libspl/os/windows/getmntany.c @@ -280,7 +280,7 @@ DisplayVolumePaths(char *VolumeName, char *out, int len) NameIdx[0] != '\0'; NameIdx += strlen(NameIdx) + 1) { // printf(" %s", NameIdx); - snprintf(out, len, "%s%s ", out, NameIdx); + snprintf(out, len, "%s%s", out, NameIdx); } // printf("\n"); } @@ -387,14 +387,21 @@ getfsstat(struct statfs *buf, int bufsize, int flags) sizeof (buf->f_mntfromname)); strlcpy(buf->f_fstypename, MNTTYPE_ZFS, sizeof (buf->f_fstypename)); - // FIXME, should be mountpoint! + // If we have a driveletter, use it + // otherwise, lookup mountpoint, and if + // no mountpoint, the device exists, but + // is not mounted. if (strlen(driveletter) > 2) strlcpy(buf->f_mntonname, driveletter, sizeof (buf->f_mntonname)); - else + else { + /* Check if it is mounted? */ +// buf->f_mntonname[0] = 0; strlcpy(buf->f_mntonname, UID->UniqueId, sizeof (buf->f_mntonname)); + } } else { + // Not ZFS - do we care? strlcpy(buf->f_mntfromname, DeviceName, sizeof (buf->f_mntfromname)); strlcpy(buf->f_fstypename, "UKN", @@ -404,10 +411,12 @@ getfsstat(struct statfs *buf, int bufsize, int flags) } UID = NULL; - buf++; // Go to next struct. - bufsize -= sizeof (*buf); + // If it is mounted, add node. + if (buf->f_mntonname[0] != 0) { + buf++; // Go to next struct. + bufsize -= sizeof (*buf); + } } - count++; } while (FindNextVolume(vh, name, sizeof (name)) != 0); diff --git a/lib/libspl/os/windows/posix.c b/lib/libspl/os/windows/posix.c index 4108210638f9..faf2958951c8 100644 --- a/lib/libspl/os/windows/posix.c +++ b/lib/libspl/os/windows/posix.c @@ -195,20 +195,21 @@ statfs(const char *path, struct statfs *buf) if (GetDiskFreeSpaceEx(path, &lpFreeBytesAvailable, &lpTotalNumberOfBytes, - &lpTotalNumberOfFreeBytes)) + &lpTotalNumberOfFreeBytes)) { return (-1); + } #endif DISK_GEOMETRY_EX geometry_ex; HANDLE handle; DWORD len; - int fd = open(path, O_RDONLY | O_BINARY); - handle = (HANDLE) _get_osfhandle(fd); + handle = wosix_open(path, O_RDONLY | O_BINARY); + if (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geometry_ex, sizeof (geometry_ex), &len, NULL)) return (-1); - close(fd); + wosix_close(handle); lbsize = (uint_t)geometry_ex.Geometry.BytesPerSector; buf->f_bsize = lbsize; diff --git a/lib/libzfs/os/windows/libzfs_mount_os.c b/lib/libzfs/os/windows/libzfs_mount_os.c index 12b0a9861f57..ca32d6c06ac0 100644 --- a/lib/libzfs/os/windows/libzfs_mount_os.c +++ b/lib/libzfs/os/windows/libzfs_mount_os.c @@ -105,57 +105,68 @@ do_mount(zfs_handle_t *zhp, const char *dir, const char *optptr, int mflag) (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); (void) strlcpy(zc.zc_value, dir, sizeof (zc.zc_value)); - // If hasprop is set, use 'driveletter' and ignore mountpoint path - // if !hasprop && rootds same - if (hasprop) { - // We just pass "\\??\\X:" to kernel. - snprintf(zc.zc_value, sizeof (zc.zc_value), "\\??\\%c:", - tolower(driveletter[0])); - } else { - // We are to mount with path. Attempt to find parent - // driveletter, if any. Otherwise assume c:/ - driveletter[0] = 'c'; - - if (!ispool) { - char parent[ZFS_MAX_DATASET_NAME_LEN] = ""; - char *slashp; - struct mnttab entry = { 0 }; - - zfs_parent_name(zhp, parent, sizeof (parent)); - - while (strlen(parent) >= 1) { - if ((libzfs_mnttab_find(zhp->zfs_hdl, parent, - &entry) == 0) && - (entry.mnt_mountp[1] == ':')) { - driveletter[0] = entry.mnt_mountp[0]; + + if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) { + // If hasprop is set, use 'driveletter' and ignore mountpoint + // path. if !hasprop && rootds same + if (hasprop) { + // We just pass "\\??\\X:" to kernel. + snprintf(zc.zc_value, sizeof (zc.zc_value), "\\??\\%c:", + tolower(driveletter[0])); + } else { + // We are to mount with path. Attempt to find parent + // driveletter, if any. Otherwise assume c:/ + driveletter[0] = 'c'; + + if (!ispool) { + char parent[ZFS_MAX_DATASET_NAME_LEN] = ""; + char *slashp; + struct mnttab entry = { 0 }; + + zfs_parent_name(zhp, parent, sizeof (parent)); + + while (strlen(parent) >= 1) { + if ((libzfs_mnttab_find(zhp->zfs_hdl, + parent, + &entry) == 0) && + (entry.mnt_mountp[1] == ':')) { + driveletter[0] = + entry.mnt_mountp[0]; #ifdef DEBUG fprintf(stderr, "we think '%s' parent is '%s' and its mounts are: '%s'\r\n", zfs_get_name(zhp), parent, entry.mnt_mountp); fflush(stderr); #endif - break; + break; + } + if ((slashp = strrchr(parent, '/')) == + NULL) + break; + *slashp = '\0'; } - if ((slashp = strrchr(parent, '/')) == NULL) - break; - *slashp = '\0'; + + /* + * We need to skip the parent name part, in mountpoint "dir" here,ie + * if parent is "BOOM/lower" we need to skip to the 3nd slash + * in "/BOOM/lower/newfs" + * So, check if the mounted name is in the string + */ + // "BOOM" -> "/BOOM/" + snprintf(parent, sizeof (parent), "/%s/", + entry.mnt_special); + char *part = strstr(dir, parent); + if (part) dir = &part[strlen(parent) - 1]; } -/* - * We need to skip the parent name part, in mountpoint "dir" here,ie - * if parent is "BOOM/lower" we need to skip to the 3nd slash - * in "/BOOM/lower/newfs" - * So, check if the mounted name is in the string - */ - // "BOOM" -> "/BOOM/" - snprintf(parent, sizeof (parent), "/%s/", - entry.mnt_special); - char *part = strstr(dir, parent); - if (part) dir = &part[strlen(parent) - 1]; + snprintf(zc.zc_value, sizeof (zc.zc_value), + "\\??\\%c:%s", tolower(driveletter[0]), dir); } - - snprintf(zc.zc_value, sizeof (zc.zc_value), "\\??\\%c:%s", - tolower(driveletter[0]), dir); + } else { + // snapshot + snprintf(zc.zc_value, sizeof (zc.zc_value), "\\??\\%s", + dir); + zc.zc_cleanup_fd = MNT_RDONLY; } // Convert Unix slash to Win32 backslash @@ -272,36 +283,27 @@ do_unmount_impl(zfs_handle_t *zhp, const char *mntpt, int flags) // from JUNCTION to a real folder char mtpt_prop[ZFS_MAXPROPLEN]; char driveletter[MAX_PATH]; - verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, - sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0); - verify(zfs_prop_get(zhp, ZFS_PROP_DRIVELETTER, driveletter, - sizeof (driveletter), NULL, NULL, 0, B_FALSE) == 0); - // if mountpoint starts with '/' we assume that it is a path - // to a directory make sure we didn't mount as driveletter - if (mtpt_prop && mtpt_prop[0] == '/' && - (strstr(driveletter, "-") != 0 || - strstr(driveletter, "off") != 0) && - (mntpt && strstr(mntpt, ":\\") == 0)) { - fprintf(stderr, "recreate mountpoint %s\n", mtpt_prop); - fflush(stderr); - BOOL val = RemoveDirectoryA(mtpt_prop); - if (!val) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - fprintf(stderr, - "RemoveDirectoryA false, err %lu\n", - GetLastError()); - fflush(stderr); - } else { - val = CreateDirectoryA(mtpt_prop, NULL); - if (!val) - fprintf(stderr, - "CreateDirectoryA false, err %lu\n", - GetLastError()); - fflush(stderr); - } + // snapshots dont have mountpoint property + if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, + sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0) { + verify(zfs_prop_get(zhp, ZFS_PROP_DRIVELETTER, + driveletter, sizeof (driveletter), NULL, + NULL, 0, B_FALSE) == 0); + // if mountpoint starts with '/' we assume that it is a + // path to a directory make sure we didn't mount as + // driveletter + if (mtpt_prop && mtpt_prop[0] == '/' && + (strstr(driveletter, "-") != 0 || + strstr(driveletter, "off") != 0) && + (mntpt && strstr(mntpt, ":\\") == 0)) { + BOOL val = RemoveDirectoryA(mtpt_prop); + if (!val) { + } else { + val = CreateDirectoryA(mtpt_prop, NULL); + } + } } - } fprintf(stderr, "zunmount(%s,%s) returns %d\n", @@ -463,7 +465,7 @@ zfs_snapshot_unmount(zfs_handle_t *zhp, int flags) char *mountpoint; if (!zfs_is_mounted(zhp, NULL)) { - return (ENOENT); + // return (ENOENT); } mountpoint = zfs_snapshot_mountpoint(zhp); diff --git a/module/os/windows/driver.c b/module/os/windows/driver.c index 9aadd9102433..c9fcb8984aef 100644 --- a/module/os/windows/driver.c +++ b/module/os/windows/driver.c @@ -55,7 +55,7 @@ extern void sysctl_os_fini(void); #endif PDRIVER_OBJECT WIN_DriverObject = NULL; -PDRIVER_UNLOAD STOR_DriverUnload; +PDRIVER_UNLOAD STOR_DriverUnload = NULL; PDRIVER_DISPATCH STOR_MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; wzvolDriverInfo STOR_wzvolDriverInfo; diff --git a/module/os/windows/spl/spl-mount.c b/module/os/windows/spl/spl-mount.c index 07176fa0c8de..df43a0448929 100644 --- a/module/os/windows/spl/spl-mount.c +++ b/module/os/windows/spl/spl-mount.c @@ -46,6 +46,18 @@ vfs_isrdonly(mount_t *mp) return (mp->mountflags & MNT_RDONLY); } +void +vfs_setrdonly(mount_t *mp) +{ + mp->mountflags |= MNT_RDONLY; +} + +void +vfs_clearrdonly(mount_t *mp) +{ + mp->mountflags &= ~MNT_RDONLY; +} + void * vfs_fsprivate(mount_t *mp) { diff --git a/module/os/windows/spl/spl-vnode.c b/module/os/windows/spl/spl-vnode.c index f582775b2e78..968567af7ca3 100644 --- a/module/os/windows/spl/spl-vnode.c +++ b/module/os/windows/spl/spl-vnode.c @@ -852,7 +852,6 @@ spl_vfs_start() spl_skip_getrootdir = 0; } - int vnode_vfsisrdonly(vnode_t *vp) { @@ -1247,6 +1246,9 @@ vnode_create(mount_t *mp, void *v_data, int type, int flags, vp->v_iocount = 1; vp->v_usecount = 0; vp->v_unlink = 0; + vp->v_reparse = NULL; + vp->v_reparse_size = 0; + atomic_inc_64(&vnode_active); list_link_init(&vp->v_list); @@ -1630,6 +1632,8 @@ vflush(struct mount *mp, struct vnode *skipvp, int flags) avl_remove(&rvp->v_fileobjects, node); kmem_free(node, sizeof (*node)); } + } else { + rvp->v_age = gethrtime() - SEC2NSEC(6); } rvp->v_flags |= VNODE_DEAD; rvp->v_data = NULL; @@ -1637,6 +1641,9 @@ vflush(struct mount *mp, struct vnode *skipvp, int flags) } mutex_exit(&vnode_all_list_lock); + if (FORCECLOSE) + vnode_drain_delayclose(1); + xprintf("vflush end: deadlisted %d nodes\n", deadlist); return (0); @@ -1994,6 +2001,40 @@ vnode_clear_easize(struct vnode *vp) vp->v_flags &= ~VNODE_EASIZE; } +void +vnode_set_reparse(struct vnode *vp, REPARSE_DATA_BUFFER *rpp, size_t size) +{ + if (vp->v_reparse != NULL && size > 0) + kmem_free(vp->v_reparse, vp->v_reparse_size); + vp->v_reparse = NULL; + vp->v_reparse_size = 0; + + if (rpp != NULL && size > 0) { + vp->v_reparse = kmem_alloc(size, KM_SLEEP); + vp->v_reparse_size = size; + memcpy(vp->v_reparse, rpp, size); + } +} + +ULONG +vnode_get_reparse_tag(struct vnode *vp) +{ + return (vp->v_reparse ? vp->v_reparse->ReparseTag : 0); +} + +int +vnode_get_reparse_point(struct vnode *vp, REPARSE_DATA_BUFFER **rpp, + size_t *size) +{ + if (vp->v_reparse == NULL || vp->v_reparse_size == 0) + return (ENOENT); + ASSERT3P(rpp, !=, NULL); + ASSERT3P(size, !=, NULL); + *rpp = vp->v_reparse; + *size = vp->v_reparse_size; + return (0); +} + #ifdef DEBUG_IOCOUNT void vnode_check_iocount(void) diff --git a/module/os/windows/zfs/zfs_ctldir.c b/module/os/windows/zfs/zfs_ctldir.c index fa76e2a22449..07e1dac44c3c 100644 --- a/module/os/windows/zfs/zfs_ctldir.c +++ b/module/os/windows/zfs/zfs_ctldir.c @@ -137,6 +137,7 @@ static list_t zfsctl_unmount_list; struct zfsctl_unmount_delay { char *se_name; /* full snapshot name */ spa_t *se_spa; /* pool spa */ + struct vnode *se_vnode; uint64_t se_objsetid; /* snapshot objset id */ time_t se_time; list_node_t se_nodelink; @@ -148,9 +149,23 @@ typedef struct zfsctl_unmount_delay zfsctl_unmount_delay_t; * Check if the given vnode is a part of the virtual .zfs directory. */ boolean_t -zfsctl_is_node(struct vnode *ip) +zfsctl_is_node(znode_t *zp) { - return (ITOZ(ip)->z_is_ctldir); + return (zp->z_is_ctldir); +} + +/* + * /.zfs/snapshot/nameofsnapshot/ + * -------------------^ + */ +boolean_t +zfsctl_is_leafnode(znode_t *zp) +{ + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + return (zp->z_is_ctldir && zp->z_id != ZFSCTL_INO_ROOT && + zp->z_id != ZFSCTL_INO_SNAPDIR && + (zp->z_id >= zfsvfs->z_ctldir_startid) && + (zp->z_id <= ZFSCTL_INO_SNAPDIRS)); } typedef int (**vnode_operations)(void *); @@ -193,6 +208,7 @@ zfsctl_vnode_alloc(zfsvfs_t *zfsvfs, uint64_t id, zp->z_mode = 0; zp->z_sync_cnt = 0; zp->z_gen = 0; + zp->z_links = 2; zp->z_mode = (S_IFDIR | (S_IRWXU|S_IRWXG|S_IRWXO)); zp->z_uid = 0; zp->z_gid = 0; @@ -225,18 +241,21 @@ zfsctl_vnode_alloc(zfsvfs_t *zfsvfs, uint64_t id, zp->z_vid = vnode_vid(vp); zp->z_vnode = vp; + // Update startid before buildpath. + if (id < zfsvfs->z_ctldir_startid) + zfsvfs->z_ctldir_startid = id; + // Build fullpath string here, for Notifications & set_name_information if (zfs_build_path(zp, NULL, &zp->z_name_cache, - &zp->z_name_len, &zp->z_name_offset) == -1) + &zp->z_name_len, &zp->z_name_offset) == -1) { dprintf("%s: failed to build fullpath\n", __func__); + } zfs_set_security(vp, NULL); mutex_enter(&zfsvfs->z_znodes_lock); list_insert_tail(&zfsvfs->z_all_znodes, zp); membar_producer(); - if (id < zfsvfs->z_ctldir_startid) - zfsvfs->z_ctldir_startid = id; mutex_exit(&zfsvfs->z_znodes_lock); return (vp); @@ -311,7 +330,6 @@ zfsctl_destroy(zfsvfs_t *zfsvfs) /* Because tagged VSYSTEM, we manually call recycle */ zfsvfs->z_ctldir->v_flags &= ~VNODE_MARKROOT; // Hack VN_RELE(zfsvfs->z_ctldir); - vnode_recycle(zfsvfs->z_ctldir); } zfsvfs->z_ctldir = NULL; } @@ -324,47 +342,50 @@ zfsctl_destroy(zfsvfs_t *zfsvfs) struct vnode * zfsctl_root(znode_t *zp) { - ASSERT(zfs_has_ctldir(zp)); VN_HOLD(ZTOZSB(zp)->z_ctldir); return (ZTOZSB(zp)->z_ctldir); } - +/* Given a entry in .zfs, find its parent */ struct vnode * zfs_root_dotdot(struct vnode *vp) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = ZTOZSB(zp); - znode_t *rootzp = NULL; + znode_t *retzp = NULL; struct vnode *retvp = NULL; + int error = 0; dprintf("%s: for id %llu\n", __func__, zp->z_id); if (zp->z_id == ZFSCTL_INO_ROOT) - zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); + error = zfs_zget(zfsvfs, zfsvfs->z_root, &retzp); else if (zp->z_id == ZFSCTL_INO_SNAPDIR) retvp = zfsctl_root(zp); else retvp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIR, - "snapshot"); + ZFS_SNAPDIR_NAME); - if (rootzp != NULL) - retvp = ZTOV(rootzp); + if (retzp != NULL) + return (ZTOV(retzp)); + if (retvp != NULL) + return (retvp); - return (retvp); + return (NULL); } /* * Special case the handling of "..". */ int -zfsctl_root_lookup(struct vnode *dvp, char *name, struct vnode **vpp, +zfsctl_root_lookup(struct vnode *dvp, char *name, znode_t **zpp, int flags, cred_t *cr, int *direntflags, struct componentname *realpnp) { znode_t *dzp = VTOZ(dvp); zfsvfs_t *zfsvfs = ZTOZSB(dzp); int error = 0; uint64_t id; + struct vnode *vp; dprintf("%s: '%s'\n", __func__, name); @@ -372,21 +393,22 @@ zfsctl_root_lookup(struct vnode *dvp, char *name, struct vnode **vpp, return (error); if (strcmp(name, "..") == 0) { - *vpp = zfs_root_dotdot(dvp); + vp = zfs_root_dotdot(dvp); } else if (strcmp(name, ZFS_SNAPDIR_NAME) == 0) { - *vpp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIR, + vp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIR, name); } else { error = dmu_snapshot_lookup(zfsvfs->z_os, name, &id); if (error != 0) goto out; - *vpp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIRS - id, + vp = zfsctl_vnode_lookup(zfsvfs, ZFSCTL_INO_SNAPDIRS - id, name); } - if (*vpp == NULL) { + if (vp == NULL) error = SET_ERROR(ENOENT); - } + else + *zpp = VTOZ(vp); out: zfs_exit(zfsvfs, FTAG); @@ -394,136 +416,12 @@ zfsctl_root_lookup(struct vnode *dvp, char *name, struct vnode **vpp, return (error); } -// int zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) -#if 0 -int -zfsctl_vnop_lookup(struct vnop_lookup_args *ap) -#if 0 - struct vnop_lookup_args { - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - vfs_context_t a_context; - }; -#endif -{ - int direntflags = 0; - int error; - struct componentname *cnp = ap->a_cnp; - char *filename = NULL; - int filename_num_bytes = 0; - cred_t *cr = (cred_t *)vfs_context_ucred((ap)->a_context); - - /* - * Darwin uses namelen as an optimisation, for example it can be - * set to 5 for the string "alpha/beta" to look up "alpha". In this - * case we need to copy it out to null-terminate. - */ - if (cnp->cn_nameptr[cnp->cn_namelen] != 0) { - filename_num_bytes = cnp->cn_namelen + 1; - filename = (char *)kmem_alloc(filename_num_bytes, KM_SLEEP); - memcpy(filename, cnp->cn_nameptr, cnp->cn_namelen); - filename[cnp->cn_namelen] = '\0'; - } - - error = zfsctl_root_lookup(ap->a_dvp, - filename ? filename : cnp->cn_nameptr, - ap->a_vpp, /* flags */ 0, cr, &direntflags, NULL); - - /* If we are to create a directory, change error code for XNU */ - if ((error == ENOENT) && - (cnp->cn_flags & ISLASTCN)) { - if ((cnp->cn_nameiop == CREATE) || - (cnp->cn_nameiop == RENAME)) - error = EJUSTRETURN; - } - - if (filename != NULL) - kmem_free(filename, filename_num_bytes); - - return (error); -} -#endif - - -static int zfsctl_dir_emit(const char *name, uint64_t id, enum vtype type, - struct vnop_readdir_args *ap, uint64_t **next) -{ -#if 0 - struct uio *uio = ap->a_uio; - boolean_t extended = (ap->a_flags & VNODE_READDIR_EXTENDED); - struct direntry *eodp; /* Extended */ - struct dirent *odp; /* Standard */ - int namelen; - void *buf; - int error = 0; - ushort_t reclen; - - dprintf("%s '%s'\n", __func__, name); - - namelen = strlen(name); - reclen = DIRENT_RECLEN(namelen, extended); - - if (reclen > uio_resid(uio)) - return (EINVAL); - - buf = kmem_zalloc(reclen, KM_SLEEP); - - if (extended) { - eodp = buf; - - /* - * NOTE: d_seekoff is the offset for the *next* entry - - * so poke in the previous struct with this id - */ - eodp->d_seekoff = uio_offset(uio) + 1; - - eodp->d_ino = id; - eodp->d_type = type; - - (void) memcpy(eodp->d_name, name, namelen + 1); - eodp->d_namlen = namelen; - eodp->d_reclen = reclen; - - } else { - odp = buf; - - odp->d_ino = id; - odp->d_type = type; - (void) memcpy(odp->d_name, name, namelen + 1); - odp->d_namlen = namelen; - odp->d_reclen = reclen; - - } - - /* Copyout this entry */ - error = uiomove(buf, (long)reclen, UIO_READ, uio); - - kmem_free(buf, reclen); - return (error); -#endif - return (-1); -} - int -zfsctl_vnop_readdir_root(struct vnop_readdir_args *ap) -#if 0 - struct vnop_readdir_args { - struct vnode a_vp; - struct uio *a_uio; - int a_flags; - int *a_eofflag; - int *a_numdirent; - vfs_context_t a_context; - }; -#endif +zfsctl_vnop_readdir_root(vnode_t *vp, emitdir_ptr_t *ctx, cred_t *cr, + zfs_dirlist_t *zccb, int flags) { int error = 0; - uint64_t *next = NULL; - int entries = 0; - uint64_t offset; - struct uio *uio = ap->a_uio; - znode_t *zp = VTOZ(ap->a_vp); + znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; dprintf("%s\n", __func__); @@ -531,82 +429,69 @@ zfsctl_vnop_readdir_root(struct vnop_readdir_args *ap) if ((error = zfs_enter(zfsvfs, FTAG)) != 0) return (error); - *ap->a_numdirent = 0; - - offset = zfs_uio_offset(uio); + ctx->numdirent = 0; - while (offset < 3 && error == 0) { + while (ctx->offset < 3 && error == 0) { - switch (offset) { + switch (ctx->offset) { case 0: /* "." */ - error = zfsctl_dir_emit(".", ZFSCTL_INO_ROOT, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, ".", + ctx, zccb, ZFSCTL_INO_ROOT); break; case 1: /* ".." */ - error = zfsctl_dir_emit("..", 2, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, "..", + ctx, zccb, zfsvfs->z_root); break; case 2: - error = zfsctl_dir_emit(ZFS_SNAPDIR_NAME, - ZFSCTL_INO_SNAPDIR, DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, ZFS_SNAPDIR_NAME, + ctx, zccb, ZFSCTL_INO_SNAPDIR); break; } - if (error == ENOENT) { - dprintf("end of snapshots reached\n"); + if (error == 0) { + ctx->numdirent++; + } else if (error == ENOSPC) { /* stop iterating */ break; + } else { + /* other error, skip over entry */ + error = 0; } - if (error != 0) { - dprintf("emit error\n"); - break; - } + ctx->offset++; + } - entries++; - offset++; - zfs_uio_setoffset(uio, offset); + if (error == ENOENT) { + dprintf("end of snapshots reached\n"); + } + + if (error != 0) { + dprintf("emit error\n"); } - zfs_uio_setoffset(uio, offset); + zfs_readdir_complete(ctx); /* Finished without error? Set EOF */ - if (offset >= 3 && error == 0) { - *ap->a_eofflag = 1; + if (ctx->offset >= 3 && error == 0) { + error = ENOENT; dprintf("Setting eof\n"); } - *ap->a_numdirent = entries; - dprintf("Returning %d entries\n", entries); - zfs_exit(zfsvfs, FTAG); return (error); } int -zfsctl_vnop_readdir_snapdir(struct vnop_readdir_args *ap) -#if 0 - struct vnop_readdir_args { - struct vnode a_vp; - struct uio *a_uio; - int a_flags; - int *a_eofflag; - int *a_numdirent; - vfs_context_t a_context; - }; -#endif +zfsctl_vnop_readdir_snapdir(vnode_t *vp, emitdir_ptr_t *ctx, cred_t *cr, + zfs_dirlist_t *zccb, int flags) { int error = 0; - uint64_t *next = NULL; - int entries = 0; - uint64_t offset; - struct uio *uio = ap->a_uio; boolean_t case_conflict; uint64_t id; char snapname[MAXNAMELEN]; - znode_t *zp = VTOZ(ap->a_vp); + znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; dprintf("%s\n", __func__); @@ -614,35 +499,32 @@ zfsctl_vnop_readdir_snapdir(struct vnop_readdir_args *ap) if ((error = zfs_enter(zfsvfs, FTAG)) != 0) return (error); - *ap->a_numdirent = 0; - - offset = zfs_uio_offset(uio); - while (error == 0) { - switch (offset) { + switch (ctx->offset) { case 0: /* "." */ - error = zfsctl_dir_emit(".", ZFSCTL_INO_SNAPDIR, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, ".", + ctx, zccb, ZFSCTL_INO_SNAPDIR); break; case 1: /* ".." */ - error = zfsctl_dir_emit("..", ZFSCTL_INO_ROOT, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, "..", + ctx, zccb, ZFSCTL_INO_ROOT); break; default: dsl_pool_config_enter(dmu_objset_pool(zfsvfs->z_os), FTAG); error = dmu_snapshot_list_next(zfsvfs->z_os, - MAXNAMELEN, snapname, &id, &offset, &case_conflict); + MAXNAMELEN, snapname, &id, &ctx->offset, + &case_conflict); dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), FTAG); if (error) break; - error = zfsctl_dir_emit(snapname, - ZFSCTL_INO_SHARES - id, DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, snapname, + ctx, zccb, ZFSCTL_INO_SNAPDIRS - id); break; } @@ -651,22 +533,10 @@ zfsctl_vnop_readdir_snapdir(struct vnop_readdir_args *ap) break; } - entries++; - offset++; - zfs_uio_setoffset(uio, offset); - } - - zfs_uio_setoffset(uio, offset); - - /* Finished without error? Set EOF */ - if (error == ENOENT) { - *ap->a_eofflag = 1; - dprintf("Setting eof\n"); - error = 0; + ctx->offset++; } - *ap->a_numdirent = entries; - dprintf("Returning %d entries\n", entries); + zfs_readdir_complete(ctx); zfs_exit(zfsvfs, FTAG); @@ -676,47 +546,27 @@ zfsctl_vnop_readdir_snapdir(struct vnop_readdir_args *ap) /* We need to spit out a valid "." ".." entries for mount to work */ int -zfsctl_vnop_readdir_snapdirs(struct vnop_readdir_args *ap) -#if 0 - struct vnop_readdir_args { - struct vnode a_vp; - struct uio *a_uio; - int a_flags; - int *a_eofflag; - int *a_numdirent; - vfs_context_t a_context; - }; -#endif +zfsctl_vnop_readdir_snapdirs(vnode_t *vp, emitdir_ptr_t *ctx, cred_t *cr, + zfs_dirlist_t *zccb, int flags) { int error = 0; - uint64_t *next = NULL; - int entries = 0; - uint64_t offset; - struct uio *uio = ap->a_uio; - znode_t *zp = VTOZ(ap->a_vp); + znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; if ((error = zfs_enter(zfsvfs, FTAG)) != 0) return (error); - *ap->a_numdirent = 0; - - offset = zfs_uio_offset(uio); - - dprintf("%s: for id %llu: offset %llu\n", __func__, - zp->z_id, offset); - while (error == 0) { - switch (offset) { + switch (ctx->offset) { case 0: /* "." */ - error = zfsctl_dir_emit(".", ZFSCTL_INO_SNAPDIR, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, ".", + ctx, zccb, zp->z_id); break; case 1: /* ".." */ - error = zfsctl_dir_emit("..", ZFSCTL_INO_ROOT, - DT_DIR, ap, &next); + error = zfs_readdir_emitdir(zfsvfs, "..", + ctx, zccb, ZFSCTL_INO_SNAPDIR); break; default: @@ -729,54 +579,38 @@ zfsctl_vnop_readdir_snapdirs(struct vnop_readdir_args *ap) break; } - entries++; - offset++; - zfs_uio_setoffset(uio, offset); + ctx->offset++; } - zfs_uio_setoffset(uio, offset); - - /* Finished without error? Set EOF */ - if (error == ENOENT) { - *ap->a_eofflag = 1; - dprintf("Setting eof\n"); - error = 0; - } - - *ap->a_numdirent = entries; - dprintf("Returning %d entries\n", entries); - zfs_exit(zfsvfs, FTAG); + zfs_readdir_complete(ctx); + return (error); } int -zfsctl_vnop_readdir(struct vnop_readdir_args *ap) -#if 0 - struct vnop_readdir_args { - struct vnode a_vp; - struct uio *a_uio; - int a_flags; - int *a_eofflag; - int *a_numdirent; - vfs_context_t a_context; - }; -#endif +zfsctl_readdir(vnode_t *vp, emitdir_ptr_t *ctx, cred_t *cr, + zfs_dirlist_t *zccb, int flags) { - znode_t *zp = VTOZ(ap->a_vp); + znode_t *zp = VTOZ(vp); dprintf("%s\n", __func__); /* Which directory are we to output? */ switch (zp->z_id) { - case ZFSCTL_INO_ROOT: - return (zfsctl_vnop_readdir_root(ap)); - case ZFSCTL_INO_SNAPDIR: - return (zfsctl_vnop_readdir_snapdir(ap)); - default: - return (zfsctl_vnop_readdir_snapdirs(ap)); + case ZFSCTL_INO_ROOT: + return (zfsctl_vnop_readdir_root(vp, ctx, cr, + zccb, flags)); + case ZFSCTL_INO_SNAPDIR: + return (zfsctl_vnop_readdir_snapdir(vp, ctx, cr, + zccb, flags)); + default: + return (zfsctl_vnop_readdir_snapdirs(vp, ctx, cr, + zccb, flags)); + break; } + panic("%s: weird snapshot state\n", __func__); return (EINVAL); } @@ -1117,7 +951,7 @@ zfsctl_snapshot_mount(struct vnode *vp, int flags) /* Called whenever zfs_vfs_mount() is called with a snapshot */ void -zfsctl_mount_signal(char *osname, boolean_t mounting) +zfsctl_mount_signal(zfsvfs_t *zfsvfs, char *osname, boolean_t mounting) { zfsctl_mounts_waiting_t *zcm; @@ -1161,14 +995,17 @@ zfsctl_mount_signal(char *osname, boolean_t mounting) for (zcu = list_head(&zfsctl_unmount_list); zcu != NULL; zcu = list_next(&zfsctl_unmount_list, zcu)) { - if (strcmp(osname, zcu->se_name) == 0) { - list_remove(&zfsctl_unmount_list, zcu); - kmem_strfree(zcu->se_name); - kmem_free(zcu, sizeof (zfsctl_unmount_delay_t)); + if (strcmp(osname, zcu->se_name) == 0) break; - } } mutex_exit(&zfsctl_unmount_list_lock); + + + if (zcu != NULL) { + list_remove(&zfsctl_unmount_list, zcu); + kmem_strfree(zcu->se_name); + kmem_free(zcu, sizeof (zfsctl_unmount_delay_t)); + } } } @@ -1183,6 +1020,7 @@ zfsctl_snapshot_unmount_node(struct vnode *vp, const char *full_name, if (zp == NULL) return (ENOENT); + return (0); zfsvfs_t *zfsvfs = zp->z_zfsvfs; int ret = ENOENT; @@ -1307,6 +1145,69 @@ zfsctl_snapshot_unmount(const char *snapname, int flags) return (0); } +int +zfsctl_mkdir(znode_t *dzp, znode_t **zpp, char *dirname) +{ + int error; + error = zfsctl_root_lookup(ZTOV(dzp), dirname, zpp, 0, NULL, + NULL, NULL); + if (error == 0) { + ZTOV(*zpp)->v_unlink = 0; + } + return (error); +} + +int +zfsctl_set_reparse_point(znode_t *zp, REPARSE_DATA_BUFFER *rdb, size_t size) +{ + if (!zfsctl_is_leafnode(zp)) + return (STATUS_INVALID_PARAMETER); + + zp->z_pflags |= ZFS_REPARSE; + vnode_set_reparse(ZTOV(zp), rdb, size); + return (STATUS_SUCCESS); +} + +int +zfsctl_delete_reparse_point(znode_t *zp) +{ + if (!zfsctl_is_leafnode(zp)) + return (STATUS_INVALID_PARAMETER); + + zp->z_pflags &= ~ZFS_REPARSE; + vnode_set_reparse(ZTOV(zp), NULL, 0); + + return (STATUS_SUCCESS); +} + +// make Tag vs Point call, to return just ULONG tag or copy over point +ULONG +zfsctl_get_reparse_tag(znode_t *zp) +{ + + if (!zfsctl_is_leafnode(zp)) + return (0); + + if (!(zp->z_pflags & ZFS_REPARSE)) + return (0); + + return (vnode_get_reparse_tag(ZTOV(zp))); +} + +int +zfsctl_get_reparse_point(znode_t *zp, REPARSE_DATA_BUFFER **buffer, + size_t *size) +{ + + if (!zfsctl_is_leafnode(zp)) + return (STATUS_NOT_A_REPARSE_POINT); + + if (!(zp->z_pflags & ZFS_REPARSE)) + return (STATUS_NOT_A_REPARSE_POINT); + + return (vnode_get_reparse_point(ZTOV(zp), buffer, size)); +} + int zfsctl_vnop_mkdir(struct vnop_mkdir_args *ap) #if 0 @@ -1346,8 +1247,8 @@ zfsctl_vnop_mkdir(struct vnop_mkdir_args *ap) if (error != 0) goto out; - error = zfsctl_root_lookup(ap->a_dvp, ap->a_cnp->cn_nameptr, - ap->a_vpp, 0, cr, NULL, NULL); + // error = zfsctl_root_lookup(ap->a_dvp, ap->a_cnp->cn_nameptr, + // ap->a_vpp, 0, cr, NULL, NULL); } out: diff --git a/module/os/windows/zfs/zfs_dir.c b/module/os/windows/zfs/zfs_dir.c index 1d5708f308d6..b95b8df3cc84 100644 --- a/module/os/windows/zfs/zfs_dir.c +++ b/module/os/windows/zfs/zfs_dir.c @@ -406,7 +406,7 @@ zfs_dirlook(znode_t *dzp, char *name, znode_t **zpp, int flags, if (parent == dzp->z_id && zfsvfs->z_parent != zfsvfs) { error = zfsctl_root_lookup(zfsvfs->z_parent->z_ctldir, - "snapshot", &vp, 0, kcred, NULL, NULL); + ZFS_SNAPDIR_NAME, &vp, 0, kcred, NULL, NULL); if (error == 0) *zpp = VTOZ(vp); return (error); diff --git a/module/os/windows/zfs/zfs_vfsops.c b/module/os/windows/zfs/zfs_vfsops.c index 00576636e831..4f5af5938911 100644 --- a/module/os/windows/zfs/zfs_vfsops.c +++ b/module/os/windows/zfs/zfs_vfsops.c @@ -103,10 +103,6 @@ static int hardlinks_compare_linkid(const void *arg1, const void *arg2) return (0); } -extern int -zfs_obtain_xattr(znode_t *, const char *, mode_t, cred_t *, vnode_t **, int); - - /* * We need to keep a count of active fs's. * This is necessary to prevent our kext @@ -1027,6 +1023,7 @@ zfs_domount(struct mount *vfsp, dev_t mount_dev, char *osname, NULL))) goto out; xattr_changed_cb(zfsvfs, pval); + zfsvfs->z_rdonly = B_TRUE; zfsvfs->z_issnap = B_TRUE; zfsvfs->z_os->os_sync = ZFS_SYNC_DISABLED; @@ -1034,7 +1031,7 @@ zfs_domount(struct mount *vfsp, dev_t mount_dev, char *osname, dmu_objset_set_user(zfsvfs->z_os, zfsvfs); mutex_exit(&zfsvfs->z_os->os_user_ptr_lock); - zfsctl_mount_signal(osname, B_TRUE); + zfsctl_mount_signal(zfsvfs, osname, B_TRUE); } else { if ((error = zfsvfs_setup(zfsvfs, B_TRUE))) @@ -1644,10 +1641,9 @@ zfs_vfs_unmount(struct mount *mp, int mntflags, vfs_context_t context) /* If we are ourselves a snapshot */ if (dmu_objset_is_snapshot(zfsvfs->z_os)) { /* Wake up anyone waiting for unmount */ - zfsctl_mount_signal(osname, B_FALSE); + zfsctl_mount_signal(zfsvfs, osname, B_FALSE); } - /* * Last chance to dump unreferenced system files. */ @@ -1876,7 +1872,7 @@ zfs_vfs_vget(struct mount *mp, ino64_t ino, vnode_t **vpp, zp = list_next(&zfsvfs->z_all_znodes, zp)) { if (zp->z_id == ino) break; - if (zp->z_id == ZFSCTL_INO_SHARES - ino) + if (zp->z_id == ZFSCTL_INO_SNAPDIRS - ino) break; } mutex_exit(&zfsvfs->z_znodes_lock); diff --git a/module/os/windows/zfs/zfs_vnops_os.c b/module/os/windows/zfs/zfs_vnops_os.c index 39129dc8db07..6e9ca7e0fa4d 100644 --- a/module/os/windows/zfs/zfs_vnops_os.c +++ b/module/os/windows/zfs/zfs_vnops_os.c @@ -358,6 +358,11 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, zfsvfs_t *zfsvfs = ZTOZSB(zdp); int error = 0; + /* .zfs lookups come in here in Windows */ + if (zfsctl_is_node(zdp)) + return (zfsctl_root_lookup(ZTOV(zdp), nm, + zpp, flags, cr, direntflags, realpnp)); + if ((error = zfs_enter_verify_zp(zfsvfs, zdp, FTAG)) != 0) return (error); @@ -966,6 +971,10 @@ zfs_mkdir(znode_t *dzp, char *dirname, vattr_t *vap, znode_t **zpp, if (dirname == NULL) return (SET_ERROR(EINVAL)); + // If it is a snapshot/name mkdir, just pretend + if (zfsctl_is_node(dzp)) + return (zfsctl_mkdir(dzp, zpp, dirname)); + if ((error = zfs_enter_verify_zp(zfsvfs, dzp, FTAG)) != 0) return (error); zilog = zfsvfs->z_log; @@ -1254,46 +1263,31 @@ zfs_rmdir(znode_t *dzp, char *name, znode_t *cwd, cred_t *cr, * we use the offset 2 for the '.zfs' directory. */ int -zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, - int flags, int dirlisttype, int *a_numdirent) +zfs_readdir(vnode_t *vp, emitdir_ptr_t *ctx, cred_t *cr, zfs_dirlist_t *zccb, + int flags) { int error = 0; - (void) cr; znode_t *zp = VTOZ(vp); - znode_t *tzp; - iovec_t *iovp; - FILE_FULL_DIR_INFORMATION *eodp = NULL; zfsvfs_t *zfsvfs = zp->z_zfsvfs; objset_t *os; - caddr_t outbuf; - size_t bufsize; zap_cursor_t zc; zap_attribute_t zap; - uint_t bytes_wanted; - uint64_t offset; /* must be unsigned; checks for < 1 */ uint64_t parent; - int local_eof; - int outcount; uint8_t prefetch; - boolean_t check_sysattrs; uint8_t type; - int numdirent = 0; - char *bufptr; - void *nameptr = NULL; - ULONG namelenholder = 0; - uint32_t *eofp = &zccb->dir_eof; - int last_alignment = 0; int skip_this_entry; - int structsize = 0; int flag_index_specified = flags & SL_INDEX_SPECIFIED ? 1 : 0; int flag_restart_scan = flags & SL_RESTART_SCAN ? 1 : 0; int flag_return_single_entry = flags & SL_RETURN_SINGLE_ENTRY ? 1 : 0; - FILE_DIRECTORY_INFORMATION *fdi; dprintf("+zfs_readdir: Index %d, Restart %d, Single %d\n", flag_index_specified, flag_restart_scan, flag_return_single_entry); + /* If we are listing a ".zfs" directory, call out */ + if (zfsctl_is_node(zp)) + return (zfsctl_readdir(vp, ctx, cr, zccb, flags)); + if ((error = zfs_enter_verify_zp(zfsvfs, zp, FTAG)) != 0) return (error); @@ -1303,18 +1297,11 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, return (error); } - /* - * If we are not given an eof variable, - * use a local one. - */ - if (eofp == NULL) - eofp = &local_eof; - /* * Check for valid iov_len. */ - // if (zfs_uio_curriovlen(uio) <= 0) { - if (zfs_uio_resid(uio) <= 0) { + // if (zfs_uio_resid(uio) <= 0) { + if (ctx->bufsize <= 0) { zfs_exit(zfsvfs, FTAG); return ((EINVAL)); } @@ -1322,13 +1309,16 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, /* * Quit if directory has been removed (posix) */ - if ((*eofp = zp->z_unlinked) != 0) { + if (zp->z_unlinked != 0) { + zccb->dir_eof = B_TRUE; zfs_exit(zfsvfs, FTAG); return (0); } // Make sure the dirlist type is a valid one - switch (dirlisttype) { + // This isn't needed, just a nicer failure until + // all types are handled. + switch (ctx->dirlisttype) { case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: case FileBothDirectoryInformation: @@ -1341,20 +1331,19 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, break; default: dprintf("%s: ** Directory type %d not handled!\n", - __func__, dirlisttype); + __func__, ctx->dirlisttype); zfs_exit(zfsvfs, FTAG); return ((EINVAL)); } error = 0; os = zfsvfs->z_os; - offset = zfs_uio_offset(uio); prefetch = zp->z_zn_prefetch; /* * Initialize the iterator cursor. */ - if (offset <= 3) { + if (ctx->offset <= 3) { /* * Start iteration from the beginning of the directory. */ @@ -1363,45 +1352,20 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, /* * The offset is a serialized cursor. */ - zap_cursor_init_serialized(&zc, os, zp->z_id, offset); + zap_cursor_init_serialized(&zc, os, zp->z_id, ctx->offset); } /* * Get space to change directory entries into fs independent format. */ - // bytes_wanted = zfs_uio_curriovlen(uio); - bytes_wanted = zfs_uio_resid(uio); - bufsize = (size_t)bytes_wanted; - outbuf = kmem_zalloc(bufsize, KM_SLEEP); // ZERO? - bufptr = (char *)outbuf; - - /* - * If this VFS supports the system attribute view interface; and - * we're looking at an extended attribute directory; and we care - * about normalization conflicts on this vfs; then we must check - * for normalization conflicts with the sysattr name space. - */ -#ifdef TODO - check_sysattrs = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) && - (vp->v_flag & V_XATTRDIR) && zfsvfs->z_norm && - (flags & V_RDDIR_ENTFLAGS); -#else - check_sysattrs = 0; -#endif /* * Transform to file-system independent format */ // zfsvfs->z_show_ctldir = ZFS_SNAPDIR_VISIBLE; - outcount = 0; - while (outcount < bytes_wanted) { + while (ctx->outcount < ctx->bufsize) { ino64_t objnum; - ushort_t reclen, rawsize; - uint64_t *next = NULL; - size_t namelen; - int force_formd_normalized_output; - size_t nfdlen; skip_this_entry = 0; @@ -1409,19 +1373,18 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, /* * Special case `.', `..', and `.zfs'. */ - if (offset == 0) { + if (ctx->offset == 0) { (void) strlcpy(zap.za_name, ".", MAXNAMELEN); zap.za_normalization_conflict = 0; - objnum = (zp->z_id == zfsvfs->z_root) ? 2 : zp->z_id; + objnum = zp->z_id; type = DT_DIR; - } else if (offset == 1) { + } else if (ctx->offset == 1) { (void) strlcpy(zap.za_name, "..", MAXNAMELEN); zap.za_normalization_conflict = 0; - objnum = (parent == zfsvfs->z_root) ? 2 : parent; - objnum = (zp->z_id == zfsvfs->z_root) ? 1 : objnum; + objnum = parent; type = DT_DIR; #if 1 - } else if (offset == 2 && zfs_show_ctldir(zp)) { + } else if (ctx->offset == 2 && zfs_show_ctldir(zp)) { (void) strlcpy(zap.za_name, ZFS_CTLDIR_NAME, MAXNAMELEN); zap.za_normalization_conflict = 0; @@ -1434,7 +1397,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, * Grab next entry. */ if ((error = zap_cursor_retrieve(&zc, &zap))) { - if ((*eofp = (error == ENOENT)) != 0) + if (error == ENOENT) break; else goto update; @@ -1445,569 +1408,59 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, zfs_dirlist_t *zccb, cmn_err(CE_WARN, "zap_readdir: bad directory " "entry, obj = %lld, offset = %lld\n", (u_longlong_t)zp->z_id, - (u_longlong_t)offset); + (u_longlong_t)ctx->offset); error = SET_ERROR(ENXIO); goto update; } objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); - /* - * MacOS X can extract the object type here such as: - * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer); - */ type = ZFS_DIRENT_TYPE(zap.za_first_integer); - if (check_sysattrs && !zap.za_normalization_conflict) { -#ifdef TODO - zap.za_normalization_conflict = - xattr_sysattr_casechk(zap.za_name); -#else - panic("%s:%u: TODO", __func__, __LINE__); -#endif - } } - /* - * Check if name will fit. - * - * Note: non-ascii names may expand (up to 3x) when converted - * to NFD - */ - namelen = strlen(zap.za_name); - - /* sysctl to force formD normalization of vnop output */ - if (zfs_vnop_force_formd_normalized_output && - !is_ascii_str(zap.za_name)) - force_formd_normalized_output = 1; - else - force_formd_normalized_output = 0; - - if (force_formd_normalized_output) - namelen = MIN(MAXNAMLEN, namelen * 3); - + if (!skip_this_entry) { - /* - * Do magic filename conversion for Windows here - */ + // emit + error = zfs_readdir_emitdir(zfsvfs, zap.za_name, ctx, + zccb, objnum); - error = RtlUTF8ToUnicodeN(NULL, 0, &namelenholder, - zap.za_name, namelen); - - // Did they provide a search pattern - if (zccb->searchname.Buffer && zccb->searchname.Length) { - UNICODE_STRING thisname; - WCHAR tmpname[PATH_MAX]; - ULONG tmpnamelen; - // We need to convert name to a tmp buffer here, as the - // output buffer might not have enough room to hold the - // whole name, and we need the whole name to do search - // match. - error = RtlUTF8ToUnicodeN(tmpname, PATH_MAX, - &tmpnamelen, zap.za_name, namelen); - // dprintf("%s: '%.*S' -> '%s'\n", __func__, - // tmpnamelen / sizeof(WCHAR), tmpname, zap.za_name); - - thisname.Buffer = tmpname; - thisname.Length = thisname.MaximumLength = tmpnamelen; - // wildcard? - if (zccb->ContainsWildCards) { - if (!FsRtlIsNameInExpression(&zccb->searchname, - &thisname, - !(zfsvfs->z_case == ZFS_CASE_SENSITIVE), - NULL)) - skip_this_entry = 1; + if (error == 0) { + ctx->numdirent++; + } else if (error == ENOSPC) { /* stop iterating */ + break; } else { - if (!FsRtlAreNamesEqual(&thisname, - &zccb->searchname, - !(zfsvfs->z_case == ZFS_CASE_SENSITIVE), - NULL)) - skip_this_entry = 1; + /* other error, skip over entry */ } -#if 0 - dprintf("comparing names '%.*S' == '%.*S' skip %d\n", - thisname.Length / sizeof (WCHAR), thisname.Buffer, - zccb->searchname.Length / sizeof (WCHAR), - zccb->searchname.Buffer, - skip_this_entry); -#endif - } - - - if (!skip_this_entry) { - // Windows combines vnop_readdir and vnop_getattr, - // so we need to lookup a bunch of values, we try - // to do that as lightweight as possible. - znode_t dummy = { 0 }; // For "." and ".." - int get_zp = ENOENT; - - tzp = &dummy; - - // If "." use zp, if ".." use dzp, neither needs - // releasing. Otherwise, call zget. - if (offset == 0 || offset == 1) - tzp = zp; - else - get_zp = zfs_zget_ext(zfsvfs, - offset == 1 ? parent : objnum, &tzp, - ZGET_FLAG_UNLINKED); - - // If we failed to get the node (someone else might have - // deleted it), but we need to return the name still, - // so it can be removed. - if (get_zp != 0 && tzp == NULL) - skip_this_entry = 1; - - // Is it worth warning about failing stat here? - if (!skip_this_entry) { - - // We need to fill in more fields. - sa_bulk_attr_t bulk[3]; - int count = 0; - uint64_t mtime[2]; - uint64_t ctime[2]; - uint64_t crtime[2]; - SA_ADD_BULK_ATTR(bulk, count, - SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, - SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, - SA_ZPL_CRTIME(zfsvfs), NULL, &crtime, 16); - sa_bulk_lookup(tzp->z_sa_hdl, bulk, count); - // Is it worth warning about failed lookup here? - - structsize = 0; - - /* - * This giant switch is a bit awkward with such - * little indentation left, and should be moved - * to own function, so that zfs_ctldir.c can - * also use it. - */ - - switch (dirlisttype) { - - case FileFullDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_FULL_DIR_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - eodp->FileIndex = offset; - eodp->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - eodp->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - eodp->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - eodp->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - eodp->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - eodp->LastAccessTime.QuadPart); - // Magic code to change dir icon to link - eodp->EaSize = - tzp->z_pflags & ZFS_REPARSE ? - 0xa0000003 : - xattr_getsize(ZTOV(tzp)); - eodp->FileAttributes = - zfs_getwinflags(tzp); - nameptr = eodp->FileName; - eodp->FileNameLength = namelenholder; - - break; - - case FileIdBothDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_ID_BOTH_DIR_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - FILE_ID_BOTH_DIR_INFORMATION *fibdi; - fibdi = (FILE_ID_BOTH_DIR_INFORMATION *) - bufptr; - fibdi->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - fibdi->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - fibdi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fibdi->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - fibdi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - fibdi->LastAccessTime.QuadPart); - fibdi->EaSize = - tzp->z_pflags & ZFS_REPARSE ? - 0xa0000003 : - xattr_getsize(ZTOV(tzp)); - fibdi->FileAttributes = - zfs_getwinflags(tzp); - fibdi->FileId.QuadPart = objnum; - fibdi->FileIndex = offset; - fibdi->ShortNameLength = 0; - nameptr = fibdi->FileName; - fibdi->FileNameLength = namelenholder; + } // !skip_this_entry - break; - - case FileBothDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_BOTH_DIR_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - FILE_BOTH_DIR_INFORMATION *fbdi = - (FILE_BOTH_DIR_INFORMATION *)bufptr; - fbdi->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - fbdi->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - fbdi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fbdi->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - fbdi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - fbdi->LastAccessTime.QuadPart); - fbdi->EaSize = - tzp->z_pflags & ZFS_REPARSE ? - 0xa0000003 : - xattr_getsize(ZTOV(tzp)); - fbdi->FileAttributes = - zfs_getwinflags(tzp); - fbdi->FileIndex = offset; - fbdi->ShortNameLength = 0; - nameptr = fbdi->FileName; - fbdi->FileNameLength = namelenholder; - - break; - - case FileDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_DIRECTORY_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - fdi = (FILE_DIRECTORY_INFORMATION *) - bufptr; - fdi->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - fdi->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - fdi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fdi->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - fdi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - fdi->LastAccessTime.QuadPart); - fdi->FileAttributes = - zfs_getwinflags(tzp); - fdi->FileIndex = offset; - nameptr = fdi->FileName; - fdi->FileNameLength = namelenholder; - break; - - case FileNamesInformation: - structsize = - FIELD_OFFSET(FILE_NAMES_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - FILE_NAMES_INFORMATION *fni = - (FILE_NAMES_INFORMATION *)bufptr; - fni->FileIndex = offset; - nameptr = fni->FileName; - fni->FileNameLength = namelenholder; - break; - - case FileIdFullDirectoryInformation: - 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; - fifdi = (FILE_ID_FULL_DIR_INFORMATION *) - bufptr; - fifdi->FileIndex = offset; - 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_REPARSE ? - 0xa0000003 : - xattr_getsize(ZTOV(tzp)); - fifdi->FileAttributes = - zfs_getwinflags(tzp); - fifdi->FileId.QuadPart = zp->z_id; - nameptr = fifdi->FileName; - fifdi->FileNameLength = namelenholder; - break; - - case FileIdExtdDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_ID_EXTD_DIR_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - FILE_ID_EXTD_DIR_INFORMATION *fiedi; - fiedi = (FILE_ID_EXTD_DIR_INFORMATION *) - bufptr; - fiedi->FileIndex = offset; - fiedi->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - fiedi->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - fiedi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fiedi->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - fiedi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - fiedi->LastAccessTime.QuadPart); - fiedi->EaSize = - tzp->z_pflags & ZFS_REPARSE ? - 0xa0000003 : - xattr_getsize(ZTOV(tzp)); - fiedi->FileAttributes = - zfs_getwinflags(tzp); - memset(&fiedi->FileId.Identifier[0], 0, - sizeof (fiedi->FileId)); - memcpy(&fiedi->FileId.Identifier[0], - &zp->z_id, sizeof (zp->z_id)); - nameptr = fiedi->FileName; - fiedi->FileNameLength = namelenholder; - break; - - case FileIdExtdBothDirectoryInformation: - structsize = FIELD_OFFSET( - FILE_ID_EXTD_BOTH_DIR_INFORMATION, - FileName[0]); - if (outcount + structsize + - namelenholder > bufsize) - break; - - eodp = - (FILE_FULL_DIR_INFORMATION *)bufptr; - FILE_ID_EXTD_BOTH_DIR_INFORMATION - *fiebdi; - fiebdi = - (FILE_ID_EXTD_BOTH_DIR_INFORMATION - *)bufptr; - fiebdi->FileIndex = offset; - fiebdi->AllocationSize.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - P2ROUNDUP(tzp->z_size, - zfs_blksz(tzp)); - fiebdi->EndOfFile.QuadPart = - S_ISDIR(tzp->z_mode) ? 0 : - tzp->z_size; - TIME_UNIX_TO_WINDOWS(mtime, - fiebdi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fiebdi->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - fiebdi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(tzp->z_atime, - fiebdi->LastAccessTime.QuadPart); - fiebdi->EaSize = - xattr_getsize(ZTOV(tzp)); - fiebdi->ReparsePointTag = - tzp->z_pflags & ZFS_REPARSE ? - 0xa0000003 : 0; - fiebdi->FileAttributes = - zfs_getwinflags(tzp); - fiebdi->ShortNameLength = 0; - memset(&fiebdi->FileId.Identifier[0], 0, - sizeof (fiebdi->FileId)); - memcpy(&fiebdi->FileId.Identifier[0], - &zp->z_id, sizeof (zp->z_id)); - nameptr = fiebdi->FileName; - fiebdi->FileNameLength = namelenholder; - break; - - } - - // Release the zp -#if 1 - if (get_zp == 0 && tzp != NULL) { - VN_RELE(ZTOV(tzp)); - } -#else - if (get_zp == 0) { - if (ZTOV(tzp) == NULL) { - zfs_zinactive(tzp); - } else { - VN_RELE(ZTOV(tzp)); - } - } -#endif - - // If know we can't fit struct, just leave - if (outcount + structsize + - namelenholder > bufsize) - break; - - rawsize = structsize + namelenholder; - reclen = DIRENT_RECLEN(rawsize); - - /* - * Will this entry fit in the buffer? - * This time with alignment - */ - if (outcount + reclen > bufsize) { - - if (!outcount) { - error = (EINVAL); - goto update; - } - break; - } - // If it is going to fit, compute alignment, - // in case this dir entry is the last one, - // we don't align last one. - last_alignment = reclen - rawsize; - - // Convert the filename over, or as much - // as we can fit - ULONG namelenholder2 = 0; - error = RtlUTF8ToUnicodeN(nameptr, - namelenholder, &namelenholder2, - zap.za_name, namelen); - ASSERT(namelenholder == namelenholder2); -#if 0 - dprintf("%s: '%.*S' -> '%s' (namelen %d bytes: " - "structsize %d)\n", __func__, - namelenholder / sizeof (WCHAR), nameptr, - zap.za_name, namelenholder, structsize); -#endif - - // If we aren't to skip, advance all pointers - eodp->NextEntryOffset = reclen; - - outcount += reclen; - bufptr += reclen; - numdirent++; - } // !skip_this_entry - } // while - - ASSERT(outcount <= bufsize); - - /* Prefetch znode */ - if (prefetch) - dmu_prefetch(os, objnum, 0, 0, 0, - ZIO_PRIORITY_SYNC_READ); + ASSERT(ctx->outcount <= ctx->bufsize); /* * Move to the next entry, fill in the previous offset. */ - if (offset > 2 || (offset == 2 && !zfs_show_ctldir(zp))) { + if (ctx->offset > 2 || (ctx->offset == 2 && + !zfs_show_ctldir(zp))) { zap_cursor_advance(&zc); - offset = zap_cursor_serialize(&zc); + ctx->offset = zap_cursor_serialize(&zc); } else { - offset += 1; + ctx->offset += 1; } if (!skip_this_entry && flag_return_single_entry) break; - } - - // The last eodp should have Next offset of 0 - // This assumes NextEntryOffset is the FIRST entry in all structs - if (eodp) eodp->NextEntryOffset = 0; + } // while - // The outcout += reclen; above unfortunately adds the possibly - // aligned (to 8 bytes) length. But the last entry should not - // be rounded-up. - if ((outcount > last_alignment) && - (last_alignment > 0)) { - outcount -= last_alignment; - } + zfs_readdir_complete(ctx); zp->z_zn_prefetch = B_FALSE; /* a lookup will re-enable pre-fetching */ - if ((error = zfs_uiomove(outbuf, (long)outcount, UIO_READ, uio))) { - /* - * Reset the pointer. - */ - offset = zfs_uio_offset(uio); - } - update: zap_cursor_fini(&zc); - if (outbuf) { - kmem_free(outbuf, bufsize); - } - - if (error == ENOENT) - error = 0; ZFS_ACCESSTIME_STAMP(zfsvfs, zp); - zfs_uio_setoffset(uio, offset); - if (a_numdirent) - *a_numdirent = numdirent; zfs_exit(zfsvfs, FTAG); - dprintf("-zfs_readdir: num %d\n", numdirent); + dprintf("-zfs_readdir: num %d\n", ctx->numdirent); return (error); } @@ -3072,7 +2525,6 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm, znode_t *szp, *tzp; zfsvfs_t *zfsvfs = ZTOZSB(sdzp); zilog_t *zilog; - uint64_t addtime[2]; zfs_dirlock_t *sdl, *tdl; dmu_tx_t *tx; zfs_zlock_t *zl; diff --git a/module/os/windows/zfs/zfs_vnops_windows.c b/module/os/windows/zfs/zfs_vnops_windows.c index fb5929c60615..57da8af94ee5 100644 --- a/module/os/windows/zfs/zfs_vnops_windows.c +++ b/module/os/windows/zfs/zfs_vnops_windows.c @@ -353,6 +353,10 @@ zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, struct componentname cn; int fullstrlen; char namebuffer[MAXNAMELEN]; + BOOLEAN FileOpenReparsePoint; + + FileOpenReparsePoint = + BooleanFlagOn(options, FILE_OPEN_REPARSE_POINT); // Iterate from dvp if given, otherwise root dvp = *dvpp; @@ -404,6 +408,11 @@ zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, error = zfs_lookup(VTOZ(dvp), namebuffer, &zp, flags, NULL, &direntflags, &cn); + // If snapshot dir and we are pretending it is deleted... + if (error == 0 && zp->z_vnode != NULL && ZTOV(zp)->v_unlink) { + VN_RELE(ZTOV(zp)); + error = ENOENT; + } if (error != 0) { // If we are creating a file, or looking up parent, // allow it not to exist @@ -429,12 +438,15 @@ zfs_find_dvp_vp(zfsvfs_t *zfsvfs, char *filename, int finalpartmaynotexist, */ if (zp->z_pflags & ZFS_REPARSE) { + // Indicate if reparse was final part if (lastname) - *lastname = word /* ? word : filename */; + *lastname = brkt; + if (dvpp != NULL) + *dvpp = dvp; if (vpp != NULL) *vpp = vp; - VN_RELE(dvp); + // VN_RELE(dvp); return (STATUS_REPARSE); } @@ -523,15 +535,12 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, int error; cred_t *cr = NULL; char *finalname = NULL; - char *brkt = NULL; - char *word = NULL; PFILE_OBJECT FileObject; ULONG outlen; struct vnode *dvp = NULL; struct vnode *vp = NULL; znode_t *zp = NULL; znode_t *dzp = NULL; - struct componentname cn; ULONG Options; BOOLEAN CreateDirectory; BOOLEAN NoIntermediateBuffering; @@ -547,6 +556,7 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, BOOLEAN OpenRoot; BOOLEAN CreateFile; BOOLEAN FileOpenByFileId; + BOOLEAN FileOpenReparsePoint; ULONG CreateDisposition; zfsvfs_t *zfsvfs = vfs_fsprivate(zmo); int flags = 0; @@ -587,6 +597,9 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, BooleanFlagOn(Options, FILE_DELETE_ON_CLOSE); FileOpenByFileId = BooleanFlagOn(Options, FILE_OPEN_BY_FILE_ID); + FileOpenReparsePoint = + BooleanFlagOn(Options, FILE_OPEN_REPARSE_POINT); + // Should be passed an 8 byte FileId instead. if (FileOpenByFileId && FileObject->FileName.Length != @@ -853,6 +866,12 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, // - dvp is HELD // we need dvp from here on down. + // If asked to open reparse point instead of following it, and + // it was the final part of the path, then just open it. + if (error == STATUS_REPARSE && FileOpenReparsePoint && + (!finalname || !*finalname)) + error = STATUS_SUCCESS; + if (error) { /* @@ -876,13 +895,13 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, */ zp = VTOZ(vp); REPARSE_DATA_BUFFER *rpb; + size_t size; + // fix me, direct vp access + size = zfsctl_is_node(zp) ? vp->v_reparse_size : + zp->z_size; rpb = ExAllocatePoolWithTag(PagedPool, - zp->z_size, '!FSZ'); - zfs_uio_t uio; - struct iovec iov = { rpb, zp->z_size }; - zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, - zp->z_size, 0); - zfs_readlink(vp, &uio, NULL); + size, '!FSZ'); + get_reparse_point_impl(zp, rpb, size); // Return in Reserved the amount of path // that was parsed. @@ -906,6 +925,8 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, } #endif VN_RELE(vp); + if (dvp) + VN_RELE(dvp); return (error); // STATUS_REPARSE } @@ -1194,7 +1215,8 @@ zfs_vnop_lookup_impl(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo, &IrpSp->Parameters.Create.SecurityContext-> AccessState->SubjectSecurityContext); #if 1 - if (!SeAccessCheck(vnode_security(vp ? vp : dvp), + if (!FileOpenReparsePoint && + !SeAccessCheck(vnode_security(vp ? vp : dvp), &IrpSp->Parameters.Create.SecurityContext-> AccessState->SubjectSecurityContext, TRUE, @@ -1481,7 +1503,7 @@ zfs_vnop_lookup(PIRP Irp, PIO_STACK_LOCATION IrpSp, mount_t *zmo) IrpSp->Parameters.Create.EaLength, &offset); if (!NT_SUCCESS(status)) { dprintf("IoCheckEaBufferValidity returned %08x " - "(error at offset %u)\n", status, offset); + "(error at offset %lu)\n", status, offset); return (status); } } @@ -1643,7 +1665,6 @@ zfs_vnop_reclaim(struct vnode *vp) } zfsvfs_t *zfsvfs = zp->z_zfsvfs; - boolean_t fastpath; dprintf(" zfs_vnop_recycle: releasing zp %p and vp %p: '%s'\n", zp, vp, zp->z_name_cache ? zp->z_name_cache : ""); @@ -1819,12 +1840,12 @@ pnp_query_id(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) Irp->IoStatus.Information = (ULONG_PTR)ExAllocatePoolWithTag(PagedPool, zmo->bus_name.Length + sizeof (UNICODE_NULL), '!OIZ'); - if (Irp->IoStatus.Information == NULL) + if (Irp->IoStatus.Information == 0) return (STATUS_NO_MEMORY); RtlCopyMemory((void *)Irp->IoStatus.Information, zmo->bus_name.Buffer, zmo->bus_name.Length); - dprintf("replying with '%.*S'\n", zmo->uuid.Length/sizeof (WCHAR), + dprintf("replying with '%.*S'\n", (int)zmo->uuid.Length/sizeof (WCHAR), Irp->IoStatus.Information); return (STATUS_SUCCESS); @@ -1846,7 +1867,6 @@ query_volume_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Status; - int len; Status = STATUS_NOT_IMPLEMENTED; int space; int error = 0; @@ -1919,10 +1939,10 @@ query_volume_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, ffai->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH; - if (zfsvfs->z_rdonly) + if (zfsvfs->z_rdonly) { SetFlag(ffai->FileSystemAttributes, FILE_READ_ONLY_VOLUME); - + } ffai->MaximumComponentNameLength = MAXNAMELEN - 1; // There is room for one char in the struct @@ -2118,7 +2138,7 @@ query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, switch (IrpSp->Parameters.QueryFile.FileInformationClass) { case FileAllInformation: - dprintf("%s: FileAllInformation: buffer 0x%x\n", __func__, + dprintf("%s: FileAllInformation: buffer 0x%lx\n", __func__, IrpSp->Parameters.QueryFile.Length); if (IrpSp->Parameters.QueryFile.Length < @@ -2186,9 +2206,9 @@ query_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, // FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) // + usedspace; - dprintf("Struct size 0x%x FileNameLen 0x%x " - "Information retsize 0x%x\n", - sizeof (FILE_ALL_INFORMATION), + dprintf("Struct size 0x%x FileNameLen 0x%lx " + "Information retsize 0x%lx\n", + (int)sizeof (FILE_ALL_INFORMATION), all->NameInformation.FileNameLength, Irp->IoStatus.Information); break; @@ -2388,7 +2408,6 @@ zfswin_insert_xattrname(struct vnode *vp, char *xattrname, uint8_t *outbuffer, // Convert filename, to get space required. ULONG needed_xattrnamelen; - int error; // Check error? Do we care about convertion errors? // error = RtlUTF8ToUnicodeN(NULL, 0, &needed_xattrnamelen, @@ -2642,10 +2661,9 @@ 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; + uint8_t *buffer = NULL; NTSTATUS Status = STATUS_SUCCESS; - struct vnode *vp = NULL, *xdvp = NULL; - vattr_t vap = { 0 }; + struct vnode *vp = NULL; if (IrpSp->FileObject == NULL) return (STATUS_INVALID_PARAMETER); @@ -2654,13 +2672,18 @@ set_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) if (vp == NULL) return (STATUS_INVALID_PARAMETER); - znode_t *zp = VTOZ(vp); - dprintf("%s\n", __func__); if (input_len == 0) return (STATUS_INVALID_PARAMETER); + mount_t *zmo = DeviceObject->DeviceExtension; + zfsvfs_t *zfsvfs; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + // This magic is straight out of fastfat buffer = BufferUserBuffer(Irp, input_len); @@ -2671,13 +2694,42 @@ set_ea(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) // ULONG isn't the right length.) Irp->IoStatus.Information = eaErrorOffset; if (!NT_SUCCESS(Status)) { - dprintf("%s: failed vnode_apply_eas: 0x%x\n", __func__, Status); + dprintf("%s: failed vnode_apply_eas: 0x%lx\n", + __func__, Status); return (Status); } return (Status); } +size_t +get_reparse_point_impl(znode_t *zp, char *buffer, size_t outlen) +{ + size_t size = 0; + if (zp->z_pflags & ZFS_REPARSE) { + int err; + + if (zfsctl_is_node(zp)) { + REPARSE_DATA_BUFFER *rdb = NULL; + NTSTATUS Status; + Status = zfsctl_get_reparse_point(zp, &rdb, &size); + if (Status == 0) + memcpy(buffer, rdb, size); + } else { + int size = MIN(zp->z_size, outlen); + struct iovec iov; + iov.iov_base = (void *)buffer; + iov.iov_len = size; + + zfs_uio_t uio; + zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, + size, 0); + err = zfs_readlink(ZTOV(zp), &uio, NULL); + } + } + return (size); +} + NTSTATUS get_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) @@ -2699,29 +2751,18 @@ get_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (zp->z_pflags & ZFS_REPARSE) { int err; - int size = MIN(zp->z_size, outlen); - struct iovec iov; - iov.iov_base = (void *)buffer; - iov.iov_len = size; + size_t size = 0; - zfs_uio_t uio; - zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, - size, 0); - err = zfs_readlink(vp, &uio, NULL); - - if (outlen < zp->z_size) + size = get_reparse_point_impl(zp, buffer, outlen); + Irp->IoStatus.Information = size; + if (outlen < 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); } VN_RELE(vp); } - dprintf("%s: returning 0x%x\n", __func__, Status); + dprintf("%s: returning 0x%lx\n", __func__, Status); return (Status); } @@ -2734,10 +2775,13 @@ set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, DWORD inlen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; void *buffer = Irp->AssociatedIrp.SystemBuffer; REPARSE_DATA_BUFFER *rdb = buffer; - ULONG tag; + + if (!FileObject || !IrpSp->FileObject->FsContext) + return (STATUS_INVALID_PARAMETER); + struct vnode *vp = IrpSp->FileObject->FsContext; - if (!FileObject) + if (!vp || !VTOZ(vp)) return (STATUS_INVALID_PARAMETER); if (Irp->UserBuffer) @@ -2749,17 +2793,24 @@ set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, Status = FsRtlValidateReparsePointBuffer(inlen, rdb); if (!NT_SUCCESS(Status)) { - dprintf("FsRtlValidateReparsePointBuffer returned %08x\n", + dprintf("FsRtlValidateReparsePointBuffer returned %08lx\n", Status); return (Status); } - RtlCopyMemory(&tag, buffer, sizeof (ULONG)); - dprintf("Received tag 0x%x\n", tag); - - VN_HOLD(vp); znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + if (zfsctl_is_node(zp)) + return (zfsctl_set_reparse_point(zp, rdb, inlen)); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + + if (zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + VN_HOLD(vp); znode_t *dzp = NULL; uint64_t parent; int error; @@ -2834,7 +2885,7 @@ set_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, zrele(dzp); VN_RELE(vp); - dprintf("%s: returning 0x%x\n", __func__, Status); + dprintf("%s: returning 0x%lx\n", __func__, Status); return (Status); } @@ -2848,7 +2899,6 @@ delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, DWORD inlen = IrpSp->Parameters.DeviceIoControl.InputBufferLength; void *buffer = Irp->AssociatedIrp.SystemBuffer; REPARSE_DATA_BUFFER *rdb = buffer; - ULONG tag; struct vnode *vp = IrpSp->FileObject->FsContext; if (!FileObject) @@ -2873,6 +2923,10 @@ delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, znode_t *zp = VTOZ(vp); + if (zfsctl_is_node(zp)) { + VN_RELE(vp); + return (zfsctl_delete_reparse_point(zp)); + } // Like zfs_symlink, write the data as SA attribute. zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -2941,7 +2995,7 @@ delete_reparse_point(PDEVICE_OBJECT DeviceObject, PIRP Irp, zrele(dzp); VN_RELE(vp); - dprintf("%s: returning 0x%x\n", __func__, Status); + dprintf("%s: returning 0x%lx\n", __func__, Status); return (Status); } @@ -3276,7 +3330,7 @@ user_fs_request(PDEVICE_OBJECT DeviceObject, PIRP *PIrp, dprintf(" FSCTL_QUERY_DEPENDENT_VOLUME: \n"); STORAGE_QUERY_DEPENDENT_VOLUME_REQUEST *req = Irp->AssociatedIrp.SystemBuffer; - dprintf("RequestLevel %d: RequestFlags 0x%x\n", + dprintf("RequestLevel %ld: RequestFlags 0x%lx\n", req->RequestLevel, req->RequestFlags); // #define QUERY_DEPENDENT_VOLUME_REQUEST_FLAG_HOST_VOLUMES 0x1 // #define QUERY_DEPENDENT_VOLUME_REQUEST_FLAG_GUEST_VOLUMES 0x2 @@ -3319,7 +3373,7 @@ user_fs_request(PDEVICE_OBJECT DeviceObject, PIRP *PIrp, break; default: - dprintf("* %s: unknown class 0x%x\n", __func__, + dprintf("* %s: unknown class 0x%lx\n", __func__, IrpSp->Parameters.FileSystemControl.FsControlCode); break; } @@ -3338,10 +3392,6 @@ query_directory_FileFullDirectoryInformation(PDEVICE_OBJECT DeviceObject, IrpSp->Flags & SL_RESTART_SCAN ? 1 : 0; int flag_return_single_entry = IrpSp->Flags & SL_RETURN_SINGLE_ENTRY ? 1 : 0; - int bytes_out = 0; - int index = 0; - int eof = 0; - int numdirent; int ret; mount_t *zmo; zfsvfs_t *zfsvfs; @@ -3431,40 +3481,64 @@ query_directory_FileFullDirectoryInformation(PDEVICE_OBJECT DeviceObject, zccb->searchname.Length); } dprintf("%s: setting up search '%wZ' (wildcards: %d) " - "status 0x%x\n", __func__, + "status 0x%lx\n", __func__, &zccb->searchname, zccb->ContainsWildCards, Status); } + emitdir_ptr_t ctx; + ctx.bufsize = (size_t)zfs_uio_resid(&uio); + ctx.alloc_buf = kmem_zalloc(ctx.bufsize, KM_SLEEP); + ctx.bufptr = ctx.alloc_buf; + ctx.outcount = 0; + ctx.next_offset = NULL; + ctx.last_alignment = 0; + ctx.offset = zccb->uio_offset; + ctx.numdirent = 0; + ctx.dirlisttype = IrpSp->Parameters.QueryDirectory.FileInformationClass; + VN_HOLD(dvp); - ret = zfs_readdir(dvp, &uio, NULL, zccb, IrpSp->Flags, - IrpSp->Parameters.QueryDirectory.FileInformationClass, &numdirent); + ret = zfs_readdir(dvp, &ctx, NULL, zccb, IrpSp->Flags); VN_RELE(dvp); - if (ret == 0) { + /* finished listing dir? */ + if (ret == ENOENT) { + zccb->dir_eof = 1; + ret = 0; + } else if (ret == ENOSPC) { + /* not finished, but ran out of room? */ + ret = 0; + } + if (ret == 0) { + if (ctx.outcount > 0) { + + if ((ret = zfs_uiomove(ctx.alloc_buf, + (long)ctx.outcount, UIO_READ, &uio))) { + /* + * Reset the pointer, by copying in old value + */ + ctx.offset = zccb->uio_offset; + } + Status = STATUS_SUCCESS; + } else { // outcount == 0 + Status = (zccb->uio_offset == 0) ? STATUS_NO_SUCH_FILE : + STATUS_NO_MORE_FILES; + } // Set correct buffer size returned. - Irp->IoStatus.Information = - IrpSp->Parameters.QueryDirectory.Length - - zfs_uio_resid(&uio); + Irp->IoStatus.Information = ctx.outcount; + // IrpSp->Parameters.QueryDirectory.Length - + // zfs_uio_resid(&uio); - dprintf("dirlist information in %d out size %d\n", + dprintf("dirlist information in %ld out size %ld\n", IrpSp->Parameters.QueryDirectory.Length, Irp->IoStatus.Information); -// Return saying there are entries in buffer, or, ] -// if we sent same data previously, but now EOF send NO MORE, -// or if there was nothing sent at all (search pattern failed), send NO SUCH - if (Irp->IoStatus.Information == 0) - Status = (zccb->uio_offset == 0) ? STATUS_NO_SUCH_FILE : - STATUS_NO_MORE_FILES; - else - Status = STATUS_SUCCESS; - // Remember directory index for next time - zccb->uio_offset = zfs_uio_offset(&uio); - + zccb->uio_offset = ctx.offset; } + kmem_free(ctx.alloc_buf, ctx.bufsize); + return (Status); } @@ -3569,7 +3643,7 @@ set_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (IrpSp->FileObject && IrpSp->FileObject->FsContext) { FILE_ALLOCATION_INFORMATION *feofi = Irp->AssociatedIrp.SystemBuffer; - dprintf("* SET FileAllocationInformation %u\n", + dprintf("* SET FileAllocationInformation %llu\n", feofi->AllocationSize.QuadPart); // This is a noop at the moment. It makes Windows Explorer and apps not crash // From the documentation, setting the allocation size smaller than EOF @@ -3583,85 +3657,32 @@ set_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, break; case FileBasicInformation: // chmod dprintf("* SET FileBasicInformation\n"); - if (IrpSp->FileObject && IrpSp->FileObject->FsContext) { - FILE_BASIC_INFORMATION *fbi = - Irp->AssociatedIrp.SystemBuffer; - struct vnode *vp = IrpSp->FileObject->FsContext; - - VN_HOLD(vp); - znode_t *zp = VTOZ(vp); - vattr_t va = { 0 }; - uint64_t unixtime[2] = { 0 }; - -// can request that the file system not update .. LastAccessTime, -// LastWriteTime, and ChangeTime .. setting the appropriate members to -1. -// ie, LastAccessTime = -1 -> atime = disabled - not implemented -// LastAccessTime = -2 -> cancel the disable (-1), return to normal. -// a value of "0" means to keep existing value. - if (fbi->ChangeTime.QuadPart > 0) { - TIME_WINDOWS_TO_UNIX(fbi->ChangeTime.QuadPart, - unixtime); - va.va_change_time.tv_sec = unixtime[0]; - va.va_change_time.tv_nsec = unixtime[1]; - va.va_active |= ATTR_CTIME; - } - if (fbi->LastWriteTime.QuadPart > 0) { - TIME_WINDOWS_TO_UNIX( - fbi->LastWriteTime.QuadPart, - unixtime); - va.va_modify_time.tv_sec = unixtime[0]; - va.va_modify_time.tv_nsec = unixtime[1]; - va.va_active |= ATTR_MTIME; - } - if (fbi->CreationTime.QuadPart > 0) { - TIME_WINDOWS_TO_UNIX(fbi->CreationTime.QuadPart, - unixtime); - va.va_create_time.tv_sec = unixtime[0]; - va.va_create_time.tv_nsec = unixtime[1]; - va.va_active |= ATTR_CRTIME; // ATTR_CRTIME - } - if (fbi->LastAccessTime.QuadPart > 0) - TIME_WINDOWS_TO_UNIX( - fbi->LastAccessTime.QuadPart, - zp->z_atime); - - if (fbi->FileAttributes) - if (zfs_setwinflags(VTOZ(vp), - fbi->FileAttributes)) - va.va_active |= ATTR_MODE; - - Status = zfs_setattr(zp, &va, 0, NULL, NULL); - - // zfs_setattr will turn ARCHIVE back on, when perhaps - // it is set off by this call - if (fbi->FileAttributes) - zfs_setwinflags(zp, fbi->FileAttributes); - - VN_RELE(vp); - } + Status = set_file_basic_information(DeviceObject, Irp, IrpSp); break; case FileDispositionInformation: // unlink dprintf("* SET FileDispositionInformation\n"); - Status = file_disposition_information(DeviceObject, Irp, IrpSp); + Status = set_file_disposition_information(DeviceObject, Irp, + IrpSp); break; case FileEndOfFileInformation: // extend? - Status = file_endoffile_information(DeviceObject, Irp, IrpSp); + Status = set_file_endoffile_information(DeviceObject, Irp, + IrpSp); break; case FileLinkInformation: // symlink - Status = file_link_information(DeviceObject, Irp, IrpSp); + Status = set_file_link_information(DeviceObject, Irp, IrpSp); break; case FilePositionInformation: // seek dprintf("* SET FilePositionInformation NOTIMPLEMENTED\n"); break; case FileRenameInformation: // vnop_rename case FileRenameInformationEx: - Status = file_rename_information(DeviceObject, Irp, IrpSp); + Status = set_file_rename_information(DeviceObject, Irp, IrpSp); break; case FileValidDataLengthInformation: // truncate? dprintf("* SET FileValidDataLengthInformation NOTIMP\n"); break; case FileDispositionInformationEx: - Status = file_disposition_information_ex(DeviceObject, Irp, + Status = set_file_disposition_information_ex(DeviceObject, Irp, IrpSp); break; default: @@ -3757,10 +3778,10 @@ fs_read(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) // nocache transfer, make sure we flush first. if (!pagingio && !nocache && fileObject->SectionObjectPointer && (fileObject->SectionObjectPointer->DataSectionObject != NULL)) { +#if 0 + // Sadly this BSODs and I'm not sure why IO_STATUS_BLOCK IoStatus = { 0 }; - // Sadly this BSODs and I'm not sure why -#if 0 ExAcquireResourceExclusiveLite(vp->FileHeader.PagingIoResource, TRUE); VERIFY3U(zccb->cacheinit, !=, 0); @@ -3928,7 +3949,7 @@ fs_write(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) } #if 0 - xprintf(" %s minor type %d flags 0x%x mdl %d System %d " + dprintf(" %s minor type %d flags 0x%x mdl %d System %d " "User %d paging %d\n", __func__, IrpSp->MinorFunction, DeviceObject->Flags, (Irp->MdlAddress != 0), (Irp->AssociatedIrp.SystemBuffer != 0), @@ -4214,11 +4235,13 @@ delete_entry(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) vp = IrpSp->FileObject->FsContext; zp = VTOZ(vp); ASSERT(zp != NULL); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; uint64_t parent = 0; znode_t *dzp; + if (zp->z_is_ctldir) + return (STATUS_SUCCESS); + // No dvp, lookup parent VERIFY(sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zp->z_zfsvfs), &parent, sizeof (parent)) == 0); @@ -4230,7 +4253,7 @@ delete_entry(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) // Unfortunately, filename is littered with "\", clean it up, // or search based on ID to get name? dprintf("%s: deleting '%.*S'\n", __func__, - IrpSp->FileObject->FileName.Length / sizeof (WCHAR), + (int)IrpSp->FileObject->FileName.Length / sizeof (WCHAR), IrpSp->FileObject->FileName.Buffer); error = RtlUnicodeToUTF8N(filename, MAXNAMELEN, &outlen, @@ -4681,8 +4704,6 @@ zfs_fileobject_close(PDEVICE_OBJECT DeviceObject, PIRP Irp, vnode_t *vp = IrpSp->FileObject->FsContext; zfs_dirlist_t *zccb = IrpSp->FileObject->FsContext2; - znode_t *zp = (VTOZ(vp)); - int isdir = vnode_isdir(vp); /* @@ -4760,7 +4781,7 @@ zfsdev_async_thread(void *arg) /* Use FKIOCTL to make sure it calls memcpy instead */ Status = zfsdev_ioctl(NULL, Irp, FKIOCTL); - dprintf("%s: finished ioctl %d\n", __func__, Status); + dprintf("%s: finished ioctl %ld\n", __func__, Status); PMDL mdl = Irp->Tail.Overlay.DriverContext[0]; if (mdl) { @@ -4829,7 +4850,7 @@ zfsdev_async(PDEVICE_OBJECT DeviceObject, PIRP Irp) KernelMode, &h); if (error != STATUS_SUCCESS) goto out; - dprintf("mapped filed is 0x%x\n", h); + dprintf("mapped filed is 0x%p\n", h); zc->zc_cookie = (uint64_t)h; Irp->Tail.Overlay.DriverContext[2] = h; } @@ -4976,7 +4997,13 @@ _Function_class_(DRIVER_DISPATCH) break; case IOCTL_DISK_IS_WRITABLE: dprintf("IOCTL_DISK_IS_WRITABLE\n"); - Status = STATUS_SUCCESS; + mount_t *zmo = DeviceObject->DeviceExtension; + VERIFY(zmo->type == MOUNT_TYPE_VCB); + zfsvfs_t *zfsvfs = vfs_fsprivate(zmo); + if (zfsvfs != NULL && zfsvfs->z_rdonly) + Status = STATUS_MEDIA_WRITE_PROTECTED; + else + Status = STATUS_SUCCESS; break; case IOCTL_DISK_MEDIA_REMOVAL: dprintf("IOCTL_DISK_MEDIA_REMOVAL\n"); @@ -5117,7 +5144,8 @@ _Function_class_(DRIVER_DISPATCH) switch (cmd) { case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: dprintf("IOCTL_VOLUME_GET_GPT_ATTRIBUTES\n"); - Status = 0; + Status = ioctl_get_gpt_attributes(DeviceObject, Irp, + IrpSp); break; case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: dprintf("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n"); @@ -5262,7 +5290,7 @@ _Function_class_(DRIVER_DISPATCH) Status = zfs_vnop_mount(DeviceObject, Irp, IrpSp); break; case IRP_MN_USER_FS_REQUEST: - dprintf("IRP_MN_USER_FS_REQUEST: FsControlCode 0x%x\n", + dprintf("IRP_MN_USER_FS_REQUEST: FsControlCode 0lx%x\n", IrpSp->Parameters.FileSystemControl.FsControlCode); Status = user_fs_request(DeviceObject, PIrp, IrpSp); break; @@ -5403,7 +5431,7 @@ _Function_class_(DRIVER_DISPATCH) if (IrpSp->Parameters.Create.Options & FILE_OPEN_BY_FILE_ID) dprintf("IRP_MJ_CREATE: FileObject %p related %p " "FileID 0x%llx flags 0x%x sharing 0x%x options " - "0x%x\n", + "0x%lx\n", IrpSp->FileObject, IrpSp->FileObject ? IrpSp->FileObject->RelatedFileObject : @@ -5414,7 +5442,7 @@ _Function_class_(DRIVER_DISPATCH) else dprintf("IRP_MJ_CREATE: FileObject %p related %p " "name '%wZ' flags 0x%x sharing 0x%x options " - "%s attr 0x%x DesAcc 0x%x\n", + "%s attr 0x%x DesAcc 0x%lx\n", IrpSp->FileObject, IrpSp->FileObject ? IrpSp->FileObject->RelatedFileObject : diff --git a/module/os/windows/zfs/zfs_vnops_windows_lib.c b/module/os/windows/zfs/zfs_vnops_windows_lib.c index bb28cc9c498c..475c3570a67e 100644 --- a/module/os/windows/zfs/zfs_vnops_windows_lib.c +++ b/module/os/windows/zfs/zfs_vnops_windows_lib.c @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include #include @@ -342,6 +344,12 @@ common_status_str(NTSTATUS Status) return ("STATUS_NO_MORE_EAS"); case STATUS_NO_EAS_ON_FILE: return ("STATUS_NO_EAS_ON_FILE"); + case 0xa0000003: + return ("STATUS_REPARSE_POINT"); + case STATUS_DIRECTORY_IS_A_REPARSE_POINT: + return ("STATUS_DIRECTORY_IS_A_REPARSE_POINT"); + case STATUS_REPARSE: + return ("STATUS_REPARSE"); default: return ("<*****>"); } @@ -479,14 +487,14 @@ zfs_getwinflags(znode_t *zp) { uint32_t winflags = 0; uint64_t zflags = zp->z_pflags; - + zfsvfs_t *zfsvfs = zp->z_zfsvfs; if (zflags & ZFS_HIDDEN) winflags |= FILE_ATTRIBUTE_HIDDEN; if (zflags & ZFS_SYSTEM) winflags |= FILE_ATTRIBUTE_SYSTEM; if (zflags & ZFS_ARCHIVE) winflags |= FILE_ATTRIBUTE_ARCHIVE; - if (zflags & ZFS_READONLY) + if (zflags & ZFS_READONLY || zfsvfs->z_rdonly) winflags |= FILE_ATTRIBUTE_READONLY; if (zflags & ZFS_REPARSE) winflags |= FILE_ATTRIBUTE_REPARSE_POINT; @@ -647,6 +655,10 @@ vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, } } + // We can land here without a sahdl, for example .zfs + if (VTOZ(vp)->z_sa_hdl == NULL) + return (Status); + struct vnode *xdvp = NULL; znode_t *xdzp = NULL; vattr_t vap = { 0 }; @@ -695,6 +707,523 @@ vnode_apply_eas(struct vnode *vp, PFILE_FULL_EA_INFORMATION eas, return (Status); } + +extern int zfs_vnop_force_formd_normalized_output; + +void +zfs_readdir_complete(emitdir_ptr_t *ctx) +{ + // The last eodp should have Next offset of 0 + // This assumes NextEntryOffset is the FIRST entry in all structs + if (ctx->next_offset != NULL) + *ctx->next_offset = 0; + + // The outcout += reclen; above unfortunately adds the possibly + // aligned (to 8 bytes) length. But the last entry should not + // be rounded-up. + if ((ctx->outcount > ctx->last_alignment) && + (ctx->last_alignment > 0)) { + ctx->outcount -= ctx->last_alignment; + } +} + +/* + * Put out one directory entry to the output buffer, using + * whatever struct specified in ctx->dirlisttype. + * Return: + * 0 : keep iterating + * ESRC : search-pattern in use, and didn't match (keep iterating) + * ENOSPC : no more room in buffer (but more to come - stop) + */ +int +zfs_readdir_emitdir(zfsvfs_t *zfsvfs, const char *name, emitdir_ptr_t *ctx, + zfs_dirlist_t *zccb, ino64_t objnum) +{ + znode_t *tzp = NULL; + int structsize = 0; + void *nameptr = NULL; + ULONG namelenholder = 0; + int get_zp = ENOENT; + size_t namelen; + int error; + int force_formd_normalized_output = 0; + ushort_t reclen, rawsize; + ULONG *next_offset; + + // Windows combines vnop_readdir and vnop_getattr, + // so we need to lookup a bunch of values, we try + // to do that as lightweight as possible. + + if ((zfsvfs->z_ctldir != NULL) && + (objnum == ZFSCTL_INO_ROOT) || + (objnum == ZFSCTL_INO_SNAPDIR) || + ((objnum >= zfsvfs->z_ctldir_startid) && + (objnum <= ZFSCTL_INO_SNAPDIRS))) { + struct vnode *vp; + + get_zp = zfs_vfs_vget(zfsvfs->z_vfs, objnum, &vp, NULL); + if (get_zp == 0) + tzp = VTOZ(vp); + + } else { + get_zp = zfs_zget_ext(zfsvfs, + objnum, &tzp, + ZGET_FLAG_UNLINKED); + } + + /* + * Could not find it, error out ? print name ? + * Can't zget the .zfs dir etc, so we need a dummy + * node, so we grab root node instead. + */ + if (get_zp != 0 && tzp == NULL) { + get_zp = zfs_zget_ext(zfsvfs, + zfsvfs->z_root, &tzp, + ZGET_FLAG_UNLINKED); + } + if (get_zp != 0 && tzp == NULL) { + return (get_zp); + } + + /* + * Check if name will fit. + * + * Note: non-ascii names may expand (up to 3x) when converted + * to NFD + */ + namelen = strlen(name); + + /* sysctl to force formD normalization of vnop output */ + if (zfs_vnop_force_formd_normalized_output && + !is_ascii_str(name)) + force_formd_normalized_output = 1; + else + force_formd_normalized_output = 0; + + if (force_formd_normalized_output) + namelen = MIN(MAXNAMLEN, namelen * 3); + + /* + * Fetch filename conversion length + */ + + error = RtlUTF8ToUnicodeN(NULL, 0, &namelenholder, + name, namelen); + + // We need to fill in more fields, for getattr + uint64_t mtime[2] = { 0 }; + uint64_t ctime[2] = { 0 }; + uint64_t crtime[2] = { 0 }; + if (tzp->z_is_sa && tzp->z_sa_hdl != NULL) { + /* dummy_zp wont have sa_hdl */ + sa_bulk_attr_t bulk[3]; + int count = 0; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CRTIME(zfsvfs), NULL, &crtime, 16); + sa_bulk_lookup(tzp->z_sa_hdl, bulk, count); + // Is it worth warning about failed lookup here? + } + + structsize = 0; /* size of win struct desired */ + /* bufptr : output memory area, incrementing */ + /* outcount : amount written to output, incrementing */ + /* bufsize : size of output area - static */ + + /* Fill in struct based on desired type. */ + switch (ctx->dirlisttype) { + + case FileFullDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_FULL_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_FULL_DIR_INFORMATION *eodp = + (FILE_FULL_DIR_INFORMATION *)ctx->bufptr; + next_offset = &eodp->NextEntryOffset; + + eodp->FileIndex = ctx->offset; + eodp->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + eodp->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + eodp->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + eodp->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + eodp->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + eodp->LastAccessTime.QuadPart); + // Magic code to change dir icon to link + eodp->EaSize = + tzp->z_pflags & ZFS_REPARSE ? + 0xa0000003 : + xattr_getsize(ZTOV(tzp)); + eodp->FileAttributes = + zfs_getwinflags(tzp); + nameptr = eodp->FileName; + eodp->FileNameLength = namelenholder; + + break; + + case FileIdBothDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_ID_BOTH_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_ID_BOTH_DIR_INFORMATION *fibdi; + fibdi = (FILE_ID_BOTH_DIR_INFORMATION *) + ctx->bufptr; + next_offset = &fibdi->NextEntryOffset; + + fibdi->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + fibdi->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + fibdi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fibdi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + fibdi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + fibdi->LastAccessTime.QuadPart); + fibdi->EaSize = + tzp->z_pflags & ZFS_REPARSE ? + 0xa0000003 : + xattr_getsize(ZTOV(tzp)); + fibdi->FileAttributes = + zfs_getwinflags(tzp); + fibdi->FileId.QuadPart = objnum; + fibdi->FileIndex = ctx->offset; + fibdi->ShortNameLength = 0; + nameptr = fibdi->FileName; + fibdi->FileNameLength = namelenholder; + + break; + + case FileBothDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_BOTH_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_BOTH_DIR_INFORMATION *fbdi = + (FILE_BOTH_DIR_INFORMATION *)ctx->bufptr; + next_offset = &fbdi->NextEntryOffset; + + fbdi->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + fbdi->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + fbdi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fbdi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + fbdi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + fbdi->LastAccessTime.QuadPart); + fbdi->EaSize = + tzp->z_pflags & ZFS_REPARSE ? + 0xa0000003 : + xattr_getsize(ZTOV(tzp)); + fbdi->FileAttributes = + zfs_getwinflags(tzp); + fbdi->FileIndex = ctx->offset; + fbdi->ShortNameLength = 0; + nameptr = fbdi->FileName; + fbdi->FileNameLength = namelenholder; + + break; + + case FileDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_DIRECTORY_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_DIRECTORY_INFORMATION *fdi = + (FILE_DIRECTORY_INFORMATION *) + ctx->bufptr; + next_offset = &fdi->NextEntryOffset; + + fdi->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + fdi->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + fdi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fdi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + fdi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + fdi->LastAccessTime.QuadPart); + fdi->FileAttributes = + zfs_getwinflags(tzp); + fdi->FileIndex = ctx->offset; + nameptr = fdi->FileName; + fdi->FileNameLength = namelenholder; + break; + + case FileNamesInformation: + structsize = + FIELD_OFFSET(FILE_NAMES_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_NAMES_INFORMATION *fni = + (FILE_NAMES_INFORMATION *)ctx->bufptr; + next_offset = &fni->NextEntryOffset; + + fni->FileIndex = ctx->offset; + nameptr = fni->FileName; + fni->FileNameLength = namelenholder; + break; + + case FileIdFullDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_ID_FULL_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_ID_FULL_DIR_INFORMATION *fifdi = + (FILE_ID_FULL_DIR_INFORMATION *) + ctx->bufptr; + next_offset = &fifdi->NextEntryOffset; + + fifdi->FileIndex = ctx->offset; + 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_REPARSE ? + 0xa0000003 : + xattr_getsize(ZTOV(tzp)); + fifdi->FileAttributes = + zfs_getwinflags(tzp); + fifdi->FileId.QuadPart = tzp->z_id; + nameptr = fifdi->FileName; + fifdi->FileNameLength = namelenholder; + break; + + case FileIdExtdDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_ID_EXTD_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_ID_EXTD_DIR_INFORMATION *fiedi = + (FILE_ID_EXTD_DIR_INFORMATION *) + ctx->bufptr; + next_offset = &fiedi->NextEntryOffset; + + fiedi->FileIndex = ctx->offset; + fiedi->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + fiedi->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + fiedi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fiedi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + fiedi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + fiedi->LastAccessTime.QuadPart); + fiedi->EaSize = + tzp->z_pflags & ZFS_REPARSE ? + 0xa0000003 : + xattr_getsize(ZTOV(tzp)); + fiedi->FileAttributes = + zfs_getwinflags(tzp); + memset(&fiedi->FileId.Identifier[0], 0, + sizeof (fiedi->FileId)); + memcpy(&fiedi->FileId.Identifier[0], + &tzp->z_id, sizeof (tzp->z_id)); + nameptr = fiedi->FileName; + fiedi->FileNameLength = namelenholder; + break; + + case FileIdExtdBothDirectoryInformation: + structsize = FIELD_OFFSET( + FILE_ID_EXTD_BOTH_DIR_INFORMATION, + FileName[0]); + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + break; + FILE_ID_EXTD_BOTH_DIR_INFORMATION *fiebdi = + (FILE_ID_EXTD_BOTH_DIR_INFORMATION *) + ctx->bufptr; + next_offset = &fiebdi->NextEntryOffset; + + fiebdi->FileIndex = ctx->offset; + fiebdi->AllocationSize.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + P2ROUNDUP(tzp->z_size, + zfs_blksz(tzp)); + fiebdi->EndOfFile.QuadPart = + S_ISDIR(tzp->z_mode) ? 0 : + tzp->z_size; + TIME_UNIX_TO_WINDOWS(mtime, + fiebdi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fiebdi->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + fiebdi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(tzp->z_atime, + fiebdi->LastAccessTime.QuadPart); + fiebdi->EaSize = + xattr_getsize(ZTOV(tzp)); + fiebdi->ReparsePointTag = + tzp->z_pflags & ZFS_REPARSE ? + get_reparse_tag(tzp) : 0; + fiebdi->FileAttributes = + zfs_getwinflags(tzp); + fiebdi->ShortNameLength = 0; + memset(&fiebdi->FileId.Identifier[0], 0, + sizeof (fiebdi->FileId)); + memcpy(&fiebdi->FileId.Identifier[0], + &tzp->z_id, sizeof (tzp->z_id)); + nameptr = fiebdi->FileName; + fiebdi->FileNameLength = namelenholder; + break; + + default: + panic("%s unknown listing type %d\n", + __func__, ctx->dirlisttype); + } + + // Release the zp + if (get_zp == 0 && tzp != NULL) { + VN_RELE(ZTOV(tzp)); + } + + // If know we can't fit struct, just leave + if (ctx->outcount + structsize + + namelenholder > ctx->bufsize) + return (ENOSPC); + + rawsize = structsize + namelenholder; + reclen = DIRENT_RECLEN(rawsize); /* align to 8 */ + + /* + * Will this entry fit in the buffer? + * This time with alignment + */ + if (ctx->outcount + reclen > ctx->bufsize) { + + if (!ctx->outcount) { // Nothing found at all? + return (EINVAL); + } + return (ENOSPC); + } + + // If it is going to fit, compute alignment, + // in case this dir entry is the last one, + // we don't align last one. + ctx->last_alignment = reclen - rawsize; + + // Convert the filename over, or as much + // as we can fit + ULONG namelenholder2 = 0; + error = RtlUTF8ToUnicodeN(nameptr, + namelenholder, &namelenholder2, + name, namelen); + ASSERT(namelenholder == namelenholder2); +#if 0 + dprintf("%s: '%.*S' -> '%s' (namelen %d bytes: " + "structsize %d)\n", __func__, + namelenholder / sizeof (WCHAR), nameptr, + name, namelenholder, structsize); +#endif + + /* SEARCH PATTERN */ + if (zccb->searchname.Buffer && zccb->searchname.Length) { + UNICODE_STRING thisname; + // dprintf("%s: '%.*S' -> '%s'\n", __func__, + // tmpnamelen / sizeof(WCHAR), tmpname, zap.za_name); + + thisname.Buffer = nameptr; + thisname.Length = thisname.MaximumLength = namelen; + // wildcard? + if (zccb->ContainsWildCards) { + if (!FsRtlIsNameInExpression(&zccb->searchname, + &thisname, + !(zfsvfs->z_case == ZFS_CASE_SENSITIVE), + NULL)) + return (ESRCH); + } else { + if (!FsRtlAreNamesEqual(&thisname, + &zccb->searchname, + !(zfsvfs->z_case == ZFS_CASE_SENSITIVE), + NULL)) + return (ESRCH); + } +#if 0 + dprintf("comparing names '%.*S' == '%.*S' skip %d\n", + thisname.Length / sizeof (WCHAR), thisname.Buffer, + zccb->searchname.Length / sizeof (WCHAR), + zccb->searchname.Buffer, + skip_this_entry); +#endif + } + /* SEARCH PATTERN */ + + + + + // If we aren't to skip, advance all pointers + VERIFY3P(next_offset, !=, NULL); + ctx->next_offset = next_offset; + *ctx->next_offset = reclen; + + ctx->outcount += reclen; + ctx->bufptr += reclen; + return (0); +} + /* * Lookup/Create an extended attribute entry. * @@ -1394,9 +1923,7 @@ zfs_build_path(znode_t *start_zp, znode_t *start_parent, char **fullpath, uint32_t *returnsize, uint32_t *start_zp_offset) { char *work; - int index, size, part, error; - struct vnode *vp = NULL; - struct vnode *dvp = NULL; + int index, size, part, error = 0; znode_t *zp = NULL; znode_t *dzp = NULL; uint64_t parent; @@ -1437,14 +1964,47 @@ zfs_build_path(znode_t *start_zp, znode_t *start_parent, char **fullpath, __func__, error); goto failed; } + } else if (zfsctl_is_node(zp)) { + struct vnode *vp = NULL; + vp = zfs_root_dotdot(ZTOV(zp)); + // .zfs/snapshot/$name - parent is snapshot + if (vp == NULL) { + dprintf("%s: snapshot dotdot failed %d\n", + __func__, error); + goto failed; + } + dzp = VTOZ(vp); } // dzp held from here. // Find name - if (zp->z_id == zfsvfs->z_root || - zp->z_id == ZFSCTL_INO_ROOT) + if (zp->z_id == zfsvfs->z_root) strlcpy(name, "", MAXPATHLEN); - else + else if (zp->z_id == ZFSCTL_INO_ROOT) + strlcpy(name, ZFS_CTLDIR_NAME, MAXPATHLEN); + else if (zp->z_id == ZFSCTL_INO_SNAPDIR) + strlcpy(name, ZFS_SNAPDIR_NAME, MAXPATHLEN); + else if (zfsctl_is_leafnode(zp)) { + while (error == 0) { + uint64_t id, pos = 0; + boolean_t case_conflict; + dsl_pool_config_enter( + dmu_objset_pool(zfsvfs->z_os), FTAG); + error = dmu_snapshot_list_next(zfsvfs->z_os, + MAXPATHLEN, name, &id, &pos, + &case_conflict); + dsl_pool_config_exit( + dmu_objset_pool(zfsvfs->z_os), FTAG); + if (error == 0 && + (ZFSCTL_INO_SNAPDIRS - id) == zp->z_id) + break; + } + if (error != 0) { + dprintf("%s: snapshot search failed %d\n", + __func__, error); + goto failed; + } + } else if ((error = zap_value_search(zfsvfs->z_os, parent, zp->z_id, ZFS_DIRENT_OBJ(-1ULL), name)) != 0) { dprintf("%s: zap_value_search failed %d\n", @@ -1479,8 +2039,7 @@ zfs_build_path(znode_t *start_zp, znode_t *start_parent, char **fullpath, break; // If parent, stop, "/" is already copied in. - if (zp->z_id == zfsvfs->z_root || - zp->z_id == ZFSCTL_INO_ROOT) + if (zp->z_id == zfsvfs->z_root) break; } @@ -1527,7 +2086,7 @@ zfs_send_notify_stream(zfsvfs_t *zfsvfs, char *name, int nameoffset, AsciiStringToUnicodeString(name, &ustr); - dprintf("%s: '%wZ' part '%S' %u %u\n", __func__, &ustr, + dprintf("%s: '%wZ' part '%S' %lu %u\n", __func__, &ustr, /* &name[nameoffset], */ &ustr.Buffer[nameoffset], FilterMatch, Action); @@ -1705,7 +2264,6 @@ zfs_set_security_root(struct vnode *vp) SECURITY_DESCRIPTOR sd; SID *usersid = NULL, *groupsid = NULL; znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; NTSTATUS Status; ACL *acl = NULL; @@ -1794,7 +2352,9 @@ zfs_set_security(struct vnode *vp, struct vnode *dvp) } dvp = ZTOV(dzp); - } // What to do if no sa_hdl ? + } else { // What to do if no sa_hdl ? + goto err; + } } else { VN_HOLD(dvp); dzp = VTOZ(dvp); @@ -1890,6 +2450,9 @@ xattr_getsize(struct vnode *vp) zp = VTOZ(vp); zfsvfs = zp->z_zfsvfs; + if (!zp->z_is_sa || zp->z_sa_hdl == NULL) + return (0); + /* * Iterate through all the xattrs, adding up namelengths and value sizes. * There was some suggestion that this should be 4 + (5 + name + valuelen) @@ -1956,27 +2519,48 @@ zfs_setunlink(FILE_OBJECT *fo, vnode_t *dvp) zfs_dirlist_t *zccb = fo->FsContext2; zfsvfs_t *zfsvfs; + VN_HOLD(vp); zp = VTOZ(vp); + // Holding vp, not dvp, use "out:" to leave + if (vp && zp) { zfsvfs = zp->z_zfsvfs; } else { Status = STATUS_INVALID_PARAMETER; - goto err; + goto out; + } + + // If it belongs in .zfs, just reply OK. + // mounting will attempted to delete directory + // to replace with reparse point. + if (zfsctl_is_node(zp)) { + if (zfsctl_is_leafnode(zp)) { + fo->DeletePending = TRUE; + ASSERT3P(zccb, !=, NULL); + zccb->deleteonclose = 1; + // We no longer use v_unlink so lets abuse + // it here until we decide we like it + vp->v_unlink = 1; + Status = STATUS_SUCCESS; + goto out; + } + Status = STATUS_CANNOT_DELETE; + goto out; } if (zfsvfs->z_rdonly || vfs_isrdonly(zfsvfs->z_vfs) || !spa_writeable(dmu_objset_spa(zfsvfs->z_os))) { Status = STATUS_MEDIA_WRITE_PROTECTED; - goto err; + goto out; } // Cannot delete a user mapped image. if (!MmFlushImageSection(&vp->SectionObjectPointers, MmFlushForDelete)) { Status = STATUS_CANNOT_DELETE; - goto err; + goto out; } // if dvp == null, find it @@ -1998,6 +2582,9 @@ zfs_setunlink(FILE_OBJECT *fo, vnode_t *dvp) VN_HOLD(dvp); } + + // Holding dvp, use "err:" to leave. + // If we are root if (zp->z_id == zfsvfs->z_root) { Status = STATUS_CANNOT_DELETE; @@ -2008,8 +2595,6 @@ zfs_setunlink(FILE_OBJECT *fo, vnode_t *dvp) // are not empty. if (S_ISDIR(zp->z_mode)) { - int nodeadlock = 0; - if (zp->z_size > 2) { Status = STATUS_DIRECTORY_NOT_EMPTY; goto err; @@ -2028,16 +2613,17 @@ zfs_setunlink(FILE_OBJECT *fo, vnode_t *dvp) } err: - if (vp) { - VN_RELE(vp); - vp = NULL; - } - if (dvp) { VN_RELE(dvp); dvp = NULL; } +out: + if (vp) { + VN_RELE(vp); + vp = NULL; + } + return (Status); } @@ -2065,10 +2651,89 @@ dmu_buf_try_add_ref(dmu_buf_t *db, objset_t *os, uint64_t object, NTSTATUS -file_disposition_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, +set_file_basic_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { - NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if (IrpSp->FileObject == NULL || IrpSp->FileObject->FsContext == NULL) + return (STATUS_INVALID_PARAMETER); + + PFILE_OBJECT FileObject = IrpSp->FileObject; + struct vnode *vp = FileObject->FsContext; + mount_t *zmo = DeviceObject->DeviceExtension; + + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + + if (VN_HOLD(vp) == 0 && VTOZ(vp) != NULL) { + FILE_BASIC_INFORMATION *fbi = + Irp->AssociatedIrp.SystemBuffer; + vattr_t va = { 0 }; + uint64_t unixtime[2] = { 0 }; + znode_t *zp = VTOZ(vp); + +// can request that the file system not update .. LastAccessTime, +// LastWriteTime, and ChangeTime .. setting the appropriate members to -1. +// ie, LastAccessTime = -1 -> atime = disabled - not implemented +// LastAccessTime = -2 -> cancel the disable (-1), return to normal. +// a value of "0" means to keep existing value. + if (fbi->ChangeTime.QuadPart > 0) { + TIME_WINDOWS_TO_UNIX(fbi->ChangeTime.QuadPart, + unixtime); + va.va_change_time.tv_sec = unixtime[0]; + va.va_change_time.tv_nsec = unixtime[1]; + va.va_active |= ATTR_CTIME; + } + if (fbi->LastWriteTime.QuadPart > 0) { + TIME_WINDOWS_TO_UNIX( + fbi->LastWriteTime.QuadPart, + unixtime); + va.va_modify_time.tv_sec = unixtime[0]; + va.va_modify_time.tv_nsec = unixtime[1]; + va.va_active |= ATTR_MTIME; + } + if (fbi->CreationTime.QuadPart > 0) { + TIME_WINDOWS_TO_UNIX(fbi->CreationTime.QuadPart, + unixtime); + va.va_create_time.tv_sec = unixtime[0]; + va.va_create_time.tv_nsec = unixtime[1]; + va.va_active |= ATTR_CRTIME; // ATTR_CRTIME + } + if (fbi->LastAccessTime.QuadPart > 0) + TIME_WINDOWS_TO_UNIX( + fbi->LastAccessTime.QuadPart, + zp->z_atime); + + if (fbi->FileAttributes) + if (zfs_setwinflags(VTOZ(vp), + fbi->FileAttributes)) + va.va_active |= ATTR_MODE; + + Status = zfs_setattr(zp, &va, 0, NULL, NULL); + + // zfs_setattr will turn ARCHIVE back on, when perhaps + // it is set off by this call + if (fbi->FileAttributes) + zfs_setwinflags(zp, fbi->FileAttributes); + + VN_RELE(vp); + } + + return (Status); +} + +NTSTATUS +set_file_disposition_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, + PIO_STACK_LOCATION IrpSp) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; if (IrpSp->FileObject == NULL || IrpSp->FileObject->FsContext == NULL) return (STATUS_INVALID_PARAMETER); @@ -2076,12 +2741,19 @@ file_disposition_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PFILE_OBJECT FileObject = IrpSp->FileObject; struct vnode *vp = FileObject->FsContext; zfs_dirlist_t *zccb = FileObject->FsContext2; - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; FILE_DISPOSITION_INFORMATION *fdi = Irp->AssociatedIrp.SystemBuffer; mount_t *zmo = DeviceObject->DeviceExtension; - if (vp) { + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + + if (VN_HOLD(vp) == 0 && VTOZ(vp) != NULL) { dprintf("Deletion %s on '%wZ'\n", fdi->DeleteFile ? "set" : "unset", IrpSp->FileObject->FileName); @@ -2098,15 +2770,17 @@ file_disposition_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, FsRtlNotifyCleanup(zmo->NotifySync, &zmo->DirNotifyList, VTOZ(vp)); } + + VN_RELE(vp); } return (Status); } NTSTATUS -file_disposition_information_ex(PDEVICE_OBJECT DeviceObject, PIRP Irp, +set_file_disposition_information_ex(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { - NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS Status = STATUS_INVALID_PARAMETER; if (IrpSp->FileObject == NULL || IrpSp->FileObject->FsContext == NULL) return (STATUS_INVALID_PARAMETER); @@ -2114,16 +2788,23 @@ file_disposition_information_ex(PDEVICE_OBJECT DeviceObject, PIRP Irp, PFILE_OBJECT FileObject = IrpSp->FileObject; struct vnode *vp = FileObject->FsContext; zfs_dirlist_t *zccb = FileObject->FsContext2; - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; FILE_DISPOSITION_INFORMATION_EX *fdie = Irp->AssociatedIrp.SystemBuffer; mount_t *zmo = DeviceObject->DeviceExtension; - if (vp) { + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + + if (VN_HOLD(vp) && VTOZ(vp) != NULL) { Status = STATUS_SUCCESS; - dprintf("%s: Flags 0x%x\n", __func__, fdie->Flags); + dprintf("%s: Flags 0x%lx\n", __func__, fdie->Flags); if (fdie->Flags | FILE_DISPOSITION_ON_CLOSE) if (fdie->Flags | FILE_DISPOSITION_DELETE) @@ -2140,12 +2821,14 @@ file_disposition_information_ex(PDEVICE_OBJECT DeviceObject, PIRP Irp, FsRtlNotifyCleanup(zmo->NotifySync, &zmo->DirNotifyList, VTOZ(vp)); } + + VN_RELE(vp); } return (Status); } NTSTATUS -file_endoffile_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, +set_file_endoffile_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Status = STATUS_SUCCESS; @@ -2156,11 +2839,16 @@ file_endoffile_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PFILE_OBJECT FileObject = IrpSp->FileObject; struct vnode *vp = FileObject->FsContext; zfs_dirlist_t *zccb = FileObject->FsContext2; - znode_t *zp = VTOZ(vp); - zfsvfs_t *zfsvfs = zp->z_zfsvfs; FILE_END_OF_FILE_INFORMATION *feofi = Irp->AssociatedIrp.SystemBuffer; int changed = 0; int error = 0; + mount_t *zmo = DeviceObject->DeviceExtension; + + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); if (zfsvfs == NULL) return (STATUS_INVALID_PARAMETER); @@ -2175,6 +2863,8 @@ file_endoffile_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, return (STATUS_INVALID_PARAMETER); } + znode_t *zp = VTOZ(vp); + // From FASTFAT // This is kinda gross, but if the file is not cached, but there is // a data section, we have to cache the file to avoid a bunch of @@ -2267,10 +2957,9 @@ file_endoffile_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, // create hardlink by calling zfs_create NTSTATUS -file_link_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, +set_file_link_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { - NTSTATUS Status; /* * typedef struct _FILE_LINK_INFORMATION { * BOOLEAN ReplaceIfExists; @@ -2282,7 +2971,7 @@ file_link_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, FILE_LINK_INFORMATION *link = Irp->AssociatedIrp.SystemBuffer; dprintf("* FileLinkInformation: %.*S\n", - link->FileNameLength / sizeof (WCHAR), link->FileName); + (int)link->FileNameLength / sizeof (WCHAR), link->FileName); // So, use FileObject to get VP. // Use VP to lookup parent. @@ -2290,12 +2979,22 @@ file_link_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (IrpSp->FileObject == NULL || IrpSp->FileObject->FsContext == NULL) return (STATUS_INVALID_PARAMETER); + mount_t *zmo = DeviceObject->DeviceExtension; + + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + FILE_OBJECT *RootFileObject = NULL; PFILE_OBJECT FileObject = IrpSp->FileObject; struct vnode *fvp = FileObject->FsContext; znode_t *zp = VTOZ(fvp); znode_t *dzp = NULL; - zfsvfs_t *zfsvfs = zp->z_zfsvfs; int error; ULONG outlen; char *remainder = NULL; @@ -2405,7 +3104,7 @@ file_link_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, } NTSTATUS -file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, +set_file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { NTSTATUS Status; @@ -2434,7 +3133,7 @@ file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, FILE_RENAME_INFORMATION *ren = Irp->AssociatedIrp.SystemBuffer; dprintf("* FileRenameInformation: %.*S\n", - ren->FileNameLength / sizeof (WCHAR), ren->FileName); + (int)ren->FileNameLength / sizeof (WCHAR), ren->FileName); // ASSERT(ren->RootDirectory == NULL); @@ -2444,11 +3143,21 @@ file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (IrpSp->FileObject == NULL || IrpSp->FileObject->FsContext == NULL) return (STATUS_INVALID_PARAMETER); + mount_t *zmo = DeviceObject->DeviceExtension; + + zfsvfs_t *zfsvfs = NULL; + if (zmo != NULL && + (zfsvfs = vfs_fsprivate(zmo)) != NULL && + zfsvfs->z_rdonly) + return (STATUS_MEDIA_WRITE_PROTECTED); + + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + PFILE_OBJECT FileObject = IrpSp->FileObject; struct vnode *fvp = FileObject->FsContext; znode_t *zp = VTOZ(fvp); znode_t *dzp = NULL; - zfsvfs_t *zfsvfs = zp->z_zfsvfs; int error; ULONG outlen; char *remainder = NULL; @@ -2490,7 +3199,7 @@ file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, // If it starts with "\" drive the lookup, if it is just a name // like "HEAD", assume tdvp is same as fdvp. - if ((filename[0] == '\\')) { + if (filename[0] == '\\') { OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK ioStatus; UNICODE_STRING uFileName; @@ -2658,20 +3367,23 @@ file_rename_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, ULONG get_reparse_tag(znode_t *zp) { - if (zp->z_pflags & ZFS_REPARSE) { - int err; - REPARSE_DATA_BUFFER tagdata; - struct iovec iov; - iov.iov_base = (void *)&tagdata; - iov.iov_len = sizeof (tagdata); + if (!(zp->z_pflags & ZFS_REPARSE)) + return (0); - zfs_uio_t uio; - zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, - sizeof (tagdata), 0); - err = zfs_readlink(ZTOV(zp), &uio, NULL); - return (tagdata.ReparseTag); - } - return (0); + if (zfsctl_is_node(zp)) + return (zfsctl_get_reparse_tag(zp)); + + int err; + REPARSE_DATA_BUFFER tagdata; + struct iovec iov; + iov.iov_base = (void *)&tagdata; + iov.iov_len = sizeof (tagdata); + + zfs_uio_t uio; + zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, + sizeof (tagdata), 0); + err = zfs_readlink(ZTOV(zp), &uio, NULL); + return (tagdata.ReparseTag); } NTSTATUS @@ -2738,28 +3450,32 @@ file_basic_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (VN_HOLD(vp) == 0) { znode_t *zp = VTOZ(vp); 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]; - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), - NULL, &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), - NULL, &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), - NULL, &crtime, 16); - sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - - TIME_UNIX_TO_WINDOWS(mtime, - basic->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - basic->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - basic->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(zp->z_atime, - basic->LastAccessTime.QuadPart); - + if (zp->z_is_sa) { + sa_bulk_attr_t bulk[3]; + int count = 0; + uint64_t mtime[2]; + uint64_t ctime[2]; + uint64_t crtime[2]; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_MTIME(zfsvfs), + NULL, &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CTIME(zfsvfs), + NULL, &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CRTIME(zfsvfs), + NULL, &crtime, 16); + sa_bulk_lookup(zp->z_sa_hdl, bulk, count); + + TIME_UNIX_TO_WINDOWS(mtime, + basic->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + basic->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + basic->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(zp->z_atime, + basic->LastAccessTime.QuadPart); + } // FileAttributes == 0 means don't set // - undocumented, but seen in fastfat // if (basic->FileAttributes != 0) @@ -2774,17 +3490,21 @@ file_basic_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, // This can be called from diskDispatcher, referring to the volume. // if so, make something up. Is this the right thing to do? if (IrpSp->FileObject && IrpSp->FileObject->FsContext == NULL) { + mount_t *zmo = DeviceObject->DeviceExtension; + zfsvfs_t *zfsvfs = vfs_fsprivate(zmo); + LARGE_INTEGER JanOne1980 = { 0xe1d58000, 0x01a8e79f }; ExLocalTimeToSystemTime(&JanOne1980, &basic->LastWriteTime); basic->CreationTime = basic->LastAccessTime = basic->LastWriteTime; - basic->FileAttributes = FILE_ATTRIBUTE_NORMAL; + basic->FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + if (zfsvfs->z_rdonly) + basic->FileAttributes |= FILE_ATTRIBUTE_READONLY; Irp->IoStatus.Information = sizeof (FILE_BASIC_INFORMATION); return (STATUS_SUCCESS); } - ASSERT(basic->FileAttributes != 0); dprintf(" %s failing\n", __func__); return (STATUS_OBJECT_NAME_NOT_FOUND); } @@ -2964,27 +3684,32 @@ file_network_open_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, struct vnode *vp = IrpSp->FileObject->FsContext; znode_t *zp = VTOZ(vp); 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]; - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, - &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, - &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, - &crtime, 16); - sa_bulk_lookup(zp->z_sa_hdl, bulk, count); + if (zp->z_is_sa) { + sa_bulk_attr_t bulk[3]; + int count = 0; + uint64_t mtime[2]; + uint64_t ctime[2]; + uint64_t crtime[2]; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_MTIME(zfsvfs), NULL, + &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CRTIME(zfsvfs), NULL, + &crtime, 16); + sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - TIME_UNIX_TO_WINDOWS(mtime, - netopen->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - netopen->ChangeTime.QuadPart); - TIME_UNIX_TO_WINDOWS(crtime, - netopen->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(zp->z_atime, - netopen->LastAccessTime.QuadPart); + TIME_UNIX_TO_WINDOWS(mtime, + netopen->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + netopen->ChangeTime.QuadPart); + TIME_UNIX_TO_WINDOWS(crtime, + netopen->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(zp->z_atime, + netopen->LastAccessTime.QuadPart); + } netopen->AllocationSize.QuadPart = P2ROUNDUP(zp->z_size, zfs_blksz(zp)); netopen->EndOfFile.QuadPart = vnode_isdir(vp) ? 0 : zp->z_size; @@ -3101,25 +3826,33 @@ file_stat_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; + if (zp->z_is_sa) { + sa_bulk_attr_t bulk[3]; + int count = 0; + uint64_t mtime[2]; + uint64_t ctime[2]; + uint64_t crtime[2]; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_MTIME(zfsvfs), NULL, + &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CTIME(zfsvfs), NULL, + &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_CRTIME(zfsvfs), NULL, + &crtime, 16); + sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - sa_bulk_attr_t bulk[3]; - int count = 0; - uint64_t mtime[2]; - uint64_t ctime[2]; - uint64_t crtime[2]; - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, - &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, - &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, - &crtime, 16); - sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - + TIME_UNIX_TO_WINDOWS(crtime, + fsi->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(zp->z_atime, + fsi->LastAccessTime.QuadPart); + TIME_UNIX_TO_WINDOWS(mtime, + fsi->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fsi->ChangeTime.QuadPart); + } fsi->FileId.QuadPart = zp->z_id; - TIME_UNIX_TO_WINDOWS(crtime, fsi->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(zp->z_atime, fsi->LastAccessTime.QuadPart); - TIME_UNIX_TO_WINDOWS(mtime, fsi->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, fsi->ChangeTime.QuadPart); fsi->AllocationSize.QuadPart = P2ROUNDUP(zp->z_size, zfs_blksz(zp)); fsi->EndOfFile.QuadPart = zp->z_size; @@ -3170,29 +3903,30 @@ file_stat_lx_information(PDEVICE_OBJECT DeviceObject, PIRP Irp, if (vp) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; + if (zp->z_is_sa) { + sa_bulk_attr_t bulk[3]; + int count = 0; + uint64_t mtime[2]; + uint64_t ctime[2]; + uint64_t crtime[2]; + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), + NULL, &mtime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), + NULL, &ctime, 16); + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), + NULL, &crtime, 16); + sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - sa_bulk_attr_t bulk[3]; - int count = 0; - uint64_t mtime[2]; - uint64_t ctime[2]; - uint64_t crtime[2]; - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, - &mtime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, - &ctime, 16); - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, - &crtime, 16); - sa_bulk_lookup(zp->z_sa_hdl, bulk, count); - + TIME_UNIX_TO_WINDOWS(crtime, + fsli->CreationTime.QuadPart); + TIME_UNIX_TO_WINDOWS(zp->z_atime, + fsli->LastAccessTime.QuadPart); + TIME_UNIX_TO_WINDOWS(mtime, + fsli->LastWriteTime.QuadPart); + TIME_UNIX_TO_WINDOWS(ctime, + fsli->ChangeTime.QuadPart); + } fsli->FileId.QuadPart = zp->z_id; - TIME_UNIX_TO_WINDOWS(crtime, - fsli->CreationTime.QuadPart); - TIME_UNIX_TO_WINDOWS(zp->z_atime, - fsli->LastAccessTime.QuadPart); - TIME_UNIX_TO_WINDOWS(mtime, - fsli->LastWriteTime.QuadPart); - TIME_UNIX_TO_WINDOWS(ctime, - fsli->ChangeTime.QuadPart); fsli->AllocationSize.QuadPart = P2ROUNDUP(zp->z_size, zfs_blksz(zp)); fsli->EndOfFile.QuadPart = zp->z_size; @@ -3565,6 +4299,47 @@ QueryCapabilities(PDEVICE_OBJECT DeviceObject, PIRP Irp, return (STATUS_SUCCESS); } +NTSTATUS +ioctl_get_gpt_attributes(PDEVICE_OBJECT DeviceObject, PIRP Irp, + PIO_STACK_LOCATION IrpSp) +{ + mount_t *zmo; + NTSTATUS Status; + VOLUME_GET_GPT_ATTRIBUTES_INFORMATION *vggai; + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION)) { + Irp->IoStatus.Information = + sizeof (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); + return (STATUS_BUFFER_TOO_SMALL); + } + + zmo = (mount_t *)DeviceObject->DeviceExtension; + vggai = (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION *) + Irp->AssociatedIrp.SystemBuffer; + + if (!zmo || + (zmo->type != MOUNT_TYPE_VCB && + zmo->type != MOUNT_TYPE_DCB)) { + return (STATUS_INVALID_PARAMETER); + } + + zfsvfs_t *zfsvfs = vfs_fsprivate(zmo); + if (zfsvfs == NULL) + return (STATUS_INVALID_PARAMETER); + + Irp->IoStatus.Information = + sizeof (VOLUME_GET_GPT_ATTRIBUTES_INFORMATION); + + if (zfsvfs->z_rdonly) + vggai->GptAttributes = + GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY; + else + vggai->GptAttributes = 0; + + return (STATUS_SUCCESS); +} + // // If overflow, set Information to sizeof (MOUNTDEV_NAME), and // NameLength to required size. diff --git a/module/os/windows/zfs/zfs_vnops_windows_mount.c b/module/os/windows/zfs/zfs_vnops_windows_mount.c index 4a5cd33c4b48..32abee53be20 100644 --- a/module/os/windows/zfs/zfs_vnops_windows_mount.c +++ b/module/os/windows/zfs/zfs_vnops_windows_mount.c @@ -716,6 +716,10 @@ zfs_windows_mount(zfs_cmd_t *zc) mnt_args.mflag = 0; // Set flags mnt_args.fspec = zc->zc_name; + // zc_cleanup_fd carrier mount flags for now. + if (zc->zc_cleanup_fd & MNT_RDONLY) + vfs_setrdonly(zmo_dcb); + // Mount will temporarily be pointing to "dcb" until the // zfs_vnop_mount() below corrects it to "vcb". status = zfs_vfs_mount(zmo_dcb, NULL, (user_addr_t)&mnt_args, NULL); @@ -1081,7 +1085,6 @@ zfs_vnop_mount(PDEVICE_OBJECT DiskDevice, PIRP Irp, PIO_STACK_LOCATION IrpSp) goto out; } #endif - mount_t *dcb = DeviceToMount->DeviceExtension; if (dcb == NULL) { dprintf("%s: Not a ZFS dataset -- ignoring\n", __func__); @@ -1109,6 +1112,9 @@ zfs_vnop_mount(PDEVICE_OBJECT DiskDevice, PIRP Irp, PIO_STACK_LOCATION IrpSp) if (!zfs_disable_removablemedia) deviceCharacteristics |= FILE_REMOVABLE_MEDIA; + if (dcb->mountflags & MNT_RDONLY) + deviceCharacteristics |= FILE_READ_ONLY_DEVICE; + status = IoCreateDevice(DriverObject, sizeof (mount_t), NULL, @@ -1133,7 +1139,7 @@ zfs_vnop_mount(PDEVICE_OBJECT DiskDevice, PIRP Irp, PIO_STACK_LOCATION IrpSp) // Move the fsprivate ptr from dcb to vcb vfs_setfsprivate(vcb, vfs_fsprivate(dcb)); // HACK - vfs_setfsprivate(dcb, NULL); + // vfs_setfsprivate(dcb, NULL); zfsvfs_t *zfsvfs = vfs_fsprivate(vcb); if (zfsvfs == NULL) { dprintf("zfsvfs not resolved yet\n"); @@ -1154,8 +1160,11 @@ zfs_vnop_mount(PDEVICE_OBJECT DiskDevice, PIRP Irp, PIO_STACK_LOCATION IrpSp) RtlDuplicateUnicodeString(0, &dcb->device_name, &vcb->device_name); RtlDuplicateUnicodeString(0, &dcb->symlink_name, &vcb->symlink_name); RtlDuplicateUnicodeString(0, &dcb->uuid, &vcb->uuid); + RtlDuplicateUnicodeString(0, &dcb->mountpoint, &vcb->mountpoint); vcb->mountflags = dcb->mountflags; + if (vfs_isrdonly(dcb)) + vfs_setrdonly(vcb); // Directory notification InitializeListHead(&vcb->DirNotifyList); @@ -1301,7 +1310,6 @@ zfs_vnop_mount(PDEVICE_OBJECT DiskDevice, PIRP Irp, PIO_STACK_LOCATION IrpSp) status = mountmgr_get_drive_letter(mountmgr, &dcb->device_name, namex); } else { - OBJECT_ATTRIBUTES poa; // 36(uuid) + 6 (punct) + 6 (Volume) DECLARE_UNICODE_STRING_SIZE(volStr, @@ -1630,6 +1638,7 @@ zfs_windows_unmount(zfs_cmd_t *zc) // If mount uses reparsepoint (not driveletter) OBJECT_ATTRIBUTES poa; + InitializeObjectAttributes(&poa, &zmo_dcb->mountpoint, OBJ_KERNEL_HANDLE, NULL, NULL); dprintf("Deleting reparse mountpoint '%wZ'\n",