]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_link.c
f31f86495e4699ec698ac2b49032f7e55882f9bf
[apple/xnu.git] / bsd / hfs / hfs_link.c
1 /*
2 * Copyright (c) 1999-2000 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 #if HFS_HARDLINKS
24
25 #include <sys/systm.h>
26 #include <sys/kernel.h>
27 #include <sys/malloc.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 "hfscommon/headers/FileMgrInternal.h"
36
37
38 /*
39 * Create a new indirect link
40 *
41 * An indirect link is a reference to a data node. The only useable fields in the
42 * link are the parentID, name and text encoding. All other catalog fields
43 * are ignored.
44 */
45 static int
46 createindirectlink(struct hfsnode *dnhp, UInt32 linkPID, char *linkName)
47 {
48 struct hfsCatalogInfo catInfo;
49 struct FInfo *fip;
50 ExtendedVCB *vcb;
51 int result;
52
53 vcb = HTOVCB(dnhp);
54
55 /* Create the indirect link directly in the catalog */
56 result = hfsCreate(vcb, linkPID, linkName, IFREG, 0);
57 if (result) return (result);
58
59 /*
60 * XXX SER Here is a good example where hfsCreate should pass in a catinfo and return
61 * things like the hint and file ID there should be no reason to call lookup here
62 */
63 catInfo.hint = 0;
64 INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
65
66 result = hfs_getcatalog(vcb, linkPID, linkName, -1, &catInfo);
67 if (result) goto errExit;
68
69 fip = (struct FInfo *)&catInfo.nodeData.cnd_finderInfo;
70 fip->fdType = kHardLinkFileType; /* 'hlnk' */
71 fip->fdCreator = kHFSPlusCreator; /* 'hfs+' */
72 fip->fdFlags |= kHasBeenInited;
73
74 /* links are matched to data nodes by nodeID and to volumes by create date */
75 catInfo.nodeData.cnd_iNodeNum = dnhp->h_meta->h_indnodeno;
76 catInfo.nodeData.cnd_createDate = vcb->vcbCrDate;
77
78 result = UpdateCatalogNode(vcb, linkPID, linkName, catInfo.hint, &catInfo.nodeData);
79 if (result) goto errExit;
80
81 CLEAN_CATALOGDATA(&catInfo.nodeData);
82 return (0);
83
84 errExit:
85 CLEAN_CATALOGDATA(&catInfo.nodeData);
86
87 /* get rid of link node */
88 (void) hfsDelete(vcb, linkPID, linkName, TRUE, 0);
89
90 return (result);
91 }
92
93
94 /*
95 * 2 locks are needed (dvp and hp)
96 * also need catalog lock
97 *
98 * caller's responsibility:
99 * componentname cleanup
100 * unlocking dvp and hp
101 */
102 static int
103 hfs_makelink(hp, dvp, cnp)
104 struct hfsnode *hp;
105 struct vnode *dvp;
106 register struct componentname *cnp;
107 {
108 struct proc *p = cnp->cn_proc;
109 struct hfsnode *dhp = VTOH(dvp);
110 u_int32_t ldirID; /* directory ID of linked nodes directory */
111 ExtendedVCB *vcb = VTOVCB(dvp);
112 u_int32_t hint;
113 u_int32_t indnodeno = 0;
114 char inodename[32];
115 int retval;
116
117 ldirID = VTOHFS(dvp)->hfs_private_metadata_dir;
118
119 /* We don't allow link nodes in our Private Meta Data folder! */
120 if ( H_FILEID(dhp) == ldirID)
121 return (EPERM);
122
123 if (vcb->freeBlocks == 0)
124 return (ENOSPC);
125
126 /* lock catalog b-tree */
127 retval = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
128 if (retval != E_NONE)
129 return retval;
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 */
135 if (hp->h_meta->h_nlink == 1) {
136 do {
137 /* get a unique indirect node number */
138 indnodeno = ((random() & 0x3fffffff) + 100);
139 MAKE_INODE_NAME(inodename, indnodeno);
140
141 /* move source file to data node directory */
142 hint = 0;
143 retval = hfsMoveRename(vcb, H_DIRID(hp), H_NAME(hp), ldirID, inodename, &hint);
144 } while (retval == cmExists);
145
146 if (retval) goto out;
147
148 hp->h_meta->h_indnodeno = indnodeno;
149
150 /* replace source file with link node */
151 retval = createindirectlink(hp, H_DIRID(hp), H_NAME(hp));
152 if (retval) {
153 /* put it source file back */
154 hint = 0;
155 (void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
156 goto out;
157 }
158 }
159
160 /*
161 * Create a catalog entry for the new link (parentID + name).
162 */
163 retval = createindirectlink(hp, H_FILEID(dhp), cnp->cn_nameptr);
164 if (retval && hp->h_meta->h_nlink == 1) {
165 /* get rid of new link */
166 (void) hfsDelete(vcb, H_DIRID(hp), H_NAME(hp), TRUE, 0);
167
168 /* put it source file back */
169 hint = 0;
170 (void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
171 goto out;
172 }
173
174 /*
175 * Finally, if this is a new hardlink then we need to mark the hfs node
176 */
177 if (hp->h_meta->h_nlink == 1) {
178 hp->h_meta->h_nlink++;
179 hp->h_nodeflags |= IN_CHANGE;
180 hp->h_meta->h_metaflags |= IN_DATANODE;
181 }
182
183 out:
184 /* unlock catalog b-tree */
185 (void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
186
187 return (retval);
188 }
189
190
191 /*
192 * link vnode call
193 #% link vp U U U
194 #% link tdvp L U U
195 #
196 vop_link {
197 IN WILLRELE struct vnode *vp;
198 IN struct vnode *targetPar_vp;
199 IN struct componentname *cnp;
200
201 */
202 int
203 hfs_link(ap)
204 struct vop_link_args /* {
205 struct vnode *a_vp;
206 struct vnode *a_tdvp;
207 struct componentname *a_cnp;
208 } */ *ap;
209 {
210 struct vnode *vp = ap->a_vp;
211 struct vnode *tdvp = ap->a_tdvp;
212 struct componentname *cnp = ap->a_cnp;
213 struct proc *p = cnp->cn_proc;
214 struct hfsnode *hp;
215 struct timeval tv;
216 int error;
217
218 #if HFS_DIAGNOSTIC
219 if ((cnp->cn_flags & HASBUF) == 0)
220 panic("hfs_link: no name");
221 #endif
222 if (tdvp->v_mount != vp->v_mount) {
223 VOP_ABORTOP(tdvp, cnp);
224 error = EXDEV;
225 goto out2;
226 }
227 if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
228 return err_link(ap); /* hfs disks don't support hard links */
229
230 if (VTOHFS(vp)->hfs_private_metadata_dir == 0)
231 return err_link(ap); /* no private metadata dir, no links possible */
232
233 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
234 VOP_ABORTOP(tdvp, cnp);
235 goto out2;
236 }
237 hp = VTOH(vp);
238 if (hp->h_meta->h_nlink >= HFS_LINK_MAX) {
239 VOP_ABORTOP(tdvp, cnp);
240 error = EMLINK;
241 goto out1;
242 }
243 if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
244 VOP_ABORTOP(tdvp, cnp);
245 error = EPERM;
246 goto out1;
247 }
248 if (vp->v_type == VBLK || vp->v_type == VCHR) {
249 VOP_ABORTOP(tdvp, cnp);
250 error = EINVAL; /* cannot link to a special file */
251 goto out1;
252 }
253
254 hp->h_meta->h_nlink++;
255 hp->h_nodeflags |= IN_CHANGE;
256 tv = time;
257 error = VOP_UPDATE(vp, &tv, &tv, 1);
258 if (!error)
259 error = hfs_makelink(hp, tdvp, cnp);
260 if (error) {
261 hp->h_meta->h_nlink--;
262 hp->h_nodeflags |= IN_CHANGE;
263 }
264 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
265 out1:
266 if (tdvp != vp)
267 VOP_UNLOCK(vp, 0, p);
268 out2:
269 vput(tdvp);
270 return (error);
271 }
272
273 #endif