/* * Create a reference to the root of a mounted file descriptor. * This routine is called from lookupname() in the event a path * is being searched that has a mounted file descriptor in it. */ static int nm_root(vfs_t *vfsp, vnode_t **vpp) { struct namenode *nodep = (struct namenode *)vfsp->vfs_data; struct vnode *vp = NMTOV(nodep); VN_HOLD(vp); *vpp = vp; return (0); }
/* * 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); }
/* * Standard access() like check. Figure out which mode bits apply * to the caller then pass the missing mode bits to the secpolicy function. */ static int nm_access_unlocked(void *vnp, int mode, cred_t *crp) { struct namenode *nodep = vnp; int shift = 0; if (crgetuid(crp) != nodep->nm_vattr.va_uid) { shift += 3; if (!groupmember(nodep->nm_vattr.va_gid, crp)) shift += 3; } return (secpolicy_vnode_access2(crp, NMTOV(nodep), nodep->nm_vattr.va_uid, nodep->nm_vattr.va_mode << shift, mode)); }
/* * 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); }
/* * Force the unmouting of a file descriptor from ALL of the nodes * that it was mounted to. * At the present time, the only usage for this routine is in the * event one end of a pipe was mounted. At the time the unmounted * end gets closed down, the mounted end is forced to be unmounted. * * This routine searches the namenode hash list for all namenodes * that have a nm_filevp field equal to vp. Each time one is found, * the dounmount() routine is called. This causes the nm_unmount() * routine to be called and thus, the file descriptor is unmounted * from the node. * * At the start of this routine, the reference count for vp is * incremented to protect the vnode from being released in the * event the mount was the only thing keeping the vnode active. * If that is the case, the VOP_CLOSE operation is applied to * the vnode, prior to it being released. */ static int nm_umountall(vnode_t *vp, cred_t *crp) { vfs_t *vfsp; struct namenode *nodep; int error = 0; int realerr = 0; /* * For each namenode that is associated with the file: * If the v_vfsp field is not namevfs, dounmount it. Otherwise, * it was created in nm_open() and will be released in time. * The following loop replicates some code from nm_find. That * routine can't be used as is since the list isn't strictly * consumed as it is traversed. */ mutex_enter(&ntable_lock); nodep = *NM_FILEVP_HASH(vp); while (nodep) { if (nodep->nm_filevp == vp && (vfsp = NMTOV(nodep)->v_vfsp) != NULL && vfsp != &namevfs && (NMTOV(nodep)->v_flag & VROOT)) { /* * If the vn_vfswlock fails, skip the vfs since * somebody else may be unmounting it. */ if (vn_vfswlock(vfsp->vfs_vnodecovered)) { realerr = EBUSY; nodep = nodep->nm_nextp; continue; } /* * Can't hold ntable_lock across call to do_unmount * because nm_unmount tries to acquire it. This means * there is a window where another mount of vp can * happen so it is possible that after nm_unmountall * there are still some mounts. This situation existed * without MT locking because dounmount can sleep * so another mount could happen during that time. * This situation is unlikely and doesn't really cause * any problems. */ mutex_exit(&ntable_lock); if ((error = dounmount(vfsp, 0, crp)) != 0) realerr = error; mutex_enter(&ntable_lock); /* * Since we dropped the ntable_lock, we * have to start over from the beginning. * If for some reasons dounmount() fails, * start from beginning means that we will keep on * trying unless another thread unmounts it for us. */ nodep = *NM_FILEVP_HASH(vp); } else nodep = nodep->nm_nextp; } mutex_exit(&ntable_lock); return (realerr); }
/* * Create a reference to the vnode representing the file descriptor. * Then, apply the VOP_OPEN operation to that vnode. * * The vnode for the file descriptor may be switched under you. * If it is, search the hash list for an nodep - nodep->nm_filevp * pair. If it exists, return that nodep to the user. * If it does not exist, create a new namenode to attach * to the nodep->nm_filevp then place the pair on the hash list. * * Newly created objects are like children/nodes in the mounted * file system, with the parent being the initial mount. */ int nm_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct) { struct namenode *nodep = VTONM(*vpp); int error = 0; struct namenode *newnamep; struct vnode *newvp; struct vnode *infilevp; struct vnode *outfilevp; /* * If the vnode is switched under us, the corresponding * VN_RELE for this VN_HOLD will be done by the file system * performing the switch. Otherwise, the corresponding * VN_RELE will be done by nm_close(). */ infilevp = outfilevp = nodep->nm_filevp; VN_HOLD(outfilevp); if ((error = VOP_OPEN(&outfilevp, flag, crp, ct)) != 0) { VN_RELE(outfilevp); return (error); } if (infilevp != outfilevp) { /* * See if the new filevp (outfilevp) is already associated * with the mount point. If it is, then it already has a * namenode associated with it. */ mutex_enter(&ntable_lock); if ((newnamep = namefind(outfilevp, nodep->nm_mountpt)) != NULL) { struct vnode *vp = NMTOV(newnamep); VN_HOLD(vp); goto gotit; } newnamep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP); newvp = vn_alloc(KM_SLEEP); newnamep->nm_vnode = newvp; mutex_init(&newnamep->nm_lock, NULL, MUTEX_DEFAULT, NULL); mutex_enter(&nodep->nm_lock); newvp->v_flag = ((*vpp)->v_flag | VNOMAP | VNOSWAP) & ~VROOT; vn_setops(newvp, vn_getops(*vpp)); newvp->v_vfsp = &namevfs; newvp->v_stream = outfilevp->v_stream; newvp->v_type = outfilevp->v_type; newvp->v_rdev = outfilevp->v_rdev; newvp->v_data = (caddr_t)newnamep; vn_exists(newvp); bcopy(&nodep->nm_vattr, &newnamep->nm_vattr, sizeof (vattr_t)); newnamep->nm_vattr.va_type = outfilevp->v_type; newnamep->nm_vattr.va_nodeid = namenodeno_alloc(); newnamep->nm_vattr.va_size = (u_offset_t)0; newnamep->nm_vattr.va_rdev = outfilevp->v_rdev; newnamep->nm_flag = NMNMNT; newnamep->nm_filevp = outfilevp; newnamep->nm_filep = nodep->nm_filep; newnamep->nm_mountpt = nodep->nm_mountpt; mutex_exit(&nodep->nm_lock); /* * Insert the new namenode into the hash list. */ nameinsert(newnamep); gotit: mutex_exit(&ntable_lock); /* * Release the above reference to the infilevp, the reference * to the NAMEFS vnode, create a reference to the new vnode * and return the new vnode to the user. */ VN_RELE(*vpp); *vpp = NMTOV(newnamep); } return (0); }