]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-344.49.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
1c79356b
A
26
27#include <sys/systm.h>
28#include <sys/kernel.h>
29#include <sys/malloc.h>
9bccf70c 30#include <sys/mount.h>
1c79356b
A
31#include <sys/namei.h>
32#include <sys/stat.h>
33#include <sys/vnode.h>
34#include <vfs/vfs_support.h>
35#include <libkern/libkern.h>
36
37#include "hfs.h"
9bccf70c
A
38#include "hfs_catalog.h"
39#include "hfs_format.h"
40#include "hfs_endian.h"
1c79356b
A
41
42
43/*
44 * Create a new indirect link
45 *
9bccf70c
A
46 * An indirect link is a reference to a data node. The only useable
47 * fields in the link are the link number, parentID, name and text
48 * encoding. All other catalog fields are ignored.
1c79356b
A
49 */
50static int
9bccf70c
A
51createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
52 u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
1c79356b 53{
9bccf70c
A
54 struct FndrFileInfo *fip;
55 struct cat_desc desc;
56 struct cat_attr attr;
1c79356b
A
57 int result;
58
9bccf70c
A
59 /* Setup the descriptor */
60 bzero(&desc, sizeof(desc));
61 desc.cd_nameptr = linkName;
62 desc.cd_namelen = strlen(linkName);
63 desc.cd_parentcnid = linkparid;
1c79356b 64
9bccf70c
A
65 /* Setup the default attributes */
66 bzero(&attr, sizeof(attr));
67
68 /* links are matched to data nodes by link ID and to volumes by create date */
69 attr.ca_rdev = linknum; /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
70 attr.ca_itime = HFSTOVCB(hfsmp)->vcbCrDate;
71 attr.ca_mode = S_IFREG;
1c79356b 72
9bccf70c
A
73 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
74 fip->fdType = SWAP_BE32 (kHardLinkFileType); /* 'hlnk' */
75 fip->fdCreator = SWAP_BE32 (kHFSPlusCreator); /* 'hfs+' */
76 fip->fdFlags = SWAP_BE16 (kHasBeenInited);
1c79356b 77
b4c24cb9
A
78 hfs_global_shared_lock_acquire(hfsmp);
79 if (hfsmp->jnl) {
80 if (journal_start_transaction(hfsmp->jnl) != 0) {
81 hfs_global_shared_lock_release(hfsmp);
82 return EINVAL;
83 }
84 }
85
9bccf70c
A
86 /* Create the indirect link directly in the catalog */
87 result = cat_create(hfsmp, &desc, &attr, NULL);
1c79356b 88
b4c24cb9 89 if (result == 0 && linkcnid != NULL)
9bccf70c 90 *linkcnid = attr.ca_fileid;
1c79356b 91
b4c24cb9
A
92 if (hfsmp->jnl) {
93 journal_end_transaction(hfsmp->jnl);
94 }
95 hfs_global_shared_lock_release(hfsmp);
96
1c79356b
A
97 return (result);
98}
99
100
101/*
9bccf70c 102 * 2 locks are needed (dvp and vp)
1c79356b
A
103 * also need catalog lock
104 *
105 * caller's responsibility:
106 * componentname cleanup
9bccf70c 107 * unlocking dvp and vp
1c79356b
A
108 */
109static int
9bccf70c
A
110hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
111 struct componentname *cnp)
1c79356b
A
112{
113 struct proc *p = cnp->cn_proc;
1c79356b
A
114 u_int32_t indnodeno = 0;
115 char inodename[32];
9bccf70c
A
116 struct cat_desc to_desc;
117 int newlink = 0;
1c79356b
A
118 int retval;
119
1c79356b
A
120
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
9bccf70c
A
128 /* Lock catalog b-tree */
129 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
b4c24cb9
A
130 if (retval) {
131 return retval;
132 }
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 */
146 indnodeno = ((random() & 0x3fffffff) + 100);
147 MAKE_INODE_NAME(inodename, indnodeno);
148
149 /* move source file to data node directory */
9bccf70c
A
150 to_desc.cd_nameptr = inodename;
151 to_desc.cd_namelen = strlen(inodename);
152
153 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
154 &to_desc, NULL);
1c79356b 155
9bccf70c
A
156 } while (retval == EEXIST);
157 if (retval)
158 goto out;
1c79356b 159
9bccf70c
A
160 /* Replace source file with link node */
161 retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
162 cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
1c79356b
A
163 if (retval) {
164 /* put it source file back */
b4c24cb9
A
165 // XXXdbg
166 #if 1
167 {
168 int err;
169 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
170 if (err)
171 panic("hfs_makelink: error %d from cat_rename backout 1", err);
172 }
173 #else
9bccf70c 174 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 175 #endif
1c79356b
A
176 goto out;
177 }
9bccf70c
A
178 cp->c_rdev = indnodeno;
179 } else {
180 indnodeno = cp->c_rdev;
181 }
1c79356b
A
182
183 /*
184 * Create a catalog entry for the new link (parentID + name).
185 */
9bccf70c
A
186 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
187 if (retval && newlink) {
188 /* Get rid of new link */
189 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
190
191 /* Put the source file back */
b4c24cb9
A
192 // XXXdbg
193 #if 1
194 {
195 int err;
196 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
197 if (err)
198 panic("hfs_makelink: error %d from cat_rename backout 2", err);
199 }
200 #else
9bccf70c 201 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 202 #endif
1c79356b
A
203 goto out;
204 }
205
206 /*
9bccf70c
A
207 * Finally, if this is a new hardlink then:
208 * - update HFS Private Data dir
209 * - mark the cnode as a hard link
1c79356b 210 */
9bccf70c
A
211 if (newlink) {
212 hfsmp->hfs_privdir_attr.ca_entries++;
213 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
214 &hfsmp->hfs_privdir_attr, NULL, NULL);
215 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
216 cp->c_flag |= (C_CHANGE | C_HARDLINK);
1c79356b
A
217 }
218
219out:
9bccf70c
A
220 /* Unlock catalog b-tree */
221 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1c79356b
A
222
223 return (retval);
224}
225
226
227/*
228 * link vnode call
229#% link vp U U U
230#% link tdvp L U U
231#
232 vop_link {
233 IN WILLRELE struct vnode *vp;
234 IN struct vnode *targetPar_vp;
235 IN struct componentname *cnp;
236
237 */
238int
239hfs_link(ap)
9bccf70c
A
240 struct vop_link_args /* {
241 struct vnode *a_vp;
242 struct vnode *a_tdvp;
243 struct componentname *a_cnp;
244 } */ *ap;
1c79356b 245{
b4c24cb9 246 struct hfsmount *hfsmp;
1c79356b
A
247 struct vnode *vp = ap->a_vp;
248 struct vnode *tdvp = ap->a_tdvp;
249 struct componentname *cnp = ap->a_cnp;
250 struct proc *p = cnp->cn_proc;
9bccf70c
A
251 struct cnode *cp;
252 struct cnode *tdcp;
1c79356b
A
253 struct timeval tv;
254 int error;
255
b4c24cb9
A
256 hfsmp = VTOHFS(vp);
257
1c79356b
A
258#if HFS_DIAGNOSTIC
259 if ((cnp->cn_flags & HASBUF) == 0)
260 panic("hfs_link: no name");
261#endif
262 if (tdvp->v_mount != vp->v_mount) {
263 VOP_ABORTOP(tdvp, cnp);
264 error = EXDEV;
265 goto out2;
266 }
267 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
268 return err_link(ap); /* hfs disks don't support hard links */
269
b4c24cb9 270 if (hfsmp->hfs_private_metadata_dir == 0)
1c79356b
A
271 return err_link(ap); /* no private metadata dir, no links possible */
272
273 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
274 VOP_ABORTOP(tdvp, cnp);
275 goto out2;
276 }
9bccf70c
A
277 cp = VTOC(vp);
278 tdcp = VTOC(tdvp);
279
280 if (cp->c_nlink >= HFS_LINK_MAX) {
1c79356b
A
281 VOP_ABORTOP(tdvp, cnp);
282 error = EMLINK;
283 goto out1;
284 }
9bccf70c 285 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b
A
286 VOP_ABORTOP(tdvp, cnp);
287 error = EPERM;
288 goto out1;
289 }
290 if (vp->v_type == VBLK || vp->v_type == VCHR) {
291 VOP_ABORTOP(tdvp, cnp);
292 error = EINVAL; /* cannot link to a special file */
293 goto out1;
294 }
295
b4c24cb9
A
296 hfs_global_shared_lock_acquire(hfsmp);
297 if (hfsmp->jnl) {
298 if (journal_start_transaction(hfsmp->jnl) != 0) {
299 hfs_global_shared_lock_release(hfsmp);
d7e50217
A
300 VOP_ABORTOP(tdvp, cnp);
301 error = EINVAL; /* cannot link to a special file */
302 goto out1;
b4c24cb9
A
303 }
304 }
305
9bccf70c
A
306 cp->c_nlink++;
307 cp->c_flag |= C_CHANGE;
1c79356b 308 tv = time;
b4c24cb9 309
1c79356b 310 error = VOP_UPDATE(vp, &tv, &tv, 1);
b4c24cb9
A
311 if (!error) {
312 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
313 }
1c79356b 314 if (error) {
9bccf70c
A
315 cp->c_nlink--;
316 cp->c_flag |= C_CHANGE;
317 } else {
318 /* Update the target directory and volume stats */
319 tdcp->c_nlink++;
320 tdcp->c_entries++;
321 tdcp->c_flag |= C_CHANGE | C_UPDATE;
322 tv = time;
323 (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
b4c24cb9
A
324
325 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 326 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 327 }
b4c24cb9
A
328
329 // XXXdbg - need to do this here as well because cp could have changed
330 error = VOP_UPDATE(vp, &tv, &tv, 1);
331
1c79356b 332 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
b4c24cb9
A
333
334 if (hfsmp->jnl) {
335 journal_end_transaction(hfsmp->jnl);
336 }
337 hfs_global_shared_lock_release(hfsmp);
338
1c79356b
A
339out1:
340 if (tdvp != vp)
341 VOP_UNLOCK(vp, 0, p);
342out2:
343 vput(tdvp);
344 return (error);
345}