2 * Copyright (c) 2000-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@
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
26 #include <sys/dirent.h>
29 #include <sys/mount.h>
30 #include <sys/vnode.h>
31 #include <sys/malloc.h>
32 #include <sys/namei.h>
34 #include <sys/quota.h>
36 #include <miscfs/specfs/specdev.h>
37 #include <miscfs/fifofs/fifo.h>
38 #include <vfs/vfs_support.h>
39 #include <machine/spl.h>
41 #include <sys/kdebug.h>
44 #include "hfs_catalog.h"
45 #include "hfs_cnode.h"
46 #include "hfs_lockf.h"
48 #include "hfs_mount.h"
49 #include "hfs_quota.h"
50 #include "hfs_endian.h"
52 #include "hfscommon/headers/BTreesInternal.h"
53 #include "hfscommon/headers/FileMgrInternal.h"
55 #define MAKE_DELETED_NAME(NAME,FID) \
56 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
59 extern uid_t console_user
;
61 /* Global vfs data structures for hfs */
64 extern int groupmember(gid_t gid
, struct ucred
*cred
);
66 static int hfs_makenode(int mode
, struct vnode
*dvp
, struct vnode
**vpp
,
67 struct componentname
*cnp
);
69 static int hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
,
70 struct vnode
**rvpp
, struct proc
*p
);
72 static int hfs_metasync(struct hfsmount
*hfsmp
, daddr_t node
, struct proc
*p
);
74 int hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
);
76 int hfs_chflags(struct vnode
*vp
, u_long flags
, struct ucred
*cred
,
78 int hfs_chmod(struct vnode
*vp
, int mode
, struct ucred
*cred
,
80 int hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
,
81 struct ucred
*cred
, struct proc
*p
);
83 /*****************************************************************************
85 * Common Operations on vnodes
87 *****************************************************************************/
90 * Create a regular file
95 IN WILLRELE struct vnode *dvp;
96 OUT struct vnode **vpp;
97 IN struct componentname *cnp;
100 We are responsible for freeing the namei buffer,
101 it is done in hfs_makenode()
106 struct vop_create_args
/* {
108 struct vnode **a_vpp;
109 struct componentname *a_cnp;
113 struct vattr
*vap
= ap
->a_vap
;
115 return (hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
116 ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
));
127 IN WILLRELE struct vnode *dvp;
128 OUT WILLRELE struct vnode **vpp;
129 IN struct componentname *cnp;
130 IN struct vattr *vap;
136 struct vop_mknod_args
/* {
138 struct vnode **a_vpp;
139 struct componentname *a_cnp;
143 struct vattr
*vap
= ap
->a_vap
;
144 struct vnode
**vpp
= ap
->a_vpp
;
148 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
149 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
154 /* Create the vnode */
155 error
= hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
156 ap
->a_dvp
, vpp
, ap
->a_cnp
);
160 cp
->c_flag
|= C_ACCESS
| C_CHANGE
| C_UPDATE
;
161 if ((vap
->va_rdev
!= VNOVAL
) &&
162 (vap
->va_type
== VBLK
|| vap
->va_type
== VCHR
))
163 cp
->c_rdev
= vap
->va_rdev
;
165 * Remove cnode so that it will be reloaded by lookup and
166 * checked to see if it is an alias of an existing vnode.
167 * Note: unlike UFS, we don't bash v_type here.
183 IN struct ucred *cred;
190 struct vop_open_args
/* {
193 struct ucred *a_cred;
197 struct vnode
*vp
= ap
->a_vp
;
200 * Files marked append-only must be opened for appending.
202 if ((vp
->v_type
!= VDIR
) && (VTOC(vp
)->c_flags
& APPEND
) &&
203 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
212 * Update the times on the cnode.
218 IN struct ucred *cred;
225 struct vop_close_args
/* {
228 struct ucred *a_cred;
232 register struct vnode
*vp
= ap
->a_vp
;
233 register struct cnode
*cp
= VTOC(vp
);
234 register struct filefork
*fp
= VTOF(vp
);
235 struct proc
*p
= ap
->a_p
;
238 u_long blks
, blocksize
;
242 simple_lock(&vp
->v_interlock
);
243 if ((!UBCISVALID(vp
) && vp
->v_usecount
> 1)
244 || (UBCISVALID(vp
) && ubc_isinuse(vp
, 1))) {
246 CTIMES(cp
, &tv
, &tv
);
248 simple_unlock(&vp
->v_interlock
);
251 * VOP_CLOSE can be called with vp locked (from vclean).
252 * We check for this case using VOP_ISLOCKED and bail.
254 * XXX During a force unmount we won't do the cleanup below!
256 if (vp
->v_type
== VDIR
|| VOP_ISLOCKED(vp
))
261 if ((fp
->ff_blocks
> 0) && !ISSET(cp
->c_flag
, C_DELETED
)) {
262 enum vtype our_type
= vp
->v_type
;
263 u_long our_id
= vp
->v_id
;
264 int was_nocache
= ISSET(vp
->v_flag
, VNOCACHE_DATA
);
266 error
= vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
270 * Since we can context switch in vn_lock our vnode
271 * could get recycled (eg umount -f). Double check
272 * that its still ours.
274 if (vp
->v_type
!= our_type
|| vp
->v_id
!= our_id
275 || cp
!= VTOC(vp
) || !UBCINFOEXISTS(vp
)) {
276 VOP_UNLOCK(vp
, 0, p
);
281 * Last chance to explicitly zero out the areas
282 * that are currently marked invalid:
284 VOP_DEVBLOCKSIZE(cp
->c_devvp
, &devBlockSize
);
285 (void) cluster_push(vp
);
286 SET(vp
->v_flag
, VNOCACHE_DATA
); /* Don't cache zeros */
287 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
288 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
289 off_t start
= invalid_range
->rl_start
;
290 off_t end
= invalid_range
->rl_end
;
292 /* The range about to be written must be validated
293 * first, so that VOP_CMAP() will return the
294 * appropriate mapping for the cluster code:
296 rl_remove(start
, end
, &fp
->ff_invalidranges
);
298 (void) cluster_write(vp
, (struct uio
*) 0, leof
,
299 invalid_range
->rl_end
+ 1, invalid_range
->rl_start
,
300 (off_t
)0, devBlockSize
, IO_HEADZEROFILL
| IO_NOZERODIRTY
);
302 if (ISSET(vp
->v_flag
, VHASDIRTY
))
303 (void) cluster_push(vp
);
305 cp
->c_flag
|= C_MODIFIED
;
307 cp
->c_flag
&= ~C_ZFWANTSYNC
;
309 blocksize
= VTOVCB(vp
)->blockSize
;
310 blks
= leof
/ blocksize
;
311 if (((off_t
)blks
* (off_t
)blocksize
) != leof
)
314 * Shrink the peof to the smallest size neccessary to contain the leof.
316 if (blks
< fp
->ff_blocks
)
317 (void) VOP_TRUNCATE(vp
, leof
, IO_NDELAY
, ap
->a_cred
, p
);
318 (void) cluster_push(vp
);
321 CLR(vp
->v_flag
, VNOCACHE_DATA
);
324 * If the VOP_TRUNCATE didn't happen to flush the vnode's
325 * information out to disk, force it to be updated now that
326 * all invalid ranges have been zero-filled and validated:
328 if (cp
->c_flag
& C_MODIFIED
) {
330 VOP_UPDATE(vp
, &tv
, &tv
, 0);
332 VOP_UNLOCK(vp
, 0, p
);
343 IN struct ucred *cred;
350 struct vop_access_args
/* {
353 struct ucred *a_cred;
357 struct vnode
*vp
= ap
->a_vp
;
358 struct cnode
*cp
= VTOC(vp
);
359 struct ucred
*cred
= ap
->a_cred
;
361 mode_t mode
= ap
->a_mode
;
367 * Disallow write attempts on read-only file systems;
368 * unless the file is a socket, fifo, or a block or
369 * character device resident on the file system.
372 switch (vp
->v_type
) {
376 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
379 if ((error
= hfs_getinoquota(cp
)))
386 /* If immutable bit set, nobody gets to write it. */
387 if ((mode
& VWRITE
) && (cp
->c_flags
& IMMUTABLE
))
390 /* Otherwise, user id 0 always gets access. */
391 if (ap
->a_cred
->cr_uid
== 0)
396 /* Otherwise, check the owner. */
397 if (hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, ap
->a_p
, false) == 0) {
404 return ((cp
->c_mode
& mask
) == mask
? 0 : EACCES
);
407 /* Otherwise, check the groups. */
408 if (! (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
)) {
409 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++)
410 if (cp
->c_gid
== *gp
) {
417 return ((cp
->c_mode
& mask
) == mask
? 0 : EACCES
);
421 /* Otherwise, check everyone else. */
428 return ((cp
->c_mode
& mask
) == mask
? 0 : EACCES
);
438 IN struct vattr *vap;
439 IN struct ucred *cred;
448 struct vop_getattr_args
/* {
451 struct ucred *a_cred;
455 struct vnode
*vp
= ap
->a_vp
;
456 struct cnode
*cp
= VTOC(vp
);
457 struct vattr
*vap
= ap
->a_vap
;
461 CTIMES(cp
, &tv
, &tv
);
463 vap
->va_type
= vp
->v_type
;
465 * [2856576] Since we are dynamically changing the owner, also
466 * effectively turn off the set-user-id and set-group-id bits,
467 * just like chmod(2) would when changing ownership. This prevents
468 * a security hole where set-user-id programs run as whoever is
469 * logged on (or root if nobody is logged in yet!)
471 vap
->va_mode
= (cp
->c_uid
== UNKNOWNUID
) ? cp
->c_mode
& ~(S_ISUID
| S_ISGID
) : cp
->c_mode
;
472 vap
->va_nlink
= cp
->c_nlink
;
473 vap
->va_uid
= (cp
->c_uid
== UNKNOWNUID
) ? console_user
: cp
->c_uid
;
474 vap
->va_gid
= cp
->c_gid
;
475 vap
->va_fsid
= cp
->c_dev
;
477 * Exporting file IDs from HFS Plus:
479 * For "normal" files the c_fileid is the same value as the
480 * c_cnid. But for hard link files, they are different - the
481 * c_cnid belongs to the active directory entry (ie the link)
482 * and the c_fileid is for the actual inode (ie the data file).
484 * The stat call (getattr) will always return the c_fileid
485 * and Carbon APIs, which are hardlink-ignorant, will always
486 * receive the c_cnid (from getattrlist).
488 vap
->va_fileid
= cp
->c_fileid
;
489 vap
->va_atime
.tv_sec
= cp
->c_atime
;
490 vap
->va_atime
.tv_nsec
= 0;
491 vap
->va_mtime
.tv_sec
= cp
->c_mtime
;
492 vap
->va_mtime
.tv_nsec
= cp
->c_mtime_nsec
;
493 vap
->va_ctime
.tv_sec
= cp
->c_ctime
;
494 vap
->va_ctime
.tv_nsec
= 0;
496 vap
->va_flags
= cp
->c_flags
;
498 vap
->va_blocksize
= VTOVFS(vp
)->mnt_stat
.f_iosize
;
501 if (vp
->v_type
== VDIR
) {
502 vap
->va_size
= cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
;
505 vap
->va_size
= VTOF(vp
)->ff_size
;
506 vap
->va_bytes
= (u_quad_t
)cp
->c_blocks
*
507 (u_quad_t
)VTOVCB(vp
)->blockSize
;
508 if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
)
509 vap
->va_rdev
= cp
->c_rdev
;
515 * Set attribute vnode op. called from several syscalls
520 IN struct vattr *vap;
521 IN struct ucred *cred;
528 struct vop_setattr_args
/* {
531 struct ucred *a_cred;
535 struct vattr
*vap
= ap
->a_vap
;
536 struct vnode
*vp
= ap
->a_vp
;
537 struct cnode
*cp
= VTOC(vp
);
538 struct ucred
*cred
= ap
->a_cred
;
539 struct proc
*p
= ap
->a_p
;
540 struct timeval atimeval
, mtimeval
;
544 * Check for unsettable attributes.
546 if ((vap
->va_type
!= VNON
) || (vap
->va_nlink
!= VNOVAL
) ||
547 (vap
->va_fsid
!= VNOVAL
) || (vap
->va_fileid
!= VNOVAL
) ||
548 (vap
->va_blocksize
!= VNOVAL
) || (vap
->va_rdev
!= VNOVAL
) ||
549 ((int)vap
->va_bytes
!= VNOVAL
) || (vap
->va_gen
!= VNOVAL
)) {
553 if (vap
->va_flags
!= VNOVAL
) {
554 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
556 if ((error
= hfs_chflags(vp
, vap
->va_flags
, cred
, p
)))
558 if (vap
->va_flags
& (IMMUTABLE
| APPEND
))
562 if (cp
->c_flags
& (IMMUTABLE
| APPEND
))
565 // XXXdbg - don't allow modification of the journal or journal_info_block
566 if (VTOHFS(vp
)->jnl
&& cp
->c_datafork
) {
567 struct HFSPlusExtentDescriptor
*extd
;
569 extd
= &cp
->c_datafork
->ff_data
.cf_extents
[0];
570 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
576 * Go through the fields and update iff not VNOVAL.
578 if (vap
->va_uid
!= (uid_t
)VNOVAL
|| vap
->va_gid
!= (gid_t
)VNOVAL
) {
579 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
581 if ((error
= hfs_chown(vp
, vap
->va_uid
, vap
->va_gid
, cred
, p
)))
584 if (vap
->va_size
!= VNOVAL
) {
586 * Disallow write attempts on read-only file systems;
587 * unless the file is a socket, fifo, or a block or
588 * character device resident on the file system.
590 switch (vp
->v_type
) {
595 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
601 if ((error
= VOP_TRUNCATE(vp
, vap
->va_size
, 0, cred
, p
)))
605 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
606 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
608 if (((error
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0) &&
609 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
610 (error
= VOP_ACCESS(vp
, VWRITE
, cred
, p
)))) {
613 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
614 cp
->c_flag
|= C_ACCESS
;
615 if (vap
->va_mtime
.tv_sec
!= VNOVAL
) {
616 cp
->c_flag
|= C_CHANGE
| C_UPDATE
;
618 * The utimes system call can reset the modification
619 * time but it doesn't know about HFS create times.
620 * So we need to insure that the creation time is
621 * always at least as old as the modification time.
623 if ((VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
624 (cp
->c_cnid
!= kRootDirID
) &&
625 (vap
->va_mtime
.tv_sec
< cp
->c_itime
)) {
626 cp
->c_itime
= vap
->va_mtime
.tv_sec
;
629 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
630 atimeval
.tv_usec
= 0;
631 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
632 mtimeval
.tv_usec
= 0;
633 if ((error
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1)))
637 if (vap
->va_mode
!= (mode_t
)VNOVAL
) {
638 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
640 error
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
);
647 * Change the mode on a file.
648 * cnode must be locked before calling.
651 hfs_chmod(vp
, mode
, cred
, p
)
652 register struct vnode
*vp
;
654 register struct ucred
*cred
;
657 register struct cnode
*cp
= VTOC(vp
);
660 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
663 // XXXdbg - don't allow modification of the journal or journal_info_block
664 if (VTOHFS(vp
)->jnl
&& cp
&& cp
->c_datafork
) {
665 struct HFSPlusExtentDescriptor
*extd
;
667 extd
= &cp
->c_datafork
->ff_data
.cf_extents
[0];
668 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
673 #if OVERRIDE_UNKNOWN_PERMISSIONS
674 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
678 if ((error
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0)
681 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
683 if (!groupmember(cp
->c_gid
, cred
) && (mode
& S_ISGID
))
686 cp
->c_mode
&= ~ALLPERMS
;
687 cp
->c_mode
|= (mode
& ALLPERMS
);
688 cp
->c_flag
|= C_CHANGE
;
694 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
696 struct cnode
*cp
= VTOC(vp
);
702 * Disallow write attempts on read-only file systems;
703 * unless the file is a socket, fifo, or a block or
704 * character device resident on the file system.
706 switch (vp
->v_type
) {
710 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
717 /* If immutable bit set, nobody gets to write it. */
718 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
721 /* Otherwise, user id 0 always gets access. */
722 if (cred
->cr_uid
== 0)
725 /* Otherwise, check the owner. */
726 if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, false)) == 0)
727 return ((cp
->c_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
729 /* Otherwise, check the groups. */
730 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++) {
731 if (cp
->c_gid
== *gp
)
732 return ((cp
->c_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
735 /* Otherwise, check everyone else. */
736 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
742 * Change the flags on a file or directory.
743 * cnode must be locked before calling.
746 hfs_chflags(vp
, flags
, cred
, p
)
747 register struct vnode
*vp
;
748 register u_long flags
;
749 register struct ucred
*cred
;
752 register struct cnode
*cp
= VTOC(vp
);
755 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
756 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
759 } else if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0) {
763 if (cred
->cr_uid
== 0) {
764 if ((cp
->c_flags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
770 if (cp
->c_flags
& (SF_IMMUTABLE
| SF_APPEND
) ||
771 (flags
& UF_SETTABLE
) != flags
) {
774 cp
->c_flags
&= SF_SETTABLE
;
775 cp
->c_flags
|= (flags
& UF_SETTABLE
);
777 cp
->c_flag
|= C_CHANGE
;
784 * Perform chown operation on cnode cp;
785 * code must be locked prior to call.
788 hfs_chown(vp
, uid
, gid
, cred
, p
)
789 register struct vnode
*vp
;
795 register struct cnode
*cp
= VTOC(vp
);
804 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
807 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
)
810 if (uid
== (uid_t
)VNOVAL
)
812 if (gid
== (gid_t
)VNOVAL
)
815 * If we don't own the file, are trying to change the owner
816 * of the file, or are not a member of the target group,
817 * the caller must be superuser or the call fails.
819 if ((cred
->cr_uid
!= cp
->c_uid
|| uid
!= cp
->c_uid
||
820 (gid
!= cp
->c_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
821 (error
= suser(cred
, &p
->p_acflag
)))
827 if ((error
= hfs_getinoquota(cp
)))
830 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
831 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
834 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
835 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
839 * Eventually need to account for (fake) a block per directory
840 *if (vp->v_type == VDIR)
841 *change = VTOVCB(vp)->blockSize;
845 change
= (int64_t)(cp
->c_blocks
) * (int64_t)VTOVCB(vp
)->blockSize
;
846 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
);
847 (void) hfs_chkiq(cp
, -1, cred
, CHOWN
);
848 for (i
= 0; i
< MAXQUOTAS
; i
++) {
849 dqrele(vp
, cp
->c_dquot
[i
]);
850 cp
->c_dquot
[i
] = NODQUOT
;
856 if ((error
= hfs_getinoquota(cp
)) == 0) {
858 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
859 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
862 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
863 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
865 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
866 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
869 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
871 for (i
= 0; i
< MAXQUOTAS
; i
++) {
872 dqrele(vp
, cp
->c_dquot
[i
]);
873 cp
->c_dquot
[i
] = NODQUOT
;
878 if (hfs_getinoquota(cp
) == 0) {
880 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
881 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
884 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
885 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
887 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
888 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
889 (void) hfs_getinoquota(cp
);
893 if (hfs_getinoquota(cp
))
894 panic("hfs_chown: lost quota");
897 if (ouid
!= uid
|| ogid
!= gid
)
898 cp
->c_flag
|= C_CHANGE
;
899 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
900 cp
->c_mode
&= ~S_ISUID
;
901 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
902 cp
->c_mode
&= ~S_ISGID
;
909 #% exchange fvp L L L
910 #% exchange tvp L L L
914 * The hfs_exchange routine swaps the fork data in two files by
915 * exchanging some of the information in the cnode. It is used
916 * to preserve the file ID when updating an existing file, in
917 * case the file is being tracked through its file ID. Typically
918 * its used after creating a new file during a safe-save.
923 struct vop_exchange_args
/* {
926 struct ucred *a_cred;
930 struct vnode
*from_vp
= ap
->a_fvp
;
931 struct vnode
*to_vp
= ap
->a_tvp
;
932 struct vnode
*from_rvp
= NULL
;
933 struct vnode
*to_rvp
= NULL
;
934 struct cnode
*from_cp
= VTOC(from_vp
);
935 struct cnode
*to_cp
= VTOC(to_vp
);
936 struct hfsmount
*hfsmp
= VTOHFS(from_vp
);
937 struct cat_desc tempdesc
;
938 struct cat_attr tempattr
;
939 int error
= 0, started_tr
= 0, grabbed_lock
= 0;
941 /* The files must be on the same volume. */
942 if (from_vp
->v_mount
!= to_vp
->v_mount
)
945 /* Only normal files can be exchanged. */
946 if ((from_vp
->v_type
!= VREG
) || (to_vp
->v_type
!= VREG
) ||
947 (from_cp
->c_flag
& C_HARDLINK
) || (to_cp
->c_flag
& C_HARDLINK
) ||
948 VNODE_IS_RSRC(from_vp
) || VNODE_IS_RSRC(to_vp
))
951 // XXXdbg - don't allow modification of the journal or journal_info_block
953 struct HFSPlusExtentDescriptor
*extd
;
955 if (from_cp
->c_datafork
) {
956 extd
= &from_cp
->c_datafork
->ff_data
.cf_extents
[0];
957 if (extd
->startBlock
== VTOVCB(from_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
962 if (to_cp
->c_datafork
) {
963 extd
= &to_cp
->c_datafork
->ff_data
.cf_extents
[0];
964 if (extd
->startBlock
== VTOVCB(to_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
970 from_rvp
= from_cp
->c_rsrc_vp
;
971 to_rvp
= to_cp
->c_rsrc_vp
;
973 /* If one of the resource forks is open then get the other one. */
974 if (from_rvp
|| to_rvp
) {
975 error
= hfs_vgetrsrc(hfsmp
, from_vp
, &from_rvp
, ap
->a_p
);
978 error
= hfs_vgetrsrc(hfsmp
, to_vp
, &to_rvp
, ap
->a_p
);
985 /* Ignore any errors, we are doing a 'best effort' on flushing */
987 (void) vinvalbuf(from_vp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
989 (void) vinvalbuf(to_vp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
991 (void) vinvalbuf(from_rvp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
993 (void) vinvalbuf(to_rvp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
996 hfs_global_shared_lock_acquire(hfsmp
);
999 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
1005 /* Lock catalog b-tree */
1006 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1007 if (error
) goto Err_Exit
;
1009 /* The backend code always tries to delete the virtual
1010 * extent id for exchanging files so we neeed to lock
1011 * the extents b-tree.
1013 error
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1015 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1019 /* Do the exchange */
1020 error
= MacToVFSError(ExchangeFileIDs(HFSTOVCB(hfsmp
),
1021 from_cp
->c_desc
.cd_nameptr
, to_cp
->c_desc
.cd_nameptr
,
1022 from_cp
->c_parentcnid
, to_cp
->c_parentcnid
,
1023 from_cp
->c_hint
, to_cp
->c_hint
));
1025 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
1026 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1028 if (error
!= E_NONE
) {
1032 /* Purge the vnodes from the name cache */
1034 cache_purge(from_vp
);
1038 /* Save a copy of from attributes before swapping. */
1039 bcopy(&from_cp
->c_desc
, &tempdesc
, sizeof(struct cat_desc
));
1040 bcopy(&from_cp
->c_attr
, &tempattr
, sizeof(struct cat_attr
));
1043 * Swap the descriptors and all non-fork related attributes.
1044 * (except the modify date)
1046 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
1048 from_cp
->c_hint
= 0;
1049 from_cp
->c_fileid
= from_cp
->c_cnid
;
1050 from_cp
->c_itime
= to_cp
->c_itime
;
1051 from_cp
->c_btime
= to_cp
->c_btime
;
1052 from_cp
->c_atime
= to_cp
->c_atime
;
1053 from_cp
->c_ctime
= to_cp
->c_ctime
;
1054 from_cp
->c_gid
= to_cp
->c_gid
;
1055 from_cp
->c_uid
= to_cp
->c_uid
;
1056 from_cp
->c_flags
= to_cp
->c_flags
;
1057 from_cp
->c_mode
= to_cp
->c_mode
;
1058 bcopy(to_cp
->c_finderinfo
, from_cp
->c_finderinfo
, 32);
1060 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
1062 to_cp
->c_fileid
= to_cp
->c_cnid
;
1063 to_cp
->c_itime
= tempattr
.ca_itime
;
1064 to_cp
->c_btime
= tempattr
.ca_btime
;
1065 to_cp
->c_atime
= tempattr
.ca_atime
;
1066 to_cp
->c_ctime
= tempattr
.ca_ctime
;
1067 to_cp
->c_gid
= tempattr
.ca_gid
;
1068 to_cp
->c_uid
= tempattr
.ca_uid
;
1069 to_cp
->c_flags
= tempattr
.ca_flags
;
1070 to_cp
->c_mode
= tempattr
.ca_mode
;
1071 bcopy(tempattr
.ca_finderinfo
, to_cp
->c_finderinfo
, 32);
1073 /* Reinsert into the cnode hash under new file IDs*/
1074 hfs_chashremove(from_cp
);
1075 hfs_chashremove(to_cp
);
1077 hfs_chashinsert(from_cp
);
1078 hfs_chashinsert(to_cp
);
1087 journal_end_transaction(hfsmp
->jnl
);
1090 hfs_global_shared_lock_release(hfsmp
);
1102 IN struct vnode *vp;
1103 IN struct ucred *cred;
1110 struct vop_fsync_args
/* {
1112 struct ucred *a_cred;
1117 struct vnode
*vp
= ap
->a_vp
;
1118 struct cnode
*cp
= VTOC(vp
);
1119 struct filefork
*fp
= NULL
;
1121 register struct buf
*bp
;
1124 struct hfsmount
*hfsmp
= VTOHFS(ap
->a_vp
);
1129 wait
= (ap
->a_waitfor
== MNT_WAIT
);
1131 /* HFS directories don't have any data blocks. */
1132 if (vp
->v_type
== VDIR
)
1136 * For system files flush the B-tree header and
1137 * for regular files write out any clusters
1139 if (vp
->v_flag
& VSYSTEM
) {
1140 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1143 if (BTIsDirty(VTOF(vp
))) {
1144 panic("hfs: system file vp 0x%x has dirty blocks (jnl 0x%x)\n",
1148 BTFlushPath(VTOF(vp
));
1151 } else if (UBCINFOEXISTS(vp
))
1152 (void) cluster_push(vp
);
1155 * When MNT_WAIT is requested and the zero fill timeout
1156 * has expired then we must explicitly zero out any areas
1157 * that are currently marked invalid (holes).
1159 if ((wait
|| (cp
->c_flag
& C_ZFWANTSYNC
)) &&
1160 UBCINFOEXISTS(vp
) && (fp
= VTOF(vp
)) &&
1161 cp
->c_zftimeout
!= 0) {
1165 if (time
.tv_sec
< cp
->c_zftimeout
) {
1166 /* Remember that a force sync was requested. */
1167 cp
->c_flag
|= C_ZFWANTSYNC
;
1170 VOP_DEVBLOCKSIZE(cp
->c_devvp
, &devblksize
);
1171 was_nocache
= ISSET(vp
->v_flag
, VNOCACHE_DATA
);
1172 SET(vp
->v_flag
, VNOCACHE_DATA
); /* Don't cache zeros */
1174 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
1175 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
1176 off_t start
= invalid_range
->rl_start
;
1177 off_t end
= invalid_range
->rl_end
;
1179 /* The range about to be written must be validated
1180 * first, so that VOP_CMAP() will return the
1181 * appropriate mapping for the cluster code:
1183 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1185 (void) cluster_write(vp
, (struct uio
*) 0,
1187 invalid_range
->rl_end
+ 1,
1188 invalid_range
->rl_start
,
1189 (off_t
)0, devblksize
,
1190 IO_HEADZEROFILL
| IO_NOZERODIRTY
);
1191 cp
->c_flag
|= C_MODIFIED
;
1193 (void) cluster_push(vp
);
1195 CLR(vp
->v_flag
, VNOCACHE_DATA
);
1196 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1197 cp
->c_zftimeout
= 0;
1201 * Flush all dirty buffers associated with a vnode.
1205 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1206 nbp
= bp
->b_vnbufs
.le_next
;
1207 if ((bp
->b_flags
& B_BUSY
))
1209 if ((bp
->b_flags
& B_DELWRI
) == 0)
1210 panic("hfs_fsync: bp 0x% not dirty (hfsmp 0x%x)", bp
, hfsmp
);
1212 if (hfsmp
->jnl
&& (bp
->b_flags
& B_LOCKED
)) {
1213 if ((bp
->b_flags
& B_META
) == 0) {
1214 panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
1217 // if journal_active() returns >= 0 then the journal is ok and we
1218 // shouldn't do anything to this locked block (because it is part
1219 // of a transaction). otherwise we'll just go through the normal
1220 // code path and flush the buffer.
1221 if (journal_active(hfsmp
->jnl
) >= 0) {
1227 bp
->b_flags
|= B_BUSY
;
1228 /* Clear B_LOCKED, should only be set on meta files */
1229 bp
->b_flags
&= ~B_LOCKED
;
1233 * Wait for I/O associated with indirect blocks to complete,
1234 * since there is no way to quickly wait for them below.
1236 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
)
1239 (void) VOP_BWRITE(bp
);
1244 while (vp
->v_numoutput
) {
1245 vp
->v_flag
|= VBWAIT
;
1246 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
1249 // XXXdbg -- is checking for hfsmp->jnl == NULL the right
1251 if (hfsmp
->jnl
== NULL
&& vp
->v_dirtyblkhd
.lh_first
) {
1252 /* still have some dirty buffers */
1254 vprint("hfs_fsync: dirty", vp
);
1257 * Looks like the requests are not
1258 * getting queued to the driver.
1259 * Retrying here causes a cpu bound loop.
1260 * Yield to the other threads and hope
1263 (void)tsleep((caddr_t
)&vp
->v_numoutput
,
1264 PRIBIO
+ 1, "hfs_fsync", hz
/10);
1277 if (vp
->v_flag
& VSYSTEM
) {
1278 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
)
1279 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1280 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
1281 } else /* User file */ {
1282 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, wait
);
1284 /* When MNT_WAIT is requested push out any delayed meta data */
1285 if ((retval
== 0) && wait
&& cp
->c_hint
&&
1286 !ISSET(cp
->c_flag
, C_DELETED
| C_NOEXISTS
)) {
1287 hfs_metasync(VTOHFS(vp
), cp
->c_hint
, ap
->a_p
);
1294 /* Sync an hfs catalog b-tree node */
1296 hfs_metasync(struct hfsmount
*hfsmp
, daddr_t node
, struct proc
*p
)
1303 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1305 // XXXdbg - don't need to do this on a journaled volume
1310 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
) != 0)
1314 * Look for a matching node that has been delayed
1315 * but is not part of a set (B_LOCKED).
1318 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1319 nbp
= bp
->b_vnbufs
.le_next
;
1320 if (bp
->b_flags
& B_BUSY
)
1322 if (bp
->b_lblkno
== node
) {
1323 if (bp
->b_flags
& B_LOCKED
)
1327 bp
->b_flags
|= B_BUSY
;
1329 (void) VOP_BWRITE(bp
);
1335 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1342 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1344 struct cnode
*cp
= VTOC(vp
);
1345 register struct buf
*bp
;
1348 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1352 * Flush all dirty buffers associated with b-tree.
1357 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1358 nbp
= bp
->b_vnbufs
.le_next
;
1359 if ((bp
->b_flags
& B_BUSY
))
1361 if ((bp
->b_flags
& B_DELWRI
) == 0)
1362 panic("hfs_btsync: not dirty (bp 0x%x hfsmp 0x%x)", bp
, hfsmp
);
1365 if (hfsmp
->jnl
&& (bp
->b_flags
& B_LOCKED
)) {
1366 if ((bp
->b_flags
& B_META
) == 0) {
1367 panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
1370 // if journal_active() returns >= 0 then the journal is ok and we
1371 // shouldn't do anything to this locked block (because it is part
1372 // of a transaction). otherwise we'll just go through the normal
1373 // code path and flush the buffer.
1374 if (journal_active(hfsmp
->jnl
) >= 0) {
1379 if (sync_transaction
&& !(bp
->b_flags
& B_LOCKED
))
1383 bp
->b_flags
|= B_BUSY
;
1384 bp
->b_flags
&= ~B_LOCKED
;
1395 if ((vp
->v_flag
& VSYSTEM
) && (VTOF(vp
)->fcbBTCBPtr
!= NULL
))
1396 (void) BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1397 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
1403 * Rmdir system call.
1408 IN WILLRELE struct vnode *dvp;
1409 IN WILLRELE struct vnode *vp;
1410 IN struct componentname *cnp;
1415 struct vop_rmdir_args
/* {
1416 struct vnode *a_dvp;
1418 struct componentname *a_cnp;
1421 struct vnode
*vp
= ap
->a_vp
;
1422 struct vnode
*dvp
= ap
->a_dvp
;
1423 struct proc
*p
= ap
->a_cnp
->cn_proc
;
1426 struct hfsmount
* hfsmp
;
1428 int error
= 0, started_tr
= 0, grabbed_lock
= 0;
1437 return (EINVAL
); /* cannot remove "." */
1441 hfs_global_shared_lock_acquire(hfsmp
);
1444 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
1451 * Verify the directory is empty (and valid).
1452 * (Rmdir ".." won't be valid since
1453 * ".." will contain a reference to
1454 * the current directory and thus be
1457 if (cp
->c_entries
!= 0) {
1461 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1466 /* Remove the entry from the namei cache: */
1469 /* Lock catalog b-tree */
1470 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1471 if (error
) goto out
;
1473 if (cp
->c_entries
> 0)
1474 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1475 /* Remove entry from catalog */
1476 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
1478 /* Unlock catalog b-tree */
1479 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1480 if (error
) goto out
;
1483 if (!hfs_getinoquota(cp
))
1484 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1487 /* The parent lost a child */
1488 if (dcp
->c_entries
> 0)
1490 if (dcp
->c_nlink
> 0)
1492 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1494 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
1496 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1498 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1499 cp
->c_flag
|= C_NOEXISTS
;
1507 journal_end_transaction(hfsmp
->jnl
);
1510 hfs_global_shared_lock_release(hfsmp
);
1522 IN WILLRELE struct vnode *dvp;
1523 IN WILLRELE struct vnode *vp;
1524 IN struct componentname *cnp;
1530 struct vop_remove_args
/* {
1531 struct vnode *a_dvp;
1533 struct componentname *a_cnp;
1536 struct vnode
*vp
= ap
->a_vp
;
1537 struct vnode
*dvp
= ap
->a_dvp
;
1538 struct vnode
*rvp
= NULL
;
1541 struct hfsmount
*hfsmp
;
1542 struct proc
*p
= current_proc();
1543 int dataforkbusy
= 0;
1544 int rsrcforkbusy
= 0;
1548 int started_tr
= 0, grabbed_lock
= 0;
1550 /* Redirect directories to rmdir */
1551 if (vp
->v_type
== VDIR
)
1552 return (hfs_rmdir(ap
));
1558 if (cp
->c_parentcnid
!= dcp
->c_cnid
) {
1563 /* Make sure a remove is permitted */
1564 if ((cp
->c_flags
& (IMMUTABLE
| APPEND
)) ||
1565 (VTOC(dvp
)->c_flags
& APPEND
) ||
1566 VNODE_IS_RSRC(vp
)) {
1572 * Aquire a vnode for a non-empty resource fork.
1573 * (needed for VOP_TRUNCATE)
1575 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1576 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
);
1581 // XXXdbg - don't allow deleting the journal or journal_info_block
1582 if (hfsmp
->jnl
&& cp
->c_datafork
) {
1583 struct HFSPlusExtentDescriptor
*extd
;
1585 extd
= &cp
->c_datafork
->ff_data
.cf_extents
[0];
1586 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
1593 * Check if this file is being used.
1595 * The namei done for the remove took a reference on the
1596 * vnode (vp). And we took a ref on the resource vnode (rvp).
1597 * Hence set 1 in the tookref parameter of ubc_isinuse().
1599 if (UBCISVALID(vp
) && ubc_isinuse(vp
, 1))
1601 if (rvp
&& UBCISVALID(rvp
) && ubc_isinuse(rvp
, 1))
1605 * Carbon semantics prohibit deleting busy files.
1606 * (enforced when NODELETEBUSY is requested)
1608 if ((dataforkbusy
|| rsrcforkbusy
) &&
1609 ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
) ||
1610 (hfsmp
->hfs_private_metadata_dir
== 0))) {
1616 hfs_global_shared_lock_acquire(hfsmp
);
1619 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
1625 /* Remove our entry from the namei cache. */
1628 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1629 if (hfsmp
->jnl
&& vp
->v_type
== VLNK
&& vp
->v_dirtyblkhd
.lh_first
) {
1630 struct buf
*bp
, *nbp
;
1633 for (bp
=vp
->v_dirtyblkhd
.lh_first
; bp
; bp
=nbp
) {
1634 nbp
= bp
->b_vnbufs
.le_next
;
1636 if ((bp
->b_flags
& B_BUSY
)) {
1637 // if it was busy, someone else must be dealing
1638 // with it so just move on.
1642 if (!(bp
->b_flags
& B_META
)) {
1643 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp
);
1646 // if it's part of the current transaction, kill it.
1647 if (bp
->b_flags
& B_LOCKED
) {
1649 bp
->b_flags
|= B_BUSY
;
1650 journal_kill_block(hfsmp
->jnl
, bp
);
1658 * Truncate any non-busy forks. Busy forks will
1659 * get trucated when their vnode goes inactive.
1661 * (Note: hard links are truncated in VOP_INACTIVE)
1663 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1664 int mode
= cp
->c_mode
;
1666 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
!= 0) {
1667 cp
->c_mode
= 0; /* Suppress VOP_UPDATES */
1668 error
= VOP_TRUNCATE(vp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
1674 if (!rsrcforkbusy
&& rvp
) {
1675 cp
->c_mode
= 0; /* Suppress VOP_UPDATES */
1676 error
= VOP_TRUNCATE(rvp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
1678 if (error
&& !dataforkbusy
)
1682 * XXX could also force an update on vp
1683 * and fail the remove.
1691 * There are 3 remove cases to consider:
1692 * 1. File is a hardlink ==> remove the link
1693 * 2. File is busy (in use) ==> move/rename the file
1694 * 3. File is not in use ==> remove the file
1697 if (cp
->c_flag
& C_HARDLINK
) {
1698 struct cat_desc desc
;
1700 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
1701 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
1702 error
= ENOENT
; /* name missing! */
1706 /* Setup a descriptor for the link */
1707 bzero(&desc
, sizeof(desc
));
1708 desc
.cd_nameptr
= ap
->a_cnp
->cn_nameptr
;
1709 desc
.cd_namelen
= ap
->a_cnp
->cn_namelen
;
1710 desc
.cd_parentcnid
= dcp
->c_cnid
;
1711 /* XXX - if cnid is out of sync then the wrong thread rec will get deleted. */
1712 desc
.cd_cnid
= cp
->c_cnid
;
1714 /* Lock catalog b-tree */
1715 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1719 /* Delete the link record */
1720 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1722 if ((error
== 0) && (--cp
->c_nlink
< 1)) {
1725 struct cat_desc to_desc
;
1726 struct cat_desc from_desc
;
1729 * This is now esentially an open deleted file.
1730 * Rename it to reflect this state which makes
1731 * orphan file cleanup easier (see hfs_remove_orphans).
1732 * Note: a rename failure here is not fatal.
1734 MAKE_INODE_NAME(inodename
, cp
->c_rdev
);
1735 bzero(&from_desc
, sizeof(from_desc
));
1736 from_desc
.cd_nameptr
= inodename
;
1737 from_desc
.cd_namelen
= strlen(inodename
);
1738 from_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
1739 from_desc
.cd_flags
= 0;
1740 from_desc
.cd_cnid
= cp
->c_fileid
;
1742 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1743 bzero(&to_desc
, sizeof(to_desc
));
1744 to_desc
.cd_nameptr
= delname
;
1745 to_desc
.cd_namelen
= strlen(delname
);
1746 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
1747 to_desc
.cd_flags
= 0;
1748 to_desc
.cd_cnid
= cp
->c_fileid
;
1750 (void) cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_privdir_desc
,
1751 &to_desc
, (struct cat_desc
*)NULL
);
1752 cp
->c_flag
|= C_DELETED
;
1755 /* Unlock the Catalog */
1756 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1758 /* All done with component name... */
1759 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
1760 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
1765 cp
->c_flag
|= C_CHANGE
;
1767 (void) VOP_UPDATE(vp
, &tv
, &tv
, 0);
1769 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1771 } else if (dataforkbusy
|| rsrcforkbusy
) {
1773 struct cat_desc to_desc
;
1774 struct cat_desc todir_desc
;
1777 * Orphan this file (move to hidden directory).
1779 bzero(&todir_desc
, sizeof(todir_desc
));
1780 todir_desc
.cd_parentcnid
= 2;
1782 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1783 bzero(&to_desc
, sizeof(to_desc
));
1784 to_desc
.cd_nameptr
= delname
;
1785 to_desc
.cd_namelen
= strlen(delname
);
1786 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
1787 to_desc
.cd_flags
= 0;
1788 to_desc
.cd_cnid
= cp
->c_cnid
;
1790 /* Lock catalog b-tree */
1791 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1795 error
= cat_rename(hfsmp
, &cp
->c_desc
, &todir_desc
,
1796 &to_desc
, (struct cat_desc
*)NULL
);
1798 // XXXdbg - only bump this count if we were successful
1800 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1802 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1803 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1805 /* Unlock the Catalog */
1806 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1807 if (error
) goto out
;
1809 cp
->c_flag
|= C_CHANGE
| C_DELETED
| C_NOEXISTS
;
1812 (void) VOP_UPDATE(vp
, &tv
, &tv
, 0);
1814 } else /* Not busy */ {
1816 if (vp
->v_type
== VDIR
&& cp
->c_entries
> 0)
1817 panic("hfs_remove: attempting to delete a non-empty directory!");
1818 if (vp
->v_type
!= VDIR
&& cp
->c_blocks
> 0)
1819 panic("hfs_remove: attempting to delete a non-empty file!");
1821 /* Lock catalog b-tree */
1822 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1826 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
1828 if (error
&& error
!= ENXIO
&& truncated
) {
1829 if ((cp
->c_datafork
&& cp
->c_datafork
->ff_data
.cf_size
!= 0) ||
1830 (cp
->c_rsrcfork
&& cp
->c_rsrcfork
->ff_data
.cf_size
!= 0)) {
1831 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1832 error
, cp
->c_datafork
->ff_data
.cf_size
, cp
->c_rsrcfork
->ff_data
.cf_size
);
1834 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1835 cp
->c_desc
.cd_nameptr
, cp
->c_attr
.ca_fileid
, error
);
1839 /* Unlock the Catalog */
1840 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1841 if (error
) goto out
;
1844 if (!hfs_getinoquota(cp
))
1845 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1849 cp
->c_flag
|= C_CHANGE
| C_NOEXISTS
;
1851 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1855 * All done with this cnode's descriptor...
1857 * Note: all future catalog calls for this cnode must be
1858 * by fileid only. This is OK for HFS (which doesn't have
1859 * file thread records) since HFS doesn't support hard
1860 * links or the removal of busy files.
1862 cat_releasedesc(&cp
->c_desc
);
1864 /* In all three cases the parent lost a child */
1865 if (dcp
->c_entries
> 0)
1867 if (dcp
->c_nlink
> 0)
1869 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1871 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
1875 VOP_UNLOCK(vp
, 0, p
);
1876 // XXXdbg - try to prevent the lost ubc_info panic
1877 if ((cp
->c_flag
& C_HARDLINK
) == 0 || cp
->c_nlink
== 0) {
1878 (void) ubc_uncache(vp
);
1885 journal_end_transaction(hfsmp
->jnl
);
1888 hfs_global_shared_lock_release(hfsmp
);
1897 /* Commit the truncation to the catalog record */
1899 cp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1901 (void) VOP_UPDATE(vp
, &tv
, &tv
, 0);
1908 journal_end_transaction(hfsmp
->jnl
);
1911 hfs_global_shared_lock_release(hfsmp
);
1918 __private_extern__
void
1919 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1921 /* First release allocated name buffer */
1922 if (cp
->c_desc
.cd_flags
& CD_HASBUF
&& cp
->c_desc
.cd_nameptr
!= 0) {
1923 char *name
= cp
->c_desc
.cd_nameptr
;
1925 cp
->c_desc
.cd_nameptr
= 0;
1926 cp
->c_desc
.cd_namelen
= 0;
1927 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
1930 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1932 /* Cnode now owns the name buffer */
1933 cdp
->cd_nameptr
= 0;
1934 cdp
->cd_namelen
= 0;
1935 cdp
->cd_flags
&= ~CD_HASBUF
;
1941 #% rename fdvp U U U
1943 #% rename tdvp L U U
1947 IN WILLRELE struct vnode *fdvp;
1948 IN WILLRELE struct vnode *fvp;
1949 IN struct componentname *fcnp;
1950 IN WILLRELE struct vnode *tdvp;
1951 IN WILLRELE struct vnode *tvp;
1952 IN struct componentname *tcnp;
1958 * The VFS layer guarantees that source and destination will
1959 * either both be directories, or both not be directories.
1961 * When the target is a directory, hfs_rename must ensure
1967 struct vop_rename_args
/* {
1968 struct vnode *a_fdvp;
1969 struct vnode *a_fvp;
1970 struct componentname *a_fcnp;
1971 struct vnode *a_tdvp;
1972 struct vnode *a_tvp;
1973 struct componentname *a_tcnp;
1976 struct vnode
*tvp
= ap
->a_tvp
;
1977 struct vnode
*tdvp
= ap
->a_tdvp
;
1978 struct vnode
*fvp
= ap
->a_fvp
;
1979 struct vnode
*fdvp
= ap
->a_fdvp
;
1980 struct componentname
*tcnp
= ap
->a_tcnp
;
1981 struct componentname
*fcnp
= ap
->a_fcnp
;
1982 struct cnode
*fcp
= NULL
;
1983 struct cnode
*fdcp
= NULL
;
1984 struct cnode
*tdcp
= NULL
;
1985 struct cnode
*tcp
= NULL
;
1986 struct cat_desc from_desc
;
1987 struct cat_desc to_desc
;
1988 struct cat_desc out_desc
;
1989 struct hfsmount
*hfsmp
;
1990 struct proc
*p
= fcnp
->cn_proc
;
1992 int retval
= 0, started_tr
= 0, grabbed_lock
= 0;
1993 int fdvp_locked
= 0;
1995 cnid_t oldparent
= 0;
1996 cnid_t newparent
= 0;
2000 hfsmp
= VTOHFS(fvp
);
2002 hfsmp
= VTOHFS(tvp
);
2007 if ((tcnp
->cn_flags
& HASBUF
) == 0 ||
2008 (fcnp
->cn_flags
& HASBUF
) == 0)
2009 panic("hfs_rename: no name");
2012 * When fvp matches tvp they must be case variants
2013 * or hard links, and if they are in the same directory then
2014 * tvp really doesn't exist (see VFS rename).
2015 * XXX Hard link rename is still broken/ignored. If they are
2016 * in different directories then we must have hard links.
2017 * Comments further down describe behaviour of hard links in same dir.
2018 * Note case insensitivity was and still is presumed.
2029 * Check for cross-device rename.
2031 if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
2032 (tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
2038 * Make sure a remove of "to" vnode is permitted.
2040 if (tvp
&& ((VTOC(tvp
)->c_flags
& (IMMUTABLE
| APPEND
)) ||
2041 (VTOC(tdvp
)->c_flags
& APPEND
))) {
2047 * Make sure "from" vnode and its parent are changeable.
2051 oldparent
= fdcp
->c_cnid
;
2052 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
2057 if (fcp
->c_parentcnid
!= fdcp
->c_cnid
) {
2063 * Check if names already match...
2064 * XXX The name being checked is from fcp rather than fcnp! If
2065 * there are hard links, fcp yields the name which was
2066 * most recently looked up (yes that design is vulnerable to races)
2067 * and the name most recently looked up was the target, so they
2068 * compare equal and we ignore the rename. XXX
2070 if (fvp
== ap
->a_tvp
&&
2071 (bcmp(fcp
->c_desc
.cd_nameptr
, tcnp
->cn_nameptr
,
2072 fcp
->c_desc
.cd_namelen
) == 0)) {
2077 /* XXX This doesn't make sense for HFS...
2079 * Be sure we are not renaming ".", "..", or an alias of ".". This
2080 * leads to a crippled directory tree. It's pretty tough to do a
2081 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2082 * doesn't work if the ".." entry is missing.
2084 if (fvp
->v_type
== VDIR
) {
2085 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
2087 || (fcnp
->cn_flags
&ISDOTDOT
)
2088 || (fcp
->c_flag
& C_RENAME
)) {
2092 fcp
->c_flag
|= C_RENAME
;
2095 /* XXX UFS does vrele(fdvp) here */
2097 /* From now on use bad instead of abort to exit */
2103 newparent
= tdcp
->c_cnid
;
2105 // XXXdbg - don't allow renaming the journal or journal_info_block
2106 if (hfsmp
->jnl
&& fcp
->c_datafork
) {
2107 struct HFSPlusExtentDescriptor
*extd
;
2109 extd
= &fcp
->c_datafork
->ff_data
.cf_extents
[0];
2110 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
2116 if (hfsmp
->jnl
&& tcp
&& tcp
->c_datafork
) {
2117 struct HFSPlusExtentDescriptor
*extd
;
2119 extd
= &tcp
->c_datafork
->ff_data
.cf_extents
[0];
2120 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
2126 retval
= VOP_ACCESS(fvp
, VWRITE
, tcnp
->cn_cred
, tcnp
->cn_proc
);
2127 if ((fvp
->v_type
== VDIR
) && (newparent
!= oldparent
)) {
2128 if (retval
) /* write access check above */
2131 retval
= 0; /* Reset value from above, we dont care about it anymore */
2134 * Prevent lock heirarchy violation (deadlock):
2136 * If fdvp is the parent of tdvp then we must drop
2137 * tdvp lock before aquiring the lock for fdvp.
2139 * XXXdbg - moved this to happen up here *before* we
2140 * start a transaction. otherwise we can
2141 * deadlock because the vnode layer may get
2142 * this lock for someone else and then they'll
2143 * never be able to start a transaction.
2145 if (newparent
!= oldparent
) {
2146 if (fdcp
->c_cnid
== tdcp
->c_parentcnid
) {
2148 vn_lock(fdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2149 vget(tdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2151 vn_lock(fdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2155 if ((retval
= vn_lock(fvp
, LK_EXCLUSIVE
| LK_RETRY
, p
)))
2160 hfs_global_shared_lock_acquire(hfsmp
);
2163 if ((retval
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
2170 * If the destination exists, then be sure its type (file or dir)
2171 * matches that of the source. And, if it is a directory make sure
2172 * it is empty. Then delete the destination.
2176 * If the parent directory is "sticky", then the user must
2177 * own the parent directory, or the destination of the rename,
2178 * otherwise the destination may not be changed (except by
2179 * root). This implements append-only directories.
2181 if ((tdcp
->c_mode
& S_ISTXT
) && (tcnp
->cn_cred
->cr_uid
!= 0) &&
2182 tcnp
->cn_cred
->cr_uid
!= tdcp
->c_uid
&&
2183 tcnp
->cn_cred
->cr_uid
!= tcp
->c_uid
) {
2189 * Target must be empty if a directory.
2191 if (S_ISDIR(tcp
->c_mode
) && (tcp
->c_nlink
> 2)) {
2197 * VOP_REMOVE will vput tdvp so we better bump
2198 * its ref count and relockit, always set tvp to
2199 * NULL afterwards to indicate that were done with it.
2205 tcnp
->cn_flags
&= ~SAVENAME
;
2207 if (tvp
->v_type
== VDIR
)
2208 retval
= VOP_RMDIR(tdvp
, tvp
, tcnp
);
2210 retval
= VOP_REMOVE(tdvp
, tvp
, tcnp
);
2212 (void) vn_lock(tdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2220 /* remove the existing entry from the namei cache: */
2223 bzero(&from_desc
, sizeof(from_desc
));
2224 from_desc
.cd_nameptr
= fcnp
->cn_nameptr
;
2225 from_desc
.cd_namelen
= fcnp
->cn_namelen
;
2226 from_desc
.cd_parentcnid
= fdcp
->c_cnid
;
2227 from_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2228 from_desc
.cd_cnid
= fcp
->c_cnid
;
2229 bzero(&to_desc
, sizeof(to_desc
));
2230 to_desc
.cd_nameptr
= tcnp
->cn_nameptr
;
2231 to_desc
.cd_namelen
= tcnp
->cn_namelen
;
2232 to_desc
.cd_parentcnid
= tdcp
->c_cnid
;
2233 to_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2234 to_desc
.cd_cnid
= fcp
->c_cnid
;
2236 /* Lock catalog b-tree */
2237 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2241 retval
= cat_rename(hfsmp
, &from_desc
, &tdcp
->c_desc
,
2242 &to_desc
, &out_desc
);
2244 /* Unlock catalog b-tree */
2245 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2247 if (newparent
!= oldparent
) {
2248 VOP_UNLOCK(fdvp
, 0, p
);
2252 if (retval
) goto bad
;
2254 /* update cnode's catalog descriptor */
2255 replace_desc(fcp
, &out_desc
);
2257 fcp
->c_flag
&= ~C_RENAME
;
2260 * Time stamp both parent directories.
2261 * Note that if this is a rename within the same directory,
2262 * (where tdcp == fdcp)
2263 * the code below is still safe and correct.
2265 if (fdcp
->c_nlink
> 0)
2267 if (fdcp
->c_entries
> 0)
2271 fdcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
2272 tdcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
2274 CTIMES(fdcp
, &tv
, &tv
);
2275 CTIMES(tdcp
, &tv
, &tv
);
2276 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2278 // make sure both directories get updated on disk.
2280 (void) VOP_UPDATE(fdvp
, &tv
, &tv
, 0);
2282 (void) VOP_UPDATE(tdvp
, &tv
, &tv
, 0);
2284 hfs_volupdate(hfsmp
, fvp
->v_type
== VDIR
? VOL_RMDIR
: VOL_RMFILE
,
2285 (fdcp
->c_cnid
== kHFSRootFolderID
));
2286 hfs_volupdate(hfsmp
, fvp
->v_type
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
2287 (tdcp
->c_cnid
== kHFSRootFolderID
));
2295 journal_end_transaction(hfsmp
->jnl
);
2298 hfs_global_shared_lock_release(hfsmp
);
2305 fcp
->c_flag
&= ~C_RENAME
;
2307 // XXXdbg make sure both directories get updated on disk.
2309 (void) VOP_UPDATE(fdvp
, &tv
, &tv
, 0);
2311 (void) VOP_UPDATE(tdvp
, &tv
, &tv
, 0);
2332 journal_end_transaction(hfsmp
->jnl
);
2335 hfs_global_shared_lock_release(hfsmp
);
2342 VOP_ABORTOP(tdvp
, tcnp
);
2349 VOP_ABORTOP(fdvp
, fcnp
);
2364 IN WILLRELE struct vnode *dvp;
2365 OUT struct vnode **vpp;
2366 IN struct componentname *cnp;
2367 IN struct vattr *vap;
2369 We are responsible for freeing the namei buffer,
2370 it is done in hfs_makenode()
2375 struct vop_mkdir_args
/* {
2376 struct vnode *a_dvp;
2377 struct vnode **a_vpp;
2378 struct componentname *a_cnp;
2379 struct vattr *a_vap;
2382 struct vattr
*vap
= ap
->a_vap
;
2384 return (hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
2385 ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
));
2390 * symlink -- make a symbolic link
2391 #% symlink dvp L U U
2392 #% symlink vpp - U -
2394 # XXX - note that the return vnode has already been VRELE'ed
2395 # by the filesystem layer. To use it you must use vget,
2396 # possibly with a further namei.
2399 IN WILLRELE struct vnode *dvp;
2400 OUT WILLRELE struct vnode **vpp;
2401 IN struct componentname *cnp;
2402 IN struct vattr *vap;
2405 We are responsible for freeing the namei buffer,
2406 it is done in hfs_makenode().
2412 struct vop_symlink_args
/* {
2413 struct vnode *a_dvp;
2414 struct vnode **a_vpp;
2415 struct componentname *a_cnp;
2416 struct vattr *a_vap;
2420 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
2421 struct hfsmount
*hfsmp
;
2422 struct filefork
*fp
;
2424 struct buf
*bp
= NULL
;
2426 /* HFS standard disks don't support symbolic links */
2427 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
2428 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2430 return (EOPNOTSUPP
);
2433 /* Check for empty target name */
2434 if (ap
->a_target
[0] == 0) {
2435 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2441 hfsmp
= VTOHFS(ap
->a_dvp
);
2443 /* Create the vnode */
2444 if ((error
= hfs_makenode(S_IFLNK
| ap
->a_vap
->va_mode
,
2445 ap
->a_dvp
, vpp
, ap
->a_cnp
))) {
2450 len
= strlen(ap
->a_target
);
2452 fp
->ff_clumpsize
= VTOVCB(vp
)->blockSize
;
2455 hfs_global_shared_lock_acquire(hfsmp
);
2457 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
2458 hfs_global_shared_lock_release(hfsmp
);
2459 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2465 /* Allocate space for the link */
2466 error
= VOP_TRUNCATE(vp
, len
, IO_NOZEROFILL
,
2467 ap
->a_cnp
->cn_cred
, ap
->a_cnp
->cn_proc
);
2469 goto out
; /* XXX need to remove link */
2471 /* Write the link to disk */
2472 bp
= getblk(vp
, 0, roundup((int)fp
->ff_size
, VTOHFS(vp
)->hfs_phys_block_size
),
2475 journal_modify_block_start(hfsmp
->jnl
, bp
);
2477 bzero(bp
->b_data
, bp
->b_bufsize
);
2478 bcopy(ap
->a_target
, bp
->b_data
, len
);
2480 journal_modify_block_end(hfsmp
->jnl
, bp
);
2486 journal_end_transaction(hfsmp
->jnl
);
2488 hfs_global_shared_lock_release(hfsmp
);
2495 * Dummy dirents to simulate the "." and ".." entries of the directory
2496 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2497 * the size of these entries is the smallest needed to represent them
2498 * (only 12 byte each).
2500 static hfsdotentry rootdots
[2] = {
2503 sizeof(struct hfsdotentry
), /* d_reclen */
2504 DT_DIR
, /* d_type */
2510 sizeof(struct hfsdotentry
), /* d_reclen */
2511 DT_DIR
, /* d_type */
2518 * There is some confusion as to what the semantics of uio_offset are.
2519 * In ufs, it represents the actual byte offset within the directory
2520 * "file." HFS, however, just uses it as an entry counter - essentially
2521 * assuming that it has no meaning except to the hfs_readdir function.
2522 * This approach would be more efficient here, but some callers may
2523 * assume the uio_offset acts like a byte offset. NFS in fact
2524 * monkeys around with the offset field a lot between readdir calls.
2526 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
2527 * fields is a mess as well. The libc function readdir() returns
2528 * NULL (indicating the end of a directory) when either
2529 * the getdirentries() syscall (which calls this and returns
2530 * the size of the buffer passed in less the value of uiop->uio_resid)
2531 * returns 0, or a direct record with a d_reclen of zero.
2532 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
2533 * of the directory by testing uiop->uio_resid == 0. The solution
2534 * is to pad the size of the last struct direct in a given
2535 * block to fill the block if we are not at the end of the directory.
2540 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
2541 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
2542 * "Nbytes must be greater than or equal to the block size associated with the file,
2543 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
2544 * room for the . and .. entries that have to added manually.
2551 IN struct vnode *vp;
2552 INOUT struct uio *uio;
2553 IN struct ucred *cred;
2556 INOUT u_long **cookies;
2560 struct vop_readdir_args
/* {
2569 register struct uio
*uio
= ap
->a_uio
;
2570 struct cnode
*cp
= VTOC(ap
->a_vp
);
2571 struct hfsmount
*hfsmp
= VTOHFS(ap
->a_vp
);
2572 struct proc
*p
= current_proc();
2573 off_t off
= uio
->uio_offset
;
2576 void *user_start
= NULL
;
2579 /* We assume it's all one big buffer... */
2580 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
)
2584 // We have to lock the user's buffer here so that we won't
2585 // fault on it after we've acquired a shared lock on the
2586 // catalog file. The issue is that you can get a 3-way
2587 // deadlock if someone else starts a transaction and then
2588 // tries to lock the catalog file but can't because we're
2589 // here and we can't service our page fault because VM is
2590 // blocked trying to start a transaction as a result of
2591 // trying to free up pages for our page fault. It's messy
2592 // but it does happen on dual-procesors that are paging
2593 // heavily (see radar 3082639 for more info). By locking
2594 // the buffer up-front we prevent ourselves from faulting
2595 // while holding the shared catalog file lock.
2597 // Fortunately this and hfs_search() are the only two places
2598 // currently (10/30/02) that can fault on user data with a
2599 // shared lock on the catalog file.
2601 if (hfsmp
->jnl
&& uio
->uio_segflg
== UIO_USERSPACE
) {
2602 user_start
= uio
->uio_iov
->iov_base
;
2603 user_len
= uio
->uio_iov
->iov_len
;
2605 if ((retval
= vslock(user_start
, user_len
)) != 0) {
2611 /* Create the entries for . and .. */
2612 if (uio
->uio_offset
< sizeof(rootdots
)) {
2616 rootdots
[0].d_fileno
= cp
->c_cnid
;
2617 rootdots
[1].d_fileno
= cp
->c_parentcnid
;
2619 if (uio
->uio_offset
== 0) {
2620 dep
= (caddr_t
) &rootdots
[0];
2621 dotsize
= 2* sizeof(struct hfsdotentry
);
2622 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
2623 dep
= (caddr_t
) &rootdots
[1];
2624 dotsize
= sizeof(struct hfsdotentry
);
2630 retval
= uiomove(dep
, dotsize
, uio
);
2635 /* If there are no children then we're done */
2636 if (cp
->c_entries
== 0) {
2642 /* Lock catalog b-tree */
2643 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
2644 if (retval
) goto Exit
;
2646 retval
= cat_getdirentries(hfsmp
, &cp
->c_desc
, uio
, &eofflag
);
2648 /* Unlock catalog b-tree */
2649 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2651 if (retval
!= E_NONE
) {
2655 /* were we already past eof ? */
2656 if (uio
->uio_offset
== off
) {
2661 cp
->c_flag
|= C_ACCESS
;
2662 /* Bake any cookies */
2663 if (!retval
&& ap
->a_ncookies
!= NULL
) {
2664 struct dirent
* dpStart
;
2665 struct dirent
* dpEnd
;
2672 * Only the NFS server uses cookies, and it loads the
2673 * directory block into system space, so we can just look at
2676 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
2677 panic("hfs_readdir: unexpected uio from NFS server");
2678 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
2679 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
2680 for (dp
= dpStart
, ncookies
= 0;
2681 dp
< dpEnd
&& dp
->d_reclen
!= 0;
2682 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
2684 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
2685 for (dp
= dpStart
, cookiep
= cookies
;
2687 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
2688 off
+= dp
->d_reclen
;
2689 *cookiep
++ = (u_long
) off
;
2691 *ap
->a_ncookies
= ncookies
;
2692 *ap
->a_cookies
= cookies
;
2696 if (hfsmp
->jnl
&& user_start
) {
2697 vsunlock(user_start
, user_len
, TRUE
);
2701 *ap
->a_eofflag
= eofflag
;
2708 * Return target name of a symbolic link
2709 #% readlink vp L L L
2712 IN struct vnode *vp;
2713 INOUT struct uio *uio;
2714 IN struct ucred *cred;
2719 struct vop_readlink_args
/* {
2722 struct ucred *a_cred;
2726 struct vnode
*vp
= ap
->a_vp
;
2728 struct filefork
*fp
;
2730 if (vp
->v_type
!= VLNK
)
2736 /* Zero length sym links are not allowed */
2737 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2738 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2742 /* Cache the path so we don't waste buffer cache resources */
2743 if (fp
->ff_symlinkptr
== NULL
) {
2744 struct buf
*bp
= NULL
;
2746 MALLOC(fp
->ff_symlinkptr
, char *, fp
->ff_size
, M_TEMP
, M_WAITOK
);
2747 retval
= meta_bread(vp
, 0,
2748 roundup((int)fp
->ff_size
,
2749 VTOHFS(vp
)->hfs_phys_block_size
),
2754 if (fp
->ff_symlinkptr
) {
2755 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2756 fp
->ff_symlinkptr
= NULL
;
2760 bcopy(bp
->b_data
, fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2762 if (VTOHFS(vp
)->jnl
&& (bp
->b_flags
& B_LOCKED
) == 0) {
2763 bp
->b_flags
|= B_INVAL
; /* data no longer needed */
2768 retval
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2775 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
2776 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
2777 #% abortop dvp = = =
2780 IN struct vnode *dvp;
2781 IN struct componentname *cnp;
2789 struct vop_abortop_args
/* {
2790 struct vnode *a_dvp;
2791 struct componentname *a_cnp;
2794 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
)
2795 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2802 * Lock an cnode. If its already locked, set the WANT bit and sleep.
2806 IN struct vnode *vp;
2813 struct vop_lock_args
/* {
2819 struct vnode
*vp
= ap
->a_vp
;
2820 struct cnode
*cp
= VTOC(vp
);
2823 panic("hfs_lock: cnode in vnode is null\n");
2825 return (lockmgr(&cp
->c_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
));
2833 IN struct vnode *vp;
2840 struct vop_unlock_args
/* {
2846 struct vnode
*vp
= ap
->a_vp
;
2847 struct cnode
*cp
= VTOC(vp
);
2850 panic("hfs_unlock: cnode in vnode is null\n");
2852 return (lockmgr(&cp
->c_lock
, ap
->a_flags
| LK_RELEASE
,
2853 &vp
->v_interlock
, ap
->a_p
));
2858 * Print out the contents of a cnode.
2862 IN struct vnode *vp;
2866 struct vop_print_args
/* {
2870 struct vnode
* vp
= ap
->a_vp
;
2871 struct cnode
*cp
= VTOC(vp
);
2873 printf("tag VT_HFS, cnid %d, on dev %d, %d", cp
->c_cnid
,
2874 major(cp
->c_dev
), minor(cp
->c_dev
));
2876 if (vp
->v_type
== VFIFO
)
2879 lockmgr_printinfo(&cp
->c_lock
);
2886 * Check for a locked cnode.
2887 #% islocked vp = = =
2890 IN struct vnode *vp;
2895 struct vop_islocked_args
/* {
2899 return (lockstatus(&VTOC(ap
->a_vp
)->c_lock
));
2904 #% pathconf vp L L L
2907 IN struct vnode *vp;
2909 OUT register_t *retval;
2914 struct vop_pathconf_args
/* {
2922 switch (ap
->a_name
) {
2924 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
2925 *ap
->a_retval
= HFS_LINK_MAX
;
2930 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
2933 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2935 case _PC_CHOWN_RESTRICTED
:
2941 case _PC_NAME_CHARS_MAX
:
2942 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2944 case _PC_CASE_SENSITIVE
:
2947 case _PC_CASE_PRESERVING
:
2959 * Advisory record locking support
2963 IN struct vnode *vp;
2966 IN struct flock *fl;
2972 struct vop_advlock_args
/* {
2980 struct vnode
*vp
= ap
->a_vp
;
2981 struct flock
*fl
= ap
->a_fl
;
2982 struct hfslockf
*lock
;
2983 struct filefork
*fork
;
2987 /* Only regular files can have locks */
2988 if (vp
->v_type
!= VREG
)
2991 fork
= VTOF(ap
->a_vp
);
2993 * Avoid the common case of unlocking when cnode has no locks.
2995 if (fork
->ff_lockf
== (struct hfslockf
*)0) {
2996 if (ap
->a_op
!= F_SETLK
) {
2997 fl
->l_type
= F_UNLCK
;
3002 * Convert the flock structure into a start and end.
3005 switch (fl
->l_whence
) {
3009 * Caller is responsible for adding any necessary offset
3010 * when SEEK_CUR is used.
3012 start
= fl
->l_start
;
3015 start
= fork
->ff_size
+ fl
->l_start
;
3026 end
= start
+ fl
->l_len
- 1;
3029 * Create the hfslockf structure
3031 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
3032 lock
->lf_start
= start
;
3034 lock
->lf_id
= ap
->a_id
;
3035 lock
->lf_fork
= fork
;
3036 lock
->lf_type
= fl
->l_type
;
3037 lock
->lf_next
= (struct hfslockf
*)0;
3038 TAILQ_INIT(&lock
->lf_blkhd
);
3039 lock
->lf_flags
= ap
->a_flags
;
3041 * Do the requested operation.
3045 retval
= hfs_setlock(lock
);
3048 retval
= hfs_clearlock(lock
);
3049 FREE(lock
, M_LOCKF
);
3052 retval
= hfs_getlock(lock
, fl
);
3053 FREE(lock
, M_LOCKF
);
3057 _FREE(lock
, M_LOCKF
);
3067 * Update the access, modified, and node change times as specified
3068 * by the C_ACCESS, C_UPDATE, and C_CHANGE flags respectively. The
3069 * C_MODIFIED flag is used to specify that the node needs to be
3070 * updated but that the times have already been set. The access and
3071 * modified times are input parameters but the node change time is
3072 * always taken from the current time. If waitfor is set, then wait
3073 * for the disk write of the node to complete.
3077 IN struct vnode *vp;
3078 IN struct timeval *access;
3079 IN struct timeval *modify;
3084 struct vop_update_args
/* {
3086 struct timeval *a_access;
3087 struct timeval *a_modify;
3091 struct vnode
*vp
= ap
->a_vp
;
3092 struct cnode
*cp
= VTOC(ap
->a_vp
);
3094 struct cat_fork
*dataforkp
= NULL
;
3095 struct cat_fork
*rsrcforkp
= NULL
;
3096 struct cat_fork datafork
;
3098 struct hfsmount
*hfsmp
;
3103 /* XXX do we really want to clear the sytem cnode flags here???? */
3104 if ((vp
->v_flag
& VSYSTEM
) ||
3105 (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) ||
3106 (cp
->c_mode
== 0)) {
3107 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
3111 updateflag
= cp
->c_flag
& (C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
3113 /* Nothing to update. */
3114 if (updateflag
== 0) {
3117 /* HFS standard doesn't have access times. */
3118 if ((updateflag
== C_ACCESS
) && (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
)) {
3121 if (updateflag
& C_ACCESS
) {
3123 * If only the access time is changing then defer
3124 * updating it on-disk util later (in hfs_inactive).
3125 * If it was recently updated then skip the update.
3127 if (updateflag
== C_ACCESS
) {
3128 cp
->c_flag
&= ~C_ACCESS
;
3130 /* Its going to disk or its sufficiently newer... */
3131 if ((cp
->c_flag
& C_ATIMEMOD
) ||
3132 (ap
->a_access
->tv_sec
> (cp
->c_atime
+ ATIME_ACCURACY
))) {
3133 cp
->c_atime
= ap
->a_access
->tv_sec
;
3134 cp
->c_flag
|= C_ATIMEMOD
;
3138 cp
->c_atime
= ap
->a_access
->tv_sec
;
3141 if (updateflag
& C_UPDATE
) {
3142 cp
->c_mtime
= ap
->a_modify
->tv_sec
;
3143 cp
->c_mtime_nsec
= ap
->a_modify
->tv_usec
* 1000;
3145 if (updateflag
& C_CHANGE
) {
3146 cp
->c_ctime
= time
.tv_sec
;
3148 * HFS dates that WE set must be adjusted for DST
3150 if ((VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
3151 cp
->c_ctime
+= 3600;
3152 cp
->c_mtime
= cp
->c_ctime
;
3157 dataforkp
= &cp
->c_datafork
->ff_data
;
3159 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
3164 * For delayed allocations updates are
3165 * postponed until an fsync or the file
3166 * gets written to disk.
3168 * Deleted files can defer meta data updates until inactive.
3170 if (ISSET(cp
->c_flag
, C_DELETED
) ||
3171 (dataforkp
&& cp
->c_datafork
->ff_unallocblocks
) ||
3172 (rsrcforkp
&& cp
->c_rsrcfork
->ff_unallocblocks
)) {
3173 if (updateflag
& (C_CHANGE
| C_UPDATE
))
3174 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
3175 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_UPDATE
);
3176 cp
->c_flag
|= C_MODIFIED
;
3183 hfs_global_shared_lock_acquire(hfsmp
);
3185 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
3186 hfs_global_shared_lock_release(hfsmp
);
3193 * For files with invalid ranges (holes) the on-disk
3194 * field representing the size of the file (cf_size)
3195 * must be no larger than the start of the first hole.
3197 if (dataforkp
&& !CIRCLEQ_EMPTY(&cp
->c_datafork
->ff_invalidranges
)) {
3198 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
3199 datafork
.cf_size
= CIRCLEQ_FIRST(&cp
->c_datafork
->ff_invalidranges
)->rl_start
;
3200 dataforkp
= &datafork
;
3204 * Lock the Catalog b-tree file.
3205 * A shared lock is sufficient since an update doesn't change
3206 * the tree and the lock on vp protects the cnode.
3208 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
3211 journal_end_transaction(hfsmp
->jnl
);
3213 hfs_global_shared_lock_release(hfsmp
);
3217 /* XXX - waitfor is not enforced */
3218 error
= cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
3220 /* Unlock the Catalog b-tree file. */
3221 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
3223 if (updateflag
& (C_CHANGE
| C_UPDATE
))
3224 hfs_volupdate(hfsmp
, VOL_UPDATE
, 0);
3228 journal_end_transaction(hfsmp
->jnl
);
3230 hfs_global_shared_lock_release(hfsmp
);
3232 /* After the updates are finished, clear the flags */
3233 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
| C_ATIMEMOD
);
3239 * Allocate a new node
3241 * Upon leaving, namei buffer must be freed.
3245 hfs_makenode(mode
, dvp
, vpp
, cnp
)
3249 struct componentname
*cnp
;
3254 struct hfsmount
*hfsmp
;
3257 struct cat_desc in_desc
, out_desc
;
3258 struct cat_attr attr
;
3259 int error
, started_tr
= 0, grabbed_lock
= 0;
3260 enum vtype vnodetype
;
3264 hfsmp
= VTOHFS(dvp
);
3267 bzero(&out_desc
, sizeof(out_desc
));
3269 if ((mode
& S_IFMT
) == 0)
3271 vnodetype
= IFTOVT(mode
);
3273 /* Check if unmount in progress */
3274 if (VTOVFS(dvp
)->mnt_kern_flag
& MNTK_UNMOUNT
) {
3278 /* Check if were out of usable disk space. */
3279 if ((suser(cnp
->cn_cred
, NULL
) != 0) && (hfs_freeblks(hfsmp
, 1) <= 0)) {
3284 /* Setup the default attributes */
3285 bzero(&attr
, sizeof(attr
));
3286 attr
.ca_mode
= mode
;
3287 attr
.ca_nlink
= vnodetype
== VDIR
? 2 : 1;
3288 attr
.ca_mtime
= time
.tv_sec
;
3289 attr
.ca_mtime_nsec
= time
.tv_usec
* 1000;
3290 if ((VTOVCB(dvp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
3291 attr
.ca_mtime
+= 3600; /* Same as what hfs_update does */
3293 attr
.ca_atime
= attr
.ca_ctime
= attr
.ca_itime
= attr
.ca_mtime
;
3294 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
3295 attr
.ca_uid
= hfsmp
->hfs_uid
;
3296 attr
.ca_gid
= hfsmp
->hfs_gid
;
3298 if (vnodetype
== VLNK
)
3299 attr
.ca_uid
= dcp
->c_uid
;
3301 attr
.ca_uid
= cnp
->cn_cred
->cr_uid
;
3302 attr
.ca_gid
= dcp
->c_gid
;
3305 * Don't tag as a special file (BLK or CHR) until *after*
3306 * hfs_getnewvnode is called. This insures that any
3307 * alias checking is defered until hfs_mknod completes.
3309 if (vnodetype
== VBLK
|| vnodetype
== VCHR
)
3310 attr
.ca_mode
= (attr
.ca_mode
& ~S_IFMT
) | S_IFREG
;
3312 /* Tag symlinks with a type and creator. */
3313 if (vnodetype
== VLNK
) {
3314 struct FndrFileInfo
*fip
;
3316 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
3317 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
3318 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
3320 if ((attr
.ca_mode
& S_ISGID
) &&
3321 !groupmember(dcp
->c_gid
, cnp
->cn_cred
) &&
3322 suser(cnp
->cn_cred
, NULL
)) {
3323 attr
.ca_mode
&= ~S_ISGID
;
3325 if (cnp
->cn_flags
& ISWHITEOUT
)
3326 attr
.ca_flags
|= UF_OPAQUE
;
3328 /* Setup the descriptor */
3329 bzero(&in_desc
, sizeof(in_desc
));
3330 in_desc
.cd_nameptr
= cnp
->cn_nameptr
;
3331 in_desc
.cd_namelen
= cnp
->cn_namelen
;
3332 in_desc
.cd_parentcnid
= dcp
->c_cnid
;
3333 in_desc
.cd_flags
= S_ISDIR(mode
) ? CD_ISDIR
: 0;
3336 hfs_global_shared_lock_acquire(hfsmp
);
3339 if ((error
= journal_start_transaction(hfsmp
->jnl
)) != 0) {
3345 /* Lock catalog b-tree */
3346 error
= hfs_metafilelocking(VTOHFS(dvp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
3350 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
3352 /* Unlock catalog b-tree */
3353 (void) hfs_metafilelocking(VTOHFS(dvp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3357 /* Update the parent directory */
3358 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
3361 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
3363 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
3365 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
3366 (dcp
->c_cnid
== kHFSRootFolderID
));
3369 // have to end the transaction here before we call hfs_getnewvnode()
3370 // because that can cause us to try and reclaim a vnode on a different
3371 // file system which could cause us to start a transaction which can
3372 // deadlock with someone on that other file system (since we could be
3373 // holding two transaction locks as well as various vnodes and we did
3374 // not obtain the locks on them in the proper order).
3376 // NOTE: this means that if the quota check fails or we have to update
3377 // the change time on a block-special device that those changes
3378 // will happen as part of independent transactions.
3381 journal_end_transaction(hfsmp
->jnl
);
3385 hfs_global_shared_lock_release(hfsmp
);
3389 /* Create a vnode for the object just created: */
3390 error
= hfs_getnewvnode(hfsmp
, NULL
, &out_desc
, 0, &attr
, NULL
, &tvp
);
3398 * We call hfs_chkiq with FORCE flag so that if we
3399 * fall through to the rmdir we actually have
3400 * accounted for the inode
3402 if ((error
= hfs_getinoquota(cp
)) ||
3403 (error
= hfs_chkiq(cp
, 1, cnp
->cn_cred
, FORCE
))) {
3404 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
3405 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
3407 if (tvp
->v_type
== VDIR
)
3408 VOP_RMDIR(dvp
,tvp
, cnp
);
3410 VOP_REMOVE(dvp
,tvp
, cnp
);
3417 * restore vtype and mode for VBLK and VCHR
3419 if (vnodetype
== VBLK
|| vnodetype
== VCHR
) {
3424 tvp
->v_type
= IFTOVT(mode
);
3425 cp
->c_flag
|= C_CHANGE
;
3427 if ((error
= VOP_UPDATE(tvp
, &tv
, &tv
, 1))) {
3435 cat_releasedesc(&out_desc
);
3437 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
)
3438 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
3443 journal_end_transaction(hfsmp
->jnl
);
3447 hfs_global_shared_lock_release(hfsmp
);
3456 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, struct proc
*p
)
3459 struct cnode
*cp
= VTOC(vp
);
3462 if ((rvp
= cp
->c_rsrc_vp
)) {
3463 /* Use exising vnode */
3464 error
= vget(rvp
, 0, p
);
3466 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
3469 printf("hfs_vgetrsrc: couldn't get"
3470 " resource fork for %s\n", name
);
3474 struct cat_fork rsrcfork
;
3476 /* Lock catalog b-tree */
3477 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
3481 /* Get resource fork data */
3482 error
= cat_lookup(hfsmp
, &cp
->c_desc
, 1, (struct cat_desc
*)0,
3483 (struct cat_attr
*)0, &rsrcfork
);
3485 /* Unlock the Catalog */
3486 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
3490 error
= hfs_getnewvnode(hfsmp
, cp
, &cp
->c_desc
, 1, &cp
->c_attr
,
3502 * Wrapper for special device reads
3506 struct vop_read_args
/* {
3510 struct ucred *a_cred;
3516 VTOC(ap
->a_vp
)->c_flag
|= C_ACCESS
;
3517 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
3521 * Wrapper for special device writes
3525 struct vop_write_args
/* {
3529 struct ucred *a_cred;
3533 * Set update and change flags.
3535 VTOC(ap
->a_vp
)->c_flag
|= C_CHANGE
| C_UPDATE
;
3536 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
3540 * Wrapper for special device close
3542 * Update the times on the cnode then do device close.
3546 struct vop_close_args
/* {
3549 struct ucred *a_cred;
3553 struct vnode
*vp
= ap
->a_vp
;
3554 struct cnode
*cp
= VTOC(vp
);
3556 simple_lock(&vp
->v_interlock
);
3557 if (ap
->a_vp
->v_usecount
> 1)
3558 CTIMES(cp
, &time
, &time
);
3559 simple_unlock(&vp
->v_interlock
);
3560 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
3565 * Wrapper for fifo reads
3569 struct vop_read_args
/* {
3573 struct ucred *a_cred;
3576 extern int (**fifo_vnodeop_p
)(void *);
3581 VTOC(ap
->a_vp
)->c_flag
|= C_ACCESS
;
3582 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
3586 * Wrapper for fifo writes
3590 struct vop_write_args
/* {
3594 struct ucred *a_cred;
3597 extern int (**fifo_vnodeop_p
)(void *);
3600 * Set update and change flags.
3602 VTOC(ap
->a_vp
)->c_flag
|= C_CHANGE
| C_UPDATE
;
3603 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
3607 * Wrapper for fifo close
3609 * Update the times on the cnode then do device close.
3613 struct vop_close_args
/* {
3616 struct ucred *a_cred;
3620 extern int (**fifo_vnodeop_p
)(void *);
3621 struct vnode
*vp
= ap
->a_vp
;
3622 struct cnode
*cp
= VTOC(vp
);
3624 simple_lock(&vp
->v_interlock
);
3625 if (ap
->a_vp
->v_usecount
> 1)
3626 CTIMES(cp
, &time
, &time
);
3627 simple_unlock(&vp
->v_interlock
);
3628 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
3633 /*****************************************************************************
3637 *****************************************************************************/
3638 int hfs_cache_lookup(); /* in hfs_lookup.c */
3639 int hfs_lookup(); /* in hfs_lookup.c */
3640 int hfs_read(); /* in hfs_readwrite.c */
3641 int hfs_write(); /* in hfs_readwrite.c */
3642 int hfs_ioctl(); /* in hfs_readwrite.c */
3643 int hfs_select(); /* in hfs_readwrite.c */
3644 int hfs_bmap(); /* in hfs_readwrite.c */
3645 int hfs_strategy(); /* in hfs_readwrite.c */
3646 int hfs_truncate(); /* in hfs_readwrite.c */
3647 int hfs_allocate(); /* in hfs_readwrite.c */
3648 int hfs_pagein(); /* in hfs_readwrite.c */
3649 int hfs_pageout(); /* in hfs_readwrite.c */
3650 int hfs_search(); /* in hfs_search.c */
3651 int hfs_bwrite(); /* in hfs_readwrite.c */
3652 int hfs_link(); /* in hfs_link.c */
3653 int hfs_blktooff(); /* in hfs_readwrite.c */
3654 int hfs_offtoblk(); /* in hfs_readwrite.c */
3655 int hfs_cmap(); /* in hfs_readwrite.c */
3656 int hfs_getattrlist(); /* in hfs_attrlist.c */
3657 int hfs_setattrlist(); /* in hfs_attrlist.c */
3658 int hfs_readdirattr(); /* in hfs_attrlist.c */
3659 int hfs_inactive(); /* in hfs_cnode.c */
3660 int hfs_reclaim(); /* in hfs_cnode.c */
3662 int (**hfs_vnodeop_p
)(void *);
3664 #define VOPFUNC int (*)(void *)
3666 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
3667 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3668 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
3669 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
3670 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
3671 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
3672 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
3673 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3674 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3675 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3676 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
3677 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
3678 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
3679 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
3680 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
3681 { &vop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
3682 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3683 { &vop_seek_desc
, (VOPFUNC
)nop_seek
}, /* seek */
3684 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
3685 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
3686 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
3687 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
3688 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
3689 { &vop_mkcomplex_desc
, (VOPFUNC
)err_mkcomplex
}, /* mkcomplex */
3690 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
3691 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
3692 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
3693 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
3694 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
3695 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
3696 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
3697 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3698 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3699 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3700 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3701 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
3702 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
3703 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3704 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3705 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
3706 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
3707 { &vop_reallocblks_desc
, (VOPFUNC
)err_reallocblks
}, /* reallocblks */
3708 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
3709 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
3710 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3711 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
3712 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
3713 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
3714 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
3715 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3716 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3717 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3718 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
3719 { NULL
, (VOPFUNC
)NULL
}
3722 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3723 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
3725 int (**hfs_specop_p
)(void *);
3726 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
3727 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3728 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
3729 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
3730 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
3731 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
3732 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
3733 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3734 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3735 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3736 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
3737 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
3738 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
3739 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
3740 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
3741 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
3742 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
3743 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3744 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
3745 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
3746 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
3747 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
3748 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
3749 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
3750 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
3751 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
3752 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
3753 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
3754 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3755 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3756 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3757 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3758 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
3759 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
3760 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3761 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3762 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
3763 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
3764 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
3765 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
3766 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
3767 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
3768 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
3769 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3770 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
3771 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
3772 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
3773 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
3774 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3775 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3776 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3777 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3779 struct vnodeopv_desc hfs_specop_opv_desc
=
3780 { &hfs_specop_p
, hfs_specop_entries
};
3783 int (**hfs_fifoop_p
)(void *);
3784 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
3785 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3786 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
3787 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
3788 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
3789 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
3790 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
3791 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3792 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3793 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3794 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
3795 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
3796 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
3797 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
3798 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
3799 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
3800 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
3801 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3802 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
3803 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
3804 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
3805 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
3806 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
3807 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
3808 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
3809 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
3810 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
3811 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
3812 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3813 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3814 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3815 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3816 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
3817 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
3818 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3819 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3820 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
3821 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
3822 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
3823 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
3824 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
3825 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
3826 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
3827 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3828 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
3829 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
3830 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
3831 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3832 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3833 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3834 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
3835 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3837 struct vnodeopv_desc hfs_fifoop_opv_desc
=
3838 { &hfs_fifoop_p
, hfs_fifoop_entries
};