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 51488Srsb * Common Development and Distribution License (the "License"). 61488Srsb * 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 /* 221488Srsb * Copyright 2006 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/param.h> 290Sstevel@tonic-gate #include <sys/errno.h> 300Sstevel@tonic-gate #include <sys/vfs.h> 310Sstevel@tonic-gate #include <sys/vnode.h> 320Sstevel@tonic-gate #include <sys/uio.h> 330Sstevel@tonic-gate #include <sys/pathname.h> 340Sstevel@tonic-gate #include <sys/kmem.h> 350Sstevel@tonic-gate #include <sys/cred.h> 360Sstevel@tonic-gate #include <sys/statvfs.h> 370Sstevel@tonic-gate #include <sys/fs/lofs_info.h> 380Sstevel@tonic-gate #include <sys/fs/lofs_node.h> 390Sstevel@tonic-gate #include <sys/mount.h> 400Sstevel@tonic-gate #include <sys/mntent.h> 410Sstevel@tonic-gate #include <sys/mkdev.h> 421676Sjpk #include <sys/priv.h> 430Sstevel@tonic-gate #include <sys/sysmacros.h> 440Sstevel@tonic-gate #include <sys/systm.h> 450Sstevel@tonic-gate #include <sys/cmn_err.h> 460Sstevel@tonic-gate #include <sys/policy.h> 471676Sjpk #include <sys/tsol/label.h> 480Sstevel@tonic-gate #include "fs/fs_subr.h" 490Sstevel@tonic-gate 500Sstevel@tonic-gate /* 510Sstevel@tonic-gate * This is the loadable module wrapper. 520Sstevel@tonic-gate */ 530Sstevel@tonic-gate #include <sys/modctl.h> 540Sstevel@tonic-gate 550Sstevel@tonic-gate static mntopts_t lofs_mntopts; 560Sstevel@tonic-gate 570Sstevel@tonic-gate static int lofsinit(int, char *); 580Sstevel@tonic-gate 590Sstevel@tonic-gate static vfsdef_t vfw = { 600Sstevel@tonic-gate VFSDEF_VERSION, 610Sstevel@tonic-gate "lofs", 620Sstevel@tonic-gate lofsinit, 631488Srsb VSW_HASPROTO|VSW_STATS, 640Sstevel@tonic-gate &lofs_mntopts 650Sstevel@tonic-gate }; 660Sstevel@tonic-gate 670Sstevel@tonic-gate /* 680Sstevel@tonic-gate * LOFS mount options table 690Sstevel@tonic-gate */ 700Sstevel@tonic-gate static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL }; 710Sstevel@tonic-gate static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL }; 720Sstevel@tonic-gate static char *sub_cancel[] = { MNTOPT_LOFS_NOSUB, NULL }; 730Sstevel@tonic-gate static char *nosub_cancel[] = { MNTOPT_LOFS_SUB, NULL }; 740Sstevel@tonic-gate 750Sstevel@tonic-gate static mntopt_t mntopts[] = { 760Sstevel@tonic-gate /* 770Sstevel@tonic-gate * option name cancel option default arg flags 780Sstevel@tonic-gate * private data 790Sstevel@tonic-gate */ 800Sstevel@tonic-gate { MNTOPT_XATTR, xattr_cancel, NULL, 0, 810Sstevel@tonic-gate (void *)0 }, 820Sstevel@tonic-gate { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 830Sstevel@tonic-gate (void *)0 }, 840Sstevel@tonic-gate { MNTOPT_LOFS_SUB, sub_cancel, NULL, 0, 850Sstevel@tonic-gate (void *)0 }, 860Sstevel@tonic-gate { MNTOPT_LOFS_NOSUB, nosub_cancel, NULL, 0, 870Sstevel@tonic-gate (void *)0 }, 880Sstevel@tonic-gate }; 890Sstevel@tonic-gate 900Sstevel@tonic-gate static mntopts_t lofs_mntopts = { 910Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 920Sstevel@tonic-gate mntopts 930Sstevel@tonic-gate }; 940Sstevel@tonic-gate 950Sstevel@tonic-gate /* 960Sstevel@tonic-gate * Module linkage information for the kernel. 970Sstevel@tonic-gate */ 980Sstevel@tonic-gate 990Sstevel@tonic-gate static struct modlfs modlfs = { 1000Sstevel@tonic-gate &mod_fsops, "filesystem for lofs", &vfw 1010Sstevel@tonic-gate }; 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate static struct modlinkage modlinkage = { 1040Sstevel@tonic-gate MODREV_1, (void *)&modlfs, NULL 1050Sstevel@tonic-gate }; 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate /* 1080Sstevel@tonic-gate * This is the module initialization routine. 1090Sstevel@tonic-gate */ 1101676Sjpk 1110Sstevel@tonic-gate int 1121676Sjpk _init(void) 1130Sstevel@tonic-gate { 1140Sstevel@tonic-gate int status; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate lofs_subrinit(); 1170Sstevel@tonic-gate status = mod_install(&modlinkage); 1180Sstevel@tonic-gate if (status != 0) { 1190Sstevel@tonic-gate /* 1200Sstevel@tonic-gate * Cleanup previously initialized work. 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate lofs_subrfini(); 1230Sstevel@tonic-gate } 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate return (status); 1260Sstevel@tonic-gate } 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate /* 1290Sstevel@tonic-gate * Don't allow the lofs module to be unloaded for now. 1300Sstevel@tonic-gate * There is a memory leak if it gets unloaded. 1310Sstevel@tonic-gate */ 1321676Sjpk 1330Sstevel@tonic-gate int 1341676Sjpk _fini(void) 1350Sstevel@tonic-gate { 1360Sstevel@tonic-gate return (EBUSY); 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate int 1400Sstevel@tonic-gate _info(struct modinfo *modinfop) 1410Sstevel@tonic-gate { 1420Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate static int lofsfstype; 1470Sstevel@tonic-gate vfsops_t *lo_vfsops; 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate /* 1500Sstevel@tonic-gate * lo mount vfsop 1510Sstevel@tonic-gate * Set up mount info record and attach it to vfs struct. 1520Sstevel@tonic-gate */ 1530Sstevel@tonic-gate /*ARGSUSED*/ 1540Sstevel@tonic-gate static int 1550Sstevel@tonic-gate lo_mount(struct vfs *vfsp, 1560Sstevel@tonic-gate struct vnode *vp, 1570Sstevel@tonic-gate struct mounta *uap, 1580Sstevel@tonic-gate struct cred *cr) 1590Sstevel@tonic-gate { 1600Sstevel@tonic-gate int error; 1610Sstevel@tonic-gate struct vnode *srootvp = NULL; /* the server's root */ 1620Sstevel@tonic-gate struct vnode *realrootvp; 1630Sstevel@tonic-gate struct loinfo *li; 1640Sstevel@tonic-gate int nodev; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL); 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate if ((error = secpolicy_fs_mount(cr, vp, vfsp)) != 0) 1690Sstevel@tonic-gate return (EPERM); 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate /* 1720Sstevel@tonic-gate * Loopback devices which get "nodevices" added can be done without 1730Sstevel@tonic-gate * "nodevices" set because we cannot import devices into a zone 1740Sstevel@tonic-gate * with loopback. Note that we have all zone privileges when 1750Sstevel@tonic-gate * this happens; if not, we'd have gotten "nosuid". 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) 1780Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1810Sstevel@tonic-gate if (!(uap->flags & MS_OVERLAY) && 1821676Sjpk (vp->v_count != 1 || (vp->v_flag & VROOT))) { 1830Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1840Sstevel@tonic-gate return (EBUSY); 1850Sstevel@tonic-gate } 1860Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* 1890Sstevel@tonic-gate * Find real root, and make vfs point to real vfs 1900Sstevel@tonic-gate */ 1910Sstevel@tonic-gate if (error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ? 1920Sstevel@tonic-gate UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, 1930Sstevel@tonic-gate &realrootvp)) 1940Sstevel@tonic-gate return (error); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /* 1971676Sjpk * Enforce MAC policy if needed. 1981676Sjpk * 1991676Sjpk * Loopback mounts must not allow writing up. The dominance test 2001676Sjpk * is intended to prevent a global zone caller from accidentally 2011676Sjpk * creating write-up conditions between two labeled zones. 2021676Sjpk * Local zones can't violate MAC on their own without help from 2031676Sjpk * the global zone because they can't name a pathname that 2041676Sjpk * they don't already have. 2051676Sjpk * 2061676Sjpk * The special case check for the NET_MAC_AWARE process flag is 2071676Sjpk * to support the case of the automounter in the global zone. We 2081676Sjpk * permit automounting of local zone directories such as home 2091676Sjpk * directories, into the global zone as required by setlabel, 2101676Sjpk * zonecopy, and saving of desktop sessions. Such mounts are 2111676Sjpk * trusted not to expose the contents of one zone's directories 2121676Sjpk * to another by leaking them through the global zone. 2131676Sjpk */ 2141676Sjpk if (is_system_labeled() && crgetzoneid(cr) == GLOBAL_ZONEID) { 2151748Srica char specname[MAXPATHLEN]; 2161748Srica zone_t *from_zptr; 2171748Srica zone_t *to_zptr; 2181676Sjpk 2191748Srica if (vnodetopath(NULL, realrootvp, specname, 2201748Srica sizeof (specname), CRED()) != 0) 2211748Srica return (EACCES); 2221748Srica 2231676Sjpk from_zptr = zone_find_by_path(specname); 2241676Sjpk to_zptr = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); 2251676Sjpk 2261676Sjpk /* 2271676Sjpk * Special case for zone devfs: the zone for /dev will 2281676Sjpk * incorrectly appear as the global zone since it's not 2291676Sjpk * under the zone rootpath. So for zone devfs check allow 2301676Sjpk * read-write mounts. 2311769Scarlsonj * 2321769Scarlsonj * Second special case for scratch zones used for Live Upgrade: 2331769Scarlsonj * this is used to mount the zone's root from /root to /a in 2341769Scarlsonj * the scratch zone. As with the other special case, this 2351769Scarlsonj * appears to be outside of the zone because it's not under 2361769Scarlsonj * the zone rootpath, which is $ZONEPATH/lu in the scratch 2371769Scarlsonj * zone case. 2381676Sjpk */ 2391676Sjpk 240*2656Sszhou if (from_zptr != to_zptr && 2411769Scarlsonj !(to_zptr->zone_flags & ZF_IS_SCRATCH)) { 2421676Sjpk /* 2431676Sjpk * We know at this point that the labels aren't equal 2441676Sjpk * because the zone pointers aren't equal, and zones 2451676Sjpk * can't share a label. 2461676Sjpk * 2471676Sjpk * If the source is the global zone then making 2481676Sjpk * it available to a local zone must be done in 2491676Sjpk * read-only mode as the label will become admin_low. 2501676Sjpk * 2511676Sjpk * If it is a mount between local zones then if 2521676Sjpk * the current process is in the global zone and has 2531676Sjpk * the NET_MAC_AWARE flag, then regular read-write 2541676Sjpk * access is allowed. If it's in some other zone, but 2551676Sjpk * the label on the mount point dominates the original 2561676Sjpk * source, then allow the mount as read-only 2571676Sjpk * ("read-down"). 2581676Sjpk */ 2591676Sjpk if (from_zptr->zone_id == GLOBAL_ZONEID) { 2601676Sjpk /* make the mount read-only */ 2611676Sjpk vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 2621676Sjpk } else { /* cross-zone mount */ 2631676Sjpk if (to_zptr->zone_id == GLOBAL_ZONEID && 2641676Sjpk /* LINTED: no consequent */ 2651676Sjpk getpflags(NET_MAC_AWARE, cr) != 0) { 2661676Sjpk /* Allow the mount as read-write */ 2671676Sjpk } else if (bldominates( 2681676Sjpk label2bslabel(to_zptr->zone_slabel), 2691676Sjpk label2bslabel(from_zptr->zone_slabel))) { 2701676Sjpk /* make the mount read-only */ 2711676Sjpk vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); 2721676Sjpk } else { 2731676Sjpk zone_rele(to_zptr); 2741676Sjpk zone_rele(from_zptr); 2751676Sjpk return (EACCES); 2761676Sjpk } 2771676Sjpk } 2781676Sjpk } 2791676Sjpk zone_rele(to_zptr); 2801676Sjpk zone_rele(from_zptr); 2811676Sjpk } 2821676Sjpk 2831676Sjpk /* 2840Sstevel@tonic-gate * realrootvp may be an AUTOFS node, in which case we 2850Sstevel@tonic-gate * perform a VOP_ACCESS() to trigger the mount of the 2860Sstevel@tonic-gate * intended filesystem, so we loopback mount the intended 2870Sstevel@tonic-gate * filesystem instead of the AUTOFS filesystem. 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate (void) VOP_ACCESS(realrootvp, 0, 0, cr); 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate /* 2920Sstevel@tonic-gate * We're interested in the top most filesystem. 2930Sstevel@tonic-gate * This is specially important when uap->spec is a trigger 2940Sstevel@tonic-gate * AUTOFS node, since we're really interested in mounting the 2950Sstevel@tonic-gate * filesystem AUTOFS mounted as result of the VOP_ACCESS() 2960Sstevel@tonic-gate * call not the AUTOFS node itself. 2970Sstevel@tonic-gate */ 2980Sstevel@tonic-gate if (vn_mountedvfs(realrootvp) != NULL) { 2990Sstevel@tonic-gate if (error = traverse(&realrootvp)) { 3000Sstevel@tonic-gate VN_RELE(realrootvp); 3010Sstevel@tonic-gate return (error); 3020Sstevel@tonic-gate } 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * Allocate a vfs info struct and attach it 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate li = kmem_zalloc(sizeof (struct loinfo), KM_SLEEP); 3090Sstevel@tonic-gate li->li_realvfs = realrootvp->v_vfsp; 3100Sstevel@tonic-gate li->li_mountvfs = vfsp; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate /* 3130Sstevel@tonic-gate * Set mount flags to be inherited by loopback vfs's 3140Sstevel@tonic-gate */ 3150Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) { 3160Sstevel@tonic-gate li->li_mflag |= VFS_RDONLY; 3170Sstevel@tonic-gate } 3180Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) { 3190Sstevel@tonic-gate li->li_mflag |= (VFS_NOSETUID|VFS_NODEVICES); 3200Sstevel@tonic-gate } 3210Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) { 3220Sstevel@tonic-gate li->li_mflag |= VFS_NODEVICES; 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) { 3250Sstevel@tonic-gate li->li_mflag |= VFS_NOSETUID; 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * Permissive flags are added to the "deny" bitmap. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) { 3310Sstevel@tonic-gate li->li_dflag |= VFS_XATTR; 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) { 3340Sstevel@tonic-gate li->li_dflag |= VFS_NBMAND; 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate /* 3380Sstevel@tonic-gate * Propagate inheritable mount flags from the real vfs. 3390Sstevel@tonic-gate */ 3400Sstevel@tonic-gate if ((li->li_realvfs->vfs_flag & VFS_RDONLY) && 3410Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_RO, NULL)) 3420Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 3430Sstevel@tonic-gate VFS_NODISPLAY); 3440Sstevel@tonic-gate if ((li->li_realvfs->vfs_flag & VFS_NOSETUID) && 3450Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) 3460Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, 3470Sstevel@tonic-gate VFS_NODISPLAY); 3480Sstevel@tonic-gate if ((li->li_realvfs->vfs_flag & VFS_NODEVICES) && 3490Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) 3500Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL, 3510Sstevel@tonic-gate VFS_NODISPLAY); 3520Sstevel@tonic-gate /* 3530Sstevel@tonic-gate * Permissive flags such as VFS_XATTR, as opposed to restrictive flags 3540Sstevel@tonic-gate * such as VFS_RDONLY, are handled differently. An explicit 3550Sstevel@tonic-gate * MNTOPT_NOXATTR should override the underlying filesystem's VFS_XATTR. 3560Sstevel@tonic-gate */ 3570Sstevel@tonic-gate if ((li->li_realvfs->vfs_flag & VFS_XATTR) && 3580Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL) && 3590Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) 3600Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_XATTR, NULL, 3610Sstevel@tonic-gate VFS_NODISPLAY); 3620Sstevel@tonic-gate if ((li->li_realvfs->vfs_flag & VFS_NBMAND) && 3630Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL) && 3640Sstevel@tonic-gate !vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) 3650Sstevel@tonic-gate vfs_setmntopt(vfsp, MNTOPT_NBMAND, NULL, 3660Sstevel@tonic-gate VFS_NODISPLAY); 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate li->li_refct = 0; 3690Sstevel@tonic-gate vfsp->vfs_data = (caddr_t)li; 3700Sstevel@tonic-gate vfsp->vfs_bcount = 0; 3710Sstevel@tonic-gate vfsp->vfs_fstype = lofsfstype; 3720Sstevel@tonic-gate vfsp->vfs_bsize = li->li_realvfs->vfs_bsize; 3730Sstevel@tonic-gate 374*2656Sszhou vfsp->vfs_dev = li->li_realvfs->vfs_dev; 375*2656Sszhou vfsp->vfs_fsid.val[0] = li->li_realvfs->vfs_fsid.val[0]; 376*2656Sszhou vfsp->vfs_fsid.val[1] = li->li_realvfs->vfs_fsid.val[1]; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate if (vfs_optionisset(vfsp, MNTOPT_LOFS_NOSUB, NULL)) { 3790Sstevel@tonic-gate li->li_flag |= LO_NOSUB; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate /* 3830Sstevel@tonic-gate * Setup the hashtable. If the root of this mount isn't a directory, 3840Sstevel@tonic-gate * there's no point in allocating a large hashtable. A table with one 3850Sstevel@tonic-gate * bucket is sufficient. 3860Sstevel@tonic-gate */ 3870Sstevel@tonic-gate if (realrootvp->v_type != VDIR) 3880Sstevel@tonic-gate lsetup(li, 1); 3890Sstevel@tonic-gate else 3900Sstevel@tonic-gate lsetup(li, 0); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate /* 3930Sstevel@tonic-gate * Make the root vnode 3940Sstevel@tonic-gate */ 395324Sowenr srootvp = makelonode(realrootvp, li, 0); 3960Sstevel@tonic-gate srootvp->v_flag |= VROOT; 3970Sstevel@tonic-gate li->li_rootvp = srootvp; 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate #ifdef LODEBUG 4000Sstevel@tonic-gate lo_dprint(4, "lo_mount: vfs %p realvfs %p root %p realroot %p li %p\n", 4010Sstevel@tonic-gate vfsp, li->li_realvfs, srootvp, realrootvp, li); 4020Sstevel@tonic-gate #endif 4030Sstevel@tonic-gate return (0); 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate /* 4070Sstevel@tonic-gate * Undo loopback mount 4080Sstevel@tonic-gate */ 4090Sstevel@tonic-gate static int 4100Sstevel@tonic-gate lo_unmount(struct vfs *vfsp, int flag, struct cred *cr) 4110Sstevel@tonic-gate { 4120Sstevel@tonic-gate struct loinfo *li; 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 4150Sstevel@tonic-gate return (EPERM); 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate /* 4180Sstevel@tonic-gate * Forced unmount is not supported by this file system 4190Sstevel@tonic-gate * and thus, ENOTSUP, is being returned. 4200Sstevel@tonic-gate */ 4210Sstevel@tonic-gate if (flag & MS_FORCE) 4220Sstevel@tonic-gate return (ENOTSUP); 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate li = vtoli(vfsp); 4250Sstevel@tonic-gate #ifdef LODEBUG 4260Sstevel@tonic-gate lo_dprint(4, "lo_unmount(%p) li %p\n", vfsp, li); 4270Sstevel@tonic-gate #endif 4280Sstevel@tonic-gate if (li->li_refct != 1 || li->li_rootvp->v_count != 1) { 4290Sstevel@tonic-gate #ifdef LODEBUG 4300Sstevel@tonic-gate lo_dprint(4, "refct %d v_ct %d\n", li->li_refct, 4310Sstevel@tonic-gate li->li_rootvp->v_count); 4320Sstevel@tonic-gate #endif 4330Sstevel@tonic-gate return (EBUSY); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate VN_RELE(li->li_rootvp); 4360Sstevel@tonic-gate return (0); 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate /* 4400Sstevel@tonic-gate * Find root of lofs mount. 4410Sstevel@tonic-gate */ 4420Sstevel@tonic-gate static int 4430Sstevel@tonic-gate lo_root(struct vfs *vfsp, struct vnode **vpp) 4440Sstevel@tonic-gate { 4450Sstevel@tonic-gate *vpp = vtoli(vfsp)->li_rootvp; 4460Sstevel@tonic-gate #ifdef LODEBUG 4470Sstevel@tonic-gate lo_dprint(4, "lo_root(0x%p) = %p\n", vfsp, *vpp); 4480Sstevel@tonic-gate #endif 4490Sstevel@tonic-gate /* 4500Sstevel@tonic-gate * If the root of the filesystem is a special file, return the specvp 4510Sstevel@tonic-gate * version of the vnode. We don't save the specvp vnode in our 4520Sstevel@tonic-gate * hashtable since that's exclusively for lnodes. 4530Sstevel@tonic-gate */ 4540Sstevel@tonic-gate if (IS_DEVVP(*vpp)) { 4550Sstevel@tonic-gate struct vnode *svp; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, kcred); 4580Sstevel@tonic-gate if (svp == NULL) 4590Sstevel@tonic-gate return (ENOSYS); 4600Sstevel@tonic-gate *vpp = svp; 4610Sstevel@tonic-gate } else { 4620Sstevel@tonic-gate VN_HOLD(*vpp); 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate return (0); 4660Sstevel@tonic-gate } 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate /* 4690Sstevel@tonic-gate * Get file system statistics. 4700Sstevel@tonic-gate */ 4710Sstevel@tonic-gate static int 4720Sstevel@tonic-gate lo_statvfs(register struct vfs *vfsp, struct statvfs64 *sbp) 4730Sstevel@tonic-gate { 4740Sstevel@tonic-gate vnode_t *realrootvp; 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate #ifdef LODEBUG 4770Sstevel@tonic-gate lo_dprint(4, "lostatvfs %p\n", vfsp); 4780Sstevel@tonic-gate #endif 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * Using realrootvp->v_vfsp (instead of the realvfsp that was 4810Sstevel@tonic-gate * cached) is necessary to make lofs work woth forced UFS unmounts. 4820Sstevel@tonic-gate * In the case of a forced unmount, UFS stores a set of dummy vfsops 4830Sstevel@tonic-gate * in all the (i)vnodes in the filesystem. The dummy ops simply 4840Sstevel@tonic-gate * returns back EIO. 4850Sstevel@tonic-gate */ 4860Sstevel@tonic-gate (void) lo_realvfs(vfsp, &realrootvp); 4870Sstevel@tonic-gate if (realrootvp != NULL) 4880Sstevel@tonic-gate return (VFS_STATVFS(realrootvp->v_vfsp, sbp)); 4890Sstevel@tonic-gate else 4900Sstevel@tonic-gate return (EIO); 4910Sstevel@tonic-gate } 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate /* 4940Sstevel@tonic-gate * LOFS doesn't have any data or metadata to flush, pending I/O on the 4950Sstevel@tonic-gate * underlying filesystem will be flushed when such filesystem is synched. 4960Sstevel@tonic-gate */ 4970Sstevel@tonic-gate /* ARGSUSED */ 4980Sstevel@tonic-gate static int 4990Sstevel@tonic-gate lo_sync(struct vfs *vfsp, 5000Sstevel@tonic-gate short flag, 5010Sstevel@tonic-gate struct cred *cr) 5020Sstevel@tonic-gate { 5030Sstevel@tonic-gate #ifdef LODEBUG 5040Sstevel@tonic-gate lo_dprint(4, "lo_sync: %p\n", vfsp); 5050Sstevel@tonic-gate #endif 5060Sstevel@tonic-gate return (0); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /* 5100Sstevel@tonic-gate * Obtain the vnode from the underlying filesystem. 5110Sstevel@tonic-gate */ 5120Sstevel@tonic-gate static int 5130Sstevel@tonic-gate lo_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp) 5140Sstevel@tonic-gate { 5150Sstevel@tonic-gate vnode_t *realrootvp; 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate #ifdef LODEBUG 5180Sstevel@tonic-gate lo_dprint(4, "lo_vget: %p\n", vfsp); 5190Sstevel@tonic-gate #endif 5200Sstevel@tonic-gate (void) lo_realvfs(vfsp, &realrootvp); 5210Sstevel@tonic-gate if (realrootvp != NULL) 5220Sstevel@tonic-gate return (VFS_VGET(realrootvp->v_vfsp, vpp, fidp)); 5230Sstevel@tonic-gate else 5240Sstevel@tonic-gate return (EIO); 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate /* 5280Sstevel@tonic-gate * Free mount-specific data. 5290Sstevel@tonic-gate */ 5300Sstevel@tonic-gate static void 5310Sstevel@tonic-gate lo_freevfs(struct vfs *vfsp) 5320Sstevel@tonic-gate { 5330Sstevel@tonic-gate struct loinfo *li = vtoli(vfsp); 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate ldestroy(li); 5360Sstevel@tonic-gate kmem_free(li, sizeof (struct loinfo)); 5370Sstevel@tonic-gate } 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate static int 5400Sstevel@tonic-gate lofsinit(int fstyp, char *name) 5410Sstevel@tonic-gate { 5420Sstevel@tonic-gate static const fs_operation_def_t lo_vfsops_template[] = { 5430Sstevel@tonic-gate VFSNAME_MOUNT, lo_mount, 5440Sstevel@tonic-gate VFSNAME_UNMOUNT, lo_unmount, 5450Sstevel@tonic-gate VFSNAME_ROOT, lo_root, 5460Sstevel@tonic-gate VFSNAME_STATVFS, lo_statvfs, 5470Sstevel@tonic-gate VFSNAME_SYNC, (fs_generic_func_p) lo_sync, 5480Sstevel@tonic-gate VFSNAME_VGET, lo_vget, 5490Sstevel@tonic-gate VFSNAME_FREEVFS, (fs_generic_func_p) lo_freevfs, 5500Sstevel@tonic-gate NULL, NULL 5510Sstevel@tonic-gate }; 5520Sstevel@tonic-gate int error; 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate error = vfs_setfsops(fstyp, lo_vfsops_template, &lo_vfsops); 5550Sstevel@tonic-gate if (error != 0) { 5560Sstevel@tonic-gate cmn_err(CE_WARN, "lofsinit: bad vfs ops template"); 5570Sstevel@tonic-gate return (error); 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate error = vn_make_ops(name, lo_vnodeops_template, &lo_vnodeops); 5610Sstevel@tonic-gate if (error != 0) { 5620Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstyp); 5630Sstevel@tonic-gate cmn_err(CE_WARN, "lofsinit: bad vnode ops template"); 5640Sstevel@tonic-gate return (error); 5650Sstevel@tonic-gate } 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate lofsfstype = fstyp; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate return (0); 5700Sstevel@tonic-gate } 571