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