2 * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/mount.h>
35 #include <sys/vnode.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
38 #include <sys/fsctl.h>
41 #include "hfs_catalog.h"
42 #include "hfs_format.h"
43 #include "hfs_endian.h"
46 static int cur_link_id
= 0;
49 * Private directories where hardlink inodes reside.
51 const char *hfs_private_names
[] = {
52 HFSPLUSMETADATAFOLDER
, /* FILE HARDLINKS */
53 HFSPLUS_DIR_METADATA_FOLDER
/* DIRECTORY HARDLINKS */
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.
62 static int setfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t firstlink
);
63 static int getfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t
*firstlink
);
65 int hfs_makelink(struct hfsmount
*hfsmp
, struct vnode
*src_vp
, struct cnode
*cp
,
66 struct cnode
*dcp
, struct componentname
*cnp
);
68 * Create a new catalog link record
70 * An indirect link is a reference to an inode (the real
71 * file or directory record).
73 * All the indirect links for a given inode are chained
74 * together in a doubly linked list.
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.
82 createindirectlink(struct hfsmount
*hfsmp
, u_int32_t linknum
, struct cat_desc
*descp
,
83 cnid_t nextcnid
, cnid_t
*linkcnid
, int is_inode_linkchain_set
)
85 struct FndrFileInfo
*fip
;
89 printf("hfs: createindirectlink: linknum is zero!\n");
93 /* Setup the default attributes */
94 bzero(&attr
, sizeof(attr
));
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
;
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
);
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.
116 if ((is_inode_linkchain_set
== 0) && (nextcnid
== 0)) {
117 attr
.ca_recflags
&= ~kHFSHasLinkChainMask
;
120 /* Create the indirect link directly in the catalog */
121 return cat_createlink(hfsmp
, descp
, &attr
, nextcnid
, linkcnid
);
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
131 * The cnodes cp and dcp must be locked.
134 hfs_makelink(struct hfsmount
*hfsmp
, struct vnode
*src_vp
, struct cnode
*cp
,
135 struct cnode
*dcp
, struct componentname
*cnp
)
137 vfs_context_t ctx
= cnp
->cn_context
;
138 struct proc
*p
= vfs_context_proc(ctx
);
139 u_int32_t indnodeno
= 0;
141 struct cat_desc to_desc
;
142 struct cat_desc link_desc
;
149 cnid_t orig_firstlink
;
150 enum privdirtype type
;
152 type
= S_ISDIR(cp
->c_mode
) ? DIR_HARDLINKS
: FILE_HARDLINKS
;
154 if (cur_link_id
== 0) {
155 cur_link_id
= ((random() & 0x3fffffff) + 100);
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
) {
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
))) {
170 lockflags
= SFL_CATALOG
| SFL_ATTRIBUTE
;
171 /* Directory hard links allocate space for a symlink. */
172 if (type
== DIR_HARDLINKS
) {
173 lockflags
|= SFL_BITMAP
;
175 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
177 /* Save the current cnid value so we restore it if an error occurs. */
178 orig_cnid
= cp
->c_desc
.cd_cnid
;
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.
184 if ((cp
->c_linkcount
== 2) && !(cp
->c_flag
& C_HARDLINK
)) {
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;
192 if (type
== DIR_HARDLINKS
) {
193 /* Directory hardlinks always use the cnid. */
194 indnodeno
= cp
->c_fileid
;
195 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
),
198 /* Get a unique indirect node number */
200 indnodeno
= cp
->c_fileid
;
202 indnodeno
= cur_link_id
++;
204 MAKE_INODE_NAME(inodename
, sizeof(inodename
),
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
);
211 retval
= cat_rename(hfsmp
, &cp
->c_desc
, &hfsmp
->hfs_private_desc
[type
],
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
);
218 } while ((retval
== EEXIST
) && (type
== FILE_HARDLINKS
));
223 * Replace original file/dir with a link record.
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;
232 retval
= createindirectlink(hfsmp
, indnodeno
, &link_desc
, 0, &linkcnid
, true);
236 /* Restore the cnode's cnid. */
237 cp
->c_desc
.cd_cnid
= orig_cnid
;
239 /* Put the original file back. */
240 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
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
);
246 if (retval
!= EIO
&& retval
!= ENXIO
) {
247 printf("hfs_makelink: createindirectlink (1) failed: %d\n", retval
);
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
;
261 cp
->c_attr
.ca_recflags
|= kHFSHasLinkChainMask
;
263 indnodeno
= cp
->c_attr
.ca_linkref
;
267 * Create a catalog entry for the new link (parentID + name).
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;
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
;
283 retval
= createindirectlink(hfsmp
, indnodeno
, &link_desc
,
284 orig_firstlink
, &linkcnid
,
285 (cp
->c_attr
.ca_recflags
& kHFSHasLinkChainMask
));
286 if (retval
&& newlink
) {
289 /* Get rid of new link */
290 (void) cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
292 /* Restore the cnode's cnid. */
293 cp
->c_desc
.cd_cnid
= orig_cnid
;
295 /* Put the original file back. */
296 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
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
);
303 cp
->c_attr
.ca_linkref
= 0;
305 if (retval
!= EIO
&& retval
!= ENXIO
) {
306 printf("hfs_makelink: createindirectlink (2) failed: %d\n", retval
);
310 } else if (retval
== 0) {
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
);
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
;
321 cp
->c_attr
.ca_firstlink
= linkcnid
;
325 * Finally, if this is a new hardlink then:
326 * - update the private system directory
327 * - mark the cnode as a hard link
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.
337 if (type
== DIR_HARDLINKS
) {
338 INC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[type
]);
340 retval
= cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[type
],
341 &hfsmp
->hfs_private_attr
[type
], NULL
, NULL
);
343 if (retval
!= EIO
&& retval
!= ENXIO
) {
344 printf("hfs_makelink: cat_update of privdir failed! (%d)\n", retval
);
347 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
349 cp
->c_flag
|= C_HARDLINK
;
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.
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.
368 if ((vp
= cp
->c_vp
) != NULLVP
) {
369 if (vnode_get(vp
) == 0) {
370 vnode_setmultipath(vp
);
372 /* we have an iocount on data fork vnode already. */
376 cp
->c_flag
|= C_NEED_DVNODE_PUT
;
380 if ((vp
= cp
->c_rsrc_vp
) != NULLVP
) {
381 if (vnode_get(vp
) == 0) {
382 vnode_setmultipath(vp
);
387 cp
->c_flag
|= C_NEED_RVNODE_PUT
;
391 cp
->c_flag
|= C_MODIFIED
;
392 cp
->c_touch_chgtime
= TRUE
;
396 hfs_systemfile_unlock(hfsmp
, lockflags
);
398 cat_postflight(hfsmp
, &cookie
, p
);
400 if (retval
== 0 && newlink
) {
401 hfs_volupdate(hfsmp
, VOL_MKFILE
, 0);
408 * link vnode operation
412 * IN struct componentname *a_cnp;
413 * IN vfs_context_t a_context;
416 hfs_vnop_link(struct vnop_link_args
*ap
)
418 struct hfsmount
*hfsmp
;
419 struct vnode
*vp
= ap
->a_vp
;
420 struct vnode
*tdvp
= ap
->a_tdvp
;
421 struct vnode
*fdvp
= NULLVP
;
422 struct componentname
*cnp
= ap
->a_cnp
;
425 struct cnode
*fdcp
= NULL
;
426 struct cat_desc todesc
;
434 v_type
= vnode_vtype(vp
);
436 /* No hard links in HFS standard file systems. */
437 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
440 /* Linking to a special file is not permitted. */
441 if (v_type
== VBLK
|| v_type
== VCHR
) {
446 * For now, return ENOTSUP for a symlink target. This can happen
447 * for linkat(2) when called without AT_SYMLINK_FOLLOW.
454 if (v_type
== VDIR
) {
455 #if CONFIG_HFS_DIRLINK
456 /* Make sure our private directory exists. */
457 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
== 0) {
461 * Directory hardlinks (ADLs) have only been qualified on
462 * journaled HFS+. If/when they are tested on non-journaled
463 * file systems then this test can be removed.
465 if (hfsmp
->jnl
== NULL
) {
469 /* Directory hardlinks also need the parent of the original directory. */
470 if ((error
= hfs_vget(hfsmp
, hfs_currentparent(cp
, /* have_lock: */ false),
475 /* some platforms don't support directory hardlinks. */
479 /* Make sure our private directory exists. */
480 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
== 0) {
484 if (hfs_freeblks(hfsmp
, 0) == 0) {
491 check_for_tracked_file(vp
, VTOC(vp
)->c_ctime
, NAMESPACE_HANDLER_LINK_CREATE
, NULL
);
494 /* Lock the cnodes. */
496 if ((error
= hfs_lockfour(VTOC(tdvp
), VTOC(vp
), VTOC(fdvp
), NULL
, HFS_EXCLUSIVE_LOCK
, NULL
))) {
504 if ((error
= hfs_lockpair(VTOC(tdvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
509 /* grab the parent CNID from originlist after grabbing cnode locks */
510 parentcnid
= hfs_currentparent(cp
, /* have_lock: */ true);
513 * Make sure we didn't race the src or dst parent directories with rmdir.
514 * Note that we should only have a src parent directory cnode lock
515 * if we're dealing with a directory hardlink here.
518 if (fdcp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
524 if (tdcp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
529 /* Check the source for errors:
530 * too many links, immutable, race with unlink
532 if (cp
->c_linkcount
>= HFS_LINK_MAX
) {
536 if (cp
->c_bsdflags
& (IMMUTABLE
| APPEND
)) {
540 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
545 tdcp
->c_flag
|= C_DIR_MODIFICATION
;
547 if (hfs_start_transaction(hfsmp
) != 0) {
553 todesc
.cd_flags
= (v_type
== VDIR
) ? CD_ISDIR
: 0;
554 todesc
.cd_encoding
= 0;
555 todesc
.cd_nameptr
= (const u_int8_t
*)cnp
->cn_nameptr
;
556 todesc
.cd_namelen
= cnp
->cn_namelen
;
557 todesc
.cd_parentcnid
= tdcp
->c_fileid
;
561 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
563 /* If destination exists then we lost a race with create. */
564 if (cat_lookup(hfsmp
, &todesc
, 0, 0, NULL
, NULL
, NULL
, NULL
) == 0) {
568 if (cp
->c_flag
& C_HARDLINK
) {
569 struct cat_attr cattr
;
571 /* If inode is missing then we lost a race with unlink. */
572 if ((cat_idlookup(hfsmp
, cp
->c_fileid
, 0, 0, NULL
, &cattr
, NULL
) != 0) ||
573 (cattr
.ca_fileid
!= cp
->c_fileid
)) {
580 /* If source is missing then we lost a race with unlink. */
581 if ((cat_lookup(hfsmp
, &cp
->c_desc
, 0, 0, NULL
, NULL
, NULL
, &fileid
) != 0) ||
582 (fileid
!= cp
->c_fileid
)) {
588 * All directory links must reside in an non-ARCHIVED hierarchy.
590 if (v_type
== VDIR
) {
592 * - Source parent and destination parent cannot match
593 * - A link is not permitted in the root directory
594 * - Parent of 'pointed at' directory is not the root directory
595 * - The 'pointed at' directory (source) is not an ancestor
596 * of the new directory hard link (destination).
597 * - No ancestor of the new directory hard link (destination)
598 * is a directory hard link.
600 if ((parentcnid
== tdcp
->c_fileid
) ||
601 (tdcp
->c_fileid
== kHFSRootFolderID
) ||
602 (parentcnid
== kHFSRootFolderID
) ||
603 cat_check_link_ancestry(hfsmp
, tdcp
->c_fileid
, cp
->c_fileid
)) {
604 error
= EPERM
; /* abide by the rules, you did not */
608 hfs_systemfile_unlock(hfsmp
, lockflags
);
612 cp
->c_flag
|= C_MODIFIED
;
613 cp
->c_touch_chgtime
= TRUE
;
614 error
= hfs_makelink(hfsmp
, vp
, cp
, tdcp
, cnp
);
617 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
619 /* Invalidate negative cache entries in the destination directory */
620 if (tdcp
->c_flag
& C_NEG_ENTRIES
) {
621 cache_purge_negatives(tdvp
);
622 tdcp
->c_flag
&= ~C_NEG_ENTRIES
;
625 /* Update the target directory and volume stats */
627 if (v_type
== VDIR
) {
628 INC_FOLDERCOUNT(hfsmp
, tdcp
->c_attr
);
629 tdcp
->c_attr
.ca_recflags
|= kHFSHasChildLinkMask
;
631 /* Set kHFSHasChildLinkBit in the destination hierarchy */
632 error
= cat_set_childlinkbit(hfsmp
, tdcp
->c_parentcnid
);
634 printf ("hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp
->c_cnid
, hfsmp
->vcbVN
);
638 tdcp
->c_dirchangecnt
++;
639 tdcp
->c_flag
|= C_MODIFIED
;
640 hfs_incr_gencount(tdcp
);
641 tdcp
->c_touch_chgtime
= TRUE
;
642 tdcp
->c_touch_modtime
= TRUE
;
644 error
= hfs_update(tdvp
, 0);
646 if (error
!= EIO
&& error
!= ENXIO
) {
647 printf("hfs_vnop_link: error %d updating tdvp %p\n", error
, tdvp
);
650 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
653 if ((v_type
== VDIR
) &&
655 ((fdcp
->c_attr
.ca_recflags
& kHFSHasChildLinkMask
) == 0)) {
657 fdcp
->c_attr
.ca_recflags
|= kHFSHasChildLinkMask
;
658 fdcp
->c_flag
|= C_MODIFIED
;
659 fdcp
->c_touch_chgtime
= TRUE
;
660 error
= hfs_update(fdvp
, 0);
662 if (error
!= EIO
&& error
!= ENXIO
) {
663 printf("hfs_vnop_link: error %d updating fdvp %p\n", error
, fdvp
);
664 // No point changing error as it's set immediate below
666 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
669 /* Set kHFSHasChildLinkBit in the source hierarchy */
670 error
= cat_set_childlinkbit(hfsmp
, fdcp
->c_parentcnid
);
672 printf ("hfs_vnop_link: error updating source parent chain for id=%u, vol=%s\n", fdcp
->c_cnid
, hfsmp
->vcbVN
);
676 hfs_volupdate(hfsmp
, VOL_MKFILE
,
677 (tdcp
->c_cnid
== kHFSRootFolderID
));
680 if (error
== 0 && (ret
= hfs_update(vp
, 0)) != 0) {
681 if (ret
!= EIO
&& ret
!= ENXIO
)
682 printf("hfs_vnop_link: error %d updating vp @ %p\n", ret
, vp
);
683 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
688 hfs_systemfile_unlock(hfsmp
, lockflags
);
691 hfs_end_transaction(hfsmp
);
694 tdcp
->c_flag
&= ~C_DIR_MODIFICATION
;
695 wakeup((caddr_t
)&tdcp
->c_flag
);
698 hfs_unlockfour(tdcp
, cp
, fdcp
, NULL
);
700 hfs_unlockpair(tdcp
, cp
);
710 * Remove a link to a hardlink file/dir.
712 * Note: dvp and vp cnodes are already locked.
715 hfs_unlink(struct hfsmount
*hfsmp
, struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
, int skip_reserve
)
719 struct cat_desc cndesc
;
728 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
734 dcp
->c_flag
|= C_DIR_MODIFICATION
;
736 /* Remove the entry from the namei cache: */
739 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
746 * Protect against a race with rename by using the component
747 * name passed in and parent id from dvp (instead of using
748 * the cp->c_desc which may have changed).
750 * Re-lookup the component name so we get the correct cnid
751 * for the name (as opposed to the c_cnid in the cnode which
752 * could have changed before the cnode was locked).
754 cndesc
.cd_flags
= vnode_isdir(vp
) ? CD_ISDIR
: 0;
755 cndesc
.cd_encoding
= cp
->c_desc
.cd_encoding
;
756 cndesc
.cd_nameptr
= (const u_int8_t
*)cnp
->cn_nameptr
;
757 cndesc
.cd_namelen
= cnp
->cn_namelen
;
758 cndesc
.cd_parentcnid
= dcp
->c_fileid
;
759 cndesc
.cd_hint
= dcp
->c_childhint
;
761 lockflags
= SFL_CATALOG
| SFL_ATTRIBUTE
;
762 if (cndesc
.cd_flags
& CD_ISDIR
) {
763 /* We'll be removing the alias resource allocation blocks. */
764 lockflags
|= SFL_BITMAP
;
766 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
768 if ((error
= cat_lookuplink(hfsmp
, &cndesc
, &cndesc
.cd_cnid
, &prevlinkid
, &nextlinkid
))) {
772 /* Reserve some space in the catalog file. */
773 if (!skip_reserve
&& (error
= cat_preflight(hfsmp
, 2 * CAT_DELETE
, NULL
, 0))) {
777 /* Purge any cached origin entries for a directory or file hard link. */
778 hfs_relorigin(cp
, dcp
->c_fileid
);
779 if (dcp
->c_fileid
!= dcp
->c_cnid
) {
780 hfs_relorigin(cp
, dcp
->c_cnid
);
783 /* Delete the link record. */
784 if ((error
= cat_deletelink(hfsmp
, &cndesc
))) {
788 /* Update the parent directory. */
789 if (dcp
->c_entries
> 0) {
792 if (cndesc
.cd_flags
& CD_ISDIR
) {
793 DEC_FOLDERCOUNT(hfsmp
, dcp
->c_attr
);
795 dcp
->c_dirchangecnt
++;
796 hfs_incr_gencount(dcp
);
798 dcp
->c_touch_chgtime
= dcp
->c_touch_modtime
= true;
799 dcp
->c_flag
|= C_MODIFIED
;
800 hfs_update(dcp
->c_vp
, 0);
803 * If this is the last link then we need to process the inode.
804 * Otherwise we need to fix up the link chain.
807 if (cp
->c_linkcount
< 1) {
809 struct cat_desc to_desc
;
810 struct cat_desc from_desc
;
813 * If a file inode or directory inode is being deleted, rename
814 * it to an open deleted file. This ensures that deletion
815 * of inode and its corresponding extended attributes does
816 * not overflow the journal. This inode will be deleted
817 * either in hfs_vnop_inactive() or in hfs_remove_orphans().
818 * Note: a rename failure here is not fatal.
820 bzero(&from_desc
, sizeof(from_desc
));
821 bzero(&to_desc
, sizeof(to_desc
));
822 if (vnode_isdir(vp
)) {
823 if (cp
->c_entries
!= 0) {
824 panic("hfs_unlink: dir not empty (id %d, %d entries)", cp
->c_fileid
, cp
->c_entries
);
826 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
),
827 cp
->c_attr
.ca_linkref
);
828 from_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
829 from_desc
.cd_flags
= CD_ISDIR
;
830 to_desc
.cd_flags
= CD_ISDIR
;
832 MAKE_INODE_NAME(inodename
, sizeof(inodename
),
833 cp
->c_attr
.ca_linkref
);
834 from_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
835 from_desc
.cd_flags
= 0;
836 to_desc
.cd_flags
= 0;
838 from_desc
.cd_nameptr
= (const u_int8_t
*)inodename
;
839 from_desc
.cd_namelen
= strlen(inodename
);
840 from_desc
.cd_cnid
= cp
->c_fileid
;
842 MAKE_DELETED_NAME(delname
, sizeof(delname
), cp
->c_fileid
);
843 to_desc
.cd_nameptr
= (const u_int8_t
*)delname
;
844 to_desc
.cd_namelen
= strlen(delname
);
845 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
846 to_desc
.cd_cnid
= cp
->c_fileid
;
848 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_private_desc
[FILE_HARDLINKS
],
849 &to_desc
, (struct cat_desc
*)NULL
);
851 cp
->c_flag
|= C_DELETED
;
852 cp
->c_attr
.ca_recflags
&= ~kHFSHasLinkChainMask
;
853 cp
->c_attr
.ca_firstlink
= 0;
854 if (vnode_isdir(vp
)) {
855 hfsmp
->hfs_private_attr
[DIR_HARDLINKS
].ca_entries
--;
856 DEC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[DIR_HARDLINKS
]);
858 hfsmp
->hfs_private_attr
[FILE_HARDLINKS
].ca_entries
++;
859 INC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[FILE_HARDLINKS
]);
861 (void)cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[DIR_HARDLINKS
],
862 &hfsmp
->hfs_private_attr
[DIR_HARDLINKS
], NULL
, NULL
);
863 (void)cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[FILE_HARDLINKS
],
864 &hfsmp
->hfs_private_attr
[FILE_HARDLINKS
], NULL
, NULL
);
867 error
= 0; /* rename failure here is not fatal */
869 } else /* Still some links left */ {
873 * Update the start of the link chain.
874 * Note: Directory hard links store the first link in an attribute.
876 if (vnode_isdir(vp
) &&
877 getfirstlink(hfsmp
, cp
->c_fileid
, &firstlink
) == 0 &&
878 firstlink
== cndesc
.cd_cnid
) {
879 if (setfirstlink(hfsmp
, cp
->c_fileid
, nextlinkid
) == 0)
880 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
881 } else if (cp
->c_attr
.ca_firstlink
== cndesc
.cd_cnid
) {
882 cp
->c_attr
.ca_firstlink
= nextlinkid
;
884 /* Update previous link. */
886 (void) cat_update_siblinglinks(hfsmp
, prevlinkid
, HFS_IGNORABLE_LINK
, nextlinkid
);
888 /* Update next link. */
890 (void) cat_update_siblinglinks(hfsmp
, nextlinkid
, prevlinkid
, HFS_IGNORABLE_LINK
);
895 * The call to cat_releasedesc below will only release the name
896 * buffer; it does not zero out the rest of the fields in the
897 * 'cat_desc' data structure.
899 * As a result, since there are still other links at this point,
900 * we need to make the current cnode descriptor point to the raw
901 * inode. If a path-based system call comes along first, it will
902 * replace the descriptor with a valid link ID. If a userland
903 * process already has a file descriptor open, then they will
904 * bypass that lookup, though. Replacing the descriptor CNID with
905 * the raw inode will force it to generate a new full path.
907 cp
->c_cnid
= cp
->c_fileid
;
909 /* Push new link count to disk. */
910 cp
->c_ctime
= tv
.tv_sec
;
911 (void) cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, NULL
, NULL
);
913 /* All done with the system files. */
914 hfs_systemfile_unlock(hfsmp
, lockflags
);
917 /* Update file system stats. */
918 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
921 * All done with this cnode's descriptor...
923 * Note: all future catalog calls for this cnode may be
924 * by fileid only. This is OK for HFS (which doesn't have
925 * file thread records) since HFS doesn't support hard links.
927 cat_releasedesc(&cp
->c_desc
);
931 hfs_systemfile_unlock(hfsmp
, lockflags
);
934 hfs_end_transaction(hfsmp
);
937 dcp
->c_flag
&= ~C_DIR_MODIFICATION
;
938 wakeup((caddr_t
)&dcp
->c_flag
);
945 * Initialize the HFS+ private system directories.
947 * These directories are used to hold the inodes
948 * for file and directory hardlinks as well as
949 * open-unlinked files.
951 * If they don't yet exist they will get created.
953 * This call is assumed to be made during mount.
956 hfs_privatedir_init(struct hfsmount
* hfsmp
, enum privdirtype type
)
958 struct vnode
* dvp
= NULLVP
;
959 struct cnode
* dcp
= NULL
;
960 struct cat_desc
*priv_descp
;
961 struct cat_attr
*priv_attrp
;
962 struct FndrDirInfo
* fndrinfo
;
968 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
972 priv_descp
= &hfsmp
->hfs_private_desc
[type
];
973 priv_attrp
= &hfsmp
->hfs_private_attr
[type
];
975 /* Check if directory already exists. */
976 if (priv_descp
->cd_cnid
!= 0) {
980 priv_descp
->cd_parentcnid
= kRootDirID
;
981 priv_descp
->cd_nameptr
= (const u_int8_t
*)hfs_private_names
[type
];
982 priv_descp
->cd_namelen
= strlen((const char *)priv_descp
->cd_nameptr
);
983 priv_descp
->cd_flags
= CD_ISDIR
| CD_DECOMPOSED
;
985 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
986 error
= cat_lookup(hfsmp
, priv_descp
, 0, 0, NULL
, priv_attrp
, NULL
, NULL
);
987 hfs_systemfile_unlock(hfsmp
, lockflags
);
990 if (type
== FILE_HARDLINKS
) {
991 hfsmp
->hfs_metadata_createdate
= priv_attrp
->ca_itime
;
993 priv_descp
->cd_cnid
= priv_attrp
->ca_fileid
;
997 /* Directory is missing, if this is read-only then we're done. */
998 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
1002 /* Grab the root directory so we can update it later. */
1003 if (hfs_vget(hfsmp
, kRootDirID
, &dvp
, 0, 0) != 0) {
1008 /* Setup the default attributes */
1009 bzero(priv_attrp
, sizeof(struct cat_attr
));
1010 priv_attrp
->ca_flags
= UF_IMMUTABLE
| UF_HIDDEN
;
1011 priv_attrp
->ca_mode
= S_IFDIR
;
1012 if (type
== DIR_HARDLINKS
) {
1013 priv_attrp
->ca_mode
|= S_ISVTX
| S_IRUSR
| S_IXUSR
| S_IRGRP
|
1014 S_IXGRP
| S_IROTH
| S_IXOTH
;
1016 priv_attrp
->ca_linkcount
= 1;
1017 priv_attrp
->ca_itime
= hfsmp
->hfs_itime
;
1018 priv_attrp
->ca_recflags
= kHFSHasFolderCountMask
;
1020 fndrinfo
= (struct FndrDirInfo
*)&priv_attrp
->ca_finderinfo
;
1021 fndrinfo
->frLocation
.v
= SWAP_BE16(16384);
1022 fndrinfo
->frLocation
.h
= SWAP_BE16(16384);
1023 fndrinfo
->frFlags
= SWAP_BE16(kIsInvisible
+ kNameLocked
);
1025 if (hfs_start_transaction(hfsmp
) != 0) {
1030 /* Need the catalog and EA b-trees for CNID acquisition */
1031 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1033 /* Make sure there's space in the Catalog file. */
1034 if (cat_preflight(hfsmp
, CAT_CREATE
, NULL
, 0) != 0) {
1035 hfs_systemfile_unlock(hfsmp
, lockflags
);
1039 /* Get the CNID for use */
1041 if ((error
= cat_acquire_cnid(hfsmp
, &new_id
))) {
1042 hfs_systemfile_unlock (hfsmp
, lockflags
);
1046 /* Create the private directory on disk. */
1047 error
= cat_create(hfsmp
, new_id
, priv_descp
, priv_attrp
, NULL
);
1049 priv_descp
->cd_cnid
= priv_attrp
->ca_fileid
;
1051 /* Update the parent directory */
1053 INC_FOLDERCOUNT(hfsmp
, dcp
->c_attr
);
1054 dcp
->c_dirchangecnt
++;
1055 hfs_incr_gencount(dcp
);
1057 dcp
->c_ctime
= tv
.tv_sec
;
1058 dcp
->c_mtime
= tv
.tv_sec
;
1059 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1062 hfs_systemfile_unlock(hfsmp
, lockflags
);
1067 if (type
== FILE_HARDLINKS
) {
1068 hfsmp
->hfs_metadata_createdate
= priv_attrp
->ca_itime
;
1070 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
1073 hfs_end_transaction(hfsmp
);
1079 if ((error
== 0) && (type
== DIR_HARDLINKS
)) {
1080 hfs_xattr_init(hfsmp
);
1086 * Lookup a hardlink link (from chain)
1089 hfs_lookup_siblinglinks(struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*prevlinkid
, cnid_t
*nextlinkid
)
1097 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1099 error
= cat_lookup_siblinglinks(hfsmp
, linkfileid
, prevlinkid
, nextlinkid
);
1100 if (error
== ENOLINK
) {
1101 hfs_systemfile_unlock(hfsmp
, lockflags
);
1102 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1104 error
= getfirstlink(hfsmp
, linkfileid
, nextlinkid
);
1106 hfs_systemfile_unlock(hfsmp
, lockflags
);
1112 /* Find the oldest / last hardlink in the link chain */
1114 hfs_lookup_lastlink (struct hfsmount
*hfsmp
, cnid_t linkfileid
,
1115 cnid_t
*lastid
, struct cat_desc
*cdesc
) {
1121 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1123 error
= cat_lookup_lastlink(hfsmp
, linkfileid
, lastid
, cdesc
);
1125 hfs_systemfile_unlock(hfsmp
, lockflags
);
1128 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed
1136 * Cache the origin of a directory or file hard link
1138 * cnode must be lock on entry
1142 hfs_savelinkorigin(cnode_t
*cp
, cnid_t parentcnid
)
1144 linkorigin_t
*origin
= NULL
;
1145 thread_t thread
= current_thread();
1147 int maxorigins
= (S_ISDIR(cp
->c_mode
)) ? MAX_CACHED_ORIGINS
: MAX_CACHED_FILE_ORIGINS
;
1149 * Look for an existing origin first. If not found, create/steal one.
1151 TAILQ_FOREACH(origin
, &cp
->c_originlist
, lo_link
) {
1153 if (origin
->lo_thread
== thread
) {
1154 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
1158 if (origin
== NULL
) {
1159 /* Recycle the last (i.e., the oldest) if we have too many. */
1160 if (count
> maxorigins
) {
1161 origin
= TAILQ_LAST(&cp
->c_originlist
, hfs_originhead
);
1162 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
1164 MALLOC(origin
, linkorigin_t
*, sizeof(linkorigin_t
), M_TEMP
, M_WAITOK
);
1166 origin
->lo_thread
= thread
;
1168 origin
->lo_cnid
= cp
->c_cnid
;
1169 origin
->lo_parentcnid
= parentcnid
;
1170 TAILQ_INSERT_HEAD(&cp
->c_originlist
, origin
, lo_link
);
1174 * Release any cached origins for a directory or file hard link
1176 * cnode must be lock on entry
1180 hfs_relorigins(struct cnode
*cp
)
1182 linkorigin_t
*origin
, *prev
;
1184 TAILQ_FOREACH_SAFE(origin
, &cp
->c_originlist
, lo_link
, prev
) {
1185 FREE(origin
, M_TEMP
);
1187 TAILQ_INIT(&cp
->c_originlist
);
1191 * Release a specific origin for a directory or file hard link
1193 * cnode must be lock on entry
1197 hfs_relorigin(struct cnode
*cp
, cnid_t parentcnid
)
1199 linkorigin_t
*origin
, *prev
;
1200 thread_t thread
= current_thread();
1202 TAILQ_FOREACH_SAFE(origin
, &cp
->c_originlist
, lo_link
, prev
) {
1203 if (origin
->lo_thread
== thread
) {
1204 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
1205 FREE(origin
, M_TEMP
);
1207 } else if (origin
->lo_parentcnid
== parentcnid
) {
1209 * If the threads don't match, then we don't want to
1210 * delete the entry because that might cause other threads
1211 * to fall back and use whatever happens to be in
1212 * c_parentcnid or the wrong link ID. By setting the
1213 * values to zero here, it should serve as an indication
1214 * that the path is no longer valid and that's better than
1215 * using a random parent ID or link ID.
1217 origin
->lo_parentcnid
= 0;
1218 origin
->lo_cnid
= 0;
1224 * Test if a directory or file hard link has a cached origin
1226 * cnode must be lock on entry
1230 hfs_haslinkorigin(cnode_t
*cp
)
1232 if (cp
->c_flag
& C_HARDLINK
) {
1233 linkorigin_t
*origin
;
1234 thread_t thread
= current_thread();
1236 TAILQ_FOREACH(origin
, &cp
->c_originlist
, lo_link
) {
1237 if (origin
->lo_thread
== thread
) {
1238 return origin
->lo_cnid
!= 0;
1246 * Obtain the current parent cnid of a directory or file hard link
1248 * cnode must be lock on entry
1252 hfs_currentparent(cnode_t
*cp
, bool have_lock
)
1254 if (cp
->c_flag
& C_HARDLINK
) {
1256 hfs_lock_always(cp
, HFS_SHARED_LOCK
);
1258 linkorigin_t
*origin
;
1259 thread_t thread
= current_thread();
1261 TAILQ_FOREACH(origin
, &cp
->c_originlist
, lo_link
) {
1262 if (origin
->lo_thread
== thread
) {
1265 return (origin
->lo_parentcnid
);
1272 return (cp
->c_parentcnid
);
1276 * Obtain the current cnid of a directory or file hard link
1278 * cnode must be lock on entry
1282 hfs_currentcnid(cnode_t
*cp
)
1284 if (cp
->c_flag
& C_HARDLINK
) {
1285 linkorigin_t
*origin
;
1286 thread_t thread
= current_thread();
1288 TAILQ_FOREACH(origin
, &cp
->c_originlist
, lo_link
) {
1289 if (origin
->lo_thread
== thread
) {
1290 return (origin
->lo_cnid
);
1294 return (cp
->c_cnid
);
1299 * Set the first link attribute for a given file id.
1301 * The attributes b-tree must already be locked.
1302 * If journaling is enabled, a transaction must already be started.
1305 setfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t firstlink
)
1308 BTreeIterator
* iterator
;
1309 FSBufferDescriptor btdata
;
1310 u_int8_t attrdata
[FIRST_LINK_XATTR_REC_SIZE
];
1311 HFSPlusAttrData
*dataptr
;
1315 if (hfsmp
->hfs_attribute_cp
== NULL
) {
1318 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1319 bzero(iterator
, sizeof(*iterator
));
1321 result
= hfs_buildattrkey(fileid
, FIRST_LINK_XATTR_NAME
, (HFSPlusAttrKey
*)&iterator
->key
);
1325 dataptr
= (HFSPlusAttrData
*)&attrdata
[0];
1326 dataptr
->recordType
= kHFSPlusAttrInlineData
;
1327 dataptr
->reserved
[0] = 0;
1328 dataptr
->reserved
[1] = 0;
1331 * Since attrData is variable length, we calculate the size of
1332 * attrData by subtracting the size of all other members of
1333 * structure HFSPlusAttData from the size of attrdata.
1335 (void)snprintf((char *)&dataptr
->attrData
[0],
1336 sizeof(dataptr
) - (4 * sizeof(uint32_t)),
1337 "%lu", (unsigned long)firstlink
);
1338 dataptr
->attrSize
= 1 + strlen((char *)&dataptr
->attrData
[0]);
1340 /* Calculate size of record rounded up to multiple of 2 bytes. */
1341 datasize
= sizeof(HFSPlusAttrData
) - 2 + dataptr
->attrSize
+ ((dataptr
->attrSize
& 1) ? 1 : 0);
1343 btdata
.bufferAddress
= dataptr
;
1344 btdata
.itemSize
= datasize
;
1345 btdata
.itemCount
= 1;
1347 btfile
= hfsmp
->hfs_attribute_cp
->c_datafork
;
1349 /* Insert the attribute. */
1350 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
1351 if (result
== btExists
) {
1352 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
1354 (void) BTFlushPath(btfile
);
1356 FREE(iterator
, M_TEMP
);
1358 return MacToVFSError(result
);
1362 * Get the first link attribute for a given file id.
1364 * The attributes b-tree must already be locked.
1367 getfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t
*firstlink
)
1370 BTreeIterator
* iterator
;
1371 FSBufferDescriptor btdata
;
1372 u_int8_t attrdata
[FIRST_LINK_XATTR_REC_SIZE
];
1373 HFSPlusAttrData
*dataptr
;
1377 if (hfsmp
->hfs_attribute_cp
== NULL
) {
1380 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1381 bzero(iterator
, sizeof(*iterator
));
1383 result
= hfs_buildattrkey(fileid
, FIRST_LINK_XATTR_NAME
, (HFSPlusAttrKey
*)&iterator
->key
);
1387 dataptr
= (HFSPlusAttrData
*)&attrdata
[0];
1388 datasize
= sizeof(attrdata
);
1390 btdata
.bufferAddress
= dataptr
;
1391 btdata
.itemSize
= sizeof(attrdata
);
1392 btdata
.itemCount
= 1;
1394 btfile
= hfsmp
->hfs_attribute_cp
->c_datafork
;
1396 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
1400 if (dataptr
->attrSize
< 3) {
1404 *firstlink
= strtoul((char*)&dataptr
->attrData
[0], NULL
, 10);
1406 FREE(iterator
, M_TEMP
);
1408 return MacToVFSError(result
);
1411 errno_t
hfs_first_link(hfsmount_t
*hfsmp
, cnode_t
*cp
, cnid_t
*link_id
)
1415 if (S_ISDIR(cp
->c_mode
)) {
1416 int lockf
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
1418 error
= getfirstlink(hfsmp
, cp
->c_fileid
, link_id
);
1420 hfs_systemfile_unlock(hfsmp
, lockf
);
1422 if (cp
->c_attr
.ca_firstlink
)
1423 *link_id
= cp
->c_attr
.ca_firstlink
;
1425 // This can happen if the cnode has been deleted