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