]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-517.12.7.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
55e303ae 2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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.
1c79356b 11 *
e5568f75
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
1c79356b
A
23
24#include <sys/systm.h>
25#include <sys/kernel.h>
26#include <sys/malloc.h>
9bccf70c 27#include <sys/mount.h>
1c79356b
A
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"
9bccf70c
A
35#include "hfs_catalog.h"
36#include "hfs_format.h"
37#include "hfs_endian.h"
1c79356b
A
38
39
40/*
41 * Create a new indirect link
42 *
9bccf70c
A
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.
1c79356b
A
46 */
47static int
9bccf70c
A
48createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
49 u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
1c79356b 50{
9bccf70c
A
51 struct FndrFileInfo *fip;
52 struct cat_desc desc;
53 struct cat_attr attr;
1c79356b
A
54 int result;
55
9bccf70c
A
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;
1c79356b 61
9bccf70c
A
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;
1c79356b 69
9bccf70c
A
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);
1c79356b 74
b4c24cb9
A
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
9bccf70c
A
83 /* Create the indirect link directly in the catalog */
84 result = cat_create(hfsmp, &desc, &attr, NULL);
1c79356b 85
b4c24cb9 86 if (result == 0 && linkcnid != NULL)
9bccf70c 87 *linkcnid = attr.ca_fileid;
1c79356b 88
b4c24cb9
A
89 if (hfsmp->jnl) {
90 journal_end_transaction(hfsmp->jnl);
91 }
92 hfs_global_shared_lock_release(hfsmp);
93
1c79356b
A
94 return (result);
95}
96
97
98/*
9bccf70c 99 * 2 locks are needed (dvp and vp)
1c79356b
A
100 * also need catalog lock
101 *
102 * caller's responsibility:
103 * componentname cleanup
9bccf70c 104 * unlocking dvp and vp
1c79356b
A
105 */
106static int
9bccf70c
A
107hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
108 struct componentname *cnp)
1c79356b
A
109{
110 struct proc *p = cnp->cn_proc;
1c79356b
A
111 u_int32_t indnodeno = 0;
112 char inodename[32];
9bccf70c
A
113 struct cat_desc to_desc;
114 int newlink = 0;
1c79356b 115 int retval;
55e303ae 116 cat_cookie_t cookie = {0};
1c79356b 117
1c79356b
A
118
119 /* We don't allow link nodes in our Private Meta Data folder! */
9bccf70c 120 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
1c79356b
A
121 return (EPERM);
122
9bccf70c 123 if (hfs_freeblks(hfsmp, 0) == 0)
1c79356b
A
124 return (ENOSPC);
125
55e303ae
A
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
9bccf70c
A
131 /* Lock catalog b-tree */
132 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
b4c24cb9 133 if (retval) {
55e303ae 134 goto out2;
b4c24cb9 135 }
1c79356b
A
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 */
9bccf70c
A
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;
b4c24cb9 146
1c79356b
A
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 */
9bccf70c
A
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);
1c79356b 158
9bccf70c
A
159 } while (retval == EEXIST);
160 if (retval)
161 goto out;
1c79356b 162
9bccf70c
A
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);
1c79356b
A
166 if (retval) {
167 /* put it source file back */
b4c24cb9
A
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
9bccf70c 177 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 178 #endif
1c79356b
A
179 goto out;
180 }
9bccf70c
A
181 cp->c_rdev = indnodeno;
182 } else {
183 indnodeno = cp->c_rdev;
184 }
1c79356b
A
185
186 /*
187 * Create a catalog entry for the new link (parentID + name).
188 */
9bccf70c
A
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 */
b4c24cb9
A
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
9bccf70c 204 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 205 #endif
1c79356b
A
206 goto out;
207 }
208
209 /*
9bccf70c
A
210 * Finally, if this is a new hardlink then:
211 * - update HFS Private Data dir
212 * - mark the cnode as a hard link
1c79356b 213 */
9bccf70c
A
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);
1c79356b
A
220 }
221
222out:
9bccf70c
A
223 /* Unlock catalog b-tree */
224 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
55e303ae
A
225out2:
226 cat_postflight(hfsmp, &cookie, p);
1c79356b
A
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 */
55e303ae 242__private_extern__
1c79356b
A
243int
244hfs_link(ap)
9bccf70c
A
245 struct vop_link_args /* {
246 struct vnode *a_vp;
247 struct vnode *a_tdvp;
248 struct componentname *a_cnp;
249 } */ *ap;
1c79356b 250{
b4c24cb9 251 struct hfsmount *hfsmp;
1c79356b
A
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;
9bccf70c
A
256 struct cnode *cp;
257 struct cnode *tdcp;
1c79356b
A
258 struct timeval tv;
259 int error;
260
b4c24cb9
A
261 hfsmp = VTOHFS(vp);
262
1c79356b
A
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
55e303ae 275 if (hfsmp->hfs_privdir_desc.cd_cnid == 0)
1c79356b
A
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 }
9bccf70c
A
282 cp = VTOC(vp);
283 tdcp = VTOC(tdvp);
284
285 if (cp->c_nlink >= HFS_LINK_MAX) {
1c79356b
A
286 VOP_ABORTOP(tdvp, cnp);
287 error = EMLINK;
288 goto out1;
289 }
9bccf70c 290 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b
A
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
b4c24cb9
A
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);
d7e50217
A
305 VOP_ABORTOP(tdvp, cnp);
306 error = EINVAL; /* cannot link to a special file */
307 goto out1;
b4c24cb9
A
308 }
309 }
310
9bccf70c
A
311 cp->c_nlink++;
312 cp->c_flag |= C_CHANGE;
1c79356b 313 tv = time;
b4c24cb9 314
1c79356b 315 error = VOP_UPDATE(vp, &tv, &tv, 1);
b4c24cb9
A
316 if (!error) {
317 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
318 }
1c79356b 319 if (error) {
9bccf70c
A
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);
b4c24cb9
A
329
330 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 331 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 332 }
b4c24cb9
A
333
334 // XXXdbg - need to do this here as well because cp could have changed
483a1d10 335 (void) VOP_UPDATE(vp, &tv, &tv, 1);
b4c24cb9 336
b4c24cb9
A
337
338 if (hfsmp->jnl) {
339 journal_end_transaction(hfsmp->jnl);
340 }
341 hfs_global_shared_lock_release(hfsmp);
342
55e303ae
A
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
1c79356b
A
354out1:
355 if (tdvp != vp)
356 VOP_UNLOCK(vp, 0, p);
357out2:
358 vput(tdvp);
359 return (error);
360}