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