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