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