1 /* $NetBSD: ext2fs_vnops.c,v 1.45 2003/05/26 21:22:19 fvdl Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Manuel Bouyer. 5 * Copyright (c) 1982, 1986, 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * (c) UNIX System Laboratories, Inc. 8 * All or some portions of this file are derived from material licensed 9 * to the University of California by American Telephone and Telegraph 10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 11 * the permission of UNIX System Laboratories, Inc. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94 42 * Modified for ext2fs by Manuel Bouyer. 43 */ 44 45 #include <sys/cdefs.h> 46 __KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.45 2003/05/26 21:22:19 fvdl Exp $"); 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/resourcevar.h> 51 #include <sys/kernel.h> 52 #include <sys/file.h> 53 #include <sys/stat.h> 54 #include <sys/buf.h> 55 #include <sys/proc.h> 56 #include <sys/mount.h> 57 #include <sys/namei.h> 58 #include <sys/vnode.h> 59 #include <sys/lockf.h> 60 #include <sys/malloc.h> 61 #include <sys/pool.h> 62 #include <sys/signalvar.h> 63 64 #include <miscfs/fifofs/fifo.h> 65 #include <miscfs/genfs/genfs.h> 66 #include <miscfs/specfs/specdev.h> 67 68 #include <ufs/ufs/inode.h> 69 #include <ufs/ufs/ufs_extern.h> 70 #include <ufs/ufs/ufsmount.h> 71 72 #include <ufs/ext2fs/ext2fs.h> 73 #include <ufs/ext2fs/ext2fs_extern.h> 74 #include <ufs/ext2fs/ext2fs_dir.h> 75 76 extern int prtactive; 77 78 static int ext2fs_chmod 79 __P((struct vnode *, int, struct ucred *, struct proc *)); 80 static int ext2fs_chown 81 __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *)); 82 83 union _qcvt { 84 int64_t qcvt; 85 int32_t val[2]; 86 }; 87 #define SETHIGH(q, h) { \ 88 union _qcvt tmp; \ 89 tmp.qcvt = (q); \ 90 tmp.val[_QUAD_HIGHWORD] = (h); \ 91 (q) = tmp.qcvt; \ 92 } 93 #define SETLOW(q, l) { \ 94 union _qcvt tmp; \ 95 tmp.qcvt = (q); \ 96 tmp.val[_QUAD_LOWWORD] = (l); \ 97 (q) = tmp.qcvt; \ 98 } 99 100 /* 101 * Create a regular file 102 */ 103 int 104 ext2fs_create(v) 105 void *v; 106 { 107 struct vop_create_args /* { 108 struct vnode *a_dvp; 109 struct vnode **a_vpp; 110 struct componentname *a_cnp; 111 struct vattr *a_vap; 112 } */ *ap = v; 113 int error; 114 115 error = 116 ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), 117 ap->a_dvp, ap->a_vpp, ap->a_cnp); 118 119 if (error) 120 return (error); 121 VN_KNOTE(ap->a_dvp, NOTE_WRITE); 122 return (0); 123 } 124 125 /* 126 * Mknod vnode call 127 */ 128 /* ARGSUSED */ 129 int 130 ext2fs_mknod(v) 131 void *v; 132 { 133 struct vop_mknod_args /* { 134 struct vnode *a_dvp; 135 struct vnode **a_vpp; 136 struct componentname *a_cnp; 137 struct vattr *a_vap; 138 } */ *ap = v; 139 struct vattr *vap = ap->a_vap; 140 struct vnode **vpp = ap->a_vpp; 141 struct inode *ip; 142 int error; 143 struct mount *mp; 144 ino_t ino; 145 146 if ((error = ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), 147 ap->a_dvp, vpp, ap->a_cnp)) != 0) 148 return (error); 149 VN_KNOTE(ap->a_dvp, NOTE_WRITE); 150 ip = VTOI(*vpp); 151 mp = (*vpp)->v_mount; 152 ino = ip->i_number; 153 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; 154 if (vap->va_rdev != VNOVAL) { 155 /* 156 * Want to be able to use this to make badblock 157 * inodes, so don't truncate the dev number. 158 */ 159 ip->i_din.e2fs_din->e2di_rdev = h2fs32(vap->va_rdev); 160 } 161 /* 162 * Remove inode so that it will be reloaded by VFS_VGET and 163 * checked to see if it is an alias of an existing entry in 164 * the inode cache. 165 */ 166 vput(*vpp); 167 (*vpp)->v_type = VNON; 168 vgone(*vpp); 169 error = VFS_VGET(mp, ino, vpp); 170 if (error != 0) { 171 *vpp = NULL; 172 return (error); 173 } 174 return (0); 175 } 176 177 /* 178 * Open called. 179 * 180 * Just check the APPEND flag. 181 */ 182 /* ARGSUSED */ 183 int 184 ext2fs_open(v) 185 void *v; 186 { 187 struct vop_open_args /* { 188 struct vnode *a_vp; 189 int a_mode; 190 struct ucred *a_cred; 191 struct proc *a_p; 192 } */ *ap = v; 193 194 /* 195 * Files marked append-only must be opened for appending. 196 */ 197 if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) && 198 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) 199 return (EPERM); 200 return (0); 201 } 202 203 int 204 ext2fs_access(v) 205 void *v; 206 { 207 struct vop_access_args /* { 208 struct vnode *a_vp; 209 int a_mode; 210 struct ucred *a_cred; 211 struct proc *a_p; 212 } */ *ap = v; 213 struct vnode *vp = ap->a_vp; 214 struct inode *ip = VTOI(vp); 215 mode_t mode = ap->a_mode; 216 217 /* 218 * Disallow write attempts on read-only file systems; 219 * unless the file is a socket, fifo, or a block or 220 * character device resident on the file system. 221 */ 222 if (mode & VWRITE) { 223 switch (vp->v_type) { 224 case VDIR: 225 case VLNK: 226 case VREG: 227 if (vp->v_mount->mnt_flag & MNT_RDONLY) 228 return (EROFS); 229 break; 230 default: 231 break; 232 } 233 } 234 235 /* If immutable bit set, nobody gets to write it. */ 236 if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE)) 237 return (EPERM); 238 239 return (vaccess(vp->v_type, ip->i_e2fs_mode & ALLPERMS, 240 ip->i_e2fs_uid, ip->i_e2fs_gid, mode, ap->a_cred)); 241 } 242 243 /* ARGSUSED */ 244 int 245 ext2fs_getattr(v) 246 void *v; 247 { 248 struct vop_getattr_args /* { 249 struct vnode *a_vp; 250 struct vattr *a_vap; 251 struct ucred *a_cred; 252 struct proc *a_p; 253 } */ *ap = v; 254 struct vnode *vp = ap->a_vp; 255 struct inode *ip = VTOI(vp); 256 struct vattr *vap = ap->a_vap; 257 struct timespec ts; 258 259 TIMEVAL_TO_TIMESPEC(&time, &ts); 260 EXT2FS_ITIMES(ip, &ts, &ts, &ts); 261 /* 262 * Copy from inode table 263 */ 264 vap->va_fsid = ip->i_dev; 265 vap->va_fileid = ip->i_number; 266 vap->va_mode = ip->i_e2fs_mode & ALLPERMS; 267 vap->va_nlink = ip->i_e2fs_nlink; 268 vap->va_uid = ip->i_e2fs_uid; 269 vap->va_gid = ip->i_e2fs_gid; 270 vap->va_rdev = (dev_t)fs2h32(ip->i_din.e2fs_din->e2di_rdev); 271 vap->va_size = vp->v_size; 272 vap->va_atime.tv_sec = ip->i_e2fs_atime; 273 vap->va_atime.tv_nsec = 0; 274 vap->va_mtime.tv_sec = ip->i_e2fs_mtime; 275 vap->va_mtime.tv_nsec = 0; 276 vap->va_ctime.tv_sec = ip->i_e2fs_ctime; 277 vap->va_ctime.tv_nsec = 0; 278 #ifdef EXT2FS_SYSTEM_FLAGS 279 vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0; 280 vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0; 281 #else 282 vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0; 283 vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0; 284 #endif 285 vap->va_gen = ip->i_e2fs_gen; 286 /* this doesn't belong here */ 287 if (vp->v_type == VBLK) 288 vap->va_blocksize = BLKDEV_IOSIZE; 289 else if (vp->v_type == VCHR) 290 vap->va_blocksize = MAXBSIZE; 291 else 292 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; 293 vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock); 294 vap->va_type = vp->v_type; 295 vap->va_filerev = ip->i_modrev; 296 return (0); 297 } 298 299 /* 300 * Set attribute vnode op. called from several syscalls 301 */ 302 int 303 ext2fs_setattr(v) 304 void *v; 305 { 306 struct vop_setattr_args /* { 307 struct vnode *a_vp; 308 struct vattr *a_vap; 309 struct ucred *a_cred; 310 struct proc *a_p; 311 } */ *ap = v; 312 struct vattr *vap = ap->a_vap; 313 struct vnode *vp = ap->a_vp; 314 struct inode *ip = VTOI(vp); 315 struct ucred *cred = ap->a_cred; 316 struct proc *p = ap->a_p; 317 int error; 318 319 /* 320 * Check for unsettable attributes. 321 */ 322 if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) || 323 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 324 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 325 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 326 return (EINVAL); 327 } 328 if (vap->va_flags != VNOVAL) { 329 if (vp->v_mount->mnt_flag & MNT_RDONLY) 330 return (EROFS); 331 if (cred->cr_uid != ip->i_e2fs_uid && 332 (error = suser(cred, &p->p_acflag))) 333 return (error); 334 #ifdef EXT2FS_SYSTEM_FLAGS 335 if (cred->cr_uid == 0) { 336 if ((ip->i_e2fs_flags & 337 (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0) 338 return (EPERM); 339 ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE); 340 ip->i_e2fs_flags |= 341 (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 | 342 (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE : 0; 343 } else 344 return (EPERM); 345 #else 346 ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE); 347 ip->i_e2fs_flags |= 348 (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 | 349 (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE : 0; 350 #endif 351 ip->i_flag |= IN_CHANGE; 352 if (vap->va_flags & (IMMUTABLE | APPEND)) 353 return (0); 354 } 355 if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE)) 356 return (EPERM); 357 /* 358 * Go through the fields and update iff not VNOVAL. 359 */ 360 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { 361 if (vp->v_mount->mnt_flag & MNT_RDONLY) 362 return (EROFS); 363 error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p); 364 if (error) 365 return (error); 366 } 367 if (vap->va_size != VNOVAL) { 368 /* 369 * Disallow write attempts on read-only file systems; 370 * unless the file is a socket, fifo, or a block or 371 * character device resident on the file system. 372 */ 373 switch (vp->v_type) { 374 case VDIR: 375 return (EISDIR); 376 case VLNK: 377 case VREG: 378 if (vp->v_mount->mnt_flag & MNT_RDONLY) 379 return (EROFS); 380 default: 381 break; 382 } 383 error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p); 384 if (error) 385 return (error); 386 } 387 ip = VTOI(vp); 388 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { 389 if (vp->v_mount->mnt_flag & MNT_RDONLY) 390 return (EROFS); 391 if (cred->cr_uid != ip->i_e2fs_uid && 392 (error = suser(cred, &p->p_acflag)) && 393 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 394 (error = VOP_ACCESS(vp, VWRITE, cred, p)))) 395 return (error); 396 if (vap->va_atime.tv_sec != VNOVAL) 397 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) 398 ip->i_flag |= IN_ACCESS; 399 if (vap->va_mtime.tv_sec != VNOVAL) 400 ip->i_flag |= IN_CHANGE | IN_UPDATE; 401 error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 402 UPDATE_WAIT); 403 if (error) 404 return (error); 405 } 406 error = 0; 407 if (vap->va_mode != (mode_t)VNOVAL) { 408 if (vp->v_mount->mnt_flag & MNT_RDONLY) 409 return (EROFS); 410 error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p); 411 } 412 VN_KNOTE(vp, NOTE_ATTRIB); 413 return (error); 414 } 415 416 /* 417 * Change the mode on a file. 418 * Inode must be locked before calling. 419 */ 420 static int 421 ext2fs_chmod(vp, mode, cred, p) 422 struct vnode *vp; 423 int mode; 424 struct ucred *cred; 425 struct proc *p; 426 { 427 struct inode *ip = VTOI(vp); 428 int error; 429 430 if (cred->cr_uid != ip->i_e2fs_uid && 431 (error = suser(cred, &p->p_acflag))) 432 return (error); 433 if (cred->cr_uid) { 434 if (vp->v_type != VDIR && (mode & S_ISTXT)) 435 return (EFTYPE); 436 if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID)) 437 return (EPERM); 438 } 439 ip->i_e2fs_mode &= ~ALLPERMS; 440 ip->i_e2fs_mode |= (mode & ALLPERMS); 441 ip->i_flag |= IN_CHANGE; 442 return (0); 443 } 444 445 /* 446 * Perform chown operation on inode ip; 447 * inode must be locked prior to call. 448 */ 449 static int 450 ext2fs_chown(vp, uid, gid, cred, p) 451 struct vnode *vp; 452 uid_t uid; 453 gid_t gid; 454 struct ucred *cred; 455 struct proc *p; 456 { 457 struct inode *ip = VTOI(vp); 458 uid_t ouid; 459 gid_t ogid; 460 int error = 0; 461 462 if (uid == (uid_t)VNOVAL) 463 uid = ip->i_e2fs_uid; 464 if (gid == (gid_t)VNOVAL) 465 gid = ip->i_e2fs_gid; 466 /* 467 * If we don't own the file, are trying to change the owner 468 * of the file, or are not a member of the target group, 469 * the caller must be superuser or the call fails. 470 */ 471 if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid || 472 (gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) && 473 (error = suser(cred, &p->p_acflag))) 474 return (error); 475 ogid = ip->i_e2fs_gid; 476 ouid = ip->i_e2fs_uid; 477 478 ip->i_e2fs_gid = gid; 479 ip->i_e2fs_uid = uid; 480 if (ouid != uid || ogid != gid) 481 ip->i_flag |= IN_CHANGE; 482 if (ouid != uid && cred->cr_uid != 0) 483 ip->i_e2fs_mode &= ~ISUID; 484 if (ogid != gid && cred->cr_uid != 0) 485 ip->i_e2fs_mode &= ~ISGID; 486 return (0); 487 } 488 489 int 490 ext2fs_remove(v) 491 void *v; 492 { 493 struct vop_remove_args /* { 494 struct vnode *a_dvp; 495 struct vnode *a_vp; 496 struct componentname *a_cnp; 497 } */ *ap = v; 498 struct inode *ip; 499 struct vnode *vp = ap->a_vp; 500 struct vnode *dvp = ap->a_dvp; 501 int error; 502 503 ip = VTOI(vp); 504 if (vp->v_type == VDIR || 505 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || 506 (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) { 507 error = EPERM; 508 } else { 509 error = ext2fs_dirremove(dvp, ap->a_cnp); 510 if (error == 0) { 511 ip->i_e2fs_nlink--; 512 ip->i_flag |= IN_CHANGE; 513 } 514 } 515 516 VN_KNOTE(vp, NOTE_DELETE); 517 VN_KNOTE(dvp, NOTE_WRITE); 518 if (dvp == vp) 519 vrele(vp); 520 else 521 vput(vp); 522 vput(dvp); 523 return (error); 524 } 525 526 /* 527 * link vnode call 528 */ 529 int 530 ext2fs_link(v) 531 void *v; 532 { 533 struct vop_link_args /* { 534 struct vnode *a_dvp; 535 struct vnode *a_vp; 536 struct componentname *a_cnp; 537 } */ *ap = v; 538 struct vnode *dvp = ap->a_dvp; 539 struct vnode *vp = ap->a_vp; 540 struct componentname *cnp = ap->a_cnp; 541 struct inode *ip; 542 int error; 543 544 #ifdef DIAGNOSTIC 545 if ((cnp->cn_flags & HASBUF) == 0) 546 panic("ext2fs_link: no name"); 547 #endif 548 if (vp->v_type == VDIR) { 549 VOP_ABORTOP(dvp, cnp); 550 error = EISDIR; 551 goto out2; 552 } 553 if (dvp->v_mount != vp->v_mount) { 554 VOP_ABORTOP(dvp, cnp); 555 error = EXDEV; 556 goto out2; 557 } 558 if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) { 559 VOP_ABORTOP(dvp, cnp); 560 goto out2; 561 } 562 ip = VTOI(vp); 563 if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) { 564 VOP_ABORTOP(dvp, cnp); 565 error = EMLINK; 566 goto out1; 567 } 568 if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) { 569 VOP_ABORTOP(dvp, cnp); 570 error = EPERM; 571 goto out1; 572 } 573 ip->i_e2fs_nlink++; 574 ip->i_flag |= IN_CHANGE; 575 error = VOP_UPDATE(vp, NULL, NULL, UPDATE_WAIT); 576 if (!error) 577 error = ext2fs_direnter(ip, dvp, cnp); 578 if (error) { 579 ip->i_e2fs_nlink--; 580 ip->i_flag |= IN_CHANGE; 581 } 582 PNBUF_PUT(cnp->cn_pnbuf); 583 out1: 584 if (dvp != vp) 585 VOP_UNLOCK(vp, 0); 586 out2: 587 VN_KNOTE(vp, NOTE_LINK); 588 VN_KNOTE(dvp, NOTE_WRITE); 589 vput(dvp); 590 return (error); 591 } 592 593 /* 594 * Rename system call. 595 * rename("foo", "bar"); 596 * is essentially 597 * unlink("bar"); 598 * link("foo", "bar"); 599 * unlink("foo"); 600 * but ``atomically''. Can't do full commit without saving state in the 601 * inode on disk which isn't feasible at this time. Best we can do is 602 * always guarantee the target exists. 603 * 604 * Basic algorithm is: 605 * 606 * 1) Bump link count on source while we're linking it to the 607 * target. This also ensure the inode won't be deleted out 608 * from underneath us while we work (it may be truncated by 609 * a concurrent `trunc' or `open' for creation). 610 * 2) Link source to destination. If destination already exists, 611 * delete it first. 612 * 3) Unlink source reference to inode if still around. If a 613 * directory was moved and the parent of the destination 614 * is different from the source, patch the ".." entry in the 615 * directory. 616 */ 617 int 618 ext2fs_rename(v) 619 void *v; 620 { 621 struct vop_rename_args /* { 622 struct vnode *a_fdvp; 623 struct vnode *a_fvp; 624 struct componentname *a_fcnp; 625 struct vnode *a_tdvp; 626 struct vnode *a_tvp; 627 struct componentname *a_tcnp; 628 } */ *ap = v; 629 struct vnode *tvp = ap->a_tvp; 630 struct vnode *tdvp = ap->a_tdvp; 631 struct vnode *fvp = ap->a_fvp; 632 struct vnode *fdvp = ap->a_fdvp; 633 struct componentname *tcnp = ap->a_tcnp; 634 struct componentname *fcnp = ap->a_fcnp; 635 struct inode *ip, *xp, *dp; 636 struct ext2fs_dirtemplate dirbuf; 637 int doingdirectory = 0, oldparent = 0, newparent = 0; 638 int error = 0; 639 u_char namlen; 640 641 #ifdef DIAGNOSTIC 642 if ((tcnp->cn_flags & HASBUF) == 0 || 643 (fcnp->cn_flags & HASBUF) == 0) 644 panic("ext2fs_rename: no name"); 645 #endif 646 /* 647 * Check for cross-device rename. 648 */ 649 if ((fvp->v_mount != tdvp->v_mount) || 650 (tvp && (fvp->v_mount != tvp->v_mount))) { 651 error = EXDEV; 652 abortit: 653 VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ 654 if (tdvp == tvp) 655 vrele(tdvp); 656 else 657 vput(tdvp); 658 if (tvp) 659 vput(tvp); 660 VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ 661 vrele(fdvp); 662 vrele(fvp); 663 return (error); 664 } 665 666 /* 667 * Check if just deleting a link name. 668 */ 669 if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || 670 (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) { 671 error = EPERM; 672 goto abortit; 673 } 674 if (fvp == tvp) { 675 if (fvp->v_type == VDIR) { 676 error = EINVAL; 677 goto abortit; 678 } 679 680 /* Release destination completely. */ 681 VOP_ABORTOP(tdvp, tcnp); 682 vput(tdvp); 683 vput(tvp); 684 685 /* Delete source. */ 686 vrele(fdvp); 687 vrele(fvp); 688 fcnp->cn_flags &= ~MODMASK; 689 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; 690 if ((fcnp->cn_flags & SAVESTART) == 0) 691 panic("ext2fs_rename: lost from startdir"); 692 fcnp->cn_nameiop = DELETE; 693 (void) relookup(fdvp, &fvp, fcnp); 694 return (VOP_REMOVE(fdvp, fvp, fcnp)); 695 } 696 if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) 697 goto abortit; 698 dp = VTOI(fdvp); 699 ip = VTOI(fvp); 700 if ((nlink_t) ip->i_e2fs_nlink >= LINK_MAX) { 701 VOP_UNLOCK(fvp, 0); 702 error = EMLINK; 703 goto abortit; 704 } 705 if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) || 706 (dp->i_e2fs_flags & EXT2_APPEND)) { 707 VOP_UNLOCK(fvp, 0); 708 error = EPERM; 709 goto abortit; 710 } 711 if ((ip->i_e2fs_mode & IFMT) == IFDIR) { 712 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); 713 if (!error && tvp) 714 error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, 715 tcnp->cn_proc); 716 if (error) { 717 VOP_UNLOCK(fvp, 0); 718 error = EACCES; 719 goto abortit; 720 } 721 /* 722 * Avoid ".", "..", and aliases of "." for obvious reasons. 723 */ 724 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || 725 dp == ip || 726 (fcnp->cn_flags&ISDOTDOT) || 727 (tcnp->cn_flags & ISDOTDOT) || 728 (ip->i_flag & IN_RENAME)) { 729 VOP_UNLOCK(fvp, 0); 730 error = EINVAL; 731 goto abortit; 732 } 733 ip->i_flag |= IN_RENAME; 734 oldparent = dp->i_number; 735 doingdirectory++; 736 } 737 VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */ 738 vrele(fdvp); 739 740 /* 741 * When the target exists, both the directory 742 * and target vnodes are returned locked. 743 */ 744 dp = VTOI(tdvp); 745 xp = NULL; 746 if (tvp) 747 xp = VTOI(tvp); 748 749 /* 750 * 1) Bump link count while we're moving stuff 751 * around. If we crash somewhere before 752 * completing our work, the link count 753 * may be wrong, but correctable. 754 */ 755 ip->i_e2fs_nlink++; 756 ip->i_flag |= IN_CHANGE; 757 if ((error = VOP_UPDATE(fvp, NULL, NULL, UPDATE_WAIT)) != 0) { 758 VOP_UNLOCK(fvp, 0); 759 goto bad; 760 } 761 762 /* 763 * If ".." must be changed (ie the directory gets a new 764 * parent) then the source directory must not be in the 765 * directory hierarchy above the target, as this would 766 * orphan everything below the source directory. Also 767 * the user must have write permission in the source so 768 * as to be able to change "..". We must repeat the call 769 * to namei, as the parent directory is unlocked by the 770 * call to checkpath(). 771 */ 772 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); 773 VOP_UNLOCK(fvp, 0); 774 if (oldparent != dp->i_number) 775 newparent = dp->i_number; 776 if (doingdirectory && newparent) { 777 if (error) /* write access check above */ 778 goto bad; 779 if (xp != NULL) 780 vput(tvp); 781 error = ext2fs_checkpath(ip, dp, tcnp->cn_cred); 782 if (error != 0) 783 goto out; 784 if ((tcnp->cn_flags & SAVESTART) == 0) 785 panic("ext2fs_rename: lost to startdir"); 786 if ((error = relookup(tdvp, &tvp, tcnp)) != 0) 787 goto out; 788 dp = VTOI(tdvp); 789 xp = NULL; 790 if (tvp) 791 xp = VTOI(tvp); 792 } 793 /* 794 * 2) If target doesn't exist, link the target 795 * to the source and unlink the source. 796 * Otherwise, rewrite the target directory 797 * entry to reference the source inode and 798 * expunge the original entry's existence. 799 */ 800 if (xp == NULL) { 801 if (dp->i_dev != ip->i_dev) 802 panic("rename: EXDEV"); 803 /* 804 * Account for ".." in new directory. 805 * When source and destination have the same 806 * parent we don't fool with the link count. 807 */ 808 if (doingdirectory && newparent) { 809 if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) { 810 error = EMLINK; 811 goto bad; 812 } 813 dp->i_e2fs_nlink++; 814 dp->i_flag |= IN_CHANGE; 815 if ((error = VOP_UPDATE(tdvp, NULL, NULL, UPDATE_WAIT)) 816 != 0) 817 goto bad; 818 } 819 error = ext2fs_direnter(ip, tdvp, tcnp); 820 if (error != 0) { 821 if (doingdirectory && newparent) { 822 dp->i_e2fs_nlink--; 823 dp->i_flag |= IN_CHANGE; 824 (void)VOP_UPDATE(tdvp, NULL, NULL, UPDATE_WAIT); 825 } 826 goto bad; 827 } 828 VN_KNOTE(tdvp, NOTE_WRITE); 829 vput(tdvp); 830 } else { 831 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) 832 panic("rename: EXDEV"); 833 /* 834 * Short circuit rename(foo, foo). 835 */ 836 if (xp->i_number == ip->i_number) 837 panic("rename: same file"); 838 /* 839 * If the parent directory is "sticky", then the user must 840 * own the parent directory, or the destination of the rename, 841 * otherwise the destination may not be changed (except by 842 * root). This implements append-only directories. 843 */ 844 if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 && 845 tcnp->cn_cred->cr_uid != dp->i_e2fs_uid && 846 xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) { 847 error = EPERM; 848 goto bad; 849 } 850 /* 851 * Target must be empty if a directory and have no links 852 * to it. Also, ensure source and target are compatible 853 * (both directories, or both not directories). 854 */ 855 if ((xp->i_e2fs_mode & IFMT) == IFDIR) { 856 if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) || 857 xp->i_e2fs_nlink > 2) { 858 error = ENOTEMPTY; 859 goto bad; 860 } 861 if (!doingdirectory) { 862 error = ENOTDIR; 863 goto bad; 864 } 865 cache_purge(tdvp); 866 } else if (doingdirectory) { 867 error = EISDIR; 868 goto bad; 869 } 870 error = ext2fs_dirrewrite(dp, ip, tcnp); 871 if (error != 0) 872 goto bad; 873 /* 874 * If the target directory is in the same 875 * directory as the source directory, 876 * decrement the link count on the parent 877 * of the target directory. 878 */ 879 if (doingdirectory && !newparent) { 880 dp->i_e2fs_nlink--; 881 dp->i_flag |= IN_CHANGE; 882 } 883 VN_KNOTE(tdvp, NOTE_WRITE); 884 vput(tdvp); 885 /* 886 * Adjust the link count of the target to 887 * reflect the dirrewrite above. If this is 888 * a directory it is empty and there are 889 * no links to it, so we can squash the inode and 890 * any space associated with it. We disallowed 891 * renaming over top of a directory with links to 892 * it above, as the remaining link would point to 893 * a directory without "." or ".." entries. 894 */ 895 xp->i_e2fs_nlink--; 896 if (doingdirectory) { 897 if (--xp->i_e2fs_nlink != 0) 898 panic("rename: linked directory"); 899 error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC, 900 tcnp->cn_cred, tcnp->cn_proc); 901 } 902 xp->i_flag |= IN_CHANGE; 903 VN_KNOTE(tvp, NOTE_DELETE); 904 vput(tvp); 905 xp = NULL; 906 } 907 908 /* 909 * 3) Unlink the source. 910 */ 911 fcnp->cn_flags &= ~MODMASK; 912 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; 913 if ((fcnp->cn_flags & SAVESTART) == 0) 914 panic("ext2fs_rename: lost from startdir"); 915 (void) relookup(fdvp, &fvp, fcnp); 916 if (fvp != NULL) { 917 xp = VTOI(fvp); 918 dp = VTOI(fdvp); 919 } else { 920 /* 921 * From name has disappeared. 922 */ 923 if (doingdirectory) 924 panic("ext2fs_rename: lost dir entry"); 925 vrele(ap->a_fvp); 926 return (0); 927 } 928 /* 929 * Ensure that the directory entry still exists and has not 930 * changed while the new name has been entered. If the source is 931 * a file then the entry may have been unlinked or renamed. In 932 * either case there is no further work to be done. If the source 933 * is a directory then it cannot have been rmdir'ed; its link 934 * count of three would cause a rmdir to fail with ENOTEMPTY. 935 * The IRENAME flag ensures that it cannot be moved by another 936 * rename. 937 */ 938 if (xp != ip) { 939 if (doingdirectory) 940 panic("ext2fs_rename: lost dir entry"); 941 } else { 942 /* 943 * If the source is a directory with a 944 * new parent, the link count of the old 945 * parent directory must be decremented 946 * and ".." set to point to the new parent. 947 */ 948 if (doingdirectory && newparent) { 949 dp->i_e2fs_nlink--; 950 dp->i_flag |= IN_CHANGE; 951 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, 952 sizeof (struct ext2fs_dirtemplate), (off_t)0, 953 UIO_SYSSPACE, IO_NODELOCKED, 954 tcnp->cn_cred, (size_t *)0, (struct proc *)0); 955 if (error == 0) { 956 namlen = dirbuf.dotdot_namlen; 957 if (namlen != 2 || 958 dirbuf.dotdot_name[0] != '.' || 959 dirbuf.dotdot_name[1] != '.') { 960 ufs_dirbad(xp, (doff_t)12, 961 "ext2fs_rename: mangled dir"); 962 } else { 963 dirbuf.dotdot_ino = h2fs32(newparent); 964 (void) vn_rdwr(UIO_WRITE, fvp, 965 (caddr_t)&dirbuf, 966 sizeof (struct dirtemplate), 967 (off_t)0, UIO_SYSSPACE, 968 IO_NODELOCKED|IO_SYNC, 969 tcnp->cn_cred, (size_t *)0, 970 (struct proc *)0); 971 cache_purge(fdvp); 972 } 973 } 974 } 975 error = ext2fs_dirremove(fdvp, fcnp); 976 if (!error) { 977 xp->i_e2fs_nlink--; 978 xp->i_flag |= IN_CHANGE; 979 } 980 xp->i_flag &= ~IN_RENAME; 981 } 982 VN_KNOTE(fvp, NOTE_RENAME); 983 if (dp) 984 vput(fdvp); 985 if (xp) 986 vput(fvp); 987 vrele(ap->a_fvp); 988 return (error); 989 990 bad: 991 if (xp) 992 vput(ITOV(xp)); 993 vput(ITOV(dp)); 994 out: 995 if (doingdirectory) 996 ip->i_flag &= ~IN_RENAME; 997 if (vn_lock(fvp, LK_EXCLUSIVE) == 0) { 998 ip->i_e2fs_nlink--; 999 ip->i_flag |= IN_CHANGE; 1000 vput(fvp); 1001 } else 1002 vrele(fvp); 1003 return (error); 1004 } 1005 1006 /* 1007 * Mkdir system call 1008 */ 1009 int 1010 ext2fs_mkdir(v) 1011 void *v; 1012 { 1013 struct vop_mkdir_args /* { 1014 struct vnode *a_dvp; 1015 struct vnode **a_vpp; 1016 struct componentname *a_cnp; 1017 struct vattr *a_vap; 1018 } */ *ap = v; 1019 struct vnode *dvp = ap->a_dvp; 1020 struct vattr *vap = ap->a_vap; 1021 struct componentname *cnp = ap->a_cnp; 1022 struct inode *ip, *dp; 1023 struct vnode *tvp; 1024 struct ext2fs_dirtemplate dirtemplate; 1025 int error, dmode; 1026 1027 #ifdef DIAGNOSTIC 1028 if ((cnp->cn_flags & HASBUF) == 0) 1029 panic("ext2fs_mkdir: no name"); 1030 #endif 1031 dp = VTOI(dvp); 1032 if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) { 1033 error = EMLINK; 1034 goto out; 1035 } 1036 dmode = vap->va_mode & ACCESSPERMS; 1037 dmode |= IFDIR; 1038 /* 1039 * Must simulate part of ext2fs_makeinode here to acquire the inode, 1040 * but not have it entered in the parent directory. The entry is 1041 * made later after writing "." and ".." entries. 1042 */ 1043 if ((error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) != 0) 1044 goto out; 1045 ip = VTOI(tvp); 1046 ip->i_e2fs_uid = cnp->cn_cred->cr_uid; 1047 ip->i_e2fs_gid = dp->i_e2fs_gid; 1048 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; 1049 ip->i_e2fs_mode = dmode; 1050 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ 1051 ip->i_e2fs_nlink = 2; 1052 error = VOP_UPDATE(tvp, NULL, NULL, UPDATE_WAIT); 1053 1054 /* 1055 * Bump link count in parent directory 1056 * to reflect work done below. Should 1057 * be done before reference is created 1058 * so reparation is possible if we crash. 1059 */ 1060 dp->i_e2fs_nlink++; 1061 dp->i_flag |= IN_CHANGE; 1062 if ((error = VOP_UPDATE(dvp, NULL, NULL, UPDATE_WAIT)) != 0) 1063 goto bad; 1064 1065 /* Initialize directory with "." and ".." from static template. */ 1066 memset(&dirtemplate, 0, sizeof(dirtemplate)); 1067 dirtemplate.dot_ino = h2fs32(ip->i_number); 1068 dirtemplate.dot_reclen = h2fs16(12); 1069 dirtemplate.dot_namlen = 1; 1070 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 1071 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 1072 dirtemplate.dot_type = EXT2_FT_DIR; 1073 } 1074 dirtemplate.dot_name[0] = '.'; 1075 dirtemplate.dotdot_ino = h2fs32(dp->i_number); 1076 dirtemplate.dotdot_reclen = h2fs16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12); 1077 dirtemplate.dotdot_namlen = 2; 1078 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 && 1079 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) { 1080 dirtemplate.dotdot_type = EXT2_FT_DIR; 1081 } 1082 dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.'; 1083 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, 1084 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, 1085 IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (size_t *)0, (struct proc *)0); 1086 if (error) { 1087 dp->i_e2fs_nlink--; 1088 dp->i_flag |= IN_CHANGE; 1089 goto bad; 1090 } 1091 if (VTOI(dvp)->i_e2fs->e2fs_bsize > 1092 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) 1093 panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */ 1094 else { 1095 ip->i_e2fs_size = VTOI(dvp)->i_e2fs->e2fs_bsize; 1096 ip->i_flag |= IN_CHANGE; 1097 } 1098 1099 /* Directory set up, now install it's entry in the parent directory. */ 1100 error = ext2fs_direnter(ip, dvp, cnp); 1101 if (error != 0) { 1102 dp->i_e2fs_nlink--; 1103 dp->i_flag |= IN_CHANGE; 1104 } 1105 bad: 1106 /* 1107 * No need to do an explicit VOP_TRUNCATE here, vrele will do this 1108 * for us because we set the link count to 0. 1109 */ 1110 if (error) { 1111 ip->i_e2fs_nlink = 0; 1112 ip->i_flag |= IN_CHANGE; 1113 vput(tvp); 1114 } else { 1115 VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); 1116 *ap->a_vpp = tvp; 1117 } 1118 out: 1119 PNBUF_PUT(cnp->cn_pnbuf); 1120 vput(dvp); 1121 return (error); 1122 } 1123 1124 /* 1125 * Rmdir system call. 1126 */ 1127 int 1128 ext2fs_rmdir(v) 1129 void *v; 1130 { 1131 struct vop_rmdir_args /* { 1132 struct vnode *a_dvp; 1133 struct vnode *a_vp; 1134 struct componentname *a_cnp; 1135 } */ *ap = v; 1136 struct vnode *vp = ap->a_vp; 1137 struct vnode *dvp = ap->a_dvp; 1138 struct componentname *cnp = ap->a_cnp; 1139 struct inode *ip, *dp; 1140 int error; 1141 1142 ip = VTOI(vp); 1143 dp = VTOI(dvp); 1144 /* 1145 * No rmdir "." please. 1146 */ 1147 if (dp == ip) { 1148 vrele(dvp); 1149 vput(vp); 1150 return (EINVAL); 1151 } 1152 /* 1153 * Verify the directory is empty (and valid). 1154 * (Rmdir ".." won't be valid since 1155 * ".." will contain a reference to 1156 * the current directory and thus be 1157 * non-empty.) 1158 */ 1159 error = 0; 1160 if (ip->i_e2fs_nlink != 2 || 1161 !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) { 1162 error = ENOTEMPTY; 1163 goto out; 1164 } 1165 if ((dp->i_e2fs_flags & EXT2_APPEND) || 1166 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) { 1167 error = EPERM; 1168 goto out; 1169 } 1170 /* 1171 * Delete reference to directory before purging 1172 * inode. If we crash in between, the directory 1173 * will be reattached to lost+found, 1174 */ 1175 error = ext2fs_dirremove(dvp, cnp); 1176 if (error != 0) 1177 goto out; 1178 dp->i_e2fs_nlink--; 1179 dp->i_flag |= IN_CHANGE; 1180 VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); 1181 cache_purge(dvp); 1182 vput(dvp); 1183 dvp = NULL; 1184 /* 1185 * Truncate inode. The only stuff left 1186 * in the directory is "." and "..". The 1187 * "." reference is inconsequential since 1188 * we're quashing it. The ".." reference 1189 * has already been adjusted above. We've 1190 * removed the "." reference and the reference 1191 * in the parent directory, but there may be 1192 * other hard links so decrement by 2 and 1193 * worry about them later. 1194 */ 1195 ip->i_e2fs_nlink -= 2; 1196 error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, 1197 cnp->cn_proc); 1198 cache_purge(ITOV(ip)); 1199 out: 1200 VN_KNOTE(vp, NOTE_DELETE); 1201 if (dvp) 1202 vput(dvp); 1203 vput(vp); 1204 return (error); 1205 } 1206 1207 /* 1208 * symlink -- make a symbolic link 1209 */ 1210 int 1211 ext2fs_symlink(v) 1212 void *v; 1213 { 1214 struct vop_symlink_args /* { 1215 struct vnode *a_dvp; 1216 struct vnode **a_vpp; 1217 struct componentname *a_cnp; 1218 struct vattr *a_vap; 1219 char *a_target; 1220 } */ *ap = v; 1221 struct vnode *vp, **vpp = ap->a_vpp; 1222 struct inode *ip; 1223 int len, error; 1224 1225 error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, 1226 vpp, ap->a_cnp); 1227 if (error) 1228 return (error); 1229 VN_KNOTE(ap->a_dvp, NOTE_WRITE); 1230 vp = *vpp; 1231 len = strlen(ap->a_target); 1232 if (len < vp->v_mount->mnt_maxsymlinklen) { 1233 ip = VTOI(vp); 1234 memcpy((char *)ip->i_din.e2fs_din->e2di_shortlink, ap->a_target, len); 1235 ip->i_e2fs_size = len; 1236 ip->i_flag |= IN_CHANGE | IN_UPDATE; 1237 } else 1238 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, 1239 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, 1240 (size_t *)0, (struct proc *)0); 1241 if (error) 1242 vput(vp); 1243 return (error); 1244 } 1245 1246 /* 1247 * Return target name of a symbolic link 1248 */ 1249 int 1250 ext2fs_readlink(v) 1251 void *v; 1252 { 1253 struct vop_readlink_args /* { 1254 struct vnode *a_vp; 1255 struct uio *a_uio; 1256 struct ucred *a_cred; 1257 } */ *ap = v; 1258 struct vnode *vp = ap->a_vp; 1259 struct inode *ip = VTOI(vp); 1260 int isize; 1261 1262 isize = ip->i_e2fs_size; 1263 if (isize < vp->v_mount->mnt_maxsymlinklen || 1264 (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) { 1265 uiomove((char *)ip->i_din.e2fs_din->e2di_shortlink, isize, ap->a_uio); 1266 return (0); 1267 } 1268 return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); 1269 } 1270 1271 /* 1272 * Advisory record locking support 1273 */ 1274 int 1275 ext2fs_advlock(v) 1276 void *v; 1277 { 1278 struct vop_advlock_args /* { 1279 struct vnode *a_vp; 1280 caddr_t a_id; 1281 int a_op; 1282 struct flock *a_fl; 1283 int a_flags; 1284 } */ *ap = v; 1285 struct inode *ip = VTOI(ap->a_vp); 1286 1287 return lf_advlock(ap, &ip->i_lockf, ip->i_e2fs_size); 1288 } 1289 1290 /* 1291 * Initialize the vnode associated with a new inode, handle aliased 1292 * vnodes. 1293 */ 1294 int 1295 ext2fs_vinit(mntp, specops, fifoops, vpp) 1296 struct mount *mntp; 1297 int (**specops) __P((void *)); 1298 int (**fifoops) __P((void *)); 1299 struct vnode **vpp; 1300 { 1301 struct inode *ip; 1302 struct vnode *vp, *nvp; 1303 1304 vp = *vpp; 1305 ip = VTOI(vp); 1306 switch(vp->v_type = IFTOVT(ip->i_e2fs_mode)) { 1307 case VCHR: 1308 case VBLK: 1309 vp->v_op = specops; 1310 if ((nvp = checkalias(vp, 1311 fs2h32(ip->i_din.e2fs_din->e2di_rdev), mntp)) != NULL) { 1312 /* 1313 * Discard unneeded vnode, but save its inode. 1314 */ 1315 nvp->v_data = vp->v_data; 1316 vp->v_data = NULL; 1317 VOP_UNLOCK(vp, 0); 1318 vp->v_op = spec_vnodeop_p; 1319 vrele(vp); 1320 vgone(vp); 1321 lockmgr(&nvp->v_lock, LK_EXCLUSIVE, &nvp->v_interlock); 1322 /* 1323 * Reinitialize aliased inode. 1324 */ 1325 vp = nvp; 1326 ip->i_vnode = vp; 1327 } 1328 break; 1329 case VFIFO: 1330 vp->v_op = fifoops; 1331 break; 1332 case VNON: 1333 case VBAD: 1334 case VSOCK: 1335 case VLNK: 1336 case VDIR: 1337 case VREG: 1338 break; 1339 } 1340 if (ip->i_number == ROOTINO) 1341 vp->v_flag |= VROOT; 1342 /* 1343 * Initialize modrev times 1344 */ 1345 SETHIGH(ip->i_modrev, mono_time.tv_sec); 1346 SETLOW(ip->i_modrev, mono_time.tv_usec * 4294); 1347 *vpp = vp; 1348 return (0); 1349 } 1350 1351 /* 1352 * Allocate a new inode. 1353 */ 1354 int 1355 ext2fs_makeinode(mode, dvp, vpp, cnp) 1356 int mode; 1357 struct vnode *dvp; 1358 struct vnode **vpp; 1359 struct componentname *cnp; 1360 { 1361 struct inode *ip, *pdir; 1362 struct vnode *tvp; 1363 int error; 1364 1365 pdir = VTOI(dvp); 1366 #ifdef DIAGNOSTIC 1367 if ((cnp->cn_flags & HASBUF) == 0) 1368 panic("ext2fs_makeinode: no name"); 1369 #endif 1370 *vpp = NULL; 1371 if ((mode & IFMT) == 0) 1372 mode |= IFREG; 1373 1374 if ((error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) != 0) { 1375 PNBUF_PUT(cnp->cn_pnbuf); 1376 vput(dvp); 1377 return (error); 1378 } 1379 ip = VTOI(tvp); 1380 ip->i_e2fs_gid = pdir->i_e2fs_gid; 1381 ip->i_e2fs_uid = cnp->cn_cred->cr_uid; 1382 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; 1383 ip->i_e2fs_mode = mode; 1384 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ 1385 ip->i_e2fs_nlink = 1; 1386 if ((ip->i_e2fs_mode & ISGID) && 1387 !groupmember(ip->i_e2fs_gid, cnp->cn_cred) && 1388 suser(cnp->cn_cred, NULL)) 1389 ip->i_e2fs_mode &= ~ISGID; 1390 1391 /* 1392 * Make sure inode goes to disk before directory entry. 1393 */ 1394 if ((error = VOP_UPDATE(tvp, NULL, NULL, UPDATE_WAIT)) != 0) 1395 goto bad; 1396 error = ext2fs_direnter(ip, dvp, cnp); 1397 if (error != 0) 1398 goto bad; 1399 if ((cnp->cn_flags & SAVESTART) == 0) 1400 PNBUF_PUT(cnp->cn_pnbuf); 1401 vput(dvp); 1402 *vpp = tvp; 1403 return (0); 1404 1405 bad: 1406 /* 1407 * Write error occurred trying to update the inode 1408 * or the directory so must deallocate the inode. 1409 */ 1410 PNBUF_PUT(cnp->cn_pnbuf); 1411 vput(dvp); 1412 ip->i_e2fs_nlink = 0; 1413 ip->i_flag |= IN_CHANGE; 1414 vput(tvp); 1415 return (error); 1416 } 1417 1418 /* 1419 * Reclaim an inode so that it can be used for other purposes. 1420 */ 1421 int 1422 ext2fs_reclaim(v) 1423 void *v; 1424 { 1425 struct vop_reclaim_args /* { 1426 struct vnode *a_vp; 1427 } */ *ap = v; 1428 struct vnode *vp = ap->a_vp; 1429 struct inode *ip; 1430 1431 if (prtactive && vp->v_usecount != 0) 1432 vprint("ext2fs_reclaim: pushing active", vp); 1433 /* 1434 * Remove the inode from its hash chain. 1435 */ 1436 ip = VTOI(vp); 1437 ufs_ihashrem(ip); 1438 /* 1439 * Purge old data structures associated with the inode. 1440 */ 1441 cache_purge(vp); 1442 if (ip->i_devvp) { 1443 vrele(ip->i_devvp); 1444 ip->i_devvp = 0; 1445 } 1446 1447 if (ip->i_din.e2fs_din != NULL) 1448 pool_put(&ext2fs_dinode_pool, ip->i_din.e2fs_din); 1449 1450 pool_put(&ext2fs_inode_pool, vp->v_data); 1451 vp->v_data = NULL; 1452 return (0); 1453 } 1454 1455 /* Global vfs data structures for ext2fs. */ 1456 int (**ext2fs_vnodeop_p) __P((void *)); 1457 const struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = { 1458 { &vop_default_desc, vn_default_error }, 1459 { &vop_lookup_desc, ext2fs_lookup }, /* lookup */ 1460 { &vop_create_desc, ext2fs_create }, /* create */ 1461 { &vop_mknod_desc, ext2fs_mknod }, /* mknod */ 1462 { &vop_open_desc, ext2fs_open }, /* open */ 1463 { &vop_close_desc, ufs_close }, /* close */ 1464 { &vop_access_desc, ext2fs_access }, /* access */ 1465 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ 1466 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ 1467 { &vop_read_desc, ext2fs_read }, /* read */ 1468 { &vop_write_desc, ext2fs_write }, /* write */ 1469 { &vop_lease_desc, ufs_lease_check }, /* lease */ 1470 { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */ 1471 { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */ 1472 { &vop_poll_desc, ufs_poll }, /* poll */ 1473 { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ 1474 { &vop_revoke_desc, ufs_revoke }, /* revoke */ 1475 { &vop_mmap_desc, ufs_mmap }, /* mmap */ 1476 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ 1477 { &vop_seek_desc, ufs_seek }, /* seek */ 1478 { &vop_remove_desc, ext2fs_remove }, /* remove */ 1479 { &vop_link_desc, ext2fs_link }, /* link */ 1480 { &vop_rename_desc, ext2fs_rename }, /* rename */ 1481 { &vop_mkdir_desc, ext2fs_mkdir }, /* mkdir */ 1482 { &vop_rmdir_desc, ext2fs_rmdir }, /* rmdir */ 1483 { &vop_symlink_desc, ext2fs_symlink }, /* symlink */ 1484 { &vop_readdir_desc, ext2fs_readdir }, /* readdir */ 1485 { &vop_readlink_desc, ext2fs_readlink }, /* readlink */ 1486 { &vop_abortop_desc, ufs_abortop }, /* abortop */ 1487 { &vop_inactive_desc, ext2fs_inactive }, /* inactive */ 1488 { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ 1489 { &vop_lock_desc, ufs_lock }, /* lock */ 1490 { &vop_unlock_desc, ufs_unlock }, /* unlock */ 1491 { &vop_bmap_desc, ext2fs_bmap }, /* bmap */ 1492 { &vop_strategy_desc, ufs_strategy }, /* strategy */ 1493 { &vop_print_desc, ufs_print }, /* print */ 1494 { &vop_islocked_desc, ufs_islocked }, /* islocked */ 1495 { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */ 1496 { &vop_advlock_desc, ext2fs_advlock }, /* advlock */ 1497 { &vop_blkatoff_desc, ext2fs_blkatoff }, /* blkatoff */ 1498 { &vop_valloc_desc, ext2fs_valloc }, /* valloc */ 1499 { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ 1500 { &vop_truncate_desc, ext2fs_truncate }, /* truncate */ 1501 { &vop_update_desc, ext2fs_update }, /* update */ 1502 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ 1503 { &vop_getpages_desc, genfs_getpages }, /* getpages */ 1504 { &vop_putpages_desc, genfs_putpages }, /* putpages */ 1505 { NULL, NULL } 1506 }; 1507 const struct vnodeopv_desc ext2fs_vnodeop_opv_desc = 1508 { &ext2fs_vnodeop_p, ext2fs_vnodeop_entries }; 1509 1510 int (**ext2fs_specop_p) __P((void *)); 1511 const struct vnodeopv_entry_desc ext2fs_specop_entries[] = { 1512 { &vop_default_desc, vn_default_error }, 1513 { &vop_lookup_desc, spec_lookup }, /* lookup */ 1514 { &vop_create_desc, spec_create }, /* create */ 1515 { &vop_mknod_desc, spec_mknod }, /* mknod */ 1516 { &vop_open_desc, spec_open }, /* open */ 1517 { &vop_close_desc, ufsspec_close }, /* close */ 1518 { &vop_access_desc, ext2fs_access }, /* access */ 1519 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ 1520 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ 1521 { &vop_read_desc, ufsspec_read }, /* read */ 1522 { &vop_write_desc, ufsspec_write }, /* write */ 1523 { &vop_lease_desc, spec_lease_check }, /* lease */ 1524 { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ 1525 { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */ 1526 { &vop_poll_desc, spec_poll }, /* poll */ 1527 { &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */ 1528 { &vop_revoke_desc, spec_revoke }, /* revoke */ 1529 { &vop_mmap_desc, spec_mmap }, /* mmap */ 1530 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ 1531 { &vop_seek_desc, spec_seek }, /* seek */ 1532 { &vop_remove_desc, spec_remove }, /* remove */ 1533 { &vop_link_desc, spec_link }, /* link */ 1534 { &vop_rename_desc, spec_rename }, /* rename */ 1535 { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ 1536 { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ 1537 { &vop_symlink_desc, spec_symlink }, /* symlink */ 1538 { &vop_readdir_desc, spec_readdir }, /* readdir */ 1539 { &vop_readlink_desc, spec_readlink }, /* readlink */ 1540 { &vop_abortop_desc, spec_abortop }, /* abortop */ 1541 { &vop_inactive_desc, ext2fs_inactive }, /* inactive */ 1542 { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ 1543 { &vop_lock_desc, ufs_lock }, /* lock */ 1544 { &vop_unlock_desc, ufs_unlock }, /* unlock */ 1545 { &vop_bmap_desc, spec_bmap }, /* bmap */ 1546 { &vop_strategy_desc, spec_strategy }, /* strategy */ 1547 { &vop_print_desc, ufs_print }, /* print */ 1548 { &vop_islocked_desc, ufs_islocked }, /* islocked */ 1549 { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ 1550 { &vop_advlock_desc, spec_advlock }, /* advlock */ 1551 { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ 1552 { &vop_valloc_desc, spec_valloc }, /* valloc */ 1553 { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ 1554 { &vop_truncate_desc, spec_truncate }, /* truncate */ 1555 { &vop_update_desc, ext2fs_update }, /* update */ 1556 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ 1557 { &vop_getpages_desc, spec_getpages }, /* getpages */ 1558 { &vop_putpages_desc, spec_putpages }, /* putpages */ 1559 { NULL, NULL } 1560 }; 1561 const struct vnodeopv_desc ext2fs_specop_opv_desc = 1562 { &ext2fs_specop_p, ext2fs_specop_entries }; 1563 1564 int (**ext2fs_fifoop_p) __P((void *)); 1565 const struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = { 1566 { &vop_default_desc, vn_default_error }, 1567 { &vop_lookup_desc, fifo_lookup }, /* lookup */ 1568 { &vop_create_desc, fifo_create }, /* create */ 1569 { &vop_mknod_desc, fifo_mknod }, /* mknod */ 1570 { &vop_open_desc, fifo_open }, /* open */ 1571 { &vop_close_desc, ufsfifo_close }, /* close */ 1572 { &vop_access_desc, ext2fs_access }, /* access */ 1573 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */ 1574 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */ 1575 { &vop_read_desc, ufsfifo_read }, /* read */ 1576 { &vop_write_desc, ufsfifo_write }, /* write */ 1577 { &vop_lease_desc, fifo_lease_check }, /* lease */ 1578 { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ 1579 { &vop_fcntl_desc, ufs_fcntl }, /* fcntl */ 1580 { &vop_poll_desc, fifo_poll }, /* poll */ 1581 { &vop_kqfilter_desc, fifo_kqfilter }, /* kqfilter */ 1582 { &vop_revoke_desc, fifo_revoke }, /* revoke */ 1583 { &vop_mmap_desc, fifo_mmap }, /* mmap */ 1584 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */ 1585 { &vop_seek_desc, fifo_seek }, /* seek */ 1586 { &vop_remove_desc, fifo_remove }, /* remove */ 1587 { &vop_link_desc, fifo_link }, /* link */ 1588 { &vop_rename_desc, fifo_rename }, /* rename */ 1589 { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ 1590 { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ 1591 { &vop_symlink_desc, fifo_symlink }, /* symlink */ 1592 { &vop_readdir_desc, fifo_readdir }, /* readdir */ 1593 { &vop_readlink_desc, fifo_readlink }, /* readlink */ 1594 { &vop_abortop_desc, fifo_abortop }, /* abortop */ 1595 { &vop_inactive_desc, ext2fs_inactive }, /* inactive */ 1596 { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */ 1597 { &vop_lock_desc, ufs_lock }, /* lock */ 1598 { &vop_unlock_desc, ufs_unlock }, /* unlock */ 1599 { &vop_bmap_desc, fifo_bmap }, /* bmap */ 1600 { &vop_strategy_desc, fifo_strategy }, /* strategy */ 1601 { &vop_print_desc, ufs_print }, /* print */ 1602 { &vop_islocked_desc, ufs_islocked }, /* islocked */ 1603 { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ 1604 { &vop_advlock_desc, fifo_advlock }, /* advlock */ 1605 { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ 1606 { &vop_valloc_desc, fifo_valloc }, /* valloc */ 1607 { &vop_vfree_desc, ext2fs_vfree }, /* vfree */ 1608 { &vop_truncate_desc, fifo_truncate }, /* truncate */ 1609 { &vop_update_desc, ext2fs_update }, /* update */ 1610 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ 1611 { &vop_putpages_desc, fifo_putpages }, /* putpages */ 1612 { NULL, NULL } 1613 }; 1614 const struct vnodeopv_desc ext2fs_fifoop_opv_desc = 1615 { &ext2fs_fifoop_p, ext2fs_fifoop_entries }; 1616