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