]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
8f6c56a5
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
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
47
1c79356b
A
48/*
49 * Create a new indirect link
50 *
9bccf70c
A
51 * An indirect link is a reference to a data node. The only useable
52 * fields in the link are the link number, parentID, name and text
53 * encoding. All other catalog fields are ignored.
1c79356b
A
54 */
55static int
9bccf70c
A
56createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
57 u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
1c79356b 58{
9bccf70c
A
59 struct FndrFileInfo *fip;
60 struct cat_desc desc;
61 struct cat_attr attr;
1c79356b
A
62 int result;
63
9bccf70c
A
64 /* Setup the descriptor */
65 bzero(&desc, sizeof(desc));
66 desc.cd_nameptr = linkName;
67 desc.cd_namelen = strlen(linkName);
68 desc.cd_parentcnid = linkparid;
1c79356b 69
9bccf70c
A
70 /* Setup the default attributes */
71 bzero(&attr, sizeof(attr));
72
73 /* links are matched to data nodes by link ID and to volumes by create date */
74 attr.ca_rdev = linknum; /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
75 attr.ca_itime = HFSTOVCB(hfsmp)->vcbCrDate;
76 attr.ca_mode = S_IFREG;
1c79356b 77
9bccf70c
A
78 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
79 fip->fdType = SWAP_BE32 (kHardLinkFileType); /* 'hlnk' */
80 fip->fdCreator = SWAP_BE32 (kHFSPlusCreator); /* 'hfs+' */
81 fip->fdFlags = SWAP_BE16 (kHasBeenInited);
1c79356b 82
9bccf70c
A
83 /* Create the indirect link directly in the catalog */
84 result = cat_create(hfsmp, &desc, &attr, NULL);
1c79356b 85
b4c24cb9 86 if (result == 0 && linkcnid != NULL)
9bccf70c 87 *linkcnid = attr.ca_fileid;
1c79356b
A
88
89 return (result);
90}
91
92
93/*
9bccf70c 94 * 2 locks are needed (dvp and vp)
1c79356b
A
95 * also need catalog lock
96 *
97 * caller's responsibility:
98 * componentname cleanup
9bccf70c 99 * unlocking dvp and vp
1c79356b
A
100 */
101static int
9bccf70c
A
102hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
103 struct componentname *cnp)
1c79356b 104{
91447636
A
105 vfs_context_t ctx = cnp->cn_context;
106 struct proc *p = vfs_context_proc(ctx);
1c79356b 107 u_int32_t indnodeno = 0;
91447636 108 char inodename[32];
9bccf70c
A
109 struct cat_desc to_desc;
110 int newlink = 0;
91447636 111 int lockflags;
1c79356b 112 int retval;
91447636
A
113 cat_cookie_t cookie;
114 cnid_t orig_cnid;
1c79356b 115
91447636
A
116 if (cur_link_id == 0) {
117 cur_link_id = ((random() & 0x3fffffff) + 100);
118 // printf("hfs: initializing cur link id to: 0x%.8x\n", cur_link_id);
119 }
120
1c79356b 121 /* We don't allow link nodes in our Private Meta Data folder! */
9bccf70c 122 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
1c79356b
A
123 return (EPERM);
124
9bccf70c 125 if (hfs_freeblks(hfsmp, 0) == 0)
1c79356b
A
126 return (ENOSPC);
127
91447636 128 bzero(&cookie, sizeof(cat_cookie_t));
55e303ae
A
129 /* Reserve some space in the Catalog file. */
130 if ((retval = cat_preflight(hfsmp, (2 * CAT_CREATE)+ CAT_RENAME, &cookie, p))) {
131 return (retval);
132 }
133
91447636
A
134 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
135
136 // save off a copy of the current cnid so we can put
137 // it back if we get errors down below
138 orig_cnid = cp->c_desc.cd_cnid;
1c79356b
A
139
140 /*
141 * If this is a new hardlink then we need to create the data
142 * node (inode) and replace the original file with a link node.
143 */
9bccf70c
A
144 if (cp->c_nlink == 2 && (cp->c_flag & C_HARDLINK) == 0) {
145 newlink = 1;
146 bzero(&to_desc, sizeof(to_desc));
147 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
148 to_desc.cd_cnid = cp->c_fileid;
b4c24cb9 149
1c79356b
A
150 do {
151 /* get a unique indirect node number */
91447636
A
152 if (retval == 0) {
153 indnodeno = cp->c_fileid;
154 } else {
155 indnodeno = cur_link_id++;
156 }
157
1c79356b
A
158 MAKE_INODE_NAME(inodename, indnodeno);
159
160 /* move source file to data node directory */
9bccf70c
A
161 to_desc.cd_nameptr = inodename;
162 to_desc.cd_namelen = strlen(inodename);
163
164 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
165 &to_desc, NULL);
1c79356b 166
91447636
A
167 if (retval != 0 && retval != EEXIST) {
168 printf("hfs_makelink: cat_rename to %s failed (%d). fileid %d\n",
169 inodename, retval, cp->c_fileid);
170 }
171
9bccf70c
A
172 } while (retval == EEXIST);
173 if (retval)
174 goto out;
1c79356b 175
9bccf70c
A
176 /* Replace source file with link node */
177 retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
178 cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
1c79356b 179 if (retval) {
91447636
A
180 /* put it source file back */
181 int err;
182
183 // Put this back to what it was before.
184 cp->c_desc.cd_cnid = orig_cnid;
185
186 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
187 if (err)
188 panic("hfs_makelink: error %d from cat_rename backout 1", err);
189 goto out;
1c79356b 190 }
9bccf70c
A
191 cp->c_rdev = indnodeno;
192 } else {
193 indnodeno = cp->c_rdev;
194 }
1c79356b
A
195
196 /*
197 * Create a catalog entry for the new link (parentID + name).
198 */
9bccf70c
A
199 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
200 if (retval && newlink) {
91447636
A
201 int err;
202
203 /* Get rid of new link */
204 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
205
206 // Put this back to what it was before.
207 cp->c_desc.cd_cnid = orig_cnid;
208
209 /* Put the source file back */
210 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
211 if (err)
212 panic("hfs_makelink: error %d from cat_rename backout 2", err);
213
214 goto out;
1c79356b
A
215 }
216
217 /*
9bccf70c
A
218 * Finally, if this is a new hardlink then:
219 * - update HFS Private Data dir
220 * - mark the cnode as a hard link
1c79356b 221 */
9bccf70c 222 if (newlink) {
91447636
A
223 vnode_t vp;
224
225 if (retval != 0) {
226 panic("hfs_makelink: retval %d but newlink = 1!\n", retval);
227 }
228
9bccf70c 229 hfsmp->hfs_privdir_attr.ca_entries++;
91447636
A
230 retval = cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
231 &hfsmp->hfs_privdir_attr, NULL, NULL);
232 if (retval != 0) {
233 panic("hfs_makelink: cat_update of privdir failed! (%d)\n",
234 retval);
235 }
9bccf70c 236 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
91447636
A
237 cp->c_flag |= C_HARDLINK;
238 if ((vp = cp->c_vp) != NULLVP) {
239 if (vnode_get(vp) == 0) {
240 vnode_set_hard_link(vp);
241 vnode_put(vp);
242 }
243 }
244 if ((vp = cp->c_rsrc_vp) != NULLVP) {
245 if (vnode_get(vp) == 0) {
246 vnode_set_hard_link(vp);
247 vnode_put(vp);
248 }
249 }
250 cp->c_touch_chgtime = TRUE;
251 cp->c_flag |= C_FORCEUPDATE;
1c79356b 252 }
91447636 253 dcp->c_flag |= C_FORCEUPDATE;
1c79356b
A
254
255out:
91447636
A
256 hfs_systemfile_unlock(hfsmp, lockflags);
257
55e303ae 258 cat_postflight(hfsmp, &cookie, p);
1c79356b
A
259 return (retval);
260}
261
262
263/*
264 * link vnode call
265#% link vp U U U
266#% link tdvp L U U
267#
91447636 268 vnop_link {
1c79356b
A
269 IN WILLRELE struct vnode *vp;
270 IN struct vnode *targetPar_vp;
271 IN struct componentname *cnp;
91447636 272 IN vfs_context_t context;
1c79356b
A
273
274 */
55e303ae 275__private_extern__
1c79356b 276int
91447636 277hfs_vnop_link(struct vnop_link_args *ap)
1c79356b 278{
b4c24cb9 279 struct hfsmount *hfsmp;
1c79356b
A
280 struct vnode *vp = ap->a_vp;
281 struct vnode *tdvp = ap->a_tdvp;
282 struct componentname *cnp = ap->a_cnp;
9bccf70c
A
283 struct cnode *cp;
284 struct cnode *tdcp;
91447636
A
285 enum vtype v_type;
286 int error, ret, lockflags;
287 struct cat_desc cndesc;
1c79356b 288
91447636 289 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord) {
1c79356b 290 return err_link(ap); /* hfs disks don't support hard links */
91447636
A
291 }
292 if (VTOHFS(vp)->hfs_privdir_desc.cd_cnid == 0) {
1c79356b 293 return err_link(ap); /* no private metadata dir, no links possible */
1c79356b 294 }
91447636
A
295 if (vnode_mount(tdvp) != vnode_mount(vp)) {
296 return (EXDEV);
297 }
298 if ((error = hfs_lockpair(VTOC(tdvp), VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
299 return (error);
300 }
9bccf70c 301 tdcp = VTOC(tdvp);
91447636
A
302 cp = VTOC(vp);
303 hfsmp = VTOHFS(vp);
9bccf70c
A
304
305 if (cp->c_nlink >= HFS_LINK_MAX) {
1c79356b 306 error = EMLINK;
91447636 307 goto out;
1c79356b 308 }
9bccf70c 309 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b 310 error = EPERM;
91447636 311 goto out;
1c79356b 312 }
91447636
A
313 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
314 error = ENOENT;
315 goto out;
316 }
317
318 v_type = vnode_vtype(vp);
319 if (v_type == VBLK || v_type == VCHR) {
1c79356b 320 error = EINVAL; /* cannot link to a special file */
91447636 321 goto out;
1c79356b
A
322 }
323
91447636
A
324 if (hfs_start_transaction(hfsmp) != 0) {
325 error = EINVAL; /* cannot link to a special file */
326 goto out;
b4c24cb9
A
327 }
328
9bccf70c 329 cp->c_nlink++;
91447636 330 cp->c_touch_chgtime = TRUE;
b4c24cb9 331
91447636 332 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
1c79356b 333 if (error) {
9bccf70c 334 cp->c_nlink--;
91447636 335 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
9bccf70c 336 } else {
91447636
A
337 /* Invalidate negative cache entries in the destination directory */
338 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
339 cache_purge_negatives(tdvp);
340
9bccf70c
A
341 /* Update the target directory and volume stats */
342 tdcp->c_nlink++;
343 tdcp->c_entries++;
91447636
A
344 tdcp->c_touch_chgtime = TRUE;
345 tdcp->c_touch_modtime = TRUE;
346 tdcp->c_flag |= C_FORCEUPDATE;
347
348 error = hfs_update(tdvp, 0);
349 if (error) {
350 panic("hfs_vnop_link: error updating tdvp 0x%x\n", tdvp);
351 }
b4c24cb9
A
352
353 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 354 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 355 }
b4c24cb9 356
91447636 357 cp->c_flag |= C_FORCEUPDATE; // otherwise hfs_update() might skip the update
b4c24cb9 358
91447636
A
359 if ((ret = hfs_update(vp, TRUE)) != 0) {
360 panic("hfs_vnop_link: error %d updating vp @ 0x%x\n", ret, vp);
55e303ae 361 }
91447636
A
362
363 hfs_end_transaction(hfsmp);
55e303ae
A
364
365 HFS_KNOTE(vp, NOTE_LINK);
366 HFS_KNOTE(tdvp, NOTE_WRITE);
91447636
A
367out:
368 hfs_unlockpair(tdcp, cp);
1c79356b
A
369 return (error);
370}