]>
Commit | Line | Data |
---|---|---|
9bccf70c | 1 | /* |
55e303ae | 2 | * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. |
9bccf70c A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
43866e37 | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
9bccf70c | 7 | * |
43866e37 A |
8 | * This file contains Original Code and/or Modifications of Original Code |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
9bccf70c A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
43866e37 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
9bccf70c A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | #include <sys/param.h> | |
26 | #include <sys/systm.h> | |
27 | #include <sys/proc.h> | |
28 | #include <sys/vnode.h> | |
29 | #include <sys/mount.h> | |
30 | #include <sys/kernel.h> | |
31 | #include <sys/malloc.h> | |
32 | #include <sys/ubc.h> | |
33 | #include <sys/quota.h> | |
34 | ||
35 | #include <miscfs/specfs/specdev.h> | |
36 | #include <miscfs/fifofs/fifo.h> | |
37 | ||
38 | #include <hfs/hfs.h> | |
39 | #include <hfs/hfs_catalog.h> | |
40 | #include <hfs/hfs_cnode.h> | |
41 | #include <hfs/hfs_quota.h> | |
42 | ||
43 | extern int prtactive; | |
44 | ||
45 | ||
46 | extern void hfs_relnamehints(struct cnode *dcp); | |
47 | ||
48 | ||
49 | /* | |
50 | * Last reference to an cnode. If necessary, write or delete it. | |
51 | */ | |
52 | __private_extern__ | |
53 | int | |
54 | hfs_inactive(ap) | |
55 | struct vop_inactive_args /* { | |
56 | struct vnode *a_vp; | |
57 | } */ *ap; | |
58 | { | |
59 | struct vnode *vp = ap->a_vp; | |
60 | struct cnode *cp = VTOC(vp); | |
61 | struct hfsmount *hfsmp = VTOHFS(vp); | |
62 | struct proc *p = ap->a_p; | |
63 | struct timeval tv; | |
64 | int error = 0; | |
65 | int recycle = 0; | |
66 | int forkcount = 0; | |
67 | int truncated = 0; | |
b4c24cb9 | 68 | int started_tr = 0, grabbed_lock = 0; |
55e303ae A |
69 | cat_cookie_t cookie; |
70 | int cat_reserve = 0; | |
9bccf70c A |
71 | |
72 | if (prtactive && vp->v_usecount != 0) | |
73 | vprint("hfs_inactive: pushing active", vp); | |
74 | ||
75 | /* | |
76 | * Ignore nodes related to stale file handles. | |
77 | */ | |
78 | if (cp->c_mode == 0) | |
79 | goto out; | |
80 | ||
55e303ae | 81 | if (hfsmp->hfs_flags & HFS_READ_ONLY) |
9bccf70c A |
82 | goto out; |
83 | ||
84 | if (cp->c_datafork) | |
85 | ++forkcount; | |
86 | if (cp->c_rsrcfork) | |
87 | ++forkcount; | |
88 | ||
89 | /* If needed, get rid of any fork's data for a deleted file */ | |
55e303ae A |
90 | if ((vp->v_type == VREG) && (cp->c_flag & C_DELETED)) { |
91 | if (VTOF(vp)->ff_blocks != 0) { | |
92 | error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p); | |
93 | if (error) | |
94 | goto out; | |
95 | truncated = 1; | |
96 | } | |
9bccf70c A |
97 | recycle = 1; |
98 | } | |
99 | ||
100 | /* | |
101 | * Check for a postponed deletion. | |
102 | * (only delete cnode when the last fork goes inactive) | |
103 | */ | |
104 | if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) { | |
105 | /* | |
55e303ae | 106 | * Mark cnode in transit so that no one can get this |
9bccf70c A |
107 | * cnode from cnode hash. |
108 | */ | |
109 | SET(cp->c_flag, C_TRANSIT); | |
110 | cp->c_flag &= ~C_DELETED; | |
111 | cp->c_rdev = 0; | |
55e303ae | 112 | |
b4c24cb9 A |
113 | // XXXdbg |
114 | hfs_global_shared_lock_acquire(hfsmp); | |
115 | grabbed_lock = 1; | |
116 | if (hfsmp->jnl) { | |
117 | if (journal_start_transaction(hfsmp->jnl) != 0) { | |
118 | error = EINVAL; | |
119 | goto out; | |
120 | } | |
121 | started_tr = 1; | |
122 | } | |
123 | ||
55e303ae A |
124 | /* |
125 | * Reserve some space in the Catalog file. | |
126 | */ | |
127 | if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) { | |
128 | goto out; | |
129 | } | |
130 | cat_reserve = 1; | |
131 | ||
132 | ||
9bccf70c A |
133 | /* Lock catalog b-tree */ |
134 | error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p); | |
135 | if (error) goto out; | |
136 | ||
137 | if (cp->c_blocks > 0) | |
138 | printf("hfs_inactive: attempting to delete a non-empty file!"); | |
139 | ||
140 | /* | |
141 | * The descriptor name may be zero, | |
142 | * in which case the fileid is used. | |
143 | */ | |
144 | error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr); | |
145 | ||
146 | if (error && truncated && (error != ENXIO)) | |
147 | printf("hfs_inactive: couldn't delete a truncated file!"); | |
148 | ||
149 | /* Update HFS Private Data dir */ | |
150 | if (error == 0) { | |
151 | hfsmp->hfs_privdir_attr.ca_entries--; | |
152 | (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc, | |
153 | &hfsmp->hfs_privdir_attr, NULL, NULL); | |
154 | } | |
155 | ||
156 | /* Unlock catalog b-tree */ | |
157 | (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); | |
158 | if (error) goto out; | |
159 | ||
160 | #if QUOTA | |
d7e50217 | 161 | (void)hfs_chkiq(cp, -1, NOCRED, 0); |
9bccf70c A |
162 | #endif /* QUOTA */ |
163 | ||
164 | cp->c_mode = 0; | |
165 | cp->c_flag |= C_NOEXISTS | C_CHANGE | C_UPDATE; | |
166 | ||
167 | if (error == 0) | |
168 | hfs_volupdate(hfsmp, VOL_RMFILE, 0); | |
169 | } | |
170 | ||
b4c24cb9 A |
171 | if (cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) { |
172 | tv = time; | |
55e303ae A |
173 | // if the only thing being updated is the access time |
174 | // then set the modified bit too so that update will | |
175 | // flush it to disk. otherwise it'll get dropped. | |
176 | if ((cp->c_flag & C_CHANGEMASK) == C_ACCESS) { | |
177 | cp->c_flag |= C_MODIFIED; | |
178 | } | |
b4c24cb9 A |
179 | VOP_UPDATE(vp, &tv, &tv, 0); |
180 | } | |
9bccf70c | 181 | out: |
55e303ae A |
182 | if (cat_reserve) |
183 | cat_postflight(hfsmp, &cookie, p); | |
184 | ||
b4c24cb9 A |
185 | // XXXdbg - have to do this because a goto could have come here |
186 | if (started_tr) { | |
187 | journal_end_transaction(hfsmp->jnl); | |
188 | started_tr = 0; | |
189 | } | |
190 | if (grabbed_lock) { | |
191 | hfs_global_shared_lock_release(hfsmp); | |
192 | } | |
193 | ||
9bccf70c A |
194 | VOP_UNLOCK(vp, 0, p); |
195 | /* | |
196 | * If we are done with the vnode, reclaim it | |
197 | * so that it can be reused immediately. | |
198 | */ | |
199 | if (cp->c_mode == 0 || recycle) | |
200 | vrecycle(vp, (struct slock *)0, p); | |
201 | ||
202 | return (error); | |
203 | } | |
204 | ||
205 | ||
206 | /* | |
207 | * Reclaim a cnode so that it can be used for other purposes. | |
208 | */ | |
209 | __private_extern__ | |
210 | int | |
211 | hfs_reclaim(ap) | |
212 | struct vop_reclaim_args /* { | |
213 | struct vnode *a_vp; | |
214 | } */ *ap; | |
215 | { | |
216 | struct vnode *vp = ap->a_vp; | |
217 | struct cnode *cp = VTOC(vp); | |
218 | struct vnode *devvp = NULL; | |
219 | struct filefork *fp = NULL; | |
220 | struct filefork *altfp = NULL; | |
221 | int i; | |
222 | ||
223 | if (prtactive && vp->v_usecount != 0) | |
224 | vprint("hfs_reclaim(): pushing active", vp); | |
225 | ||
55e303ae A |
226 | /* |
227 | * Keep track of an inactive hot file. | |
228 | */ | |
229 | (void) hfs_addhotfile(vp); | |
230 | ||
231 | devvp = cp->c_devvp; /* For later releasing */ | |
9bccf70c A |
232 | |
233 | /* | |
234 | * Find file fork for this vnode (if any) | |
235 | * Also check if another fork is active | |
236 | */ | |
237 | if ((fp = cp->c_datafork) && (cp->c_vp == vp)) { | |
238 | cp->c_datafork = NULL; | |
239 | cp->c_vp = NULL; | |
240 | altfp = cp->c_rsrcfork; | |
241 | } else if ((fp = cp->c_rsrcfork) && (cp->c_rsrc_vp == vp)) { | |
242 | cp->c_rsrcfork = NULL; | |
243 | cp->c_rsrc_vp = NULL; | |
55e303ae A |
244 | if (VPARENT(vp) == cp->c_vp) { |
245 | cp->c_flag &= ~C_VPREFHELD; | |
246 | } | |
9bccf70c A |
247 | altfp = cp->c_datafork; |
248 | } else { | |
249 | cp->c_vp = NULL; | |
250 | fp = NULL; | |
251 | altfp = NULL; | |
252 | } | |
253 | ||
254 | /* | |
255 | * On the last fork, remove the cnode from its hash chain. | |
256 | */ | |
257 | if (altfp == NULL) | |
258 | hfs_chashremove(cp); | |
259 | ||
260 | /* Release the file fork and related data (can block) */ | |
261 | if (fp) { | |
262 | fp->ff_cp = NULL; | |
263 | /* Dump cached symlink data */ | |
264 | if ((vp->v_type == VLNK) && (fp->ff_symlinkptr != NULL)) { | |
265 | FREE(fp->ff_symlinkptr, M_TEMP); | |
266 | fp->ff_symlinkptr = NULL; | |
267 | } | |
268 | FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK); | |
269 | fp = NULL; | |
270 | } | |
271 | ||
272 | /* | |
273 | * Purge old data structures associated with the cnode. | |
274 | */ | |
275 | cache_purge(vp); | |
276 | if (devvp && altfp == NULL) { | |
277 | cp->c_devvp = NULL; | |
278 | vrele(devvp); | |
279 | } | |
280 | ||
281 | vp->v_data = NULL; | |
282 | ||
283 | /* | |
284 | * If there was only one active fork then we can release the cnode. | |
285 | */ | |
286 | if (altfp == NULL) { | |
287 | #if QUOTA | |
288 | for (i = 0; i < MAXQUOTAS; i++) { | |
289 | if (cp->c_dquot[i] != NODQUOT) { | |
d7e50217 | 290 | dqreclaim(vp, cp->c_dquot[i]); |
9bccf70c A |
291 | cp->c_dquot[i] = NODQUOT; |
292 | } | |
293 | } | |
294 | #endif /* QUOTA */ | |
295 | /* | |
296 | * Free any left over directory indices | |
297 | */ | |
298 | if (vp->v_type == VDIR) | |
299 | hfs_relnamehints(cp); | |
300 | ||
301 | /* | |
302 | * If the descriptor has a name then release it | |
303 | */ | |
304 | if (cp->c_desc.cd_flags & CD_HASBUF) { | |
305 | char *nameptr; | |
306 | ||
307 | nameptr = cp->c_desc.cd_nameptr; | |
308 | cp->c_desc.cd_nameptr = 0; | |
309 | cp->c_desc.cd_flags &= ~CD_HASBUF; | |
310 | cp->c_desc.cd_namelen = 0; | |
55e303ae | 311 | remove_name(nameptr); |
9bccf70c A |
312 | } |
313 | CLR(cp->c_flag, (C_ALLOC | C_TRANSIT)); | |
314 | if (ISSET(cp->c_flag, C_WALLOC) || ISSET(cp->c_flag, C_WTRANSIT)) | |
315 | wakeup(cp); | |
316 | FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE); | |
317 | ||
318 | } | |
319 | ||
320 | return (0); | |
321 | } | |
322 | ||
323 | ||
324 | /* | |
325 | * get a cnode | |
326 | * | |
327 | * called by hfs_lookup and hfs_vget (descp == NULL) | |
328 | * | |
329 | * returns a locked vnode for cnode for given cnid/fileid | |
330 | */ | |
331 | __private_extern__ | |
332 | int | |
333 | hfs_getcnode(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *descp, int wantrsrc, | |
334 | struct cat_attr *attrp, struct cat_fork *forkp, struct vnode **vpp) | |
335 | { | |
336 | dev_t dev = hfsmp->hfs_raw_dev; | |
337 | struct vnode *vp = NULL; | |
338 | struct vnode *rvp = NULL; | |
339 | struct vnode *new_vp = NULL; | |
340 | struct cnode *cp = NULL; | |
341 | struct proc *p = current_proc(); | |
342 | int retval = E_NONE; | |
343 | ||
344 | /* Check if unmount in progress */ | |
345 | if (HFSTOVFS(hfsmp)->mnt_kern_flag & MNTK_UNMOUNT) { | |
346 | *vpp = NULL; | |
347 | return (EPERM); | |
348 | } | |
349 | ||
350 | /* | |
351 | * Check the hash for an active cnode | |
352 | */ | |
353 | cp = hfs_chashget(dev, cnid, wantrsrc, &vp, &rvp); | |
354 | if (cp != NULL) { | |
355 | /* hide open files that have been deleted */ | |
55e303ae A |
356 | if ((hfsmp->hfs_privdir_desc.cd_cnid != 0) |
357 | && (cp->c_parentcnid == hfsmp->hfs_privdir_desc.cd_cnid) | |
9bccf70c A |
358 | && (cp->c_nlink == 0)) { |
359 | retval = ENOENT; | |
360 | goto exit; | |
361 | } | |
b4c24cb9 A |
362 | |
363 | /* Hide private journal files */ | |
364 | if (hfsmp->jnl && | |
365 | (cp->c_parentcnid == kRootDirID) && | |
366 | ((cp->c_cnid == hfsmp->hfs_jnlfileid) || | |
367 | (cp->c_cnid == hfsmp->hfs_jnlinfoblkid))) { | |
368 | retval = ENOENT; | |
369 | goto exit; | |
370 | } | |
371 | ||
9bccf70c A |
372 | if (wantrsrc && rvp != NULL) { |
373 | vp = rvp; | |
374 | rvp = NULL; | |
375 | goto done; | |
376 | } | |
377 | if (!wantrsrc && vp != NULL) { | |
378 | /* Hardlinks need an updated catalog descriptor */ | |
379 | if (descp && cp->c_flag & C_HARDLINK) { | |
380 | replace_desc(cp, descp); | |
381 | } | |
382 | /* We have a vnode so we're done. */ | |
383 | goto done; | |
384 | } | |
385 | } | |
386 | ||
387 | /* | |
388 | * There was no active vnode so get a new one. | |
389 | * Use the existing cnode (if any). | |
390 | */ | |
391 | if (descp != NULL) { | |
392 | /* | |
393 | * hfs_lookup case, use descp, attrp and forkp | |
394 | */ | |
395 | retval = hfs_getnewvnode(hfsmp, cp, descp, wantrsrc, attrp, | |
396 | forkp, &new_vp); | |
397 | } else { | |
398 | struct cat_desc cndesc = {0}; | |
399 | struct cat_attr cnattr = {0}; | |
400 | struct cat_fork cnfork = {0}; | |
401 | ||
402 | /* | |
403 | * hfs_vget case, need to lookup entry (by file id) | |
404 | */ | |
405 | if (cnid == kRootParID) { | |
406 | static char hfs_rootname[] = "/"; | |
407 | ||
408 | cndesc.cd_nameptr = &hfs_rootname[0]; | |
409 | cndesc.cd_namelen = 1; | |
410 | cndesc.cd_parentcnid = kRootParID; | |
411 | cndesc.cd_cnid = kRootParID; | |
412 | cndesc.cd_flags = CD_ISDIR; | |
413 | ||
414 | cnattr.ca_fileid = kRootParID; | |
415 | cnattr.ca_nlink = 2; | |
55e303ae | 416 | cnattr.ca_entries = 1; |
9bccf70c A |
417 | cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO); |
418 | } else { | |
419 | /* Lock catalog b-tree */ | |
420 | retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p); | |
421 | if (retval) | |
422 | goto exit; | |
423 | ||
424 | retval = cat_idlookup(hfsmp, cnid, &cndesc, &cnattr, &cnfork); | |
425 | ||
426 | /* Unlock catalog b-tree */ | |
427 | (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); | |
428 | if (retval) | |
429 | goto exit; | |
430 | ||
431 | /* Hide open files that have been deleted */ | |
55e303ae A |
432 | if ((hfsmp->hfs_privdir_desc.cd_cnid != 0) && |
433 | (cndesc.cd_parentcnid == hfsmp->hfs_privdir_desc.cd_cnid) && | |
434 | (cnattr.ca_nlink == 0)) { | |
9bccf70c A |
435 | cat_releasedesc(&cndesc); |
436 | retval = ENOENT; | |
437 | goto exit; | |
438 | } | |
439 | } | |
440 | ||
441 | retval = hfs_getnewvnode(hfsmp, cp, &cndesc, 0, &cnattr, &cnfork, &new_vp); | |
442 | ||
443 | /* Hardlinks may need an updated catalog descriptor */ | |
444 | if (retval == 0 | |
445 | && new_vp | |
446 | && (VTOC(new_vp)->c_flag & C_HARDLINK) | |
447 | && cndesc.cd_nameptr | |
448 | && cndesc.cd_namelen > 0) { | |
449 | replace_desc(VTOC(new_vp), &cndesc); | |
450 | } | |
55e303ae | 451 | |
9bccf70c A |
452 | cat_releasedesc(&cndesc); |
453 | } | |
55e303ae | 454 | |
9bccf70c A |
455 | exit: |
456 | /* Release reference taken on opposite vnode (if any). */ | |
457 | if (vp) | |
55e303ae | 458 | vrele(vp); |
9bccf70c | 459 | else if (rvp) |
55e303ae | 460 | vrele(rvp); |
9bccf70c A |
461 | |
462 | if (retval) { | |
463 | *vpp = NULL; | |
464 | return (retval); | |
465 | } | |
466 | vp = new_vp; | |
467 | done: | |
468 | /* The cnode's vnode should be in vp. */ | |
469 | if (vp == NULL) | |
470 | panic("hfs_getcnode: missing vp!"); | |
471 | ||
55e303ae A |
472 | if (UBCISVALID(vp)) |
473 | UBCINFOCHECK("hfs_getcnode", vp); | |
9bccf70c A |
474 | *vpp = vp; |
475 | return (0); | |
476 | } | |
477 | ||
478 | ||
479 | /* | |
480 | * hfs_getnewvnode - get new default vnode | |
481 | * | |
482 | * the vnode is returned locked | |
483 | */ | |
484 | extern int (**hfs_vnodeop_p) (void *); | |
485 | extern int (**hfs_specop_p) (void *); | |
486 | extern int (**hfs_fifoop_p) (void *); | |
487 | ||
488 | __private_extern__ | |
489 | int | |
490 | hfs_getnewvnode(struct hfsmount *hfsmp, struct cnode *cp, | |
491 | struct cat_desc *descp, int wantrsrc, | |
492 | struct cat_attr *attrp, struct cat_fork *forkp, | |
493 | struct vnode **vpp) | |
494 | { | |
495 | struct mount *mp = HFSTOVFS(hfsmp); | |
496 | struct vnode *vp = NULL; | |
497 | struct vnode *rvp = NULL; | |
498 | struct vnode *new_vp = NULL; | |
499 | struct cnode *cp2 = NULL; | |
500 | struct filefork *fp = NULL; | |
501 | int allocated = 0; | |
502 | int i; | |
503 | int retval; | |
504 | dev_t dev; | |
505 | struct proc *p = current_proc(); | |
55e303ae | 506 | #if 0 |
9bccf70c A |
507 | /* Bail when unmount is in progress */ |
508 | if (mp->mnt_kern_flag & MNTK_UNMOUNT) { | |
509 | *vpp = NULL; | |
510 | return (EPERM); | |
511 | } | |
55e303ae | 512 | #endif |
9bccf70c A |
513 | |
514 | #if !FIFO | |
515 | if (IFTOVT(attrp->ca_mode) == VFIFO) { | |
516 | *vpp = NULL; | |
517 | return (EOPNOTSUPP); | |
518 | } | |
519 | #endif | |
520 | dev = hfsmp->hfs_raw_dev; | |
521 | ||
522 | /* If no cnode was passed in then create one */ | |
523 | if (cp == NULL) { | |
524 | MALLOC_ZONE(cp2, struct cnode *, sizeof(struct cnode), | |
525 | M_HFSNODE, M_WAITOK); | |
526 | bzero(cp2, sizeof(struct cnode)); | |
527 | allocated = 1; | |
528 | SET(cp2->c_flag, C_ALLOC); | |
529 | cp2->c_cnid = descp->cd_cnid; | |
530 | cp2->c_fileid = attrp->ca_fileid; | |
55e303ae A |
531 | if (cp2->c_fileid == 0) { |
532 | FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE); | |
533 | *vpp = NULL; | |
534 | return (ENOENT); | |
535 | } | |
9bccf70c A |
536 | cp2->c_dev = dev; |
537 | lockinit(&cp2->c_lock, PINOD, "cnode", 0, 0); | |
538 | (void) lockmgr(&cp2->c_lock, LK_EXCLUSIVE, (struct slock *)0, p); | |
539 | /* | |
540 | * There were several blocking points since we first | |
541 | * checked the hash. Now that we're through blocking, | |
542 | * check the hash again in case we're racing for the | |
543 | * same cnode. | |
544 | */ | |
545 | cp = hfs_chashget(dev, attrp->ca_fileid, wantrsrc, &vp, &rvp); | |
546 | if (cp != NULL) { | |
547 | /* We lost the race - use the winner's cnode */ | |
548 | FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE); | |
549 | allocated = 0; | |
550 | if (wantrsrc && rvp != NULL) { | |
551 | *vpp = rvp; | |
552 | return (0); | |
553 | } | |
554 | if (!wantrsrc && vp != NULL) { | |
555 | *vpp = vp; | |
556 | return (0); | |
557 | } | |
558 | } else /* allocated */ { | |
559 | cp = cp2; | |
560 | hfs_chashinsert(cp); | |
561 | } | |
562 | } | |
563 | ||
564 | /* Allocate a new vnode. If unsuccesful, leave after freeing memory */ | |
565 | if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &new_vp))) { | |
566 | if (allocated) { | |
567 | hfs_chashremove(cp); | |
568 | if (ISSET(cp->c_flag, C_WALLOC)) { | |
569 | CLR(cp->c_flag, C_WALLOC); | |
570 | wakeup(cp); | |
571 | } | |
572 | FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE); | |
573 | allocated = 0; | |
574 | } else if (rvp) { | |
575 | vput(rvp); | |
576 | } else if (vp) { | |
577 | vput(vp); | |
578 | } | |
579 | *vpp = NULL; | |
580 | return (retval); | |
581 | } | |
582 | if (allocated) { | |
583 | bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr)); | |
584 | bcopy(descp, &cp->c_desc, sizeof(struct cat_desc)); | |
585 | } | |
586 | new_vp->v_data = cp; | |
587 | if (wantrsrc && S_ISREG(cp->c_mode)) | |
588 | cp->c_rsrc_vp = new_vp; | |
589 | else | |
590 | cp->c_vp = new_vp; | |
591 | ||
592 | /* Release reference taken on opposite vnode (if any). */ | |
593 | if (rvp) | |
55e303ae | 594 | vrele(rvp); |
9bccf70c | 595 | if (vp) |
55e303ae | 596 | vrele(vp); |
9bccf70c A |
597 | |
598 | vp = new_vp; | |
599 | vp->v_ubcinfo = UBC_NOINFO; | |
600 | ||
601 | /* | |
602 | * If this is a new cnode then initialize it using descp and attrp... | |
603 | */ | |
604 | if (allocated) { | |
605 | /* The name was inherited so clear descriptor state... */ | |
606 | descp->cd_namelen = 0; | |
607 | descp->cd_nameptr = NULL; | |
608 | descp->cd_flags &= ~CD_HASBUF; | |
609 | ||
610 | /* Tag hardlinks */ | |
611 | if (IFTOVT(cp->c_mode) == VREG && | |
612 | (descp->cd_cnid != attrp->ca_fileid)) { | |
613 | cp->c_flag |= C_HARDLINK; | |
614 | } | |
615 | ||
616 | /* Take one dev reference for each non-directory cnode */ | |
617 | if (IFTOVT(cp->c_mode) != VDIR) { | |
618 | cp->c_devvp = hfsmp->hfs_devvp; | |
619 | VREF(cp->c_devvp); | |
620 | } | |
621 | #if QUOTA | |
622 | for (i = 0; i < MAXQUOTAS; i++) | |
623 | cp->c_dquot[i] = NODQUOT; | |
624 | #endif /* QUOTA */ | |
625 | } | |
626 | ||
627 | if (IFTOVT(cp->c_mode) != VDIR) { | |
628 | if (forkp && attrp->ca_blocks < forkp->cf_blocks) | |
629 | panic("hfs_getnewvnode: bad ca_blocks (too small)"); | |
630 | /* | |
631 | * Allocate and initialize a file fork... | |
632 | */ | |
633 | MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork), | |
634 | M_HFSFORK, M_WAITOK); | |
635 | bzero(fp, sizeof(struct filefork)); | |
636 | fp->ff_cp = cp; | |
637 | if (forkp) | |
55e303ae | 638 | bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork)); |
9bccf70c A |
639 | rl_init(&fp->ff_invalidranges); |
640 | if (wantrsrc) { | |
641 | if (cp->c_rsrcfork != NULL) | |
642 | panic("stale rsrc fork"); | |
643 | cp->c_rsrcfork = fp; | |
644 | } else { | |
645 | if (cp->c_datafork != NULL) | |
646 | panic("stale data fork"); | |
647 | cp->c_datafork = fp; | |
648 | } | |
649 | } | |
650 | ||
651 | /* | |
652 | * Finish vnode initialization. | |
653 | * Setting the v_type 'stamps' the vnode as 'complete', | |
654 | * so should be done almost last. | |
655 | * | |
656 | * At this point the vnode should be locked and fully | |
657 | * allocated. And ready to be used or accessed. (though | |
658 | * having it locked prevents most of this, it can still | |
659 | * be accessed through lists and hashes). | |
660 | */ | |
661 | vp->v_type = IFTOVT(cp->c_mode); | |
662 | ||
663 | /* Tag system files */ | |
55e303ae | 664 | if ((descp->cd_flags & CD_ISMETA) && (vp->v_type == VREG)) |
9bccf70c A |
665 | vp->v_flag |= VSYSTEM; |
666 | /* Tag root directory */ | |
667 | if (cp->c_cnid == kRootDirID) | |
668 | vp->v_flag |= VROOT; | |
669 | ||
670 | if ((vp->v_type == VREG) && !(vp->v_flag & VSYSTEM) | |
671 | && (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) { | |
672 | ubc_info_init(vp); | |
673 | } else { | |
674 | vp->v_ubcinfo = UBC_NOINFO; | |
675 | } | |
676 | ||
677 | if (vp->v_type == VCHR || vp->v_type == VBLK) { | |
678 | struct vnode *nvp; | |
679 | ||
680 | vp->v_op = hfs_specop_p; | |
681 | if ((nvp = checkalias(vp, cp->c_rdev, mp))) { | |
682 | /* | |
683 | * Discard unneeded vnode, but save its cnode. | |
684 | * Note that the lock is carried over in the | |
685 | * cnode to the replacement vnode. | |
686 | */ | |
687 | nvp->v_data = vp->v_data; | |
688 | vp->v_data = NULL; | |
689 | vp->v_op = spec_vnodeop_p; | |
690 | vrele(vp); | |
691 | vgone(vp); | |
692 | /* | |
693 | * Reinitialize aliased cnode. | |
694 | * Assume its not a resource fork. | |
695 | */ | |
696 | cp->c_vp = nvp; | |
697 | vp = nvp; | |
698 | } | |
699 | } else if (vp->v_type == VFIFO) { | |
700 | #if FIFO | |
701 | vp->v_op = hfs_fifoop_p; | |
702 | #endif | |
703 | } | |
704 | ||
55e303ae A |
705 | /* |
706 | * Stop tracking an active hot file. | |
707 | */ | |
708 | (void) hfs_removehotfile(vp); | |
709 | ||
9bccf70c A |
710 | /* Vnode is now initialized - see if anyone was waiting for it. */ |
711 | CLR(cp->c_flag, C_ALLOC); | |
712 | if (ISSET(cp->c_flag, C_WALLOC)) { | |
713 | CLR(cp->c_flag, C_WALLOC); | |
714 | wakeup((caddr_t)cp); | |
715 | } | |
716 | ||
717 | *vpp = vp; | |
718 | return (0); | |
719 | } | |
720 |