]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_link.c
xnu-344.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
9bccf70c
A
75 /* Create the indirect link directly in the catalog */
76 result = cat_create(hfsmp, &desc, &attr, NULL);
1c79356b 77
9bccf70c
A
78 if (linkcnid != NULL)
79 *linkcnid = attr.ca_fileid;
1c79356b
A
80
81 return (result);
82}
83
84
85/*
9bccf70c 86 * 2 locks are needed (dvp and vp)
1c79356b
A
87 * also need catalog lock
88 *
89 * caller's responsibility:
90 * componentname cleanup
9bccf70c 91 * unlocking dvp and vp
1c79356b
A
92 */
93static int
9bccf70c
A
94hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
95 struct componentname *cnp)
1c79356b
A
96{
97 struct proc *p = cnp->cn_proc;
1c79356b
A
98 u_int32_t indnodeno = 0;
99 char inodename[32];
9bccf70c
A
100 struct cat_desc to_desc;
101 int newlink = 0;
1c79356b
A
102 int retval;
103
1c79356b
A
104
105 /* We don't allow link nodes in our Private Meta Data folder! */
9bccf70c 106 if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
1c79356b
A
107 return (EPERM);
108
9bccf70c 109 if (hfs_freeblks(hfsmp, 0) == 0)
1c79356b
A
110 return (ENOSPC);
111
9bccf70c
A
112 /* Lock catalog b-tree */
113 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
114 if (retval)
1c79356b
A
115 return retval;
116
117 /*
118 * If this is a new hardlink then we need to create the data
119 * node (inode) and replace the original file with a link node.
120 */
9bccf70c
A
121 if (cp->c_nlink == 2 && (cp->c_flag & C_HARDLINK) == 0) {
122 newlink = 1;
123 bzero(&to_desc, sizeof(to_desc));
124 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
125 to_desc.cd_cnid = cp->c_fileid;
1c79356b
A
126 do {
127 /* get a unique indirect node number */
128 indnodeno = ((random() & 0x3fffffff) + 100);
129 MAKE_INODE_NAME(inodename, indnodeno);
130
131 /* move source file to data node directory */
9bccf70c
A
132 to_desc.cd_nameptr = inodename;
133 to_desc.cd_namelen = strlen(inodename);
134
135 retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
136 &to_desc, NULL);
1c79356b 137
9bccf70c
A
138 } while (retval == EEXIST);
139 if (retval)
140 goto out;
1c79356b 141
9bccf70c
A
142 /* Replace source file with link node */
143 retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
144 cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
1c79356b
A
145 if (retval) {
146 /* put it source file back */
9bccf70c 147 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
1c79356b
A
148 goto out;
149 }
9bccf70c
A
150 cp->c_rdev = indnodeno;
151 } else {
152 indnodeno = cp->c_rdev;
153 }
1c79356b
A
154
155 /*
156 * Create a catalog entry for the new link (parentID + name).
157 */
9bccf70c
A
158 retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
159 if (retval && newlink) {
160 /* Get rid of new link */
161 (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
162
163 /* Put the source file back */
164 (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
1c79356b
A
165 goto out;
166 }
167
168 /*
9bccf70c
A
169 * Finally, if this is a new hardlink then:
170 * - update HFS Private Data dir
171 * - mark the cnode as a hard link
1c79356b 172 */
9bccf70c
A
173 if (newlink) {
174 hfsmp->hfs_privdir_attr.ca_entries++;
175 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
176 &hfsmp->hfs_privdir_attr, NULL, NULL);
177 hfs_volupdate(hfsmp, VOL_MKFILE, 0);
178 cp->c_flag |= (C_CHANGE | C_HARDLINK);
1c79356b
A
179 }
180
181out:
9bccf70c
A
182 /* Unlock catalog b-tree */
183 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1c79356b
A
184
185 return (retval);
186}
187
188
189/*
190 * link vnode call
191#% link vp U U U
192#% link tdvp L U U
193#
194 vop_link {
195 IN WILLRELE struct vnode *vp;
196 IN struct vnode *targetPar_vp;
197 IN struct componentname *cnp;
198
199 */
200int
201hfs_link(ap)
9bccf70c
A
202 struct vop_link_args /* {
203 struct vnode *a_vp;
204 struct vnode *a_tdvp;
205 struct componentname *a_cnp;
206 } */ *ap;
1c79356b
A
207{
208 struct vnode *vp = ap->a_vp;
209 struct vnode *tdvp = ap->a_tdvp;
210 struct componentname *cnp = ap->a_cnp;
211 struct proc *p = cnp->cn_proc;
9bccf70c
A
212 struct cnode *cp;
213 struct cnode *tdcp;
1c79356b
A
214 struct timeval tv;
215 int error;
216
217#if HFS_DIAGNOSTIC
218 if ((cnp->cn_flags & HASBUF) == 0)
219 panic("hfs_link: no name");
220#endif
221 if (tdvp->v_mount != vp->v_mount) {
222 VOP_ABORTOP(tdvp, cnp);
223 error = EXDEV;
224 goto out2;
225 }
226 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
227 return err_link(ap); /* hfs disks don't support hard links */
228
229 if (VTOHFS(vp)->hfs_private_metadata_dir == 0)
230 return err_link(ap); /* no private metadata dir, no links possible */
231
232 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
233 VOP_ABORTOP(tdvp, cnp);
234 goto out2;
235 }
9bccf70c
A
236 cp = VTOC(vp);
237 tdcp = VTOC(tdvp);
238
239 if (cp->c_nlink >= HFS_LINK_MAX) {
1c79356b
A
240 VOP_ABORTOP(tdvp, cnp);
241 error = EMLINK;
242 goto out1;
243 }
9bccf70c 244 if (cp->c_flags & (IMMUTABLE | APPEND)) {
1c79356b
A
245 VOP_ABORTOP(tdvp, cnp);
246 error = EPERM;
247 goto out1;
248 }
249 if (vp->v_type == VBLK || vp->v_type == VCHR) {
250 VOP_ABORTOP(tdvp, cnp);
251 error = EINVAL; /* cannot link to a special file */
252 goto out1;
253 }
254
9bccf70c
A
255 cp->c_nlink++;
256 cp->c_flag |= C_CHANGE;
1c79356b
A
257 tv = time;
258 error = VOP_UPDATE(vp, &tv, &tv, 1);
259 if (!error)
9bccf70c 260 error = hfs_makelink(VTOHFS(vp), cp, tdcp, cnp);
1c79356b 261 if (error) {
9bccf70c
A
262 cp->c_nlink--;
263 cp->c_flag |= C_CHANGE;
264 } else {
265 /* Update the target directory and volume stats */
266 tdcp->c_nlink++;
267 tdcp->c_entries++;
268 tdcp->c_flag |= C_CHANGE | C_UPDATE;
269 tv = time;
270 (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
271 hfs_volupdate(VTOHFS(vp), VOL_MKFILE,
272 (tdcp->c_cnid == kHFSRootFolderID));
1c79356b
A
273 }
274 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
275out1:
276 if (tdvp != vp)
277 VOP_UNLOCK(vp, 0, p);
278out2:
279 vput(tdvp);
280 return (error);
281}