]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
xnu-344.21.73.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2002 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
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 /* Lock catalog b-tree */
129 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
130 if (retval) {
131 return retval;
132 }
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 */
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;
143
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 */
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);
155
156 } while (retval == EEXIST);
157 if (retval)
158 goto out;
159
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);
163 if (retval) {
164 /* put it source file back */
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
174 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
175 #endif
176 goto out;
177 }
178 cp->c_rdev = indnodeno;
179 } else {
180 indnodeno = cp->c_rdev;
181 }
182
183 /*
184 * Create a catalog entry for the new link (parentID + name).
185 */
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 */
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
201 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
202 #endif
203 goto out;
204 }
205
206 /*
207 * Finally, if this is a new hardlink then:
208 * - update HFS Private Data dir
209 * - mark the cnode as a hard link
210 */
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);
217 }
218
219 out:
220 /* Unlock catalog b-tree */
221 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
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 */
238 int
239 hfs_link(ap)
240 struct vop_link_args /* {
241 struct vnode *a_vp;
242 struct vnode *a_tdvp;
243 struct componentname *a_cnp;
244 } */ *ap;
245 {
246 struct hfsmount *hfsmp;
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;
251 struct cnode *cp;
252 struct cnode *tdcp;
253 struct timeval tv;
254 int error;
255
256 hfsmp = VTOHFS(vp);
257
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
270 if (hfsmp->hfs_private_metadata_dir == 0)
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 }
277 cp = VTOC(vp);
278 tdcp = VTOC(tdvp);
279
280 if (cp->c_nlink >= HFS_LINK_MAX) {
281 VOP_ABORTOP(tdvp, cnp);
282 error = EMLINK;
283 goto out1;
284 }
285 if (cp->c_flags & (IMMUTABLE | APPEND)) {
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
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);
300 VOP_ABORTOP(tdvp, cnp);
301 error = EINVAL; /* cannot link to a special file */
302 goto out1;
303 }
304 }
305
306 cp->c_nlink++;
307 cp->c_flag |= C_CHANGE;
308 tv = time;
309
310 error = VOP_UPDATE(vp, &tv, &tv, 1);
311 if (!error) {
312 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
313 }
314 if (error) {
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);
324
325 hfs_volupdate(hfsmp, VOL_MKFILE,
326 (tdcp->c_cnid == kHFSRootFolderID));
327 }
328
329 // XXXdbg - need to do this here as well because cp could have changed
330 error = VOP_UPDATE(vp, &tv, &tv, 1);
331
332 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
333
334 if (hfsmp->jnl) {
335 journal_end_transaction(hfsmp->jnl);
336 }
337 hfs_global_shared_lock_release(hfsmp);
338
339 out1:
340 if (tdvp != vp)
341 VOP_UNLOCK(vp, 0, p);
342 out2:
343 vput(tdvp);
344 return (error);
345 }