]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-344.12.2.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_link.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
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
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
A
115 int retval;
116
1c79356b
A
117
118 /* We don't allow link nodes in our Private Meta Data folder! */
9bccf70c 119 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
1c79356b
A
120 return (EPERM);
121
9bccf70c 122 if (hfs_freeblks(hfsmp, 0) == 0)
1c79356b
A
123 return (ENOSPC);
124
9bccf70c
A
125 /* Lock catalog b-tree */
126 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
b4c24cb9
A
127 if (retval) {
128 return retval;
129 }
1c79356b
A
130
131 /*
132 * If this is a new hardlink then we need to create the data
133 * node (inode) and replace the original file with a link node.
134 */
9bccf70c
A
135 if (cp->c_nlink == 2 && (cp->c_flag & C_HARDLINK) == 0) {
136 newlink = 1;
137 bzero(&to_desc, sizeof(to_desc));
138 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
139 to_desc.cd_cnid = cp->c_fileid;
b4c24cb9 140
1c79356b
A
141 do {
142 /* get a unique indirect node number */
143 indnodeno = ((random() & 0x3fffffff) + 100);
144 MAKE_INODE_NAME(inodename, indnodeno);
145
146 /* move source file to data node directory */
9bccf70c
A
147 to_desc.cd_nameptr = inodename;
148 to_desc.cd_namelen = strlen(inodename);
149
150 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
151 &to_desc, NULL);
1c79356b 152
9bccf70c
A
153 } while (retval == EEXIST);
154 if (retval)
155 goto out;
1c79356b 156
9bccf70c
A
157 /* Replace source file with link node */
158 retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
159 cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
1c79356b
A
160 if (retval) {
161 /* put it source file back */
b4c24cb9
A
162 // XXXdbg
163 #if 1
164 {
165 int err;
166 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
167 if (err)
168 panic("hfs_makelink: error %d from cat_rename backout 1", err);
169 }
170 #else
9bccf70c 171 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 172 #endif
1c79356b
A
173 goto out;
174 }
9bccf70c
A
175 cp->c_rdev = indnodeno;
176 } else {
177 indnodeno = cp->c_rdev;
178 }
1c79356b
A
179
180 /*
181 * Create a catalog entry for the new link (parentID + name).
182 */
9bccf70c
A
183 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
184 if (retval && newlink) {
185 /* Get rid of new link */
186 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
187
188 /* Put the source file back */
b4c24cb9
A
189 // XXXdbg
190 #if 1
191 {
192 int err;
193 err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
194 if (err)
195 panic("hfs_makelink: error %d from cat_rename backout 2", err);
196 }
197 #else
9bccf70c 198 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
b4c24cb9 199 #endif
1c79356b
A
200 goto out;
201 }
202
203 /*
9bccf70c
A
204 * Finally, if this is a new hardlink then:
205 * - update HFS Private Data dir
206 * - mark the cnode as a hard link
1c79356b 207 */
9bccf70c
A
208 if (newlink) {
209 hfsmp->hfs_privdir_attr.ca_entries++;
210 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
211 &hfsmp->hfs_privdir_attr, NULL, NULL);
212 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
213 cp->c_flag |= (C_CHANGE | C_HARDLINK);
1c79356b
A
214 }
215
216out:
9bccf70c
A
217 /* Unlock catalog b-tree */
218 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1c79356b
A
219
220 return (retval);
221}
222
223
224/*
225 * link vnode call
226#% link vp U U U
227#% link tdvp L U U
228#
229 vop_link {
230 IN WILLRELE struct vnode *vp;
231 IN struct vnode *targetPar_vp;
232 IN struct componentname *cnp;
233
234 */
235int
236hfs_link(ap)
9bccf70c
A
237 struct vop_link_args /* {
238 struct vnode *a_vp;
239 struct vnode *a_tdvp;
240 struct componentname *a_cnp;
241 } */ *ap;
1c79356b 242{
b4c24cb9 243 struct hfsmount *hfsmp;
1c79356b
A
244 struct vnode *vp = ap->a_vp;
245 struct vnode *tdvp = ap->a_tdvp;
246 struct componentname *cnp = ap->a_cnp;
247 struct proc *p = cnp->cn_proc;
9bccf70c
A
248 struct cnode *cp;
249 struct cnode *tdcp;
1c79356b
A
250 struct timeval tv;
251 int error;
252
b4c24cb9
A
253 hfsmp = VTOHFS(vp);
254
1c79356b
A
255#if HFS_DIAGNOSTIC
256 if ((cnp->cn_flags & HASBUF) == 0)
257 panic("hfs_link: no name");
258#endif
259 if (tdvp->v_mount != vp->v_mount) {
260 VOP_ABORTOP(tdvp, cnp);
261 error = EXDEV;
262 goto out2;
263 }
264 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
265 return err_link(ap); /* hfs disks don't support hard links */
266
b4c24cb9 267 if (hfsmp->hfs_private_metadata_dir == 0)
1c79356b
A
268 return err_link(ap); /* no private metadata dir, no links possible */
269
270 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
271 VOP_ABORTOP(tdvp, cnp);
272 goto out2;
273 }
9bccf70c
A
274 cp = VTOC(vp);
275 tdcp = VTOC(tdvp);
276
277 if (cp->c_nlink >= HFS_LINK_MAX) {
1c79356b
A
278 VOP_ABORTOP(tdvp, cnp);
279 error = EMLINK;
280 goto out1;
281 }
9bccf70c 282 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b
A
283 VOP_ABORTOP(tdvp, cnp);
284 error = EPERM;
285 goto out1;
286 }
287 if (vp->v_type == VBLK || vp->v_type == VCHR) {
288 VOP_ABORTOP(tdvp, cnp);
289 error = EINVAL; /* cannot link to a special file */
290 goto out1;
291 }
292
b4c24cb9
A
293 hfs_global_shared_lock_acquire(hfsmp);
294 if (hfsmp->jnl) {
295 if (journal_start_transaction(hfsmp->jnl) != 0) {
296 hfs_global_shared_lock_release(hfsmp);
297 return EINVAL;
298 }
299 }
300
9bccf70c
A
301 cp->c_nlink++;
302 cp->c_flag |= C_CHANGE;
1c79356b 303 tv = time;
b4c24cb9 304
1c79356b 305 error = VOP_UPDATE(vp, &tv, &tv, 1);
b4c24cb9
A
306 if (!error) {
307 error = hfs_makelink(hfsmp, cp, tdcp, cnp);
308 }
1c79356b 309 if (error) {
9bccf70c
A
310 cp->c_nlink--;
311 cp->c_flag |= C_CHANGE;
312 } else {
313 /* Update the target directory and volume stats */
314 tdcp->c_nlink++;
315 tdcp->c_entries++;
316 tdcp->c_flag |= C_CHANGE | C_UPDATE;
317 tv = time;
318 (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
b4c24cb9
A
319
320 hfs_volupdate(hfsmp, VOL_MKFILE,
9bccf70c 321 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b 322 }
b4c24cb9
A
323
324 // XXXdbg - need to do this here as well because cp could have changed
325 error = VOP_UPDATE(vp, &tv, &tv, 1);
326
1c79356b 327 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
b4c24cb9
A
328
329 if (hfsmp->jnl) {
330 journal_end_transaction(hfsmp->jnl);
331 }
332 hfs_global_shared_lock_release(hfsmp);
333
1c79356b
A
334out1:
335 if (tdvp != vp)
336 VOP_UNLOCK(vp, 0, p);
337out2:
338 vput(tdvp);
339 return (error);
340}