2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* @(#)hfs_vnodeops.c 3.0
24 * (c) 1997-1999 Apple Computer, Inc. All Rights Reserved
25 * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved
28 * hfs_vnodeops.c -- vnode layer for loadable Macintosh file system
30 * MODIFICATION HISTORY:
31 * 11-Nov-1999 Scott Roberts Does not update if times have not changed (#2409116)
32 * 9-Nov-1999 Scott Roberts Added cluster_close to hfs_fsync(#2398208)
33 * 9-Nov-1999 Don Brady Fix locking bug in hfs_close [#2399157].
34 * 15-Sep-1999 Pat Dirks Changed hfs_setattrlist to allow changing flags on plain-HFS volumes w/o ownership [#2365108];
35 * Changed to use hfs_write_access instead of obsolete hfs_writepermission uniformly throughout.
36 * 7-Sep-1999 Don Brady Add HFS Plus hard-link support.
37 * 8-Sep-1999 Pat Dirks Changed hfs_rename to change mod. date on parent directory [#2297825].
38 * 26-Aug-1999 Pat Dirks Changed hfs_chflags to allow locking on HFS volumes w. write access only as workaround [#2313439].
39 * 2-Sep-1999 Pat Dirks Fixed hfs_pathconf to return same info for hfs/hfs+ for max. name length [#2382208]
40 * 26-Aug-1999 Pat Dirks Changed hfs_chflags to allow locking on HFS volumes w. write access only as workaround [#2313439].
41 * 24-Jul-1999 Earsh Nandkeshwar Rewrote readdirattr.
42 * 15-Jul-1999 Pat Dirks Fixed hfs_readdir to return EINVAL if design assumption of uio->uio_iovcnt == 1 is violated
43 * and cleaned up call to uiomove to check space available first.
44 * 2-Jul-1999 Pat Dirks Fixed hfs_setattrlist to ignore attempts to set null volume name (#2331829).
45 * 18-May-1999 Don Brady Add support for rooting from HFS Plus.
46 * 4-May-1999 Don Brady Split off hfs_search.c
47 * 15-Apr-1999 Don Brady Change va_nlink back to 1 for directories in hfs_getattr.
48 * 6-Apr-1999 Don Brady Fix deference of NULL h_sibling in hfs_chid.
49 * 29-Mar-1999 Scott Roberts Put in the correct . and .. entries for readdir
50 * 22-Mar-1999 Don Brady Add UFS delete semantic support to hfs_remove.
51 * 1-Mar-1999 Scott Roberts h_meta is now released when the complex vnode is relesed
52 * 26-Feb-1999 Pat Dirks (copied by Chw) Fixed hfs_lookup to check for
53 * error return on vget.
54 * 25-Feb-1999 Pat Dirks Fixed hfs_remove to use a local copy of the h_sibling pointer around vnode_uncache.
55 * 3-Feb-1999 Pat Dirks Changed to stop updating wrapper volume name in MDB since wrapper volume's
56 * catalog isn't updated and this inconsistency trips Disk First Aid's checks.
57 * 22-Jan-1999 Pat Dirks Changed hfs_rename, hfs_remove, and hfs_rmdir to call cache_purge.
58 * 22-Jan-1999 Don Brady After calling hfsMoveRename call hfs_getcatalog to get new name.
59 * 12-Jan-1999 Don Brady Fixed the size of ATTR_CMN_NAME buffer to NAME_MAX + 1.
60 * 8-Jan-1999 Pat Dirks Added hfs_writepermission and change hfs_setattrlist to use it instead of
61 * including an incorrect derivative of hfs_access in-line.
62 * 15-Dec-1998 Pat Dirks Changed setattrlist to do permission checking as appropriate (Radar #2290212).
63 * 17-Nov-1998 Scott Roberts Added support for long volume names in SetAttrList().
64 * 6-Nov-1998 Don Brady Add support for UTF-8 names.
65 * 3-Nov-1998 Umesh Vaishampayan Changes to deal with "struct timespec"
66 * change in the kernel.
67 * 21-Oct-1998 Scott Roberts Added support for advisory locking (Radar #2237914).
68 * 25-Sep-1998 Don Brady Changed hfs_exchange to call hfs_chid after updating catalog (radar #2276605).
69 * 23-Sep-1998 Don Brady hfs_setattrlist now calls hfs_chown and hfs_chmod to change values.
70 * 15-Sep-1998 Pat Dirks Cleaned up vnode unlocking on various error exit paths and changed
71 * to use new error stub routines in place of hfs_mknod and hfs_link.
72 * 16-Sep-1998 Don Brady When renaming a volume in hfs_setattrlist, also update hfs+ wrapper name (radar #2272925).
73 * 1-Sep-1998 Don Brady Fix uninitiazed time variable in hfs_makenode (radar #2270372).
74 * 31-Aug-1998 Don Brady Adjust change time for DST in hfs_update (radar #2265075).
75 * 12-Aug-1998 Don Brady Update complex node name in hfs_rename (radar #2262111).
76 * 5-Aug-1998 Don Brady In hfs_setattrlist call MacToVFSError after calling UpdateCatalogNode (radar #2261247).
77 * 21-Jul-1998 Don Brady Fixed broken preflight in hfs_getattrlist.
78 * 17-Jul-1998 Clark Warner Fixed the one left out case of freeing M_NAMEI in hfs_abort
79 * 13-Jul-1998 Don Brady Add uio_resid preflight check to hfs_search (radar #2251855).
80 * 30-Jun-1998 Scott Roberts Changed hfs_makenode and its callers to free M_NAMEI.
81 * 29-Jun-1998 Don Brady Fix unpacking order in UnpackSearchAttributeBlock (radar #2249248).
82 * 13-Jun-1998 Scott Roberts Integrated changes to hfs_lock (radar #2237243).
83 * 4-Jun-1998 Pat Dirks Split off hfs_lookup.c and hfs_readwrite.c
84 * 3-Jun-1998 Don Brady Fix hfs_rename bugs (radar #2229259, #2239823, 2231108 and #2237380).
85 * Removed extra vputs in hfs_rmdir (radar #2240309).
86 * 28-May-1998 Don Brady Fix hfs_truncate to correctly extend files (radar #2237242).
87 * 20-May-1998 Don Brady In hfs_close shrink the peof to the smallest size neccessary (radar #2230094).
88 * 5-May-1998 Don Brady Fixed typo in hfs_rename (apply H_FILEID macro to VTOH result).
89 * 29-Apr-1998 Joe Sokol Don't do cluster I/O when logical block size is not 4K multiple.
90 * 28-Apr-1998 Pat Dirks Cleaned up unused variable physBlockNo in hfs_write.
91 * 28-Apr-1998 Joe Sokol Touched up support for cluster_read/cluster_write and enabled it.
92 * 27-Apr-1998 Don Brady Remove some DEBUG_BREAK calls in DbgVopTest.
93 * 24-Apr-1998 Pat Dirks Fixed read logic to read-ahead only ONE block, and of only logBlockSize instead of 64K...
94 * Added calls to brelse() on errors from bread[n]().
95 * Changed logic to add overall length field to AttrBlockSize only on attribute return operations.
96 * 23-Apr-1998 Don Brady The hfs_symlink call is only supported on HFS Plus disks.
97 * 23-Apr-1998 Deric Horn Fixed hfs_search bug where matches were skipped when buffer was full.
98 * 22-Apr-1998 Scott Roberts Return on error if catalog mgr returns an error in truncate.
99 * 21-Apr-1998 Don Brady Fix up time/date conversions.
100 * 20-Apr-1998 Don Brady Remove course-grained hfs metadata locking.
101 * 17-Apr-1998 Pat Dirks Officially enabled searchfs in vops table.
102 * 17-Apr-1998 Deric Horn Bug fixes to hfs_search, reenabled searchfs trap for upcoming kernel build.
103 * 15-Apr-1998 Don Brady Add locking for HFS B-trees. Don't lock file meta lock for VSYSTEM files.
104 * Don't call VOP_UPDATE for system files. Roll set_time into hfs_update.
105 * 14-Apr-1998 Pat Dirks Cleaned up fsync to skip complex nodes and not hit sibling nodes.
106 * 14-Apr-1998 Deric Horn Added hfs_search() and related routines for searchfs() support.
107 * 14-Apr-1998 Scott Roberts Fixed paramaters to ExchangeFileIDs()
108 * 13-Apr-1998 Pat Dirks Changed to update H_HINT whenever hfs_getcatalog was called.
109 * 8-Apr-1998 Pat Dirks Added page-in and page-out passthrough routines to keep MapFS happy.
110 * 6-Apr-1998 Pat Dirks Changed hfs_write to clean up code and fix bug that caused
111 * zeroes to be interspersed in data. Added debug printf to hfs_read.
112 * 6-Apr-1998 Scott Roberts Added complex file support.
113 * 02-apr-1998 Don Brady UpdateCatalogNode now takes parID and name as input.
114 * 31-mar-1998 Don Brady Sync up with final HFSVolumes.h header file.
115 * 27-mar-1998 Don Brady Check result from UFSToHFSStr to make sure hfs/hfs+ names are not greater than 31 characters.
116 * 27-mar-1998 chw minor link fixes.
117 * 19-Mar-1998 ser Added hfs_readdirattr.
118 * 17-Mar-1998 ser Removed CheckUserAccess. Added code to implement ExchangeFileIDs
119 * 16-Mar-1998 Pat Dirks Fixed logic in hfs_read to properly account for space
120 * remaining past selected offset and avoid premature panic.
121 * 16-jun-1997 Scott Roberts
122 * Dec-1991 Kevin Wells at NeXT:
123 * Significantly modified for Macintosh file system.
124 * Added support for NFS exportability.
125 * 25-Jun-1990 Doug Mitchell at NeXT:
126 * Created (for DOS file system).
129 #include <sys/systm.h>
130 #include <sys/kernel.h>
131 #include <sys/file.h>
132 #include <sys/dirent.h>
133 #include <sys/stat.h>
135 #include <sys/mount.h>
136 #include <sys/vnode.h>
137 #include <sys/malloc.h>
138 #include <sys/namei.h>
139 #include <sys/attr.h>
141 #include <sys/utfconv.h>
142 #include <miscfs/specfs/specdev.h>
143 #include <miscfs/fifofs/fifo.h>
145 #include <machine/spl.h>
147 #include <sys/kdebug.h>
150 #include "hfs_lockf.h"
152 #include "hfs_mount.h"
154 #include "hfscommon/headers/CatalogPrivate.h"
155 #include "hfscommon/headers/BTreesInternal.h"
156 #include "hfscommon/headers/FileMgrInternal.h"
157 #include "hfscommon/headers/HFSUnicodeWrappers.h"
159 #define OWNERSHIP_ONLY_ATTRS (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS)
161 #define MAKE_DELETED_NAME(NAME,FID) \
162 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
165 extern uid_t console_user
;
167 /* Global vfs data structures for hfs */
168 int (**hfs_vnodeop_p
)(void *);
170 /* external routines defined in hfs_vhash.c */
171 extern void hfs_vhashrem(struct hfsnode
*hp
);
172 extern int vinvalbuf_vhash(register struct vnode
*vp
, int flags
, struct ucred
*cred
, struct proc
*p
);
173 extern void hfs_vhashmove( struct hfsnode
*hp
,UInt32 nodeID
);
174 extern struct vnode
* hfs_vhashget(dev_t dev
, UInt32 nodeID
, UInt8 forkType
);
176 extern OSErr
PositionIterator(CatalogIterator
*cip
, UInt32 offset
, BTreeIterator
*bip
, UInt16
*op
);
178 extern void hfs_name_CatToMeta(CatalogNodeData
*nodeData
, struct hfsfilemeta
*fm
);
180 extern groupmember(gid_t gid
, struct ucred
*cred
);
182 static int hfs_makenode( int mode
,
183 dev_t rawdev
, struct vnode
*dvp
, struct vnode
**vpp
,
184 struct componentname
*cnp
, struct proc
*p
);
186 static void hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
);
188 static int hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
);
190 static int hfs_chown( struct vnode
*vp
, uid_t uid
, gid_t gid
, struct ucred
*cred
, struct proc
*p
);
191 static int hfs_chmod( struct vnode
*vp
, int mode
, struct ucred
*cred
, struct proc
*p
);
192 static int hfs_chflags( struct vnode
*vp
, u_long flags
, struct ucred
*cred
, struct proc
*p
);
195 int hfs_cache_lookup(); /* in hfs_lookup.c */
196 int hfs_lookup(); /* in hfs_lookup.c */
197 int hfs_read(); /* in hfs_readwrite.c */
198 int hfs_write(); /* in hfs_readwrite.c */
199 int hfs_ioctl(); /* in hfs_readwrite.c */
200 int hfs_select(); /* in hfs_readwrite.c */
201 int hfs_mmap(); /* in hfs_readwrite.c */
202 int hfs_seek(); /* in hfs_readwrite.c */
203 int hfs_bmap(); /* in hfs_readwrite.c */
204 int hfs_strategy(); /* in hfs_readwrite.c */
205 int hfs_reallocblks(); /* in hfs_readwrite.c */
206 int hfs_truncate(); /* in hfs_readwrite.c */
207 int hfs_allocate(); /* in hfs_readwrite.c */
208 int hfs_pagein(); /* in hfs_readwrite.c */
209 int hfs_pageout(); /* in hfs_readwrite.c */
210 int hfs_search(); /* in hfs_search.c */
211 int hfs_bwrite(); /* in hfs_readwrite.c */
212 int hfs_link(); /* in hfs_link.c */
213 int hfs_blktooff(); /* in hfs_readwrite.c */
214 int hfs_offtoblk(); /* in hfs_readwrite.c */
215 int hfs_cmap(); /* in hfs_readwrite.c */
217 /*****************************************************************************
219 * Operations on vnodes
221 *****************************************************************************/
224 * Create a regular file
229 IN WILLRELE struct vnode *dvp;
230 OUT struct vnode **vpp;
231 IN struct componentname *cnp;
232 IN struct vattr *vap;
234 We are responsible for freeing the namei buffer,
235 it is done in hfs_makenode()
240 struct vop_create_args
/* {
242 struct vnode **a_vpp;
243 struct componentname *a_cnp;
247 struct proc
*p
= current_proc();
249 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
250 DBG_FUNC_NAME("create");
251 DBG_VOP_LOCKS_DECL(2);
252 DBG_VOP_PRINT_FUNCNAME();
253 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
254 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);
256 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
257 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
258 DBG_VOP_CONT(("\tva_type %d va_mode 0x%x\n",
259 ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
));
262 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
263 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
264 if(ap
->a_vap
== NULL
) {
265 panic("NULL attr on create");
268 switch(ap
->a_vap
->va_type
) {
270 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
272 DBG_VOP_LOCKS_TEST(EISDIR
);
273 return (EISDIR
); /* use hfs_mkdir instead */
278 DBG_ERR(("%s: INVALID va_type: %d, %s, %s\n", funcname
, ap
->a_vap
->va_type
, H_NAME(VTOH(ap
->a_dvp
)), ap
->a_cnp
->cn_nameptr
));
279 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
281 DBG_VOP_LOCKS_TEST(EINVAL
);
284 // if(ap->a_vap->va_mode & (VSUID | VSGID | VSVTX)) {
285 // DBG_ERR(("%s: INVALID va_mode (%o): %s, %s\n", funcname, ap->a_vap->va_mode, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
286 // DBG_VOP_LOCKS_TEST(EINVAL);
287 // VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
289 // return (EINVAL); /* Can't do these */
293 /* Create the vnode */
294 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
295 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
297 if (retval
!= E_NONE
) {
298 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
300 DBG_VOP_LOCKS_TEST(retval
);
312 IN WILLRELE struct vnode *dvp;
313 OUT WILLRELE struct vnode **vpp;
314 IN struct componentname *cnp;
315 IN struct vattr *vap;
321 struct vop_mknod_args
/* {
323 struct vnode **a_vpp;
324 struct componentname *a_cnp;
328 struct vattr
*vap
= ap
->a_vap
;
329 struct vnode
**vpp
= ap
->a_vpp
;
330 struct proc
*p
= current_proc();
334 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
335 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
340 if (vap
->va_rdev
!= VNOVAL
) {
342 * Want to be able to use this to make badblock
343 * inodes, so don't truncate the dev number.
345 rawdev
= vap
->va_rdev
;
348 /* Create the vnode */
349 error
= hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
350 rawdev
, ap
->a_dvp
, vpp
, ap
->a_cnp
, p
);
352 if (error
!= E_NONE
) {
357 * Remove inode so that it will be reloaded by lookup and
358 * checked to see if it is an alias of an existing vnode.
359 * Note: unlike UFS, we don't bash v_type here.
369 * mkcomplex vnode call
372 #% mkcomplex dvp L U U
373 #% mkcomplex vpp - L -
376 IN WILLRELE struct vnode *dvp;
377 OUT struct vnode **vpp;
378 IN struct componentname *cnp;
379 IN struct vattr *vap;
387 struct vop_mkcomplex_args
/* {
389 struct vnode **a_vpp;
390 struct componentname *a_cnp;
396 DBG_FUNC_NAME("make_complex");
397 DBG_VOP_LOCKS_DECL(2);
398 DBG_VOP_PRINT_FUNCNAME();
399 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
400 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
402 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
403 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
405 retval
= VOP_CREATE(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
);
407 DBG_VOP_LOCKS_TEST(retval
);
419 IN struct ucred *cred;
426 struct vop_open_args
/* {
429 struct ucred *a_cred;
433 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
435 DBG_FUNC_NAME("open");
436 DBG_VOP_LOCKS_DECL(1);
437 DBG_VOP_PRINT_FUNCNAME();
438 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
439 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
441 if (ap
->a_vp
->v_type
== VREG
) /* Only files */
444 * Files marked append-only must be opened for appending.
446 if ((hp
->h_meta
->h_pflags
& APPEND
) &&
447 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
452 DBG_VOP_LOCKS_TEST(retval
);
459 * Update the times on the hfsnode.
465 IN struct ucred *cred;
472 struct vop_close_args
/* {
475 struct ucred *a_cred;
479 register struct vnode
*vp
= ap
->a_vp
;
480 struct hfsnode
*hp
= VTOH(vp
);
481 struct proc
*p
= ap
->a_p
;
485 u_long blks
, blocksize
;
488 DBG_FUNC_NAME("close");
489 DBG_VOP_LOCKS_DECL(1);
490 DBG_VOP_PRINT_FUNCNAME();
491 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
492 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
494 simple_lock(&vp
->v_interlock
);
495 if (vp
->v_usecount
> (UBCINFOEXISTS(vp
) ? 2 : 1)) {
497 HFSTIMES(hp
, &tv
, &tv
);
499 simple_unlock(&vp
->v_interlock
);
502 * VOP_CLOSE can be called with vp locked (from vclean).
503 * We check for this case using VOP_ISLOCKED and bail.
505 * also, ignore complex nodes; there's no data associated with them.
507 if (H_FORKTYPE(hp
) == kDirectory
|| VOP_ISLOCKED(vp
)) {
508 DBG_VOP_LOCKS_TEST(E_NONE
);
516 enum vtype our_type
= vp
->v_type
;
517 u_long our_id
= vp
->v_id
;
519 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
521 * Since we can contact switch in vn_lock our vnode
522 * could get recycled (eg umount -f). Double check
523 * that its still ours.
525 if (vp
->v_type
!= our_type
|| vp
->v_id
!= our_id
) {
526 VOP_UNLOCK(vp
, 0, p
);
527 DBG_VOP_LOCKS_TEST(E_NONE
);
531 blocksize
= HTOVCB(hp
)->blockSize
;
532 blks
= leof
/ blocksize
;
533 if (((off_t
)blks
* (off_t
)blocksize
) != leof
)
537 * Shrink the peof to the smallest size neccessary to contain the leof.
539 if (((off_t
)blks
* (off_t
)blocksize
) < fcb
->fcbPLen
) {
540 retval
= VOP_TRUNCATE(vp
, leof
, IO_NDELAY
, ap
->a_cred
, p
);
543 VOP_UNLOCK(vp
, 0, p
);
546 DBG_VOP_LOCKS_TEST(retval
);
556 IN struct ucred *cred;
563 struct vop_access_args
/* {
566 struct ucred *a_cred;
570 struct vnode
*vp
= ap
->a_vp
;
571 struct ucred
*cred
= ap
->a_cred
;
572 struct hfsnode
*hp
= VTOH(vp
);
573 ExtendedVCB
*vcb
= HTOVCB(hp
);
579 DBG_FUNC_NAME("access");
580 DBG_VOP_LOCKS_DECL(1);
581 // DBG_VOP_PRINT_FUNCNAME();
582 // DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
584 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
587 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
590 * Disallow write attempts on read-only file systems;
591 * unless the file is a socket, fifo, or a block or
592 * character device resident on the file system.
595 switch (vp
->v_type
) {
599 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
607 /* If immutable bit set, nobody gets to write it. */
608 if ((mode
& VWRITE
) && (hp
->h_meta
->h_pflags
& IMMUTABLE
))
611 /* Otherwise, user id 0 always gets access. */
612 if (ap
->a_cred
->cr_uid
== 0) {
619 /* Otherwise, check the owner. */
620 if (hfs_owner_rights(vp
, cred
, ap
->a_p
, false) == 0) {
627 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
631 /* Otherwise, check the groups. */
632 if (! (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
)) {
633 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++) {
634 if (hp
->h_meta
->h_gid
== *gp
) {
641 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
647 /* Otherwise, check everyone else. */
654 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
657 DBG_VOP_LOCKS_TEST(retval
);
668 IN struct vattr *vap;
669 IN struct ucred *cred;
678 struct vop_getattr_args
/* {
681 struct ucred *a_cred;
685 register struct vnode
*vp
= ap
->a_vp
;
686 register struct hfsnode
*hp
= VTOH(vp
);
687 register struct vattr
*vap
= ap
->a_vap
;
689 DBG_FUNC_NAME("getattr");
690 DBG_VOP_LOCKS_DECL(1);
691 DBG_VOP_PRINT_FUNCNAME();
692 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
694 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
696 DBG_HFS_NODE_CHECK(ap
->a_vp
);
699 HFSTIMES(hp
, &tv
, &tv
);
701 vap
->va_fsid
= H_DEV(hp
);
702 vap
->va_fileid
= H_FILEID(hp
);
703 vap
->va_mode
= hp
->h_meta
->h_mode
;
704 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
705 vap
->va_uid
= (VTOHFS(vp
)->hfs_uid
== UNKNOWNUID
) ? console_user
: VTOHFS(vp
)->hfs_uid
;
707 vap
->va_uid
= (hp
->h_meta
->h_uid
== UNKNOWNUID
) ? console_user
: hp
->h_meta
->h_uid
;
709 vap
->va_gid
= hp
->h_meta
->h_gid
;
710 if (vp
->v_type
== VDIR
) {
711 vap
->va_size
= hp
->h_meta
->h_size
;
714 vap
->va_nlink
= hp
->h_meta
->h_nlink
;
716 * account for hidden data nodes directory
718 if ((H_FILEID(hp
) == kRootDirID
) &&
719 (VTOHFS(vp
)->hfs_private_metadata_dir
!= 0)) {
720 vap
->va_size
-= AVERAGE_HFSDIRENTRY_SIZE
;
725 vap
->va_size
= hp
->fcbEOF
;
726 vap
->va_bytes
= hp
->h_meta
->h_size
;
728 if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
)
729 vap
->va_rdev
= hp
->h_meta
->h_rdev
;
733 if (hp
->h_meta
->h_metaflags
& IN_DELETED
)
736 else if ((hp
->h_meta
->h_metaflags
& IN_DATANODE
) &&
737 (hp
->h_meta
->h_nlink
> 0))
738 vap
->va_nlink
= hp
->h_meta
->h_nlink
;
745 vap
->va_atime
.tv_nsec
= 0;
746 vap
->va_atime
.tv_sec
= hp
->h_meta
->h_atime
;
747 vap
->va_mtime
.tv_nsec
= 0;
748 vap
->va_mtime
.tv_sec
= hp
->h_meta
->h_mtime
;
749 vap
->va_ctime
.tv_nsec
= 0;
750 vap
->va_ctime
.tv_sec
= hp
->h_meta
->h_ctime
;
751 vap
->va_flags
= hp
->h_meta
->h_pflags
;
753 /* this doesn't belong here */
754 if (vp
->v_type
== VBLK
)
755 vap
->va_blocksize
= BLKDEV_IOSIZE
;
756 else if (vp
->v_type
== VCHR
)
757 vap
->va_blocksize
= MAXPHYSIO
;
759 vap
->va_blocksize
= VTOVFS(vp
)->mnt_stat
.f_iosize
;
760 vap
->va_type
= vp
->v_type
;
763 DBG_VOP_LOCKS_TEST(E_NONE
);
768 * Set attribute vnode op. called from several syscalls
773 IN struct vattr *vap;
774 IN struct ucred *cred;
781 struct vop_setattr_args
/* {
784 struct ucred *a_cred;
788 struct vnode
*vp
= ap
->a_vp
;
789 struct hfsnode
*hp
= VTOH(vp
);
790 struct vattr
*vap
= ap
->a_vap
;
791 struct ucred
*cred
= ap
->a_cred
;
792 struct proc
*p
= ap
->a_p
;
793 struct timeval atimeval
, mtimeval
;
795 DBG_FUNC_NAME("setattr");
796 DBG_VOP_LOCKS_DECL(1);
797 DBG_VOP_PRINT_FUNCNAME();
798 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
799 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
800 WRITE_CK(vp
, funcname
);
801 DBG_HFS_NODE_CHECK(ap
->a_vp
);
804 * Check for unsettable attributes.
806 if ((vap
->va_type
!= VNON
) || (vap
->va_nlink
!= VNOVAL
) ||
807 (vap
->va_fsid
!= VNOVAL
) || (vap
->va_fileid
!= VNOVAL
) ||
808 (vap
->va_blocksize
!= VNOVAL
) || (vap
->va_rdev
!= VNOVAL
) ||
809 ((int)vap
->va_bytes
!= VNOVAL
) || (vap
->va_gen
!= VNOVAL
)) {
814 if (vap
->va_flags
!= VNOVAL
) {
815 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
819 if ((retval
= hfs_chflags(vp
, vap
->va_flags
, cred
, p
))) {
822 if (vap
->va_flags
& (IMMUTABLE
| APPEND
)) {
828 if (hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) {
833 * Go through the fields and update iff not VNOVAL.
835 if (vap
->va_uid
!= (uid_t
)VNOVAL
|| vap
->va_gid
!= (gid_t
)VNOVAL
) {
836 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
840 if ((retval
= hfs_chown(vp
, vap
->va_uid
, vap
->va_gid
, cred
, p
))) {
844 if (vap
->va_size
!= VNOVAL
) {
846 * Disallow write attempts on read-only file systems;
847 * unless the file is a socket, fifo, or a block or
848 * character device resident on the file system.
850 switch (vp
->v_type
) {
856 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
864 if ((retval
= VOP_TRUNCATE(vp
, vap
->va_size
, 0, cred
, p
))) {
869 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
870 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
874 if (((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) &&
875 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
876 (retval
= VOP_ACCESS(vp
, VWRITE
, cred
, p
)))) {
879 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
880 hp
->h_nodeflags
|= IN_ACCESS
;
881 if (vap
->va_mtime
.tv_sec
!= VNOVAL
) {
882 hp
->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
884 * The utimes system call can reset the modification time
885 * but it doesn't know about the HFS+ create time. So we
886 * need to insure that the creation time is always at least
887 * as old as the modification time.
889 if (( VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
890 ( H_FILEID(hp
) != kRootDirID
) &&
891 ( vap
->va_mtime
.tv_sec
< hp
->h_meta
->h_crtime
))
892 hp
->h_meta
->h_crtime
= vap
->va_mtime
.tv_sec
;
894 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
895 atimeval
.tv_usec
= 0;
896 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
897 mtimeval
.tv_usec
= 0;
898 if ((retval
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1))) {
903 if (vap
->va_mode
!= (mode_t
)VNOVAL
) {
904 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
908 retval
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
);
913 DBG_VOP(("hfs_setattr: returning %d...\n", retval
));
914 DBG_VOP_LOCKS_TEST(retval
);
922 #% getattrlist vp = = =
926 IN struct attrlist *alist;
927 INOUT struct uio *uio;
928 IN struct ucred *cred;
936 struct vop_getattrlist_args
/* {
938 struct attrlist *a_alist
940 struct ucred *a_cred;
944 struct vnode
*vp
= ap
->a_vp
;
945 struct hfsnode
*hp
= VTOH(vp
);
946 struct attrlist
*alist
= ap
->a_alist
;
948 struct hfsCatalogInfo catInfo
;
949 struct hfsCatalogInfo
*catInfoPtr
= NULL
;
958 DBG_FUNC_NAME("getattrlist");
959 DBG_VOP_LOCKS_DECL(1);
961 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
962 DBG_HFS_NODE_CHECK(ap
->a_vp
);
963 DBG_VOP(("%s: Common attr:0x%lx, buff size Ox%lX,\n",funcname
, (u_long
)alist
->commonattr
,(u_long
)ap
->a_uio
->uio_resid
));
965 DBG_ASSERT(ap
->a_uio
->uio_rw
== UIO_READ
);
967 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
968 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
969 ((alist
->volattr
& ~ATTR_VOL_VALIDMASK
) != 0) ||
970 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
971 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
972 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0)) {
973 DBG_ERR(("%s: bad attrlist\n", funcname
));
974 DBG_VOP_LOCKS_TEST(EINVAL
);
978 /* Requesting volume information requires setting the ATTR_VOL_INFO bit and
979 volume info requests are mutually exclusive with all other info requests: */
980 if ((alist
->volattr
!= 0) && (((alist
->volattr
& ATTR_VOL_INFO
) == 0) ||
981 (alist
->dirattr
!= 0) || (alist
->fileattr
!= 0) || (alist
->forkattr
!= 0)
983 DBG_ERR(("%s: conflicting information requested\n", funcname
));
984 DBG_VOP_LOCKS_TEST(EINVAL
);
988 /* Reject requests for unsupported options for now: */
989 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
990 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
))) {
991 DBG_ERR(("%s: illegal bits in attlist\n", funcname
));
992 DBG_VOP_LOCKS_TEST(EINVAL
);
996 /* Requesting volume information requires root vnode */
997 if ((alist
->volattr
) && (H_FILEID(hp
) != kRootDirID
)) {
998 DBG_ERR(("%s: not root vnode\n", funcname
));
999 DBG_VOP_LOCKS_TEST(EINVAL
);
1004 /* Update times if needed */
1006 HFSTIMES(hp
, &tv
, &tv
);
1008 /* If a FileID (ATTR_CMN_OBJPERMANENTID) is requested on an HFS volume we must be sure
1009 to create the thread record before returning it:
1011 if ((vp
->v_type
== VREG
) &&
1012 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
)) {
1013 /* Only HFS-Plus volumes are guaranteed to have a thread record in place already: */
1014 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
) {
1015 /* Create a thread record and return the FileID [which is the file's fileNumber] */
1016 /* lock catalog b-tree */
1017 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1018 error
= hfsCreateFileID(VTOVCB(vp
), H_DIRID(hp
), H_NAME(hp
), H_HINT(hp
), &fileID
);
1019 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1021 DBG_VOP_LOCKS_TEST(error
);
1022 DBG_ERR(("hfs_getattrlist: error %d on CreateFileIDRef.\n", error
));
1025 DBG_ASSERT(fileID
== H_FILEID(hp
));
1029 /* Asking for data fork attributes from the rsrc fork is not supported */
1030 if ((H_FORKTYPE(hp
) == kRsrcFork
) && (alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
)) {
1035 * Avoid unnecessary catalog lookups for volume info which is available directly
1036 * in the VCB and root vnode, or can be synthesized.
1038 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
1039 catInfo
.hint
= kNoHint
;
1041 if (((alist
->volattr
== 0) && ((alist
->commonattr
& HFS_ATTR_CMN_LOOKUPMASK
) != 0)) ||
1042 ((alist
->dirattr
& HFS_ATTR_DIR_LOOKUPMASK
) != 0) ||
1043 ((alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
) != 0)) {
1045 /* lock catalog b-tree */
1046 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, ap
->a_p
);
1047 if (error
) goto GetCatalogErr_Exit
;
1049 if (alist
->volattr
!= 0) {
1050 /* Look up the root info, regardless of the vnode provided */
1051 error
= hfs_getcatalog(VTOVCB(vp
), 2, NULL
, -1, &catInfo
);
1053 error
= hfs_getcatalog(VTOVCB(vp
), H_DIRID(hp
), H_NAME(hp
), -1, &catInfo
);
1054 if (error
== 0) H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1057 /* unlock catalog b-tree */
1058 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1061 * If a data fork has an active sibling and we need
1062 * rsrc fork attributes then we need to lock the
1063 * sibling and make a copy of its attributes.
1065 if ((hp
->h_meta
->h_usecount
> 1) &&
1066 (H_FORKTYPE(hp
) == kDataFork
) &&
1067 (alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
)) {
1068 struct vnode
*sib_vp
= NULL
;
1069 struct hfsnode
*nhp
;
1070 struct proc
*p
= current_proc();
1072 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
1073 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
1074 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
1076 /* Loop through all siblings, skipping ourselves */
1077 simple_lock(&hp
->h_meta
->h_siblinglock
);
1078 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1079 if (nhp
== hp
) /* skip ourselves */
1083 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1085 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1086 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1087 if (VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1088 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1089 CopyVNodeToCatalogNode (sib_vp
, &catInfo
.nodeData
);
1094 }; /* h_use_count > 1 */
1096 /* Update to the in-memory state, if it has been modified...just to make sure */
1097 if (VTOH(vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1098 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1099 CopyVNodeToCatalogNode (vp
, &catInfo
.nodeData
);
1102 /* XXX What if hfs_getcatalog fails...we just continue??? */
1103 catInfoPtr
= &catInfo
;
1107 fixedblocksize
= AttributeBlockSize(alist
);
1108 attrblocksize
= fixedblocksize
+ (sizeof(u_long
)); /* u_long for length longword */
1109 if (alist
->commonattr
& ATTR_CMN_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1110 if (alist
->commonattr
& ATTR_CMN_NAMEDATTRLIST
) attrblocksize
+= 0; /* XXX PPD */
1111 if (alist
->volattr
& ATTR_VOL_MOUNTPOINT
) attrblocksize
+= PATH_MAX
;
1112 if (alist
->volattr
& ATTR_VOL_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1113 if (alist
->fileattr
& ATTR_FILE_FORKLIST
) attrblocksize
+= 0; /* XXX PPD */
1115 attrbufsize
= MIN(ap
->a_uio
->uio_resid
, attrblocksize
);
1116 DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
1119 attrblocksize
- fixedblocksize
));
1120 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1121 attrptr
= attrbufptr
;
1122 *((u_long
*)attrptr
) = 0; /* Set buffer length in case of errors */
1123 ++((u_long
*)attrptr
); /* Reserve space for length field */
1124 varptr
= ((char *)attrptr
) + fixedblocksize
; /* Point to variable-length storage */
1125 DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int
)attrptr
, (u_int
)varptr
));
1127 PackAttributeBlock(alist
, vp
, catInfoPtr
, &attrptr
, &varptr
);
1128 attrbufsize
= MIN(attrbufsize
, (u_int
)varptr
- (u_int
)attrbufptr
); /* Don't copy out more data than was generated */
1129 *((u_long
*)attrbufptr
) = attrbufsize
; /* Set actual buffer length for return to caller */
1130 DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize
, (u_int
)ap
->a_uio
->uio_iov
->iov_base
));
1131 error
= uiomove((caddr_t
)attrbufptr
, attrbufsize
, ap
->a_uio
);
1132 if (error
!= E_NONE
) {
1133 DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error
));
1136 FREE(attrbufptr
, M_TEMP
);
1140 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1141 DBG_VOP_LOCKS_TEST(error
);
1150 #% setattrlist vp L L L
1153 IN struct vnode *vp;
1154 IN struct attrlist *alist;
1155 INOUT struct uio *uio;
1156 IN struct ucred *cred;
1164 struct vop_setattrlist_args
/* {
1166 struct attrlist *a_alist
1168 struct ucred *a_cred;
1172 struct vnode
*vp
= ap
->a_vp
;
1173 struct hfsnode
*hp
= VTOH(vp
);
1174 struct attrlist
*alist
= ap
->a_alist
;
1175 struct ucred
*cred
= ap
->a_cred
;
1176 struct proc
*p
= ap
->a_p
;
1178 struct hfsCatalogInfo catInfo
;
1180 void *attrbufptr
= NULL
;
1182 void *varptr
= NULL
;
1192 DBG_FUNC_NAME("setattrlist");
1193 DBG_VOP_LOCKS_DECL(1);
1195 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
1196 DBG_HFS_NODE_CHECK(ap
->a_vp
);
1197 DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname
, (u_int
)alist
->commonattr
,(u_int
)ap
->a_uio
->uio_resid
));
1199 DBG_ASSERT(ap
->a_uio
->uio_rw
== UIO_WRITE
);
1201 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
1202 ((alist
->commonattr
& ~ATTR_CMN_SETMASK
) != 0) ||
1203 ((alist
->volattr
& ~ATTR_VOL_SETMASK
) != 0) ||
1204 ((alist
->dirattr
& ~ATTR_DIR_SETMASK
) != 0) ||
1205 ((alist
->fileattr
& ~ATTR_FILE_SETMASK
) != 0) ||
1206 ((alist
->forkattr
& ~ATTR_FORK_SETMASK
) != 0)) {
1207 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1208 DBG_VOP_LOCKS_TEST(EINVAL
);
1212 if ((alist
->volattr
!= 0) && /* Setting volume info */
1213 (((alist
->volattr
& ATTR_VOL_INFO
) == 0) || /* Not explicitly indicating this or ... */
1214 (alist
->commonattr
& ~ATTR_CMN_VOLSETMASK
))) /* ... setting invalid attributes for volume */
1216 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1217 DBG_VOP_LOCKS_TEST(EINVAL
);
1221 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
1222 DBG_VOP_LOCKS_TEST(EROFS
);
1227 Ownership of the file (in addition to write access, checked below,
1228 is required in one of two classes of calls:
1230 (a) When setting any ownership-requiring attribute other than ATTR_CMN_FLAGS, or
1231 (b) When setting ATTR_CMN_FLAGS on a volume that's not plain HFS (for which no
1232 real per-object ownership information is stored):
1234 if ((alist
->commonattr
& (OWNERSHIP_ONLY_ATTRS
& ~ATTR_CMN_FLAGS
)) ||
1235 ((alist
->commonattr
& ATTR_CMN_FLAGS
) && (VTOVCB(vp
)->vcbSigWord
!= kHFSSigWord
))) {
1236 /* NOTE: The following isn't ENTIRELY complete: even if you're the superuser
1237 you cannot change the flags as long as SF_IMMUTABLE or SF_APPEND is
1238 set and securelevel > 0. This is verified in hfs_chflags which gets
1239 invoked to do the actual flags field change so this check is sufficient
1242 /* Check to see if the user owns the object [or is superuser]: */
1243 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1244 DBG_VOP_LOCKS_TEST(retval
);
1248 DBG_ASSERT(((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == 0) ||
1249 (((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == ATTR_CMN_FLAGS
) &&
1250 (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
)));
1251 /* No ownership access is required: mere write access (checked below) will do... */
1254 /* For any other attributes, check to see if the user has write access to
1255 the object in question [unlike VOP_ACCESS, ignore IMMUTABLE here]: */
1257 if ((((alist
->commonattr
& ~(OWNERSHIP_ONLY_ATTRS
)) != 0) ||
1258 (alist
->volattr
!= 0) ||
1259 (alist
->dirattr
!= 0) ||
1260 (alist
->fileattr
!= 0) ||
1261 (alist
->forkattr
!= 0)) &&
1262 ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0)) {
1263 DBG_VOP_LOCKS_TEST(retval
);
1265 }; /* end of if ownership attr */
1267 /* Allocate the buffer now to minimize the time we might be blocked holding the catalog lock */
1268 attrblocksize
= ap
->a_uio
->uio_resid
;
1269 if (attrblocksize
< AttributeBlockSize(alist
)) {
1270 DBG_ERR(("%s: bad attrblocksize\n", funcname
));
1271 DBG_VOP_LOCKS_TEST(EINVAL
);
1275 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1277 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
1278 catInfo
.hint
= kNoHint
;
1280 /* lock catalog b-tree */
1281 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1282 if (error
!= E_NONE
) {
1286 filename
= H_NAME(hp
);
1291 * Force an update of the indirect node instead of the link
1292 * by using the name and parent of the indirect node.
1294 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
1295 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
1296 filename
= iNodeName
;
1297 pid
= VTOHFS(vp
)->hfs_private_metadata_dir
;
1302 error
= hfs_getcatalog(VTOVCB(vp
), pid
, filename
, -1, &catInfo
);
1303 if (error
!= E_NONE
) {
1304 DBG_ERR(("%s: Lookup failed on file '%s'\n", funcname
, filename
));
1307 H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1309 error
= uiomove((caddr_t
)attrbufptr
, attrblocksize
, ap
->a_uio
);
1310 if (error
) goto ErrorExit
;
1312 if ((alist
->volattr
) && (H_FILEID(hp
) != kRootDirID
)) {
1317 /* do we have permission to change the dates? */
1318 // if (alist->commonattr & (ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME | ATTR_CMN_BKUPTIME)) {
1319 if (alist
->commonattr
& (ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1320 if ((error
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1325 /* save these in case hfs_chown() or hfs_chmod() fail */
1326 saved_uid
= hp
->h_meta
->h_uid
;
1327 saved_gid
= hp
->h_meta
->h_gid
;
1328 saved_mode
= hp
->h_meta
->h_mode
;
1329 saved_flags
= hp
->h_meta
->h_pflags
;
1331 attrptr
= attrbufptr
;
1332 UnpackAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
1334 /* if unpacking changed the owner or group then call hfs_chown() */
1335 if (saved_uid
!= hp
->h_meta
->h_uid
|| saved_gid
!= hp
->h_meta
->h_gid
) {
1339 uid
= hp
->h_meta
->h_uid
;
1340 hp
->h_meta
->h_uid
= saved_uid
;
1341 gid
= hp
->h_meta
->h_gid
;
1342 hp
->h_meta
->h_gid
= saved_gid
;
1343 if ((error
= hfs_chown(vp
, uid
, gid
, cred
, p
)))
1347 /* if unpacking changed the mode then call hfs_chmod() */
1348 if (saved_mode
!= hp
->h_meta
->h_mode
) {
1351 mode
= hp
->h_meta
->h_mode
;
1352 hp
->h_meta
->h_mode
= saved_mode
;
1353 if ((error
= hfs_chmod(vp
, mode
, cred
, p
)))
1357 /* if unpacking changed the flags then call hfs_chflags */
1358 if (saved_flags
!= hp
->h_meta
->h_pflags
) {
1361 flags
= hp
->h_meta
->h_pflags
;
1362 hp
->h_meta
->h_pflags
= saved_flags
;
1363 if ((error
= hfs_chflags(vp
, flags
, cred
, p
)))
1367 if (alist
->volattr
== 0) {
1368 error
= MacToVFSError( UpdateCatalogNode(HTOVCB(hp
), pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
));
1371 if (alist
->volattr
& ATTR_VOL_NAME
) {
1372 ExtendedVCB
*vcb
= VTOVCB(vp
);
1373 int namelen
= strlen(vcb
->vcbVN
);
1375 if (vcb
->vcbVN
[0] == 0) {
1377 Ignore attempts to rename a volume to a zero-length name:
1378 restore the original name from the metadata.
1380 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
);
1382 error
= MoveRenameCatalogNode(vcb
, kRootParID
, H_NAME(hp
), H_HINT(hp
), kRootParID
, vcb
->vcbVN
, &H_HINT(hp
));
1385 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
); /* Restore the old name in the VCB */
1386 vcb
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1391 hfs_set_metaname(vcb
->vcbVN
, hp
->h_meta
, HTOHFS(hp
));
1392 hp
->h_nodeflags
|= IN_CHANGE
;
1395 /* if hfs wrapper exists, update its name too */
1396 if (vcb
->vcbSigWord
== kHFSPlusSigWord
&& vcb
->vcbAlBlSt
!= 0) {
1397 HFSMasterDirectoryBlock
*mdb
;
1398 struct buf
*bp
= NULL
;
1399 int size
= kMDBSize
; /* 512 */
1400 int volnamelen
= MIN(sizeof(Str27
), namelen
);
1402 if ( bread(VTOHFS(vp
)->hfs_devvp
, IOBLKNOFORBLK(kMasterDirectoryBlock
, size
),
1403 IOBYTECCNTFORBLK(kMasterDirectoryBlock
, kMDBSize
, size
), NOCRED
, &bp
) == 0) {
1405 mdb
= (HFSMasterDirectoryBlock
*)((char *)bp
->b_data
+ IOBYTEOFFSETFORBLK(kMasterDirectoryBlock
, size
));
1406 if (SWAP_BE16 (mdb
->drSigWord
) == kHFSSigWord
) {
1407 /* Convert the string to MacRoman, ignoring any errors, */
1408 (void) utf8_to_hfs(vcb
, volnamelen
, vcb
->vcbVN
, Str31 mdb
->drVN
)
1417 }; /* vcb->vcbVN[0] == 0 ... else ... */
1418 }; /* alist->volattr & ATTR_VOL_NAME */
1421 /* unlock catalog b-tree */
1422 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1424 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1427 if (attrbufptr
) FREE(attrbufptr
, M_TEMP
);
1429 DBG_VOP_LOCKS_TEST(error
);
1434 * Change the mode on a file.
1435 * Inode must be locked before calling.
1438 hfs_chmod(vp
, mode
, cred
, p
)
1439 register struct vnode
*vp
;
1441 register struct ucred
*cred
;
1444 register struct hfsnode
*hp
= VTOH(vp
);
1447 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1450 #if OVERRIDE_UNKNOWN_PERMISSIONS
1451 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1456 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1459 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
1461 if (!groupmember(hp
->h_meta
->h_gid
, cred
) && (mode
& ISGID
))
1464 hp
->h_meta
->h_mode
&= ~ALLPERMS
;
1465 hp
->h_meta
->h_mode
|= (mode
& ALLPERMS
);
1466 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1467 hp
->h_nodeflags
|= IN_CHANGE
;
1473 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
1475 struct hfsnode
*hp
= VTOH(vp
);
1476 ExtendedVCB
*vcb
= HTOVCB(hp
);
1479 int retval
= E_NONE
;
1482 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
1485 * Disallow write attempts on read-only file systems;
1486 * unless the file is a socket, fifo, or a block or
1487 * character device resident on the file system.
1489 switch (vp
->v_type
) {
1493 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
1500 /* If immutable bit set, nobody gets to write it. */
1501 if (considerFlags
&& (hp
->h_meta
->h_pflags
& IMMUTABLE
))
1504 /* Otherwise, user id 0 always gets access. */
1505 if (cred
->cr_uid
== 0) {
1510 /* Otherwise, check the owner. */
1511 if ((retval
= hfs_owner_rights(vp
, cred
, p
, false)) == 0) {
1512 retval
= ((hp
->h_meta
->h_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
1516 /* Otherwise, check the groups. */
1517 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++)
1518 if (hp
->h_meta
->h_gid
== *gp
) {
1519 retval
= ((hp
->h_meta
->h_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
1523 /* Otherwise, check everyone else. */
1524 retval
= ((hp
->h_meta
->h_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
1533 * Change the flags on a file or directory.
1534 * Inode must be locked before calling.
1537 hfs_chflags(vp
, flags
, cred
, p
)
1538 register struct vnode
*vp
;
1539 register u_long flags
;
1540 register struct ucred
*cred
;
1543 register struct hfsnode
*hp
= VTOH(vp
);
1546 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
1547 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
1550 } else if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1554 if (cred
->cr_uid
== 0) {
1555 if ((hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
1559 hp
->h_meta
->h_pflags
= flags
;
1561 if (hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
) ||
1562 (flags
& UF_SETTABLE
) != flags
) {
1565 hp
->h_meta
->h_pflags
&= SF_SETTABLE
;
1566 hp
->h_meta
->h_pflags
|= (flags
& UF_SETTABLE
);
1568 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1569 hp
->h_nodeflags
|= IN_CHANGE
;
1576 * Perform chown operation on hfsnode hp;
1577 * hfsnode must be locked prior to call.
1580 hfs_chown(vp
, uid
, gid
, cred
, p
)
1581 register struct vnode
*vp
;
1587 register struct hfsnode
*hp
= VTOH(vp
);
1592 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1595 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1599 if (uid
== (uid_t
)VNOVAL
)
1600 uid
= hp
->h_meta
->h_uid
;
1601 if (gid
== (gid_t
)VNOVAL
)
1602 gid
= hp
->h_meta
->h_gid
;
1604 * If we don't own the file, are trying to change the owner
1605 * of the file, or are not a member of the target group,
1606 * the caller must be superuser or the call fails.
1608 if ((cred
->cr_uid
!= hp
->h_meta
->h_uid
|| uid
!= hp
->h_meta
->h_uid
||
1609 (gid
!= hp
->h_meta
->h_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
1610 (retval
= suser(cred
, &p
->p_acflag
)))
1613 ogid
= hp
->h_meta
->h_gid
;
1614 ouid
= hp
->h_meta
->h_uid
;
1616 hp
->h_meta
->h_gid
= gid
;
1617 hp
->h_meta
->h_uid
= uid
;
1619 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1620 if (ouid
!= uid
|| ogid
!= gid
)
1621 hp
->h_nodeflags
|= IN_CHANGE
;
1622 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
1623 hp
->h_meta
->h_mode
&= ~ISUID
;
1624 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
1625 hp
->h_meta
->h_mode
&= ~ISGID
;
1633 #% exchange fvp L L L
1634 #% exchange tvp L L L
1637 IN struct vnode *fvp;
1638 IN struct vnode *tvp;
1639 IN struct ucred *cred;
1645 * exchange is a very tricky routine, because we might have to unlock the
1646 * passed in vnode, and then retry locking it and all its siblings, and then
1647 * unlocking them in reverse.
1648 * Also the sibling list lock must be kept during the whole operation to
1649 * make sure nothing changes underneath us.
1650 * Also it depends on behavior of the sibling list and hash, so
1651 * careful if you change anything.
1656 struct vop_exchange_args
/* {
1657 struct vnode *a_fvp;
1658 struct vnode *a_tvp;
1659 struct ucred *a_cred;
1663 struct hfsnode
*from_hp
, *to_hp
, *nhp
;
1664 struct hfsnode
*fromFirst
, *fromSecond
, *toFirst
, *toSecond
;
1665 struct vnode
*from_vp
, *to_vp
;
1666 struct hfsmount
*hfsmp
;
1667 u_char tmp_name
[kHFSPlusMaxFileNameBytes
+1]; /* 766 bytes! */
1669 u_int32_t fromFileID
, toFileID
;
1670 u_int32_t fromParID
;
1672 int retval
= E_NONE
;
1673 DBG_FUNC_NAME("exchange");
1674 DBG_VOP_LOCKS_DECL(2);
1675 DBG_VOP_LOCKS_INIT(0,ap
->a_fvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1676 DBG_VOP_LOCKS_INIT(1,ap
->a_tvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1678 /* Set up variables and checks */
1679 from_vp
= ap
->a_fvp
;
1681 from_hp
= VTOH(from_vp
);
1682 to_hp
= VTOH(to_vp
);
1683 hfsmp
= VTOHFS(from_vp
);
1684 vcb
= HTOVCB(from_hp
);
1685 toFileID
= H_FILEID(to_hp
);
1686 fromFileID
= H_FILEID(from_hp
);
1687 fromParID
= H_DIRID(from_hp
);
1689 if (from_vp
->v_mount
!= to_vp
->v_mount
) {
1690 DBG_VOP_LOCKS_TEST(EXDEV
);
1694 /* Can only exchange file objects */
1695 if (from_vp
->v_type
!= VREG
|| to_vp
->v_type
!= VREG
) {
1696 DBG_VOP_LOCKS_TEST(EINVAL
);
1701 * Lock the siblink list
1702 * Check for multiple forks
1703 * If there are, we would need to:
1704 * 1. Unlock ourselves
1705 * 3. Traverse the list in a forward order...locking all vnodes
1706 * 4. Flush all buffers
1707 * 5. Perform the exchange
1708 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1709 * Notice that the sibling lock is kept during the whole operation. This quarentees
1710 * that no new forks are taken off or put on
1712 DBG_ASSERT(H_FORKTYPE(from_hp
)==kDataFork
&& H_FORKTYPE(to_hp
)==kDataFork
);
1713 fromFirst
= fromSecond
= toFirst
= toSecond
= NULL
;
1715 if (from_hp
->h_meta
->h_usecount
> 1) {
1717 * This has siblings, so remember the passed-in vnode,
1718 * unlock it if it is not the 'first' sibling,
1719 * and then lock the rest of the vnodes by sibling order.
1720 * Notice that the passed-in vnode is not vrele(), this
1721 * keeps the usecount>0, so it wont go away.
1723 simple_lock(&from_hp
->h_meta
->h_siblinglock
);
1724 fromFirst
= from_hp
->h_meta
->h_siblinghead
.cqh_first
;
1725 fromSecond
= fromFirst
->h_sibling
.cqe_next
;
1726 simple_unlock(&from_hp
->h_meta
->h_siblinglock
);
1728 if (fromFirst
== from_hp
) {
1729 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1730 fromSecond
= NULL
; /* its going away */
1732 VOP_UNLOCK(HTOV(from_hp
), 0, ap
->a_p
);
1733 if (vget(HTOV(fromFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1734 fromFirst
= NULL
; /* its going away */
1735 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1736 fromSecond
= NULL
; /* its going away */
1740 fromFirst
= from_hp
;
1743 if (to_hp
->h_meta
->h_usecount
> 1) {
1745 simple_lock(&to_hp
->h_meta
->h_siblinglock
);
1746 toFirst
= to_hp
->h_meta
->h_siblinghead
.cqh_first
;
1747 toSecond
= toFirst
->h_sibling
.cqe_next
;
1748 simple_unlock(&to_hp
->h_meta
->h_siblinglock
);
1750 if (toFirst
== to_hp
) {
1751 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1752 toSecond
= NULL
; /* its going away */
1754 VOP_UNLOCK(HTOV(to_hp
), 0, ap
->a_p
);
1755 if (vget(HTOV(toFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1756 toFirst
= NULL
; /* its going away */
1757 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1758 toSecond
= NULL
; /* its going away */
1766 /* Ignore any errors, we are doing a 'best effort' on flushing */
1768 (void) vinvalbuf(HTOV(fromFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1770 (void) vinvalbuf(HTOV(fromSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1772 (void) vinvalbuf(HTOV(toFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1774 (void) vinvalbuf(HTOV(toSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1777 /* lock catalog b-tree */
1778 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1779 if (retval
) goto Err_Exit
;
1781 /* lock extents b-tree iff there are overflow extents */
1782 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1783 so we neeed the tree to be always locked.
1785 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1786 if (retval
) goto Err_Exit_Relse
;
1788 /* Do the exchange */
1789 retval
= MacToVFSError( ExchangeFileIDs(vcb
, H_NAME(from_hp
), H_NAME(to_hp
), H_DIRID(from_hp
), H_DIRID(to_hp
), H_HINT(from_hp
), H_HINT(to_hp
) ));
1791 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
1793 if (retval
!= E_NONE
) {
1794 DBG_ERR(("/tError trying to exchange: %d\n", retval
));
1795 goto Err_Exit_Relse
;
1799 /* Purge the vnodes from the name */
1801 cache_purge(HTOV(fromFirst
));
1803 cache_purge(HTOV(fromSecond
));
1805 cache_purge(HTOV(toFirst
));
1807 cache_purge(HTOV(toSecond
));
1809 /* Now exchange fileID, parID, name for the vnode itself */
1810 copystr(H_NAME(from_hp
), (char*) tmp_name
, strlen(H_NAME(from_hp
))+1, NULL
);
1811 hfs_chid(from_hp
, toFileID
, H_DIRID(to_hp
), H_NAME(to_hp
));
1812 hfs_chid(to_hp
, fromFileID
, fromParID
, (char*) tmp_name
);
1815 tmpLong
= HTOFCB(from_hp
)->fcbFlags
;
1816 HTOFCB(from_hp
)->fcbFlags
= HTOFCB(to_hp
)->fcbFlags
;
1817 HTOFCB(to_hp
)->fcbFlags
= tmpLong
;
1819 tmpLong
= from_hp
->h_meta
->h_crtime
;
1820 from_hp
->h_meta
->h_crtime
= to_hp
->h_meta
->h_crtime
;
1821 to_hp
->h_meta
->h_crtime
= tmpLong
;
1823 tmpLong
= from_hp
->h_meta
->h_butime
;
1824 from_hp
->h_meta
->h_butime
= to_hp
->h_meta
->h_butime
;
1825 to_hp
->h_meta
->h_butime
= tmpLong
;
1827 tmpLong
= from_hp
->h_meta
->h_atime
;
1828 from_hp
->h_meta
->h_atime
= to_hp
->h_meta
->h_atime
;
1829 to_hp
->h_meta
->h_atime
= tmpLong
;
1831 tmpLong
= from_hp
->h_meta
->h_ctime
;
1832 from_hp
->h_meta
->h_ctime
= to_hp
->h_meta
->h_ctime
;
1833 to_hp
->h_meta
->h_ctime
= tmpLong
;
1835 tmpLong
= from_hp
->h_meta
->h_gid
;
1836 from_hp
->h_meta
->h_gid
= to_hp
->h_meta
->h_gid
;
1837 to_hp
->h_meta
->h_gid
= tmpLong
;
1839 tmpLong
= from_hp
->h_meta
->h_uid
;
1840 from_hp
->h_meta
->h_uid
= to_hp
->h_meta
->h_uid
;
1841 to_hp
->h_meta
->h_uid
= tmpLong
;
1843 tmpLong
= from_hp
->h_meta
->h_pflags
;
1844 from_hp
->h_meta
->h_pflags
= to_hp
->h_meta
->h_pflags
;
1845 to_hp
->h_meta
->h_pflags
= tmpLong
;
1847 tmpLong
= from_hp
->h_meta
->h_mode
;
1848 from_hp
->h_meta
->h_mode
= to_hp
->h_meta
->h_mode
;
1849 to_hp
->h_meta
->h_mode
= tmpLong
;
1851 tmpLong
= from_hp
->h_meta
->h_rdev
;
1852 from_hp
->h_meta
->h_rdev
= to_hp
->h_meta
->h_rdev
;
1853 to_hp
->h_meta
->h_rdev
= tmpLong
;
1855 tmpLong
= from_hp
->h_meta
->h_size
;
1856 from_hp
->h_meta
->h_size
= to_hp
->h_meta
->h_size
;
1857 to_hp
->h_meta
->h_size
= tmpLong
;
1863 /* unlock catalog b-tree */
1864 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1871 * At this point, the vnodes' data is switched, but are on the old hash list.
1872 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1875 * A fork is trying to be added while exchanging...It got the hash lock,
1876 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1877 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1878 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1879 * Not very pretty, becarefull that this works ok
1881 * Same as the above, but before the move is made (like at this very spot), the new vnode
1882 * is added and a vget is requested for that new vnode, it would have old data
1883 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1885 * Hey! Same as above, but it is added after all the moving
1886 * So now there is a vnode with the old data, on the old hash...it will become
1887 * lost next time that a vget() is done
1889 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1890 * opposite that we are doing now
1892 hfs_vhashmove(from_hp
, fromFileID
);
1893 hfs_vhashmove(to_hp
, toFileID
);
1898 debug_check_vnode(HTOV(fromFirst
), 0);
1900 debug_check_vnode(HTOV(fromSecond
), 0);
1902 debug_check_vnode(HTOV(toFirst
), 0);
1904 debug_check_vnode(HTOV(toSecond
), 0);
1908 /* Unlock any forks, and the sibling list */
1909 if (to_hp
->h_meta
->h_usecount
> 1) {
1910 if (to_hp
== toFirst
) {
1912 vput(HTOV(toSecond
));
1915 vrele(HTOV(toSecond
)); /* decrement, return it locked */
1917 vput(HTOV(toFirst
));
1920 if (from_hp
->h_meta
->h_usecount
> 1) {
1921 if (from_hp
== fromFirst
) {
1923 vput(HTOV(fromSecond
));
1926 vrele(HTOV(fromSecond
)); /* decrement, return it locked */
1928 vput(HTOV(fromFirst
));
1932 DBG_VOP_LOCKS_TEST(retval
);
1938 * Change a vnode's file id, parent id and name
1940 * Assumes the vnode is locked and is of type VREG
1943 hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
)
1945 DBG_ASSERT(HTOV(hp
)->v_type
== VREG
);
1948 H_FILEID(hp
) = fid
; /* change h_nodeID */
1951 hfs_set_metaname(name
, hp
->h_meta
, HTOHFS(hp
));
1962 IN struct vnode *vp;
1963 IN struct ucred *cred;
1972 struct vop_fsync_args
/* {
1974 struct ucred *a_cred;
1979 struct vnode
*vp
= ap
->a_vp
;
1980 struct hfsnode
*hp
= VTOH(vp
);
1982 register struct buf
*bp
;
1987 DBG_FUNC_NAME("fsync");
1988 DBG_VOP_LOCKS_DECL(1);
1989 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
1990 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
1991 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
1992 DBG_HFS_NODE_CHECK(ap
->a_vp
);
1995 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2000 * First of all, write out any clusters.
2005 * Flush all dirty buffers associated with a vnode.
2009 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2010 nbp
= bp
->b_vnbufs
.le_next
;
2011 if ((bp
->b_flags
& B_BUSY
))
2013 if ((bp
->b_flags
& B_DELWRI
) == 0)
2014 panic("hfs_fsync: not dirty");
2016 bp
->b_flags
|= B_BUSY
;
2017 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2020 * Wait for I/O associated with indirect blocks to complete,
2021 * since there is no way to quickly wait for them below.
2023 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp
->b_blkno
, bp
->b_lblkno
));
2024 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
) {
2027 (void) VOP_BWRITE(bp
);
2031 if (vp
->v_flag
& VHASDIRTY
)
2034 if (ap
->a_waitfor
== MNT_WAIT
) {
2035 while (vp
->v_numoutput
) {
2036 vp
->v_flag
|= VBWAIT
;
2037 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
2040 /* I have seen this happen for swapfile. So it is safer to
2041 * check for dirty buffers again. --Umesh
2043 if (vp
->v_dirtyblkhd
.lh_first
|| (vp
->v_flag
& VHASDIRTY
)) {
2044 vprint("hfs_fsync: dirty", vp
);
2052 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2056 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2057 BTSetLastSync(HTOFCB(hp
), tv
.tv_sec
);
2059 if (H_FORKTYPE(hp
) != kSysFile
) {
2060 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, ap
->a_waitfor
== MNT_WAIT
);
2062 if (retval
!= E_NONE
) {
2063 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname
, H_NAME(hp
)));
2067 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2069 if (ap
->a_waitfor
== MNT_WAIT
) {
2070 DBG_ASSERT(vp
->v_dirtyblkhd
.lh_first
== NULL
);
2072 DBG_VOP_LOCKS_TEST(retval
);
2073 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2079 hfs_fsync_transaction(struct vnode
*vp
)
2081 struct hfsnode
*hp
= VTOH(vp
);
2082 register struct buf
*bp
;
2088 * Flush all dirty buffers associated with a vnode.
2093 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2094 nbp
= bp
->b_vnbufs
.le_next
;
2095 if ((bp
->b_flags
& B_BUSY
))
2097 if ((bp
->b_flags
& B_DELWRI
) == 0)
2098 panic("hfs_fsync: not dirty");
2099 if ( !(bp
->b_flags
& B_LOCKED
))
2103 bp
->b_flags
|= B_BUSY
;
2104 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2114 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2115 (void) BTSetLastSync(VTOFCB(vp
), tv
.tv_sec
);
2116 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2127 IN WILLRELE struct vnode *dvp;
2128 IN WILLRELE struct vnode *vp;
2129 IN struct componentname *cnp;
2135 struct vop_remove_args
/* {
2136 struct vnode *a_dvp;
2138 struct componentname *a_cnp;
2141 struct vnode
*vp
= ap
->a_vp
;
2142 struct vnode
*dvp
= ap
->a_dvp
;
2143 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
2144 struct hfsmount
*hfsmp
= HTOHFS(hp
);
2145 struct proc
*p
= current_proc();
2147 int retval
, use_count
;
2149 DBG_FUNC_NAME("remove");
2150 DBG_VOP_LOCKS_DECL(2);
2151 DBG_VOP_PRINT_FUNCNAME();
2152 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
2153 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2154 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2155 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2159 if ((hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2160 (VTOH(dvp
)->h_meta
->h_pflags
& APPEND
)) {
2165 if (vp
->v_usecount
> 1) {
2167 * the namei done for the rename took a reference on the
2168 * vnode. Hence set 1 in the tookref parameter
2171 if(UBCISVALID(vp
) && !ubc_isinuse(vp
, 1))
2173 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2174 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2175 /* Carbon semantics prohibits deleting busy files */
2184 tv
= time
; /* Done here, so all times are the same */
2186 /* Check other siblings for in use also */
2187 /* Uncache everything and make sure no other usecount */
2189 * This assumes the presence of the most 1 sibling
2191 * a. loop through the siblings looking for another
2192 * b. If we find ourselves...skip it
2193 * If there was a sibling:
2194 * a. Check for a positve usecount
2195 * b. uncache any pages
2196 * c. Write out and memory changes
2197 * The idea is to keep the h_siblinglock as little as possible
2199 if (hp
->h_meta
->h_usecount
> 1) {
2200 struct vnode
*sib_vp
= NULL
;
2201 struct hfsnode
*nhp
;
2203 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
2204 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
2205 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
2207 /* Loop through all siblings, skipping ourselves */
2208 simple_lock(&hp
->h_meta
->h_siblinglock
);
2209 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
2210 if (nhp
== hp
) /* skip ourselves */
2214 simple_unlock(&hp
->h_meta
->h_siblinglock
);
2216 /* Check to see if the other fork is in use */
2217 DBG_ASSERT(sib_vp
!= NULL
);
2218 simple_lock(&sib_vp
->v_interlock
);
2219 use_count
= sib_vp
->v_usecount
;
2220 simple_unlock(&sib_vp
->v_interlock
);
2221 if (use_count
> 0) {
2223 * This is a sibling vnode and we did not take
2224 * a reference on it.
2225 * Hence set 0 in the tookref parameter
2228 if(UBCISVALID(sib_vp
) && !ubc_isinuse(sib_vp
, 0))
2230 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2231 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2232 /* Carbon semantics prohibits deleting busy files */
2237 } /* use_count > 0 */
2241 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2242 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
2244 * XXX SER An intelligient person would ask, why flush out changes
2245 * that are going to be deleted? See the next comment.
2247 if ((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) || (VTOFCB(sib_vp
)->fcbFlags
2248 & fcbModifiedMask
)) {
2249 DBG_ASSERT((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) != 0);
2250 VOP_UPDATE(sib_vp
, &tv
, &tv
, 0);
2253 /* Invalidate the buffers, ignore the results */
2254 (void) vinvalbuf(sib_vp
, 0, NOCRED
, p
, 0, 0);
2258 }; /* h_use_count > 1 */
2261 * remove the entry from the namei cache:
2262 * We do it early before any linking/busy file wierdness, make sure the
2267 /* Flush out any catalog changes */
2268 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2269 * and not from memory which is more correct
2271 if ((hp
->h_nodeflags
& IN_MODIFIED
) || (HTOFCB(hp
)->fcbFlags
& fcbModifiedMask
))
2273 DBG_ASSERT((hp
->h_nodeflags
& IN_MODIFIED
) != 0);
2274 VOP_UPDATE(vp
, &tv
, &tv
, 0);
2277 /* lock catalog b-tree */
2278 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2279 if (retval
!= E_NONE
) {
2285 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2290 * Multi-linked files just need their link node deleted from the catalog
2292 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
2294 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
2295 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
2296 retval
= ENOENT
; /* name missing */
2300 /* lock extents b-tree (also protects volume bitmap) */
2301 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2302 if (retval
!= E_NONE
) {
2304 goto out2
; /* unlock catalog b-tree on the way out */
2307 retval
= hfsDelete (HTOVCB(hp
), H_FILEID(VTOH(dvp
)),
2308 ap
->a_cnp
->cn_nameptr
, TRUE
, H_HINT(hp
));
2310 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2315 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2316 hp
->h_nodeflags
|= IN_CHANGE
;
2317 if (--hp
->h_meta
->h_nlink
< 1)
2318 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2320 /* name and parent fields are no longer valid so invalidate them */
2321 H_DIRID(hp
) = kUnknownID
;
2322 hfs_set_metaname("\0", hp
->h_meta
, HTOHFS(hp
));
2324 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
2325 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2327 goto out2
; /* link deleted, all done */
2332 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2333 * an active vnode is translated to a move/rename so the file appears
2334 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2337 UInt32 hint
= H_HINT(hp
);
2340 MAKE_DELETED_NAME(nodeName
, H_FILEID(hp
));
2342 retval
= hfsMoveRename (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
),
2343 hfsmp
->hfs_private_metadata_dir
, nodeName
, &hint
);
2344 if (retval
) goto out2
;
2346 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2347 hp
->h_nodeflags
|= IN_CHANGE
;
2349 /* update name so Catalog lookups succeed */
2351 H_DIRID(hp
) = hfsmp
->hfs_private_metadata_dir
;
2352 hfs_set_metaname(nodeName
, hp
->h_meta
, HTOHFS(hp
));
2354 goto out2
; /* all done, unlock the catalog */
2357 /* unlock the Catalog */
2358 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2360 /* Invalidate the buffers */
2361 if ((retval
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0)))
2364 if(UBCINFOEXISTS(vp
))
2365 (void)ubc_setsize(vp
, (off_t
)0);
2368 /* lock catalog b-tree */
2369 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2370 if (retval
!= E_NONE
) {
2374 /* lock extents b-tree (also protects volume bitmap) */
2375 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2376 if (retval
!= E_NONE
) {
2378 goto out2
; /* unlock catalog b-tree on the way out */
2381 /* remove entry from catalog and free any blocks used */
2382 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
2385 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2386 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2391 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2392 hp
->h_meta
->h_mode
= 0; /* Makes the node go away...see inactive */
2393 /* clear the block mappings */
2394 hp
->fcbPLen
= (u_int64_t
)0;
2395 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
2397 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2406 DBG_VOP_LOCKS_TEST(retval
);
2408 if (UBCINFOEXISTS(vp
)) {
2409 (void) ubc_uncache(vp
);
2411 /* WARNING vp may not be valid after this */
2416 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2421 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2430 DBG_VOP_LOCKS_TEST(retval
);
2437 #% rename sourcePar_vp U U U
2438 #% rename source_vp U U U
2439 #% rename targetPar_vp L U U
2440 #% rename target_vp X U U
2443 IN WILLRELE struct vnode *sourcePar_vp;
2444 IN WILLRELE struct vnode *source_vp;
2445 IN struct componentname *source_cnp;
2446 IN WILLRELE struct vnode *targetPar_vp;
2447 IN WILLRELE struct vnode *target_vp;
2448 IN struct componentname *target_cnp;
2454 * source's parent directory is unlocked
2455 * source file or directory is unlocked
2456 * destination's parent directory is locked
2457 * destination file or directory is locked if it exists
2460 * all denodes should be released
2466 struct vop_rename_args
/* {
2467 struct vnode *a_fdvp;
2468 struct vnode *a_fvp;
2469 struct componentname *a_fcnp;
2470 struct vnode *a_tdvp;
2471 struct vnode *a_tvp;
2472 struct componentname *a_tcnp;
2475 struct vnode
*target_vp
= ap
->a_tvp
;
2476 struct vnode
*targetPar_vp
= ap
->a_tdvp
;
2477 struct vnode
*source_vp
= ap
->a_fvp
;
2478 struct vnode
*sourcePar_vp
= ap
->a_fdvp
;
2479 struct componentname
*target_cnp
= ap
->a_tcnp
;
2480 struct componentname
*source_cnp
= ap
->a_fcnp
;
2481 struct proc
*p
= source_cnp
->cn_proc
;
2482 struct hfsnode
*target_hp
, *targetPar_hp
, *source_hp
, *sourcePar_hp
;
2483 u_int32_t oldparent
= 0, newparent
= 0;
2484 int doingdirectory
= 0;
2487 struct hfsCatalogInfo catInfo
;
2488 DBG_VOP_LOCKS_DECL(4);
2490 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2491 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fvp
);DBG_VOP_CONT(("\n"));
2492 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fdvp
);DBG_VOP_CONT(("\n"));
2493 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tvp
);DBG_VOP_CONT(("\n"));
2494 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tdvp
);DBG_VOP_CONT(("\n"));
2495 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_fcnp
);DBG_VOP_CONT(("\n"));
2496 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_tcnp
);DBG_VOP_CONT(("\n"));
2497 DBG_VOP_LOCKS_INIT(0,ap
->a_fdvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2498 DBG_VOP_LOCKS_INIT(1,ap
->a_fvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2499 DBG_VOP_LOCKS_INIT(2,ap
->a_tdvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2500 DBG_VOP_LOCKS_INIT(3,ap
->a_tvp
, VOPDBG_LOCKNOTNIL
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2501 WRITE_CK(ap
->a_fdvp
, funcname
);
2502 DBG_HFS_NODE_CHECK(ap
->a_fdvp
);
2503 DBG_HFS_NODE_CHECK(ap
->a_tdvp
);
2506 if ((target_cnp
->cn_flags
& HASBUF
) == 0 ||
2507 (source_cnp
->cn_flags
& HASBUF
) == 0)
2508 panic("hfs_rename: no name");
2511 DBG_ASSERT((ap
->a_fdvp
->v_type
== VDIR
) && (ap
->a_tdvp
->v_type
== VDIR
));
2512 target_hp
= targetPar_hp
= source_hp
= sourcePar_hp
= 0;
2515 * Check for cross-device rename.
2517 if ((source_vp
->v_mount
!= targetPar_vp
->v_mount
) ||
2518 (target_vp
&& (source_vp
->v_mount
!= target_vp
->v_mount
))) {
2524 * Check for access permissions
2526 if (target_vp
&& ((VTOH(target_vp
)->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2527 (VTOH(targetPar_vp
)->h_meta
->h_pflags
& APPEND
))) {
2532 if ((retval
= vn_lock(source_vp
, LK_EXCLUSIVE
, p
)))
2535 sourcePar_hp
= VTOH(sourcePar_vp
);
2536 source_hp
= VTOH(source_vp
);
2537 oldparent
= H_FILEID(sourcePar_hp
);
2538 if ((source_hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) || (sourcePar_hp
->h_meta
->h_pflags
& APPEND
)) {
2539 VOP_UNLOCK(source_vp
, 0, p
);
2545 * Be sure we are not renaming ".", "..", or an alias of ".". This
2546 * leads to a crippled directory tree. It's pretty tough to do a
2547 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2548 * doesn't work if the ".." entry is missing.
2550 if ((source_hp
->h_meta
->h_mode
& IFMT
) == IFDIR
) {
2551 if ((source_cnp
->cn_namelen
== 1 && source_cnp
->cn_nameptr
[0] == '.')
2552 || sourcePar_hp
== source_hp
2553 || (source_cnp
->cn_flags
&ISDOTDOT
)
2554 || (source_hp
->h_nodeflags
& IN_RENAME
)) {
2555 VOP_UNLOCK(source_vp
, 0, p
);
2559 source_hp
->h_nodeflags
|= IN_RENAME
;
2560 doingdirectory
= TRUE
;
2565 * >>>> Transit between abort and bad <<<<
2569 targetPar_hp
= VTOH(targetPar_vp
);
2571 target_hp
= VTOH(target_vp
);
2573 DBG_ASSERT(target_hp
== NULL
);
2575 newparent
= H_FILEID(targetPar_hp
);
2577 /* Test to make sure we are not crossing devices */
2578 /* XXX SER Is this necesary, does catalog manager take care of this? */
2580 if (H_DEV(target_hp
) != H_DEV(targetPar_hp
) || H_DEV(target_hp
) != H_DEV(source_hp
))
2581 panic("rename: EXDEV");
2584 if (H_DEV(targetPar_hp
) != H_DEV(source_hp
))
2585 panic("rename: EXDEV");
2588 retval
= VOP_ACCESS(source_vp
, VWRITE
, target_cnp
->cn_cred
, target_cnp
->cn_proc
);
2589 if (doingdirectory
&& (newparent
!= oldparent
)) {
2590 if (retval
) /* write access check above */
2593 retval
= 0; /* Reset value from above, we dont care about it anymore */
2596 * If the destination exists, then be sure its type (file or dir)
2597 * matches that of the source. And, if it is a directory make sure
2598 * it is empty. Then delete the destination.
2603 * If the parent directory is "sticky", then the user must
2604 * own the parent directory, or the destination of the rename,
2605 * otherwise the destination may not be changed (except by
2606 * root). This implements append-only directories.
2608 if ((targetPar_hp
->h_meta
->h_mode
& S_ISTXT
) && (target_cnp
->cn_cred
->cr_uid
!= 0) &&
2609 target_cnp
->cn_cred
->cr_uid
!= targetPar_hp
->h_meta
->h_uid
&&
2610 target_cnp
->cn_cred
->cr_uid
!= target_hp
->h_meta
->h_uid
) {
2616 * VOP_REMOVE will vput targetPar_vp so we better bump
2617 * its ref count and relockit, always set target_vp to
2618 * NULL afterwards to indicate that were done with it.
2622 cache_purge(target_vp
);
2625 target_cnp
->cn_flags
&= ~SAVENAME
;
2628 retval
= VOP_REMOVE(targetPar_vp
, target_vp
, target_cnp
);
2629 (void) vn_lock(targetPar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2634 if (retval
) goto bad
;
2639 if (newparent
!= oldparent
)
2640 vn_lock(sourcePar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2642 /* remove the existing entry from the namei cache: */
2643 cache_purge(source_vp
);
2645 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
2647 /* lock catalog b-tree */
2648 retval
= hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2650 if (newparent
!= oldparent
) /* unlock the lock we just got */
2651 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2655 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2656 retval
= hfsMoveRename( HTOVCB(source_hp
), H_DIRID(source_hp
), source_cnp
->cn_nameptr
,
2657 H_FILEID(VTOH(targetPar_vp
)), target_cnp
->cn_nameptr
, &H_HINT(source_hp
));
2660 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2661 catInfo
.hint
= H_HINT(source_hp
);
2662 retval
= hfs_getcatalog(HTOVCB(source_hp
), H_FILEID(targetPar_hp
), target_cnp
->cn_nameptr
, target_cnp
->cn_namelen
, &catInfo
);
2665 /* unlock catalog b-tree */
2666 (void) hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2668 if (newparent
!= oldparent
)
2669 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2671 if (retval
) goto bad
;
2673 H_DIRID(source_hp
) = H_FILEID(targetPar_hp
);
2675 hfs_name_CatToMeta(&catInfo
.nodeData
, source_hp
->h_meta
);
2677 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
2679 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2683 * Timestamp both parent directories.
2684 * Note that if this is a rename within the same directory,
2685 * (where targetPar_hp == sourcePar_hp)
2686 * the code below is still safe and correct.
2688 targetPar_hp
->h_nodeflags
|= IN_UPDATE
;
2689 sourcePar_hp
->h_nodeflags
|= IN_UPDATE
;
2691 HFSTIMES(targetPar_hp
, &tv
, &tv
);
2692 HFSTIMES(sourcePar_hp
, &tv
, &tv
);
2695 vrele(sourcePar_vp
);
2698 DBG_VOP_LOCKS_TEST(retval
);
2699 if (retval
!= E_NONE
) {
2700 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2705 if (retval
&& doingdirectory
)
2706 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2708 if (targetPar_vp
== target_vp
)
2709 vrele(targetPar_vp
);
2716 vrele(sourcePar_vp
);
2718 if (VOP_ISLOCKED(source_vp
))
2723 DBG_VOP_LOCKS_TEST(retval
);
2724 if (retval
!= E_NONE
) {
2725 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2731 VOP_ABORTOP(targetPar_vp
, target_cnp
); /* XXX, why not in NFS? */
2733 if (targetPar_vp
== target_vp
)
2734 vrele(targetPar_vp
);
2741 VOP_ABORTOP(sourcePar_vp
, source_cnp
); /* XXX, why not in NFS? */
2743 vrele(sourcePar_vp
);
2746 DBG_VOP_LOCKS_TEST(retval
);
2747 if (retval
!= E_NONE
) {
2748 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2761 IN WILLRELE struct vnode *dvp;
2762 OUT struct vnode **vpp;
2763 IN struct componentname *cnp;
2764 IN struct vattr *vap;
2766 We are responsible for freeing the namei buffer,
2767 it is done in hfs_makenode()
2772 struct vop_mkdir_args
/* {
2773 struct vnode *a_dvp;
2774 struct vnode **a_vpp;
2775 struct componentname *a_cnp;
2776 struct vattr *a_vap;
2779 struct proc
*p
= current_proc();
2781 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
2783 DBG_FUNC_NAME("mkdir");
2784 DBG_VOP_LOCKS_DECL(2);
2785 DBG_VOP_PRINT_FUNCNAME();
2786 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
2787 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2789 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2790 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2792 DBG_VOP(("%s: parent 0x%x (%s) ap->a_cnp->cn_nameptr %s\n", funcname
, (u_int
)VTOH(ap
->a_dvp
), H_NAME(VTOH(ap
->a_dvp
)), ap
->a_cnp
->cn_nameptr
));
2793 WRITE_CK( ap
->a_dvp
, funcname
);
2794 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
2795 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
2797 /* Create the vnode */
2798 DBG_ASSERT((ap
->a_cnp
->cn_flags
& SAVESTART
) == 0);
2799 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
2800 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2802 if (retval
!= E_NONE
) {
2803 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
2804 DBG_VOP_LOCKS_TEST(retval
);
2808 DBG_VOP_LOCKS_TEST(E_NONE
);
2813 * Rmdir system call.
2818 IN WILLRELE struct vnode *dvp;
2819 IN WILLRELE struct vnode *vp;
2820 IN struct componentname *cnp;
2826 struct vop_rmdir_args
/* {
2827 struct vnode *a_dvp;
2829 struct componentname *a_cnp;
2832 struct vnode
*vp
= ap
->a_vp
;
2833 struct vnode
*dvp
= ap
->a_dvp
;
2834 struct hfsnode
*hp
= VTOH(vp
);
2835 struct proc
*p
= current_proc();
2837 DBG_FUNC_NAME("rmdir");
2838 DBG_VOP_LOCKS_DECL(2);
2839 DBG_VOP_PRINT_FUNCNAME();
2840 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);DBG_VOP_CONT(("\n"));
2841 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2842 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2844 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2845 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2850 DBG_VOP_LOCKS_TEST(EINVAL
);
2855 * HFS differs from UFS here in that we don't allow removing
2856 * a directory that in use by others - even if its empty.
2858 * In the future we might want to allow this just like we do
2859 * for files (by renaming the busy directory).
2862 if (vp
->v_usecount
> 1) {
2863 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname
, vp
->v_usecount
));
2868 /* remove the entry from the namei cache: */
2871 /* lock catalog b-tree */
2872 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2873 if (retval
!= E_NONE
) {
2877 /* remove entry from catalog */
2878 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), FALSE
, H_HINT(hp
));
2880 /* unlock catalog b-tree */
2881 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2884 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
; /* Set the parent to be updated */
2885 hp
->h_meta
->h_mode
= 0; /* Makes the vnode go away...see inactive */
2886 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2894 DBG_VOP_LOCKS_TEST(retval
);
2899 * symlink -- make a symbolic link
2900 #% symlink dvp L U U
2901 #% symlink vpp - U -
2903 # XXX - note that the return vnode has already been VRELE'ed
2904 # by the filesystem layer. To use it you must use vget,
2905 # possibly with a further namei.
2908 IN WILLRELE struct vnode *dvp;
2909 OUT WILLRELE struct vnode **vpp;
2910 IN struct componentname *cnp;
2911 IN struct vattr *vap;
2914 We are responsible for freeing the namei buffer,
2915 it is done in hfs_makenode().
2921 struct vop_symlink_args
/* {
2922 struct vnode *a_dvp;
2923 struct vnode **a_vpp;
2924 struct componentname *a_cnp;
2925 struct vattr *a_vap;
2929 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
2930 struct proc
*p
= current_proc();
2932 u_int32_t dfltClump
;
2934 DBG_FUNC_NAME("symlink");
2935 DBG_VOP_LOCKS_DECL(2);
2936 DBG_VOP_PRINT_FUNCNAME();
2937 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2938 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2940 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
2941 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2943 DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
2944 DBG_VOP_LOCKS_TEST(EOPNOTSUPP
);
2945 return (EOPNOTSUPP
);
2948 /* Create the vnode */
2949 retval
= hfs_makenode(IFLNK
| ap
->a_vap
->va_mode
, 0, ap
->a_dvp
,
2951 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2953 if (retval
!= E_NONE
) {
2954 DBG_VOP_LOCKS_TEST(retval
);
2960 len
= strlen(ap
->a_target
);
2962 dfltClump
= hp
->fcbClmpSize
;
2963 /* make clump size minimal */
2964 hp
->fcbClmpSize
= VTOVCB(vp
)->blockSize
;
2965 retval
= vn_rdwr(UIO_WRITE
, vp
, ap
->a_target
, len
, (off_t
)0,
2966 UIO_SYSSPACE
, IO_NODELOCKED
, ap
->a_cnp
->cn_cred
, (int *)0,
2968 hp
->fcbClmpSize
= dfltClump
;
2972 DBG_VOP_LOCKS_TEST(retval
);
2978 * Dummy dirents to simulate the "." and ".." entries of the directory
2979 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2980 * the size of these entries is the smallest needed to represent them
2981 * (only 12 byte each).
2983 static hfsdotentry rootdots
[2] = {
2986 sizeof(struct hfsdotentry
), /* d_reclen */
2987 DT_DIR
, /* d_type */
2993 sizeof(struct hfsdotentry
), /* d_reclen */
2994 DT_DIR
, /* d_type */
3000 static hfsdotentry emptyentry
= { 0 };
3003 * There is some confusion as to what the semantics of uio_offset are.
3004 * In ufs, it represents the actual byte offset within the directory
3005 * "file." HFS, however, just uses it as an entry counter - essentially
3006 * assuming that it has no meaning except to the hfs_readdir function.
3007 * This approach would be more efficient here, but some callers may
3008 * assume the uio_offset acts like a byte offset. NFS in fact
3009 * monkeys around with the offset field a lot between readdir calls.
3011 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3012 * fields is a mess as well. The libc function readdir() returns
3013 * NULL (indicating the end of a directory) when either
3014 * the getdirentries() syscall (which calls this and returns
3015 * the size of the buffer passed in less the value of uiop->uio_resid)
3016 * returns 0, or a direct record with a d_reclen of zero.
3017 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3018 * of the directory by testing uiop->uio_resid == 0. The solution
3019 * is to pad the size of the last struct direct in a given
3020 * block to fill the block if we are not at the end of the directory.
3023 struct callbackstate
{
3024 u_int32_t cbs_parentID
;
3025 u_int32_t cbs_hiddenDirID
;
3026 off_t cbs_lastoffset
;
3027 struct uio
* cbs_uio
;
3028 ExtendedVCB
* cbs_vcb
;
3029 int16_t cbs_hfsPlus
;
3035 ProcessCatalogEntry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3036 u_int16_t recordLen
, struct callbackstate
*state
)
3042 struct dirent catent
;
3044 if (state
->cbs_hfsPlus
)
3045 curID
= ckp
->hfsPlus
.parentID
;
3047 curID
= ckp
->hfs
.parentID
;
3049 /* We're done when parent directory changes */
3050 if (state
->cbs_parentID
!= curID
) {
3053 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3054 * so remove padding for now...
3058 * Pad the end of list with an empty record.
3059 * This eliminates an extra call by readdir(3c).
3061 catent
.d_fileno
= 0;
3062 catent
.d_reclen
= 0;
3064 catent
.d_namlen
= 0;
3065 *(int32_t*)&catent
.d_name
[0] = 0;
3067 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3069 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
3070 if (state
->cbs_result
== 0)
3071 state
->cbs_result
= ENOENT
;
3073 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3074 state
->cbs_result
= ENOENT
;
3076 return (0); /* stop */
3079 if (state
->cbs_hfsPlus
) {
3080 switch(crp
->recordType
) {
3081 case kHFSPlusFolderRecord
:
3082 catent
.d_type
= DT_DIR
;
3083 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
3085 case kHFSPlusFileRecord
:
3086 catent
.d_type
= DT_REG
;
3087 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
3090 return (0); /* stop */
3093 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3094 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
3095 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
3096 if (result
== ENAMETOOLONG
) {
3097 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3098 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
3101 switch(crp
->recordType
) {
3102 case kHFSFolderRecord
:
3103 catent
.d_type
= DT_DIR
;
3104 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
3106 case kHFSFileRecord
:
3107 catent
.d_type
= DT_REG
;
3108 catent
.d_fileno
= crp
->hfsFile
.fileID
;
3111 return (0); /* stop */
3114 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
3115 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3116 (ByteCount
*)&utf8chars
, catent
.d_name
);
3118 * When an HFS name cannot be encoded with the current
3119 * volume encoding we use MacRoman as a fallback.
3122 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3123 (ByteCount
*)&utf8chars
, catent
.d_name
);
3126 catent
.d_namlen
= utf8chars
;
3127 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
3129 /* hide our private meta data directory */
3130 if (curID
== kRootDirID
&&
3131 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
3132 catent
.d_type
== DT_DIR
)
3135 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3137 /* if this entry won't fit then we're done */
3138 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
)
3139 return (0); /* stop */
3141 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
3143 /* continue iteration if there's room */
3144 return (state
->cbs_result
== 0 &&
3145 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
3149 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3150 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3151 * "Nbytes must be greater than or equal to the block size associated with the file,
3152 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3153 * room for the . and .. entries that have to added manually.
3160 IN struct vnode *vp;
3161 INOUT struct uio *uio;
3162 IN struct ucred *cred;
3165 INOUT u_long **cookies;
3169 struct vop_readdir_args
/* {
3178 register struct uio
*uio
= ap
->a_uio
;
3179 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3180 struct proc
*p
= current_proc();
3181 ExtendedVCB
*vcb
= HTOVCB(hp
);
3182 off_t off
= uio
->uio_offset
;
3183 u_int32_t dirID
= H_FILEID(hp
);
3185 OSErr result
= noErr
;
3186 u_int32_t diroffset
;
3188 CatalogIterator
*cip
;
3190 struct callbackstate state
;
3193 DBG_FUNC_NAME("readdir");
3194 DBG_VOP_LOCKS_DECL(1);
3196 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3197 DBG_VOP_PRINT_FUNCNAME();
3198 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3199 DBG_HFS_NODE_CHECK(ap
->a_vp
);
3201 /* We assume it's all one big buffer... */
3202 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
) {
3206 /* Create the entries for . and .. */
3207 if (uio
->uio_offset
< sizeof(rootdots
)) {
3211 rootdots
[0].d_fileno
= dirID
;
3212 rootdots
[1].d_fileno
= H_DIRID(hp
);
3214 if (uio
->uio_offset
== 0) {
3215 dep
= (caddr_t
) &rootdots
[0];
3216 dotsize
= 2* sizeof(struct hfsdotentry
);
3217 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
3218 dep
= (caddr_t
) &rootdots
[1];
3219 dotsize
= sizeof(struct hfsdotentry
);
3225 retval
= uiomove(dep
, dotsize
, uio
);
3230 diroffset
= uio
->uio_offset
;
3232 /* lock catalog b-tree */
3233 retval
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
3234 if (retval
!= E_NONE
)
3237 /* get an iterator and position it */
3238 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
3240 result
= PositionIterator(cip
, diroffset
, &bi
, &op
);
3241 if (result
== cmNotFound
) {
3244 AgeCatalogIterator(cip
);
3246 } else if ((retval
= MacToVFSError(result
)))
3249 state
.cbs_hiddenDirID
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
3250 state
.cbs_lastoffset
= cip
->currentOffset
;
3251 state
.cbs_vcb
= vcb
;
3252 state
.cbs_uio
= uio
;
3253 state
.cbs_result
= 0;
3254 state
.cbs_parentID
= dirID
;
3256 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3257 state
.cbs_hfsPlus
= 1;
3259 state
.cbs_hfsPlus
= 0;
3261 /* process as many entries as possible... */
3262 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
, &bi
,
3263 (IterateCallBackProcPtr
)ProcessCatalogEntry
, &state
);
3265 if (state
.cbs_result
)
3266 retval
= state
.cbs_result
;
3268 retval
= MacToVFSError(result
);
3270 if (retval
== ENOENT
) {
3276 cip
->currentOffset
= state
.cbs_lastoffset
;
3277 cip
->nextOffset
= uio
->uio_offset
;
3278 UpdateCatalogIterator(&bi
, cip
);
3285 AgeCatalogIterator(cip
);
3288 (void) ReleaseCatalogIterator(cip
);
3290 /* unlock catalog b-tree */
3291 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3293 if (retval
!= E_NONE
) {
3294 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname
, retval
,
3295 H_FILEID(hp
), H_NAME(hp
)));
3300 /* were we already past eof ? */
3301 if (uio
->uio_offset
== off
) {
3306 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3307 hp
->h_nodeflags
|= IN_ACCESS
;
3309 /* Bake any cookies */
3310 if (!retval
&& ap
->a_ncookies
!= NULL
) {
3311 struct dirent
* dpStart
;
3312 struct dirent
* dpEnd
;
3319 * Only the NFS server uses cookies, and it loads the
3320 * directory block into system space, so we can just look at
3323 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
3324 panic("hfs_readdir: unexpected uio from NFS server");
3325 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
3326 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
3327 for (dp
= dpStart
, ncookies
= 0;
3328 dp
< dpEnd
&& dp
->d_reclen
!= 0;
3329 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
3331 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
3332 for (dp
= dpStart
, cookiep
= cookies
;
3334 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
3335 off
+= dp
->d_reclen
;
3336 *cookiep
++ = (u_long
) off
;
3338 *ap
->a_ncookies
= ncookies
;
3339 *ap
->a_cookies
= cookies
;
3345 *ap
->a_eofflag
= eofflag
;
3347 DBG_VOP_LOCKS_TEST(retval
);
3353 * readdirattr operation will return attributes for the items in the
3354 * directory specified.
3356 * It does not do . and .. entries. The problem is if you are at the root of the
3357 * hfs directory and go to .. you could be crossing a mountpoint into a
3358 * different (ufs) file system. The attributes that apply for it may not
3359 * apply for the file system you are doing the readdirattr on. To make life
3360 * simpler, this call will only return entries in its directory, hfs like.
3362 * 1.getattrlist creates a thread record if the objpermanentid attribute
3363 * is requested. Just do EINVAL for now and fix later.
3364 * 2. more than one for uiovcnt support.
3365 * 3. put knohint (hints) in state for next call in
3366 * 4. credentials checking when rest of hfs does it.
3367 * 5. Do return permissions concatenation ???
3372 #% readdirattr vp L L L
3375 IN struct vnode *vp;
3376 IN struct attrlist *alist;
3377 INOUT struct uio *uio;
3380 OUT u_long *newstate;
3382 OUT u_long *actualCount;
3383 OUT u_long **cookies;
3384 IN struct ucred *cred;
3389 struct vop_readdirattr_args
/* {
3391 struct attrlist *alist;
3397 u_long *actualcount;
3402 struct vnode
*vp
= ap
->a_vp
;
3403 struct attrlist
*alist
= ap
->a_alist
;
3404 register struct uio
*uio
= ap
->a_uio
;
3405 u_long maxcount
= ap
->a_maxcount
;
3407 ExtendedVCB
*vcb
= HTOVCB(VTOH(vp
));
3408 UInt32 dirID
= H_FILEID(VTOH(vp
));
3409 struct proc
*proc
= current_proc(); /* could get this out of uio */
3410 off_t startoffset
= uio
->uio_offset
;
3411 struct hfsCatalogInfo catInfo
;
3414 u_long fixedblocksize
;
3415 u_long maxattrblocksize
;
3416 u_long currattrbufsize
;
3417 void *attrbufptr
= NULL
;
3420 struct vnode
*entryvnode
;
3423 *(ap
->a_actualcount
) = 0;
3424 *(ap
->a_eofflag
) = 0;
3426 /* check for invalid options, check vnode, and buffer space */
3427 if (((ap
->a_options
& ~FSOPT_NOINMEMUPDATE
) != 0) ||
3429 (uio
->uio_resid
<= 0) || (uio
->uio_iovcnt
> 1))
3432 /* this call doesn't take volume attributes */
3433 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
3434 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
3435 (alist
->volattr
!= 0) ||
3436 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
3437 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
3438 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0))
3441 /* Reject requests for unsupported options for now: */
3442 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
3443 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
)) ||
3444 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
) )
3447 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3448 * uiomove afterwards. It's an extra copy, but for now leave it alone
3450 fixedblocksize
= (sizeof(u_long
) + AttributeBlockSize(alist
)); /* u_long for length */
3451 maxattrblocksize
= fixedblocksize
;
3452 if (alist
->commonattr
& ATTR_CMN_NAME
)
3453 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
3454 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
3455 attrptr
= attrbufptr
;
3456 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3458 /* Since attributes passed back can contain variable ones (name), we can't just use
3459 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3460 * as hfs_readdir was originally doing. If this all we need to represent the current
3461 * state, then ap->a_state is not needed at all.
3463 /* index = ap->a_state; should not be less than 1 */
3464 index
= (uio
->uio_offset
/ sizeof(struct dirent
)) + 1;
3465 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
3467 /* Lock catalog b-tree */
3468 if ((retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, proc
)) != E_NONE
)
3471 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3472 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3473 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3475 catInfo
.hint
= kNoHint
; /* note, we may want to save the latest in state */
3476 while ((uio
->uio_resid
>= 0) && (maxcount
!=0 )) {
3477 /* better to check uio_resid against max or fixedblocksize, but won't work.
3478 * Depending on if dir or file, the attributes returned will be different.
3479 * Thus fixedblocksize is too large in some cases.Also, the variable
3480 * part (like name) could be between fixedblocksize and the max.
3482 OSErr result
= GetCatalogOffspring(vcb
, dirID
, index
, &catInfo
.nodeData
, NULL
, NULL
);
3483 if (result
!= noErr
) {
3484 if (result
== cmNotFound
) {
3485 *(ap
->a_eofflag
) = TRUE
;
3488 else retval
= MacToVFSError(result
);
3492 /* hide our private meta data directory as does hfs_readdir */
3493 if ((dirID
== kRootDirID
) &&
3494 catInfo
.nodeData
.cnd_nodeID
== VCBTOHFS(vcb
)->hfs_private_metadata_dir
&&
3495 catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) {
3501 *((u_long
*)attrptr
)++ = 0; /* move it past length */
3503 if (ap
->a_options
& FSOPT_NOINMEMUPDATE
) {
3504 /* vp okay to use instead of root vp */
3505 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3507 /* Check to see if there's a vnode for this item in the cache: */
3508 entryvnode
= hfs_vhashget(H_DEV(VTOH(vp
)), catInfo
.nodeData
.cnd_nodeID
, kDefault
);
3509 if (entryvnode
!= NULL
) {
3510 PackAttributeBlock(alist
, entryvnode
, &catInfo
, &attrptr
, &varptr
);
3513 /* vp okay to use instead of root vp */
3514 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3517 currattrbufsize
= *((u_long
*)attrbufptr
) = ((char *)varptr
- (char *)attrbufptr
);
3519 /* now check if we can't fit in the buffer space remaining */
3520 if (currattrbufsize
> uio
->uio_resid
)
3523 retval
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, ap
->a_uio
);
3524 if (retval
!= E_NONE
)
3526 attrptr
= attrbufptr
;
3527 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3529 *ap
->a_actualcount
+= 1;
3533 *ap
->a_newstate
= VTOH(vp
)->h_meta
->h_mtime
;/* before we unlock, know the mod date */
3534 /* Unlock catalog b-tree, finally. Ties up the everything during enumeration */
3535 (void) hfs_metafilelocking( VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, proc
);
3537 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3539 if (!retval
&& ap
->a_cookies
!= NULL
) { /* CHECK THAT 0 wasn't passed in */
3546 /* Only the NFS server uses cookies, and it loads the
3547 * directory block into system space, so we can just look at
3550 if (uio
->uio_segflg
!= UIO_SYSSPACE
) /* || uio->uio_iovcnt != 1 checked earlier */
3551 panic("hfs_readdirattr: unexpected uio from NFS server");
3552 dpStart
= uio
->uio_iov
->iov_base
- (uio
->uio_offset
- startoffset
);
3553 dpEnd
= uio
->uio_iov
->iov_base
;
3554 MALLOC(cookies
, u_long
*, (*ap
->a_actualcount
)*sizeof(u_long
), M_TEMP
, M_WAITOK
);
3555 for (dp
= dpStart
, cookiep
= cookies
;
3557 dp
= ((caddr_t
) dp
+ *((u_long
*)dp
))) {
3558 *cookiep
++ = (u_long
)((caddr_t
)dp
+ sizeof(u_long
));
3560 *ap
->a_cookies
= cookies
;
3563 uio
->uio_offset
= startoffset
+ (*ap
->a_actualcount
)*sizeof(struct dirent
);
3566 if (attrbufptr
!= NULL
)
3567 FREE(attrbufptr
, M_TEMP
);
3573 * Return target name of a symbolic link
3574 #% readlink vp L L L
3577 IN struct vnode *vp;
3578 INOUT struct uio *uio;
3579 IN struct ucred *cred;
3584 struct vop_readlink_args
/* {
3587 struct ucred *a_cred;
3591 DBG_FUNC_NAME("readlink");
3592 DBG_VOP_LOCKS_DECL(1);
3593 DBG_VOP_PRINT_FUNCNAME();
3594 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3596 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3597 retval
= VOP_READ(ap
->a_vp
, ap
->a_uio
, 0, ap
->a_cred
);
3598 /* clear IN_ACCESS to prevent needless update of symlink vnode */
3599 VTOH(ap
->a_vp
)->h_nodeflags
&= ~IN_ACCESS
;
3601 DBG_VOP_LOCKS_TEST(retval
);
3608 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3609 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3610 #% abortop dvp = = =
3613 IN struct vnode *dvp;
3614 IN struct componentname *cnp;
3622 struct vop_abortop_args
/* {
3623 struct vnode *a_dvp;
3624 struct componentname *a_cnp;
3627 DBG_FUNC_NAME("abortop");
3628 DBG_VOP_LOCKS_DECL(1);
3629 DBG_VOP_PRINT_FUNCNAME();
3630 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
3631 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
3634 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3636 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
3637 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
3639 DBG_VOP_LOCKS_TEST(E_NONE
);
3643 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3646 #% inactive vp L U U
3649 IN struct vnode *vp;
3656 struct vop_inactive_args
/* {
3660 struct vnode
*vp
= ap
->a_vp
;
3661 struct hfsnode
*hp
= VTOH(vp
);
3662 struct proc
*p
= ap
->a_p
;
3665 extern int prtactive
;
3667 DBG_FUNC_NAME("inactive");
3668 DBG_VOP_LOCKS_DECL(1);
3669 DBG_VOP_PRINT_FUNCNAME();
3670 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3672 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3675 if (prtactive
&& vp
->v_usecount
<= 0)
3676 vprint("hfs_inactive: pushing active", vp
);
3678 if (vp
->v_usecount
!= 0)
3679 DBG_VOP(("%s: bad usecount = %d\n",funcname
,vp
->v_usecount
));
3682 * Ignore nodes related to stale file handles.
3684 if (hp
->h_meta
->h_mode
== 0)
3688 * Check for a postponed deletion
3690 if (hp
->h_meta
->h_metaflags
& IN_DELETED
) {
3691 hp
->h_meta
->h_metaflags
&= ~IN_DELETED
;
3693 error
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0);
3694 if (error
) goto out
;
3696 if(UBCINFOEXISTS(vp
))
3697 (void)ubc_setsize(vp
, (off_t
)0);
3700 * Note: we do not need a lock on the private metadata directory
3701 * since it never has a vnode associated with it.
3703 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3704 if (error
) goto out
;
3705 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3707 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3711 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
3714 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
3715 error
= hfsDelete(HTOVCB(hp
), VTOHFS(vp
)->hfs_private_metadata_dir
, iNodeName
, TRUE
, H_HINT(hp
));
3717 /* XXX can we leave orphaned sibling? */
3718 error
= hfsDelete(HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
3719 if (error
== ENOENT
) {
3720 /* try by fileID as a backup */
3721 error
= hfsDelete(HTOVCB(hp
), H_FILEID(hp
), NULL
, TRUE
, H_HINT(hp
));
3725 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
3726 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3727 if (error
) goto out
;
3729 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
3730 hp
->h_meta
->h_mode
= 0;
3731 /* clear the block mappings */
3732 hp
->fcbPLen
= (u_int64_t
)0;
3733 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
3735 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
3738 if (hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
3740 VOP_UPDATE(vp
, &tv
, &tv
, 0);
3744 VOP_UNLOCK(vp
, 0, p
);
3746 * If we are done with the inode, reclaim it
3747 * so that it can be reused immediately.
3749 if (hp
->h_meta
->h_mode
== 0)
3750 vrecycle(vp
, (struct slock
*)0, p
);
3752 /* XXX SER Here we might want to get rid of any other forks
3753 * The problem is that if we call vrecycle(), our structure
3754 * disappear from under us, we would need to remember, and expect
3755 * things to go to null or to disappear
3756 * But it stillw would be a good thing to remove vnodes
3757 * referencing stale data
3760 DBG_VOP_LOCKS_TEST(E_NONE
);
3765 Ignored since the locks are gone......
3769 IN struct vnode *vp;
3776 struct vop_reclaim_args
/* {
3780 struct vnode
*vp
= ap
->a_vp
;
3781 struct hfsnode
*hp
= VTOH(vp
);
3782 void *tdata
= vp
->v_data
;
3784 Boolean freeMeta
= true;
3785 struct vnode
*devvp
= NULL
;
3787 extern int prtactive
;
3788 DBG_FUNC_NAME("reclaim");
3789 DBG_VOP_LOCKS_DECL(1);
3790 DBG_VOP_PRINT_FUNCNAME();
3791 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3793 DBG_VOP_LOCKS_INIT(0, ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_ZERO
);
3796 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3797 created in their entirity could be getting cleaned up here.
3800 if (prtactive
&& vp
->v_usecount
!= 0)
3801 vprint("hfs_reclaim(): pushing active", vp
);
3803 hp
->h_nodeflags
|= IN_ALLOCATING
; /* Mark this as being incomplete */
3805 * This will remove the entry from the hash AND the sibling list
3806 * This will make sure everything is in a stable state to see if we can remove the meta
3807 * i.e. if this is the only fork...the sibling list will be empty
3811 DBG_ASSERT(tdata
!= NULL
);
3812 DBG_ASSERT(hp
->h_meta
!= NULL
);
3814 devvp
= hp
->h_meta
->h_devvp
; /* For later releasing */
3815 hp
->h_meta
->h_usecount
--;
3817 /* release the file meta if this is the last fork */
3818 if (H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
) {
3819 if (hp
->h_meta
->h_siblinghead
.cqh_first
!= (void *) &hp
->h_meta
->h_siblinghead
)
3824 DBG_ASSERT(hp
->h_meta
->h_usecount
== 0);
3825 if (hp
->h_meta
->h_metaflags
& IN_LONGNAME
) {
3827 DBG_ASSERT(tname
!= NULL
);
3828 FREE(tname
, M_TEMP
);
3830 FREE_ZONE(hp
->h_meta
, sizeof(struct hfsfilemeta
), M_HFSFMETA
);
3834 DBG_ASSERT(hp
->h_meta
->h_usecount
== 1);
3838 * Purge old data structures associated with the inode.
3845 /* Free our data structs */
3846 FREE_ZONE(tdata
, sizeof(struct hfsnode
), M_HFSNODE
);
3849 DBG_VOP_LOCKS_TEST(E_NONE
);
3855 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
3859 IN struct vnode *vp;
3866 struct vop_lock_args
/* {
3872 struct vnode
* vp
= ap
->a_vp
;
3873 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3876 DBG_FUNC_NAME("lock");
3877 DBG_VOP_LOCKS_DECL(1);
3878 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
3879 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
3880 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3882 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
);
3883 if (retval
!= E_NONE
) {
3884 if ((ap
->a_flags
& LK_NOWAIT
) == 0)
3885 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval
, ap
->a_flags
));
3890 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
3891 DBG_VOP_LOCKS_TEST(retval
);
3896 * Unlock an hfsnode.
3900 IN struct vnode *vp;
3907 struct vop_unlock_args
/* {
3913 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3914 struct vnode
*vp
= ap
->a_vp
;
3915 int retval
= E_NONE
;
3917 DBG_FUNC_NAME("unlock");
3918 DBG_VOP_LOCKS_DECL(1);
3919 DBG_VOP_PRINT_FUNCNAME();
3920 DBG_VOP_PRINT_VNODE_INFO(vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
3921 DBG_VOP_LOCKS_INIT(0,vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
3924 DBG_ASSERT((ap
->a_flags
& (LK_EXCLUSIVE
|LK_SHARED
)) == 0);
3925 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
| LK_RELEASE
, &vp
->v_interlock
, ap
->a_p
);
3926 if (retval
!= E_NONE
) {
3927 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval
, H_FORKTYPE(hp
)));
3930 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
3931 DBG_VOP_LOCKS_TEST(retval
);
3937 * Print out the contents of an hfsnode.
3941 IN struct vnode *vp;
3945 struct vop_print_args
/* {
3949 register struct vnode
* vp
= ap
->a_vp
;
3950 register struct hfsnode
*hp
= VTOH( vp
);
3951 DBG_FUNC_NAME("print");
3952 DBG_VOP_LOCKS_DECL(1);
3953 DBG_VOP_PRINT_FUNCNAME();
3954 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
3956 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3958 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp
),
3959 major(H_DEV(hp
)), minor(H_DEV(hp
)));
3960 /* lockmgr_printinfo(&hp->h_lock); */
3962 DBG_VOP_LOCKS_TEST(E_NONE
);
3968 * Check for a locked hfsnode.
3969 #% islocked vp = = =
3972 IN struct vnode *vp;
3977 struct vop_islocked_args
/* {
3982 //DBG_FUNC_NAME("islocked");
3983 //DBG_VOP_LOCKS_DECL(1);
3984 //DBG_VOP_PRINT_FUNCNAME();
3985 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
3987 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
3989 lockStatus
= lockstatus(&VTOH( ap
->a_vp
)->h_lock
);
3990 //DBG_VOP_LOCKS_TEST(E_NONE);
3991 return (lockStatus
);
3996 #% pathconf vp L L L
3999 IN struct vnode *vp;
4001 OUT register_t *retval;
4006 struct vop_pathconf_args
/* {
4012 int retval
= E_NONE
;
4013 DBG_FUNC_NAME("pathconf");
4014 DBG_VOP_LOCKS_DECL(1);
4015 DBG_VOP_PRINT_FUNCNAME();
4016 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4018 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
4020 DBG_HFS_NODE_CHECK (ap
->a_vp
);
4022 switch (ap
->a_name
) {
4025 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
4026 *ap
->a_retval
= HFS_LINK_MAX
;
4034 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
4037 *ap
->a_retval
= PATH_MAX
; /* 1024 */
4039 case _PC_CHOWN_RESTRICTED
:
4049 DBG_VOP_LOCKS_TEST(retval
);
4058 * Advisory record locking support
4062 IN struct vnode *vp;
4065 IN struct flock *fl;
4071 struct vop_advlock_args
/* {
4079 register struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4080 register struct flock
*fl
= ap
->a_fl
;
4081 register struct hfslockf
*lock
;
4084 DBG_FUNC_NAME("advlock");
4085 DBG_VOP_LOCKS_DECL(1);
4086 DBG_VOP_PRINT_FUNCNAME();
4087 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP(("\n"));
4088 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
4090 * Avoid the common case of unlocking when inode has no locks.
4092 if (hp
->h_lockf
== (struct hfslockf
*)0) {
4093 if (ap
->a_op
!= F_SETLK
) {
4094 fl
->l_type
= F_UNLCK
;
4099 * Convert the flock structure into a start and end.
4102 switch (fl
->l_whence
) {
4106 * Caller is responsible for adding any necessary offset
4107 * when SEEK_CUR is used.
4109 start
= fl
->l_start
;
4113 start
= HTOFCB(hp
)->fcbEOF
+ fl
->l_start
;
4125 end
= start
+ fl
->l_len
- 1;
4128 * Create the hfslockf structure
4130 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
4131 lock
->lf_start
= start
;
4133 lock
->lf_id
= ap
->a_id
;
4134 lock
->lf_hfsnode
= hp
;
4135 lock
->lf_type
= fl
->l_type
;
4136 lock
->lf_next
= (struct hfslockf
*)0;
4137 TAILQ_INIT(&lock
->lf_blkhd
);
4138 lock
->lf_flags
= ap
->a_flags
;
4140 * Do the requested operation.
4144 retval
= hfs_setlock(lock
);
4148 retval
= hfs_clearlock(lock
);
4149 FREE(lock
, M_LOCKF
);
4153 retval
= hfs_getlock(lock
, fl
);
4154 FREE(lock
, M_LOCKF
);
4159 _FREE(lock
, M_LOCKF
);
4163 DBG_VOP_LOCKS_TEST(retval
);
4170 * Update the access, modified, and node change times as specified by the
4171 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4172 * used to specify that the node needs to be updated but that the times have
4173 * already been set. The access and modified times are taken from the second
4174 * and third parameters; the node change time is always taken from the current
4175 * time. If waitfor is set, then wait for the disk write of the node to
4180 IN struct vnode *vp;
4181 IN struct timeval *access;
4182 IN struct timeval *modify;
4188 struct vop_update_args
/* {
4190 struct timeval *a_access;
4191 struct timeval *a_modify;
4197 hfsCatalogInfo catInfo
;
4203 DBG_FUNC_NAME("update");
4204 DBG_VOP_LOCKS_DECL(1);
4205 DBG_VOP_PRINT_FUNCNAME();
4206 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
4207 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4209 hp
= VTOH(ap
->a_vp
);
4211 DBG_ASSERT(hp
&& hp
->h_meta
);
4212 DBG_ASSERT(*((int*)&ap
->a_vp
->v_interlock
) == 0);
4214 if ((H_FORKTYPE(hp
) == kSysFile
) ||
4215 (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) ||
4216 (hp
->h_meta
->h_mode
== 0)) {
4217 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4218 DBG_VOP_LOCKS_TEST(0);
4222 if (H_FORKTYPE(hp
) == kSysFile
) {
4223 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4224 DBG_VOP_LOCKS_TEST(0);
4228 if (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) {
4229 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4230 DBG_VOP_LOCKS_TEST(0);
4234 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4235 if (HTOFCB(hp
)->fcbFlags
&fcbModifiedMask
)
4236 hp
->h_nodeflags
|= IN_MODIFIED
;
4238 if ((hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) == 0) {
4239 DBG_VOP_LOCKS_TEST(0);
4243 if (hp
->h_nodeflags
& IN_ACCESS
)
4244 hp
->h_meta
->h_atime
= ap
->a_access
->tv_sec
;
4245 if (hp
->h_nodeflags
& IN_UPDATE
)
4246 hp
->h_meta
->h_mtime
= ap
->a_modify
->tv_sec
;
4247 if (hp
->h_nodeflags
& IN_CHANGE
) {
4248 hp
->h_meta
->h_ctime
= time
.tv_sec
;
4250 * HFS dates that WE set must be adjusted for DST
4252 if ((HTOVCB(hp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
4253 hp
->h_meta
->h_ctime
+= 3600;
4254 hp
->h_meta
->h_mtime
= hp
->h_meta
->h_ctime
;
4259 filename
= H_NAME(hp
);
4262 catInfo
.hint
= H_HINT(hp
);
4266 * Force an update of the indirect node instead of the link
4267 * by using the name and parent of the indirect node.
4269 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
4270 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
4271 filename
= iNodeName
;
4272 pid
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
4276 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
4279 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4280 * the Catalog b-tree may aready be locked by the current thread. So we
4281 * allow recursive locking of the Catalog from within VOP_UPDATE.
4283 /* Lock the Catalog b-tree file */
4284 retval
= hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
4286 DBG_VOP_LOCKS_TEST(retval
);
4290 retval
= hfs_getcatalog(vcb
, pid
, filename
, -1, &catInfo
);
4291 if (retval
!= noErr
) {
4292 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4293 retval
= MacToVFSError(retval
);
4297 H_HINT(hp
) = catInfo
.hint
;
4298 CopyVNodeToCatalogNode (HTOV(hp
), &catInfo
.nodeData
);
4300 retval
= UpdateCatalogNode(vcb
, pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
);
4302 /* unlock the Catalog b-tree file */
4303 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4305 if (retval
!= noErr
) { /* from UpdateCatalogNode() */
4306 retval
= MacToVFSError(retval
);
4310 /* After the updates are finished, clear the flags */
4311 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4312 HTOFCB(hp
)->fcbFlags
&= ~fcbModifiedMask
;
4314 /* Update general data */
4315 if (ap
->a_vp
->v_type
== VDIR
) {
4316 hp
->h_meta
->h_nlink
= 2 + catInfo
.nodeData
.cnd_valence
;
4317 hp
->h_meta
->h_size
= sizeof(rootdots
) +
4318 (catInfo
.nodeData
.cnd_valence
* AVERAGE_HFSDIRENTRY_SIZE
);
4319 if (hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
)
4320 hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
;
4322 hp
->h_meta
->h_size
= (off_t
)vcb
->blockSize
*
4323 (off_t
)(catInfo
.nodeData
.cnd_rsrcfork
.totalBlocks
+
4324 catInfo
.nodeData
.cnd_datafork
.totalBlocks
);
4330 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
4332 DBG_VOP_LOCKS_TEST(retval
);
4338 * Initialize the vnode associated with a new hfsnode,
4339 * handle aliased vnodes.
4342 hfs_vinit(mntp
, specops
, fifoops
, vpp
)
4344 int (**specops
)(void *);
4345 int (**fifoops
)(void *);
4349 struct vnode
*vp
, *nvp
;
4353 /* vp->v_type set in CopyCatalogToHFSNode */
4354 switch(vp
->v_type
) {
4358 if ((nvp
= checkalias(vp
, hp
->h_meta
->h_rdev
, mntp
))) {
4360 * Discard unneeded vnode, but save its hfsnode.
4361 * Note that the lock is carried over in the hfsnode
4362 * to the replacement vnode.
4364 nvp
->v_data
= vp
->v_data
;
4366 vp
->v_op
= spec_vnodeop_p
;
4370 * Reinitialize aliased hfsnode.
4382 return (EOPNOTSUPP
);
4387 if (H_FILEID(hp
) == kRootDirID
)
4388 vp
->v_flag
|= VROOT
;
4395 * Allocate a new node
4397 * Upon leaving, namei buffer must be freed.
4401 hfs_makenode(mode
, rawdev
, dvp
, vpp
, cnp
, p
)
4406 struct componentname
*cnp
;
4409 register struct hfsnode
*hp
, *parhp
;
4412 struct hfsCatalogInfo catInfo
;
4416 int hasmetalock
= 0;
4417 DBG_FUNC_NAME("makenode");
4420 vcb
= HTOVCB(parhp
);
4423 if ((mode
& IFMT
) == 0)
4427 if ((cnp
->cn_flags
& HASBUF
) == 0)
4428 panic("hfs_makenode: no name");
4431 /* lock catalog b-tree */
4432 retval
= hfs_metafilelocking(VTOHFS(dvp
),
4433 kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
4434 if (retval
!= E_NONE
)
4439 /* Create the Catalog B*-Tree entry */
4440 retval
= hfsCreate(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, mode
);
4441 if (retval
!= E_NONE
) {
4442 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4446 /* Look up the catalog entry just created: */
4447 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
4448 catInfo
.hint
= kNoHint
;
4450 retval
= hfs_getcatalog(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, cnp
->cn_namelen
, &catInfo
);
4451 if (retval
!= E_NONE
) {
4452 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4456 /* unlock catalog b-tree */
4458 (void) hfs_metafilelocking(VTOHFS(dvp
),
4459 kHFSCatalogFileID
, LK_RELEASE
, p
);
4461 /* hfs plus has additional metadata to initialize */
4462 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4466 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
4467 catInfo
.nodeData
.cnd_ownerID
= VTOHFS(dvp
)->hfs_uid
;
4468 catInfo
.nodeData
.cnd_groupID
= VTOHFS(dvp
)->hfs_gid
;
4471 catInfo
.nodeData
.cnd_ownerID
= cnp
->cn_cred
->cr_uid
;
4472 catInfo
.nodeData
.cnd_groupID
= parhp
->h_meta
->h_gid
;
4476 switch (catmode
& IFMT
) {
4478 catInfo
.nodeData
.cnd_ownerID
= parhp
->h_meta
->h_uid
;
4483 /* XXX should we move this to post hfsGet? */
4484 catInfo
.nodeData
.cnd_rawDevice
= rawdev
;
4486 * Don't tag as a special file (BLK or CHR) until *after*
4487 * hfsGet is called. This insures that the checkalias call
4488 * is defered until hfs_mknod completes.
4490 catmode
= (catmode
& ~IFMT
) | IFREG
;
4494 if ((catmode
& ISGID
) && !groupmember(parhp
->h_meta
->h_gid
, cnp
->cn_cred
) &&
4495 suser(cnp
->cn_cred
, NULL
))
4498 if (cnp
->cn_flags
& ISWHITEOUT
)
4504 * The 32-bit pflags field has two bytes of significance which
4505 * are stored separately as admin and owner flags.
4507 * +------------------------------------+
4508 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4509 * +------------------------------------+
4511 catInfo
.nodeData
.cnd_adminFlags
= (pflags
>> 16) & 0x00FF;
4512 catInfo
.nodeData
.cnd_ownerFlags
= pflags
& 0x00FF;
4513 catInfo
.nodeData
.cnd_mode
= catmode
;
4516 /* Create a vnode for the object just created: */
4517 forkType
= (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) ? kDirectory
: kDataFork
;
4518 retval
= hfs_vcreate(vcb
, &catInfo
, forkType
, &tvp
);
4520 CLEAN_CATALOGDATA(&catInfo
.nodeData
); /* Should do nothing */
4522 if (retval
) goto bad1
; /* from hfs_vcreate() */
4524 /* flush out pflags, mode, gid, uid and rdev */
4526 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4528 /* reset mode and v_type in case it was BLK/CHR */
4529 hp
->h_meta
->h_mode
= mode
;
4530 tvp
->v_type
= IFTOVT(mode
);
4531 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
4532 hp
->h_nodeflags
|= IN_ACCESS
| IN_CHANGE
| IN_UPDATE
;
4533 if ((retval
= VOP_UPDATE(tvp
, &tv
, &tv
, 1)))
4537 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4538 if ((retval
= VOP_UPDATE(dvp
, &tv
, &tv
, 1)))
4541 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4542 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4545 if (UBCINFOMISSING(tvp
) || UBCINFORECLAIMED(tvp
))
4553 * Write retval occurred trying to update the node
4554 * or the directory so must deallocate the node.
4556 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4561 /* unlock catalog b-tree */
4563 (void) hfs_metafilelocking(VTOHFS(dvp
),
4564 kHFSCatalogFileID
, LK_RELEASE
, p
);
4566 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4567 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4575 #if DBG_VOP_TEST_LOCKS
4577 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4579 void DbgVopTest( int maxSlots
,
4581 VopDbgStoreRec
*VopDbgStore
,
4586 for (index
= 0; index
< maxSlots
; index
++)
4588 if (VopDbgStore
[index
].id
!= index
) {
4589 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname
, VopDbgStore
[index
].id
, index
));
4592 if ((VopDbgStore
[index
].vp
!= NULL
) &&
4593 ((VopDbgStore
[index
].vp
->v_data
==NULL
) || (VTOH(VopDbgStore
[index
].vp
)->h_valid
!= HFS_VNODE_MAGIC
)))
4596 if (VopDbgStore
[index
].vp
!= NULL
)
4597 debug_check_vnode(VopDbgStore
[index
].vp
, 0);
4599 switch (VopDbgStore
[index
].inState
)
4603 /* Do Nothing !!! */
4606 case VOPDBG_UNLOCKED
:
4607 case VOPDBG_LOCKNOTNIL
:
4609 if (VopDbgStore
[index
].vp
== NULL
&& (VopDbgStore
[index
].inState
!= VOPDBG_LOCKNOTNIL
)) {
4610 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4611 } else if (VopDbgStore
[index
].vp
!= NULL
) {
4612 switch (VopDbgStore
[index
].inState
)
4615 case VOPDBG_LOCKNOTNIL
:
4616 if (VopDbgStore
[index
].inValue
== 0)
4618 DBG_ERR (("%s: Entry: not LOCKED:", funcname
));
4619 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4623 case VOPDBG_UNLOCKED
:
4624 if (VopDbgStore
[index
].inValue
!= 0)
4626 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname
));
4627 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4636 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4642 switch (VopDbgStore
[index
].errState
)
4645 /* Do Nothing !!! */
4648 case VOPDBG_UNLOCKED
:
4651 if (VopDbgStore
[index
].vp
== NULL
) {
4652 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4654 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4655 switch (VopDbgStore
[index
].errState
)
4658 if (VopDbgStore
[index
].outValue
== 0)
4660 DBG_ERR (("%s: Error: not LOCKED:", funcname
));
4661 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4665 case VOPDBG_UNLOCKED
:
4666 if (VopDbgStore
[index
].outValue
!= 0)
4668 DBG_ERR (("%s: Error: not UNLOCKED:", funcname
));
4669 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4674 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4675 DBG_ERR (("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname
, (u_int
)VopDbgStore
[index
].vp
, VopDbgStore
[index
].inValue
, VopDbgStore
[index
].outValue
));
4681 case VOPDBG_LOCKNOTNIL
:
4682 if (VopDbgStore
[index
].vp
!= NULL
) {
4683 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4684 if (VopDbgStore
[index
].outValue
== 0)
4685 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4689 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4694 switch (VopDbgStore
[index
].outState
)
4697 /* Do Nothing !!! */
4700 case VOPDBG_UNLOCKED
:
4702 if (VopDbgStore
[index
].vp
== NULL
) {
4703 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname
, index
));
4705 if (VopDbgStore
[index
].vp
!= NULL
)
4707 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4708 switch (VopDbgStore
[index
].outState
)
4711 if (VopDbgStore
[index
].outValue
== 0)
4713 DBG_ERR (("%s: Out: not LOCKED:", funcname
));
4714 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4718 case VOPDBG_UNLOCKED
:
4719 if (VopDbgStore
[index
].outValue
!= 0)
4721 DBG_ERR (("%s: Out: not UNLOCKED:", funcname
));
4722 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4727 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4728 DBG_ERR (("%s: Out: In/Out locks are DIFFERENT: 0x%x, in is %d and out is %d\n", funcname
, (u_int
)VopDbgStore
[index
].vp
, VopDbgStore
[index
].inValue
, VopDbgStore
[index
].outValue
));
4733 case VOPDBG_LOCKNOTNIL
:
4734 if (VopDbgStore
[index
].vp
!= NULL
) {
4735 if (&VTOH(VopDbgStore
[index
].vp
)->h_lock
== NULL
) {
4736 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4739 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4740 if (VopDbgStore
[index
].outValue
== 0)
4742 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname
));
4743 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
); DBG_ERR (("\n"));
4749 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].outState
));
4753 VopDbgStore
[index
].id
= -1; /* Invalidate the entry to allow panic-free re-use */
4757 #endif /* DBG_VOP_TEST_LOCKS */
4760 * Wrapper for special device reads
4764 struct vop_read_args
/* {
4768 struct ucred *a_cred;
4775 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4776 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
4780 * Wrapper for special device writes
4784 struct vop_write_args
/* {
4788 struct ucred *a_cred;
4793 * Set update and change flags.
4795 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4796 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
4800 * Wrapper for special device close
4802 * Update the times on the hfsnode then do device close.
4806 struct vop_close_args
/* {
4809 struct ucred *a_cred;
4813 struct vnode
*vp
= ap
->a_vp
;
4814 struct hfsnode
*hp
= VTOH(vp
);
4816 simple_lock(&vp
->v_interlock
);
4817 if (ap
->a_vp
->v_usecount
> 1)
4818 HFSTIMES(hp
, &time
, &time
);
4819 simple_unlock(&vp
->v_interlock
);
4820 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
4825 * Wrapper for fifo reads
4829 struct vop_read_args
/* {
4833 struct ucred *a_cred;
4836 extern int (**fifo_vnodeop_p
)(void *);
4841 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4842 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
4846 * Wrapper for fifo writes
4850 struct vop_write_args
/* {
4854 struct ucred *a_cred;
4857 extern int (**fifo_vnodeop_p
)(void *);
4860 * Set update and change flags.
4862 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4863 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
4867 * Wrapper for fifo close
4869 * Update the times on the hfsnode then do device close.
4873 struct vop_close_args
/* {
4876 struct ucred *a_cred;
4880 extern int (**fifo_vnodeop_p
)(void *);
4881 struct vnode
*vp
= ap
->a_vp
;
4882 struct hfsnode
*hp
= VTOH(vp
);
4884 simple_lock(&vp
->v_interlock
);
4885 if (ap
->a_vp
->v_usecount
> 1)
4886 HFSTIMES(hp
, &time
, &time
);
4887 simple_unlock(&vp
->v_interlock
);
4888 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
4893 /*****************************************************************************
4897 *****************************************************************************/
4899 #define VOPFUNC int (*)(void *)
4901 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
4902 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
4903 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
4904 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
4905 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
4906 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
4907 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
4908 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
4909 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
4910 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
4911 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
4912 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
4913 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
4914 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
4915 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
4916 { &vop_mmap_desc
, (VOPFUNC
)hfs_mmap
}, /* mmap */
4917 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
4918 { &vop_seek_desc
, (VOPFUNC
)hfs_seek
}, /* seek */
4919 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
4921 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
4923 { &vop_link_desc
, (VOPFUNC
)err_link
}, /* link (NOT SUPPORTED) */
4925 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
4926 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
4927 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
4928 { &vop_mkcomplex_desc
, (VOPFUNC
)hfs_mkcomplex
}, /* mkcomplex */
4929 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
4930 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
4931 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
4932 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
4933 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
4934 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
4935 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
4936 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
4937 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
4938 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
4939 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
4940 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
4941 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
4942 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
4943 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
4944 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
4945 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
4946 { &vop_reallocblks_desc
, (VOPFUNC
)hfs_reallocblks
}, /* reallocblks */
4947 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
4948 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
4949 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
4950 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
4951 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
4952 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
4953 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
4954 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
4955 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
4956 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
4957 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
4958 { NULL
, (VOPFUNC
)NULL
}
4961 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
4962 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
4964 int (**hfs_specop_p
)(void *);
4965 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
4966 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
4967 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
4968 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
4969 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
4970 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
4971 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
4972 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
4973 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
4974 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
4975 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
4976 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
4977 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
4978 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
4979 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
4980 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
4981 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
4982 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
4983 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
4984 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
4985 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
4986 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
4987 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
4988 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
4989 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
4990 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
4991 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
4992 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
4993 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
4994 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
4995 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
4996 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
4997 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
4998 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
4999 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5000 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5001 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
5002 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
5003 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
5004 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
5005 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
5006 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5007 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
5008 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5009 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5010 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
5011 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5012 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5013 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5014 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5015 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5016 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5018 struct vnodeopv_desc hfs_specop_opv_desc
=
5019 { &hfs_specop_p
, hfs_specop_entries
};
5022 int (**hfs_fifoop_p
)(void *);
5023 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
5024 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5025 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
5026 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
5027 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
5028 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
5029 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
5030 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5031 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5032 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5033 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
5034 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
5035 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
5036 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
5037 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
5038 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
5039 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
5040 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5041 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
5042 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
5043 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
5044 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
5045 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
5046 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
5047 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
5048 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
5049 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
5050 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
5051 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5052 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5053 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5054 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5055 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
5056 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
5057 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5058 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5059 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
5060 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
5061 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
5062 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
5063 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
5064 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5065 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
5066 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5067 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5068 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5069 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5070 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5071 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5072 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5073 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5074 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5076 struct vnodeopv_desc hfs_fifoop_opv_desc
=
5077 { &hfs_fifoop_p
, hfs_fifoop_entries
};