1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
6 * Created by Or Haimovich on 17/05/2018.
9 #include "lf_hfs_link.h"
10 #include "lf_hfs_vfsutils.h"
11 #include "lf_hfs_vnops.h"
12 #include "lf_hfs_vfsops.h"
13 #include "lf_hfs_logger.h"
14 #include "lf_hfs_utils.h"
15 #include "lf_hfs_btrees_internal.h"
16 #include "lf_hfs_xattr.h"
17 #include "lf_hfs_endian.h"
18 #include "lf_hfs_format.h"
19 #include "lf_hfs_defs.h"
22 * Private directories where hardlink inodes reside.
24 const char *hfs_private_names
[] = {
25 HFSPLUSMETADATAFOLDER
, /* FILE HARDLINKS */
26 HFSPLUS_DIR_METADATA_FOLDER
/* DIRECTORY HARDLINKS */
29 static int getfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t
*firstlink
);
30 static int setfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t firstlink
);
32 * Set the first link attribute for a given file id.
34 * The attributes b-tree must already be locked.
35 * If journaling is enabled, a transaction must already be started.
38 setfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t firstlink
)
41 BTreeIterator
* iterator
;
42 FSBufferDescriptor btdata
;
43 u_int8_t attrdata
[FIRST_LINK_XATTR_REC_SIZE
];
44 HFSPlusAttrData
*dataptr
;
48 if (hfsmp
->hfs_attribute_cp
== NULL
) {
51 iterator
= hfs_mallocz(sizeof(*iterator
));
55 result
= hfs_buildattrkey(fileid
, FIRST_LINK_XATTR_NAME
, (HFSPlusAttrKey
*)&iterator
->key
);
59 dataptr
= (HFSPlusAttrData
*)&attrdata
[0];
60 dataptr
->recordType
= kHFSPlusAttrInlineData
;
61 dataptr
->reserved
[0] = 0;
62 dataptr
->reserved
[1] = 0;
65 * Since attrData is variable length, we calculate the size of
66 * attrData by subtracting the size of all other members of
67 * structure HFSPlusAttData from the size of attrdata.
69 (void)snprintf((char *)&dataptr
->attrData
[0], sizeof(dataptr
) - (4 * sizeof(uint32_t)), "%lu", (unsigned long)firstlink
);
71 dataptr
->attrSize
= (u_int32_t
)( 1 + strlen((char *)&dataptr
->attrData
[0]));
73 /* Calculate size of record rounded up to multiple of 2 bytes. */
74 datasize
= sizeof(HFSPlusAttrData
) - 2 + dataptr
->attrSize
+ ((dataptr
->attrSize
& 1) ? 1 : 0);
76 btdata
.bufferAddress
= dataptr
;
77 btdata
.itemSize
= datasize
;
80 btfile
= hfsmp
->hfs_attribute_cp
->c_datafork
;
82 /* Insert the attribute. */
83 result
= BTInsertRecord(btfile
, iterator
, &btdata
, datasize
);
84 if (result
== btExists
) {
85 result
= BTReplaceRecord(btfile
, iterator
, &btdata
, datasize
);
87 (void) BTFlushPath(btfile
);
91 return MacToVFSError(result
);
95 * Get the first link attribute for a given file id.
97 * The attributes b-tree must already be locked.
100 getfirstlink(struct hfsmount
* hfsmp
, cnid_t fileid
, cnid_t
*firstlink
)
103 BTreeIterator
* iterator
;
104 FSBufferDescriptor btdata
;
105 u_int8_t attrdata
[FIRST_LINK_XATTR_REC_SIZE
];
106 HFSPlusAttrData
*dataptr
;
109 if (hfsmp
->hfs_attribute_cp
== NULL
) {
112 iterator
= hfs_mallocz(sizeof(*iterator
));
113 if (iterator
== NULL
)
116 result
= hfs_buildattrkey(fileid
, FIRST_LINK_XATTR_NAME
, (HFSPlusAttrKey
*)&iterator
->key
);
120 dataptr
= (HFSPlusAttrData
*)&attrdata
[0];
122 btdata
.bufferAddress
= dataptr
;
123 btdata
.itemSize
= sizeof(attrdata
);
124 btdata
.itemCount
= 1;
126 btfile
= hfsmp
->hfs_attribute_cp
->c_datafork
;
128 result
= BTSearchRecord(btfile
, iterator
, &btdata
, NULL
, NULL
);
132 if (dataptr
->attrSize
< 3) {
136 *firstlink
= (cnid_t
) strtoul((char*)&dataptr
->attrData
[0], NULL
, 10);
140 return MacToVFSError(result
);
143 /* Find the oldest / last hardlink in the link chain */
145 hfs_lookup_lastlink (struct hfsmount
*hfsmp
, cnid_t linkfileid
, cnid_t
*lastid
, struct cat_desc
*cdesc
) {
151 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
153 error
= cat_lookup_lastlink(hfsmp
, linkfileid
, lastid
, cdesc
);
155 hfs_systemfile_unlock(hfsmp
, lockflags
);
158 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed
165 * Release a specific origin for a directory or file hard link
167 * cnode must be lock on entry
170 hfs_relorigin(struct cnode
*cp
, cnid_t parentcnid
)
172 linkorigin_t
*origin
, *prev
;
173 pthread_t thread
= pthread_self();
175 TAILQ_FOREACH_SAFE(origin
, &cp
->c_originlist
, lo_link
, prev
)
177 if (origin
->lo_thread
== thread
) {
178 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
181 } else if (origin
->lo_parentcnid
== parentcnid
) {
183 * If the threads don't match, then we don't want to
184 * delete the entry because that might cause other threads
185 * to fall back and use whatever happens to be in
186 * c_parentcnid or the wrong link ID. By setting the
187 * values to zero here, it should serve as an indication
188 * that the path is no longer valid and that's better than
189 * using a random parent ID or link ID.
191 origin
->lo_parentcnid
= 0;
198 * Remove a link to a hardlink file/dir.
200 * Note: dvp and vp cnodes are already locked.
203 hfs_unlink(struct hfsmount
*hfsmp
, struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
, int skip_reserve
)
207 struct cat_desc cndesc
;
218 dcp
->c_flag
|= C_DIR_MODIFICATION
;
220 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
227 * Protect against a race with rename by using the component
228 * name passed in and parent id from dvp (instead of using
229 * the cp->c_desc which may have changed).
231 * Re-lookup the component name so we get the correct cnid
232 * for the name (as opposed to the c_cnid in the cnode which
233 * could have changed before the cnode was locked).
235 cndesc
.cd_flags
= vnode_isdir(vp
) ? CD_ISDIR
: 0;
236 cndesc
.cd_encoding
= cp
->c_desc
.cd_encoding
;
237 cndesc
.cd_nameptr
= (const u_int8_t
*)cnp
->cn_nameptr
;
238 cndesc
.cd_namelen
= cnp
->cn_namelen
;
239 cndesc
.cd_parentcnid
= dcp
->c_fileid
;
240 cndesc
.cd_hint
= dcp
->c_childhint
;
242 lockflags
= SFL_CATALOG
| SFL_ATTRIBUTE
;
243 if (cndesc
.cd_flags
& CD_ISDIR
) {
244 /* We'll be removing the alias resource allocation blocks. */
245 lockflags
|= SFL_BITMAP
;
247 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
249 if ((error
= cat_lookuplink(hfsmp
, &cndesc
, &cndesc
.cd_cnid
, &prevlinkid
, &nextlinkid
))) {
253 /* Reserve some space in the catalog file. */
254 if (!skip_reserve
&& (error
= cat_preflight(hfsmp
, 2 * CAT_DELETE
, NULL
))) {
258 /* Purge any cached origin entries for a directory or file hard link. */
259 hfs_relorigin(cp
, dcp
->c_fileid
);
260 if (dcp
->c_fileid
!= dcp
->c_cnid
) {
261 hfs_relorigin(cp
, dcp
->c_cnid
);
264 /* Delete the link record. */
265 if ((error
= cat_deletelink(hfsmp
, &cndesc
))) {
269 /* Update the parent directory. */
270 if (dcp
->c_entries
> 0) {
273 if (cndesc
.cd_flags
& CD_ISDIR
) {
274 DEC_FOLDERCOUNT(hfsmp
, dcp
->c_attr
);
276 dcp
->c_dirchangecnt
++;
277 hfs_incr_gencount(dcp
);
281 dcp
->c_touch_chgtime
= dcp
->c_touch_modtime
= true;
282 dcp
->c_flag
|= C_MODIFIED
;
283 hfs_update(dcp
->c_vp
, 0);
286 * If this is the last link then we need to process the inode.
287 * Otherwise we need to fix up the link chain.
290 if (cp
->c_linkcount
< 1) {
292 struct cat_desc to_desc
;
293 struct cat_desc from_desc
;
296 * If a file inode or directory inode is being deleted, rename
297 * it to an open deleted file. This ensures that deletion
298 * of inode and its corresponding extended attributes does
299 * not overflow the journal. This inode will be deleted
300 * either in hfs_vnop_inactive() or in hfs_remove_orphans().
301 * Note: a rename failure here is not fatal.
303 bzero(&from_desc
, sizeof(from_desc
));
304 bzero(&to_desc
, sizeof(to_desc
));
305 if (vnode_isdir(vp
)) {
306 if (cp
->c_entries
!= 0) {
307 LFHFS_LOG(LEVEL_ERROR
, "hfs_unlink: dir not empty (id %d, %d entries)", cp
->c_fileid
, cp
->c_entries
);
310 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
),
311 cp
->c_attr
.ca_linkref
);
312 from_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
;
313 from_desc
.cd_flags
= CD_ISDIR
;
314 to_desc
.cd_flags
= CD_ISDIR
;
316 MAKE_INODE_NAME(inodename
, sizeof(inodename
),
317 cp
->c_attr
.ca_linkref
);
318 from_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
319 from_desc
.cd_flags
= 0;
320 to_desc
.cd_flags
= 0;
322 from_desc
.cd_nameptr
= (const u_int8_t
*)inodename
;
323 from_desc
.cd_namelen
= strlen(inodename
);
324 from_desc
.cd_cnid
= cp
->c_fileid
;
326 MAKE_DELETED_NAME(delname
, sizeof(delname
), cp
->c_fileid
);
327 to_desc
.cd_nameptr
= (const u_int8_t
*)delname
;
328 to_desc
.cd_namelen
= strlen(delname
);
329 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
;
330 to_desc
.cd_cnid
= cp
->c_fileid
;
332 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_private_desc
[FILE_HARDLINKS
],
333 &to_desc
, (struct cat_desc
*)NULL
);
335 cp
->c_flag
|= C_DELETED
;
336 cp
->c_attr
.ca_recflags
&= ~kHFSHasLinkChainMask
;
337 cp
->c_attr
.ca_firstlink
= 0;
338 if (vnode_isdir(vp
)) {
339 hfsmp
->hfs_private_attr
[DIR_HARDLINKS
].ca_entries
--;
340 DEC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[DIR_HARDLINKS
]);
342 hfsmp
->hfs_private_attr
[FILE_HARDLINKS
].ca_entries
++;
343 INC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[FILE_HARDLINKS
]);
345 (void)cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[DIR_HARDLINKS
],
346 &hfsmp
->hfs_private_attr
[DIR_HARDLINKS
], NULL
, NULL
);
347 (void)cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[FILE_HARDLINKS
],
348 &hfsmp
->hfs_private_attr
[FILE_HARDLINKS
], NULL
, NULL
);
351 error
= 0; /* rename failure here is not fatal */
353 } else /* Still some links left */ {
354 cnid_t firstlink
= 0;
357 * Update the start of the link chain.
358 * Note: Directory hard links store the first link in an attribute.
361 getfirstlink(hfsmp
, cp
->c_fileid
, &firstlink
) == 0 &&
362 firstlink
== cndesc
.cd_cnid
) {
363 if (setfirstlink(hfsmp
, cp
->c_fileid
, nextlinkid
) == 0)
364 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
365 } else if (cp
->c_attr
.ca_firstlink
== cndesc
.cd_cnid
) {
366 cp
->c_attr
.ca_firstlink
= nextlinkid
;
368 /* Update previous link. */
370 (void) cat_update_siblinglinks(hfsmp
, prevlinkid
, HFS_IGNORABLE_LINK
, nextlinkid
);
372 /* Update next link. */
374 (void) cat_update_siblinglinks(hfsmp
, nextlinkid
, prevlinkid
, HFS_IGNORABLE_LINK
);
379 * The call to cat_releasedesc below will only release the name
380 * buffer; it does not zero out the rest of the fields in the
381 * 'cat_desc' data structure.
383 * As a result, since there are still other links at this point,
384 * we need to make the current cnode descriptor point to the raw
385 * inode. If a path-based system call comes along first, it will
386 * replace the descriptor with a valid link ID. If a userland
387 * process already has a file descriptor open, then they will
388 * bypass that lookup, though. Replacing the descriptor CNID with
389 * the raw inode will force it to generate a new full path.
391 cp
->c_cnid
= cp
->c_fileid
;
393 /* Push new link count to disk. */
394 cp
->c_ctime
= tv
.tv_sec
;
395 (void) cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, NULL
, NULL
);
397 /* All done with the system files. */
398 hfs_systemfile_unlock(hfsmp
, lockflags
);
401 /* Update file system stats. */
402 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
405 * All done with this cnode's descriptor...
407 * Note: all future catalog calls for this cnode may be
408 * by fileid only. This is OK for HFS (which doesn't have
409 * file thread records) since HFS doesn't support hard links.
411 cat_releasedesc(&cp
->c_desc
);
415 hfs_systemfile_unlock(hfsmp
, lockflags
);
418 hfs_end_transaction(hfsmp
);
421 dcp
->c_flag
&= ~C_DIR_MODIFICATION
;
422 //TBD - We have wakeup here but can't see anyone who's msleeping on c_flag...
423 //wakeup((caddr_t)&dcp->c_flag);
429 * Cache the origin of a directory or file hard link
431 * cnode must be lock on entry
434 hfs_savelinkorigin(cnode_t
*cp
, cnid_t parentcnid
)
436 linkorigin_t
*origin
= NULL
, *next
= NULL
;
437 pthread_t thread
= pthread_self();
439 int maxorigins
= (S_ISDIR(cp
->c_mode
)) ? MAX_CACHED_ORIGINS
: MAX_CACHED_FILE_ORIGINS
;
441 * Look for an existing origin first. If not found, create/steal one.
443 TAILQ_FOREACH_SAFE(origin
, &cp
->c_originlist
, lo_link
, next
) {
445 if (origin
->lo_thread
== thread
) {
446 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
450 if (origin
== NULL
) {
451 /* Recycle the last (i.e., the oldest) if we have too many. */
452 if (count
> maxorigins
) {
453 origin
= TAILQ_LAST(&cp
->c_originlist
, hfs_originhead
);
454 TAILQ_REMOVE(&cp
->c_originlist
, origin
, lo_link
);
456 origin
= hfs_malloc(sizeof(linkorigin_t
));
458 origin
->lo_thread
= thread
;
460 origin
->lo_cnid
= cp
->c_cnid
;
461 origin
->lo_parentcnid
= parentcnid
;
462 TAILQ_INSERT_HEAD(&cp
->c_originlist
, origin
, lo_link
);
466 * Initialize the HFS+ private system directories.
468 * These directories are used to hold the inodes
469 * for file and directory hardlinks as well as
470 * open-unlinked files.
472 * If they don't yet exist they will get created.
474 * This call is assumed to be made during mount.
477 hfs_privatedir_init(struct hfsmount
* hfsmp
, enum privdirtype type
)
479 struct vnode
* dvp
= NULL
;
480 struct cnode
* dcp
= NULL
;
481 struct cat_desc
*priv_descp
;
482 struct cat_attr
*priv_attrp
;
488 priv_descp
= &hfsmp
->hfs_private_desc
[type
];
489 priv_attrp
= &hfsmp
->hfs_private_attr
[type
];
491 /* Check if directory already exists. */
492 if (priv_descp
->cd_cnid
!= 0) {
496 priv_descp
->cd_parentcnid
= kRootDirID
;
497 priv_descp
->cd_nameptr
= (const u_int8_t
*)hfs_private_names
[type
];
498 priv_descp
->cd_namelen
= strlen((const char *)priv_descp
->cd_nameptr
);
499 priv_descp
->cd_flags
= CD_ISDIR
| CD_DECOMPOSED
;
501 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
502 error
= cat_lookup(hfsmp
, priv_descp
, 0, NULL
, priv_attrp
, NULL
, NULL
);
504 hfs_systemfile_unlock(hfsmp
, lockflags
);
507 if (type
== FILE_HARDLINKS
) {
508 hfsmp
->hfs_metadata_createdate
= (uint32_t) priv_attrp
->ca_itime
;
510 priv_descp
->cd_cnid
= priv_attrp
->ca_fileid
;
514 /* Directory is missing, if this is read-only then we're done. */
515 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
519 /* Grab the root directory so we can update it later. */
520 if (hfs_vget(hfsmp
, kRootDirID
, &dvp
, 0, 0) != 0) {
526 /* Setup the default attributes */
527 bzero(priv_attrp
, sizeof(struct cat_attr
));
528 priv_attrp
->ca_flags
= UF_IMMUTABLE
| UF_HIDDEN
;
529 priv_attrp
->ca_mode
= S_IFDIR
;
530 if (type
== DIR_HARDLINKS
) {
531 priv_attrp
->ca_mode
|= S_ISVTX
| S_IRUSR
| S_IXUSR
| S_IRGRP
|
532 S_IXGRP
| S_IROTH
| S_IXOTH
;
534 priv_attrp
->ca_linkcount
= 1;
535 priv_attrp
->ca_itime
= hfsmp
->hfs_itime
;
536 priv_attrp
->ca_recflags
= kHFSHasFolderCountMask
;
538 //TBD - Probebly need to adjust for files app and not for finder....
539 struct FndrDirInfo
* fndrinfo
;
540 fndrinfo
= (struct FndrDirInfo
*)&priv_attrp
->ca_finderinfo
;
541 fndrinfo
->frLocation
.v
= SWAP_BE16(16384);
542 fndrinfo
->frLocation
.h
= SWAP_BE16(16384);
543 fndrinfo
->frFlags
= SWAP_BE16(kIsInvisible
+ kNameLocked
);
545 if (hfs_start_transaction(hfsmp
) != 0) {
550 /* Need the catalog and EA b-trees for CNID acquisition */
551 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
553 /* Make sure there's space in the Catalog file. */
554 if (cat_preflight(hfsmp
, CAT_CREATE
, NULL
) != 0) {
555 hfs_systemfile_unlock(hfsmp
, lockflags
);
559 /* Get the CNID for use */
561 if ((error
= cat_acquire_cnid(hfsmp
, &new_id
))) {
562 hfs_systemfile_unlock (hfsmp
, lockflags
);
566 /* Create the private directory on disk. */
567 error
= cat_create(hfsmp
, new_id
, priv_descp
, priv_attrp
, NULL
);
569 priv_descp
->cd_cnid
= priv_attrp
->ca_fileid
;
571 /* Update the parent directory */
573 INC_FOLDERCOUNT(hfsmp
, dcp
->c_attr
);
574 dcp
->c_dirchangecnt
++;
575 hfs_incr_gencount(dcp
);
577 dcp
->c_ctime
= tv
.tv_sec
;
578 dcp
->c_mtime
= tv
.tv_sec
;
579 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
582 hfs_systemfile_unlock(hfsmp
, lockflags
);
587 if (type
== FILE_HARDLINKS
) {
588 hfsmp
->hfs_metadata_createdate
= (uint32_t) priv_attrp
->ca_itime
;
590 hfs_volupdate(hfsmp
, VOL_MKDIR
, 1);
593 hfs_end_transaction(hfsmp
);
597 hfs_vnop_reclaim(dvp
);
600 //Curently disable -need to understand how much we need this...
601 // if ((error == 0) && (type == DIR_HARDLINKS)) {
602 // hfs_xattr_init(hfsmp);
607 * Release any cached origins for a directory or file hard link
609 * cnode must be lock on entry
612 hfs_relorigins(struct cnode
*cp
)
614 linkorigin_t
*origin
, *prev
;
616 TAILQ_FOREACH_SAFE(origin
, &cp
->c_originlist
, lo_link
, prev
) {
619 TAILQ_INIT(&cp
->c_originlist
);
623 * Obtain the current parent cnid of a directory or file hard link
625 * cnode must be lock on entry
628 hfs_currentparent(cnode_t
*cp
, bool have_lock
)
630 if (cp
->c_flag
& C_HARDLINK
) {
632 hfs_lock(cp
, HFS_SHARED_LOCK
, HFS_LOCK_ALWAYS
);
634 linkorigin_t
*origin
;
635 pthread_t thread
= pthread_self();
637 TAILQ_FOREACH(origin
, &cp
->c_originlist
, lo_link
) {
638 if (origin
->lo_thread
== thread
) {
641 return (origin
->lo_parentcnid
);
648 return (cp
->c_parentcnid
);
652 * Create a new catalog link record
654 * An indirect link is a reference to an inode (the real
655 * file or directory record).
657 * All the indirect links for a given inode are chained
658 * together in a doubly linked list.
660 * Pre-Leopard file hard links do not have kHFSHasLinkChainBit
661 * set and do not have first/prev/next link IDs i.e. the values
662 * are zero. If a new link is being added to an existing
663 * pre-Leopard file hard link chain, do not set kHFSHasLinkChainBit.
666 createindirectlink(struct hfsmount
*hfsmp
, u_int32_t linknum
, struct cat_desc
*descp
,
667 cnid_t nextcnid
, cnid_t
*linkcnid
, int is_inode_linkchain_set
)
669 struct FndrFileInfo
*fip
;
670 struct cat_attr attr
;
673 LFHFS_LOG(LEVEL_ERROR
, "createindirectlink: linknum is zero!\n");
677 /* Setup the default attributes */
678 bzero(&attr
, sizeof(attr
));
680 /* Links are matched to inodes by link ID and to volumes by create date */
681 attr
.ca_linkref
= linknum
;
682 attr
.ca_itime
= hfsmp
->hfs_metadata_createdate
;
683 attr
.ca_mode
= S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
;
684 attr
.ca_recflags
= kHFSHasLinkChainMask
| kHFSThreadExistsMask
;
685 attr
.ca_flags
= UF_IMMUTABLE
;
686 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
688 if (descp
->cd_flags
& CD_ISDIR
) {
689 fip
->fdType
= SWAP_BE32 (kHFSAliasType
);
690 fip
->fdCreator
= SWAP_BE32 (kHFSAliasCreator
);
691 fip
->fdFlags
= SWAP_BE16 (kIsAlias
);
693 fip
->fdType
= SWAP_BE32 (kHardLinkFileType
);
694 fip
->fdCreator
= SWAP_BE32 (kHFSPlusCreator
);
695 fip
->fdFlags
= SWAP_BE16 (kHasBeenInited
);
696 /* If the file inode does not have kHFSHasLinkChainBit set
697 * and the next link chain ID is zero, assume that this
698 * is pre-Leopard file inode. Therefore clear the bit.
700 if ((is_inode_linkchain_set
== 0) && (nextcnid
== 0)) {
701 attr
.ca_recflags
&= ~kHFSHasLinkChainMask
;
704 /* Create the indirect link directly in the catalog */
705 return cat_createlink(hfsmp
, descp
, &attr
, nextcnid
, linkcnid
);
710 * Make a link to the cnode cp in the directory dp
711 * using the name in cnp. src_vp is the vnode that
712 * corresponds to 'cp' which was part of the arguments to
715 * The cnodes cp and dcp must be locked.
718 hfs_makelink(struct hfsmount
*hfsmp
, struct vnode
*src_vp
, struct cnode
*cp
,struct cnode
*dcp
, struct componentname
*cnp
)
720 u_int32_t indnodeno
= 0;
722 struct cat_desc to_desc
;
723 struct cat_desc link_desc
;
727 cnid_t orig_firstlink
= 0;
728 enum privdirtype type
= S_ISDIR(cp
->c_mode
) ? DIR_HARDLINKS
: FILE_HARDLINKS
;
730 if (hfsmp
->cur_link_id
== 0) {
731 hfsmp
->cur_link_id
= ((random() & 0x3fffffff) + 100);
734 /* We don't allow link nodes in our private system directories. */
735 if (dcp
->c_fileid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
736 dcp
->c_fileid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
741 bzero(&cookie
, sizeof(cat_cookie_t
));
742 /* Reserve some space in the Catalog file. */
743 if ((retval
= cat_preflight(hfsmp
, (2 * CAT_CREATE
)+ CAT_RENAME
, &cookie
))) {
747 int lockflags
= SFL_CATALOG
| SFL_ATTRIBUTE
;
748 /* Directory hard links allocate space for a symlink. */
749 if (type
== DIR_HARDLINKS
) {
750 lockflags
|= SFL_BITMAP
;
752 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
754 /* Save the current cnid value so we restore it if an error occurs. */
755 cnid_t orig_cnid
= cp
->c_desc
.cd_cnid
;
758 * If this is a new hardlink then we need to create the inode
759 * and replace the original file/dir object with a link node.
761 if ((cp
->c_linkcount
== 2) && !(cp
->c_flag
& C_HARDLINK
)) {
763 bzero(&to_desc
, sizeof(to_desc
));
764 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_desc
[type
].cd_cnid
;
765 to_desc
.cd_cnid
= cp
->c_fileid
;
766 to_desc
.cd_flags
= (type
== DIR_HARDLINKS
) ? CD_ISDIR
: 0;
769 if (type
== DIR_HARDLINKS
) {
770 /* Directory hardlinks always use the cnid. */
771 indnodeno
= cp
->c_fileid
;
772 MAKE_DIRINODE_NAME(inodename
, sizeof(inodename
),
775 /* Get a unique indirect node number */
777 indnodeno
= cp
->c_fileid
;
779 indnodeno
= hfsmp
->cur_link_id
++;
781 MAKE_INODE_NAME(inodename
, sizeof(inodename
),
784 /* Move original file/dir to data node directory */
785 to_desc
.cd_nameptr
= (const u_int8_t
*)inodename
;
786 to_desc
.cd_namelen
= strlen(inodename
);
788 retval
= cat_rename(hfsmp
, &cp
->c_desc
, &hfsmp
->hfs_private_desc
[type
],
791 if (retval
!= 0 && retval
!= EEXIST
) {
792 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: cat_rename to %s failed (%d) fileid=%d, vol=%s\n",
793 inodename
, retval
, cp
->c_fileid
, hfsmp
->vcbVN
);
795 } while ((retval
== EEXIST
) && (type
== FILE_HARDLINKS
));
800 * Replace original file/dir with a link record.
803 bzero(&link_desc
, sizeof(link_desc
));
804 link_desc
.cd_nameptr
= cp
->c_desc
.cd_nameptr
;
805 link_desc
.cd_namelen
= cp
->c_desc
.cd_namelen
;
806 link_desc
.cd_parentcnid
= cp
->c_parentcnid
;
807 link_desc
.cd_flags
= S_ISDIR(cp
->c_mode
) ? CD_ISDIR
: 0;
809 retval
= createindirectlink(hfsmp
, indnodeno
, &link_desc
, 0, &linkcnid
, true);
814 /* Restore the cnode's cnid. */
815 cp
->c_desc
.cd_cnid
= orig_cnid
;
817 /* Put the original file back. */
818 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
820 if (err
!= EIO
&& err
!= ENXIO
)
821 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: error %d from cat_rename backout 1", err
);
822 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
824 if (retval
!= EIO
&& retval
!= ENXIO
) {
825 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: createindirectlink (1) failed: %d\n", retval
);
830 cp
->c_attr
.ca_linkref
= indnodeno
;
831 cp
->c_desc
.cd_cnid
= linkcnid
;
832 /* Directory hard links store the first link in an attribute. */
833 if (type
== DIR_HARDLINKS
) {
834 if (setfirstlink(hfsmp
, cp
->c_fileid
, linkcnid
) == 0)
835 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
836 } else /* FILE_HARDLINKS */ {
837 cp
->c_attr
.ca_firstlink
= linkcnid
;
839 cp
->c_attr
.ca_recflags
|= kHFSHasLinkChainMask
;
841 indnodeno
= cp
->c_attr
.ca_linkref
;
845 * Create a catalog entry for the new link (parentID + name).
848 bzero(&link_desc
, sizeof(link_desc
));
849 link_desc
.cd_nameptr
= (const u_int8_t
*)cnp
->cn_nameptr
;
850 link_desc
.cd_namelen
= strlen(cnp
->cn_nameptr
);
851 link_desc
.cd_parentcnid
= dcp
->c_fileid
;
852 link_desc
.cd_flags
= S_ISDIR(cp
->c_mode
) ? CD_ISDIR
: 0;
854 /* Directory hard links store the first link in an attribute. */
855 if (type
== DIR_HARDLINKS
) {
856 retval
= getfirstlink(hfsmp
, cp
->c_fileid
, &orig_firstlink
);
857 } else /* FILE_HARDLINKS */ {
858 orig_firstlink
= cp
->c_attr
.ca_firstlink
;
861 retval
= createindirectlink(hfsmp
, indnodeno
, &link_desc
, orig_firstlink
, &linkcnid
, (cp
->c_attr
.ca_recflags
& kHFSHasLinkChainMask
));
863 if (retval
&& newlink
) {
866 /* Get rid of new link */
867 (void) cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
869 /* Restore the cnode's cnid. */
870 cp
->c_desc
.cd_cnid
= orig_cnid
;
872 /* Put the original file back. */
873 err
= cat_rename(hfsmp
, &to_desc
, &dcp
->c_desc
, &cp
->c_desc
, NULL
);
875 if (err
!= EIO
&& err
!= ENXIO
)
876 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: error %d from cat_rename backout 2", err
);
877 hfs_mark_inconsistent(hfsmp
, HFS_ROLLBACK_FAILED
);
880 cp
->c_attr
.ca_linkref
= 0;
882 if (retval
!= EIO
&& retval
!= ENXIO
) {
883 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: createindirectlink (2) failed: %d\n", retval
);
887 } else if (retval
== 0) {
889 /* Update the original first link to point back to the new first link. */
890 if (cp
->c_attr
.ca_recflags
& kHFSHasLinkChainMask
) {
891 (void) cat_update_siblinglinks(hfsmp
, orig_firstlink
, linkcnid
, HFS_IGNORABLE_LINK
);
893 /* Update the inode's first link value. */
894 if (type
== DIR_HARDLINKS
) {
895 if (setfirstlink(hfsmp
, cp
->c_fileid
, linkcnid
) == 0)
896 cp
->c_attr
.ca_recflags
|= kHFSHasAttributesMask
;
898 cp
->c_attr
.ca_firstlink
= linkcnid
;
902 * Finally, if this is a new hardlink then:
903 * - update the private system directory
904 * - mark the cnode as a hard link
908 hfsmp
->hfs_private_attr
[type
].ca_entries
++;
909 /* From application perspective, directory hard link is a
910 * normal directory. Therefore count the new directory
911 * hard link for folder count calculation.
913 if (type
== DIR_HARDLINKS
) {
914 INC_FOLDERCOUNT(hfsmp
, hfsmp
->hfs_private_attr
[type
]);
916 retval
= cat_update(hfsmp
, &hfsmp
->hfs_private_desc
[type
], &hfsmp
->hfs_private_attr
[type
], NULL
, NULL
);
918 if (retval
!= EIO
&& retval
!= ENXIO
) {
919 LFHFS_LOG(LEVEL_ERROR
, "hfs_makelink: cat_update of privdir failed! (%d)\n", retval
);
922 hfs_mark_inconsistent(hfsmp
, HFS_OP_INCOMPLETE
);
924 cp
->c_flag
|= C_HARDLINK
;
927 if ((vp
= cp
->c_vp
) != NULL
) {
929 cp
->c_flag
|= C_NEED_DVNODE_PUT
;
932 if ((vp
= cp
->c_rsrc_vp
) != NULL
) {
934 cp
->c_flag
|= C_NEED_RVNODE_PUT
;
937 cp
->c_flag
|= C_MODIFIED
;
938 cp
->c_touch_chgtime
= TRUE
;
942 hfs_systemfile_unlock(hfsmp
, lockflags
);
944 cat_postflight(hfsmp
, &cookie
);
946 if (retval
== 0 && newlink
) {
947 hfs_volupdate(hfsmp
, VOL_MKFILE
, 0);