/* * Unmount a file descriptor from a node in the file system. * If the user is not the owner of the file and is not privileged, * the request is denied. * Otherwise, remove the namenode from the hash list. * If the mounted file descriptor was that of a stream and this * was the last mount of the stream, turn off the STRMOUNT flag. * If the rootvp is referenced other than through the mount, * nm_inactive will clean up. */ static int nm_unmount(vfs_t *vfsp, int flag, cred_t *crp) { struct namenode *nodep = (struct namenode *)vfsp->vfs_data; vnode_t *vp, *thisvp; struct file *fp = NULL; ASSERT((nodep->nm_flag & NMNMNT) == 0); /* * forced unmount is not supported by this file system * and thus, ENOTSUP, is being returned. */ if (flag & MS_FORCE) { return (ENOTSUP); } vp = nodep->nm_filevp; mutex_enter(&nodep->nm_lock); if (secpolicy_vnode_owner(crp, nodep->nm_vattr.va_uid) != 0) { mutex_exit(&nodep->nm_lock); return (EPERM); } mutex_exit(&nodep->nm_lock); mutex_enter(&ntable_lock); nameremove(nodep); thisvp = NMTOV(nodep); mutex_enter(&thisvp->v_lock); if (thisvp->v_count-- == 1) { fp = nodep->nm_filep; mutex_exit(&thisvp->v_lock); vn_invalid(thisvp); vn_free(thisvp); VFS_RELE(vfsp); namenodeno_free(nodep->nm_vattr.va_nodeid); kmem_free(nodep, sizeof (struct namenode)); } else { thisvp->v_flag &= ~VROOT; mutex_exit(&thisvp->v_lock); } if (namefind(vp, NULLVP) == NULL && vp->v_stream) { struct stdata *stp = vp->v_stream; mutex_enter(&stp->sd_lock); stp->sd_flag &= ~STRMOUNT; mutex_exit(&stp->sd_lock); } mutex_exit(&ntable_lock); if (fp != NULL) (void) closef(fp); return (0); }
static int zfs_vfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context) { char *osname = NULL; size_t osnamelen = 0; int error = 0; int canwrite; /* * Get the objset name (the "special" mount argument). * The filesystem that we mount as root is defined in the * "zfs-bootfs" property. */ if (data) { user_addr_t fspec = USER_ADDR_NULL; #ifndef __APPLE__ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bootpath) != DDI_SUCCESS) return (EIO); error = parse_bootpath(zfs_bootpath, rootfs.bo_name); ddi_prop_free(zfs_bootpath); #endif osname = kmem_alloc(MAXPATHLEN, KM_SLEEP); if (vfs_context_is64bit(context)) { if ( (error = copyin(data, (caddr_t)&fspec, sizeof(fspec))) ) goto out; } else { #ifdef ZFS_LEOPARD_ONLY char *tmp; #else user32_addr_t tmp; #endif if ( (error = copyin(data, (caddr_t)&tmp, sizeof(tmp))) ) goto out; /* munge into LP64 addr */ fspec = CAST_USER_ADDR_T(tmp); } if ( (error = copyinstr(fspec, osname, MAXPATHLEN, &osnamelen)) ) goto out; } #if 0 if (mvp->v_type != VDIR) return (ENOTDIR); mutex_enter(&mvp->v_lock); if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * ZFS does not support passing unparsed data in via MS_DATA. * Users should use the MS_OPTIONSTR interface; this means * that all option parsing is already done and the options struct * can be interrogated. */ if ((uap->flags & MS_DATA) && uap->datalen > 0) return (EINVAL); /* * Get the objset name (the "special" mount argument). */ if (error = pn_get(uap->spec, fromspace, &spn)) return (error); osname = spn.pn_path; #endif /* * Check for mount privilege? * * If we don't have privilege then see if * we have local permission to allow it */ #ifndef __APPLE__ error = secpolicy_fs_mount(cr, mvp, vfsp); if (error) { error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr); if (error == 0) { vattr_t vattr; /* * Make sure user is the owner of the mount point * or has sufficient privileges. */ vattr.va_mask = AT_UID; if (error = VOP_GETATTR(mvp, &vattr, 0, cr)) { goto out; } if (error = secpolicy_vnode_owner(cr, vattr.va_uid)) { goto out; } if (error = VOP_ACCESS(mvp, VWRITE, 0, cr)) { goto out; } secpolicy_fs_mount_clearopts(cr, vfsp); } else { goto out; } } #endif error = zfs_domount(mp, 0, osname, context); if (error) printf("zfs_vfs_mount: error %d\n", error); if (error == 0) { zfsvfs_t *zfsvfs = NULL; /* Make the Finder treat sub file systems just like a folder */ if (strpbrk(osname, "/")) vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_DONTBROWSE)); /* Indicate to VFS that we support ACLs. */ vfs_setextendedsecurity(mp); /* Advisory locking should be handled at the VFS layer */ vfs_setlocklocal(mp); /* * Mac OS X needs a file system modify time * * We use the mtime of the "com.apple.system.mtime" * extended attribute, which is associated with the * file system root directory. * * Here we need to take a ref on z_mtime_vp to keep it around. * If the attribute isn't there, attempt to create it. */ zfsvfs = vfs_fsprivate(mp); if (zfsvfs->z_mtime_vp == NULL) { struct vnode * rvp; struct vnode *xdvp = NULLVP; struct vnode *xvp = NULLVP; znode_t *rootzp; timestruc_t modify_time; cred_t *cr; timestruc_t now; int flag; int result; if (zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp) != 0) { goto out; } rvp = ZTOV(rootzp); cr = (cred_t *)vfs_context_ucred(context); /* Grab the hidden attribute directory vnode. */ result = zfs_get_xattrdir(rootzp, &xdvp, cr, CREATE_XATTR_DIR); vnode_put(rvp); /* all done with root vnode */ rvp = NULL; if (result) { goto out; } /* * HACK - workaround missing vnode_setnoflush() KPI... * * We tag zfsvfs so that zfs_attach_vnode() can then set * vnfs_marksystem when the vnode gets created. */ zfsvfs->z_last_unmount_time = 0xBADC0DE; zfsvfs->z_last_mtime_synced = VTOZ(xdvp)->z_id; flag = vfs_isrdonly(mp) ? 0 : ZEXISTS; /* Lookup or create the named attribute. */ if ( zfs_obtain_xattr(VTOZ(xdvp), ZFS_MTIME_XATTR, S_IRUSR | S_IWUSR, cr, &xvp, flag) ) { zfsvfs->z_last_unmount_time = 0; zfsvfs->z_last_mtime_synced = 0; vnode_put(xdvp); goto out; } gethrestime(&now); ZFS_TIME_ENCODE(&now, VTOZ(xvp)->z_phys->zp_mtime); vnode_put(xdvp); vnode_ref(xvp); zfsvfs->z_mtime_vp = xvp; ZFS_TIME_DECODE(&modify_time, VTOZ(xvp)->z_phys->zp_mtime); zfsvfs->z_last_unmount_time = modify_time.tv_sec; zfsvfs->z_last_mtime_synced = modify_time.tv_sec; /* * Keep this referenced vnode from impeding an unmount. * * XXX vnode_setnoflush() is MIA from KPI (see workaround above). */ #if 0 vnode_setnoflush(xvp); #endif vnode_put(xvp); } } out: if (osname) { kmem_free(osname, MAXPATHLEN); } return (error); }
int /* ERRNO if error, 0 if successful. */ sam_setattr_ino( sam_node_t *ip, /* pointer to inode. */ vattr_t *vap, /* vattr pointer. */ int flags, /* flags. */ cred_t *credp) /* credentials pointer. */ { uint_t mask; int error = 0; vnode_t *vp; sam_mode_t oldmode, mode; timespec_t system_time; vattr_t oldva; oldva.va_mode = ip->di.mode; oldva.va_uid = ip->di.uid; oldva.va_gid = ip->di.gid; vp = SAM_ITOV(ip); if (vap->va_mask & AT_NOSET) { return (EINVAL); } mode = vap->va_mode & ~S_IFMT; SAM_HRESTIME(&system_time); /* * Enforce the "read only" portion of WORM files. */ if (ip->di.status.b.worm_rdonly && !S_ISDIR(ip->di.mode)) { error = sam_chk_worm(mode, vap->va_mask, ip); if (error) { return (error); } } /* * Generic setattr security policy check. */ if (error = secpolicy_vnode_setattr(credp, vp, vap, &oldva, flags, sam_access_ino_ul, ip)) { return (error); } mask = vap->va_mask; if (mask & AT_SIZE) { /* -----Change size */ if (error == 0) { /* Can only truncate a regular file */ if (S_ISREQ(ip->di.mode)) { error = EINVAL; goto out; } else if (SAM_PRIVILEGE_INO(ip->di.version, ip->di.id.ino)) { error = EPERM; /* Can't trunc priv'ed inodes */ goto out; } if (S_ISSEGI(&ip->di) && (vap->va_size != 0)) { /* * If file is segment access and not truncating * to zero--fix. */ error = EINVAL; goto out; } /* * Might need to do TRANS_ITRUNC here for LQFS.... */ if ((error = sam_clear_ino(ip, (offset_t)vap->va_size, STALE_ARCHIVE, credp))) { goto out; } } } if (mask & AT_MODE) { /* -----Change mode */ /* Cannot change .inodes file */ if (ip->di.id.ino == SAM_INO_INO) { error = EPERM; goto out; } oldmode = ip->di.mode; ip->di.mode &= S_IFMT; ip->di.mode |= vap->va_mode & ~S_IFMT; if (ip->di.status.b.worm_rdonly) { if (!S_ISDIR(ip->di.mode)) { ip->di.mode &= ~WMASK; } if (oldmode & S_ISUID) { ip->di.mode |= S_ISUID; } } /* * In 4.6 there are two modes of WORM trigger operation. * One is compatible with the 53xx SUN NAS series. This * mode uses the SUID bit by itself. The second mode is * called compatibility mode. This mode uses the transition * from a writeable mode as the trigger. Note, copying a * read-only file to a WORM capable volume does *NOT* * initiate the WORM trigger in this mode. */ if (samgt.license.license.lic_u.b.WORM_fs && (ip->di.version >= SAM_INODE_VERS_2) && (((vap->va_mode == S_ISUID) && (ip->mp->mt.fi_config & MT_ALLWORM)) || ((ip->mp->mt.fi_config & MT_ALLEMUL) && (((oldmode & RWXALLMASK) == RWXALLMASK) || ((vap->va_mode != S_ISUID) && (oldmode & WMASK) && !(ip->di.mode & WMASK)))))) { error = sam_worm_trigger(ip, oldmode, system_time); if (error) { ip->di.mode = oldmode; goto out; } else if ((ip->mp->mt.fi_config & MT_ALLEMUL) && ((oldmode & RWXALLMASK) == RWXALLMASK)) { ip->di.mode = oldmode; } else if ((vap->va_mode == S_ISUID) && (!S_ISDIR(ip->di.mode))) { if (ip->mp->mt.fi_config & MT_ALLEMUL) { ip->di.mode = oldmode & (S_IFMT | RMASK); } else { ip->di.mode = S_ISUID | (oldmode & (S_IFMT | RMASK)); } } } TRANS_INODE(ip->mp, ip); if (S_ISATTRDIR(oldmode)) { ip->di.mode |= S_IFATTRDIR; } sam_mark_ino(ip, SAM_CHANGED); } if (mask & (AT_UID | AT_GID)) { /* -----Change uid/gid */ int ouid, ogid; if (vap->va_mask & AT_MODE) { ip->di.mode = (ip->di.mode & S_IFMT) | (vap->va_mode & ~S_IFMT); } /* * To change file ownership, a process must have * privilege if: * * If it is not the owner of the file, or * if doing restricted chown semantics and * either changing the ownership to someone else or * changing the group to a group that we are not * currently in. */ if (crgetuid(credp) != ip->di.uid || (rstchown && (((mask & AT_UID) && vap->va_uid != ip->di.uid) || ((mask & AT_GID) && !groupmember(vap->va_gid, credp))))) { error = secpolicy_vnode_owner(credp, vap->va_uid); if (error) { goto out; } } ouid = ip->di.uid; ogid = ip->di.gid; if (error = sam_quota_chown(ip->mp, ip, (mask&AT_UID) ? vap->va_uid : ouid, (mask&AT_GID) ? vap->va_gid : ogid, credp)) { goto out; } if (mask & AT_UID) ip->di.uid = vap->va_uid; if (mask & AT_GID) ip->di.gid = vap->va_gid; ip->di.status.b.archdone = 0; TRANS_INODE(ip->mp, ip); sam_mark_ino(ip, SAM_CHANGED); /* * Notify arfind and event daemon of setattr. */ sam_send_to_arfind(ip, AE_change, 0); if (ip->mp->ms.m_fsev_buf) { sam_send_event(ip->mp, &ip->di, ev_change, 0, 0, ip->di.change_time.tv_sec); } } if (mask & (AT_ATIME | AT_MTIME)) { /* -----Modify times */ /* * Synchronously flush pages so dates do not get changed after * utime. If staging, finish stage and then flush pages. */ if (ip->flags.b.staging) { /* * Might need to do TRANS_ITRUNC or similar here * for LQFS */ if ((error = sam_clear_file(ip, ip->di.rm.size, MAKE_ONLINE, credp))) { goto out; } } sam_flush_pages(ip, 0); if (mask & AT_ATIME) { /* * The access time field is used by WORM operations to * store the retention timestamp. This is intercepted * here and stored in the inode's retention period * time fields if either the field hasn't been set or * the provided value exceeds what is currently there * (i.e. we're extending the period). */ error = sam_check_worm_capable(ip, TRUE); if (!error) { boolean_t lite_mode = ((ip->mp->mt.fi_config & MT_LITE_WORM) != 0); boolean_t is_priv = (secpolicy_fs_config(credp, ip->mp->mi.m_vfsp) == 0); if (S_ISREG(ip->di.mode) && !WORM(ip)) { /* * Regular file in WORM capable * directory. Set access time per the * request. */ ip->di.access_time.tv_sec = vap->va_atime.tv_sec; ip->di.access_time.tv_nsec = vap->va_atime.tv_nsec; ip->flags.b.accessed = 1; } else if (WORM(ip) && (ip->di.version >= SAM_INODE_VERS_2)) { boolean_t extend_period; /* * Extend the retention period if so * requested. If lite mode and a * privileged user or a directory allow * the retention period to be shortened. */ if (vap->va_atime.tv_sec > ip->di2.rperiod_start_time + ip->di2.rperiod_duration * 60) { extend_period = 1; } else { extend_period = 0; } if (S_ISREG(ip->di.mode) && extend_period) { error = sam_set_rperiod(ip, vap, is_priv && lite_mode); } else if (S_ISDIR(ip->di.mode) || (S_ISREG(ip->di.mode) && is_priv && lite_mode)) { /* * If the requested time would * result in a non- negative * retention period, set the * period to the difference of * the request and current time. * A negative retention period * is not allowed. */ if (vap->va_atime.tv_sec > system_time.tv_sec) { if (vap->va_atime.tv_sec == INT_MAX) { ip->di2.rperiod_duration = 0; } else { ip->di2.rperiod_duration = 1 + (vap->va_atime.tv_sec - system_time.tv_sec)/60; } } else { error = EINVAL; } } if (error) { goto out; } } else { /* * Shouldn't get here, invalid request. */ error = EINVAL; goto out; } TRANS_INODE(ip->mp, ip); sam_mark_ino(ip, SAM_CHANGED); } else { error = 0; ip->di.access_time.tv_sec = vap->va_atime.tv_sec; ip->di.access_time.tv_nsec = vap->va_atime.tv_nsec; ip->flags.b.accessed = 1; TRANS_INODE(ip->mp, ip); } } if (mask & AT_MTIME) { if (!ip->di.status.b.worm_rdonly) { ip->di.modify_time.tv_sec = vap->va_mtime.tv_sec; ip->di.modify_time.tv_nsec = vap->va_mtime.tv_nsec; ip->di.change_time.tv_sec = system_time.tv_sec; ip->di.change_time.tv_nsec = system_time.tv_nsec; ip->flags.b.updated = 1; /* Modify time has been set */ ip->flags.b.dirty = 1; } TRANS_INODE(ip->mp, ip); } } /* * Check for and apply ACL info, if present. */ if (ip->di.status.b.acl && !ip->di.status.b.worm_rdonly) { if (SAM_IS_SHARED_FS(ip->mp) && SAM_IS_SHARED_SERVER(ip->mp)) { RW_UNLOCK_OS(&ip->inode_rwl, RW_WRITER); sam_callout_acl(ip, ip->mp->ms.m_client_ord); RW_LOCK_OS(&ip->inode_rwl, RW_WRITER); } if (error = sam_acl_setattr(ip, vap)) { goto out; } } if (ip->mp->mt.fi_config & MT_SHARED_WRITER) { if ((error == 0) && (ip->flags.bits & (SAM_ACCESSED|SAM_UPDATED|SAM_CHANGED))) { (void) sam_update_inode(ip, SAM_SYNC_ONE, FALSE); } } out: return (error); }
/* * Mount a file descriptor onto the node in the file system. * Create a new vnode, update the attributes with info from the * file descriptor and the mount point. The mask, mode, uid, gid, * atime, mtime and ctime are taken from the mountpt. Link count is * set to one, the file system id is namedev and nodeid is unique * for each mounted object. Other attributes are taken from mount point. * Make sure user is owner (or root) with write permissions on mount point. * Hash the new vnode and return 0. * Upon entry to this routine, the file descriptor is in the * fd field of a struct namefd. Copy that structure from user * space and retrieve the file descriptor. */ static int nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp) { struct namefd namefdp; struct vnode *filevp; /* file descriptor vnode */ struct file *fp; struct vnode *newvp; /* vnode representing this mount */ struct vnode *rvp; /* realvp (if any) for the mountpt */ struct namenode *nodep; /* namenode for this mount */ struct vattr filevattr; /* attributes of file dec. */ struct vattr *vattrp; /* attributes of this mount */ char *resource_name; char *resource_nodetype; statvfs64_t *svfsp; int error = 0; /* * Get the file descriptor from user space. * Make sure the file descriptor is valid and has an * associated file pointer. * If so, extract the vnode from the file pointer. */ if (uap->datalen != sizeof (struct namefd)) return (EINVAL); if (copyin(uap->dataptr, &namefdp, uap->datalen)) return (EFAULT); if ((fp = getf(namefdp.fd)) == NULL) return (EBADF); /* * If the mount point already has something mounted * on it, disallow this mount. (This restriction may * be removed in a later release). * Or unmount has completed but the namefs ROOT vnode * count has not decremented to zero, disallow this mount. */ mutex_enter(&mvp->v_lock); if ((mvp->v_flag & VROOT) || vfs_matchops(mvp->v_vfsp, namefs_vfsops)) { mutex_exit(&mvp->v_lock); releasef(namefdp.fd); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * Cannot allow users to fattach() in /dev/pts. * First, there is no need for doing so and secondly * we cannot allow arbitrary users to park on a node in * /dev/pts or /dev/vt. */ rvp = NULLVP; if (vn_matchops(mvp, spec_getvnodeops()) && VOP_REALVP(mvp, &rvp, NULL) == 0 && rvp && (vn_matchops(rvp, devpts_getvnodeops()) || vn_matchops(rvp, devvt_getvnodeops()))) { releasef(namefdp.fd); return (ENOTSUP); } filevp = fp->f_vnode; if (filevp->v_type == VDIR || filevp->v_type == VPORT) { releasef(namefdp.fd); return (EINVAL); } /* * If the fd being mounted refers to neither a door nor a stream, * make sure the caller is privileged. */ if (filevp->v_type != VDOOR && filevp->v_stream == NULL) { if (secpolicy_fs_mount(crp, filevp, vfsp) != 0) { /* fd is neither a stream nor a door */ releasef(namefdp.fd); return (EINVAL); } } /* * Make sure the file descriptor is not the root of some * file system. * If it's not, create a reference and allocate a namenode * to represent this mount request. */ if (filevp->v_flag & VROOT) { releasef(namefdp.fd); return (EBUSY); } nodep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP); mutex_init(&nodep->nm_lock, NULL, MUTEX_DEFAULT, NULL); vattrp = &nodep->nm_vattr; vattrp->va_mask = AT_ALL; if (error = VOP_GETATTR(mvp, vattrp, 0, crp, NULL)) goto out; filevattr.va_mask = AT_ALL; if (error = VOP_GETATTR(filevp, &filevattr, 0, crp, NULL)) goto out; /* * Make sure the user is the owner of the mount point * or has sufficient privileges. */ if (error = secpolicy_vnode_owner(crp, vattrp->va_uid)) goto out; /* * Make sure the user has write permissions on the * mount point (or has sufficient privileges). */ if (!(vattrp->va_mode & VWRITE) && secpolicy_vnode_access(crp, mvp, vattrp->va_uid, VWRITE) != 0) { error = EACCES; goto out; } /* * If the file descriptor has file/record locking, don't * allow the mount to succeed. */ if (vn_has_flocks(filevp)) { error = EACCES; goto out; } /* * Initialize the namenode. */ if (filevp->v_stream) { struct stdata *stp = filevp->v_stream; mutex_enter(&stp->sd_lock); stp->sd_flag |= STRMOUNT; mutex_exit(&stp->sd_lock); } nodep->nm_filevp = filevp; mutex_enter(&fp->f_tlock); fp->f_count++; mutex_exit(&fp->f_tlock); releasef(namefdp.fd); nodep->nm_filep = fp; nodep->nm_mountpt = mvp; /* * The attributes for the mounted file descriptor were initialized * above by applying VOP_GETATTR to the mount point. Some of * the fields of the attributes structure will be overwritten * by the attributes from the file descriptor. */ vattrp->va_type = filevattr.va_type; vattrp->va_fsid = namedev; vattrp->va_nodeid = namenodeno_alloc(); vattrp->va_nlink = 1; vattrp->va_size = filevattr.va_size; vattrp->va_rdev = filevattr.va_rdev; vattrp->va_blksize = filevattr.va_blksize; vattrp->va_nblocks = filevattr.va_nblocks; vattrp->va_seq = 0; /* * Initialize new vnode structure for the mounted file descriptor. */ nodep->nm_vnode = vn_alloc(KM_SLEEP); newvp = NMTOV(nodep); newvp->v_flag = filevp->v_flag | VROOT | VNOMAP | VNOSWAP; vn_setops(newvp, nm_vnodeops); newvp->v_vfsp = vfsp; newvp->v_stream = filevp->v_stream; newvp->v_type = filevp->v_type; newvp->v_rdev = filevp->v_rdev; newvp->v_data = (caddr_t)nodep; VFS_HOLD(vfsp); vn_exists(newvp); /* * Initialize the vfs structure. */ vfsp->vfs_vnodecovered = NULL; vfsp->vfs_flag |= VFS_UNLINKABLE; vfsp->vfs_bsize = 1024; vfsp->vfs_fstype = namefstype; vfs_make_fsid(&vfsp->vfs_fsid, namedev, namefstype); vfsp->vfs_data = (caddr_t)nodep; vfsp->vfs_dev = namedev; vfsp->vfs_bcount = 0; /* * Set the name we mounted from. */ switch (filevp->v_type) { case VPROC: /* VOP_GETATTR() translates this to VREG */ case VREG: resource_nodetype = "file"; break; case VDIR: resource_nodetype = "directory"; break; case VBLK: resource_nodetype = "device"; break; case VCHR: resource_nodetype = "device"; break; case VLNK: resource_nodetype = "link"; break; case VFIFO: resource_nodetype = "fifo"; break; case VDOOR: resource_nodetype = "door"; break; case VSOCK: resource_nodetype = "socket"; break; default: resource_nodetype = "resource"; break; } #define RESOURCE_NAME_SZ 128 /* Maximum length of the resource name */ resource_name = kmem_alloc(RESOURCE_NAME_SZ, KM_SLEEP); svfsp = kmem_alloc(sizeof (statvfs64_t), KM_SLEEP); error = VFS_STATVFS(filevp->v_vfsp, svfsp); if (error == 0) { (void) snprintf(resource_name, RESOURCE_NAME_SZ, "unspecified_%s_%s", svfsp->f_basetype, resource_nodetype); } else { (void) snprintf(resource_name, RESOURCE_NAME_SZ, "unspecified_%s", resource_nodetype); } vfs_setresource(vfsp, resource_name); kmem_free(svfsp, sizeof (statvfs64_t)); kmem_free(resource_name, RESOURCE_NAME_SZ); #undef RESOURCE_NAME_SZ /* * Insert the namenode. */ mutex_enter(&ntable_lock); nameinsert(nodep); mutex_exit(&ntable_lock); return (0); out: releasef(namefdp.fd); kmem_free(nodep, sizeof (struct namenode)); return (error); }
/*ARGSUSED*/ static int zfs_mount(vfs_t *vfsp) { kthread_t *td = curthread; vnode_t *mvp = vfsp->mnt_vnodecovered; cred_t *cr = td->td_ucred; char *osname; int error = 0; int canwrite; if (vfs_getopt(vfsp->mnt_optnew, "from", (void **)&osname, NULL)) return (EINVAL); /* * If full-owner-access is enabled and delegated administration is * turned on, we must set nosuid. */ if (zfs_super_owner && dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr) != ECANCELED) { secpolicy_fs_mount_clearopts(cr, vfsp); } /* * Check for mount privilege? * * If we don't have privilege then see if * we have local permission to allow it */ error = secpolicy_fs_mount(cr, mvp, vfsp); if (error) { error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr); if (error != 0) goto out; if (!(vfsp->vfs_flag & MS_REMOUNT)) { vattr_t vattr; /* * Make sure user is the owner of the mount point * or has sufficient privileges. */ vattr.va_mask = AT_UID; vn_lock(mvp, LK_SHARED | LK_RETRY); if (error = VOP_GETATTR(mvp, &vattr, cr)) { VOP_UNLOCK(mvp, 0); goto out; } #if 0 /* CHECK THIS! Is probably needed for zfs_suser. */ if (secpolicy_vnode_owner(mvp, cr, vattr.va_uid) != 0 && VOP_ACCESS(mvp, VWRITE, cr, td) != 0) { error = EPERM; goto out; } #else if (error = secpolicy_vnode_owner(mvp, cr, vattr.va_uid)) { VOP_UNLOCK(mvp, 0); goto out; } if (error = VOP_ACCESS(mvp, VWRITE, cr, td)) { VOP_UNLOCK(mvp, 0); goto out; } VOP_UNLOCK(mvp, 0); #endif } secpolicy_fs_mount_clearopts(cr, vfsp); } /* * Refuse to mount a filesystem if we are in a local zone and the * dataset is not visible. */ if (!INGLOBALZONE(curthread) && (!zone_dataset_visible(osname, &canwrite) || !canwrite)) { error = EPERM; goto out; } /* * When doing a remount, we simply refresh our temporary properties * according to those options set in the current VFS options. */ if (vfsp->vfs_flag & MS_REMOUNT) { /* refresh mount options */ zfs_unregister_callbacks(vfsp->vfs_data); error = zfs_register_callbacks(vfsp); goto out; } DROP_GIANT(); error = zfs_domount(vfsp, osname); PICKUP_GIANT(); out: return (error); }