/* * The nbmand mount option can be changed at mount time. * We can't allow it to be toggled on live file systems or incorrect * behavior may be seen from cifs clients * * This property isn't registered via dsl_prop_register(), but this callback * will be called when a file system is first mounted */ static void nbmand_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == FALSE) { vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND, NULL, 0); } else { vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND, NULL, 0); } }
static void exec_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == FALSE) { zfsvfs->z_vfs->vfs_flag |= VFS_NOEXEC; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_EXEC); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC, NULL, 0); } else { zfsvfs->z_vfs->vfs_flag &= ~VFS_NOEXEC; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_EXEC, NULL, 0); } }
static void setuid_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == FALSE) { zfsvfs->z_vfs->vfs_flag |= VFS_NOSETUID; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_SETUID); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID, NULL, 0); } else { zfsvfs->z_vfs->vfs_flag &= ~VFS_NOSETUID; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_SETUID, NULL, 0); } }
static void devices_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == FALSE) { zfsvfs->z_vfs->vfs_flag |= VFS_NODEVICES; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES, NULL, 0); } else { zfsvfs->z_vfs->vfs_flag &= ~VFS_NODEVICES; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES, NULL, 0); } }
static void atime_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == TRUE) { zfsvfs->z_atime = TRUE; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_ATIME, NULL, 0); } else { zfsvfs->z_atime = FALSE; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_ATIME); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME, NULL, 0); } }
static void readonly_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval) { /* XXX locking on vfs_flag? */ zfsvfs->z_vfs->vfs_flag |= VFS_RDONLY; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RW); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RO, NULL, 0); } else { /* XXX locking on vfs_flag? */ zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY; vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RO); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RW, NULL, 0); } }
static void xattr_changed_cb(void *arg, uint64_t newval) { zfsvfs_t *zfsvfs = arg; if (newval == TRUE) { /* XXX locking on vfs_flag? */ #ifdef TODO zfsvfs->z_vfs->vfs_flag |= VFS_XATTR; #endif vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_XATTR, NULL, 0); } else { /* XXX locking on vfs_flag? */ #ifdef TODO zfsvfs->z_vfs->vfs_flag &= ~VFS_XATTR; #endif vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_XATTR); vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR, NULL, 0); } }
/* * Close off disk quotas for a file system. */ int closedq(struct ufsvfs *ufsvfsp, struct cred *cr) { struct inode *qip; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * Quotas are not enabled on this file system so there is * nothing more to do. */ if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } /* * At this point, the quota subsystem is quiescent on this file * system so we can do all the work necessary to dismantle the * quota stuff. */ qip = ufsvfsp->vfs_qinod; if (!qip) return (ufs_fault(ufsvfsp->vfs_root, "closedq: NULL qip")); ufsvfsp->vfs_qflags = 0; /* disable quotas */ vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_NOQUOTA, NULL, 0); /* * ufs_scan_inodes() depends on vfs_qinod, so we can't * clear it until afterwards. */ (void) ufs_scan_inodes(0, closedq_scan_inode, ufsvfsp, ufsvfsp); ufsvfsp->vfs_qinod = NULL; rw_exit(&ufsvfsp->vfs_dqrwlock); /* * Sync and release the quota file inode. Since we have a private * pointer to the quota inode and vfs_qinod is now clear we do not * need to hold vfs_dqrwlock. */ (void) TRANS_SYNCIP(qip, 0, I_SYNC, TOP_SYNCIP_CLOSEDQ); VN_RELE(ITOV(qip)); return (0); }
/*ARGSUSED*/ static int lo_mount(struct vfs *vfsp, struct vnode *vp, struct mounta *uap, struct cred *cr) { int error; struct vnode *srootvp = NULL; /* the server's root */ struct vnode *realrootvp; struct loinfo *li; int nodev; nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL); if ((error = secpolicy_fs_mount(cr, vp, vfsp)) != 0) return (EPERM); /* * Loopback devices which get "nodevices" added can be done without * "nodevices" set because we cannot import devices into a zone * with loopback. Note that we have all zone privileges when * this happens; if not, we'd have gotten "nosuid". */ if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY); mutex_enter(&vp->v_lock); if (!(uap->flags & MS_OVERLAY) && (vp->v_count != 1 || (vp->v_flag & VROOT))) { mutex_exit(&vp->v_lock); return (EBUSY); } mutex_exit(&vp->v_lock); /* * Find real root, and make vfs point to real vfs */ if (error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp)) return (error); /* * Enforce MAC policy if needed. * * Loopback mounts must not allow writing up. The dominance test * is intended to prevent a global zone caller from accidentally * creating write-up conditions between two labeled zones. * Local zones can't violate MAC on their own without help from * the global zone because they can't name a pathname that * they don't already have. * * The special case check for the NET_MAC_AWARE process flag is * to support the case of the automounter in the global zone. We * permit automounting of local zone directories such as home * directories, into the global zone as required by setlabel, * zonecopy, and saving of desktop sessions. Such mounts are * trusted not to expose the contents of one zone's directories * to another by leaking them through the global zone. */ if (is_system_labeled() && crgetzoneid(cr) == GLOBAL_ZONEID) { char specname[MAXPATHLEN]; zone_t *from_zptr; zone_t *to_zptr; if (vnodetopath(NULL, realrootvp, specname, sizeof (specname), CRED()) != 0) { VN_RELE(realrootvp); return (EACCES); } from_zptr = zone_find_by_path(specname); to_zptr = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); /* * Special case for zone devfs: the zone for /dev will * incorrectly appear as the global zone since it's not * under the zone rootpath. So for zone devfs check allow * read-write mounts. * * Second special case for scratch zones used for Live Upgrade: * this is used to mount the zone's root from /root to /a in * the scratch zone. As with the other special case, this * appears to be outside of the zone because it's not under * the zone rootpath, which is $ZONEPATH/lu in the scratch * zone case. */ if (from_zptr != to_zptr && !(to_zptr->zone_flags & ZF_IS_SCRATCH)) { /* * We know at this point that the labels aren't equal * because the zone pointers aren't equal, and zones * can't share a label. * * If the source is the global zone then making * it available to a local zone must be done in * read-only mode as the label will become admin_low. * * If it is a mount between local zones then if * the current process is in the global zone and has * the NET_MAC_AWARE flag, then regular read-write * access is allowed. If it's in some other zone, but * the label on the mount point dominates the original * source, then allow the mount as read-only * ("read-down"). */ if (from_zptr->zone_id == GLOBAL_ZONEID) { /* make the mount read-only */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } else { /* cross-zone mount */ if (to_zptr->zone_id == GLOBAL_ZONEID && /* LINTED: no consequent */ getpflags(NET_MAC_AWARE, cr) != 0) { /* Allow the mount as read-write */ } else if (bldominates( label2bslabel(to_zptr->zone_slabel), label2bslabel(from_zptr->zone_slabel))) { /* make the mount read-only */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } else { VN_RELE(realrootvp); zone_rele(to_zptr); zone_rele(from_zptr); return (EACCES); } } } zone_rele(to_zptr); zone_rele(from_zptr); } /* * realrootvp may be an AUTOFS node, in which case we * perform a VOP_ACCESS() to trigger the mount of the * intended filesystem, so we loopback mount the intended * filesystem instead of the AUTOFS filesystem. */ (void) VOP_ACCESS(realrootvp, 0, 0, cr, NULL); /* * We're interested in the top most filesystem. * This is specially important when uap->spec is a trigger * AUTOFS node, since we're really interested in mounting the * filesystem AUTOFS mounted as result of the VOP_ACCESS() * call not the AUTOFS node itself. */ if (vn_mountedvfs(realrootvp) != NULL) { if (error = traverse(&realrootvp)) { VN_RELE(realrootvp); return (error); } } /* * Allocate a vfs info struct and attach it */ li = kmem_zalloc(sizeof (struct loinfo), KM_SLEEP); li->li_realvfs = realrootvp->v_vfsp; li->li_mountvfs = vfsp; /* * Set mount flags to be inherited by loopback vfs's */ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) { li->li_mflag |= VFS_RDONLY; } if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) { li->li_mflag |= (VFS_NOSETUID|VFS_NODEVICES); } if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) { li->li_mflag |= VFS_NODEVICES; } if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) { li->li_mflag |= VFS_NOSETUID; } /* * Permissive flags are added to the "deny" bitmap. */ if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) { li->li_dflag |= VFS_XATTR; } if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) { li->li_dflag |= VFS_NBMAND; } /* * Propagate inheritable mount flags from the real vfs. */ if ((li->li_realvfs->vfs_flag & VFS_RDONLY) && !vfs_optionisset(vfsp, MNTOPT_RO, NULL)) vfs_setmntopt(vfsp, MNTOPT_RO, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NOSETUID) && !vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NODEVICES) && !vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL, VFS_NODISPLAY); /* * Permissive flags such as VFS_XATTR, as opposed to restrictive flags * such as VFS_RDONLY, are handled differently. An explicit * MNTOPT_NOXATTR should override the underlying filesystem's VFS_XATTR. */ if ((li->li_realvfs->vfs_flag & VFS_XATTR) && !vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL) && !vfs_optionisset(vfsp, MNTOPT_XATTR, NULL)) vfs_setmntopt(vfsp, MNTOPT_XATTR, NULL, VFS_NODISPLAY); if ((li->li_realvfs->vfs_flag & VFS_NBMAND) && !vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL) && !vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) vfs_setmntopt(vfsp, MNTOPT_NBMAND, NULL, VFS_NODISPLAY); li->li_refct = 0; vfsp->vfs_data = (caddr_t)li; vfsp->vfs_bcount = 0; vfsp->vfs_fstype = lofsfstype; vfsp->vfs_bsize = li->li_realvfs->vfs_bsize; vfsp->vfs_dev = li->li_realvfs->vfs_dev; vfsp->vfs_fsid.val[0] = li->li_realvfs->vfs_fsid.val[0]; vfsp->vfs_fsid.val[1] = li->li_realvfs->vfs_fsid.val[1]; if (vfs_optionisset(vfsp, MNTOPT_LOFS_NOSUB, NULL)) { li->li_flag |= LO_NOSUB; } /* * Propagate any VFS features */ vfs_propagate_features(li->li_realvfs, vfsp); /* * Setup the hashtable. If the root of this mount isn't a directory, * there's no point in allocating a large hashtable. A table with one * bucket is sufficient. */ if (realrootvp->v_type != VDIR) lsetup(li, 1); else lsetup(li, 0); /* * Make the root vnode */ srootvp = makelonode(realrootvp, li, 0); srootvp->v_flag |= VROOT; li->li_rootvp = srootvp; #ifdef LODEBUG lo_dprint(4, "lo_mount: vfs %p realvfs %p root %p realroot %p li %p\n", vfsp, li->li_realvfs, srootvp, realrootvp, li); #endif return (0); }
/* * Enable logging */ int lqfs_enable(struct vnode *vp, struct fiolog *flp, cred_t *cr) { int error; inode_t *ip = VTOI(vp); qfsvfs_t *qfsvfsp = ip->i_qfsvfs; fs_lqfs_common_t *fs = VFS_FS_PTR(qfsvfsp); ml_unit_t *ul; #ifdef LQFS_TODO_LOCKFS int reclaim = 0; struct lockfs lf; struct ulockfs *ulp; #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_TODO_LOCKFS */ vfs_t *vfsp = qfsvfsp->vfs_vfs; uint64_t tmp_nbytes_actual; char fsclean; sam_sblk_t *sblk = qfsvfsp->mi.m_sbp; /* * File system is not capable of logging. */ if (!LQFS_CAPABLE(qfsvfsp)) { flp->error = FIOLOG_ENOTSUP; error = 0; goto out; } if (!SAM_MAGIC_V2A_OR_HIGHER(&sblk->info.sb)) { cmn_err(CE_WARN, "SAM-QFS: %s: Not enabling logging, " " file system is not version 2A.", qfsvfsp->mt.fi_name); cmn_err(CE_WARN, "\tUpgrade file system with samfsck -u " "first."); flp->error = FIOLOG_ENOTSUP; error = 0; goto out; } if (LQFS_GET_LOGBNO(fs)) { error = lqfs_log_validate(qfsvfsp, flp, cr); } /* * Check if logging is already enabled */ if (LQFS_GET_LOGP(qfsvfsp)) { flp->error = FIOLOG_ETRANS; /* for root ensure logging option is set */ vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0); error = 0; goto out; } /* * Come back here to recheck if we had to disable the log. */ recheck: error = 0; flp->error = FIOLOG_ENONE; /* * Adjust requested log size */ flp->nbytes_actual = flp->nbytes_requested; if (flp->nbytes_actual == 0) { tmp_nbytes_actual = (((uint64_t)FS_SIZE(fs)) / ldl_divisor) << FS_FSHIFT(fs); flp->nbytes_actual = (uint_t)MIN(tmp_nbytes_actual, INT_MAX); } flp->nbytes_actual = MAX(flp->nbytes_actual, ldl_minlogsize); flp->nbytes_actual = MIN(flp->nbytes_actual, ldl_maxlogsize); flp->nbytes_actual = blkroundup(fs, flp->nbytes_actual); /* * logging is enabled and the log is the right size; done */ ul = LQFS_GET_LOGP(qfsvfsp); if (ul && LQFS_GET_LOGBNO(fs) && (flp->nbytes_actual == ul->un_requestsize)) { vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0); error = 0; goto out; } /* * Readonly file system */ if (FS_RDONLY(fs)) { flp->error = FIOLOG_EROFS; error = 0; goto out; } #ifdef LQFS_TODO_LOCKFS /* * File system must be write locked to enable logging */ error = qfs_fiolfss(vp, &lf); if (error) { goto out; } if (!LOCKFS_IS_ULOCK(&lf)) { flp->error = FIOLOG_EULOCK; error = 0; goto out; } lf.lf_lock = LOCKFS_WLOCK; lf.lf_flags = 0; lf.lf_comment = NULL; error = qfs_fiolfs(vp, &lf, 1); if (error) { flp->error = FIOLOG_EWLOCK; error = 0; goto out; } #else /* QFS doesn't really support lockfs. */ #endif /* LQFS_TODO_LOCKFS */ /* * Grab appropriate locks to synchronize with the rest * of the system */ vfs_lock_wait(vfsp); #ifdef LQFS_TODO_LOCKFS ulp = &ufsvfsp->vfs_ulockfs; mutex_enter(&ulp->ul_lock); #else /* QFS doesn't really support lockfs. */ #endif /* LQFS_TODO_LOCKFS */ /* * File system must be fairly consistent to enable logging */ fsclean = LQFS_GET_FS_CLEAN(fs); if (fsclean != FSLOG && fsclean != FSACTIVE && fsclean != FSSTABLE && fsclean != FSCLEAN) { flp->error = FIOLOG_ECLEAN; goto unlockout; } #ifdef LUFS /* * A write-locked file system is only active if there are * open deleted files; so remember to set FS_RECLAIM later. */ if (LQFS_GET_FS_CLEAN(fs) == FSACTIVE) { reclaim = FS_RECLAIM; } #else /* QFS doesn't have a reclaim file thread. */ #endif /* LUFS */ /* * Logging is already enabled; must be changing the log's size */ if (LQFS_GET_LOGBNO(fs) && LQFS_GET_LOGP(qfsvfsp)) { #ifdef LQFS_TODO_LOCKFS /* * Before we can disable logging, we must give up our * lock. As a consequence of unlocking and disabling the * log, the fs structure may change. Because of this, when * disabling is complete, we will go back to recheck to * repeat all of the checks that we performed to get to * this point. Disabling sets fs->fs_logbno to 0, so this * will not put us into an infinite loop. */ mutex_exit(&ulp->ul_lock); #else /* QFS doesn't really support lockfs. */ #endif /* LQFS_TODO_LOCKFS */ vfs_unlock(vfsp); #ifdef LQFS_TODO_LOCKFS lf.lf_lock = LOCKFS_ULOCK; lf.lf_flags = 0; error = qfs_fiolfs(vp, &lf, 1); if (error) { flp->error = FIOLOG_ENOULOCK; error = 0; goto out; } #else /* QFS doesn't really support lockfs. */ #endif /* LQFS_TODO_LOCKFS */ error = lqfs_disable(vp, flp); if (error || (flp->error != FIOLOG_ENONE)) { error = 0; goto out; } goto recheck; } error = lqfs_alloc(qfsvfsp, flp, cr); if (error) { goto errout; } #ifdef LUFS #else if ((error = lqfs_log_validate(qfsvfsp, flp, cr)) != 0) { goto errout; } #endif /* LUFS */ /* * Create all of the incore structs */ error = lqfs_snarf(qfsvfsp, fs, 0); if (error) { goto errout; } /* * DON'T ``GOTO ERROUT'' PAST THIS POINT */ /* * Pretend we were just mounted with logging enabled * freeze and drain the file system of readers * Get the ops vector * If debug, record metadata locations with log subsystem * Start the delete thread * Start the reclaim thread, if necessary * Thaw readers */ vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0); TRANS_DOMATAMAP(qfsvfsp); TRANS_MATA_MOUNT(qfsvfsp); TRANS_MATA_SI(qfsvfsp, fs); #ifdef LUFS qfs_thread_start(&qfsvfsp->vfs_delete, qfs_thread_delete, vfsp); if (fs->fs_reclaim & (FS_RECLAIM|FS_RECLAIMING)) { fs->fs_reclaim &= ~FS_RECLAIM; fs->fs_reclaim |= FS_RECLAIMING; qfs_thread_start(&qfsvfsp->vfs_reclaim, qfs_thread_reclaim, vfsp); } else { fs->fs_reclaim |= reclaim; } #else /* QFS doesn't have file reclaim nor i-node delete threads. */ #endif /* LUFS */ #ifdef LUFS mutex_exit(&ulp->ul_lock); #else /* QFS doesn't really support LOCKFS. */ #endif /* LUFS */ vfs_unlock(vfsp); #ifdef LQFS_TODO_LOCKFS /* * Unlock the file system */ lf.lf_lock = LOCKFS_ULOCK; lf.lf_flags = 0; error = qfs_fiolfs(vp, &lf, 1); if (error) { flp->error = FIOLOG_ENOULOCK; error = 0; goto out; } #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_TODO_LOCKFS */ /* * There's nothing in the log yet (we've just allocated it) * so directly write out the super block. * Note, we have to force this sb out to disk * (not just to the log) so that if we crash we know we are logging */ VFS_LOCK_MUTEX_ENTER(qfsvfsp); LQFS_SET_FS_CLEAN(fs, FSLOG); LQFS_SET_FS_ROLLED(fs, FS_NEED_ROLL); /* Mark the fs as unrolled */ #ifdef LUFS QFS_BWRITE2(NULL, qfsvfsp->vfs_bufp); #else sam_update_sblk(qfsvfsp, 0, 0, TRUE); #endif /* LUFS */ VFS_LOCK_MUTEX_EXIT(qfsvfsp); error = 0; goto out; errout: /* * Aquire the qfs_scan_lock before de-linking the mtm data * structure so that we keep qfs_sync() and qfs_update() away * when they execute the ufs_scan_inodes() run while we're in * progress of enabling/disabling logging. */ mutex_enter(&qfs_scan_lock); (void) lqfs_unsnarf(qfsvfsp); mutex_exit(&qfs_scan_lock); (void) lqfs_free(qfsvfsp); unlockout: #ifdef LQFS_TODO_LOCKFS mutex_exit(&ulp->ul_lock); #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_TODO_LOCKFS */ vfs_unlock(vfsp); #ifdef LQFS_TODO_LOCKFS lf.lf_lock = LOCKFS_ULOCK; lf.lf_flags = 0; (void) qfs_fiolfs(vp, &lf, 1); #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_TODO_LOCKFS */ out: mutex_enter(&ip->mp->ms.m_waitwr_mutex); ip->mp->mt.fi_status |= FS_LOGSTATE_KNOWN; mutex_exit(&ip->mp->ms.m_waitwr_mutex); return (error); }
/* * Disable logging */ int lqfs_disable(vnode_t *vp, struct fiolog *flp) { int error = 0; inode_t *ip = VTOI(vp); qfsvfs_t *qfsvfsp = ip->i_qfsvfs; fs_lqfs_common_t *fs = VFS_FS_PTR(qfsvfsp); #ifdef LUFS struct lockfs lf; struct ulockfs *ulp; #else /* QFS doesn't really support LOCKFS. */ #endif /* LUFS */ flp->error = FIOLOG_ENONE; /* * Logging is already disabled; done */ if (LQFS_GET_LOGBNO(fs) == 0 || LQFS_GET_LOGP(qfsvfsp) == NULL || !LQFS_CAPABLE(qfsvfsp)) { vfs_setmntopt(qfsvfsp->vfs_vfs, MNTOPT_NOLOGGING, NULL, 0); error = 0; goto out; } #ifdef LUFS /* * File system must be write locked to disable logging */ error = qfs_fiolfss(vp, &lf); if (error) { goto out; } if (!LOCKFS_IS_ULOCK(&lf)) { flp->error = FIOLOG_EULOCK; error = 0; goto out; } lf.lf_lock = LOCKFS_WLOCK; lf.lf_flags = 0; lf.lf_comment = NULL; error = qfs_fiolfs(vp, &lf, 1); if (error) { flp->error = FIOLOG_EWLOCK; error = 0; goto out; } #else /* QFS doesn't really support LOCKFS. */ #endif /* LUFS */ if (LQFS_GET_LOGP(qfsvfsp) == NULL || LQFS_GET_LOGBNO(fs) == 0) { goto errout; } /* * WE ARE COMMITTED TO DISABLING LOGGING PAST THIS POINT */ /* * Disable logging: * Suspend the reclaim thread and force the delete thread to exit. * When a nologging mount has completed there may still be * work for reclaim to do so just suspend this thread until * it's [deadlock-] safe for it to continue. The delete * thread won't be needed as qfs_iinactive() calls * qfs_delete() when logging is disabled. * Freeze and drain reader ops. * Commit any outstanding reader transactions (lqfs_flush). * Set the ``unmounted'' bit in the qfstrans struct. * If debug, remove metadata from matamap. * Disable matamap processing. * NULL the trans ops table. * Free all of the incore structs related to logging. * Allow reader ops. */ #ifdef LUFS qfs_thread_suspend(&qfsvfsp->vfs_reclaim); qfs_thread_exit(&qfsvfsp->vfs_delete); #else /* QFS doesn't have file reclaim nor i-node delete threads. */ #endif /* LUFS */ vfs_lock_wait(qfsvfsp->vfs_vfs); #ifdef LQFS_TODO_LOCKFS ulp = &qfsvfsp->vfs_ulockfs; mutex_enter(&ulp->ul_lock); (void) qfs_quiesce(ulp); #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_TODO_LOCKFS */ #ifdef LQFS_TODO (void) qfs_flush(qfsvfsp->vfs_vfs); #else (void) lqfs_flush(qfsvfsp); if (LQFS_GET_LOGP(qfsvfsp)) { logmap_start_roll(LQFS_GET_LOGP(qfsvfsp)); } #endif /* LQFS_TODO */ TRANS_MATA_UMOUNT(qfsvfsp); LQFS_SET_DOMATAMAP(qfsvfsp, 0); /* * Free all of the incore structs * Aquire the ufs_scan_lock before de-linking the mtm data * structure so that we keep ufs_sync() and ufs_update() away * when they execute the ufs_scan_inodes() run while we're in * progress of enabling/disabling logging. */ mutex_enter(&qfs_scan_lock); (void) lqfs_unsnarf(qfsvfsp); mutex_exit(&qfs_scan_lock); #ifdef LQFS_TODO_LOCKFS atomic_add_long(&ufs_quiesce_pend, -1); mutex_exit(&ulp->ul_lock); #else /* QFS doesn't do this yet. */ #endif /* LQFS_TODO_LOCKFS */ vfs_setmntopt(qfsvfsp->vfs_vfs, MNTOPT_NOLOGGING, NULL, 0); vfs_unlock(qfsvfsp->vfs_vfs); LQFS_SET_FS_ROLLED(fs, FS_ALL_ROLLED); LQFS_SET_NOLOG_SI(qfsvfsp, 0); /* * Free the log space and mark the superblock as FSACTIVE */ (void) lqfs_free(qfsvfsp); #ifdef LUFS /* * Allow the reclaim thread to continue. */ qfs_thread_continue(&qfsvfsp->vfs_reclaim); #else /* QFS doesn't have a file reclaim thread. */ #endif /* LUFS */ #ifdef LQFS_TODO_LOCKFS /* * Unlock the file system */ lf.lf_lock = LOCKFS_ULOCK; lf.lf_flags = 0; error = qfs_fiolfs(vp, &lf, 1); if (error) { flp->error = FIOLOG_ENOULOCK; } #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_LOCKFS */ error = 0; goto out; errout: #ifdef LQFS_LOCKFS lf.lf_lock = LOCKFS_ULOCK; lf.lf_flags = 0; (void) qfs_fiolfs(vp, &lf, 1); #else /* QFS doesn't really support LOCKFS. */ #endif /* LQFS_LOCKFS */ out: mutex_enter(&ip->mp->ms.m_waitwr_mutex); ip->mp->mt.fi_status |= FS_LOGSTATE_KNOWN; mutex_exit(&ip->mp->ms.m_waitwr_mutex); return (error); }
/* * Set the quota file up for a particular file system. * Called as the result of a quotaon (Q_QUOTAON) ioctl. */ static int opendq( struct ufsvfs *ufsvfsp, struct vnode *vp, /* quota file */ struct cred *cr) { struct inode *qip; struct dquot *dqp; int error; int quotaon = 0; if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) return (EPERM); VN_HOLD(vp); /* * Check to be sure its a regular file. */ if (vp->v_type != VREG) { VN_RELE(vp); return (EACCES); } rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); /* * We have vfs_dqrwlock as writer, so if quotas are disabled, * then vfs_qinod should be NULL or we have a race somewhere. */ ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0)); if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) { /* * Quotas are already enabled on this file system. * * If the "quotas" file was replaced (different inode) * while quotas were enabled we don't want to re-enable * them with a new "quotas" file. Simply print a warning * message to the console, release the new vnode, and * return. * XXX - The right way to fix this is to return EBUSY * for the ioctl() issued by 'quotaon'. */ if (VTOI(vp) != ufsvfsp->vfs_qinod) { cmn_err(CE_WARN, "Previous quota file still in use." " Disable quotas on %s before enabling.\n", VTOI(vp)->i_fs->fs_fsmnt); VN_RELE(vp); rw_exit(&ufsvfsp->vfs_dqrwlock); return (0); } (void) quotasync(ufsvfsp, /* do_lock */ 0); /* remove extra hold on quota file */ VN_RELE(vp); quotaon++; qip = ufsvfsp->vfs_qinod; } else { int qlen; ufsvfsp->vfs_qinod = VTOI(vp); qip = ufsvfsp->vfs_qinod; /* * Force the file to have no partially allocated blocks * to prevent a realloc from changing the location of * the data. We must do this even if not logging in * case we later remount to logging. */ qlen = qip->i_fs->fs_bsize * NDADDR; /* * Largefiles: i_size needs to be atomically accessed now. */ rw_enter(&qip->i_contents, RW_WRITER); if (qip->i_size < qlen) { if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0) cmn_err(CE_WARN, "opendq failed to remove frags" " from quota file\n"); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size, B_INVAL, kcred, NULL); } else { rw_exit(&qip->i_contents); } TRANS_MATA_IGET(ufsvfsp, qip); } /* * The file system time limits are in the dquot for uid 0. * The time limits set the relative time the other users * can be over quota for this file system. * If it is zero a default is used (see quota.h). */ error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp); if (error == 0) { mutex_enter(&dqp->dq_lock); ufsvfsp->vfs_btimelimit = (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT); ufsvfsp->vfs_ftimelimit = (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT); ufsvfsp->vfs_qflags = MQ_ENABLED; /* enable quotas */ vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0); dqput(dqp); mutex_exit(&dqp->dq_lock); } else if (!quotaon) { /* * Some sort of I/O error on the quota file, and quotas were * not already on when we got here so clean up. */ ufsvfsp->vfs_qflags = 0; ufsvfsp->vfs_qinod = NULL; VN_RELE(ITOV(qip)); } /* * If quotas are enabled update all valid inodes in the * cache with quota information. */ if (ufsvfsp->vfs_qflags & MQ_ENABLED) { (void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp); } rw_exit(&ufsvfsp->vfs_dqrwlock); return (error); }
int domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath, char *fspec, int fsflags) { struct mount *mp; struct vfsconf *vfsp; struct ucred *newcr, *oldcr; int error; /* * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. */ if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) return (ENAMETOOLONG); vfsp = vfs_byname_kld(fstype, td, &error); if (vfsp == NULL) return (ENODEV); if (vp->v_type != VDIR) return (ENOTDIR); simple_lock(&vp->v_interlock); if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { simple_unlock(&vp->v_interlock); return (EBUSY); } vp->v_iflag |= VI_MOUNT; simple_unlock(&vp->v_interlock); /* * Allocate and initialize the filesystem. */ vn_lock(vp, LK_SHARED | LK_RETRY); mp = vfs_mount_alloc(vp, vfsp, fspath, td); VOP_UNLOCK(vp); mp->mnt_optnew = NULL; vfs_setmntopt(mp, "from", fspec, 0); mp->mnt_optnew = mp->mnt_opt; mp->mnt_opt = NULL; /* * Set the mount level flags. * crdup() can sleep, so do it before acquiring a mutex. */ newcr = crdup(kcred); MNT_ILOCK(mp); if (fsflags & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; mp->mnt_flag &=~ MNT_UPDATEMASK; mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS); /* * Unprivileged user can trigger mounting a snapshot, but we don't want * him to unmount it, so we switch to privileged credentials. */ oldcr = mp->mnt_cred; mp->mnt_cred = newcr; mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid; MNT_IUNLOCK(mp); crfree(oldcr); /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. */ error = VFS_MOUNT(mp, td); if (!error) { if (mp->mnt_opt != NULL) vfs_freeopts(mp->mnt_opt); mp->mnt_opt = mp->mnt_optnew; (void)VFS_STATFS(mp, &mp->mnt_stat, td); } /* * Prevent external consumers of mount options from reading * mnt_optnew. */ mp->mnt_optnew = NULL; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* * Put the new filesystem on the mount list after root. */ #ifdef FREEBSD_NAMECACHE cache_purge(vp); #endif if (!error) { vnode_t *mvp; simple_lock(&vp->v_interlock); vp->v_iflag &= ~VI_MOUNT; simple_unlock(&vp->v_interlock); vp->v_mountedhere = mp; mountlist_append(mp); vfs_event_signal(NULL, VQ_MOUNT, 0); if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td)) panic("mount: lost mount"); mountcheckdirs(vp, mvp); vput(mvp); VOP_UNLOCK(vp); if ((mp->mnt_flag & MNT_RDONLY) == 0) vfs_syncer_add_to_worklist(mp); vfs_unbusy(mp, td); vfs_mountedfrom(mp, fspec); } else { simple_lock(&vp->v_interlock); vp->v_iflag &= ~VI_MOUNT; simple_unlock(&vp->v_interlock); VOP_UNLOCK(vp); vfs_unbusy(mp, td); vfs_mount_destroy(mp); } return (error); }
int mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath, char *fspec, int fsflags) { struct vfsconf *vfsp; struct mount *mp; vnode_t *vp, *mvp; struct ucred *cr; int error; /* * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. */ if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) return (ENAMETOOLONG); vfsp = vfs_byname_kld(fstype, td, &error); if (vfsp == NULL) return (ENODEV); vp = *vpp; if (vp->v_type != VDIR) return (ENOTDIR); /* * We need vnode lock to protect v_mountedhere and vnode interlock * to protect v_iflag. */ vn_lock(vp, LK_SHARED | LK_RETRY); VI_LOCK(vp); if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { VI_UNLOCK(vp); VOP_UNLOCK(vp, 0); return (EBUSY); } vp->v_iflag |= VI_MOUNT; VI_UNLOCK(vp); VOP_UNLOCK(vp, 0); /* * Allocate and initialize the filesystem. * We don't want regular user that triggered snapshot mount to be able * to unmount it, so pass credentials of the parent mount. */ mp = vfs_mount_alloc(vp, vfsp, fspath, vp->v_mount->mnt_cred); mp->mnt_optnew = NULL; vfs_setmntopt(mp, "from", fspec, 0); mp->mnt_optnew = mp->mnt_opt; mp->mnt_opt = NULL; /* * Set the mount level flags. */ mp->mnt_flag = fsflags & MNT_UPDATEMASK; /* * Snapshots are always read-only. */ mp->mnt_flag |= MNT_RDONLY; /* * We don't want snapshots to allow access to vulnerable setuid * programs, so we turn off setuid when mounting snapshots. */ mp->mnt_flag |= MNT_NOSUID; /* * We don't want snapshots to be visible in regular * mount(8) and df(1) output. */ mp->mnt_flag |= MNT_IGNORE; /* * XXX: This is evil, but we can't mount a snapshot as a regular user. * XXX: Is is safe when snapshot is mounted from within a jail? */ cr = td->td_ucred; td->td_ucred = kcred; error = VFS_MOUNT(mp); td->td_ucred = cr; if (error != 0) { VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; VI_UNLOCK(vp); vrele(vp); vfs_unbusy(mp); vfs_mount_destroy(mp); *vpp = NULL; return (error); } if (mp->mnt_opt != NULL) vfs_freeopts(mp->mnt_opt); mp->mnt_opt = mp->mnt_optnew; (void)VFS_STATFS(mp, &mp->mnt_stat); /* * Prevent external consumers of mount options from reading * mnt_optnew. */ mp->mnt_optnew = NULL; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef FREEBSD_NAMECACHE cache_purge(vp); #endif VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; VI_UNLOCK(vp); vp->v_mountedhere = mp; /* Put the new filesystem on the mount list. */ mtx_lock(&mountlist_mtx); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); vfs_event_signal(NULL, VQ_MOUNT, 0); if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp)) panic("mount: lost mount"); vput(vp); vfs_unbusy(mp); *vpp = mvp; return (0); }
/* * smbfs mount vfsop * Set up mount info record and attach it to vfs struct. */ static int smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { char *data = uap->dataptr; int error; smbnode_t *rtnp = NULL; /* root of this fs */ smbmntinfo_t *smi = NULL; dev_t smbfs_dev; int version; int devfd; zone_t *zone = curproc->p_zone; zone_t *mntzone = NULL; smb_share_t *ssp = NULL; smb_cred_t scred; int flags, sec; STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) return (error); if (mvp->v_type != VDIR) return (ENOTDIR); /* * get arguments * * uap->datalen might be different from sizeof (args) * in a compatible situation. */ STRUCT_INIT(args, get_udatamodel()); bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)); if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen, SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)))) return (EFAULT); /* * Check mount program version */ version = STRUCT_FGET(args, version); if (version != SMBFS_VERSION) { cmn_err(CE_WARN, "mount version mismatch:" " kernel=%d, mount=%d\n", SMBFS_VERSION, version); return (EINVAL); } /* * Deal with re-mount requests. */ if (uap->flags & MS_REMOUNT) { cmn_err(CE_WARN, "MS_REMOUNT not implemented"); return (ENOTSUP); } /* * Check for busy */ mutex_enter(&mvp->v_lock); if (!(uap->flags & MS_OVERLAY) && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * Get the "share" from the netsmb driver (ssp). * It is returned with a "ref" (hold) for us. * Release this hold: at errout below, or in * smbfs_freevfs(). */ devfd = STRUCT_FGET(args, devfd); error = smb_dev2share(devfd, &ssp); if (error) { cmn_err(CE_WARN, "invalid device handle %d (%d)\n", devfd, error); return (error); } /* * Use "goto errout" from here on. * See: ssp, smi, rtnp, mntzone */ /* * Determine the zone we're being mounted into. */ zone_hold(mntzone = zone); /* start with this assumption */ if (getzoneid() == GLOBAL_ZONEID) { zone_rele(mntzone); mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); ASSERT(mntzone != NULL); if (mntzone != zone) { error = EBUSY; goto errout; } } /* * Stop the mount from going any further if the zone is going away. */ if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) { error = EBUSY; goto errout; } /* * On a Trusted Extensions client, we may have to force read-only * for read-down mounts. */ if (is_system_labeled()) { void *addr; int ipvers = 0; struct smb_vc *vcp; vcp = SSTOVC(ssp); addr = smb_vc_getipaddr(vcp, &ipvers); error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr); if (error > 0) goto errout; if (error == -1) { /* change mount to read-only to prevent write-down */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } } /* Prevent unload. */ atomic_inc_32(&smbfs_mountcount); /* * Create a mount record and link it to the vfs struct. * No more possiblities for errors from here on. * Tear-down of this stuff is in smbfs_free_smi() * * Compare with NFS: nfsrootvp() */ smi = kmem_zalloc(sizeof (*smi), KM_SLEEP); mutex_init(&smi->smi_lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&smi->smi_statvfs_cv, NULL, CV_DEFAULT, NULL); rw_init(&smi->smi_hash_lk, NULL, RW_DEFAULT, NULL); smbfs_init_hash_avl(&smi->smi_hash_avl); smi->smi_share = ssp; ssp = NULL; /* * Convert the anonymous zone hold acquired via zone_hold() above * into a zone reference. */ zone_init_ref(&smi->smi_zone_ref); zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS); zone_rele(mntzone); mntzone = NULL; /* * Initialize option defaults */ smi->smi_flags = SMI_LLOCK; smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN); smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX); smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN); smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX); /* * All "generic" mount options have already been * handled in vfs.c:domount() - see mntopts stuff. * Query generic options using vfs_optionisset(). */ if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL)) smi->smi_flags |= SMI_INT; if (vfs_optionisset(vfsp, MNTOPT_ACL, NULL)) smi->smi_flags |= SMI_ACL; /* * Get the mount options that come in as smbfs_args, * starting with args.flags (SMBFS_MF_xxx) */ flags = STRUCT_FGET(args, flags); smi->smi_uid = STRUCT_FGET(args, uid); smi->smi_gid = STRUCT_FGET(args, gid); smi->smi_fmode = STRUCT_FGET(args, file_mode) & 0777; smi->smi_dmode = STRUCT_FGET(args, dir_mode) & 0777; /* * Hande the SMBFS_MF_xxx flags. */ if (flags & SMBFS_MF_NOAC) smi->smi_flags |= SMI_NOAC; if (flags & SMBFS_MF_ACREGMIN) { sec = STRUCT_FGET(args, acregmin); if (sec < 0 || sec > SMBFS_ACMINMAX) sec = SMBFS_ACMINMAX; smi->smi_acregmin = SEC2HR(sec); } if (flags & SMBFS_MF_ACREGMAX) { sec = STRUCT_FGET(args, acregmax); if (sec < 0 || sec > SMBFS_ACMAXMAX) sec = SMBFS_ACMAXMAX; smi->smi_acregmax = SEC2HR(sec); } if (flags & SMBFS_MF_ACDIRMIN) { sec = STRUCT_FGET(args, acdirmin); if (sec < 0 || sec > SMBFS_ACMINMAX) sec = SMBFS_ACMINMAX; smi->smi_acdirmin = SEC2HR(sec); } if (flags & SMBFS_MF_ACDIRMAX) { sec = STRUCT_FGET(args, acdirmax); if (sec < 0 || sec > SMBFS_ACMAXMAX) sec = SMBFS_ACMAXMAX; smi->smi_acdirmax = SEC2HR(sec); } /* * Get attributes of the remote file system, * i.e. ACL support, named streams, etc. */ smb_credinit(&scred, cr); error = smbfs_smb_qfsattr(smi->smi_share, &smi->smi_fsa, &scred); smb_credrele(&scred); if (error) { SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); } /* * We enable XATTR by default (via smbfs_mntopts) * but if the share does not support named streams, * force the NOXATTR option (also clears XATTR). * Caller will set or clear VFS_XATTR after this. */ if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0) vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0); /* * Ditto ACLs (disable if not supported on this share) */ if ((smi->smi_fsattr & FILE_PERSISTENT_ACLS) == 0) { vfs_setmntopt(vfsp, MNTOPT_NOACL, NULL, 0); smi->smi_flags &= ~SMI_ACL; } /* * Assign a unique device id to the mount */ mutex_enter(&smbfs_minor_lock); do { smbfs_minor = (smbfs_minor + 1) & MAXMIN32; smbfs_dev = makedevice(smbfs_major, smbfs_minor); } while (vfs_devismounted(smbfs_dev)); mutex_exit(&smbfs_minor_lock); vfsp->vfs_dev = smbfs_dev; vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp); vfsp->vfs_data = (caddr_t)smi; vfsp->vfs_fstype = smbfsfstyp; vfsp->vfs_bsize = MAXBSIZE; vfsp->vfs_bcount = 0; smi->smi_vfsp = vfsp; smbfs_zonelist_add(smi); /* undo in smbfs_freevfs */ /* * Create the root vnode, which we need in unmount * for the call to smbfs_check_table(), etc. * Release this hold in smbfs_unmount. */ rtnp = smbfs_node_findcreate(smi, "\\", 1, NULL, 0, 0, &smbfs_fattr0); ASSERT(rtnp != NULL); rtnp->r_vnode->v_type = VDIR; rtnp->r_vnode->v_flag |= VROOT; smi->smi_root = rtnp; /* * NFS does other stuff here too: * async worker threads * init kstats * * End of code from NFS nfsrootvp() */ return (0); errout: vfsp->vfs_data = NULL; if (smi != NULL) smbfs_free_smi(smi); if (mntzone != NULL) zone_rele(mntzone); if (ssp != NULL) smb_share_rele(ssp); return (error); }