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