Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/external/cddl/osnet/dist/uts/common/fs/zfs Implement the ZFS...



details:   https://anonhg.NetBSD.org/src/rev/a12338a0a6e2
branches:  trunk
changeset: 448574:a12338a0a6e2
user:      hannken <hannken%NetBSD.org@localhost>
date:      Tue Feb 05 09:55:48 2019 +0000

description:
Implement the ZFS control directory ".zfs" and its subdirectory 'snapshot".

Automatically mount snapshots on access of ".zfs/snapshot/<snapname>".

diffstat:

 external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c |  681 +++++++++++++++-
 1 files changed, 672 insertions(+), 9 deletions(-)

diffs (truncated from 742 to 300 lines):

diff -r 6881a2ebae40 -r a12338a0a6e2 external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c
--- a/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c   Tue Feb 05 09:54:36 2019 +0000
+++ b/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_ctldir.c   Tue Feb 05 09:55:48 2019 +0000
@@ -1261,14 +1261,584 @@
 
 #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;
+



Home | Main Index | Thread Index | Old Index