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 extern void hfs_resolvelink(ExtendedVCB
*vcb
, CatalogNodeData
*cndp
);
184 static int hfs_makenode( int mode
,
185 dev_t rawdev
, struct vnode
*dvp
, struct vnode
**vpp
,
186 struct componentname
*cnp
, struct proc
*p
);
188 static void hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
);
190 static int hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
);
192 static int hfs_chown( struct vnode
*vp
, uid_t uid
, gid_t gid
, struct ucred
*cred
, struct proc
*p
);
193 static int hfs_chmod( struct vnode
*vp
, int mode
, struct ucred
*cred
, struct proc
*p
);
194 static int hfs_chflags( struct vnode
*vp
, u_long flags
, struct ucred
*cred
, struct proc
*p
);
197 int hfs_cache_lookup(); /* in hfs_lookup.c */
198 int hfs_lookup(); /* in hfs_lookup.c */
199 int hfs_read(); /* in hfs_readwrite.c */
200 int hfs_write(); /* in hfs_readwrite.c */
201 int hfs_ioctl(); /* in hfs_readwrite.c */
202 int hfs_select(); /* in hfs_readwrite.c */
203 int hfs_mmap(); /* in hfs_readwrite.c */
204 int hfs_seek(); /* in hfs_readwrite.c */
205 int hfs_bmap(); /* in hfs_readwrite.c */
206 int hfs_strategy(); /* in hfs_readwrite.c */
207 int hfs_reallocblks(); /* in hfs_readwrite.c */
208 int hfs_truncate(); /* in hfs_readwrite.c */
209 int hfs_allocate(); /* in hfs_readwrite.c */
210 int hfs_pagein(); /* in hfs_readwrite.c */
211 int hfs_pageout(); /* in hfs_readwrite.c */
212 int hfs_search(); /* in hfs_search.c */
213 int hfs_bwrite(); /* in hfs_readwrite.c */
214 int hfs_link(); /* in hfs_link.c */
215 int hfs_blktooff(); /* in hfs_readwrite.c */
216 int hfs_offtoblk(); /* in hfs_readwrite.c */
217 int hfs_cmap(); /* in hfs_readwrite.c */
219 /*****************************************************************************
221 * Operations on vnodes
223 *****************************************************************************/
226 * Create a regular file
231 IN WILLRELE struct vnode *dvp;
232 OUT struct vnode **vpp;
233 IN struct componentname *cnp;
234 IN struct vattr *vap;
236 We are responsible for freeing the namei buffer,
237 it is done in hfs_makenode()
242 struct vop_create_args
/* {
244 struct vnode **a_vpp;
245 struct componentname *a_cnp;
249 struct proc
*p
= current_proc();
251 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
252 DBG_FUNC_NAME("create");
253 DBG_VOP_LOCKS_DECL(2);
254 DBG_VOP_PRINT_FUNCNAME();
255 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
256 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);
258 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
259 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
260 DBG_VOP_CONT(("\tva_type %d va_mode 0x%x\n",
261 ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
));
264 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
265 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
266 if(ap
->a_vap
== NULL
) {
267 panic("NULL attr on create");
270 switch(ap
->a_vap
->va_type
) {
272 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
274 DBG_VOP_LOCKS_TEST(EISDIR
);
275 return (EISDIR
); /* use hfs_mkdir instead */
280 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
));
281 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
283 DBG_VOP_LOCKS_TEST(EINVAL
);
286 // if(ap->a_vap->va_mode & (VSUID | VSGID | VSVTX)) {
287 // 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));
288 // DBG_VOP_LOCKS_TEST(EINVAL);
289 // VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
291 // return (EINVAL); /* Can't do these */
295 /* Create the vnode */
296 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
297 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
299 if (retval
!= E_NONE
) {
300 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
302 DBG_VOP_LOCKS_TEST(retval
);
314 IN WILLRELE struct vnode *dvp;
315 OUT WILLRELE struct vnode **vpp;
316 IN struct componentname *cnp;
317 IN struct vattr *vap;
323 struct vop_mknod_args
/* {
325 struct vnode **a_vpp;
326 struct componentname *a_cnp;
330 struct vattr
*vap
= ap
->a_vap
;
331 struct vnode
**vpp
= ap
->a_vpp
;
332 struct proc
*p
= current_proc();
336 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
337 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
342 if (vap
->va_rdev
!= VNOVAL
) {
344 * Want to be able to use this to make badblock
345 * inodes, so don't truncate the dev number.
347 rawdev
= vap
->va_rdev
;
350 /* Create the vnode */
351 error
= hfs_makenode(MAKEIMODE(vap
->va_type
, vap
->va_mode
),
352 rawdev
, ap
->a_dvp
, vpp
, ap
->a_cnp
, p
);
354 if (error
!= E_NONE
) {
359 * Remove inode so that it will be reloaded by lookup and
360 * checked to see if it is an alias of an existing vnode.
361 * Note: unlike UFS, we don't bash v_type here.
371 * mkcomplex vnode call
374 #% mkcomplex dvp L U U
375 #% mkcomplex vpp - L -
378 IN WILLRELE struct vnode *dvp;
379 OUT struct vnode **vpp;
380 IN struct componentname *cnp;
381 IN struct vattr *vap;
389 struct vop_mkcomplex_args
/* {
391 struct vnode **a_vpp;
392 struct componentname *a_cnp;
398 DBG_FUNC_NAME("make_complex");
399 DBG_VOP_LOCKS_DECL(2);
400 DBG_VOP_PRINT_FUNCNAME();
401 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
402 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
404 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
405 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
407 retval
= VOP_CREATE(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
);
409 DBG_VOP_LOCKS_TEST(retval
);
421 IN struct ucred *cred;
428 struct vop_open_args
/* {
431 struct ucred *a_cred;
435 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
437 DBG_FUNC_NAME("open");
438 DBG_VOP_LOCKS_DECL(1);
439 DBG_VOP_PRINT_FUNCNAME();
440 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
441 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
443 if (ap
->a_vp
->v_type
== VREG
) /* Only files */
446 * Files marked append-only must be opened for appending.
448 if ((hp
->h_meta
->h_pflags
& APPEND
) &&
449 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
454 DBG_VOP_LOCKS_TEST(retval
);
461 * Update the times on the hfsnode.
467 IN struct ucred *cred;
474 struct vop_close_args
/* {
477 struct ucred *a_cred;
481 register struct vnode
*vp
= ap
->a_vp
;
482 struct hfsnode
*hp
= VTOH(vp
);
483 struct proc
*p
= ap
->a_p
;
487 u_long blks
, blocksize
;
492 DBG_FUNC_NAME("close");
493 DBG_VOP_LOCKS_DECL(1);
494 DBG_VOP_PRINT_FUNCNAME();
495 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
496 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
498 simple_lock(&vp
->v_interlock
);
499 if (vp
->v_usecount
> (UBCINFOEXISTS(vp
) ? 2 : 1)) {
501 HFSTIMES(hp
, &tv
, &tv
);
503 simple_unlock(&vp
->v_interlock
);
506 * VOP_CLOSE can be called with vp locked (from vclean).
507 * We check for this case using VOP_ISLOCKED and bail.
509 * also, ignore complex nodes; there's no data associated with them.
511 if (H_FORKTYPE(hp
) == kDirectory
|| VOP_ISLOCKED(vp
)) {
512 DBG_VOP_LOCKS_TEST(E_NONE
);
520 enum vtype our_type
= vp
->v_type
;
521 u_long our_id
= vp
->v_id
;
523 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
525 * Since we can context switch in vn_lock our vnode
526 * could get recycled (eg umount -f). Double check
527 * that its still ours.
529 if (vp
->v_type
!= our_type
|| vp
->v_id
!= our_id
) {
530 VOP_UNLOCK(vp
, 0, p
);
531 DBG_VOP_LOCKS_TEST(E_NONE
);
535 /* Last chance to explicitly zero out the areas that are currently marked invalid: */
536 VOP_DEVBLOCKSIZE(hp
->h_meta
->h_devvp
, &devBlockSize
);
537 while (!CIRCLEQ_EMPTY(&hp
->h_invalidranges
)) {
538 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&hp
->h_invalidranges
);
539 off_t start
= invalid_range
->rl_start
;
540 off_t end
= invalid_range
->rl_end
;
542 /* The range about to be written must be validated first, so that
543 VOP_CMAP() will return the appropriate mapping for the cluster code: */
544 rl_remove(start
, end
, &hp
->h_invalidranges
);
546 retval
= cluster_write(vp
, (struct uio
*) 0, fcb
->fcbEOF
, invalid_range
->rl_end
+ 1, invalid_range
->rl_start
,
547 (off_t
)0, devBlockSize
, IO_HEADZEROFILL
| 0x8000);
551 /* Make sure the EOF gets written out at least once more
552 now that all invalid ranges have been zero-filled and validated: */
553 if (forceUpdate
) hp
->h_nodeflags
|= IN_MODIFIED
;
555 blocksize
= HTOVCB(hp
)->blockSize
;
556 blks
= leof
/ blocksize
;
557 if (((off_t
)blks
* (off_t
)blocksize
) != leof
)
561 * Shrink the peof to the smallest size neccessary to contain the leof.
563 if (((off_t
)blks
* (off_t
)blocksize
) < fcb
->fcbPLen
) {
564 retval
= VOP_TRUNCATE(vp
, leof
, IO_NDELAY
, ap
->a_cred
, p
);
568 /* If the VOP_TRUNCATE didn't happen to flush the vnode's information out to
569 disk, force it to be updated now that all invalid ranges have been zero-filled
572 if (hp
->h_nodeflags
& IN_MODIFIED
) VOP_UPDATE(vp
, &time
, &time
, 0);
574 VOP_UNLOCK(vp
, 0, p
);
577 DBG_VOP_LOCKS_TEST(retval
);
587 IN struct ucred *cred;
594 struct vop_access_args
/* {
597 struct ucred *a_cred;
601 struct vnode
*vp
= ap
->a_vp
;
602 struct ucred
*cred
= ap
->a_cred
;
603 struct hfsnode
*hp
= VTOH(vp
);
604 ExtendedVCB
*vcb
= HTOVCB(hp
);
610 DBG_FUNC_NAME("access");
611 DBG_VOP_LOCKS_DECL(1);
612 // DBG_VOP_PRINT_FUNCNAME();
613 // DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
615 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
618 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
621 * Disallow write attempts on read-only file systems;
622 * unless the file is a socket, fifo, or a block or
623 * character device resident on the file system.
626 switch (vp
->v_type
) {
630 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
638 /* If immutable bit set, nobody gets to write it. */
639 if ((mode
& VWRITE
) && (hp
->h_meta
->h_pflags
& IMMUTABLE
))
642 /* Otherwise, user id 0 always gets access. */
643 if (ap
->a_cred
->cr_uid
== 0) {
650 /* Otherwise, check the owner. */
651 if (hfs_owner_rights(vp
, cred
, ap
->a_p
, false) == 0) {
658 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
662 /* Otherwise, check the groups. */
663 if (! (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
)) {
664 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++) {
665 if (hp
->h_meta
->h_gid
== *gp
) {
672 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
678 /* Otherwise, check everyone else. */
685 retval
= ((hp
->h_meta
->h_mode
& mask
) == mask
? 0 : EACCES
);
688 DBG_VOP_LOCKS_TEST(retval
);
699 IN struct vattr *vap;
700 IN struct ucred *cred;
709 struct vop_getattr_args
/* {
712 struct ucred *a_cred;
716 register struct vnode
*vp
= ap
->a_vp
;
717 register struct hfsnode
*hp
= VTOH(vp
);
718 register struct vattr
*vap
= ap
->a_vap
;
720 DBG_FUNC_NAME("getattr");
721 DBG_VOP_LOCKS_DECL(1);
722 DBG_VOP_PRINT_FUNCNAME();
723 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
725 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
727 DBG_HFS_NODE_CHECK(ap
->a_vp
);
730 HFSTIMES(hp
, &tv
, &tv
);
732 vap
->va_fsid
= H_DEV(hp
);
733 vap
->va_fileid
= H_FILEID(hp
);
734 vap
->va_mode
= hp
->h_meta
->h_mode
;
735 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
736 vap
->va_uid
= (VTOHFS(vp
)->hfs_uid
== UNKNOWNUID
) ? console_user
: VTOHFS(vp
)->hfs_uid
;
738 vap
->va_uid
= (hp
->h_meta
->h_uid
== UNKNOWNUID
) ? console_user
: hp
->h_meta
->h_uid
;
740 vap
->va_gid
= hp
->h_meta
->h_gid
;
741 if (vp
->v_type
== VDIR
) {
742 vap
->va_size
= hp
->h_meta
->h_size
;
745 vap
->va_nlink
= hp
->h_meta
->h_nlink
;
747 * account for hidden data nodes directory
749 if ((H_FILEID(hp
) == kRootDirID
) &&
750 (VTOHFS(vp
)->hfs_private_metadata_dir
!= 0)) {
751 vap
->va_size
-= AVERAGE_HFSDIRENTRY_SIZE
;
756 vap
->va_size
= hp
->fcbEOF
;
757 vap
->va_bytes
= hp
->h_meta
->h_size
;
759 if (vp
->v_type
== VBLK
|| vp
->v_type
== VCHR
)
760 vap
->va_rdev
= hp
->h_meta
->h_rdev
;
764 if (hp
->h_meta
->h_metaflags
& IN_DELETED
)
767 else if ((hp
->h_meta
->h_metaflags
& IN_DATANODE
) &&
768 (hp
->h_meta
->h_nlink
> 0))
769 vap
->va_nlink
= hp
->h_meta
->h_nlink
;
776 vap
->va_atime
.tv_nsec
= 0;
777 vap
->va_atime
.tv_sec
= hp
->h_meta
->h_atime
;
778 vap
->va_mtime
.tv_nsec
= 0;
779 vap
->va_mtime
.tv_sec
= hp
->h_meta
->h_mtime
;
780 vap
->va_ctime
.tv_nsec
= 0;
781 vap
->va_ctime
.tv_sec
= hp
->h_meta
->h_ctime
;
782 vap
->va_flags
= hp
->h_meta
->h_pflags
;
784 /* this doesn't belong here */
785 if (vp
->v_type
== VBLK
)
786 vap
->va_blocksize
= BLKDEV_IOSIZE
;
787 else if (vp
->v_type
== VCHR
)
788 vap
->va_blocksize
= MAXPHYSIO
;
790 vap
->va_blocksize
= VTOVFS(vp
)->mnt_stat
.f_iosize
;
791 vap
->va_type
= vp
->v_type
;
794 DBG_VOP_LOCKS_TEST(E_NONE
);
799 * Set attribute vnode op. called from several syscalls
804 IN struct vattr *vap;
805 IN struct ucred *cred;
812 struct vop_setattr_args
/* {
815 struct ucred *a_cred;
819 struct vnode
*vp
= ap
->a_vp
;
820 struct hfsnode
*hp
= VTOH(vp
);
821 struct vattr
*vap
= ap
->a_vap
;
822 struct ucred
*cred
= ap
->a_cred
;
823 struct proc
*p
= ap
->a_p
;
824 struct timeval atimeval
, mtimeval
;
826 DBG_FUNC_NAME("setattr");
827 DBG_VOP_LOCKS_DECL(1);
828 DBG_VOP_PRINT_FUNCNAME();
829 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
830 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
831 WRITE_CK(vp
, funcname
);
832 DBG_HFS_NODE_CHECK(ap
->a_vp
);
835 * Check for unsettable attributes.
837 if ((vap
->va_type
!= VNON
) || (vap
->va_nlink
!= VNOVAL
) ||
838 (vap
->va_fsid
!= VNOVAL
) || (vap
->va_fileid
!= VNOVAL
) ||
839 (vap
->va_blocksize
!= VNOVAL
) || (vap
->va_rdev
!= VNOVAL
) ||
840 ((int)vap
->va_bytes
!= VNOVAL
) || (vap
->va_gen
!= VNOVAL
)) {
845 if (vap
->va_flags
!= VNOVAL
) {
846 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
850 if ((retval
= hfs_chflags(vp
, vap
->va_flags
, cred
, p
))) {
853 if (vap
->va_flags
& (IMMUTABLE
| APPEND
)) {
859 if (hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) {
864 * Go through the fields and update iff not VNOVAL.
866 if (vap
->va_uid
!= (uid_t
)VNOVAL
|| vap
->va_gid
!= (gid_t
)VNOVAL
) {
867 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
871 if ((retval
= hfs_chown(vp
, vap
->va_uid
, vap
->va_gid
, cred
, p
))) {
875 if (vap
->va_size
!= VNOVAL
) {
877 * Disallow write attempts on read-only file systems;
878 * unless the file is a socket, fifo, or a block or
879 * character device resident on the file system.
881 switch (vp
->v_type
) {
887 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
895 if ((retval
= VOP_TRUNCATE(vp
, vap
->va_size
, 0, cred
, p
))) {
900 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
901 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
905 if (((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) &&
906 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
907 (retval
= VOP_ACCESS(vp
, VWRITE
, cred
, p
)))) {
910 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
911 hp
->h_nodeflags
|= IN_ACCESS
;
912 if (vap
->va_mtime
.tv_sec
!= VNOVAL
) {
913 hp
->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
915 * The utimes system call can reset the modification time
916 * but it doesn't know about the HFS+ create time. So we
917 * need to insure that the creation time is always at least
918 * as old as the modification time.
920 if (( VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
921 ( H_FILEID(hp
) != kRootDirID
) &&
922 ( vap
->va_mtime
.tv_sec
< hp
->h_meta
->h_crtime
))
923 hp
->h_meta
->h_crtime
= vap
->va_mtime
.tv_sec
;
925 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
926 atimeval
.tv_usec
= 0;
927 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
928 mtimeval
.tv_usec
= 0;
929 if ((retval
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1))) {
934 if (vap
->va_mode
!= (mode_t
)VNOVAL
) {
935 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
939 retval
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
);
944 DBG_VOP(("hfs_setattr: returning %d...\n", retval
));
945 DBG_VOP_LOCKS_TEST(retval
);
953 #% getattrlist vp = = =
957 IN struct attrlist *alist;
958 INOUT struct uio *uio;
959 IN struct ucred *cred;
967 struct vop_getattrlist_args
/* {
969 struct attrlist *a_alist
971 struct ucred *a_cred;
975 struct vnode
*vp
= ap
->a_vp
;
976 struct hfsnode
*hp
= VTOH(vp
);
977 struct attrlist
*alist
= ap
->a_alist
;
979 struct hfsCatalogInfo catInfo
;
980 struct hfsCatalogInfo
*catInfoPtr
= NULL
;
989 DBG_FUNC_NAME("getattrlist");
990 DBG_VOP_LOCKS_DECL(1);
992 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
993 DBG_HFS_NODE_CHECK(ap
->a_vp
);
994 DBG_VOP(("%s: Common attr:0x%lx, buff size Ox%lX,\n",funcname
, (u_long
)alist
->commonattr
,(u_long
)ap
->a_uio
->uio_resid
));
996 DBG_ASSERT(ap
->a_uio
->uio_rw
== UIO_READ
);
998 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
999 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
1000 ((alist
->volattr
& ~ATTR_VOL_VALIDMASK
) != 0) ||
1001 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
1002 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
1003 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0)) {
1004 DBG_ERR(("%s: bad attrlist\n", funcname
));
1005 DBG_VOP_LOCKS_TEST(EINVAL
);
1009 /* Requesting volume information requires setting the ATTR_VOL_INFO bit and
1010 volume info requests are mutually exclusive with all other info requests: */
1011 if ((alist
->volattr
!= 0) && (((alist
->volattr
& ATTR_VOL_INFO
) == 0) ||
1012 (alist
->dirattr
!= 0) || (alist
->fileattr
!= 0) || (alist
->forkattr
!= 0)
1014 DBG_ERR(("%s: conflicting information requested\n", funcname
));
1015 DBG_VOP_LOCKS_TEST(EINVAL
);
1019 /* Reject requests for unsupported options for now: */
1020 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
1021 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
))) {
1022 DBG_ERR(("%s: illegal bits in attlist\n", funcname
));
1023 DBG_VOP_LOCKS_TEST(EINVAL
);
1027 /* Requesting volume information requires root vnode */
1028 if ((alist
->volattr
) && (H_FILEID(hp
) != kRootDirID
)) {
1029 DBG_ERR(("%s: not root vnode\n", funcname
));
1030 DBG_VOP_LOCKS_TEST(EINVAL
);
1035 /* Update times if needed */
1037 HFSTIMES(hp
, &tv
, &tv
);
1039 /* If a FileID (ATTR_CMN_OBJPERMANENTID) is requested on an HFS volume we must be sure
1040 to create the thread record before returning it:
1042 if ((vp
->v_type
== VREG
) &&
1043 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
)) {
1044 /* Only HFS-Plus volumes are guaranteed to have a thread record in place already: */
1045 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
) {
1046 /* Create a thread record and return the FileID [which is the file's fileNumber] */
1047 /* lock catalog b-tree */
1048 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1049 error
= hfsCreateFileID(VTOVCB(vp
), H_DIRID(hp
), H_NAME(hp
), H_HINT(hp
), &fileID
);
1050 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1052 DBG_VOP_LOCKS_TEST(error
);
1053 DBG_ERR(("hfs_getattrlist: error %d on CreateFileIDRef.\n", error
));
1056 DBG_ASSERT(fileID
== H_FILEID(hp
));
1060 /* Asking for data fork attributes from the rsrc fork is not supported */
1061 if ((H_FORKTYPE(hp
) == kRsrcFork
) && (alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
)) {
1066 * Avoid unnecessary catalog lookups for volume info which is available directly
1067 * in the VCB and root vnode, or can be synthesized.
1069 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
1070 catInfo
.hint
= kNoHint
;
1072 if (((alist
->volattr
== 0) && ((alist
->commonattr
& HFS_ATTR_CMN_LOOKUPMASK
) != 0)) ||
1073 ((alist
->dirattr
& HFS_ATTR_DIR_LOOKUPMASK
) != 0) ||
1074 ((alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
) != 0)) {
1076 /* lock catalog b-tree */
1077 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, ap
->a_p
);
1078 if (error
) goto GetCatalogErr_Exit
;
1080 if (alist
->volattr
!= 0) {
1081 /* Look up the root info, regardless of the vnode provided */
1082 error
= hfs_getcatalog(VTOVCB(vp
), 2, NULL
, -1, &catInfo
);
1084 error
= hfs_getcatalog(VTOVCB(vp
), H_DIRID(hp
), H_NAME(hp
), -1, &catInfo
);
1085 if (error
== 0) H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1088 /* unlock catalog b-tree */
1089 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1092 * If a data fork has an active sibling and we need
1093 * rsrc fork attributes then we need to lock the
1094 * sibling and make a copy of its attributes.
1096 if ((hp
->h_meta
->h_usecount
> 1) &&
1097 (H_FORKTYPE(hp
) == kDataFork
) &&
1098 (alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
)) {
1099 struct vnode
*sib_vp
= NULL
;
1100 struct hfsnode
*nhp
;
1101 struct proc
*p
= current_proc();
1103 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
1104 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
1105 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
1107 /* Loop through all siblings, skipping ourselves */
1108 simple_lock(&hp
->h_meta
->h_siblinglock
);
1109 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1110 if (nhp
== hp
) /* skip ourselves */
1114 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1116 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1117 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1118 if (VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1119 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1120 CopyVNodeToCatalogNode (sib_vp
, &catInfo
.nodeData
);
1125 }; /* h_use_count > 1 */
1127 /* Update to the in-memory state, if it has been modified...just to make sure */
1128 if (VTOH(vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1129 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1130 CopyVNodeToCatalogNode (vp
, &catInfo
.nodeData
);
1133 /* XXX What if hfs_getcatalog fails...we just continue??? */
1134 catInfoPtr
= &catInfo
;
1138 fixedblocksize
= AttributeBlockSize(alist
);
1139 attrblocksize
= fixedblocksize
+ (sizeof(u_long
)); /* u_long for length longword */
1140 if (alist
->commonattr
& ATTR_CMN_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1141 if (alist
->commonattr
& ATTR_CMN_NAMEDATTRLIST
) attrblocksize
+= 0; /* XXX PPD */
1142 if (alist
->volattr
& ATTR_VOL_MOUNTPOINT
) attrblocksize
+= PATH_MAX
;
1143 if (alist
->volattr
& ATTR_VOL_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1144 if (alist
->fileattr
& ATTR_FILE_FORKLIST
) attrblocksize
+= 0; /* XXX PPD */
1146 attrbufsize
= MIN(ap
->a_uio
->uio_resid
, attrblocksize
);
1147 DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
1150 attrblocksize
- fixedblocksize
));
1151 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1152 attrptr
= attrbufptr
;
1153 *((u_long
*)attrptr
) = 0; /* Set buffer length in case of errors */
1154 ++((u_long
*)attrptr
); /* Reserve space for length field */
1155 varptr
= ((char *)attrptr
) + fixedblocksize
; /* Point to variable-length storage */
1156 DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int
)attrptr
, (u_int
)varptr
));
1158 PackAttributeBlock(alist
, vp
, catInfoPtr
, &attrptr
, &varptr
);
1159 attrbufsize
= MIN(attrbufsize
, (u_int
)varptr
- (u_int
)attrbufptr
); /* Don't copy out more data than was generated */
1160 *((u_long
*)attrbufptr
) = attrbufsize
; /* Set actual buffer length for return to caller */
1161 DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize
, (u_int
)ap
->a_uio
->uio_iov
->iov_base
));
1162 error
= uiomove((caddr_t
)attrbufptr
, attrbufsize
, ap
->a_uio
);
1163 if (error
!= E_NONE
) {
1164 DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error
));
1167 FREE(attrbufptr
, M_TEMP
);
1171 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1172 DBG_VOP_LOCKS_TEST(error
);
1181 #% setattrlist vp L L L
1184 IN struct vnode *vp;
1185 IN struct attrlist *alist;
1186 INOUT struct uio *uio;
1187 IN struct ucred *cred;
1195 struct vop_setattrlist_args
/* {
1197 struct attrlist *a_alist
1199 struct ucred *a_cred;
1203 struct vnode
*vp
= ap
->a_vp
;
1204 struct hfsnode
*hp
= VTOH(vp
);
1205 struct attrlist
*alist
= ap
->a_alist
;
1206 struct ucred
*cred
= ap
->a_cred
;
1207 struct proc
*p
= ap
->a_p
;
1209 struct hfsCatalogInfo catInfo
;
1211 void *attrbufptr
= NULL
;
1213 void *varptr
= NULL
;
1223 DBG_FUNC_NAME("setattrlist");
1224 DBG_VOP_LOCKS_DECL(1);
1226 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
1227 DBG_HFS_NODE_CHECK(ap
->a_vp
);
1228 DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname
, (u_int
)alist
->commonattr
,(u_int
)ap
->a_uio
->uio_resid
));
1230 DBG_ASSERT(ap
->a_uio
->uio_rw
== UIO_WRITE
);
1232 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
1233 ((alist
->commonattr
& ~ATTR_CMN_SETMASK
) != 0) ||
1234 ((alist
->volattr
& ~ATTR_VOL_SETMASK
) != 0) ||
1235 ((alist
->dirattr
& ~ATTR_DIR_SETMASK
) != 0) ||
1236 ((alist
->fileattr
& ~ATTR_FILE_SETMASK
) != 0) ||
1237 ((alist
->forkattr
& ~ATTR_FORK_SETMASK
) != 0)) {
1238 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1239 DBG_VOP_LOCKS_TEST(EINVAL
);
1243 if ((alist
->volattr
!= 0) && /* Setting volume info */
1244 (((alist
->volattr
& ATTR_VOL_INFO
) == 0) || /* Not explicitly indicating this or ... */
1245 (alist
->commonattr
& ~ATTR_CMN_VOLSETMASK
))) /* ... setting invalid attributes for volume */
1247 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1248 DBG_VOP_LOCKS_TEST(EINVAL
);
1252 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
1253 DBG_VOP_LOCKS_TEST(EROFS
);
1258 Ownership of the file (in addition to write access, checked below,
1259 is required in one of two classes of calls:
1261 (a) When setting any ownership-requiring attribute other than ATTR_CMN_FLAGS, or
1262 (b) When setting ATTR_CMN_FLAGS on a volume that's not plain HFS (for which no
1263 real per-object ownership information is stored):
1265 if ((alist
->commonattr
& (OWNERSHIP_ONLY_ATTRS
& ~ATTR_CMN_FLAGS
)) ||
1266 ((alist
->commonattr
& ATTR_CMN_FLAGS
) && (VTOVCB(vp
)->vcbSigWord
!= kHFSSigWord
))) {
1267 /* NOTE: The following isn't ENTIRELY complete: even if you're the superuser
1268 you cannot change the flags as long as SF_IMMUTABLE or SF_APPEND is
1269 set and securelevel > 0. This is verified in hfs_chflags which gets
1270 invoked to do the actual flags field change so this check is sufficient
1273 /* Check to see if the user owns the object [or is superuser]: */
1274 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1275 DBG_VOP_LOCKS_TEST(retval
);
1279 DBG_ASSERT(((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == 0) ||
1280 (((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == ATTR_CMN_FLAGS
) &&
1281 (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
)));
1282 /* No ownership access is required: mere write access (checked below) will do... */
1285 /* For any other attributes, check to see if the user has write access to
1286 the object in question [unlike VOP_ACCESS, ignore IMMUTABLE here]: */
1288 if ((((alist
->commonattr
& ~(OWNERSHIP_ONLY_ATTRS
)) != 0) ||
1289 (alist
->volattr
!= 0) ||
1290 (alist
->dirattr
!= 0) ||
1291 (alist
->fileattr
!= 0) ||
1292 (alist
->forkattr
!= 0)) &&
1293 ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0)) {
1294 DBG_VOP_LOCKS_TEST(retval
);
1296 }; /* end of if ownership attr */
1298 /* Allocate the buffer now to minimize the time we might be blocked holding the catalog lock */
1299 attrblocksize
= ap
->a_uio
->uio_resid
;
1300 if (attrblocksize
< AttributeBlockSize(alist
)) {
1301 DBG_ERR(("%s: bad attrblocksize\n", funcname
));
1302 DBG_VOP_LOCKS_TEST(EINVAL
);
1306 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1308 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
1309 catInfo
.hint
= kNoHint
;
1311 filename
= H_NAME(hp
);
1316 * Force an update of the indirect node instead of the link
1317 * by using the name and parent of the indirect node.
1319 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
1320 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
1321 filename
= iNodeName
;
1322 pid
= VTOHFS(vp
)->hfs_private_metadata_dir
;
1326 /* lock catalog b-tree */
1327 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1328 if (error
!= E_NONE
)
1331 error
= hfs_getcatalog(VTOVCB(vp
), pid
, filename
, -1, &catInfo
);
1333 /* unlock catalog b-tree */
1334 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1335 if (error
!= E_NONE
)
1338 H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1340 error
= uiomove((caddr_t
)attrbufptr
, attrblocksize
, ap
->a_uio
);
1341 if (error
) goto ErrorExit
;
1343 if ((alist
->volattr
) && (H_FILEID(hp
) != kRootDirID
)) {
1349 * If we are going to change the times:
1350 * 1. do we have permission to change the dates?
1351 * 2. Is there another fork? If so then clear any flags associated with the times
1353 if (alist
->commonattr
& (ATTR_CMN_MODTIME
| ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1354 if (alist
->commonattr
& (ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1355 if ((error
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1359 /* If there is another fork, clear the flags */
1360 if ((hp
->h_meta
->h_usecount
> 1) && (H_FORKTYPE(hp
) == kDataFork
)) {
1361 struct vnode
*sib_vp
= NULL
;
1362 struct hfsnode
*nhp
;
1364 /* Loop through all siblings, skipping ourselves */
1365 simple_lock(&hp
->h_meta
->h_siblinglock
);
1366 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1367 if (nhp
== hp
) /* skip ourselves */
1371 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1374 * The only error that vget returns is when the vnode is going away,
1375 * so ignore the vnode
1377 if (sib_vp
&& vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1378 if ((sib_vp
->v_tag
== VT_HFS
)
1379 && VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_UPDATE
)) {
1380 if (alist
->commonattr
& ATTR_CMN_MODTIME
)
1381 VTOH(sib_vp
)->h_nodeflags
&= ~IN_UPDATE
;
1382 if (alist
->commonattr
& ATTR_CMN_CHGTIME
)
1383 VTOH(sib_vp
)->h_nodeflags
&= ~IN_CHANGE
;
1384 if (alist
->commonattr
& ATTR_CMN_ACCTIME
)
1385 VTOH(sib_vp
)->h_nodeflags
&= ~IN_ACCESS
;
1392 /* save these in case hfs_chown() or hfs_chmod() fail */
1393 saved_uid
= hp
->h_meta
->h_uid
;
1394 saved_gid
= hp
->h_meta
->h_gid
;
1395 saved_mode
= hp
->h_meta
->h_mode
;
1396 saved_flags
= hp
->h_meta
->h_pflags
;
1398 attrptr
= attrbufptr
;
1399 UnpackAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
1401 /* if unpacking changed the owner or group then call hfs_chown() */
1402 if (saved_uid
!= hp
->h_meta
->h_uid
|| saved_gid
!= hp
->h_meta
->h_gid
) {
1406 uid
= hp
->h_meta
->h_uid
;
1407 hp
->h_meta
->h_uid
= saved_uid
;
1408 gid
= hp
->h_meta
->h_gid
;
1409 hp
->h_meta
->h_gid
= saved_gid
;
1410 if ((error
= hfs_chown(vp
, uid
, gid
, cred
, p
)))
1414 /* if unpacking changed the mode then call hfs_chmod() */
1415 if (saved_mode
!= hp
->h_meta
->h_mode
) {
1418 mode
= hp
->h_meta
->h_mode
;
1419 hp
->h_meta
->h_mode
= saved_mode
;
1420 if ((error
= hfs_chmod(vp
, mode
, cred
, p
)))
1424 /* if unpacking changed the flags then call hfs_chflags */
1425 if (saved_flags
!= hp
->h_meta
->h_pflags
) {
1428 flags
= hp
->h_meta
->h_pflags
;
1429 hp
->h_meta
->h_pflags
= saved_flags
;
1430 if ((error
= hfs_chflags(vp
, flags
, cred
, p
)))
1435 /* lock catalog b-tree */
1436 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1437 if (error
!= E_NONE
)
1440 /* Update Catalog Tree */
1441 if (alist
->volattr
== 0) {
1442 error
= MacToVFSError( UpdateCatalogNode(HTOVCB(hp
), pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
));
1446 if (alist
->volattr
& ATTR_VOL_NAME
) {
1447 ExtendedVCB
*vcb
= VTOVCB(vp
);
1448 int namelen
= strlen(vcb
->vcbVN
);
1450 if (vcb
->vcbVN
[0] == 0) {
1452 * Ignore attempts to rename a volume to a zero-length name:
1453 * restore the original name from the metadata.
1455 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
);
1460 * Force Carbon renames to have MacUnicode encoding
1462 if ((hp
->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
1463 tehint
= kTextEncodingMacUnicode
;
1466 error
= MoveRenameCatalogNode(vcb
, kRootParID
, H_NAME(hp
), H_HINT(hp
),
1467 kRootParID
, vcb
->vcbVN
, &H_HINT(hp
), tehint
);
1470 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
); /* Restore the old name in the VCB */
1471 vcb
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1476 hfs_set_metaname(vcb
->vcbVN
, hp
->h_meta
, HTOHFS(hp
));
1477 hp
->h_nodeflags
|= IN_CHANGE
;
1479 } /* vcb->vcbVN[0] == 0 ... else ... */
1480 } /* alist->volattr & ATTR_VOL_NAME */
1483 /* unlock catalog b-tree */
1484 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1488 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1490 if (attrbufptr
) FREE(attrbufptr
, M_TEMP
);
1492 DBG_VOP_LOCKS_TEST(error
);
1497 * Change the mode on a file.
1498 * Inode must be locked before calling.
1501 hfs_chmod(vp
, mode
, cred
, p
)
1502 register struct vnode
*vp
;
1504 register struct ucred
*cred
;
1507 register struct hfsnode
*hp
= VTOH(vp
);
1510 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1513 #if OVERRIDE_UNKNOWN_PERMISSIONS
1514 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1519 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1522 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
1524 if (!groupmember(hp
->h_meta
->h_gid
, cred
) && (mode
& ISGID
))
1527 hp
->h_meta
->h_mode
&= ~ALLPERMS
;
1528 hp
->h_meta
->h_mode
|= (mode
& ALLPERMS
);
1529 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1530 hp
->h_nodeflags
|= IN_CHANGE
;
1536 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
1538 struct hfsnode
*hp
= VTOH(vp
);
1539 ExtendedVCB
*vcb
= HTOVCB(hp
);
1542 int retval
= E_NONE
;
1545 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
1548 * Disallow write attempts on read-only file systems;
1549 * unless the file is a socket, fifo, or a block or
1550 * character device resident on the file system.
1552 switch (vp
->v_type
) {
1556 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
1563 /* If immutable bit set, nobody gets to write it. */
1564 if (considerFlags
&& (hp
->h_meta
->h_pflags
& IMMUTABLE
))
1567 /* Otherwise, user id 0 always gets access. */
1568 if (cred
->cr_uid
== 0) {
1573 /* Otherwise, check the owner. */
1574 if ((retval
= hfs_owner_rights(vp
, cred
, p
, false)) == 0) {
1575 retval
= ((hp
->h_meta
->h_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
1579 /* Otherwise, check the groups. */
1580 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++)
1581 if (hp
->h_meta
->h_gid
== *gp
) {
1582 retval
= ((hp
->h_meta
->h_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
1586 /* Otherwise, check everyone else. */
1587 retval
= ((hp
->h_meta
->h_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
1596 * Change the flags on a file or directory.
1597 * Inode must be locked before calling.
1600 hfs_chflags(vp
, flags
, cred
, p
)
1601 register struct vnode
*vp
;
1602 register u_long flags
;
1603 register struct ucred
*cred
;
1606 register struct hfsnode
*hp
= VTOH(vp
);
1609 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
1610 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
1613 } else if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1617 if (cred
->cr_uid
== 0) {
1618 if ((hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
1622 hp
->h_meta
->h_pflags
= flags
;
1624 if (hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
) ||
1625 (flags
& UF_SETTABLE
) != flags
) {
1628 hp
->h_meta
->h_pflags
&= SF_SETTABLE
;
1629 hp
->h_meta
->h_pflags
|= (flags
& UF_SETTABLE
);
1631 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1632 hp
->h_nodeflags
|= IN_CHANGE
;
1639 * Perform chown operation on hfsnode hp;
1640 * hfsnode must be locked prior to call.
1643 hfs_chown(vp
, uid
, gid
, cred
, p
)
1644 register struct vnode
*vp
;
1650 register struct hfsnode
*hp
= VTOH(vp
);
1655 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1658 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1662 if (uid
== (uid_t
)VNOVAL
)
1663 uid
= hp
->h_meta
->h_uid
;
1664 if (gid
== (gid_t
)VNOVAL
)
1665 gid
= hp
->h_meta
->h_gid
;
1667 * If we don't own the file, are trying to change the owner
1668 * of the file, or are not a member of the target group,
1669 * the caller must be superuser or the call fails.
1671 if ((cred
->cr_uid
!= hp
->h_meta
->h_uid
|| uid
!= hp
->h_meta
->h_uid
||
1672 (gid
!= hp
->h_meta
->h_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
1673 (retval
= suser(cred
, &p
->p_acflag
)))
1676 ogid
= hp
->h_meta
->h_gid
;
1677 ouid
= hp
->h_meta
->h_uid
;
1679 hp
->h_meta
->h_gid
= gid
;
1680 hp
->h_meta
->h_uid
= uid
;
1682 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1683 if (ouid
!= uid
|| ogid
!= gid
)
1684 hp
->h_nodeflags
|= IN_CHANGE
;
1685 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
1686 hp
->h_meta
->h_mode
&= ~ISUID
;
1687 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
1688 hp
->h_meta
->h_mode
&= ~ISGID
;
1696 #% exchange fvp L L L
1697 #% exchange tvp L L L
1700 IN struct vnode *fvp;
1701 IN struct vnode *tvp;
1702 IN struct ucred *cred;
1708 * exchange is a very tricky routine, because we might have to unlock the
1709 * passed in vnode, and then retry locking it and all its siblings, and then
1710 * unlocking them in reverse.
1711 * Also the sibling list lock must be kept during the whole operation to
1712 * make sure nothing changes underneath us.
1713 * Also it depends on behavior of the sibling list and hash, so
1714 * careful if you change anything.
1719 struct vop_exchange_args
/* {
1720 struct vnode *a_fvp;
1721 struct vnode *a_tvp;
1722 struct ucred *a_cred;
1726 struct hfsnode
*from_hp
, *to_hp
, *nhp
;
1727 struct hfsnode
*fromFirst
, *fromSecond
, *toFirst
, *toSecond
;
1728 struct vnode
*from_vp
, *to_vp
;
1729 struct hfsmount
*hfsmp
;
1730 u_char tmp_name
[kHFSPlusMaxFileNameBytes
+1]; /* 766 bytes! */
1732 u_int32_t fromFileID
, toFileID
;
1733 u_int32_t fromParID
;
1735 int retval
= E_NONE
;
1736 DBG_FUNC_NAME("exchange");
1737 DBG_VOP_LOCKS_DECL(2);
1738 DBG_VOP_LOCKS_INIT(0,ap
->a_fvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1739 DBG_VOP_LOCKS_INIT(1,ap
->a_tvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1741 /* Set up variables and checks */
1742 from_vp
= ap
->a_fvp
;
1744 from_hp
= VTOH(from_vp
);
1745 to_hp
= VTOH(to_vp
);
1746 hfsmp
= VTOHFS(from_vp
);
1747 vcb
= HTOVCB(from_hp
);
1748 toFileID
= H_FILEID(to_hp
);
1749 fromFileID
= H_FILEID(from_hp
);
1750 fromParID
= H_DIRID(from_hp
);
1752 if (from_vp
->v_mount
!= to_vp
->v_mount
) {
1753 DBG_VOP_LOCKS_TEST(EXDEV
);
1757 /* Can only exchange file objects */
1758 if (from_vp
->v_type
!= VREG
|| to_vp
->v_type
!= VREG
) {
1759 DBG_VOP_LOCKS_TEST(EINVAL
);
1764 * Lock the siblink list
1765 * Check for multiple forks
1766 * If there are, we would need to:
1767 * 1. Unlock ourselves
1768 * 3. Traverse the list in a forward order...locking all vnodes
1769 * 4. Flush all buffers
1770 * 5. Perform the exchange
1771 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1772 * Notice that the sibling lock is kept during the whole operation. This quarentees
1773 * that no new forks are taken off or put on
1775 DBG_ASSERT(H_FORKTYPE(from_hp
)==kDataFork
&& H_FORKTYPE(to_hp
)==kDataFork
);
1776 fromFirst
= fromSecond
= toFirst
= toSecond
= NULL
;
1778 if (from_hp
->h_meta
->h_usecount
> 1) {
1780 * This has siblings, so remember the passed-in vnode,
1781 * unlock it if it is not the 'first' sibling,
1782 * and then lock the rest of the vnodes by sibling order.
1783 * Notice that the passed-in vnode is not vrele(), this
1784 * keeps the usecount>0, so it wont go away.
1786 simple_lock(&from_hp
->h_meta
->h_siblinglock
);
1787 fromFirst
= from_hp
->h_meta
->h_siblinghead
.cqh_first
;
1788 fromSecond
= fromFirst
->h_sibling
.cqe_next
;
1789 simple_unlock(&from_hp
->h_meta
->h_siblinglock
);
1791 if (fromFirst
== from_hp
) {
1792 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1793 fromSecond
= NULL
; /* its going away */
1795 VOP_UNLOCK(HTOV(from_hp
), 0, ap
->a_p
);
1796 if (vget(HTOV(fromFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1797 fromFirst
= NULL
; /* its going away */
1798 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1799 fromSecond
= NULL
; /* its going away */
1803 fromFirst
= from_hp
;
1806 if (to_hp
->h_meta
->h_usecount
> 1) {
1808 simple_lock(&to_hp
->h_meta
->h_siblinglock
);
1809 toFirst
= to_hp
->h_meta
->h_siblinghead
.cqh_first
;
1810 toSecond
= toFirst
->h_sibling
.cqe_next
;
1811 simple_unlock(&to_hp
->h_meta
->h_siblinglock
);
1813 if (toFirst
== to_hp
) {
1814 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1815 toSecond
= NULL
; /* its going away */
1817 VOP_UNLOCK(HTOV(to_hp
), 0, ap
->a_p
);
1818 if (vget(HTOV(toFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1819 toFirst
= NULL
; /* its going away */
1820 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1821 toSecond
= NULL
; /* its going away */
1829 /* Ignore any errors, we are doing a 'best effort' on flushing */
1831 (void) vinvalbuf(HTOV(fromFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1833 (void) vinvalbuf(HTOV(fromSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1835 (void) vinvalbuf(HTOV(toFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1837 (void) vinvalbuf(HTOV(toSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1840 /* lock catalog b-tree */
1841 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1842 if (retval
) goto Err_Exit
;
1844 /* lock extents b-tree iff there are overflow extents */
1845 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1846 so we neeed the tree to be always locked.
1848 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1849 if (retval
) goto Err_Exit_Relse
;
1851 /* Do the exchange */
1852 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
) ));
1854 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
1856 if (retval
!= E_NONE
) {
1857 DBG_ERR(("/tError trying to exchange: %d\n", retval
));
1858 goto Err_Exit_Relse
;
1862 /* Purge the vnodes from the name */
1864 cache_purge(HTOV(fromFirst
));
1866 cache_purge(HTOV(fromSecond
));
1868 cache_purge(HTOV(toFirst
));
1870 cache_purge(HTOV(toSecond
));
1872 /* Now exchange fileID, parID, name for the vnode itself */
1873 copystr(H_NAME(from_hp
), (char*) tmp_name
, strlen(H_NAME(from_hp
))+1, NULL
);
1874 hfs_chid(from_hp
, toFileID
, H_DIRID(to_hp
), H_NAME(to_hp
));
1875 hfs_chid(to_hp
, fromFileID
, fromParID
, (char*) tmp_name
);
1878 tmpLong
= HTOFCB(from_hp
)->fcbFlags
;
1879 HTOFCB(from_hp
)->fcbFlags
= HTOFCB(to_hp
)->fcbFlags
;
1880 HTOFCB(to_hp
)->fcbFlags
= tmpLong
;
1882 tmpLong
= from_hp
->h_meta
->h_crtime
;
1883 from_hp
->h_meta
->h_crtime
= to_hp
->h_meta
->h_crtime
;
1884 to_hp
->h_meta
->h_crtime
= tmpLong
;
1886 tmpLong
= from_hp
->h_meta
->h_butime
;
1887 from_hp
->h_meta
->h_butime
= to_hp
->h_meta
->h_butime
;
1888 to_hp
->h_meta
->h_butime
= tmpLong
;
1890 tmpLong
= from_hp
->h_meta
->h_atime
;
1891 from_hp
->h_meta
->h_atime
= to_hp
->h_meta
->h_atime
;
1892 to_hp
->h_meta
->h_atime
= tmpLong
;
1894 tmpLong
= from_hp
->h_meta
->h_ctime
;
1895 from_hp
->h_meta
->h_ctime
= to_hp
->h_meta
->h_ctime
;
1896 to_hp
->h_meta
->h_ctime
= tmpLong
;
1898 tmpLong
= from_hp
->h_meta
->h_gid
;
1899 from_hp
->h_meta
->h_gid
= to_hp
->h_meta
->h_gid
;
1900 to_hp
->h_meta
->h_gid
= tmpLong
;
1902 tmpLong
= from_hp
->h_meta
->h_uid
;
1903 from_hp
->h_meta
->h_uid
= to_hp
->h_meta
->h_uid
;
1904 to_hp
->h_meta
->h_uid
= tmpLong
;
1906 tmpLong
= from_hp
->h_meta
->h_pflags
;
1907 from_hp
->h_meta
->h_pflags
= to_hp
->h_meta
->h_pflags
;
1908 to_hp
->h_meta
->h_pflags
= tmpLong
;
1910 tmpLong
= from_hp
->h_meta
->h_mode
;
1911 from_hp
->h_meta
->h_mode
= to_hp
->h_meta
->h_mode
;
1912 to_hp
->h_meta
->h_mode
= tmpLong
;
1914 tmpLong
= from_hp
->h_meta
->h_rdev
;
1915 from_hp
->h_meta
->h_rdev
= to_hp
->h_meta
->h_rdev
;
1916 to_hp
->h_meta
->h_rdev
= tmpLong
;
1918 tmpLong
= from_hp
->h_meta
->h_size
;
1919 from_hp
->h_meta
->h_size
= to_hp
->h_meta
->h_size
;
1920 to_hp
->h_meta
->h_size
= tmpLong
;
1926 /* unlock catalog b-tree */
1927 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1934 * At this point, the vnodes' data is switched, but are on the old hash list.
1935 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1938 * A fork is trying to be added while exchanging...It got the hash lock,
1939 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1940 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1941 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1942 * Not very pretty, becarefull that this works ok
1944 * Same as the above, but before the move is made (like at this very spot), the new vnode
1945 * is added and a vget is requested for that new vnode, it would have old data
1946 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1948 * Hey! Same as above, but it is added after all the moving
1949 * So now there is a vnode with the old data, on the old hash...it will become
1950 * lost next time that a vget() is done
1952 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1953 * opposite that we are doing now
1955 hfs_vhashmove(from_hp
, fromFileID
);
1956 hfs_vhashmove(to_hp
, toFileID
);
1961 debug_check_vnode(HTOV(fromFirst
), 0);
1963 debug_check_vnode(HTOV(fromSecond
), 0);
1965 debug_check_vnode(HTOV(toFirst
), 0);
1967 debug_check_vnode(HTOV(toSecond
), 0);
1971 /* Unlock any forks, and the sibling list */
1972 if (to_hp
->h_meta
->h_usecount
> 1) {
1973 if (to_hp
== toFirst
) {
1975 vput(HTOV(toSecond
));
1978 vrele(HTOV(toSecond
)); /* decrement, return it locked */
1980 vput(HTOV(toFirst
));
1983 if (from_hp
->h_meta
->h_usecount
> 1) {
1984 if (from_hp
== fromFirst
) {
1986 vput(HTOV(fromSecond
));
1989 vrele(HTOV(fromSecond
)); /* decrement, return it locked */
1991 vput(HTOV(fromFirst
));
1995 DBG_VOP_LOCKS_TEST(retval
);
2001 * Change a vnode's file id, parent id and name
2003 * Assumes the vnode is locked and is of type VREG
2006 hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
)
2008 DBG_ASSERT(HTOV(hp
)->v_type
== VREG
);
2011 H_FILEID(hp
) = fid
; /* change h_nodeID */
2014 hfs_set_metaname(name
, hp
->h_meta
, HTOHFS(hp
));
2025 IN struct vnode *vp;
2026 IN struct ucred *cred;
2035 struct vop_fsync_args
/* {
2037 struct ucred *a_cred;
2042 struct vnode
*vp
= ap
->a_vp
;
2043 struct hfsnode
*hp
= VTOH(vp
);
2045 register struct buf
*bp
;
2050 DBG_FUNC_NAME("fsync");
2051 DBG_VOP_LOCKS_DECL(1);
2052 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
2053 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2054 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
2055 DBG_HFS_NODE_CHECK(ap
->a_vp
);
2058 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2063 * First of all, write out any clusters.
2068 * Flush all dirty buffers associated with a vnode.
2072 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2073 nbp
= bp
->b_vnbufs
.le_next
;
2074 if ((bp
->b_flags
& B_BUSY
))
2076 if ((bp
->b_flags
& B_DELWRI
) == 0)
2077 panic("hfs_fsync: not dirty");
2079 bp
->b_flags
|= B_BUSY
;
2080 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2083 * Wait for I/O associated with indirect blocks to complete,
2084 * since there is no way to quickly wait for them below.
2086 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp
->b_blkno
, bp
->b_lblkno
));
2087 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
) {
2090 (void) VOP_BWRITE(bp
);
2094 if (vp
->v_flag
& VHASDIRTY
)
2097 if (ap
->a_waitfor
== MNT_WAIT
) {
2098 while (vp
->v_numoutput
) {
2099 vp
->v_flag
|= VBWAIT
;
2100 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
2103 /* I have seen this happen for swapfile. So it is safer to
2104 * check for dirty buffers again. --Umesh
2106 if (vp
->v_dirtyblkhd
.lh_first
|| (vp
->v_flag
& VHASDIRTY
)) {
2107 vprint("hfs_fsync: dirty", vp
);
2115 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2119 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2120 BTSetLastSync(HTOFCB(hp
), tv
.tv_sec
);
2122 if (H_FORKTYPE(hp
) != kSysFile
) {
2123 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, ap
->a_waitfor
== MNT_WAIT
);
2125 if (retval
!= E_NONE
) {
2126 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname
, H_NAME(hp
)));
2130 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2132 if (ap
->a_waitfor
== MNT_WAIT
) {
2133 DBG_ASSERT(vp
->v_dirtyblkhd
.lh_first
== NULL
);
2135 DBG_VOP_LOCKS_TEST(retval
);
2136 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2142 hfs_fsync_transaction(struct vnode
*vp
)
2144 struct hfsnode
*hp
= VTOH(vp
);
2145 register struct buf
*bp
;
2151 * Flush all dirty buffers associated with a vnode.
2156 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2157 nbp
= bp
->b_vnbufs
.le_next
;
2158 if ((bp
->b_flags
& B_BUSY
))
2160 if ((bp
->b_flags
& B_DELWRI
) == 0)
2161 panic("hfs_fsync: not dirty");
2162 if ( !(bp
->b_flags
& B_LOCKED
))
2166 bp
->b_flags
|= B_BUSY
;
2167 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2177 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2178 (void) BTSetLastSync(VTOFCB(vp
), tv
.tv_sec
);
2179 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2190 IN WILLRELE struct vnode *dvp;
2191 IN WILLRELE struct vnode *vp;
2192 IN struct componentname *cnp;
2198 struct vop_remove_args
/* {
2199 struct vnode *a_dvp;
2201 struct componentname *a_cnp;
2204 struct vnode
*vp
= ap
->a_vp
;
2205 struct vnode
*dvp
= ap
->a_dvp
;
2206 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
2207 struct hfsmount
*hfsmp
= HTOHFS(hp
);
2208 struct proc
*p
= current_proc();
2210 int retval
, use_count
;
2213 DBG_FUNC_NAME("remove");
2214 DBG_VOP_LOCKS_DECL(2);
2215 DBG_VOP_PRINT_FUNCNAME();
2216 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
2217 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2218 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2219 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2223 if ((hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2224 (VTOH(dvp
)->h_meta
->h_pflags
& APPEND
)) {
2229 if (vp
->v_usecount
> 1) {
2231 * the namei done for the rename took a reference on the
2232 * vnode. Hence set 1 in the tookref parameter
2235 if(UBCISVALID(vp
) && !ubc_isinuse(vp
, 1))
2237 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2238 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2239 /* Carbon semantics prohibits deleting busy files */
2248 tv
= time
; /* Done here, so all times are the same */
2250 /* Check other siblings for in use also */
2251 /* Uncache everything and make sure no other usecount */
2253 * This assumes the presence of the most 1 sibling
2255 * a. loop through the siblings looking for another
2256 * b. If we find ourselves...skip it
2257 * If there was a sibling:
2258 * a. Check for a positve usecount
2259 * b. uncache any pages
2260 * c. Write out and memory changes
2261 * The idea is to keep the h_siblinglock as little as possible
2263 if (hp
->h_meta
->h_usecount
> 1) {
2264 struct vnode
*sib_vp
= NULL
;
2265 struct hfsnode
*nhp
;
2267 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
2268 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
2269 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
2271 /* Loop through all siblings, skipping ourselves */
2272 simple_lock(&hp
->h_meta
->h_siblinglock
);
2273 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
2274 if (nhp
== hp
) /* skip ourselves */
2278 simple_unlock(&hp
->h_meta
->h_siblinglock
);
2280 /* Check to see if the other fork is in use */
2281 DBG_ASSERT(sib_vp
!= NULL
);
2282 simple_lock(&sib_vp
->v_interlock
);
2283 use_count
= sib_vp
->v_usecount
;
2284 simple_unlock(&sib_vp
->v_interlock
);
2285 if (use_count
> 0) {
2287 * This is a sibling vnode and we did not take
2288 * a reference on it.
2289 * Hence set 0 in the tookref parameter
2292 if(UBCISVALID(sib_vp
) && !ubc_isinuse(sib_vp
, 0))
2294 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2295 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2296 /* Carbon semantics prohibits deleting busy files */
2301 } /* use_count > 0 */
2305 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2306 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
2308 * XXX SER An intelligient person would ask, why flush out changes
2309 * that are going to be deleted? See the next comment.
2311 if ((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) || (VTOFCB(sib_vp
)->fcbFlags
2312 & fcbModifiedMask
)) {
2313 DBG_ASSERT((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) != 0);
2314 VOP_UPDATE(sib_vp
, &tv
, &tv
, 0);
2317 /* Invalidate the buffers, ignore the results */
2318 (void) vinvalbuf(sib_vp
, 0, NOCRED
, p
, 0, 0);
2322 }; /* h_use_count > 1 */
2325 * remove the entry from the namei cache:
2326 * We do it early before any linking/busy file wierdness, make sure the
2331 /* Flush out any catalog changes */
2332 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2333 * and not from memory which is more correct
2335 if ((hp
->h_nodeflags
& IN_MODIFIED
) || (HTOFCB(hp
)->fcbFlags
& fcbModifiedMask
))
2337 DBG_ASSERT((hp
->h_nodeflags
& IN_MODIFIED
) != 0);
2338 VOP_UPDATE(vp
, &tv
, &tv
, 0);
2341 /* lock catalog b-tree */
2342 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2343 if (retval
!= E_NONE
) {
2349 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2354 * Multi-linked files just need their link node deleted from the catalog
2356 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
2358 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
2359 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
2360 retval
= ENOENT
; /* name missing */
2364 /* lock extents b-tree (also protects volume bitmap) */
2365 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2366 if (retval
!= E_NONE
) {
2368 goto out2
; /* unlock catalog b-tree on the way out */
2371 retval
= hfsDelete (HTOVCB(hp
), H_FILEID(VTOH(dvp
)),
2372 ap
->a_cnp
->cn_nameptr
, TRUE
, H_HINT(hp
));
2374 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2379 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2380 hp
->h_nodeflags
|= IN_CHANGE
;
2381 if (--hp
->h_meta
->h_nlink
< 1)
2382 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2384 /* name and parent fields are no longer valid so invalidate them */
2385 H_DIRID(hp
) = kUnknownID
;
2386 hfs_set_metaname("\0", hp
->h_meta
, HTOHFS(hp
));
2388 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
2389 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2391 * This is a deleted file no new clients
2392 * would be able to look it up. Maked the VM object
2393 * not cachable so that it dies as soon as the last
2394 * mapping disappears. This will reclaim the disk
2395 * space as soon as possible.
2398 goto out2
; /* link deleted, all done */
2403 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2404 * an active vnode is translated to a move/rename so the file appears
2405 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2408 UInt32 hint
= H_HINT(hp
);
2411 MAKE_DELETED_NAME(nodeName
, H_FILEID(hp
));
2413 retval
= hfsMoveRename (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
),
2414 hfsmp
->hfs_private_metadata_dir
, nodeName
, &hint
);
2415 if (retval
) goto out2
;
2417 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2418 hp
->h_nodeflags
|= IN_CHANGE
;
2420 /* update name so Catalog lookups succeed */
2422 H_DIRID(hp
) = hfsmp
->hfs_private_metadata_dir
;
2423 hfs_set_metaname(nodeName
, hp
->h_meta
, HTOHFS(hp
));
2426 * This is an open deleted file no new clients
2427 * would be able to look it up. Maked the VM object
2428 * not cachable so that it dies as soon as the last
2429 * mapping disappears. This will reclaim the disk
2430 * space as soon as possible.
2433 goto out2
; /* all done, unlock the catalog */
2436 /* unlock the Catalog */
2437 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2439 /* Invalidate the buffers */
2440 if ((retval
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0)))
2443 if(UBCINFOEXISTS(vp
))
2444 (void)ubc_setsize(vp
, (off_t
)0);
2447 /* lock catalog b-tree */
2448 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2449 if (retval
!= E_NONE
) {
2453 /* lock extents b-tree (also protects volume bitmap) */
2454 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2455 if (retval
!= E_NONE
) {
2457 goto out2
; /* unlock catalog b-tree on the way out */
2460 /* remove entry from catalog and free any blocks used */
2461 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
2464 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2465 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2470 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2471 hp
->h_meta
->h_mode
= 0; /* Makes the node go away...see inactive */
2472 /* clear the block mappings */
2473 hp
->fcbPLen
= (u_int64_t
)0;
2474 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
2476 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2482 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2487 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2491 VOP_UNLOCK(vp
, 0, p
);
2499 DBG_VOP_LOCKS_TEST(retval
);
2506 #% rename sourcePar_vp U U U
2507 #% rename source_vp U U U
2508 #% rename targetPar_vp L U U
2509 #% rename target_vp X U U
2512 IN WILLRELE struct vnode *sourcePar_vp;
2513 IN WILLRELE struct vnode *source_vp;
2514 IN struct componentname *source_cnp;
2515 IN WILLRELE struct vnode *targetPar_vp;
2516 IN WILLRELE struct vnode *target_vp;
2517 IN struct componentname *target_cnp;
2523 * source's parent directory is unlocked
2524 * source file or directory is unlocked
2525 * destination's parent directory is locked
2526 * destination file or directory is locked if it exists
2529 * all denodes should be released
2535 struct vop_rename_args
/* {
2536 struct vnode *a_fdvp;
2537 struct vnode *a_fvp;
2538 struct componentname *a_fcnp;
2539 struct vnode *a_tdvp;
2540 struct vnode *a_tvp;
2541 struct componentname *a_tcnp;
2544 struct vnode
*target_vp
= ap
->a_tvp
;
2545 struct vnode
*targetPar_vp
= ap
->a_tdvp
;
2546 struct vnode
*source_vp
= ap
->a_fvp
;
2547 struct vnode
*sourcePar_vp
= ap
->a_fdvp
;
2548 struct componentname
*target_cnp
= ap
->a_tcnp
;
2549 struct componentname
*source_cnp
= ap
->a_fcnp
;
2550 struct proc
*p
= source_cnp
->cn_proc
;
2551 struct hfsnode
*target_hp
, *targetPar_hp
, *source_hp
, *sourcePar_hp
;
2552 u_int32_t oldparent
= 0, newparent
= 0;
2553 int doingdirectory
= 0;
2556 struct hfsCatalogInfo catInfo
;
2557 u_int32_t tehint
= 0;
2558 DBG_VOP_LOCKS_DECL(4);
2560 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2561 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fvp
);DBG_VOP_CONT(("\n"));
2562 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fdvp
);DBG_VOP_CONT(("\n"));
2563 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tvp
);DBG_VOP_CONT(("\n"));
2564 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tdvp
);DBG_VOP_CONT(("\n"));
2565 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_fcnp
);DBG_VOP_CONT(("\n"));
2566 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_tcnp
);DBG_VOP_CONT(("\n"));
2567 DBG_VOP_LOCKS_INIT(0,ap
->a_fdvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2568 DBG_VOP_LOCKS_INIT(1,ap
->a_fvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2569 DBG_VOP_LOCKS_INIT(2,ap
->a_tdvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2570 DBG_VOP_LOCKS_INIT(3,ap
->a_tvp
, VOPDBG_LOCKNOTNIL
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2571 WRITE_CK(ap
->a_fdvp
, funcname
);
2572 DBG_HFS_NODE_CHECK(ap
->a_fdvp
);
2573 DBG_HFS_NODE_CHECK(ap
->a_tdvp
);
2576 if ((target_cnp
->cn_flags
& HASBUF
) == 0 ||
2577 (source_cnp
->cn_flags
& HASBUF
) == 0)
2578 panic("hfs_rename: no name");
2581 DBG_ASSERT((ap
->a_fdvp
->v_type
== VDIR
) && (ap
->a_tdvp
->v_type
== VDIR
));
2582 target_hp
= targetPar_hp
= source_hp
= sourcePar_hp
= 0;
2584 /* If fvp is the same as tvp...then we are just changing case, ignore target_vp */
2586 * This must be done now, since the value of target_vp is used to
2587 * determine wether to unlock it (for instance, goto abortit).
2588 * In this case, target_vp comes in unlocked
2590 if (source_vp
== target_vp
)
2594 * Check for cross-device rename.
2596 if ((source_vp
->v_mount
!= targetPar_vp
->v_mount
) ||
2597 (target_vp
&& (source_vp
->v_mount
!= target_vp
->v_mount
))) {
2603 * Check for access permissions
2605 if (target_vp
&& ((VTOH(target_vp
)->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2606 (VTOH(targetPar_vp
)->h_meta
->h_pflags
& APPEND
))) {
2612 * Force Carbon renames to have MacUnicode encoding
2614 if ((VTOH(targetPar_vp
)->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
2615 tehint
= kTextEncodingMacUnicode
;
2618 if ((retval
= vn_lock(source_vp
, LK_EXCLUSIVE
, p
)))
2621 sourcePar_hp
= VTOH(sourcePar_vp
);
2622 source_hp
= VTOH(source_vp
);
2623 oldparent
= H_FILEID(sourcePar_hp
);
2624 if ((source_hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) || (sourcePar_hp
->h_meta
->h_pflags
& APPEND
)) {
2625 VOP_UNLOCK(source_vp
, 0, p
);
2631 * Be sure we are not renaming ".", "..", or an alias of ".". This
2632 * leads to a crippled directory tree. It's pretty tough to do a
2633 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2634 * doesn't work if the ".." entry is missing.
2636 if ((source_hp
->h_meta
->h_mode
& IFMT
) == IFDIR
) {
2637 if ((source_cnp
->cn_namelen
== 1 && source_cnp
->cn_nameptr
[0] == '.')
2638 || sourcePar_hp
== source_hp
2639 || (source_cnp
->cn_flags
&ISDOTDOT
)
2640 || (source_hp
->h_nodeflags
& IN_RENAME
)) {
2641 VOP_UNLOCK(source_vp
, 0, p
);
2645 source_hp
->h_nodeflags
|= IN_RENAME
;
2646 doingdirectory
= TRUE
;
2651 * >>>> Transit between abort and bad <<<<
2655 targetPar_hp
= VTOH(targetPar_vp
);
2657 target_hp
= VTOH(target_vp
);
2659 DBG_ASSERT(target_hp
== NULL
);
2661 newparent
= H_FILEID(targetPar_hp
);
2663 /* Test to make sure we are not crossing devices */
2664 /* XXX SER Is this necesary, does catalog manager take care of this? */
2666 if (H_DEV(target_hp
) != H_DEV(targetPar_hp
) || H_DEV(target_hp
) != H_DEV(source_hp
))
2667 panic("rename: EXDEV");
2670 if (H_DEV(targetPar_hp
) != H_DEV(source_hp
))
2671 panic("rename: EXDEV");
2674 retval
= VOP_ACCESS(source_vp
, VWRITE
, target_cnp
->cn_cred
, target_cnp
->cn_proc
);
2675 if (doingdirectory
&& (newparent
!= oldparent
)) {
2676 if (retval
) /* write access check above */
2679 retval
= 0; /* Reset value from above, we dont care about it anymore */
2682 * If the destination exists, then be sure its type (file or dir)
2683 * matches that of the source. And, if it is a directory make sure
2684 * it is empty. Then delete the destination.
2689 * If the parent directory is "sticky", then the user must
2690 * own the parent directory, or the destination of the rename,
2691 * otherwise the destination may not be changed (except by
2692 * root). This implements append-only directories.
2694 if ((targetPar_hp
->h_meta
->h_mode
& S_ISTXT
) && (target_cnp
->cn_cred
->cr_uid
!= 0) &&
2695 target_cnp
->cn_cred
->cr_uid
!= targetPar_hp
->h_meta
->h_uid
&&
2696 target_cnp
->cn_cred
->cr_uid
!= target_hp
->h_meta
->h_uid
) {
2702 * VOP_REMOVE will vput targetPar_vp so we better bump
2703 * its ref count and relockit, always set target_vp to
2704 * NULL afterwards to indicate that were done with it.
2708 cache_purge(target_vp
);
2711 target_cnp
->cn_flags
&= ~SAVENAME
;
2714 retval
= VOP_REMOVE(targetPar_vp
, target_vp
, target_cnp
);
2715 (void) vn_lock(targetPar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2720 if (retval
) goto bad
;
2725 if (newparent
!= oldparent
)
2726 vn_lock(sourcePar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2728 /* remove the existing entry from the namei cache: */
2729 cache_purge(source_vp
);
2731 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
2733 /* lock catalog b-tree */
2734 retval
= hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2736 if (newparent
!= oldparent
) /* unlock the lock we just got */
2737 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2741 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2742 retval
= MoveRenameCatalogNode(HTOVCB(source_hp
), H_DIRID(source_hp
),
2743 source_cnp
->cn_nameptr
, H_HINT(source_hp
),
2744 H_FILEID(VTOH(targetPar_vp
)),
2745 target_cnp
->cn_nameptr
, &H_HINT(source_hp
), tehint
);
2746 retval
= MacToVFSError(retval
);
2749 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2750 catInfo
.hint
= H_HINT(source_hp
);
2751 retval
= hfs_getcatalog(HTOVCB(source_hp
), H_FILEID(targetPar_hp
), target_cnp
->cn_nameptr
, target_cnp
->cn_namelen
, &catInfo
);
2754 /* unlock catalog b-tree */
2755 (void) hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2757 if (newparent
!= oldparent
)
2758 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2760 if (retval
) goto bad
;
2762 H_DIRID(source_hp
) = H_FILEID(targetPar_hp
);
2764 hfs_name_CatToMeta(&catInfo
.nodeData
, source_hp
->h_meta
);
2766 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
2768 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2772 * Timestamp both parent directories.
2773 * Note that if this is a rename within the same directory,
2774 * (where targetPar_hp == sourcePar_hp)
2775 * the code below is still safe and correct.
2777 targetPar_hp
->h_nodeflags
|= IN_UPDATE
;
2778 sourcePar_hp
->h_nodeflags
|= IN_UPDATE
;
2780 HFSTIMES(targetPar_hp
, &tv
, &tv
);
2781 HFSTIMES(sourcePar_hp
, &tv
, &tv
);
2784 vrele(sourcePar_vp
);
2787 DBG_VOP_LOCKS_TEST(retval
);
2788 if (retval
!= E_NONE
) {
2789 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2794 if (retval
&& doingdirectory
)
2795 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2797 if (targetPar_vp
== target_vp
)
2798 vrele(targetPar_vp
);
2805 vrele(sourcePar_vp
);
2807 if (VOP_ISLOCKED(source_vp
))
2812 DBG_VOP_LOCKS_TEST(retval
);
2813 if (retval
!= E_NONE
) {
2814 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2820 VOP_ABORTOP(targetPar_vp
, target_cnp
); /* XXX, why not in NFS? */
2822 if (targetPar_vp
== target_vp
)
2823 vrele(targetPar_vp
);
2830 VOP_ABORTOP(sourcePar_vp
, source_cnp
); /* XXX, why not in NFS? */
2832 vrele(sourcePar_vp
);
2835 DBG_VOP_LOCKS_TEST(retval
);
2836 if (retval
!= E_NONE
) {
2837 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2850 IN WILLRELE struct vnode *dvp;
2851 OUT struct vnode **vpp;
2852 IN struct componentname *cnp;
2853 IN struct vattr *vap;
2855 We are responsible for freeing the namei buffer,
2856 it is done in hfs_makenode()
2861 struct vop_mkdir_args
/* {
2862 struct vnode *a_dvp;
2863 struct vnode **a_vpp;
2864 struct componentname *a_cnp;
2865 struct vattr *a_vap;
2868 struct proc
*p
= current_proc();
2870 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
2872 DBG_FUNC_NAME("mkdir");
2873 DBG_VOP_LOCKS_DECL(2);
2874 DBG_VOP_PRINT_FUNCNAME();
2875 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
2876 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2878 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2879 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2881 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
));
2882 WRITE_CK( ap
->a_dvp
, funcname
);
2883 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
2884 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
2886 /* Create the vnode */
2887 DBG_ASSERT((ap
->a_cnp
->cn_flags
& SAVESTART
) == 0);
2888 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
2889 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2891 if (retval
!= E_NONE
) {
2892 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
2893 DBG_VOP_LOCKS_TEST(retval
);
2897 DBG_VOP_LOCKS_TEST(E_NONE
);
2902 * Rmdir system call.
2907 IN WILLRELE struct vnode *dvp;
2908 IN WILLRELE struct vnode *vp;
2909 IN struct componentname *cnp;
2915 struct vop_rmdir_args
/* {
2916 struct vnode *a_dvp;
2918 struct componentname *a_cnp;
2921 struct vnode
*vp
= ap
->a_vp
;
2922 struct vnode
*dvp
= ap
->a_dvp
;
2923 struct hfsnode
*hp
= VTOH(vp
);
2924 struct proc
*p
= current_proc();
2926 DBG_FUNC_NAME("rmdir");
2927 DBG_VOP_LOCKS_DECL(2);
2928 DBG_VOP_PRINT_FUNCNAME();
2929 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);DBG_VOP_CONT(("\n"));
2930 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2931 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2933 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2934 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2939 DBG_VOP_LOCKS_TEST(EINVAL
);
2944 * HFS differs from UFS here in that we don't allow removing
2945 * a directory that in use by others - even if its empty.
2947 * In the future we might want to allow this just like we do
2948 * for files (by renaming the busy directory).
2951 if (vp
->v_usecount
> 1) {
2952 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname
, vp
->v_usecount
));
2957 /* remove the entry from the namei cache: */
2960 /* lock catalog b-tree */
2961 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2962 if (retval
!= E_NONE
) {
2966 /* remove entry from catalog */
2967 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), FALSE
, H_HINT(hp
));
2969 /* unlock catalog b-tree */
2970 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2973 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
; /* Set the parent to be updated */
2974 hp
->h_meta
->h_mode
= 0; /* Makes the vnode go away...see inactive */
2975 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2983 DBG_VOP_LOCKS_TEST(retval
);
2988 * symlink -- make a symbolic link
2989 #% symlink dvp L U U
2990 #% symlink vpp - U -
2992 # XXX - note that the return vnode has already been VRELE'ed
2993 # by the filesystem layer. To use it you must use vget,
2994 # possibly with a further namei.
2997 IN WILLRELE struct vnode *dvp;
2998 OUT WILLRELE struct vnode **vpp;
2999 IN struct componentname *cnp;
3000 IN struct vattr *vap;
3003 We are responsible for freeing the namei buffer,
3004 it is done in hfs_makenode().
3010 struct vop_symlink_args
/* {
3011 struct vnode *a_dvp;
3012 struct vnode **a_vpp;
3013 struct componentname *a_cnp;
3014 struct vattr *a_vap;
3018 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
3019 struct proc
*p
= current_proc();
3022 struct buf
*bp
= NULL
;
3024 /* HFS standard disks don't support symbolic links */
3025 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
3026 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
3028 return (EOPNOTSUPP
);
3031 /* Check for empty target name */
3032 if (ap
->a_target
[0] == 0) {
3033 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
3038 /* Create the vnode */
3039 retval
= hfs_makenode(IFLNK
| ap
->a_vap
->va_mode
, 0, ap
->a_dvp
,
3041 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
3043 if (retval
!= E_NONE
) {
3048 len
= strlen(ap
->a_target
);
3050 hp
->fcbClmpSize
= VTOVCB(vp
)->blockSize
;
3052 /* Allocate space for the link */
3053 retval
= VOP_TRUNCATE(vp
, len
, IO_NOZEROFILL
,
3054 ap
->a_cnp
->cn_cred
, ap
->a_cnp
->cn_proc
);
3058 /* Write the link to disk */
3059 bp
= getblk(vp
, 0, roundup((int)hp
->fcbEOF
, kHFSBlockSize
), 0, 0, BLK_META
);
3060 bzero(bp
->b_data
, bp
->b_bufsize
);
3061 bcopy(ap
->a_target
, bp
->b_data
, len
);
3062 bp
->b_flags
|= B_DIRTY
;
3072 * Dummy dirents to simulate the "." and ".." entries of the directory
3073 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
3074 * the size of these entries is the smallest needed to represent them
3075 * (only 12 byte each).
3077 static hfsdotentry rootdots
[2] = {
3080 sizeof(struct hfsdotentry
), /* d_reclen */
3081 DT_DIR
, /* d_type */
3087 sizeof(struct hfsdotentry
), /* d_reclen */
3088 DT_DIR
, /* d_type */
3094 static hfsdotentry emptyentry
= { 0 };
3097 * There is some confusion as to what the semantics of uio_offset are.
3098 * In ufs, it represents the actual byte offset within the directory
3099 * "file." HFS, however, just uses it as an entry counter - essentially
3100 * assuming that it has no meaning except to the hfs_readdir function.
3101 * This approach would be more efficient here, but some callers may
3102 * assume the uio_offset acts like a byte offset. NFS in fact
3103 * monkeys around with the offset field a lot between readdir calls.
3105 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3106 * fields is a mess as well. The libc function readdir() returns
3107 * NULL (indicating the end of a directory) when either
3108 * the getdirentries() syscall (which calls this and returns
3109 * the size of the buffer passed in less the value of uiop->uio_resid)
3110 * returns 0, or a direct record with a d_reclen of zero.
3111 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3112 * of the directory by testing uiop->uio_resid == 0. The solution
3113 * is to pad the size of the last struct direct in a given
3114 * block to fill the block if we are not at the end of the directory.
3117 struct callbackstate
{
3118 u_int32_t cbs_parentID
;
3119 u_int32_t cbs_hiddenDirID
;
3120 off_t cbs_lastoffset
;
3121 struct uio
* cbs_uio
;
3122 ExtendedVCB
* cbs_vcb
;
3123 int16_t cbs_hfsPlus
;
3129 ProcessCatalogEntry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3130 u_int16_t recordLen
, struct callbackstate
*state
)
3136 struct dirent catent
;
3138 if (state
->cbs_hfsPlus
)
3139 curID
= ckp
->hfsPlus
.parentID
;
3141 curID
= ckp
->hfs
.parentID
;
3143 /* We're done when parent directory changes */
3144 if (state
->cbs_parentID
!= curID
) {
3147 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3148 * so remove padding for now...
3152 * Pad the end of list with an empty record.
3153 * This eliminates an extra call by readdir(3c).
3155 catent
.d_fileno
= 0;
3156 catent
.d_reclen
= 0;
3158 catent
.d_namlen
= 0;
3159 *(int32_t*)&catent
.d_name
[0] = 0;
3161 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3163 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
3164 if (state
->cbs_result
== 0)
3165 state
->cbs_result
= ENOENT
;
3167 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3168 state
->cbs_result
= ENOENT
;
3170 return (0); /* stop */
3173 if (state
->cbs_hfsPlus
) {
3174 switch(crp
->recordType
) {
3175 case kHFSPlusFolderRecord
:
3176 catent
.d_type
= DT_DIR
;
3177 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
3179 case kHFSPlusFileRecord
:
3180 catent
.d_type
= DT_REG
;
3181 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
3184 return (0); /* stop */
3187 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3188 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
3189 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
3190 if (result
== ENAMETOOLONG
) {
3191 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3192 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
3195 switch(crp
->recordType
) {
3196 case kHFSFolderRecord
:
3197 catent
.d_type
= DT_DIR
;
3198 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
3200 case kHFSFileRecord
:
3201 catent
.d_type
= DT_REG
;
3202 catent
.d_fileno
= crp
->hfsFile
.fileID
;
3205 return (0); /* stop */
3208 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
3209 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3210 (ByteCount
*)&utf8chars
, catent
.d_name
);
3212 * When an HFS name cannot be encoded with the current
3213 * volume encoding we use MacRoman as a fallback.
3216 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3217 (ByteCount
*)&utf8chars
, catent
.d_name
);
3220 catent
.d_namlen
= utf8chars
;
3221 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
3223 /* hide our private meta data directory */
3224 if (curID
== kRootDirID
&&
3225 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
3226 catent
.d_type
== DT_DIR
)
3229 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3231 /* if this entry won't fit then we're done */
3232 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
)
3233 return (0); /* stop */
3235 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
3237 /* continue iteration if there's room */
3238 return (state
->cbs_result
== 0 &&
3239 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
3243 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3244 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3245 * "Nbytes must be greater than or equal to the block size associated with the file,
3246 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3247 * room for the . and .. entries that have to added manually.
3254 IN struct vnode *vp;
3255 INOUT struct uio *uio;
3256 IN struct ucred *cred;
3259 INOUT u_long **cookies;
3263 struct vop_readdir_args
/* {
3272 register struct uio
*uio
= ap
->a_uio
;
3273 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3274 struct proc
*p
= current_proc();
3275 ExtendedVCB
*vcb
= HTOVCB(hp
);
3276 off_t off
= uio
->uio_offset
;
3277 u_int32_t dirID
= H_FILEID(hp
);
3279 OSErr result
= noErr
;
3280 u_int32_t diroffset
;
3282 CatalogIterator
*cip
;
3284 struct callbackstate state
;
3287 DBG_FUNC_NAME("readdir");
3288 DBG_VOP_LOCKS_DECL(1);
3290 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3291 DBG_VOP_PRINT_FUNCNAME();
3292 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3293 DBG_HFS_NODE_CHECK(ap
->a_vp
);
3295 /* We assume it's all one big buffer... */
3296 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
) {
3300 /* Create the entries for . and .. */
3301 if (uio
->uio_offset
< sizeof(rootdots
)) {
3305 rootdots
[0].d_fileno
= dirID
;
3306 rootdots
[1].d_fileno
= H_DIRID(hp
);
3308 if (uio
->uio_offset
== 0) {
3309 dep
= (caddr_t
) &rootdots
[0];
3310 dotsize
= 2* sizeof(struct hfsdotentry
);
3311 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
3312 dep
= (caddr_t
) &rootdots
[1];
3313 dotsize
= sizeof(struct hfsdotentry
);
3319 retval
= uiomove(dep
, dotsize
, uio
);
3324 diroffset
= uio
->uio_offset
;
3326 /* lock catalog b-tree */
3327 retval
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
3328 if (retval
!= E_NONE
)
3331 /* get an iterator and position it */
3332 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
3334 result
= PositionIterator(cip
, diroffset
, &bi
, &op
);
3335 if (result
== cmNotFound
) {
3338 AgeCatalogIterator(cip
);
3340 } else if ((retval
= MacToVFSError(result
)))
3343 state
.cbs_hiddenDirID
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
3344 state
.cbs_lastoffset
= cip
->currentOffset
;
3345 state
.cbs_vcb
= vcb
;
3346 state
.cbs_uio
= uio
;
3347 state
.cbs_result
= 0;
3348 state
.cbs_parentID
= dirID
;
3350 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3351 state
.cbs_hfsPlus
= 1;
3353 state
.cbs_hfsPlus
= 0;
3355 /* process as many entries as possible... */
3356 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
, &bi
,
3357 (IterateCallBackProcPtr
)ProcessCatalogEntry
, &state
);
3359 if (state
.cbs_result
)
3360 retval
= state
.cbs_result
;
3362 retval
= MacToVFSError(result
);
3364 if (retval
== ENOENT
) {
3370 cip
->currentOffset
= state
.cbs_lastoffset
;
3371 cip
->nextOffset
= uio
->uio_offset
;
3372 UpdateCatalogIterator(&bi
, cip
);
3379 AgeCatalogIterator(cip
);
3382 (void) ReleaseCatalogIterator(cip
);
3384 /* unlock catalog b-tree */
3385 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3387 if (retval
!= E_NONE
) {
3388 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname
, retval
,
3389 H_FILEID(hp
), H_NAME(hp
)));
3394 /* were we already past eof ? */
3395 if (uio
->uio_offset
== off
) {
3400 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3401 hp
->h_nodeflags
|= IN_ACCESS
;
3403 /* Bake any cookies */
3404 if (!retval
&& ap
->a_ncookies
!= NULL
) {
3405 struct dirent
* dpStart
;
3406 struct dirent
* dpEnd
;
3413 * Only the NFS server uses cookies, and it loads the
3414 * directory block into system space, so we can just look at
3417 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
3418 panic("hfs_readdir: unexpected uio from NFS server");
3419 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
3420 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
3421 for (dp
= dpStart
, ncookies
= 0;
3422 dp
< dpEnd
&& dp
->d_reclen
!= 0;
3423 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
3425 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
3426 for (dp
= dpStart
, cookiep
= cookies
;
3428 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
3429 off
+= dp
->d_reclen
;
3430 *cookiep
++ = (u_long
) off
;
3432 *ap
->a_ncookies
= ncookies
;
3433 *ap
->a_cookies
= cookies
;
3439 *ap
->a_eofflag
= eofflag
;
3441 DBG_VOP_LOCKS_TEST(retval
);
3447 * readdirattr operation will return attributes for the items in the
3448 * directory specified.
3450 * It does not do . and .. entries. The problem is if you are at the root of the
3451 * hfs directory and go to .. you could be crossing a mountpoint into a
3452 * different (ufs) file system. The attributes that apply for it may not
3453 * apply for the file system you are doing the readdirattr on. To make life
3454 * simpler, this call will only return entries in its directory, hfs like.
3456 * 1.getattrlist creates a thread record if the objpermanentid attribute
3457 * is requested. Just do EINVAL for now and fix later.
3458 * 2. more than one for uiovcnt support.
3459 * 3. put knohint (hints) in state for next call in
3460 * 4. credentials checking when rest of hfs does it.
3461 * 5. Do return permissions concatenation ???
3466 #% readdirattr vp L L L
3469 IN struct vnode *vp;
3470 IN struct attrlist *alist;
3471 INOUT struct uio *uio;
3474 OUT u_long *newstate;
3476 OUT u_long *actualCount;
3477 OUT u_long **cookies;
3478 IN struct ucred *cred;
3483 struct vop_readdirattr_args
/* {
3485 struct attrlist *alist;
3491 u_long *actualcount;
3496 struct vnode
*vp
= ap
->a_vp
;
3497 struct attrlist
*alist
= ap
->a_alist
;
3498 register struct uio
*uio
= ap
->a_uio
;
3499 u_long maxcount
= ap
->a_maxcount
;
3501 ExtendedVCB
*vcb
= HTOVCB(VTOH(vp
));
3502 UInt32 dirID
= H_FILEID(VTOH(vp
));
3503 struct proc
*proc
= current_proc(); /* could get this out of uio */
3504 off_t startoffset
= uio
->uio_offset
;
3505 struct hfsCatalogInfo catInfo
;
3508 u_long fixedblocksize
;
3509 u_long maxattrblocksize
;
3510 u_long currattrbufsize
;
3511 void *attrbufptr
= NULL
;
3515 *(ap
->a_actualcount
) = 0;
3516 *(ap
->a_eofflag
) = 0;
3518 /* check for invalid options, check vnode, and buffer space */
3519 if (((ap
->a_options
& ~FSOPT_NOINMEMUPDATE
) != 0) ||
3521 (uio
->uio_resid
<= 0) || (uio
->uio_iovcnt
> 1))
3524 /* this call doesn't take volume attributes */
3525 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
3526 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
3527 (alist
->volattr
!= 0) ||
3528 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
3529 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
3530 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0))
3533 /* Reject requests for unsupported options for now: */
3534 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
3535 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
)) ||
3536 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
) )
3539 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3540 * uiomove afterwards. It's an extra copy, but for now leave it alone
3542 fixedblocksize
= (sizeof(u_long
) + AttributeBlockSize(alist
)); /* u_long for length */
3543 maxattrblocksize
= fixedblocksize
;
3544 if (alist
->commonattr
& ATTR_CMN_NAME
)
3545 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
3546 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
3547 attrptr
= attrbufptr
;
3548 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3550 /* Since attributes passed back can contain variable ones (name), we can't just use
3551 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3552 * as hfs_readdir was originally doing. If this all we need to represent the current
3553 * state, then ap->a_state is not needed at all.
3555 /* index = ap->a_state; should not be less than 1 */
3556 index
= (uio
->uio_offset
/ sizeof(struct dirent
)) + 1;
3557 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
3560 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3561 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3562 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3564 catInfo
.hint
= kNoHint
; /* note, we may want to save the latest in state */
3565 while ((uio
->uio_resid
>= 0) && (maxcount
!=0 )) {
3566 /* better to check uio_resid against max or fixedblocksize, but won't work.
3567 * Depending on if dir or file, the attributes returned will be different.
3568 * Thus fixedblocksize is too large in some cases.Also, the variable
3569 * part (like name) could be between fixedblocksize and the max.
3573 /* Lock catalog b-tree */
3574 if ((retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, proc
)) != E_NONE
)
3577 catInfo
.nodeData
.cnd_iNodeNumCopy
= 0;
3578 result
= GetCatalogOffspring(vcb
, dirID
, index
, &catInfo
.nodeData
, NULL
, NULL
);
3580 hfs_resolvelink(vcb
, &catInfo
.nodeData
);
3582 /* Unlock catalog b-tree, unconditionally . Ties up the everything during enumeration */
3583 (void) hfs_metafilelocking( VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, proc
);
3585 if (result
!= noErr
) {
3586 if (result
== cmNotFound
) {
3587 *(ap
->a_eofflag
) = TRUE
;
3590 else retval
= MacToVFSError(result
);
3594 /* hide our private meta data directory as does hfs_readdir */
3595 if ((dirID
== kRootDirID
) &&
3596 catInfo
.nodeData
.cnd_nodeID
== VCBTOHFS(vcb
)->hfs_private_metadata_dir
&&
3597 catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) {
3600 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3604 *((u_long
*)attrptr
)++ = 0; /* move it past length */
3607 * Don't use data from cached vnodes when FSOPT_NOINMEMUPDATE
3608 * option is active or if this entry is a hard link.
3610 if ((ap
->a_options
& FSOPT_NOINMEMUPDATE
)
3611 || (catInfo
.nodeData
.cnd_iNodeNumCopy
!= 0)) {
3612 /* vp okay to use instead of root vp */
3613 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3615 struct vnode
*entry_vp
= NULL
;
3616 struct vnode
*rsrc_vp
= NULL
;
3621 * Flush out any in-memory state to the catalog record.
3623 * In the HFS locking hierarchy, the data fork vnode must
3624 * be acquired before the resource fork vnode.
3626 nodeid
= catInfo
.nodeData
.cnd_nodeID
;
3627 if (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
)
3628 nodetype
= kDirectory
;
3630 nodetype
= kDataFork
;
3632 /* Check for this entry's cached vnode: */
3633 entry_vp
= hfs_vhashget(H_DEV(VTOH(vp
)), nodeid
, nodetype
);
3635 /* Also check for a cached resource fork vnode: */
3636 if (nodetype
== kDataFork
) {
3637 rsrc_vp
= hfs_vhashget(H_DEV(VTOH(vp
)), nodeid
, kRsrcFork
);
3638 if ((rsrc_vp
!= NULL
)
3639 && (VTOH(rsrc_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
))) {
3640 /* Pick up resource fork info */
3641 CopyVNodeToCatalogNode(rsrc_vp
, &catInfo
.nodeData
);
3645 if (entry_vp
!= NULL
)
3646 PackAttributeBlock(alist
, entry_vp
, &catInfo
, &attrptr
, &varptr
);
3647 else if (rsrc_vp
!= NULL
)
3648 PackAttributeBlock(alist
, rsrc_vp
, &catInfo
, &attrptr
, &varptr
);
3650 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3657 currattrbufsize
= *((u_long
*)attrbufptr
) = ((char *)varptr
- (char *)attrbufptr
);
3659 /* now check if we can't fit in the buffer space remaining */
3660 if (currattrbufsize
> uio
->uio_resid
)
3663 retval
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, ap
->a_uio
);
3664 if (retval
!= E_NONE
)
3666 attrptr
= attrbufptr
;
3667 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3669 *ap
->a_actualcount
+= 1;
3672 /* Clean for the next loop */
3673 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3675 *ap
->a_newstate
= VTOH(vp
)->h_meta
->h_mtime
;/* before we unlock, know the mod date */
3677 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3679 if (!retval
&& ap
->a_cookies
!= NULL
) { /* CHECK THAT 0 wasn't passed in */
3686 /* Only the NFS server uses cookies, and it loads the
3687 * directory block into system space, so we can just look at
3690 if (uio
->uio_segflg
!= UIO_SYSSPACE
) /* || uio->uio_iovcnt != 1 checked earlier */
3691 panic("hfs_readdirattr: unexpected uio from NFS server");
3692 dpStart
= uio
->uio_iov
->iov_base
- (uio
->uio_offset
- startoffset
);
3693 dpEnd
= uio
->uio_iov
->iov_base
;
3694 MALLOC(cookies
, u_long
*, (*ap
->a_actualcount
)*sizeof(u_long
), M_TEMP
, M_WAITOK
);
3695 for (dp
= dpStart
, cookiep
= cookies
;
3697 dp
= ((caddr_t
) dp
+ *((u_long
*)dp
))) {
3698 *cookiep
++ = (u_long
)((caddr_t
)dp
+ sizeof(u_long
));
3700 *ap
->a_cookies
= cookies
;
3703 uio
->uio_offset
= startoffset
+ (*ap
->a_actualcount
)*sizeof(struct dirent
);
3706 if (attrbufptr
!= NULL
)
3707 FREE(attrbufptr
, M_TEMP
);
3713 * Return target name of a symbolic link
3714 #% readlink vp L L L
3717 IN struct vnode *vp;
3718 INOUT struct uio *uio;
3719 IN struct ucred *cred;
3724 struct vop_readlink_args
/* {
3727 struct ucred *a_cred;
3731 struct vnode
*vp
= ap
->a_vp
;
3732 struct hfsnode
*hp
= VTOH(vp
);
3734 if (vp
->v_type
!= VLNK
)
3737 /* Zero length sym links are not allowed */
3738 if (hp
->fcbEOF
== 0) {
3739 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
3743 /* Cache the path so we don't waste buffer cache resources */
3744 if (hp
->h_symlinkptr
== NULL
) {
3745 struct buf
*bp
= NULL
;
3747 if (H_ISBIGLINK(hp
))
3748 MALLOC(hp
->h_symlinkptr
, char *, hp
->fcbEOF
, M_TEMP
, M_WAITOK
);
3750 retval
= meta_bread(vp
, 0, roundup((int)hp
->fcbEOF
, kHFSBlockSize
), ap
->a_cred
, &bp
);
3754 if (hp
->h_symlinkptr
) {
3755 FREE(hp
->h_symlinkptr
, M_TEMP
);
3756 hp
->h_symlinkptr
= NULL
;
3761 bcopy(bp
->b_data
, H_SYMLINK(hp
), (size_t)hp
->fcbEOF
);
3764 bp
->b_flags
|= B_INVAL
; /* data no longer needed */
3769 retval
= uiomove((caddr_t
)H_SYMLINK(hp
), (int)hp
->fcbEOF
, ap
->a_uio
);
3776 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3777 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3778 #% abortop dvp = = =
3781 IN struct vnode *dvp;
3782 IN struct componentname *cnp;
3790 struct vop_abortop_args
/* {
3791 struct vnode *a_dvp;
3792 struct componentname *a_cnp;
3795 DBG_FUNC_NAME("abortop");
3796 DBG_VOP_LOCKS_DECL(1);
3797 DBG_VOP_PRINT_FUNCNAME();
3798 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
3799 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
3802 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3804 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
3805 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
3807 DBG_VOP_LOCKS_TEST(E_NONE
);
3811 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3814 #% inactive vp L U U
3817 IN struct vnode *vp;
3824 struct vop_inactive_args
/* {
3828 struct vnode
*vp
= ap
->a_vp
;
3829 struct hfsnode
*hp
= VTOH(vp
);
3830 struct proc
*p
= ap
->a_p
;
3833 extern int prtactive
;
3835 DBG_FUNC_NAME("inactive");
3836 DBG_VOP_LOCKS_DECL(1);
3837 DBG_VOP_PRINT_FUNCNAME();
3838 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3840 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3843 if (prtactive
&& vp
->v_usecount
<= 0)
3844 vprint("hfs_inactive: pushing active", vp
);
3846 if (vp
->v_usecount
!= 0)
3847 DBG_VOP(("%s: bad usecount = %d\n",funcname
,vp
->v_usecount
));
3850 * Ignore nodes related to stale file handles.
3852 if (hp
->h_meta
->h_mode
== 0)
3856 * Check for a postponed deletion
3858 if (hp
->h_meta
->h_metaflags
& IN_DELETED
) {
3859 hp
->h_meta
->h_metaflags
&= ~IN_DELETED
;
3861 error
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0);
3862 if (error
) goto out
;
3864 if(UBCINFOEXISTS(vp
))
3865 (void)ubc_setsize(vp
, (off_t
)0);
3868 * Note: we do not need a lock on the private metadata directory
3869 * since it never has a vnode associated with it.
3871 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3872 if (error
) goto out
;
3873 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3875 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3879 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
3882 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
3883 error
= hfsDelete(HTOVCB(hp
), VTOHFS(vp
)->hfs_private_metadata_dir
, iNodeName
, TRUE
, H_HINT(hp
));
3885 /* XXX can we leave orphaned sibling? */
3886 error
= hfsDelete(HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
3887 if (error
== ENOENT
) {
3888 /* try by fileID as a backup */
3889 error
= hfsDelete(HTOVCB(hp
), H_FILEID(hp
), NULL
, TRUE
, H_HINT(hp
));
3893 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
3894 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3895 if (error
) goto out
;
3897 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
3898 hp
->h_meta
->h_mode
= 0;
3899 /* clear the block mappings */
3900 hp
->fcbPLen
= (u_int64_t
)0;
3901 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
3903 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
3906 if (hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
3908 VOP_UPDATE(vp
, &tv
, &tv
, 0);
3912 VOP_UNLOCK(vp
, 0, p
);
3914 * If we are done with the inode, reclaim it
3915 * so that it can be reused immediately.
3917 if (hp
->h_meta
->h_mode
== 0)
3918 vrecycle(vp
, (struct slock
*)0, p
);
3920 /* XXX SER Here we might want to get rid of any other forks
3921 * The problem is that if we call vrecycle(), our structure
3922 * disappear from under us, we would need to remember, and expect
3923 * things to go to null or to disappear
3924 * But it stillw would be a good thing to remove vnodes
3925 * referencing stale data
3928 DBG_VOP_LOCKS_TEST(E_NONE
);
3933 Ignored since the locks are gone......
3937 IN struct vnode *vp;
3944 struct vop_reclaim_args
/* {
3948 struct vnode
*vp
= ap
->a_vp
;
3949 struct hfsnode
*hp
= VTOH(vp
);
3950 void *tdata
= vp
->v_data
;
3952 Boolean freeMeta
= true;
3953 struct vnode
*devvp
= NULL
;
3955 extern int prtactive
;
3956 DBG_FUNC_NAME("reclaim");
3957 DBG_VOP_LOCKS_DECL(1);
3958 DBG_VOP_PRINT_FUNCNAME();
3959 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3961 DBG_VOP_LOCKS_INIT(0, ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_ZERO
);
3964 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3965 created in their entirity could be getting cleaned up here.
3968 if (prtactive
&& vp
->v_usecount
!= 0)
3969 vprint("hfs_reclaim(): pushing active", vp
);
3971 hp
->h_nodeflags
|= IN_ALLOCATING
; /* Mark this as being incomplete */
3973 * This will remove the entry from the hash AND the sibling list
3974 * This will make sure everything is in a stable state to see if we can remove the meta
3975 * i.e. if this is the only fork...the sibling list will be empty
3979 DBG_ASSERT(tdata
!= NULL
);
3980 DBG_ASSERT(hp
->h_meta
!= NULL
);
3982 devvp
= hp
->h_meta
->h_devvp
; /* For later releasing */
3983 hp
->h_meta
->h_usecount
--;
3985 /* release the file meta if this is the last fork */
3986 if (H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
) {
3987 if (hp
->h_meta
->h_siblinghead
.cqh_first
!= (void *) &hp
->h_meta
->h_siblinghead
)
3992 DBG_ASSERT(hp
->h_meta
->h_usecount
== 0);
3993 if (hp
->h_meta
->h_metaflags
& IN_LONGNAME
) {
3995 DBG_ASSERT(tname
!= NULL
);
3996 FREE(tname
, M_TEMP
);
3998 FREE_ZONE(hp
->h_meta
, sizeof(struct hfsfilemeta
), M_HFSFMETA
);
4002 DBG_ASSERT(hp
->h_meta
->h_usecount
== 1);
4004 /* Dump cached symlink data */
4005 if ((vp
->v_type
== VLNK
) && (hp
->h_symlinkptr
!= NULL
)) {
4006 if (H_ISBIGLINK(hp
))
4007 FREE(hp
->h_symlinkptr
, M_TEMP
);
4008 hp
->h_symlinkptr
= NULL
;
4012 * Purge old data structures associated with the inode.
4019 /* Free our data structs */
4020 FREE_ZONE(tdata
, sizeof(struct hfsnode
), M_HFSNODE
);
4023 DBG_VOP_LOCKS_TEST(E_NONE
);
4029 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
4033 IN struct vnode *vp;
4040 struct vop_lock_args
/* {
4046 struct vnode
* vp
= ap
->a_vp
;
4047 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4050 DBG_FUNC_NAME("lock");
4051 DBG_VOP_LOCKS_DECL(1);
4052 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
4053 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
4054 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
4056 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
);
4057 if (retval
!= E_NONE
) {
4058 if ((ap
->a_flags
& LK_NOWAIT
) == 0)
4059 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval
, ap
->a_flags
));
4063 if (vp
->v_type
== VDIR
)
4064 hp
->h_nodeflags
&= ~IN_BYCNID
;
4067 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
4068 DBG_VOP_LOCKS_TEST(retval
);
4073 * Unlock an hfsnode.
4077 IN struct vnode *vp;
4084 struct vop_unlock_args
/* {
4090 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4091 struct vnode
*vp
= ap
->a_vp
;
4092 int retval
= E_NONE
;
4094 DBG_FUNC_NAME("unlock");
4095 DBG_VOP_LOCKS_DECL(1);
4096 DBG_VOP_PRINT_FUNCNAME();
4097 DBG_VOP_PRINT_VNODE_INFO(vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
4098 DBG_VOP_LOCKS_INIT(0,vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4100 if (vp
->v_type
== VDIR
)
4101 hp
->h_nodeflags
&= ~IN_BYCNID
;
4103 DBG_ASSERT((ap
->a_flags
& (LK_EXCLUSIVE
|LK_SHARED
)) == 0);
4104 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
| LK_RELEASE
, &vp
->v_interlock
, ap
->a_p
);
4105 if (retval
!= E_NONE
) {
4106 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval
, H_FORKTYPE(hp
)));
4109 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
4110 DBG_VOP_LOCKS_TEST(retval
);
4116 * Print out the contents of an hfsnode.
4120 IN struct vnode *vp;
4124 struct vop_print_args
/* {
4128 register struct vnode
* vp
= ap
->a_vp
;
4129 register struct hfsnode
*hp
= VTOH( vp
);
4130 DBG_FUNC_NAME("print");
4131 DBG_VOP_LOCKS_DECL(1);
4132 DBG_VOP_PRINT_FUNCNAME();
4133 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4135 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
4137 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp
),
4138 major(H_DEV(hp
)), minor(H_DEV(hp
)));
4139 /* lockmgr_printinfo(&hp->h_lock); */
4141 DBG_VOP_LOCKS_TEST(E_NONE
);
4147 * Check for a locked hfsnode.
4148 #% islocked vp = = =
4151 IN struct vnode *vp;
4156 struct vop_islocked_args
/* {
4161 //DBG_FUNC_NAME("islocked");
4162 //DBG_VOP_LOCKS_DECL(1);
4163 //DBG_VOP_PRINT_FUNCNAME();
4164 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
4166 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
4168 lockStatus
= lockstatus(&VTOH( ap
->a_vp
)->h_lock
);
4169 //DBG_VOP_LOCKS_TEST(E_NONE);
4170 return (lockStatus
);
4175 #% pathconf vp L L L
4178 IN struct vnode *vp;
4180 OUT register_t *retval;
4185 struct vop_pathconf_args
/* {
4191 int retval
= E_NONE
;
4192 DBG_FUNC_NAME("pathconf");
4193 DBG_VOP_LOCKS_DECL(1);
4194 DBG_VOP_PRINT_FUNCNAME();
4195 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4197 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
4199 DBG_HFS_NODE_CHECK (ap
->a_vp
);
4201 switch (ap
->a_name
) {
4204 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
4205 *ap
->a_retval
= HFS_LINK_MAX
;
4213 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
4216 *ap
->a_retval
= PATH_MAX
; /* 1024 */
4218 case _PC_CHOWN_RESTRICTED
:
4224 case _PC_NAME_CHARS_MAX
:
4225 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
4227 case _PC_CASE_SENSITIVE
:
4230 case _PC_CASE_PRESERVING
:
4237 DBG_VOP_LOCKS_TEST(retval
);
4246 * Advisory record locking support
4250 IN struct vnode *vp;
4253 IN struct flock *fl;
4259 struct vop_advlock_args
/* {
4267 register struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4268 register struct flock
*fl
= ap
->a_fl
;
4269 register struct hfslockf
*lock
;
4272 DBG_FUNC_NAME("advlock");
4273 DBG_VOP_LOCKS_DECL(1);
4274 DBG_VOP_PRINT_FUNCNAME();
4275 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP(("\n"));
4276 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
4278 * Avoid the common case of unlocking when inode has no locks.
4280 if (hp
->h_lockf
== (struct hfslockf
*)0) {
4281 if (ap
->a_op
!= F_SETLK
) {
4282 fl
->l_type
= F_UNLCK
;
4287 * Convert the flock structure into a start and end.
4290 switch (fl
->l_whence
) {
4294 * Caller is responsible for adding any necessary offset
4295 * when SEEK_CUR is used.
4297 start
= fl
->l_start
;
4301 start
= HTOFCB(hp
)->fcbEOF
+ fl
->l_start
;
4313 end
= start
+ fl
->l_len
- 1;
4316 * Create the hfslockf structure
4318 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
4319 lock
->lf_start
= start
;
4321 lock
->lf_id
= ap
->a_id
;
4322 lock
->lf_hfsnode
= hp
;
4323 lock
->lf_type
= fl
->l_type
;
4324 lock
->lf_next
= (struct hfslockf
*)0;
4325 TAILQ_INIT(&lock
->lf_blkhd
);
4326 lock
->lf_flags
= ap
->a_flags
;
4328 * Do the requested operation.
4332 retval
= hfs_setlock(lock
);
4336 retval
= hfs_clearlock(lock
);
4337 FREE(lock
, M_LOCKF
);
4341 retval
= hfs_getlock(lock
, fl
);
4342 FREE(lock
, M_LOCKF
);
4347 _FREE(lock
, M_LOCKF
);
4351 DBG_VOP_LOCKS_TEST(retval
);
4358 * Update the access, modified, and node change times as specified by the
4359 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4360 * used to specify that the node needs to be updated but that the times have
4361 * already been set. The access and modified times are taken from the second
4362 * and third parameters; the node change time is always taken from the current
4363 * time. If waitfor is set, then wait for the disk write of the node to
4368 IN struct vnode *vp;
4369 IN struct timeval *access;
4370 IN struct timeval *modify;
4376 struct vop_update_args
/* {
4378 struct timeval *a_access;
4379 struct timeval *a_modify;
4385 hfsCatalogInfo catInfo
;
4391 DBG_FUNC_NAME("update");
4392 DBG_VOP_LOCKS_DECL(1);
4393 DBG_VOP_PRINT_FUNCNAME();
4394 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
4395 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4397 hp
= VTOH(ap
->a_vp
);
4399 DBG_ASSERT(hp
&& hp
->h_meta
);
4400 DBG_ASSERT(*((int*)&ap
->a_vp
->v_interlock
) == 0);
4402 if ((H_FORKTYPE(hp
) == kSysFile
) ||
4403 (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) ||
4404 (hp
->h_meta
->h_mode
== 0)) {
4405 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4406 DBG_VOP_LOCKS_TEST(0);
4410 if (H_FORKTYPE(hp
) == kSysFile
) {
4411 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4412 DBG_VOP_LOCKS_TEST(0);
4416 if (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) {
4417 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4418 DBG_VOP_LOCKS_TEST(0);
4422 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4423 if (HTOFCB(hp
)->fcbFlags
&fcbModifiedMask
)
4424 hp
->h_nodeflags
|= IN_MODIFIED
;
4426 if ((hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) == 0) {
4427 DBG_VOP_LOCKS_TEST(0);
4431 if (hp
->h_nodeflags
& IN_ACCESS
)
4432 hp
->h_meta
->h_atime
= ap
->a_access
->tv_sec
;
4433 if (hp
->h_nodeflags
& IN_UPDATE
)
4434 hp
->h_meta
->h_mtime
= ap
->a_modify
->tv_sec
;
4435 if (hp
->h_nodeflags
& IN_CHANGE
) {
4436 hp
->h_meta
->h_ctime
= time
.tv_sec
;
4438 * HFS dates that WE set must be adjusted for DST
4440 if ((HTOVCB(hp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
4441 hp
->h_meta
->h_ctime
+= 3600;
4442 hp
->h_meta
->h_mtime
= hp
->h_meta
->h_ctime
;
4447 filename
= H_NAME(hp
);
4450 catInfo
.hint
= H_HINT(hp
);
4454 * Force an update of the indirect node instead of the link
4455 * by using the name and parent of the indirect node.
4457 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
4458 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
4459 filename
= iNodeName
;
4460 pid
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
4464 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
4467 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4468 * the Catalog b-tree may aready be locked by the current thread. So we
4469 * allow recursive locking of the Catalog from within VOP_UPDATE.
4471 /* Lock the Catalog b-tree file */
4472 retval
= hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
4474 DBG_VOP_LOCKS_TEST(retval
);
4478 retval
= hfs_getcatalog(vcb
, pid
, filename
, -1, &catInfo
);
4479 if (retval
!= noErr
) {
4480 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4481 retval
= MacToVFSError(retval
);
4485 H_HINT(hp
) = catInfo
.hint
;
4486 CopyVNodeToCatalogNode (HTOV(hp
), &catInfo
.nodeData
);
4488 retval
= UpdateCatalogNode(vcb
, pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
);
4490 /* unlock the Catalog b-tree file */
4491 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4493 if (retval
!= noErr
) { /* from UpdateCatalogNode() */
4494 retval
= MacToVFSError(retval
);
4498 /* After the updates are finished, clear the flags */
4499 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4500 HTOFCB(hp
)->fcbFlags
&= ~fcbModifiedMask
;
4502 /* Update general data */
4503 if (ap
->a_vp
->v_type
== VDIR
) {
4504 hp
->h_meta
->h_nlink
= 2 + catInfo
.nodeData
.cnd_valence
;
4505 hp
->h_meta
->h_size
= sizeof(rootdots
) +
4506 (catInfo
.nodeData
.cnd_valence
* AVERAGE_HFSDIRENTRY_SIZE
);
4507 if (hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
)
4508 hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
;
4510 hp
->h_meta
->h_size
= (off_t
)vcb
->blockSize
*
4511 (off_t
)(catInfo
.nodeData
.cnd_rsrcfork
.totalBlocks
+
4512 catInfo
.nodeData
.cnd_datafork
.totalBlocks
);
4518 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
4520 DBG_VOP_LOCKS_TEST(retval
);
4526 * Initialize the vnode associated with a new hfsnode,
4527 * handle aliased vnodes.
4530 hfs_vinit(mntp
, specops
, fifoops
, vpp
)
4532 int (**specops
)(void *);
4533 int (**fifoops
)(void *);
4537 struct vnode
*vp
, *nvp
;
4541 /* vp->v_type set in CopyCatalogToHFSNode */
4542 switch(vp
->v_type
) {
4546 if ((nvp
= checkalias(vp
, hp
->h_meta
->h_rdev
, mntp
))) {
4548 * Discard unneeded vnode, but save its hfsnode.
4549 * Note that the lock is carried over in the hfsnode
4550 * to the replacement vnode.
4552 nvp
->v_data
= vp
->v_data
;
4554 vp
->v_op
= spec_vnodeop_p
;
4558 * Reinitialize aliased hfsnode.
4570 return (EOPNOTSUPP
);
4575 if (H_FILEID(hp
) == kRootDirID
)
4576 vp
->v_flag
|= VROOT
;
4583 * Allocate a new node
4585 * Upon leaving, namei buffer must be freed.
4589 hfs_makenode(mode
, rawdev
, dvp
, vpp
, cnp
, p
)
4594 struct componentname
*cnp
;
4597 register struct hfsnode
*hp
, *parhp
;
4600 struct hfsCatalogInfo catInfo
;
4604 int hasmetalock
= 0;
4605 u_int32_t tehint
= 0;
4606 DBG_FUNC_NAME("makenode");
4609 vcb
= HTOVCB(parhp
);
4612 if ((mode
& IFMT
) == 0)
4616 if ((cnp
->cn_flags
& HASBUF
) == 0)
4617 panic("hfs_makenode: no name");
4620 /* lock catalog b-tree */
4621 retval
= hfs_metafilelocking(VTOHFS(dvp
),
4622 kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
4623 if (retval
!= E_NONE
)
4629 * Force Carbon creates to have MacUnicode encoding
4631 if ((parhp
->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
4632 tehint
= kTextEncodingMacUnicode
;
4635 /* Create the Catalog B*-Tree entry */
4636 retval
= hfsCreate(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, mode
, tehint
);
4637 if (retval
!= E_NONE
) {
4638 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4642 /* Look up the catalog entry just created: */
4643 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
4644 catInfo
.hint
= kNoHint
;
4646 retval
= hfs_getcatalog(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, cnp
->cn_namelen
, &catInfo
);
4647 if (retval
!= E_NONE
) {
4648 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4652 /* unlock catalog b-tree */
4654 (void) hfs_metafilelocking(VTOHFS(dvp
),
4655 kHFSCatalogFileID
, LK_RELEASE
, p
);
4657 /* hfs plus has additional metadata to initialize */
4658 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4662 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
4663 catInfo
.nodeData
.cnd_ownerID
= VTOHFS(dvp
)->hfs_uid
;
4664 catInfo
.nodeData
.cnd_groupID
= VTOHFS(dvp
)->hfs_gid
;
4667 catInfo
.nodeData
.cnd_ownerID
= cnp
->cn_cred
->cr_uid
;
4668 catInfo
.nodeData
.cnd_groupID
= parhp
->h_meta
->h_gid
;
4672 switch (catmode
& IFMT
) {
4674 catInfo
.nodeData
.cnd_ownerID
= parhp
->h_meta
->h_uid
;
4679 /* XXX should we move this to post hfsGet? */
4680 catInfo
.nodeData
.cnd_rawDevice
= rawdev
;
4682 * Don't tag as a special file (BLK or CHR) until *after*
4683 * hfsGet is called. This insures that the checkalias call
4684 * is defered until hfs_mknod completes.
4686 catmode
= (catmode
& ~IFMT
) | IFREG
;
4690 if ((catmode
& ISGID
) && !groupmember(parhp
->h_meta
->h_gid
, cnp
->cn_cred
) &&
4691 suser(cnp
->cn_cred
, NULL
))
4694 if (cnp
->cn_flags
& ISWHITEOUT
)
4700 * The 32-bit pflags field has two bytes of significance which
4701 * are stored separately as admin and owner flags.
4703 * +------------------------------------+
4704 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4705 * +------------------------------------+
4707 catInfo
.nodeData
.cnd_adminFlags
= (pflags
>> 16) & 0x00FF;
4708 catInfo
.nodeData
.cnd_ownerFlags
= pflags
& 0x00FF;
4709 catInfo
.nodeData
.cnd_mode
= catmode
;
4712 /* Create a vnode for the object just created: */
4713 forkType
= (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) ? kDirectory
: kDataFork
;
4714 retval
= hfs_vcreate(vcb
, &catInfo
, forkType
, &tvp
);
4716 CLEAN_CATALOGDATA(&catInfo
.nodeData
); /* Should do nothing */
4718 if (retval
) goto bad1
; /* from hfs_vcreate() */
4720 /* flush out pflags, mode, gid, uid and rdev */
4722 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4724 /* reset mode and v_type in case it was BLK/CHR */
4725 hp
->h_meta
->h_mode
= mode
;
4726 tvp
->v_type
= IFTOVT(mode
);
4727 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
4728 hp
->h_nodeflags
|= IN_ACCESS
| IN_CHANGE
| IN_UPDATE
;
4729 if ((retval
= VOP_UPDATE(tvp
, &tv
, &tv
, 1)))
4733 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4734 if ((retval
= VOP_UPDATE(dvp
, &tv
, &tv
, 1)))
4737 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4738 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4741 if (UBCINFOMISSING(tvp
) || UBCINFORECLAIMED(tvp
))
4749 * Write retval occurred trying to update the node
4750 * or the directory so must deallocate the node.
4752 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4757 /* unlock catalog b-tree */
4759 (void) hfs_metafilelocking(VTOHFS(dvp
),
4760 kHFSCatalogFileID
, LK_RELEASE
, p
);
4762 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4763 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4771 #if DBG_VOP_TEST_LOCKS
4773 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4775 void DbgVopTest( int maxSlots
,
4777 VopDbgStoreRec
*VopDbgStore
,
4782 for (index
= 0; index
< maxSlots
; index
++)
4784 if (VopDbgStore
[index
].id
!= index
) {
4785 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname
, VopDbgStore
[index
].id
, index
));
4788 if ((VopDbgStore
[index
].vp
!= NULL
) &&
4789 ((VopDbgStore
[index
].vp
->v_data
==NULL
) || (VTOH(VopDbgStore
[index
].vp
)->h_valid
!= HFS_VNODE_MAGIC
)))
4792 if (VopDbgStore
[index
].vp
!= NULL
)
4793 debug_check_vnode(VopDbgStore
[index
].vp
, 0);
4795 switch (VopDbgStore
[index
].inState
)
4799 /* Do Nothing !!! */
4802 case VOPDBG_UNLOCKED
:
4803 case VOPDBG_LOCKNOTNIL
:
4805 if (VopDbgStore
[index
].vp
== NULL
&& (VopDbgStore
[index
].inState
!= VOPDBG_LOCKNOTNIL
)) {
4806 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4807 } else if (VopDbgStore
[index
].vp
!= NULL
) {
4808 switch (VopDbgStore
[index
].inState
)
4811 case VOPDBG_LOCKNOTNIL
:
4812 if (VopDbgStore
[index
].inValue
== 0)
4814 DBG_ERR (("%s: Entry: not LOCKED:", funcname
));
4815 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4819 case VOPDBG_UNLOCKED
:
4820 if (VopDbgStore
[index
].inValue
!= 0)
4822 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname
));
4823 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4832 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4838 switch (VopDbgStore
[index
].errState
)
4841 /* Do Nothing !!! */
4844 case VOPDBG_UNLOCKED
:
4847 if (VopDbgStore
[index
].vp
== NULL
) {
4848 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4850 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4851 switch (VopDbgStore
[index
].errState
)
4854 if (VopDbgStore
[index
].outValue
== 0)
4856 DBG_ERR (("%s: Error: not LOCKED:", funcname
));
4857 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4861 case VOPDBG_UNLOCKED
:
4862 if (VopDbgStore
[index
].outValue
!= 0)
4864 DBG_ERR (("%s: Error: not UNLOCKED:", funcname
));
4865 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4870 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4871 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
));
4877 case VOPDBG_LOCKNOTNIL
:
4878 if (VopDbgStore
[index
].vp
!= NULL
) {
4879 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4880 if (VopDbgStore
[index
].outValue
== 0)
4881 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4885 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4890 switch (VopDbgStore
[index
].outState
)
4893 /* Do Nothing !!! */
4896 case VOPDBG_UNLOCKED
:
4898 if (VopDbgStore
[index
].vp
== NULL
) {
4899 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname
, index
));
4901 if (VopDbgStore
[index
].vp
!= NULL
)
4903 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4904 switch (VopDbgStore
[index
].outState
)
4907 if (VopDbgStore
[index
].outValue
== 0)
4909 DBG_ERR (("%s: Out: not LOCKED:", funcname
));
4910 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4914 case VOPDBG_UNLOCKED
:
4915 if (VopDbgStore
[index
].outValue
!= 0)
4917 DBG_ERR (("%s: Out: not UNLOCKED:", funcname
));
4918 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4923 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4924 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
));
4929 case VOPDBG_LOCKNOTNIL
:
4930 if (VopDbgStore
[index
].vp
!= NULL
) {
4931 if (&VTOH(VopDbgStore
[index
].vp
)->h_lock
== NULL
) {
4932 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4935 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4936 if (VopDbgStore
[index
].outValue
== 0)
4938 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname
));
4939 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
); DBG_ERR (("\n"));
4945 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].outState
));
4949 VopDbgStore
[index
].id
= -1; /* Invalidate the entry to allow panic-free re-use */
4953 #endif /* DBG_VOP_TEST_LOCKS */
4956 * Wrapper for special device reads
4960 struct vop_read_args
/* {
4964 struct ucred *a_cred;
4971 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4972 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
4976 * Wrapper for special device writes
4980 struct vop_write_args
/* {
4984 struct ucred *a_cred;
4989 * Set update and change flags.
4991 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4992 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
4996 * Wrapper for special device close
4998 * Update the times on the hfsnode then do device close.
5002 struct vop_close_args
/* {
5005 struct ucred *a_cred;
5009 struct vnode
*vp
= ap
->a_vp
;
5010 struct hfsnode
*hp
= VTOH(vp
);
5012 simple_lock(&vp
->v_interlock
);
5013 if (ap
->a_vp
->v_usecount
> 1)
5014 HFSTIMES(hp
, &time
, &time
);
5015 simple_unlock(&vp
->v_interlock
);
5016 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
5021 * Wrapper for fifo reads
5025 struct vop_read_args
/* {
5029 struct ucred *a_cred;
5032 extern int (**fifo_vnodeop_p
)(void *);
5037 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
5038 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
5042 * Wrapper for fifo writes
5046 struct vop_write_args
/* {
5050 struct ucred *a_cred;
5053 extern int (**fifo_vnodeop_p
)(void *);
5056 * Set update and change flags.
5058 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
5059 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
5063 * Wrapper for fifo close
5065 * Update the times on the hfsnode then do device close.
5069 struct vop_close_args
/* {
5072 struct ucred *a_cred;
5076 extern int (**fifo_vnodeop_p
)(void *);
5077 struct vnode
*vp
= ap
->a_vp
;
5078 struct hfsnode
*hp
= VTOH(vp
);
5080 simple_lock(&vp
->v_interlock
);
5081 if (ap
->a_vp
->v_usecount
> 1)
5082 HFSTIMES(hp
, &time
, &time
);
5083 simple_unlock(&vp
->v_interlock
);
5084 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
5089 /*****************************************************************************
5093 *****************************************************************************/
5095 #define VOPFUNC int (*)(void *)
5097 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
5098 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5099 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
5100 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
5101 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
5102 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
5103 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
5104 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5105 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5106 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5107 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
5108 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
5109 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
5110 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
5111 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
5112 { &vop_mmap_desc
, (VOPFUNC
)hfs_mmap
}, /* mmap */
5113 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5114 { &vop_seek_desc
, (VOPFUNC
)hfs_seek
}, /* seek */
5115 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
5117 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
5119 { &vop_link_desc
, (VOPFUNC
)err_link
}, /* link (NOT SUPPORTED) */
5121 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
5122 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
5123 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
5124 { &vop_mkcomplex_desc
, (VOPFUNC
)hfs_mkcomplex
}, /* mkcomplex */
5125 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
5126 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
5127 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
5128 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
5129 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
5130 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
5131 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
5132 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5133 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5134 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5135 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5136 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
5137 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
5138 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5139 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5140 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
5141 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
5142 { &vop_reallocblks_desc
, (VOPFUNC
)hfs_reallocblks
}, /* reallocblks */
5143 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
5144 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
5145 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5146 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
5147 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
5148 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
5149 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
5150 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5151 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5152 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5153 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5154 { NULL
, (VOPFUNC
)NULL
}
5157 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
5158 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
5160 int (**hfs_specop_p
)(void *);
5161 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
5162 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5163 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
5164 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
5165 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
5166 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
5167 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
5168 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5169 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5170 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5171 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
5172 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
5173 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
5174 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
5175 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
5176 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
5177 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
5178 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5179 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
5180 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
5181 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
5182 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
5183 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
5184 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
5185 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
5186 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
5187 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
5188 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
5189 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5190 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5191 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5192 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5193 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
5194 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
5195 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5196 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5197 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
5198 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
5199 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
5200 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
5201 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
5202 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5203 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
5204 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5205 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5206 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
5207 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5208 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5209 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5210 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5211 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5212 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5214 struct vnodeopv_desc hfs_specop_opv_desc
=
5215 { &hfs_specop_p
, hfs_specop_entries
};
5218 int (**hfs_fifoop_p
)(void *);
5219 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
5220 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5221 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
5222 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
5223 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
5224 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
5225 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
5226 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5227 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5228 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5229 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
5230 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
5231 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
5232 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
5233 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
5234 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
5235 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
5236 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5237 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
5238 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
5239 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
5240 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
5241 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
5242 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
5243 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
5244 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
5245 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
5246 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
5247 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5248 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5249 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5250 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5251 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
5252 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
5253 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5254 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5255 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
5256 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
5257 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
5258 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
5259 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
5260 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5261 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
5262 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5263 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5264 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5265 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5266 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5267 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5268 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5269 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5270 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5272 struct vnodeopv_desc hfs_fifoop_opv_desc
=
5273 { &hfs_fifoop_p
, hfs_fifoop_entries
};