]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-1228.12.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 1999-2007 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
A
85 if (linknum == 0) {
86 printf("createindirectlink: linknum is zero!\n");
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;
380 int lockflags = 0;
381 int intrans = 0;
91447636 382 enum vtype v_type;
2d21ac55
A
383 int error, ret;
384
385 hfsmp = VTOHFS(vp);
386 v_type = vnode_vtype(vp);
1c79356b 387
2d21ac55
A
388 /* No hard links in HFS standard file systems. */
389 if (hfsmp->hfs_flags & HFS_STANDARD) {
390 return (ENOTSUP);
91447636 391 }
2d21ac55
A
392 /* Linking to a special file is not permitted. */
393 if (v_type == VBLK || v_type == VCHR) {
394 return (EPERM);
1c79356b 395 }
2d21ac55
A
396 if (v_type == VDIR) {
397 /* Make sure our private directory exists. */
398 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid == 0) {
399 return (EPERM);
400 }
401 /*
402 * Directory hardlinks (ADLs) have only been qualified on
403 * journaled HFS+. If/when they are tested on non-journaled
404 * file systems then this test can be removed.
405 */
406 if (hfsmp->jnl == NULL) {
407 return (EPERM);
408 }
409 /* Directory hardlinks also need the parent of the original directory. */
410 if ((error = hfs_vget(hfsmp, hfs_currentparent(VTOC(vp)), &fdvp, 1))) {
411 return (error);
412 }
413 } else {
414 /* Make sure our private directory exists. */
415 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
416 return (ENOTSUP);
417 }
91447636 418 }
2d21ac55
A
419 if (hfs_freeblks(hfsmp, 0) == 0) {
420 if (fdvp) {
421 vnode_put(fdvp);
422 }
423 return (ENOSPC);
424 }
425 /* Lock the cnodes. */
426 if (fdvp) {
427 if ((error = hfs_lockfour(VTOC(tdvp), VTOC(vp), VTOC(fdvp), NULL, HFS_EXCLUSIVE_LOCK))) {
428 if (fdvp) {
429 vnode_put(fdvp);
430 }
431 return (error);
432 }
433 fdcp = VTOC(fdvp);
434 } else {
435 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
436 return (error);
437 }
91447636 438 }
9bccf70c 439 tdcp = VTOC(tdvp);
91447636 440 cp = VTOC(vp);
c910b4d9
A
441
442 /*
443 * Make sure we don't race the src or dst parent directories with rmdir.
444 * Note that we should only have a src parent directory cnode lock
445 * if we're dealing with a directory hardlink here.
446 */
447 if (fdcp) {
448 if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
449 error = ENOENT;
450 goto out;
451 }
452 }
453
454 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
455 error = ENOENT;
456 goto out;
457 }
458
459 /* Check src for errors: too many links, immutable, race with unlink */
2d21ac55 460 if (cp->c_linkcount >= HFS_LINK_MAX) {
1c79356b 461 error = EMLINK;
91447636 462 goto out;
1c79356b 463 }
9bccf70c 464 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b 465 error = EPERM;
91447636 466 goto out;
1c79356b 467 }
91447636 468 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
2d21ac55 469 error = ENOENT;
91447636 470 goto out;
1c79356b
A
471 }
472
2d21ac55
A
473 tdcp->c_flag |= C_DIR_MODIFICATION;
474
91447636 475 if (hfs_start_transaction(hfsmp) != 0) {
2d21ac55
A
476 error = EINVAL;
477 goto out;
b4c24cb9 478 }
2d21ac55 479 intrans = 1;
b4c24cb9 480
2d21ac55
A
481 todesc.cd_flags = (v_type == VDIR) ? CD_ISDIR : 0;
482 todesc.cd_encoding = 0;
483 todesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
484 todesc.cd_namelen = cnp->cn_namelen;
485 todesc.cd_parentcnid = tdcp->c_fileid;
486 todesc.cd_hint = 0;
487 todesc.cd_cnid = 0;
b4c24cb9 488
2d21ac55
A
489 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
490
491 /* If destination exists then we lost a race with create. */
492 if (cat_lookup(hfsmp, &todesc, 0, NULL, NULL, NULL, NULL) == 0) {
493 error = EEXIST;
494 goto out;
495 }
496 if (cp->c_flag & C_HARDLINK) {
497 struct cat_attr cattr;
498
499 /* If inode is missing then we lost a race with unlink. */
500 if ((cat_idlookup(hfsmp, cp->c_fileid, 0, NULL, &cattr, NULL) != 0) ||
501 (cattr.ca_fileid != cp->c_fileid)) {
502 error = ENOENT;
503 goto out;
504 }
505 } else {
506 cnid_t fileid;
507
508 /* If source is missing then we lost a race with unlink. */
509 if ((cat_lookup(hfsmp, &cp->c_desc, 0, NULL, NULL, NULL, &fileid) != 0) ||
510 (fileid != cp->c_fileid)) {
511 error = ENOENT;
512 goto out;
513 }
514 }
515 /*
516 * All directory links must reside in an non-ARCHIVED hierarchy.
517 */
518 if (v_type == VDIR) {
519 /*
520 * - Source parent and destination parent cannot match
521 * - A link is not permitted in the root directory
522 * - Parent of 'pointed at' directory is not the root directory
523 * - The 'pointed at' directory (source) is not an ancestor
524 * of the new directory hard link (destination).
525 * - No ancestor of the new directory hard link (destination)
526 * is a directory hard link.
527 */
528 if ((cp->c_parentcnid == tdcp->c_fileid) ||
529 (tdcp->c_fileid == kHFSRootFolderID) ||
530 (cp->c_parentcnid == kHFSRootFolderID) ||
531 cat_check_link_ancestry(hfsmp, tdcp->c_fileid, cp->c_fileid)) {
532 error = EPERM; /* abide by the rules, you did not */
533 goto out;
534 }
535 }
536 hfs_systemfile_unlock(hfsmp, lockflags);
537 lockflags = 0;
538
539 cp->c_linkcount++;
540 cp->c_touch_chgtime = TRUE;
91447636 541 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
1c79356b 542 if (error) {
2d21ac55 543 cp->c_linkcount--;
91447636 544 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
9bccf70c 545 } else {
91447636 546 /* Invalidate negative cache entries in the destination directory */
2d21ac55 547 if (tdcp->c_flag & C_NEG_ENTRIES) {
91447636 548 cache_purge_negatives(tdvp);
2d21ac55
A
549 tdcp->c_flag &= ~C_NEG_ENTRIES;
550 }
91447636 551
9bccf70c 552 /* Update the target directory and volume stats */
9bccf70c 553 tdcp->c_entries++;
2d21ac55
A
554 if (v_type == VDIR) {
555 INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
556 tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
557
558 /* Set kHFSHasChildLinkBit in the destination hierarchy */
559 error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
560 if (error) {
561 printf ("hfs_vnop_link: error updating destination parent chain for %u\n", tdcp->c_cnid);
562 error = 0;
563 }
564 }
565 tdcp->c_dirchangecnt++;
91447636
A
566 tdcp->c_touch_chgtime = TRUE;
567 tdcp->c_touch_modtime = TRUE;
568 tdcp->c_flag |= C_FORCEUPDATE;
569
570 error = hfs_update(tdvp, 0);
2d21ac55
A
571 if (error && error != EIO && error != ENXIO) {
572 panic("hfs_vnop_link: error updating tdvp %p\n", tdvp);
91447636 573 }
2d21ac55
A
574
575 if ((v_type == VDIR) &&
576 (fdcp != NULL) &&
577 ((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) {
b4c24cb9 578
2d21ac55
A
579 fdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
580 fdcp->c_touch_chgtime = TRUE;
581 fdcp->c_flag |= C_FORCEUPDATE;
582 error = hfs_update(fdvp, 0);
583 if (error && error != EIO && error != ENXIO) {
584 panic("hfs_vnop_link: error updating fdvp %p\n", fdvp);
585 }
586
587 /* Set kHFSHasChildLinkBit in the source hierarchy */
588 error = cat_set_childlinkbit(hfsmp, fdcp->c_parentcnid);
589 if (error) {
590 printf ("hfs_vnop_link: error updating source parent chain for %u\n", fdcp->c_cnid);
591 error = 0;
592 }
593 }
b4c24cb9 594 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 595 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 596 }
2d21ac55
A
597 /* Make sure update occurs inside transaction */
598 cp->c_flag |= C_FORCEUPDATE;
b4c24cb9 599
2d21ac55
A
600 if ((error == 0) && (ret = hfs_update(vp, TRUE)) != 0 && ret != EIO && ret != ENXIO) {
601 panic("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
55e303ae 602 }
91447636 603
55e303ae
A
604 HFS_KNOTE(vp, NOTE_LINK);
605 HFS_KNOTE(tdvp, NOTE_WRITE);
91447636 606out:
2d21ac55
A
607 if (lockflags) {
608 hfs_systemfile_unlock(hfsmp, lockflags);
609 }
610 if (intrans) {
611 hfs_end_transaction(hfsmp);
612 }
613
614 tdcp->c_flag &= ~C_DIR_MODIFICATION;
615 wakeup((caddr_t)&tdcp->c_flag);
616
617 if (fdcp) {
618 hfs_unlockfour(tdcp, cp, fdcp, NULL);
619 } else {
620 hfs_unlockpair(tdcp, cp);
621 }
622 if (fdvp) {
623 vnode_put(fdvp);
624 }
1c79356b
A
625 return (error);
626}
2d21ac55
A
627
628
629/*
630 * Remove a link to a hardlink file/dir.
631 *
632 * Note: dvp and vp cnodes are already locked.
633 */
634__private_extern__
635int
636hfs_unlink(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve)
637{
638 struct cnode *cp;
639 struct cnode *dcp;
640 struct cat_desc cndesc;
641 struct timeval tv;
642 char inodename[32];
643 cnid_t prevlinkid;
644 cnid_t nextlinkid;
645 int lockflags = 0;
646 int started_tr;
647 int rm_priv_file = 0;
648 int error;
649
650 if (hfsmp->hfs_flags & HFS_STANDARD) {
651 return (EPERM);
652 }
653 cp = VTOC(vp);
654 dcp = VTOC(dvp);
655
656 dcp->c_flag |= C_DIR_MODIFICATION;
657
658 /* Remove the entry from the namei cache: */
659 cache_purge(vp);
660
661 if ((error = hfs_start_transaction(hfsmp)) != 0) {
662 started_tr = 0;
663 goto out;
664 }
665 started_tr = 1;
666
667 /*
668 * Protect against a race with rename by using the component
669 * name passed in and parent id from dvp (instead of using
670 * the cp->c_desc which may have changed).
671 *
672 * Re-lookup the component name so we get the correct cnid
673 * for the name (as opposed to the c_cnid in the cnode which
674 * could have changed before the cnode was locked).
675 */
676 cndesc.cd_flags = vnode_isdir(vp) ? CD_ISDIR : 0;
677 cndesc.cd_encoding = cp->c_desc.cd_encoding;
678 cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
679 cndesc.cd_namelen = cnp->cn_namelen;
680 cndesc.cd_parentcnid = dcp->c_fileid;
681 cndesc.cd_hint = dcp->c_childhint;
682
683 lockflags = SFL_CATALOG | SFL_ATTRIBUTE;
684 if (cndesc.cd_flags & CD_ISDIR) {
685 /* We'll be removing the alias resource allocation blocks. */
686 lockflags |= SFL_BITMAP;
687 }
688 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
689
690 if ((error = cat_lookuplink(hfsmp, &cndesc, &cndesc.cd_cnid, &prevlinkid, &nextlinkid))) {
691 goto out;
692 }
693
694 /* Reserve some space in the catalog file. */
695 if (!skip_reserve && (error = cat_preflight(hfsmp, 2 * CAT_DELETE, NULL, 0))) {
696 goto out;
697 }
698
935ed37a
A
699 /* Purge any cached origin entries for a directory or file hard link. */
700 hfs_relorigin(cp, dcp->c_fileid);
701 if (dcp->c_fileid != dcp->c_cnid) {
702 hfs_relorigin(cp, dcp->c_cnid);
2d21ac55
A
703 }
704
705 /* Delete the link record. */
706 if ((error = cat_deletelink(hfsmp, &cndesc))) {
707 goto out;
708 }
709
710 /* Update the parent directory. */
711 if (dcp->c_entries > 0) {
712 dcp->c_entries--;
713 }
714 if (cndesc.cd_flags & CD_ISDIR) {
715 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
716 }
717 dcp->c_dirchangecnt++;
718 microtime(&tv);
719 dcp->c_ctime = tv.tv_sec;
720 dcp->c_mtime = tv.tv_sec;
721 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
722
723 /*
724 * If this is the last link then we need to process the inode.
725 * Otherwise we need to fix up the link chain.
726 */
727 --cp->c_linkcount;
728 if (cp->c_linkcount < 1) {
729 char delname[32];
730 struct cat_desc to_desc;
731 struct cat_desc from_desc;
732
733 /*
734 * If a file inode or directory inode is being deleted, rename
735 * it to an open deleted file. This ensures that deletion
736 * of inode and its corresponding extended attributes does
737 * not overflow the journal. This inode will be deleted
738 * either in hfs_vnop_inactive() or in hfs_remove_orphans().
739 * Note: a rename failure here is not fatal.
740 */
741 bzero(&from_desc, sizeof(from_desc));
742 bzero(&to_desc, sizeof(to_desc));
743 if (vnode_isdir(vp)) {
744 if (cp->c_entries != 0) {
745 panic("hfs_unlink: dir not empty (id %d, %d entries)", cp->c_fileid, cp->c_entries);
746 }
747 MAKE_DIRINODE_NAME(inodename, sizeof(inodename),
748 cp->c_attr.ca_linkref);
749 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
750 from_desc.cd_flags = CD_ISDIR;
751 to_desc.cd_flags = CD_ISDIR;
752 } else {
753 MAKE_INODE_NAME(inodename, sizeof(inodename),
754 cp->c_attr.ca_linkref);
755 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
756 from_desc.cd_flags = 0;
757 to_desc.cd_flags = 0;
758 }
759 from_desc.cd_nameptr = (const u_int8_t *)inodename;
760 from_desc.cd_namelen = strlen(inodename);
761 from_desc.cd_cnid = cp->c_fileid;
762
763 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
764 to_desc.cd_nameptr = (const u_int8_t *)delname;
765 to_desc.cd_namelen = strlen(delname);
766 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
767 to_desc.cd_cnid = cp->c_fileid;
768
769 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
770 &to_desc, (struct cat_desc *)NULL);
771 if (error == 0) {
772 cp->c_flag |= C_DELETED;
773 cp->c_attr.ca_recflags &= ~kHFSHasLinkChainMask;
774 cp->c_attr.ca_firstlink = 0;
775 if (vnode_isdir(vp)) {
776 hfsmp->hfs_private_attr[DIR_HARDLINKS].ca_entries--;
777 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[DIR_HARDLINKS]);
778
779 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
780 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
781
782 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[DIR_HARDLINKS],
783 &hfsmp->hfs_private_attr[DIR_HARDLINKS], NULL, NULL);
784 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
785 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
786 }
787 } else {
788 error = 0; /* rename failure here is not fatal */
789 }
790 } else /* Still some links left */ {
791 cnid_t firstlink;
792
793 /*
794 * Update the start of the link chain.
795 * Note: Directory hard links store the first link in an attribute.
796 */
797 if (vnode_isdir(vp) &&
798 getfirstlink(hfsmp, cp->c_fileid, &firstlink) == 0 &&
799 firstlink == cndesc.cd_cnid) {
800 if (setfirstlink(hfsmp, cp->c_fileid, nextlinkid) == 0)
801 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
802 } else if (vnode_isreg(vp) && cp->c_attr.ca_firstlink == cndesc.cd_cnid) {
803 cp->c_attr.ca_firstlink = nextlinkid;
804 }
805 /* Update previous link. */
806 if (prevlinkid) {
807 (void) cat_updatelink(hfsmp, prevlinkid, HFS_IGNORABLE_LINK, nextlinkid);
808 }
809 /* Update next link. */
810 if (nextlinkid) {
811 (void) cat_updatelink(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK);
812 }
813 }
814
815 /* Push new link count to disk. */
816 cp->c_ctime = tv.tv_sec;
817 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
818
819 /* All done with the system files. */
820 hfs_systemfile_unlock(hfsmp, lockflags);
821 lockflags = 0;
822
823 /* Update file system stats. */
824 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
825 /* The last link of a directory removed the inode. */
826 if (rm_priv_file) {
827 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
828 }
829 /*
830 * All done with this cnode's descriptor...
831 *
832 * Note: all future catalog calls for this cnode may be
833 * by fileid only. This is OK for HFS (which doesn't have
834 * file thread records) since HFS doesn't support hard links.
835 */
836 cat_releasedesc(&cp->c_desc);
837
838 HFS_KNOTE(dvp, NOTE_WRITE);
839 HFS_KNOTE(vp, NOTE_DELETE);
840out:
841 if (lockflags) {
842 hfs_systemfile_unlock(hfsmp, lockflags);
843 }
844 if (started_tr) {
845 hfs_end_transaction(hfsmp);
846 }
847
848 dcp->c_flag &= ~C_DIR_MODIFICATION;
849 wakeup((caddr_t)&dcp->c_flag);
850
851 return (error);
852}
853
854
855/*
856 * Initialize the HFS+ private system directories.
857 *
858 * These directories are used to hold the inodes
859 * for file and directory hardlinks as well as
860 * open-unlinked files.
861 *
862 * If they don't yet exist they will get created.
863 *
864 * This call is assumed to be made during mount.
865 */
866__private_extern__
867void
868hfs_privatedir_init(struct hfsmount * hfsmp, enum privdirtype type)
869{
870 struct vnode * dvp = NULLVP;
871 struct cnode * dcp = NULL;
872 struct cat_desc *priv_descp;
873 struct cat_attr *priv_attrp;
874 struct FndrDirInfo * fndrinfo;
875 struct timeval tv;
876 int lockflags;
877 int trans = 0;
878 int error;
879
880 if (hfsmp->hfs_flags & HFS_STANDARD) {
881 return;
882 }
883
884 priv_descp = &hfsmp->hfs_private_desc[type];
885 priv_attrp = &hfsmp->hfs_private_attr[type];
886
887 /* Check if directory already exists. */
888 if (priv_descp->cd_cnid != 0) {
889 return;
890 }
891
892 priv_descp->cd_parentcnid = kRootDirID;
893 priv_descp->cd_nameptr = (const u_int8_t *)hfs_private_names[type];
894 priv_descp->cd_namelen = strlen((const char *)priv_descp->cd_nameptr);
895 priv_descp->cd_flags = CD_ISDIR | CD_DECOMPOSED;
896
897 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
898 error = cat_lookup(hfsmp, priv_descp, 0, NULL, priv_attrp, NULL, NULL);
899 hfs_systemfile_unlock(hfsmp, lockflags);
900
901 if (error == 0) {
902 if (type == FILE_HARDLINKS) {
903 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
904 }
905 priv_descp->cd_cnid = priv_attrp->ca_fileid;
906 goto exit;
907 }
908
909 /* Directory is missing, if this is read-only then we're done. */
910 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
911 goto exit;
912 }
913
914 /* Grab the root directory so we can update it later. */
915 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0) != 0) {
916 goto exit;
917 }
918 dcp = VTOC(dvp);
919
920 /* Setup the default attributes */
921 bzero(priv_attrp, sizeof(struct cat_attr));
922 priv_attrp->ca_flags = UF_IMMUTABLE | UF_HIDDEN;
923 priv_attrp->ca_mode = S_IFDIR;
924 if (type == DIR_HARDLINKS) {
925 priv_attrp->ca_mode |= S_ISVTX | S_IRUSR | S_IXUSR | S_IRGRP |
926 S_IXGRP | S_IROTH | S_IXOTH;
927 }
928 priv_attrp->ca_linkcount = 1;
929 priv_attrp->ca_itime = hfsmp->hfs_itime;
930 priv_attrp->ca_recflags = kHFSHasFolderCountMask;
931
932 fndrinfo = (struct FndrDirInfo *)&priv_attrp->ca_finderinfo;
933 fndrinfo->frLocation.v = SWAP_BE16(16384);
934 fndrinfo->frLocation.h = SWAP_BE16(16384);
935 fndrinfo->frFlags = SWAP_BE16(kIsInvisible + kNameLocked);
936
937 if (hfs_start_transaction(hfsmp) != 0) {
938 goto exit;
939 }
940 trans = 1;
941
942 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
943
944 /* Make sure there's space in the Catalog file. */
945 if (cat_preflight(hfsmp, CAT_CREATE, NULL, 0) != 0) {
946 hfs_systemfile_unlock(hfsmp, lockflags);
947 goto exit;
948 }
949
950 /* Create the private directory on disk. */
951 error = cat_create(hfsmp, priv_descp, priv_attrp, NULL);
952 if (error == 0) {
953 priv_descp->cd_cnid = priv_attrp->ca_fileid;
954
955 /* Update the parent directory */
956 dcp->c_entries++;
957 INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
958 dcp->c_dirchangecnt++;
959 microtime(&tv);
960 dcp->c_ctime = tv.tv_sec;
961 dcp->c_mtime = tv.tv_sec;
962 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
963 }
964
965 hfs_systemfile_unlock(hfsmp, lockflags);
966
967 if (error) {
968 goto exit;
969 }
970 if (type == FILE_HARDLINKS) {
971 hfsmp->hfs_metadata_createdate = hfsmp->hfs_itime;
972 }
973 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
974exit:
975 if (trans) {
976 hfs_end_transaction(hfsmp);
977 }
978 if (dvp) {
979 hfs_unlock(dcp);
980 vnode_put(dvp);
981 }
982 if ((error == 0) && (type == DIR_HARDLINKS)) {
983 hfs_xattr_init(hfsmp);
984 }
985}
986
987
988/*
989 * Lookup a hardlink link (from chain)
990 */
991__private_extern__
992int
993hfs_lookuplink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
994{
995 int lockflags;
996 int error;
997
998 *prevlinkid = 0;
999 *nextlinkid = 0;
1000
1001 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1002
1003 error = cat_lookuplinkbyid(hfsmp, linkfileid, prevlinkid, nextlinkid);
1004 if (error == ENOLINK) {
1005 hfs_systemfile_unlock(hfsmp, lockflags);
1006 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1007
1008 error = getfirstlink(hfsmp, linkfileid, nextlinkid);
1009 }
1010 hfs_systemfile_unlock(hfsmp, lockflags);
1011
1012 return (error);
1013}
1014
1015/*
935ed37a 1016 * Cache the origin of a directory or file hard link
2d21ac55
A
1017 *
1018 * cnode must be lock on entry
1019 */
1020__private_extern__
1021void
1022hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid)
1023{
1024 linkorigin_t *origin = NULL;
1025 void * thread = current_thread();
1026 int count = 0;
935ed37a 1027 int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS;
2d21ac55
A
1028
1029 /*
1030 * Look for an existing origin first. If not found, create/steal one.
1031 */
1032 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1033 ++count;
1034 if (origin->lo_thread == thread) {
1035 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1036 break;
1037 }
1038 }
1039 if (origin == NULL) {
1040 /* Recycle the last (i.e., the oldest) if we have too many. */
935ed37a 1041 if (count > maxorigins) {
2d21ac55
A
1042 origin = TAILQ_LAST(&cp->c_originlist, hfs_originhead);
1043 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1044 } else {
1045 MALLOC(origin, linkorigin_t *, sizeof(linkorigin_t), M_TEMP, M_WAITOK);
1046 }
1047 origin->lo_thread = thread;
1048 }
1049 origin->lo_cnid = cp->c_cnid;
1050 origin->lo_parentcnid = parentcnid;
1051 TAILQ_INSERT_HEAD(&cp->c_originlist, origin, lo_link);
1052}
1053
1054/*
935ed37a 1055 * Release any cached origins for a directory or file hard link
2d21ac55
A
1056 *
1057 * cnode must be lock on entry
1058 */
1059__private_extern__
1060void
1061hfs_relorigins(struct cnode *cp)
1062{
1063 linkorigin_t *origin, *prev;
1064
1065 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
1066 FREE(origin, M_TEMP);
1067 }
1068 TAILQ_INIT(&cp->c_originlist);
1069}
1070
1071/*
935ed37a 1072 * Release a specific origin for a directory or file hard link
2d21ac55
A
1073 *
1074 * cnode must be lock on entry
1075 */
1076__private_extern__
1077void
1078hfs_relorigin(struct cnode *cp, cnid_t parentcnid)
1079{
4a3eedf9 1080 linkorigin_t *origin, *prev;
2d21ac55
A
1081 void * thread = current_thread();
1082
4a3eedf9 1083 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
2d21ac55
A
1084 if ((origin->lo_thread == thread) ||
1085 (origin->lo_parentcnid == parentcnid)) {
1086 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
4a3eedf9 1087 FREE(origin, M_TEMP);
2d21ac55
A
1088 break;
1089 }
1090 }
1091}
1092
1093/*
935ed37a 1094 * Test if a directory or file hard link has a cached origin
2d21ac55
A
1095 *
1096 * cnode must be lock on entry
1097 */
1098__private_extern__
1099int
1100hfs_haslinkorigin(cnode_t *cp)
1101{
1102 if (cp->c_flag & C_HARDLINK) {
1103 linkorigin_t *origin;
1104 void * thread = current_thread();
1105
1106 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1107 if (origin->lo_thread == thread) {
1108 return (1);
1109 }
1110 }
1111 }
1112 return (0);
1113}
1114
1115/*
935ed37a 1116 * Obtain the current parent cnid of a directory or file hard link
2d21ac55
A
1117 *
1118 * cnode must be lock on entry
1119 */
1120__private_extern__
1121cnid_t
1122hfs_currentparent(cnode_t *cp)
1123{
1124 if (cp->c_flag & C_HARDLINK) {
1125 linkorigin_t *origin;
1126 void * thread = current_thread();
1127
1128 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1129 if (origin->lo_thread == thread) {
1130 return (origin->lo_parentcnid);
1131 }
1132 }
1133 }
1134 return (cp->c_parentcnid);
1135}
1136
1137/*
935ed37a 1138 * Obtain the current cnid of a directory or file hard link
2d21ac55
A
1139 *
1140 * cnode must be lock on entry
1141 */
1142__private_extern__
1143cnid_t
1144hfs_currentcnid(cnode_t *cp)
1145{
1146 if (cp->c_flag & C_HARDLINK) {
1147 linkorigin_t *origin;
1148 void * thread = current_thread();
1149
1150 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1151 if (origin->lo_thread == thread) {
1152 return (origin->lo_cnid);
1153 }
1154 }
1155 }
1156 return (cp->c_cnid);
1157}
1158
1159
1160/*
1161 * Set the first link attribute for a given file id.
1162 *
1163 * The attributes b-tree must already be locked.
1164 * If journaling is enabled, a transaction must already be started.
1165 */
1166static int
1167setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink)
1168{
1169 FCB * btfile;
1170 BTreeIterator * iterator;
1171 FSBufferDescriptor btdata;
1172 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1173 HFSPlusAttrData *dataptr;
1174 int result;
1175 u_int16_t datasize;
1176
1177 if (hfsmp->hfs_attribute_cp == NULL) {
1178 return (EPERM);
1179 }
1180 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1181 bzero(iterator, sizeof(*iterator));
1182
1183 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1184 if (result) {
1185 goto out;
1186 }
1187 dataptr = (HFSPlusAttrData *)&attrdata[0];
1188 dataptr->recordType = kHFSPlusAttrInlineData;
1189 dataptr->reserved[0] = 0;
1190 dataptr->reserved[1] = 0;
1191
1192 /*
1193 * Since attrData is variable length, we calculate the size of
1194 * attrData by subtracting the size of all other members of
1195 * structure HFSPlusAttData from the size of attrdata.
1196 */
1197 (void)snprintf((char *)&dataptr->attrData[0],
1198 sizeof(dataptr) - (4 * sizeof(uint32_t)),
1199 "%lu", (unsigned long)firstlink);
1200 dataptr->attrSize = 1 + strlen((char *)&dataptr->attrData[0]);
1201
1202 /* Calculate size of record rounded up to multiple of 2 bytes. */
1203 datasize = sizeof(HFSPlusAttrData) - 2 + dataptr->attrSize + ((dataptr->attrSize & 1) ? 1 : 0);
1204
1205 btdata.bufferAddress = dataptr;
1206 btdata.itemSize = datasize;
1207 btdata.itemCount = 1;
1208
1209 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1210
1211 /* Insert the attribute. */
1212 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
1213 if (result == btExists) {
1214 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
1215 }
1216 (void) BTFlushPath(btfile);
1217out:
1218 FREE(iterator, M_TEMP);
1219
1220 return MacToVFSError(result);
1221}
1222
1223/*
1224 * Get the first link attribute for a given file id.
1225 *
1226 * The attributes b-tree must already be locked.
1227 */
1228static int
1229getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink)
1230{
1231 FCB * btfile;
1232 BTreeIterator * iterator;
1233 FSBufferDescriptor btdata;
1234 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1235 HFSPlusAttrData *dataptr;
1236 int result;
1237 u_int16_t datasize;
1238
1239 if (hfsmp->hfs_attribute_cp == NULL) {
1240 return (EPERM);
1241 }
1242 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1243 bzero(iterator, sizeof(*iterator));
1244
1245 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1246 if (result)
1247 goto out;
1248
1249 dataptr = (HFSPlusAttrData *)&attrdata[0];
1250 datasize = sizeof(attrdata);
1251
1252 btdata.bufferAddress = dataptr;
1253 btdata.itemSize = sizeof(attrdata);
1254 btdata.itemCount = 1;
1255
1256 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1257
1258 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
1259 if (result)
1260 goto out;
1261
1262 if (dataptr->attrSize < 3) {
1263 result = ENOENT;
1264 goto out;
1265 }
1266 *firstlink = strtoul((char*)&dataptr->attrData[0], NULL, 10);
1267out:
1268 FREE(iterator, M_TEMP);
1269
1270 return MacToVFSError(result);
1271}
1272