zfs_ctldir.c revision 503ad85c168c7992ccc310af845a581cff3c72b5
fa9e4066f08beec538e775443c5be79dd423fcabahrens * CDDL HEADER START
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The contents of this file are subject to the terms of the
0f2dc02ed2763dadc70bec1db8dd407e16db8e49ek * Common Development and Distribution License (the "License").
0f2dc02ed2763dadc70bec1db8dd407e16db8e49ek * You may not use this file except in compliance with the License.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * See the License for the specific language governing permissions
fa9e4066f08beec538e775443c5be79dd423fcabahrens * and limitations under the License.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * When distributing Covered Code, include this CDDL HEADER in each
fa9e4066f08beec538e775443c5be79dd423fcabahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If applicable, add the following below this CDDL HEADER, with the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * fields enclosed by brackets "[]" replaced with your own identifying
fa9e4066f08beec538e775443c5be79dd423fcabahrens * information: Portions Copyright [yyyy] [name of copyright owner]
fa9e4066f08beec538e775443c5be79dd423fcabahrens * CDDL HEADER END
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaum * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Use is subject to license terms.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * ZFS control directory (a.k.a. ".zfs")
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This directory provides a common location for all ZFS meta-objects.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Currently, this is only the 'snapshot' directory, but this may expand in the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * future. The elements are built using the GFS primitives, as the hierarchy
fa9e4066f08beec538e775443c5be79dd423fcabahrens * does not actually exist on disk.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * For 'snapshot', we don't want to have all snapshots always mounted, because
fa9e4066f08beec538e775443c5be79dd423fcabahrens * this would take up a huge amount of space in /etc/mnttab. We have three
fa9e4066f08beec538e775443c5be79dd423fcabahrens * types of objects:
fa9e4066f08beec538e775443c5be79dd423fcabahrens * ctldir ------> snapshotdir -------> snapshot
fa9e4066f08beec538e775443c5be79dd423fcabahrens * mounted fs
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The 'snapshot' node contains just enough information to lookup '..' and act
fa9e4066f08beec538e775443c5be79dd423fcabahrens * as a mountpoint for the snapshot. Whenever we lookup a specific snapshot, we
fa9e4066f08beec538e775443c5be79dd423fcabahrens * perform an automount of the underlying filesystem and return the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * corresponding vnode.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * All mounts are handled automatically by the kernel, but unmounts are
fa9e4066f08beec538e775443c5be79dd423fcabahrens * (currently) handled from user land. The main reason is that there is no
fa9e4066f08beec538e775443c5be79dd423fcabahrens * reliable way to auto-unmount the filesystem when it's "no longer in use".
fa9e4066f08beec538e775443c5be79dd423fcabahrens * When the user unmounts a filesystem, we call zfsctl_unmount(), which
fa9e4066f08beec538e775443c5be79dd423fcabahrens * unmounts any snapshots within the snapshot directory.
f18faf3f3e5def85fdfff681617d227703ace2adek * The '.zfs', '.zfs/snapshot', and all directories created under
f18faf3f3e5def85fdfff681617d227703ace2adek * '.zfs/snapshot' (ie: '.zfs/snapshot/<snapname>') are all GFS nodes and
f18faf3f3e5def85fdfff681617d227703ace2adek * share the same vfs_t as the head filesystem (what '.zfs' lives under).
f18faf3f3e5def85fdfff681617d227703ace2adek * File systems mounted ontop of the GFS nodes '.zfs/snapshot/<snapname>'
f18faf3f3e5def85fdfff681617d227703ace2adek * (ie: snapshots) are ZFS nodes and have their own unique vfs_t.
f18faf3f3e5def85fdfff681617d227703ace2adek * However, vnodes within these mounted on file systems have their v_vfsp
f18faf3f3e5def85fdfff681617d227703ace2adek * fields set to the head filesystem to make NFS happy (see
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * zfsctl_snapdir_lookup()). We VFS_HOLD the head filesystem's vfs_t
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * so that it cannot be freed until all snapshots have been unmounted.
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bcktypedef struct zfsctl_node {
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck timestruc_t zc_cmtime; /* ctime and mtime, always the same */
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bcktypedef struct zfsctl_snapdir {
fa9e4066f08beec538e775443c5be79dd423fcabahrenstypedef struct {
fa9e4066f08beec538e775443c5be79dd423fcabahrenssnapentry_compare(const void *a, const void *b)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (-1);
fa9e4066f08beec538e775443c5be79dd423fcabahrens else if (ret > 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (1);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic const fs_operation_def_t zfsctl_tops_snapdir[];
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic const fs_operation_def_t zfsctl_tops_snapshot[];
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightstatic const fs_operation_def_t zfsctl_tops_shares[];
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset);
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bckstatic int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *);
fa9e4066f08beec538e775443c5be79dd423fcabahrens { ".zfs/snapshot", zfsctl_tops_snapdir, &zfsctl_ops_snapdir },
fa9e4066f08beec538e775443c5be79dd423fcabahrens { ".zfs/snapshot/vnode", zfsctl_tops_snapshot, &zfsctl_ops_snapshot },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { ".zfs/shares", zfsctl_tops_shares, &zfsctl_ops_shares_dir },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { ".zfs/shares/vnode", zfsctl_tops_shares, &zfsctl_ops_shares },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright * Root directory elements. We only have two entries
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright * snapshot and shares.
fa9e4066f08beec538e775443c5be79dd423fcabahrens { "snapshot", zfsctl_mknode_snapdir, GFS_CACHE_VNODE },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { "shares", zfsctl_mknode_shares, GFS_CACHE_VNODE },
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* include . and .. in the calculation */
fa9e4066f08beec538e775443c5be79dd423fcabahrens#define NROOT_ENTRIES ((sizeof (zfsctl_root_entries) / \
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Initialize the various GFS pieces we'll need to create and manipulate .zfs
fa9e4066f08beec538e775443c5be79dd423fcabahrens * directories. This is called from the ZFS init routine, and initializes the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * vnode ops vectors that we'll be using.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Remove vfsctl vnode ops
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright * Return the inode number associated with the 'snapshot' or
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright * 'shares' directory.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Create the '.zfs' directory. This directory is cached as part of the VFS
fa9e4066f08beec538e775443c5be79dd423fcabahrens * structure. This results in a hold on the vfs_t. The code in zfs_umount()
fa9e4066f08beec538e775443c5be79dd423fcabahrens * therefore checks against a vfs_count of 2 instead of 1. This reference
fa9e4066f08beec538e775443c5be79dd423fcabahrens * is removed when the ctldir is destroyed in the unmount.
fa9e4066f08beec538e775443c5be79dd423fcabahrens vp = gfs_root_create(sizeof (zfsctl_node_t), zfsvfs->z_vfs,
fa9e4066f08beec538e775443c5be79dd423fcabahrens zfsctl_ops_root, ZFSCTL_INO_ROOT, zfsctl_root_entries,
a32fded1a905061abae92da5bcbab429cb6146f1ek ZFS_TIME_DECODE(&zcp->zc_cmtime, VTOZ(rvp)->z_phys->zp_crtime);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * We're only faking the fact that we have a root of a filesystem for
fa9e4066f08beec538e775443c5be79dd423fcabahrens * the sake of the GFS interfaces. Undo the flag manipulation it did
fa9e4066f08beec538e775443c5be79dd423fcabahrens vp->v_flag &= ~(VROOT | VNOCACHE | VNOMAP | VNOSWAP | VNOMOUNT);
8afd4dd66b1dd487290f73b2f35040343a3cb821perrin * Destroy the '.zfs' directory. Only called when the filesystem is unmounted.
8afd4dd66b1dd487290f73b2f35040343a3cb821perrin * There might still be more references if we were force unmounted, but only
8afd4dd66b1dd487290f73b2f35040343a3cb821perrin * new zfs_inactive() calls can occur and they don't reference .zfs
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Given a root znode, retrieve the associated .zfs directory.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Add a hold to the vnode and return it.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Common open routine. Disallow any write access.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Common close routine. Nothing to do here.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_common_close(vnode_t *vpp, int flags, int count, offset_t off,
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Common access routine. Disallow writes.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_common_access(vnode_t *vp, int mode, int flags, cred_t *cr,
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Common getattr function. Fill in basic information.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * We are a purly virtual object, so we have no
fa9e4066f08beec538e775443c5be79dd423fcabahrens * blocksize or allocated blocks.
fa9e4066f08beec538e775443c5be79dd423fcabahrens vap->va_mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
a32fded1a905061abae92da5bcbab429cb6146f1ek * We live in the now (for atime).
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*ARGSUSED*/
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* .zfs znodes always have a generation number of 0 */
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightzfsctl_shares_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * .zfs inode namespace
fa9e4066f08beec538e775443c5be79dd423fcabahrens * We need to generate unique inode numbers for all files and directories
fa9e4066f08beec538e775443c5be79dd423fcabahrens * within the .zfs pseudo-filesystem. We use the following scheme:
fa9e4066f08beec538e775443c5be79dd423fcabahrens * ENTRY ZFSCTL_INODE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * .zfs/snapshot/<snap> objectid(snap)
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Get root directory attributes.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Special case the handling of "..".
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * No extended attributes allowed under .zfs
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaumzfsctl_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaum * We only care about ACL_ENABLED so that libsec can
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaum * display ACL correctly and not default to POSIX draft.
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaum return (fs_pathconf(vp, cmd, valp, cr, ct));
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic const fs_operation_def_t zfsctl_tops_root[] = {
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_CLOSE, { .vop_close = zfsctl_common_close } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_GETATTR, { .vop_getattr = zfsctl_root_getattr } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_LOOKUP, { .vop_lookup = zfsctl_root_lookup } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } },
60030f278c9b10a64cb18b6443f7e8d97d6e45e4Mark Shellenbaum { VOPNAME_PATHCONF, { .vop_pathconf = zfsctl_pathconf } },
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname)
fa9e4066f08beec538e775443c5be79dd423fcabahrens objset_t *os = ((zfsvfs_t *)((vp)->v_vfsp->vfs_data))->z_os;
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bckzfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* this will be dropped by dounmount() */
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * We can't use VN_RELE(), as that will try to invoke
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * zfsctl_snapdir_inactive(), which would cause us to destroy
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * the sd_lock mutex held by our caller.
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm)
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Change the name in the AVL tree.
fa9e4066f08beec538e775443c5be79dd423fcabahrens VERIFY(avl_find(&sdp->sd_snaps, sep, &where) == NULL);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Change the current mountpoint info:
fa9e4066f08beec538e775443c5be79dd423fcabahrens * - update the tail of the mntpoint path
fa9e4066f08beec538e775443c5be79dd423fcabahrens * - update the tail of the resource path
0b69c2f001a429251e2d38f25aca860396551214ahrens (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath));
0b69c2f001a429251e2d38f25aca860396551214ahrens ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath));
0b69c2f001a429251e2d38f25aca860396551214ahrens (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath));
0b69c2f001a429251e2d38f25aca860396551214ahrens ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath));
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/*ARGSUSED*/
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh if (err == 0) {
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh err = zfsctl_snapshot_zname(sdvp, snm, MAXNAMELEN, from);
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh err = zfsctl_snapshot_zname(tdvp, tnm, MAXNAMELEN, to);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Cannot move snapshots out of the snapdir.
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
5422900b426b0077b9c2720a47bf829be8225830maybee if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh if ((flags & FIGNORECASE) || zfsvfs->z_case == ZFS_CASE_INSENSITIVE) {
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh if (err == 0) {
5422900b426b0077b9c2720a47bf829be8225830maybee err = zfsctl_snapshot_zname(dvp, name, MAXNAMELEN, snapname);
f18faf3f3e5def85fdfff681617d227703ace2adek * This creates a snapshot under '.zfs/snapshot'.
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks/* ARGSUSED */
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarkszfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw cred_t *cr, caller_context_t *cc, int flags, vsecattr_t *vsecp)
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks if (err == 0) {
ea2f5b9e5bf4966630882d6d681a94768aea1d75Matthew Ahrens err = dmu_objset_snapshot(name, dirname, NULL, B_FALSE);
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks err = lookupnameat(dirname, seg, follow, NULL, vpp, dvp);
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Lookup entry point for the 'snapshot' directory. Try to open the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * snapshot if it exist, creating the pseudo filesystem vnode as necessary.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Perform a mount of the associated dataset on top of the vnode.
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_snapdir_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw * No extended attributes allowed under .zfs
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If we get a recursive call, that means we got called
fa9e4066f08beec538e775443c5be79dd423fcabahrens * from the domount() code while it was trying to look up the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * spec (which looks like a local path for zfs). We need to
fa9e4066f08beec538e775443c5be79dd423fcabahrens * add some flag to domount() to tell it not to do this lookup.
142ae85d92129bf4eed1eb71b99a957379f6083aChris Kirby if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh if (err == 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) != NULL) {
bbf4a8df08a5ccce4c75fe2b82fafa4bb55b77dbmaybee * The snapshot was unmounted behind our backs,
bbf4a8df08a5ccce4c75fe2b82fafa4bb55b77dbmaybee * try to remount it.
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * VROOT was set during the traverse call. We need
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * to clear it since we're pretending to be part
8bf40bf00a7564d0ccb4a33906734188cb2d1e9bck * of our parent's vfs.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The requested snapshot is not currently mounted, look it up.
5422900b426b0077b9c2720a47bf829be8225830maybee err = zfsctl_snapshot_zname(dvp, nm, MAXNAMELEN, snapname);
2283022c7c1b8c8d3595e7fcb96a4e4bacc6141amarks * handle "ls *" or "?" in a graceful manner,
2283022c7c1b8c8d3595e7fcb96a4e4bacc6141amarks * forcing EILSEQ to ENOENT.
2283022c7c1b8c8d3595e7fcb96a4e4bacc6141amarks * Since shell ultimately passes "*" or "?" as name to lookup
503ad85c168c7992ccc310af845a581cff3c72b5Matthew Ahrens if (dmu_objset_hold(snapname, FTAG, &snap) != 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens sep = kmem_alloc(sizeof (zfs_snapentry_t), KM_SLEEP);
fa9e4066f08beec538e775443c5be79dd423fcabahrens *vpp = sep->se_root = zfsctl_snapshot_mknode(dvp, dmu_objset_id(snap));
fa9e4066f08beec538e775443c5be79dd423fcabahrens mountpoint_len = strlen(refstr_value(dvp->v_vfsp->vfs_mntpt)) +
fa9e4066f08beec538e775443c5be79dd423fcabahrens (void) snprintf(mountpoint, mountpoint_len, "%s/.zfs/snapshot/%s",
95c9592adb96db1ca55dd488cb18abe5e5c261f4maybee if (err == 0) {
95c9592adb96db1ca55dd488cb18abe5e5c261f4maybee * Return the mounted root rather than the covered mount point.
f18faf3f3e5def85fdfff681617d227703ace2adek * Takes the GFS vnode at .zfs/snapshot/<snapname> and returns
f18faf3f3e5def85fdfff681617d227703ace2adek * the ZFS vnode mounted on top of the GFS node. This ZFS
142ae85d92129bf4eed1eb71b99a957379f6083aChris Kirby * vnode is the root of the newly created vfsp.
95c9592adb96db1ca55dd488cb18abe5e5c261f4maybee if (err == 0) {
f18faf3f3e5def85fdfff681617d227703ace2adek * Fix up the root vnode mounted on .zfs/snapshot/<snapname>.
ed097989586a51afeee476d7fbba56222d447ad8ek * This is where we lie about our v_vfsp in order to
f18faf3f3e5def85fdfff681617d227703ace2adek * make .zfs/snapshot/<snapname> accessible over NFS
f18faf3f3e5def85fdfff681617d227703ace2adek * without requiring manual mounts of <snapname>.
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * If we had an error, drop our hold on the vnode and
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * zfsctl_snapshot_inactive() will clean up.
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright/* ARGSUSED */
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightzfsctl_shares_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0)
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
b38f097029665f4ece801ca129913d36f757b49cckzfsctl_snapdir_readdir_cb(vnode_t *vp, void *dp, int *eofp,
b38f097029665f4ece801ca129913d36f757b49cck error = dmu_snapshot_list_next(zfsvfs->z_os, MAXNAMELEN, snapname, &id,
b38f097029665f4ece801ca129913d36f757b49cck return (0);
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright/* ARGSUSED */
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightzfsctl_shares_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright error = VOP_READDIR(ZTOV(dzp), uiop, cr, eofp, ct, flags);
f18faf3f3e5def85fdfff681617d227703ace2adek * pvp is the '.zfs' directory (zfsctl_node_t).
f18faf3f3e5def85fdfff681617d227703ace2adek * Creates vp, which is '.zfs/snapshot' (zfsctl_snapdir_t).
f18faf3f3e5def85fdfff681617d227703ace2adek * This function is the callback to create a GFS vnode for '.zfs/snapshot'
f18faf3f3e5def85fdfff681617d227703ace2adek * when a lookup is performed on .zfs for "snapshot".
a32fded1a905061abae92da5bcbab429cb6146f1ek sdp->sd_node.zc_cmtime = ((zfsctl_node_t *)pvp->v_data)->zc_cmtime;
fa9e4066f08beec538e775443c5be79dd423fcabahrens mutex_init(&sdp->sd_lock, NULL, MUTEX_DEFAULT, NULL);
fa9e4066f08beec538e775443c5be79dd423fcabahrens sizeof (zfs_snapentry_t), offsetof(zfs_snapentry_t, se_node));
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright vp = gfs_dir_create(sizeof (zfsctl_node_t), pvp,
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright sdp->zc_cmtime = ((zfsctl_node_t *)pvp->v_data)->zc_cmtime;
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright/* ARGSUSED */
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightzfsctl_shares_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright error = VOP_GETATTR(ZTOV(dzp), vap, flags, cr, ct);
fa9e4066f08beec538e775443c5be79dd423fcabahrens/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
fa9e4066f08beec538e775443c5be79dd423fcabahrens vap->va_nlink = vap->va_size = avl_numnodes(&sdp->sd_snaps) + 2;
fa9e4066f08beec538e775443c5be79dd423fcabahrens return (0);
bb963f1cfd0c29d3203584c421c00a566367047cmaybee/* ARGSUSED */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_snapdir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic const fs_operation_def_t zfsctl_tops_snapdir[] = {
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_CLOSE, { .vop_close = zfsctl_common_close } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_GETATTR, { .vop_getattr = zfsctl_snapdir_getattr } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_RENAME, { .vop_rename = zfsctl_snapdir_rename } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_RMDIR, { .vop_rmdir = zfsctl_snapdir_remove } },
ecd6cf800b63704be73fb264c3f5b6e0dafc068dmarks { VOPNAME_MKDIR, { .vop_mkdir = zfsctl_snapdir_mkdir } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_LOOKUP, { .vop_lookup = zfsctl_snapdir_lookup } },
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb { VOPNAME_INACTIVE, { .vop_inactive = zfsctl_snapdir_inactive } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wrightstatic const fs_operation_def_t zfsctl_tops_shares[] = {
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_OPEN, { .vop_open = zfsctl_common_open } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_CLOSE, { .vop_close = zfsctl_common_close } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_GETATTR, { .vop_getattr = zfsctl_shares_getattr } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_ACCESS, { .vop_access = zfsctl_common_access } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_READDIR, { .vop_readdir = zfsctl_shares_readdir } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_LOOKUP, { .vop_lookup = zfsctl_shares_lookup } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } },
743a77ed89085d3c232c4a2f65ab4e19576839e2Alan Wright { VOPNAME_FID, { .vop_fid = zfsctl_shares_fid } },
f18faf3f3e5def85fdfff681617d227703ace2adek * pvp is the GFS vnode '.zfs/snapshot'.
f18faf3f3e5def85fdfff681617d227703ace2adek * This creates a GFS node under '.zfs/snapshot' representing each
f18faf3f3e5def85fdfff681617d227703ace2adek * snapshot. This newly created GFS node is what we mount snapshot
f18faf3f3e5def85fdfff681617d227703ace2adek * vfs_t's ontop of.
fa9e4066f08beec538e775443c5be79dd423fcabahrens zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwzfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
ab04eb8ef60d9dc9614d6cccffc474f24ca1d162timh VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr, 0, NULL, NULL) == 0);
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * Dispose of the vnode for the snapshot mount point.
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * This is safe to do because once this entry has been removed
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * from the AVL tree, it can't be found again, so cannot become
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * "active". If we lookup the same name again we will end up
bb963f1cfd0c29d3203584c421c00a566367047cmaybee * creating a new vnode.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * These VP's should never see the light of day. They should always
fa9e4066f08beec538e775443c5be79dd423fcabahrens * be covered.
fa9e4066f08beec538e775443c5be79dd423fcabahrensstatic const fs_operation_def_t zfsctl_tops_snapshot[] = {
aa59c4cb15a6ac5d4e585dadf7a055b580abf579rsb VOPNAME_INACTIVE, { .vop_inactive = zfsctl_snapshot_inactive },
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp)
fa9e4066f08beec538e775443c5be79dd423fcabahrens error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
f18faf3f3e5def85fdfff681617d227703ace2adek * Return the mounted root rather than the covered mount point.
f18faf3f3e5def85fdfff681617d227703ace2adek * Takes the GFS vnode at .zfs/snapshot/<snapshot objsetid>
f18faf3f3e5def85fdfff681617d227703ace2adek * and returns the ZFS vnode mounted on top of the GFS node.
f18faf3f3e5def85fdfff681617d227703ace2adek * This ZFS vnode is the root of the vfs for objset 'objsetid'.
bbf4a8df08a5ccce4c75fe2b82fafa4bb55b77dbmaybee if (error == 0) {
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Unmount any snapshots for the given filesystem. This is called from
fa9e4066f08beec538e775443c5be79dd423fcabahrens * zfs_umount() - if we have a ctldir, then go through and unmount all the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * snapshots.
fa9e4066f08beec538e775443c5be79dd423fcabahrenszfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr)
fa9e4066f08beec538e775443c5be79dd423fcabahrens error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * If this snapshot is not mounted, then it must
fa9e4066f08beec538e775443c5be79dd423fcabahrens * have just been unmounted by somebody else, and
fa9e4066f08beec538e775443c5be79dd423fcabahrens * will be cleaned up by zfsctl_snapdir_inactive().