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