]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2015 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
46 static int cur_link_id = 0;
47
48 /*
49 * Private directories where hardlink inodes reside.
50 */
51 const 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 */
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);
64
65 int 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 */
81 static int
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)
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 */
133 int
134 hfs_makelink(struct hfsmount *hfsmp, struct vnode *src_vp, struct cnode *cp,
135 struct cnode *dcp, struct componentname *cnp)
136 {
137 vfs_context_t ctx = cnp->cn_context;
138 struct proc *p = vfs_context_proc(ctx);
139 u_int32_t indnodeno = 0;
140 char inodename[32];
141 struct cat_desc to_desc;
142 struct cat_desc link_desc;
143 int newlink = 0;
144 int lockflags;
145 int retval = 0;
146 cat_cookie_t cookie;
147 cnid_t orig_cnid;
148 cnid_t linkcnid;
149 cnid_t orig_firstlink;
150 enum privdirtype type;
151
152 type = S_ISDIR(cp->c_mode) ? DIR_HARDLINKS : FILE_HARDLINKS;
153
154 if (cur_link_id == 0) {
155 cur_link_id = ((random() & 0x3fffffff) + 100);
156 }
157
158 /* We don't allow link nodes in our private system directories. */
159 if (dcp->c_fileid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
160 dcp->c_fileid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
161 return (EPERM);
162 }
163
164 bzero(&cookie, sizeof(cat_cookie_t));
165 /* Reserve some space in the Catalog file. */
166 if ((retval = cat_preflight(hfsmp, (2 * CAT_CREATE)+ CAT_RENAME, &cookie, p))) {
167 return (retval);
168 }
169
170 lockflags = SFL_CATALOG | SFL_ATTRIBUTE;
171 /* Directory hard links allocate space for a symlink. */
172 if (type == DIR_HARDLINKS) {
173 lockflags |= SFL_BITMAP;
174 }
175 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
176
177 /* Save the current cnid value so we restore it if an error occurs. */
178 orig_cnid = cp->c_desc.cd_cnid;
179
180 /*
181 * If this is a new hardlink then we need to create the inode
182 * and replace the original file/dir object with a link node.
183 */
184 if ((cp->c_linkcount == 2) && !(cp->c_flag & C_HARDLINK)) {
185 newlink = 1;
186 bzero(&to_desc, sizeof(to_desc));
187 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[type].cd_cnid;
188 to_desc.cd_cnid = cp->c_fileid;
189 to_desc.cd_flags = (type == DIR_HARDLINKS) ? CD_ISDIR : 0;
190
191 do {
192 if (type == DIR_HARDLINKS) {
193 /* Directory hardlinks always use the cnid. */
194 indnodeno = cp->c_fileid;
195 MAKE_DIRINODE_NAME(inodename, sizeof(inodename),
196 indnodeno);
197 } else {
198 /* Get a unique indirect node number */
199 if (retval == 0) {
200 indnodeno = cp->c_fileid;
201 } else {
202 indnodeno = cur_link_id++;
203 }
204 MAKE_INODE_NAME(inodename, sizeof(inodename),
205 indnodeno);
206 }
207 /* Move original file/dir to data node directory */
208 to_desc.cd_nameptr = (const u_int8_t *)inodename;
209 to_desc.cd_namelen = strlen(inodename);
210
211 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_private_desc[type],
212 &to_desc, NULL);
213
214 if (retval != 0 && retval != EEXIST) {
215 printf("hfs_makelink: cat_rename to %s failed (%d) fileid=%d, vol=%s\n",
216 inodename, retval, cp->c_fileid, hfsmp->vcbVN);
217 }
218 } while ((retval == EEXIST) && (type == FILE_HARDLINKS));
219 if (retval)
220 goto out;
221
222 /*
223 * Replace original file/dir with a link record.
224 */
225
226 bzero(&link_desc, sizeof(link_desc));
227 link_desc.cd_nameptr = cp->c_desc.cd_nameptr;
228 link_desc.cd_namelen = cp->c_desc.cd_namelen;
229 link_desc.cd_parentcnid = cp->c_parentcnid;
230 link_desc.cd_flags = S_ISDIR(cp->c_mode) ? CD_ISDIR : 0;
231
232 retval = createindirectlink(hfsmp, indnodeno, &link_desc, 0, &linkcnid, true);
233 if (retval) {
234 int err;
235
236 /* Restore the cnode's cnid. */
237 cp->c_desc.cd_cnid = orig_cnid;
238
239 /* Put the original file back. */
240 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
241 if (err) {
242 if (err != EIO && err != ENXIO)
243 printf("hfs_makelink: error %d from cat_rename backout 1", err);
244 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
245 }
246 if (retval != EIO && retval != ENXIO) {
247 printf("hfs_makelink: createindirectlink (1) failed: %d\n", retval);
248 retval = EIO;
249 }
250 goto out;
251 }
252 cp->c_attr.ca_linkref = indnodeno;
253 cp->c_desc.cd_cnid = linkcnid;
254 /* Directory hard links store the first link in an attribute. */
255 if (type == DIR_HARDLINKS) {
256 if (setfirstlink(hfsmp, cp->c_fileid, linkcnid) == 0)
257 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
258 } else /* FILE_HARDLINKS */ {
259 cp->c_attr.ca_firstlink = linkcnid;
260 }
261 cp->c_attr.ca_recflags |= kHFSHasLinkChainMask;
262 } else {
263 indnodeno = cp->c_attr.ca_linkref;
264 }
265
266 /*
267 * Create a catalog entry for the new link (parentID + name).
268 */
269
270 bzero(&link_desc, sizeof(link_desc));
271 link_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
272 link_desc.cd_namelen = strlen(cnp->cn_nameptr);
273 link_desc.cd_parentcnid = dcp->c_fileid;
274 link_desc.cd_flags = S_ISDIR(cp->c_mode) ? CD_ISDIR : 0;
275
276 /* Directory hard links store the first link in an attribute. */
277 if (type == DIR_HARDLINKS) {
278 retval = getfirstlink(hfsmp, cp->c_fileid, &orig_firstlink);
279 } else /* FILE_HARDLINKS */ {
280 orig_firstlink = cp->c_attr.ca_firstlink;
281 }
282 if (retval == 0)
283 retval = createindirectlink(hfsmp, indnodeno, &link_desc,
284 orig_firstlink, &linkcnid,
285 (cp->c_attr.ca_recflags & kHFSHasLinkChainMask));
286 if (retval && newlink) {
287 int err;
288
289 /* Get rid of new link */
290 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
291
292 /* Restore the cnode's cnid. */
293 cp->c_desc.cd_cnid = orig_cnid;
294
295 /* Put the original file back. */
296 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
297 if (err) {
298 if (err != EIO && err != ENXIO)
299 printf("hfs_makelink: error %d from cat_rename backout 2", err);
300 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
301 }
302
303 cp->c_attr.ca_linkref = 0;
304
305 if (retval != EIO && retval != ENXIO) {
306 printf("hfs_makelink: createindirectlink (2) failed: %d\n", retval);
307 retval = EIO;
308 }
309 goto out;
310 } else if (retval == 0) {
311
312 /* Update the original first link to point back to the new first link. */
313 if (cp->c_attr.ca_recflags & kHFSHasLinkChainMask) {
314 (void) cat_update_siblinglinks(hfsmp, orig_firstlink, linkcnid, HFS_IGNORABLE_LINK);
315
316 /* Update the inode's first link value. */
317 if (type == DIR_HARDLINKS) {
318 if (setfirstlink(hfsmp, cp->c_fileid, linkcnid) == 0)
319 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
320 } else {
321 cp->c_attr.ca_firstlink = linkcnid;
322 }
323 }
324 /*
325 * Finally, if this is a new hardlink then:
326 * - update the private system directory
327 * - mark the cnode as a hard link
328 */
329 if (newlink) {
330 vnode_t vp;
331
332 hfsmp->hfs_private_attr[type].ca_entries++;
333 /* From application perspective, directory hard link is a
334 * normal directory. Therefore count the new directory
335 * hard link for folder count calculation.
336 */
337 if (type == DIR_HARDLINKS) {
338 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[type]);
339 }
340 retval = cat_update(hfsmp, &hfsmp->hfs_private_desc[type],
341 &hfsmp->hfs_private_attr[type], NULL, NULL);
342 if (retval) {
343 if (retval != EIO && retval != ENXIO) {
344 printf("hfs_makelink: cat_update of privdir failed! (%d)\n", retval);
345 retval = EIO;
346 }
347 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
348 }
349 cp->c_flag |= C_HARDLINK;
350
351 /*
352 * Now we need to mark the vnodes as being hardlinks via the vnode_setmultipath call.
353 * Note that we're calling vnode_get here, which should simply add an iocount if possible, without
354 * doing much checking. It's safe to call this because we are protected by the cnode lock, which
355 * ensures that anyone trying to reclaim it will block until we release it. vnode_get will usually
356 * give us an extra iocount, unless the vnode is about to be reclaimed (and has no iocounts).
357 * In that case, we'd error out, but we'd also not care if we added the VISHARDLINK bit to the vnode.
358 *
359 * As for the iocount we're about to add, we can't necessarily always call vnode_put here.
360 * If the one we add is the only iocount on the vnode, and there was
361 * sufficient vnode pressure, it could go through VNOP_INACTIVE immediately, which would
362 * require the cnode lock and cause us to double-lock panic. We can only call vnode_put if we know
363 * that the vnode we're operating on is the one with which we came into hfs_vnop_link, because
364 * that means VFS took an iocount on it for us. If it's *not* the one that we came into the call
365 * with, then mark it as NEED_VNODE_PUT to have hfs_unlock drop it for us. hfs_vnop_link will
366 * unlock the cnode when it is finished.
367 */
368 if ((vp = cp->c_vp) != NULLVP) {
369 if (vnode_get(vp) == 0) {
370 vnode_setmultipath(vp);
371 if (vp == src_vp) {
372 /* we have an iocount on data fork vnode already. */
373 vnode_put(vp);
374 }
375 else {
376 cp->c_flag |= C_NEED_DVNODE_PUT;
377 }
378 }
379 }
380 if ((vp = cp->c_rsrc_vp) != NULLVP) {
381 if (vnode_get(vp) == 0) {
382 vnode_setmultipath(vp);
383 if (vp == src_vp) {
384 vnode_put(vp);
385 }
386 else {
387 cp->c_flag |= C_NEED_RVNODE_PUT;
388 }
389 }
390 }
391 cp->c_flag |= C_MODIFIED;
392 cp->c_touch_chgtime = TRUE;
393 }
394 }
395 out:
396 hfs_systemfile_unlock(hfsmp, lockflags);
397
398 cat_postflight(hfsmp, &cookie, p);
399
400 if (retval == 0 && newlink) {
401 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
402 }
403 return (retval);
404 }
405
406
407 /*
408 * link vnode operation
409 *
410 * IN vnode_t a_vp;
411 * IN vnode_t a_tdvp;
412 * IN struct componentname *a_cnp;
413 * IN vfs_context_t a_context;
414 */
415 int
416 hfs_vnop_link(struct vnop_link_args *ap)
417 {
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;
423 struct cnode *cp;
424 struct cnode *tdcp;
425 struct cnode *fdcp = NULL;
426 struct cat_desc todesc;
427 cnid_t parentcnid;
428 int lockflags = 0;
429 int intrans = 0;
430 enum vtype v_type;
431 int error, ret;
432
433 hfsmp = VTOHFS(vp);
434 v_type = vnode_vtype(vp);
435
436 /* No hard links in HFS standard file systems. */
437 if (hfsmp->hfs_flags & HFS_STANDARD) {
438 return (ENOTSUP);
439 }
440 /* Linking to a special file is not permitted. */
441 if (v_type == VBLK || v_type == VCHR) {
442 return (EPERM);
443 }
444
445 /*
446 * For now, return ENOTSUP for a symlink target. This can happen
447 * for linkat(2) when called without AT_SYMLINK_FOLLOW.
448 */
449 if (v_type == VLNK)
450 return (ENOTSUP);
451
452 cp = VTOC(vp);
453
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) {
458 return (EPERM);
459 }
460 /*
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.
464 */
465 if (hfsmp->jnl == NULL) {
466 return (EPERM);
467 }
468
469 /* Directory hardlinks also need the parent of the original directory. */
470 if ((error = hfs_vget(hfsmp, hfs_currentparent(cp, /* have_lock: */ false),
471 &fdvp, 1, 0))) {
472 return (error);
473 }
474 #else
475 /* some platforms don't support directory hardlinks. */
476 return EPERM;
477 #endif
478 } else {
479 /* Make sure our private directory exists. */
480 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
481 return (ENOTSUP);
482 }
483 }
484 if (hfs_freeblks(hfsmp, 0) == 0) {
485 if (fdvp) {
486 vnode_put(fdvp);
487 }
488 return (ENOSPC);
489 }
490
491 check_for_tracked_file(vp, VTOC(vp)->c_ctime, NAMESPACE_HANDLER_LINK_CREATE, NULL);
492
493
494 /* Lock the cnodes. */
495 if (fdvp) {
496 if ((error = hfs_lockfour(VTOC(tdvp), VTOC(vp), VTOC(fdvp), NULL, HFS_EXCLUSIVE_LOCK, NULL))) {
497 if (fdvp) {
498 vnode_put(fdvp);
499 }
500 return (error);
501 }
502 fdcp = VTOC(fdvp);
503 } else {
504 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
505 return (error);
506 }
507 }
508 tdcp = VTOC(tdvp);
509 /* grab the parent CNID from originlist after grabbing cnode locks */
510 parentcnid = hfs_currentparent(cp, /* have_lock: */ true);
511
512 /*
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.
516 */
517 if (fdcp) {
518 if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
519 error = ENOENT;
520 goto out;
521 }
522 }
523
524 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
525 error = ENOENT;
526 goto out;
527 }
528
529 /* Check the source for errors:
530 * too many links, immutable, race with unlink
531 */
532 if (cp->c_linkcount >= HFS_LINK_MAX) {
533 error = EMLINK;
534 goto out;
535 }
536 if (cp->c_bsdflags & (IMMUTABLE | APPEND)) {
537 error = EPERM;
538 goto out;
539 }
540 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
541 error = ENOENT;
542 goto out;
543 }
544
545 tdcp->c_flag |= C_DIR_MODIFICATION;
546
547 if (hfs_start_transaction(hfsmp) != 0) {
548 error = EINVAL;
549 goto out;
550 }
551 intrans = 1;
552
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;
558 todesc.cd_hint = 0;
559 todesc.cd_cnid = 0;
560
561 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
562
563 /* If destination exists then we lost a race with create. */
564 if (cat_lookup(hfsmp, &todesc, 0, 0, NULL, NULL, NULL, NULL) == 0) {
565 error = EEXIST;
566 goto out;
567 }
568 if (cp->c_flag & C_HARDLINK) {
569 struct cat_attr cattr;
570
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)) {
574 error = ENOENT;
575 goto out;
576 }
577 } else {
578 cnid_t fileid;
579
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)) {
583 error = ENOENT;
584 goto out;
585 }
586 }
587 /*
588 * All directory links must reside in an non-ARCHIVED hierarchy.
589 */
590 if (v_type == VDIR) {
591 /*
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.
599 */
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 */
605 goto out;
606 }
607 }
608 hfs_systemfile_unlock(hfsmp, lockflags);
609 lockflags = 0;
610
611 cp->c_linkcount++;
612 cp->c_flag |= C_MODIFIED;
613 cp->c_touch_chgtime = TRUE;
614 error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
615 if (error) {
616 cp->c_linkcount--;
617 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
618 } else {
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;
623 }
624
625 /* Update the target directory and volume stats */
626 tdcp->c_entries++;
627 if (v_type == VDIR) {
628 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
629 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
630
631 /* Set kHFSHasChildLinkBit in the destination hierarchy */
632 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
633 if (error) {
634 printf ("hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN);
635 error = 0;
636 }
637 }
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;
643
644 error = hfs_update(tdvp, 0);
645 if (error) {
646 if (error != EIO && error != ENXIO) {
647 printf("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
648 error = EIO;
649 }
650 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
651 }
652
653 if ((v_type == VDIR) &&
654 (fdcp != NULL) &&
655 ((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) {
656
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);
661 if (error) {
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
665 }
666 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
667 }
668
669 /* Set kHFSHasChildLinkBit in the source hierarchy */
670 error = cat_set_childlinkbit(hfsmp, fdcp->c_parentcnid);
671 if (error) {
672 printf ("hfs_vnop_link: error updating source parent chain for id=%u, vol=%s\n", fdcp->c_cnid, hfsmp->vcbVN);
673 error = 0;
674 }
675 }
676 hfs_volupdate(hfsmp, VOL_MKFILE,
677 (tdcp->c_cnid == kHFSRootFolderID));
678 }
679
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);
684 }
685
686 out:
687 if (lockflags) {
688 hfs_systemfile_unlock(hfsmp, lockflags);
689 }
690 if (intrans) {
691 hfs_end_transaction(hfsmp);
692 }
693
694 tdcp->c_flag &= ~C_DIR_MODIFICATION;
695 wakeup((caddr_t)&tdcp->c_flag);
696
697 if (fdcp) {
698 hfs_unlockfour(tdcp, cp, fdcp, NULL);
699 } else {
700 hfs_unlockpair(tdcp, cp);
701 }
702 if (fdvp) {
703 vnode_put(fdvp);
704 }
705 return (error);
706 }
707
708
709 /*
710 * Remove a link to a hardlink file/dir.
711 *
712 * Note: dvp and vp cnodes are already locked.
713 */
714 int
715 hfs_unlink(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve)
716 {
717 struct cnode *cp;
718 struct cnode *dcp;
719 struct cat_desc cndesc;
720 struct timeval tv;
721 char inodename[32];
722 cnid_t prevlinkid;
723 cnid_t nextlinkid;
724 int lockflags = 0;
725 int started_tr;
726 int error;
727
728 if (hfsmp->hfs_flags & HFS_STANDARD) {
729 return (EPERM);
730 }
731 cp = VTOC(vp);
732 dcp = VTOC(dvp);
733
734 dcp->c_flag |= C_DIR_MODIFICATION;
735
736 /* Remove the entry from the namei cache: */
737 cache_purge(vp);
738
739 if ((error = hfs_start_transaction(hfsmp)) != 0) {
740 started_tr = 0;
741 goto out;
742 }
743 started_tr = 1;
744
745 /*
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).
749 *
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).
753 */
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;
760
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;
765 }
766 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
767
768 if ((error = cat_lookuplink(hfsmp, &cndesc, &cndesc.cd_cnid, &prevlinkid, &nextlinkid))) {
769 goto out;
770 }
771
772 /* Reserve some space in the catalog file. */
773 if (!skip_reserve && (error = cat_preflight(hfsmp, 2 * CAT_DELETE, NULL, 0))) {
774 goto out;
775 }
776
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);
781 }
782
783 /* Delete the link record. */
784 if ((error = cat_deletelink(hfsmp, &cndesc))) {
785 goto out;
786 }
787
788 /* Update the parent directory. */
789 if (dcp->c_entries > 0) {
790 dcp->c_entries--;
791 }
792 if (cndesc.cd_flags & CD_ISDIR) {
793 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
794 }
795 dcp->c_dirchangecnt++;
796 hfs_incr_gencount(dcp);
797 microtime(&tv);
798 dcp->c_touch_chgtime = dcp->c_touch_modtime = true;
799 dcp->c_flag |= C_MODIFIED;
800 hfs_update(dcp->c_vp, 0);
801
802 /*
803 * If this is the last link then we need to process the inode.
804 * Otherwise we need to fix up the link chain.
805 */
806 --cp->c_linkcount;
807 if (cp->c_linkcount < 1) {
808 char delname[32];
809 struct cat_desc to_desc;
810 struct cat_desc from_desc;
811
812 /*
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.
819 */
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);
825 }
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;
831 } else {
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;
837 }
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;
841
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;
847
848 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
849 &to_desc, (struct cat_desc *)NULL);
850 if (error == 0) {
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]);
857
858 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
859 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
860
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);
865 }
866 } else {
867 error = 0; /* rename failure here is not fatal */
868 }
869 } else /* Still some links left */ {
870 cnid_t firstlink;
871
872 /*
873 * Update the start of the link chain.
874 * Note: Directory hard links store the first link in an attribute.
875 */
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;
883 }
884 /* Update previous link. */
885 if (prevlinkid) {
886 (void) cat_update_siblinglinks(hfsmp, prevlinkid, HFS_IGNORABLE_LINK, nextlinkid);
887 }
888 /* Update next link. */
889 if (nextlinkid) {
890 (void) cat_update_siblinglinks(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK);
891 }
892 }
893
894 /*
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.
898 *
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.
906 */
907 cp->c_cnid = cp->c_fileid;
908
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);
912
913 /* All done with the system files. */
914 hfs_systemfile_unlock(hfsmp, lockflags);
915 lockflags = 0;
916
917 /* Update file system stats. */
918 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
919
920 /*
921 * All done with this cnode's descriptor...
922 *
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.
926 */
927 cat_releasedesc(&cp->c_desc);
928
929 out:
930 if (lockflags) {
931 hfs_systemfile_unlock(hfsmp, lockflags);
932 }
933 if (started_tr) {
934 hfs_end_transaction(hfsmp);
935 }
936
937 dcp->c_flag &= ~C_DIR_MODIFICATION;
938 wakeup((caddr_t)&dcp->c_flag);
939
940 return (error);
941 }
942
943
944 /*
945 * Initialize the HFS+ private system directories.
946 *
947 * These directories are used to hold the inodes
948 * for file and directory hardlinks as well as
949 * open-unlinked files.
950 *
951 * If they don't yet exist they will get created.
952 *
953 * This call is assumed to be made during mount.
954 */
955 void
956 hfs_privatedir_init(struct hfsmount * hfsmp, enum privdirtype type)
957 {
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;
963 struct timeval tv;
964 int lockflags;
965 int trans = 0;
966 int error;
967
968 if (hfsmp->hfs_flags & HFS_STANDARD) {
969 return;
970 }
971
972 priv_descp = &hfsmp->hfs_private_desc[type];
973 priv_attrp = &hfsmp->hfs_private_attr[type];
974
975 /* Check if directory already exists. */
976 if (priv_descp->cd_cnid != 0) {
977 return;
978 }
979
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;
984
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);
988
989 if (error == 0) {
990 if (type == FILE_HARDLINKS) {
991 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
992 }
993 priv_descp->cd_cnid = priv_attrp->ca_fileid;
994 goto exit;
995 }
996
997 /* Directory is missing, if this is read-only then we're done. */
998 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
999 goto exit;
1000 }
1001
1002 /* Grab the root directory so we can update it later. */
1003 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0, 0) != 0) {
1004 goto exit;
1005 }
1006 dcp = VTOC(dvp);
1007
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;
1015 }
1016 priv_attrp->ca_linkcount = 1;
1017 priv_attrp->ca_itime = hfsmp->hfs_itime;
1018 priv_attrp->ca_recflags = kHFSHasFolderCountMask;
1019
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);
1024
1025 if (hfs_start_transaction(hfsmp) != 0) {
1026 goto exit;
1027 }
1028 trans = 1;
1029
1030 /* Need the catalog and EA b-trees for CNID acquisition */
1031 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1032
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);
1036 goto exit;
1037 }
1038
1039 /* Get the CNID for use */
1040 cnid_t new_id;
1041 if ((error = cat_acquire_cnid(hfsmp, &new_id))) {
1042 hfs_systemfile_unlock (hfsmp, lockflags);
1043 goto exit;
1044 }
1045
1046 /* Create the private directory on disk. */
1047 error = cat_create(hfsmp, new_id, priv_descp, priv_attrp, NULL);
1048 if (error == 0) {
1049 priv_descp->cd_cnid = priv_attrp->ca_fileid;
1050
1051 /* Update the parent directory */
1052 dcp->c_entries++;
1053 INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
1054 dcp->c_dirchangecnt++;
1055 hfs_incr_gencount(dcp);
1056 microtime(&tv);
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);
1060 }
1061
1062 hfs_systemfile_unlock(hfsmp, lockflags);
1063
1064 if (error) {
1065 goto exit;
1066 }
1067 if (type == FILE_HARDLINKS) {
1068 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
1069 }
1070 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
1071 exit:
1072 if (trans) {
1073 hfs_end_transaction(hfsmp);
1074 }
1075 if (dvp) {
1076 hfs_unlock(dcp);
1077 vnode_put(dvp);
1078 }
1079 if ((error == 0) && (type == DIR_HARDLINKS)) {
1080 hfs_xattr_init(hfsmp);
1081 }
1082 }
1083
1084
1085 /*
1086 * Lookup a hardlink link (from chain)
1087 */
1088 int
1089 hfs_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
1090 {
1091 int lockflags;
1092 int error;
1093
1094 *prevlinkid = 0;
1095 *nextlinkid = 0;
1096
1097 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1098
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);
1103
1104 error = getfirstlink(hfsmp, linkfileid, nextlinkid);
1105 }
1106 hfs_systemfile_unlock(hfsmp, lockflags);
1107
1108 return (error);
1109 }
1110
1111
1112 /* Find the oldest / last hardlink in the link chain */
1113 int
1114 hfs_lookup_lastlink (struct hfsmount *hfsmp, cnid_t linkfileid,
1115 cnid_t *lastid, struct cat_desc *cdesc) {
1116 int lockflags;
1117 int error;
1118
1119 *lastid = 0;
1120
1121 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1122
1123 error = cat_lookup_lastlink(hfsmp, linkfileid, lastid, cdesc);
1124
1125 hfs_systemfile_unlock(hfsmp, lockflags);
1126
1127 /*
1128 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed
1129 * upon error cases.
1130 */
1131 return error;
1132 }
1133
1134
1135 /*
1136 * Cache the origin of a directory or file hard link
1137 *
1138 * cnode must be lock on entry
1139 */
1140 __private_extern__
1141 void
1142 hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid)
1143 {
1144 linkorigin_t *origin = NULL;
1145 thread_t thread = current_thread();
1146 int count = 0;
1147 int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS;
1148 /*
1149 * Look for an existing origin first. If not found, create/steal one.
1150 */
1151 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1152 ++count;
1153 if (origin->lo_thread == thread) {
1154 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1155 break;
1156 }
1157 }
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);
1163 } else {
1164 MALLOC(origin, linkorigin_t *, sizeof(linkorigin_t), M_TEMP, M_WAITOK);
1165 }
1166 origin->lo_thread = thread;
1167 }
1168 origin->lo_cnid = cp->c_cnid;
1169 origin->lo_parentcnid = parentcnid;
1170 TAILQ_INSERT_HEAD(&cp->c_originlist, origin, lo_link);
1171 }
1172
1173 /*
1174 * Release any cached origins for a directory or file hard link
1175 *
1176 * cnode must be lock on entry
1177 */
1178 __private_extern__
1179 void
1180 hfs_relorigins(struct cnode *cp)
1181 {
1182 linkorigin_t *origin, *prev;
1183
1184 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
1185 FREE(origin, M_TEMP);
1186 }
1187 TAILQ_INIT(&cp->c_originlist);
1188 }
1189
1190 /*
1191 * Release a specific origin for a directory or file hard link
1192 *
1193 * cnode must be lock on entry
1194 */
1195 __private_extern__
1196 void
1197 hfs_relorigin(struct cnode *cp, cnid_t parentcnid)
1198 {
1199 linkorigin_t *origin, *prev;
1200 thread_t thread = current_thread();
1201
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);
1206 break;
1207 } else if (origin->lo_parentcnid == parentcnid) {
1208 /*
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.
1216 */
1217 origin->lo_parentcnid = 0;
1218 origin->lo_cnid = 0;
1219 }
1220 }
1221 }
1222
1223 /*
1224 * Test if a directory or file hard link has a cached origin
1225 *
1226 * cnode must be lock on entry
1227 */
1228 __private_extern__
1229 int
1230 hfs_haslinkorigin(cnode_t *cp)
1231 {
1232 if (cp->c_flag & C_HARDLINK) {
1233 linkorigin_t *origin;
1234 thread_t thread = current_thread();
1235
1236 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1237 if (origin->lo_thread == thread) {
1238 return origin->lo_cnid != 0;
1239 }
1240 }
1241 }
1242 return (0);
1243 }
1244
1245 /*
1246 * Obtain the current parent cnid of a directory or file hard link
1247 *
1248 * cnode must be lock on entry
1249 */
1250 __private_extern__
1251 cnid_t
1252 hfs_currentparent(cnode_t *cp, bool have_lock)
1253 {
1254 if (cp->c_flag & C_HARDLINK) {
1255 if (!have_lock)
1256 hfs_lock_always(cp, HFS_SHARED_LOCK);
1257
1258 linkorigin_t *origin;
1259 thread_t thread = current_thread();
1260
1261 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1262 if (origin->lo_thread == thread) {
1263 if (!have_lock)
1264 hfs_unlock(cp);
1265 return (origin->lo_parentcnid);
1266 }
1267 }
1268
1269 if (!have_lock)
1270 hfs_unlock(cp);
1271 }
1272 return (cp->c_parentcnid);
1273 }
1274
1275 /*
1276 * Obtain the current cnid of a directory or file hard link
1277 *
1278 * cnode must be lock on entry
1279 */
1280 __private_extern__
1281 cnid_t
1282 hfs_currentcnid(cnode_t *cp)
1283 {
1284 if (cp->c_flag & C_HARDLINK) {
1285 linkorigin_t *origin;
1286 thread_t thread = current_thread();
1287
1288 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1289 if (origin->lo_thread == thread) {
1290 return (origin->lo_cnid);
1291 }
1292 }
1293 }
1294 return (cp->c_cnid);
1295 }
1296
1297
1298 /*
1299 * Set the first link attribute for a given file id.
1300 *
1301 * The attributes b-tree must already be locked.
1302 * If journaling is enabled, a transaction must already be started.
1303 */
1304 static int
1305 setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink)
1306 {
1307 FCB * btfile;
1308 BTreeIterator * iterator;
1309 FSBufferDescriptor btdata;
1310 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1311 HFSPlusAttrData *dataptr;
1312 int result;
1313 u_int16_t datasize;
1314
1315 if (hfsmp->hfs_attribute_cp == NULL) {
1316 return (EPERM);
1317 }
1318 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1319 bzero(iterator, sizeof(*iterator));
1320
1321 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1322 if (result) {
1323 goto out;
1324 }
1325 dataptr = (HFSPlusAttrData *)&attrdata[0];
1326 dataptr->recordType = kHFSPlusAttrInlineData;
1327 dataptr->reserved[0] = 0;
1328 dataptr->reserved[1] = 0;
1329
1330 /*
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.
1334 */
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]);
1339
1340 /* Calculate size of record rounded up to multiple of 2 bytes. */
1341 datasize = sizeof(HFSPlusAttrData) - 2 + dataptr->attrSize + ((dataptr->attrSize & 1) ? 1 : 0);
1342
1343 btdata.bufferAddress = dataptr;
1344 btdata.itemSize = datasize;
1345 btdata.itemCount = 1;
1346
1347 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1348
1349 /* Insert the attribute. */
1350 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
1351 if (result == btExists) {
1352 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
1353 }
1354 (void) BTFlushPath(btfile);
1355 out:
1356 FREE(iterator, M_TEMP);
1357
1358 return MacToVFSError(result);
1359 }
1360
1361 /*
1362 * Get the first link attribute for a given file id.
1363 *
1364 * The attributes b-tree must already be locked.
1365 */
1366 static int
1367 getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink)
1368 {
1369 FCB * btfile;
1370 BTreeIterator * iterator;
1371 FSBufferDescriptor btdata;
1372 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1373 HFSPlusAttrData *dataptr;
1374 int result;
1375 u_int16_t datasize;
1376
1377 if (hfsmp->hfs_attribute_cp == NULL) {
1378 return (EPERM);
1379 }
1380 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1381 bzero(iterator, sizeof(*iterator));
1382
1383 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1384 if (result)
1385 goto out;
1386
1387 dataptr = (HFSPlusAttrData *)&attrdata[0];
1388 datasize = sizeof(attrdata);
1389
1390 btdata.bufferAddress = dataptr;
1391 btdata.itemSize = sizeof(attrdata);
1392 btdata.itemCount = 1;
1393
1394 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1395
1396 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
1397 if (result)
1398 goto out;
1399
1400 if (dataptr->attrSize < 3) {
1401 result = ENOENT;
1402 goto out;
1403 }
1404 *firstlink = strtoul((char*)&dataptr->attrData[0], NULL, 10);
1405 out:
1406 FREE(iterator, M_TEMP);
1407
1408 return MacToVFSError(result);
1409 }
1410
1411 errno_t hfs_first_link(hfsmount_t *hfsmp, cnode_t *cp, cnid_t *link_id)
1412 {
1413 errno_t error = 0;
1414
1415 if (S_ISDIR(cp->c_mode)) {
1416 int lockf = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1417
1418 error = getfirstlink(hfsmp, cp->c_fileid, link_id);
1419
1420 hfs_systemfile_unlock(hfsmp, lockf);
1421 } else {
1422 if (cp->c_attr.ca_firstlink)
1423 *link_id = cp->c_attr.ca_firstlink;
1424 else {
1425 // This can happen if the cnode has been deleted
1426 error = ENOENT;
1427 }
1428 }
1429
1430 return error;
1431 }