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