]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/hfs/hfs_cnode.c
xnu-344.32.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_cnode.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
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
40extern int prtactive;
41
42
43extern 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__
50int
51hfs_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;
65 int started_tr = 0, grabbed_lock = 0;
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 */
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);
92 recycle = 1;
93 if (error) goto out;
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
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
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
148 (void)hfs_chkiq(cp, -1, NOCRED, 0);
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 }
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 }
169out:
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
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__
195int
196hfs_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) {
267 dqreclaim(vp, cp->c_dquot[i]);
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__
309int
310hfs_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 }
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
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 }
428exit:
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;
440done:
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 */
456extern int (**hfs_vnodeop_p) (void *);
457extern int (**hfs_specop_p) (void *);
458extern int (**hfs_fifoop_p) (void *);
459
460__private_extern__
461int
462hfs_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