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