xref: /onnv-gate/usr/src/uts/common/fs/mntfs/mntvnops.c (revision 5331:3047ad28a67b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53898Srsb  * Common Development and Distribution License (the "License").
63898Srsb  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
223898Srsb  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/file.h>
290Sstevel@tonic-gate #include <sys/stat.h>
300Sstevel@tonic-gate #include <sys/atomic.h>
310Sstevel@tonic-gate #include <sys/mntio.h>
320Sstevel@tonic-gate #include <sys/mnttab.h>
330Sstevel@tonic-gate #include <sys/mount.h>
340Sstevel@tonic-gate #include <sys/sunddi.h>
350Sstevel@tonic-gate #include <sys/sysmacros.h>
360Sstevel@tonic-gate #include <sys/systm.h>
370Sstevel@tonic-gate #include <sys/vfs.h>
383898Srsb #include <sys/vfs_opreg.h>
390Sstevel@tonic-gate #include <sys/fs/mntdata.h>
400Sstevel@tonic-gate #include <fs/fs_subr.h>
410Sstevel@tonic-gate #include <sys/vmsystm.h>
420Sstevel@tonic-gate #include <vm/seg_vn.h>
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #define	MNTROOTINO	2
450Sstevel@tonic-gate 
460Sstevel@tonic-gate static mntnode_t *mntgetnode(vnode_t *);
470Sstevel@tonic-gate 
480Sstevel@tonic-gate vnodeops_t *mntvnodeops;
494863Spraks extern void vfs_mnttab_readop(void);
500Sstevel@tonic-gate 
510Sstevel@tonic-gate /*
520Sstevel@tonic-gate  * Design of kernel mnttab accounting.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * To support whitespace in mount names, we implement an ioctl
550Sstevel@tonic-gate  * (MNTIOC_GETMNTENT) which allows a programmatic interface to the data in
560Sstevel@tonic-gate  * /etc/mnttab.  The libc functions getmntent() and getextmntent() are built
570Sstevel@tonic-gate  * atop this interface.
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  * To minimize the amount of memory used in the kernel, we keep all the
600Sstevel@tonic-gate  * necessary information in the user's address space.  Large server
610Sstevel@tonic-gate  * configurations can have /etc/mnttab files in excess of 64k.
620Sstevel@tonic-gate  *
630Sstevel@tonic-gate  * To support both vanilla read() calls as well as ioctl() calls, we have two
640Sstevel@tonic-gate  * different snapshots of the kernel data structures, mnt_read and mnt_ioctl.
650Sstevel@tonic-gate  * These snapshots include the base location in user memory, the number of
660Sstevel@tonic-gate  * mounts in the snapshot, and any metadata associated with it.  The metadata is
670Sstevel@tonic-gate  * used only to support the ioctl() interface, and is a series of extmnttab
680Sstevel@tonic-gate  * structures.  When the user issues an ioctl(), we simply copyout a pointer to
690Sstevel@tonic-gate  * that structure, and the rest is handled in userland.
700Sstevel@tonic-gate  */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /*
730Sstevel@tonic-gate  * NOTE: The following variable enables the generation of the "dev=xxx"
740Sstevel@tonic-gate  * in the option string for a mounted file system.  Really this should
750Sstevel@tonic-gate  * be gotten rid of altogether, but for the sake of backwards compatibility
760Sstevel@tonic-gate  * we had to leave it in.  It is defined as a 32-bit device number.  This
770Sstevel@tonic-gate  * means that when 64-bit device numbers are in use, if either the major or
780Sstevel@tonic-gate  * minor part of the device number will not fit in a 16 bit quantity, the
790Sstevel@tonic-gate  * "dev=" will be set to NODEV (0x7fffffff).  See PSARC 1999/566 and
800Sstevel@tonic-gate  * 1999/131 for details.  The cmpldev() function used to generate the 32-bit
810Sstevel@tonic-gate  * device number handles this check and assigns the proper value.
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate int mntfs_enabledev = 1;	/* enable old "dev=xxx" option */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static int
860Sstevel@tonic-gate mntfs_devsize(struct vfs *vfsp)
870Sstevel@tonic-gate {
880Sstevel@tonic-gate 	dev32_t odev;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	(void) cmpldev(&odev, vfsp->vfs_dev);
910Sstevel@tonic-gate 	return (snprintf(NULL, 0, "dev=%x", odev));
920Sstevel@tonic-gate }
930Sstevel@tonic-gate 
940Sstevel@tonic-gate static int
950Sstevel@tonic-gate mntfs_devprint(struct vfs *vfsp, char *buf)
960Sstevel@tonic-gate {
970Sstevel@tonic-gate 	dev32_t odev;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	(void) cmpldev(&odev, vfsp->vfs_dev);
1000Sstevel@tonic-gate 	return (snprintf(buf, MAX_MNTOPT_STR, "dev=%x", odev));
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate static int
1040Sstevel@tonic-gate mntfs_optsize(struct vfs *vfsp)
1050Sstevel@tonic-gate {
1060Sstevel@tonic-gate 	int i, size = 0;
1070Sstevel@tonic-gate 	mntopt_t *mop;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) {
1100Sstevel@tonic-gate 		mop = &vfsp->vfs_mntopts.mo_list[i];
1110Sstevel@tonic-gate 		if (mop->mo_flags & MO_NODISPLAY)
1120Sstevel@tonic-gate 			continue;
1130Sstevel@tonic-gate 		if (mop->mo_flags & MO_SET) {
1140Sstevel@tonic-gate 			if (size)
1150Sstevel@tonic-gate 				size++; /* space for comma */
1160Sstevel@tonic-gate 			size += strlen(mop->mo_name);
1170Sstevel@tonic-gate 			/*
1180Sstevel@tonic-gate 			 * count option value if there is one
1190Sstevel@tonic-gate 			 */
1200Sstevel@tonic-gate 			if (mop->mo_arg != NULL) {
1210Sstevel@tonic-gate 				size += strlen(mop->mo_arg) + 1;
1220Sstevel@tonic-gate 			}
1230Sstevel@tonic-gate 		}
1240Sstevel@tonic-gate 	}
1250Sstevel@tonic-gate 	if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) {
1260Sstevel@tonic-gate 		/*
1270Sstevel@tonic-gate 		 * Add space for "zone=<zone_name>" if required.
1280Sstevel@tonic-gate 		 */
1290Sstevel@tonic-gate 		if (size)
1300Sstevel@tonic-gate 			size++;	/* space for comma */
1310Sstevel@tonic-gate 		size += sizeof ("zone=") - 1;
1320Sstevel@tonic-gate 		size += strlen(vfsp->vfs_zone->zone_name);
1330Sstevel@tonic-gate 	}
1340Sstevel@tonic-gate 	if (mntfs_enabledev) {
1350Sstevel@tonic-gate 		if (size != 0)
1360Sstevel@tonic-gate 			size++; /* space for comma */
1370Sstevel@tonic-gate 		size += mntfs_devsize(vfsp);
1380Sstevel@tonic-gate 	}
1390Sstevel@tonic-gate 	if (size == 0)
1400Sstevel@tonic-gate 		size = strlen("-");
1410Sstevel@tonic-gate 	return (size);
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static int
1450Sstevel@tonic-gate mntfs_optprint(struct vfs *vfsp, char *buf)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate 	int i, optinbuf = 0;
1480Sstevel@tonic-gate 	mntopt_t *mop;
1490Sstevel@tonic-gate 	char *origbuf = buf;
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) {
1520Sstevel@tonic-gate 		mop = &vfsp->vfs_mntopts.mo_list[i];
1530Sstevel@tonic-gate 		if (mop->mo_flags & MO_NODISPLAY)
1540Sstevel@tonic-gate 			continue;
1550Sstevel@tonic-gate 		if (mop->mo_flags & MO_SET) {
1560Sstevel@tonic-gate 			if (optinbuf)
1570Sstevel@tonic-gate 				*buf++ = ',';
1580Sstevel@tonic-gate 			else
1590Sstevel@tonic-gate 				optinbuf = 1;
1600Sstevel@tonic-gate 			buf += snprintf(buf, MAX_MNTOPT_STR,
1610Sstevel@tonic-gate 				"%s", mop->mo_name);
1620Sstevel@tonic-gate 			/*
1630Sstevel@tonic-gate 			 * print option value if there is one
1640Sstevel@tonic-gate 			 */
1650Sstevel@tonic-gate 			if (mop->mo_arg != NULL) {
1660Sstevel@tonic-gate 				buf += snprintf(buf, MAX_MNTOPT_STR, "=%s",
1670Sstevel@tonic-gate 					mop->mo_arg);
1680Sstevel@tonic-gate 			}
1690Sstevel@tonic-gate 		}
1700Sstevel@tonic-gate 	}
1710Sstevel@tonic-gate 	if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) {
1720Sstevel@tonic-gate 		if (optinbuf)
1730Sstevel@tonic-gate 			*buf++ = ',';
1740Sstevel@tonic-gate 		else
1750Sstevel@tonic-gate 			optinbuf = 1;
1760Sstevel@tonic-gate 		buf += snprintf(buf, MAX_MNTOPT_STR, "zone=%s",
1770Sstevel@tonic-gate 		    vfsp->vfs_zone->zone_name);
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate 	if (mntfs_enabledev) {
1800Sstevel@tonic-gate 		if (optinbuf++)
1810Sstevel@tonic-gate 			*buf++ = ',';
1820Sstevel@tonic-gate 		buf += mntfs_devprint(vfsp, buf);
1830Sstevel@tonic-gate 	}
1840Sstevel@tonic-gate 	if (!optinbuf) {
1850Sstevel@tonic-gate 		buf += snprintf(buf, MAX_MNTOPT_STR, "-");
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 	return (buf - origbuf);
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate static size_t
1910Sstevel@tonic-gate mntfs_vfs_len(vfs_t *vfsp, zone_t *zone)
1920Sstevel@tonic-gate {
1930Sstevel@tonic-gate 	size_t size = 0;
1940Sstevel@tonic-gate 	const char *resource, *mntpt;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	mntpt = refstr_value(vfsp->vfs_mntpt);
1970Sstevel@tonic-gate 	if (mntpt != NULL && mntpt[0] != '\0') {
1980Sstevel@tonic-gate 		size += strlen(ZONE_PATH_TRANSLATE(mntpt, zone)) + 1;
1990Sstevel@tonic-gate 	} else {
2000Sstevel@tonic-gate 		size += strlen("-") + 1;
2010Sstevel@tonic-gate 	}
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	resource = refstr_value(vfsp->vfs_resource);
2040Sstevel@tonic-gate 	if (resource != NULL && resource[0] != '\0') {
2050Sstevel@tonic-gate 		if (resource[0] != '/') {
2060Sstevel@tonic-gate 			size += strlen(resource) + 1;
2070Sstevel@tonic-gate 		} else if (!ZONE_PATH_VISIBLE(resource, zone)) {
2080Sstevel@tonic-gate 			/*
2090Sstevel@tonic-gate 			 * Same as the zone's view of the mount point.
2100Sstevel@tonic-gate 			 */
2110Sstevel@tonic-gate 			size += strlen(ZONE_PATH_TRANSLATE(mntpt, zone)) + 1;
2120Sstevel@tonic-gate 		} else {
2130Sstevel@tonic-gate 			size += strlen(ZONE_PATH_TRANSLATE(resource, zone)) + 1;
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 	} else {
2160Sstevel@tonic-gate 		size += strlen("-") + 1;
2170Sstevel@tonic-gate 	}
2180Sstevel@tonic-gate 	size += strlen(vfssw[vfsp->vfs_fstype].vsw_name) + 1;
2190Sstevel@tonic-gate 	size += mntfs_optsize(vfsp);
2200Sstevel@tonic-gate 	size += snprintf(NULL, 0, "\t%ld\n", vfsp->vfs_mtime);
2210Sstevel@tonic-gate 	return (size);
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate static void
2250Sstevel@tonic-gate mntfs_zonerootvfs(zone_t *zone, vfs_t *rootvfsp)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	/*
2280Sstevel@tonic-gate 	 * Basically copy over the real vfs_t on which the root vnode is
2290Sstevel@tonic-gate 	 * located, changing its mountpoint and resource to match those of
2300Sstevel@tonic-gate 	 * the zone's rootpath.
2310Sstevel@tonic-gate 	 */
2320Sstevel@tonic-gate 	*rootvfsp = *zone->zone_rootvp->v_vfsp;
2330Sstevel@tonic-gate 	rootvfsp->vfs_mntpt = refstr_alloc(zone->zone_rootpath);
2340Sstevel@tonic-gate 	rootvfsp->vfs_resource = rootvfsp->vfs_mntpt;
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate static size_t
2380Sstevel@tonic-gate mntfs_zone_len(uint_t *nent_ptr, zone_t *zone, int showhidden)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	struct vfs *zonelist;
2410Sstevel@tonic-gate 	struct vfs *vfsp;
2420Sstevel@tonic-gate 	size_t size = 0;
2430Sstevel@tonic-gate 	uint_t cnt = 0;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	ASSERT(zone->zone_rootpath != NULL);
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	/*
2480Sstevel@tonic-gate 	 * If the zone has a root entry, it will be the first in the list.  If
2490Sstevel@tonic-gate 	 * it doesn't, we conjure one up.
2500Sstevel@tonic-gate 	 */
2510Sstevel@tonic-gate 	vfsp = zonelist = zone->zone_vfslist;
2520Sstevel@tonic-gate 	if (zonelist == NULL ||
2530Sstevel@tonic-gate 	    strcmp(refstr_value(vfsp->vfs_mntpt), zone->zone_rootpath) != 0) {
2540Sstevel@tonic-gate 		vfs_t tvfs;
2550Sstevel@tonic-gate 		/*
2560Sstevel@tonic-gate 		 * The root of the zone is not a mount point.  The vfs we want
2570Sstevel@tonic-gate 		 * to report is that of the zone's root vnode.
2580Sstevel@tonic-gate 		 */
2590Sstevel@tonic-gate 		ASSERT(zone != global_zone);
2600Sstevel@tonic-gate 		mntfs_zonerootvfs(zone, &tvfs);
2610Sstevel@tonic-gate 		size += mntfs_vfs_len(&tvfs, zone);
2620Sstevel@tonic-gate 		refstr_rele(tvfs.vfs_mntpt);
2630Sstevel@tonic-gate 		cnt++;
2640Sstevel@tonic-gate 	}
2650Sstevel@tonic-gate 	if (zonelist == NULL)
2660Sstevel@tonic-gate 		goto out;
2670Sstevel@tonic-gate 	do {
2680Sstevel@tonic-gate 		/*
2690Sstevel@tonic-gate 		 * Skip mounts that should not show up in mnttab
2700Sstevel@tonic-gate 		 */
2710Sstevel@tonic-gate 		if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) {
2720Sstevel@tonic-gate 			vfsp = vfsp->vfs_zone_next;
2730Sstevel@tonic-gate 			continue;
2740Sstevel@tonic-gate 		}
2750Sstevel@tonic-gate 		cnt++;
2760Sstevel@tonic-gate 		size += mntfs_vfs_len(vfsp, zone);
2770Sstevel@tonic-gate 		vfsp = vfsp->vfs_zone_next;
2780Sstevel@tonic-gate 	} while (vfsp != zonelist);
2790Sstevel@tonic-gate out:
2800Sstevel@tonic-gate 	*nent_ptr = cnt;
2810Sstevel@tonic-gate 	return (size);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate static size_t
2850Sstevel@tonic-gate mntfs_global_len(uint_t *nent_ptr, int showhidden)
2860Sstevel@tonic-gate {
2870Sstevel@tonic-gate 	struct vfs *vfsp;
2880Sstevel@tonic-gate 	size_t size = 0;
2890Sstevel@tonic-gate 	uint_t cnt = 0;
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	vfsp = rootvfs;
2920Sstevel@tonic-gate 	do {
2930Sstevel@tonic-gate 		/*
2940Sstevel@tonic-gate 		 * Skip mounts that should not show up in mnttab
2950Sstevel@tonic-gate 		 */
2960Sstevel@tonic-gate 		if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) {
2970Sstevel@tonic-gate 			vfsp = vfsp->vfs_next;
2980Sstevel@tonic-gate 			continue;
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 		cnt++;
3010Sstevel@tonic-gate 		size += mntfs_vfs_len(vfsp, global_zone);
3020Sstevel@tonic-gate 		vfsp = vfsp->vfs_next;
3030Sstevel@tonic-gate 	} while (vfsp != rootvfs);
3040Sstevel@tonic-gate 	*nent_ptr = cnt;
3050Sstevel@tonic-gate 	return (size);
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate static void
3090Sstevel@tonic-gate mntfs_vfs_generate(vfs_t *vfsp, zone_t *zone, struct extmnttab *tab,
3100Sstevel@tonic-gate     char **basep, int forread)
3110Sstevel@tonic-gate {
3120Sstevel@tonic-gate 	const char *resource, *mntpt;
3130Sstevel@tonic-gate 	char *cp = *basep;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	mntpt = refstr_value(vfsp->vfs_mntpt);
3160Sstevel@tonic-gate 	resource = refstr_value(vfsp->vfs_resource);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	if (tab)
3190Sstevel@tonic-gate 		tab->mnt_special = cp;
3200Sstevel@tonic-gate 	if (resource != NULL && resource[0] != '\0') {
3210Sstevel@tonic-gate 		if (resource[0] != '/') {
3220Sstevel@tonic-gate 			cp += snprintf(cp, MAXPATHLEN, "%s", resource);
3230Sstevel@tonic-gate 		} else if (!ZONE_PATH_VISIBLE(resource, zone)) {
3240Sstevel@tonic-gate 			/*
3250Sstevel@tonic-gate 			 * Use the mount point as the resource.
3260Sstevel@tonic-gate 			 */
3270Sstevel@tonic-gate 			cp += snprintf(cp, MAXPATHLEN, "%s",
3280Sstevel@tonic-gate 			    ZONE_PATH_TRANSLATE(mntpt, zone));
3290Sstevel@tonic-gate 		} else {
3300Sstevel@tonic-gate 			cp += snprintf(cp, MAXPATHLEN, "%s",
3310Sstevel@tonic-gate 			    ZONE_PATH_TRANSLATE(resource, zone));
3320Sstevel@tonic-gate 		}
3330Sstevel@tonic-gate 	} else {
3340Sstevel@tonic-gate 		cp += snprintf(cp, MAXPATHLEN, "-");
3350Sstevel@tonic-gate 	}
3360Sstevel@tonic-gate 	*cp++ = forread ? '\t' : '\0';
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	if (tab)
3390Sstevel@tonic-gate 		tab->mnt_mountp = cp;
3400Sstevel@tonic-gate 	if (mntpt != NULL && mntpt[0] != '\0') {
3410Sstevel@tonic-gate 		/*
3420Sstevel@tonic-gate 		 * We know the mount point is visible from within the zone,
3430Sstevel@tonic-gate 		 * otherwise it wouldn't be on the zone's vfs list.
3440Sstevel@tonic-gate 		 */
3450Sstevel@tonic-gate 		cp += snprintf(cp, MAXPATHLEN, "%s",
3460Sstevel@tonic-gate 		    ZONE_PATH_TRANSLATE(mntpt, zone));
3470Sstevel@tonic-gate 	} else {
3480Sstevel@tonic-gate 		cp += snprintf(cp, MAXPATHLEN, "-");
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 	*cp++ = forread ? '\t' : '\0';
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if (tab)
3530Sstevel@tonic-gate 		tab->mnt_fstype = cp;
3540Sstevel@tonic-gate 	cp += snprintf(cp, MAXPATHLEN, "%s",
3550Sstevel@tonic-gate 	    vfssw[vfsp->vfs_fstype].vsw_name);
3560Sstevel@tonic-gate 	*cp++ = forread ? '\t' : '\0';
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	if (tab)
3590Sstevel@tonic-gate 		tab->mnt_mntopts = cp;
3600Sstevel@tonic-gate 	cp += mntfs_optprint(vfsp, cp);
3610Sstevel@tonic-gate 	*cp++ = forread ? '\t' : '\0';
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	if (tab)
3640Sstevel@tonic-gate 		tab->mnt_time = cp;
3650Sstevel@tonic-gate 	cp += snprintf(cp, MAX_MNTOPT_STR, "%ld", vfsp->vfs_mtime);
3660Sstevel@tonic-gate 	*cp++ = forread ? '\n' : '\0';
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	if (tab) {
3690Sstevel@tonic-gate 		tab->mnt_major = getmajor(vfsp->vfs_dev);
3700Sstevel@tonic-gate 		tab->mnt_minor = getminor(vfsp->vfs_dev);
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	*basep = cp;
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate static void
3770Sstevel@tonic-gate mntfs_zone_generate(zone_t *zone, int showhidden, struct extmnttab *tab,
3780Sstevel@tonic-gate     char *basep, int forread)
3790Sstevel@tonic-gate {
3800Sstevel@tonic-gate 	vfs_t *zonelist;
3810Sstevel@tonic-gate 	vfs_t *vfsp;
3820Sstevel@tonic-gate 	char *cp = basep;
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	/*
3850Sstevel@tonic-gate 	 * If the zone has a root entry, it will be the first in the list.  If
3860Sstevel@tonic-gate 	 * it doesn't, we conjure one up.
3870Sstevel@tonic-gate 	 */
3880Sstevel@tonic-gate 	vfsp = zonelist = zone->zone_vfslist;
3890Sstevel@tonic-gate 	if (zonelist == NULL ||
3900Sstevel@tonic-gate 	    strcmp(refstr_value(vfsp->vfs_mntpt), zone->zone_rootpath) != 0) {
3910Sstevel@tonic-gate 		vfs_t tvfs;
3920Sstevel@tonic-gate 		/*
3930Sstevel@tonic-gate 		 * The root of the zone is not a mount point.  The vfs we want
3940Sstevel@tonic-gate 		 * to report is that of the zone's root vnode.
3950Sstevel@tonic-gate 		 */
3960Sstevel@tonic-gate 		ASSERT(zone != global_zone);
3970Sstevel@tonic-gate 		mntfs_zonerootvfs(zone, &tvfs);
3980Sstevel@tonic-gate 		mntfs_vfs_generate(&tvfs, zone, tab, &cp, forread);
3990Sstevel@tonic-gate 		refstr_rele(tvfs.vfs_mntpt);
4000Sstevel@tonic-gate 		if (tab)
4010Sstevel@tonic-gate 			tab++;
4020Sstevel@tonic-gate 	}
4030Sstevel@tonic-gate 	if (zonelist == NULL)
4040Sstevel@tonic-gate 		return;
4050Sstevel@tonic-gate 	do {
4060Sstevel@tonic-gate 		/*
4070Sstevel@tonic-gate 		 * Skip mounts that should not show up in mnttab
4080Sstevel@tonic-gate 		 */
4090Sstevel@tonic-gate 		if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) {
4100Sstevel@tonic-gate 			vfsp = vfsp->vfs_zone_next;
4110Sstevel@tonic-gate 			continue;
4120Sstevel@tonic-gate 		}
4130Sstevel@tonic-gate 		mntfs_vfs_generate(vfsp, zone, tab, &cp, forread);
4140Sstevel@tonic-gate 		if (tab)
4150Sstevel@tonic-gate 			tab++;
4160Sstevel@tonic-gate 		vfsp = vfsp->vfs_zone_next;
4170Sstevel@tonic-gate 	} while (vfsp != zonelist);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate static void
4210Sstevel@tonic-gate mntfs_global_generate(int showhidden, struct extmnttab *tab, char *basep,
4220Sstevel@tonic-gate     int forread)
4230Sstevel@tonic-gate {
4240Sstevel@tonic-gate 	vfs_t *vfsp;
4250Sstevel@tonic-gate 	char *cp = basep;
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	vfsp = rootvfs;
4280Sstevel@tonic-gate 	do {
4290Sstevel@tonic-gate 		/*
4300Sstevel@tonic-gate 		 * Skip mounts that should not show up in mnttab
4310Sstevel@tonic-gate 		 */
4320Sstevel@tonic-gate 		if (!showhidden && vfsp->vfs_flag & VFS_NOMNTTAB) {
4330Sstevel@tonic-gate 			vfsp = vfsp->vfs_next;
4340Sstevel@tonic-gate 			continue;
4350Sstevel@tonic-gate 		}
4360Sstevel@tonic-gate 		mntfs_vfs_generate(vfsp, global_zone, tab, &cp, forread);
4370Sstevel@tonic-gate 		if (tab)
4380Sstevel@tonic-gate 			tab++;
4390Sstevel@tonic-gate 		vfsp = vfsp->vfs_next;
4400Sstevel@tonic-gate 	} while (vfsp != rootvfs);
4410Sstevel@tonic-gate }
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate static char *
4440Sstevel@tonic-gate mntfs_mapin(char *base, size_t size)
4450Sstevel@tonic-gate {
4460Sstevel@tonic-gate 	size_t rlen = roundup(size, PAGESIZE);
4470Sstevel@tonic-gate 	struct as *as = curproc->p_as;
4480Sstevel@tonic-gate 	char *addr;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	as_rangelock(as);
4510Sstevel@tonic-gate 	map_addr(&addr, rlen, 0, 1, 0);
4520Sstevel@tonic-gate 	if (addr == NULL || as_map(as, addr, rlen, segvn_create, zfod_argsp)) {
4530Sstevel@tonic-gate 		as_rangeunlock(as);
4540Sstevel@tonic-gate 		return (NULL);
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 	as_rangeunlock(as);
4570Sstevel@tonic-gate 	if (copyout(base, addr, size)) {
4580Sstevel@tonic-gate 		(void) as_unmap(as, addr, rlen);
4590Sstevel@tonic-gate 		return (NULL);
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 	return (addr);
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate static void
4650Sstevel@tonic-gate mntfs_freesnap(mntsnap_t *snap)
4660Sstevel@tonic-gate {
4670Sstevel@tonic-gate 	if (snap->mnts_text != NULL)
4680Sstevel@tonic-gate 		(void) as_unmap(curproc->p_as, snap->mnts_text,
4690Sstevel@tonic-gate 			roundup(snap->mnts_textsize, PAGESIZE));
4700Sstevel@tonic-gate 	snap->mnts_textsize = snap->mnts_count = 0;
4710Sstevel@tonic-gate 	if (snap->mnts_metadata != NULL)
4720Sstevel@tonic-gate 		(void) as_unmap(curproc->p_as, snap->mnts_metadata,
4730Sstevel@tonic-gate 			roundup(snap->mnts_metasize, PAGESIZE));
4740Sstevel@tonic-gate 	snap->mnts_metasize = 0;
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate typedef struct extmnttab32 {
4800Sstevel@tonic-gate 	uint32_t	mnt_special;
4810Sstevel@tonic-gate 	uint32_t	mnt_mountp;
4820Sstevel@tonic-gate 	uint32_t	mnt_fstype;
4830Sstevel@tonic-gate 	uint32_t	mnt_mntopts;
4840Sstevel@tonic-gate 	uint32_t	mnt_time;
4850Sstevel@tonic-gate 	uint_t		mnt_major;
4860Sstevel@tonic-gate 	uint_t		mnt_minor;
4870Sstevel@tonic-gate } extmnttab32_t;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate #endif
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate /*
4920Sstevel@tonic-gate  * Snapshot the latest version of the kernel mounted resource information
4930Sstevel@tonic-gate  *
4940Sstevel@tonic-gate  * There are two types of snapshots: one destined for reading, and one destined
4950Sstevel@tonic-gate  * for ioctl().  The difference is that the ioctl() interface is delimited by
4960Sstevel@tonic-gate  * NULLs, while the read() interface is delimited by tabs and newlines.
4970Sstevel@tonic-gate  */
4980Sstevel@tonic-gate /* ARGSUSED */
4990Sstevel@tonic-gate static int
5000Sstevel@tonic-gate mntfs_snapshot(mntnode_t *mnp, int forread, int datamodel)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	size_t size;
5030Sstevel@tonic-gate 	timespec_t lastmodt;
5040Sstevel@tonic-gate 	mntdata_t *mntdata = MTOD(mnp);
5050Sstevel@tonic-gate 	zone_t *zone = mntdata->mnt_zone;
5060Sstevel@tonic-gate 	boolean_t global_view = (MTOD(mnp)->mnt_zone == global_zone);
5070Sstevel@tonic-gate 	boolean_t showhidden = ((mnp->mnt_flags & MNT_SHOWHIDDEN) != 0);
5080Sstevel@tonic-gate 	struct extmnttab *metadata_baseaddr;
5090Sstevel@tonic-gate 	char *text_baseaddr;
5100Sstevel@tonic-gate 	int i;
5110Sstevel@tonic-gate 	mntsnap_t *snap;
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	if (forread)
5140Sstevel@tonic-gate 		snap = &mnp->mnt_read;
5150Sstevel@tonic-gate 	else
5160Sstevel@tonic-gate 		snap = &mnp->mnt_ioctl;
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	vfs_list_read_lock();
5190Sstevel@tonic-gate 	/*
5200Sstevel@tonic-gate 	 * Check if the mnttab info has changed since the last snapshot
5210Sstevel@tonic-gate 	 */
5220Sstevel@tonic-gate 	vfs_mnttab_modtime(&lastmodt);
5230Sstevel@tonic-gate 	if (snap->mnts_count &&
5240Sstevel@tonic-gate 	    lastmodt.tv_sec == snap->mnts_time.tv_sec &&
5250Sstevel@tonic-gate 	    lastmodt.tv_nsec == snap->mnts_time.tv_nsec) {
5260Sstevel@tonic-gate 		vfs_list_unlock();
5270Sstevel@tonic-gate 		return (0);
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	if (snap->mnts_count != 0)
5320Sstevel@tonic-gate 		mntfs_freesnap(snap);
5330Sstevel@tonic-gate 	if (global_view)
5340Sstevel@tonic-gate 		size = mntfs_global_len(&snap->mnts_count, showhidden);
5350Sstevel@tonic-gate 	else
5360Sstevel@tonic-gate 		size = mntfs_zone_len(&snap->mnts_count, zone, showhidden);
5370Sstevel@tonic-gate 	ASSERT(size != 0);
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	if (!forread)
5400Sstevel@tonic-gate 		metadata_baseaddr = kmem_alloc(
5410Sstevel@tonic-gate 		    snap->mnts_count * sizeof (struct extmnttab), KM_SLEEP);
5420Sstevel@tonic-gate 	else
5430Sstevel@tonic-gate 		metadata_baseaddr = NULL;
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	text_baseaddr = kmem_alloc(size, KM_SLEEP);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	if (global_view)
5480Sstevel@tonic-gate 		mntfs_global_generate(showhidden, metadata_baseaddr,
5490Sstevel@tonic-gate 		    text_baseaddr, forread);
5500Sstevel@tonic-gate 	else
5510Sstevel@tonic-gate 		mntfs_zone_generate(zone, showhidden,
5520Sstevel@tonic-gate 		    metadata_baseaddr, text_baseaddr, forread);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	vfs_mnttab_modtime(&snap->mnts_time);
5550Sstevel@tonic-gate 	vfs_list_unlock();
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	snap->mnts_text = mntfs_mapin(text_baseaddr, size);
5580Sstevel@tonic-gate 	snap->mnts_textsize = size;
5590Sstevel@tonic-gate 	kmem_free(text_baseaddr, size);
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 	/*
5620Sstevel@tonic-gate 	 * The pointers in the metadata refer to addreesses in the range
5630Sstevel@tonic-gate 	 * [base_addr, base_addr + size].  Now that we have mapped the text into
5640Sstevel@tonic-gate 	 * the user's address space, we have to convert these addresses into the
5650Sstevel@tonic-gate 	 * new (user) range.  We also handle the conversion for 32-bit and
5660Sstevel@tonic-gate 	 * 32-bit applications here.
5670Sstevel@tonic-gate 	 */
5680Sstevel@tonic-gate 	if (!forread) {
5690Sstevel@tonic-gate 		struct extmnttab *tab;
5700Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
5710Sstevel@tonic-gate 		struct extmnttab32 *tab32;
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 		if (datamodel == DATAMODEL_ILP32) {
5740Sstevel@tonic-gate 			tab = (struct extmnttab *)metadata_baseaddr;
5750Sstevel@tonic-gate 			tab32 = (struct extmnttab32 *)metadata_baseaddr;
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 			for (i = 0; i < snap->mnts_count; i++) {
5780Sstevel@tonic-gate 				tab32[i].mnt_special =
5790Sstevel@tonic-gate 				    (uintptr_t)snap->mnts_text +
5800Sstevel@tonic-gate 				    (tab[i].mnt_special - text_baseaddr);
5810Sstevel@tonic-gate 				tab32[i].mnt_mountp =
5820Sstevel@tonic-gate 				    (uintptr_t)snap->mnts_text +
5830Sstevel@tonic-gate 				    (tab[i].mnt_mountp - text_baseaddr);
5840Sstevel@tonic-gate 				tab32[i].mnt_fstype =
5850Sstevel@tonic-gate 				    (uintptr_t)snap->mnts_text +
5860Sstevel@tonic-gate 				    (tab[i].mnt_fstype - text_baseaddr);
5870Sstevel@tonic-gate 				tab32[i].mnt_mntopts =
5880Sstevel@tonic-gate 				    (uintptr_t)snap->mnts_text +
5890Sstevel@tonic-gate 				    (tab[i].mnt_mntopts - text_baseaddr);
5900Sstevel@tonic-gate 				tab32[i].mnt_time = (uintptr_t)snap->mnts_text +
5910Sstevel@tonic-gate 				    (tab[i].mnt_time - text_baseaddr);
5920Sstevel@tonic-gate 				tab32[i].mnt_major = tab[i].mnt_major;
5930Sstevel@tonic-gate 				tab32[i].mnt_minor = tab[i].mnt_minor;
5940Sstevel@tonic-gate 			}
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 			snap->mnts_metasize =
5970Sstevel@tonic-gate 			    snap->mnts_count * sizeof (struct extmnttab32);
5980Sstevel@tonic-gate 			snap->mnts_metadata = mntfs_mapin(
5990Sstevel@tonic-gate 			    (char *)metadata_baseaddr,
6000Sstevel@tonic-gate 			    snap->mnts_metasize);
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 		} else {
6030Sstevel@tonic-gate #endif
6040Sstevel@tonic-gate 			tab = (struct extmnttab *)metadata_baseaddr;
6050Sstevel@tonic-gate 			for (i = 0; i < snap->mnts_count; i++) {
6060Sstevel@tonic-gate 				tab[i].mnt_special = snap->mnts_text +
6070Sstevel@tonic-gate 				    (tab[i].mnt_special - text_baseaddr);
6080Sstevel@tonic-gate 				tab[i].mnt_mountp = snap->mnts_text +
6090Sstevel@tonic-gate 				    (tab[i].mnt_mountp - text_baseaddr);
6100Sstevel@tonic-gate 				tab[i].mnt_fstype = snap->mnts_text +
6110Sstevel@tonic-gate 				    (tab[i].mnt_fstype - text_baseaddr);
6120Sstevel@tonic-gate 				tab[i].mnt_mntopts = snap->mnts_text +
6130Sstevel@tonic-gate 				    (tab[i].mnt_mntopts - text_baseaddr);
6140Sstevel@tonic-gate 				tab[i].mnt_time = snap->mnts_text +
6150Sstevel@tonic-gate 				    (tab[i].mnt_time - text_baseaddr);
6160Sstevel@tonic-gate 			}
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 			snap->mnts_metasize =
6190Sstevel@tonic-gate 			    snap->mnts_count * sizeof (struct extmnttab);
6200Sstevel@tonic-gate 			snap->mnts_metadata = mntfs_mapin(
6210Sstevel@tonic-gate 			    (char *)metadata_baseaddr, snap->mnts_metasize);
6220Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
6230Sstevel@tonic-gate 		}
6240Sstevel@tonic-gate #endif
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		kmem_free(metadata_baseaddr,
6270Sstevel@tonic-gate 		    snap->mnts_count * sizeof (struct extmnttab));
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	mntdata->mnt_size = size;
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	if (snap->mnts_text == NULL ||
6330Sstevel@tonic-gate 	    (!forread && snap->mnts_metadata == NULL)) {
6340Sstevel@tonic-gate 		mntfs_freesnap(snap);
6350Sstevel@tonic-gate 		return (ENOMEM);
6360Sstevel@tonic-gate 	}
6374863Spraks 	vfs_mnttab_readop();
6380Sstevel@tonic-gate 	return (0);
6390Sstevel@tonic-gate }
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate /*
6420Sstevel@tonic-gate  * Public function to convert vfs_mntopts into a string.
6430Sstevel@tonic-gate  * A buffer of sufficient size is allocated, which is returned via bufp,
6440Sstevel@tonic-gate  * and whose length is returned via lenp.
6450Sstevel@tonic-gate  */
6460Sstevel@tonic-gate void
6470Sstevel@tonic-gate mntfs_getmntopts(struct vfs *vfsp, char **bufp, size_t *lenp)
6480Sstevel@tonic-gate {
6490Sstevel@tonic-gate 	size_t len;
6500Sstevel@tonic-gate 	char *buf;
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	vfs_list_read_lock();
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	len = mntfs_optsize(vfsp) + 1;
6550Sstevel@tonic-gate 	buf = kmem_alloc(len, KM_NOSLEEP);
6560Sstevel@tonic-gate 	if (buf == NULL) {
6570Sstevel@tonic-gate 		*bufp = NULL;
6580Sstevel@tonic-gate 		vfs_list_unlock();
6590Sstevel@tonic-gate 		return;
6600Sstevel@tonic-gate 	}
6610Sstevel@tonic-gate 	buf[len - 1] = '\0';
6620Sstevel@tonic-gate 	(void) mntfs_optprint(vfsp, buf);
6630Sstevel@tonic-gate 	ASSERT(buf[len - 1] == '\0');
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	vfs_list_unlock();
6660Sstevel@tonic-gate 	*bufp = buf;
6670Sstevel@tonic-gate 	*lenp = len;
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate /* ARGSUSED */
6720Sstevel@tonic-gate static int
673*5331Samw mntopen(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
6740Sstevel@tonic-gate {
6750Sstevel@tonic-gate 	vnode_t *vp = *vpp;
6760Sstevel@tonic-gate 	mntnode_t *nmnp;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	/*
6790Sstevel@tonic-gate 	 * Not allowed to open for writing, return error.
6800Sstevel@tonic-gate 	 */
6810Sstevel@tonic-gate 	if (flag & FWRITE)
6820Sstevel@tonic-gate 		return (EPERM);
6830Sstevel@tonic-gate 	/*
6840Sstevel@tonic-gate 	 * Create a new mnt/vnode for each open, this will give us a handle to
6850Sstevel@tonic-gate 	 * hang the snapshot on.
6860Sstevel@tonic-gate 	 */
6870Sstevel@tonic-gate 	nmnp = mntgetnode(vp);
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	*vpp = MTOV(nmnp);
6900Sstevel@tonic-gate 	atomic_add_32(&MTOD(nmnp)->mnt_nopen, 1);
6910Sstevel@tonic-gate 	VN_RELE(vp);
6920Sstevel@tonic-gate 	return (0);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate /* ARGSUSED */
6960Sstevel@tonic-gate static int
697*5331Samw mntclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
698*5331Samw 	caller_context_t *ct)
6990Sstevel@tonic-gate {
7000Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	/* Clean up any locks or shares held by the current process */
7030Sstevel@tonic-gate 	cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
7040Sstevel@tonic-gate 	cleanshares(vp, ttoproc(curthread)->p_pid);
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 	if (count > 1)
7070Sstevel@tonic-gate 		return (0);
7080Sstevel@tonic-gate 	if (vp->v_count == 1) {
7090Sstevel@tonic-gate 		mntfs_freesnap(&mnp->mnt_read);
7100Sstevel@tonic-gate 		mntfs_freesnap(&mnp->mnt_ioctl);
7110Sstevel@tonic-gate 		atomic_add_32(&MTOD(mnp)->mnt_nopen, -1);
7120Sstevel@tonic-gate 	}
7130Sstevel@tonic-gate 	return (0);
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate /* ARGSUSED */
7170Sstevel@tonic-gate static int
7180Sstevel@tonic-gate mntread(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred, caller_context_t *ct)
7190Sstevel@tonic-gate {
7200Sstevel@tonic-gate 	int error = 0;
7210Sstevel@tonic-gate 	off_t off = uio->uio_offset;
7220Sstevel@tonic-gate 	size_t len = uio->uio_resid;
7230Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
7240Sstevel@tonic-gate 	char *buf;
7250Sstevel@tonic-gate 	mntsnap_t *snap = &mnp->mnt_read;
7260Sstevel@tonic-gate 	int datamodel;
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	if (off == (off_t)0 || snap->mnts_count == 0) {
7290Sstevel@tonic-gate 		/*
7300Sstevel@tonic-gate 		 * It is assumed that any kernel callers wishing
7310Sstevel@tonic-gate 		 * to read mnttab will be using extmnttab entries
7320Sstevel@tonic-gate 		 * and not extmnttab32 entries, whether or not
7330Sstevel@tonic-gate 		 * the kernel is LP64 or ILP32.  Thus, force the
7340Sstevel@tonic-gate 		 * datamodel that mntfs_snapshot uses to be
7350Sstevel@tonic-gate 		 * DATAMODEL_LP64.
7360Sstevel@tonic-gate 		 */
7370Sstevel@tonic-gate 		if (uio->uio_segflg == UIO_SYSSPACE)
7380Sstevel@tonic-gate 			datamodel = DATAMODEL_LP64;
7390Sstevel@tonic-gate 		else
7400Sstevel@tonic-gate 			datamodel = get_udatamodel();
7410Sstevel@tonic-gate 		if ((error = mntfs_snapshot(mnp, 1, datamodel)) != 0)
7420Sstevel@tonic-gate 			return (error);
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 	if ((size_t)(off + len) > snap->mnts_textsize)
7450Sstevel@tonic-gate 		len = snap->mnts_textsize - off;
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	if (off < 0 || len > snap->mnts_textsize)
7480Sstevel@tonic-gate 		return (EFAULT);
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	if (len == 0)
7510Sstevel@tonic-gate 		return (0);
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	/*
7540Sstevel@tonic-gate 	 * The mnttab image is stored in the user's address space,
7550Sstevel@tonic-gate 	 * so we have to copy it into the kernel from userland,
7560Sstevel@tonic-gate 	 * then copy it back out to the specified address.
7570Sstevel@tonic-gate 	 */
7580Sstevel@tonic-gate 	buf = kmem_alloc(len, KM_SLEEP);
7590Sstevel@tonic-gate 	if (copyin(snap->mnts_text + off, buf, len))
7600Sstevel@tonic-gate 		error = EFAULT;
7610Sstevel@tonic-gate 	else {
7620Sstevel@tonic-gate 		error = uiomove(buf, len, UIO_READ, uio);
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate 	kmem_free(buf, len);
7654863Spraks 	vfs_mnttab_readop();
7660Sstevel@tonic-gate 	return (error);
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate static int
771*5331Samw mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
772*5331Samw 	caller_context_t *ct)
7730Sstevel@tonic-gate {
7740Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
7750Sstevel@tonic-gate 	int error;
7760Sstevel@tonic-gate 	vnode_t *rvp;
7770Sstevel@tonic-gate 	extern timespec_t vfs_mnttab_ctime;
7780Sstevel@tonic-gate 	mntdata_t *mntdata = MTOD(VTOM(vp));
7790Sstevel@tonic-gate 	mntsnap_t *snap = mnp->mnt_read.mnts_count ?
7800Sstevel@tonic-gate 	    &mnp->mnt_read : &mnp->mnt_ioctl;
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	/*
7830Sstevel@tonic-gate 	 * Return all the attributes.  Should be refined
7840Sstevel@tonic-gate 	 * so that it returns only those asked for.
7850Sstevel@tonic-gate 	 * Most of this is complete fakery anyway.
7860Sstevel@tonic-gate 	 */
7870Sstevel@tonic-gate 	rvp = mnp->mnt_mountvp;
7880Sstevel@tonic-gate 	/*
7890Sstevel@tonic-gate 	 * Attributes are same as underlying file with modifications
7900Sstevel@tonic-gate 	 */
791*5331Samw 	if (error = VOP_GETATTR(rvp, vap, flags, cr, ct))
7920Sstevel@tonic-gate 		return (error);
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	/*
7950Sstevel@tonic-gate 	 * We always look like a regular file
7960Sstevel@tonic-gate 	 */
7970Sstevel@tonic-gate 	vap->va_type = VREG;
7980Sstevel@tonic-gate 	/*
7990Sstevel@tonic-gate 	 * mode should basically be read only
8000Sstevel@tonic-gate 	 */
8010Sstevel@tonic-gate 	vap->va_mode &= 07444;
8020Sstevel@tonic-gate 	vap->va_fsid = vp->v_vfsp->vfs_dev;
8030Sstevel@tonic-gate 	vap->va_blksize = DEV_BSIZE;
8040Sstevel@tonic-gate 	vap->va_rdev = 0;
8050Sstevel@tonic-gate 	vap->va_seq = 0;
8060Sstevel@tonic-gate 	/*
8070Sstevel@tonic-gate 	 * Set nlink to the number of open vnodes for mnttab info
8080Sstevel@tonic-gate 	 * plus one for existing.
8090Sstevel@tonic-gate 	 */
8100Sstevel@tonic-gate 	vap->va_nlink = mntdata->mnt_nopen + 1;
8110Sstevel@tonic-gate 	/*
8120Sstevel@tonic-gate 	 * If we haven't taken a snapshot yet, set the
8130Sstevel@tonic-gate 	 * size to the size of the latest snapshot.
8140Sstevel@tonic-gate 	 */
8150Sstevel@tonic-gate 	vap->va_size = snap->mnts_textsize ? snap->mnts_textsize :
8160Sstevel@tonic-gate 	    mntdata->mnt_size;
8170Sstevel@tonic-gate 	/*
8180Sstevel@tonic-gate 	 * Fetch mtime from the vfs mnttab timestamp
8190Sstevel@tonic-gate 	 */
8200Sstevel@tonic-gate 	vap->va_ctime = vfs_mnttab_ctime;
8210Sstevel@tonic-gate 	vfs_list_read_lock();
8220Sstevel@tonic-gate 	vfs_mnttab_modtime(&vap->va_mtime);
8230Sstevel@tonic-gate 	vap->va_atime = vap->va_mtime;
8240Sstevel@tonic-gate 	vfs_list_unlock();
8250Sstevel@tonic-gate 	/*
8260Sstevel@tonic-gate 	 * Nodeid is always ROOTINO;
8270Sstevel@tonic-gate 	 */
8280Sstevel@tonic-gate 	vap->va_nodeid = (ino64_t)MNTROOTINO;
8290Sstevel@tonic-gate 	vap->va_nblocks = btod(vap->va_size);
8300Sstevel@tonic-gate 	return (0);
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate static int
835*5331Samw mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr,
836*5331Samw 	caller_context_t *ct)
8370Sstevel@tonic-gate {
8380Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	if (mode & (VWRITE|VEXEC))
8410Sstevel@tonic-gate 		return (EROFS);
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	/*
8440Sstevel@tonic-gate 	 * Do access check on the underlying directory vnode.
8450Sstevel@tonic-gate 	 */
846*5331Samw 	return (VOP_ACCESS(mnp->mnt_mountvp, mode, flags, cr, ct));
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate /*
8510Sstevel@tonic-gate  * New /mntfs vnode required; allocate it and fill in most of the fields.
8520Sstevel@tonic-gate  */
8530Sstevel@tonic-gate static mntnode_t *
8540Sstevel@tonic-gate mntgetnode(vnode_t *dp)
8550Sstevel@tonic-gate {
8560Sstevel@tonic-gate 	mntnode_t *mnp;
8570Sstevel@tonic-gate 	vnode_t *vp;
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	mnp = kmem_zalloc(sizeof (mntnode_t), KM_SLEEP);
8600Sstevel@tonic-gate 	mnp->mnt_vnode = vn_alloc(KM_SLEEP);
8610Sstevel@tonic-gate 	mnp->mnt_mountvp = VTOM(dp)->mnt_mountvp;
8620Sstevel@tonic-gate 	vp = MTOV(mnp);
8630Sstevel@tonic-gate 	vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
8640Sstevel@tonic-gate 	vn_setops(vp, mntvnodeops);
8650Sstevel@tonic-gate 	vp->v_vfsp = dp->v_vfsp;
8660Sstevel@tonic-gate 	vp->v_type = VREG;
8670Sstevel@tonic-gate 	vp->v_data = (caddr_t)mnp;
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	return (mnp);
8700Sstevel@tonic-gate }
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate /*
8730Sstevel@tonic-gate  * Free the storage obtained from mntgetnode().
8740Sstevel@tonic-gate  */
8750Sstevel@tonic-gate static void
8760Sstevel@tonic-gate mntfreenode(mntnode_t *mnp)
8770Sstevel@tonic-gate {
8780Sstevel@tonic-gate 	vnode_t *vp = MTOV(mnp);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	vn_invalid(vp);
8810Sstevel@tonic-gate 	vn_free(vp);
8820Sstevel@tonic-gate 	kmem_free(mnp, sizeof (*mnp));
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate /* ARGSUSED */
8870Sstevel@tonic-gate static int
888*5331Samw mntfsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
8890Sstevel@tonic-gate {
8900Sstevel@tonic-gate 	return (0);
8910Sstevel@tonic-gate }
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate /* ARGSUSED */
8940Sstevel@tonic-gate static void
895*5331Samw mntinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	mntfreenode(mnp);
9000Sstevel@tonic-gate }
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate /* ARGSUSED */
9030Sstevel@tonic-gate static int
904*5331Samw mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp,
905*5331Samw 	caller_context_t *ct)
9060Sstevel@tonic-gate {
9070Sstevel@tonic-gate 	if (*noffp == 0)
9080Sstevel@tonic-gate 		VTOM(vp)->mnt_offset = 0;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	return (0);
9110Sstevel@tonic-gate }
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate /*
9140Sstevel@tonic-gate  * Return the answer requested to poll().
9150Sstevel@tonic-gate  * POLLRDBAND will return when the mtime of the mnttab
9160Sstevel@tonic-gate  * information is newer than the latest one read for this open.
9170Sstevel@tonic-gate  */
9180Sstevel@tonic-gate /* ARGSUSED */
9190Sstevel@tonic-gate static int
920*5331Samw mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp,
921*5331Samw 	caller_context_t *ct)
9220Sstevel@tonic-gate {
9230Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
9240Sstevel@tonic-gate 	mntsnap_t *snap = &mnp->mnt_read;
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	if (mnp->mnt_ioctl.mnts_time.tv_sec > snap->mnts_time.tv_sec ||
9270Sstevel@tonic-gate 	    (mnp->mnt_ioctl.mnts_time.tv_sec == snap->mnts_time.tv_sec &&
9280Sstevel@tonic-gate 	    mnp->mnt_ioctl.mnts_time.tv_nsec > snap->mnts_time.tv_nsec))
9290Sstevel@tonic-gate 		snap = &mnp->mnt_ioctl;
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	*revp = 0;
9320Sstevel@tonic-gate 	*phpp = (pollhead_t *)NULL;
9330Sstevel@tonic-gate 	if (ev & POLLIN)
9340Sstevel@tonic-gate 		*revp |= POLLIN;
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 	if (ev & POLLRDNORM)
9370Sstevel@tonic-gate 		*revp |= POLLRDNORM;
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	if (ev & POLLRDBAND) {
9400Sstevel@tonic-gate 		vfs_mnttab_poll(&snap->mnts_time, phpp);
9410Sstevel@tonic-gate 		if (*phpp == (pollhead_t *)NULL)
9420Sstevel@tonic-gate 			*revp |= POLLRDBAND;
9430Sstevel@tonic-gate 	}
9440Sstevel@tonic-gate 	if (*revp || *phpp != NULL || any) {
9450Sstevel@tonic-gate 		return (0);
9460Sstevel@tonic-gate 	}
9470Sstevel@tonic-gate 	/*
9480Sstevel@tonic-gate 	 * If someone is polling an unsupported poll events (e.g.
9490Sstevel@tonic-gate 	 * POLLOUT, POLLPRI, etc.), just return POLLERR revents.
9500Sstevel@tonic-gate 	 * That way we will ensure that we don't return a 0
9510Sstevel@tonic-gate 	 * revents with a NULL pollhead pointer.
9520Sstevel@tonic-gate 	 */
9530Sstevel@tonic-gate 	*revp = POLLERR;
9540Sstevel@tonic-gate 	return (0);
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate /* ARGSUSED */
9570Sstevel@tonic-gate static int
9580Sstevel@tonic-gate mntioctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
959*5331Samw 	cred_t *cr, int *rvalp, caller_context_t *ct)
9600Sstevel@tonic-gate {
9610Sstevel@tonic-gate 	uint_t *up = (uint_t *)arg;
9620Sstevel@tonic-gate 	mntnode_t *mnp = VTOM(vp);
9630Sstevel@tonic-gate 	mntsnap_t *snap = &mnp->mnt_ioctl;
9640Sstevel@tonic-gate 	int error;
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	error = 0;
9670Sstevel@tonic-gate 	switch (cmd) {
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	case MNTIOC_NMNTS: {		/* get no. of mounted resources */
9700Sstevel@tonic-gate 		if (snap->mnts_count == 0) {
9710Sstevel@tonic-gate 			if ((error =
9720Sstevel@tonic-gate 			    mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0)
9730Sstevel@tonic-gate 				return (error);
9740Sstevel@tonic-gate 		}
9750Sstevel@tonic-gate 		if (suword32(up, snap->mnts_count) != 0)
9760Sstevel@tonic-gate 			error = EFAULT;
9770Sstevel@tonic-gate 		break;
9780Sstevel@tonic-gate 	}
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	case MNTIOC_GETDEVLIST: {	/* get mounted device major/minor nos */
9810Sstevel@tonic-gate 		uint_t *devlist;
9820Sstevel@tonic-gate 		int i;
9830Sstevel@tonic-gate 		size_t len;
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate 		if (snap->mnts_count == 0) {
9860Sstevel@tonic-gate 			if ((error =
9870Sstevel@tonic-gate 			    mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0)
9880Sstevel@tonic-gate 				return (error);
9890Sstevel@tonic-gate 		}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 		len = 2 * snap->mnts_count * sizeof (uint_t);
9920Sstevel@tonic-gate 		devlist = kmem_alloc(len, KM_SLEEP);
9930Sstevel@tonic-gate 		for (i = 0; i < snap->mnts_count; i++) {
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
9960Sstevel@tonic-gate 			if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
9970Sstevel@tonic-gate 				struct extmnttab32 tab;
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 				if ((error = xcopyin(snap->mnts_text +
10000Sstevel@tonic-gate 				    i * sizeof (struct extmnttab32), &tab,
10010Sstevel@tonic-gate 				    sizeof (tab))) != 0)
10020Sstevel@tonic-gate 					break;
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 				devlist[i*2] = tab.mnt_major;
10050Sstevel@tonic-gate 				devlist[i*2+1] = tab.mnt_minor;
10060Sstevel@tonic-gate 			} else {
10070Sstevel@tonic-gate #endif
10080Sstevel@tonic-gate 				struct extmnttab tab;
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 				if ((error = xcopyin(snap->mnts_text +
10110Sstevel@tonic-gate 				    i * sizeof (struct extmnttab), &tab,
10120Sstevel@tonic-gate 				    sizeof (tab))) != 0)
10130Sstevel@tonic-gate 					break;
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 				devlist[i*2] = tab.mnt_major;
10160Sstevel@tonic-gate 				devlist[i*2+1] = tab.mnt_minor;
10170Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
10180Sstevel@tonic-gate 			}
10190Sstevel@tonic-gate #endif
10200Sstevel@tonic-gate 		}
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 		if (error == 0)
10230Sstevel@tonic-gate 			error = xcopyout(devlist, up, len);
10240Sstevel@tonic-gate 		kmem_free(devlist, len);
10250Sstevel@tonic-gate 		break;
10260Sstevel@tonic-gate 	}
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 	case MNTIOC_SETTAG:		/* set tag on mounted file system */
10290Sstevel@tonic-gate 	case MNTIOC_CLRTAG:		/* clear tag on mounted file system */
10300Sstevel@tonic-gate 	{
10310Sstevel@tonic-gate 		struct mnttagdesc *dp = (struct mnttagdesc *)arg;
10320Sstevel@tonic-gate 		STRUCT_DECL(mnttagdesc, tagdesc);
10330Sstevel@tonic-gate 		char *cptr;
10340Sstevel@tonic-gate 		uint32_t major, minor;
10350Sstevel@tonic-gate 		char tagbuf[MAX_MNTOPT_TAG];
10360Sstevel@tonic-gate 		char *pbuf;
10370Sstevel@tonic-gate 		size_t len;
10380Sstevel@tonic-gate 		uint_t start = 0;
10390Sstevel@tonic-gate 		mntdata_t *mntdata = MTOD(mnp);
10400Sstevel@tonic-gate 		zone_t *zone = mntdata->mnt_zone;
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 		STRUCT_INIT(tagdesc, flag & DATAMODEL_MASK);
10430Sstevel@tonic-gate 		if (copyin(dp, STRUCT_BUF(tagdesc), STRUCT_SIZE(tagdesc))) {
10440Sstevel@tonic-gate 			error = EFAULT;
10450Sstevel@tonic-gate 			break;
10460Sstevel@tonic-gate 		}
10470Sstevel@tonic-gate 		pbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10480Sstevel@tonic-gate 		if (zone != global_zone) {
10490Sstevel@tonic-gate 			(void) strcpy(pbuf, zone->zone_rootpath);
10500Sstevel@tonic-gate 			/* truncate "/" and nul */
10510Sstevel@tonic-gate 			start = zone->zone_rootpathlen - 2;
10520Sstevel@tonic-gate 			ASSERT(pbuf[start] == '/');
10530Sstevel@tonic-gate 		}
10540Sstevel@tonic-gate 		cptr = STRUCT_FGETP(tagdesc, mtd_mntpt);
10550Sstevel@tonic-gate 		error = copyinstr(cptr, pbuf + start, MAXPATHLEN - start, &len);
10560Sstevel@tonic-gate 		if (error) {
10570Sstevel@tonic-gate 			kmem_free(pbuf, MAXPATHLEN);
10580Sstevel@tonic-gate 			break;
10590Sstevel@tonic-gate 		}
10600Sstevel@tonic-gate 		if (start != 0 && pbuf[start] != '/') {
10610Sstevel@tonic-gate 			kmem_free(pbuf, MAXPATHLEN);
10620Sstevel@tonic-gate 			error = EINVAL;
10630Sstevel@tonic-gate 			break;
10640Sstevel@tonic-gate 		}
10650Sstevel@tonic-gate 		cptr = STRUCT_FGETP(tagdesc, mtd_tag);
10660Sstevel@tonic-gate 		if ((error = copyinstr(cptr, tagbuf, MAX_MNTOPT_TAG, &len))) {
10670Sstevel@tonic-gate 			kmem_free(pbuf, MAXPATHLEN);
10680Sstevel@tonic-gate 			break;
10690Sstevel@tonic-gate 		}
10700Sstevel@tonic-gate 		major = STRUCT_FGET(tagdesc, mtd_major);
10710Sstevel@tonic-gate 		minor = STRUCT_FGET(tagdesc, mtd_minor);
10720Sstevel@tonic-gate 		if (cmd == MNTIOC_SETTAG)
10730Sstevel@tonic-gate 			error = vfs_settag(major, minor, pbuf, tagbuf, cr);
10740Sstevel@tonic-gate 		else
10750Sstevel@tonic-gate 			error = vfs_clrtag(major, minor, pbuf, tagbuf, cr);
10760Sstevel@tonic-gate 		kmem_free(pbuf, MAXPATHLEN);
10770Sstevel@tonic-gate 		break;
10780Sstevel@tonic-gate 	}
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	case MNTIOC_SHOWHIDDEN:
10810Sstevel@tonic-gate 	{
10820Sstevel@tonic-gate 		mutex_enter(&vp->v_lock);
10830Sstevel@tonic-gate 		mnp->mnt_flags |= MNT_SHOWHIDDEN;
10840Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
10850Sstevel@tonic-gate 		break;
10860Sstevel@tonic-gate 	}
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 	case MNTIOC_GETMNTENT:
10890Sstevel@tonic-gate 	{
10900Sstevel@tonic-gate 		size_t idx;
10910Sstevel@tonic-gate 		uintptr_t addr;
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 		idx = mnp->mnt_offset;
10940Sstevel@tonic-gate 		if (snap->mnts_count == 0 || idx == 0) {
10950Sstevel@tonic-gate 			if ((error =
10960Sstevel@tonic-gate 			    mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0)
10970Sstevel@tonic-gate 				return (error);
10980Sstevel@tonic-gate 		}
10990Sstevel@tonic-gate 		/*
11000Sstevel@tonic-gate 		 * If the next index is beyond the end of the current mnttab,
11010Sstevel@tonic-gate 		 * return EOF
11020Sstevel@tonic-gate 		 */
11030Sstevel@tonic-gate 		if (idx >= snap->mnts_count) {
11040Sstevel@tonic-gate 			*rvalp = 1;
11050Sstevel@tonic-gate 			return (0);
11060Sstevel@tonic-gate 		}
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
11090Sstevel@tonic-gate 		if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) {
11100Sstevel@tonic-gate 			addr = (uintptr_t)(snap->mnts_metadata + idx *
11110Sstevel@tonic-gate 			    sizeof (struct extmnttab32));
11120Sstevel@tonic-gate 			error = suword32((void *)arg, addr);
11130Sstevel@tonic-gate 		} else {
11140Sstevel@tonic-gate #endif
11150Sstevel@tonic-gate 			addr = (uintptr_t)(snap->mnts_metadata + idx *
11160Sstevel@tonic-gate 			    sizeof (struct extmnttab));
11170Sstevel@tonic-gate 			error = sulword((void *)arg, addr);
11180Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
11190Sstevel@tonic-gate 		}
11200Sstevel@tonic-gate #endif
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 		if (error != 0)
11230Sstevel@tonic-gate 			return (error);
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate 		mnp->mnt_offset++;
11260Sstevel@tonic-gate 		break;
11270Sstevel@tonic-gate 	}
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 	default:
11300Sstevel@tonic-gate 		error = EINVAL;
11310Sstevel@tonic-gate 		break;
11320Sstevel@tonic-gate 	}
11330Sstevel@tonic-gate 
11340Sstevel@tonic-gate 	return (error);
11350Sstevel@tonic-gate }
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate /*
11380Sstevel@tonic-gate  * /mntfs vnode operations vector
11390Sstevel@tonic-gate  */
11400Sstevel@tonic-gate const fs_operation_def_t mnt_vnodeops_template[] = {
11413898Srsb 	VOPNAME_OPEN,		{ .vop_open = mntopen },
11423898Srsb 	VOPNAME_CLOSE,		{ .vop_close = mntclose },
11433898Srsb 	VOPNAME_READ,		{ .vop_read = mntread },
11443898Srsb 	VOPNAME_IOCTL,		{ .vop_ioctl = mntioctl },
11453898Srsb 	VOPNAME_GETATTR,	{ .vop_getattr = mntgetattr },
11463898Srsb 	VOPNAME_ACCESS,		{ .vop_access = mntaccess },
11473898Srsb 	VOPNAME_FSYNC,		{ .vop_fsync = mntfsync },
11483898Srsb 	VOPNAME_INACTIVE,	{ .vop_inactive = mntinactive },
11493898Srsb 	VOPNAME_SEEK,		{ .vop_seek = mntseek },
11503898Srsb 	VOPNAME_POLL,		{ .vop_poll = mntpoll },
11513898Srsb 	VOPNAME_DISPOSE,	{ .error = fs_error },
11523898Srsb 	VOPNAME_SHRLOCK,	{ .error = fs_error },
11533898Srsb 	NULL,			NULL
11540Sstevel@tonic-gate };
1155