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 * Go through the fields and update iff not VNOVAL.
567 if (vap
->va_uid
!= (uid_t
)VNOVAL
|| vap
->va_gid
!= (gid_t
)VNOVAL
) {
568 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
570 if ((error
= hfs_chown(vp
, vap
->va_uid
, vap
->va_gid
, cred
, p
)))
573 if (vap
->va_size
!= VNOVAL
) {
575 * Disallow write attempts on read-only file systems;
576 * unless the file is a socket, fifo, or a block or
577 * character device resident on the file system.
579 switch (vp
->v_type
) {
584 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
590 if ((error
= VOP_TRUNCATE(vp
, vap
->va_size
, 0, cred
, p
)))
594 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
595 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
597 if (((error
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0) &&
598 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
599 (error
= VOP_ACCESS(vp
, VWRITE
, cred
, p
)))) {
602 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
603 cp
->c_flag
|= C_ACCESS
;
604 if (vap
->va_mtime
.tv_sec
!= VNOVAL
) {
605 cp
->c_flag
|= C_CHANGE
| C_UPDATE
;
607 * The utimes system call can reset the modification
608 * time but it doesn't know about HFS create times.
609 * So we need to insure that the creation time is
610 * always at least as old as the modification time.
612 if ((VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
613 (cp
->c_cnid
!= kRootDirID
) &&
614 (vap
->va_mtime
.tv_sec
< cp
->c_itime
)) {
615 cp
->c_itime
= vap
->va_mtime
.tv_sec
;
618 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
619 atimeval
.tv_usec
= 0;
620 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
621 mtimeval
.tv_usec
= 0;
622 if ((error
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1)))
626 if (vap
->va_mode
!= (mode_t
)VNOVAL
) {
627 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
629 error
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
);
636 * Change the mode on a file.
637 * cnode must be locked before calling.
640 hfs_chmod(vp
, mode
, cred
, p
)
641 register struct vnode
*vp
;
643 register struct ucred
*cred
;
646 register struct cnode
*cp
= VTOC(vp
);
649 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
652 #if OVERRIDE_UNKNOWN_PERMISSIONS
653 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
657 if ((error
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0)
660 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
662 if (!groupmember(cp
->c_gid
, cred
) && (mode
& S_ISGID
))
665 cp
->c_mode
&= ~ALLPERMS
;
666 cp
->c_mode
|= (mode
& ALLPERMS
);
667 cp
->c_flag
|= C_CHANGE
;
673 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
675 struct cnode
*cp
= VTOC(vp
);
681 * Disallow write attempts on read-only file systems;
682 * unless the file is a socket, fifo, or a block or
683 * character device resident on the file system.
685 switch (vp
->v_type
) {
689 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
696 /* If immutable bit set, nobody gets to write it. */
697 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
700 /* Otherwise, user id 0 always gets access. */
701 if (cred
->cr_uid
== 0)
704 /* Otherwise, check the owner. */
705 if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, false)) == 0)
706 return ((cp
->c_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
708 /* Otherwise, check the groups. */
709 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++) {
710 if (cp
->c_gid
== *gp
)
711 return ((cp
->c_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
714 /* Otherwise, check everyone else. */
715 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
721 * Change the flags on a file or directory.
722 * cnode must be locked before calling.
725 hfs_chflags(vp
, flags
, cred
, p
)
726 register struct vnode
*vp
;
727 register u_long flags
;
728 register struct ucred
*cred
;
731 register struct cnode
*cp
= VTOC(vp
);
734 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
735 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
738 } else if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, true)) != 0) {
742 if (cred
->cr_uid
== 0) {
743 if ((cp
->c_flags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
749 if (cp
->c_flags
& (SF_IMMUTABLE
| SF_APPEND
) ||
750 (flags
& UF_SETTABLE
) != flags
) {
753 cp
->c_flags
&= SF_SETTABLE
;
754 cp
->c_flags
|= (flags
& UF_SETTABLE
);
756 cp
->c_flag
|= C_CHANGE
;
763 * Perform chown operation on cnode cp;
764 * code must be locked prior to call.
767 hfs_chown(vp
, uid
, gid
, cred
, p
)
768 register struct vnode
*vp
;
774 register struct cnode
*cp
= VTOC(vp
);
783 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
786 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
)
789 if (uid
== (uid_t
)VNOVAL
)
791 if (gid
== (gid_t
)VNOVAL
)
794 * If we don't own the file, are trying to change the owner
795 * of the file, or are not a member of the target group,
796 * the caller must be superuser or the call fails.
798 if ((cred
->cr_uid
!= cp
->c_uid
|| uid
!= cp
->c_uid
||
799 (gid
!= cp
->c_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
800 (error
= suser(cred
, &p
->p_acflag
)))
806 if ((error
= hfs_getinoquota(cp
)))
809 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
810 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
813 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
814 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
818 * Eventually need to account for (fake) a block per directory
819 *if (vp->v_type == VDIR)
820 *change = VTOVCB(vp)->blockSize;
824 change
= (int64_t)(cp
->c_blocks
) * (int64_t)VTOVCB(vp
)->blockSize
;
825 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
);
826 (void) hfs_chkiq(cp
, -1, cred
, CHOWN
);
827 for (i
= 0; i
< MAXQUOTAS
; i
++) {
828 dqrele(vp
, cp
->c_dquot
[i
]);
829 cp
->c_dquot
[i
] = NODQUOT
;
835 if ((error
= hfs_getinoquota(cp
)) == 0) {
837 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
838 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
841 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
842 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
844 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
845 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
848 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
850 for (i
= 0; i
< MAXQUOTAS
; i
++) {
851 dqrele(vp
, cp
->c_dquot
[i
]);
852 cp
->c_dquot
[i
] = NODQUOT
;
857 if (hfs_getinoquota(cp
) == 0) {
859 dqrele(vp
, cp
->c_dquot
[USRQUOTA
]);
860 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
863 dqrele(vp
, cp
->c_dquot
[GRPQUOTA
]);
864 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
866 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
867 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
868 (void) hfs_getinoquota(cp
);
872 if (hfs_getinoquota(cp
))
873 panic("hfs_chown: lost quota");
876 if (ouid
!= uid
|| ogid
!= gid
)
877 cp
->c_flag
|= C_CHANGE
;
878 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
879 cp
->c_mode
&= ~S_ISUID
;
880 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
881 cp
->c_mode
&= ~S_ISGID
;
888 #% exchange fvp L L L
889 #% exchange tvp L L L
893 * The hfs_exchange routine swaps the fork data in two files by
894 * exchanging some of the information in the cnode. It is used
895 * to preserve the file ID when updating an existing file, in
896 * case the file is being tracked through its file ID. Typically
897 * its used after creating a new file during a safe-save.
902 struct vop_exchange_args
/* {
905 struct ucred *a_cred;
909 struct vnode
*from_vp
= ap
->a_fvp
;
910 struct vnode
*to_vp
= ap
->a_tvp
;
911 struct vnode
*from_rvp
= NULL
;
912 struct vnode
*to_rvp
= NULL
;
913 struct cnode
*from_cp
= VTOC(from_vp
);
914 struct cnode
*to_cp
= VTOC(to_vp
);
915 struct hfsmount
*hfsmp
= VTOHFS(from_vp
);
916 struct cat_desc tempdesc
;
917 struct cat_attr tempattr
;
920 /* The files must be on the same volume. */
921 if (from_vp
->v_mount
!= to_vp
->v_mount
)
924 /* Only normal files can be exchanged. */
925 if ((from_vp
->v_type
!= VREG
) || (to_vp
->v_type
!= VREG
) ||
926 (from_cp
->c_flag
& C_HARDLINK
) || (to_cp
->c_flag
& C_HARDLINK
) ||
927 VNODE_IS_RSRC(from_vp
) || VNODE_IS_RSRC(to_vp
))
930 from_rvp
= from_cp
->c_rsrc_vp
;
931 to_rvp
= to_cp
->c_rsrc_vp
;
933 /* If one of the resource forks is open then get the other one. */
934 if (from_rvp
|| to_rvp
) {
935 error
= hfs_vgetrsrc(hfsmp
, from_vp
, &from_rvp
, ap
->a_p
);
938 error
= hfs_vgetrsrc(hfsmp
, to_vp
, &to_rvp
, ap
->a_p
);
945 /* Ignore any errors, we are doing a 'best effort' on flushing */
947 (void) vinvalbuf(from_vp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
949 (void) vinvalbuf(to_vp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
951 (void) vinvalbuf(from_rvp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
953 (void) vinvalbuf(to_rvp
, V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
955 /* Lock catalog b-tree */
956 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
957 if (error
) goto Err_Exit
;
959 /* The backend code always tries to delete the virtual
960 * extent id for exchanging files so we neeed to lock
961 * the extents b-tree.
963 error
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
965 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
969 /* Do the exchange */
970 error
= MacToVFSError(ExchangeFileIDs(HFSTOVCB(hfsmp
),
971 from_cp
->c_desc
.cd_nameptr
, to_cp
->c_desc
.cd_nameptr
,
972 from_cp
->c_parentcnid
, to_cp
->c_parentcnid
,
973 from_cp
->c_hint
, to_cp
->c_hint
));
975 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
976 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
978 if (error
!= E_NONE
) {
982 /* Purge the vnodes from the name cache */
984 cache_purge(from_vp
);
988 /* Save a copy of from attributes before swapping. */
989 bcopy(&from_cp
->c_desc
, &tempdesc
, sizeof(struct cat_desc
));
990 bcopy(&from_cp
->c_attr
, &tempattr
, sizeof(struct cat_attr
));
993 * Swap the descriptors and all non-fork related attributes.
994 * (except the modify date)
996 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
998 from_cp
->c_fileid
= from_cp
->c_cnid
;
999 from_cp
->c_itime
= to_cp
->c_itime
;
1000 from_cp
->c_btime
= to_cp
->c_btime
;
1001 from_cp
->c_atime
= to_cp
->c_atime
;
1002 from_cp
->c_ctime
= to_cp
->c_ctime
;
1003 from_cp
->c_gid
= to_cp
->c_gid
;
1004 from_cp
->c_uid
= to_cp
->c_uid
;
1005 from_cp
->c_flags
= to_cp
->c_flags
;
1006 from_cp
->c_mode
= to_cp
->c_mode
;
1007 bcopy(to_cp
->c_finderinfo
, from_cp
->c_finderinfo
, 32);
1009 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
1011 to_cp
->c_fileid
= to_cp
->c_cnid
;
1012 to_cp
->c_itime
= tempattr
.ca_itime
;
1013 to_cp
->c_btime
= tempattr
.ca_btime
;
1014 to_cp
->c_atime
= tempattr
.ca_atime
;
1015 to_cp
->c_ctime
= tempattr
.ca_ctime
;
1016 to_cp
->c_gid
= tempattr
.ca_gid
;
1017 to_cp
->c_uid
= tempattr
.ca_uid
;
1018 to_cp
->c_flags
= tempattr
.ca_flags
;
1019 to_cp
->c_mode
= tempattr
.ca_mode
;
1020 bcopy(tempattr
.ca_finderinfo
, to_cp
->c_finderinfo
, 32);
1022 /* Reinsert into the cnode hash under new file IDs*/
1023 hfs_chashremove(from_cp
);
1024 hfs_chashremove(to_cp
);
1026 hfs_chashinsert(from_cp
);
1027 hfs_chashinsert(to_cp
);
1043 IN struct vnode *vp;
1044 IN struct ucred *cred;
1052 struct vop_fsync_args
/* {
1054 struct ucred *a_cred;
1059 struct vnode
*vp
= ap
->a_vp
;
1060 struct cnode
*cp
= VTOC(vp
);
1061 struct filefork
*fp
= NULL
;
1063 register struct buf
*bp
;
1070 wait
= (ap
->a_waitfor
== MNT_WAIT
);
1072 /* HFS directories don't have any data blocks. */
1073 if (vp
->v_type
== VDIR
)
1077 * For system files flush the B-tree header and
1078 * for regular files write out any clusters
1080 if (vp
->v_flag
& VSYSTEM
) {
1081 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
)
1082 BTFlushPath(VTOF(vp
));
1083 } else if (UBCINFOEXISTS(vp
))
1084 (void) cluster_push(vp
);
1087 * When MNT_WAIT is requested and the zero fill timeout
1088 * has expired then we must explicitly zero out any areas
1089 * that are currently marked invalid (holes).
1091 if ((wait
|| (cp
->c_flag
& C_ZFWANTSYNC
)) &&
1092 UBCINFOEXISTS(vp
) && (fp
= VTOF(vp
)) &&
1093 cp
->c_zftimeout
!= 0) {
1097 if (time
.tv_sec
< cp
->c_zftimeout
) {
1098 /* Remember that a force sync was requested. */
1099 cp
->c_flag
|= C_ZFWANTSYNC
;
1102 VOP_DEVBLOCKSIZE(cp
->c_devvp
, &devblksize
);
1103 was_nocache
= ISSET(vp
->v_flag
, VNOCACHE_DATA
);
1104 SET(vp
->v_flag
, VNOCACHE_DATA
); /* Don't cache zeros */
1106 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
1107 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
1108 off_t start
= invalid_range
->rl_start
;
1109 off_t end
= invalid_range
->rl_end
;
1111 /* The range about to be written must be validated
1112 * first, so that VOP_CMAP() will return the
1113 * appropriate mapping for the cluster code:
1115 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1117 (void) cluster_write(vp
, (struct uio
*) 0,
1119 invalid_range
->rl_end
+ 1,
1120 invalid_range
->rl_start
,
1121 (off_t
)0, devblksize
,
1122 IO_HEADZEROFILL
| IO_NOZERODIRTY
);
1123 cp
->c_flag
|= C_MODIFIED
;
1125 (void) cluster_push(vp
);
1127 CLR(vp
->v_flag
, VNOCACHE_DATA
);
1128 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1129 cp
->c_zftimeout
= 0;
1133 * Flush all dirty buffers associated with a vnode.
1137 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1138 nbp
= bp
->b_vnbufs
.le_next
;
1139 if ((bp
->b_flags
& B_BUSY
))
1141 if ((bp
->b_flags
& B_DELWRI
) == 0)
1142 panic("hfs_fsync: not dirty");
1144 bp
->b_flags
|= B_BUSY
;
1145 /* Clear B_LOCKED, should only be set on meta files */
1146 bp
->b_flags
&= ~B_LOCKED
;
1149 * Wait for I/O associated with indirect blocks to complete,
1150 * since there is no way to quickly wait for them below.
1152 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
)
1155 (void) VOP_BWRITE(bp
);
1160 while (vp
->v_numoutput
) {
1161 vp
->v_flag
|= VBWAIT
;
1162 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
1165 if (vp
->v_dirtyblkhd
.lh_first
) {
1166 /* still have some dirty buffers */
1168 vprint("hfs_fsync: dirty", vp
);
1171 * Looks like the requests are not
1172 * getting queued to the driver.
1173 * Retrying here causes a cpu bound loop.
1174 * Yield to the other threads and hope
1177 (void)tsleep((caddr_t
)&vp
->v_numoutput
,
1178 PRIBIO
+ 1, "hfs_fsync", hz
/10);
1191 if (vp
->v_flag
& VSYSTEM
) {
1192 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
)
1193 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1194 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
1195 } else /* User file */ {
1196 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, wait
);
1198 /* When MNT_WAIT is requested push out any delayed meta data */
1199 if ((retval
== 0) && wait
&& cp
->c_hint
&&
1200 !ISSET(cp
->c_flag
, C_DELETED
| C_NOEXISTS
)) {
1201 hfs_metasync(VTOHFS(vp
), cp
->c_hint
, ap
->a_p
);
1208 /* Sync an hfs catalog b-tree node */
1210 hfs_metasync(struct hfsmount
*hfsmp
, daddr_t node
, struct proc
*p
)
1217 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1219 if (hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
) != 0)
1223 * Look for a matching node that has been delayed
1224 * but is not part of a set (B_LOCKED).
1227 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1228 nbp
= bp
->b_vnbufs
.le_next
;
1229 if (bp
->b_flags
& B_BUSY
)
1231 if (bp
->b_lblkno
== node
) {
1232 if (bp
->b_flags
& B_LOCKED
)
1236 bp
->b_flags
|= B_BUSY
;
1238 (void) VOP_BWRITE(bp
);
1244 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1251 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1253 struct cnode
*cp
= VTOC(vp
);
1254 register struct buf
*bp
;
1260 * Flush all dirty buffers associated with b-tree.
1265 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
1266 nbp
= bp
->b_vnbufs
.le_next
;
1267 if ((bp
->b_flags
& B_BUSY
))
1269 if ((bp
->b_flags
& B_DELWRI
) == 0)
1270 panic("hfs_fsync: not dirty");
1271 if (sync_transaction
&& !(bp
->b_flags
& B_LOCKED
))
1275 bp
->b_flags
|= B_BUSY
;
1276 bp
->b_flags
&= ~B_LOCKED
;
1286 if ((vp
->v_flag
& VSYSTEM
) && (VTOF(vp
)->fcbBTCBPtr
!= NULL
))
1287 (void) BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1288 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
1294 * Rmdir system call.
1299 IN WILLRELE struct vnode *dvp;
1300 IN WILLRELE struct vnode *vp;
1301 IN struct componentname *cnp;
1306 struct vop_rmdir_args
/* {
1307 struct vnode *a_dvp;
1309 struct componentname *a_cnp;
1312 struct vnode
*vp
= ap
->a_vp
;
1313 struct vnode
*dvp
= ap
->a_dvp
;
1314 struct proc
*p
= ap
->a_cnp
->cn_proc
;
1317 struct hfsmount
* hfsmp
;
1328 return (EINVAL
); /* cannot remove "." */
1331 * Verify the directory is empty (and valid).
1332 * (Rmdir ".." won't be valid since
1333 * ".." will contain a reference to
1334 * the current directory and thus be
1337 if (cp
->c_entries
!= 0) {
1341 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1346 /* Remove the entry from the namei cache: */
1349 /* Lock catalog b-tree */
1350 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1351 if (error
) goto out
;
1353 if (cp
->c_entries
> 0)
1354 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1355 /* Remove entry from catalog */
1356 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
1358 /* Unlock catalog b-tree */
1359 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1360 if (error
) goto out
;
1363 if (!hfs_getinoquota(cp
))
1364 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1367 /* The parent lost a child */
1368 if (dcp
->c_entries
> 0)
1370 if (dcp
->c_nlink
> 0)
1372 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1374 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
1375 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1377 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1378 cp
->c_flag
|= C_NOEXISTS
;
1392 IN WILLRELE struct vnode *dvp;
1393 IN WILLRELE struct vnode *vp;
1394 IN struct componentname *cnp;
1400 struct vop_remove_args
/* {
1401 struct vnode *a_dvp;
1403 struct componentname *a_cnp;
1406 struct vnode
*vp
= ap
->a_vp
;
1407 struct vnode
*dvp
= ap
->a_dvp
;
1408 struct vnode
*rvp
= NULL
;
1411 struct hfsmount
*hfsmp
;
1412 struct proc
*p
= current_proc();
1413 int dataforkbusy
= 0;
1414 int rsrcforkbusy
= 0;
1419 /* Redirect directories to rmdir */
1420 if (vp
->v_type
== VDIR
)
1421 return (hfs_rmdir(ap
));
1427 if (cp
->c_parentcnid
!= dcp
->c_cnid
) {
1432 /* Make sure a remove is permitted */
1433 if ((cp
->c_flags
& (IMMUTABLE
| APPEND
)) ||
1434 (VTOC(dvp
)->c_flags
& APPEND
) ||
1435 VNODE_IS_RSRC(vp
)) {
1441 * Aquire a vnode for a non-empty resource fork.
1442 * (needed for VOP_TRUNCATE)
1444 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1445 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, p
);
1451 * Check if this file is being used.
1453 * The namei done for the remove took a reference on the
1454 * vnode (vp). And we took a ref on the resource vnode (rvp).
1455 * Hence set 1 in the tookref parameter of ubc_isinuse().
1457 if (UBCISVALID(vp
) && ubc_isinuse(vp
, 1))
1459 if (rvp
&& UBCISVALID(rvp
) && ubc_isinuse(rvp
, 1))
1463 * Carbon semantics prohibit deleting busy files.
1464 * (enforced when NODELETEBUSY is requested)
1466 if ((dataforkbusy
|| rsrcforkbusy
) &&
1467 ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
) ||
1468 (hfsmp
->hfs_private_metadata_dir
== 0))) {
1473 /* Remove our entry from the namei cache. */
1477 * Truncate any non-busy forks. Busy forks will
1478 * get trucated when their vnode goes inactive.
1480 * (Note: hard links are truncated in VOP_INACTIVE)
1482 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1483 int mode
= cp
->c_mode
;
1485 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
!= 0) {
1486 cp
->c_mode
= 0; /* Suppress VOP_UPDATES */
1487 error
= VOP_TRUNCATE(vp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
1493 if (!rsrcforkbusy
&& rvp
) {
1494 cp
->c_mode
= 0; /* Suppress VOP_UPDATES */
1495 error
= VOP_TRUNCATE(rvp
, (off_t
)0, IO_NDELAY
, NOCRED
, p
);
1497 if (error
&& !dataforkbusy
)
1501 * XXX could also force an update on vp
1502 * and fail the remove.
1510 * There are 3 remove cases to consider:
1511 * 1. File is a hardlink ==> remove the link
1512 * 2. File is busy (in use) ==> move/rename the file
1513 * 3. File is not in use ==> remove the file
1516 if (cp
->c_flag
& C_HARDLINK
) {
1517 struct cat_desc desc
;
1519 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
1520 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
1521 error
= ENOENT
; /* name missing! */
1525 /* Setup a descriptor for the link */
1526 bzero(&desc
, sizeof(desc
));
1527 desc
.cd_nameptr
= ap
->a_cnp
->cn_nameptr
;
1528 desc
.cd_namelen
= ap
->a_cnp
->cn_namelen
;
1529 desc
.cd_parentcnid
= dcp
->c_cnid
;
1530 /* XXX - if cnid is out of sync then the wrong thread rec will get deleted. */
1531 desc
.cd_cnid
= cp
->c_cnid
;
1533 /* Lock catalog b-tree */
1534 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1538 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1540 /* Unlock the Catalog */
1541 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1543 /* All done with component name... */
1544 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
1545 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
1550 cp
->c_flag
|= C_CHANGE
;
1551 if (--cp
->c_nlink
< 1)
1552 cp
->c_flag
|= C_DELETED
;
1553 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1555 } else if (dataforkbusy
|| rsrcforkbusy
) {
1557 struct cat_desc to_desc
;
1558 struct cat_desc todir_desc
;
1561 * Orphan this file (move to hidden directory).
1563 bzero(&todir_desc
, sizeof(todir_desc
));
1564 todir_desc
.cd_parentcnid
= 2;
1566 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1567 bzero(&to_desc
, sizeof(to_desc
));
1568 to_desc
.cd_nameptr
= delname
;
1569 to_desc
.cd_namelen
= strlen(delname
);
1570 to_desc
.cd_parentcnid
= hfsmp
->hfs_private_metadata_dir
;
1571 to_desc
.cd_flags
= 0;
1572 to_desc
.cd_cnid
= cp
->c_cnid
;
1574 /* Lock catalog b-tree */
1575 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1576 if (error
) goto out
;
1578 error
= cat_rename(hfsmp
, &cp
->c_desc
, &todir_desc
,
1579 &to_desc
, (struct cat_desc
*)NULL
);
1581 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1582 (void)cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1583 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1585 /* Unlock the Catalog */
1586 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1587 if (error
) goto out
;
1589 cp
->c_flag
|= C_CHANGE
| C_DELETED
| C_NOEXISTS
;
1592 } else /* Not busy */ {
1594 /* Lock catalog b-tree */
1595 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1596 if (error
) goto out
;
1598 if (vp
->v_type
== VDIR
&& cp
->c_entries
> 0)
1599 panic("hfs_remove: attempting to delete a non-empty directory!");
1600 if (vp
->v_type
!= VDIR
&& cp
->c_blocks
> 0)
1601 panic("hfs_remove: attempting to delete a non-empty file!");
1603 error
= cat_delete(hfsmp
, &cp
->c_desc
, &cp
->c_attr
);
1605 if (error
&& truncated
)
1606 panic("hfs_remove: couldn't delete a truncated file!");
1608 /* Unlock the Catalog */
1609 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1610 if (error
) goto out
;
1613 if (!hfs_getinoquota(cp
))
1614 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1618 cp
->c_flag
|= C_CHANGE
| C_NOEXISTS
;
1620 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1624 * All done with this cnode's descriptor...
1626 * Note: all future catalog calls for this cnode must be
1627 * by fileid only. This is OK for HFS (which doesn't have
1628 * file thread records) since HFS doesn't support hard
1629 * links or the removal of busy files.
1631 cat_releasedesc(&cp
->c_desc
);
1633 /* In all three cases the parent lost a child */
1634 if (dcp
->c_entries
> 0)
1636 if (dcp
->c_nlink
> 0)
1638 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1640 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
1644 VOP_UNLOCK(vp
, 0, p
);
1645 (void) ubc_uncache(vp
);
1653 /* Commit the truncation to the catalog record */
1655 cp
->c_flag
|= C_CHANGE
| C_UPDATE
;
1657 (void) VOP_UPDATE(vp
, &tv
, &tv
, 0);
1665 __private_extern__
void
1666 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1668 /* First release allocated name buffer */
1669 if (cp
->c_desc
.cd_flags
& CD_HASBUF
&& cp
->c_desc
.cd_nameptr
!= 0) {
1670 char *name
= cp
->c_desc
.cd_nameptr
;
1672 cp
->c_desc
.cd_nameptr
= 0;
1673 cp
->c_desc
.cd_namelen
= 0;
1674 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
1677 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1679 /* Cnode now owns the name buffer */
1680 cdp
->cd_nameptr
= 0;
1681 cdp
->cd_namelen
= 0;
1682 cdp
->cd_flags
&= ~CD_HASBUF
;
1688 #% rename fdvp U U U
1690 #% rename tdvp L U U
1694 IN WILLRELE struct vnode *fdvp;
1695 IN WILLRELE struct vnode *fvp;
1696 IN struct componentname *fcnp;
1697 IN WILLRELE struct vnode *tdvp;
1698 IN WILLRELE struct vnode *tvp;
1699 IN struct componentname *tcnp;
1705 * The VFS layer guarantees that source and destination will
1706 * either both be directories, or both not be directories.
1708 * When the target is a directory, hfs_rename must ensure
1714 struct vop_rename_args
/* {
1715 struct vnode *a_fdvp;
1716 struct vnode *a_fvp;
1717 struct componentname *a_fcnp;
1718 struct vnode *a_tdvp;
1719 struct vnode *a_tvp;
1720 struct componentname *a_tcnp;
1723 struct vnode
*tvp
= ap
->a_tvp
;
1724 struct vnode
*tdvp
= ap
->a_tdvp
;
1725 struct vnode
*fvp
= ap
->a_fvp
;
1726 struct vnode
*fdvp
= ap
->a_fdvp
;
1727 struct componentname
*tcnp
= ap
->a_tcnp
;
1728 struct componentname
*fcnp
= ap
->a_fcnp
;
1729 struct cnode
*fcp
= NULL
;
1730 struct cnode
*fdcp
= NULL
;
1731 struct cnode
*tdcp
= NULL
;
1732 struct cnode
*tcp
= NULL
;
1733 struct cat_desc from_desc
;
1734 struct cat_desc to_desc
;
1735 struct cat_desc out_desc
;
1736 struct hfsmount
*hfsmp
;
1737 struct proc
*p
= fcnp
->cn_proc
;
1740 cnid_t oldparent
= 0;
1741 cnid_t newparent
= 0;
1744 if ((tcnp
->cn_flags
& HASBUF
) == 0 ||
1745 (fcnp
->cn_flags
& HASBUF
) == 0)
1746 panic("hfs_rename: no name");
1749 * When fvp matches tvp they must be case variants
1750 * or hard links, and if they are in the same directory then
1751 * tvp really doesn't exist (see VFS rename).
1752 * XXX Hard link rename is still broken/ignored. If they are
1753 * in different directories then we must have hard links.
1754 * Comments further down describe behaviour of hard links in same dir.
1755 * Note case insensitivity was and still is presumed.
1766 * Check for cross-device rename.
1768 if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
1769 (tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
1775 * Make sure a remove of "to" vnode is permitted.
1777 if (tvp
&& ((VTOC(tvp
)->c_flags
& (IMMUTABLE
| APPEND
)) ||
1778 (VTOC(tdvp
)->c_flags
& APPEND
))) {
1783 if ((retval
= vn_lock(fvp
, LK_EXCLUSIVE
| LK_RETRY
, p
)))
1787 * Make sure "from" vnode and its parent are changeable.
1791 oldparent
= fdcp
->c_cnid
;
1792 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
1793 VOP_UNLOCK(fvp
, 0, p
);
1798 if (fcp
->c_parentcnid
!= fdcp
->c_cnid
) {
1799 VOP_UNLOCK(fvp
, 0, p
);
1805 * Check if names already match...
1806 * XXX The name being checked is from fcp rather than fcnp! If
1807 * there are hard links, fcp yields the name which was
1808 * most recently looked up (yes that design is vulnerable to races)
1809 * and the name most recently looked up was the target, so they
1810 * compare equal and we ignore the rename. XXX
1812 if (fvp
== ap
->a_tvp
&&
1813 (bcmp(fcp
->c_desc
.cd_nameptr
, tcnp
->cn_nameptr
,
1814 fcp
->c_desc
.cd_namelen
) == 0)) {
1815 VOP_UNLOCK(fvp
, 0, p
);
1820 /* XXX This doesn't make sense for HFS...
1822 * Be sure we are not renaming ".", "..", or an alias of ".". This
1823 * leads to a crippled directory tree. It's pretty tough to do a
1824 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
1825 * doesn't work if the ".." entry is missing.
1827 if (fvp
->v_type
== VDIR
) {
1828 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
1830 || (fcnp
->cn_flags
&ISDOTDOT
)
1831 || (fcp
->c_flag
& C_RENAME
)) {
1832 VOP_UNLOCK(fvp
, 0, p
);
1836 fcp
->c_flag
|= C_RENAME
;
1839 /* XXX UFS does vrele(fdvp) here */
1841 /* From now on use bad instead of abort to exit */
1847 newparent
= tdcp
->c_cnid
;
1849 retval
= VOP_ACCESS(fvp
, VWRITE
, tcnp
->cn_cred
, tcnp
->cn_proc
);
1850 if ((fvp
->v_type
== VDIR
) && (newparent
!= oldparent
)) {
1851 if (retval
) /* write access check above */
1854 retval
= 0; /* Reset value from above, we dont care about it anymore */
1857 * If the destination exists, then be sure its type (file or dir)
1858 * matches that of the source. And, if it is a directory make sure
1859 * it is empty. Then delete the destination.
1863 * If the parent directory is "sticky", then the user must
1864 * own the parent directory, or the destination of the rename,
1865 * otherwise the destination may not be changed (except by
1866 * root). This implements append-only directories.
1868 if ((tdcp
->c_mode
& S_ISTXT
) && (tcnp
->cn_cred
->cr_uid
!= 0) &&
1869 tcnp
->cn_cred
->cr_uid
!= tdcp
->c_uid
&&
1870 tcnp
->cn_cred
->cr_uid
!= tcp
->c_uid
) {
1876 * Target must be empty if a directory.
1878 if (S_ISDIR(tcp
->c_mode
) && (tcp
->c_nlink
> 2)) {
1884 * VOP_REMOVE will vput tdvp so we better bump
1885 * its ref count and relockit, always set tvp to
1886 * NULL afterwards to indicate that were done with it.
1892 tcnp
->cn_flags
&= ~SAVENAME
;
1894 if (tvp
->v_type
== VDIR
)
1895 retval
= VOP_RMDIR(tdvp
, tvp
, tcnp
);
1897 retval
= VOP_REMOVE(tdvp
, tvp
, tcnp
);
1899 (void) vn_lock(tdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1908 * Prevent lock heirarchy violation (deadlock):
1910 * If fdvp is the parent of tdvp then we must drop
1911 * tdvp lock before aquiring the lock for fdvp.
1913 if (newparent
!= oldparent
)
1914 vn_lock(fdvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1916 /* remove the existing entry from the namei cache: */
1919 hfsmp
= VTOHFS(fvp
);
1920 bzero(&from_desc
, sizeof(from_desc
));
1921 from_desc
.cd_nameptr
= fcnp
->cn_nameptr
;
1922 from_desc
.cd_namelen
= fcnp
->cn_namelen
;
1923 from_desc
.cd_parentcnid
= fdcp
->c_cnid
;
1924 from_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
1925 from_desc
.cd_cnid
= fcp
->c_cnid
;
1926 bzero(&to_desc
, sizeof(to_desc
));
1927 to_desc
.cd_nameptr
= tcnp
->cn_nameptr
;
1928 to_desc
.cd_namelen
= tcnp
->cn_namelen
;
1929 to_desc
.cd_parentcnid
= tdcp
->c_cnid
;
1930 to_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
1931 to_desc
.cd_cnid
= fcp
->c_cnid
;
1933 /* Lock catalog b-tree */
1934 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1936 if (newparent
!= oldparent
) /* unlock the lock we just got */
1937 VOP_UNLOCK(fdvp
, 0, p
);
1940 retval
= cat_rename(hfsmp
, &from_desc
, &tdcp
->c_desc
,
1941 &to_desc
, &out_desc
);
1943 /* Unlock catalog b-tree */
1944 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1946 if (newparent
!= oldparent
)
1947 VOP_UNLOCK(fdvp
, 0, p
);
1949 if (retval
) goto bad
;
1951 /* update cnode's catalog descriptor */
1952 replace_desc(fcp
, &out_desc
);
1954 fcp
->c_flag
&= ~C_RENAME
;
1957 * Time stamp both parent directories.
1958 * Note that if this is a rename within the same directory,
1959 * (where tdcp == fdcp)
1960 * the code below is still safe and correct.
1962 if (fdcp
->c_nlink
> 0)
1964 if (fdcp
->c_entries
> 0)
1968 fdcp
->c_flag
|= C_UPDATE
;
1969 tdcp
->c_flag
|= C_UPDATE
;
1971 CTIMES(fdcp
, &tv
, &tv
);
1972 CTIMES(tdcp
, &tv
, &tv
);
1973 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
1975 hfs_volupdate(hfsmp
, fvp
->v_type
== VDIR
? VOL_RMDIR
: VOL_RMFILE
,
1976 (fdcp
->c_cnid
== kHFSRootFolderID
));
1977 hfs_volupdate(hfsmp
, fvp
->v_type
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
1978 (tdcp
->c_cnid
== kHFSRootFolderID
));
1987 fcp
->c_flag
&= ~C_RENAME
;
1996 if (VOP_ISLOCKED(fvp
))
2004 VOP_ABORTOP(tdvp
, tcnp
);
2011 VOP_ABORTOP(fdvp
, fcnp
);
2025 IN WILLRELE struct vnode *dvp;
2026 OUT struct vnode **vpp;
2027 IN struct componentname *cnp;
2028 IN struct vattr *vap;
2030 We are responsible for freeing the namei buffer,
2031 it is done in hfs_makenode()
2036 struct vop_mkdir_args
/* {
2037 struct vnode *a_dvp;
2038 struct vnode **a_vpp;
2039 struct componentname *a_cnp;
2040 struct vattr *a_vap;
2043 struct vattr
*vap
= ap
->a_vap
;
2045 return (hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
2046 ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
));
2051 * symlink -- make a symbolic link
2052 #% symlink dvp L U U
2053 #% symlink vpp - U -
2055 # XXX - note that the return vnode has already been VRELE'ed
2056 # by the filesystem layer. To use it you must use vget,
2057 # possibly with a further namei.
2060 IN WILLRELE struct vnode *dvp;
2061 OUT WILLRELE struct vnode **vpp;
2062 IN struct componentname *cnp;
2063 IN struct vattr *vap;
2066 We are responsible for freeing the namei buffer,
2067 it is done in hfs_makenode().
2073 struct vop_symlink_args
/* {
2074 struct vnode *a_dvp;
2075 struct vnode **a_vpp;
2076 struct componentname *a_cnp;
2077 struct vattr *a_vap;
2081 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
2082 struct filefork
*fp
;
2084 struct buf
*bp
= NULL
;
2086 /* HFS standard disks don't support symbolic links */
2087 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
2088 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2090 return (EOPNOTSUPP
);
2093 /* Check for empty target name */
2094 if (ap
->a_target
[0] == 0) {
2095 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2100 /* Create the vnode */
2101 if ((error
= hfs_makenode(S_IFLNK
| ap
->a_vap
->va_mode
,
2102 ap
->a_dvp
, vpp
, ap
->a_cnp
)))
2106 len
= strlen(ap
->a_target
);
2108 fp
->ff_clumpsize
= VTOVCB(vp
)->blockSize
;
2110 /* Allocate space for the link */
2111 error
= VOP_TRUNCATE(vp
, len
, IO_NOZEROFILL
,
2112 ap
->a_cnp
->cn_cred
, ap
->a_cnp
->cn_proc
);
2114 goto out
; /* XXX need to remove link */
2116 /* Write the link to disk */
2117 bp
= getblk(vp
, 0, roundup((int)fp
->ff_size
, VTOHFS(vp
)->hfs_phys_block_size
),
2119 bzero(bp
->b_data
, bp
->b_bufsize
);
2120 bcopy(ap
->a_target
, bp
->b_data
, len
);
2129 * Dummy dirents to simulate the "." and ".." entries of the directory
2130 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2131 * the size of these entries is the smallest needed to represent them
2132 * (only 12 byte each).
2134 static hfsdotentry rootdots
[2] = {
2137 sizeof(struct hfsdotentry
), /* d_reclen */
2138 DT_DIR
, /* d_type */
2144 sizeof(struct hfsdotentry
), /* d_reclen */
2145 DT_DIR
, /* d_type */
2152 * There is some confusion as to what the semantics of uio_offset are.
2153 * In ufs, it represents the actual byte offset within the directory
2154 * "file." HFS, however, just uses it as an entry counter - essentially
2155 * assuming that it has no meaning except to the hfs_readdir function.
2156 * This approach would be more efficient here, but some callers may
2157 * assume the uio_offset acts like a byte offset. NFS in fact
2158 * monkeys around with the offset field a lot between readdir calls.
2160 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
2161 * fields is a mess as well. The libc function readdir() returns
2162 * NULL (indicating the end of a directory) when either
2163 * the getdirentries() syscall (which calls this and returns
2164 * the size of the buffer passed in less the value of uiop->uio_resid)
2165 * returns 0, or a direct record with a d_reclen of zero.
2166 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
2167 * of the directory by testing uiop->uio_resid == 0. The solution
2168 * is to pad the size of the last struct direct in a given
2169 * block to fill the block if we are not at the end of the directory.
2174 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
2175 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
2176 * "Nbytes must be greater than or equal to the block size associated with the file,
2177 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
2178 * room for the . and .. entries that have to added manually.
2185 IN struct vnode *vp;
2186 INOUT struct uio *uio;
2187 IN struct ucred *cred;
2190 INOUT u_long **cookies;
2194 struct vop_readdir_args
/* {
2203 register struct uio
*uio
= ap
->a_uio
;
2204 struct cnode
*cp
= VTOC(ap
->a_vp
);
2205 struct hfsmount
*hfsmp
= VTOHFS(ap
->a_vp
);
2206 struct proc
*p
= current_proc();
2207 off_t off
= uio
->uio_offset
;
2211 /* We assume it's all one big buffer... */
2212 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
)
2215 /* Create the entries for . and .. */
2216 if (uio
->uio_offset
< sizeof(rootdots
)) {
2220 rootdots
[0].d_fileno
= cp
->c_cnid
;
2221 rootdots
[1].d_fileno
= cp
->c_parentcnid
;
2223 if (uio
->uio_offset
== 0) {
2224 dep
= (caddr_t
) &rootdots
[0];
2225 dotsize
= 2* sizeof(struct hfsdotentry
);
2226 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
2227 dep
= (caddr_t
) &rootdots
[1];
2228 dotsize
= sizeof(struct hfsdotentry
);
2234 retval
= uiomove(dep
, dotsize
, uio
);
2239 /* If there are no children then we're done */
2240 if (cp
->c_entries
== 0) {
2246 /* Lock catalog b-tree */
2247 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
2248 if (retval
) goto Exit
;
2250 retval
= cat_getdirentries(hfsmp
, &cp
->c_desc
, uio
, &eofflag
);
2252 /* Unlock catalog b-tree */
2253 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2255 if (retval
!= E_NONE
) {
2259 /* were we already past eof ? */
2260 if (uio
->uio_offset
== off
) {
2265 cp
->c_flag
|= C_ACCESS
;
2266 /* Bake any cookies */
2267 if (!retval
&& ap
->a_ncookies
!= NULL
) {
2268 struct dirent
* dpStart
;
2269 struct dirent
* dpEnd
;
2276 * Only the NFS server uses cookies, and it loads the
2277 * directory block into system space, so we can just look at
2280 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
2281 panic("hfs_readdir: unexpected uio from NFS server");
2282 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
2283 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
2284 for (dp
= dpStart
, ncookies
= 0;
2285 dp
< dpEnd
&& dp
->d_reclen
!= 0;
2286 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
2288 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
2289 for (dp
= dpStart
, cookiep
= cookies
;
2291 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
2292 off
+= dp
->d_reclen
;
2293 *cookiep
++ = (u_long
) off
;
2295 *ap
->a_ncookies
= ncookies
;
2296 *ap
->a_cookies
= cookies
;
2301 *ap
->a_eofflag
= eofflag
;
2308 * Return target name of a symbolic link
2309 #% readlink vp L L L
2312 IN struct vnode *vp;
2313 INOUT struct uio *uio;
2314 IN struct ucred *cred;
2319 struct vop_readlink_args
/* {
2322 struct ucred *a_cred;
2326 struct vnode
*vp
= ap
->a_vp
;
2328 struct filefork
*fp
;
2330 if (vp
->v_type
!= VLNK
)
2336 /* Zero length sym links are not allowed */
2337 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2338 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2342 /* Cache the path so we don't waste buffer cache resources */
2343 if (fp
->ff_symlinkptr
== NULL
) {
2344 struct buf
*bp
= NULL
;
2346 MALLOC(fp
->ff_symlinkptr
, char *, fp
->ff_size
, M_TEMP
, M_WAITOK
);
2347 retval
= meta_bread(vp
, 0,
2348 roundup((int)fp
->ff_size
,
2349 VTOHFS(vp
)->hfs_phys_block_size
),
2354 if (fp
->ff_symlinkptr
) {
2355 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2356 fp
->ff_symlinkptr
= NULL
;
2360 bcopy(bp
->b_data
, fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2362 bp
->b_flags
|= B_INVAL
; /* data no longer needed */
2366 retval
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2373 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
2374 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
2375 #% abortop dvp = = =
2378 IN struct vnode *dvp;
2379 IN struct componentname *cnp;
2387 struct vop_abortop_args
/* {
2388 struct vnode *a_dvp;
2389 struct componentname *a_cnp;
2392 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
)
2393 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2400 * Lock an cnode. If its already locked, set the WANT bit and sleep.
2404 IN struct vnode *vp;
2411 struct vop_lock_args
/* {
2417 struct vnode
*vp
= ap
->a_vp
;
2418 struct cnode
*cp
= VTOC(vp
);
2421 panic("hfs_lock: cnode in vnode is null\n");
2423 return (lockmgr(&cp
->c_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
));
2431 IN struct vnode *vp;
2438 struct vop_unlock_args
/* {
2444 struct vnode
*vp
= ap
->a_vp
;
2445 struct cnode
*cp
= VTOC(vp
);
2448 panic("hfs_unlock: cnode in vnode is null\n");
2450 return (lockmgr(&cp
->c_lock
, ap
->a_flags
| LK_RELEASE
,
2451 &vp
->v_interlock
, ap
->a_p
));
2456 * Print out the contents of a cnode.
2460 IN struct vnode *vp;
2464 struct vop_print_args
/* {
2468 struct vnode
* vp
= ap
->a_vp
;
2469 struct cnode
*cp
= VTOC(vp
);
2471 printf("tag VT_HFS, cnid %d, on dev %d, %d", cp
->c_cnid
,
2472 major(cp
->c_dev
), minor(cp
->c_dev
));
2474 if (vp
->v_type
== VFIFO
)
2477 lockmgr_printinfo(&cp
->c_lock
);
2484 * Check for a locked cnode.
2485 #% islocked vp = = =
2488 IN struct vnode *vp;
2493 struct vop_islocked_args
/* {
2497 return (lockstatus(&VTOC(ap
->a_vp
)->c_lock
));
2502 #% pathconf vp L L L
2505 IN struct vnode *vp;
2507 OUT register_t *retval;
2512 struct vop_pathconf_args
/* {
2520 switch (ap
->a_name
) {
2522 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
2523 *ap
->a_retval
= HFS_LINK_MAX
;
2528 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
2531 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2533 case _PC_CHOWN_RESTRICTED
:
2539 case _PC_NAME_CHARS_MAX
:
2540 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2542 case _PC_CASE_SENSITIVE
:
2545 case _PC_CASE_PRESERVING
:
2557 * Advisory record locking support
2561 IN struct vnode *vp;
2564 IN struct flock *fl;
2570 struct vop_advlock_args
/* {
2578 struct vnode
*vp
= ap
->a_vp
;
2579 struct flock
*fl
= ap
->a_fl
;
2580 struct hfslockf
*lock
;
2581 struct filefork
*fork
;
2585 /* Only regular files can have locks */
2586 if (vp
->v_type
!= VREG
)
2589 fork
= VTOF(ap
->a_vp
);
2591 * Avoid the common case of unlocking when cnode has no locks.
2593 if (fork
->ff_lockf
== (struct hfslockf
*)0) {
2594 if (ap
->a_op
!= F_SETLK
) {
2595 fl
->l_type
= F_UNLCK
;
2600 * Convert the flock structure into a start and end.
2603 switch (fl
->l_whence
) {
2607 * Caller is responsible for adding any necessary offset
2608 * when SEEK_CUR is used.
2610 start
= fl
->l_start
;
2613 start
= fork
->ff_size
+ fl
->l_start
;
2624 end
= start
+ fl
->l_len
- 1;
2627 * Create the hfslockf structure
2629 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
2630 lock
->lf_start
= start
;
2632 lock
->lf_id
= ap
->a_id
;
2633 lock
->lf_fork
= fork
;
2634 lock
->lf_type
= fl
->l_type
;
2635 lock
->lf_next
= (struct hfslockf
*)0;
2636 TAILQ_INIT(&lock
->lf_blkhd
);
2637 lock
->lf_flags
= ap
->a_flags
;
2639 * Do the requested operation.
2643 retval
= hfs_setlock(lock
);
2646 retval
= hfs_clearlock(lock
);
2647 FREE(lock
, M_LOCKF
);
2650 retval
= hfs_getlock(lock
, fl
);
2651 FREE(lock
, M_LOCKF
);
2655 _FREE(lock
, M_LOCKF
);
2665 * Update the access, modified, and node change times as specified
2666 * by the C_ACCESS, C_UPDATE, and C_CHANGE flags respectively. The
2667 * C_MODIFIED flag is used to specify that the node needs to be
2668 * updated but that the times have already been set. The access and
2669 * modified times are input parameters but the node change time is
2670 * always taken from the current time. If waitfor is set, then wait
2671 * for the disk write of the node to complete.
2675 IN struct vnode *vp;
2676 IN struct timeval *access;
2677 IN struct timeval *modify;
2682 struct vop_update_args
/* {
2684 struct timeval *a_access;
2685 struct timeval *a_modify;
2689 struct vnode
*vp
= ap
->a_vp
;
2690 struct cnode
*cp
= VTOC(ap
->a_vp
);
2692 struct cat_fork
*dataforkp
= NULL
;
2693 struct cat_fork
*rsrcforkp
= NULL
;
2694 struct cat_fork datafork
;
2698 /* XXX do we really want to clear the sytem cnode flags here???? */
2699 if ((vp
->v_flag
& VSYSTEM
) ||
2700 (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) ||
2701 (cp
->c_mode
== 0)) {
2702 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
2706 updateflag
= cp
->c_flag
& (C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
);
2708 /* Nothing to update. */
2709 if (updateflag
== 0)
2711 /* HFS standard doesn't have access times. */
2712 if ((updateflag
== C_ACCESS
) && (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
))
2714 if (updateflag
& C_ACCESS
) {
2716 * If only the access time is changing then defer
2717 * updating it on-disk util later (in hfs_inactive).
2718 * If it was recently updated then skip the update.
2720 if (updateflag
== C_ACCESS
) {
2721 cp
->c_flag
&= ~C_ACCESS
;
2723 /* Its going to disk or its sufficiently newer... */
2724 if ((cp
->c_flag
& C_ATIMEMOD
) ||
2725 (ap
->a_access
->tv_sec
> (cp
->c_atime
+ ATIME_ACCURACY
))) {
2726 cp
->c_atime
= ap
->a_access
->tv_sec
;
2727 cp
->c_flag
|= C_ATIMEMOD
;
2731 cp
->c_atime
= ap
->a_access
->tv_sec
;
2734 if (updateflag
& C_UPDATE
) {
2735 cp
->c_mtime
= ap
->a_modify
->tv_sec
;
2736 cp
->c_mtime_nsec
= ap
->a_modify
->tv_usec
* 1000;
2738 if (updateflag
& C_CHANGE
) {
2739 cp
->c_ctime
= time
.tv_sec
;
2741 * HFS dates that WE set must be adjusted for DST
2743 if ((VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
2744 cp
->c_ctime
+= 3600;
2745 cp
->c_mtime
= cp
->c_ctime
;
2750 dataforkp
= &cp
->c_datafork
->ff_data
;
2752 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
2757 * For delayed allocations updates are
2758 * postponed until an fsync or the file
2759 * gets written to disk.
2761 * Deleted files can defer meta data updates until inactive.
2763 if (ISSET(cp
->c_flag
, C_DELETED
) ||
2764 (dataforkp
&& cp
->c_datafork
->ff_unallocblocks
) ||
2765 (rsrcforkp
&& cp
->c_rsrcfork
->ff_unallocblocks
)) {
2766 if (updateflag
& (C_CHANGE
| C_UPDATE
))
2767 hfs_volupdate(VTOHFS(vp
), VOL_UPDATE
, 0);
2768 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_UPDATE
);
2769 cp
->c_flag
|= C_MODIFIED
;
2774 * For files with invalid ranges (holes) the on-disk
2775 * field representing the size of the file (cf_size)
2776 * must be no larger than the start of the first hole.
2778 if (dataforkp
&& !CIRCLEQ_EMPTY(&cp
->c_datafork
->ff_invalidranges
)) {
2779 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2780 datafork
.cf_size
= CIRCLEQ_FIRST(&cp
->c_datafork
->ff_invalidranges
)->rl_start
;
2781 dataforkp
= &datafork
;
2785 * Lock the Catalog b-tree file.
2786 * A shared lock is sufficient since an update doesn't change
2787 * the tree and the lock on vp protects the cnode.
2789 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
2793 /* XXX - waitfor is not enforced */
2794 error
= cat_update(VTOHFS(vp
), &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
2796 /* Unlock the Catalog b-tree file. */
2797 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2799 if (updateflag
& (C_CHANGE
| C_UPDATE
))
2800 hfs_volupdate(VTOHFS(vp
), VOL_UPDATE
, 0);
2802 /* After the updates are finished, clear the flags */
2803 cp
->c_flag
&= ~(C_ACCESS
| C_CHANGE
| C_MODIFIED
| C_UPDATE
| C_ATIMEMOD
);
2809 * Allocate a new node
2811 * Upon leaving, namei buffer must be freed.
2815 hfs_makenode(mode
, dvp
, vpp
, cnp
)
2819 struct componentname
*cnp
;
2824 struct hfsmount
*hfsmp
;
2827 struct cat_desc in_desc
, out_desc
;
2828 struct cat_attr attr
;
2830 enum vtype vnodetype
;
2834 hfsmp
= VTOHFS(dvp
);
2837 bzero(&out_desc
, sizeof(out_desc
));
2839 if ((mode
& S_IFMT
) == 0)
2841 vnodetype
= IFTOVT(mode
);
2843 /* Check if unmount in progress */
2844 if (VTOVFS(dvp
)->mnt_kern_flag
& MNTK_UNMOUNT
) {
2848 /* Check if were out of usable disk space. */
2849 if ((suser(cnp
->cn_cred
, NULL
) != 0) && (hfs_freeblks(hfsmp
, 1) <= 0)) {
2854 /* Setup the default attributes */
2855 bzero(&attr
, sizeof(attr
));
2856 attr
.ca_mode
= mode
;
2857 attr
.ca_nlink
= vnodetype
== VDIR
? 2 : 1;
2858 attr
.ca_mtime
= time
.tv_sec
;
2859 attr
.ca_mtime_nsec
= time
.tv_usec
* 1000;
2860 if ((VTOVCB(dvp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
2861 attr
.ca_mtime
+= 3600; /* Same as what hfs_update does */
2863 attr
.ca_atime
= attr
.ca_ctime
= attr
.ca_itime
= attr
.ca_mtime
;
2864 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
2865 attr
.ca_uid
= hfsmp
->hfs_uid
;
2866 attr
.ca_gid
= hfsmp
->hfs_gid
;
2868 if (vnodetype
== VLNK
)
2869 attr
.ca_uid
= dcp
->c_uid
;
2871 attr
.ca_uid
= cnp
->cn_cred
->cr_uid
;
2872 attr
.ca_gid
= dcp
->c_gid
;
2875 * Don't tag as a special file (BLK or CHR) until *after*
2876 * hfs_getnewvnode is called. This insures that any
2877 * alias checking is defered until hfs_mknod completes.
2879 if (vnodetype
== VBLK
|| vnodetype
== VCHR
)
2880 attr
.ca_mode
= (attr
.ca_mode
& ~S_IFMT
) | S_IFREG
;
2882 /* Tag symlinks with a type and creator. */
2883 if (vnodetype
== VLNK
) {
2884 struct FndrFileInfo
*fip
;
2886 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
2887 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
2888 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
2890 if ((attr
.ca_mode
& S_ISGID
) &&
2891 !groupmember(dcp
->c_gid
, cnp
->cn_cred
) &&
2892 suser(cnp
->cn_cred
, NULL
)) {
2893 attr
.ca_mode
&= ~S_ISGID
;
2895 if (cnp
->cn_flags
& ISWHITEOUT
)
2896 attr
.ca_flags
|= UF_OPAQUE
;
2898 /* Setup the descriptor */
2899 bzero(&in_desc
, sizeof(in_desc
));
2900 in_desc
.cd_nameptr
= cnp
->cn_nameptr
;
2901 in_desc
.cd_namelen
= cnp
->cn_namelen
;
2902 in_desc
.cd_parentcnid
= dcp
->c_cnid
;
2903 in_desc
.cd_flags
= S_ISDIR(mode
) ? CD_ISDIR
: 0;
2905 /* Lock catalog b-tree */
2906 error
= hfs_metafilelocking(VTOHFS(dvp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2910 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
2912 /* Unlock catalog b-tree */
2913 (void) hfs_metafilelocking(VTOHFS(dvp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2917 /* Update the parent directory */
2918 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2921 dcp
->c_flag
|= C_CHANGE
| C_UPDATE
;
2923 (void) VOP_UPDATE(dvp
, &tv
, &tv
, 0);
2924 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
2925 (dcp
->c_cnid
== kHFSRootFolderID
));
2927 /* Create a vnode for the object just created: */
2928 error
= hfs_getnewvnode(hfsmp
, NULL
, &out_desc
, 0, &attr
, NULL
, &tvp
);
2935 * We call hfs_chkiq with FORCE flag so that if we
2936 * fall through to the rmdir we actually have
2937 * accounted for the inode
2939 if ((error
= hfs_getinoquota(cp
)) ||
2940 (error
= hfs_chkiq(cp
, 1, cnp
->cn_cred
, FORCE
))) {
2941 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
2942 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
2944 if (tvp
->v_type
== VDIR
)
2945 VOP_RMDIR(dvp
,tvp
, cnp
);
2947 VOP_REMOVE(dvp
,tvp
, cnp
);
2953 * restore vtype and mode for VBLK and VCHR
2955 if (vnodetype
== VBLK
|| vnodetype
== VCHR
) {
2960 tvp
->v_type
= IFTOVT(mode
);
2961 cp
->c_flag
|= C_CHANGE
;
2963 if ((error
= VOP_UPDATE(tvp
, &tv
, &tv
, 1))) {
2971 cat_releasedesc(&out_desc
);
2973 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
)
2974 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
2982 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, struct proc
*p
)
2985 struct cnode
*cp
= VTOC(vp
);
2988 if ((rvp
= cp
->c_rsrc_vp
)) {
2989 /* Use exising vnode */
2990 error
= vget(rvp
, 0, p
);
2992 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
2995 printf("hfs_vgetrsrc: couldn't get"
2996 " resource fork for %s\n", name
);
3000 struct cat_fork rsrcfork
;
3002 /* Lock catalog b-tree */
3003 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
3007 /* Get resource fork data */
3008 error
= cat_lookup(hfsmp
, &cp
->c_desc
, 1, (struct cat_desc
*)0,
3009 (struct cat_attr
*)0, &rsrcfork
);
3011 /* Unlock the Catalog */
3012 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
3016 error
= hfs_getnewvnode(hfsmp
, cp
, &cp
->c_desc
, 1, &cp
->c_attr
,
3028 * Wrapper for special device reads
3032 struct vop_read_args
/* {
3036 struct ucred *a_cred;
3042 VTOC(ap
->a_vp
)->c_flag
|= C_ACCESS
;
3043 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
3047 * Wrapper for special device writes
3051 struct vop_write_args
/* {
3055 struct ucred *a_cred;
3059 * Set update and change flags.
3061 VTOC(ap
->a_vp
)->c_flag
|= C_CHANGE
| C_UPDATE
;
3062 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
3066 * Wrapper for special device close
3068 * Update the times on the cnode then do device close.
3072 struct vop_close_args
/* {
3075 struct ucred *a_cred;
3079 struct vnode
*vp
= ap
->a_vp
;
3080 struct cnode
*cp
= VTOC(vp
);
3082 simple_lock(&vp
->v_interlock
);
3083 if (ap
->a_vp
->v_usecount
> 1)
3084 CTIMES(cp
, &time
, &time
);
3085 simple_unlock(&vp
->v_interlock
);
3086 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
3091 * Wrapper for fifo reads
3095 struct vop_read_args
/* {
3099 struct ucred *a_cred;
3102 extern int (**fifo_vnodeop_p
)(void *);
3107 VTOC(ap
->a_vp
)->c_flag
|= C_ACCESS
;
3108 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
3112 * Wrapper for fifo writes
3116 struct vop_write_args
/* {
3120 struct ucred *a_cred;
3123 extern int (**fifo_vnodeop_p
)(void *);
3126 * Set update and change flags.
3128 VTOC(ap
->a_vp
)->c_flag
|= C_CHANGE
| C_UPDATE
;
3129 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
3133 * Wrapper for fifo close
3135 * Update the times on the cnode then do device close.
3139 struct vop_close_args
/* {
3142 struct ucred *a_cred;
3146 extern int (**fifo_vnodeop_p
)(void *);
3147 struct vnode
*vp
= ap
->a_vp
;
3148 struct cnode
*cp
= VTOC(vp
);
3150 simple_lock(&vp
->v_interlock
);
3151 if (ap
->a_vp
->v_usecount
> 1)
3152 CTIMES(cp
, &time
, &time
);
3153 simple_unlock(&vp
->v_interlock
);
3154 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
3159 /*****************************************************************************
3163 *****************************************************************************/
3164 int hfs_cache_lookup(); /* in hfs_lookup.c */
3165 int hfs_lookup(); /* in hfs_lookup.c */
3166 int hfs_read(); /* in hfs_readwrite.c */
3167 int hfs_write(); /* in hfs_readwrite.c */
3168 int hfs_ioctl(); /* in hfs_readwrite.c */
3169 int hfs_select(); /* in hfs_readwrite.c */
3170 int hfs_bmap(); /* in hfs_readwrite.c */
3171 int hfs_strategy(); /* in hfs_readwrite.c */
3172 int hfs_truncate(); /* in hfs_readwrite.c */
3173 int hfs_allocate(); /* in hfs_readwrite.c */
3174 int hfs_pagein(); /* in hfs_readwrite.c */
3175 int hfs_pageout(); /* in hfs_readwrite.c */
3176 int hfs_search(); /* in hfs_search.c */
3177 int hfs_bwrite(); /* in hfs_readwrite.c */
3178 int hfs_link(); /* in hfs_link.c */
3179 int hfs_blktooff(); /* in hfs_readwrite.c */
3180 int hfs_offtoblk(); /* in hfs_readwrite.c */
3181 int hfs_cmap(); /* in hfs_readwrite.c */
3182 int hfs_getattrlist(); /* in hfs_attrlist.c */
3183 int hfs_setattrlist(); /* in hfs_attrlist.c */
3184 int hfs_readdirattr(); /* in hfs_attrlist.c */
3185 int hfs_inactive(); /* in hfs_cnode.c */
3186 int hfs_reclaim(); /* in hfs_cnode.c */
3188 int (**hfs_vnodeop_p
)(void *);
3190 #define VOPFUNC int (*)(void *)
3192 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
3193 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3194 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
3195 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
3196 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
3197 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
3198 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
3199 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3200 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3201 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3202 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
3203 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
3204 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
3205 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
3206 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
3207 { &vop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
3208 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3209 { &vop_seek_desc
, (VOPFUNC
)nop_seek
}, /* seek */
3210 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
3211 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
3212 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
3213 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
3214 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
3215 { &vop_mkcomplex_desc
, (VOPFUNC
)err_mkcomplex
}, /* mkcomplex */
3216 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
3217 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
3218 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
3219 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
3220 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
3221 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
3222 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
3223 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3224 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3225 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3226 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3227 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
3228 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
3229 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3230 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3231 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
3232 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
3233 { &vop_reallocblks_desc
, (VOPFUNC
)err_reallocblks
}, /* reallocblks */
3234 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
3235 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
3236 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3237 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
3238 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
3239 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
3240 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
3241 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3242 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3243 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3244 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
3245 { NULL
, (VOPFUNC
)NULL
}
3248 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3249 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
3251 int (**hfs_specop_p
)(void *);
3252 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
3253 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3254 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
3255 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
3256 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
3257 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
3258 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
3259 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3260 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3261 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3262 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
3263 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
3264 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
3265 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
3266 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
3267 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
3268 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
3269 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3270 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
3271 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
3272 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
3273 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
3274 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
3275 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
3276 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
3277 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
3278 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
3279 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
3280 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3281 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3282 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3283 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3284 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
3285 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
3286 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3287 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3288 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
3289 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
3290 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
3291 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
3292 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
3293 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
3294 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
3295 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3296 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
3297 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
3298 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
3299 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
3300 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3301 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3302 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3303 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3305 struct vnodeopv_desc hfs_specop_opv_desc
=
3306 { &hfs_specop_p
, hfs_specop_entries
};
3309 int (**hfs_fifoop_p
)(void *);
3310 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
3311 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
3312 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
3313 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
3314 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
3315 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
3316 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
3317 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
3318 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
3319 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
3320 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
3321 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
3322 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
3323 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
3324 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
3325 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
3326 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
3327 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
3328 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
3329 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
3330 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
3331 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
3332 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
3333 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
3334 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
3335 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
3336 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
3337 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
3338 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
3339 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
3340 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
3341 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
3342 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
3343 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
3344 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
3345 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
3346 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
3347 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
3348 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
3349 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
3350 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
3351 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
3352 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
3353 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
3354 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
3355 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
3356 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
3357 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3358 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
3359 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
3360 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
3361 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3363 struct vnodeopv_desc hfs_fifoop_opv_desc
=
3364 { &hfs_fifoop_p
, hfs_fifoop_entries
};