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