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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * This file defines the vnode operations for mounted file descriptors. 35 * The routines in this file act as a layer between the NAMEFS file 36 * system and SPECFS/FIFOFS. With the exception of nm_open(), nm_setattr(), 37 * nm_getattr() and nm_access(), the routines simply apply the VOP operation 38 * to the vnode representing the file descriptor. This switches control 39 * to the underlying file system to which the file descriptor belongs. 40 */ 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/cred.h> 45 #include <sys/errno.h> 46 #include <sys/time.h> 47 #include <sys/file.h> 48 #include <sys/fcntl.h> 49 #include <sys/flock.h> 50 #include <sys/kmem.h> 51 #include <sys/uio.h> 52 #include <sys/vfs.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) 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)) != 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 { 168 struct namenode *nodep = VTONM(vp); 169 int error = 0; 170 171 (void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0); 172 cleanshares(vp, ttoproc(curthread)->p_pid); 173 error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp); 174 if (count == 1) { 175 (void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp); 176 /* 177 * Before VN_RELE() we need to remove the vnode from 178 * the hash table. We should only do so in the NMNMNT case. 179 * In other cases, nodep->nm_filep keeps a reference 180 * to nm_filevp and the entry in the hash table doesn't 181 * hurt. 182 */ 183 if ((nodep->nm_flag & NMNMNT) != 0) { 184 mutex_enter(&ntable_lock); 185 nameremove(nodep); 186 mutex_exit(&ntable_lock); 187 } 188 VN_RELE(nodep->nm_filevp); 189 } 190 return (error); 191 } 192 193 static int 194 nm_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp, 195 caller_context_t *ct) 196 { 197 return (VOP_READ(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct)); 198 } 199 200 static int 201 nm_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp, 202 caller_context_t *ct) 203 { 204 return (VOP_WRITE(VTONM(vp)->nm_filevp, uiop, ioflag, crp, ct)); 205 } 206 207 static int 208 nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp) 209 { 210 return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp)); 211 } 212 213 /* 214 * Return in vap the attributes that are stored in the namenode 215 * structure. Only the size is taken from the mounted object. 216 */ 217 /* ARGSUSED */ 218 static int 219 nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp) 220 { 221 struct namenode *nodep = VTONM(vp); 222 struct vattr va; 223 int error; 224 225 mutex_enter(&nodep->nm_lock); 226 bcopy(&nodep->nm_vattr, vap, sizeof (vattr_t)); 227 mutex_exit(&nodep->nm_lock); 228 229 if ((va.va_mask = vap->va_mask & AT_SIZE) != 0) { 230 if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp)) 231 return (error); 232 vap->va_size = va.va_size; 233 } 234 235 return (0); 236 } 237 238 /* 239 * Standard access() like check. Figure out which mode bits apply 240 * to the caller then pass the missing mode bits to the secpolicy function. 241 */ 242 static int 243 nm_access_unlocked(void *vnp, int mode, cred_t *crp) 244 { 245 struct namenode *nodep = vnp; 246 int shift = 0; 247 248 if (crgetuid(crp) != nodep->nm_vattr.va_uid) { 249 shift += 3; 250 if (!groupmember(nodep->nm_vattr.va_gid, crp)) 251 shift += 3; 252 } 253 mode &= ~(nodep->nm_vattr.va_mode << shift); 254 255 if (mode == 0) 256 return (0); 257 258 return (secpolicy_vnode_access(crp, NMTOV(nodep), 259 nodep->nm_vattr.va_uid, mode)); 260 } 261 /* 262 * Set the attributes of the namenode from the attributes in vap. 263 */ 264 /* ARGSUSED */ 265 static int 266 nm_setattr( 267 vnode_t *vp, 268 vattr_t *vap, 269 int flags, 270 cred_t *crp, 271 caller_context_t *ctp) 272 { 273 struct namenode *nodep = VTONM(vp); 274 struct vattr *nmvap = &nodep->nm_vattr; 275 long mask = vap->va_mask; 276 int error = 0; 277 278 /* 279 * Cannot set these attributes. 280 */ 281 if (mask & (AT_NOSET|AT_SIZE)) 282 return (EINVAL); 283 284 (void) VOP_RWLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp); 285 mutex_enter(&nodep->nm_lock); 286 287 /* 288 * Change ownership/group/time/access mode of mounted file 289 * descriptor. 290 */ 291 292 error = secpolicy_vnode_setattr(crp, vp, vap, nmvap, flags, 293 nm_access_unlocked, nodep); 294 if (error) 295 goto out; 296 297 mask = vap->va_mask; 298 /* 299 * If request to change mode, copy new 300 * mode into existing attribute structure. 301 */ 302 if (mask & AT_MODE) 303 nmvap->va_mode = vap->va_mode & ~VSVTX; 304 305 /* 306 * If request was to change user or group, turn off suid and sgid 307 * bits. 308 * If the system was configured with the "rstchown" option, the 309 * owner is not permitted to give away the file, and can change 310 * the group id only to a group of which he or she is a member. 311 */ 312 if (mask & AT_UID) 313 nmvap->va_uid = vap->va_uid; 314 if (mask & AT_GID) 315 nmvap->va_gid = vap->va_gid; 316 /* 317 * If request is to modify times, make sure user has write 318 * permissions on the file. 319 */ 320 if (mask & AT_ATIME) 321 nmvap->va_atime = vap->va_atime; 322 if (mask & AT_MTIME) { 323 nmvap->va_mtime = vap->va_mtime; 324 gethrestime(&nmvap->va_ctime); 325 } 326 out: 327 mutex_exit(&nodep->nm_lock); 328 VOP_RWUNLOCK(nodep->nm_filevp, V_WRITELOCK_TRUE, ctp); 329 return (error); 330 } 331 332 /* 333 * Check mode permission on the namenode. First nm_access_unlocked() 334 * checks the bits on the name node, then an access check is performed 335 * on the underlying file. 336 */ 337 /* ARGSUSED */ 338 static int 339 nm_access(vnode_t *vp, int mode, int flags, cred_t *crp) 340 { 341 struct namenode *nodep = VTONM(vp); 342 int error; 343 344 mutex_enter(&nodep->nm_lock); 345 error = nm_access_unlocked(nodep, mode, crp); 346 mutex_exit(&nodep->nm_lock); 347 if (error == 0) 348 return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp)); 349 else 350 return (error); 351 } 352 353 /* 354 * We can get here if a creat or open with O_CREAT is done on a namefs 355 * mount point, for example, as the object of a shell output redirection to 356 * the mount point. 357 */ 358 /*ARGSUSED*/ 359 static int 360 nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl, 361 int mode, vnode_t **vpp, cred_t *cr, int flag) 362 { 363 int error; 364 365 ASSERT(dvp && *name == '\0'); 366 if (excl == NONEXCL) { 367 if (mode && (error = nm_access(dvp, mode, 0, cr)) != 0) 368 return (error); 369 VN_HOLD(dvp); 370 return (0); 371 } 372 return (EEXIST); 373 } 374 375 /* 376 * Links are not allowed on mounted file descriptors. 377 */ 378 /*ARGSUSED*/ 379 static int 380 nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp) 381 { 382 return (EXDEV); 383 } 384 385 static int 386 nm_fsync(vnode_t *vp, int syncflag, cred_t *crp) 387 { 388 return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp)); 389 } 390 391 /* Free the namenode */ 392 /* ARGSUSED */ 393 static void 394 nm_inactive(vnode_t *vp, cred_t *crp) 395 { 396 struct namenode *nodep = VTONM(vp); 397 398 mutex_enter(&vp->v_lock); 399 ASSERT(vp->v_count >= 1); 400 if (--vp->v_count != 0) { 401 mutex_exit(&vp->v_lock); 402 return; 403 } 404 mutex_exit(&vp->v_lock); 405 if (!(nodep->nm_flag & NMNMNT)) { 406 ASSERT(nodep->nm_filep->f_vnode == nodep->nm_filevp); 407 (void) closef(nodep->nm_filep); 408 } 409 vn_invalid(vp); 410 vn_free(vp); 411 namenodeno_free(nodep->nm_vattr.va_nodeid); 412 kmem_free(nodep, sizeof (struct namenode)); 413 } 414 415 static int 416 nm_fid(vnode_t *vp, struct fid *fidnodep) 417 { 418 return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep)); 419 } 420 421 static int 422 nm_rwlock(vnode_t *vp, int write, caller_context_t *ctp) 423 { 424 return (VOP_RWLOCK(VTONM(vp)->nm_filevp, write, ctp)); 425 } 426 427 static void 428 nm_rwunlock(vnode_t *vp, int write, caller_context_t *ctp) 429 { 430 VOP_RWUNLOCK(VTONM(vp)->nm_filevp, write, ctp); 431 } 432 433 static int 434 nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) 435 { 436 return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp)); 437 } 438 439 /* 440 * Return the vnode representing the file descriptor in vpp. 441 */ 442 static int 443 nm_realvp(vnode_t *vp, vnode_t **vpp) 444 { 445 struct vnode *rvp; 446 447 vp = VTONM(vp)->nm_filevp; 448 if (VOP_REALVP(vp, &rvp) == 0) 449 vp = rvp; 450 *vpp = vp; 451 return (0); 452 } 453 454 static int 455 nm_poll(vnode_t *vp, short events, int anyyet, short *reventsp, 456 pollhead_t **phpp) 457 { 458 return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp, phpp)); 459 } 460 461 struct vnodeops *nm_vnodeops; 462 463 const fs_operation_def_t nm_vnodeops_template[] = { 464 VOPNAME_OPEN, nm_open, 465 VOPNAME_CLOSE, nm_close, 466 VOPNAME_READ, nm_read, 467 VOPNAME_WRITE, nm_write, 468 VOPNAME_IOCTL, nm_ioctl, 469 VOPNAME_GETATTR, nm_getattr, 470 VOPNAME_SETATTR, nm_setattr, 471 VOPNAME_ACCESS, nm_access, 472 VOPNAME_CREATE, nm_create, 473 VOPNAME_LINK, nm_link, 474 VOPNAME_FSYNC, nm_fsync, 475 VOPNAME_INACTIVE, (fs_generic_func_p) nm_inactive, 476 VOPNAME_FID, nm_fid, 477 VOPNAME_RWLOCK, nm_rwlock, 478 VOPNAME_RWUNLOCK, (fs_generic_func_p) nm_rwunlock, 479 VOPNAME_SEEK, nm_seek, 480 VOPNAME_REALVP, nm_realvp, 481 VOPNAME_POLL, (fs_generic_func_p) nm_poll, 482 VOPNAME_DISPOSE, fs_error, 483 NULL, NULL 484 }; 485