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