1 /* $NetBSD: v7fs_vnops.c,v 1.4 2011/07/16 12:35:40 uch Exp $ */ 2 3 /*- 4 * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: v7fs_vnops.c,v 1.4 2011/07/16 12:35:40 uch Exp $"); 34 #if defined _KERNEL_OPT 35 #include "opt_v7fs.h" 36 #endif 37 38 #include <sys/param.h> 39 #include <sys/kernel.h> 40 #include <sys/resource.h> 41 #include <sys/vnode.h> 42 #include <sys/namei.h> 43 #include <sys/dirent.h> 44 #include <sys/malloc.h> 45 #include <sys/lockf.h> 46 #include <sys/unistd.h> 47 #include <sys/fcntl.h> 48 #include <sys/kauth.h> 49 #include <sys/buf.h> 50 #include <sys/stat.h> /*APPEND */ 51 #include <miscfs/genfs/genfs.h> 52 53 #include <fs/v7fs/v7fs.h> 54 #include <fs/v7fs/v7fs_impl.h> 55 #include <fs/v7fs/v7fs_inode.h> 56 #include <fs/v7fs/v7fs_dirent.h> 57 #include <fs/v7fs/v7fs_file.h> 58 #include <fs/v7fs/v7fs_datablock.h> 59 #include <fs/v7fs/v7fs_extern.h> 60 61 #ifdef V7FS_VNOPS_DEBUG 62 #define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) 63 #else 64 #define DPRINTF(arg...) ((void)0) 65 #endif 66 67 MALLOC_JUSTDEFINE(M_V7FS_VNODE, "v7fs vnode", "v7fs vnode structures"); 68 MALLOC_DECLARE(M_V7FS); 69 70 static v7fs_mode_t vtype_to_v7fs_mode(enum vtype); 71 static uint8_t v7fs_mode_to_d_type(v7fs_mode_t); 72 73 static v7fs_mode_t 74 vtype_to_v7fs_mode(enum vtype type) 75 { 76 /* Convert Vnode types to V7FS types (sys/vnode.h)*/ 77 v7fs_mode_t table[] = { 0, V7FS_IFREG, V7FS_IFDIR, V7FS_IFBLK, 78 V7FS_IFCHR, V7FSBSD_IFLNK, V7FSBSD_IFSOCK, 79 V7FSBSD_IFFIFO }; 80 return table[type]; 81 } 82 83 static uint8_t 84 v7fs_mode_to_d_type(v7fs_mode_t mode) 85 { 86 /* Convert V7FS types to dirent d_type (sys/dirent.h)*/ 87 88 return (mode & V7FS_IFMT) >> 12; 89 } 90 91 int 92 v7fs_lookup(void *v) 93 { 94 struct vop_lookup_args /* { 95 struct vnode *a_dvp; 96 struct vnode **a_vpp; 97 struct componentname *a_cnp; 98 } */ *a = v; 99 struct vnode *dvp = a->a_dvp; 100 struct v7fs_node *parent_node = dvp->v_data; 101 struct v7fs_inode *parent = &parent_node->inode; 102 struct v7fs_self *fs = parent_node->v7fsmount->core;/* my filesystem */ 103 struct vnode *vpp; 104 struct componentname *cnp = a->a_cnp; 105 int nameiop = cnp->cn_nameiop; 106 const char *name = cnp->cn_nameptr; 107 int namelen = cnp->cn_namelen; 108 int flags = cnp->cn_flags; 109 bool isdotdot = flags & ISDOTDOT; 110 bool islastcn = flags & ISLASTCN; 111 v7fs_ino_t ino; 112 int error; 113 114 DPRINTF("%s op=%d flags=%d parent=%d %o %dbyte\n", name, 115 nameiop, cnp->cn_flags, parent->inode_number, parent->mode, 116 parent->filesize); 117 118 *a->a_vpp = 0; 119 120 /* Check directory permission for search */ 121 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred))) { 122 DPRINTF("***perm.\n"); 123 return error; 124 } 125 126 /* Deny last component write operation on a read-only mount */ 127 if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) && 128 (nameiop == DELETE || nameiop == RENAME)) { 129 DPRINTF("***ROFS.\n"); 130 return EROFS; 131 } 132 133 /* "." */ 134 if (namelen == 1 && name[0] == '.') { 135 vref(dvp); /* v_usecount++ */ 136 *a->a_vpp = dvp; 137 DPRINTF("done.(.)\n"); 138 return 0; 139 } 140 141 /* ".." and reguler file. */ 142 if ((error = v7fs_file_lookup_by_name(fs, parent, name, &ino))) { 143 /* Not found. Tell this entry be able to allocate. */ 144 if (((nameiop == CREATE) || (nameiop == RENAME)) && islastcn) { 145 /* Check directory permission to allocate. */ 146 if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) { 147 DPRINTF("access denied. (%s)\n", name); 148 return error; 149 } 150 DPRINTF("EJUSTRETURN op=%d (%s)\n", nameiop, name); 151 return EJUSTRETURN; 152 } 153 DPRINTF("lastcn=%d\n", flags & ISLASTCN); 154 return error; 155 } 156 157 /* Entry found. Allocate v-node */ 158 // Check permissions? 159 vpp = 0; 160 if (isdotdot) { 161 VOP_UNLOCK(dvp); /* preserve reference count. (not vput) */ 162 } 163 DPRINTF("enter vget\n"); 164 if ((error = v7fs_vget(dvp->v_mount, ino, &vpp))) { 165 DPRINTF("***can't get vnode.\n"); 166 return error; 167 } 168 DPRINTF("exit vget\n"); 169 if (isdotdot) { 170 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 171 } 172 *a->a_vpp = vpp; 173 DPRINTF("done.\n"); 174 175 return 0; 176 } 177 178 int 179 v7fs_create(void *v) 180 { 181 struct vop_create_args /* { 182 struct vnode *a_dvp; 183 struct vnode **a_vpp; 184 struct componentname *a_cnp; 185 struct vattr *a_vap; 186 } */ *a = v; 187 struct v7fs_node *parent_node = a->a_dvp->v_data; 188 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 189 struct v7fs_self *fs = v7fsmount->core; 190 struct mount *mp = v7fsmount->mountp; 191 struct v7fs_fileattr attr; 192 struct vattr *va = a->a_vap; 193 kauth_cred_t cr = a->a_cnp->cn_cred; 194 v7fs_ino_t ino; 195 int error = 0; 196 197 DPRINTF("%s parent#%d\n", a->a_cnp->cn_nameptr, 198 parent_node->inode.inode_number); 199 KDASSERT((va->va_type == VREG) || (va->va_type == VSOCK)); 200 201 memset(&attr, 0, sizeof(attr)); 202 attr.uid = kauth_cred_geteuid(cr); 203 attr.gid = kauth_cred_getegid(cr); 204 attr.mode = va->va_mode | vtype_to_v7fs_mode (va->va_type); 205 attr.device = 0; 206 207 /* Allocate disk entry. and register its entry to parent directory. */ 208 if ((error = v7fs_file_allocate(fs, &parent_node->inode, 209 a->a_cnp->cn_nameptr, &attr, &ino))) { 210 DPRINTF("v7fs_file_allocate failed.\n"); 211 goto unlock_exit; 212 } 213 /* Sync dirent size change. */ 214 uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); 215 216 /* Get myself vnode. */ 217 *a->a_vpp = 0; 218 if ((error = v7fs_vget(mp, ino, a->a_vpp))) { 219 DPRINTF("v7fs_vget failed.\n"); 220 goto unlock_exit; 221 } 222 223 /* Scheduling update time. real update by v7fs_update */ 224 struct v7fs_node *newnode = (*a->a_vpp)->v_data; 225 newnode->update_ctime = true; 226 newnode->update_mtime = true; 227 newnode->update_atime = true; 228 DPRINTF("allocated %s->#%d\n", a->a_cnp->cn_nameptr, ino); 229 230 unlock_exit: 231 /* unlock parent directory */ 232 vput(a->a_dvp); /* locked at v7fs_lookup(); */ 233 234 return error; 235 } 236 237 int 238 v7fs_mknod(void *v) 239 { 240 struct vop_mknod_args /* { 241 struct vnode *a_dvp; 242 struct vnode **a_vpp; 243 struct componentname *a_cnp; 244 struct vattr *a_vap; 245 } */ *a = v; 246 struct componentname *cnp = a->a_cnp; 247 kauth_cred_t cr = cnp->cn_cred; 248 struct vnode *dvp = a->a_dvp; 249 struct vattr *va = a->a_vap; 250 struct v7fs_node *parent_node = dvp->v_data; 251 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 252 struct v7fs_self *fs = v7fsmount->core; 253 struct mount *mp = v7fsmount->mountp; 254 struct v7fs_fileattr attr; 255 256 v7fs_ino_t ino; 257 int error = 0; 258 259 DPRINTF("%s %06o %lx %d\n", cnp->cn_nameptr, va->va_mode, 260 (long)va->va_rdev, va->va_type); 261 memset(&attr, 0, sizeof(attr)); 262 attr.uid = kauth_cred_geteuid(cr); 263 attr.gid = kauth_cred_getegid(cr); 264 attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); 265 attr.device = va->va_rdev; 266 267 if ((error = v7fs_file_allocate(fs, &parent_node->inode, 268 cnp->cn_nameptr, &attr, &ino))) 269 goto unlock_exit; 270 /* Sync dirent size change. */ 271 uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); 272 273 if ((error = v7fs_vget(mp, ino, a->a_vpp))) { 274 DPRINTF("can't get vnode.\n"); 275 goto unlock_exit; 276 } 277 struct v7fs_node *newnode = (*a->a_vpp)->v_data; 278 newnode->update_ctime = true; 279 newnode->update_mtime = true; 280 newnode->update_atime = true; 281 282 unlock_exit: 283 vput(dvp); 284 285 return error; 286 } 287 288 int 289 v7fs_open(void *v) 290 { 291 struct vop_open_args /* { 292 struct vnode *a_vp; 293 int a_mode; 294 kauth_cred_t a_cred; 295 } */ *a = v; 296 297 struct vnode *vp = a->a_vp; 298 struct v7fs_node *v7node = vp->v_data; 299 struct v7fs_inode *inode = &v7node->inode; 300 301 DPRINTF("inode %d\n", inode->inode_number); 302 /* Append mode file pointer is managed by kernel. */ 303 if (inode->append_mode && 304 ((a->a_mode & (FWRITE | O_APPEND)) == FWRITE)) { 305 DPRINTF("file is already opened by append mode.\n"); 306 return EPERM; 307 } 308 309 return 0; 310 } 311 312 int 313 v7fs_close(void *v) 314 { 315 struct vop_close_args /* { 316 struct vnodeop_desc *a_desc; 317 struct vnode *a_vp; 318 int a_fflag; 319 kauth_cred_t a_cred; 320 } */ *a = v; 321 struct vnode *vp = a->a_vp; 322 #ifdef V7FS_VNOPS_DEBUG 323 struct v7fs_node *v7node = vp->v_data; 324 struct v7fs_inode *inode = &v7node->inode; 325 #endif 326 DPRINTF("#%d (i)%dbyte (v)%zubyte\n", inode->inode_number, 327 v7fs_inode_filesize(inode), vp->v_size); 328 329 /* Update timestamp */ 330 v7fs_update(vp, 0, 0, UPDATE_WAIT); 331 332 return 0; 333 } 334 335 static int 336 v7fs_check_possible(struct vnode *vp, struct v7fs_node *v7node, 337 mode_t mode) 338 { 339 340 if (!(mode & VWRITE)) 341 return 0; 342 343 switch (vp->v_type) { 344 default: 345 /* special file is always writable. */ 346 return 0; 347 case VDIR: 348 case VLNK: 349 case VREG: 350 break; 351 } 352 353 return vp->v_mount->mnt_flag & MNT_RDONLY ? EROFS : 0; 354 } 355 356 static int 357 v7fs_check_permitted(struct vnode *vp, struct v7fs_node *v7node, 358 mode_t mode, kauth_cred_t cred) 359 { 360 361 struct v7fs_inode *inode = &v7node->inode; 362 363 return genfs_can_access(vp->v_type, inode->mode, inode->uid, inode->gid, 364 mode, cred); 365 } 366 367 int 368 v7fs_access(void *v) 369 { 370 struct vop_access_args /* { 371 struct vnode *a_vp; 372 int a_mode; 373 kauth_cred_t a_cred; 374 } */ *ap = v; 375 struct vnode *vp = ap->a_vp; 376 struct v7fs_node *v7node = vp->v_data; 377 int error; 378 379 error = v7fs_check_possible(vp, v7node, ap->a_mode); 380 if (error) 381 return error; 382 383 error = v7fs_check_permitted(vp, v7node, ap->a_mode, ap->a_cred); 384 385 return error; 386 } 387 388 int 389 v7fs_getattr(void *v) 390 { 391 struct vop_getattr_args /* { 392 struct vnode *a_vp; 393 struct vattr *a_vap; 394 kauth_cred_t a_cred; 395 } */ *ap = v; 396 struct vnode *vp = ap->a_vp; 397 struct v7fs_node *v7node = vp->v_data; 398 struct v7fs_inode *inode = &v7node->inode; 399 struct v7fs_mount *v7fsmount = v7node->v7fsmount; 400 struct vattr *vap = ap->a_vap; 401 402 DPRINTF("\n"); 403 vap->va_type = vp->v_type; 404 vap->va_mode = inode->mode; 405 vap->va_nlink = inode->nlink; 406 vap->va_uid = inode->uid; 407 vap->va_gid = inode->gid; 408 vap->va_fsid = v7fsmount->devvp->v_rdev; 409 vap->va_fileid = inode->inode_number; 410 vap->va_size = vp->v_size; 411 vap->va_atime.tv_sec = inode->atime; 412 vap->va_mtime.tv_sec = inode->mtime; 413 vap->va_ctime.tv_sec = inode->ctime; 414 vap->va_birthtime.tv_sec = 0; 415 vap->va_gen = 1; 416 vap->va_flags = 0; 417 vap->va_rdev = inode->device; 418 vap->va_bytes = vap->va_size; /* No sparse support. */ 419 vap->va_filerev = 0; 420 vap->va_vaflags = 0; 421 /* PAGE_SIZE is larger than sizeof(struct dirent). OK. 422 getcwd_scandir()@vfs_getcwd.c */ 423 vap->va_blocksize = PAGE_SIZE; 424 425 return 0; 426 } 427 428 int 429 v7fs_setattr(void *v) 430 { 431 struct vop_setattr_args /* { 432 struct vnode *a_vp; 433 struct vattr *a_vap; 434 kauth_cred_t a_cred; 435 struct proc *p; 436 } */ *ap = v; 437 struct vnode *vp = ap->a_vp; 438 struct vattr *vap = ap->a_vap; 439 struct v7fs_node *v7node = vp->v_data; 440 struct v7fs_self *fs = v7node->v7fsmount->core; 441 struct v7fs_inode *inode = &v7node->inode; 442 struct timespec *acc, *mod; 443 int error = 0; 444 acc = mod = NULL; 445 446 DPRINTF("\n"); 447 448 if (vp->v_mount->mnt_flag & MNT_RDONLY) { 449 switch (vp->v_type) { 450 default: 451 /* special file is always writable. */ 452 break; 453 case VDIR: 454 case VLNK: 455 case VREG: 456 DPRINTF("read-only mount\n"); 457 return EROFS; 458 } 459 } 460 461 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 462 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 463 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 464 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 465 DPRINTF("invalid request\n"); 466 return EINVAL; 467 } 468 /* File pointer mode. */ 469 if ((vap->va_flags != VNOVAL) && (vap->va_flags & SF_APPEND)) { 470 DPRINTF("Set append-mode.\n"); 471 inode->append_mode = true; 472 } 473 474 /* File size change. */ 475 if ((vap->va_size != VNOVAL) && (vp->v_type == VREG)) { 476 error = v7fs_datablock_size_change(fs, vap->va_size, inode); 477 if (error == 0) 478 uvm_vnp_setsize(vp, vap->va_size); 479 } 480 481 if (vap->va_uid != (uid_t)VNOVAL) { 482 inode->uid = vap->va_uid; 483 } 484 if (vap->va_gid != (uid_t)VNOVAL) { 485 inode->gid = vap->va_gid; 486 } 487 if (vap->va_mode != (mode_t)VNOVAL) { 488 v7fs_inode_chmod(inode, vap->va_mode); 489 } 490 if (vap->va_atime.tv_sec != VNOVAL) { 491 acc = &vap->va_atime; 492 } 493 if (vap->va_mtime.tv_sec != VNOVAL) { 494 mod = &vap->va_mtime; 495 v7node->update_mtime = true; 496 } 497 if (vap->va_ctime.tv_sec != VNOVAL) { 498 v7node->update_ctime = true; 499 } 500 501 v7node->update_atime = true; 502 v7fs_update(vp, acc, mod, 0); 503 504 return error; 505 } 506 507 int 508 v7fs_read(void *v) 509 { 510 struct vop_read_args /* { 511 struct vnode *a_vp; 512 struct uio *a_uio; 513 int a_ioflag; 514 kauth_cred_t a_cred; 515 } */ *a = v; 516 struct vnode *vp = a->a_vp; 517 struct uio *uio = a->a_uio; 518 struct v7fs_node *v7node = vp->v_data; 519 struct v7fs_inode *inode = &v7node->inode; 520 vsize_t sz, filesz = v7fs_inode_filesize(inode); 521 const int advice = IO_ADV_DECODE(a->a_ioflag); 522 int error = 0; 523 524 DPRINTF("type=%d inode=%d\n", vp->v_type, v7node->inode.inode_number); 525 526 while (uio->uio_resid > 0) { 527 if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0) 528 break; 529 530 error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, UBC_READ | 531 UBC_PARTIALOK | UBC_UNMAP_FLAG(v)); 532 if (error) { 533 break; 534 } 535 DPRINTF("read %zubyte\n", sz); 536 } 537 v7node->update_atime = true; 538 539 return error; 540 } 541 542 int 543 v7fs_write(void *v) 544 { 545 struct vop_write_args /* { 546 struct vnode *a_vp; 547 struct uio *a_uio; 548 int a_ioflag; 549 kauth_cred_t a_cred; 550 } */ *a = v; 551 struct vnode *vp = a->a_vp; 552 struct uio *uio = a->a_uio; 553 int advice = IO_ADV_DECODE(a->a_ioflag); 554 struct v7fs_node *v7node = vp->v_data; 555 struct v7fs_inode *inode = &v7node->inode; 556 struct v7fs_self *fs = v7node->v7fsmount->core; 557 vsize_t sz; 558 int error = 0; 559 560 if (uio->uio_resid == 0) 561 return 0; 562 563 sz = v7fs_inode_filesize(inode); 564 DPRINTF("(i)%ld (v)%zu ofs=%zu + res=%zu = %zu\n", sz, vp->v_size, 565 uio->uio_offset, uio->uio_resid, uio->uio_offset + uio->uio_resid); 566 567 /* Append mode file offset is managed by kernel. */ 568 if (a->a_ioflag & IO_APPEND) 569 uio->uio_offset = sz; 570 571 /* If write region is over filesize, expand. */ 572 size_t newsize= uio->uio_offset + uio->uio_resid; 573 ssize_t expand = newsize - sz; 574 if (expand > 0) { 575 if ((error = v7fs_datablock_expand(fs, inode, expand))) 576 return error; 577 uvm_vnp_setsize(vp, newsize); 578 } 579 580 while (uio->uio_resid > 0) { 581 sz = uio->uio_resid; 582 if ((error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, 583 UBC_WRITE | UBC_UNMAP_FLAG(v)))) 584 break; 585 DPRINTF("write %zubyte\n", sz); 586 } 587 v7node->update_mtime = true; 588 589 return error; 590 } 591 592 int 593 v7fs_fsync(void *v) 594 { 595 struct vop_fsync_args /* { 596 struct vnode *a_vp; 597 kauth_cred_t a_cred; 598 int a_flags; 599 off_t offlo; 600 off_t offhi; 601 } */ *a = v; 602 struct vnode *vp = a->a_vp; 603 int error, wait; 604 605 DPRINTF("%p\n", a->a_vp); 606 if (a->a_flags & FSYNC_CACHE) { 607 return EOPNOTSUPP; 608 } 609 610 wait = (a->a_flags & FSYNC_WAIT); 611 error = vflushbuf(vp, wait); 612 613 if (error == 0 && (a->a_flags & FSYNC_DATAONLY) == 0) 614 error = v7fs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0); 615 616 return error; 617 } 618 619 int 620 v7fs_remove(void *v) 621 { 622 struct vop_remove_args /* { 623 struct vnodeop_desc *a_desc; 624 struct vnode * a_dvp; 625 struct vnode * a_vp; 626 struct componentname * a_cnp; 627 } */ *a = v; 628 struct v7fs_node *parent_node = a->a_dvp->v_data; 629 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 630 struct vnode *vp = a->a_vp; 631 struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; 632 struct vnode *dvp = a->a_dvp; 633 struct v7fs_self *fs = v7fsmount->core; 634 bool remove; 635 int error = 0; 636 637 DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); 638 639 if (vp->v_type == VDIR) { 640 error = EPERM; 641 goto out; 642 } 643 644 remove = v7fs_inode_nlink(inode) == 1; 645 if (remove) 646 uvm_vnp_setsize(vp, 0); 647 648 if ((error = v7fs_file_deallocate(fs, &parent_node->inode, 649 a->a_cnp->cn_nameptr))) { 650 DPRINTF("v7fs_file_delete failed.\n"); 651 goto out; 652 } 653 /* Sync dirent size change. */ 654 uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); 655 /* This inode is no longer used. -> v7fs_inactive */ 656 if (remove) 657 memset(inode, 0, sizeof(*inode)); 658 659 out: 660 if (dvp == vp) 661 vrele(vp); /* v_usecount-- of unlocked vp */ 662 else 663 vput(vp); /* unlock vp and then v_usecount-- */ 664 vput(dvp); 665 666 return error; 667 } 668 669 int 670 v7fs_link(void *v) 671 { 672 struct vop_link_args /* { 673 struct vnode *a_dvp; 674 struct vnode *a_vp; 675 struct componentname *a_cnp; 676 } */ *a = v; 677 struct vnode *dvp = a->a_dvp; 678 struct vnode *vp = a->a_vp; 679 struct v7fs_node *parent_node = dvp->v_data; 680 struct v7fs_node *node = vp->v_data; 681 struct v7fs_inode *parent = &parent_node->inode; 682 struct v7fs_inode *p = &node->inode; 683 struct v7fs_self *fs = node->v7fsmount->core; 684 struct componentname *cnp = a->a_cnp; 685 int error = 0; 686 687 DPRINTF("%p\n", vp); 688 /* Lock soruce file */ 689 if ((error = vn_lock(vp, LK_EXCLUSIVE))) { 690 DPRINTF("lock failed. %p\n", vp); 691 VOP_ABORTOP(dvp, cnp); 692 goto unlock; 693 } 694 error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr); 695 /* Sync dirent size change. */ 696 uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); 697 698 VOP_UNLOCK(vp); 699 unlock: 700 vput(dvp); 701 702 return error; 703 } 704 705 int 706 v7fs_rename(void *v) 707 { 708 struct vop_rename_args /* { 709 struct vnode *a_fdvp; from parent-directory 710 struct vnode *a_fvp; from file 711 struct componentname *a_fcnp; 712 struct vnode *a_tdvp; to parent-directory 713 struct vnode *a_tvp; to file 714 struct componentname *a_tcnp; 715 } */ *a = v; 716 struct vnode *fvp = a->a_fvp; 717 struct vnode *tvp = a->a_tvp; 718 struct vnode *fdvp = a->a_fdvp; 719 struct vnode *tdvp = a->a_tdvp; 720 struct v7fs_node *parent_from = fdvp->v_data; 721 struct v7fs_node *parent_to = tdvp->v_data; 722 struct v7fs_node *v7node = fvp->v_data; 723 struct v7fs_self *fs = v7node->v7fsmount->core; 724 const char *from_name = a->a_fcnp->cn_nameptr; 725 const char *to_name = a->a_tcnp->cn_nameptr; 726 int error; 727 728 DPRINTF("%s->%s\n", from_name, to_name); 729 /* tvp may be NULL. allocated here. */ 730 if ((fvp->v_mount != tdvp->v_mount) || 731 (tvp && (fvp->v_mount != tvp->v_mount))) { 732 error = EXDEV; 733 DPRINTF("cross-device link\n"); 734 goto out; 735 } 736 // XXXsource file lock? 737 error = v7fs_file_rename(fs, &parent_from->inode, from_name, 738 &parent_to->inode, to_name); 739 /* Sync dirent size change. */ 740 uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode)); 741 uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode)); 742 out: 743 if (tvp) 744 vput(tvp); /* locked on entry */ 745 if (tdvp == tvp) 746 vrele(tdvp); 747 else 748 vput(tdvp); 749 vrele(fdvp); 750 vrele(fvp); 751 752 return error; 753 } 754 755 int 756 v7fs_mkdir(void *v) 757 { 758 struct vop_mkdir_args /* { 759 struct vnode *a_dvp; 760 struct vnode **a_vpp; 761 struct componentname *a_cnp; 762 struct vattr *a_vap; 763 } */ *a = v; 764 struct componentname *cnp = a->a_cnp; 765 kauth_cred_t cr = cnp->cn_cred; 766 struct vnode *dvp = a->a_dvp; 767 struct vattr *va = a->a_vap; 768 struct v7fs_node *parent_node = dvp->v_data; 769 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 770 struct v7fs_self *fs = v7fsmount->core; 771 struct v7fs_fileattr attr; 772 struct mount *mp = v7fsmount->mountp; 773 v7fs_ino_t ino; 774 int error = 0; 775 776 DPRINTF("\n"); 777 memset(&attr, 0, sizeof(attr)); 778 attr.uid = kauth_cred_geteuid(cr); 779 attr.gid = kauth_cred_getegid(cr); 780 attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); 781 782 if ((error = v7fs_file_allocate(fs, &parent_node->inode, 783 cnp->cn_nameptr, &attr, &ino))) 784 goto unlock_exit; 785 /* Sync dirent size change. */ 786 uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); 787 788 if ((error = v7fs_vget(mp, ino, a->a_vpp))) { 789 DPRINTF("can't get vnode.\n"); 790 } 791 struct v7fs_node *newnode = (*a->a_vpp)->v_data; 792 newnode->update_ctime = true; 793 newnode->update_mtime = true; 794 newnode->update_atime = true; 795 796 unlock_exit: 797 vput(dvp); 798 799 return error; 800 } 801 802 int 803 v7fs_rmdir(void *v) 804 { 805 struct vop_rmdir_args /* { 806 struct vnode *a_dvp; 807 struct vnode *a_vp; 808 struct componentname *a_cnp; 809 } */ *a = v; 810 struct vnode *vp = a->a_vp; 811 struct vnode *dvp = a->a_dvp; 812 struct v7fs_node *parent_node = dvp->v_data; 813 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 814 struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; 815 struct v7fs_self *fs = v7fsmount->core; 816 int error = 0; 817 818 DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); 819 820 KDASSERT(vp->v_type == VDIR); 821 822 if ((error = v7fs_file_deallocate(fs, &parent_node->inode, 823 a->a_cnp->cn_nameptr))) { 824 DPRINTF("v7fs_directory_deallocate failed.\n"); 825 goto out; 826 } 827 uvm_vnp_setsize(vp, 0); 828 /* Sync dirent size change. */ 829 uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); 830 /* This inode is no longer used. -> v7fs_inactive */ 831 memset(inode, 0, sizeof(*inode)); 832 out: 833 vput(vp); 834 vput(dvp); 835 836 return error; 837 } 838 839 struct v7fs_readdir_arg { 840 struct dirent *dp; 841 struct uio *uio; 842 int start; 843 int end; 844 int cnt; 845 }; 846 static int readdir_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); 847 848 int 849 readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) 850 { 851 struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx; 852 struct v7fs_dirent *dir; 853 struct dirent *dp = p->dp; 854 struct v7fs_inode inode; 855 char filename[V7FS_NAME_MAX + 1]; 856 int i, n; 857 int error = 0; 858 void *buf; 859 860 if (!(buf = scratch_read(fs, blk))) 861 return EIO; 862 dir = (struct v7fs_dirent *)buf; 863 864 n = sz / sizeof(*dir); 865 866 for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) { 867 if (p->cnt < p->start) 868 continue; 869 870 if ((error = v7fs_inode_load(fs, &inode, dir->inode_number))) 871 break; 872 873 v7fs_dirent_filename(filename, dir->name); 874 875 DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename, 876 v7fs_inode_isdir(&inode) ? "DIR" : "FILE"); 877 memset(dp, 0, sizeof(*dp)); 878 dp->d_fileno = dir->inode_number; 879 dp->d_type = v7fs_mode_to_d_type(inode.mode); 880 dp->d_namlen = strlen(filename); 881 strcpy(dp->d_name, filename); 882 dp->d_reclen = sizeof(*dp); 883 if ((error = uiomove(dp, dp->d_reclen, p->uio))) { 884 DPRINTF("uiomove failed.\n"); 885 break; 886 } 887 } 888 scratch_free(fs, buf); 889 890 if (p->cnt == p->end) 891 return V7FS_ITERATOR_BREAK; 892 893 return error; 894 } 895 896 int 897 v7fs_readdir(void *v) 898 { 899 struct vop_readdir_args /* { 900 struct vnode *a_vp; 901 struct uio *a_uio; 902 kauth_cred_t a_cred; 903 int *a_eofflag; 904 off_t **a_cookies; 905 int *a_ncookies; 906 } */ *a = v; 907 struct uio *uio = a->a_uio; 908 struct vnode *vp = a->a_vp; 909 struct v7fs_node *v7node = vp->v_data; 910 struct v7fs_inode *inode = &v7node->inode; 911 struct v7fs_self *fs = v7node->v7fsmount->core; 912 struct dirent *dp; 913 int error; 914 915 DPRINTF("offset=%zu residue=%zu\n", uio->uio_offset, uio->uio_resid); 916 917 KDASSERT(vp->v_type == VDIR); 918 KDASSERT(uio->uio_offset >= 0); 919 KDASSERT(v7fs_inode_isdir(inode)); 920 921 struct v7fs_readdir_arg arg; 922 arg.start = uio->uio_offset / sizeof(*dp); 923 arg.end = arg.start + uio->uio_resid / sizeof(*dp); 924 if (arg.start == arg.end) {/* user buffer has not enuf space. */ 925 DPRINTF("uio buffer too small\n"); 926 return ENOMEM; 927 } 928 dp = malloc(sizeof(*dp), M_V7FS, M_WAITOK | M_ZERO); 929 arg.cnt = 0; 930 arg.dp = dp; 931 arg.uio = uio; 932 933 *a->a_eofflag = false; 934 error = v7fs_datablock_foreach(fs, inode, readdir_subr, &arg); 935 if (error == V7FS_ITERATOR_END) { 936 *a->a_eofflag = true; 937 } 938 if (error < 0) 939 error = 0; 940 941 free(dp, M_V7FS); 942 943 return error; 944 } 945 946 int 947 v7fs_inactive(void *v) 948 { 949 struct vop_inactive_args /* { 950 struct vnode *a_vp; 951 bool *a_recycle; 952 } */ *a = v; 953 struct vnode *vp = a->a_vp; 954 struct v7fs_node *v7node = vp->v_data; 955 struct v7fs_inode *inode = &v7node->inode; 956 957 DPRINTF("%p\n", vp); 958 959 if (v7fs_inode_allocated(inode)) { 960 v7fs_update(vp, 0, 0, UPDATE_WAIT); 961 } 962 963 *a->a_recycle = true; 964 965 VOP_UNLOCK(vp); 966 967 return 0; 968 } 969 970 int 971 v7fs_reclaim(void *v) 972 { 973 /*This vnode is no longer referenced by kernel. */ 974 extern struct pool v7fs_node_pool; 975 struct vop_reclaim_args /* { 976 struct vnode *a_vp; 977 } */ *a = v; 978 struct vnode *vp = a->a_vp; 979 struct v7fs_node *v7node = vp->v_data; 980 981 DPRINTF("%p\n", vp); 982 mutex_enter(&mntvnode_lock); 983 LIST_REMOVE(v7node, link); 984 mutex_exit(&mntvnode_lock); 985 genfs_node_destroy(vp); 986 pool_put(&v7fs_node_pool, v7node); 987 vp->v_data = NULL; 988 989 return 0; 990 } 991 992 int 993 v7fs_bmap(void *v) 994 { 995 struct vop_bmap_args /* { 996 struct vnode *a_vp; 997 daddr_t a_bn; 998 struct vnode **a_vpp; 999 daddr_t *a_bnp; 1000 int *a_runp; 1001 } */ *a = v; 1002 struct vnode *vp = a->a_vp; 1003 struct v7fs_node *v7node = vp->v_data; 1004 struct v7fs_mount *v7fsmount = v7node->v7fsmount; 1005 struct v7fs_self *fs = v7node->v7fsmount->core; 1006 struct v7fs_inode *inode = &v7node->inode; 1007 int error = 0; 1008 1009 DPRINTF("inode=%d offset=%zu %p\n", inode->inode_number, a->a_bn, vp); 1010 DPRINTF("filesize: %d\n", inode->filesize); 1011 if (!a->a_bnp) 1012 return 0; 1013 1014 v7fs_daddr_t blk; 1015 if (!(blk = v7fs_datablock_last(fs, inode, 1016 (a->a_bn + 1) << V7FS_BSHIFT))) { 1017 /* +1 converts block # to file offset. */ 1018 return ENOSPC; 1019 } 1020 1021 *a->a_bnp = blk; 1022 1023 if (a->a_vpp) 1024 *a->a_vpp = v7fsmount->devvp; 1025 if (a->a_runp) 1026 *a->a_runp = 0; /*XXX TODO */ 1027 1028 DPRINTF("%d %zu->%zu status=%d\n", inode->inode_number, a->a_bn, 1029 *a->a_bnp, error); 1030 1031 return error; 1032 } 1033 1034 int 1035 v7fs_strategy(void *v) 1036 { 1037 struct vop_strategy_args /* { 1038 struct vnode *a_vp; 1039 struct buf *a_bp; 1040 } */ *a = v; 1041 struct buf *b = a->a_bp; 1042 struct vnode *vp = a->a_vp; 1043 struct v7fs_node *v7node = vp->v_data; 1044 struct v7fs_mount *v7fsmount = v7node->v7fsmount; 1045 int error; 1046 1047 DPRINTF("%p\n", vp); 1048 KDASSERT(vp->v_type == VREG); 1049 if (b->b_blkno == b->b_lblkno) { 1050 error = VOP_BMAP(vp, b->b_lblkno, NULL, &b->b_blkno, NULL); 1051 if (error) { 1052 b->b_error = error; 1053 biodone(b); 1054 return error; 1055 } 1056 if ((long)b->b_blkno == -1) 1057 clrbuf(b); 1058 } 1059 if ((long)b->b_blkno == -1) { 1060 biodone(b); 1061 return 0; 1062 } 1063 1064 return VOP_STRATEGY(v7fsmount->devvp, b); 1065 } 1066 1067 int 1068 v7fs_print(void *v) 1069 { 1070 struct vop_print_args /* { 1071 struct vnode *a_vp; 1072 } */ *a = v; 1073 struct v7fs_node *v7node = a->a_vp->v_data; 1074 1075 v7fs_inode_dump(&v7node->inode); 1076 1077 return 0; 1078 } 1079 1080 int 1081 v7fs_advlock(void *v) 1082 { 1083 struct vop_advlock_args /* { 1084 struct vnode *a_vp; 1085 void *a_id; 1086 int a_op; 1087 struct flock *a_fl; 1088 int a_flags; 1089 } */ *a = v; 1090 struct v7fs_node *v7node = a->a_vp->v_data; 1091 1092 DPRINTF("op=%d\n", a->a_op); 1093 1094 return lf_advlock(a, &v7node->lockf, 1095 v7fs_inode_filesize(&v7node->inode)); 1096 } 1097 1098 int 1099 v7fs_pathconf(void *v) 1100 { 1101 struct vop_pathconf_args /* { 1102 struct vnode *a_vp; 1103 int a_name; 1104 register_t *a_retval; 1105 } */ *a = v; 1106 int err = 0; 1107 1108 DPRINTF("%p\n", a->a_vp); 1109 1110 switch (a->a_name) { 1111 case _PC_LINK_MAX: 1112 *a->a_retval = V7FS_LINK_MAX; 1113 break; 1114 case _PC_NAME_MAX: 1115 *a->a_retval = V7FS_NAME_MAX; 1116 break; 1117 case _PC_PATH_MAX: 1118 *a->a_retval = V7FS_PATH_MAX; 1119 break; 1120 case _PC_CHOWN_RESTRICTED: 1121 *a->a_retval = 1; 1122 break; 1123 case _PC_NO_TRUNC: 1124 *a->a_retval = 0; 1125 break; 1126 case _PC_SYNC_IO: 1127 *a->a_retval = 1; 1128 break; 1129 case _PC_FILESIZEBITS: 1130 *a->a_retval = 30; /* ~1G */ 1131 break; 1132 case _PC_SYMLINK_MAX: 1133 *a->a_retval = V7FSBSD_MAXSYMLINKLEN; 1134 break; 1135 case _PC_2_SYMLINKS: 1136 *a->a_retval = 1; 1137 break; 1138 default: 1139 err = EINVAL; 1140 break; 1141 } 1142 1143 return err; 1144 } 1145 1146 int 1147 v7fs_update(struct vnode *vp, const struct timespec *acc, 1148 const struct timespec *mod, int flags) 1149 { 1150 struct v7fs_node *v7node = vp->v_data; 1151 struct v7fs_inode *inode = &v7node->inode; 1152 struct v7fs_self *fs = v7node->v7fsmount->core; 1153 bool update = false; 1154 1155 DPRINTF("%p %zu %d\n", vp, vp->v_size, v7fs_inode_filesize(inode)); 1156 KDASSERT(vp->v_size == v7fs_inode_filesize(inode)); 1157 1158 if (v7node->update_atime) { 1159 inode->atime = acc ? acc->tv_sec : time_second; 1160 v7node->update_atime = false; 1161 update = true; 1162 } 1163 if (v7node->update_ctime) { 1164 inode->ctime = time_second; 1165 v7node->update_ctime = false; 1166 update = true; 1167 } 1168 if (v7node->update_mtime) { 1169 inode->mtime = mod ? mod->tv_sec : time_second; 1170 v7node->update_mtime = false; 1171 update = true; 1172 } 1173 1174 if (update) 1175 v7fs_inode_writeback(fs, inode); 1176 1177 return 0; 1178 } 1179 1180 int 1181 v7fs_symlink(void *v) 1182 { 1183 struct vop_symlink_args /* { 1184 struct vnode *a_dvp; 1185 struct vnode **a_vpp; 1186 struct componentname *a_cnp; 1187 struct vattr *a_vap; 1188 char *a_target; 1189 } */ *a = v; 1190 struct v7fs_node *parent_node = a->a_dvp->v_data; 1191 struct v7fs_mount *v7fsmount = parent_node->v7fsmount; 1192 struct v7fs_self *fs = v7fsmount->core; 1193 struct vattr *va = a->a_vap; 1194 kauth_cred_t cr = a->a_cnp->cn_cred; 1195 struct componentname *cnp = a->a_cnp; 1196 struct v7fs_fileattr attr; 1197 v7fs_ino_t ino; 1198 const char *from = a->a_target; 1199 const char *to = cnp->cn_nameptr; 1200 size_t len = strlen(from) + 1; 1201 int error = 0; 1202 1203 if (len > V7FS_BSIZE) { /* limited to 512byte pathname */ 1204 DPRINTF("too long pathname."); 1205 return ENAMETOOLONG; 1206 } 1207 1208 memset(&attr, 0, sizeof(attr)); 1209 attr.uid = kauth_cred_geteuid(cr); 1210 attr.gid = kauth_cred_getegid(cr); 1211 attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); 1212 1213 if ((error = v7fs_file_allocate 1214 (fs, &parent_node->inode, to, &attr, &ino))) { 1215 goto unlock_exit; 1216 } 1217 /* Sync dirent size change. */ 1218 uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); 1219 1220 /* Get myself vnode. */ 1221 if ((error = v7fs_vget(v7fsmount->mountp, ino, a->a_vpp))) { 1222 DPRINTF("can't get vnode.\n"); 1223 } 1224 1225 struct v7fs_node *newnode = (*a->a_vpp)->v_data; 1226 struct v7fs_inode *p = &newnode->inode; 1227 v7fs_file_symlink(fs, p, from); 1228 uvm_vnp_setsize(*a->a_vpp, v7fs_inode_filesize(p)); 1229 1230 newnode->update_ctime = true; 1231 newnode->update_mtime = true; 1232 newnode->update_atime = true; 1233 unlock_exit: 1234 /* unlock parent directory */ 1235 vput(a->a_dvp); 1236 1237 return error; 1238 } 1239 1240 int 1241 v7fs_readlink(void *v) 1242 { 1243 struct vop_readlink_args /* { 1244 struct vnode *a_vp; 1245 struct uio *a_uio; 1246 kauth_cred_t a_cred; 1247 } */ *a = v; 1248 struct uio *uio = a->a_uio; 1249 struct vnode *vp = a->a_vp; 1250 struct v7fs_node *v7node = vp->v_data; 1251 struct v7fs_inode *inode = &v7node->inode; 1252 struct v7fs_self *fs = v7node->v7fsmount->core; 1253 int error = 0; 1254 1255 KDASSERT(vp->v_type == VLNK); 1256 KDASSERT(uio->uio_offset >= 0); 1257 KDASSERT(v7fs_inode_islnk(inode)); 1258 1259 v7fs_daddr_t blk = inode->addr[0]; 1260 void *buf; 1261 if (!(buf = scratch_read(fs, blk))) { 1262 error = EIO; 1263 goto error_exit; 1264 } 1265 1266 if ((error = uiomove(buf, strlen(buf), uio))) { 1267 DPRINTF("uiomove failed.\n"); 1268 } 1269 scratch_free(fs, buf); 1270 1271 error_exit: 1272 return error; 1273 } 1274 1275 1276