]>
Commit | Line | Data |
---|---|---|
1c79356b A |
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 */ | |
0b4e3aa0 | 56 | result = hfsCreate(vcb, linkPID, linkName, IFREG, 0); |
1c79356b A |
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 |