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
)) {
1318 * If we are going to change the times:
1319 * 1. do we have permission to change the dates?
1320 * 2. Is there another fork? If so then clear any flags associated with the times
1322 if (alist
->commonattr
& (ATTR_CMN_MODTIME
| ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1323 if (alist
->commonattr
& (ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1324 if ((error
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1328 /* If there is another fork, clear the flags */
1329 if ((hp
->h_meta
->h_usecount
> 1) && (H_FORKTYPE(hp
) == kDataFork
)) {
1330 struct vnode
*sib_vp
= NULL
;
1331 struct hfsnode
*nhp
;
1333 /* Loop through all siblings, skipping ourselves */
1334 simple_lock(&hp
->h_meta
->h_siblinglock
);
1335 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1336 if (nhp
== hp
) /* skip ourselves */
1340 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1342 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1343 if (sib_vp
&& vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1344 if (VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_UPDATE
)) {
1345 if (alist
->commonattr
& ATTR_CMN_MODTIME
)
1346 VTOH(sib_vp
)->h_nodeflags
&= ~IN_UPDATE
;
1347 if (alist
->commonattr
& ATTR_CMN_CHGTIME
)
1348 VTOH(sib_vp
)->h_nodeflags
&= ~IN_CHANGE
;
1349 if (alist
->commonattr
& ATTR_CMN_ACCTIME
)
1350 VTOH(sib_vp
)->h_nodeflags
&= ~IN_ACCESS
;
1354 } /* h_use_count > 1 */
1357 /* save these in case hfs_chown() or hfs_chmod() fail */
1358 saved_uid
= hp
->h_meta
->h_uid
;
1359 saved_gid
= hp
->h_meta
->h_gid
;
1360 saved_mode
= hp
->h_meta
->h_mode
;
1361 saved_flags
= hp
->h_meta
->h_pflags
;
1363 attrptr
= attrbufptr
;
1364 UnpackAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
1366 /* if unpacking changed the owner or group then call hfs_chown() */
1367 if (saved_uid
!= hp
->h_meta
->h_uid
|| saved_gid
!= hp
->h_meta
->h_gid
) {
1371 uid
= hp
->h_meta
->h_uid
;
1372 hp
->h_meta
->h_uid
= saved_uid
;
1373 gid
= hp
->h_meta
->h_gid
;
1374 hp
->h_meta
->h_gid
= saved_gid
;
1375 if ((error
= hfs_chown(vp
, uid
, gid
, cred
, p
)))
1379 /* if unpacking changed the mode then call hfs_chmod() */
1380 if (saved_mode
!= hp
->h_meta
->h_mode
) {
1383 mode
= hp
->h_meta
->h_mode
;
1384 hp
->h_meta
->h_mode
= saved_mode
;
1385 if ((error
= hfs_chmod(vp
, mode
, cred
, p
)))
1389 /* if unpacking changed the flags then call hfs_chflags */
1390 if (saved_flags
!= hp
->h_meta
->h_pflags
) {
1393 flags
= hp
->h_meta
->h_pflags
;
1394 hp
->h_meta
->h_pflags
= saved_flags
;
1395 if ((error
= hfs_chflags(vp
, flags
, cred
, p
)))
1400 /* Update Catalog Tree */
1401 if (alist
->volattr
== 0) {
1402 error
= MacToVFSError( UpdateCatalogNode(HTOVCB(hp
), pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
));
1406 if (alist
->volattr
& ATTR_VOL_NAME
) {
1407 ExtendedVCB
*vcb
= VTOVCB(vp
);
1408 int namelen
= strlen(vcb
->vcbVN
);
1410 if (vcb
->vcbVN
[0] == 0) {
1412 * Ignore attempts to rename a volume to a zero-length name:
1413 * restore the original name from the metadata.
1415 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
);
1417 error
= MoveRenameCatalogNode(vcb
, kRootParID
, H_NAME(hp
), H_HINT(hp
),
1418 kRootParID
, vcb
->vcbVN
, &H_HINT(hp
));
1421 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
); /* Restore the old name in the VCB */
1422 vcb
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1427 hfs_set_metaname(vcb
->vcbVN
, hp
->h_meta
, HTOHFS(hp
));
1428 hp
->h_nodeflags
|= IN_CHANGE
;
1430 } /* vcb->vcbVN[0] == 0 ... else ... */
1431 } /* alist->volattr & ATTR_VOL_NAME */
1434 /* unlock catalog b-tree */
1435 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1437 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1440 if (attrbufptr
) FREE(attrbufptr
, M_TEMP
);
1442 DBG_VOP_LOCKS_TEST(error
);
1447 * Change the mode on a file.
1448 * Inode must be locked before calling.
1451 hfs_chmod(vp
, mode
, cred
, p
)
1452 register struct vnode
*vp
;
1454 register struct ucred
*cred
;
1457 register struct hfsnode
*hp
= VTOH(vp
);
1460 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1463 #if OVERRIDE_UNKNOWN_PERMISSIONS
1464 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1469 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1472 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
1474 if (!groupmember(hp
->h_meta
->h_gid
, cred
) && (mode
& ISGID
))
1477 hp
->h_meta
->h_mode
&= ~ALLPERMS
;
1478 hp
->h_meta
->h_mode
|= (mode
& ALLPERMS
);
1479 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1480 hp
->h_nodeflags
|= IN_CHANGE
;
1486 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
1488 struct hfsnode
*hp
= VTOH(vp
);
1489 ExtendedVCB
*vcb
= HTOVCB(hp
);
1492 int retval
= E_NONE
;
1495 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
1498 * Disallow write attempts on read-only file systems;
1499 * unless the file is a socket, fifo, or a block or
1500 * character device resident on the file system.
1502 switch (vp
->v_type
) {
1506 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
1513 /* If immutable bit set, nobody gets to write it. */
1514 if (considerFlags
&& (hp
->h_meta
->h_pflags
& IMMUTABLE
))
1517 /* Otherwise, user id 0 always gets access. */
1518 if (cred
->cr_uid
== 0) {
1523 /* Otherwise, check the owner. */
1524 if ((retval
= hfs_owner_rights(vp
, cred
, p
, false)) == 0) {
1525 retval
= ((hp
->h_meta
->h_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
1529 /* Otherwise, check the groups. */
1530 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++)
1531 if (hp
->h_meta
->h_gid
== *gp
) {
1532 retval
= ((hp
->h_meta
->h_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
1536 /* Otherwise, check everyone else. */
1537 retval
= ((hp
->h_meta
->h_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
1546 * Change the flags on a file or directory.
1547 * Inode must be locked before calling.
1550 hfs_chflags(vp
, flags
, cred
, p
)
1551 register struct vnode
*vp
;
1552 register u_long flags
;
1553 register struct ucred
*cred
;
1556 register struct hfsnode
*hp
= VTOH(vp
);
1559 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
1560 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
1563 } else if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1567 if (cred
->cr_uid
== 0) {
1568 if ((hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
1572 hp
->h_meta
->h_pflags
= flags
;
1574 if (hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
) ||
1575 (flags
& UF_SETTABLE
) != flags
) {
1578 hp
->h_meta
->h_pflags
&= SF_SETTABLE
;
1579 hp
->h_meta
->h_pflags
|= (flags
& UF_SETTABLE
);
1581 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1582 hp
->h_nodeflags
|= IN_CHANGE
;
1589 * Perform chown operation on hfsnode hp;
1590 * hfsnode must be locked prior to call.
1593 hfs_chown(vp
, uid
, gid
, cred
, p
)
1594 register struct vnode
*vp
;
1600 register struct hfsnode
*hp
= VTOH(vp
);
1605 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1608 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1612 if (uid
== (uid_t
)VNOVAL
)
1613 uid
= hp
->h_meta
->h_uid
;
1614 if (gid
== (gid_t
)VNOVAL
)
1615 gid
= hp
->h_meta
->h_gid
;
1617 * If we don't own the file, are trying to change the owner
1618 * of the file, or are not a member of the target group,
1619 * the caller must be superuser or the call fails.
1621 if ((cred
->cr_uid
!= hp
->h_meta
->h_uid
|| uid
!= hp
->h_meta
->h_uid
||
1622 (gid
!= hp
->h_meta
->h_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
1623 (retval
= suser(cred
, &p
->p_acflag
)))
1626 ogid
= hp
->h_meta
->h_gid
;
1627 ouid
= hp
->h_meta
->h_uid
;
1629 hp
->h_meta
->h_gid
= gid
;
1630 hp
->h_meta
->h_uid
= uid
;
1632 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1633 if (ouid
!= uid
|| ogid
!= gid
)
1634 hp
->h_nodeflags
|= IN_CHANGE
;
1635 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
1636 hp
->h_meta
->h_mode
&= ~ISUID
;
1637 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
1638 hp
->h_meta
->h_mode
&= ~ISGID
;
1646 #% exchange fvp L L L
1647 #% exchange tvp L L L
1650 IN struct vnode *fvp;
1651 IN struct vnode *tvp;
1652 IN struct ucred *cred;
1658 * exchange is a very tricky routine, because we might have to unlock the
1659 * passed in vnode, and then retry locking it and all its siblings, and then
1660 * unlocking them in reverse.
1661 * Also the sibling list lock must be kept during the whole operation to
1662 * make sure nothing changes underneath us.
1663 * Also it depends on behavior of the sibling list and hash, so
1664 * careful if you change anything.
1669 struct vop_exchange_args
/* {
1670 struct vnode *a_fvp;
1671 struct vnode *a_tvp;
1672 struct ucred *a_cred;
1676 struct hfsnode
*from_hp
, *to_hp
, *nhp
;
1677 struct hfsnode
*fromFirst
, *fromSecond
, *toFirst
, *toSecond
;
1678 struct vnode
*from_vp
, *to_vp
;
1679 struct hfsmount
*hfsmp
;
1680 u_char tmp_name
[kHFSPlusMaxFileNameBytes
+1]; /* 766 bytes! */
1682 u_int32_t fromFileID
, toFileID
;
1683 u_int32_t fromParID
;
1685 int retval
= E_NONE
;
1686 DBG_FUNC_NAME("exchange");
1687 DBG_VOP_LOCKS_DECL(2);
1688 DBG_VOP_LOCKS_INIT(0,ap
->a_fvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1689 DBG_VOP_LOCKS_INIT(1,ap
->a_tvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1691 /* Set up variables and checks */
1692 from_vp
= ap
->a_fvp
;
1694 from_hp
= VTOH(from_vp
);
1695 to_hp
= VTOH(to_vp
);
1696 hfsmp
= VTOHFS(from_vp
);
1697 vcb
= HTOVCB(from_hp
);
1698 toFileID
= H_FILEID(to_hp
);
1699 fromFileID
= H_FILEID(from_hp
);
1700 fromParID
= H_DIRID(from_hp
);
1702 if (from_vp
->v_mount
!= to_vp
->v_mount
) {
1703 DBG_VOP_LOCKS_TEST(EXDEV
);
1707 /* Can only exchange file objects */
1708 if (from_vp
->v_type
!= VREG
|| to_vp
->v_type
!= VREG
) {
1709 DBG_VOP_LOCKS_TEST(EINVAL
);
1714 * Lock the siblink list
1715 * Check for multiple forks
1716 * If there are, we would need to:
1717 * 1. Unlock ourselves
1718 * 3. Traverse the list in a forward order...locking all vnodes
1719 * 4. Flush all buffers
1720 * 5. Perform the exchange
1721 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1722 * Notice that the sibling lock is kept during the whole operation. This quarentees
1723 * that no new forks are taken off or put on
1725 DBG_ASSERT(H_FORKTYPE(from_hp
)==kDataFork
&& H_FORKTYPE(to_hp
)==kDataFork
);
1726 fromFirst
= fromSecond
= toFirst
= toSecond
= NULL
;
1728 if (from_hp
->h_meta
->h_usecount
> 1) {
1730 * This has siblings, so remember the passed-in vnode,
1731 * unlock it if it is not the 'first' sibling,
1732 * and then lock the rest of the vnodes by sibling order.
1733 * Notice that the passed-in vnode is not vrele(), this
1734 * keeps the usecount>0, so it wont go away.
1736 simple_lock(&from_hp
->h_meta
->h_siblinglock
);
1737 fromFirst
= from_hp
->h_meta
->h_siblinghead
.cqh_first
;
1738 fromSecond
= fromFirst
->h_sibling
.cqe_next
;
1739 simple_unlock(&from_hp
->h_meta
->h_siblinglock
);
1741 if (fromFirst
== from_hp
) {
1742 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1743 fromSecond
= NULL
; /* its going away */
1745 VOP_UNLOCK(HTOV(from_hp
), 0, ap
->a_p
);
1746 if (vget(HTOV(fromFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1747 fromFirst
= NULL
; /* its going away */
1748 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1749 fromSecond
= NULL
; /* its going away */
1753 fromFirst
= from_hp
;
1756 if (to_hp
->h_meta
->h_usecount
> 1) {
1758 simple_lock(&to_hp
->h_meta
->h_siblinglock
);
1759 toFirst
= to_hp
->h_meta
->h_siblinghead
.cqh_first
;
1760 toSecond
= toFirst
->h_sibling
.cqe_next
;
1761 simple_unlock(&to_hp
->h_meta
->h_siblinglock
);
1763 if (toFirst
== to_hp
) {
1764 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1765 toSecond
= NULL
; /* its going away */
1767 VOP_UNLOCK(HTOV(to_hp
), 0, ap
->a_p
);
1768 if (vget(HTOV(toFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1769 toFirst
= NULL
; /* its going away */
1770 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1771 toSecond
= NULL
; /* its going away */
1779 /* Ignore any errors, we are doing a 'best effort' on flushing */
1781 (void) vinvalbuf(HTOV(fromFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1783 (void) vinvalbuf(HTOV(fromSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1785 (void) vinvalbuf(HTOV(toFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1787 (void) vinvalbuf(HTOV(toSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1790 /* lock catalog b-tree */
1791 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1792 if (retval
) goto Err_Exit
;
1794 /* lock extents b-tree iff there are overflow extents */
1795 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1796 so we neeed the tree to be always locked.
1798 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1799 if (retval
) goto Err_Exit_Relse
;
1801 /* Do the exchange */
1802 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
) ));
1804 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
1806 if (retval
!= E_NONE
) {
1807 DBG_ERR(("/tError trying to exchange: %d\n", retval
));
1808 goto Err_Exit_Relse
;
1812 /* Purge the vnodes from the name */
1814 cache_purge(HTOV(fromFirst
));
1816 cache_purge(HTOV(fromSecond
));
1818 cache_purge(HTOV(toFirst
));
1820 cache_purge(HTOV(toSecond
));
1822 /* Now exchange fileID, parID, name for the vnode itself */
1823 copystr(H_NAME(from_hp
), (char*) tmp_name
, strlen(H_NAME(from_hp
))+1, NULL
);
1824 hfs_chid(from_hp
, toFileID
, H_DIRID(to_hp
), H_NAME(to_hp
));
1825 hfs_chid(to_hp
, fromFileID
, fromParID
, (char*) tmp_name
);
1828 tmpLong
= HTOFCB(from_hp
)->fcbFlags
;
1829 HTOFCB(from_hp
)->fcbFlags
= HTOFCB(to_hp
)->fcbFlags
;
1830 HTOFCB(to_hp
)->fcbFlags
= tmpLong
;
1832 tmpLong
= from_hp
->h_meta
->h_crtime
;
1833 from_hp
->h_meta
->h_crtime
= to_hp
->h_meta
->h_crtime
;
1834 to_hp
->h_meta
->h_crtime
= tmpLong
;
1836 tmpLong
= from_hp
->h_meta
->h_butime
;
1837 from_hp
->h_meta
->h_butime
= to_hp
->h_meta
->h_butime
;
1838 to_hp
->h_meta
->h_butime
= tmpLong
;
1840 tmpLong
= from_hp
->h_meta
->h_atime
;
1841 from_hp
->h_meta
->h_atime
= to_hp
->h_meta
->h_atime
;
1842 to_hp
->h_meta
->h_atime
= tmpLong
;
1844 tmpLong
= from_hp
->h_meta
->h_ctime
;
1845 from_hp
->h_meta
->h_ctime
= to_hp
->h_meta
->h_ctime
;
1846 to_hp
->h_meta
->h_ctime
= tmpLong
;
1848 tmpLong
= from_hp
->h_meta
->h_gid
;
1849 from_hp
->h_meta
->h_gid
= to_hp
->h_meta
->h_gid
;
1850 to_hp
->h_meta
->h_gid
= tmpLong
;
1852 tmpLong
= from_hp
->h_meta
->h_uid
;
1853 from_hp
->h_meta
->h_uid
= to_hp
->h_meta
->h_uid
;
1854 to_hp
->h_meta
->h_uid
= tmpLong
;
1856 tmpLong
= from_hp
->h_meta
->h_pflags
;
1857 from_hp
->h_meta
->h_pflags
= to_hp
->h_meta
->h_pflags
;
1858 to_hp
->h_meta
->h_pflags
= tmpLong
;
1860 tmpLong
= from_hp
->h_meta
->h_mode
;
1861 from_hp
->h_meta
->h_mode
= to_hp
->h_meta
->h_mode
;
1862 to_hp
->h_meta
->h_mode
= tmpLong
;
1864 tmpLong
= from_hp
->h_meta
->h_rdev
;
1865 from_hp
->h_meta
->h_rdev
= to_hp
->h_meta
->h_rdev
;
1866 to_hp
->h_meta
->h_rdev
= tmpLong
;
1868 tmpLong
= from_hp
->h_meta
->h_size
;
1869 from_hp
->h_meta
->h_size
= to_hp
->h_meta
->h_size
;
1870 to_hp
->h_meta
->h_size
= tmpLong
;
1876 /* unlock catalog b-tree */
1877 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1884 * At this point, the vnodes' data is switched, but are on the old hash list.
1885 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1888 * A fork is trying to be added while exchanging...It got the hash lock,
1889 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1890 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1891 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1892 * Not very pretty, becarefull that this works ok
1894 * Same as the above, but before the move is made (like at this very spot), the new vnode
1895 * is added and a vget is requested for that new vnode, it would have old data
1896 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1898 * Hey! Same as above, but it is added after all the moving
1899 * So now there is a vnode with the old data, on the old hash...it will become
1900 * lost next time that a vget() is done
1902 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1903 * opposite that we are doing now
1905 hfs_vhashmove(from_hp
, fromFileID
);
1906 hfs_vhashmove(to_hp
, toFileID
);
1911 debug_check_vnode(HTOV(fromFirst
), 0);
1913 debug_check_vnode(HTOV(fromSecond
), 0);
1915 debug_check_vnode(HTOV(toFirst
), 0);
1917 debug_check_vnode(HTOV(toSecond
), 0);
1921 /* Unlock any forks, and the sibling list */
1922 if (to_hp
->h_meta
->h_usecount
> 1) {
1923 if (to_hp
== toFirst
) {
1925 vput(HTOV(toSecond
));
1928 vrele(HTOV(toSecond
)); /* decrement, return it locked */
1930 vput(HTOV(toFirst
));
1933 if (from_hp
->h_meta
->h_usecount
> 1) {
1934 if (from_hp
== fromFirst
) {
1936 vput(HTOV(fromSecond
));
1939 vrele(HTOV(fromSecond
)); /* decrement, return it locked */
1941 vput(HTOV(fromFirst
));
1945 DBG_VOP_LOCKS_TEST(retval
);
1951 * Change a vnode's file id, parent id and name
1953 * Assumes the vnode is locked and is of type VREG
1956 hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
)
1958 DBG_ASSERT(HTOV(hp
)->v_type
== VREG
);
1961 H_FILEID(hp
) = fid
; /* change h_nodeID */
1964 hfs_set_metaname(name
, hp
->h_meta
, HTOHFS(hp
));
1975 IN struct vnode *vp;
1976 IN struct ucred *cred;
1985 struct vop_fsync_args
/* {
1987 struct ucred *a_cred;
1992 struct vnode
*vp
= ap
->a_vp
;
1993 struct hfsnode
*hp
= VTOH(vp
);
1995 register struct buf
*bp
;
2000 DBG_FUNC_NAME("fsync");
2001 DBG_VOP_LOCKS_DECL(1);
2002 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
2003 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2004 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
2005 DBG_HFS_NODE_CHECK(ap
->a_vp
);
2008 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2013 * First of all, write out any clusters.
2018 * Flush all dirty buffers associated with a vnode.
2022 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2023 nbp
= bp
->b_vnbufs
.le_next
;
2024 if ((bp
->b_flags
& B_BUSY
))
2026 if ((bp
->b_flags
& B_DELWRI
) == 0)
2027 panic("hfs_fsync: not dirty");
2029 bp
->b_flags
|= B_BUSY
;
2030 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2033 * Wait for I/O associated with indirect blocks to complete,
2034 * since there is no way to quickly wait for them below.
2036 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp
->b_blkno
, bp
->b_lblkno
));
2037 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
) {
2040 (void) VOP_BWRITE(bp
);
2044 if (vp
->v_flag
& VHASDIRTY
)
2047 if (ap
->a_waitfor
== MNT_WAIT
) {
2048 while (vp
->v_numoutput
) {
2049 vp
->v_flag
|= VBWAIT
;
2050 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
2053 /* I have seen this happen for swapfile. So it is safer to
2054 * check for dirty buffers again. --Umesh
2056 if (vp
->v_dirtyblkhd
.lh_first
|| (vp
->v_flag
& VHASDIRTY
)) {
2057 vprint("hfs_fsync: dirty", vp
);
2065 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2069 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2070 BTSetLastSync(HTOFCB(hp
), tv
.tv_sec
);
2072 if (H_FORKTYPE(hp
) != kSysFile
) {
2073 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, ap
->a_waitfor
== MNT_WAIT
);
2075 if (retval
!= E_NONE
) {
2076 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname
, H_NAME(hp
)));
2080 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2082 if (ap
->a_waitfor
== MNT_WAIT
) {
2083 DBG_ASSERT(vp
->v_dirtyblkhd
.lh_first
== NULL
);
2085 DBG_VOP_LOCKS_TEST(retval
);
2086 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2092 hfs_fsync_transaction(struct vnode
*vp
)
2094 struct hfsnode
*hp
= VTOH(vp
);
2095 register struct buf
*bp
;
2101 * Flush all dirty buffers associated with a vnode.
2106 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2107 nbp
= bp
->b_vnbufs
.le_next
;
2108 if ((bp
->b_flags
& B_BUSY
))
2110 if ((bp
->b_flags
& B_DELWRI
) == 0)
2111 panic("hfs_fsync: not dirty");
2112 if ( !(bp
->b_flags
& B_LOCKED
))
2116 bp
->b_flags
|= B_BUSY
;
2117 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2127 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2128 (void) BTSetLastSync(VTOFCB(vp
), tv
.tv_sec
);
2129 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2140 IN WILLRELE struct vnode *dvp;
2141 IN WILLRELE struct vnode *vp;
2142 IN struct componentname *cnp;
2148 struct vop_remove_args
/* {
2149 struct vnode *a_dvp;
2151 struct componentname *a_cnp;
2154 struct vnode
*vp
= ap
->a_vp
;
2155 struct vnode
*dvp
= ap
->a_dvp
;
2156 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
2157 struct hfsmount
*hfsmp
= HTOHFS(hp
);
2158 struct proc
*p
= current_proc();
2160 int retval
, use_count
;
2162 DBG_FUNC_NAME("remove");
2163 DBG_VOP_LOCKS_DECL(2);
2164 DBG_VOP_PRINT_FUNCNAME();
2165 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
2166 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2167 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2168 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2172 if ((hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2173 (VTOH(dvp
)->h_meta
->h_pflags
& APPEND
)) {
2178 if (vp
->v_usecount
> 1) {
2180 * the namei done for the rename took a reference on the
2181 * vnode. Hence set 1 in the tookref parameter
2184 if(UBCISVALID(vp
) && !ubc_isinuse(vp
, 1))
2186 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2187 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2188 /* Carbon semantics prohibits deleting busy files */
2197 tv
= time
; /* Done here, so all times are the same */
2199 /* Check other siblings for in use also */
2200 /* Uncache everything and make sure no other usecount */
2202 * This assumes the presence of the most 1 sibling
2204 * a. loop through the siblings looking for another
2205 * b. If we find ourselves...skip it
2206 * If there was a sibling:
2207 * a. Check for a positve usecount
2208 * b. uncache any pages
2209 * c. Write out and memory changes
2210 * The idea is to keep the h_siblinglock as little as possible
2212 if (hp
->h_meta
->h_usecount
> 1) {
2213 struct vnode
*sib_vp
= NULL
;
2214 struct hfsnode
*nhp
;
2216 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
2217 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
2218 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
2220 /* Loop through all siblings, skipping ourselves */
2221 simple_lock(&hp
->h_meta
->h_siblinglock
);
2222 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
2223 if (nhp
== hp
) /* skip ourselves */
2227 simple_unlock(&hp
->h_meta
->h_siblinglock
);
2229 /* Check to see if the other fork is in use */
2230 DBG_ASSERT(sib_vp
!= NULL
);
2231 simple_lock(&sib_vp
->v_interlock
);
2232 use_count
= sib_vp
->v_usecount
;
2233 simple_unlock(&sib_vp
->v_interlock
);
2234 if (use_count
> 0) {
2236 * This is a sibling vnode and we did not take
2237 * a reference on it.
2238 * Hence set 0 in the tookref parameter
2241 if(UBCISVALID(sib_vp
) && !ubc_isinuse(sib_vp
, 0))
2243 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2244 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2245 /* Carbon semantics prohibits deleting busy files */
2250 } /* use_count > 0 */
2254 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2255 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
2257 * XXX SER An intelligient person would ask, why flush out changes
2258 * that are going to be deleted? See the next comment.
2260 if ((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) || (VTOFCB(sib_vp
)->fcbFlags
2261 & fcbModifiedMask
)) {
2262 DBG_ASSERT((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) != 0);
2263 VOP_UPDATE(sib_vp
, &tv
, &tv
, 0);
2266 /* Invalidate the buffers, ignore the results */
2267 (void) vinvalbuf(sib_vp
, 0, NOCRED
, p
, 0, 0);
2271 }; /* h_use_count > 1 */
2274 * remove the entry from the namei cache:
2275 * We do it early before any linking/busy file wierdness, make sure the
2280 /* Flush out any catalog changes */
2281 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2282 * and not from memory which is more correct
2284 if ((hp
->h_nodeflags
& IN_MODIFIED
) || (HTOFCB(hp
)->fcbFlags
& fcbModifiedMask
))
2286 DBG_ASSERT((hp
->h_nodeflags
& IN_MODIFIED
) != 0);
2287 VOP_UPDATE(vp
, &tv
, &tv
, 0);
2290 /* lock catalog b-tree */
2291 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2292 if (retval
!= E_NONE
) {
2298 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2303 * Multi-linked files just need their link node deleted from the catalog
2305 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
2307 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
2308 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
2309 retval
= ENOENT
; /* name missing */
2313 /* lock extents b-tree (also protects volume bitmap) */
2314 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2315 if (retval
!= E_NONE
) {
2317 goto out2
; /* unlock catalog b-tree on the way out */
2320 retval
= hfsDelete (HTOVCB(hp
), H_FILEID(VTOH(dvp
)),
2321 ap
->a_cnp
->cn_nameptr
, TRUE
, H_HINT(hp
));
2323 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2328 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2329 hp
->h_nodeflags
|= IN_CHANGE
;
2330 if (--hp
->h_meta
->h_nlink
< 1)
2331 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2333 /* name and parent fields are no longer valid so invalidate them */
2334 H_DIRID(hp
) = kUnknownID
;
2335 hfs_set_metaname("\0", hp
->h_meta
, HTOHFS(hp
));
2337 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
2338 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2340 goto out2
; /* link deleted, all done */
2345 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2346 * an active vnode is translated to a move/rename so the file appears
2347 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2350 UInt32 hint
= H_HINT(hp
);
2353 MAKE_DELETED_NAME(nodeName
, H_FILEID(hp
));
2355 retval
= hfsMoveRename (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
),
2356 hfsmp
->hfs_private_metadata_dir
, nodeName
, &hint
);
2357 if (retval
) goto out2
;
2359 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2360 hp
->h_nodeflags
|= IN_CHANGE
;
2362 /* update name so Catalog lookups succeed */
2364 H_DIRID(hp
) = hfsmp
->hfs_private_metadata_dir
;
2365 hfs_set_metaname(nodeName
, hp
->h_meta
, HTOHFS(hp
));
2367 goto out2
; /* all done, unlock the catalog */
2370 /* unlock the Catalog */
2371 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2373 /* Invalidate the buffers */
2374 if ((retval
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0)))
2377 if(UBCINFOEXISTS(vp
))
2378 (void)ubc_setsize(vp
, (off_t
)0);
2381 /* lock catalog b-tree */
2382 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2383 if (retval
!= E_NONE
) {
2387 /* lock extents b-tree (also protects volume bitmap) */
2388 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2389 if (retval
!= E_NONE
) {
2391 goto out2
; /* unlock catalog b-tree on the way out */
2394 /* remove entry from catalog and free any blocks used */
2395 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
2398 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2399 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2404 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2405 hp
->h_meta
->h_mode
= 0; /* Makes the node go away...see inactive */
2406 /* clear the block mappings */
2407 hp
->fcbPLen
= (u_int64_t
)0;
2408 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
2410 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2419 DBG_VOP_LOCKS_TEST(retval
);
2421 if (UBCINFOEXISTS(vp
)) {
2422 (void) ubc_uncache(vp
);
2424 /* WARNING vp may not be valid after this */
2429 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2434 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2443 DBG_VOP_LOCKS_TEST(retval
);
2450 #% rename sourcePar_vp U U U
2451 #% rename source_vp U U U
2452 #% rename targetPar_vp L U U
2453 #% rename target_vp X U U
2456 IN WILLRELE struct vnode *sourcePar_vp;
2457 IN WILLRELE struct vnode *source_vp;
2458 IN struct componentname *source_cnp;
2459 IN WILLRELE struct vnode *targetPar_vp;
2460 IN WILLRELE struct vnode *target_vp;
2461 IN struct componentname *target_cnp;
2467 * source's parent directory is unlocked
2468 * source file or directory is unlocked
2469 * destination's parent directory is locked
2470 * destination file or directory is locked if it exists
2473 * all denodes should be released
2479 struct vop_rename_args
/* {
2480 struct vnode *a_fdvp;
2481 struct vnode *a_fvp;
2482 struct componentname *a_fcnp;
2483 struct vnode *a_tdvp;
2484 struct vnode *a_tvp;
2485 struct componentname *a_tcnp;
2488 struct vnode
*target_vp
= ap
->a_tvp
;
2489 struct vnode
*targetPar_vp
= ap
->a_tdvp
;
2490 struct vnode
*source_vp
= ap
->a_fvp
;
2491 struct vnode
*sourcePar_vp
= ap
->a_fdvp
;
2492 struct componentname
*target_cnp
= ap
->a_tcnp
;
2493 struct componentname
*source_cnp
= ap
->a_fcnp
;
2494 struct proc
*p
= source_cnp
->cn_proc
;
2495 struct hfsnode
*target_hp
, *targetPar_hp
, *source_hp
, *sourcePar_hp
;
2496 u_int32_t oldparent
= 0, newparent
= 0;
2497 int doingdirectory
= 0;
2500 struct hfsCatalogInfo catInfo
;
2501 DBG_VOP_LOCKS_DECL(4);
2503 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2504 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fvp
);DBG_VOP_CONT(("\n"));
2505 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fdvp
);DBG_VOP_CONT(("\n"));
2506 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tvp
);DBG_VOP_CONT(("\n"));
2507 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tdvp
);DBG_VOP_CONT(("\n"));
2508 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_fcnp
);DBG_VOP_CONT(("\n"));
2509 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_tcnp
);DBG_VOP_CONT(("\n"));
2510 DBG_VOP_LOCKS_INIT(0,ap
->a_fdvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2511 DBG_VOP_LOCKS_INIT(1,ap
->a_fvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2512 DBG_VOP_LOCKS_INIT(2,ap
->a_tdvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2513 DBG_VOP_LOCKS_INIT(3,ap
->a_tvp
, VOPDBG_LOCKNOTNIL
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2514 WRITE_CK(ap
->a_fdvp
, funcname
);
2515 DBG_HFS_NODE_CHECK(ap
->a_fdvp
);
2516 DBG_HFS_NODE_CHECK(ap
->a_tdvp
);
2519 if ((target_cnp
->cn_flags
& HASBUF
) == 0 ||
2520 (source_cnp
->cn_flags
& HASBUF
) == 0)
2521 panic("hfs_rename: no name");
2524 DBG_ASSERT((ap
->a_fdvp
->v_type
== VDIR
) && (ap
->a_tdvp
->v_type
== VDIR
));
2525 target_hp
= targetPar_hp
= source_hp
= sourcePar_hp
= 0;
2528 * Check for cross-device rename.
2530 if ((source_vp
->v_mount
!= targetPar_vp
->v_mount
) ||
2531 (target_vp
&& (source_vp
->v_mount
!= target_vp
->v_mount
))) {
2537 * Check for access permissions
2539 if (target_vp
&& ((VTOH(target_vp
)->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2540 (VTOH(targetPar_vp
)->h_meta
->h_pflags
& APPEND
))) {
2545 if ((retval
= vn_lock(source_vp
, LK_EXCLUSIVE
, p
)))
2548 sourcePar_hp
= VTOH(sourcePar_vp
);
2549 source_hp
= VTOH(source_vp
);
2550 oldparent
= H_FILEID(sourcePar_hp
);
2551 if ((source_hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) || (sourcePar_hp
->h_meta
->h_pflags
& APPEND
)) {
2552 VOP_UNLOCK(source_vp
, 0, p
);
2558 * Be sure we are not renaming ".", "..", or an alias of ".". This
2559 * leads to a crippled directory tree. It's pretty tough to do a
2560 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2561 * doesn't work if the ".." entry is missing.
2563 if ((source_hp
->h_meta
->h_mode
& IFMT
) == IFDIR
) {
2564 if ((source_cnp
->cn_namelen
== 1 && source_cnp
->cn_nameptr
[0] == '.')
2565 || sourcePar_hp
== source_hp
2566 || (source_cnp
->cn_flags
&ISDOTDOT
)
2567 || (source_hp
->h_nodeflags
& IN_RENAME
)) {
2568 VOP_UNLOCK(source_vp
, 0, p
);
2572 source_hp
->h_nodeflags
|= IN_RENAME
;
2573 doingdirectory
= TRUE
;
2578 * >>>> Transit between abort and bad <<<<
2582 targetPar_hp
= VTOH(targetPar_vp
);
2584 target_hp
= VTOH(target_vp
);
2586 DBG_ASSERT(target_hp
== NULL
);
2588 newparent
= H_FILEID(targetPar_hp
);
2590 /* Test to make sure we are not crossing devices */
2591 /* XXX SER Is this necesary, does catalog manager take care of this? */
2593 if (H_DEV(target_hp
) != H_DEV(targetPar_hp
) || H_DEV(target_hp
) != H_DEV(source_hp
))
2594 panic("rename: EXDEV");
2597 if (H_DEV(targetPar_hp
) != H_DEV(source_hp
))
2598 panic("rename: EXDEV");
2601 retval
= VOP_ACCESS(source_vp
, VWRITE
, target_cnp
->cn_cred
, target_cnp
->cn_proc
);
2602 if (doingdirectory
&& (newparent
!= oldparent
)) {
2603 if (retval
) /* write access check above */
2606 retval
= 0; /* Reset value from above, we dont care about it anymore */
2609 * If the destination exists, then be sure its type (file or dir)
2610 * matches that of the source. And, if it is a directory make sure
2611 * it is empty. Then delete the destination.
2616 * If the parent directory is "sticky", then the user must
2617 * own the parent directory, or the destination of the rename,
2618 * otherwise the destination may not be changed (except by
2619 * root). This implements append-only directories.
2621 if ((targetPar_hp
->h_meta
->h_mode
& S_ISTXT
) && (target_cnp
->cn_cred
->cr_uid
!= 0) &&
2622 target_cnp
->cn_cred
->cr_uid
!= targetPar_hp
->h_meta
->h_uid
&&
2623 target_cnp
->cn_cred
->cr_uid
!= target_hp
->h_meta
->h_uid
) {
2629 * VOP_REMOVE will vput targetPar_vp so we better bump
2630 * its ref count and relockit, always set target_vp to
2631 * NULL afterwards to indicate that were done with it.
2635 cache_purge(target_vp
);
2638 target_cnp
->cn_flags
&= ~SAVENAME
;
2641 retval
= VOP_REMOVE(targetPar_vp
, target_vp
, target_cnp
);
2642 (void) vn_lock(targetPar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2647 if (retval
) goto bad
;
2652 if (newparent
!= oldparent
)
2653 vn_lock(sourcePar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2655 /* remove the existing entry from the namei cache: */
2656 cache_purge(source_vp
);
2658 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
2660 /* lock catalog b-tree */
2661 retval
= hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2663 if (newparent
!= oldparent
) /* unlock the lock we just got */
2664 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2668 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2669 retval
= hfsMoveRename( HTOVCB(source_hp
), H_DIRID(source_hp
), source_cnp
->cn_nameptr
,
2670 H_FILEID(VTOH(targetPar_vp
)), target_cnp
->cn_nameptr
, &H_HINT(source_hp
));
2673 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2674 catInfo
.hint
= H_HINT(source_hp
);
2675 retval
= hfs_getcatalog(HTOVCB(source_hp
), H_FILEID(targetPar_hp
), target_cnp
->cn_nameptr
, target_cnp
->cn_namelen
, &catInfo
);
2678 /* unlock catalog b-tree */
2679 (void) hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2681 if (newparent
!= oldparent
)
2682 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2684 if (retval
) goto bad
;
2686 H_DIRID(source_hp
) = H_FILEID(targetPar_hp
);
2688 hfs_name_CatToMeta(&catInfo
.nodeData
, source_hp
->h_meta
);
2690 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
2692 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2696 * Timestamp both parent directories.
2697 * Note that if this is a rename within the same directory,
2698 * (where targetPar_hp == sourcePar_hp)
2699 * the code below is still safe and correct.
2701 targetPar_hp
->h_nodeflags
|= IN_UPDATE
;
2702 sourcePar_hp
->h_nodeflags
|= IN_UPDATE
;
2704 HFSTIMES(targetPar_hp
, &tv
, &tv
);
2705 HFSTIMES(sourcePar_hp
, &tv
, &tv
);
2708 vrele(sourcePar_vp
);
2711 DBG_VOP_LOCKS_TEST(retval
);
2712 if (retval
!= E_NONE
) {
2713 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2718 if (retval
&& doingdirectory
)
2719 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2721 if (targetPar_vp
== target_vp
)
2722 vrele(targetPar_vp
);
2729 vrele(sourcePar_vp
);
2731 if (VOP_ISLOCKED(source_vp
))
2736 DBG_VOP_LOCKS_TEST(retval
);
2737 if (retval
!= E_NONE
) {
2738 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2744 VOP_ABORTOP(targetPar_vp
, target_cnp
); /* XXX, why not in NFS? */
2746 if (targetPar_vp
== target_vp
)
2747 vrele(targetPar_vp
);
2754 VOP_ABORTOP(sourcePar_vp
, source_cnp
); /* XXX, why not in NFS? */
2756 vrele(sourcePar_vp
);
2759 DBG_VOP_LOCKS_TEST(retval
);
2760 if (retval
!= E_NONE
) {
2761 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2774 IN WILLRELE struct vnode *dvp;
2775 OUT struct vnode **vpp;
2776 IN struct componentname *cnp;
2777 IN struct vattr *vap;
2779 We are responsible for freeing the namei buffer,
2780 it is done in hfs_makenode()
2785 struct vop_mkdir_args
/* {
2786 struct vnode *a_dvp;
2787 struct vnode **a_vpp;
2788 struct componentname *a_cnp;
2789 struct vattr *a_vap;
2792 struct proc
*p
= current_proc();
2794 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
2796 DBG_FUNC_NAME("mkdir");
2797 DBG_VOP_LOCKS_DECL(2);
2798 DBG_VOP_PRINT_FUNCNAME();
2799 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
2800 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2802 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2803 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2805 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
));
2806 WRITE_CK( ap
->a_dvp
, funcname
);
2807 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
2808 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
2810 /* Create the vnode */
2811 DBG_ASSERT((ap
->a_cnp
->cn_flags
& SAVESTART
) == 0);
2812 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
2813 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2815 if (retval
!= E_NONE
) {
2816 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
2817 DBG_VOP_LOCKS_TEST(retval
);
2821 DBG_VOP_LOCKS_TEST(E_NONE
);
2826 * Rmdir system call.
2831 IN WILLRELE struct vnode *dvp;
2832 IN WILLRELE struct vnode *vp;
2833 IN struct componentname *cnp;
2839 struct vop_rmdir_args
/* {
2840 struct vnode *a_dvp;
2842 struct componentname *a_cnp;
2845 struct vnode
*vp
= ap
->a_vp
;
2846 struct vnode
*dvp
= ap
->a_dvp
;
2847 struct hfsnode
*hp
= VTOH(vp
);
2848 struct proc
*p
= current_proc();
2850 DBG_FUNC_NAME("rmdir");
2851 DBG_VOP_LOCKS_DECL(2);
2852 DBG_VOP_PRINT_FUNCNAME();
2853 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);DBG_VOP_CONT(("\n"));
2854 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2855 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2857 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2858 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2863 DBG_VOP_LOCKS_TEST(EINVAL
);
2868 * HFS differs from UFS here in that we don't allow removing
2869 * a directory that in use by others - even if its empty.
2871 * In the future we might want to allow this just like we do
2872 * for files (by renaming the busy directory).
2875 if (vp
->v_usecount
> 1) {
2876 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname
, vp
->v_usecount
));
2881 /* remove the entry from the namei cache: */
2884 /* lock catalog b-tree */
2885 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2886 if (retval
!= E_NONE
) {
2890 /* remove entry from catalog */
2891 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), FALSE
, H_HINT(hp
));
2893 /* unlock catalog b-tree */
2894 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2897 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
; /* Set the parent to be updated */
2898 hp
->h_meta
->h_mode
= 0; /* Makes the vnode go away...see inactive */
2899 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2907 DBG_VOP_LOCKS_TEST(retval
);
2912 * symlink -- make a symbolic link
2913 #% symlink dvp L U U
2914 #% symlink vpp - U -
2916 # XXX - note that the return vnode has already been VRELE'ed
2917 # by the filesystem layer. To use it you must use vget,
2918 # possibly with a further namei.
2921 IN WILLRELE struct vnode *dvp;
2922 OUT WILLRELE struct vnode **vpp;
2923 IN struct componentname *cnp;
2924 IN struct vattr *vap;
2927 We are responsible for freeing the namei buffer,
2928 it is done in hfs_makenode().
2934 struct vop_symlink_args
/* {
2935 struct vnode *a_dvp;
2936 struct vnode **a_vpp;
2937 struct componentname *a_cnp;
2938 struct vattr *a_vap;
2942 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
2943 struct proc
*p
= current_proc();
2945 u_int32_t dfltClump
;
2947 DBG_FUNC_NAME("symlink");
2948 DBG_VOP_LOCKS_DECL(2);
2949 DBG_VOP_PRINT_FUNCNAME();
2950 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2951 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2953 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
2954 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
2956 DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
2957 DBG_VOP_LOCKS_TEST(EOPNOTSUPP
);
2958 return (EOPNOTSUPP
);
2961 /* Create the vnode */
2962 retval
= hfs_makenode(IFLNK
| ap
->a_vap
->va_mode
, 0, ap
->a_dvp
,
2964 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2966 if (retval
!= E_NONE
) {
2967 DBG_VOP_LOCKS_TEST(retval
);
2973 len
= strlen(ap
->a_target
);
2975 dfltClump
= hp
->fcbClmpSize
;
2976 /* make clump size minimal */
2977 hp
->fcbClmpSize
= VTOVCB(vp
)->blockSize
;
2978 retval
= vn_rdwr(UIO_WRITE
, vp
, ap
->a_target
, len
, (off_t
)0,
2979 UIO_SYSSPACE
, IO_NODELOCKED
, ap
->a_cnp
->cn_cred
, (int *)0,
2981 hp
->fcbClmpSize
= dfltClump
;
2985 DBG_VOP_LOCKS_TEST(retval
);
2991 * Dummy dirents to simulate the "." and ".." entries of the directory
2992 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2993 * the size of these entries is the smallest needed to represent them
2994 * (only 12 byte each).
2996 static hfsdotentry rootdots
[2] = {
2999 sizeof(struct hfsdotentry
), /* d_reclen */
3000 DT_DIR
, /* d_type */
3006 sizeof(struct hfsdotentry
), /* d_reclen */
3007 DT_DIR
, /* d_type */
3013 static hfsdotentry emptyentry
= { 0 };
3016 * There is some confusion as to what the semantics of uio_offset are.
3017 * In ufs, it represents the actual byte offset within the directory
3018 * "file." HFS, however, just uses it as an entry counter - essentially
3019 * assuming that it has no meaning except to the hfs_readdir function.
3020 * This approach would be more efficient here, but some callers may
3021 * assume the uio_offset acts like a byte offset. NFS in fact
3022 * monkeys around with the offset field a lot between readdir calls.
3024 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3025 * fields is a mess as well. The libc function readdir() returns
3026 * NULL (indicating the end of a directory) when either
3027 * the getdirentries() syscall (which calls this and returns
3028 * the size of the buffer passed in less the value of uiop->uio_resid)
3029 * returns 0, or a direct record with a d_reclen of zero.
3030 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3031 * of the directory by testing uiop->uio_resid == 0. The solution
3032 * is to pad the size of the last struct direct in a given
3033 * block to fill the block if we are not at the end of the directory.
3036 struct callbackstate
{
3037 u_int32_t cbs_parentID
;
3038 u_int32_t cbs_hiddenDirID
;
3039 off_t cbs_lastoffset
;
3040 struct uio
* cbs_uio
;
3041 ExtendedVCB
* cbs_vcb
;
3042 int16_t cbs_hfsPlus
;
3048 ProcessCatalogEntry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3049 u_int16_t recordLen
, struct callbackstate
*state
)
3055 struct dirent catent
;
3057 if (state
->cbs_hfsPlus
)
3058 curID
= ckp
->hfsPlus
.parentID
;
3060 curID
= ckp
->hfs
.parentID
;
3062 /* We're done when parent directory changes */
3063 if (state
->cbs_parentID
!= curID
) {
3066 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3067 * so remove padding for now...
3071 * Pad the end of list with an empty record.
3072 * This eliminates an extra call by readdir(3c).
3074 catent
.d_fileno
= 0;
3075 catent
.d_reclen
= 0;
3077 catent
.d_namlen
= 0;
3078 *(int32_t*)&catent
.d_name
[0] = 0;
3080 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3082 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
3083 if (state
->cbs_result
== 0)
3084 state
->cbs_result
= ENOENT
;
3086 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3087 state
->cbs_result
= ENOENT
;
3089 return (0); /* stop */
3092 if (state
->cbs_hfsPlus
) {
3093 switch(crp
->recordType
) {
3094 case kHFSPlusFolderRecord
:
3095 catent
.d_type
= DT_DIR
;
3096 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
3098 case kHFSPlusFileRecord
:
3099 catent
.d_type
= DT_REG
;
3100 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
3103 return (0); /* stop */
3106 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3107 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
3108 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
3109 if (result
== ENAMETOOLONG
) {
3110 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3111 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
3114 switch(crp
->recordType
) {
3115 case kHFSFolderRecord
:
3116 catent
.d_type
= DT_DIR
;
3117 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
3119 case kHFSFileRecord
:
3120 catent
.d_type
= DT_REG
;
3121 catent
.d_fileno
= crp
->hfsFile
.fileID
;
3124 return (0); /* stop */
3127 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
3128 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3129 (ByteCount
*)&utf8chars
, catent
.d_name
);
3131 * When an HFS name cannot be encoded with the current
3132 * volume encoding we use MacRoman as a fallback.
3135 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3136 (ByteCount
*)&utf8chars
, catent
.d_name
);
3139 catent
.d_namlen
= utf8chars
;
3140 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
3142 /* hide our private meta data directory */
3143 if (curID
== kRootDirID
&&
3144 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
3145 catent
.d_type
== DT_DIR
)
3148 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3150 /* if this entry won't fit then we're done */
3151 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
)
3152 return (0); /* stop */
3154 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
3156 /* continue iteration if there's room */
3157 return (state
->cbs_result
== 0 &&
3158 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
3162 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3163 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3164 * "Nbytes must be greater than or equal to the block size associated with the file,
3165 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3166 * room for the . and .. entries that have to added manually.
3173 IN struct vnode *vp;
3174 INOUT struct uio *uio;
3175 IN struct ucred *cred;
3178 INOUT u_long **cookies;
3182 struct vop_readdir_args
/* {
3191 register struct uio
*uio
= ap
->a_uio
;
3192 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3193 struct proc
*p
= current_proc();
3194 ExtendedVCB
*vcb
= HTOVCB(hp
);
3195 off_t off
= uio
->uio_offset
;
3196 u_int32_t dirID
= H_FILEID(hp
);
3198 OSErr result
= noErr
;
3199 u_int32_t diroffset
;
3201 CatalogIterator
*cip
;
3203 struct callbackstate state
;
3206 DBG_FUNC_NAME("readdir");
3207 DBG_VOP_LOCKS_DECL(1);
3209 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3210 DBG_VOP_PRINT_FUNCNAME();
3211 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3212 DBG_HFS_NODE_CHECK(ap
->a_vp
);
3214 /* We assume it's all one big buffer... */
3215 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
) {
3219 /* Create the entries for . and .. */
3220 if (uio
->uio_offset
< sizeof(rootdots
)) {
3224 rootdots
[0].d_fileno
= dirID
;
3225 rootdots
[1].d_fileno
= H_DIRID(hp
);
3227 if (uio
->uio_offset
== 0) {
3228 dep
= (caddr_t
) &rootdots
[0];
3229 dotsize
= 2* sizeof(struct hfsdotentry
);
3230 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
3231 dep
= (caddr_t
) &rootdots
[1];
3232 dotsize
= sizeof(struct hfsdotentry
);
3238 retval
= uiomove(dep
, dotsize
, uio
);
3243 diroffset
= uio
->uio_offset
;
3245 /* lock catalog b-tree */
3246 retval
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
3247 if (retval
!= E_NONE
)
3250 /* get an iterator and position it */
3251 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
3253 result
= PositionIterator(cip
, diroffset
, &bi
, &op
);
3254 if (result
== cmNotFound
) {
3257 AgeCatalogIterator(cip
);
3259 } else if ((retval
= MacToVFSError(result
)))
3262 state
.cbs_hiddenDirID
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
3263 state
.cbs_lastoffset
= cip
->currentOffset
;
3264 state
.cbs_vcb
= vcb
;
3265 state
.cbs_uio
= uio
;
3266 state
.cbs_result
= 0;
3267 state
.cbs_parentID
= dirID
;
3269 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3270 state
.cbs_hfsPlus
= 1;
3272 state
.cbs_hfsPlus
= 0;
3274 /* process as many entries as possible... */
3275 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
, &bi
,
3276 (IterateCallBackProcPtr
)ProcessCatalogEntry
, &state
);
3278 if (state
.cbs_result
)
3279 retval
= state
.cbs_result
;
3281 retval
= MacToVFSError(result
);
3283 if (retval
== ENOENT
) {
3289 cip
->currentOffset
= state
.cbs_lastoffset
;
3290 cip
->nextOffset
= uio
->uio_offset
;
3291 UpdateCatalogIterator(&bi
, cip
);
3298 AgeCatalogIterator(cip
);
3301 (void) ReleaseCatalogIterator(cip
);
3303 /* unlock catalog b-tree */
3304 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3306 if (retval
!= E_NONE
) {
3307 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname
, retval
,
3308 H_FILEID(hp
), H_NAME(hp
)));
3313 /* were we already past eof ? */
3314 if (uio
->uio_offset
== off
) {
3319 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3320 hp
->h_nodeflags
|= IN_ACCESS
;
3322 /* Bake any cookies */
3323 if (!retval
&& ap
->a_ncookies
!= NULL
) {
3324 struct dirent
* dpStart
;
3325 struct dirent
* dpEnd
;
3332 * Only the NFS server uses cookies, and it loads the
3333 * directory block into system space, so we can just look at
3336 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
3337 panic("hfs_readdir: unexpected uio from NFS server");
3338 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
3339 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
3340 for (dp
= dpStart
, ncookies
= 0;
3341 dp
< dpEnd
&& dp
->d_reclen
!= 0;
3342 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
3344 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
3345 for (dp
= dpStart
, cookiep
= cookies
;
3347 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
3348 off
+= dp
->d_reclen
;
3349 *cookiep
++ = (u_long
) off
;
3351 *ap
->a_ncookies
= ncookies
;
3352 *ap
->a_cookies
= cookies
;
3358 *ap
->a_eofflag
= eofflag
;
3360 DBG_VOP_LOCKS_TEST(retval
);
3366 * readdirattr operation will return attributes for the items in the
3367 * directory specified.
3369 * It does not do . and .. entries. The problem is if you are at the root of the
3370 * hfs directory and go to .. you could be crossing a mountpoint into a
3371 * different (ufs) file system. The attributes that apply for it may not
3372 * apply for the file system you are doing the readdirattr on. To make life
3373 * simpler, this call will only return entries in its directory, hfs like.
3375 * 1.getattrlist creates a thread record if the objpermanentid attribute
3376 * is requested. Just do EINVAL for now and fix later.
3377 * 2. more than one for uiovcnt support.
3378 * 3. put knohint (hints) in state for next call in
3379 * 4. credentials checking when rest of hfs does it.
3380 * 5. Do return permissions concatenation ???
3385 #% readdirattr vp L L L
3388 IN struct vnode *vp;
3389 IN struct attrlist *alist;
3390 INOUT struct uio *uio;
3393 OUT u_long *newstate;
3395 OUT u_long *actualCount;
3396 OUT u_long **cookies;
3397 IN struct ucred *cred;
3402 struct vop_readdirattr_args
/* {
3404 struct attrlist *alist;
3410 u_long *actualcount;
3415 struct vnode
*vp
= ap
->a_vp
;
3416 struct attrlist
*alist
= ap
->a_alist
;
3417 register struct uio
*uio
= ap
->a_uio
;
3418 u_long maxcount
= ap
->a_maxcount
;
3420 ExtendedVCB
*vcb
= HTOVCB(VTOH(vp
));
3421 UInt32 dirID
= H_FILEID(VTOH(vp
));
3422 struct proc
*proc
= current_proc(); /* could get this out of uio */
3423 off_t startoffset
= uio
->uio_offset
;
3424 struct hfsCatalogInfo catInfo
;
3427 u_long fixedblocksize
;
3428 u_long maxattrblocksize
;
3429 u_long currattrbufsize
;
3430 void *attrbufptr
= NULL
;
3433 struct vnode
*entryvnode
;
3436 *(ap
->a_actualcount
) = 0;
3437 *(ap
->a_eofflag
) = 0;
3439 /* check for invalid options, check vnode, and buffer space */
3440 if (((ap
->a_options
& ~FSOPT_NOINMEMUPDATE
) != 0) ||
3442 (uio
->uio_resid
<= 0) || (uio
->uio_iovcnt
> 1))
3445 /* this call doesn't take volume attributes */
3446 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
3447 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
3448 (alist
->volattr
!= 0) ||
3449 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
3450 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
3451 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0))
3454 /* Reject requests for unsupported options for now: */
3455 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
3456 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
)) ||
3457 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
) )
3460 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3461 * uiomove afterwards. It's an extra copy, but for now leave it alone
3463 fixedblocksize
= (sizeof(u_long
) + AttributeBlockSize(alist
)); /* u_long for length */
3464 maxattrblocksize
= fixedblocksize
;
3465 if (alist
->commonattr
& ATTR_CMN_NAME
)
3466 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
3467 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
3468 attrptr
= attrbufptr
;
3469 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3471 /* Since attributes passed back can contain variable ones (name), we can't just use
3472 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3473 * as hfs_readdir was originally doing. If this all we need to represent the current
3474 * state, then ap->a_state is not needed at all.
3476 /* index = ap->a_state; should not be less than 1 */
3477 index
= (uio
->uio_offset
/ sizeof(struct dirent
)) + 1;
3478 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
3480 /* Lock catalog b-tree */
3481 if ((retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, proc
)) != E_NONE
)
3484 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3485 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3486 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3488 catInfo
.hint
= kNoHint
; /* note, we may want to save the latest in state */
3489 while ((uio
->uio_resid
>= 0) && (maxcount
!=0 )) {
3490 /* better to check uio_resid against max or fixedblocksize, but won't work.
3491 * Depending on if dir or file, the attributes returned will be different.
3492 * Thus fixedblocksize is too large in some cases.Also, the variable
3493 * part (like name) could be between fixedblocksize and the max.
3495 OSErr result
= GetCatalogOffspring(vcb
, dirID
, index
, &catInfo
.nodeData
, NULL
, NULL
);
3496 if (result
!= noErr
) {
3497 if (result
== cmNotFound
) {
3498 *(ap
->a_eofflag
) = TRUE
;
3501 else retval
= MacToVFSError(result
);
3505 /* hide our private meta data directory as does hfs_readdir */
3506 if ((dirID
== kRootDirID
) &&
3507 catInfo
.nodeData
.cnd_nodeID
== VCBTOHFS(vcb
)->hfs_private_metadata_dir
&&
3508 catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) {
3514 *((u_long
*)attrptr
)++ = 0; /* move it past length */
3516 if (ap
->a_options
& FSOPT_NOINMEMUPDATE
) {
3517 /* vp okay to use instead of root vp */
3518 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3520 /* Check to see if there's a vnode for this item in the cache: */
3521 entryvnode
= hfs_vhashget(H_DEV(VTOH(vp
)), catInfo
.nodeData
.cnd_nodeID
, kDefault
);
3522 if (entryvnode
!= NULL
) {
3523 PackAttributeBlock(alist
, entryvnode
, &catInfo
, &attrptr
, &varptr
);
3526 /* vp okay to use instead of root vp */
3527 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3530 currattrbufsize
= *((u_long
*)attrbufptr
) = ((char *)varptr
- (char *)attrbufptr
);
3532 /* now check if we can't fit in the buffer space remaining */
3533 if (currattrbufsize
> uio
->uio_resid
)
3536 retval
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, ap
->a_uio
);
3537 if (retval
!= E_NONE
)
3539 attrptr
= attrbufptr
;
3540 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3542 *ap
->a_actualcount
+= 1;
3546 *ap
->a_newstate
= VTOH(vp
)->h_meta
->h_mtime
;/* before we unlock, know the mod date */
3547 /* Unlock catalog b-tree, finally. Ties up the everything during enumeration */
3548 (void) hfs_metafilelocking( VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, proc
);
3550 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3552 if (!retval
&& ap
->a_cookies
!= NULL
) { /* CHECK THAT 0 wasn't passed in */
3559 /* Only the NFS server uses cookies, and it loads the
3560 * directory block into system space, so we can just look at
3563 if (uio
->uio_segflg
!= UIO_SYSSPACE
) /* || uio->uio_iovcnt != 1 checked earlier */
3564 panic("hfs_readdirattr: unexpected uio from NFS server");
3565 dpStart
= uio
->uio_iov
->iov_base
- (uio
->uio_offset
- startoffset
);
3566 dpEnd
= uio
->uio_iov
->iov_base
;
3567 MALLOC(cookies
, u_long
*, (*ap
->a_actualcount
)*sizeof(u_long
), M_TEMP
, M_WAITOK
);
3568 for (dp
= dpStart
, cookiep
= cookies
;
3570 dp
= ((caddr_t
) dp
+ *((u_long
*)dp
))) {
3571 *cookiep
++ = (u_long
)((caddr_t
)dp
+ sizeof(u_long
));
3573 *ap
->a_cookies
= cookies
;
3576 uio
->uio_offset
= startoffset
+ (*ap
->a_actualcount
)*sizeof(struct dirent
);
3579 if (attrbufptr
!= NULL
)
3580 FREE(attrbufptr
, M_TEMP
);
3586 * Return target name of a symbolic link
3587 #% readlink vp L L L
3590 IN struct vnode *vp;
3591 INOUT struct uio *uio;
3592 IN struct ucred *cred;
3597 struct vop_readlink_args
/* {
3600 struct ucred *a_cred;
3604 DBG_FUNC_NAME("readlink");
3605 DBG_VOP_LOCKS_DECL(1);
3606 DBG_VOP_PRINT_FUNCNAME();
3607 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3609 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3610 retval
= VOP_READ(ap
->a_vp
, ap
->a_uio
, 0, ap
->a_cred
);
3611 /* clear IN_ACCESS to prevent needless update of symlink vnode */
3612 VTOH(ap
->a_vp
)->h_nodeflags
&= ~IN_ACCESS
;
3614 DBG_VOP_LOCKS_TEST(retval
);
3621 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3622 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3623 #% abortop dvp = = =
3626 IN struct vnode *dvp;
3627 IN struct componentname *cnp;
3635 struct vop_abortop_args
/* {
3636 struct vnode *a_dvp;
3637 struct componentname *a_cnp;
3640 DBG_FUNC_NAME("abortop");
3641 DBG_VOP_LOCKS_DECL(1);
3642 DBG_VOP_PRINT_FUNCNAME();
3643 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
3644 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
3647 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3649 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
3650 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
3652 DBG_VOP_LOCKS_TEST(E_NONE
);
3656 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3659 #% inactive vp L U U
3662 IN struct vnode *vp;
3669 struct vop_inactive_args
/* {
3673 struct vnode
*vp
= ap
->a_vp
;
3674 struct hfsnode
*hp
= VTOH(vp
);
3675 struct proc
*p
= ap
->a_p
;
3678 extern int prtactive
;
3680 DBG_FUNC_NAME("inactive");
3681 DBG_VOP_LOCKS_DECL(1);
3682 DBG_VOP_PRINT_FUNCNAME();
3683 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3685 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3688 if (prtactive
&& vp
->v_usecount
<= 0)
3689 vprint("hfs_inactive: pushing active", vp
);
3691 if (vp
->v_usecount
!= 0)
3692 DBG_VOP(("%s: bad usecount = %d\n",funcname
,vp
->v_usecount
));
3695 * Ignore nodes related to stale file handles.
3697 if (hp
->h_meta
->h_mode
== 0)
3701 * Check for a postponed deletion
3703 if (hp
->h_meta
->h_metaflags
& IN_DELETED
) {
3704 hp
->h_meta
->h_metaflags
&= ~IN_DELETED
;
3706 error
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0);
3707 if (error
) goto out
;
3709 if(UBCINFOEXISTS(vp
))
3710 (void)ubc_setsize(vp
, (off_t
)0);
3713 * Note: we do not need a lock on the private metadata directory
3714 * since it never has a vnode associated with it.
3716 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3717 if (error
) goto out
;
3718 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3720 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3724 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
3727 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
3728 error
= hfsDelete(HTOVCB(hp
), VTOHFS(vp
)->hfs_private_metadata_dir
, iNodeName
, TRUE
, H_HINT(hp
));
3730 /* XXX can we leave orphaned sibling? */
3731 error
= hfsDelete(HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
3732 if (error
== ENOENT
) {
3733 /* try by fileID as a backup */
3734 error
= hfsDelete(HTOVCB(hp
), H_FILEID(hp
), NULL
, TRUE
, H_HINT(hp
));
3738 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
3739 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3740 if (error
) goto out
;
3742 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
3743 hp
->h_meta
->h_mode
= 0;
3744 /* clear the block mappings */
3745 hp
->fcbPLen
= (u_int64_t
)0;
3746 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
3748 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
3751 if (hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
3753 VOP_UPDATE(vp
, &tv
, &tv
, 0);
3757 VOP_UNLOCK(vp
, 0, p
);
3759 * If we are done with the inode, reclaim it
3760 * so that it can be reused immediately.
3762 if (hp
->h_meta
->h_mode
== 0)
3763 vrecycle(vp
, (struct slock
*)0, p
);
3765 /* XXX SER Here we might want to get rid of any other forks
3766 * The problem is that if we call vrecycle(), our structure
3767 * disappear from under us, we would need to remember, and expect
3768 * things to go to null or to disappear
3769 * But it stillw would be a good thing to remove vnodes
3770 * referencing stale data
3773 DBG_VOP_LOCKS_TEST(E_NONE
);
3778 Ignored since the locks are gone......
3782 IN struct vnode *vp;
3789 struct vop_reclaim_args
/* {
3793 struct vnode
*vp
= ap
->a_vp
;
3794 struct hfsnode
*hp
= VTOH(vp
);
3795 void *tdata
= vp
->v_data
;
3797 Boolean freeMeta
= true;
3798 struct vnode
*devvp
= NULL
;
3800 extern int prtactive
;
3801 DBG_FUNC_NAME("reclaim");
3802 DBG_VOP_LOCKS_DECL(1);
3803 DBG_VOP_PRINT_FUNCNAME();
3804 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3806 DBG_VOP_LOCKS_INIT(0, ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_ZERO
);
3809 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3810 created in their entirity could be getting cleaned up here.
3813 if (prtactive
&& vp
->v_usecount
!= 0)
3814 vprint("hfs_reclaim(): pushing active", vp
);
3816 hp
->h_nodeflags
|= IN_ALLOCATING
; /* Mark this as being incomplete */
3818 * This will remove the entry from the hash AND the sibling list
3819 * This will make sure everything is in a stable state to see if we can remove the meta
3820 * i.e. if this is the only fork...the sibling list will be empty
3824 DBG_ASSERT(tdata
!= NULL
);
3825 DBG_ASSERT(hp
->h_meta
!= NULL
);
3827 devvp
= hp
->h_meta
->h_devvp
; /* For later releasing */
3828 hp
->h_meta
->h_usecount
--;
3830 /* release the file meta if this is the last fork */
3831 if (H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
) {
3832 if (hp
->h_meta
->h_siblinghead
.cqh_first
!= (void *) &hp
->h_meta
->h_siblinghead
)
3837 DBG_ASSERT(hp
->h_meta
->h_usecount
== 0);
3838 if (hp
->h_meta
->h_metaflags
& IN_LONGNAME
) {
3840 DBG_ASSERT(tname
!= NULL
);
3841 FREE(tname
, M_TEMP
);
3843 FREE_ZONE(hp
->h_meta
, sizeof(struct hfsfilemeta
), M_HFSFMETA
);
3847 DBG_ASSERT(hp
->h_meta
->h_usecount
== 1);
3851 * Purge old data structures associated with the inode.
3858 /* Free our data structs */
3859 FREE_ZONE(tdata
, sizeof(struct hfsnode
), M_HFSNODE
);
3862 DBG_VOP_LOCKS_TEST(E_NONE
);
3868 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
3872 IN struct vnode *vp;
3879 struct vop_lock_args
/* {
3885 struct vnode
* vp
= ap
->a_vp
;
3886 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3889 DBG_FUNC_NAME("lock");
3890 DBG_VOP_LOCKS_DECL(1);
3891 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
3892 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
3893 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3895 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
);
3896 if (retval
!= E_NONE
) {
3897 if ((ap
->a_flags
& LK_NOWAIT
) == 0)
3898 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval
, ap
->a_flags
));
3903 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
3904 DBG_VOP_LOCKS_TEST(retval
);
3909 * Unlock an hfsnode.
3913 IN struct vnode *vp;
3920 struct vop_unlock_args
/* {
3926 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3927 struct vnode
*vp
= ap
->a_vp
;
3928 int retval
= E_NONE
;
3930 DBG_FUNC_NAME("unlock");
3931 DBG_VOP_LOCKS_DECL(1);
3932 DBG_VOP_PRINT_FUNCNAME();
3933 DBG_VOP_PRINT_VNODE_INFO(vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
3934 DBG_VOP_LOCKS_INIT(0,vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
3937 DBG_ASSERT((ap
->a_flags
& (LK_EXCLUSIVE
|LK_SHARED
)) == 0);
3938 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
| LK_RELEASE
, &vp
->v_interlock
, ap
->a_p
);
3939 if (retval
!= E_NONE
) {
3940 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval
, H_FORKTYPE(hp
)));
3943 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
3944 DBG_VOP_LOCKS_TEST(retval
);
3950 * Print out the contents of an hfsnode.
3954 IN struct vnode *vp;
3958 struct vop_print_args
/* {
3962 register struct vnode
* vp
= ap
->a_vp
;
3963 register struct hfsnode
*hp
= VTOH( vp
);
3964 DBG_FUNC_NAME("print");
3965 DBG_VOP_LOCKS_DECL(1);
3966 DBG_VOP_PRINT_FUNCNAME();
3967 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
3969 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3971 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp
),
3972 major(H_DEV(hp
)), minor(H_DEV(hp
)));
3973 /* lockmgr_printinfo(&hp->h_lock); */
3975 DBG_VOP_LOCKS_TEST(E_NONE
);
3981 * Check for a locked hfsnode.
3982 #% islocked vp = = =
3985 IN struct vnode *vp;
3990 struct vop_islocked_args
/* {
3995 //DBG_FUNC_NAME("islocked");
3996 //DBG_VOP_LOCKS_DECL(1);
3997 //DBG_VOP_PRINT_FUNCNAME();
3998 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
4000 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
4002 lockStatus
= lockstatus(&VTOH( ap
->a_vp
)->h_lock
);
4003 //DBG_VOP_LOCKS_TEST(E_NONE);
4004 return (lockStatus
);
4009 #% pathconf vp L L L
4012 IN struct vnode *vp;
4014 OUT register_t *retval;
4019 struct vop_pathconf_args
/* {
4025 int retval
= E_NONE
;
4026 DBG_FUNC_NAME("pathconf");
4027 DBG_VOP_LOCKS_DECL(1);
4028 DBG_VOP_PRINT_FUNCNAME();
4029 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4031 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
4033 DBG_HFS_NODE_CHECK (ap
->a_vp
);
4035 switch (ap
->a_name
) {
4038 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
4039 *ap
->a_retval
= HFS_LINK_MAX
;
4047 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
4050 *ap
->a_retval
= PATH_MAX
; /* 1024 */
4052 case _PC_CHOWN_RESTRICTED
:
4062 DBG_VOP_LOCKS_TEST(retval
);
4071 * Advisory record locking support
4075 IN struct vnode *vp;
4078 IN struct flock *fl;
4084 struct vop_advlock_args
/* {
4092 register struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4093 register struct flock
*fl
= ap
->a_fl
;
4094 register struct hfslockf
*lock
;
4097 DBG_FUNC_NAME("advlock");
4098 DBG_VOP_LOCKS_DECL(1);
4099 DBG_VOP_PRINT_FUNCNAME();
4100 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP(("\n"));
4101 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
4103 * Avoid the common case of unlocking when inode has no locks.
4105 if (hp
->h_lockf
== (struct hfslockf
*)0) {
4106 if (ap
->a_op
!= F_SETLK
) {
4107 fl
->l_type
= F_UNLCK
;
4112 * Convert the flock structure into a start and end.
4115 switch (fl
->l_whence
) {
4119 * Caller is responsible for adding any necessary offset
4120 * when SEEK_CUR is used.
4122 start
= fl
->l_start
;
4126 start
= HTOFCB(hp
)->fcbEOF
+ fl
->l_start
;
4138 end
= start
+ fl
->l_len
- 1;
4141 * Create the hfslockf structure
4143 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
4144 lock
->lf_start
= start
;
4146 lock
->lf_id
= ap
->a_id
;
4147 lock
->lf_hfsnode
= hp
;
4148 lock
->lf_type
= fl
->l_type
;
4149 lock
->lf_next
= (struct hfslockf
*)0;
4150 TAILQ_INIT(&lock
->lf_blkhd
);
4151 lock
->lf_flags
= ap
->a_flags
;
4153 * Do the requested operation.
4157 retval
= hfs_setlock(lock
);
4161 retval
= hfs_clearlock(lock
);
4162 FREE(lock
, M_LOCKF
);
4166 retval
= hfs_getlock(lock
, fl
);
4167 FREE(lock
, M_LOCKF
);
4172 _FREE(lock
, M_LOCKF
);
4176 DBG_VOP_LOCKS_TEST(retval
);
4183 * Update the access, modified, and node change times as specified by the
4184 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4185 * used to specify that the node needs to be updated but that the times have
4186 * already been set. The access and modified times are taken from the second
4187 * and third parameters; the node change time is always taken from the current
4188 * time. If waitfor is set, then wait for the disk write of the node to
4193 IN struct vnode *vp;
4194 IN struct timeval *access;
4195 IN struct timeval *modify;
4201 struct vop_update_args
/* {
4203 struct timeval *a_access;
4204 struct timeval *a_modify;
4210 hfsCatalogInfo catInfo
;
4216 DBG_FUNC_NAME("update");
4217 DBG_VOP_LOCKS_DECL(1);
4218 DBG_VOP_PRINT_FUNCNAME();
4219 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
4220 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4222 hp
= VTOH(ap
->a_vp
);
4224 DBG_ASSERT(hp
&& hp
->h_meta
);
4225 DBG_ASSERT(*((int*)&ap
->a_vp
->v_interlock
) == 0);
4227 if ((H_FORKTYPE(hp
) == kSysFile
) ||
4228 (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) ||
4229 (hp
->h_meta
->h_mode
== 0)) {
4230 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4231 DBG_VOP_LOCKS_TEST(0);
4235 if (H_FORKTYPE(hp
) == kSysFile
) {
4236 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4237 DBG_VOP_LOCKS_TEST(0);
4241 if (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) {
4242 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4243 DBG_VOP_LOCKS_TEST(0);
4247 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4248 if (HTOFCB(hp
)->fcbFlags
&fcbModifiedMask
)
4249 hp
->h_nodeflags
|= IN_MODIFIED
;
4251 if ((hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) == 0) {
4252 DBG_VOP_LOCKS_TEST(0);
4256 if (hp
->h_nodeflags
& IN_ACCESS
)
4257 hp
->h_meta
->h_atime
= ap
->a_access
->tv_sec
;
4258 if (hp
->h_nodeflags
& IN_UPDATE
)
4259 hp
->h_meta
->h_mtime
= ap
->a_modify
->tv_sec
;
4260 if (hp
->h_nodeflags
& IN_CHANGE
) {
4261 hp
->h_meta
->h_ctime
= time
.tv_sec
;
4263 * HFS dates that WE set must be adjusted for DST
4265 if ((HTOVCB(hp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
4266 hp
->h_meta
->h_ctime
+= 3600;
4267 hp
->h_meta
->h_mtime
= hp
->h_meta
->h_ctime
;
4272 filename
= H_NAME(hp
);
4275 catInfo
.hint
= H_HINT(hp
);
4279 * Force an update of the indirect node instead of the link
4280 * by using the name and parent of the indirect node.
4282 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
4283 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
4284 filename
= iNodeName
;
4285 pid
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
4289 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
4292 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4293 * the Catalog b-tree may aready be locked by the current thread. So we
4294 * allow recursive locking of the Catalog from within VOP_UPDATE.
4296 /* Lock the Catalog b-tree file */
4297 retval
= hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
4299 DBG_VOP_LOCKS_TEST(retval
);
4303 retval
= hfs_getcatalog(vcb
, pid
, filename
, -1, &catInfo
);
4304 if (retval
!= noErr
) {
4305 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4306 retval
= MacToVFSError(retval
);
4310 H_HINT(hp
) = catInfo
.hint
;
4311 CopyVNodeToCatalogNode (HTOV(hp
), &catInfo
.nodeData
);
4313 retval
= UpdateCatalogNode(vcb
, pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
);
4315 /* unlock the Catalog b-tree file */
4316 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4318 if (retval
!= noErr
) { /* from UpdateCatalogNode() */
4319 retval
= MacToVFSError(retval
);
4323 /* After the updates are finished, clear the flags */
4324 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4325 HTOFCB(hp
)->fcbFlags
&= ~fcbModifiedMask
;
4327 /* Update general data */
4328 if (ap
->a_vp
->v_type
== VDIR
) {
4329 hp
->h_meta
->h_nlink
= 2 + catInfo
.nodeData
.cnd_valence
;
4330 hp
->h_meta
->h_size
= sizeof(rootdots
) +
4331 (catInfo
.nodeData
.cnd_valence
* AVERAGE_HFSDIRENTRY_SIZE
);
4332 if (hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
)
4333 hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
;
4335 hp
->h_meta
->h_size
= (off_t
)vcb
->blockSize
*
4336 (off_t
)(catInfo
.nodeData
.cnd_rsrcfork
.totalBlocks
+
4337 catInfo
.nodeData
.cnd_datafork
.totalBlocks
);
4343 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
4345 DBG_VOP_LOCKS_TEST(retval
);
4351 * Initialize the vnode associated with a new hfsnode,
4352 * handle aliased vnodes.
4355 hfs_vinit(mntp
, specops
, fifoops
, vpp
)
4357 int (**specops
)(void *);
4358 int (**fifoops
)(void *);
4362 struct vnode
*vp
, *nvp
;
4366 /* vp->v_type set in CopyCatalogToHFSNode */
4367 switch(vp
->v_type
) {
4371 if ((nvp
= checkalias(vp
, hp
->h_meta
->h_rdev
, mntp
))) {
4373 * Discard unneeded vnode, but save its hfsnode.
4374 * Note that the lock is carried over in the hfsnode
4375 * to the replacement vnode.
4377 nvp
->v_data
= vp
->v_data
;
4379 vp
->v_op
= spec_vnodeop_p
;
4383 * Reinitialize aliased hfsnode.
4395 return (EOPNOTSUPP
);
4400 if (H_FILEID(hp
) == kRootDirID
)
4401 vp
->v_flag
|= VROOT
;
4408 * Allocate a new node
4410 * Upon leaving, namei buffer must be freed.
4414 hfs_makenode(mode
, rawdev
, dvp
, vpp
, cnp
, p
)
4419 struct componentname
*cnp
;
4422 register struct hfsnode
*hp
, *parhp
;
4425 struct hfsCatalogInfo catInfo
;
4429 int hasmetalock
= 0;
4430 DBG_FUNC_NAME("makenode");
4433 vcb
= HTOVCB(parhp
);
4436 if ((mode
& IFMT
) == 0)
4440 if ((cnp
->cn_flags
& HASBUF
) == 0)
4441 panic("hfs_makenode: no name");
4444 /* lock catalog b-tree */
4445 retval
= hfs_metafilelocking(VTOHFS(dvp
),
4446 kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
4447 if (retval
!= E_NONE
)
4452 /* Create the Catalog B*-Tree entry */
4453 retval
= hfsCreate(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, mode
);
4454 if (retval
!= E_NONE
) {
4455 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4459 /* Look up the catalog entry just created: */
4460 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
4461 catInfo
.hint
= kNoHint
;
4463 retval
= hfs_getcatalog(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, cnp
->cn_namelen
, &catInfo
);
4464 if (retval
!= E_NONE
) {
4465 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4469 /* unlock catalog b-tree */
4471 (void) hfs_metafilelocking(VTOHFS(dvp
),
4472 kHFSCatalogFileID
, LK_RELEASE
, p
);
4474 /* hfs plus has additional metadata to initialize */
4475 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4479 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
4480 catInfo
.nodeData
.cnd_ownerID
= VTOHFS(dvp
)->hfs_uid
;
4481 catInfo
.nodeData
.cnd_groupID
= VTOHFS(dvp
)->hfs_gid
;
4484 catInfo
.nodeData
.cnd_ownerID
= cnp
->cn_cred
->cr_uid
;
4485 catInfo
.nodeData
.cnd_groupID
= parhp
->h_meta
->h_gid
;
4489 switch (catmode
& IFMT
) {
4491 catInfo
.nodeData
.cnd_ownerID
= parhp
->h_meta
->h_uid
;
4496 /* XXX should we move this to post hfsGet? */
4497 catInfo
.nodeData
.cnd_rawDevice
= rawdev
;
4499 * Don't tag as a special file (BLK or CHR) until *after*
4500 * hfsGet is called. This insures that the checkalias call
4501 * is defered until hfs_mknod completes.
4503 catmode
= (catmode
& ~IFMT
) | IFREG
;
4507 if ((catmode
& ISGID
) && !groupmember(parhp
->h_meta
->h_gid
, cnp
->cn_cred
) &&
4508 suser(cnp
->cn_cred
, NULL
))
4511 if (cnp
->cn_flags
& ISWHITEOUT
)
4517 * The 32-bit pflags field has two bytes of significance which
4518 * are stored separately as admin and owner flags.
4520 * +------------------------------------+
4521 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4522 * +------------------------------------+
4524 catInfo
.nodeData
.cnd_adminFlags
= (pflags
>> 16) & 0x00FF;
4525 catInfo
.nodeData
.cnd_ownerFlags
= pflags
& 0x00FF;
4526 catInfo
.nodeData
.cnd_mode
= catmode
;
4529 /* Create a vnode for the object just created: */
4530 forkType
= (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) ? kDirectory
: kDataFork
;
4531 retval
= hfs_vcreate(vcb
, &catInfo
, forkType
, &tvp
);
4533 CLEAN_CATALOGDATA(&catInfo
.nodeData
); /* Should do nothing */
4535 if (retval
) goto bad1
; /* from hfs_vcreate() */
4537 /* flush out pflags, mode, gid, uid and rdev */
4539 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4541 /* reset mode and v_type in case it was BLK/CHR */
4542 hp
->h_meta
->h_mode
= mode
;
4543 tvp
->v_type
= IFTOVT(mode
);
4544 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
4545 hp
->h_nodeflags
|= IN_ACCESS
| IN_CHANGE
| IN_UPDATE
;
4546 if ((retval
= VOP_UPDATE(tvp
, &tv
, &tv
, 1)))
4550 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4551 if ((retval
= VOP_UPDATE(dvp
, &tv
, &tv
, 1)))
4554 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4555 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4558 if (UBCINFOMISSING(tvp
) || UBCINFORECLAIMED(tvp
))
4566 * Write retval occurred trying to update the node
4567 * or the directory so must deallocate the node.
4569 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4574 /* unlock catalog b-tree */
4576 (void) hfs_metafilelocking(VTOHFS(dvp
),
4577 kHFSCatalogFileID
, LK_RELEASE
, p
);
4579 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4580 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4588 #if DBG_VOP_TEST_LOCKS
4590 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4592 void DbgVopTest( int maxSlots
,
4594 VopDbgStoreRec
*VopDbgStore
,
4599 for (index
= 0; index
< maxSlots
; index
++)
4601 if (VopDbgStore
[index
].id
!= index
) {
4602 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname
, VopDbgStore
[index
].id
, index
));
4605 if ((VopDbgStore
[index
].vp
!= NULL
) &&
4606 ((VopDbgStore
[index
].vp
->v_data
==NULL
) || (VTOH(VopDbgStore
[index
].vp
)->h_valid
!= HFS_VNODE_MAGIC
)))
4609 if (VopDbgStore
[index
].vp
!= NULL
)
4610 debug_check_vnode(VopDbgStore
[index
].vp
, 0);
4612 switch (VopDbgStore
[index
].inState
)
4616 /* Do Nothing !!! */
4619 case VOPDBG_UNLOCKED
:
4620 case VOPDBG_LOCKNOTNIL
:
4622 if (VopDbgStore
[index
].vp
== NULL
&& (VopDbgStore
[index
].inState
!= VOPDBG_LOCKNOTNIL
)) {
4623 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4624 } else if (VopDbgStore
[index
].vp
!= NULL
) {
4625 switch (VopDbgStore
[index
].inState
)
4628 case VOPDBG_LOCKNOTNIL
:
4629 if (VopDbgStore
[index
].inValue
== 0)
4631 DBG_ERR (("%s: Entry: not LOCKED:", funcname
));
4632 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4636 case VOPDBG_UNLOCKED
:
4637 if (VopDbgStore
[index
].inValue
!= 0)
4639 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname
));
4640 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4649 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4655 switch (VopDbgStore
[index
].errState
)
4658 /* Do Nothing !!! */
4661 case VOPDBG_UNLOCKED
:
4664 if (VopDbgStore
[index
].vp
== NULL
) {
4665 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4667 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4668 switch (VopDbgStore
[index
].errState
)
4671 if (VopDbgStore
[index
].outValue
== 0)
4673 DBG_ERR (("%s: Error: not LOCKED:", funcname
));
4674 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4678 case VOPDBG_UNLOCKED
:
4679 if (VopDbgStore
[index
].outValue
!= 0)
4681 DBG_ERR (("%s: Error: not UNLOCKED:", funcname
));
4682 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4687 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4688 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
));
4694 case VOPDBG_LOCKNOTNIL
:
4695 if (VopDbgStore
[index
].vp
!= NULL
) {
4696 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4697 if (VopDbgStore
[index
].outValue
== 0)
4698 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4702 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4707 switch (VopDbgStore
[index
].outState
)
4710 /* Do Nothing !!! */
4713 case VOPDBG_UNLOCKED
:
4715 if (VopDbgStore
[index
].vp
== NULL
) {
4716 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname
, index
));
4718 if (VopDbgStore
[index
].vp
!= NULL
)
4720 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4721 switch (VopDbgStore
[index
].outState
)
4724 if (VopDbgStore
[index
].outValue
== 0)
4726 DBG_ERR (("%s: Out: not LOCKED:", funcname
));
4727 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4731 case VOPDBG_UNLOCKED
:
4732 if (VopDbgStore
[index
].outValue
!= 0)
4734 DBG_ERR (("%s: Out: not UNLOCKED:", funcname
));
4735 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4740 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4741 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
));
4746 case VOPDBG_LOCKNOTNIL
:
4747 if (VopDbgStore
[index
].vp
!= NULL
) {
4748 if (&VTOH(VopDbgStore
[index
].vp
)->h_lock
== NULL
) {
4749 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4752 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4753 if (VopDbgStore
[index
].outValue
== 0)
4755 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname
));
4756 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
); DBG_ERR (("\n"));
4762 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].outState
));
4766 VopDbgStore
[index
].id
= -1; /* Invalidate the entry to allow panic-free re-use */
4770 #endif /* DBG_VOP_TEST_LOCKS */
4773 * Wrapper for special device reads
4777 struct vop_read_args
/* {
4781 struct ucred *a_cred;
4788 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4789 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
4793 * Wrapper for special device writes
4797 struct vop_write_args
/* {
4801 struct ucred *a_cred;
4806 * Set update and change flags.
4808 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4809 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
4813 * Wrapper for special device close
4815 * Update the times on the hfsnode then do device close.
4819 struct vop_close_args
/* {
4822 struct ucred *a_cred;
4826 struct vnode
*vp
= ap
->a_vp
;
4827 struct hfsnode
*hp
= VTOH(vp
);
4829 simple_lock(&vp
->v_interlock
);
4830 if (ap
->a_vp
->v_usecount
> 1)
4831 HFSTIMES(hp
, &time
, &time
);
4832 simple_unlock(&vp
->v_interlock
);
4833 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
4838 * Wrapper for fifo reads
4842 struct vop_read_args
/* {
4846 struct ucred *a_cred;
4849 extern int (**fifo_vnodeop_p
)(void *);
4854 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4855 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
4859 * Wrapper for fifo writes
4863 struct vop_write_args
/* {
4867 struct ucred *a_cred;
4870 extern int (**fifo_vnodeop_p
)(void *);
4873 * Set update and change flags.
4875 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4876 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
4880 * Wrapper for fifo close
4882 * Update the times on the hfsnode then do device close.
4886 struct vop_close_args
/* {
4889 struct ucred *a_cred;
4893 extern int (**fifo_vnodeop_p
)(void *);
4894 struct vnode
*vp
= ap
->a_vp
;
4895 struct hfsnode
*hp
= VTOH(vp
);
4897 simple_lock(&vp
->v_interlock
);
4898 if (ap
->a_vp
->v_usecount
> 1)
4899 HFSTIMES(hp
, &time
, &time
);
4900 simple_unlock(&vp
->v_interlock
);
4901 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
4906 /*****************************************************************************
4910 *****************************************************************************/
4912 #define VOPFUNC int (*)(void *)
4914 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
4915 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
4916 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
4917 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
4918 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
4919 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
4920 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
4921 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
4922 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
4923 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
4924 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
4925 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
4926 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
4927 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
4928 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
4929 { &vop_mmap_desc
, (VOPFUNC
)hfs_mmap
}, /* mmap */
4930 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
4931 { &vop_seek_desc
, (VOPFUNC
)hfs_seek
}, /* seek */
4932 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
4934 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
4936 { &vop_link_desc
, (VOPFUNC
)err_link
}, /* link (NOT SUPPORTED) */
4938 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
4939 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
4940 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
4941 { &vop_mkcomplex_desc
, (VOPFUNC
)hfs_mkcomplex
}, /* mkcomplex */
4942 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
4943 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
4944 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
4945 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
4946 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
4947 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
4948 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
4949 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
4950 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
4951 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
4952 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
4953 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
4954 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
4955 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
4956 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
4957 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
4958 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
4959 { &vop_reallocblks_desc
, (VOPFUNC
)hfs_reallocblks
}, /* reallocblks */
4960 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
4961 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
4962 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
4963 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
4964 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
4965 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
4966 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
4967 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
4968 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
4969 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
4970 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
4971 { NULL
, (VOPFUNC
)NULL
}
4974 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
4975 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
4977 int (**hfs_specop_p
)(void *);
4978 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
4979 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
4980 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
4981 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
4982 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
4983 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
4984 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
4985 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
4986 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
4987 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
4988 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
4989 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
4990 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
4991 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
4992 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
4993 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
4994 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
4995 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
4996 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
4997 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
4998 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
4999 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
5000 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
5001 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
5002 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
5003 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
5004 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
5005 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
5006 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5007 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5008 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5009 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5010 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
5011 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
5012 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5013 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5014 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
5015 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
5016 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
5017 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
5018 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
5019 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5020 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
5021 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5022 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5023 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
5024 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5025 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5026 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5027 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5028 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5029 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5031 struct vnodeopv_desc hfs_specop_opv_desc
=
5032 { &hfs_specop_p
, hfs_specop_entries
};
5035 int (**hfs_fifoop_p
)(void *);
5036 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
5037 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5038 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
5039 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
5040 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
5041 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
5042 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
5043 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5044 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5045 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5046 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
5047 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
5048 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
5049 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
5050 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
5051 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
5052 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
5053 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5054 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
5055 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
5056 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
5057 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
5058 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
5059 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
5060 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
5061 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
5062 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
5063 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
5064 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5065 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5066 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5067 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5068 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
5069 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
5070 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5071 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5072 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
5073 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
5074 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
5075 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
5076 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
5077 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5078 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
5079 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5080 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5081 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5082 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5083 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5084 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5085 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5086 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5087 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5089 struct vnodeopv_desc hfs_fifoop_opv_desc
=
5090 { &hfs_fifoop_p
, hfs_fifoop_entries
};