Home | History | Annotate | Download | only in namefs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     22 /*	  All Rights Reserved  	*/
     23 
     24 
     25 /*
     26  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     27  * Use is subject to license terms.
     28  */
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 /*
     33  * This file defines the vnode operations for mounted file descriptors.
     34  * The routines in this file act as a layer between the NAMEFS file
     35  * system and SPECFS/FIFOFS.  With the exception of nm_open(), nm_setattr(),
     36  * nm_getattr() and nm_access(), the routines simply apply the VOP operation
     37  * to the vnode representing the file descriptor.  This switches control
     38  * to the underlying file system to which the file descriptor belongs.
     39  */
     40 #include <sys/types.h>
     41 #include <sys/param.h>
     42 #include <sys/systm.h>
     43 #include <sys/cred.h>
     44 #include <sys/errno.h>
     45 #include <sys/time.h>
     46 #include <sys/file.h>
     47 #include <sys/fcntl.h>
     48 #include <sys/flock.h>
     49 #include <sys/kmem.h>
     50 #include <sys/uio.h>
     51 #include <sys/vfs.h>
     52 #include <sys/vfs_opreg.h>
     53 #include <sys/vnode.h>
     54 #include <sys/pcb.h>
     55 #include <sys/signal.h>
     56 #include <sys/user.h>
     57 #include <sys/proc.h>
     58 #include <sys/conf.h>
     59 #include <sys/debug.h>
     60 #include <vm/seg.h>
     61 #include <sys/fs/namenode.h>
     62 #include <sys/stream.h>
     63 #include <fs/fs_subr.h>
     64 #include <sys/policy.h>
     65 
     66 /*
     67  * Create a reference to the vnode representing the file descriptor.
     68  * Then, apply the VOP_OPEN operation to that vnode.
     69  *
     70  * The vnode for the file descriptor may be switched under you.
     71  * If it is, search the hash list for an nodep - nodep->nm_filevp
     72  * pair. If it exists, return that nodep to the user.
     73  * If it does not exist, create a new namenode to attach
     74  * to the nodep->nm_filevp then place the pair on the hash list.
     75  *
     76  * Newly created objects are like children/nodes in the mounted
     77  * file system, with the parent being the initial mount.
     78  */
     79 int
     80 nm_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct)
     81 {
     82 	struct namenode *nodep = VTONM(*vpp);
     83 	int error = 0;
     84 	struct namenode *newnamep;
     85 	struct vnode *newvp;
     86 	struct vnode *infilevp;
     87 	struct vnode *outfilevp;
     88 
     89 	/*
     90 	 * If the vnode is switched under us, the corresponding
     91 	 * VN_RELE for this VN_HOLD will be done by the file system
     92 	 * performing the switch. Otherwise, the corresponding
     93 	 * VN_RELE will be done by nm_close().
     94 	 */
     95 	infilevp = outfilevp = nodep->nm_filevp;
     96 	VN_HOLD(outfilevp);
     97 
     98 	if ((error = VOP_OPEN(&outfilevp, flag, crp, ct)) != 0) {
     99 		VN_RELE(outfilevp);
    100 		return (error);
    101 	}
    102 	if (infilevp != outfilevp) {
    103 		/*
    104 		 * See if the new filevp (outfilevp) is already associated
    105 		 * with the mount point. If it is, then it already has a
    106 		 * namenode associated with it.
    107 		 */
    108 		mutex_enter(&ntable_lock);
    109 		if ((newnamep =
    110 		    namefind(outfilevp, nodep->nm_mountpt)) != NULL) {
    111 			struct vnode *vp = NMTOV(newnamep);
    112 
    113 			VN_HOLD(vp);
    114 			goto gotit;
    115 		}
    116 
    117 		newnamep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP);
    118 		newvp = vn_alloc(KM_SLEEP);
    119 		newnamep->nm_vnode = newvp;
    120 
    121 		mutex_init(&newnamep->nm_lock, NULL, MUTEX_DEFAULT, NULL);
    122 
    123 		mutex_enter(&nodep->nm_lock);
    124 		newvp->v_flag = ((*vpp)->v_flag | VNOMAP | VNOSWAP) & ~VROOT;
    125 		vn_setops(newvp, vn_getops(*vpp));
    126 		newvp->v_vfsp = &namevfs;
    127 		newvp->v_stream = outfilevp->v_stream;
    128 		newvp->v_type = outfilevp->v_type;
    129 		newvp->v_rdev = outfilevp->v_rdev;
    130 		newvp->v_data = (caddr_t)newnamep;
    131 		vn_exists(newvp);
    132 		bcopy(&nodep->nm_vattr, &newnamep->nm_vattr, sizeof (vattr_t));
    133 		newnamep->nm_vattr.va_type = outfilevp->v_type;
    134 		newnamep->nm_vattr.va_nodeid = namenodeno_alloc();
    135 		newnamep->nm_vattr.va_size = (u_offset_t)0;
    136 		newnamep->nm_vattr.va_rdev = outfilevp->v_rdev;
    137 		newnamep->nm_flag = NMNMNT;
    138 		newnamep->nm_filevp = outfilevp;
    139 		newnamep->nm_filep = nodep->nm_filep;
    140 		newnamep->nm_mountpt = nodep->nm_mountpt;
    141 		mutex_exit(&nodep->nm_lock);
    142 
    143 		/*
    144 		 * Insert the new namenode into the hash list.
    145 		 */
    146 		nameinsert(newnamep);
    147 gotit:
    148 		mutex_exit(&ntable_lock);
    149 		/*
    150 		 * Release the above reference to the infilevp, the reference
    151 		 * to the NAMEFS vnode, create a reference to the new vnode
    152 		 * and return the new vnode to the user.
    153 		 */
    154 		VN_RELE(*vpp);
    155 		*vpp = NMTOV(newnamep);
    156 	}
    157 	return (0);
    158 }
    159 
    160 /*
    161  * Close a mounted file descriptor.
    162  * Remove any locks and apply the VOP_CLOSE operation to the vnode for
    163  * the file descriptor.
    164  */
    165 static int
    166 nm_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp,
    167 	caller_context_t *ct)
    168 {
    169 	struct namenode *nodep = VTONM(vp);
    170 	int error = 0;
    171 
    172 	(void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
    173 	cleanshares(vp, ttoproc(curthread)->p_pid);
    174 	error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp, ct);
    175 	if (count == 1) {
    176 		(void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp, ct);
    177 		/*
    178 		 * Before VN_RELE() we need to remove the vnode from
    179 		 * the hash table.  We should only do so in the  NMNMNT case.
    180 		 * In other cases, nodep->nm_filep keeps a reference
    181 		 * to nm_filevp and the entry in the hash table doesn't
    182 		 * hurt.
    183 		 */
    184 		if ((nodep->nm_flag & NMNMNT) != 0) {
    185 			mutex_enter(&ntable_lock);
    186 			nameremove(nodep);
    187 			mutex_exit(&ntable_lock);
    188 		}
    189 		VN_RELE(nodep->nm_filevp);
    190 	}
    191 	return (error);
    192 }
    193 
    194 static int
    195 nm_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
    196 	caller_context_t *ct)
    197 {
    198 	return (VOP_READ(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
    199 }
    200 
    201 static int
    202 nm_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp,
    203 	caller_context_t *ct)
    204 {
    205 	return (VOP_WRITE(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct));
    206 }
    207 
    208 static int
    209 nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp,
    210 	caller_context_t *ct)
    211 {
    212 	return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp, ct));
    213 }
    214 
    215 /*
    216  * Return in vap the attributes that are stored in the namenode
    217  * structure.  Only the size is taken from the mounted object.
    218  */
    219 /* ARGSUSED */
    220 static int
    221 nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp,
    222 	caller_context_t *ct)
    223 {
    224 	struct namenode *nodep = VTONM(vp);
    225 	struct vattr va;
    226 	int error;
    227 
    228 	mutex_enter(&nodep->nm_lock);
    229 	bcopy(&nodep->nm_vattr, vap, sizeof (vattr_t));
    230 	mutex_exit(&nodep->nm_lock);
    231 
    232 	if ((va.va_mask = vap->va_mask & AT_SIZE) != 0) {
    233 		if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp, ct))
    234 			return (error);
    235 		vap->va_size = va.va_size;
    236 	}
    237 
    238 	return (0);
    239 }
    240 
    241 /*
    242  * Standard access() like check.  Figure out which mode bits apply
    243  * to the caller then pass the missing mode bits to the secpolicy function.
    244  */
    245 static int
    246 nm_access_unlocked(void *vnp, int mode, cred_t *crp)
    247 {
    248 	struct namenode *nodep = vnp;
    249 	int shift = 0;
    250 
    251 	if (crgetuid(crp) != nodep->nm_vattr.va_uid) {
    252 		shift += 3;
    253 		if (!groupmember(nodep->nm_vattr.va_gid, crp))
    254 			shift += 3;
    255 	}
    256 	mode &= ~(nodep->nm_vattr.va_mode << shift);
    257 
    258 	if (mode == 0)
    259 		return (0);
    260 
    261 	return (secpolicy_vnode_access(crp, NMTOV(nodep),
    262 	    nodep->nm_vattr.va_uid, mode));
    263 }
    264 /*
    265  * Set the attributes of the namenode from the attributes in vap.
    266  */
    267 /* ARGSUSED */
    268 static int
    269 nm_setattr(
    270 	vnode_t *vp,
    271 	vattr_t *vap,
    272 	int flags,
    273 	cred_t *crp,
    274 	caller_context_t *ctp)
    275 {
    276 	struct namenode *nodep = VTONM(vp);
    277 	struct vattr *nmvap = &nodep->nm_vattr;
    278 	long mask = vap->va_mask;
    279 	int error = 0;
    280 
    281 	/*
    282 	 * Cannot set these attributes.
    283 	 */
    284 	if (mask & (AT_NOSET|AT_SIZE))
    285 		return (EINVAL);
    286 
    287 	(void) VOP_RWLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
    288 	mutex_enter(&nodep->nm_lock);
    289 
    290 	/*
    291 	 * Change ownership/group/time/access mode of mounted file
    292 	 * descriptor.
    293 	 */
    294 
    295 	error = secpolicy_vnode_setattr(crp, vp, vap, nmvap, flags,
    296 	    nm_access_unlocked, nodep);
    297 	if (error)
    298 		goto out;
    299 
    300 	mask = vap->va_mask;
    301 	/*
    302 	 * If request to change mode, copy new
    303 	 * mode into existing attribute structure.
    304 	 */
    305 	if (mask & AT_MODE)
    306 		nmvap->va_mode = vap->va_mode & ~VSVTX;
    307 
    308 	/*
    309 	 * If request was to change user or group, turn off suid and sgid
    310 	 * bits.
    311 	 * If the system was configured with the "rstchown" option, the
    312 	 * owner is not permitted to give away the file, and can change
    313 	 * the group id only to a group of which he or she is a member.
    314 	 */
    315 	if (mask & AT_UID)
    316 		nmvap->va_uid = vap->va_uid;
    317 	if (mask & AT_GID)
    318 		nmvap->va_gid = vap->va_gid;
    319 	/*
    320 	 * If request is to modify times, make sure user has write
    321 	 * permissions on the file.
    322 	 */
    323 	if (mask & AT_ATIME)
    324 		nmvap->va_atime = vap->va_atime;
    325 	if (mask & AT_MTIME) {
    326 		nmvap->va_mtime = vap->va_mtime;
    327 		gethrestime(&nmvap->va_ctime);
    328 	}
    329 out:
    330 	mutex_exit(&nodep->nm_lock);
    331 	VOP_RWUNLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp);
    332 	return (error);
    333 }
    334 
    335 /*
    336  * Check mode permission on the namenode.  First nm_access_unlocked()
    337  * checks the bits on the name node, then an access check is performed
    338  * on the underlying file.
    339  */
    340 /* ARGSUSED */
    341 static int
    342 nm_access(vnode_t *vp, int mode, int flags, cred_t *crp,
    343 	caller_context_t *ct)
    344 {
    345 	struct namenode *nodep = VTONM(vp);
    346 	int error;
    347 
    348 	mutex_enter(&nodep->nm_lock);
    349 	error = nm_access_unlocked(nodep, mode, crp);
    350 	mutex_exit(&nodep->nm_lock);
    351 	if (error == 0)
    352 		return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp, ct));
    353 	else
    354 		return (error);
    355 }
    356 
    357 /*
    358  * We can get here if a creat or open with O_CREAT is done on a namefs
    359  * mount point, for example, as the object of a shell output redirection to
    360  * the mount point.
    361  */
    362 /*ARGSUSED*/
    363 static int
    364 nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl,
    365 	int mode, vnode_t **vpp, cred_t *cr, int flag,
    366 	caller_context_t *ct, vsecattr_t *vsecp)
    367 {
    368 	int error;
    369 
    370 	ASSERT(dvp && *name == '\0');
    371 	if (excl == NONEXCL) {
    372 		if (mode && (error = nm_access(dvp, mode, 0, cr, ct)) != 0)
    373 			return (error);
    374 		VN_HOLD(dvp);
    375 		return (0);
    376 	}
    377 	return (EEXIST);
    378 }
    379 
    380 /*
    381  * Links are not allowed on mounted file descriptors.
    382  */
    383 /*ARGSUSED*/
    384 static int
    385 nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp,
    386 	caller_context_t *ct, int flags)
    387 {
    388 	return (EXDEV);
    389 }
    390 
    391 static int
    392 nm_fsync(vnode_t *vp, int syncflag, cred_t *crp, caller_context_t *ct)
    393 {
    394 	return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp, ct));
    395 }
    396 
    397 /* Free the namenode */
    398 /* ARGSUSED */
    399 static void
    400 nm_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct)
    401 {
    402 	struct namenode *nodep = VTONM(vp);
    403 	vfs_t *vfsp = vp->v_vfsp;
    404 
    405 	mutex_enter(&vp->v_lock);
    406 	ASSERT(vp->v_count >= 1);
    407 	if (--vp->v_count != 0) {
    408 		mutex_exit(&vp->v_lock);
    409 		return;
    410 	}
    411 	mutex_exit(&vp->v_lock);
    412 	if (!(nodep->nm_flag & NMNMNT)) {
    413 		ASSERT(nodep->nm_filep->f_vnode == nodep->nm_filevp);
    414 		(void) closef(nodep->nm_filep);
    415 	}
    416 	vn_invalid(vp);
    417 	vn_free(vp);
    418 	if (vfsp != &namevfs)
    419 		VFS_RELE(vfsp);
    420 	namenodeno_free(nodep->nm_vattr.va_nodeid);
    421 	kmem_free(nodep, sizeof (struct namenode));
    422 }
    423 
    424 static int
    425 nm_fid(vnode_t *vp, struct fid *fidnodep, caller_context_t *ct)
    426 {
    427 	return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep, ct));
    428 }
    429 
    430 static int
    431 nm_rwlock(vnode_t *vp, int write, caller_context_t *ctp)
    432 {
    433 	return (VOP_RWLOCK(VTONM(vp)->nm_filevp, write, ctp));
    434 }
    435 
    436 static void
    437 nm_rwunlock(vnode_t *vp, int write, caller_context_t *ctp)
    438 {
    439 	VOP_RWUNLOCK(VTONM(vp)->nm_filevp, write, ctp);
    440 }
    441 
    442 static int
    443 nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
    444 {
    445 	return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp, ct));
    446 }
    447 
    448 /*
    449  * Return the vnode representing the file descriptor in vpp.
    450  */
    451 static int
    452 nm_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
    453 {
    454 	struct vnode *rvp;
    455 
    456 	vp = VTONM(vp)->nm_filevp;
    457 	if (VOP_REALVP(vp, &rvp, ct) == 0)
    458 		vp = rvp;
    459 	*vpp = vp;
    460 	return (0);
    461 }
    462 
    463 static int
    464 nm_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
    465 	pollhead_t **phpp, caller_context_t *ct)
    466 {
    467 	return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp,
    468 	    phpp, ct));
    469 }
    470 
    471 struct vnodeops *nm_vnodeops;
    472 
    473 const fs_operation_def_t nm_vnodeops_template[] = {
    474 	VOPNAME_OPEN,		{ .vop_open = nm_open },
    475 	VOPNAME_CLOSE,		{ .vop_close = nm_close },
    476 	VOPNAME_READ,		{ .vop_read = nm_read },
    477 	VOPNAME_WRITE,		{ .vop_write = nm_write },
    478 	VOPNAME_IOCTL,		{ .vop_ioctl = nm_ioctl },
    479 	VOPNAME_GETATTR,	{ .vop_getattr = nm_getattr },
    480 	VOPNAME_SETATTR,	{ .vop_setattr = nm_setattr },
    481 	VOPNAME_ACCESS,		{ .vop_access = nm_access },
    482 	VOPNAME_CREATE,		{ .vop_create = nm_create },
    483 	VOPNAME_LINK,		{ .vop_link = nm_link },
    484 	VOPNAME_FSYNC,		{ .vop_fsync = nm_fsync },
    485 	VOPNAME_INACTIVE,	{ .vop_inactive = nm_inactive },
    486 	VOPNAME_FID,		{ .vop_fid = nm_fid },
    487 	VOPNAME_RWLOCK,		{ .vop_rwlock = nm_rwlock },
    488 	VOPNAME_RWUNLOCK,	{ .vop_rwunlock = nm_rwunlock },
    489 	VOPNAME_SEEK,		{ .vop_seek = nm_seek },
    490 	VOPNAME_REALVP,		{ .vop_realvp = nm_realvp },
    491 	VOPNAME_POLL,		{ .vop_poll = nm_poll },
    492 	VOPNAME_DISPOSE,	{ .error = fs_error },
    493 	NULL,			NULL
    494 };
    495