Module Name: src
Committed By: hannken
Date: Tue Feb 5 09:55:48 UTC 2019
Modified Files:
src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_ctldir.c
Log Message:
Implement the ZFS control directory ".zfs" and its subdirectory 'snapshot".
Automatically mount snapshots on access of ".zfs/snapshot/<snapname>".
To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 \
src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c:1.4 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c:1.5
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c:1.4 Tue Feb 5 09:54:36 2019
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c Tue Feb 5 09:55:48 2019
@@ -1261,14 +1261,584 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int
#ifdef __NetBSD__
+#include <sys/fstrans.h>
+#include <sys/malloc.h>
#include <sys/pathname.h>
+#include <miscfs/genfs/genfs.h>
#include <sys/zfs_context.h>
#include <sys/zfs_ctldir.h>
+#include <sys/dsl_dataset.h>
+#include <sys/zap.h>
+
+struct zfsctl_root {
+ timestruc_t zc_cmtime;
+};
+
+struct sfs_node_key {
+ uint64_t parent_id;
+ uint64_t id;
+};
+struct sfs_node {
+ struct sfs_node_key sn_key;
+#define sn_parent_id sn_key.parent_id
+#define sn_id sn_key.id
+ lwp_t *sn_mounting;
+};
+
+#define ZFS_SNAPDIR_NAME "snapshot"
+
+#define VTOSFS(vp) ((struct sfs_node *)((vp)->v_data))
+
+#define SFS_NODE_ASSERT(vp) \
+ do { \
+ struct sfs_node *np = VTOSFS(vp); \
+ ASSERT((vp)->v_op == zfs_sfsop_p); \
+ ASSERT((vp)->v_type == VDIR); \
+ } while (/*CONSTCOND*/ 0)
static int (**zfs_sfsop_p)(void *);
-static const struct vnodeopv_entry_desc zfs_sfsop_entries[] = {
+/*
+ * Mount a snapshot. Cannot use do_sys_umount() as it
+ * doesn't allow its "path" argument from SYSSPACE.
+ */
+static int
+sfs_snapshot_mount(vnode_t *vp, const char *snapname)
+{
+ struct sfs_node *node = VTOSFS(vp);
+ zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
+ vfs_t *vfsp;
+ char *path, *osname;
+ int error;
+ extern int zfs_domount(vfs_t *, char *);
+
+ path = PNBUF_GET();
+ osname = PNBUF_GET();
+
+ dmu_objset_name(zfsvfs->z_os, path);
+ snprintf(osname, MAXPATHLEN, "%s@%s", path, snapname);
+ snprintf(path, MAXPATHLEN,
+ "%s/" ZFS_CTLDIR_NAME "/" ZFS_SNAPDIR_NAME "/%s",
+ vp->v_vfsp->mnt_stat.f_mntonname, snapname);
+
+ vfsp = vfs_mountalloc(vp->v_vfsp->mnt_op, vp);
+ if (vfsp == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ vfsp->mnt_op->vfs_refcount++;
+ vfsp->mnt_stat.f_owner = 0;
+ vfsp->mnt_flag = MNT_RDONLY | MNT_NOSUID | MNT_IGNORE;
+
+ mutex_enter(&vfsp->mnt_updating);
+
+ error = zfs_domount(vfsp, osname);
+ if (error)
+ goto out;
+
+ vfs_getnewfsid(vfsp);
+ strlcpy(vfsp->mnt_stat.f_mntfromname, osname,
+ sizeof(vfsp->mnt_stat.f_mntfromname));
+ set_statvfs_info(path, UIO_SYSSPACE, vfsp->mnt_stat.f_mntfromname,
+ UIO_SYSSPACE, vfsp->mnt_op->vfs_name, vfsp, curlwp);
+
+ vfsp->mnt_lower = vp->v_vfsp;
+
+ mountlist_append(vfsp);
+ vref(vp);
+ vp->v_mountedhere = vfsp;
+
+ mutex_exit(&vfsp->mnt_updating);
+ (void) VFS_STATVFS(vfsp, &vfsp->mnt_stat);
+
+out:;
+ if (error && vfsp) {
+ mutex_exit(&vfsp->mnt_updating);
+ fstrans_unmount(vfsp);
+ vfs_rele(vfsp);
+ }
+ PNBUF_PUT(osname);
+ PNBUF_PUT(path);
+
+ return error;
+}
+
+static int
+sfs_lookup_snapshot(vnode_t *dvp, struct componentname *cnp, vnode_t **vpp)
+{
+ zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
+ vnode_t *vp;
+ struct sfs_node *node;
+ struct sfs_node_key key;
+ char snapname[ZFS_MAX_DATASET_NAME_LEN];
+ int error;
+
+ /* Retrieve the snapshot object id and the to be mounted on vnode. */
+ if (cnp->cn_namelen >= sizeof(snapname))
+ return ENOENT;
+
+ strlcpy(snapname, cnp->cn_nameptr, cnp->cn_namelen + 1);
+ error = dsl_dataset_snap_lookup( dmu_objset_ds(zfsvfs->z_os),
+ snapname, &key.id);
+ if (error)
+ return error;
+ key.parent_id = ZFSCTL_INO_SNAPDIR;
+ error = vcache_get(zfsvfs->z_vfs, &key, sizeof(key), vpp);
+ if (error)
+ return error;
+
+ /* Handle case where the vnode is currently mounting. */
+ vp = *vpp;
+ mutex_enter(vp->v_interlock);
+ node = VTOSFS(vp);
+ if (node->sn_mounting) {
+ if (node->sn_mounting == curlwp)
+ error = 0;
+ else
+ error = ERESTART;
+ mutex_exit(vp->v_interlock);
+ if (error)
+ yield();
+ return error;
+ }
+
+ /* If not yet mounted mount the snapshot. */
+ if (vp->v_mountedhere == NULL) {
+ ASSERT(node->sn_mounting == NULL);
+ node->sn_mounting = curlwp;
+ mutex_exit(vp->v_interlock);
+
+ VOP_UNLOCK(dvp, 0);
+ error = sfs_snapshot_mount(vp, snapname);
+ if (vn_lock(dvp, LK_EXCLUSIVE) != 0) {
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+ error = ENOENT;
+ }
+
+ mutex_enter(vp->v_interlock);
+ if ((node = VTOSFS(vp)))
+ node->sn_mounting = NULL;
+ mutex_exit(vp->v_interlock);
+
+ if (error) {
+ vrele(vp);
+ *vpp = NULL;
+ return error;
+ }
+ } else
+ mutex_exit(vp->v_interlock);
+
+ /* Return the mounted root rather than the covered mount point. */
+ ASSERT(vp->v_mountedhere);
+ error = VFS_ROOT(vp->v_mountedhere, vpp);
+ vrele(vp);
+ if (error)
+ return error;
+
+ /*
+ * Fix up the root vnode mounted on .zfs/snapshot/<snapname>
+ *
+ * Here we make .zfs/snapshot/<snapname> accessible over NFS
+ * without requiring manual mounts of <snapname>.
+ */
+ if (((*vpp)->v_vflag & VV_ROOT)) {
+ ASSERT(VTOZ(*vpp)->z_zfsvfs != zfsvfs);
+ VTOZ(*vpp)->z_zfsvfs->z_parent = zfsvfs;
+ (*vpp)->v_vflag &= ~VV_ROOT;
+ }
+ VOP_UNLOCK(*vpp, 0);
+
+ return 0;
+}
+
+static int
+sfs_lookup(void *v)
+{
+ struct vop_lookup_v2_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ vnode_t *dvp = ap->a_dvp;
+ vnode_t **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
+ struct sfs_node *dnode = VTOSFS(dvp);
+ int error;
+
+ SFS_NODE_ASSERT(dvp);
+ ZFS_ENTER(zfsvfs);
+
+ /*
+ * No CREATE, DELETE or RENAME.
+ */
+ if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop != LOOKUP) {
+ ZFS_EXIT(zfsvfs);
+
+ return ENOTSUP;
+ }
+
+ /*
+ * Handle DOT and DOTDOT.
+ */
+ if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
+ vref(dvp);
+ *vpp = dvp;
+ ZFS_EXIT(zfsvfs);
+
+ return 0;
+ }
+ if ((cnp->cn_flags & ISDOTDOT)) {
+ if (dnode->sn_parent_id == 0) {
+ error = vcache_get(zfsvfs->z_vfs,
+ &zfsvfs->z_root, sizeof(zfsvfs->z_root), vpp);
+ } else if (dnode->sn_parent_id == ZFSCTL_INO_ROOT) {
+ error = zfsctl_root(zfsvfs, vpp);
+ } else if (dnode->sn_parent_id == ZFSCTL_INO_SNAPDIR) {
+ error = zfsctl_snapshot(zfsvfs, vpp);
+ } else {
+ error = ENOENT;
+ }
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+ }
+
+ /*
+ * Lookup in ".zfs".
+ */
+ if (dnode->sn_id == ZFSCTL_INO_ROOT) {
+ if (cnp->cn_namelen == strlen(ZFS_SNAPDIR_NAME) &&
+ strncmp(cnp->cn_nameptr, ZFS_SNAPDIR_NAME,
+ cnp->cn_namelen) == 0) {
+ error = zfsctl_snapshot(zfsvfs, vpp);
+ } else {
+ error = ENOENT;
+ }
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+ }
+
+ /*
+ * Lookup in ".zfs/snapshot".
+ */
+ if (dnode->sn_id == ZFSCTL_INO_SNAPDIR) {
+ error = sfs_lookup_snapshot(dvp, cnp, vpp);
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+ }
+
+ vprint("sfs_lookup: unexpected node for lookup", dvp);
+ ZFS_EXIT(zfsvfs);
+
+ return ENOENT;
+}
+
+static int
+sfs_open(void *v)
+{
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ kauth_cred_t a_cred;
+ } */ *ap = v;
+ zfsvfs_t *zfsvfs = ap->a_vp->v_vfsp->vfs_data;
+ int error = 0;
+
+ SFS_NODE_ASSERT(ap->a_vp);
+ ZFS_ENTER(zfsvfs);
+
+ if (ap->a_mode & FWRITE)
+ error = EACCES;
+
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+}
+
+static int
+sfs_close(void *v)
+{
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ kauth_cred_t a_cred;
+ } */ *ap = v;
+ zfsvfs_t *zfsvfs = ap->a_vp->v_vfsp->vfs_data;
+
+ SFS_NODE_ASSERT(ap->a_vp);
+ ZFS_ENTER(zfsvfs);
+
+ ZFS_EXIT(zfsvfs);
+
+ return 0;
+}
+
+static int
+sfs_access(void *v)
+{
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ kauth_cred_t a_cred;
+ } */ *ap = v;
+ zfsvfs_t *zfsvfs = ap->a_vp->v_vfsp->vfs_data;
+ int error = 0;
+
+ SFS_NODE_ASSERT(ap->a_vp);
+ ZFS_ENTER(zfsvfs);
+
+ if (ap->a_mode & FWRITE)
+ error = EACCES;
+
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+}
+
+static int
+sfs_getattr(void *v)
+{
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ kauth_cred_t a_cred;
+ } */ *ap = v;
+ vnode_t *vp = ap->a_vp;
+ struct sfs_node *node = VTOSFS(vp);
+ struct vattr *vap = ap->a_vap;
+ zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
+ dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os);
+ timestruc_t now;
+ uint64_t snap_count;
+ int error;
+
+ SFS_NODE_ASSERT(vp);
+ ZFS_ENTER(zfsvfs);
+
+ vap->va_type = VDIR;
+ vap->va_mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH;
+ vap->va_nlink = 2;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_vfsp->mnt_stat.f_fsidx.__fsid_val[0];
+ vap->va_fileid = node->sn_id;
+ vap->va_size = 0;
+ vap->va_blocksize = 0;
+ gethrestime(&now);
+ vap->va_atime = now;
+ vap->va_ctime = zfsvfs->z_ctldir->zc_cmtime;
+ vap->va_mtime = vap->va_ctime;
+ vap->va_birthtime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ vap->va_bytes = 0;
+ vap->va_filerev = 0;
+
+ switch (node->sn_id){
+ case ZFSCTL_INO_ROOT:
+ vap->va_nlink += 1; /* snapdir */
+ vap->va_size = vap->va_nlink;
+ break;
+ case ZFSCTL_INO_SNAPDIR:
+ if (dsl_dataset_phys(ds)->ds_snapnames_zapobj) {
+ error = zap_count(
+ dmu_objset_pool(ds->ds_objset)->dp_meta_objset,
+ dsl_dataset_phys(ds)->ds_snapnames_zapobj,
+ &snap_count);
+ if (error)
+ return error;
+ vap->va_nlink += snap_count;
+ }
+ vap->va_size = vap->va_nlink;
+ break;
+ }
+
+ ZFS_EXIT(zfsvfs);
+
+ return 0;
+}
+
+static int
+sfs_readdir_one(struct vop_readdir_args *ap, struct dirent *dp,
+ const char *name, ino_t ino, off_t *offp)
+{
+ int error;
+
+ dp->d_fileno = ino;
+ dp->d_type = DT_DIR;
+ strlcpy(dp->d_name, name, sizeof(dp->d_name));
+ dp->d_namlen = strlen(dp->d_name);
+ dp->d_reclen = _DIRENT_SIZE(dp);
+
+ if (ap->a_uio->uio_resid < dp->d_reclen)
+ return ENAMETOOLONG;
+ if (ap->a_uio->uio_offset > *offp) {
+ *offp += dp->d_reclen;
+ return 0;
+ }
+
+ error = uiomove(dp, dp->d_reclen, UIO_READ, ap->a_uio);
+ if (error)
+ return error;
+ if (ap->a_ncookies)
+ (*ap->a_cookies)[(*ap->a_ncookies)++] = *offp;
+ *offp += dp->d_reclen;
+
+ return 0;
+}
+
+static int
+sfs_readdir(void *v)
+{
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ kauth_cred_t a_cred;
+ int *a_eofflag;
+ off_t **a_cookies;
+ int *a_ncookies;
+ } */ *ap = v;
+ vnode_t *vp = ap->a_vp;
+ struct sfs_node *node = VTOSFS(vp);
+ zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
+ struct dirent *dp;
+ uint64_t parent;
+ off_t offset;
+ int error, ncookies;
+
+ SFS_NODE_ASSERT(ap->a_vp);
+ ZFS_ENTER(zfsvfs);
+
+ parent = node->sn_parent_id == 0 ? zfsvfs->z_root : node->sn_parent_id;
+ dp = kmem_alloc(sizeof(*dp), KM_SLEEP);
+ if (ap->a_ncookies) {
+ ncookies = ap->a_uio->uio_resid / _DIRENT_MINSIZE(dp);
+ *ap->a_ncookies = 0;
+ *ap->a_cookies = malloc(ncookies * sizeof (off_t),
+ M_TEMP, M_WAITOK);
+ }
+
+ offset = 0;
+ error = sfs_readdir_one(ap, dp, ".", node->sn_id, &offset);
+ if (error == 0)
+ error = sfs_readdir_one(ap, dp, "..", parent, &offset);
+ if (error == 0 && node->sn_id == ZFSCTL_INO_ROOT) {
+ error = sfs_readdir_one(ap, dp, ZFS_SNAPDIR_NAME,
+ ZFSCTL_INO_SNAPDIR, &offset);
+ } else if (error == 0 && node->sn_id == ZFSCTL_INO_SNAPDIR) {
+ char snapname[ZFS_MAX_DATASET_NAME_LEN];
+ uint64_t cookie, id;
+
+ cookie = 0;
+ for (;;) {
+ dsl_pool_config_enter(dmu_objset_pool(zfsvfs->z_os),
+ FTAG);
+ error = dmu_snapshot_list_next(zfsvfs->z_os,
+ sizeof(snapname), snapname, &id, &cookie, NULL);
+ dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os),
+ FTAG);
+ if (error) {
+ if (error == ENOENT)
+ error = 0;
+ break;
+ }
+ error = sfs_readdir_one(ap, dp, snapname, id, &offset);
+ if (error)
+ break;
+ }
+ }
+
+ if (ap->a_eofflag && error == 0)
+ *ap->a_eofflag = 1;
+
+ if (error == ENAMETOOLONG)
+ error = 0;
+
+ if (ap->a_ncookies && error) {
+ free(*ap->a_cookies, M_TEMP);
+ *ap->a_ncookies = 0;
+ *ap->a_cookies = NULL;
+ }
+ kmem_free(dp, sizeof(*dp));
+
+ ZFS_EXIT(zfsvfs);
+
+ return error;
+}
+
+static int
+sfs_inactive(void *v)
+{
+ struct vop_inactive_v2_args /* {
+ struct vnode *a_vp;
+ bool *a_recycle;
+ } */ *ap = v;
+ vnode_t *vp = ap->a_vp;
+ struct sfs_node *node = VTOSFS(vp);
+
+ SFS_NODE_ASSERT(vp);
+
+ *ap->a_recycle = (node->sn_parent_id == ZFSCTL_INO_SNAPDIR);
+
+ return 0;
+}
+
+static int
+sfs_reclaim(void *v)
+{
+ struct vop_reclaim_v2_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+ vnode_t *vp = ap->a_vp;
+ struct sfs_node *node = VTOSFS(vp);
+
+ SFS_NODE_ASSERT(ap->a_vp);
+
+ vp->v_data = NULL;
+ VOP_UNLOCK(vp, 0);
+
+ kmem_free(node, sizeof(*node));
+
+ return 0;
+}
+
+static int
+sfs_print(void *v)
+{
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+ struct sfs_node *node = VTOSFS(ap->a_vp);
+
+ SFS_NODE_ASSERT(ap->a_vp);
+
+ printf("\tid %" PRIu64 ", parent %" PRIu64 "\n",
+ node->sn_id, node->sn_parent_id);
+
+ return 0;
+}
+
+const struct vnodeopv_entry_desc zfs_sfsop_entries[] = {
{ &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, sfs_lookup },
+ { &vop_open_desc, sfs_open },
+ { &vop_close_desc, sfs_close },
+ { &vop_access_desc, sfs_access },
+ { &vop_getattr_desc, sfs_getattr },
+ { &vop_lock_desc, genfs_lock },
+ { &vop_unlock_desc, genfs_unlock },
+ { &vop_readdir_desc, sfs_readdir },
+ { &vop_inactive_desc, sfs_inactive },
+ { &vop_reclaim_desc, sfs_reclaim },
+ { &vop_seek_desc, genfs_seek },
+ { &vop_putpages_desc, genfs_null_putpages },
+ { &vop_islocked_desc, genfs_islocked },
+ { &vop_print_desc, sfs_print },
{ NULL, NULL }
};
@@ -1289,36 +1859,88 @@ int
zfsctl_loadvnode(vfs_t *vfsp, vnode_t *vp,
const void *key, size_t key_len, const void **new_key)
{
+ struct sfs_node_key node_key;
+ struct sfs_node *node;
- return EINVAL;
+ if (key_len != sizeof(node_key))
+ return EINVAL;
+ if ((vfsp->mnt_iflag & IMNT_UNMOUNT))
+ return ENOENT;
+
+ memcpy(&node_key, key, key_len);
+
+ node = kmem_alloc(sizeof(*node), KM_SLEEP);
+
+ node->sn_mounting = NULL;
+ node->sn_key = node_key;
+
+ vp->v_data = node;
+ vp->v_op = zfs_sfsop_p;
+ vp->v_tag = VT_ZFS;
+ vp->v_type = VDIR;
+ uvm_vnp_setsize(vp, 0);
+
+ *new_key = &node->sn_key;
+
+ return 0;
}
+/*
+ * Return the ".zfs" vnode.
+ */
int
-zfsctl_root(zfsvfs_t *zfsvfs, vnode_t **znode)
+zfsctl_root(zfsvfs_t *zfsvfs, vnode_t **vpp)
{
+ struct sfs_node_key key = {
+ .parent_id = 0,
+ .id = ZFSCTL_INO_ROOT
+ };
- return ENOENT;
+ return vcache_get(zfsvfs->z_vfs, &key, sizeof(key), vpp);
}
+/*
+ * Return the ".zfs/snapshot" vnode.
+ */
int
-zfsctl_snapshot(zfsvfs_t *zfsvfs, vnode_t **znode)
+zfsctl_snapshot(zfsvfs_t *zfsvfs, vnode_t **vpp)
{
+ struct sfs_node_key key = {
+ .parent_id = ZFSCTL_INO_ROOT,
+ .id = ZFSCTL_INO_SNAPDIR
+ };
- return ENOENT;
+ return vcache_get(zfsvfs->z_vfs, &key, sizeof(key), vpp);
}
void
zfsctl_create(zfsvfs_t *zfsvfs)
{
+ vnode_t *vp;
+ struct zfsctl_root *zc;
+ uint64_t crtime[2];
+
+ zc = kmem_alloc(sizeof(*zc), KM_SLEEP);
+
+ VERIFY(0 == VFS_ROOT(zfsvfs->z_vfs, &vp));
+ VERIFY(0 == sa_lookup(VTOZ(vp)->z_sa_hdl, SA_ZPL_CRTIME(zfsvfs),
+ &crtime, sizeof(crtime)));
+ vput(vp);
+
+ ZFS_TIME_DECODE(&zc->zc_cmtime, crtime);
ASSERT(zfsvfs->z_ctldir == NULL);
+ zfsvfs->z_ctldir = zc;
}
void
zfsctl_destroy(zfsvfs_t *zfsvfs)
{
+ struct zfsctl_root *zc = zfsvfs->z_ctldir;
- ASSERT(zfsvfs->z_ctldir == NULL);
+ ASSERT(zfsvfs->z_ctldir);
+ zfsvfs->z_ctldir = NULL;
+ kmem_free(zc, sizeof(*zc));
}
int
@@ -1331,14 +1953,55 @@ zfsctl_lookup_objset(vfs_t *vfsp, uint64
int
zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
{
+ char snapname[ZFS_MAX_DATASET_NAME_LEN];
+ zfsvfs_t *zfsvfs = vfsp->vfs_data;
+ struct mount *mp;
+ vnode_t *vp;
+ struct sfs_node_key key;
+ uint64_t cookie;
+ int error;
+
+ ASSERT(zfsvfs->z_ctldir);
+
+ cookie = 0;
+ key.parent_id = ZFSCTL_INO_SNAPDIR;
+ for (;;) {
+ dsl_pool_config_enter(dmu_objset_pool(zfsvfs->z_os), FTAG);
+ error = dmu_snapshot_list_next(zfsvfs->z_os, sizeof(snapname),
+ snapname, &key.id, &cookie, NULL);
+ dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), FTAG);
+ if (error) {
+ if (error == ENOENT)
+ error = 0;
+ break;
+ }
+
+ error = vcache_get(zfsvfs->z_vfs, &key, sizeof(key), &vp);
+ if (error == ENOENT)
+ continue;
+ else if (error)
+ break;
+
+ mp = vp->v_mountedhere;
+ if (mp == NULL) {
+ vrele(vp);
+ continue;
+ }
+
+ error = dounmount(mp, fflags, curthread);
+ vrele(vp);
+ if (error)
+ break;
+ }
+ ASSERT((fflags & MS_FORCE) == 0 || error == 0);
- return 0;
+ return (error);
}
boolean_t
zfsctl_is_node(vnode_t *vp)
{
- return B_FALSE;
+ return (vp->v_op == zfs_sfsop_p);
}
#endif /* __NetBSD__ */