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