1 /* $NetBSD: hfs_vnops.c,v 1.40 2022/08/06 18:26:42 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Yevgeny Binder and Dieter Baron. 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 /* 33 * Copyright (c) 1992, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * This code is derived from software donated to Berkeley by 37 * Jan-Simon Pendry. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 /* 65 * Copyright (c) 1982, 1986, 1989, 1993, 1995 66 * The Regents of the University of California. All rights reserved. 67 * (c) UNIX System Laboratories, Inc. 68 * All or some portions of this file are derived from material licensed 69 * to the University of California by American Telephone and Telegraph 70 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 71 * the permission of UNIX System Laboratories, Inc. 72 * 73 * Redistribution and use in source and binary forms, with or without 74 * modification, are permitted provided that the following conditions 75 * are met: 76 * 1. Redistributions of source code must retain the above copyright 77 * notice, this list of conditions and the following disclaimer. 78 * 2. Redistributions in binary form must reproduce the above copyright 79 * notice, this list of conditions and the following disclaimer in the 80 * documentation and/or other materials provided with the distribution. 81 * 3. Neither the name of the University nor the names of its contributors 82 * may be used to endorse or promote products derived from this software 83 * without specific prior written permission. 84 * 85 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 86 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 87 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 88 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 89 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 90 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 91 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 92 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 93 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 94 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 95 * SUCH DAMAGE. 96 */ 97 98 99 /* 100 * Apple HFS+ filesystem 101 */ 102 103 #include <sys/cdefs.h> 104 __KERNEL_RCSID(0, "$NetBSD: hfs_vnops.c,v 1.40 2022/08/06 18:26:42 andvar Exp $"); 105 106 #ifdef _KERNEL_OPT 107 #include "opt_ipsec.h" 108 #endif 109 110 #include <sys/param.h> 111 #include <sys/systm.h> 112 #include <sys/kernel.h> 113 #include <sys/vmmeter.h> 114 #include <sys/time.h> 115 #include <sys/proc.h> 116 #include <sys/vnode.h> 117 #include <sys/malloc.h> 118 #include <sys/pool.h> 119 #include <sys/file.h> 120 #include <sys/stat.h> 121 #include <sys/mount.h> 122 #include <sys/namei.h> 123 #include <sys/buf.h> 124 #include <sys/dirent.h> 125 #include <sys/msgbuf.h> 126 127 #include <miscfs/fifofs/fifo.h> 128 #include <miscfs/specfs/specdev.h> 129 130 #include <fs/hfs/hfs.h> 131 #include <fs/hfs/unicode.h> 132 133 #include <miscfs/genfs/genfs.h> 134 135 int hfs_vop_parsepath(void *); 136 int hfs_vop_lookup(void *); 137 int hfs_vop_open(void *); 138 int hfs_vop_close(void *); 139 int hfs_vop_access(void *); 140 int hfs_vop_getattr(void *); 141 int hfs_vop_setattr(void *); 142 int hfs_vop_bmap(void *); 143 int hfs_vop_read(void *); 144 int hfs_vop_readdir(void *); 145 int hfs_vop_readlink(void *); 146 int hfs_vop_reclaim(void *); 147 int hfs_vop_print(void *); 148 149 #ifdef HFS_DEBUG 150 #define DPRINTF(a) printf a 151 #else 152 #define DPRINTF(a) 153 #endif 154 155 156 int (**hfs_vnodeop_p) (void *); 157 const struct vnodeopv_entry_desc hfs_vnodeop_entries[] = { 158 { &vop_default_desc, vn_default_error }, 159 { &vop_parsepath_desc, genfs_parsepath }, /* parsepath */ 160 { &vop_lookup_desc, hfs_vop_lookup }, /* lookup */ 161 { &vop_create_desc, genfs_eopnotsupp }, /* create */ 162 { &vop_whiteout_desc, genfs_eopnotsupp }, /* whiteout */ 163 { &vop_mknod_desc, genfs_eopnotsupp }, /* mknod */ 164 { &vop_open_desc, hfs_vop_open }, /* open */ 165 { &vop_close_desc, hfs_vop_close }, /* close */ 166 { &vop_access_desc, hfs_vop_access }, /* access */ 167 { &vop_accessx_desc, genfs_accessx }, /* accessx */ 168 { &vop_getattr_desc, hfs_vop_getattr }, /* getattr */ 169 { &vop_setattr_desc, hfs_vop_setattr }, /* setattr */ 170 { &vop_read_desc, hfs_vop_read }, /* read */ 171 { &vop_write_desc, genfs_eopnotsupp }, /* write */ 172 { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */ 173 { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */ 174 { &vop_ioctl_desc, genfs_eopnotsupp }, /* ioctl */ 175 { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ 176 { &vop_poll_desc, genfs_eopnotsupp }, /* poll */ 177 { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ 178 { &vop_revoke_desc, genfs_eopnotsupp }, /* revoke */ 179 { &vop_mmap_desc, genfs_mmap }, /* mmap */ 180 { &vop_fsync_desc, genfs_nullop }, /* fsync */ 181 { &vop_seek_desc, genfs_seek }, /* seek */ 182 { &vop_remove_desc, genfs_eopnotsupp }, /* remove */ 183 { &vop_link_desc, genfs_eopnotsupp }, /* link */ 184 { &vop_rename_desc, genfs_eopnotsupp }, /* rename */ 185 { &vop_mkdir_desc, genfs_eopnotsupp }, /* mkdir */ 186 { &vop_rmdir_desc, genfs_eopnotsupp }, /* rmdir */ 187 { &vop_symlink_desc, genfs_eopnotsupp }, /* symlink */ 188 { &vop_readdir_desc, hfs_vop_readdir }, /* readdir */ 189 { &vop_readlink_desc, hfs_vop_readlink }, /* readlink */ 190 { &vop_abortop_desc, genfs_abortop }, /* abortop */ 191 { &vop_inactive_desc, genfs_eopnotsupp }, /* inactive */ 192 { &vop_reclaim_desc, hfs_vop_reclaim }, /* reclaim */ 193 { &vop_lock_desc, genfs_lock }, /* lock */ 194 { &vop_unlock_desc, genfs_unlock }, /* unlock */ 195 { &vop_bmap_desc, hfs_vop_bmap }, /* bmap */ 196 { &vop_strategy_desc, genfs_eopnotsupp }, /* strategy */ 197 { &vop_print_desc, hfs_vop_print }, /* print */ 198 { &vop_islocked_desc, genfs_islocked }, /* islocked */ 199 { &vop_pathconf_desc, genfs_eopnotsupp }, /* pathconf */ 200 { &vop_advlock_desc, genfs_eopnotsupp }, /* advlock */ 201 { &vop_bwrite_desc, genfs_eopnotsupp }, /* bwrite */ 202 { &vop_getpages_desc, genfs_getpages }, /* getpages */ 203 { &vop_putpages_desc, genfs_putpages }, /* putpages */ 204 { &vop_openextattr_desc, genfs_eopnotsupp }, /* openextattr */ 205 { &vop_closeextattr_desc, genfs_eopnotsupp }, /* closeextattr */ 206 { &vop_getextattr_desc, genfs_eopnotsupp }, /* getextattr */ 207 { &vop_setextattr_desc, genfs_eopnotsupp }, /* setextattr */ 208 { &vop_listextattr_desc, genfs_eopnotsupp }, /* listextattr */ 209 { &vop_deleteextattr_desc, genfs_eopnotsupp }, /* deleteextattr */ 210 { NULL, NULL } 211 }; 212 const struct vnodeopv_desc hfs_vnodeop_opv_desc = 213 { &hfs_vnodeop_p, hfs_vnodeop_entries }; 214 215 int (**hfs_specop_p) (void *); 216 const struct vnodeopv_entry_desc hfs_specop_entries[] = { 217 { &vop_default_desc, vn_default_error }, 218 GENFS_SPECOP_ENTRIES, 219 { &vop_close_desc, spec_close }, /* close */ 220 { &vop_access_desc, hfs_vop_access }, /* access */ 221 { &vop_accessx_desc, genfs_accessx }, /* accessx */ 222 { &vop_getattr_desc, hfs_vop_getattr }, /* getattr */ 223 { &vop_setattr_desc, hfs_vop_setattr }, /* setattr */ 224 { &vop_read_desc, spec_read }, /* read */ 225 { &vop_write_desc, spec_write }, /* write */ 226 { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ 227 { &vop_fsync_desc, spec_fsync }, /* fsync */ 228 { &vop_inactive_desc, genfs_eopnotsupp }, /* inactive */ 229 { &vop_reclaim_desc, hfs_vop_reclaim }, /* reclaim */ 230 { &vop_lock_desc, genfs_lock }, /* lock */ 231 { &vop_unlock_desc, genfs_unlock }, /* unlock */ 232 { &vop_print_desc, hfs_vop_print }, /* print */ 233 { &vop_islocked_desc, genfs_islocked }, /* islocked */ 234 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ 235 #if 0 236 { &vop_openextattr_desc, hfs_openextattr }, /* openextattr */ 237 { &vop_closeextattr_desc, hfs_closeextattr }, /* closeextattr */ 238 { &vop_getextattr_desc, hfs_getextattr }, /* getextattr */ 239 { &vop_setextattr_desc, hfs_setextattr }, /* setextattr */ 240 { &vop_listextattr_desc, hfs_listextattr }, /* listextattr */ 241 { &vop_deleteextattr_desc, hfs_deleteextattr }, /* deleteextattr */ 242 #endif 243 { NULL, NULL } 244 }; 245 const struct vnodeopv_desc hfs_specop_opv_desc = 246 { &hfs_specop_p, hfs_specop_entries }; 247 248 int (**hfs_fifoop_p) (void *); 249 const struct vnodeopv_entry_desc hfs_fifoop_entries[] = { 250 { &vop_default_desc, vn_default_error }, 251 GENFS_FIFOOP_ENTRIES, 252 { &vop_close_desc, vn_fifo_bypass }, /* close */ 253 { &vop_access_desc, hfs_vop_access }, /* access */ 254 { &vop_accessx_desc, genfs_accessx }, /* accessx */ 255 { &vop_getattr_desc, hfs_vop_getattr }, /* getattr */ 256 { &vop_setattr_desc, hfs_vop_setattr }, /* setattr */ 257 { &vop_read_desc, vn_fifo_bypass }, /* read */ 258 { &vop_write_desc, vn_fifo_bypass }, /* write */ 259 { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ 260 { &vop_fsync_desc, vn_fifo_bypass }, /* fsync */ 261 { &vop_inactive_desc, genfs_eopnotsupp }, /* inactive */ 262 { &vop_reclaim_desc, hfs_vop_reclaim }, /* reclaim */ 263 { &vop_lock_desc, genfs_lock }, /* lock */ 264 { &vop_unlock_desc, genfs_unlock }, /* unlock */ 265 { &vop_strategy_desc, vn_fifo_bypass }, /* strategy */ 266 { &vop_print_desc, hfs_vop_print }, /* print */ 267 { &vop_islocked_desc, genfs_islocked }, /* islocked */ 268 { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ 269 #if 0 270 { &vop_openextattr_desc, hfs_openextattr }, /* openextattr */ 271 { &vop_closeextattr_desc, hfs_closeextattr }, /* closeextattr */ 272 { &vop_getextattr_desc, hfs_getextattr }, /* getextattr */ 273 { &vop_setextattr_desc, hfs_setextattr }, /* setextattr */ 274 { &vop_listextattr_desc, hfs_listextattr }, /* listextattr */ 275 { &vop_deleteextattr_desc, hfs_deleteextattr }, /* deleteextattr */ 276 #endif 277 { NULL, NULL } 278 }; 279 const struct vnodeopv_desc hfs_fifoop_opv_desc = 280 { &hfs_fifoop_p, hfs_fifoop_entries }; 281 282 int 283 hfs_vop_parsepath(void *v) 284 { 285 struct vop_parsepath_args /* { 286 struct vnode *a_dvp; 287 const char *a_name; 288 size_t *a_retval; 289 } */ *ap = v; 290 size_t len; 291 int error; 292 293 error = genfs_parsepath(v); 294 if (error) { 295 return error; 296 } 297 298 len = *ap->a_retval; 299 if (!strcmp(ap->a_name + len, "/rsrc")) { 300 *ap->a_retval += 5; 301 } 302 return 0; 303 } 304 305 int 306 hfs_vop_lookup(void *v) 307 { 308 struct vop_lookup_v2_args /* { 309 struct vnode * a_dvp; 310 struct vnode ** a_vpp; 311 struct componentname * a_cnp; 312 } */ *ap = v; 313 struct componentname *cnp; 314 struct hfsnode *dp; /* hfsnode for directory being searched */ 315 kauth_cred_t cred; 316 struct vnode **vpp; /* resultant vnode */ 317 struct vnode *tdp; /* returned by VFS_VGET */ 318 struct vnode *vdp; /* vnode for directory being searched */ 319 hfs_catalog_key_t key; /* hfs+ catalog search key for requested child */ 320 hfs_catalog_keyed_record_t rec; /* catalog record of requested child */ 321 size_t namelen; 322 int use_resource_fork = 0; 323 unichar_t* unicn; /* name of component, in Unicode */ 324 const char *pname; 325 int error; 326 int flags; 327 int result; /* result of libhfs operations */ 328 329 DPRINTF(("VOP = hfs_vop_lookup()\n")); 330 331 cnp = ap->a_cnp; 332 cred = cnp->cn_cred; 333 vdp = ap->a_dvp; 334 dp = VTOH(vdp); 335 error = 0; 336 pname = cnp->cn_nameptr; 337 result = 0; 338 unicn = NULL; 339 vpp = ap->a_vpp; 340 *vpp = NULL; 341 342 flags = cnp->cn_flags; 343 344 345 /* 346 * Check accessibility of directory. 347 */ 348 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) 349 return error; 350 351 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 352 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 353 return EROFS; 354 355 /* 356 * We now have a segment name to search for, and a directory to search. 357 * 358 * Before tediously performing a linear scan of the directory, 359 * check the name cache to see if the directory/name pair 360 * we are looking for is known already. 361 */ 362 /* XXX Cache disabled until we can make sure it works. */ 363 #if 0 364 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) 365 return error; 366 #endif 367 368 369 #if 0 370 if (cnp->cn_namelen == 1 && *pname == '.') { 371 *vpp = vdp; 372 vref(vdp); 373 return 0; 374 } 375 #endif 376 377 if (flags & ISDOTDOT) { 378 DPRINTF(("DOTDOT ")); 379 error = hfs_vget_internal(vdp->v_mount, dp->h_parent, 380 HFS_RSRCFORK, &tdp); 381 if (error != 0) 382 goto error; 383 *vpp = tdp; 384 #if 0 385 } else if (dp->h_rec.u.cnid == rec.file.u.cnid) { 386 #endif 387 } else if (cnp->cn_namelen == 1 && pname[0] == '.') { 388 DPRINTF(("DOT ")); 389 vref(vdp); /* we want ourself, ie "." */ 390 *vpp = vdp; 391 } else { 392 hfs_callback_args cbargs; 393 uint8_t len, ni; 394 395 hfslib_init_cbargs(&cbargs); 396 397 namelen = cnp->cn_namelen; 398 if (namelen > 5 && 399 !strcmp(cnp->cn_nameptr + namelen - 5, "/rsrc")) { 400 namelen -= 5; 401 use_resource_fork = 1; 402 } 403 404 /* XXX: when decomposing, string could grow 405 and we have to handle overflow */ 406 unicn = malloc(namelen * sizeof(unicn[0]), M_TEMP, M_WAITOK); 407 len = utf8_to_utf16(unicn, namelen, 408 cnp->cn_nameptr, namelen, 0, NULL); 409 for (ni = 0; ni < len; ni++) 410 if (unicn[ni] == (unichar_t)':') 411 unicn[ni] = (unichar_t)'/'; 412 /* XXX: check conversion errors? */ 413 if (hfslib_make_catalog_key(VTOH(vdp)->h_rec.u.cnid, len, unicn, 414 &key) == 0) { 415 DPRINTF(("ERROR in hfslib_make_catalog_key\n")); 416 error = EINVAL; 417 goto error; 418 } 419 420 result = hfslib_find_catalog_record_with_key(&dp->h_hmp->hm_vol, 421 &key, &rec, &cbargs); 422 if (result > 0) { 423 error = EINVAL; 424 goto error; 425 } 426 if (result < 0) { 427 if (cnp->cn_nameiop == CREATE) 428 error = EROFS; 429 else 430 error = ENOENT; 431 goto error; 432 } 433 434 if (rec.file.user_info.file_type == HFS_HARD_LINK_FILE_TYPE 435 && rec.file.user_info.file_creator == HFS_HFSLUS_CREATOR) { 436 if (hfslib_get_hardlink(&dp->h_hmp->hm_vol, 437 rec.file.bsd.special.inode_num, 438 &rec, &cbargs) != 0) { 439 error = EINVAL; 440 goto error; 441 } 442 } 443 444 if (rec.type == HFS_REC_FILE 445 && use_resource_fork 446 && rec.file.rsrc_fork.logical_size > 0) { 447 /* advance namei next pointer to end of string */ 448 error = hfs_vget_internal(vdp->v_mount, rec.file.cnid, 449 HFS_RSRCFORK, &tdp); 450 } else 451 error = hfs_vget_internal(vdp->v_mount, rec.file.cnid, 452 HFS_DATAFORK, &tdp); 453 if (error != 0) 454 goto error; 455 *vpp = tdp; 456 } 457 DPRINTF(("\n")); 458 /* 459 * Insert name into cache if appropriate. 460 */ 461 /* XXX Cache disabled until we can make sure it works. */ 462 #if 0 463 cache_enter(vdp, *vpp, cnp); 464 #endif 465 466 error = 0; 467 468 /* FALLTHROUGH */ 469 error: 470 if (unicn != NULL) 471 free(unicn, M_TEMP); 472 473 return error; 474 } 475 476 int 477 hfs_vop_open(void *v) 478 { 479 #if 0 480 struct vop_open_args /* { 481 struct vnode *a_vp; 482 int a_mode; 483 kauth_cred_t a_cred; 484 } */ *ap = v; 485 struct hfsnode *hn = VTOH(ap->a_vp); 486 #endif 487 DPRINTF(("VOP = hfs_vop_open()\n")); 488 489 /* 490 * XXX This is a good place to read and cache the file's extents to 491 * XXX avoid doing it upon every read/write. Must however keep the 492 * XXX cache in sync when the file grows/shrinks. (So would that go 493 * XXX in vop_truncate?) 494 */ 495 496 return 0; 497 } 498 499 int 500 hfs_vop_close(void *v) 501 { 502 #if 0 503 struct vop_close_args /* { 504 struct vnode *a_vp; 505 int a_fflag; 506 kauth_cred_t a_cred; 507 } */ *ap = v; 508 struct hfsnode *hn = VTOH(ap->a_vp); 509 #endif 510 DPRINTF(("VOP = hfs_vop_close()\n")); 511 512 /* Release extents cache here. */ 513 514 return 0; 515 } 516 517 static int 518 hfs_check_possible(struct vnode *vp, accmode_t accmode) 519 { 520 521 /* 522 * Disallow writes on files, directories, and symlinks 523 * since we have no write support yet. 524 */ 525 526 if (accmode & VWRITE) { 527 switch (vp->v_type) { 528 case VDIR: 529 case VLNK: 530 case VREG: 531 return EROFS; 532 default: 533 break; 534 } 535 } 536 537 return 0; 538 } 539 540 static int 541 hfs_check_permitted(vnode_t *vp, struct vattr *va, accmode_t accmode, 542 kauth_cred_t cred) 543 { 544 545 return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode, 546 va->va_type, va->va_mode), vp, NULL, genfs_can_access(vp, cred, 547 va->va_uid, va->va_gid, va->va_mode, NULL, accmode)); 548 } 549 550 int 551 hfs_vop_access(void *v) 552 { 553 struct vop_access_args /* { 554 struct vnode *a_vp; 555 int a_accmode; 556 kauth_cred_t a_cred; 557 } */ *ap = v; 558 struct vattr va; 559 int error; 560 561 DPRINTF(("VOP = hfs_vop_access()\n")); 562 563 error = hfs_check_possible(ap->a_vp, ap->a_accmode); 564 if (error) 565 return error; 566 567 if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0) 568 return error; 569 570 error = hfs_check_permitted(ap->a_vp, &va, ap->a_accmode, ap->a_cred); 571 572 return error; 573 } 574 575 int 576 hfs_vop_getattr(void *v) 577 { 578 struct vop_getattr_args /* { 579 struct vnode *a_vp; 580 struct vattr *a_vap; 581 struct ucred *a_cred; 582 } */ *ap = v; 583 struct vnode *vp; 584 struct hfsnode *hp; 585 struct vattr *vap; 586 hfs_bsd_data_t *bsd; 587 hfs_fork_t *fork; 588 589 DPRINTF(("VOP = hfs_vop_getattr()\n")); 590 591 vp = ap->a_vp; 592 hp = VTOH(vp); 593 vap = ap->a_vap; 594 595 vattr_null(vap); 596 597 /* 598 * XXX Cannot trust permissions/modes/flags stored in an HFS+ catalog 599 * XXX record those values are not set on files created under Mac OS 9. 600 */ 601 vap->va_type = ap->a_vp->v_type; 602 if (hp->h_rec.u.rec_type == HFS_REC_FILE) { 603 hfs_file_record_t *f = &hp->h_rec.file; 604 if (hp->h_fork == HFS_RSRCFORK) 605 fork = &f->rsrc_fork; 606 else 607 fork = &f->data_fork; 608 vap->va_fileid = f->cnid; 609 bsd = &f->bsd; 610 vap->va_bytes = fork->total_blocks * HFS_BLOCKSIZE(vp); 611 vap->va_size = fork->logical_size; 612 hfs_time_to_timespec(f->date_created, &vap->va_ctime); 613 hfs_time_to_timespec(f->date_content_mod, &vap->va_mtime); 614 hfs_time_to_timespec(f->date_accessed, &vap->va_atime); 615 vap->va_nlink = 1; 616 } else if (hp->h_rec.u.rec_type == HFS_REC_FLDR) { 617 hfs_folder_record_t *f = &hp->h_rec.folder; 618 vap->va_fileid = hp->h_rec.folder.cnid; 619 bsd = &f->bsd; 620 vap->va_size = 512; /* XXX Temporary */ 621 vap->va_bytes = 512; /* XXX Temporary */ 622 hfs_time_to_timespec(f->date_created, &vap->va_ctime); 623 hfs_time_to_timespec(f->date_content_mod,&vap->va_mtime); 624 hfs_time_to_timespec(f->date_accessed, &vap->va_atime); 625 vap->va_nlink = 2; /* XXX */ 626 } else { 627 DPRINTF(("hfs+: hfs_vop_getattr(): invalid record type %i", 628 hp->h_rec.u.rec_type)); 629 return EINVAL; 630 } 631 632 if ((bsd->file_mode & S_IFMT) == 0) { 633 /* no bsd permissions recorded, use default values */ 634 if (hp->h_rec.u.rec_type == HFS_REC_FILE) 635 vap->va_mode = (S_IFREG | HFS_DEFAULT_FILE_MODE); 636 else 637 vap->va_mode = (S_IFDIR | HFS_DEFAULT_DIR_MODE); 638 vap->va_uid = HFS_DEFAULT_UID; 639 vap->va_gid = HFS_DEFAULT_GID; 640 } else { 641 vap->va_mode = bsd->file_mode; 642 vap->va_uid = bsd->owner_id; 643 vap->va_gid = bsd->group_id; 644 if ((vap->va_mode & S_IFMT) == S_IFCHR 645 || (vap->va_mode & S_IFMT) == S_IFBLK) { 646 vap->va_rdev 647 = HFS_CONVERT_RDEV(bsd->special.raw_device); 648 } 649 else if (bsd->special.link_count != 0) { 650 /* XXX: only if in metadata directory */ 651 vap->va_nlink = bsd->special.link_count; 652 } 653 } 654 655 vap->va_fsid = hp->h_dev; 656 vap->va_blocksize = hp->h_hmp->hm_vol.vh.block_size; 657 vap->va_gen = 1; 658 vap->va_flags = 0; 659 660 return 0; 661 } 662 663 int 664 hfs_vop_setattr(void *v) 665 { 666 struct vop_setattr_args /* { 667 struct vnode *a_vp; 668 struct vattr *a_vap; 669 kauth_cred_t a_cred; 670 } */ *ap = v; 671 struct vattr *vap; 672 struct vnode *vp; 673 674 vap = ap->a_vap; 675 vp = ap->a_vp; 676 677 /* 678 * Check for unsettable attributes. 679 */ 680 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 681 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 682 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 683 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 684 return EINVAL; 685 } 686 687 /* XXX: needs revisiting for write support */ 688 if (vap->va_flags != VNOVAL 689 || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL 690 || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL 691 || vap->va_birthtime.tv_sec != VNOVAL) { 692 return EROFS; 693 } 694 695 if (vap->va_size != VNOVAL) { 696 /* 697 * Disallow write attempts on read-only file systems; 698 * unless the file is a socket, fifo, or a block or 699 * character device resident on the file system. 700 */ 701 switch (vp->v_type) { 702 case VDIR: 703 return EISDIR; 704 case VCHR: 705 case VBLK: 706 case VFIFO: 707 break; 708 case VREG: 709 return EROFS; 710 default: 711 return EOPNOTSUPP; 712 } 713 } 714 715 return 0; 716 } 717 718 int 719 hfs_vop_bmap(void *v) 720 { 721 struct vop_bmap_args /* { 722 struct vnode *a_vp; 723 daddr_t a_bn; 724 struct vnode **a_vpp; 725 daddr_t *a_bnp; 726 int *a_runp; 727 } */ *ap = v; 728 struct vnode *vp; 729 struct hfsnode *hp; 730 daddr_t lblkno; 731 hfs_callback_args cbargs; 732 hfs_libcb_argsread argsread; 733 hfs_extent_descriptor_t *extents; 734 uint16_t numextents, i; 735 int bshift; 736 737 vp = ap->a_vp; 738 hp = VTOH(vp); 739 lblkno = ap->a_bn; 740 bshift = vp->v_mount->mnt_fs_bshift; 741 742 /* 743 * Check for underlying vnode requests and ensure that logical 744 * to physical mapping is requested. 745 */ 746 if (ap->a_vpp != NULL) 747 *ap->a_vpp = hp->h_devvp; 748 if (ap->a_bnp == NULL) 749 return 0; 750 751 hfslib_init_cbargs(&cbargs); 752 argsread.cred = NULL; 753 argsread.l = NULL; 754 cbargs.read = &argsread; 755 756 numextents = hfslib_get_file_extents(&hp->h_hmp->hm_vol, 757 hp->h_rec.u.cnid, hp->h_fork, &extents, &cbargs); 758 759 /* XXX: is this correct for 0-length files? */ 760 if (numextents == 0) 761 return EBADF; 762 763 for (i = 0; i < numextents; i++) { 764 if (lblkno < extents[i].block_count) 765 break; 766 lblkno -= extents[i].block_count; 767 } 768 769 if (i == numextents) { 770 /* XXX: block number past EOF */ 771 i--; 772 lblkno += extents[i].block_count; 773 } 774 775 *ap->a_bnp = ((extents[i].start_block + lblkno) << (bshift-DEV_BSHIFT)) 776 + (hp->h_hmp->hm_vol.offset >> DEV_BSHIFT); 777 778 if (ap->a_runp) { 779 int nblk; 780 781 nblk = extents[i].block_count - lblkno - 1; 782 if (nblk <= 0) 783 *ap->a_runp = 0; 784 else if (nblk > MAXBSIZE >> bshift) 785 *ap->a_runp = (MAXBSIZE >> bshift) - 1; 786 else 787 *ap->a_runp = nblk; 788 } 789 790 free(extents, M_TEMP); 791 792 return 0; 793 } 794 795 int 796 hfs_vop_read(void *v) 797 { 798 struct vop_read_args /* { 799 struct vnode *a_vp; 800 struct uio *a_uio; 801 int a_ioflag; 802 kauth_cred_t a_cred; 803 } */ *ap = v; 804 struct vnode *vp; 805 struct hfsnode *hp; 806 struct uio *uio; 807 uint64_t fsize; /* logical size of file */ 808 int advice; 809 int error; 810 811 vp = ap->a_vp; 812 hp = VTOH(vp); 813 uio = ap->a_uio; 814 if (hp->h_fork == HFS_RSRCFORK) 815 fsize = hp->h_rec.file.rsrc_fork.logical_size; 816 else 817 fsize = hp->h_rec.file.data_fork.logical_size; 818 error = 0; 819 advice = IO_ADV_DECODE(ap->a_ioflag); 820 821 if (uio->uio_offset < 0) 822 return EINVAL; 823 824 if (uio->uio_resid == 0 || uio->uio_offset >= fsize) 825 return 0; 826 827 if (vp->v_type != VREG && vp->v_type != VLNK) 828 return EINVAL; 829 830 error = 0; 831 while (uio->uio_resid > 0 && error == 0) { 832 vsize_t len; 833 834 len = MIN(uio->uio_resid, fsize - uio->uio_offset); 835 if (len == 0) 836 break; 837 838 error = ubc_uiomove(&vp->v_uobj, uio, len, advice, 839 UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp)); 840 } 841 842 return error; 843 } 844 845 int 846 hfs_vop_readdir(void *v) 847 { 848 struct vop_readdir_args /* { 849 struct vnode *a_vp; 850 struct uio *a_uio; 851 kauth_cred_t a_cred; 852 int *a_eofflag; 853 off_t **a_cookies; 854 int a_*ncookies; 855 } */ *ap = v; 856 857 DPRINTF(("VOP = hfs_vop_readdir()\n")); 858 859 struct dirent curent; /* the dirent entry we are constructing */ 860 struct hfsnode *hp; 861 hfs_catalog_keyed_record_t *children; 862 hfs_unistr255_t *childnames; 863 hfs_callback_args cbargs; 864 hfs_libcb_argsread argsread; 865 struct uio *uio; 866 off_t bufoff; /* offset in buffer relative to start of dirents */ 867 uint32_t numchildren; 868 uint32_t curchild; /* index of child we are stuffing into dirent */ 869 size_t namlen, ni; 870 int error; 871 int i; /* dummy variable */ 872 873 bufoff = 0; 874 children = NULL; 875 error = 0; 876 numchildren = 0; 877 hp = VTOH(ap->a_vp); 878 uio = ap->a_uio; 879 880 if (uio->uio_offset < 0) 881 return EINVAL; 882 if (ap->a_eofflag != NULL) 883 *ap->a_eofflag = 0; 884 885 /* XXX Inform that we don't support NFS, for now. */ 886 #if 0 887 if(ap->a_eofflag != NULL || ap->a_cookies != NULL || 888 ap->a_ncookies != NULL) 889 return EOPNOTSUPP; 890 #endif 891 DPRINTF(("READDIR uio: offset=%td, resid=%zu\n", 892 uio->uio_offset, uio->uio_resid)); 893 hfslib_init_cbargs(&cbargs); 894 argsread.cred = ap->a_cred; 895 argsread.l = NULL; 896 cbargs.read = &argsread; 897 898 /* XXX Should we cache this? */ 899 if (hfslib_get_directory_contents(&hp->h_hmp->hm_vol, hp->h_rec.u.cnid, 900 &children, &childnames, &numchildren, &cbargs) != 0) { 901 DPRINTF(("ENOENT\n")); 902 error = ENOENT; 903 goto error; 904 } 905 906 DPRINTF(("numchildren = %u\n", numchildren)); 907 for (curchild = 0; curchild < numchildren && uio->uio_resid > 0; 908 curchild++) { 909 namlen = utf16_to_utf8(curent.d_name, NAME_MAX, 910 childnames[curchild].unicode, childnames[curchild].length, 911 0, NULL); 912 /* XXX: check conversion errors? */ 913 if (namlen > NAME_MAX) { 914 /* XXX: how to handle name too long? */ 915 continue; 916 } 917 for (ni = 0; ni < namlen; ni++) 918 if (curent.d_name[ni] == '/') 919 curent.d_name[ni] = ':'; 920 curent.d_namlen = namlen; 921 curent.d_reclen = _DIRENT_SIZE(&curent); 922 923 /* Skip to desired dirent. */ 924 bufoff += curent.d_reclen; 925 if (bufoff - curent.d_reclen < uio->uio_offset) 926 continue; 927 928 /* Make sure we don't return partial entries. */ 929 if (uio->uio_resid < curent.d_reclen) { 930 DPRINTF(("PARTIAL ENTRY\n")); 931 if (ap->a_eofflag != NULL) 932 *ap->a_eofflag = 1; 933 break; 934 } 935 936 curent.d_fileno = children[curchild].file.cnid; 937 switch (hfs_catalog_keyed_record_vtype(children+curchild)) { 938 case VREG: 939 curent.d_type = DT_REG; 940 break; 941 case VDIR: 942 curent.d_type = DT_DIR; 943 break; 944 case VBLK: 945 curent.d_type = DT_BLK; 946 break; 947 case VCHR: 948 curent.d_type = DT_CHR; 949 break; 950 case VLNK: 951 curent.d_type = DT_LNK; 952 break; 953 case VSOCK: 954 curent.d_type = DT_SOCK; 955 break; 956 case VFIFO: 957 curent.d_type = DT_FIFO; 958 break; 959 default: 960 curent.d_type = DT_UNKNOWN; 961 break; 962 } 963 DPRINTF(("curchildname = %s\t\t", curchildname)); 964 /* pad curent.d_name to aligned byte boundary */ 965 for (i = curent.d_namlen; 966 i < curent.d_reclen - _DIRENT_NAMEOFF(&curent); i++) 967 curent.d_name[i] = 0; 968 969 DPRINTF(("curent.d_name = %s\n", curent.d_name)); 970 971 if ((error = uiomove(&curent, curent.d_reclen, uio)) != 0) 972 goto error; 973 } 974 975 /* FALLTHROUGH */ 976 977 error: 978 if (numchildren > 0) { 979 if (children != NULL) 980 free(children, M_TEMP); 981 if (childnames != NULL) 982 free(childnames, M_TEMP); 983 } 984 985 if (error) { 986 DPRINTF(("ERROR = %i\n", error)); 987 } 988 989 return error; 990 } 991 992 int 993 hfs_vop_readlink(void *v) { 994 struct vop_readlink_args /* { 995 struct vnode *a_vp; 996 struct uio *a_uio; 997 kauth_cred_t a_cred; 998 } */ *ap = v; 999 1000 return VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred); 1001 } 1002 1003 int 1004 hfs_vop_reclaim(void *v) 1005 { 1006 struct vop_reclaim_v2_args /* { 1007 struct vnode *a_vp; 1008 } */ *ap = v; 1009 struct vnode *vp; 1010 struct hfsnode *hp; 1011 1012 VOP_UNLOCK(ap->a_vp); 1013 1014 DPRINTF(("VOP = hfs_vop_reclaim()\n")); 1015 1016 vp = ap->a_vp; 1017 hp = VTOH(vp); 1018 1019 /* Decrement the reference count to the volume's device. */ 1020 if (hp->h_devvp) { 1021 vrele(hp->h_devvp); 1022 hp->h_devvp = 0; 1023 } 1024 1025 genfs_node_destroy(vp); 1026 pool_put(&hfs_node_pool, hp); 1027 vp->v_data = NULL; 1028 1029 return 0; 1030 } 1031 1032 int 1033 hfs_vop_print(void *v) 1034 { 1035 struct vop_print_args /* { 1036 struct vnode *a_vp; 1037 } */ *ap = v; 1038 struct vnode *vp; 1039 struct hfsnode *hp; 1040 1041 DPRINTF(("VOP = hfs_vop_print()\n")); 1042 1043 vp = ap->a_vp; 1044 hp = VTOH(vp); 1045 1046 printf("dummy = %X\n", (unsigned)hp->dummy); 1047 printf("\n"); 1048 1049 return 0; 1050 } 1051