1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/file.h> 30*0Sstevel@tonic-gate #include <sys/stat.h> 31*0Sstevel@tonic-gate #include <sys/atomic.h> 32*0Sstevel@tonic-gate #include <sys/mntio.h> 33*0Sstevel@tonic-gate #include <sys/mnttab.h> 34*0Sstevel@tonic-gate #include <sys/mount.h> 35*0Sstevel@tonic-gate #include <sys/sunddi.h> 36*0Sstevel@tonic-gate #include <sys/sysmacros.h> 37*0Sstevel@tonic-gate #include <sys/systm.h> 38*0Sstevel@tonic-gate #include <sys/vfs.h> 39*0Sstevel@tonic-gate #include <sys/fs/mntdata.h> 40*0Sstevel@tonic-gate #include <fs/fs_subr.h> 41*0Sstevel@tonic-gate #include <sys/vmsystm.h> 42*0Sstevel@tonic-gate #include <vm/seg_vn.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #define MNTROOTINO 2 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate static mntnode_t *mntgetnode(vnode_t *); 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate vnodeops_t *mntvnodeops; 49*0Sstevel@tonic-gate 50*0Sstevel@tonic-gate /* 51*0Sstevel@tonic-gate * Design of kernel mnttab accounting. 52*0Sstevel@tonic-gate * 53*0Sstevel@tonic-gate * To support whitespace in mount names, we implement an ioctl 54*0Sstevel@tonic-gate * (MNTIOC_GETMNTENT) which allows a programmatic interface to the data in 55*0Sstevel@tonic-gate * /etc/mnttab. The libc functions getmntent() and getextmntent() are built 56*0Sstevel@tonic-gate * atop this interface. 57*0Sstevel@tonic-gate * 58*0Sstevel@tonic-gate * To minimize the amount of memory used in the kernel, we keep all the 59*0Sstevel@tonic-gate * necessary information in the user's address space. Large server 60*0Sstevel@tonic-gate * configurations can have /etc/mnttab files in excess of 64k. 61*0Sstevel@tonic-gate * 62*0Sstevel@tonic-gate * To support both vanilla read() calls as well as ioctl() calls, we have two 63*0Sstevel@tonic-gate * different snapshots of the kernel data structures, mnt_read and mnt_ioctl. 64*0Sstevel@tonic-gate * These snapshots include the base location in user memory, the number of 65*0Sstevel@tonic-gate * mounts in the snapshot, and any metadata associated with it. The metadata is 66*0Sstevel@tonic-gate * used only to support the ioctl() interface, and is a series of extmnttab 67*0Sstevel@tonic-gate * structures. When the user issues an ioctl(), we simply copyout a pointer to 68*0Sstevel@tonic-gate * that structure, and the rest is handled in userland. 69*0Sstevel@tonic-gate */ 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate /* 72*0Sstevel@tonic-gate * NOTE: The following variable enables the generation of the "dev=xxx" 73*0Sstevel@tonic-gate * in the option string for a mounted file system. Really this should 74*0Sstevel@tonic-gate * be gotten rid of altogether, but for the sake of backwards compatibility 75*0Sstevel@tonic-gate * we had to leave it in. It is defined as a 32-bit device number. This 76*0Sstevel@tonic-gate * means that when 64-bit device numbers are in use, if either the major or 77*0Sstevel@tonic-gate * minor part of the device number will not fit in a 16 bit quantity, the 78*0Sstevel@tonic-gate * "dev=" will be set to NODEV (0x7fffffff). See PSARC 1999/566 and 79*0Sstevel@tonic-gate * 1999/131 for details. The cmpldev() function used to generate the 32-bit 80*0Sstevel@tonic-gate * device number handles this check and assigns the proper value. 81*0Sstevel@tonic-gate */ 82*0Sstevel@tonic-gate int mntfs_enabledev = 1; /* enable old "dev=xxx" option */ 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate static int 85*0Sstevel@tonic-gate mntfs_devsize(struct vfs *vfsp) 86*0Sstevel@tonic-gate { 87*0Sstevel@tonic-gate dev32_t odev; 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate (void) cmpldev(&odev, vfsp->vfs_dev); 90*0Sstevel@tonic-gate return (snprintf(NULL, 0, "dev=%x", odev)); 91*0Sstevel@tonic-gate } 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate static int 94*0Sstevel@tonic-gate mntfs_devprint(struct vfs *vfsp, char *buf) 95*0Sstevel@tonic-gate { 96*0Sstevel@tonic-gate dev32_t odev; 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate (void) cmpldev(&odev, vfsp->vfs_dev); 99*0Sstevel@tonic-gate return (snprintf(buf, MAX_MNTOPT_STR, "dev=%x", odev)); 100*0Sstevel@tonic-gate } 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate static int 103*0Sstevel@tonic-gate mntfs_optsize(struct vfs *vfsp) 104*0Sstevel@tonic-gate { 105*0Sstevel@tonic-gate int i, size = 0; 106*0Sstevel@tonic-gate mntopt_t *mop; 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) { 109*0Sstevel@tonic-gate mop = &vfsp->vfs_mntopts.mo_list[i]; 110*0Sstevel@tonic-gate if (mop->mo_flags & MO_NODISPLAY) 111*0Sstevel@tonic-gate continue; 112*0Sstevel@tonic-gate if (mop->mo_flags & MO_SET) { 113*0Sstevel@tonic-gate if (size) 114*0Sstevel@tonic-gate size++; /* space for comma */ 115*0Sstevel@tonic-gate size += strlen(mop->mo_name); 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * count option value if there is one 118*0Sstevel@tonic-gate */ 119*0Sstevel@tonic-gate if (mop->mo_arg != NULL) { 120*0Sstevel@tonic-gate size += strlen(mop->mo_arg) + 1; 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate } 123*0Sstevel@tonic-gate } 124*0Sstevel@tonic-gate if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) { 125*0Sstevel@tonic-gate /* 126*0Sstevel@tonic-gate * Add space for "zone=<zone_name>" if required. 127*0Sstevel@tonic-gate */ 128*0Sstevel@tonic-gate if (size) 129*0Sstevel@tonic-gate size++; /* space for comma */ 130*0Sstevel@tonic-gate size += sizeof ("zone=") - 1; 131*0Sstevel@tonic-gate size += strlen(vfsp->vfs_zone->zone_name); 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate if (mntfs_enabledev) { 134*0Sstevel@tonic-gate if (size != 0) 135*0Sstevel@tonic-gate size++; /* space for comma */ 136*0Sstevel@tonic-gate size += mntfs_devsize(vfsp); 137*0Sstevel@tonic-gate } 138*0Sstevel@tonic-gate if (size == 0) 139*0Sstevel@tonic-gate size = strlen("-"); 140*0Sstevel@tonic-gate return (size); 141*0Sstevel@tonic-gate } 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate static int 144*0Sstevel@tonic-gate mntfs_optprint(struct vfs *vfsp, char *buf) 145*0Sstevel@tonic-gate { 146*0Sstevel@tonic-gate int i, optinbuf = 0; 147*0Sstevel@tonic-gate mntopt_t *mop; 148*0Sstevel@tonic-gate char *origbuf = buf; 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) { 151*0Sstevel@tonic-gate mop = &vfsp->vfs_mntopts.mo_list[i]; 152*0Sstevel@tonic-gate if (mop->mo_flags & MO_NODISPLAY) 153*0Sstevel@tonic-gate continue; 154*0Sstevel@tonic-gate if (mop->mo_flags & MO_SET) { 155*0Sstevel@tonic-gate if (optinbuf) 156*0Sstevel@tonic-gate *buf++ = ','; 157*0Sstevel@tonic-gate else 158*0Sstevel@tonic-gate optinbuf = 1; 159*0Sstevel@tonic-gate buf += snprintf(buf, MAX_MNTOPT_STR, 160*0Sstevel@tonic-gate "%s", mop->mo_name); 161*0Sstevel@tonic-gate /* 162*0Sstevel@tonic-gate * print option value if there is one 163*0Sstevel@tonic-gate */ 164*0Sstevel@tonic-gate if (mop->mo_arg != NULL) { 165*0Sstevel@tonic-gate buf += snprintf(buf, MAX_MNTOPT_STR, "=%s", 166*0Sstevel@tonic-gate mop->mo_arg); 167*0Sstevel@tonic-gate } 168*0Sstevel@tonic-gate } 169*0Sstevel@tonic-gate } 170*0Sstevel@tonic-gate if (vfsp->vfs_zone != NULL && vfsp->vfs_zone != global_zone) { 171*0Sstevel@tonic-gate if (optinbuf) 172*0Sstevel@tonic-gate *buf++ = ','; 173*0Sstevel@tonic-gate else 174*0Sstevel@tonic-gate optinbuf = 1; 175*0Sstevel@tonic-gate buf += snprintf(buf, MAX_MNTOPT_STR, "zone=%s", 176*0Sstevel@tonic-gate vfsp->vfs_zone->zone_name); 177*0Sstevel@tonic-gate } 178*0Sstevel@tonic-gate if (mntfs_enabledev) { 179*0Sstevel@tonic-gate if (optinbuf++) 180*0Sstevel@tonic-gate *buf++ = ','; 181*0Sstevel@tonic-gate buf += mntfs_devprint(vfsp, buf); 182*0Sstevel@tonic-gate } 183*0Sstevel@tonic-gate if (!optinbuf) { 184*0Sstevel@tonic-gate buf += snprintf(buf, MAX_MNTOPT_STR, "-"); 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate return (buf - origbuf); 187*0Sstevel@tonic-gate } 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate static size_t 190*0Sstevel@tonic-gate mntfs_vfs_len(vfs_t *vfsp, zone_t *zone) 191*0Sstevel@tonic-gate { 192*0Sstevel@tonic-gate size_t size = 0; 193*0Sstevel@tonic-gate const char *resource, *mntpt; 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate mntpt = refstr_value(vfsp->vfs_mntpt); 196*0Sstevel@tonic-gate if (mntpt != NULL && mntpt[0] != '\0') { 197*0Sstevel@tonic-gate size += strlen(ZONE_PATH_TRANSLATE(mntpt, zone)) + 1; 198*0Sstevel@tonic-gate } else { 199*0Sstevel@tonic-gate size += strlen("-") + 1; 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate resource = refstr_value(vfsp->vfs_resource); 203*0Sstevel@tonic-gate if (resource != NULL && resource[0] != '\0') { 204*0Sstevel@tonic-gate if (resource[0] != '/') { 205*0Sstevel@tonic-gate size += strlen(resource) + 1; 206*0Sstevel@tonic-gate } else if (!ZONE_PATH_VISIBLE(resource, zone)) { 207*0Sstevel@tonic-gate /* 208*0Sstevel@tonic-gate * Same as the zone's view of the mount point. 209*0Sstevel@tonic-gate */ 210*0Sstevel@tonic-gate size += strlen(ZONE_PATH_TRANSLATE(mntpt, zone)) + 1; 211*0Sstevel@tonic-gate } else { 212*0Sstevel@tonic-gate size += strlen(ZONE_PATH_TRANSLATE(resource, zone)) + 1; 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate } else { 215*0Sstevel@tonic-gate size += strlen("-") + 1; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate size += strlen(vfssw[vfsp->vfs_fstype].vsw_name) + 1; 218*0Sstevel@tonic-gate size += mntfs_optsize(vfsp); 219*0Sstevel@tonic-gate size += snprintf(NULL, 0, "\t%ld\n", vfsp->vfs_mtime); 220*0Sstevel@tonic-gate return (size); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate static void 224*0Sstevel@tonic-gate mntfs_zonerootvfs(zone_t *zone, vfs_t *rootvfsp) 225*0Sstevel@tonic-gate { 226*0Sstevel@tonic-gate /* 227*0Sstevel@tonic-gate * Basically copy over the real vfs_t on which the root vnode is 228*0Sstevel@tonic-gate * located, changing its mountpoint and resource to match those of 229*0Sstevel@tonic-gate * the zone's rootpath. 230*0Sstevel@tonic-gate */ 231*0Sstevel@tonic-gate *rootvfsp = *zone->zone_rootvp->v_vfsp; 232*0Sstevel@tonic-gate rootvfsp->vfs_mntpt = refstr_alloc(zone->zone_rootpath); 233*0Sstevel@tonic-gate rootvfsp->vfs_resource = rootvfsp->vfs_mntpt; 234*0Sstevel@tonic-gate } 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate static size_t 237*0Sstevel@tonic-gate mntfs_zone_len(uint_t *nent_ptr, zone_t *zone, int showhidden) 238*0Sstevel@tonic-gate { 239*0Sstevel@tonic-gate struct vfs *zonelist; 240*0Sstevel@tonic-gate struct vfs *vfsp; 241*0Sstevel@tonic-gate size_t size = 0; 242*0Sstevel@tonic-gate uint_t cnt = 0; 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate ASSERT(zone->zone_rootpath != NULL); 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate /* 247*0Sstevel@tonic-gate * If the zone has a root entry, it will be the first in the list. If 248*0Sstevel@tonic-gate * it doesn't, we conjure one up. 249*0Sstevel@tonic-gate */ 250*0Sstevel@tonic-gate vfsp = zonelist = zone->zone_vfslist; 251*0Sstevel@tonic-gate if (zonelist == NULL || 252*0Sstevel@tonic-gate strcmp(refstr_value(vfsp->vfs_mntpt), zone->zone_rootpath) != 0) { 253*0Sstevel@tonic-gate vfs_t tvfs; 254*0Sstevel@tonic-gate /* 255*0Sstevel@tonic-gate * The root of the zone is not a mount point. The vfs we want 256*0Sstevel@tonic-gate * to report is that of the zone's root vnode. 257*0Sstevel@tonic-gate */ 258*0Sstevel@tonic-gate ASSERT(zone != global_zone); 259*0Sstevel@tonic-gate mntfs_zonerootvfs(zone, &tvfs); 260*0Sstevel@tonic-gate size += mntfs_vfs_len(&tvfs, zone); 261*0Sstevel@tonic-gate refstr_rele(tvfs.vfs_mntpt); 262*0Sstevel@tonic-gate cnt++; 263*0Sstevel@tonic-gate } 264*0Sstevel@tonic-gate if (zonelist == NULL) 265*0Sstevel@tonic-gate goto out; 266*0Sstevel@tonic-gate do { 267*0Sstevel@tonic-gate /* 268*0Sstevel@tonic-gate * Skip mounts that should not show up in mnttab 269*0Sstevel@tonic-gate */ 270*0Sstevel@tonic-gate if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) { 271*0Sstevel@tonic-gate vfsp = vfsp->vfs_zone_next; 272*0Sstevel@tonic-gate continue; 273*0Sstevel@tonic-gate } 274*0Sstevel@tonic-gate cnt++; 275*0Sstevel@tonic-gate size += mntfs_vfs_len(vfsp, zone); 276*0Sstevel@tonic-gate vfsp = vfsp->vfs_zone_next; 277*0Sstevel@tonic-gate } while (vfsp != zonelist); 278*0Sstevel@tonic-gate out: 279*0Sstevel@tonic-gate *nent_ptr = cnt; 280*0Sstevel@tonic-gate return (size); 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate static size_t 284*0Sstevel@tonic-gate mntfs_global_len(uint_t *nent_ptr, int showhidden) 285*0Sstevel@tonic-gate { 286*0Sstevel@tonic-gate struct vfs *vfsp; 287*0Sstevel@tonic-gate size_t size = 0; 288*0Sstevel@tonic-gate uint_t cnt = 0; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate vfsp = rootvfs; 291*0Sstevel@tonic-gate do { 292*0Sstevel@tonic-gate /* 293*0Sstevel@tonic-gate * Skip mounts that should not show up in mnttab 294*0Sstevel@tonic-gate */ 295*0Sstevel@tonic-gate if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) { 296*0Sstevel@tonic-gate vfsp = vfsp->vfs_next; 297*0Sstevel@tonic-gate continue; 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate cnt++; 300*0Sstevel@tonic-gate size += mntfs_vfs_len(vfsp, global_zone); 301*0Sstevel@tonic-gate vfsp = vfsp->vfs_next; 302*0Sstevel@tonic-gate } while (vfsp != rootvfs); 303*0Sstevel@tonic-gate *nent_ptr = cnt; 304*0Sstevel@tonic-gate return (size); 305*0Sstevel@tonic-gate } 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate static void 308*0Sstevel@tonic-gate mntfs_vfs_generate(vfs_t *vfsp, zone_t *zone, struct extmnttab *tab, 309*0Sstevel@tonic-gate char **basep, int forread) 310*0Sstevel@tonic-gate { 311*0Sstevel@tonic-gate const char *resource, *mntpt; 312*0Sstevel@tonic-gate char *cp = *basep; 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate mntpt = refstr_value(vfsp->vfs_mntpt); 315*0Sstevel@tonic-gate resource = refstr_value(vfsp->vfs_resource); 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate if (tab) 318*0Sstevel@tonic-gate tab->mnt_special = cp; 319*0Sstevel@tonic-gate if (resource != NULL && resource[0] != '\0') { 320*0Sstevel@tonic-gate if (resource[0] != '/') { 321*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "%s", resource); 322*0Sstevel@tonic-gate } else if (!ZONE_PATH_VISIBLE(resource, zone)) { 323*0Sstevel@tonic-gate /* 324*0Sstevel@tonic-gate * Use the mount point as the resource. 325*0Sstevel@tonic-gate */ 326*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "%s", 327*0Sstevel@tonic-gate ZONE_PATH_TRANSLATE(mntpt, zone)); 328*0Sstevel@tonic-gate } else { 329*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "%s", 330*0Sstevel@tonic-gate ZONE_PATH_TRANSLATE(resource, zone)); 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate } else { 333*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "-"); 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate *cp++ = forread ? '\t' : '\0'; 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate if (tab) 338*0Sstevel@tonic-gate tab->mnt_mountp = cp; 339*0Sstevel@tonic-gate if (mntpt != NULL && mntpt[0] != '\0') { 340*0Sstevel@tonic-gate /* 341*0Sstevel@tonic-gate * We know the mount point is visible from within the zone, 342*0Sstevel@tonic-gate * otherwise it wouldn't be on the zone's vfs list. 343*0Sstevel@tonic-gate */ 344*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "%s", 345*0Sstevel@tonic-gate ZONE_PATH_TRANSLATE(mntpt, zone)); 346*0Sstevel@tonic-gate } else { 347*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "-"); 348*0Sstevel@tonic-gate } 349*0Sstevel@tonic-gate *cp++ = forread ? '\t' : '\0'; 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate if (tab) 352*0Sstevel@tonic-gate tab->mnt_fstype = cp; 353*0Sstevel@tonic-gate cp += snprintf(cp, MAXPATHLEN, "%s", 354*0Sstevel@tonic-gate vfssw[vfsp->vfs_fstype].vsw_name); 355*0Sstevel@tonic-gate *cp++ = forread ? '\t' : '\0'; 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate if (tab) 358*0Sstevel@tonic-gate tab->mnt_mntopts = cp; 359*0Sstevel@tonic-gate cp += mntfs_optprint(vfsp, cp); 360*0Sstevel@tonic-gate *cp++ = forread ? '\t' : '\0'; 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate if (tab) 363*0Sstevel@tonic-gate tab->mnt_time = cp; 364*0Sstevel@tonic-gate cp += snprintf(cp, MAX_MNTOPT_STR, "%ld", vfsp->vfs_mtime); 365*0Sstevel@tonic-gate *cp++ = forread ? '\n' : '\0'; 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate if (tab) { 368*0Sstevel@tonic-gate tab->mnt_major = getmajor(vfsp->vfs_dev); 369*0Sstevel@tonic-gate tab->mnt_minor = getminor(vfsp->vfs_dev); 370*0Sstevel@tonic-gate } 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate *basep = cp; 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate static void 376*0Sstevel@tonic-gate mntfs_zone_generate(zone_t *zone, int showhidden, struct extmnttab *tab, 377*0Sstevel@tonic-gate char *basep, int forread) 378*0Sstevel@tonic-gate { 379*0Sstevel@tonic-gate vfs_t *zonelist; 380*0Sstevel@tonic-gate vfs_t *vfsp; 381*0Sstevel@tonic-gate char *cp = basep; 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate /* 384*0Sstevel@tonic-gate * If the zone has a root entry, it will be the first in the list. If 385*0Sstevel@tonic-gate * it doesn't, we conjure one up. 386*0Sstevel@tonic-gate */ 387*0Sstevel@tonic-gate vfsp = zonelist = zone->zone_vfslist; 388*0Sstevel@tonic-gate if (zonelist == NULL || 389*0Sstevel@tonic-gate strcmp(refstr_value(vfsp->vfs_mntpt), zone->zone_rootpath) != 0) { 390*0Sstevel@tonic-gate vfs_t tvfs; 391*0Sstevel@tonic-gate /* 392*0Sstevel@tonic-gate * The root of the zone is not a mount point. The vfs we want 393*0Sstevel@tonic-gate * to report is that of the zone's root vnode. 394*0Sstevel@tonic-gate */ 395*0Sstevel@tonic-gate ASSERT(zone != global_zone); 396*0Sstevel@tonic-gate mntfs_zonerootvfs(zone, &tvfs); 397*0Sstevel@tonic-gate mntfs_vfs_generate(&tvfs, zone, tab, &cp, forread); 398*0Sstevel@tonic-gate refstr_rele(tvfs.vfs_mntpt); 399*0Sstevel@tonic-gate if (tab) 400*0Sstevel@tonic-gate tab++; 401*0Sstevel@tonic-gate } 402*0Sstevel@tonic-gate if (zonelist == NULL) 403*0Sstevel@tonic-gate return; 404*0Sstevel@tonic-gate do { 405*0Sstevel@tonic-gate /* 406*0Sstevel@tonic-gate * Skip mounts that should not show up in mnttab 407*0Sstevel@tonic-gate */ 408*0Sstevel@tonic-gate if (!showhidden && (vfsp->vfs_flag & VFS_NOMNTTAB)) { 409*0Sstevel@tonic-gate vfsp = vfsp->vfs_zone_next; 410*0Sstevel@tonic-gate continue; 411*0Sstevel@tonic-gate } 412*0Sstevel@tonic-gate mntfs_vfs_generate(vfsp, zone, tab, &cp, forread); 413*0Sstevel@tonic-gate if (tab) 414*0Sstevel@tonic-gate tab++; 415*0Sstevel@tonic-gate vfsp = vfsp->vfs_zone_next; 416*0Sstevel@tonic-gate } while (vfsp != zonelist); 417*0Sstevel@tonic-gate } 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gate static void 420*0Sstevel@tonic-gate mntfs_global_generate(int showhidden, struct extmnttab *tab, char *basep, 421*0Sstevel@tonic-gate int forread) 422*0Sstevel@tonic-gate { 423*0Sstevel@tonic-gate vfs_t *vfsp; 424*0Sstevel@tonic-gate char *cp = basep; 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate vfsp = rootvfs; 427*0Sstevel@tonic-gate do { 428*0Sstevel@tonic-gate /* 429*0Sstevel@tonic-gate * Skip mounts that should not show up in mnttab 430*0Sstevel@tonic-gate */ 431*0Sstevel@tonic-gate if (!showhidden && vfsp->vfs_flag & VFS_NOMNTTAB) { 432*0Sstevel@tonic-gate vfsp = vfsp->vfs_next; 433*0Sstevel@tonic-gate continue; 434*0Sstevel@tonic-gate } 435*0Sstevel@tonic-gate mntfs_vfs_generate(vfsp, global_zone, tab, &cp, forread); 436*0Sstevel@tonic-gate if (tab) 437*0Sstevel@tonic-gate tab++; 438*0Sstevel@tonic-gate vfsp = vfsp->vfs_next; 439*0Sstevel@tonic-gate } while (vfsp != rootvfs); 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate static char * 443*0Sstevel@tonic-gate mntfs_mapin(char *base, size_t size) 444*0Sstevel@tonic-gate { 445*0Sstevel@tonic-gate size_t rlen = roundup(size, PAGESIZE); 446*0Sstevel@tonic-gate struct as *as = curproc->p_as; 447*0Sstevel@tonic-gate char *addr; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate as_rangelock(as); 450*0Sstevel@tonic-gate map_addr(&addr, rlen, 0, 1, 0); 451*0Sstevel@tonic-gate if (addr == NULL || as_map(as, addr, rlen, segvn_create, zfod_argsp)) { 452*0Sstevel@tonic-gate as_rangeunlock(as); 453*0Sstevel@tonic-gate return (NULL); 454*0Sstevel@tonic-gate } 455*0Sstevel@tonic-gate as_rangeunlock(as); 456*0Sstevel@tonic-gate if (copyout(base, addr, size)) { 457*0Sstevel@tonic-gate (void) as_unmap(as, addr, rlen); 458*0Sstevel@tonic-gate return (NULL); 459*0Sstevel@tonic-gate } 460*0Sstevel@tonic-gate return (addr); 461*0Sstevel@tonic-gate } 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate static void 464*0Sstevel@tonic-gate mntfs_freesnap(mntsnap_t *snap) 465*0Sstevel@tonic-gate { 466*0Sstevel@tonic-gate if (snap->mnts_text != NULL) 467*0Sstevel@tonic-gate (void) as_unmap(curproc->p_as, snap->mnts_text, 468*0Sstevel@tonic-gate roundup(snap->mnts_textsize, PAGESIZE)); 469*0Sstevel@tonic-gate snap->mnts_textsize = snap->mnts_count = 0; 470*0Sstevel@tonic-gate if (snap->mnts_metadata != NULL) 471*0Sstevel@tonic-gate (void) as_unmap(curproc->p_as, snap->mnts_metadata, 472*0Sstevel@tonic-gate roundup(snap->mnts_metasize, PAGESIZE)); 473*0Sstevel@tonic-gate snap->mnts_metasize = 0; 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate typedef struct extmnttab32 { 479*0Sstevel@tonic-gate uint32_t mnt_special; 480*0Sstevel@tonic-gate uint32_t mnt_mountp; 481*0Sstevel@tonic-gate uint32_t mnt_fstype; 482*0Sstevel@tonic-gate uint32_t mnt_mntopts; 483*0Sstevel@tonic-gate uint32_t mnt_time; 484*0Sstevel@tonic-gate uint_t mnt_major; 485*0Sstevel@tonic-gate uint_t mnt_minor; 486*0Sstevel@tonic-gate } extmnttab32_t; 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate #endif 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate /* 491*0Sstevel@tonic-gate * Snapshot the latest version of the kernel mounted resource information 492*0Sstevel@tonic-gate * 493*0Sstevel@tonic-gate * There are two types of snapshots: one destined for reading, and one destined 494*0Sstevel@tonic-gate * for ioctl(). The difference is that the ioctl() interface is delimited by 495*0Sstevel@tonic-gate * NULLs, while the read() interface is delimited by tabs and newlines. 496*0Sstevel@tonic-gate */ 497*0Sstevel@tonic-gate /* ARGSUSED */ 498*0Sstevel@tonic-gate static int 499*0Sstevel@tonic-gate mntfs_snapshot(mntnode_t *mnp, int forread, int datamodel) 500*0Sstevel@tonic-gate { 501*0Sstevel@tonic-gate size_t size; 502*0Sstevel@tonic-gate timespec_t lastmodt; 503*0Sstevel@tonic-gate mntdata_t *mntdata = MTOD(mnp); 504*0Sstevel@tonic-gate zone_t *zone = mntdata->mnt_zone; 505*0Sstevel@tonic-gate boolean_t global_view = (MTOD(mnp)->mnt_zone == global_zone); 506*0Sstevel@tonic-gate boolean_t showhidden = ((mnp->mnt_flags & MNT_SHOWHIDDEN) != 0); 507*0Sstevel@tonic-gate struct extmnttab *metadata_baseaddr; 508*0Sstevel@tonic-gate char *text_baseaddr; 509*0Sstevel@tonic-gate int i; 510*0Sstevel@tonic-gate mntsnap_t *snap; 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate if (forread) 513*0Sstevel@tonic-gate snap = &mnp->mnt_read; 514*0Sstevel@tonic-gate else 515*0Sstevel@tonic-gate snap = &mnp->mnt_ioctl; 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate vfs_list_read_lock(); 518*0Sstevel@tonic-gate /* 519*0Sstevel@tonic-gate * Check if the mnttab info has changed since the last snapshot 520*0Sstevel@tonic-gate */ 521*0Sstevel@tonic-gate vfs_mnttab_modtime(&lastmodt); 522*0Sstevel@tonic-gate if (snap->mnts_count && 523*0Sstevel@tonic-gate lastmodt.tv_sec == snap->mnts_time.tv_sec && 524*0Sstevel@tonic-gate lastmodt.tv_nsec == snap->mnts_time.tv_nsec) { 525*0Sstevel@tonic-gate vfs_list_unlock(); 526*0Sstevel@tonic-gate return (0); 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate if (snap->mnts_count != 0) 531*0Sstevel@tonic-gate mntfs_freesnap(snap); 532*0Sstevel@tonic-gate if (global_view) 533*0Sstevel@tonic-gate size = mntfs_global_len(&snap->mnts_count, showhidden); 534*0Sstevel@tonic-gate else 535*0Sstevel@tonic-gate size = mntfs_zone_len(&snap->mnts_count, zone, showhidden); 536*0Sstevel@tonic-gate ASSERT(size != 0); 537*0Sstevel@tonic-gate 538*0Sstevel@tonic-gate if (!forread) 539*0Sstevel@tonic-gate metadata_baseaddr = kmem_alloc( 540*0Sstevel@tonic-gate snap->mnts_count * sizeof (struct extmnttab), KM_SLEEP); 541*0Sstevel@tonic-gate else 542*0Sstevel@tonic-gate metadata_baseaddr = NULL; 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate text_baseaddr = kmem_alloc(size, KM_SLEEP); 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate if (global_view) 547*0Sstevel@tonic-gate mntfs_global_generate(showhidden, metadata_baseaddr, 548*0Sstevel@tonic-gate text_baseaddr, forread); 549*0Sstevel@tonic-gate else 550*0Sstevel@tonic-gate mntfs_zone_generate(zone, showhidden, 551*0Sstevel@tonic-gate metadata_baseaddr, text_baseaddr, forread); 552*0Sstevel@tonic-gate 553*0Sstevel@tonic-gate vfs_mnttab_modtime(&snap->mnts_time); 554*0Sstevel@tonic-gate vfs_list_unlock(); 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate snap->mnts_text = mntfs_mapin(text_baseaddr, size); 557*0Sstevel@tonic-gate snap->mnts_textsize = size; 558*0Sstevel@tonic-gate kmem_free(text_baseaddr, size); 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate /* 561*0Sstevel@tonic-gate * The pointers in the metadata refer to addreesses in the range 562*0Sstevel@tonic-gate * [base_addr, base_addr + size]. Now that we have mapped the text into 563*0Sstevel@tonic-gate * the user's address space, we have to convert these addresses into the 564*0Sstevel@tonic-gate * new (user) range. We also handle the conversion for 32-bit and 565*0Sstevel@tonic-gate * 32-bit applications here. 566*0Sstevel@tonic-gate */ 567*0Sstevel@tonic-gate if (!forread) { 568*0Sstevel@tonic-gate struct extmnttab *tab; 569*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 570*0Sstevel@tonic-gate struct extmnttab32 *tab32; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate if (datamodel == DATAMODEL_ILP32) { 573*0Sstevel@tonic-gate tab = (struct extmnttab *)metadata_baseaddr; 574*0Sstevel@tonic-gate tab32 = (struct extmnttab32 *)metadata_baseaddr; 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate for (i = 0; i < snap->mnts_count; i++) { 577*0Sstevel@tonic-gate tab32[i].mnt_special = 578*0Sstevel@tonic-gate (uintptr_t)snap->mnts_text + 579*0Sstevel@tonic-gate (tab[i].mnt_special - text_baseaddr); 580*0Sstevel@tonic-gate tab32[i].mnt_mountp = 581*0Sstevel@tonic-gate (uintptr_t)snap->mnts_text + 582*0Sstevel@tonic-gate (tab[i].mnt_mountp - text_baseaddr); 583*0Sstevel@tonic-gate tab32[i].mnt_fstype = 584*0Sstevel@tonic-gate (uintptr_t)snap->mnts_text + 585*0Sstevel@tonic-gate (tab[i].mnt_fstype - text_baseaddr); 586*0Sstevel@tonic-gate tab32[i].mnt_mntopts = 587*0Sstevel@tonic-gate (uintptr_t)snap->mnts_text + 588*0Sstevel@tonic-gate (tab[i].mnt_mntopts - text_baseaddr); 589*0Sstevel@tonic-gate tab32[i].mnt_time = (uintptr_t)snap->mnts_text + 590*0Sstevel@tonic-gate (tab[i].mnt_time - text_baseaddr); 591*0Sstevel@tonic-gate tab32[i].mnt_major = tab[i].mnt_major; 592*0Sstevel@tonic-gate tab32[i].mnt_minor = tab[i].mnt_minor; 593*0Sstevel@tonic-gate } 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate snap->mnts_metasize = 596*0Sstevel@tonic-gate snap->mnts_count * sizeof (struct extmnttab32); 597*0Sstevel@tonic-gate snap->mnts_metadata = mntfs_mapin( 598*0Sstevel@tonic-gate (char *)metadata_baseaddr, 599*0Sstevel@tonic-gate snap->mnts_metasize); 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate } else { 602*0Sstevel@tonic-gate #endif 603*0Sstevel@tonic-gate tab = (struct extmnttab *)metadata_baseaddr; 604*0Sstevel@tonic-gate for (i = 0; i < snap->mnts_count; i++) { 605*0Sstevel@tonic-gate tab[i].mnt_special = snap->mnts_text + 606*0Sstevel@tonic-gate (tab[i].mnt_special - text_baseaddr); 607*0Sstevel@tonic-gate tab[i].mnt_mountp = snap->mnts_text + 608*0Sstevel@tonic-gate (tab[i].mnt_mountp - text_baseaddr); 609*0Sstevel@tonic-gate tab[i].mnt_fstype = snap->mnts_text + 610*0Sstevel@tonic-gate (tab[i].mnt_fstype - text_baseaddr); 611*0Sstevel@tonic-gate tab[i].mnt_mntopts = snap->mnts_text + 612*0Sstevel@tonic-gate (tab[i].mnt_mntopts - text_baseaddr); 613*0Sstevel@tonic-gate tab[i].mnt_time = snap->mnts_text + 614*0Sstevel@tonic-gate (tab[i].mnt_time - text_baseaddr); 615*0Sstevel@tonic-gate } 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gate snap->mnts_metasize = 618*0Sstevel@tonic-gate snap->mnts_count * sizeof (struct extmnttab); 619*0Sstevel@tonic-gate snap->mnts_metadata = mntfs_mapin( 620*0Sstevel@tonic-gate (char *)metadata_baseaddr, snap->mnts_metasize); 621*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 622*0Sstevel@tonic-gate } 623*0Sstevel@tonic-gate #endif 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate kmem_free(metadata_baseaddr, 626*0Sstevel@tonic-gate snap->mnts_count * sizeof (struct extmnttab)); 627*0Sstevel@tonic-gate } 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate mntdata->mnt_size = size; 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate if (snap->mnts_text == NULL || 632*0Sstevel@tonic-gate (!forread && snap->mnts_metadata == NULL)) { 633*0Sstevel@tonic-gate mntfs_freesnap(snap); 634*0Sstevel@tonic-gate return (ENOMEM); 635*0Sstevel@tonic-gate } 636*0Sstevel@tonic-gate 637*0Sstevel@tonic-gate return (0); 638*0Sstevel@tonic-gate } 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate /* 641*0Sstevel@tonic-gate * Public function to convert vfs_mntopts into a string. 642*0Sstevel@tonic-gate * A buffer of sufficient size is allocated, which is returned via bufp, 643*0Sstevel@tonic-gate * and whose length is returned via lenp. 644*0Sstevel@tonic-gate */ 645*0Sstevel@tonic-gate void 646*0Sstevel@tonic-gate mntfs_getmntopts(struct vfs *vfsp, char **bufp, size_t *lenp) 647*0Sstevel@tonic-gate { 648*0Sstevel@tonic-gate size_t len; 649*0Sstevel@tonic-gate char *buf; 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate vfs_list_read_lock(); 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate len = mntfs_optsize(vfsp) + 1; 654*0Sstevel@tonic-gate buf = kmem_alloc(len, KM_NOSLEEP); 655*0Sstevel@tonic-gate if (buf == NULL) { 656*0Sstevel@tonic-gate *bufp = NULL; 657*0Sstevel@tonic-gate vfs_list_unlock(); 658*0Sstevel@tonic-gate return; 659*0Sstevel@tonic-gate } 660*0Sstevel@tonic-gate buf[len - 1] = '\0'; 661*0Sstevel@tonic-gate (void) mntfs_optprint(vfsp, buf); 662*0Sstevel@tonic-gate ASSERT(buf[len - 1] == '\0'); 663*0Sstevel@tonic-gate 664*0Sstevel@tonic-gate vfs_list_unlock(); 665*0Sstevel@tonic-gate *bufp = buf; 666*0Sstevel@tonic-gate *lenp = len; 667*0Sstevel@tonic-gate } 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate /* ARGSUSED */ 671*0Sstevel@tonic-gate static int 672*0Sstevel@tonic-gate mntopen(vnode_t **vpp, int flag, cred_t *cr) 673*0Sstevel@tonic-gate { 674*0Sstevel@tonic-gate vnode_t *vp = *vpp; 675*0Sstevel@tonic-gate mntnode_t *nmnp; 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate /* 678*0Sstevel@tonic-gate * Not allowed to open for writing, return error. 679*0Sstevel@tonic-gate */ 680*0Sstevel@tonic-gate if (flag & FWRITE) 681*0Sstevel@tonic-gate return (EPERM); 682*0Sstevel@tonic-gate /* 683*0Sstevel@tonic-gate * Create a new mnt/vnode for each open, this will give us a handle to 684*0Sstevel@tonic-gate * hang the snapshot on. 685*0Sstevel@tonic-gate */ 686*0Sstevel@tonic-gate nmnp = mntgetnode(vp); 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate *vpp = MTOV(nmnp); 689*0Sstevel@tonic-gate atomic_add_32(&MTOD(nmnp)->mnt_nopen, 1); 690*0Sstevel@tonic-gate VN_RELE(vp); 691*0Sstevel@tonic-gate return (0); 692*0Sstevel@tonic-gate } 693*0Sstevel@tonic-gate 694*0Sstevel@tonic-gate /* ARGSUSED */ 695*0Sstevel@tonic-gate static int 696*0Sstevel@tonic-gate mntclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) 697*0Sstevel@tonic-gate { 698*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate /* Clean up any locks or shares held by the current process */ 701*0Sstevel@tonic-gate cleanlocks(vp, ttoproc(curthread)->p_pid, 0); 702*0Sstevel@tonic-gate cleanshares(vp, ttoproc(curthread)->p_pid); 703*0Sstevel@tonic-gate 704*0Sstevel@tonic-gate if (count > 1) 705*0Sstevel@tonic-gate return (0); 706*0Sstevel@tonic-gate if (vp->v_count == 1) { 707*0Sstevel@tonic-gate mntfs_freesnap(&mnp->mnt_read); 708*0Sstevel@tonic-gate mntfs_freesnap(&mnp->mnt_ioctl); 709*0Sstevel@tonic-gate atomic_add_32(&MTOD(mnp)->mnt_nopen, -1); 710*0Sstevel@tonic-gate } 711*0Sstevel@tonic-gate return (0); 712*0Sstevel@tonic-gate } 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate /* ARGSUSED */ 715*0Sstevel@tonic-gate static int 716*0Sstevel@tonic-gate mntread(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred, caller_context_t *ct) 717*0Sstevel@tonic-gate { 718*0Sstevel@tonic-gate int error = 0; 719*0Sstevel@tonic-gate off_t off = uio->uio_offset; 720*0Sstevel@tonic-gate size_t len = uio->uio_resid; 721*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 722*0Sstevel@tonic-gate char *buf; 723*0Sstevel@tonic-gate mntsnap_t *snap = &mnp->mnt_read; 724*0Sstevel@tonic-gate int datamodel; 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate if (off == (off_t)0 || snap->mnts_count == 0) { 727*0Sstevel@tonic-gate /* 728*0Sstevel@tonic-gate * It is assumed that any kernel callers wishing 729*0Sstevel@tonic-gate * to read mnttab will be using extmnttab entries 730*0Sstevel@tonic-gate * and not extmnttab32 entries, whether or not 731*0Sstevel@tonic-gate * the kernel is LP64 or ILP32. Thus, force the 732*0Sstevel@tonic-gate * datamodel that mntfs_snapshot uses to be 733*0Sstevel@tonic-gate * DATAMODEL_LP64. 734*0Sstevel@tonic-gate */ 735*0Sstevel@tonic-gate if (uio->uio_segflg == UIO_SYSSPACE) 736*0Sstevel@tonic-gate datamodel = DATAMODEL_LP64; 737*0Sstevel@tonic-gate else 738*0Sstevel@tonic-gate datamodel = get_udatamodel(); 739*0Sstevel@tonic-gate if ((error = mntfs_snapshot(mnp, 1, datamodel)) != 0) 740*0Sstevel@tonic-gate return (error); 741*0Sstevel@tonic-gate } 742*0Sstevel@tonic-gate if ((size_t)(off + len) > snap->mnts_textsize) 743*0Sstevel@tonic-gate len = snap->mnts_textsize - off; 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate if (off < 0 || len > snap->mnts_textsize) 746*0Sstevel@tonic-gate return (EFAULT); 747*0Sstevel@tonic-gate 748*0Sstevel@tonic-gate if (len == 0) 749*0Sstevel@tonic-gate return (0); 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate /* 752*0Sstevel@tonic-gate * The mnttab image is stored in the user's address space, 753*0Sstevel@tonic-gate * so we have to copy it into the kernel from userland, 754*0Sstevel@tonic-gate * then copy it back out to the specified address. 755*0Sstevel@tonic-gate */ 756*0Sstevel@tonic-gate buf = kmem_alloc(len, KM_SLEEP); 757*0Sstevel@tonic-gate if (copyin(snap->mnts_text + off, buf, len)) 758*0Sstevel@tonic-gate error = EFAULT; 759*0Sstevel@tonic-gate else { 760*0Sstevel@tonic-gate error = uiomove(buf, len, UIO_READ, uio); 761*0Sstevel@tonic-gate } 762*0Sstevel@tonic-gate kmem_free(buf, len); 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate return (error); 765*0Sstevel@tonic-gate } 766*0Sstevel@tonic-gate 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate static int 769*0Sstevel@tonic-gate mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) 770*0Sstevel@tonic-gate { 771*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 772*0Sstevel@tonic-gate int error; 773*0Sstevel@tonic-gate vnode_t *rvp; 774*0Sstevel@tonic-gate extern timespec_t vfs_mnttab_ctime; 775*0Sstevel@tonic-gate mntdata_t *mntdata = MTOD(VTOM(vp)); 776*0Sstevel@tonic-gate mntsnap_t *snap = mnp->mnt_read.mnts_count ? 777*0Sstevel@tonic-gate &mnp->mnt_read : &mnp->mnt_ioctl; 778*0Sstevel@tonic-gate 779*0Sstevel@tonic-gate /* 780*0Sstevel@tonic-gate * Return all the attributes. Should be refined 781*0Sstevel@tonic-gate * so that it returns only those asked for. 782*0Sstevel@tonic-gate * Most of this is complete fakery anyway. 783*0Sstevel@tonic-gate */ 784*0Sstevel@tonic-gate rvp = mnp->mnt_mountvp; 785*0Sstevel@tonic-gate /* 786*0Sstevel@tonic-gate * Attributes are same as underlying file with modifications 787*0Sstevel@tonic-gate */ 788*0Sstevel@tonic-gate if (error = VOP_GETATTR(rvp, vap, flags, cr)) 789*0Sstevel@tonic-gate return (error); 790*0Sstevel@tonic-gate 791*0Sstevel@tonic-gate /* 792*0Sstevel@tonic-gate * We always look like a regular file 793*0Sstevel@tonic-gate */ 794*0Sstevel@tonic-gate vap->va_type = VREG; 795*0Sstevel@tonic-gate /* 796*0Sstevel@tonic-gate * mode should basically be read only 797*0Sstevel@tonic-gate */ 798*0Sstevel@tonic-gate vap->va_mode &= 07444; 799*0Sstevel@tonic-gate vap->va_fsid = vp->v_vfsp->vfs_dev; 800*0Sstevel@tonic-gate vap->va_blksize = DEV_BSIZE; 801*0Sstevel@tonic-gate vap->va_rdev = 0; 802*0Sstevel@tonic-gate vap->va_seq = 0; 803*0Sstevel@tonic-gate /* 804*0Sstevel@tonic-gate * Set nlink to the number of open vnodes for mnttab info 805*0Sstevel@tonic-gate * plus one for existing. 806*0Sstevel@tonic-gate */ 807*0Sstevel@tonic-gate vap->va_nlink = mntdata->mnt_nopen + 1; 808*0Sstevel@tonic-gate /* 809*0Sstevel@tonic-gate * If we haven't taken a snapshot yet, set the 810*0Sstevel@tonic-gate * size to the size of the latest snapshot. 811*0Sstevel@tonic-gate */ 812*0Sstevel@tonic-gate vap->va_size = snap->mnts_textsize ? snap->mnts_textsize : 813*0Sstevel@tonic-gate mntdata->mnt_size; 814*0Sstevel@tonic-gate /* 815*0Sstevel@tonic-gate * Fetch mtime from the vfs mnttab timestamp 816*0Sstevel@tonic-gate */ 817*0Sstevel@tonic-gate vap->va_ctime = vfs_mnttab_ctime; 818*0Sstevel@tonic-gate vfs_list_read_lock(); 819*0Sstevel@tonic-gate vfs_mnttab_modtime(&vap->va_mtime); 820*0Sstevel@tonic-gate vap->va_atime = vap->va_mtime; 821*0Sstevel@tonic-gate vfs_list_unlock(); 822*0Sstevel@tonic-gate /* 823*0Sstevel@tonic-gate * Nodeid is always ROOTINO; 824*0Sstevel@tonic-gate */ 825*0Sstevel@tonic-gate vap->va_nodeid = (ino64_t)MNTROOTINO; 826*0Sstevel@tonic-gate vap->va_nblocks = btod(vap->va_size); 827*0Sstevel@tonic-gate return (0); 828*0Sstevel@tonic-gate } 829*0Sstevel@tonic-gate 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate static int 832*0Sstevel@tonic-gate mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr) 833*0Sstevel@tonic-gate { 834*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 835*0Sstevel@tonic-gate 836*0Sstevel@tonic-gate if (mode & (VWRITE|VEXEC)) 837*0Sstevel@tonic-gate return (EROFS); 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate /* 840*0Sstevel@tonic-gate * Do access check on the underlying directory vnode. 841*0Sstevel@tonic-gate */ 842*0Sstevel@tonic-gate return (VOP_ACCESS(mnp->mnt_mountvp, mode, flags, cr)); 843*0Sstevel@tonic-gate } 844*0Sstevel@tonic-gate 845*0Sstevel@tonic-gate 846*0Sstevel@tonic-gate /* 847*0Sstevel@tonic-gate * New /mntfs vnode required; allocate it and fill in most of the fields. 848*0Sstevel@tonic-gate */ 849*0Sstevel@tonic-gate static mntnode_t * 850*0Sstevel@tonic-gate mntgetnode(vnode_t *dp) 851*0Sstevel@tonic-gate { 852*0Sstevel@tonic-gate mntnode_t *mnp; 853*0Sstevel@tonic-gate vnode_t *vp; 854*0Sstevel@tonic-gate 855*0Sstevel@tonic-gate mnp = kmem_zalloc(sizeof (mntnode_t), KM_SLEEP); 856*0Sstevel@tonic-gate mnp->mnt_vnode = vn_alloc(KM_SLEEP); 857*0Sstevel@tonic-gate mnp->mnt_mountvp = VTOM(dp)->mnt_mountvp; 858*0Sstevel@tonic-gate vp = MTOV(mnp); 859*0Sstevel@tonic-gate vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT; 860*0Sstevel@tonic-gate vn_setops(vp, mntvnodeops); 861*0Sstevel@tonic-gate vp->v_vfsp = dp->v_vfsp; 862*0Sstevel@tonic-gate vp->v_type = VREG; 863*0Sstevel@tonic-gate vp->v_data = (caddr_t)mnp; 864*0Sstevel@tonic-gate 865*0Sstevel@tonic-gate return (mnp); 866*0Sstevel@tonic-gate } 867*0Sstevel@tonic-gate 868*0Sstevel@tonic-gate /* 869*0Sstevel@tonic-gate * Free the storage obtained from mntgetnode(). 870*0Sstevel@tonic-gate */ 871*0Sstevel@tonic-gate static void 872*0Sstevel@tonic-gate mntfreenode(mntnode_t *mnp) 873*0Sstevel@tonic-gate { 874*0Sstevel@tonic-gate vnode_t *vp = MTOV(mnp); 875*0Sstevel@tonic-gate 876*0Sstevel@tonic-gate vn_invalid(vp); 877*0Sstevel@tonic-gate vn_free(vp); 878*0Sstevel@tonic-gate kmem_free(mnp, sizeof (*mnp)); 879*0Sstevel@tonic-gate } 880*0Sstevel@tonic-gate 881*0Sstevel@tonic-gate 882*0Sstevel@tonic-gate /* ARGSUSED */ 883*0Sstevel@tonic-gate static int 884*0Sstevel@tonic-gate mntfsync(vnode_t *vp, int syncflag, cred_t *cr) 885*0Sstevel@tonic-gate { 886*0Sstevel@tonic-gate return (0); 887*0Sstevel@tonic-gate } 888*0Sstevel@tonic-gate 889*0Sstevel@tonic-gate /* ARGSUSED */ 890*0Sstevel@tonic-gate static void 891*0Sstevel@tonic-gate mntinactive(vnode_t *vp, cred_t *cr) 892*0Sstevel@tonic-gate { 893*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate mntfreenode(mnp); 896*0Sstevel@tonic-gate } 897*0Sstevel@tonic-gate 898*0Sstevel@tonic-gate /* ARGSUSED */ 899*0Sstevel@tonic-gate static int 900*0Sstevel@tonic-gate mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp) 901*0Sstevel@tonic-gate { 902*0Sstevel@tonic-gate if (*noffp == 0) 903*0Sstevel@tonic-gate VTOM(vp)->mnt_offset = 0; 904*0Sstevel@tonic-gate 905*0Sstevel@tonic-gate return (0); 906*0Sstevel@tonic-gate } 907*0Sstevel@tonic-gate 908*0Sstevel@tonic-gate /* 909*0Sstevel@tonic-gate * Return the answer requested to poll(). 910*0Sstevel@tonic-gate * POLLRDBAND will return when the mtime of the mnttab 911*0Sstevel@tonic-gate * information is newer than the latest one read for this open. 912*0Sstevel@tonic-gate */ 913*0Sstevel@tonic-gate /* ARGSUSED */ 914*0Sstevel@tonic-gate static int 915*0Sstevel@tonic-gate mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp) 916*0Sstevel@tonic-gate { 917*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 918*0Sstevel@tonic-gate mntsnap_t *snap = &mnp->mnt_read; 919*0Sstevel@tonic-gate 920*0Sstevel@tonic-gate if (mnp->mnt_ioctl.mnts_time.tv_sec > snap->mnts_time.tv_sec || 921*0Sstevel@tonic-gate (mnp->mnt_ioctl.mnts_time.tv_sec == snap->mnts_time.tv_sec && 922*0Sstevel@tonic-gate mnp->mnt_ioctl.mnts_time.tv_nsec > snap->mnts_time.tv_nsec)) 923*0Sstevel@tonic-gate snap = &mnp->mnt_ioctl; 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate *revp = 0; 926*0Sstevel@tonic-gate *phpp = (pollhead_t *)NULL; 927*0Sstevel@tonic-gate if (ev & POLLIN) 928*0Sstevel@tonic-gate *revp |= POLLIN; 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate if (ev & POLLRDNORM) 931*0Sstevel@tonic-gate *revp |= POLLRDNORM; 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate if (ev & POLLRDBAND) { 934*0Sstevel@tonic-gate vfs_mnttab_poll(&snap->mnts_time, phpp); 935*0Sstevel@tonic-gate if (*phpp == (pollhead_t *)NULL) 936*0Sstevel@tonic-gate *revp |= POLLRDBAND; 937*0Sstevel@tonic-gate } 938*0Sstevel@tonic-gate if (*revp || *phpp != NULL || any) { 939*0Sstevel@tonic-gate return (0); 940*0Sstevel@tonic-gate } 941*0Sstevel@tonic-gate /* 942*0Sstevel@tonic-gate * If someone is polling an unsupported poll events (e.g. 943*0Sstevel@tonic-gate * POLLOUT, POLLPRI, etc.), just return POLLERR revents. 944*0Sstevel@tonic-gate * That way we will ensure that we don't return a 0 945*0Sstevel@tonic-gate * revents with a NULL pollhead pointer. 946*0Sstevel@tonic-gate */ 947*0Sstevel@tonic-gate *revp = POLLERR; 948*0Sstevel@tonic-gate return (0); 949*0Sstevel@tonic-gate } 950*0Sstevel@tonic-gate /* ARGSUSED */ 951*0Sstevel@tonic-gate static int 952*0Sstevel@tonic-gate mntioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, 953*0Sstevel@tonic-gate cred_t *cr, int *rvalp) 954*0Sstevel@tonic-gate { 955*0Sstevel@tonic-gate uint_t *up = (uint_t *)arg; 956*0Sstevel@tonic-gate mntnode_t *mnp = VTOM(vp); 957*0Sstevel@tonic-gate mntsnap_t *snap = &mnp->mnt_ioctl; 958*0Sstevel@tonic-gate int error; 959*0Sstevel@tonic-gate 960*0Sstevel@tonic-gate error = 0; 961*0Sstevel@tonic-gate switch (cmd) { 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate case MNTIOC_NMNTS: { /* get no. of mounted resources */ 964*0Sstevel@tonic-gate if (snap->mnts_count == 0) { 965*0Sstevel@tonic-gate if ((error = 966*0Sstevel@tonic-gate mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0) 967*0Sstevel@tonic-gate return (error); 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate if (suword32(up, snap->mnts_count) != 0) 970*0Sstevel@tonic-gate error = EFAULT; 971*0Sstevel@tonic-gate break; 972*0Sstevel@tonic-gate } 973*0Sstevel@tonic-gate 974*0Sstevel@tonic-gate case MNTIOC_GETDEVLIST: { /* get mounted device major/minor nos */ 975*0Sstevel@tonic-gate uint_t *devlist; 976*0Sstevel@tonic-gate int i; 977*0Sstevel@tonic-gate size_t len; 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate if (snap->mnts_count == 0) { 980*0Sstevel@tonic-gate if ((error = 981*0Sstevel@tonic-gate mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0) 982*0Sstevel@tonic-gate return (error); 983*0Sstevel@tonic-gate } 984*0Sstevel@tonic-gate 985*0Sstevel@tonic-gate len = 2 * snap->mnts_count * sizeof (uint_t); 986*0Sstevel@tonic-gate devlist = kmem_alloc(len, KM_SLEEP); 987*0Sstevel@tonic-gate for (i = 0; i < snap->mnts_count; i++) { 988*0Sstevel@tonic-gate 989*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 990*0Sstevel@tonic-gate if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) { 991*0Sstevel@tonic-gate struct extmnttab32 tab; 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate if ((error = xcopyin(snap->mnts_text + 994*0Sstevel@tonic-gate i * sizeof (struct extmnttab32), &tab, 995*0Sstevel@tonic-gate sizeof (tab))) != 0) 996*0Sstevel@tonic-gate break; 997*0Sstevel@tonic-gate 998*0Sstevel@tonic-gate devlist[i*2] = tab.mnt_major; 999*0Sstevel@tonic-gate devlist[i*2+1] = tab.mnt_minor; 1000*0Sstevel@tonic-gate } else { 1001*0Sstevel@tonic-gate #endif 1002*0Sstevel@tonic-gate struct extmnttab tab; 1003*0Sstevel@tonic-gate 1004*0Sstevel@tonic-gate if ((error = xcopyin(snap->mnts_text + 1005*0Sstevel@tonic-gate i * sizeof (struct extmnttab), &tab, 1006*0Sstevel@tonic-gate sizeof (tab))) != 0) 1007*0Sstevel@tonic-gate break; 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gate devlist[i*2] = tab.mnt_major; 1010*0Sstevel@tonic-gate devlist[i*2+1] = tab.mnt_minor; 1011*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 1012*0Sstevel@tonic-gate } 1013*0Sstevel@tonic-gate #endif 1014*0Sstevel@tonic-gate } 1015*0Sstevel@tonic-gate 1016*0Sstevel@tonic-gate if (error == 0) 1017*0Sstevel@tonic-gate error = xcopyout(devlist, up, len); 1018*0Sstevel@tonic-gate kmem_free(devlist, len); 1019*0Sstevel@tonic-gate break; 1020*0Sstevel@tonic-gate } 1021*0Sstevel@tonic-gate 1022*0Sstevel@tonic-gate case MNTIOC_SETTAG: /* set tag on mounted file system */ 1023*0Sstevel@tonic-gate case MNTIOC_CLRTAG: /* clear tag on mounted file system */ 1024*0Sstevel@tonic-gate { 1025*0Sstevel@tonic-gate struct mnttagdesc *dp = (struct mnttagdesc *)arg; 1026*0Sstevel@tonic-gate STRUCT_DECL(mnttagdesc, tagdesc); 1027*0Sstevel@tonic-gate char *cptr; 1028*0Sstevel@tonic-gate uint32_t major, minor; 1029*0Sstevel@tonic-gate char tagbuf[MAX_MNTOPT_TAG]; 1030*0Sstevel@tonic-gate char *pbuf; 1031*0Sstevel@tonic-gate size_t len; 1032*0Sstevel@tonic-gate uint_t start = 0; 1033*0Sstevel@tonic-gate mntdata_t *mntdata = MTOD(mnp); 1034*0Sstevel@tonic-gate zone_t *zone = mntdata->mnt_zone; 1035*0Sstevel@tonic-gate 1036*0Sstevel@tonic-gate STRUCT_INIT(tagdesc, flag & DATAMODEL_MASK); 1037*0Sstevel@tonic-gate if (copyin(dp, STRUCT_BUF(tagdesc), STRUCT_SIZE(tagdesc))) { 1038*0Sstevel@tonic-gate error = EFAULT; 1039*0Sstevel@tonic-gate break; 1040*0Sstevel@tonic-gate } 1041*0Sstevel@tonic-gate pbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1042*0Sstevel@tonic-gate if (zone != global_zone) { 1043*0Sstevel@tonic-gate (void) strcpy(pbuf, zone->zone_rootpath); 1044*0Sstevel@tonic-gate /* truncate "/" and nul */ 1045*0Sstevel@tonic-gate start = zone->zone_rootpathlen - 2; 1046*0Sstevel@tonic-gate ASSERT(pbuf[start] == '/'); 1047*0Sstevel@tonic-gate } 1048*0Sstevel@tonic-gate cptr = STRUCT_FGETP(tagdesc, mtd_mntpt); 1049*0Sstevel@tonic-gate error = copyinstr(cptr, pbuf + start, MAXPATHLEN - start, &len); 1050*0Sstevel@tonic-gate if (error) { 1051*0Sstevel@tonic-gate kmem_free(pbuf, MAXPATHLEN); 1052*0Sstevel@tonic-gate break; 1053*0Sstevel@tonic-gate } 1054*0Sstevel@tonic-gate if (start != 0 && pbuf[start] != '/') { 1055*0Sstevel@tonic-gate kmem_free(pbuf, MAXPATHLEN); 1056*0Sstevel@tonic-gate error = EINVAL; 1057*0Sstevel@tonic-gate break; 1058*0Sstevel@tonic-gate } 1059*0Sstevel@tonic-gate cptr = STRUCT_FGETP(tagdesc, mtd_tag); 1060*0Sstevel@tonic-gate if ((error = copyinstr(cptr, tagbuf, MAX_MNTOPT_TAG, &len))) { 1061*0Sstevel@tonic-gate kmem_free(pbuf, MAXPATHLEN); 1062*0Sstevel@tonic-gate break; 1063*0Sstevel@tonic-gate } 1064*0Sstevel@tonic-gate major = STRUCT_FGET(tagdesc, mtd_major); 1065*0Sstevel@tonic-gate minor = STRUCT_FGET(tagdesc, mtd_minor); 1066*0Sstevel@tonic-gate if (cmd == MNTIOC_SETTAG) 1067*0Sstevel@tonic-gate error = vfs_settag(major, minor, pbuf, tagbuf, cr); 1068*0Sstevel@tonic-gate else 1069*0Sstevel@tonic-gate error = vfs_clrtag(major, minor, pbuf, tagbuf, cr); 1070*0Sstevel@tonic-gate kmem_free(pbuf, MAXPATHLEN); 1071*0Sstevel@tonic-gate break; 1072*0Sstevel@tonic-gate } 1073*0Sstevel@tonic-gate 1074*0Sstevel@tonic-gate case MNTIOC_SHOWHIDDEN: 1075*0Sstevel@tonic-gate { 1076*0Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1077*0Sstevel@tonic-gate mnp->mnt_flags |= MNT_SHOWHIDDEN; 1078*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1079*0Sstevel@tonic-gate break; 1080*0Sstevel@tonic-gate } 1081*0Sstevel@tonic-gate 1082*0Sstevel@tonic-gate case MNTIOC_GETMNTENT: 1083*0Sstevel@tonic-gate { 1084*0Sstevel@tonic-gate size_t idx; 1085*0Sstevel@tonic-gate uintptr_t addr; 1086*0Sstevel@tonic-gate 1087*0Sstevel@tonic-gate idx = mnp->mnt_offset; 1088*0Sstevel@tonic-gate if (snap->mnts_count == 0 || idx == 0) { 1089*0Sstevel@tonic-gate if ((error = 1090*0Sstevel@tonic-gate mntfs_snapshot(mnp, 0, flag & DATAMODEL_MASK)) != 0) 1091*0Sstevel@tonic-gate return (error); 1092*0Sstevel@tonic-gate } 1093*0Sstevel@tonic-gate /* 1094*0Sstevel@tonic-gate * If the next index is beyond the end of the current mnttab, 1095*0Sstevel@tonic-gate * return EOF 1096*0Sstevel@tonic-gate */ 1097*0Sstevel@tonic-gate if (idx >= snap->mnts_count) { 1098*0Sstevel@tonic-gate *rvalp = 1; 1099*0Sstevel@tonic-gate return (0); 1100*0Sstevel@tonic-gate } 1101*0Sstevel@tonic-gate 1102*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 1103*0Sstevel@tonic-gate if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) { 1104*0Sstevel@tonic-gate addr = (uintptr_t)(snap->mnts_metadata + idx * 1105*0Sstevel@tonic-gate sizeof (struct extmnttab32)); 1106*0Sstevel@tonic-gate error = suword32((void *)arg, addr); 1107*0Sstevel@tonic-gate } else { 1108*0Sstevel@tonic-gate #endif 1109*0Sstevel@tonic-gate addr = (uintptr_t)(snap->mnts_metadata + idx * 1110*0Sstevel@tonic-gate sizeof (struct extmnttab)); 1111*0Sstevel@tonic-gate error = sulword((void *)arg, addr); 1112*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 1113*0Sstevel@tonic-gate } 1114*0Sstevel@tonic-gate #endif 1115*0Sstevel@tonic-gate 1116*0Sstevel@tonic-gate if (error != 0) 1117*0Sstevel@tonic-gate return (error); 1118*0Sstevel@tonic-gate 1119*0Sstevel@tonic-gate mnp->mnt_offset++; 1120*0Sstevel@tonic-gate break; 1121*0Sstevel@tonic-gate } 1122*0Sstevel@tonic-gate 1123*0Sstevel@tonic-gate default: 1124*0Sstevel@tonic-gate error = EINVAL; 1125*0Sstevel@tonic-gate break; 1126*0Sstevel@tonic-gate } 1127*0Sstevel@tonic-gate 1128*0Sstevel@tonic-gate return (error); 1129*0Sstevel@tonic-gate } 1130*0Sstevel@tonic-gate 1131*0Sstevel@tonic-gate 1132*0Sstevel@tonic-gate /* 1133*0Sstevel@tonic-gate * /mntfs vnode operations vector 1134*0Sstevel@tonic-gate */ 1135*0Sstevel@tonic-gate const fs_operation_def_t mnt_vnodeops_template[] = { 1136*0Sstevel@tonic-gate VOPNAME_OPEN, mntopen, 1137*0Sstevel@tonic-gate VOPNAME_CLOSE, mntclose, 1138*0Sstevel@tonic-gate VOPNAME_READ, mntread, 1139*0Sstevel@tonic-gate VOPNAME_IOCTL, mntioctl, 1140*0Sstevel@tonic-gate VOPNAME_GETATTR, mntgetattr, 1141*0Sstevel@tonic-gate VOPNAME_ACCESS, mntaccess, 1142*0Sstevel@tonic-gate VOPNAME_FSYNC, mntfsync, 1143*0Sstevel@tonic-gate VOPNAME_INACTIVE, (fs_generic_func_p) mntinactive, 1144*0Sstevel@tonic-gate VOPNAME_SEEK, mntseek, 1145*0Sstevel@tonic-gate VOPNAME_POLL, (fs_generic_func_p) mntpoll, 1146*0Sstevel@tonic-gate VOPNAME_DISPOSE, fs_error, 1147*0Sstevel@tonic-gate VOPNAME_SHRLOCK, fs_error, 1148*0Sstevel@tonic-gate NULL, NULL 1149*0Sstevel@tonic-gate }; 1150