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