]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
667bad9c6581434ffe2a494c35b7f81f4c29c330
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/mount.h>
34 #include <sys/stat.h>
35 #include <sys/vnode.h>
36 #include <vfs/vfs_support.h>
37 #include <libkern/libkern.h>
38 #include <sys/fsctl.h>
39
40 #include "hfs.h"
41 #include "hfs_catalog.h"
42 #include "hfs_format.h"
43 #include "hfs_endian.h"
44
45
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_touch_chgtime = TRUE;
392 cp->c_flag |= C_FORCEUPDATE;
393 }
394 dcp->c_flag |= C_FORCEUPDATE;
395 }
396 out:
397 hfs_systemfile_unlock(hfsmp, lockflags);
398
399 cat_postflight(hfsmp, &cookie, p);
400
401 if (retval == 0 && newlink) {
402 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
403 }
404 return (retval);
405 }
406
407
408 /*
409 * link vnode operation
410 *
411 * IN vnode_t a_vp;
412 * IN vnode_t a_tdvp;
413 * IN struct componentname *a_cnp;
414 * IN vfs_context_t a_context;
415 */
416 int
417 hfs_vnop_link(struct vnop_link_args *ap)
418 {
419 struct hfsmount *hfsmp;
420 struct vnode *vp = ap->a_vp;
421 struct vnode *tdvp = ap->a_tdvp;
422 struct vnode *fdvp = NULLVP;
423 struct componentname *cnp = ap->a_cnp;
424 struct cnode *cp;
425 struct cnode *tdcp;
426 struct cnode *fdcp = NULL;
427 struct cat_desc todesc;
428 cnid_t parentcnid;
429 int lockflags = 0;
430 int intrans = 0;
431 enum vtype v_type;
432 int error, ret;
433
434 hfsmp = VTOHFS(vp);
435 v_type = vnode_vtype(vp);
436
437 /* No hard links in HFS standard file systems. */
438 if (hfsmp->hfs_flags & HFS_STANDARD) {
439 return (ENOTSUP);
440 }
441 /* Linking to a special file is not permitted. */
442 if (v_type == VBLK || v_type == VCHR) {
443 return (EPERM);
444 }
445
446 /*
447 * For now, return ENOTSUP for a symlink target. This can happen
448 * for linkat(2) when called without AT_SYMLINK_FOLLOW.
449 */
450 if (v_type == VLNK)
451 return (ENOTSUP);
452
453 if (v_type == VDIR) {
454 #if CONFIG_HFS_DIRLINK
455 /* Make sure our private directory exists. */
456 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid == 0) {
457 return (EPERM);
458 }
459 /*
460 * Directory hardlinks (ADLs) have only been qualified on
461 * journaled HFS+. If/when they are tested on non-journaled
462 * file systems then this test can be removed.
463 */
464 if (hfsmp->jnl == NULL) {
465 return (EPERM);
466 }
467 /* Directory hardlinks also need the parent of the original directory. */
468 if ((error = hfs_vget(hfsmp, hfs_currentparent(VTOC(vp)), &fdvp, 1, 0))) {
469 return (error);
470 }
471 #else
472 /* some platforms don't support directory hardlinks. */
473 return EPERM;
474 #endif
475 } else {
476 /* Make sure our private directory exists. */
477 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
478 return (ENOTSUP);
479 }
480 }
481 if (hfs_freeblks(hfsmp, 0) == 0) {
482 if (fdvp) {
483 vnode_put(fdvp);
484 }
485 return (ENOSPC);
486 }
487
488 check_for_tracked_file(vp, VTOC(vp)->c_ctime, NAMESPACE_HANDLER_LINK_CREATE, NULL);
489
490
491 /* Lock the cnodes. */
492 if (fdvp) {
493 if ((error = hfs_lockfour(VTOC(tdvp), VTOC(vp), VTOC(fdvp), NULL, HFS_EXCLUSIVE_LOCK, NULL))) {
494 if (fdvp) {
495 vnode_put(fdvp);
496 }
497 return (error);
498 }
499 fdcp = VTOC(fdvp);
500 } else {
501 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
502 return (error);
503 }
504 }
505 tdcp = VTOC(tdvp);
506 cp = VTOC(vp);
507 /* grab the parent CNID from originlist after grabbing cnode locks */
508 parentcnid = hfs_currentparent(cp);
509
510 /*
511 * Make sure we didn't race the src or dst parent directories with rmdir.
512 * Note that we should only have a src parent directory cnode lock
513 * if we're dealing with a directory hardlink here.
514 */
515 if (fdcp) {
516 if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
517 error = ENOENT;
518 goto out;
519 }
520 }
521
522 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
523 error = ENOENT;
524 goto out;
525 }
526
527 /* Check the source for errors:
528 * too many links, immutable, race with unlink
529 */
530 if (cp->c_linkcount >= HFS_LINK_MAX) {
531 error = EMLINK;
532 goto out;
533 }
534 if (cp->c_bsdflags & (IMMUTABLE | APPEND)) {
535 error = EPERM;
536 goto out;
537 }
538 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
539 error = ENOENT;
540 goto out;
541 }
542
543 tdcp->c_flag |= C_DIR_MODIFICATION;
544
545 if (hfs_start_transaction(hfsmp) != 0) {
546 error = EINVAL;
547 goto out;
548 }
549 intrans = 1;
550
551 todesc.cd_flags = (v_type == VDIR) ? CD_ISDIR : 0;
552 todesc.cd_encoding = 0;
553 todesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
554 todesc.cd_namelen = cnp->cn_namelen;
555 todesc.cd_parentcnid = tdcp->c_fileid;
556 todesc.cd_hint = 0;
557 todesc.cd_cnid = 0;
558
559 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
560
561 /* If destination exists then we lost a race with create. */
562 if (cat_lookup(hfsmp, &todesc, 0, 0, NULL, NULL, NULL, NULL) == 0) {
563 error = EEXIST;
564 goto out;
565 }
566 if (cp->c_flag & C_HARDLINK) {
567 struct cat_attr cattr;
568
569 /* If inode is missing then we lost a race with unlink. */
570 if ((cat_idlookup(hfsmp, cp->c_fileid, 0, 0, NULL, &cattr, NULL) != 0) ||
571 (cattr.ca_fileid != cp->c_fileid)) {
572 error = ENOENT;
573 goto out;
574 }
575 } else {
576 cnid_t fileid;
577
578 /* If source is missing then we lost a race with unlink. */
579 if ((cat_lookup(hfsmp, &cp->c_desc, 0, 0, NULL, NULL, NULL, &fileid) != 0) ||
580 (fileid != cp->c_fileid)) {
581 error = ENOENT;
582 goto out;
583 }
584 }
585 /*
586 * All directory links must reside in an non-ARCHIVED hierarchy.
587 */
588 if (v_type == VDIR) {
589 /*
590 * - Source parent and destination parent cannot match
591 * - A link is not permitted in the root directory
592 * - Parent of 'pointed at' directory is not the root directory
593 * - The 'pointed at' directory (source) is not an ancestor
594 * of the new directory hard link (destination).
595 * - No ancestor of the new directory hard link (destination)
596 * is a directory hard link.
597 */
598 if ((parentcnid == tdcp->c_fileid) ||
599 (tdcp->c_fileid == kHFSRootFolderID) ||
600 (parentcnid == kHFSRootFolderID) ||
601 cat_check_link_ancestry(hfsmp, tdcp->c_fileid, cp->c_fileid)) {
602 error = EPERM; /* abide by the rules, you did not */
603 goto out;
604 }
605 }
606 hfs_systemfile_unlock(hfsmp, lockflags);
607 lockflags = 0;
608
609 cp->c_linkcount++;
610 cp->c_touch_chgtime = TRUE;
611 error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
612 if (error) {
613 cp->c_linkcount--;
614 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
615 } else {
616 /* Invalidate negative cache entries in the destination directory */
617 if (tdcp->c_flag & C_NEG_ENTRIES) {
618 cache_purge_negatives(tdvp);
619 tdcp->c_flag &= ~C_NEG_ENTRIES;
620 }
621
622 /* Update the target directory and volume stats */
623 tdcp->c_entries++;
624 if (v_type == VDIR) {
625 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
626 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
627
628 /* Set kHFSHasChildLinkBit in the destination hierarchy */
629 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
630 if (error) {
631 printf ("hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN);
632 error = 0;
633 }
634 }
635 tdcp->c_dirchangecnt++;
636 hfs_incr_gencount(tdcp);
637 tdcp->c_touch_chgtime = TRUE;
638 tdcp->c_touch_modtime = TRUE;
639 tdcp->c_flag |= C_FORCEUPDATE;
640
641 error = hfs_update(tdvp, 0);
642 if (error) {
643 if (error != EIO && error != ENXIO) {
644 printf("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
645 error = EIO;
646 }
647 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
648 }
649
650 if ((v_type == VDIR) &&
651 (fdcp != NULL) &&
652 ((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) {
653
654 fdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
655 fdcp->c_touch_chgtime = TRUE;
656 fdcp->c_flag |= C_FORCEUPDATE;
657 error = hfs_update(fdvp, 0);
658 if (error) {
659 if (error != EIO && error != ENXIO) {
660 printf("hfs_vnop_link: error %d updating fdvp %p\n", error, fdvp);
661 // No point changing error as it's set immediate below
662 }
663 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
664 }
665
666 /* Set kHFSHasChildLinkBit in the source hierarchy */
667 error = cat_set_childlinkbit(hfsmp, fdcp->c_parentcnid);
668 if (error) {
669 printf ("hfs_vnop_link: error updating source parent chain for id=%u, vol=%s\n", fdcp->c_cnid, hfsmp->vcbVN);
670 error = 0;
671 }
672 }
673 hfs_volupdate(hfsmp, VOL_MKFILE,
674 (tdcp->c_cnid == kHFSRootFolderID));
675 }
676 /* Make sure update occurs inside transaction */
677 cp->c_flag |= C_FORCEUPDATE;
678
679 if (error == 0 && (ret = hfs_update(vp, TRUE)) != 0) {
680 if (ret != EIO && ret != ENXIO)
681 printf("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
682 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
683 }
684
685 out:
686 if (lockflags) {
687 hfs_systemfile_unlock(hfsmp, lockflags);
688 }
689 if (intrans) {
690 hfs_end_transaction(hfsmp);
691 }
692
693 tdcp->c_flag &= ~C_DIR_MODIFICATION;
694 wakeup((caddr_t)&tdcp->c_flag);
695
696 if (fdcp) {
697 hfs_unlockfour(tdcp, cp, fdcp, NULL);
698 } else {
699 hfs_unlockpair(tdcp, cp);
700 }
701 if (fdvp) {
702 vnode_put(fdvp);
703 }
704 return (error);
705 }
706
707
708 /*
709 * Remove a link to a hardlink file/dir.
710 *
711 * Note: dvp and vp cnodes are already locked.
712 */
713 int
714 hfs_unlink(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve)
715 {
716 struct cnode *cp;
717 struct cnode *dcp;
718 struct cat_desc cndesc;
719 struct timeval tv;
720 char inodename[32];
721 cnid_t prevlinkid;
722 cnid_t nextlinkid;
723 int lockflags = 0;
724 int started_tr;
725 int error;
726
727 if (hfsmp->hfs_flags & HFS_STANDARD) {
728 return (EPERM);
729 }
730 cp = VTOC(vp);
731 dcp = VTOC(dvp);
732
733 dcp->c_flag |= C_DIR_MODIFICATION;
734
735 /* Remove the entry from the namei cache: */
736 cache_purge(vp);
737
738 if ((error = hfs_start_transaction(hfsmp)) != 0) {
739 started_tr = 0;
740 goto out;
741 }
742 started_tr = 1;
743
744 /*
745 * Protect against a race with rename by using the component
746 * name passed in and parent id from dvp (instead of using
747 * the cp->c_desc which may have changed).
748 *
749 * Re-lookup the component name so we get the correct cnid
750 * for the name (as opposed to the c_cnid in the cnode which
751 * could have changed before the cnode was locked).
752 */
753 cndesc.cd_flags = vnode_isdir(vp) ? CD_ISDIR : 0;
754 cndesc.cd_encoding = cp->c_desc.cd_encoding;
755 cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
756 cndesc.cd_namelen = cnp->cn_namelen;
757 cndesc.cd_parentcnid = dcp->c_fileid;
758 cndesc.cd_hint = dcp->c_childhint;
759
760 lockflags = SFL_CATALOG | SFL_ATTRIBUTE;
761 if (cndesc.cd_flags & CD_ISDIR) {
762 /* We'll be removing the alias resource allocation blocks. */
763 lockflags |= SFL_BITMAP;
764 }
765 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
766
767 if ((error = cat_lookuplink(hfsmp, &cndesc, &cndesc.cd_cnid, &prevlinkid, &nextlinkid))) {
768 goto out;
769 }
770
771 /* Reserve some space in the catalog file. */
772 if (!skip_reserve && (error = cat_preflight(hfsmp, 2 * CAT_DELETE, NULL, 0))) {
773 goto out;
774 }
775
776 /* Purge any cached origin entries for a directory or file hard link. */
777 hfs_relorigin(cp, dcp->c_fileid);
778 if (dcp->c_fileid != dcp->c_cnid) {
779 hfs_relorigin(cp, dcp->c_cnid);
780 }
781
782 /* Delete the link record. */
783 if ((error = cat_deletelink(hfsmp, &cndesc))) {
784 goto out;
785 }
786
787 /* Update the parent directory. */
788 if (dcp->c_entries > 0) {
789 dcp->c_entries--;
790 }
791 if (cndesc.cd_flags & CD_ISDIR) {
792 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
793 }
794 dcp->c_dirchangecnt++;
795 hfs_incr_gencount(dcp);
796 microtime(&tv);
797 dcp->c_ctime = tv.tv_sec;
798 dcp->c_mtime = tv.tv_sec;
799 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
800
801 /*
802 * If this is the last link then we need to process the inode.
803 * Otherwise we need to fix up the link chain.
804 */
805 --cp->c_linkcount;
806 if (cp->c_linkcount < 1) {
807 char delname[32];
808 struct cat_desc to_desc;
809 struct cat_desc from_desc;
810
811 /*
812 * If a file inode or directory inode is being deleted, rename
813 * it to an open deleted file. This ensures that deletion
814 * of inode and its corresponding extended attributes does
815 * not overflow the journal. This inode will be deleted
816 * either in hfs_vnop_inactive() or in hfs_remove_orphans().
817 * Note: a rename failure here is not fatal.
818 */
819 bzero(&from_desc, sizeof(from_desc));
820 bzero(&to_desc, sizeof(to_desc));
821 if (vnode_isdir(vp)) {
822 if (cp->c_entries != 0) {
823 panic("hfs_unlink: dir not empty (id %d, %d entries)", cp->c_fileid, cp->c_entries);
824 }
825 MAKE_DIRINODE_NAME(inodename, sizeof(inodename),
826 cp->c_attr.ca_linkref);
827 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
828 from_desc.cd_flags = CD_ISDIR;
829 to_desc.cd_flags = CD_ISDIR;
830 } else {
831 MAKE_INODE_NAME(inodename, sizeof(inodename),
832 cp->c_attr.ca_linkref);
833 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
834 from_desc.cd_flags = 0;
835 to_desc.cd_flags = 0;
836 }
837 from_desc.cd_nameptr = (const u_int8_t *)inodename;
838 from_desc.cd_namelen = strlen(inodename);
839 from_desc.cd_cnid = cp->c_fileid;
840
841 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
842 to_desc.cd_nameptr = (const u_int8_t *)delname;
843 to_desc.cd_namelen = strlen(delname);
844 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
845 to_desc.cd_cnid = cp->c_fileid;
846
847 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
848 &to_desc, (struct cat_desc *)NULL);
849 if (error == 0) {
850 cp->c_flag |= C_DELETED;
851 cp->c_attr.ca_recflags &= ~kHFSHasLinkChainMask;
852 cp->c_attr.ca_firstlink = 0;
853 if (vnode_isdir(vp)) {
854 hfsmp->hfs_private_attr[DIR_HARDLINKS].ca_entries--;
855 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[DIR_HARDLINKS]);
856
857 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
858 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
859
860 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[DIR_HARDLINKS],
861 &hfsmp->hfs_private_attr[DIR_HARDLINKS], NULL, NULL);
862 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
863 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
864 }
865 } else {
866 error = 0; /* rename failure here is not fatal */
867 }
868 } else /* Still some links left */ {
869 cnid_t firstlink;
870
871 /*
872 * Update the start of the link chain.
873 * Note: Directory hard links store the first link in an attribute.
874 */
875 if (vnode_isdir(vp) &&
876 getfirstlink(hfsmp, cp->c_fileid, &firstlink) == 0 &&
877 firstlink == cndesc.cd_cnid) {
878 if (setfirstlink(hfsmp, cp->c_fileid, nextlinkid) == 0)
879 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
880 } else if (vnode_isreg(vp) && cp->c_attr.ca_firstlink == cndesc.cd_cnid) {
881 cp->c_attr.ca_firstlink = nextlinkid;
882 }
883 /* Update previous link. */
884 if (prevlinkid) {
885 (void) cat_update_siblinglinks(hfsmp, prevlinkid, HFS_IGNORABLE_LINK, nextlinkid);
886 }
887 /* Update next link. */
888 if (nextlinkid) {
889 (void) cat_update_siblinglinks(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK);
890 }
891
892 /*
893 * The call to cat_releasedesc below will only release the name buffer;
894 * it does not zero out the rest of the fields in the 'cat_desc' data structure.
895 *
896 * As a result, since there are still other links at this point, we need
897 * to make the current cnode descriptor point to the raw inode. If a path-based
898 * system call comes along first, it will replace the descriptor with a valid link
899 * ID. If a userland process already has a file descriptor open, then they will
900 * bypass that lookup, though. Replacing the descriptor CNID with the raw
901 * inode will force it to generate a new full path.
902 */
903 cp->c_cnid = cp->c_fileid;
904
905 }
906
907 /* Push new link count to disk. */
908 cp->c_ctime = tv.tv_sec;
909 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
910
911 /* All done with the system files. */
912 hfs_systemfile_unlock(hfsmp, lockflags);
913 lockflags = 0;
914
915 /* Update file system stats. */
916 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
917
918 /*
919 * All done with this cnode's descriptor...
920 *
921 * Note: all future catalog calls for this cnode may be
922 * by fileid only. This is OK for HFS (which doesn't have
923 * file thread records) since HFS doesn't support hard links.
924 */
925 cat_releasedesc(&cp->c_desc);
926
927 out:
928 if (lockflags) {
929 hfs_systemfile_unlock(hfsmp, lockflags);
930 }
931 if (started_tr) {
932 hfs_end_transaction(hfsmp);
933 }
934
935 dcp->c_flag &= ~C_DIR_MODIFICATION;
936 wakeup((caddr_t)&dcp->c_flag);
937
938 return (error);
939 }
940
941
942 /*
943 * Initialize the HFS+ private system directories.
944 *
945 * These directories are used to hold the inodes
946 * for file and directory hardlinks as well as
947 * open-unlinked files.
948 *
949 * If they don't yet exist they will get created.
950 *
951 * This call is assumed to be made during mount.
952 */
953 void
954 hfs_privatedir_init(struct hfsmount * hfsmp, enum privdirtype type)
955 {
956 struct vnode * dvp = NULLVP;
957 struct cnode * dcp = NULL;
958 struct cat_desc *priv_descp;
959 struct cat_attr *priv_attrp;
960 struct FndrDirInfo * fndrinfo;
961 struct timeval tv;
962 int lockflags;
963 int trans = 0;
964 int error;
965
966 if (hfsmp->hfs_flags & HFS_STANDARD) {
967 return;
968 }
969
970 priv_descp = &hfsmp->hfs_private_desc[type];
971 priv_attrp = &hfsmp->hfs_private_attr[type];
972
973 /* Check if directory already exists. */
974 if (priv_descp->cd_cnid != 0) {
975 return;
976 }
977
978 priv_descp->cd_parentcnid = kRootDirID;
979 priv_descp->cd_nameptr = (const u_int8_t *)hfs_private_names[type];
980 priv_descp->cd_namelen = strlen((const char *)priv_descp->cd_nameptr);
981 priv_descp->cd_flags = CD_ISDIR | CD_DECOMPOSED;
982
983 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
984 error = cat_lookup(hfsmp, priv_descp, 0, 0, NULL, priv_attrp, NULL, NULL);
985 hfs_systemfile_unlock(hfsmp, lockflags);
986
987 if (error == 0) {
988 if (type == FILE_HARDLINKS) {
989 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
990 }
991 priv_descp->cd_cnid = priv_attrp->ca_fileid;
992 goto exit;
993 }
994
995 /* Directory is missing, if this is read-only then we're done. */
996 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
997 goto exit;
998 }
999
1000 /* Grab the root directory so we can update it later. */
1001 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0, 0) != 0) {
1002 goto exit;
1003 }
1004 dcp = VTOC(dvp);
1005
1006 /* Setup the default attributes */
1007 bzero(priv_attrp, sizeof(struct cat_attr));
1008 priv_attrp->ca_flags = UF_IMMUTABLE | UF_HIDDEN;
1009 priv_attrp->ca_mode = S_IFDIR;
1010 if (type == DIR_HARDLINKS) {
1011 priv_attrp->ca_mode |= S_ISVTX | S_IRUSR | S_IXUSR | S_IRGRP |
1012 S_IXGRP | S_IROTH | S_IXOTH;
1013 }
1014 priv_attrp->ca_linkcount = 1;
1015 priv_attrp->ca_itime = hfsmp->hfs_itime;
1016 priv_attrp->ca_recflags = kHFSHasFolderCountMask;
1017
1018 fndrinfo = (struct FndrDirInfo *)&priv_attrp->ca_finderinfo;
1019 fndrinfo->frLocation.v = SWAP_BE16(16384);
1020 fndrinfo->frLocation.h = SWAP_BE16(16384);
1021 fndrinfo->frFlags = SWAP_BE16(kIsInvisible + kNameLocked);
1022
1023 if (hfs_start_transaction(hfsmp) != 0) {
1024 goto exit;
1025 }
1026 trans = 1;
1027
1028 /* Need the catalog and EA b-trees for CNID acquisition */
1029 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1030
1031 /* Make sure there's space in the Catalog file. */
1032 if (cat_preflight(hfsmp, CAT_CREATE, NULL, 0) != 0) {
1033 hfs_systemfile_unlock(hfsmp, lockflags);
1034 goto exit;
1035 }
1036
1037 /* Get the CNID for use */
1038 cnid_t new_id;
1039 if ((error = cat_acquire_cnid(hfsmp, &new_id))) {
1040 hfs_systemfile_unlock (hfsmp, lockflags);
1041 goto exit;
1042 }
1043
1044 /* Create the private directory on disk. */
1045 error = cat_create(hfsmp, new_id, priv_descp, priv_attrp, NULL);
1046 if (error == 0) {
1047 priv_descp->cd_cnid = priv_attrp->ca_fileid;
1048
1049 /* Update the parent directory */
1050 dcp->c_entries++;
1051 INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
1052 dcp->c_dirchangecnt++;
1053 hfs_incr_gencount(dcp);
1054 microtime(&tv);
1055 dcp->c_ctime = tv.tv_sec;
1056 dcp->c_mtime = tv.tv_sec;
1057 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1058 }
1059
1060 hfs_systemfile_unlock(hfsmp, lockflags);
1061
1062 if (error) {
1063 goto exit;
1064 }
1065 if (type == FILE_HARDLINKS) {
1066 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
1067 }
1068 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
1069 exit:
1070 if (trans) {
1071 hfs_end_transaction(hfsmp);
1072 }
1073 if (dvp) {
1074 hfs_unlock(dcp);
1075 vnode_put(dvp);
1076 }
1077 if ((error == 0) && (type == DIR_HARDLINKS)) {
1078 hfs_xattr_init(hfsmp);
1079 }
1080 }
1081
1082
1083 /*
1084 * Lookup a hardlink link (from chain)
1085 */
1086 int
1087 hfs_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
1088 {
1089 int lockflags;
1090 int error;
1091
1092 *prevlinkid = 0;
1093 *nextlinkid = 0;
1094
1095 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1096
1097 error = cat_lookup_siblinglinks(hfsmp, linkfileid, prevlinkid, nextlinkid);
1098 if (error == ENOLINK) {
1099 hfs_systemfile_unlock(hfsmp, lockflags);
1100 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1101
1102 error = getfirstlink(hfsmp, linkfileid, nextlinkid);
1103 }
1104 hfs_systemfile_unlock(hfsmp, lockflags);
1105
1106 return (error);
1107 }
1108
1109
1110 /* Find the oldest / last hardlink in the link chain */
1111 int
1112 hfs_lookup_lastlink (struct hfsmount *hfsmp, cnid_t linkfileid,
1113 cnid_t *lastid, struct cat_desc *cdesc) {
1114 int lockflags;
1115 int error;
1116
1117 *lastid = 0;
1118
1119 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1120
1121 error = cat_lookup_lastlink(hfsmp, linkfileid, lastid, cdesc);
1122
1123 hfs_systemfile_unlock(hfsmp, lockflags);
1124
1125 /*
1126 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed
1127 * upon error cases.
1128 */
1129 return error;
1130 }
1131
1132
1133 /*
1134 * Cache the origin of a directory or file hard link
1135 *
1136 * cnode must be lock on entry
1137 */
1138 __private_extern__
1139 void
1140 hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid)
1141 {
1142 linkorigin_t *origin = NULL;
1143 thread_t thread = current_thread();
1144 int count = 0;
1145 int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS;
1146 /*
1147 * Look for an existing origin first. If not found, create/steal one.
1148 */
1149 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1150 ++count;
1151 if (origin->lo_thread == thread) {
1152 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1153 break;
1154 }
1155 }
1156 if (origin == NULL) {
1157 /* Recycle the last (i.e., the oldest) if we have too many. */
1158 if (count > maxorigins) {
1159 origin = TAILQ_LAST(&cp->c_originlist, hfs_originhead);
1160 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1161 } else {
1162 MALLOC(origin, linkorigin_t *, sizeof(linkorigin_t), M_TEMP, M_WAITOK);
1163 }
1164 origin->lo_thread = thread;
1165 }
1166 origin->lo_cnid = cp->c_cnid;
1167 origin->lo_parentcnid = parentcnid;
1168 TAILQ_INSERT_HEAD(&cp->c_originlist, origin, lo_link);
1169 }
1170
1171 /*
1172 * Release any cached origins for a directory or file hard link
1173 *
1174 * cnode must be lock on entry
1175 */
1176 __private_extern__
1177 void
1178 hfs_relorigins(struct cnode *cp)
1179 {
1180 linkorigin_t *origin, *prev;
1181
1182 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
1183 FREE(origin, M_TEMP);
1184 }
1185 TAILQ_INIT(&cp->c_originlist);
1186 }
1187
1188 /*
1189 * Release a specific origin for a directory or file hard link
1190 *
1191 * cnode must be lock on entry
1192 */
1193 __private_extern__
1194 void
1195 hfs_relorigin(struct cnode *cp, cnid_t parentcnid)
1196 {
1197 linkorigin_t *origin, *prev;
1198 thread_t thread = current_thread();
1199
1200 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
1201 if ((origin->lo_thread == thread) ||
1202 (origin->lo_parentcnid == parentcnid)) {
1203 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1204 FREE(origin, M_TEMP);
1205 break;
1206 }
1207 }
1208 }
1209
1210 /*
1211 * Test if a directory or file hard link has a cached origin
1212 *
1213 * cnode must be lock on entry
1214 */
1215 __private_extern__
1216 int
1217 hfs_haslinkorigin(cnode_t *cp)
1218 {
1219 if (cp->c_flag & C_HARDLINK) {
1220 linkorigin_t *origin;
1221 thread_t thread = current_thread();
1222
1223 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1224 if (origin->lo_thread == thread) {
1225 return (1);
1226 }
1227 }
1228 }
1229 return (0);
1230 }
1231
1232 /*
1233 * Obtain the current parent cnid of a directory or file hard link
1234 *
1235 * cnode must be lock on entry
1236 */
1237 __private_extern__
1238 cnid_t
1239 hfs_currentparent(cnode_t *cp)
1240 {
1241 if (cp->c_flag & C_HARDLINK) {
1242 linkorigin_t *origin;
1243 thread_t thread = current_thread();
1244
1245 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1246 if (origin->lo_thread == thread) {
1247 return (origin->lo_parentcnid);
1248 }
1249 }
1250 }
1251 return (cp->c_parentcnid);
1252 }
1253
1254 /*
1255 * Obtain the current cnid of a directory or file hard link
1256 *
1257 * cnode must be lock on entry
1258 */
1259 __private_extern__
1260 cnid_t
1261 hfs_currentcnid(cnode_t *cp)
1262 {
1263 if (cp->c_flag & C_HARDLINK) {
1264 linkorigin_t *origin;
1265 thread_t thread = current_thread();
1266
1267 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1268 if (origin->lo_thread == thread) {
1269 return (origin->lo_cnid);
1270 }
1271 }
1272 }
1273 return (cp->c_cnid);
1274 }
1275
1276
1277 /*
1278 * Set the first link attribute for a given file id.
1279 *
1280 * The attributes b-tree must already be locked.
1281 * If journaling is enabled, a transaction must already be started.
1282 */
1283 static int
1284 setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink)
1285 {
1286 FCB * btfile;
1287 BTreeIterator * iterator;
1288 FSBufferDescriptor btdata;
1289 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1290 HFSPlusAttrData *dataptr;
1291 int result;
1292 u_int16_t datasize;
1293
1294 if (hfsmp->hfs_attribute_cp == NULL) {
1295 return (EPERM);
1296 }
1297 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1298 bzero(iterator, sizeof(*iterator));
1299
1300 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1301 if (result) {
1302 goto out;
1303 }
1304 dataptr = (HFSPlusAttrData *)&attrdata[0];
1305 dataptr->recordType = kHFSPlusAttrInlineData;
1306 dataptr->reserved[0] = 0;
1307 dataptr->reserved[1] = 0;
1308
1309 /*
1310 * Since attrData is variable length, we calculate the size of
1311 * attrData by subtracting the size of all other members of
1312 * structure HFSPlusAttData from the size of attrdata.
1313 */
1314 (void)snprintf((char *)&dataptr->attrData[0],
1315 sizeof(dataptr) - (4 * sizeof(uint32_t)),
1316 "%lu", (unsigned long)firstlink);
1317 dataptr->attrSize = 1 + strlen((char *)&dataptr->attrData[0]);
1318
1319 /* Calculate size of record rounded up to multiple of 2 bytes. */
1320 datasize = sizeof(HFSPlusAttrData) - 2 + dataptr->attrSize + ((dataptr->attrSize & 1) ? 1 : 0);
1321
1322 btdata.bufferAddress = dataptr;
1323 btdata.itemSize = datasize;
1324 btdata.itemCount = 1;
1325
1326 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1327
1328 /* Insert the attribute. */
1329 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
1330 if (result == btExists) {
1331 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
1332 }
1333 (void) BTFlushPath(btfile);
1334 out:
1335 FREE(iterator, M_TEMP);
1336
1337 return MacToVFSError(result);
1338 }
1339
1340 /*
1341 * Get the first link attribute for a given file id.
1342 *
1343 * The attributes b-tree must already be locked.
1344 */
1345 static int
1346 getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink)
1347 {
1348 FCB * btfile;
1349 BTreeIterator * iterator;
1350 FSBufferDescriptor btdata;
1351 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1352 HFSPlusAttrData *dataptr;
1353 int result;
1354 u_int16_t datasize;
1355
1356 if (hfsmp->hfs_attribute_cp == NULL) {
1357 return (EPERM);
1358 }
1359 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1360 bzero(iterator, sizeof(*iterator));
1361
1362 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1363 if (result)
1364 goto out;
1365
1366 dataptr = (HFSPlusAttrData *)&attrdata[0];
1367 datasize = sizeof(attrdata);
1368
1369 btdata.bufferAddress = dataptr;
1370 btdata.itemSize = sizeof(attrdata);
1371 btdata.itemCount = 1;
1372
1373 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1374
1375 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
1376 if (result)
1377 goto out;
1378
1379 if (dataptr->attrSize < 3) {
1380 result = ENOENT;
1381 goto out;
1382 }
1383 *firstlink = strtoul((char*)&dataptr->attrData[0], NULL, 10);
1384 out:
1385 FREE(iterator, M_TEMP);
1386
1387 return MacToVFSError(result);
1388 }
1389