2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
22 #include <sys/param.h>
23 #include <sys/systm.h>
25 #include <sys/vnode.h>
26 #include <sys/mount.h>
27 #include <sys/kernel.h>
28 #include <sys/malloc.h>
30 #include <sys/quota.h>
32 #include <miscfs/specfs/specdev.h>
33 #include <miscfs/fifofs/fifo.h>
36 #include <hfs/hfs_catalog.h>
37 #include <hfs/hfs_cnode.h>
38 #include <hfs/hfs_quota.h>
43 extern void hfs_relnamehints(struct cnode
*dcp
);
47 * Last reference to an cnode. If necessary, write or delete it.
52 struct vop_inactive_args
/* {
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
;
66 if (prtactive
&& vp
->v_usecount
!= 0)
67 vprint("hfs_inactive: pushing active", vp
);
70 * Ignore nodes related to stale file handles.
75 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
83 /* If needed, get rid of any fork's data for a deleted file */
84 if ((cp
->c_flag
& C_DELETED
) &&
86 (VTOF(vp
)->ff_blocks
!= 0)) {
87 error
= VOP_TRUNCATE(vp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
94 * Check for a postponed deletion.
95 * (only delete cnode when the last fork goes inactive)
97 if ((cp
->c_flag
& C_DELETED
) && (forkcount
<= 1)) {
99 * Mark cnode in transit so that one can get this
100 * cnode from cnode hash.
102 SET(cp
->c_flag
, C_TRANSIT
);
103 cp
->c_flag
&= ~C_DELETED
;
106 /* Lock catalog b-tree */
107 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
110 if (cp
->c_blocks
> 0)
111 printf("hfs_inactive: attempting to delete a non-empty file!");
114 * The descriptor name may be zero,
115 * in which case the fileid is used.
117 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
119 if (error
&& truncated
&& (error
!= ENXIO
))
120 printf("hfs_inactive: couldn't delete a truncated file!");
122 /* Update HFS Private Data dir */
124 hfsmp
->hfs_privdir_attr
.ca_entries
--;
125 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
126 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
129 /* Unlock catalog b-tree */
130 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
134 if (!hfs_getinoquota(cp
))
135 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
139 cp
->c_flag
|= C_NOEXISTS
| C_CHANGE
| C_UPDATE
;
142 hfs_volupdate(hfsmp
, VOL_RMFILE
, 0);
145 /* Push any defered access times to disk */
146 if (cp
->c_flag
& C_ATIMEMOD
) {
147 cp
->c_flag
&= ~C_ATIMEMOD
;
148 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSPlusSigWord
)
149 cp
->c_flag
|= C_MODIFIED
;
151 if (cp
->c_flag
& (C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
)) {
153 VOP_UPDATE(vp
, &tv
, &tv
, 0);
156 VOP_UNLOCK(vp
, 0, p
);
158 * If we are done with the vnode, reclaim it
159 * so that it can be reused immediately.
161 if (cp
->c_mode
== 0 || recycle
)
162 vrecycle(vp
, (struct slock
*)0, p
);
169 * Reclaim a cnode so that it can be used for other purposes.
174 struct vop_reclaim_args
/* {
178 struct vnode
*vp
= ap
->a_vp
;
179 struct cnode
*cp
= VTOC(vp
);
180 struct vnode
*devvp
= NULL
;
181 struct filefork
*fp
= NULL
;
182 struct filefork
*altfp
= NULL
;
185 if (prtactive
&& vp
->v_usecount
!= 0)
186 vprint("hfs_reclaim(): pushing active", vp
);
188 devvp
= cp
->c_devvp
; /* For later releasing */
191 * Find file fork for this vnode (if any)
192 * Also check if another fork is active
194 if ((fp
= cp
->c_datafork
) && (cp
->c_vp
== vp
)) {
195 cp
->c_datafork
= NULL
;
197 altfp
= cp
->c_rsrcfork
;
198 } else if ((fp
= cp
->c_rsrcfork
) && (cp
->c_rsrc_vp
== vp
)) {
199 cp
->c_rsrcfork
= NULL
;
200 cp
->c_rsrc_vp
= NULL
;
201 altfp
= cp
->c_datafork
;
209 * On the last fork, remove the cnode from its hash chain.
214 /* Release the file fork and related data (can block) */
217 /* Dump cached symlink data */
218 if ((vp
->v_type
== VLNK
) && (fp
->ff_symlinkptr
!= NULL
)) {
219 FREE(fp
->ff_symlinkptr
, M_TEMP
);
220 fp
->ff_symlinkptr
= NULL
;
222 FREE_ZONE(fp
, sizeof(struct filefork
), M_HFSFORK
);
227 * Purge old data structures associated with the cnode.
230 if (devvp
&& altfp
== NULL
) {
238 * If there was only one active fork then we can release the cnode.
242 for (i
= 0; i
< MAXQUOTAS
; i
++) {
243 if (cp
->c_dquot
[i
] != NODQUOT
) {
244 dqrele(vp
, cp
->c_dquot
[i
]);
245 cp
->c_dquot
[i
] = NODQUOT
;
250 * Free any left over directory indices
252 if (vp
->v_type
== VDIR
)
253 hfs_relnamehints(cp
);
256 * If the descriptor has a name then release it
258 if (cp
->c_desc
.cd_flags
& CD_HASBUF
) {
261 nameptr
= cp
->c_desc
.cd_nameptr
;
262 cp
->c_desc
.cd_nameptr
= 0;
263 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
264 cp
->c_desc
.cd_namelen
= 0;
265 FREE(nameptr
, M_TEMP
);
267 CLR(cp
->c_flag
, (C_ALLOC
| C_TRANSIT
));
268 if (ISSET(cp
->c_flag
, C_WALLOC
) || ISSET(cp
->c_flag
, C_WTRANSIT
))
270 FREE_ZONE(cp
, sizeof(struct cnode
), M_HFSNODE
);
281 * called by hfs_lookup and hfs_vget (descp == NULL)
283 * returns a locked vnode for cnode for given cnid/fileid
287 hfs_getcnode(struct hfsmount
*hfsmp
, cnid_t cnid
, struct cat_desc
*descp
, int wantrsrc
,
288 struct cat_attr
*attrp
, struct cat_fork
*forkp
, struct vnode
**vpp
)
290 dev_t dev
= hfsmp
->hfs_raw_dev
;
291 struct vnode
*vp
= NULL
;
292 struct vnode
*rvp
= NULL
;
293 struct vnode
*new_vp
= NULL
;
294 struct cnode
*cp
= NULL
;
295 struct proc
*p
= current_proc();
298 /* Check if unmount in progress */
299 if (HFSTOVFS(hfsmp
)->mnt_kern_flag
& MNTK_UNMOUNT
) {
305 * Check the hash for an active cnode
307 cp
= hfs_chashget(dev
, cnid
, wantrsrc
, &vp
, &rvp
);
309 /* hide open files that have been deleted */
310 if ((hfsmp
->hfs_private_metadata_dir
!= 0)
311 && (cp
->c_parentcnid
== hfsmp
->hfs_private_metadata_dir
)
312 && (cp
->c_nlink
== 0)) {
316 if (wantrsrc
&& rvp
!= NULL
) {
321 if (!wantrsrc
&& vp
!= NULL
) {
322 /* Hardlinks need an updated catalog descriptor */
323 if (descp
&& cp
->c_flag
& C_HARDLINK
) {
324 replace_desc(cp
, descp
);
326 /* We have a vnode so we're done. */
332 * There was no active vnode so get a new one.
333 * Use the existing cnode (if any).
337 * hfs_lookup case, use descp, attrp and forkp
339 retval
= hfs_getnewvnode(hfsmp
, cp
, descp
, wantrsrc
, attrp
,
342 struct cat_desc cndesc
= {0};
343 struct cat_attr cnattr
= {0};
344 struct cat_fork cnfork
= {0};
347 * hfs_vget case, need to lookup entry (by file id)
349 if (cnid
== kRootParID
) {
350 static char hfs_rootname
[] = "/";
352 cndesc
.cd_nameptr
= &hfs_rootname
[0];
353 cndesc
.cd_namelen
= 1;
354 cndesc
.cd_parentcnid
= kRootParID
;
355 cndesc
.cd_cnid
= kRootParID
;
356 cndesc
.cd_flags
= CD_ISDIR
;
358 cnattr
.ca_fileid
= kRootParID
;
360 cnattr
.ca_mode
= (S_IFDIR
| S_IRWXU
| S_IRWXG
| S_IRWXO
);
362 /* Lock catalog b-tree */
363 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
367 retval
= cat_idlookup(hfsmp
, cnid
, &cndesc
, &cnattr
, &cnfork
);
369 /* Unlock catalog b-tree */
370 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
374 /* Hide open files that have been deleted */
375 if ((hfsmp
->hfs_private_metadata_dir
!= 0) &&
376 (cndesc
.cd_parentcnid
== hfsmp
->hfs_private_metadata_dir
)) {
377 cat_releasedesc(&cndesc
);
383 retval
= hfs_getnewvnode(hfsmp
, cp
, &cndesc
, 0, &cnattr
, &cnfork
, &new_vp
);
385 /* Hardlinks may need an updated catalog descriptor */
388 && (VTOC(new_vp
)->c_flag
& C_HARDLINK
)
390 && cndesc
.cd_namelen
> 0) {
391 replace_desc(VTOC(new_vp
), &cndesc
);
393 cat_releasedesc(&cndesc
);
396 /* Release reference taken on opposite vnode (if any). */
408 /* The cnode's vnode should be in vp. */
410 panic("hfs_getcnode: missing vp!");
412 UBCINFOCHECK("hfs_getcnode", vp
);
419 * hfs_getnewvnode - get new default vnode
421 * the vnode is returned locked
423 extern int (**hfs_vnodeop_p
) (void *);
424 extern int (**hfs_specop_p
) (void *);
425 extern int (**hfs_fifoop_p
) (void *);
429 hfs_getnewvnode(struct hfsmount
*hfsmp
, struct cnode
*cp
,
430 struct cat_desc
*descp
, int wantrsrc
,
431 struct cat_attr
*attrp
, struct cat_fork
*forkp
,
434 struct mount
*mp
= HFSTOVFS(hfsmp
);
435 struct vnode
*vp
= NULL
;
436 struct vnode
*rvp
= NULL
;
437 struct vnode
*new_vp
= NULL
;
438 struct cnode
*cp2
= NULL
;
439 struct filefork
*fp
= NULL
;
444 struct proc
*p
= current_proc();
446 /* Bail when unmount is in progress */
447 if (mp
->mnt_kern_flag
& MNTK_UNMOUNT
) {
453 if (IFTOVT(attrp
->ca_mode
) == VFIFO
) {
458 dev
= hfsmp
->hfs_raw_dev
;
460 /* If no cnode was passed in then create one */
462 MALLOC_ZONE(cp2
, struct cnode
*, sizeof(struct cnode
),
463 M_HFSNODE
, M_WAITOK
);
464 bzero(cp2
, sizeof(struct cnode
));
466 SET(cp2
->c_flag
, C_ALLOC
);
467 cp2
->c_cnid
= descp
->cd_cnid
;
468 cp2
->c_fileid
= attrp
->ca_fileid
;
470 lockinit(&cp2
->c_lock
, PINOD
, "cnode", 0, 0);
471 (void) lockmgr(&cp2
->c_lock
, LK_EXCLUSIVE
, (struct slock
*)0, p
);
473 * There were several blocking points since we first
474 * checked the hash. Now that we're through blocking,
475 * check the hash again in case we're racing for the
478 cp
= hfs_chashget(dev
, attrp
->ca_fileid
, wantrsrc
, &vp
, &rvp
);
480 /* We lost the race - use the winner's cnode */
481 FREE_ZONE(cp2
, sizeof(struct cnode
), M_HFSNODE
);
483 if (wantrsrc
&& rvp
!= NULL
) {
487 if (!wantrsrc
&& vp
!= NULL
) {
491 } else /* allocated */ {
497 /* Allocate a new vnode. If unsuccesful, leave after freeing memory */
498 if ((retval
= getnewvnode(VT_HFS
, mp
, hfs_vnodeop_p
, &new_vp
))) {
501 if (ISSET(cp
->c_flag
, C_WALLOC
)) {
502 CLR(cp
->c_flag
, C_WALLOC
);
505 FREE_ZONE(cp2
, sizeof(struct cnode
), M_HFSNODE
);
516 bcopy(attrp
, &cp
->c_attr
, sizeof(struct cat_attr
));
517 bcopy(descp
, &cp
->c_desc
, sizeof(struct cat_desc
));
520 if (wantrsrc
&& S_ISREG(cp
->c_mode
))
521 cp
->c_rsrc_vp
= new_vp
;
525 /* Release reference taken on opposite vnode (if any). */
532 vp
->v_ubcinfo
= UBC_NOINFO
;
535 * If this is a new cnode then initialize it using descp and attrp...
538 /* The name was inherited so clear descriptor state... */
539 descp
->cd_namelen
= 0;
540 descp
->cd_nameptr
= NULL
;
541 descp
->cd_flags
&= ~CD_HASBUF
;
544 if (IFTOVT(cp
->c_mode
) == VREG
&&
545 (descp
->cd_cnid
!= attrp
->ca_fileid
)) {
546 cp
->c_flag
|= C_HARDLINK
;
549 /* Take one dev reference for each non-directory cnode */
550 if (IFTOVT(cp
->c_mode
) != VDIR
) {
551 cp
->c_devvp
= hfsmp
->hfs_devvp
;
555 for (i
= 0; i
< MAXQUOTAS
; i
++)
556 cp
->c_dquot
[i
] = NODQUOT
;
560 if (IFTOVT(cp
->c_mode
) != VDIR
) {
561 if (forkp
&& attrp
->ca_blocks
< forkp
->cf_blocks
)
562 panic("hfs_getnewvnode: bad ca_blocks (too small)");
564 * Allocate and initialize a file fork...
566 MALLOC_ZONE(fp
, struct filefork
*, sizeof(struct filefork
),
567 M_HFSFORK
, M_WAITOK
);
568 bzero(fp
, sizeof(struct filefork
));
571 bcopy(forkp
, &fp
->ff_data
, sizeof(HFSPlusForkData
));
572 if (fp
->ff_clumpsize
== 0)
573 fp
->ff_clumpsize
= HFSTOVCB(hfsmp
)->vcbClpSiz
;
574 rl_init(&fp
->ff_invalidranges
);
576 if (cp
->c_rsrcfork
!= NULL
)
577 panic("stale rsrc fork");
580 if (cp
->c_datafork
!= NULL
)
581 panic("stale data fork");
587 * Finish vnode initialization.
588 * Setting the v_type 'stamps' the vnode as 'complete',
589 * so should be done almost last.
591 * At this point the vnode should be locked and fully
592 * allocated. And ready to be used or accessed. (though
593 * having it locked prevents most of this, it can still
594 * be accessed through lists and hashes).
596 vp
->v_type
= IFTOVT(cp
->c_mode
);
598 /* Tag system files */
599 if ((descp
->cd_cnid
< kHFSFirstUserCatalogNodeID
) && (vp
->v_type
== VREG
))
600 vp
->v_flag
|= VSYSTEM
;
601 /* Tag root directory */
602 if (cp
->c_cnid
== kRootDirID
)
605 if ((vp
->v_type
== VREG
) && !(vp
->v_flag
& VSYSTEM
)
606 && (UBCINFOMISSING(vp
) || UBCINFORECLAIMED(vp
))) {
609 vp
->v_ubcinfo
= UBC_NOINFO
;
612 if (vp
->v_type
== VCHR
|| vp
->v_type
== VBLK
) {
615 vp
->v_op
= hfs_specop_p
;
616 if ((nvp
= checkalias(vp
, cp
->c_rdev
, mp
))) {
618 * Discard unneeded vnode, but save its cnode.
619 * Note that the lock is carried over in the
620 * cnode to the replacement vnode.
622 nvp
->v_data
= vp
->v_data
;
624 vp
->v_op
= spec_vnodeop_p
;
628 * Reinitialize aliased cnode.
629 * Assume its not a resource fork.
634 } else if (vp
->v_type
== VFIFO
) {
636 vp
->v_op
= hfs_fifoop_p
;
640 /* Vnode is now initialized - see if anyone was waiting for it. */
641 CLR(cp
->c_flag
, C_ALLOC
);
642 if (ISSET(cp
->c_flag
, C_WALLOC
)) {
643 CLR(cp
->c_flag
, C_WALLOC
);