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
46static int cur_link_id = 0;
47
48/*
49 * Private directories where hardlink inodes reside.
50 */
51const 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 */
62static int  setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink);
63static int  getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink);
64
65int 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 */
81static int
82createindirectlink(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 */
133int
134hfs_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	}
396out:
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 */
416int
417hfs_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
685out:
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 */
713int
714hfs_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
927out:
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 */
953void
954hfs_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);
1069exit:
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 */
1086int
1087hfs_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 */
1111int
1112hfs_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__
1139void
1140hfs_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__
1177void
1178hfs_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__
1194void
1195hfs_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__
1216int
1217hfs_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__
1238cnid_t
1239hfs_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__
1260cnid_t
1261hfs_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 */
1283static int
1284setfirstlink(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);
1334out:
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 */
1345static int
1346getfirstlink(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);
1384out:
1385	FREE(iterator, M_TEMP);
1386
1387	return MacToVFSError(result);
1388}
1389
1390