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