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