]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/malloc.h>
30 #include <sys/mount.h>
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"
38 #include "hfs_catalog.h"
39 #include "hfs_format.h"
40 #include "hfs_endian.h"
41
42
43 /*
44 * Create a new indirect link
45 *
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.
49 */
50 static int
51 createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
52 u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
53 {
54 struct FndrFileInfo *fip;
55 struct cat_desc desc;
56 struct cat_attr attr;
57 int result;
58
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;
64
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;
72
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);
77
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
86 /* Create the indirect link directly in the catalog */
87 result = cat_create(hfsmp, &desc, &attr, NULL);
88
89 if (result == 0 && linkcnid != NULL)
90 *linkcnid = attr.ca_fileid;
91
92 if (hfsmp->jnl) {
93 journal_end_transaction(hfsmp->jnl);
94 }
95 hfs_global_shared_lock_release(hfsmp);
96
97 return (result);
98 }
99
100
101 /*
102 * 2 locks are needed (dvp and vp)
103 * also need catalog lock
104 *
105 * caller's responsibility:
106 * componentname cleanup
107 * unlocking dvp and vp
108 */
109 static int
110 hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
111 struct componentname *cnp)
112 {
113 struct proc *p = cnp->cn_proc;
114 u_int32_t indnodeno = 0;
115 char inodename[32];
116 struct cat_desc to_desc;
117 int newlink = 0;
118 int retval;
119 cat_cookie_t cookie = {0};
120
121
122 /* We don't allow link nodes in our Private Meta Data folder! */
123 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
124 return (EPERM);
125
126 if (hfs_freeblks(hfsmp, 0) == 0)
127 return (ENOSPC);
128
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
134 /* Lock catalog b-tree */
135 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
136 if (retval) {
137 goto out2;
138 }
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 */
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;
149
150 do {
151 /* get a unique indirect node number */
152 indnodeno = ((random() & 0x3fffffff) + 100);
153 MAKE_INODE_NAME(inodename, indnodeno);
154
155 /* move source file to data node directory */
156 to_desc.cd_nameptr = inodename;
157 to_desc.cd_namelen = strlen(inodename);
158
159 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
160 &to_desc, NULL);
161
162 } while (retval == EEXIST);
163 if (retval)
164 goto out;
165
166 /* Replace source file with link node */
167 retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
168 cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
169 if (retval) {
170 /* put it source file back */
171 // XXXdbg
172 #if 1
173 {
174 int err;
175 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
176 if (err)
177 panic("hfs_makelink: error %d from cat_rename backout 1", err);
178 }
179 #else
180 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
181 #endif
182 goto out;
183 }
184 cp->c_rdev = indnodeno;
185 } else {
186 indnodeno = cp->c_rdev;
187 }
188
189 /*
190 * Create a catalog entry for the new link (parentID + name).
191 */
192 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
193 if (retval && newlink) {
194 /* Get rid of new link */
195 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
196
197 /* Put the source file back */
198 // XXXdbg
199 #if 1
200 {
201 int err;
202 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
203 if (err)
204 panic("hfs_makelink: error %d from cat_rename backout 2", err);
205 }
206 #else
207 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
208 #endif
209 goto out;
210 }
211
212 /*
213 * Finally, if this is a new hardlink then:
214 * - update HFS Private Data dir
215 * - mark the cnode as a hard link
216 */
217 if (newlink) {
218 hfsmp->hfs_privdir_attr.ca_entries++;
219 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
220 &hfsmp->hfs_privdir_attr, NULL, NULL);
221 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
222 cp->c_flag |= (C_CHANGE | C_HARDLINK);
223 }
224
225 out:
226 /* Unlock catalog b-tree */
227 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
228 out2:
229 cat_postflight(hfsmp, &cookie, p);
230 return (retval);
231 }
232
233
234 /*
235 * link vnode call
236 #% link vp U U U
237 #% link tdvp L U U
238 #
239 vop_link {
240 IN WILLRELE struct vnode *vp;
241 IN struct vnode *targetPar_vp;
242 IN struct componentname *cnp;
243
244 */
245 __private_extern__
246 int
247 hfs_link(ap)
248 struct vop_link_args /* {
249 struct vnode *a_vp;
250 struct vnode *a_tdvp;
251 struct componentname *a_cnp;
252 } */ *ap;
253 {
254 struct hfsmount *hfsmp;
255 struct vnode *vp = ap->a_vp;
256 struct vnode *tdvp = ap->a_tdvp;
257 struct componentname *cnp = ap->a_cnp;
258 struct proc *p = cnp->cn_proc;
259 struct cnode *cp;
260 struct cnode *tdcp;
261 struct timeval tv;
262 int error;
263
264 hfsmp = VTOHFS(vp);
265
266 #if HFS_DIAGNOSTIC
267 if ((cnp->cn_flags & HASBUF) == 0)
268 panic("hfs_link: no name");
269 #endif
270 if (tdvp->v_mount != vp->v_mount) {
271 VOP_ABORTOP(tdvp, cnp);
272 error = EXDEV;
273 goto out2;
274 }
275 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
276 return err_link(ap); /* hfs disks don't support hard links */
277
278 if (hfsmp->hfs_privdir_desc.cd_cnid == 0)
279 return err_link(ap); /* no private metadata dir, no links possible */
280
281 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
282 VOP_ABORTOP(tdvp, cnp);
283 goto out2;
284 }
285 cp = VTOC(vp);
286 tdcp = VTOC(tdvp);
287
288 if (cp->c_nlink >= HFS_LINK_MAX) {
289 VOP_ABORTOP(tdvp, cnp);
290 error = EMLINK;
291 goto out1;
292 }
293 if (cp->c_flags & (IMMUTABLE | APPEND)) {
294 VOP_ABORTOP(tdvp, cnp);
295 error = EPERM;
296 goto out1;
297 }
298 if (vp->v_type == VBLK || vp->v_type == VCHR) {
299 VOP_ABORTOP(tdvp, cnp);
300 error = EINVAL; /* cannot link to a special file */
301 goto out1;
302 }
303
304 hfs_global_shared_lock_acquire(hfsmp);
305 if (hfsmp->jnl) {
306 if (journal_start_transaction(hfsmp->jnl) != 0) {
307 hfs_global_shared_lock_release(hfsmp);
308 VOP_ABORTOP(tdvp, cnp);
309 error = EINVAL; /* cannot link to a special file */
310 goto out1;
311 }
312 }
313
314 cp->c_nlink++;
315 cp->c_flag |= C_CHANGE;
316 tv = time;
317
318 error = VOP_UPDATE(vp, &tv, &tv, 1);
319 if (!error) {
320 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
321 }
322 if (error) {
323 cp->c_nlink--;
324 cp->c_flag |= C_CHANGE;
325 } else {
326 /* Update the target directory and volume stats */
327 tdcp->c_nlink++;
328 tdcp->c_entries++;
329 tdcp->c_flag |= C_CHANGE | C_UPDATE;
330 tv = time;
331 (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
332
333 hfs_volupdate(hfsmp, VOL_MKFILE,
334 (tdcp->c_cnid == kHFSRootFolderID));
335 }
336
337 // XXXdbg - need to do this here as well because cp could have changed
338 error = VOP_UPDATE(vp, &tv, &tv, 1);
339
340
341 if (hfsmp->jnl) {
342 journal_end_transaction(hfsmp->jnl);
343 }
344 hfs_global_shared_lock_release(hfsmp);
345
346 /* free the pathname buffer */
347 {
348 char *tmp = cnp->cn_pnbuf;
349 cnp->cn_pnbuf = NULL;
350 cnp->cn_flags &= ~HASBUF;
351 FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
352 }
353
354 HFS_KNOTE(vp, NOTE_LINK);
355 HFS_KNOTE(tdvp, NOTE_WRITE);
356
357 out1:
358 if (tdvp != vp)
359 VOP_UNLOCK(vp, 0, p);
360 out2:
361 vput(tdvp);
362 return (error);
363 }