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