]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
db609669 2 * Copyright (c) 1999-2013 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>
6d2010ae 38#include <sys/fsctl.h>
1c79356b
A
39
40#include "hfs.h"
9bccf70c
A
41#include "hfs_catalog.h"
42#include "hfs_format.h"
43#include "hfs_endian.h"
1c79356b
A
44
45
91447636
A
46static int cur_link_id = 0;
47
2d21ac55
A
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);
91447636 64
6d2010ae
A
65int hfs_makelink(struct hfsmount *hfsmp, struct vnode *src_vp, struct cnode *cp,
66 struct cnode *dcp, struct componentname *cnp);
1c79356b 67/*
2d21ac55
A
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.
1c79356b 75 *
2d21ac55
A
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.
1c79356b
A
80 */
81static int
2d21ac55
A
82createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum, struct cat_desc *descp,
83 cnid_t nextcnid, cnid_t *linkcnid, int is_inode_linkchain_set)
1c79356b 84{
9bccf70c 85 struct FndrFileInfo *fip;
9bccf70c 86 struct cat_attr attr;
1c79356b 87
2d21ac55 88 if (linknum == 0) {
b0d623f7 89 printf("hfs: createindirectlink: linknum is zero!\n");
2d21ac55
A
90 return (EINVAL);
91 }
1c79356b 92
9bccf70c
A
93 /* Setup the default attributes */
94 bzero(&attr, sizeof(attr));
95
2d21ac55
A
96 /* Links are matched to inodes by link ID and to volumes by create date */
97 attr.ca_linkref = linknum;
6d2010ae 98 attr.ca_itime = hfsmp->hfs_metadata_createdate;
2d21ac55
A
99 attr.ca_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
100 attr.ca_recflags = kHFSHasLinkChainMask | kHFSThreadExistsMask;
101 attr.ca_flags = UF_IMMUTABLE;
9bccf70c 102 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
1c79356b 103
2d21ac55
A
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 }
9bccf70c 120 /* Create the indirect link directly in the catalog */
2d21ac55 121 return cat_createlink(hfsmp, descp, &attr, nextcnid, linkcnid);
1c79356b
A
122}
123
124
125/*
2d21ac55 126 * Make a link to the cnode cp in the directory dp
6d2010ae
A
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.
1c79356b 130 *
2d21ac55 131 * The cnodes cp and dcp must be locked.
1c79356b 132 */
6d2010ae
A
133int
134hfs_makelink(struct hfsmount *hfsmp, struct vnode *src_vp, struct cnode *cp,
135 struct cnode *dcp, struct componentname *cnp)
1c79356b 136{
91447636
A
137 vfs_context_t ctx = cnp->cn_context;
138 struct proc *p = vfs_context_proc(ctx);
1c79356b 139 u_int32_t indnodeno = 0;
91447636 140 char inodename[32];
9bccf70c 141 struct cat_desc to_desc;
2d21ac55 142 struct cat_desc link_desc;
9bccf70c 143 int newlink = 0;
91447636 144 int lockflags;
2d21ac55 145 int retval = 0;
91447636
A
146 cat_cookie_t cookie;
147 cnid_t orig_cnid;
2d21ac55
A
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;
1c79356b 153
91447636 154 if (cur_link_id == 0) {
2d21ac55 155 cur_link_id = ((random() & 0x3fffffff) + 100);
91447636
A
156 }
157
2d21ac55
A
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) {
1c79356b 161 return (EPERM);
2d21ac55 162 }
1c79356b 163
91447636 164 bzero(&cookie, sizeof(cat_cookie_t));
55e303ae
A
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
2d21ac55
A
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);
91447636 176
2d21ac55 177 /* Save the current cnid value so we restore it if an error occurs. */
91447636 178 orig_cnid = cp->c_desc.cd_cnid;
1c79356b
A
179
180 /*
2d21ac55
A
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.
1c79356b 183 */
2d21ac55 184 if ((cp->c_linkcount == 2) && !(cp->c_flag & C_HARDLINK)) {
9bccf70c
A
185 newlink = 1;
186 bzero(&to_desc, sizeof(to_desc));
2d21ac55 187 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[type].cd_cnid;
9bccf70c 188 to_desc.cd_cnid = cp->c_fileid;
2d21ac55 189 to_desc.cd_flags = (type == DIR_HARDLINKS) ? CD_ISDIR : 0;
b4c24cb9 190
1c79356b 191 do {
2d21ac55
A
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);
91447636 197 } else {
2d21ac55
A
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);
91447636 206 }
2d21ac55
A
207 /* Move original file/dir to data node directory */
208 to_desc.cd_nameptr = (const u_int8_t *)inodename;
9bccf70c
A
209 to_desc.cd_namelen = strlen(inodename);
210
2d21ac55 211 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_private_desc[type],
9bccf70c 212 &to_desc, NULL);
1c79356b 213
91447636 214 if (retval != 0 && retval != EEXIST) {
39236c6e
A
215 printf("hfs_makelink: cat_rename to %s failed (%d) fileid=%d, vol=%s\n",
216 inodename, retval, cp->c_fileid, hfsmp->vcbVN);
91447636 217 }
2d21ac55 218 } while ((retval == EEXIST) && (type == FILE_HARDLINKS));
9bccf70c
A
219 if (retval)
220 goto out;
1c79356b 221
2d21ac55
A
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);
1c79356b 233 if (retval) {
2d21ac55 234 int err;
91447636 235
2d21ac55
A
236 /* Restore the cnode's cnid. */
237 cp->c_desc.cd_cnid = orig_cnid;
91447636 238
2d21ac55
A
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;
1c79356b 253 }
2d21ac55 254 cp->c_attr.ca_recflags |= kHFSHasLinkChainMask;
9bccf70c 255 } else {
2d21ac55 256 indnodeno = cp->c_attr.ca_linkref;
9bccf70c 257 }
1c79356b
A
258
259 /*
260 * Create a catalog entry for the new link (parentID + name).
261 */
2d21ac55
A
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));
9bccf70c 279 if (retval && newlink) {
2d21ac55 280 int err;
91447636 281
2d21ac55
A
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);
91447636 292
2d21ac55
A
293 cp->c_attr.ca_linkref = 0;
294 goto out;
295 } else if (retval == 0) {
91447636 296
2d21ac55
A
297 /* Update the original first link to point back to the new first link. */
298 if (cp->c_attr.ca_recflags & kHFSHasLinkChainMask) {
6d2010ae 299 (void) cat_update_siblinglinks(hfsmp, orig_firstlink, linkcnid, HFS_IGNORABLE_LINK);
1c79356b 300
2d21ac55
A
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) {
91447636
A
315 vnode_t vp;
316
91447636 317 if (retval != 0) {
2d21ac55
A
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);
91447636 333 }
91447636 334 cp->c_flag |= C_HARDLINK;
6d2010ae
A
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 */
91447636 353 if ((vp = cp->c_vp) != NULLVP) {
6d2010ae
A
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 }
91447636
A
364 }
365 if ((vp = cp->c_rsrc_vp) != NULLVP) {
6d2010ae
A
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 }
91447636
A
375 }
376 cp->c_touch_chgtime = TRUE;
377 cp->c_flag |= C_FORCEUPDATE;
2d21ac55
A
378 }
379 dcp->c_flag |= C_FORCEUPDATE;
1c79356b 380 }
1c79356b 381out:
91447636
A
382 hfs_systemfile_unlock(hfsmp, lockflags);
383
55e303ae 384 cat_postflight(hfsmp, &cookie, p);
2d21ac55
A
385
386 if (retval == 0 && newlink) {
387 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
388 }
1c79356b
A
389 return (retval);
390}
391
392
393/*
2d21ac55
A
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 */
1c79356b 401int
91447636 402hfs_vnop_link(struct vnop_link_args *ap)
1c79356b 403{
b4c24cb9 404 struct hfsmount *hfsmp;
1c79356b
A
405 struct vnode *vp = ap->a_vp;
406 struct vnode *tdvp = ap->a_tdvp;
2d21ac55 407 struct vnode *fdvp = NULLVP;
1c79356b 408 struct componentname *cnp = ap->a_cnp;
9bccf70c
A
409 struct cnode *cp;
410 struct cnode *tdcp;
2d21ac55
A
411 struct cnode *fdcp = NULL;
412 struct cat_desc todesc;
b0d623f7 413 cnid_t parentcnid;
2d21ac55
A
414 int lockflags = 0;
415 int intrans = 0;
91447636 416 enum vtype v_type;
2d21ac55
A
417 int error, ret;
418
419 hfsmp = VTOHFS(vp);
420 v_type = vnode_vtype(vp);
1c79356b 421
2d21ac55
A
422 /* No hard links in HFS standard file systems. */
423 if (hfsmp->hfs_flags & HFS_STANDARD) {
424 return (ENOTSUP);
91447636 425 }
2d21ac55
A
426 /* Linking to a special file is not permitted. */
427 if (v_type == VBLK || v_type == VCHR) {
428 return (EPERM);
1c79356b 429 }
2d21ac55 430 if (v_type == VDIR) {
39236c6e 431#if CONFIG_HFS_DIRLINK
2d21ac55
A
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. */
6d2010ae 445 if ((error = hfs_vget(hfsmp, hfs_currentparent(VTOC(vp)), &fdvp, 1, 0))) {
2d21ac55
A
446 return (error);
447 }
39236c6e
A
448#else
449 /* some platforms don't support directory hardlinks. */
450 return EPERM;
451#endif
2d21ac55
A
452 } else {
453 /* Make sure our private directory exists. */
454 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0) {
455 return (ENOTSUP);
456 }
91447636 457 }
2d21ac55
A
458 if (hfs_freeblks(hfsmp, 0) == 0) {
459 if (fdvp) {
460 vnode_put(fdvp);
461 }
462 return (ENOSPC);
463 }
6d2010ae
A
464
465 check_for_tracked_file(vp, VTOC(vp)->c_ctime, NAMESPACE_HANDLER_LINK_CREATE, NULL);
466
467
2d21ac55
A
468 /* Lock the cnodes. */
469 if (fdvp) {
b0d623f7 470 if ((error = hfs_lockfour(VTOC(tdvp), VTOC(vp), VTOC(fdvp), NULL, HFS_EXCLUSIVE_LOCK, NULL))) {
2d21ac55
A
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 }
91447636 481 }
9bccf70c 482 tdcp = VTOC(tdvp);
91447636 483 cp = VTOC(vp);
b0d623f7
A
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.
c910b4d9
A
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 }
b0d623f7 498
c910b4d9
A
499 if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
500 error = ENOENT;
501 goto out;
502 }
b0d623f7
A
503
504 /* Check the source for errors:
505 * too many links, immutable, race with unlink
506 */
2d21ac55 507 if (cp->c_linkcount >= HFS_LINK_MAX) {
1c79356b 508 error = EMLINK;
91447636 509 goto out;
1c79356b 510 }
316670eb 511 if (cp->c_bsdflags & (IMMUTABLE | APPEND)) {
1c79356b 512 error = EPERM;
91447636 513 goto out;
1c79356b 514 }
91447636 515 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
2d21ac55 516 error = ENOENT;
91447636 517 goto out;
1c79356b
A
518 }
519
2d21ac55
A
520 tdcp->c_flag |= C_DIR_MODIFICATION;
521
91447636 522 if (hfs_start_transaction(hfsmp) != 0) {
2d21ac55
A
523 error = EINVAL;
524 goto out;
b4c24cb9 525 }
2d21ac55 526 intrans = 1;
b4c24cb9 527
2d21ac55
A
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;
b4c24cb9 535
2d21ac55
A
536 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
537
538 /* If destination exists then we lost a race with create. */
39236c6e 539 if (cat_lookup(hfsmp, &todesc, 0, 0, NULL, NULL, NULL, NULL) == 0) {
2d21ac55
A
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. */
db609669 547 if ((cat_idlookup(hfsmp, cp->c_fileid, 0, 0, NULL, &cattr, NULL) != 0) ||
2d21ac55
A
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. */
39236c6e 556 if ((cat_lookup(hfsmp, &cp->c_desc, 0, 0, NULL, NULL, NULL, &fileid) != 0) ||
2d21ac55
A
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 */
b0d623f7 575 if ((parentcnid == tdcp->c_fileid) ||
2d21ac55 576 (tdcp->c_fileid == kHFSRootFolderID) ||
b0d623f7 577 (parentcnid == kHFSRootFolderID) ||
2d21ac55
A
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;
6d2010ae 588 error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
1c79356b 589 if (error) {
2d21ac55 590 cp->c_linkcount--;
91447636 591 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
9bccf70c 592 } else {
91447636 593 /* Invalidate negative cache entries in the destination directory */
2d21ac55 594 if (tdcp->c_flag & C_NEG_ENTRIES) {
91447636 595 cache_purge_negatives(tdvp);
2d21ac55
A
596 tdcp->c_flag &= ~C_NEG_ENTRIES;
597 }
91447636 598
9bccf70c 599 /* Update the target directory and volume stats */
9bccf70c 600 tdcp->c_entries++;
2d21ac55
A
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) {
39236c6e 608 printf ("hfs_vnop_link: error updating destination parent chain for id=%u, vol=%s\n", tdcp->c_cnid, hfsmp->vcbVN);
2d21ac55
A
609 error = 0;
610 }
611 }
612 tdcp->c_dirchangecnt++;
91447636
A
613 tdcp->c_touch_chgtime = TRUE;
614 tdcp->c_touch_modtime = TRUE;
615 tdcp->c_flag |= C_FORCEUPDATE;
616
617 error = hfs_update(tdvp, 0);
2d21ac55 618 if (error && error != EIO && error != ENXIO) {
b0d623f7 619 panic("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
91447636 620 }
2d21ac55
A
621
622 if ((v_type == VDIR) &&
623 (fdcp != NULL) &&
624 ((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) {
b4c24cb9 625
2d21ac55
A
626 fdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
627 fdcp->c_touch_chgtime = TRUE;
628 fdcp->c_flag |= C_FORCEUPDATE;
629 error = hfs_update(fdvp, 0);
630 if (error && error != EIO && error != ENXIO) {
b0d623f7 631 panic("hfs_vnop_link: error %d updating fdvp %p\n", error, fdvp);
2d21ac55
A
632 }
633
634 /* Set kHFSHasChildLinkBit in the source hierarchy */
635 error = cat_set_childlinkbit(hfsmp, fdcp->c_parentcnid);
636 if (error) {
39236c6e 637 printf ("hfs_vnop_link: error updating source parent chain for id=%u, vol=%s\n", fdcp->c_cnid, hfsmp->vcbVN);
2d21ac55
A
638 error = 0;
639 }
640 }
b4c24cb9 641 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 642 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 643 }
2d21ac55
A
644 /* Make sure update occurs inside transaction */
645 cp->c_flag |= C_FORCEUPDATE;
b4c24cb9 646
2d21ac55
A
647 if ((error == 0) && (ret = hfs_update(vp, TRUE)) != 0 && ret != EIO && ret != ENXIO) {
648 panic("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
55e303ae 649 }
91447636 650
91447636 651out:
2d21ac55
A
652 if (lockflags) {
653 hfs_systemfile_unlock(hfsmp, lockflags);
654 }
655 if (intrans) {
656 hfs_end_transaction(hfsmp);
657 }
658
659 tdcp->c_flag &= ~C_DIR_MODIFICATION;
660 wakeup((caddr_t)&tdcp->c_flag);
661
662 if (fdcp) {
663 hfs_unlockfour(tdcp, cp, fdcp, NULL);
664 } else {
665 hfs_unlockpair(tdcp, cp);
666 }
667 if (fdvp) {
668 vnode_put(fdvp);
669 }
1c79356b
A
670 return (error);
671}
2d21ac55
A
672
673
674/*
675 * Remove a link to a hardlink file/dir.
676 *
677 * Note: dvp and vp cnodes are already locked.
678 */
2d21ac55
A
679int
680hfs_unlink(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int skip_reserve)
681{
682 struct cnode *cp;
683 struct cnode *dcp;
684 struct cat_desc cndesc;
685 struct timeval tv;
686 char inodename[32];
687 cnid_t prevlinkid;
688 cnid_t nextlinkid;
689 int lockflags = 0;
690 int started_tr;
2d21ac55
A
691 int error;
692
693 if (hfsmp->hfs_flags & HFS_STANDARD) {
694 return (EPERM);
695 }
696 cp = VTOC(vp);
697 dcp = VTOC(dvp);
698
699 dcp->c_flag |= C_DIR_MODIFICATION;
700
701 /* Remove the entry from the namei cache: */
702 cache_purge(vp);
703
704 if ((error = hfs_start_transaction(hfsmp)) != 0) {
705 started_tr = 0;
706 goto out;
707 }
708 started_tr = 1;
709
710 /*
711 * Protect against a race with rename by using the component
712 * name passed in and parent id from dvp (instead of using
713 * the cp->c_desc which may have changed).
714 *
715 * Re-lookup the component name so we get the correct cnid
716 * for the name (as opposed to the c_cnid in the cnode which
717 * could have changed before the cnode was locked).
718 */
719 cndesc.cd_flags = vnode_isdir(vp) ? CD_ISDIR : 0;
720 cndesc.cd_encoding = cp->c_desc.cd_encoding;
721 cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
722 cndesc.cd_namelen = cnp->cn_namelen;
723 cndesc.cd_parentcnid = dcp->c_fileid;
724 cndesc.cd_hint = dcp->c_childhint;
725
726 lockflags = SFL_CATALOG | SFL_ATTRIBUTE;
727 if (cndesc.cd_flags & CD_ISDIR) {
728 /* We'll be removing the alias resource allocation blocks. */
729 lockflags |= SFL_BITMAP;
730 }
731 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
732
733 if ((error = cat_lookuplink(hfsmp, &cndesc, &cndesc.cd_cnid, &prevlinkid, &nextlinkid))) {
734 goto out;
735 }
736
737 /* Reserve some space in the catalog file. */
738 if (!skip_reserve && (error = cat_preflight(hfsmp, 2 * CAT_DELETE, NULL, 0))) {
739 goto out;
740 }
741
935ed37a
A
742 /* Purge any cached origin entries for a directory or file hard link. */
743 hfs_relorigin(cp, dcp->c_fileid);
744 if (dcp->c_fileid != dcp->c_cnid) {
745 hfs_relorigin(cp, dcp->c_cnid);
2d21ac55
A
746 }
747
748 /* Delete the link record. */
749 if ((error = cat_deletelink(hfsmp, &cndesc))) {
750 goto out;
751 }
752
753 /* Update the parent directory. */
754 if (dcp->c_entries > 0) {
755 dcp->c_entries--;
756 }
757 if (cndesc.cd_flags & CD_ISDIR) {
758 DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
759 }
760 dcp->c_dirchangecnt++;
761 microtime(&tv);
762 dcp->c_ctime = tv.tv_sec;
763 dcp->c_mtime = tv.tv_sec;
764 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
765
766 /*
767 * If this is the last link then we need to process the inode.
768 * Otherwise we need to fix up the link chain.
769 */
770 --cp->c_linkcount;
771 if (cp->c_linkcount < 1) {
772 char delname[32];
773 struct cat_desc to_desc;
774 struct cat_desc from_desc;
775
776 /*
777 * If a file inode or directory inode is being deleted, rename
778 * it to an open deleted file. This ensures that deletion
779 * of inode and its corresponding extended attributes does
780 * not overflow the journal. This inode will be deleted
781 * either in hfs_vnop_inactive() or in hfs_remove_orphans().
782 * Note: a rename failure here is not fatal.
783 */
784 bzero(&from_desc, sizeof(from_desc));
785 bzero(&to_desc, sizeof(to_desc));
786 if (vnode_isdir(vp)) {
787 if (cp->c_entries != 0) {
788 panic("hfs_unlink: dir not empty (id %d, %d entries)", cp->c_fileid, cp->c_entries);
789 }
790 MAKE_DIRINODE_NAME(inodename, sizeof(inodename),
791 cp->c_attr.ca_linkref);
792 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
793 from_desc.cd_flags = CD_ISDIR;
794 to_desc.cd_flags = CD_ISDIR;
795 } else {
796 MAKE_INODE_NAME(inodename, sizeof(inodename),
797 cp->c_attr.ca_linkref);
798 from_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
799 from_desc.cd_flags = 0;
800 to_desc.cd_flags = 0;
801 }
802 from_desc.cd_nameptr = (const u_int8_t *)inodename;
803 from_desc.cd_namelen = strlen(inodename);
804 from_desc.cd_cnid = cp->c_fileid;
805
806 MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
807 to_desc.cd_nameptr = (const u_int8_t *)delname;
808 to_desc.cd_namelen = strlen(delname);
809 to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
810 to_desc.cd_cnid = cp->c_fileid;
811
812 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
813 &to_desc, (struct cat_desc *)NULL);
814 if (error == 0) {
815 cp->c_flag |= C_DELETED;
816 cp->c_attr.ca_recflags &= ~kHFSHasLinkChainMask;
817 cp->c_attr.ca_firstlink = 0;
818 if (vnode_isdir(vp)) {
819 hfsmp->hfs_private_attr[DIR_HARDLINKS].ca_entries--;
820 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[DIR_HARDLINKS]);
821
822 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
823 INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
824
825 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[DIR_HARDLINKS],
826 &hfsmp->hfs_private_attr[DIR_HARDLINKS], NULL, NULL);
827 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
828 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
829 }
830 } else {
831 error = 0; /* rename failure here is not fatal */
832 }
833 } else /* Still some links left */ {
834 cnid_t firstlink;
835
836 /*
837 * Update the start of the link chain.
838 * Note: Directory hard links store the first link in an attribute.
839 */
840 if (vnode_isdir(vp) &&
841 getfirstlink(hfsmp, cp->c_fileid, &firstlink) == 0 &&
842 firstlink == cndesc.cd_cnid) {
843 if (setfirstlink(hfsmp, cp->c_fileid, nextlinkid) == 0)
844 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
845 } else if (vnode_isreg(vp) && cp->c_attr.ca_firstlink == cndesc.cd_cnid) {
846 cp->c_attr.ca_firstlink = nextlinkid;
847 }
848 /* Update previous link. */
849 if (prevlinkid) {
6d2010ae 850 (void) cat_update_siblinglinks(hfsmp, prevlinkid, HFS_IGNORABLE_LINK, nextlinkid);
2d21ac55
A
851 }
852 /* Update next link. */
853 if (nextlinkid) {
6d2010ae 854 (void) cat_update_siblinglinks(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK);
2d21ac55 855 }
39236c6e
A
856
857 /*
858 * The call to cat_releasedesc below will only release the name buffer;
859 * it does not zero out the rest of the fields in the 'cat_desc' data structure.
860 *
861 * As a result, since there are still other links at this point, we need
862 * to make the current cnode descriptor point to the raw inode. If a path-based
863 * system call comes along first, it will replace the descriptor with a valid link
864 * ID. If a userland process already has a file descriptor open, then they will
865 * bypass that lookup, though. Replacing the descriptor CNID with the raw
866 * inode will force it to generate a new full path.
867 */
868 cp->c_cnid = cp->c_fileid;
869
2d21ac55
A
870 }
871
872 /* Push new link count to disk. */
873 cp->c_ctime = tv.tv_sec;
874 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
875
876 /* All done with the system files. */
877 hfs_systemfile_unlock(hfsmp, lockflags);
878 lockflags = 0;
879
880 /* Update file system stats. */
881 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
b0d623f7 882
2d21ac55
A
883 /*
884 * All done with this cnode's descriptor...
885 *
886 * Note: all future catalog calls for this cnode may be
887 * by fileid only. This is OK for HFS (which doesn't have
888 * file thread records) since HFS doesn't support hard links.
889 */
890 cat_releasedesc(&cp->c_desc);
891
2d21ac55
A
892out:
893 if (lockflags) {
894 hfs_systemfile_unlock(hfsmp, lockflags);
895 }
896 if (started_tr) {
897 hfs_end_transaction(hfsmp);
898 }
899
900 dcp->c_flag &= ~C_DIR_MODIFICATION;
901 wakeup((caddr_t)&dcp->c_flag);
902
903 return (error);
904}
905
906
907/*
908 * Initialize the HFS+ private system directories.
909 *
910 * These directories are used to hold the inodes
911 * for file and directory hardlinks as well as
912 * open-unlinked files.
913 *
914 * If they don't yet exist they will get created.
915 *
916 * This call is assumed to be made during mount.
917 */
2d21ac55
A
918void
919hfs_privatedir_init(struct hfsmount * hfsmp, enum privdirtype type)
920{
921 struct vnode * dvp = NULLVP;
922 struct cnode * dcp = NULL;
923 struct cat_desc *priv_descp;
924 struct cat_attr *priv_attrp;
925 struct FndrDirInfo * fndrinfo;
926 struct timeval tv;
927 int lockflags;
928 int trans = 0;
929 int error;
930
931 if (hfsmp->hfs_flags & HFS_STANDARD) {
932 return;
933 }
934
935 priv_descp = &hfsmp->hfs_private_desc[type];
936 priv_attrp = &hfsmp->hfs_private_attr[type];
937
938 /* Check if directory already exists. */
939 if (priv_descp->cd_cnid != 0) {
940 return;
941 }
942
943 priv_descp->cd_parentcnid = kRootDirID;
944 priv_descp->cd_nameptr = (const u_int8_t *)hfs_private_names[type];
945 priv_descp->cd_namelen = strlen((const char *)priv_descp->cd_nameptr);
946 priv_descp->cd_flags = CD_ISDIR | CD_DECOMPOSED;
947
948 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
39236c6e 949 error = cat_lookup(hfsmp, priv_descp, 0, 0, NULL, priv_attrp, NULL, NULL);
2d21ac55
A
950 hfs_systemfile_unlock(hfsmp, lockflags);
951
952 if (error == 0) {
953 if (type == FILE_HARDLINKS) {
954 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
955 }
956 priv_descp->cd_cnid = priv_attrp->ca_fileid;
957 goto exit;
958 }
959
960 /* Directory is missing, if this is read-only then we're done. */
961 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
962 goto exit;
963 }
964
965 /* Grab the root directory so we can update it later. */
6d2010ae 966 if (hfs_vget(hfsmp, kRootDirID, &dvp, 0, 0) != 0) {
2d21ac55
A
967 goto exit;
968 }
969 dcp = VTOC(dvp);
970
971 /* Setup the default attributes */
972 bzero(priv_attrp, sizeof(struct cat_attr));
973 priv_attrp->ca_flags = UF_IMMUTABLE | UF_HIDDEN;
974 priv_attrp->ca_mode = S_IFDIR;
975 if (type == DIR_HARDLINKS) {
976 priv_attrp->ca_mode |= S_ISVTX | S_IRUSR | S_IXUSR | S_IRGRP |
977 S_IXGRP | S_IROTH | S_IXOTH;
978 }
979 priv_attrp->ca_linkcount = 1;
980 priv_attrp->ca_itime = hfsmp->hfs_itime;
981 priv_attrp->ca_recflags = kHFSHasFolderCountMask;
982
983 fndrinfo = (struct FndrDirInfo *)&priv_attrp->ca_finderinfo;
984 fndrinfo->frLocation.v = SWAP_BE16(16384);
985 fndrinfo->frLocation.h = SWAP_BE16(16384);
986 fndrinfo->frFlags = SWAP_BE16(kIsInvisible + kNameLocked);
987
988 if (hfs_start_transaction(hfsmp) != 0) {
989 goto exit;
990 }
991 trans = 1;
992
39236c6e
A
993 /* Need the catalog and EA b-trees for CNID acquisition */
994 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2d21ac55
A
995
996 /* Make sure there's space in the Catalog file. */
997 if (cat_preflight(hfsmp, CAT_CREATE, NULL, 0) != 0) {
998 hfs_systemfile_unlock(hfsmp, lockflags);
999 goto exit;
1000 }
1001
39236c6e
A
1002 /* Get the CNID for use */
1003 cnid_t new_id;
1004 if ((error = cat_acquire_cnid(hfsmp, &new_id))) {
1005 hfs_systemfile_unlock (hfsmp, lockflags);
1006 goto exit;
1007 }
1008
2d21ac55 1009 /* Create the private directory on disk. */
39236c6e 1010 error = cat_create(hfsmp, new_id, priv_descp, priv_attrp, NULL);
2d21ac55
A
1011 if (error == 0) {
1012 priv_descp->cd_cnid = priv_attrp->ca_fileid;
1013
1014 /* Update the parent directory */
1015 dcp->c_entries++;
1016 INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
1017 dcp->c_dirchangecnt++;
1018 microtime(&tv);
1019 dcp->c_ctime = tv.tv_sec;
1020 dcp->c_mtime = tv.tv_sec;
1021 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1022 }
1023
1024 hfs_systemfile_unlock(hfsmp, lockflags);
1025
1026 if (error) {
1027 goto exit;
1028 }
1029 if (type == FILE_HARDLINKS) {
6d2010ae 1030 hfsmp->hfs_metadata_createdate = priv_attrp->ca_itime;
2d21ac55
A
1031 }
1032 hfs_volupdate(hfsmp, VOL_MKDIR, 1);
1033exit:
1034 if (trans) {
1035 hfs_end_transaction(hfsmp);
1036 }
1037 if (dvp) {
1038 hfs_unlock(dcp);
1039 vnode_put(dvp);
1040 }
1041 if ((error == 0) && (type == DIR_HARDLINKS)) {
1042 hfs_xattr_init(hfsmp);
1043 }
1044}
1045
1046
1047/*
1048 * Lookup a hardlink link (from chain)
1049 */
2d21ac55 1050int
6d2010ae 1051hfs_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
2d21ac55
A
1052{
1053 int lockflags;
1054 int error;
1055
1056 *prevlinkid = 0;
1057 *nextlinkid = 0;
1058
1059 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1060
6d2010ae 1061 error = cat_lookup_siblinglinks(hfsmp, linkfileid, prevlinkid, nextlinkid);
2d21ac55
A
1062 if (error == ENOLINK) {
1063 hfs_systemfile_unlock(hfsmp, lockflags);
1064 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
1065
1066 error = getfirstlink(hfsmp, linkfileid, nextlinkid);
1067 }
1068 hfs_systemfile_unlock(hfsmp, lockflags);
1069
1070 return (error);
1071}
1072
39236c6e
A
1073
1074/* Find the oldest / last hardlink in the link chain */
1075int
1076hfs_lookup_lastlink (struct hfsmount *hfsmp, cnid_t linkfileid,
1077 cnid_t *lastid, struct cat_desc *cdesc) {
1078 int lockflags;
1079 int error;
1080
1081 *lastid = 0;
1082
1083 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1084
1085 error = cat_lookup_lastlink(hfsmp, linkfileid, lastid, cdesc);
1086
1087 hfs_systemfile_unlock(hfsmp, lockflags);
1088
1089 /*
1090 * cat_lookup_lastlink will zero out the lastid/cdesc arguments as needed
1091 * upon error cases.
1092 */
1093 return error;
1094}
1095
1096
2d21ac55 1097/*
935ed37a 1098 * Cache the origin of a directory or file hard link
2d21ac55
A
1099 *
1100 * cnode must be lock on entry
1101 */
1102__private_extern__
1103void
1104hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid)
1105{
1106 linkorigin_t *origin = NULL;
1107 void * thread = current_thread();
1108 int count = 0;
935ed37a 1109 int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS;
2d21ac55
A
1110 /*
1111 * Look for an existing origin first. If not found, create/steal one.
1112 */
1113 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1114 ++count;
1115 if (origin->lo_thread == thread) {
1116 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1117 break;
1118 }
1119 }
1120 if (origin == NULL) {
1121 /* Recycle the last (i.e., the oldest) if we have too many. */
935ed37a 1122 if (count > maxorigins) {
2d21ac55
A
1123 origin = TAILQ_LAST(&cp->c_originlist, hfs_originhead);
1124 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
1125 } else {
1126 MALLOC(origin, linkorigin_t *, sizeof(linkorigin_t), M_TEMP, M_WAITOK);
1127 }
1128 origin->lo_thread = thread;
1129 }
1130 origin->lo_cnid = cp->c_cnid;
1131 origin->lo_parentcnid = parentcnid;
1132 TAILQ_INSERT_HEAD(&cp->c_originlist, origin, lo_link);
1133}
1134
1135/*
935ed37a 1136 * Release any cached origins for a directory or file hard link
2d21ac55
A
1137 *
1138 * cnode must be lock on entry
1139 */
1140__private_extern__
1141void
1142hfs_relorigins(struct cnode *cp)
1143{
1144 linkorigin_t *origin, *prev;
1145
1146 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
1147 FREE(origin, M_TEMP);
1148 }
1149 TAILQ_INIT(&cp->c_originlist);
1150}
1151
1152/*
935ed37a 1153 * Release a specific origin for a directory or file hard link
2d21ac55
A
1154 *
1155 * cnode must be lock on entry
1156 */
1157__private_extern__
1158void
1159hfs_relorigin(struct cnode *cp, cnid_t parentcnid)
1160{
4a3eedf9 1161 linkorigin_t *origin, *prev;
2d21ac55
A
1162 void * thread = current_thread();
1163
4a3eedf9 1164 TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
2d21ac55
A
1165 if ((origin->lo_thread == thread) ||
1166 (origin->lo_parentcnid == parentcnid)) {
1167 TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
4a3eedf9 1168 FREE(origin, M_TEMP);
2d21ac55
A
1169 break;
1170 }
1171 }
1172}
1173
1174/*
935ed37a 1175 * Test if a directory or file hard link has a cached origin
2d21ac55
A
1176 *
1177 * cnode must be lock on entry
1178 */
1179__private_extern__
1180int
1181hfs_haslinkorigin(cnode_t *cp)
1182{
1183 if (cp->c_flag & C_HARDLINK) {
1184 linkorigin_t *origin;
1185 void * thread = current_thread();
1186
1187 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1188 if (origin->lo_thread == thread) {
1189 return (1);
1190 }
1191 }
1192 }
1193 return (0);
1194}
1195
1196/*
935ed37a 1197 * Obtain the current parent cnid of a directory or file hard link
2d21ac55
A
1198 *
1199 * cnode must be lock on entry
1200 */
1201__private_extern__
1202cnid_t
1203hfs_currentparent(cnode_t *cp)
1204{
1205 if (cp->c_flag & C_HARDLINK) {
1206 linkorigin_t *origin;
1207 void * thread = current_thread();
1208
1209 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1210 if (origin->lo_thread == thread) {
1211 return (origin->lo_parentcnid);
1212 }
1213 }
1214 }
1215 return (cp->c_parentcnid);
1216}
1217
1218/*
935ed37a 1219 * Obtain the current cnid of a directory or file hard link
2d21ac55
A
1220 *
1221 * cnode must be lock on entry
1222 */
1223__private_extern__
1224cnid_t
1225hfs_currentcnid(cnode_t *cp)
1226{
1227 if (cp->c_flag & C_HARDLINK) {
1228 linkorigin_t *origin;
1229 void * thread = current_thread();
1230
1231 TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
1232 if (origin->lo_thread == thread) {
1233 return (origin->lo_cnid);
1234 }
1235 }
1236 }
1237 return (cp->c_cnid);
1238}
1239
1240
1241/*
1242 * Set the first link attribute for a given file id.
1243 *
1244 * The attributes b-tree must already be locked.
1245 * If journaling is enabled, a transaction must already be started.
1246 */
1247static int
1248setfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t firstlink)
1249{
1250 FCB * btfile;
1251 BTreeIterator * iterator;
1252 FSBufferDescriptor btdata;
1253 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1254 HFSPlusAttrData *dataptr;
1255 int result;
1256 u_int16_t datasize;
1257
1258 if (hfsmp->hfs_attribute_cp == NULL) {
1259 return (EPERM);
1260 }
1261 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1262 bzero(iterator, sizeof(*iterator));
1263
1264 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1265 if (result) {
1266 goto out;
1267 }
1268 dataptr = (HFSPlusAttrData *)&attrdata[0];
1269 dataptr->recordType = kHFSPlusAttrInlineData;
1270 dataptr->reserved[0] = 0;
1271 dataptr->reserved[1] = 0;
1272
1273 /*
1274 * Since attrData is variable length, we calculate the size of
1275 * attrData by subtracting the size of all other members of
1276 * structure HFSPlusAttData from the size of attrdata.
1277 */
1278 (void)snprintf((char *)&dataptr->attrData[0],
1279 sizeof(dataptr) - (4 * sizeof(uint32_t)),
1280 "%lu", (unsigned long)firstlink);
1281 dataptr->attrSize = 1 + strlen((char *)&dataptr->attrData[0]);
1282
1283 /* Calculate size of record rounded up to multiple of 2 bytes. */
1284 datasize = sizeof(HFSPlusAttrData) - 2 + dataptr->attrSize + ((dataptr->attrSize & 1) ? 1 : 0);
1285
1286 btdata.bufferAddress = dataptr;
1287 btdata.itemSize = datasize;
1288 btdata.itemCount = 1;
1289
1290 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1291
1292 /* Insert the attribute. */
1293 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
1294 if (result == btExists) {
1295 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
1296 }
1297 (void) BTFlushPath(btfile);
1298out:
1299 FREE(iterator, M_TEMP);
1300
1301 return MacToVFSError(result);
1302}
1303
1304/*
1305 * Get the first link attribute for a given file id.
1306 *
1307 * The attributes b-tree must already be locked.
1308 */
1309static int
1310getfirstlink(struct hfsmount * hfsmp, cnid_t fileid, cnid_t *firstlink)
1311{
1312 FCB * btfile;
1313 BTreeIterator * iterator;
1314 FSBufferDescriptor btdata;
1315 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE];
1316 HFSPlusAttrData *dataptr;
1317 int result;
1318 u_int16_t datasize;
1319
1320 if (hfsmp->hfs_attribute_cp == NULL) {
1321 return (EPERM);
1322 }
1323 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1324 bzero(iterator, sizeof(*iterator));
1325
1326 result = hfs_buildattrkey(fileid, FIRST_LINK_XATTR_NAME, (HFSPlusAttrKey *)&iterator->key);
1327 if (result)
1328 goto out;
1329
1330 dataptr = (HFSPlusAttrData *)&attrdata[0];
1331 datasize = sizeof(attrdata);
1332
1333 btdata.bufferAddress = dataptr;
1334 btdata.itemSize = sizeof(attrdata);
1335 btdata.itemCount = 1;
1336
1337 btfile = hfsmp->hfs_attribute_cp->c_datafork;
1338
1339 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
1340 if (result)
1341 goto out;
1342
1343 if (dataptr->attrSize < 3) {
1344 result = ENOENT;
1345 goto out;
1346 }
1347 *firstlink = strtoul((char*)&dataptr->attrData[0], NULL, 10);
1348out:
1349 FREE(iterator, M_TEMP);
1350
1351 return MacToVFSError(result);
1352}
1353