]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/mount.h>
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"
40 #include "hfs_catalog.h"
41 #include "hfs_format.h"
42 #include "hfs_endian.h"
43
44
45 static int cur_link_id = 0;
46
47
48 /*
49 * Create a new indirect link
50 *
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.
54 */
55 static int
56 createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
57 u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
58 {
59 struct FndrFileInfo *fip;
60 struct cat_desc desc;
61 struct cat_attr attr;
62 int result;
63
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;
69
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;
77
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);
82
83 /* Create the indirect link directly in the catalog */
84 result = cat_create(hfsmp, &desc, &attr, NULL);
85
86 if (result == 0 && linkcnid != NULL)
87 *linkcnid = attr.ca_fileid;
88
89 return (result);
90 }
91
92
93 /*
94 * 2 locks are needed (dvp and vp)
95 * also need catalog lock
96 *
97 * caller's responsibility:
98 * componentname cleanup
99 * unlocking dvp and vp
100 */
101 static int
102 hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
103 struct componentname *cnp)
104 {
105 vfs_context_t ctx = cnp->cn_context;
106 struct proc *p = vfs_context_proc(ctx);
107 u_int32_t indnodeno = 0;
108 char inodename[32];
109 struct cat_desc to_desc;
110 int newlink = 0;
111 int lockflags;
112 int retval;
113 cat_cookie_t cookie;
114 cnid_t orig_cnid;
115
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
121 /* We don't allow link nodes in our Private Meta Data folder! */
122 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
123 return (EPERM);
124
125 if (hfs_freeblks(hfsmp, 0) == 0)
126 return (ENOSPC);
127
128 bzero(&cookie, sizeof(cat_cookie_t));
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 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;
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 if (retval == 0) {
153 indnodeno = cp->c_fileid;
154 } else {
155 indnodeno = cur_link_id++;
156 }
157
158 MAKE_INODE_NAME(inodename, indnodeno);
159
160 /* move source file to data node directory */
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);
166
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
172 } while (retval == EEXIST);
173 if (retval)
174 goto out;
175
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);
179 if (retval) {
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;
190 }
191 cp->c_rdev = indnodeno;
192 } else {
193 indnodeno = cp->c_rdev;
194 }
195
196 /*
197 * Create a catalog entry for the new link (parentID + name).
198 */
199 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
200 if (retval && newlink) {
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;
215 }
216
217 /*
218 * Finally, if this is a new hardlink then:
219 * - update HFS Private Data dir
220 * - mark the cnode as a hard link
221 */
222 if (newlink) {
223 vnode_t vp;
224
225 if (retval != 0) {
226 panic("hfs_makelink: retval %d but newlink = 1!\n", retval);
227 }
228
229 hfsmp->hfs_privdir_attr.ca_entries++;
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 }
236 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
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;
252 }
253 dcp->c_flag |= C_FORCEUPDATE;
254
255 out:
256 hfs_systemfile_unlock(hfsmp, lockflags);
257
258 cat_postflight(hfsmp, &cookie, p);
259 return (retval);
260 }
261
262
263 /*
264 * link vnode call
265 #% link vp U U U
266 #% link tdvp L U U
267 #
268 vnop_link {
269 IN WILLRELE struct vnode *vp;
270 IN struct vnode *targetPar_vp;
271 IN struct componentname *cnp;
272 IN vfs_context_t context;
273
274 */
275 __private_extern__
276 int
277 hfs_vnop_link(struct vnop_link_args *ap)
278 {
279 struct hfsmount *hfsmp;
280 struct vnode *vp = ap->a_vp;
281 struct vnode *tdvp = ap->a_tdvp;
282 struct componentname *cnp = ap->a_cnp;
283 struct cnode *cp;
284 struct cnode *tdcp;
285 enum vtype v_type;
286 int error, ret, lockflags;
287 struct cat_desc cndesc;
288
289 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord) {
290 return err_link(ap); /* hfs disks don't support hard links */
291 }
292 if (VTOHFS(vp)->hfs_privdir_desc.cd_cnid == 0) {
293 return err_link(ap); /* no private metadata dir, no links possible */
294 }
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 }
301 tdcp = VTOC(tdvp);
302 cp = VTOC(vp);
303 hfsmp = VTOHFS(vp);
304
305 if (cp->c_nlink >= HFS_LINK_MAX) {
306 error = EMLINK;
307 goto out;
308 }
309 if (cp->c_flags & (IMMUTABLE | APPEND)) {
310 error = EPERM;
311 goto out;
312 }
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) {
320 error = EINVAL; /* cannot link to a special file */
321 goto out;
322 }
323
324 if (hfs_start_transaction(hfsmp) != 0) {
325 error = EINVAL; /* cannot link to a special file */
326 goto out;
327 }
328
329 cp->c_nlink++;
330 cp->c_touch_chgtime = TRUE;
331
332 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
333 if (error) {
334 cp->c_nlink--;
335 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
336 } else {
337 /* Invalidate negative cache entries in the destination directory */
338 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
339 cache_purge_negatives(tdvp);
340
341 /* Update the target directory and volume stats */
342 tdcp->c_nlink++;
343 tdcp->c_entries++;
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 }
352
353 hfs_volupdate(hfsmp, VOL_MKFILE,
354 (tdcp->c_cnid == kHFSRootFolderID));
355 }
356
357 cp->c_flag |= C_FORCEUPDATE; // otherwise hfs_update() might skip the update
358
359 if ((ret = hfs_update(vp, TRUE)) != 0) {
360 panic("hfs_vnop_link: error %d updating vp @ 0x%x\n", ret, vp);
361 }
362
363 hfs_end_transaction(hfsmp);
364
365 HFS_KNOTE(vp, NOTE_LINK);
366 HFS_KNOTE(tdvp, NOTE_WRITE);
367 out:
368 hfs_unlockpair(tdcp, cp);
369 return (error);
370 }