]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_cnode.c
xnu-344.49.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 ((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);
95 recycle = 1;
96 if (error) goto out;
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
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
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
151 (void)hfs_chkiq(cp, -1, NOCRED, 0);
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 }
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 }
172 out:
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
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) {
270 dqreclaim(vp, cp->c_dquot[i]);
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 }
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
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