예제 #1
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);
}
예제 #2
0
파일: zfs_vfsops.c 프로젝트: roddi/mac-zfs
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);
}
예제 #3
0
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);
}
예제 #4
0
/*
 * 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);
}
예제 #5
0
/*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);
}