xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_ctldir.c (revision 13043:8c712bbb18ea)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51512Sek110237  * Common Development and Distribution License (the "License").
61512Sek110237  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212079SMark.Shellenbaum@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens /*
26789Sahrens  * ZFS control directory (a.k.a. ".zfs")
27789Sahrens  *
28789Sahrens  * This directory provides a common location for all ZFS meta-objects.
29789Sahrens  * Currently, this is only the 'snapshot' directory, but this may expand in the
30789Sahrens  * future.  The elements are built using the GFS primitives, as the hierarchy
31789Sahrens  * does not actually exist on disk.
32789Sahrens  *
33789Sahrens  * For 'snapshot', we don't want to have all snapshots always mounted, because
34789Sahrens  * this would take up a huge amount of space in /etc/mnttab.  We have three
35789Sahrens  * types of objects:
36789Sahrens  *
37789Sahrens  * 	ctldir ------> snapshotdir -------> snapshot
38789Sahrens  *                                             |
39789Sahrens  *                                             |
40789Sahrens  *                                             V
41789Sahrens  *                                         mounted fs
42789Sahrens  *
43789Sahrens  * The 'snapshot' node contains just enough information to lookup '..' and act
44789Sahrens  * as a mountpoint for the snapshot.  Whenever we lookup a specific snapshot, we
45789Sahrens  * perform an automount of the underlying filesystem and return the
46789Sahrens  * corresponding vnode.
47789Sahrens  *
48789Sahrens  * All mounts are handled automatically by the kernel, but unmounts are
49789Sahrens  * (currently) handled from user land.  The main reason is that there is no
50789Sahrens  * reliable way to auto-unmount the filesystem when it's "no longer in use".
51789Sahrens  * When the user unmounts a filesystem, we call zfsctl_unmount(), which
52789Sahrens  * unmounts any snapshots within the snapshot directory.
535326Sek110237  *
545326Sek110237  * The '.zfs', '.zfs/snapshot', and all directories created under
555326Sek110237  * '.zfs/snapshot' (ie: '.zfs/snapshot/<snapname>') are all GFS nodes and
565326Sek110237  * share the same vfs_t as the head filesystem (what '.zfs' lives under).
575326Sek110237  *
585326Sek110237  * File systems mounted ontop of the GFS nodes '.zfs/snapshot/<snapname>'
595326Sek110237  * (ie: snapshots) are ZFS nodes and have their own unique vfs_t.
605326Sek110237  * However, vnodes within these mounted on file systems have their v_vfsp
615326Sek110237  * fields set to the head filesystem to make NFS happy (see
626068Sck153898  * zfsctl_snapdir_lookup()). We VFS_HOLD the head filesystem's vfs_t
636068Sck153898  * so that it cannot be freed until all snapshots have been unmounted.
64789Sahrens  */
65789Sahrens 
66789Sahrens #include <fs/fs_subr.h>
67789Sahrens #include <sys/zfs_ctldir.h>
68789Sahrens #include <sys/zfs_ioctl.h>
69789Sahrens #include <sys/zfs_vfsops.h>
703898Srsb #include <sys/vfs_opreg.h>
71789Sahrens #include <sys/gfs.h>
72789Sahrens #include <sys/stat.h>
73789Sahrens #include <sys/dmu.h>
744543Smarks #include <sys/dsl_deleg.h>
75789Sahrens #include <sys/mount.h>
766492Stimh #include <sys/sunddi.h>
77789Sahrens 
786658Smarks #include "zfs_namecheck.h"
796658Smarks 
806068Sck153898 typedef struct zfsctl_node {
816068Sck153898 	gfs_dir_t	zc_gfs_private;
826068Sck153898 	uint64_t	zc_id;
836068Sck153898 	timestruc_t	zc_cmtime;	/* ctime and mtime, always the same */
846068Sck153898 } zfsctl_node_t;
856068Sck153898 
866068Sck153898 typedef struct zfsctl_snapdir {
876068Sck153898 	zfsctl_node_t	sd_node;
886068Sck153898 	kmutex_t	sd_lock;
896068Sck153898 	avl_tree_t	sd_snaps;
906068Sck153898 } zfsctl_snapdir_t;
916068Sck153898 
92789Sahrens typedef struct {
93789Sahrens 	char		*se_name;
94789Sahrens 	vnode_t		*se_root;
95789Sahrens 	avl_node_t	se_node;
96789Sahrens } zfs_snapentry_t;
97789Sahrens 
98789Sahrens static int
snapentry_compare(const void * a,const void * b)99789Sahrens snapentry_compare(const void *a, const void *b)
100789Sahrens {
101789Sahrens 	const zfs_snapentry_t *sa = a;
102789Sahrens 	const zfs_snapentry_t *sb = b;
103789Sahrens 	int ret = strcmp(sa->se_name, sb->se_name);
104789Sahrens 
105789Sahrens 	if (ret < 0)
106789Sahrens 		return (-1);
107789Sahrens 	else if (ret > 0)
108789Sahrens 		return (1);
109789Sahrens 	else
110789Sahrens 		return (0);
111789Sahrens }
112789Sahrens 
113789Sahrens vnodeops_t *zfsctl_ops_root;
114789Sahrens vnodeops_t *zfsctl_ops_snapdir;
115789Sahrens vnodeops_t *zfsctl_ops_snapshot;
1168845Samw@Sun.COM vnodeops_t *zfsctl_ops_shares;
1178845Samw@Sun.COM vnodeops_t *zfsctl_ops_shares_dir;
118789Sahrens 
119789Sahrens static const fs_operation_def_t zfsctl_tops_root[];
120789Sahrens static const fs_operation_def_t zfsctl_tops_snapdir[];
121789Sahrens static const fs_operation_def_t zfsctl_tops_snapshot[];
1228845Samw@Sun.COM static const fs_operation_def_t zfsctl_tops_shares[];
123789Sahrens 
124789Sahrens static vnode_t *zfsctl_mknode_snapdir(vnode_t *);
1258845Samw@Sun.COM static vnode_t *zfsctl_mknode_shares(vnode_t *);
126789Sahrens static vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset);
1276068Sck153898 static int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *);
128789Sahrens 
129789Sahrens static gfs_opsvec_t zfsctl_opsvec[] = {
130789Sahrens 	{ ".zfs", zfsctl_tops_root, &zfsctl_ops_root },
131789Sahrens 	{ ".zfs/snapshot", zfsctl_tops_snapdir, &zfsctl_ops_snapdir },
132789Sahrens 	{ ".zfs/snapshot/vnode", zfsctl_tops_snapshot, &zfsctl_ops_snapshot },
1338845Samw@Sun.COM 	{ ".zfs/shares", zfsctl_tops_shares, &zfsctl_ops_shares_dir },
1348845Samw@Sun.COM 	{ ".zfs/shares/vnode", zfsctl_tops_shares, &zfsctl_ops_shares },
135789Sahrens 	{ NULL }
136789Sahrens };
137789Sahrens 
138789Sahrens /*
1398845Samw@Sun.COM  * Root directory elements.  We only have two entries
1408845Samw@Sun.COM  * snapshot and shares.
141789Sahrens  */
142789Sahrens static gfs_dirent_t zfsctl_root_entries[] = {
143789Sahrens 	{ "snapshot", zfsctl_mknode_snapdir, GFS_CACHE_VNODE },
1448845Samw@Sun.COM 	{ "shares", zfsctl_mknode_shares, GFS_CACHE_VNODE },
145789Sahrens 	{ NULL }
146789Sahrens };
147789Sahrens 
148789Sahrens /* include . and .. in the calculation */
149789Sahrens #define	NROOT_ENTRIES	((sizeof (zfsctl_root_entries) / \
150789Sahrens     sizeof (gfs_dirent_t)) + 1)
151789Sahrens 
152789Sahrens 
153789Sahrens /*
154789Sahrens  * Initialize the various GFS pieces we'll need to create and manipulate .zfs
155789Sahrens  * directories.  This is called from the ZFS init routine, and initializes the
156789Sahrens  * vnode ops vectors that we'll be using.
157789Sahrens  */
158789Sahrens void
zfsctl_init(void)159789Sahrens zfsctl_init(void)
160789Sahrens {
161789Sahrens 	VERIFY(gfs_make_opsvec(zfsctl_opsvec) == 0);
162789Sahrens }
163789Sahrens 
164789Sahrens void
zfsctl_fini(void)165789Sahrens zfsctl_fini(void)
166789Sahrens {
167789Sahrens 	/*
168789Sahrens 	 * Remove vfsctl vnode ops
169789Sahrens 	 */
170789Sahrens 	if (zfsctl_ops_root)
171789Sahrens 		vn_freevnodeops(zfsctl_ops_root);
172789Sahrens 	if (zfsctl_ops_snapdir)
173789Sahrens 		vn_freevnodeops(zfsctl_ops_snapdir);
174789Sahrens 	if (zfsctl_ops_snapshot)
175789Sahrens 		vn_freevnodeops(zfsctl_ops_snapshot);
1768845Samw@Sun.COM 	if (zfsctl_ops_shares)
1778845Samw@Sun.COM 		vn_freevnodeops(zfsctl_ops_shares);
1788845Samw@Sun.COM 	if (zfsctl_ops_shares_dir)
1798845Samw@Sun.COM 		vn_freevnodeops(zfsctl_ops_shares_dir);
180789Sahrens 
181789Sahrens 	zfsctl_ops_root = NULL;
182789Sahrens 	zfsctl_ops_snapdir = NULL;
183789Sahrens 	zfsctl_ops_snapshot = NULL;
1848845Samw@Sun.COM 	zfsctl_ops_shares = NULL;
1858845Samw@Sun.COM 	zfsctl_ops_shares_dir = NULL;
186789Sahrens }
187789Sahrens 
18812079SMark.Shellenbaum@Sun.COM boolean_t
zfsctl_is_node(vnode_t * vp)18912079SMark.Shellenbaum@Sun.COM zfsctl_is_node(vnode_t *vp)
19012079SMark.Shellenbaum@Sun.COM {
19112079SMark.Shellenbaum@Sun.COM 	return (vn_matchops(vp, zfsctl_ops_root) ||
19212079SMark.Shellenbaum@Sun.COM 	    vn_matchops(vp, zfsctl_ops_snapdir) ||
19312079SMark.Shellenbaum@Sun.COM 	    vn_matchops(vp, zfsctl_ops_snapshot) ||
19412079SMark.Shellenbaum@Sun.COM 	    vn_matchops(vp, zfsctl_ops_shares) ||
19512079SMark.Shellenbaum@Sun.COM 	    vn_matchops(vp, zfsctl_ops_shares_dir));
19612079SMark.Shellenbaum@Sun.COM 
19712079SMark.Shellenbaum@Sun.COM }
19812079SMark.Shellenbaum@Sun.COM 
199789Sahrens /*
2008845Samw@Sun.COM  * Return the inode number associated with the 'snapshot' or
2018845Samw@Sun.COM  * 'shares' directory.
202789Sahrens  */
203789Sahrens /* ARGSUSED */
204789Sahrens static ino64_t
zfsctl_root_inode_cb(vnode_t * vp,int index)205789Sahrens zfsctl_root_inode_cb(vnode_t *vp, int index)
206789Sahrens {
2078845Samw@Sun.COM 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
2088845Samw@Sun.COM 
2098845Samw@Sun.COM 	ASSERT(index <= 2);
2108845Samw@Sun.COM 
2118845Samw@Sun.COM 	if (index == 0)
2128845Samw@Sun.COM 		return (ZFSCTL_INO_SNAPDIR);
2138845Samw@Sun.COM 
2148845Samw@Sun.COM 	return (zfsvfs->z_shares_dir);
215789Sahrens }
216789Sahrens 
217789Sahrens /*
218789Sahrens  * Create the '.zfs' directory.  This directory is cached as part of the VFS
219789Sahrens  * structure.  This results in a hold on the vfs_t.  The code in zfs_umount()
220789Sahrens  * therefore checks against a vfs_count of 2 instead of 1.  This reference
221789Sahrens  * is removed when the ctldir is destroyed in the unmount.
222789Sahrens  */
223789Sahrens void
zfsctl_create(zfsvfs_t * zfsvfs)224789Sahrens zfsctl_create(zfsvfs_t *zfsvfs)
225789Sahrens {
2261571Sek110237 	vnode_t *vp, *rvp;
227789Sahrens 	zfsctl_node_t *zcp;
22811935SMark.Shellenbaum@Sun.COM 	uint64_t crtime[2];
229789Sahrens 
230789Sahrens 	ASSERT(zfsvfs->z_ctldir == NULL);
231789Sahrens 
232789Sahrens 	vp = gfs_root_create(sizeof (zfsctl_node_t), zfsvfs->z_vfs,
233789Sahrens 	    zfsctl_ops_root, ZFSCTL_INO_ROOT, zfsctl_root_entries,
234789Sahrens 	    zfsctl_root_inode_cb, MAXNAMELEN, NULL, NULL);
235789Sahrens 	zcp = vp->v_data;
236789Sahrens 	zcp->zc_id = ZFSCTL_INO_ROOT;
237789Sahrens 
2381571Sek110237 	VERIFY(VFS_ROOT(zfsvfs->z_vfs, &rvp) == 0);
23911935SMark.Shellenbaum@Sun.COM 	VERIFY(0 == sa_lookup(VTOZ(rvp)->z_sa_hdl, SA_ZPL_CRTIME(zfsvfs),
24011935SMark.Shellenbaum@Sun.COM 	    &crtime, sizeof (crtime)));
24111935SMark.Shellenbaum@Sun.COM 	ZFS_TIME_DECODE(&zcp->zc_cmtime, crtime);
2421571Sek110237 	VN_RELE(rvp);
2431571Sek110237 
244789Sahrens 	/*
245789Sahrens 	 * We're only faking the fact that we have a root of a filesystem for
246789Sahrens 	 * the sake of the GFS interfaces.  Undo the flag manipulation it did
247789Sahrens 	 * for us.
248789Sahrens 	 */
249789Sahrens 	vp->v_flag &= ~(VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT);
250789Sahrens 
251789Sahrens 	zfsvfs->z_ctldir = vp;
252789Sahrens }
253789Sahrens 
254789Sahrens /*
2551298Sperrin  * Destroy the '.zfs' directory.  Only called when the filesystem is unmounted.
2561298Sperrin  * There might still be more references if we were force unmounted, but only
2571298Sperrin  * new zfs_inactive() calls can occur and they don't reference .zfs
258789Sahrens  */
259789Sahrens void
zfsctl_destroy(zfsvfs_t * zfsvfs)260789Sahrens zfsctl_destroy(zfsvfs_t *zfsvfs)
261789Sahrens {
262789Sahrens 	VN_RELE(zfsvfs->z_ctldir);
263789Sahrens 	zfsvfs->z_ctldir = NULL;
264789Sahrens }
265789Sahrens 
266789Sahrens /*
267789Sahrens  * Given a root znode, retrieve the associated .zfs directory.
268789Sahrens  * Add a hold to the vnode and return it.
269789Sahrens  */
270789Sahrens vnode_t *
zfsctl_root(znode_t * zp)271789Sahrens zfsctl_root(znode_t *zp)
272789Sahrens {
273789Sahrens 	ASSERT(zfs_has_ctldir(zp));
274789Sahrens 	VN_HOLD(zp->z_zfsvfs->z_ctldir);
275789Sahrens 	return (zp->z_zfsvfs->z_ctldir);
276789Sahrens }
277789Sahrens 
278789Sahrens /*
279789Sahrens  * Common open routine.  Disallow any write access.
280789Sahrens  */
281789Sahrens /* ARGSUSED */
282789Sahrens static int
zfsctl_common_open(vnode_t ** vpp,int flags,cred_t * cr,caller_context_t * ct)2835331Samw zfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
284789Sahrens {
285789Sahrens 	if (flags & FWRITE)
286789Sahrens 		return (EACCES);
287789Sahrens 
288789Sahrens 	return (0);
289789Sahrens }
290789Sahrens 
291789Sahrens /*
292789Sahrens  * Common close routine.  Nothing to do here.
293789Sahrens  */
294789Sahrens /* ARGSUSED */
295789Sahrens static int
zfsctl_common_close(vnode_t * vpp,int flags,int count,offset_t off,cred_t * cr,caller_context_t * ct)296789Sahrens zfsctl_common_close(vnode_t *vpp, int flags, int count, offset_t off,
2975331Samw     cred_t *cr, caller_context_t *ct)
298789Sahrens {
299789Sahrens 	return (0);
300789Sahrens }
301789Sahrens 
302789Sahrens /*
303789Sahrens  * Common access routine.  Disallow writes.
304789Sahrens  */
305789Sahrens /* ARGSUSED */
306789Sahrens static int
zfsctl_common_access(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)3075331Samw zfsctl_common_access(vnode_t *vp, int mode, int flags, cred_t *cr,
3085331Samw     caller_context_t *ct)
309789Sahrens {
3108547SMark.Shellenbaum@Sun.COM 	if (flags & V_ACE_MASK) {
3118547SMark.Shellenbaum@Sun.COM 		if (mode & ACE_ALL_WRITE_PERMS)
3128547SMark.Shellenbaum@Sun.COM 			return (EACCES);
3138547SMark.Shellenbaum@Sun.COM 	} else {
3148547SMark.Shellenbaum@Sun.COM 		if (mode & VWRITE)
3158547SMark.Shellenbaum@Sun.COM 			return (EACCES);
3168547SMark.Shellenbaum@Sun.COM 	}
317789Sahrens 
318789Sahrens 	return (0);
319789Sahrens }
320789Sahrens 
321789Sahrens /*
322789Sahrens  * Common getattr function.  Fill in basic information.
323789Sahrens  */
324789Sahrens static void
zfsctl_common_getattr(vnode_t * vp,vattr_t * vap)325789Sahrens zfsctl_common_getattr(vnode_t *vp, vattr_t *vap)
326789Sahrens {
3271571Sek110237 	timestruc_t	now;
328789Sahrens 
329789Sahrens 	vap->va_uid = 0;
330789Sahrens 	vap->va_gid = 0;
331789Sahrens 	vap->va_rdev = 0;
332789Sahrens 	/*
33310373Schris.kirby@sun.com 	 * We are a purely virtual object, so we have no
334789Sahrens 	 * blocksize or allocated blocks.
335789Sahrens 	 */
336789Sahrens 	vap->va_blksize = 0;
337789Sahrens 	vap->va_nblocks = 0;
338789Sahrens 	vap->va_seq = 0;
339789Sahrens 	vap->va_fsid = vp->v_vfsp->vfs_dev;
340789Sahrens 	vap->va_mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
341789Sahrens 	    S_IROTH | S_IXOTH;
342789Sahrens 	vap->va_type = VDIR;
343789Sahrens 	/*
3441571Sek110237 	 * We live in the now (for atime).
345789Sahrens 	 */
346789Sahrens 	gethrestime(&now);
3471571Sek110237 	vap->va_atime = now;
348789Sahrens }
349789Sahrens 
3505331Samw /*ARGSUSED*/
351789Sahrens static int
zfsctl_common_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)3525331Samw zfsctl_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
353789Sahrens {
354789Sahrens 	zfsvfs_t	*zfsvfs = vp->v_vfsp->vfs_data;
355789Sahrens 	zfsctl_node_t	*zcp = vp->v_data;
356789Sahrens 	uint64_t	object = zcp->zc_id;
357789Sahrens 	zfid_short_t	*zfid;
358789Sahrens 	int		i;
359789Sahrens 
360789Sahrens 	ZFS_ENTER(zfsvfs);
361789Sahrens 
362789Sahrens 	if (fidp->fid_len < SHORT_FID_LEN) {
363789Sahrens 		fidp->fid_len = SHORT_FID_LEN;
3641512Sek110237 		ZFS_EXIT(zfsvfs);
365789Sahrens 		return (ENOSPC);
366789Sahrens 	}
367789Sahrens 
368789Sahrens 	zfid = (zfid_short_t *)fidp;
369789Sahrens 
370789Sahrens 	zfid->zf_len = SHORT_FID_LEN;
371789Sahrens 
372789Sahrens 	for (i = 0; i < sizeof (zfid->zf_object); i++)
373789Sahrens 		zfid->zf_object[i] = (uint8_t)(object >> (8 * i));
374789Sahrens 
375789Sahrens 	/* .zfs znodes always have a generation number of 0 */
376789Sahrens 	for (i = 0; i < sizeof (zfid->zf_gen); i++)
377789Sahrens 		zfid->zf_gen[i] = 0;
378789Sahrens 
379789Sahrens 	ZFS_EXIT(zfsvfs);
380789Sahrens 	return (0);
381789Sahrens }
382789Sahrens 
3838845Samw@Sun.COM 
3848845Samw@Sun.COM /*ARGSUSED*/
3858845Samw@Sun.COM static int
zfsctl_shares_fid(vnode_t * vp,fid_t * fidp,caller_context_t * ct)3868845Samw@Sun.COM zfsctl_shares_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
3878845Samw@Sun.COM {
3888845Samw@Sun.COM 	zfsvfs_t	*zfsvfs = vp->v_vfsp->vfs_data;
3898845Samw@Sun.COM 	znode_t		*dzp;
3908845Samw@Sun.COM 	int		error;
3918845Samw@Sun.COM 
3928845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
3939030SMark.Shellenbaum@Sun.COM 
3949030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
3959030SMark.Shellenbaum@Sun.COM 		ZFS_EXIT(zfsvfs);
3969030SMark.Shellenbaum@Sun.COM 		return (ENOTSUP);
3979030SMark.Shellenbaum@Sun.COM 	}
3989030SMark.Shellenbaum@Sun.COM 
3998845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
4008845Samw@Sun.COM 		error = VOP_FID(ZTOV(dzp), fidp, ct);
4018845Samw@Sun.COM 		VN_RELE(ZTOV(dzp));
4028845Samw@Sun.COM 	}
4038845Samw@Sun.COM 
4048845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
4058845Samw@Sun.COM 	return (error);
4068845Samw@Sun.COM }
407789Sahrens /*
408789Sahrens  * .zfs inode namespace
409789Sahrens  *
410789Sahrens  * We need to generate unique inode numbers for all files and directories
411789Sahrens  * within the .zfs pseudo-filesystem.  We use the following scheme:
412789Sahrens  *
413789Sahrens  * 	ENTRY			ZFSCTL_INODE
414789Sahrens  * 	.zfs			1
415789Sahrens  * 	.zfs/snapshot		2
416789Sahrens  * 	.zfs/snapshot/<snap>	objectid(snap)
417789Sahrens  */
418789Sahrens 
419789Sahrens #define	ZFSCTL_INO_SNAP(id)	(id)
420789Sahrens 
421789Sahrens /*
422789Sahrens  * Get root directory attributes.
423789Sahrens  */
424789Sahrens /* ARGSUSED */
425789Sahrens static int
zfsctl_root_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)4265331Samw zfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
4275331Samw     caller_context_t *ct)
428789Sahrens {
429789Sahrens 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
43010373Schris.kirby@sun.com 	zfsctl_node_t *zcp = vp->v_data;
431789Sahrens 
432789Sahrens 	ZFS_ENTER(zfsvfs);
433789Sahrens 	vap->va_nodeid = ZFSCTL_INO_ROOT;
434789Sahrens 	vap->va_nlink = vap->va_size = NROOT_ENTRIES;
43510373Schris.kirby@sun.com 	vap->va_mtime = vap->va_ctime = zcp->zc_cmtime;
436789Sahrens 
437789Sahrens 	zfsctl_common_getattr(vp, vap);
438789Sahrens 	ZFS_EXIT(zfsvfs);
439789Sahrens 
440789Sahrens 	return (0);
441789Sahrens }
442789Sahrens 
443789Sahrens /*
444789Sahrens  * Special case the handling of "..".
445789Sahrens  */
446789Sahrens /* ARGSUSED */
447789Sahrens int
zfsctl_root_lookup(vnode_t * dvp,char * nm,vnode_t ** vpp,pathname_t * pnp,int flags,vnode_t * rdir,cred_t * cr,caller_context_t * ct,int * direntflags,pathname_t * realpnp)448789Sahrens zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
4495331Samw     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
4505331Samw     int *direntflags, pathname_t *realpnp)
451789Sahrens {
452789Sahrens 	zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
453789Sahrens 	int err;
454789Sahrens 
4555331Samw 	/*
4565331Samw 	 * No extended attributes allowed under .zfs
4575331Samw 	 */
4585331Samw 	if (flags & LOOKUP_XATTR)
4595331Samw 		return (EINVAL);
4605331Samw 
461789Sahrens 	ZFS_ENTER(zfsvfs);
462789Sahrens 
463789Sahrens 	if (strcmp(nm, "..") == 0) {
464789Sahrens 		err = VFS_ROOT(dvp->v_vfsp, vpp);
465789Sahrens 	} else {
4666492Stimh 		err = gfs_vop_lookup(dvp, nm, vpp, pnp, flags, rdir,
4676492Stimh 		    cr, ct, direntflags, realpnp);
468789Sahrens 	}
469789Sahrens 
470789Sahrens 	ZFS_EXIT(zfsvfs);
471789Sahrens 
472789Sahrens 	return (err);
473789Sahrens }
474789Sahrens 
4758547SMark.Shellenbaum@Sun.COM static int
zfsctl_pathconf(vnode_t * vp,int cmd,ulong_t * valp,cred_t * cr,caller_context_t * ct)4768547SMark.Shellenbaum@Sun.COM zfsctl_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
4778547SMark.Shellenbaum@Sun.COM     caller_context_t *ct)
4788547SMark.Shellenbaum@Sun.COM {
4798547SMark.Shellenbaum@Sun.COM 	/*
4808547SMark.Shellenbaum@Sun.COM 	 * We only care about ACL_ENABLED so that libsec can
4818547SMark.Shellenbaum@Sun.COM 	 * display ACL correctly and not default to POSIX draft.
4828547SMark.Shellenbaum@Sun.COM 	 */
4838547SMark.Shellenbaum@Sun.COM 	if (cmd == _PC_ACL_ENABLED) {
4848547SMark.Shellenbaum@Sun.COM 		*valp = _ACL_ACE_ENABLED;
4858547SMark.Shellenbaum@Sun.COM 		return (0);
4868547SMark.Shellenbaum@Sun.COM 	}
4878547SMark.Shellenbaum@Sun.COM 
4888547SMark.Shellenbaum@Sun.COM 	return (fs_pathconf(vp, cmd, valp, cr, ct));
4898547SMark.Shellenbaum@Sun.COM }
4908547SMark.Shellenbaum@Sun.COM 
491789Sahrens static const fs_operation_def_t zfsctl_tops_root[] = {
4923898Srsb 	{ VOPNAME_OPEN,		{ .vop_open = zfsctl_common_open }	},
4933898Srsb 	{ VOPNAME_CLOSE,	{ .vop_close = zfsctl_common_close }	},
4943898Srsb 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
4953898Srsb 	{ VOPNAME_GETATTR,	{ .vop_getattr = zfsctl_root_getattr }	},
4963898Srsb 	{ VOPNAME_ACCESS,	{ .vop_access = zfsctl_common_access }	},
4973898Srsb 	{ VOPNAME_READDIR,	{ .vop_readdir = gfs_vop_readdir } 	},
4983898Srsb 	{ VOPNAME_LOOKUP,	{ .vop_lookup = zfsctl_root_lookup }	},
4993898Srsb 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
5003898Srsb 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive }	},
5018547SMark.Shellenbaum@Sun.COM 	{ VOPNAME_PATHCONF,	{ .vop_pathconf = zfsctl_pathconf }	},
5023898Srsb 	{ VOPNAME_FID,		{ .vop_fid = zfsctl_common_fid	}	},
503789Sahrens 	{ NULL }
504789Sahrens };
505789Sahrens 
506789Sahrens static int
zfsctl_snapshot_zname(vnode_t * vp,const char * name,int len,char * zname)507789Sahrens zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
508789Sahrens {
509789Sahrens 	objset_t *os = ((zfsvfs_t *)((vp)->v_vfsp->vfs_data))->z_os;
510789Sahrens 
5116658Smarks 	if (snapshot_namecheck(name, NULL, NULL) != 0)
5126658Smarks 		return (EILSEQ);
513789Sahrens 	dmu_objset_name(os, zname);
5141154Smaybee 	if (strlen(zname) + 1 + strlen(name) >= len)
5151154Smaybee 		return (ENAMETOOLONG);
516789Sahrens 	(void) strcat(zname, "@");
517789Sahrens 	(void) strcat(zname, name);
518789Sahrens 	return (0);
519789Sahrens }
520789Sahrens 
5216068Sck153898 static int
zfsctl_unmount_snap(zfs_snapentry_t * sep,int fflags,cred_t * cr)5226068Sck153898 zfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr)
523789Sahrens {
5246068Sck153898 	vnode_t *svp = sep->se_root;
5256068Sck153898 	int error;
526789Sahrens 
5276068Sck153898 	ASSERT(vn_ismntpt(svp));
528789Sahrens 
529789Sahrens 	/* this will be dropped by dounmount() */
5306068Sck153898 	if ((error = vn_vfswlock(svp)) != 0)
5316068Sck153898 		return (error);
532789Sahrens 
5336068Sck153898 	VN_HOLD(svp);
5346068Sck153898 	error = dounmount(vn_mountedvfs(svp), fflags, cr);
5356068Sck153898 	if (error) {
5366068Sck153898 		VN_RELE(svp);
5376068Sck153898 		return (error);
5381589Smaybee 	}
5399214Schris.kirby@sun.com 
5406068Sck153898 	/*
5416068Sck153898 	 * We can't use VN_RELE(), as that will try to invoke
5426068Sck153898 	 * zfsctl_snapdir_inactive(), which would cause us to destroy
5436068Sck153898 	 * the sd_lock mutex held by our caller.
5446068Sck153898 	 */
5456068Sck153898 	ASSERT(svp->v_count == 1);
5466068Sck153898 	gfs_vop_inactive(svp, cr, NULL);
547789Sahrens 
548789Sahrens 	kmem_free(sep->se_name, strlen(sep->se_name) + 1);
549789Sahrens 	kmem_free(sep, sizeof (zfs_snapentry_t));
550789Sahrens 
551789Sahrens 	return (0);
552789Sahrens }
553789Sahrens 
5541154Smaybee static void
zfsctl_rename_snap(zfsctl_snapdir_t * sdp,zfs_snapentry_t * sep,const char * nm)555789Sahrens zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm)
556789Sahrens {
557789Sahrens 	avl_index_t where;
558789Sahrens 	vfs_t *vfsp;
559789Sahrens 	refstr_t *pathref;
560789Sahrens 	char newpath[MAXNAMELEN];
561789Sahrens 	char *tail;
562789Sahrens 
563789Sahrens 	ASSERT(MUTEX_HELD(&sdp->sd_lock));
564789Sahrens 	ASSERT(sep != NULL);
565789Sahrens 
566789Sahrens 	vfsp = vn_mountedvfs(sep->se_root);
567789Sahrens 	ASSERT(vfsp != NULL);
568789Sahrens 
5691154Smaybee 	vfs_lock_wait(vfsp);
570789Sahrens 
571789Sahrens 	/*
572789Sahrens 	 * Change the name in the AVL tree.
573789Sahrens 	 */
574789Sahrens 	avl_remove(&sdp->sd_snaps, sep);
575789Sahrens 	kmem_free(sep->se_name, strlen(sep->se_name) + 1);
576789Sahrens 	sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP);
577789Sahrens 	(void) strcpy(sep->se_name, nm);
578789Sahrens 	VERIFY(avl_find(&sdp->sd_snaps, sep, &where) == NULL);
579789Sahrens 	avl_insert(&sdp->sd_snaps, sep, where);
580789Sahrens 
581789Sahrens 	/*
582789Sahrens 	 * Change the current mountpoint info:
583789Sahrens 	 * 	- update the tail of the mntpoint path
584789Sahrens 	 *	- update the tail of the resource path
585789Sahrens 	 */
586789Sahrens 	pathref = vfs_getmntpoint(vfsp);
5872417Sahrens 	(void) strncpy(newpath, refstr_value(pathref), sizeof (newpath));
5882417Sahrens 	VERIFY((tail = strrchr(newpath, '/')) != NULL);
5892417Sahrens 	*(tail+1) = '\0';
5902417Sahrens 	ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath));
591789Sahrens 	(void) strcat(newpath, nm);
592789Sahrens 	refstr_rele(pathref);
59312894SRobert.Harris@Sun.COM 	vfs_setmntpoint(vfsp, newpath, 0);
594789Sahrens 
595789Sahrens 	pathref = vfs_getresource(vfsp);
5962417Sahrens 	(void) strncpy(newpath, refstr_value(pathref), sizeof (newpath));
5972417Sahrens 	VERIFY((tail = strrchr(newpath, '@')) != NULL);
5982417Sahrens 	*(tail+1) = '\0';
5992417Sahrens 	ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath));
600789Sahrens 	(void) strcat(newpath, nm);
601789Sahrens 	refstr_rele(pathref);
60212894SRobert.Harris@Sun.COM 	vfs_setresource(vfsp, newpath, 0);
603789Sahrens 
604789Sahrens 	vfs_unlock(vfsp);
605789Sahrens }
606789Sahrens 
6075331Samw /*ARGSUSED*/
608789Sahrens static int
zfsctl_snapdir_rename(vnode_t * sdvp,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)609789Sahrens zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
6105331Samw     cred_t *cr, caller_context_t *ct, int flags)
611789Sahrens {
612789Sahrens 	zfsctl_snapdir_t *sdp = sdvp->v_data;
613789Sahrens 	zfs_snapentry_t search, *sep;
6146492Stimh 	zfsvfs_t *zfsvfs;
615789Sahrens 	avl_index_t where;
616789Sahrens 	char from[MAXNAMELEN], to[MAXNAMELEN];
6176492Stimh 	char real[MAXNAMELEN];
618789Sahrens 	int err;
619789Sahrens 
6206492Stimh 	zfsvfs = sdvp->v_vfsp->vfs_data;
6216492Stimh 	ZFS_ENTER(zfsvfs);
6226492Stimh 
6236492Stimh 	if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
6246492Stimh 		err = dmu_snapshot_realname(zfsvfs->z_os, snm, real,
6256492Stimh 		    MAXNAMELEN, NULL);
6266492Stimh 		if (err == 0) {
6276492Stimh 			snm = real;
6286492Stimh 		} else if (err != ENOTSUP) {
6296492Stimh 			ZFS_EXIT(zfsvfs);
6306492Stimh 			return (err);
6316492Stimh 		}
6326492Stimh 	}
6336492Stimh 
6346492Stimh 	ZFS_EXIT(zfsvfs);
6356492Stimh 
6361154Smaybee 	err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
6376492Stimh 	if (!err)
6386492Stimh 		err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
6396492Stimh 	if (!err)
6406492Stimh 		err = zfs_secpolicy_rename_perms(from, to, cr);
6411154Smaybee 	if (err)
6421154Smaybee 		return (err);
6434543Smarks 
644789Sahrens 	/*
645789Sahrens 	 * Cannot move snapshots out of the snapdir.
646789Sahrens 	 */
647789Sahrens 	if (sdvp != tdvp)
648789Sahrens 		return (EINVAL);
649789Sahrens 
650789Sahrens 	if (strcmp(snm, tnm) == 0)
651789Sahrens 		return (0);
652789Sahrens 
653789Sahrens 	mutex_enter(&sdp->sd_lock);
654789Sahrens 
655789Sahrens 	search.se_name = (char *)snm;
6561154Smaybee 	if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL) {
6571154Smaybee 		mutex_exit(&sdp->sd_lock);
6581154Smaybee 		return (ENOENT);
659789Sahrens 	}
660789Sahrens 
6614007Smmusante 	err = dmu_objset_rename(from, to, B_FALSE);
6621154Smaybee 	if (err == 0)
6631154Smaybee 		zfsctl_rename_snap(sdp, sep, tnm);
664789Sahrens 
665789Sahrens 	mutex_exit(&sdp->sd_lock);
666789Sahrens 
667789Sahrens 	return (err);
668789Sahrens }
669789Sahrens 
670789Sahrens /* ARGSUSED */
671789Sahrens static int
zfsctl_snapdir_remove(vnode_t * dvp,char * name,vnode_t * cwd,cred_t * cr,caller_context_t * ct,int flags)6725331Samw zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
6735331Samw     caller_context_t *ct, int flags)
674789Sahrens {
675789Sahrens 	zfsctl_snapdir_t *sdp = dvp->v_data;
6766068Sck153898 	zfs_snapentry_t *sep;
6776068Sck153898 	zfs_snapentry_t search;
6786492Stimh 	zfsvfs_t *zfsvfs;
679789Sahrens 	char snapname[MAXNAMELEN];
6806492Stimh 	char real[MAXNAMELEN];
681789Sahrens 	int err;
682789Sahrens 
6836492Stimh 	zfsvfs = dvp->v_vfsp->vfs_data;
6846492Stimh 	ZFS_ENTER(zfsvfs);
6856492Stimh 
6866492Stimh 	if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
6876492Stimh 
6886492Stimh 		err = dmu_snapshot_realname(zfsvfs->z_os, name, real,
6896492Stimh 		    MAXNAMELEN, NULL);
6906492Stimh 		if (err == 0) {
6916492Stimh 			name = real;
6926492Stimh 		} else if (err != ENOTSUP) {
6936492Stimh 			ZFS_EXIT(zfsvfs);
6946492Stimh 			return (err);
6956492Stimh 		}
6966492Stimh 	}
6976492Stimh 
6986492Stimh 	ZFS_EXIT(zfsvfs);
6996492Stimh 
7001154Smaybee 	err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
7016492Stimh 	if (!err)
7026492Stimh 		err = zfs_secpolicy_destroy_perms(snapname, cr);
7031154Smaybee 	if (err)
7041154Smaybee 		return (err);
7054543Smarks 
706789Sahrens 	mutex_enter(&sdp->sd_lock);
707789Sahrens 
7086068Sck153898 	search.se_name = name;
7096068Sck153898 	sep = avl_find(&sdp->sd_snaps, &search, NULL);
7106068Sck153898 	if (sep) {
7116068Sck153898 		avl_remove(&sdp->sd_snaps, sep);
7126068Sck153898 		err = zfsctl_unmount_snap(sep, MS_FORCE, cr);
7136068Sck153898 		if (err)
7146068Sck153898 			avl_add(&sdp->sd_snaps, sep);
7156068Sck153898 		else
71610242Schris.kirby@sun.com 			err = dmu_objset_destroy(snapname, B_FALSE);
7176068Sck153898 	} else {
7186068Sck153898 		err = ENOENT;
719789Sahrens 	}
720789Sahrens 
721789Sahrens 	mutex_exit(&sdp->sd_lock);
722789Sahrens 
723789Sahrens 	return (err);
724789Sahrens }
725789Sahrens 
7265326Sek110237 /*
7275326Sek110237  * This creates a snapshot under '.zfs/snapshot'.
7285326Sek110237  */
7294543Smarks /* ARGSUSED */
7304543Smarks static int
zfsctl_snapdir_mkdir(vnode_t * dvp,char * dirname,vattr_t * vap,vnode_t ** vpp,cred_t * cr,caller_context_t * cc,int flags,vsecattr_t * vsecp)7314543Smarks zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t  **vpp,
7325331Samw     cred_t *cr, caller_context_t *cc, int flags, vsecattr_t *vsecp)
7334543Smarks {
7344543Smarks 	zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
7354543Smarks 	char name[MAXNAMELEN];
7364543Smarks 	int err;
7374543Smarks 	static enum symfollow follow = NO_FOLLOW;
7384543Smarks 	static enum uio_seg seg = UIO_SYSSPACE;
7394543Smarks 
7406658Smarks 	if (snapshot_namecheck(dirname, NULL, NULL) != 0)
7416658Smarks 		return (EILSEQ);
7426658Smarks 
7434543Smarks 	dmu_objset_name(zfsvfs->z_os, name);
7444543Smarks 
7454543Smarks 	*vpp = NULL;
7464543Smarks 
7474543Smarks 	err = zfs_secpolicy_snapshot_perms(name, cr);
7484543Smarks 	if (err)
7494543Smarks 		return (err);
7504543Smarks 
7514543Smarks 	if (err == 0) {
752*13043STim.Haley@Sun.COM 		err = dmu_objset_snapshot(name, dirname, NULL, NULL,
753*13043STim.Haley@Sun.COM 		    B_FALSE, B_FALSE, -1);
7544543Smarks 		if (err)
7554543Smarks 			return (err);
7564543Smarks 		err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp);
7574543Smarks 	}
7584543Smarks 
7594543Smarks 	return (err);
7604543Smarks }
7614543Smarks 
762789Sahrens /*
763789Sahrens  * Lookup entry point for the 'snapshot' directory.  Try to open the
764789Sahrens  * snapshot if it exist, creating the pseudo filesystem vnode as necessary.
765789Sahrens  * Perform a mount of the associated dataset on top of the vnode.
766789Sahrens  */
767789Sahrens /* ARGSUSED */
768789Sahrens static int
zfsctl_snapdir_lookup(vnode_t * dvp,char * nm,vnode_t ** vpp,pathname_t * pnp,int flags,vnode_t * rdir,cred_t * cr,caller_context_t * ct,int * direntflags,pathname_t * realpnp)769789Sahrens zfsctl_snapdir_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
7705331Samw     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
7715331Samw     int *direntflags, pathname_t *realpnp)
772789Sahrens {
773789Sahrens 	zfsctl_snapdir_t *sdp = dvp->v_data;
774789Sahrens 	objset_t *snap;
775789Sahrens 	char snapname[MAXNAMELEN];
7766492Stimh 	char real[MAXNAMELEN];
777789Sahrens 	char *mountpoint;
778789Sahrens 	zfs_snapentry_t *sep, search;
779789Sahrens 	struct mounta margs;
780789Sahrens 	vfs_t *vfsp;
781789Sahrens 	size_t mountpoint_len;
782789Sahrens 	avl_index_t where;
783789Sahrens 	zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
784789Sahrens 	int err;
785789Sahrens 
7865331Samw 	/*
7875331Samw 	 * No extended attributes allowed under .zfs
7885331Samw 	 */
7895331Samw 	if (flags & LOOKUP_XATTR)
7905331Samw 		return (EINVAL);
7915331Samw 
792789Sahrens 	ASSERT(dvp->v_type == VDIR);
793789Sahrens 
794789Sahrens 	/*
795789Sahrens 	 * If we get a recursive call, that means we got called
796789Sahrens 	 * from the domount() code while it was trying to look up the
797789Sahrens 	 * spec (which looks like a local path for zfs).  We need to
798789Sahrens 	 * add some flag to domount() to tell it not to do this lookup.
799789Sahrens 	 */
800789Sahrens 	if (MUTEX_HELD(&sdp->sd_lock))
801789Sahrens 		return (ENOENT);
802789Sahrens 
803789Sahrens 	ZFS_ENTER(zfsvfs);
804789Sahrens 
8059214Schris.kirby@sun.com 	if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
8069214Schris.kirby@sun.com 		ZFS_EXIT(zfsvfs);
8079214Schris.kirby@sun.com 		return (0);
8089214Schris.kirby@sun.com 	}
8099214Schris.kirby@sun.com 
8106492Stimh 	if (flags & FIGNORECASE) {
8116492Stimh 		boolean_t conflict = B_FALSE;
8126492Stimh 
8136492Stimh 		err = dmu_snapshot_realname(zfsvfs->z_os, nm, real,
8146492Stimh 		    MAXNAMELEN, &conflict);
8156492Stimh 		if (err == 0) {
8166492Stimh 			nm = real;
8176492Stimh 		} else if (err != ENOTSUP) {
8186492Stimh 			ZFS_EXIT(zfsvfs);
8196492Stimh 			return (err);
8206492Stimh 		}
8216492Stimh 		if (realpnp)
8226492Stimh 			(void) strlcpy(realpnp->pn_buf, nm,
8236492Stimh 			    realpnp->pn_bufsize);
8246492Stimh 		if (conflict && direntflags)
8256492Stimh 			*direntflags = ED_CASE_CONFLICT;
8266492Stimh 	}
8276492Stimh 
828789Sahrens 	mutex_enter(&sdp->sd_lock);
829789Sahrens 	search.se_name = (char *)nm;
830789Sahrens 	if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) {
831789Sahrens 		*vpp = sep->se_root;
832789Sahrens 		VN_HOLD(*vpp);
8331589Smaybee 		err = traverse(vpp);
8341589Smaybee 		if (err) {
8351589Smaybee 			VN_RELE(*vpp);
8361589Smaybee 			*vpp = NULL;
8371589Smaybee 		} else if (*vpp == sep->se_root) {
8381589Smaybee 			/*
8391589Smaybee 			 * The snapshot was unmounted behind our backs,
8401589Smaybee 			 * try to remount it.
8411589Smaybee 			 */
842789Sahrens 			goto domount;
8436068Sck153898 		} else {
8446068Sck153898 			/*
8456068Sck153898 			 * VROOT was set during the traverse call.  We need
8466068Sck153898 			 * to clear it since we're pretending to be part
8476068Sck153898 			 * of our parent's vfs.
8486068Sck153898 			 */
8496068Sck153898 			(*vpp)->v_flag &= ~VROOT;
8501566Smaybee 		}
851789Sahrens 		mutex_exit(&sdp->sd_lock);
852789Sahrens 		ZFS_EXIT(zfsvfs);
8531589Smaybee 		return (err);
854789Sahrens 	}
855789Sahrens 
856789Sahrens 	/*
857789Sahrens 	 * The requested snapshot is not currently mounted, look it up.
858789Sahrens 	 */
8591154Smaybee 	err = zfsctl_snapshot_zname(dvp, nm, MAXNAMELEN, snapname);
8601154Smaybee 	if (err) {
8611154Smaybee 		mutex_exit(&sdp->sd_lock);
8621154Smaybee 		ZFS_EXIT(zfsvfs);
8637229Smarks 		/*
8647229Smarks 		 * handle "ls *" or "?" in a graceful manner,
8657229Smarks 		 * forcing EILSEQ to ENOENT.
8667229Smarks 		 * Since shell ultimately passes "*" or "?" as name to lookup
8677229Smarks 		 */
8687229Smarks 		return (err == EILSEQ ? ENOENT : err);
8691154Smaybee 	}
87010298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(snapname, FTAG, &snap) != 0) {
871789Sahrens 		mutex_exit(&sdp->sd_lock);
872789Sahrens 		ZFS_EXIT(zfsvfs);
873789Sahrens 		return (ENOENT);
874789Sahrens 	}
875789Sahrens 
876789Sahrens 	sep = kmem_alloc(sizeof (zfs_snapentry_t), KM_SLEEP);
877789Sahrens 	sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP);
878789Sahrens 	(void) strcpy(sep->se_name, nm);
879789Sahrens 	*vpp = sep->se_root = zfsctl_snapshot_mknode(dvp, dmu_objset_id(snap));
880789Sahrens 	avl_insert(&sdp->sd_snaps, sep, where);
881789Sahrens 
88210298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(snap, FTAG);
883789Sahrens domount:
884789Sahrens 	mountpoint_len = strlen(refstr_value(dvp->v_vfsp->vfs_mntpt)) +
885789Sahrens 	    strlen("/.zfs/snapshot/") + strlen(nm) + 1;
886789Sahrens 	mountpoint = kmem_alloc(mountpoint_len, KM_SLEEP);
887789Sahrens 	(void) snprintf(mountpoint, mountpoint_len, "%s/.zfs/snapshot/%s",
888789Sahrens 	    refstr_value(dvp->v_vfsp->vfs_mntpt), nm);
889789Sahrens 
890789Sahrens 	margs.spec = snapname;
891789Sahrens 	margs.dir = mountpoint;
892789Sahrens 	margs.flags = MS_SYSSPACE | MS_NOMNTTAB;
893789Sahrens 	margs.fstype = "zfs";
894789Sahrens 	margs.dataptr = NULL;
895789Sahrens 	margs.datalen = 0;
896789Sahrens 	margs.optptr = NULL;
897789Sahrens 	margs.optlen = 0;
898789Sahrens 
899789Sahrens 	err = domount("zfs", &margs, *vpp, kcred, &vfsp);
900789Sahrens 	kmem_free(mountpoint, mountpoint_len);
901789Sahrens 
902816Smaybee 	if (err == 0) {
903816Smaybee 		/*
904816Smaybee 		 * Return the mounted root rather than the covered mount point.
9055326Sek110237 		 * Takes the GFS vnode at .zfs/snapshot/<snapname> and returns
9065326Sek110237 		 * the ZFS vnode mounted on top of the GFS node.  This ZFS
9079214Schris.kirby@sun.com 		 * vnode is the root of the newly created vfsp.
908816Smaybee 		 */
909816Smaybee 		VFS_RELE(vfsp);
910816Smaybee 		err = traverse(vpp);
911816Smaybee 	}
912789Sahrens 
913816Smaybee 	if (err == 0) {
914816Smaybee 		/*
9155326Sek110237 		 * Fix up the root vnode mounted on .zfs/snapshot/<snapname>.
9164736Sek110237 		 *
9174736Sek110237 		 * This is where we lie about our v_vfsp in order to
9185326Sek110237 		 * make .zfs/snapshot/<snapname> accessible over NFS
9195326Sek110237 		 * without requiring manual mounts of <snapname>.
920816Smaybee 		 */
921816Smaybee 		ASSERT(VTOZ(*vpp)->z_zfsvfs != zfsvfs);
922816Smaybee 		VTOZ(*vpp)->z_zfsvfs->z_parent = zfsvfs;
923816Smaybee 		(*vpp)->v_vfsp = zfsvfs->z_vfs;
924816Smaybee 		(*vpp)->v_flag &= ~VROOT;
925816Smaybee 	}
926789Sahrens 	mutex_exit(&sdp->sd_lock);
927789Sahrens 	ZFS_EXIT(zfsvfs);
928789Sahrens 
9291566Smaybee 	/*
9301566Smaybee 	 * If we had an error, drop our hold on the vnode and
9311566Smaybee 	 * zfsctl_snapshot_inactive() will clean up.
9321566Smaybee 	 */
9331566Smaybee 	if (err) {
934816Smaybee 		VN_RELE(*vpp);
9351566Smaybee 		*vpp = NULL;
9361566Smaybee 	}
937816Smaybee 	return (err);
938789Sahrens }
939789Sahrens 
940789Sahrens /* ARGSUSED */
941789Sahrens static int
zfsctl_shares_lookup(vnode_t * dvp,char * nm,vnode_t ** vpp,pathname_t * pnp,int flags,vnode_t * rdir,cred_t * cr,caller_context_t * ct,int * direntflags,pathname_t * realpnp)9428845Samw@Sun.COM zfsctl_shares_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
9438845Samw@Sun.COM     int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
9448845Samw@Sun.COM     int *direntflags, pathname_t *realpnp)
9458845Samw@Sun.COM {
9468845Samw@Sun.COM 	zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
9478845Samw@Sun.COM 	znode_t *dzp;
9488845Samw@Sun.COM 	int error;
9498845Samw@Sun.COM 
9508845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
9518845Samw@Sun.COM 
9528845Samw@Sun.COM 	if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
9538845Samw@Sun.COM 		ZFS_EXIT(zfsvfs);
9548845Samw@Sun.COM 		return (0);
9558845Samw@Sun.COM 	}
9568845Samw@Sun.COM 
9579030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
9589030SMark.Shellenbaum@Sun.COM 		ZFS_EXIT(zfsvfs);
9599030SMark.Shellenbaum@Sun.COM 		return (ENOTSUP);
9609030SMark.Shellenbaum@Sun.COM 	}
9618845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0)
9628845Samw@Sun.COM 		error = VOP_LOOKUP(ZTOV(dzp), nm, vpp, pnp,
9638845Samw@Sun.COM 		    flags, rdir, cr, ct, direntflags, realpnp);
9648845Samw@Sun.COM 
9658845Samw@Sun.COM 	VN_RELE(ZTOV(dzp));
9668845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
9678845Samw@Sun.COM 
9688845Samw@Sun.COM 	return (error);
9698845Samw@Sun.COM }
9708845Samw@Sun.COM 
9718845Samw@Sun.COM /* ARGSUSED */
9728845Samw@Sun.COM static int
zfsctl_snapdir_readdir_cb(vnode_t * vp,void * dp,int * eofp,offset_t * offp,offset_t * nextp,void * data,int flags)9735663Sck153898 zfsctl_snapdir_readdir_cb(vnode_t *vp, void *dp, int *eofp,
9745663Sck153898     offset_t *offp, offset_t *nextp, void *data, int flags)
975789Sahrens {
976789Sahrens 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
977789Sahrens 	char snapname[MAXNAMELEN];
978789Sahrens 	uint64_t id, cookie;
9795663Sck153898 	boolean_t case_conflict;
9805663Sck153898 	int error;
981789Sahrens 
982789Sahrens 	ZFS_ENTER(zfsvfs);
983789Sahrens 
984789Sahrens 	cookie = *offp;
9855663Sck153898 	error = dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, snapname, &id,
9865663Sck153898 	    &cookie, &case_conflict);
9875663Sck153898 	if (error) {
988789Sahrens 		ZFS_EXIT(zfsvfs);
9895663Sck153898 		if (error == ENOENT) {
9905663Sck153898 			*eofp = 1;
9915663Sck153898 			return (0);
9925663Sck153898 		}
9935663Sck153898 		return (error);
994789Sahrens 	}
995789Sahrens 
9965663Sck153898 	if (flags & V_RDDIR_ENTFLAGS) {
9975663Sck153898 		edirent_t *eodp = dp;
9985663Sck153898 
9995663Sck153898 		(void) strcpy(eodp->ed_name, snapname);
10005663Sck153898 		eodp->ed_ino = ZFSCTL_INO_SNAP(id);
10015663Sck153898 		eodp->ed_eflags = case_conflict ? ED_CASE_CONFLICT : 0;
10025663Sck153898 	} else {
10035663Sck153898 		struct dirent64 *odp = dp;
10045663Sck153898 
10055663Sck153898 		(void) strcpy(odp->d_name, snapname);
10065663Sck153898 		odp->d_ino = ZFSCTL_INO_SNAP(id);
10075663Sck153898 	}
1008789Sahrens 	*nextp = cookie;
1009789Sahrens 
1010789Sahrens 	ZFS_EXIT(zfsvfs);
1011789Sahrens 
1012789Sahrens 	return (0);
1013789Sahrens }
1014789Sahrens 
10158845Samw@Sun.COM /* ARGSUSED */
10168845Samw@Sun.COM static int
zfsctl_shares_readdir(vnode_t * vp,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)10178845Samw@Sun.COM zfsctl_shares_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
10188845Samw@Sun.COM     caller_context_t *ct, int flags)
10198845Samw@Sun.COM {
10208845Samw@Sun.COM 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
10218845Samw@Sun.COM 	znode_t *dzp;
10228845Samw@Sun.COM 	int error;
10238845Samw@Sun.COM 
10248845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
10258845Samw@Sun.COM 
10269030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
10279030SMark.Shellenbaum@Sun.COM 		ZFS_EXIT(zfsvfs);
10289030SMark.Shellenbaum@Sun.COM 		return (ENOTSUP);
10299030SMark.Shellenbaum@Sun.COM 	}
10308845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
10318845Samw@Sun.COM 		error = VOP_READDIR(ZTOV(dzp), uiop, cr, eofp, ct, flags);
10328845Samw@Sun.COM 		VN_RELE(ZTOV(dzp));
10338845Samw@Sun.COM 	} else {
10348845Samw@Sun.COM 		*eofp = 1;
10358845Samw@Sun.COM 		error = ENOENT;
10368845Samw@Sun.COM 	}
10378845Samw@Sun.COM 
10388845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
10398845Samw@Sun.COM 	return (error);
10408845Samw@Sun.COM }
10418845Samw@Sun.COM 
10425326Sek110237 /*
10435326Sek110237  * pvp is the '.zfs' directory (zfsctl_node_t).
10445326Sek110237  * Creates vp, which is '.zfs/snapshot' (zfsctl_snapdir_t).
10455326Sek110237  *
10465326Sek110237  * This function is the callback to create a GFS vnode for '.zfs/snapshot'
10475326Sek110237  * when a lookup is performed on .zfs for "snapshot".
10485326Sek110237  */
1049789Sahrens vnode_t *
zfsctl_mknode_snapdir(vnode_t * pvp)1050789Sahrens zfsctl_mknode_snapdir(vnode_t *pvp)
1051789Sahrens {
1052789Sahrens 	vnode_t *vp;
1053789Sahrens 	zfsctl_snapdir_t *sdp;
1054789Sahrens 
1055789Sahrens 	vp = gfs_dir_create(sizeof (zfsctl_snapdir_t), pvp,
1056789Sahrens 	    zfsctl_ops_snapdir, NULL, NULL, MAXNAMELEN,
1057789Sahrens 	    zfsctl_snapdir_readdir_cb, NULL);
1058789Sahrens 	sdp = vp->v_data;
1059789Sahrens 	sdp->sd_node.zc_id = ZFSCTL_INO_SNAPDIR;
10601571Sek110237 	sdp->sd_node.zc_cmtime = ((zfsctl_node_t *)pvp->v_data)->zc_cmtime;
1061789Sahrens 	mutex_init(&sdp->sd_lock, NULL, MUTEX_DEFAULT, NULL);
1062789Sahrens 	avl_create(&sdp->sd_snaps, snapentry_compare,
1063789Sahrens 	    sizeof (zfs_snapentry_t), offsetof(zfs_snapentry_t, se_node));
1064789Sahrens 	return (vp);
1065789Sahrens }
1066789Sahrens 
10678845Samw@Sun.COM vnode_t *
zfsctl_mknode_shares(vnode_t * pvp)10688845Samw@Sun.COM zfsctl_mknode_shares(vnode_t *pvp)
10698845Samw@Sun.COM {
10708845Samw@Sun.COM 	vnode_t *vp;
10718845Samw@Sun.COM 	zfsctl_node_t *sdp;
10728845Samw@Sun.COM 
10738845Samw@Sun.COM 	vp = gfs_dir_create(sizeof (zfsctl_node_t), pvp,
10748845Samw@Sun.COM 	    zfsctl_ops_shares, NULL, NULL, MAXNAMELEN,
10758845Samw@Sun.COM 	    NULL, NULL);
10768845Samw@Sun.COM 	sdp = vp->v_data;
10778845Samw@Sun.COM 	sdp->zc_cmtime = ((zfsctl_node_t *)pvp->v_data)->zc_cmtime;
10788845Samw@Sun.COM 	return (vp);
10798845Samw@Sun.COM 
10808845Samw@Sun.COM }
10818845Samw@Sun.COM 
10828845Samw@Sun.COM /* ARGSUSED */
10838845Samw@Sun.COM static int
zfsctl_shares_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)10848845Samw@Sun.COM zfsctl_shares_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
10858845Samw@Sun.COM     caller_context_t *ct)
10868845Samw@Sun.COM {
10878845Samw@Sun.COM 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
10888845Samw@Sun.COM 	znode_t *dzp;
10898845Samw@Sun.COM 	int error;
10908845Samw@Sun.COM 
10918845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
10929030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
10939030SMark.Shellenbaum@Sun.COM 		ZFS_EXIT(zfsvfs);
10949030SMark.Shellenbaum@Sun.COM 		return (ENOTSUP);
10959030SMark.Shellenbaum@Sun.COM 	}
10968845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
10978845Samw@Sun.COM 		error = VOP_GETATTR(ZTOV(dzp), vap, flags, cr, ct);
10988845Samw@Sun.COM 		VN_RELE(ZTOV(dzp));
10998845Samw@Sun.COM 	}
11008845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
11018845Samw@Sun.COM 	return (error);
11028845Samw@Sun.COM 
11038845Samw@Sun.COM 
11048845Samw@Sun.COM }
11058845Samw@Sun.COM 
1106789Sahrens /* ARGSUSED */
1107789Sahrens static int
zfsctl_snapdir_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)11085331Samw zfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
11095331Samw     caller_context_t *ct)
1110789Sahrens {
1111789Sahrens 	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
1112789Sahrens 	zfsctl_snapdir_t *sdp = vp->v_data;
1113789Sahrens 
1114789Sahrens 	ZFS_ENTER(zfsvfs);
1115789Sahrens 	zfsctl_common_getattr(vp, vap);
1116789Sahrens 	vap->va_nodeid = gfs_file_inode(vp);
1117789Sahrens 	vap->va_nlink = vap->va_size = avl_numnodes(&sdp->sd_snaps) + 2;
111810373Schris.kirby@sun.com 	vap->va_ctime = vap->va_mtime = dmu_objset_snap_cmtime(zfsvfs->z_os);
1119789Sahrens 	ZFS_EXIT(zfsvfs);
1120789Sahrens 
1121789Sahrens 	return (0);
1122789Sahrens }
1123789Sahrens 
11241566Smaybee /* ARGSUSED */
1125789Sahrens static void
zfsctl_snapdir_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)11265331Samw zfsctl_snapdir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1127789Sahrens {
1128789Sahrens 	zfsctl_snapdir_t *sdp = vp->v_data;
11291566Smaybee 	void *private;
1130789Sahrens 
11311566Smaybee 	private = gfs_dir_inactive(vp);
11321566Smaybee 	if (private != NULL) {
11331566Smaybee 		ASSERT(avl_numnodes(&sdp->sd_snaps) == 0);
11341566Smaybee 		mutex_destroy(&sdp->sd_lock);
11351566Smaybee 		avl_destroy(&sdp->sd_snaps);
11361566Smaybee 		kmem_free(private, sizeof (zfsctl_snapdir_t));
11371566Smaybee 	}
1138789Sahrens }
1139789Sahrens 
1140789Sahrens static const fs_operation_def_t zfsctl_tops_snapdir[] = {
11413898Srsb 	{ VOPNAME_OPEN,		{ .vop_open = zfsctl_common_open }	},
11423898Srsb 	{ VOPNAME_CLOSE,	{ .vop_close = zfsctl_common_close }	},
11433898Srsb 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
11443898Srsb 	{ VOPNAME_GETATTR,	{ .vop_getattr = zfsctl_snapdir_getattr } },
11453898Srsb 	{ VOPNAME_ACCESS,	{ .vop_access = zfsctl_common_access }	},
11463898Srsb 	{ VOPNAME_RENAME,	{ .vop_rename = zfsctl_snapdir_rename }	},
11473898Srsb 	{ VOPNAME_RMDIR,	{ .vop_rmdir = zfsctl_snapdir_remove }	},
11484543Smarks 	{ VOPNAME_MKDIR,	{ .vop_mkdir = zfsctl_snapdir_mkdir }	},
11493898Srsb 	{ VOPNAME_READDIR,	{ .vop_readdir = gfs_vop_readdir }	},
11503898Srsb 	{ VOPNAME_LOOKUP,	{ .vop_lookup = zfsctl_snapdir_lookup }	},
11513898Srsb 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
11523898Srsb 	{ VOPNAME_INACTIVE,	{ .vop_inactive = zfsctl_snapdir_inactive } },
11533898Srsb 	{ VOPNAME_FID,		{ .vop_fid = zfsctl_common_fid }	},
1154789Sahrens 	{ NULL }
1155789Sahrens };
1156789Sahrens 
11578845Samw@Sun.COM static const fs_operation_def_t zfsctl_tops_shares[] = {
11588845Samw@Sun.COM 	{ VOPNAME_OPEN,		{ .vop_open = zfsctl_common_open }	},
11598845Samw@Sun.COM 	{ VOPNAME_CLOSE,	{ .vop_close = zfsctl_common_close }	},
11608845Samw@Sun.COM 	{ VOPNAME_IOCTL,	{ .error = fs_inval }			},
11618845Samw@Sun.COM 	{ VOPNAME_GETATTR,	{ .vop_getattr = zfsctl_shares_getattr } },
11628845Samw@Sun.COM 	{ VOPNAME_ACCESS,	{ .vop_access = zfsctl_common_access }	},
11638845Samw@Sun.COM 	{ VOPNAME_READDIR,	{ .vop_readdir = zfsctl_shares_readdir } },
11648845Samw@Sun.COM 	{ VOPNAME_LOOKUP,	{ .vop_lookup = zfsctl_shares_lookup }	},
11658845Samw@Sun.COM 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek }			},
11668845Samw@Sun.COM 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive } },
11678845Samw@Sun.COM 	{ VOPNAME_FID,		{ .vop_fid = zfsctl_shares_fid } },
11688845Samw@Sun.COM 	{ NULL }
11698845Samw@Sun.COM };
11708845Samw@Sun.COM 
11715326Sek110237 /*
11725326Sek110237  * pvp is the GFS vnode '.zfs/snapshot'.
11735326Sek110237  *
11745326Sek110237  * This creates a GFS node under '.zfs/snapshot' representing each
11755326Sek110237  * snapshot.  This newly created GFS node is what we mount snapshot
11765326Sek110237  * vfs_t's ontop of.
11775326Sek110237  */
1178789Sahrens static vnode_t *
zfsctl_snapshot_mknode(vnode_t * pvp,uint64_t objset)1179789Sahrens zfsctl_snapshot_mknode(vnode_t *pvp, uint64_t objset)
1180789Sahrens {
1181789Sahrens 	vnode_t *vp;
1182789Sahrens 	zfsctl_node_t *zcp;
1183789Sahrens 
1184789Sahrens 	vp = gfs_dir_create(sizeof (zfsctl_node_t), pvp,
1185789Sahrens 	    zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL);
1186789Sahrens 	zcp = vp->v_data;
1187789Sahrens 	zcp->zc_id = objset;
1188789Sahrens 
1189789Sahrens 	return (vp);
1190789Sahrens }
1191789Sahrens 
1192789Sahrens static void
zfsctl_snapshot_inactive(vnode_t * vp,cred_t * cr,caller_context_t * ct)11935331Samw zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1194789Sahrens {
1195789Sahrens 	zfsctl_snapdir_t *sdp;
1196789Sahrens 	zfs_snapentry_t *sep, *next;
1197789Sahrens 	vnode_t *dvp;
1198789Sahrens 
11996492Stimh 	VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0);
1200789Sahrens 	sdp = dvp->v_data;
1201789Sahrens 
1202789Sahrens 	mutex_enter(&sdp->sd_lock);
1203789Sahrens 
1204789Sahrens 	if (vp->v_count > 1) {
1205789Sahrens 		mutex_exit(&sdp->sd_lock);
1206789Sahrens 		return;
1207789Sahrens 	}
1208789Sahrens 	ASSERT(!vn_ismntpt(vp));
1209789Sahrens 
1210789Sahrens 	sep = avl_first(&sdp->sd_snaps);
1211789Sahrens 	while (sep != NULL) {
1212789Sahrens 		next = AVL_NEXT(&sdp->sd_snaps, sep);
1213789Sahrens 
1214789Sahrens 		if (sep->se_root == vp) {
1215789Sahrens 			avl_remove(&sdp->sd_snaps, sep);
1216789Sahrens 			kmem_free(sep->se_name, strlen(sep->se_name) + 1);
1217789Sahrens 			kmem_free(sep, sizeof (zfs_snapentry_t));
1218789Sahrens 			break;
1219789Sahrens 		}
1220789Sahrens 		sep = next;
1221789Sahrens 	}
1222789Sahrens 	ASSERT(sep != NULL);
1223789Sahrens 
1224789Sahrens 	mutex_exit(&sdp->sd_lock);
1225789Sahrens 	VN_RELE(dvp);
1226789Sahrens 
12271566Smaybee 	/*
12281566Smaybee 	 * Dispose of the vnode for the snapshot mount point.
12291566Smaybee 	 * This is safe to do because once this entry has been removed
12301566Smaybee 	 * from the AVL tree, it can't be found again, so cannot become
12311566Smaybee 	 * "active".  If we lookup the same name again we will end up
12321566Smaybee 	 * creating a new vnode.
12331566Smaybee 	 */
12345331Samw 	gfs_vop_inactive(vp, cr, ct);
1235789Sahrens }
1236789Sahrens 
1237789Sahrens 
1238789Sahrens /*
1239789Sahrens  * These VP's should never see the light of day.  They should always
1240789Sahrens  * be covered.
1241789Sahrens  */
1242789Sahrens static const fs_operation_def_t zfsctl_tops_snapshot[] = {
12433898Srsb 	VOPNAME_INACTIVE, { .vop_inactive =  zfsctl_snapshot_inactive },
1244789Sahrens 	NULL, NULL
1245789Sahrens };
1246789Sahrens 
1247789Sahrens int
zfsctl_lookup_objset(vfs_t * vfsp,uint64_t objsetid,zfsvfs_t ** zfsvfsp)1248789Sahrens zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp)
1249789Sahrens {
1250789Sahrens 	zfsvfs_t *zfsvfs = vfsp->vfs_data;
1251789Sahrens 	vnode_t *dvp, *vp;
1252789Sahrens 	zfsctl_snapdir_t *sdp;
1253789Sahrens 	zfsctl_node_t *zcp;
1254789Sahrens 	zfs_snapentry_t *sep;
1255789Sahrens 	int error;
1256789Sahrens 
1257789Sahrens 	ASSERT(zfsvfs->z_ctldir != NULL);
1258789Sahrens 	error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
12595331Samw 	    NULL, 0, NULL, kcred, NULL, NULL, NULL);
1260789Sahrens 	if (error != 0)
1261789Sahrens 		return (error);
1262789Sahrens 	sdp = dvp->v_data;
1263789Sahrens 
1264789Sahrens 	mutex_enter(&sdp->sd_lock);
1265789Sahrens 	sep = avl_first(&sdp->sd_snaps);
1266789Sahrens 	while (sep != NULL) {
1267789Sahrens 		vp = sep->se_root;
1268789Sahrens 		zcp = vp->v_data;
1269789Sahrens 		if (zcp->zc_id == objsetid)
1270789Sahrens 			break;
1271789Sahrens 
1272789Sahrens 		sep = AVL_NEXT(&sdp->sd_snaps, sep);
1273789Sahrens 	}
1274789Sahrens 
1275789Sahrens 	if (sep != NULL) {
1276789Sahrens 		VN_HOLD(vp);
12775326Sek110237 		/*
12785326Sek110237 		 * Return the mounted root rather than the covered mount point.
12795326Sek110237 		 * Takes the GFS vnode at .zfs/snapshot/<snapshot objsetid>
12805326Sek110237 		 * and returns the ZFS vnode mounted on top of the GFS node.
12815326Sek110237 		 * This ZFS vnode is the root of the vfs for objset 'objsetid'.
12825326Sek110237 		 */
1283789Sahrens 		error = traverse(&vp);
12841589Smaybee 		if (error == 0) {
12851589Smaybee 			if (vp == sep->se_root)
12861589Smaybee 				error = EINVAL;
12871589Smaybee 			else
12881589Smaybee 				*zfsvfsp = VTOZ(vp)->z_zfsvfs;
12891589Smaybee 		}
12901572Snd150628 		mutex_exit(&sdp->sd_lock);
1291789Sahrens 		VN_RELE(vp);
1292789Sahrens 	} else {
1293789Sahrens 		error = EINVAL;
12941572Snd150628 		mutex_exit(&sdp->sd_lock);
1295789Sahrens 	}
1296789Sahrens 
1297789Sahrens 	VN_RELE(dvp);
1298789Sahrens 
1299789Sahrens 	return (error);
1300789Sahrens }
1301789Sahrens 
1302789Sahrens /*
1303789Sahrens  * Unmount any snapshots for the given filesystem.  This is called from
1304789Sahrens  * zfs_umount() - if we have a ctldir, then go through and unmount all the
1305789Sahrens  * snapshots.
1306789Sahrens  */
1307789Sahrens int
zfsctl_umount_snapshots(vfs_t * vfsp,int fflags,cred_t * cr)1308789Sahrens zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
1309789Sahrens {
1310789Sahrens 	zfsvfs_t *zfsvfs = vfsp->vfs_data;
13116068Sck153898 	vnode_t *dvp;
1312789Sahrens 	zfsctl_snapdir_t *sdp;
1313789Sahrens 	zfs_snapentry_t *sep, *next;
1314789Sahrens 	int error;
1315789Sahrens 
1316789Sahrens 	ASSERT(zfsvfs->z_ctldir != NULL);
1317789Sahrens 	error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
13185331Samw 	    NULL, 0, NULL, cr, NULL, NULL, NULL);
1319789Sahrens 	if (error != 0)
1320789Sahrens 		return (error);
1321789Sahrens 	sdp = dvp->v_data;
1322789Sahrens 
1323789Sahrens 	mutex_enter(&sdp->sd_lock);
1324789Sahrens 
1325789Sahrens 	sep = avl_first(&sdp->sd_snaps);
1326789Sahrens 	while (sep != NULL) {
1327789Sahrens 		next = AVL_NEXT(&sdp->sd_snaps, sep);
1328789Sahrens 
1329789Sahrens 		/*
1330789Sahrens 		 * If this snapshot is not mounted, then it must
1331789Sahrens 		 * have just been unmounted by somebody else, and
1332789Sahrens 		 * will be cleaned up by zfsctl_snapdir_inactive().
1333789Sahrens 		 */
13346068Sck153898 		if (vn_ismntpt(sep->se_root)) {
13356068Sck153898 			avl_remove(&sdp->sd_snaps, sep);
13366068Sck153898 			error = zfsctl_unmount_snap(sep, fflags, cr);
1337789Sahrens 			if (error) {
13386068Sck153898 				avl_add(&sdp->sd_snaps, sep);
13396068Sck153898 				break;
1340789Sahrens 			}
1341789Sahrens 		}
1342789Sahrens 		sep = next;
1343789Sahrens 	}
13446068Sck153898 
1345789Sahrens 	mutex_exit(&sdp->sd_lock);
1346789Sahrens 	VN_RELE(dvp);
1347789Sahrens 
1348789Sahrens 	return (error);
1349789Sahrens }
1350