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",