]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_cnode.c
b644dced2ba621702c1b2c0ec69b5f1a28e58512
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/param.h>
26 #include <sys/systm.h>
28 #include <sys/vnode.h>
29 #include <sys/mount.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
33 #include <sys/quota.h>
35 #include <miscfs/specfs/specdev.h>
36 #include <miscfs/fifofs/fifo.h>
39 #include <hfs/hfs_catalog.h>
40 #include <hfs/hfs_cnode.h>
41 #include <hfs/hfs_quota.h>
46 extern void hfs_relnamehints(struct cnode
*dcp
);
50 * Last reference to an cnode. If necessary, write or delete it.
55 struct vop_inactive_args
/* {
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
;
68 int started_tr
= 0, grabbed_lock
= 0;
72 if (prtactive
&& vp
->v_usecount
!= 0)
73 vprint("hfs_inactive: pushing active", vp
);
76 * Ignore nodes related to stale file handles.
81 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
)
89 /* If needed, get rid of any fork's data for a deleted file */
90 if ((vp
->v_type
== VREG
) && (cp
->c_flag
& C_DELETED
)) {
91 if (VTOF(vp
)->ff_blocks
!= 0) {
92 error
= VOP_TRUNCATE(vp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
101 * Check for a postponed deletion.
102 * (only delete cnode when the last fork goes inactive)
104 if ((cp
->c_flag
& C_DELETED
) && (forkcount
<= 1)) {
106 * Mark cnode in transit so that no one can get this
107 * cnode from cnode hash.
109 SET(cp
->c_flag
, C_TRANSIT
);
110 cp
->c_flag
&= ~C_DELETED
;
114 hfs_global_shared_lock_acquire(hfsmp
);
117 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
125 * Reserve some space in the Catalog file.
127 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
))) {
133 /* Lock catalog b-tree */
134 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
137 if (cp
->c_blocks
> 0)
138 printf("hfs_inactive: attempting to delete a non-empty file!");
141 * The descriptor name may be zero,
142 * in which case the fileid is used.
144 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
146 if (error
&& truncated
&& (error
!= ENXIO
))
147 printf("hfs_inactive: couldn't delete a truncated file!");
149 /* Update HFS Private Data dir */
151 hfsmp
->hfs_privdir_attr
.ca_entries
--;
152 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
153 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
156 /* Unlock catalog b-tree */
157 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
161 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
165 cp
->c_flag
|= C_NOEXISTS
| C_CHANGE
| C_UPDATE
;
168 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
171 if (cp
->c_flag
& (C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
)) {
173 // if the only thing being updated is the access time
174 // then set the modified bit too so that update will
175 // flush it to disk. otherwise it'll get dropped.
176 if ((cp
->c_flag
& C_CHANGEMASK
) == C_ACCESS
) {
177 cp
->c_flag
|= C_MODIFIED
;
179 VOP_UPDATE(vp
, &tv
, &tv
, 0);
183 cat_postflight(hfsmp
, &cookie
, p
);
185 // XXXdbg - have to do this because a goto could have come here
187 journal_end_transaction(hfsmp
->jnl
);
191 hfs_global_shared_lock_release(hfsmp
);
194 VOP_UNLOCK(vp
, 0, p
);
196 * If we are done with the vnode, reclaim it
197 * so that it can be reused immediately.
199 if (cp
->c_mode
== 0 || recycle
)
200 vrecycle(vp
, (struct slock
*)0, p
);
207 * Reclaim a cnode so that it can be used for other purposes.
212 struct vop_reclaim_args
/* {
216 struct vnode
*vp
= ap
->a_vp
;
217 struct cnode
*cp
= VTOC(vp
);
218 struct vnode
*devvp
= NULL
;
219 struct filefork
*fp
= NULL
;
220 struct filefork
*altfp
= NULL
;
223 if (prtactive
&& vp
->v_usecount
!= 0)
224 vprint("hfs_reclaim(): pushing active", vp
);
227 * Keep track of an inactive hot file.
229 (void) hfs_addhotfile(vp
);
231 devvp
= cp
->c_devvp
; /* For later releasing */
234 * Find file fork for this vnode (if any)
235 * Also check if another fork is active
237 if ((fp
= cp
->c_datafork
) && (cp
->c_vp
== vp
)) {
238 cp
->c_datafork
= NULL
;
240 altfp
= cp
->c_rsrcfork
;
241 } else if ((fp
= cp
->c_rsrcfork
) && (cp
->c_rsrc_vp
== vp
)) {
242 cp
->c_rsrcfork
= NULL
;
243 cp
->c_rsrc_vp
= NULL
;
244 if (VPARENT(vp
) == cp
->c_vp
) {
245 cp
->c_flag
&= ~C_VPREFHELD
;
247 altfp
= cp
->c_datafork
;
255 * On the last fork, remove the cnode from its hash chain.
260 /* Release the file fork and related data (can block) */
263 /* Dump cached symlink data */
264 if ((vp
->v_type
== VLNK
) && (fp
->ff_symlinkptr
!= NULL
)) {
265 FREE(fp
->ff_symlinkptr
, M_TEMP
);
266 fp
->ff_symlinkptr
= NULL
;
268 FREE_ZONE(fp
, sizeof(struct filefork
), M_HFSFORK
);
273 * Purge old data structures associated with the cnode.
276 if (devvp
&& altfp
== NULL
) {
284 * If there was only one active fork then we can release the cnode.
288 for (i
= 0; i
< MAXQUOTAS
; i
++) {
289 if (cp
->c_dquot
[i
] != NODQUOT
) {
290 dqreclaim(vp
, cp
->c_dquot
[i
]);
291 cp
->c_dquot
[i
] = NODQUOT
;
296 * Free any left over directory indices
298 if (vp
->v_type
== VDIR
)
299 hfs_relnamehints(cp
);
302 * If the descriptor has a name then release it
304 if (cp
->c_desc
.cd_flags
& CD_HASBUF
) {
307 nameptr
= cp
->c_desc
.cd_nameptr
;
308 cp
->c_desc
.cd_nameptr
= 0;
309 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
310 cp
->c_desc
.cd_namelen
= 0;
311 remove_name(nameptr
);
313 CLR(cp
->c_flag
, (C_ALLOC
| C_TRANSIT
));
314 if (ISSET(cp
->c_flag
, C_WALLOC
) || ISSET(cp
->c_flag
, C_WTRANSIT
))
316 FREE_ZONE(cp
, sizeof(struct cnode
), M_HFSNODE
);
327 * called by hfs_lookup and hfs_vget (descp == NULL)
329 * returns a locked vnode for cnode for given cnid/fileid
333 hfs_getcnode(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*descp
, int wantrsrc
,
334 struct cat_attr
*attrp
, struct cat_fork
*forkp
, struct vnode
**vpp
)
336 dev_t dev
= hfsmp
->hfs_raw_dev
;
337 struct vnode
*vp
= NULL
;
338 struct vnode
*rvp
= NULL
;
339 struct vnode
*new_vp
= NULL
;
340 struct cnode
*cp
= NULL
;
341 struct proc
*p
= current_proc();
344 /* Check if unmount in progress */
345 if (HFSTOVFS(hfsmp
)->mnt_kern_flag
& MNTK_UNMOUNT
) {
351 * Check the hash for an active cnode
353 cp
= hfs_chashget(dev
, cnid
, wantrsrc
, &vp
, &rvp
);
355 /* hide open files that have been deleted */
356 if ((hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
357 && (cp
->c_parentcnid
== hfsmp
->hfs_privdir_desc
.cd_cnid
)
358 && (cp
->c_nlink
== 0)) {
363 /* Hide private journal files */
365 (cp
->c_parentcnid
== kRootDirID
) &&
366 ((cp
->c_cnid
== hfsmp
->hfs_jnlfileid
) ||
367 (cp
->c_cnid
== hfsmp
->hfs_jnlinfoblkid
))) {
372 if (wantrsrc
&& rvp
!= NULL
) {
377 if (!wantrsrc
&& vp
!= NULL
) {
378 /* Hardlinks need an updated catalog descriptor */
379 if (descp
&& cp
->c_flag
& C_HARDLINK
) {
380 replace_desc(cp
, descp
);
382 /* We have a vnode so we're done. */
388 * There was no active vnode so get a new one.
389 * Use the existing cnode (if any).
393 * hfs_lookup case, use descp, attrp and forkp
395 retval
= hfs_getnewvnode(hfsmp
, cp
, descp
, wantrsrc
, attrp
,
398 struct cat_desc cndesc
= {0};
399 struct cat_attr cnattr
= {0};
400 struct cat_fork cnfork
= {0};
403 * hfs_vget case, need to lookup entry (by file id)
405 if (cnid
== kRootParID
) {
406 static char hfs_rootname
[] = "/";
408 cndesc
.cd_nameptr
= &hfs_rootname
[0];
409 cndesc
.cd_namelen
= 1;
410 cndesc
.cd_parentcnid
= kRootParID
;
411 cndesc
.cd_cnid
= kRootParID
;
412 cndesc
.cd_flags
= CD_ISDIR
;
414 cnattr
.ca_fileid
= kRootParID
;
416 cnattr
.ca_entries
= 1;
417 cnattr
.ca_mode
= (S_IFDIR
| S_IRWXU
| S_IRWXG
| S_IRWXO
);
419 /* Lock catalog b-tree */
420 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
424 retval
= cat_idlookup(hfsmp
, cnid
, &cndesc
, &cnattr
, &cnfork
);
426 /* Unlock catalog b-tree */
427 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
431 /* Hide open files that have been deleted */
432 if ((hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0) &&
433 (cndesc
.cd_parentcnid
== hfsmp
->hfs_privdir_desc
.cd_cnid
) &&
434 (cnattr
.ca_nlink
== 0)) {
435 cat_releasedesc(&cndesc
);
441 retval
= hfs_getnewvnode(hfsmp
, cp
, &cndesc
, 0, &cnattr
, &cnfork
, &new_vp
);
443 /* Hardlinks may need an updated catalog descriptor */
446 && (VTOC(new_vp
)->c_flag
& C_HARDLINK
)
448 && cndesc
.cd_namelen
> 0) {
449 replace_desc(VTOC(new_vp
), &cndesc
);
452 cat_releasedesc(&cndesc
);
456 /* Release reference taken on opposite vnode (if any). */
468 /* The cnode's vnode should be in vp. */
470 panic("hfs_getcnode: missing vp!");
473 UBCINFOCHECK("hfs_getcnode", vp
);
480 * hfs_getnewvnode - get new default vnode
482 * the vnode is returned locked
484 extern int (**hfs_vnodeop_p
) (void *);
485 extern int (**hfs_specop_p
) (void *);
486 extern int (**hfs_fifoop_p
) (void *);
490 hfs_getnewvnode(struct hfsmount
*hfsmp
, struct cnode
*cp
,
491 struct cat_desc
*descp
, int wantrsrc
,
492 struct cat_attr
*attrp
, struct cat_fork
*forkp
,
495 struct mount
*mp
= HFSTOVFS(hfsmp
);
496 struct vnode
*vp
= NULL
;
497 struct vnode
*rvp
= NULL
;
498 struct vnode
*new_vp
= NULL
;
499 struct cnode
*cp2
= NULL
;
500 struct filefork
*fp
= NULL
;
505 struct proc
*p
= current_proc();
507 /* Bail when unmount is in progress */
508 if (mp
->mnt_kern_flag
& MNTK_UNMOUNT
) {
515 if (IFTOVT(attrp
->ca_mode
) == VFIFO
) {
520 dev
= hfsmp
->hfs_raw_dev
;
522 /* If no cnode was passed in then create one */
524 MALLOC_ZONE(cp2
, struct cnode
*, sizeof(struct cnode
),
525 M_HFSNODE
, M_WAITOK
);
526 bzero(cp2
, sizeof(struct cnode
));
528 SET(cp2
->c_flag
, C_ALLOC
);
529 cp2
->c_cnid
= descp
->cd_cnid
;
530 cp2
->c_fileid
= attrp
->ca_fileid
;
531 if (cp2
->c_fileid
== 0) {
532 FREE_ZONE(cp2
, sizeof(struct cnode
), M_HFSNODE
);
537 lockinit(&cp2
->c_lock
, PINOD
, "cnode", 0, 0);
538 (void) lockmgr(&cp2
->c_lock
, LK_EXCLUSIVE
, (struct slock
*)0, p
);
540 * There were several blocking points since we first
541 * checked the hash. Now that we're through blocking,
542 * check the hash again in case we're racing for the
545 cp
= hfs_chashget(dev
, attrp
->ca_fileid
, wantrsrc
, &vp
, &rvp
);
547 /* We lost the race - use the winner's cnode */
548 FREE_ZONE(cp2
, sizeof(struct cnode
), M_HFSNODE
);
550 if (wantrsrc
&& rvp
!= NULL
) {
554 if (!wantrsrc
&& vp
!= NULL
) {
558 } else /* allocated */ {
564 /* Allocate a new vnode. If unsuccesful, leave after freeing memory */
565 if ((retval
= getnewvnode(VT_HFS
, mp
, hfs_vnodeop_p
, &new_vp
))) {
568 if (ISSET(cp
->c_flag
, C_WALLOC
)) {
569 CLR(cp
->c_flag
, C_WALLOC
);
572 FREE_ZONE(cp2
, sizeof(struct cnode
), M_HFSNODE
);
583 bcopy(attrp
, &cp
->c_attr
, sizeof(struct cat_attr
));
584 bcopy(descp
, &cp
->c_desc
, sizeof(struct cat_desc
));
587 if (wantrsrc
&& S_ISREG(cp
->c_mode
))
588 cp
->c_rsrc_vp
= new_vp
;
592 /* Release reference taken on opposite vnode (if any). */
599 vp
->v_ubcinfo
= UBC_NOINFO
;
602 * If this is a new cnode then initialize it using descp and attrp...
605 /* The name was inherited so clear descriptor state... */
606 descp
->cd_namelen
= 0;
607 descp
->cd_nameptr
= NULL
;
608 descp
->cd_flags
&= ~CD_HASBUF
;
611 if (IFTOVT(cp
->c_mode
) == VREG
&&
612 (descp
->cd_cnid
!= attrp
->ca_fileid
)) {
613 cp
->c_flag
|= C_HARDLINK
;
616 /* Take one dev reference for each non-directory cnode */
617 if (IFTOVT(cp
->c_mode
) != VDIR
) {
618 cp
->c_devvp
= hfsmp
->hfs_devvp
;
622 for (i
= 0; i
< MAXQUOTAS
; i
++)
623 cp
->c_dquot
[i
] = NODQUOT
;
627 if (IFTOVT(cp
->c_mode
) != VDIR
) {
628 if (forkp
&& attrp
->ca_blocks
< forkp
->cf_blocks
)
629 panic("hfs_getnewvnode: bad ca_blocks (too small)");
631 * Allocate and initialize a file fork...
633 MALLOC_ZONE(fp
, struct filefork
*, sizeof(struct filefork
),
634 M_HFSFORK
, M_WAITOK
);
635 bzero(fp
, sizeof(struct filefork
));
638 bcopy(forkp
, &fp
->ff_data
, sizeof(struct cat_fork
));
639 rl_init(&fp
->ff_invalidranges
);
641 if (cp
->c_rsrcfork
!= NULL
)
642 panic("stale rsrc fork");
645 if (cp
->c_datafork
!= NULL
)
646 panic("stale data fork");
652 * Finish vnode initialization.
653 * Setting the v_type 'stamps' the vnode as 'complete',
654 * so should be done almost last.
656 * At this point the vnode should be locked and fully
657 * allocated. And ready to be used or accessed. (though
658 * having it locked prevents most of this, it can still
659 * be accessed through lists and hashes).
661 vp
->v_type
= IFTOVT(cp
->c_mode
);
663 /* Tag system files */
664 if ((descp
->cd_flags
& CD_ISMETA
) && (vp
->v_type
== VREG
))
665 vp
->v_flag
|= VSYSTEM
;
666 /* Tag root directory */
667 if (cp
->c_cnid
== kRootDirID
)
670 if ((vp
->v_type
== VREG
) && !(vp
->v_flag
& VSYSTEM
)
671 && (UBCINFOMISSING(vp
) || UBCINFORECLAIMED(vp
))) {
674 vp
->v_ubcinfo
= UBC_NOINFO
;
677 if (vp
->v_type
== VCHR
|| vp
->v_type
== VBLK
) {
680 vp
->v_op
= hfs_specop_p
;
681 if ((nvp
= checkalias(vp
, cp
->c_rdev
, mp
))) {
683 * Discard unneeded vnode, but save its cnode.
684 * Note that the lock is carried over in the
685 * cnode to the replacement vnode.
687 nvp
->v_data
= vp
->v_data
;
689 vp
->v_op
= spec_vnodeop_p
;
693 * Reinitialize aliased cnode.
694 * Assume its not a resource fork.
699 } else if (vp
->v_type
== VFIFO
) {
701 vp
->v_op
= hfs_fifoop_p
;
706 * Stop tracking an active hot file.
708 (void) hfs_removehotfile(vp
);
710 /* Vnode is now initialized - see if anyone was waiting for it. */
711 CLR(cp
->c_flag
, C_ALLOC
);
712 if (ISSET(cp
->c_flag
, C_WALLOC
)) {
713 CLR(cp
->c_flag
, C_WALLOC
);