1/* 2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33#include <sys/mount.h> 34#include <sys/stat.h> 35#include <sys/vnode.h> 36#include <vfs/vfs_support.h> 37#include <libkern/libkern.h> 38#include <sys/fsctl.h> 39 40#include "hfs.h" 41#include "hfs_catalog.h" 42#include "hfs_format.h" 43#include "hfs_endian.h" 44 45 46static int cur_link_id = 0; 47 48/* 49 * Private directories where hardlink inodes reside. 50 */ 51const char *hfs_private_names[] = { 52 HFSPLUSMETADATAFOLDER, /* FILE HARDLINKS */ 53 HFSPLUS_DIR_METADATA_FOLDER /* DIRECTORY HARDLINKS */ 54}; 55 56 57/* 58 * Hardlink inodes save the head of their link chain in a 59 * private extended attribute. The following calls are 60 * used to access this attribute. 61 */ 62static int setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink); 63static int getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink); 64 65int hfs_makelink(struct hfsmount *hfsmp, struct vnode *src_vp, struct cnode *cp, 66 struct cnode *dcp, struct componentname *cnp); 67/* 68 * Create a new catalog link record 69 * 70 * An indirect link is a reference to an inode (the real 71 * file or directory record). 72 * 73 * All the indirect links for a given inode are chained 74 * together in a doubly linked list. 75 * 76 * Pre-Leopard file hard links do not have kHFSHasLinkChainBit 77 * set and do not have first/prev/next link IDs i.e. the values 78 * are zero. If a new link is being added to an existing 79 * pre-Leopard file hard link chain, do not set kHFSHasLinkChainBit. 80 */ 81static int 82createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum, struct cat_desc *descp, 83 cnid_t nextcnid, cnid_t *linkcnid, int is_inode_linkchain_set) 84{ 85 struct FndrFileInfo *fip; 86 struct cat_attr attr; 87 88 if (linknum == 0) { 89 printf("hfs: createindirectlink: linknum is zero!\n"); 90 return (EINVAL); 91 } 92 93 /* Setup the default attributes */ 94 bzero(&attr, sizeof(attr)); 95 96 /* Links are matched to inodes by link ID and to volumes by create date */ 97 attr.ca_linkref = linknum; 98 attr.ca_itime = hfsmp->hfs_metadata_createdate; 99 attr.ca_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 100 attr.ca_recflags = kHFSHasLinkChainMask | kHFSThreadExistsMask; 101 attr.ca_flags = UF_IMMUTABLE; 102 fip = (struct FndrFileInfo *)&attr.ca_finderinfo; 103 104 if (descp->cd_flags & CD_ISDIR) { 105 fip->fdType = SWAP_BE32 (kHFSAliasType); 106 fip->fdCreator = SWAP_BE32 (kHFSAliasCreator); 107 fip->fdFlags = SWAP_BE16 (kIsAlias); 108 } else /* file */ { 109 fip->fdType = SWAP_BE32 (kHardLinkFileType); 110 fip->fdCreator = SWAP_BE32 (kHFSPlusCreator); 111 fip->fdFlags = SWAP_BE16 (kHasBeenInited); 112 /* If the file inode does not have kHFSHasLinkChainBit set 113 * and the next link chain ID is zero, assume that this 114 * is pre-Leopard file inode. Therefore clear the bit. 115 */ 116 if ((is_inode_linkchain_set == 0) && (nextcnid == 0)) { 117 attr.ca_recflags &= ~kHFSHasLinkChainMask; 118 } 119 } 120 /* Create the indirect link directly in the catalog */ 121 return cat_createlink(hfsmp, descp, &attr, nextcnid, linkcnid); 122} 123 124 125/* 126 * Make a link to the cnode cp in the directory dp 127 * using the name in cnp. src_vp is the vnode that 128 * corresponds to 'cp' which was part of the arguments to 129 * hfs_vnop_link. 130 * 131 * The cnodes cp and dcp must be locked. 132 */ 133int 134hfs_makelink(struct hfsmount *hfsmp, struct vnode *src_vp, struct cnode *cp, 135 struct cnode *dcp, struct componentname *cnp) 136{ 137 vfs_context_t ctx = cnp->cn_context; 138 struct proc *p = vfs_context_proc(ctx); 139 u_int32_t indnodeno = 0; 140 char inodename[32]; 141 struct cat_desc to_desc; 142 struct cat_desc link_desc; 143 int newlink = 0; 144 int lockflags; 145 int retval = 0; 146 cat_cookie_t cookie; 147 cnid_t orig_cnid; 148 cnid_t linkcnid; 149 cnid_t orig_firstlink; 150 enum privdirtype type; 151 152 type = S_ISDIR(cp->c_mode) ? DIR_HARDLINKS : FILE_HARDLINKS; 153 154 if (cur_link_id == 0) { 155 cur_link_id = ((random() & 0x3fffffff) + 100); 156 } 157 158 /* We don't allow link nodes in our private system directories. */ 159 if (dcp->c_fileid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || 160 dcp->c_fileid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 161 return (EPERM); 162 } 163 164 bzero(&cookie, sizeof(cat_cookie_t)); 165 /* Reserve some space in the Catalog file. */ 166 if ((retval = cat_preflight(hfsmp, (2 * CAT_CREATE)+ CAT_RENAME, &cookie, p))) { 167 return (retval); 168 } 169 170 lockflags = SFL_CATALOG | SFL_ATTRIBUTE; 171 /* Directory hard links allocate space for a symlink. */ 172 if (type == DIR_HARDLINKS) { 173 lockflags |= SFL_BITMAP; 174 } 175 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK); 176 177 /* Save the current cnid value so we restore it if an error occurs. */ 178 orig_cnid = cp->c_desc.cd_cnid; 179 180 /* 181 * If this is a new hardlink then we need to create the inode 182 * and replace the original file/dir object with a link node. 183 */ 184 if ((cp->c_linkcount == 2) && !(cp->c_flag & C_HARDLINK)) { 185 newlink = 1; 186 bzero(&to_desc, sizeof(to_desc)); 187 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[type].cd_cnid; 188 to_desc.cd_cnid = cp->c_fileid; 189 to_desc.cd_flags = (type == DIR_HARDLINKS) ? CD_ISDIR : 0; 190 191 do { 192 if (type == DIR_HARDLINKS) { 193 /* Directory hardlinks always use the cnid. */ 194 indnodeno = cp->c_fileid; 195 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), 196 indnodeno); 197 } else { 198 /* Get a unique indirect node number */ 199 if (retval == 0) { 200 indnodeno = cp->c_fileid; 201 } else { 202 indnodeno = cur_link_id++; 203 } 204 MAKE_INODE_NAME(inodename, sizeof(inodename), 205 indnodeno); 206 } 207 /* Move original file/dir to data node directory */ 208 to_desc.cd_nameptr = (const u_int8_t *)inodename; 209 to_desc.cd_namelen = strlen(inodename); 210 211 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_private_desc[type], 212 &to_desc, NULL); 213 214 if (retval != 0 && retval != EEXIST) { 215 printf("hfs_makelink: cat_rename to %s failed (%d) fileid=%d, vol=%s\n", 216 inodename, retval, cp->c_fileid, hfsmp->vcbVN); 217 } 218 } while ((retval == EEXIST) && (type == FILE_HARDLINKS)); 219 if (retval) 220 goto out; 221 222 /* 223 * Replace original file/dir with a link record. 224 */ 225 226 bzero(&link_desc, sizeof(link_desc)); 227 link_desc.cd_nameptr = cp->c_desc.cd_nameptr; 228 link_desc.cd_namelen = cp->c_desc.cd_namelen; 229 link_desc.cd_parentcnid = cp->c_parentcnid; 230 link_desc.cd_flags = S_ISDIR(cp->c_mode) ? CD_ISDIR : 0; 231 232 retval = createindirectlink(hfsmp, indnodeno, &link_desc, 0, &linkcnid, true); 233 if (retval) { 234 int err; 235 236 /* Restore the cnode's cnid. */ 237 cp->c_desc.cd_cnid = orig_cnid; 238 239 /* Put the original file back. */ 240 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL); 241 if (err) { 242 if (err != EIO && err != ENXIO) 243 printf("hfs_makelink: error %d from cat_rename backout 1", err); 244 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); 245 } 246 if (retval != EIO && retval != ENXIO) { 247 printf("hfs_makelink: createindirectlink (1) failed: %d\n", retval); 248 retval = EIO; 249 } 250 goto out; 251 } 252 cp->c_attr.ca_linkref = indnodeno; 253 cp->c_desc.cd_cnid = linkcnid; 254 /* Directory hard links store the first link in an attribute. */ 255 if (type == DIR_HARDLINKS) { 256 if (setfirstlink(hfsmp, cp->c_fileid, linkcnid) == 0) 257 cp->c_attr.ca_recflags |= kHFSHasAttributesMask; 258 } else /* FILE_HARDLINKS */ { 259 cp->c_attr.ca_firstlink = linkcnid; 260 } 261 cp->c_attr.ca_recflags |= kHFSHasLinkChainMask; 262 } else { 263 indnodeno = cp->c_attr.ca_linkref; 264 } 265 266 /* 267 * Create a catalog entry for the new link (parentID + name). 268 */ 269 270 bzero(&link_desc, sizeof(link_desc)); 271 link_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; 272 link_desc.cd_namelen = strlen(cnp->cn_nameptr); 273 link_desc.cd_parentcnid = dcp->c_fileid; 274 link_desc.cd_flags = S_ISDIR(cp->c_mode) ? CD_ISDIR : 0; 275 276 /* Directory hard links store the first link in an attribute. */ 277 if (type == DIR_HARDLINKS) { 278 retval = getfirstlink(hfsmp, cp->c_fileid, &orig_firstlink); 279 } else /* FILE_HARDLINKS */ { 280 orig_firstlink = cp->c_attr.ca_firstlink; 281 } 282 if (retval == 0) 283 retval = createindirectlink(hfsmp, indnodeno, &link_desc, 284 orig_firstlink, &linkcnid, 285 (cp->c_attr.ca_recflags & kHFSHasLinkChainMask)); 286 if (retval && newlink) { 287 int err; 288 289 /* Get rid of new link */ 290 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr); 291 292 /* Restore the cnode's cnid. */ 293 cp->c_desc.cd_cnid = orig_cnid; 294 295 /* Put the original file back. */ 296 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL); 297 if (err) { 298 if (err != EIO && err != ENXIO) 299 printf("hfs_makelink: error %d from cat_rename backout 2", err); 300 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); 301 } 302 303 cp->c_attr.ca_linkref = 0; 304 305 if (retval != EIO && retval != ENXIO) { 306 printf("hfs_makelink: createindirectlink (2) failed: %d\n", retval); 307 retval = EIO; 308 } 309 goto out; 310 } else if (retval == 0) { 311 312 /* Update the original first link to point back to the new first link. */ 313 if (cp->c_attr.ca_recflags & kHFSHasLinkChainMask) { 314 (void) cat_update_siblinglinks(hfsmp, orig_firstlink, linkcnid, HFS_IGNORABLE_LINK); 315 316 /* Update the inode's first link value. */ 317 if (type == DIR_HARDLINKS) { 318 if (setfirstlink(hfsmp, cp->c_fileid, linkcnid) == 0) 319 cp->c_attr.ca_recflags |= kHFSHasAttributesMask; 320 } else { 321 cp->c_attr.ca_firstlink = linkcnid; 322 } 323 } 324 /* 325 * Finally, if this is a new hardlink then: 326 * - update the private system directory 327 * - mark the cnode as a hard link 328 */ 329 if (newlink) { 330 vnode_t vp; 331 332 hfsmp->hfs_private_attr[type].ca_entries++; 333 /* From application perspective, directory hard link is a 334 * normal directory. Therefore count the new directory 335 * hard link for folder count calculation. 336 */ 337 if (type == DIR_HARDLINKS) { 338 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[type]); 339 } 340 retval = cat_update(hfsmp, &hfsmp->hfs_private_desc[type], 341 &hfsmp->hfs_private_attr[type], NULL, NULL); 342 if (retval) { 343 if (retval != EIO && retval != ENXIO) { 344 printf("hfs_makelink: cat_update of privdir failed! (%d)\n", retval); 345 retval = EIO; 346 } 347 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE); 348 } 349 cp->c_flag |= C_HARDLINK; 350 351 /* 352 * Now we need to mark the vnodes as being hardlinks via the vnode_setmultipath call. 353 * Note that we're calling vnode_get here, which should simply add an iocount if possible, without 354 * doing much checking. It's safe to call this because we are protected by the cnode lock, which 355 * ensures that anyone trying to reclaim it will block until we release it. vnode_get will usually 356 * give us an extra iocount, unless the vnode is about to be reclaimed (and has no iocounts). 357 * In that case, we'd error out, but we'd also not care if we added the VISHARDLINK bit to the vnode. 358 * 359 * As for the iocount we're about to add, we can't necessarily always call vnode_put here. 360 * If the one we add is the only iocount on the vnode, and there was 361 * sufficient vnode pressure, it could go through VNOP_INACTIVE immediately, which would 362 * require the cnode lock and cause us to double-lock panic. We can only call vnode_put if we know 363 * that the vnode we're operating on is the one with which we came into hfs_vnop_link, because 364 * that means VFS took an iocount on it for us. If it's *not* the one that we came into the call 365 * with, then mark it as NEED_VNODE_PUT to have hfs_unlock drop it for us. hfs_vnop_link will 366 * unlock the cnode when it is finished. 367 */ 368 if ((vp = cp->c_vp) != NULLVP) { 369 if (vnode_get(vp) == 0) { 370 vnode_setmultipath(vp); 371 if (vp == src_vp) { 372 /* we have an iocount on data fork vnode already. */ 373 vnode_put(vp); 374 } 375 else { 376 cp->c_flag |= C_NEED_DVNODE_PUT; 377 } 378 } 379 } 380 if ((vp = cp->c_rsrc_vp) != NULLVP) { 381 if (vnode_get(vp) == 0) { 382 vnode_setmultipath(vp); 383 if (vp == src_vp) { 384 vnode_put(vp); 385 } 386 else { 387 cp->c_flag |= C_NEED_RVNODE_PUT; 388 } 389 } 390 } 391 cp->c_touch_chgtime = TRUE; 392 cp->c_flag |= C_FORCEUPDATE; 393 } 394 dcp->c_flag |= C_FORCEUPDATE; 395 } 396out: 397 hfs_systemfile_unlock(hfsmp, lockflags); 398 399 cat_postflight(hfsmp, &cookie, p); 400 401 if (retval == 0 && newlink) { 402 hfs_volupdate(hfsmp, VOL_MKFILE, 0); 403 } 404 return (retval); 405} 406 407 408/* 409 * link vnode operation 410 * 411 * IN vnode_t a_vp; 412 * IN vnode_t a_tdvp; 413 * IN struct componentname *a_cnp; 414 * IN vfs_context_t a_context; 415 */ 416int 417hfs_vnop_link(struct vnop_link_args *ap) 418{ 419 struct hfsmount *hfsmp; 420 struct vnode *vp = ap->a_vp; 421 struct vnode *tdvp = ap->a_tdvp; 422 struct vnode *fdvp = NULLVP; 423 struct componentname *cnp = ap->a_cnp; 424 struct cnode *cp; 425 struct cnode *tdcp; 426 struct cnode *fdcp = NULL; 427 struct cat_desc todesc; 428 cnid_t parentcnid; 429 int lockflags = 0; 430 int intrans = 0; 431 enum vtype v_type; 432 int error, ret; 433 434 hfsmp = VTOHFS(vp); 435 v_type = vnode_vtype(vp); 436 437 /* No hard links in HFS standard file systems. */ 438 if (hfsmp->hfs_flags & HFS_STANDARD) { 439 return (ENOTSUP); 440 } 441 /* Linking to a special file is not permitted. */ 442 if (v_type == VBLK || v_type == VCHR) { 443 return (EPERM); 444 } 445 446 /* 447 * For now, return ENOTSUP for a symlink target. This can happen 448 * for linkat(2) when called without AT_SYMLINK_FOLLOW. 449 */ 450 if (v_type == VLNK) 451 return (ENOTSUP); 452 453 if (v_type == VDIR) { 454#if CONFIG_HFS_DIRLINK 455 /* Make sure our private directory exists. */ 456 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid == 0) { 457 return (EPERM); 458 } 459 /* 460 * Directory hardlinks (ADLs) have only been qualified on 461 * journaled HFS+. If/when they are tested on non-journaled 462 * file systems then this test can be removed. 463 */ 464 if (hfsmp->jnl == NULL) { 465 return (EPERM); 466 } 467 /* Directory hardlinks also need the parent of the original directory. */ 468 if ((error = hfs_vget(hfsmp, hfs_currentparent(VTOC(vp)), &fdvp, 1, 0))) { 469 return (error); 470 } 471#else 472 /* some platforms don't support directory hardlinks. */ 473 return EPERM; 474#endif 475 } else { 476 /* Make sure our private directory exists. */ 477 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) { 478 return (ENOTSUP); 479 } 480 } 481 if (hfs_freeblks(hfsmp, 0) == 0) { 482 if (fdvp) { 483 vnode_put(fdvp); 484 } 485 return (ENOSPC); 486 } 487 488 check_for_tracked_file(vp, VTOC(vp)->c_ctime, NAMESPACE_HANDLER_LINK_CREATE, NULL); 489 490 491 /* Lock the cnodes. */ 492 if (fdvp) { 493 if ((error = hfs_lockfour(VTOC(tdvp), VTOC(vp), VTOC(fdvp), NULL, HFS_EXCLUSIVE_LOCK, NULL))) { 494 if (fdvp) { 495 vnode_put(fdvp); 496 } 497 return (error); 498 } 499 fdcp = VTOC(fdvp); 500 } else { 501 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) { 502 return (error); 503 } 504 } 505 tdcp = VTOC(tdvp); 506 cp = VTOC(vp); 507 /* grab the parent CNID from originlist after grabbing cnode locks */ 508 parentcnid = hfs_currentparent(cp); 509 510 /* 511 * Make sure we didn't race the src or dst parent directories with rmdir. 512 * Note that we should only have a src parent directory cnode lock 513 * if we're dealing with a directory hardlink here. 514 */ 515 if (fdcp) { 516 if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) { 517 error = ENOENT; 518 goto out; 519 } 520 } 521 522 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) { 523 error = ENOENT; 524 goto out; 525 } 526 527 /* Check the source for errors: 528 * too many links, immutable, race with unlink 529 */ 530 if (cp->c_linkcount >= HFS_LINK_MAX) { 531 error = EMLINK; 532 goto out; 533 } 534 if (cp->c_bsdflags & (IMMUTABLE | APPEND)) { 535 error = EPERM; 536 goto out; 537 } 538 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { 539 error = ENOENT; 540 goto out; 541 } 542 543 tdcp->c_flag |= C_DIR_MODIFICATION; 544 545 if (hfs_start_transaction(hfsmp) != 0) { 546 error = EINVAL; 547 goto out; 548 } 549 intrans = 1; 550 551 todesc.cd_flags = (v_type == VDIR) ? CD_ISDIR : 0; 552 todesc.cd_encoding = 0; 553 todesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; 554 todesc.cd_namelen = cnp->cn_namelen; 555 todesc.cd_parentcnid = tdcp->c_fileid; 556 todesc.cd_hint = 0; 557 todesc.cd_cnid = 0; 558 559 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); 560 561 /* If destination exists then we lost a race with create. */ 562 if (cat_lookup(hfsmp, &todesc, 0, 0, NULL, NULL, NULL, NULL) == 0) { 563 error = EEXIST; 564 goto out; 565 } 566 if (cp->c_flag & C_HARDLINK) { 567 struct cat_attr cattr; 568 569 /* If inode is missing then we lost a race with unlink. */ 570 if ((cat_idlookup(hfsmp, cp->c_fileid, 0, 0, NULL, &cattr, NULL) != 0) || 571 (cattr.ca_fileid != cp->c_fileid)) { 572 error = ENOENT; 573 goto out; 574 } 575 } else { 576 cnid_t fileid; 577 578 /* If source is missing then we lost a race with unlink. */ 579 if ((cat_lookup(hfsmp, &cp->c_desc, 0, 0, NULL, NULL, NULL, &fileid) != 0) || 580 (fileid != cp->c_fileid)) { 581 error = ENOENT; 582 goto out; 583 } 584 } 585 /* 586 * All directory links must reside in an non-ARCHIVED hierarchy. 587 */ 588 if (v_type == VDIR) { 589 /* 590 * - Source parent and destination parent cannot match 591 * - A link is not permitted in the root directory 592 * - Parent of 'pointed at' directory is not the root directory 593 * - The 'pointed at' directory (source) is not an ancestor 594 * of the new directory hard link (destination). 595 * - No ancestor of the new directory hard link (destination) 596 * is a directory hard link. 597 */ 598 if ((parentcnid == tdcp->c_fileid) || 599 (tdcp->c_fileid == kHFSRootFolderID) || 600 (parentcnid == kHFSRootFolderID) || 601 cat_check_link_ancestry(hfsmp, tdcp->c_fileid, cp->c_fileid)) { 602 error = EPERM; /* abide by the rules, you did not */ 603 goto out; 604 } 605 } 606 hfs_systemfile_unlock(hfsmp, lockflags); 607 lockflags = 0; 608 609 cp->c_linkcount++; 610 cp->c_touch_chgtime = TRUE; 611 error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp); 612 if (error) { 613 cp->c_linkcount--; 614 hfs_volupdate(hfsmp, VOL_UPDATE, 0); 615 } else { 616 /* Invalidate negative cache entries in the destination directory */ 617 if (tdcp->c_flag & C_NEG_ENTRIES) { 618 cache_purge_negatives(tdvp); 619 tdcp->c_flag &= ~C_NEG_ENTRIES; 620 } 621 622 /* Update the target directory and volume stats */ 623 tdcp->c_entries++; 624 if (v_type == VDIR) { 625 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr); 626 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask; 627 628 /* Set kHFSHasChildLinkBit in the destination hierarchy */ 629 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid); 630 if (error) { 631 printf ("hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN); 632 error = 0; 633 } 634 } 635 tdcp->c_dirchangecnt++; 636 hfs_incr_gencount(tdcp); 637 tdcp->c_touch_chgtime = TRUE; 638 tdcp->c_touch_modtime = TRUE; 639 tdcp->c_flag |= C_FORCEUPDATE; 640 641 error = hfs_update(tdvp, 0); 642 if (error) { 643 if (error != EIO && error != ENXIO) { 644 printf("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp); 645 error = EIO; 646 } 647 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE); 648 } 649 650 if ((v_type == VDIR) && 651 (fdcp != NULL) && 652 ((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) { 653 654 fdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask; 655 fdcp->c_touch_chgtime = TRUE; 656 fdcp->c_flag |= C_FORCEUPDATE; 657 error = hfs_update(fdvp, 0); 658 if (error) { 659 if (error != EIO && error != ENXIO) { 660 printf("hfs_vnop_link: error %d updating fdvp %p\n", error, fdvp); 661 // No point changing error as it's set immediate below 662 } 663 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE); 664 } 665 666 /* Set kHFSHasChildLinkBit in the source hierarchy */ 667 error = cat_set_childlinkbit(hfsmp, fdcp->c_parentcnid); 668 if (error) { 669 printf ("hfs_vnop_link: error updating source parent chain for id=%u, vol=%s\n", fdcp->c_cnid, hfsmp->vcbVN); 670 error = 0; 671 } 672 } 673 hfs_volupdate(hfsmp, VOL_MKFILE, 674 (tdcp->c_cnid == kHFSRootFolderID)); 675 } 676 /* Make sure update occurs inside transaction */ 677 cp->c_flag |= C_FORCEUPDATE; 678 679 if (error == 0 && (ret = hfs_update(vp, TRUE)) != 0) { 680 if (ret != EIO && ret != ENXIO) 681 printf("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp); 682 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE); 683 } 684 685out: 686 if (lockflags) { 687 hfs_systemfile_unlock(hfsmp, lockflags); 688 } 689 if (intrans) { 690 hfs_end_transaction(hfsmp); 691 } 692 693 tdcp->c_flag &= ~C_DIR_MODIFICATION; 694 wakeup((caddr_t)&tdcp->c_flag); 695 696 if (fdcp) { 697 hfs_unlockfour(tdcp, cp, fdcp, NULL); 698 } else { 699 hfs_unlockpair(tdcp, cp); 700 } 701 if (fdvp) { 702 vnode_put(fdvp); 703 } 704 return (error); 705} 706 707 708/* 709 * Remove a link to a hardlink file/dir. 710 * 711 * Note: dvp and vp cnodes are already locked. 712 */ 713int 714hfs_unlink(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve) 715{ 716 struct cnode *cp; 717 struct cnode *dcp; 718 struct cat_desc cndesc; 719 struct timeval tv; 720 char inodename[32]; 721 cnid_t prevlinkid; 722 cnid_t nextlinkid; 723 int lockflags = 0; 724 int started_tr; 725 int error; 726 727 if (hfsmp->hfs_flags & HFS_STANDARD) { 728 return (EPERM); 729 } 730 cp = VTOC(vp); 731 dcp = VTOC(dvp); 732 733 dcp->c_flag |= C_DIR_MODIFICATION; 734 735 /* Remove the entry from the namei cache: */ 736 cache_purge(vp); 737 738 if ((error = hfs_start_transaction(hfsmp)) != 0) { 739 started_tr = 0; 740 goto out; 741 } 742 started_tr = 1; 743 744 /* 745 * Protect against a race with rename by using the component 746 * name passed in and parent id from dvp (instead of using 747 * the cp->c_desc which may have changed). 748 * 749 * Re-lookup the component name so we get the correct cnid 750 * for the name (as opposed to the c_cnid in the cnode which 751 * could have changed before the cnode was locked). 752 */ 753 cndesc.cd_flags = vnode_isdir(vp) ? CD_ISDIR : 0; 754 cndesc.cd_encoding = cp->c_desc.cd_encoding; 755 cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr; 756 cndesc.cd_namelen = cnp->cn_namelen; 757 cndesc.cd_parentcnid = dcp->c_fileid; 758 cndesc.cd_hint = dcp->c_childhint; 759 760 lockflags = SFL_CATALOG | SFL_ATTRIBUTE; 761 if (cndesc.cd_flags & CD_ISDIR) { 762 /* We'll be removing the alias resource allocation blocks. */ 763 lockflags |= SFL_BITMAP; 764 } 765 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK); 766 767 if ((error = cat_lookuplink(hfsmp, &cndesc, &cndesc.cd_cnid, &prevlinkid, &nextlinkid))) { 768 goto out; 769 } 770 771 /* Reserve some space in the catalog file. */ 772 if (!skip_reserve && (error = cat_preflight(hfsmp, 2 * CAT_DELETE, NULL, 0))) { 773 goto out; 774 } 775 776 /* Purge any cached origin entries for a directory or file hard link. */ 777 hfs_relorigin(cp, dcp->c_fileid); 778 if (dcp->c_fileid != dcp->c_cnid) { 779 hfs_relorigin(cp, dcp->c_cnid); 780 } 781 782 /* Delete the link record. */ 783 if ((error = cat_deletelink(hfsmp, &cndesc))) { 784 goto out; 785 } 786 787 /* Update the parent directory. */ 788 if (dcp->c_entries > 0) { 789 dcp->c_entries--; 790 } 791 if (cndesc.cd_flags & CD_ISDIR) { 792 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr); 793 } 794 dcp->c_dirchangecnt++; 795 hfs_incr_gencount(dcp); 796 microtime(&tv); 797 dcp->c_ctime = tv.tv_sec; 798 dcp->c_mtime = tv.tv_sec; 799 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); 800 801 /* 802 * If this is the last link then we need to process the inode. 803 * Otherwise we need to fix up the link chain. 804 */ 805 --cp->c_linkcount; 806 if (cp->c_linkcount < 1) { 807 char delname[32]; 808 struct cat_desc to_desc; 809 struct cat_desc from_desc; 810 811 /* 812 * If a file inode or directory inode is being deleted, rename 813 * it to an open deleted file. This ensures that deletion 814 * of inode and its corresponding extended attributes does 815 * not overflow the journal. This inode will be deleted 816 * either in hfs_vnop_inactive() or in hfs_remove_orphans(). 817 * Note: a rename failure here is not fatal. 818 */ 819 bzero(&from_desc, sizeof(from_desc)); 820 bzero(&to_desc, sizeof(to_desc)); 821 if (vnode_isdir(vp)) { 822 if (cp->c_entries != 0) { 823 panic("hfs_unlink: dir not empty (id %d, %d entries)", cp->c_fileid, cp->c_entries); 824 } 825 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), 826 cp->c_attr.ca_linkref); 827 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid; 828 from_desc.cd_flags = CD_ISDIR; 829 to_desc.cd_flags = CD_ISDIR; 830 } else { 831 MAKE_INODE_NAME(inodename, sizeof(inodename), 832 cp->c_attr.ca_linkref); 833 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; 834 from_desc.cd_flags = 0; 835 to_desc.cd_flags = 0; 836 } 837 from_desc.cd_nameptr = (const u_int8_t *)inodename; 838 from_desc.cd_namelen = strlen(inodename); 839 from_desc.cd_cnid = cp->c_fileid; 840 841 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid); 842 to_desc.cd_nameptr = (const u_int8_t *)delname; 843 to_desc.cd_namelen = strlen(delname); 844 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; 845 to_desc.cd_cnid = cp->c_fileid; 846 847 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_private_desc[FILE_HARDLINKS], 848 &to_desc, (struct cat_desc *)NULL); 849 if (error == 0) { 850 cp->c_flag |= C_DELETED; 851 cp->c_attr.ca_recflags &= ~kHFSHasLinkChainMask; 852 cp->c_attr.ca_firstlink = 0; 853 if (vnode_isdir(vp)) { 854 hfsmp->hfs_private_attr[DIR_HARDLINKS].ca_entries--; 855 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[DIR_HARDLINKS]); 856 857 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++; 858 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]); 859 860 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[DIR_HARDLINKS], 861 &hfsmp->hfs_private_attr[DIR_HARDLINKS], NULL, NULL); 862 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS], 863 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL); 864 } 865 } else { 866 error = 0; /* rename failure here is not fatal */ 867 } 868 } else /* Still some links left */ { 869 cnid_t firstlink; 870 871 /* 872 * Update the start of the link chain. 873 * Note: Directory hard links store the first link in an attribute. 874 */ 875 if (vnode_isdir(vp) && 876 getfirstlink(hfsmp, cp->c_fileid, &firstlink) == 0 && 877 firstlink == cndesc.cd_cnid) { 878 if (setfirstlink(hfsmp, cp->c_fileid, nextlinkid) == 0) 879 cp->c_attr.ca_recflags |= kHFSHasAttributesMask; 880 } else if (vnode_isreg(vp) && cp->c_attr.ca_firstlink == cndesc.cd_cnid) { 881 cp->c_attr.ca_firstlink = nextlinkid; 882 } 883 /* Update previous link. */ 884 if (prevlinkid) { 885 (void) cat_update_siblinglinks(hfsmp, prevlinkid, HFS_IGNORABLE_LINK, nextlinkid); 886 } 887 /* Update next link. */ 888 if (nextlinkid) { 889 (void) cat_update_siblinglinks(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK); 890 } 891 892 /* 893 * The call to cat_releasedesc below will only release the name buffer; 894 * it does not zero out the rest of the fields in the 'cat_desc' data structure. 895 * 896 * As a result, since there are still other links at this point, we need 897 * to make the current cnode descriptor point to the raw inode. If a path-based 898 * system call comes along first, it will replace the descriptor with a valid link 899 * ID. If a userland process already has a file descriptor open, then they will 900 * bypass that lookup, though. Replacing the descriptor CNID with the raw 901 * inode will force it to generate a new full path. 902 */ 903 cp->c_cnid = cp->c_fileid; 904 905 } 906 907 /* Push new link count to disk. */ 908 cp->c_ctime = tv.tv_sec; 909 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL); 910 911 /* All done with the system files. */ 912 hfs_systemfile_unlock(hfsmp, lockflags); 913 lockflags = 0; 914 915 /* Update file system stats. */ 916 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID)); 917 918 /* 919 * All done with this cnode's descriptor... 920 * 921 * Note: all future catalog calls for this cnode may be 922 * by fileid only. This is OK for HFS (which doesn't have 923 * file thread records) since HFS doesn't support hard links. 924 */ 925 cat_releasedesc(&cp->c_desc); 926 927out: 928 if (lockflags) { 929 hfs_systemfile_unlock(hfsmp, lockflags); 930 } 931 if (started_tr) { 932 hfs_end_transaction(hfsmp); 933 } 934 935 dcp->c_flag &= ~C_DIR_MODIFICATION; 936 wakeup((caddr_t)&dcp->c_flag); 937 938 return (error); 939} 940 941 942/* 943 * Initialize the HFS+ private system directories. 944 * 945 * These directories are used to hold the inodes 946 * for file and directory hardlinks as well as 947 * open-unlinked files. 948 * 949 * If they don't yet exist they will get created. 950 * 951 * This call is assumed to be made during mount. 952 */ 953void 954hfs_privatedir_init(struct hfsmount * hfsmp, enum privdirtype type) 955{ 956 struct vnode * dvp = NULLVP; 957 struct cnode * dcp = NULL; 958 struct cat_desc *priv_descp; 959 struct cat_attr *priv_attrp; 960 struct FndrDirInfo * fndrinfo; 961 struct timeval tv; 962 int lockflags; 963 int trans = 0; 964 int error; 965 966 if (hfsmp->hfs_flags & HFS_STANDARD) { 967 return; 968 } 969 970 priv_descp = &hfsmp->hfs_private_desc[type]; 971 priv_attrp = &hfsmp->hfs_private_attr[type]; 972 973 /* Check if directory already exists. */ 974 if (priv_descp->cd_cnid != 0) { 975 return; 976 } 977 978 priv_descp->cd_parentcnid = kRootDirID; 979 priv_descp->cd_nameptr = (const u_int8_t *)hfs_private_names[type]; 980 priv_descp->cd_namelen = strlen((const char *)priv_descp->cd_nameptr); 981 priv_descp->cd_flags = CD_ISDIR | CD_DECOMPOSED; 982 983 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); 984 error = cat_lookup(hfsmp, priv_descp, 0, 0, NULL, priv_attrp, NULL, NULL); 985 hfs_systemfile_unlock(hfsmp, lockflags); 986 987 if (error == 0) { 988 if (type == FILE_HARDLINKS) { 989 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime; 990 } 991 priv_descp->cd_cnid = priv_attrp->ca_fileid; 992 goto exit; 993 } 994 995 /* Directory is missing, if this is read-only then we're done. */ 996 if (hfsmp->hfs_flags & HFS_READ_ONLY) { 997 goto exit; 998 } 999 1000 /* Grab the root directory so we can update it later. */ 1001 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0, 0) != 0) { 1002 goto exit; 1003 } 1004 dcp = VTOC(dvp); 1005 1006 /* Setup the default attributes */ 1007 bzero(priv_attrp, sizeof(struct cat_attr)); 1008 priv_attrp->ca_flags = UF_IMMUTABLE | UF_HIDDEN; 1009 priv_attrp->ca_mode = S_IFDIR; 1010 if (type == DIR_HARDLINKS) { 1011 priv_attrp->ca_mode |= S_ISVTX | S_IRUSR | S_IXUSR | S_IRGRP | 1012 S_IXGRP | S_IROTH | S_IXOTH; 1013 } 1014 priv_attrp->ca_linkcount = 1; 1015 priv_attrp->ca_itime = hfsmp->hfs_itime; 1016 priv_attrp->ca_recflags = kHFSHasFolderCountMask; 1017 1018 fndrinfo = (struct FndrDirInfo *)&priv_attrp->ca_finderinfo; 1019 fndrinfo->frLocation.v = SWAP_BE16(16384); 1020 fndrinfo->frLocation.h = SWAP_BE16(16384); 1021 fndrinfo->frFlags = SWAP_BE16(kIsInvisible + kNameLocked); 1022 1023 if (hfs_start_transaction(hfsmp) != 0) { 1024 goto exit; 1025 } 1026 trans = 1; 1027 1028 /* Need the catalog and EA b-trees for CNID acquisition */ 1029 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK); 1030 1031 /* Make sure there's space in the Catalog file. */ 1032 if (cat_preflight(hfsmp, CAT_CREATE, NULL, 0) != 0) { 1033 hfs_systemfile_unlock(hfsmp, lockflags); 1034 goto exit; 1035 } 1036 1037 /* Get the CNID for use */ 1038 cnid_t new_id; 1039 if ((error = cat_acquire_cnid(hfsmp, &new_id))) { 1040 hfs_systemfile_unlock (hfsmp, lockflags); 1041 goto exit; 1042 } 1043 1044 /* Create the private directory on disk. */ 1045 error = cat_create(hfsmp, new_id, priv_descp, priv_attrp, NULL); 1046 if (error == 0) { 1047 priv_descp->cd_cnid = priv_attrp->ca_fileid; 1048 1049 /* Update the parent directory */ 1050 dcp->c_entries++; 1051 INC_FOLDERCOUNT(hfsmp, dcp->c_attr); 1052 dcp->c_dirchangecnt++; 1053 hfs_incr_gencount(dcp); 1054 microtime(&tv); 1055 dcp->c_ctime = tv.tv_sec; 1056 dcp->c_mtime = tv.tv_sec; 1057 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL); 1058 } 1059 1060 hfs_systemfile_unlock(hfsmp, lockflags); 1061 1062 if (error) { 1063 goto exit; 1064 } 1065 if (type == FILE_HARDLINKS) { 1066 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime; 1067 } 1068 hfs_volupdate(hfsmp, VOL_MKDIR, 1); 1069exit: 1070 if (trans) { 1071 hfs_end_transaction(hfsmp); 1072 } 1073 if (dvp) { 1074 hfs_unlock(dcp); 1075 vnode_put(dvp); 1076 } 1077 if ((error == 0) && (type == DIR_HARDLINKS)) { 1078 hfs_xattr_init(hfsmp); 1079 } 1080} 1081 1082 1083/* 1084 * Lookup a hardlink link (from chain) 1085 */ 1086int 1087hfs_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid) 1088{ 1089 int lockflags; 1090 int error; 1091 1092 *prevlinkid = 0; 1093 *nextlinkid = 0; 1094 1095 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); 1096 1097 error = cat_lookup_siblinglinks(hfsmp, linkfileid, prevlinkid, nextlinkid); 1098 if (error == ENOLINK) { 1099 hfs_systemfile_unlock(hfsmp, lockflags); 1100 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK); 1101 1102 error = getfirstlink(hfsmp, linkfileid, nextlinkid); 1103 } 1104 hfs_systemfile_unlock(hfsmp, lockflags); 1105 1106 return (error); 1107} 1108 1109 1110/* Find the oldest / last hardlink in the link chain */ 1111int 1112hfs_lookup_lastlink (struct hfsmount *hfsmp, cnid_t linkfileid, 1113 cnid_t *lastid, struct cat_desc *cdesc) { 1114 int lockflags; 1115 int error; 1116 1117 *lastid = 0; 1118 1119 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); 1120 1121 error = cat_lookup_lastlink(hfsmp, linkfileid, lastid, cdesc); 1122 1123 hfs_systemfile_unlock(hfsmp, lockflags); 1124 1125 /* 1126 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed 1127 * upon error cases. 1128 */ 1129 return error; 1130} 1131 1132 1133/* 1134 * Cache the origin of a directory or file hard link 1135 * 1136 * cnode must be lock on entry 1137 */ 1138__private_extern__ 1139void 1140hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid) 1141{ 1142 linkorigin_t *origin = NULL; 1143 thread_t thread = current_thread(); 1144 int count = 0; 1145 int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS; 1146 /* 1147 * Look for an existing origin first. If not found, create/steal one. 1148 */ 1149 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) { 1150 ++count; 1151 if (origin->lo_thread == thread) { 1152 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link); 1153 break; 1154 } 1155 } 1156 if (origin == NULL) { 1157 /* Recycle the last (i.e., the oldest) if we have too many. */ 1158 if (count > maxorigins) { 1159 origin = TAILQ_LAST(&cp->c_originlist, hfs_originhead); 1160 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link); 1161 } else { 1162 MALLOC(origin, linkorigin_t *, sizeof(linkorigin_t), M_TEMP, M_WAITOK); 1163 } 1164 origin->lo_thread = thread; 1165 } 1166 origin->lo_cnid = cp->c_cnid; 1167 origin->lo_parentcnid = parentcnid; 1168 TAILQ_INSERT_HEAD(&cp->c_originlist, origin, lo_link); 1169} 1170 1171/* 1172 * Release any cached origins for a directory or file hard link 1173 * 1174 * cnode must be lock on entry 1175 */ 1176__private_extern__ 1177void 1178hfs_relorigins(struct cnode *cp) 1179{ 1180 linkorigin_t *origin, *prev; 1181 1182 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) { 1183 FREE(origin, M_TEMP); 1184 } 1185 TAILQ_INIT(&cp->c_originlist); 1186} 1187 1188/* 1189 * Release a specific origin for a directory or file hard link 1190 * 1191 * cnode must be lock on entry 1192 */ 1193__private_extern__ 1194void 1195hfs_relorigin(struct cnode *cp, cnid_t parentcnid) 1196{ 1197 linkorigin_t *origin, *prev; 1198 thread_t thread = current_thread(); 1199 1200 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) { 1201 if ((origin->lo_thread == thread) || 1202 (origin->lo_parentcnid == parentcnid)) { 1203 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link); 1204 FREE(origin, M_TEMP); 1205 break; 1206 } 1207 } 1208} 1209 1210/* 1211 * Test if a directory or file hard link has a cached origin 1212 * 1213 * cnode must be lock on entry 1214 */ 1215__private_extern__ 1216int 1217hfs_haslinkorigin(cnode_t *cp) 1218{ 1219 if (cp->c_flag & C_HARDLINK) { 1220 linkorigin_t *origin; 1221 thread_t thread = current_thread(); 1222 1223 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) { 1224 if (origin->lo_thread == thread) { 1225 return (1); 1226 } 1227 } 1228 } 1229 return (0); 1230} 1231 1232/* 1233 * Obtain the current parent cnid of a directory or file hard link 1234 * 1235 * cnode must be lock on entry 1236 */ 1237__private_extern__ 1238cnid_t 1239hfs_currentparent(cnode_t *cp) 1240{ 1241 if (cp->c_flag & C_HARDLINK) { 1242 linkorigin_t *origin; 1243 thread_t thread = current_thread(); 1244 1245 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) { 1246 if (origin->lo_thread == thread) { 1247 return (origin->lo_parentcnid); 1248 } 1249 } 1250 } 1251 return (cp->c_parentcnid); 1252} 1253 1254/* 1255 * Obtain the current cnid of a directory or file hard link 1256 * 1257 * cnode must be lock on entry 1258 */ 1259__private_extern__ 1260cnid_t 1261hfs_currentcnid(cnode_t *cp) 1262{ 1263 if (cp->c_flag & C_HARDLINK) { 1264 linkorigin_t *origin; 1265 thread_t thread = current_thread(); 1266 1267 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) { 1268 if (origin->lo_thread == thread) { 1269 return (origin->lo_cnid); 1270 } 1271 } 1272 } 1273 return (cp->c_cnid); 1274} 1275 1276 1277/* 1278 * Set the first link attribute for a given file id. 1279 * 1280 * The attributes b-tree must already be locked. 1281 * If journaling is enabled, a transaction must already be started. 1282 */ 1283static int 1284setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink) 1285{ 1286 FCB * btfile; 1287 BTreeIterator * iterator; 1288 FSBufferDescriptor btdata; 1289 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE]; 1290 HFSPlusAttrData *dataptr; 1291 int result; 1292 u_int16_t datasize; 1293 1294 if (hfsmp->hfs_attribute_cp == NULL) { 1295 return (EPERM); 1296 } 1297 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1298 bzero(iterator, sizeof(*iterator)); 1299 1300 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key); 1301 if (result) { 1302 goto out; 1303 } 1304 dataptr = (HFSPlusAttrData *)&attrdata[0]; 1305 dataptr->recordType = kHFSPlusAttrInlineData; 1306 dataptr->reserved[0] = 0; 1307 dataptr->reserved[1] = 0; 1308 1309 /* 1310 * Since attrData is variable length, we calculate the size of 1311 * attrData by subtracting the size of all other members of 1312 * structure HFSPlusAttData from the size of attrdata. 1313 */ 1314 (void)snprintf((char *)&dataptr->attrData[0], 1315 sizeof(dataptr) - (4 * sizeof(uint32_t)), 1316 "%lu", (unsigned long)firstlink); 1317 dataptr->attrSize = 1 + strlen((char *)&dataptr->attrData[0]); 1318 1319 /* Calculate size of record rounded up to multiple of 2 bytes. */ 1320 datasize = sizeof(HFSPlusAttrData) - 2 + dataptr->attrSize + ((dataptr->attrSize & 1) ? 1 : 0); 1321 1322 btdata.bufferAddress = dataptr; 1323 btdata.itemSize = datasize; 1324 btdata.itemCount = 1; 1325 1326 btfile = hfsmp->hfs_attribute_cp->c_datafork; 1327 1328 /* Insert the attribute. */ 1329 result = BTInsertRecord(btfile, iterator, &btdata, datasize); 1330 if (result == btExists) { 1331 result = BTReplaceRecord(btfile, iterator, &btdata, datasize); 1332 } 1333 (void) BTFlushPath(btfile); 1334out: 1335 FREE(iterator, M_TEMP); 1336 1337 return MacToVFSError(result); 1338} 1339 1340/* 1341 * Get the first link attribute for a given file id. 1342 * 1343 * The attributes b-tree must already be locked. 1344 */ 1345static int 1346getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink) 1347{ 1348 FCB * btfile; 1349 BTreeIterator * iterator; 1350 FSBufferDescriptor btdata; 1351 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE]; 1352 HFSPlusAttrData *dataptr; 1353 int result; 1354 u_int16_t datasize; 1355 1356 if (hfsmp->hfs_attribute_cp == NULL) { 1357 return (EPERM); 1358 } 1359 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 1360 bzero(iterator, sizeof(*iterator)); 1361 1362 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key); 1363 if (result) 1364 goto out; 1365 1366 dataptr = (HFSPlusAttrData *)&attrdata[0]; 1367 datasize = sizeof(attrdata); 1368 1369 btdata.bufferAddress = dataptr; 1370 btdata.itemSize = sizeof(attrdata); 1371 btdata.itemCount = 1; 1372 1373 btfile = hfsmp->hfs_attribute_cp->c_datafork; 1374 1375 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL); 1376 if (result) 1377 goto out; 1378 1379 if (dataptr->attrSize < 3) { 1380 result = ENOENT; 1381 goto out; 1382 } 1383 *firstlink = strtoul((char*)&dataptr->attrData[0], NULL, 10); 1384out: 1385 FREE(iterator, M_TEMP); 1386 1387 return MacToVFSError(result); 1388} 1389 1390