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) ||
1075 ((alist
->commonattr
& (ATTR_CMN_OBJID
| ATTR_CMN_OBJPERMANENTID
))
1076 && (hp
->h_meta
->h_metaflags
& IN_DATANODE
))) {
1078 /* lock catalog b-tree */
1079 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, ap
->a_p
);
1080 if (error
) goto GetCatalogErr_Exit
;
1082 if (alist
->volattr
!= 0) {
1083 /* Look up the root info, regardless of the vnode provided */
1084 error
= hfs_getcatalog(VTOVCB(vp
), 2, NULL
, -1, &catInfo
);
1086 error
= hfs_getcatalog(VTOVCB(vp
), H_DIRID(hp
), H_NAME(hp
), -1, &catInfo
);
1087 if (error
== 0) H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1090 /* unlock catalog b-tree */
1091 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1094 * If a data fork has an active sibling and we need
1095 * rsrc fork attributes then we need to lock the
1096 * sibling and make a copy of its attributes.
1098 if ((hp
->h_meta
->h_usecount
> 1) &&
1099 (H_FORKTYPE(hp
) == kDataFork
) &&
1100 (alist
->fileattr
& HFS_ATTR_FILE_LOOKUPMASK
)) {
1101 struct vnode
*sib_vp
= NULL
;
1102 struct hfsnode
*nhp
;
1103 struct proc
*p
= current_proc();
1105 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
1106 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
1107 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
1109 /* Loop through all siblings, skipping ourselves */
1110 simple_lock(&hp
->h_meta
->h_siblinglock
);
1111 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1112 if (nhp
== hp
) /* skip ourselves */
1116 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1118 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1119 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1120 if (VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1121 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1122 CopyVNodeToCatalogNode (sib_vp
, &catInfo
.nodeData
);
1127 }; /* h_use_count > 1 */
1129 /* Update to the in-memory state, if it has been modified...just to make sure */
1130 if (VTOH(vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
1131 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1132 CopyVNodeToCatalogNode (vp
, &catInfo
.nodeData
);
1135 /* XXX What if hfs_getcatalog fails...we just continue??? */
1136 catInfoPtr
= &catInfo
;
1140 fixedblocksize
= AttributeBlockSize(alist
);
1141 attrblocksize
= fixedblocksize
+ (sizeof(u_long
)); /* u_long for length longword */
1142 if (alist
->commonattr
& ATTR_CMN_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1143 if (alist
->commonattr
& ATTR_CMN_NAMEDATTRLIST
) attrblocksize
+= 0; /* XXX PPD */
1144 if (alist
->volattr
& ATTR_VOL_MOUNTPOINT
) attrblocksize
+= PATH_MAX
;
1145 if (alist
->volattr
& ATTR_VOL_NAME
) attrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
1146 if (alist
->fileattr
& ATTR_FILE_FORKLIST
) attrblocksize
+= 0; /* XXX PPD */
1148 attrbufsize
= MIN(ap
->a_uio
->uio_resid
, attrblocksize
);
1149 DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
1152 attrblocksize
- fixedblocksize
));
1153 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1154 attrptr
= attrbufptr
;
1155 *((u_long
*)attrptr
) = 0; /* Set buffer length in case of errors */
1156 ++((u_long
*)attrptr
); /* Reserve space for length field */
1157 varptr
= ((char *)attrptr
) + fixedblocksize
; /* Point to variable-length storage */
1158 DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int
)attrptr
, (u_int
)varptr
));
1160 PackAttributeBlock(alist
, vp
, catInfoPtr
, &attrptr
, &varptr
);
1161 attrbufsize
= MIN(attrbufsize
, (u_int
)varptr
- (u_int
)attrbufptr
); /* Don't copy out more data than was generated */
1162 *((u_long
*)attrbufptr
) = attrbufsize
; /* Set actual buffer length for return to caller */
1163 DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize
, (u_int
)ap
->a_uio
->uio_iov
->iov_base
));
1164 error
= uiomove((caddr_t
)attrbufptr
, attrbufsize
, ap
->a_uio
);
1165 if (error
!= E_NONE
) {
1166 DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error
));
1169 FREE(attrbufptr
, M_TEMP
);
1173 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1174 DBG_VOP_LOCKS_TEST(error
);
1183 #% setattrlist vp L L L
1186 IN struct vnode *vp;
1187 IN struct attrlist *alist;
1188 INOUT struct uio *uio;
1189 IN struct ucred *cred;
1197 struct vop_setattrlist_args
/* {
1199 struct attrlist *a_alist
1201 struct ucred *a_cred;
1205 struct vnode
*vp
= ap
->a_vp
;
1206 struct hfsnode
*hp
= VTOH(vp
);
1207 struct attrlist
*alist
= ap
->a_alist
;
1208 struct ucred
*cred
= ap
->a_cred
;
1209 struct proc
*p
= ap
->a_p
;
1211 struct hfsCatalogInfo catInfo
;
1213 void *attrbufptr
= NULL
;
1215 void *varptr
= NULL
;
1225 DBG_FUNC_NAME("setattrlist");
1226 DBG_VOP_LOCKS_DECL(1);
1228 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_SAME
, VOPDBG_POS
);
1229 DBG_HFS_NODE_CHECK(ap
->a_vp
);
1230 DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname
, (u_int
)alist
->commonattr
,(u_int
)ap
->a_uio
->uio_resid
));
1232 DBG_ASSERT(ap
->a_uio
->uio_rw
== UIO_WRITE
);
1234 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
1235 ((alist
->commonattr
& ~ATTR_CMN_SETMASK
) != 0) ||
1236 ((alist
->volattr
& ~ATTR_VOL_SETMASK
) != 0) ||
1237 ((alist
->dirattr
& ~ATTR_DIR_SETMASK
) != 0) ||
1238 ((alist
->fileattr
& ~ATTR_FILE_SETMASK
) != 0) ||
1239 ((alist
->forkattr
& ~ATTR_FORK_SETMASK
) != 0)) {
1240 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1241 DBG_VOP_LOCKS_TEST(EINVAL
);
1245 if ((alist
->volattr
!= 0) && /* Setting volume info */
1246 (((alist
->volattr
& ATTR_VOL_INFO
) == 0) || /* Not explicitly indicating this or ... */
1247 (alist
->commonattr
& ~ATTR_CMN_VOLSETMASK
))) /* ... setting invalid attributes for volume */
1249 DBG_ERR(("%s: Bad attrlist\n", funcname
));
1250 DBG_VOP_LOCKS_TEST(EINVAL
);
1254 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
) {
1255 DBG_VOP_LOCKS_TEST(EROFS
);
1260 Ownership of the file (in addition to write access, checked below,
1261 is required in one of two classes of calls:
1263 (a) When setting any ownership-requiring attribute other than ATTR_CMN_FLAGS, or
1264 (b) When setting ATTR_CMN_FLAGS on a volume that's not plain HFS (for which no
1265 real per-object ownership information is stored):
1267 if ((alist
->commonattr
& (OWNERSHIP_ONLY_ATTRS
& ~ATTR_CMN_FLAGS
)) ||
1268 ((alist
->commonattr
& ATTR_CMN_FLAGS
) && (VTOVCB(vp
)->vcbSigWord
!= kHFSSigWord
))) {
1269 /* NOTE: The following isn't ENTIRELY complete: even if you're the superuser
1270 you cannot change the flags as long as SF_IMMUTABLE or SF_APPEND is
1271 set and securelevel > 0. This is verified in hfs_chflags which gets
1272 invoked to do the actual flags field change so this check is sufficient
1275 /* Check to see if the user owns the object [or is superuser]: */
1276 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1277 DBG_VOP_LOCKS_TEST(retval
);
1281 DBG_ASSERT(((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == 0) ||
1282 (((alist
->commonattr
& OWNERSHIP_ONLY_ATTRS
) == ATTR_CMN_FLAGS
) &&
1283 (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
)));
1284 /* No ownership access is required: mere write access (checked below) will do... */
1287 /* For any other attributes, check to see if the user has write access to
1288 the object in question [unlike VOP_ACCESS, ignore IMMUTABLE here]: */
1290 if ((((alist
->commonattr
& ~(OWNERSHIP_ONLY_ATTRS
)) != 0) ||
1291 (alist
->volattr
!= 0) ||
1292 (alist
->dirattr
!= 0) ||
1293 (alist
->fileattr
!= 0) ||
1294 (alist
->forkattr
!= 0)) &&
1295 ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0)) {
1296 DBG_VOP_LOCKS_TEST(retval
);
1298 }; /* end of if ownership attr */
1300 /* Allocate the buffer now to minimize the time we might be blocked holding the catalog lock */
1301 attrblocksize
= ap
->a_uio
->uio_resid
;
1302 if (attrblocksize
< AttributeBlockSize(alist
)) {
1303 DBG_ERR(("%s: bad attrblocksize\n", funcname
));
1304 DBG_VOP_LOCKS_TEST(EINVAL
);
1308 MALLOC(attrbufptr
, void *, attrblocksize
, M_TEMP
, M_WAITOK
);
1310 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
1311 catInfo
.hint
= kNoHint
;
1313 filename
= H_NAME(hp
);
1318 * Force an update of the indirect node instead of the link
1319 * by using the name and parent of the indirect node.
1321 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
1322 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
1323 filename
= iNodeName
;
1324 pid
= VTOHFS(vp
)->hfs_private_metadata_dir
;
1328 /* lock catalog b-tree */
1329 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1330 if (error
!= E_NONE
)
1333 error
= hfs_getcatalog(VTOVCB(vp
), pid
, filename
, -1, &catInfo
);
1335 /* unlock catalog b-tree */
1336 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1337 if (error
!= E_NONE
)
1340 H_HINT(hp
) = catInfo
.hint
; /* Remember the last valid hint */
1342 error
= uiomove((caddr_t
)attrbufptr
, attrblocksize
, ap
->a_uio
);
1343 if (error
) goto ErrorExit
;
1345 if ((alist
->volattr
) && (H_FILEID(hp
) != kRootDirID
)) {
1351 * If we are going to change the times:
1352 * 1. do we have permission to change the dates?
1353 * 2. Is there another fork? If so then clear any flags associated with the times
1355 if (alist
->commonattr
& (ATTR_CMN_MODTIME
| ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1356 if (alist
->commonattr
& (ATTR_CMN_CHGTIME
| ATTR_CMN_ACCTIME
)) {
1357 if ((error
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1361 /* If there is another fork, clear the flags */
1362 if ((hp
->h_meta
->h_usecount
> 1) && (H_FORKTYPE(hp
) == kDataFork
)) {
1363 struct vnode
*sib_vp
= NULL
;
1364 struct hfsnode
*nhp
;
1366 /* Loop through all siblings, skipping ourselves */
1367 simple_lock(&hp
->h_meta
->h_siblinglock
);
1368 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
1369 if (nhp
== hp
) /* skip ourselves */
1373 simple_unlock(&hp
->h_meta
->h_siblinglock
);
1376 * The only error that vget returns is when the vnode is going away,
1377 * so ignore the vnode
1379 if (sib_vp
&& vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1380 if ((sib_vp
->v_tag
== VT_HFS
)
1381 && VTOH(sib_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_UPDATE
)) {
1382 if (alist
->commonattr
& ATTR_CMN_MODTIME
)
1383 VTOH(sib_vp
)->h_nodeflags
&= ~IN_UPDATE
;
1384 if (alist
->commonattr
& ATTR_CMN_CHGTIME
)
1385 VTOH(sib_vp
)->h_nodeflags
&= ~IN_CHANGE
;
1386 if (alist
->commonattr
& ATTR_CMN_ACCTIME
)
1387 VTOH(sib_vp
)->h_nodeflags
&= ~IN_ACCESS
;
1394 /* save these in case hfs_chown() or hfs_chmod() fail */
1395 saved_uid
= hp
->h_meta
->h_uid
;
1396 saved_gid
= hp
->h_meta
->h_gid
;
1397 saved_mode
= hp
->h_meta
->h_mode
;
1398 saved_flags
= hp
->h_meta
->h_pflags
;
1400 attrptr
= attrbufptr
;
1401 UnpackAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
1403 /* if unpacking changed the owner or group then call hfs_chown() */
1404 if (saved_uid
!= hp
->h_meta
->h_uid
|| saved_gid
!= hp
->h_meta
->h_gid
) {
1408 uid
= hp
->h_meta
->h_uid
;
1409 hp
->h_meta
->h_uid
= saved_uid
;
1410 gid
= hp
->h_meta
->h_gid
;
1411 hp
->h_meta
->h_gid
= saved_gid
;
1412 if ((error
= hfs_chown(vp
, uid
, gid
, cred
, p
)))
1416 /* if unpacking changed the mode then call hfs_chmod() */
1417 if (saved_mode
!= hp
->h_meta
->h_mode
) {
1420 mode
= hp
->h_meta
->h_mode
;
1421 hp
->h_meta
->h_mode
= saved_mode
;
1422 if ((error
= hfs_chmod(vp
, mode
, cred
, p
)))
1426 /* if unpacking changed the flags then call hfs_chflags */
1427 if (saved_flags
!= hp
->h_meta
->h_pflags
) {
1430 flags
= hp
->h_meta
->h_pflags
;
1431 hp
->h_meta
->h_pflags
= saved_flags
;
1432 if ((error
= hfs_chflags(vp
, flags
, cred
, p
)))
1437 /* lock catalog b-tree */
1438 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
1439 if (error
!= E_NONE
)
1442 /* Update Catalog Tree */
1443 if (alist
->volattr
== 0) {
1444 error
= MacToVFSError( UpdateCatalogNode(HTOVCB(hp
), pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
));
1448 if (alist
->volattr
& ATTR_VOL_NAME
) {
1449 ExtendedVCB
*vcb
= VTOVCB(vp
);
1450 int namelen
= strlen(vcb
->vcbVN
);
1452 if (vcb
->vcbVN
[0] == 0) {
1454 * Ignore attempts to rename a volume to a zero-length name:
1455 * restore the original name from the metadata.
1457 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
);
1462 * Force Carbon renames to have MacUnicode encoding
1464 if ((hp
->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
1465 tehint
= kTextEncodingMacUnicode
;
1468 error
= MoveRenameCatalogNode(vcb
, kRootParID
, H_NAME(hp
), H_HINT(hp
),
1469 kRootParID
, vcb
->vcbVN
, &H_HINT(hp
), tehint
);
1472 copystr(H_NAME(hp
), vcb
->vcbVN
, sizeof(vcb
->vcbVN
), NULL
); /* Restore the old name in the VCB */
1473 vcb
->vcbFlags
|= 0xFF00; // Mark the VCB dirty
1478 hfs_set_metaname(vcb
->vcbVN
, hp
->h_meta
, HTOHFS(hp
));
1479 hp
->h_nodeflags
|= IN_CHANGE
;
1481 } /* vcb->vcbVN[0] == 0 ... else ... */
1482 } /* alist->volattr & ATTR_VOL_NAME */
1485 /* unlock catalog b-tree */
1486 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
1490 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
1492 if (attrbufptr
) FREE(attrbufptr
, M_TEMP
);
1494 DBG_VOP_LOCKS_TEST(error
);
1499 * Change the mode on a file.
1500 * Inode must be locked before calling.
1503 hfs_chmod(vp
, mode
, cred
, p
)
1504 register struct vnode
*vp
;
1506 register struct ucred
*cred
;
1509 register struct hfsnode
*hp
= VTOH(vp
);
1512 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1515 #if OVERRIDE_UNKNOWN_PERMISSIONS
1516 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1521 if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0)
1524 if (vp
->v_type
!= VDIR
&& (mode
& S_ISTXT
))
1526 if (!groupmember(hp
->h_meta
->h_gid
, cred
) && (mode
& ISGID
))
1529 hp
->h_meta
->h_mode
&= ~ALLPERMS
;
1530 hp
->h_meta
->h_mode
|= (mode
& ALLPERMS
);
1531 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1532 hp
->h_nodeflags
|= IN_CHANGE
;
1538 hfs_write_access(struct vnode
*vp
, struct ucred
*cred
, struct proc
*p
, Boolean considerFlags
)
1540 struct hfsnode
*hp
= VTOH(vp
);
1541 ExtendedVCB
*vcb
= HTOVCB(hp
);
1544 int retval
= E_NONE
;
1547 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
1550 * Disallow write attempts on read-only file systems;
1551 * unless the file is a socket, fifo, or a block or
1552 * character device resident on the file system.
1554 switch (vp
->v_type
) {
1558 if (VTOVFS(vp
)->mnt_flag
& MNT_RDONLY
)
1565 /* If immutable bit set, nobody gets to write it. */
1566 if (considerFlags
&& (hp
->h_meta
->h_pflags
& IMMUTABLE
))
1569 /* Otherwise, user id 0 always gets access. */
1570 if (cred
->cr_uid
== 0) {
1575 /* Otherwise, check the owner. */
1576 if ((retval
= hfs_owner_rights(vp
, cred
, p
, false)) == 0) {
1577 retval
= ((hp
->h_meta
->h_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
1581 /* Otherwise, check the groups. */
1582 for (i
= 0, gp
= cred
->cr_groups
; i
< cred
->cr_ngroups
; i
++, gp
++)
1583 if (hp
->h_meta
->h_gid
== *gp
) {
1584 retval
= ((hp
->h_meta
->h_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
1588 /* Otherwise, check everyone else. */
1589 retval
= ((hp
->h_meta
->h_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
1598 * Change the flags on a file or directory.
1599 * Inode must be locked before calling.
1602 hfs_chflags(vp
, flags
, cred
, p
)
1603 register struct vnode
*vp
;
1604 register u_long flags
;
1605 register struct ucred
*cred
;
1608 register struct hfsnode
*hp
= VTOH(vp
);
1611 if (VTOVCB(vp
)->vcbSigWord
== kHFSSigWord
) {
1612 if ((retval
= hfs_write_access(vp
, cred
, p
, false)) != 0) {
1615 } else if ((retval
= hfs_owner_rights(vp
, cred
, p
, true)) != 0) {
1619 if (cred
->cr_uid
== 0) {
1620 if ((hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
)) &&
1624 hp
->h_meta
->h_pflags
= flags
;
1626 if (hp
->h_meta
->h_pflags
& (SF_IMMUTABLE
| SF_APPEND
) ||
1627 (flags
& UF_SETTABLE
) != flags
) {
1630 hp
->h_meta
->h_pflags
&= SF_SETTABLE
;
1631 hp
->h_meta
->h_pflags
|= (flags
& UF_SETTABLE
);
1633 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1634 hp
->h_nodeflags
|= IN_CHANGE
;
1641 * Perform chown operation on hfsnode hp;
1642 * hfsnode must be locked prior to call.
1645 hfs_chown(vp
, uid
, gid
, cred
, p
)
1646 register struct vnode
*vp
;
1652 register struct hfsnode
*hp
= VTOH(vp
);
1657 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
1660 if (VTOVFS(vp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
1664 if (uid
== (uid_t
)VNOVAL
)
1665 uid
= hp
->h_meta
->h_uid
;
1666 if (gid
== (gid_t
)VNOVAL
)
1667 gid
= hp
->h_meta
->h_gid
;
1669 * If we don't own the file, are trying to change the owner
1670 * of the file, or are not a member of the target group,
1671 * the caller must be superuser or the call fails.
1673 if ((cred
->cr_uid
!= hp
->h_meta
->h_uid
|| uid
!= hp
->h_meta
->h_uid
||
1674 (gid
!= hp
->h_meta
->h_gid
&& !groupmember((gid_t
)gid
, cred
))) &&
1675 (retval
= suser(cred
, &p
->p_acflag
)))
1678 ogid
= hp
->h_meta
->h_gid
;
1679 ouid
= hp
->h_meta
->h_uid
;
1681 hp
->h_meta
->h_gid
= gid
;
1682 hp
->h_meta
->h_uid
= uid
;
1684 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
1685 if (ouid
!= uid
|| ogid
!= gid
)
1686 hp
->h_nodeflags
|= IN_CHANGE
;
1687 if (ouid
!= uid
&& cred
->cr_uid
!= 0)
1688 hp
->h_meta
->h_mode
&= ~ISUID
;
1689 if (ogid
!= gid
&& cred
->cr_uid
!= 0)
1690 hp
->h_meta
->h_mode
&= ~ISGID
;
1698 #% exchange fvp L L L
1699 #% exchange tvp L L L
1702 IN struct vnode *fvp;
1703 IN struct vnode *tvp;
1704 IN struct ucred *cred;
1710 * exchange is a very tricky routine, because we might have to unlock the
1711 * passed in vnode, and then retry locking it and all its siblings, and then
1712 * unlocking them in reverse.
1713 * Also the sibling list lock must be kept during the whole operation to
1714 * make sure nothing changes underneath us.
1715 * Also it depends on behavior of the sibling list and hash, so
1716 * careful if you change anything.
1721 struct vop_exchange_args
/* {
1722 struct vnode *a_fvp;
1723 struct vnode *a_tvp;
1724 struct ucred *a_cred;
1728 struct hfsnode
*from_hp
, *to_hp
, *nhp
;
1729 struct hfsnode
*fromFirst
, *fromSecond
, *toFirst
, *toSecond
;
1730 struct vnode
*from_vp
, *to_vp
;
1731 struct hfsmount
*hfsmp
;
1732 u_char tmp_name
[kHFSPlusMaxFileNameBytes
+1]; /* 766 bytes! */
1734 u_int32_t fromFileID
, toFileID
;
1735 u_int32_t fromParID
;
1737 int retval
= E_NONE
;
1738 DBG_FUNC_NAME("exchange");
1739 DBG_VOP_LOCKS_DECL(2);
1740 DBG_VOP_LOCKS_INIT(0,ap
->a_fvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1741 DBG_VOP_LOCKS_INIT(1,ap
->a_tvp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
1743 /* Set up variables and checks */
1744 from_vp
= ap
->a_fvp
;
1746 from_hp
= VTOH(from_vp
);
1747 to_hp
= VTOH(to_vp
);
1748 hfsmp
= VTOHFS(from_vp
);
1749 vcb
= HTOVCB(from_hp
);
1750 toFileID
= H_FILEID(to_hp
);
1751 fromFileID
= H_FILEID(from_hp
);
1752 fromParID
= H_DIRID(from_hp
);
1754 if (from_vp
->v_mount
!= to_vp
->v_mount
) {
1755 DBG_VOP_LOCKS_TEST(EXDEV
);
1759 /* Can only exchange file objects */
1760 if (from_vp
->v_type
!= VREG
|| to_vp
->v_type
!= VREG
) {
1761 DBG_VOP_LOCKS_TEST(EINVAL
);
1766 * Lock the siblink list
1767 * Check for multiple forks
1768 * If there are, we would need to:
1769 * 1. Unlock ourselves
1770 * 3. Traverse the list in a forward order...locking all vnodes
1771 * 4. Flush all buffers
1772 * 5. Perform the exchange
1773 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1774 * Notice that the sibling lock is kept during the whole operation. This quarentees
1775 * that no new forks are taken off or put on
1777 DBG_ASSERT(H_FORKTYPE(from_hp
)==kDataFork
&& H_FORKTYPE(to_hp
)==kDataFork
);
1778 fromFirst
= fromSecond
= toFirst
= toSecond
= NULL
;
1780 if (from_hp
->h_meta
->h_usecount
> 1) {
1782 * This has siblings, so remember the passed-in vnode,
1783 * unlock it if it is not the 'first' sibling,
1784 * and then lock the rest of the vnodes by sibling order.
1785 * Notice that the passed-in vnode is not vrele(), this
1786 * keeps the usecount>0, so it wont go away.
1788 simple_lock(&from_hp
->h_meta
->h_siblinglock
);
1789 fromFirst
= from_hp
->h_meta
->h_siblinghead
.cqh_first
;
1790 fromSecond
= fromFirst
->h_sibling
.cqe_next
;
1791 simple_unlock(&from_hp
->h_meta
->h_siblinglock
);
1793 if (fromFirst
== from_hp
) {
1794 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1795 fromSecond
= NULL
; /* its going away */
1797 VOP_UNLOCK(HTOV(from_hp
), 0, ap
->a_p
);
1798 if (vget(HTOV(fromFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1799 fromFirst
= NULL
; /* its going away */
1800 if (vget(HTOV(fromSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1801 fromSecond
= NULL
; /* its going away */
1805 fromFirst
= from_hp
;
1808 if (to_hp
->h_meta
->h_usecount
> 1) {
1810 simple_lock(&to_hp
->h_meta
->h_siblinglock
);
1811 toFirst
= to_hp
->h_meta
->h_siblinghead
.cqh_first
;
1812 toSecond
= toFirst
->h_sibling
.cqe_next
;
1813 simple_unlock(&to_hp
->h_meta
->h_siblinglock
);
1815 if (toFirst
== to_hp
) {
1816 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1817 toSecond
= NULL
; /* its going away */
1819 VOP_UNLOCK(HTOV(to_hp
), 0, ap
->a_p
);
1820 if (vget(HTOV(toFirst
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1821 toFirst
= NULL
; /* its going away */
1822 if (vget(HTOV(toSecond
), LK_EXCLUSIVE
| LK_RETRY
, ap
->a_p
))
1823 toSecond
= NULL
; /* its going away */
1831 /* Ignore any errors, we are doing a 'best effort' on flushing */
1833 (void) vinvalbuf(HTOV(fromFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1835 (void) vinvalbuf(HTOV(fromSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1837 (void) vinvalbuf(HTOV(toFirst
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1839 (void) vinvalbuf(HTOV(toSecond
), V_SAVE
, ap
->a_cred
, ap
->a_p
, 0, 0);
1842 /* lock catalog b-tree */
1843 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1844 if (retval
) goto Err_Exit
;
1846 /* lock extents b-tree iff there are overflow extents */
1847 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1848 so we neeed the tree to be always locked.
1850 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, ap
->a_p
);
1851 if (retval
) goto Err_Exit_Relse
;
1853 /* Do the exchange */
1854 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
) ));
1856 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, ap
->a_p
);
1858 if (retval
!= E_NONE
) {
1859 DBG_ERR(("/tError trying to exchange: %d\n", retval
));
1860 goto Err_Exit_Relse
;
1864 /* Purge the vnodes from the name */
1866 cache_purge(HTOV(fromFirst
));
1868 cache_purge(HTOV(fromSecond
));
1870 cache_purge(HTOV(toFirst
));
1872 cache_purge(HTOV(toSecond
));
1874 /* Now exchange fileID, parID, name for the vnode itself */
1875 copystr(H_NAME(from_hp
), (char*) tmp_name
, strlen(H_NAME(from_hp
))+1, NULL
);
1876 hfs_chid(from_hp
, toFileID
, H_DIRID(to_hp
), H_NAME(to_hp
));
1877 hfs_chid(to_hp
, fromFileID
, fromParID
, (char*) tmp_name
);
1880 tmpLong
= HTOFCB(from_hp
)->fcbFlags
;
1881 HTOFCB(from_hp
)->fcbFlags
= HTOFCB(to_hp
)->fcbFlags
;
1882 HTOFCB(to_hp
)->fcbFlags
= tmpLong
;
1884 tmpLong
= from_hp
->h_meta
->h_crtime
;
1885 from_hp
->h_meta
->h_crtime
= to_hp
->h_meta
->h_crtime
;
1886 to_hp
->h_meta
->h_crtime
= tmpLong
;
1888 tmpLong
= from_hp
->h_meta
->h_butime
;
1889 from_hp
->h_meta
->h_butime
= to_hp
->h_meta
->h_butime
;
1890 to_hp
->h_meta
->h_butime
= tmpLong
;
1892 tmpLong
= from_hp
->h_meta
->h_atime
;
1893 from_hp
->h_meta
->h_atime
= to_hp
->h_meta
->h_atime
;
1894 to_hp
->h_meta
->h_atime
= tmpLong
;
1896 tmpLong
= from_hp
->h_meta
->h_ctime
;
1897 from_hp
->h_meta
->h_ctime
= to_hp
->h_meta
->h_ctime
;
1898 to_hp
->h_meta
->h_ctime
= tmpLong
;
1900 tmpLong
= from_hp
->h_meta
->h_gid
;
1901 from_hp
->h_meta
->h_gid
= to_hp
->h_meta
->h_gid
;
1902 to_hp
->h_meta
->h_gid
= tmpLong
;
1904 tmpLong
= from_hp
->h_meta
->h_uid
;
1905 from_hp
->h_meta
->h_uid
= to_hp
->h_meta
->h_uid
;
1906 to_hp
->h_meta
->h_uid
= tmpLong
;
1908 tmpLong
= from_hp
->h_meta
->h_pflags
;
1909 from_hp
->h_meta
->h_pflags
= to_hp
->h_meta
->h_pflags
;
1910 to_hp
->h_meta
->h_pflags
= tmpLong
;
1912 tmpLong
= from_hp
->h_meta
->h_mode
;
1913 from_hp
->h_meta
->h_mode
= to_hp
->h_meta
->h_mode
;
1914 to_hp
->h_meta
->h_mode
= tmpLong
;
1916 tmpLong
= from_hp
->h_meta
->h_rdev
;
1917 from_hp
->h_meta
->h_rdev
= to_hp
->h_meta
->h_rdev
;
1918 to_hp
->h_meta
->h_rdev
= tmpLong
;
1920 tmpLong
= from_hp
->h_meta
->h_size
;
1921 from_hp
->h_meta
->h_size
= to_hp
->h_meta
->h_size
;
1922 to_hp
->h_meta
->h_size
= tmpLong
;
1928 /* unlock catalog b-tree */
1929 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, ap
->a_p
);
1936 * At this point, the vnodes' data is switched, but are on the old hash list.
1937 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1940 * A fork is trying to be added while exchanging...It got the hash lock,
1941 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1942 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1943 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1944 * Not very pretty, becarefull that this works ok
1946 * Same as the above, but before the move is made (like at this very spot), the new vnode
1947 * is added and a vget is requested for that new vnode, it would have old data
1948 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1950 * Hey! Same as above, but it is added after all the moving
1951 * So now there is a vnode with the old data, on the old hash...it will become
1952 * lost next time that a vget() is done
1954 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1955 * opposite that we are doing now
1957 hfs_vhashmove(from_hp
, fromFileID
);
1958 hfs_vhashmove(to_hp
, toFileID
);
1963 debug_check_vnode(HTOV(fromFirst
), 0);
1965 debug_check_vnode(HTOV(fromSecond
), 0);
1967 debug_check_vnode(HTOV(toFirst
), 0);
1969 debug_check_vnode(HTOV(toSecond
), 0);
1973 /* Unlock any forks, and the sibling list */
1974 if (to_hp
->h_meta
->h_usecount
> 1) {
1975 if (to_hp
== toFirst
) {
1977 vput(HTOV(toSecond
));
1980 vrele(HTOV(toSecond
)); /* decrement, return it locked */
1982 vput(HTOV(toFirst
));
1985 if (from_hp
->h_meta
->h_usecount
> 1) {
1986 if (from_hp
== fromFirst
) {
1988 vput(HTOV(fromSecond
));
1991 vrele(HTOV(fromSecond
)); /* decrement, return it locked */
1993 vput(HTOV(fromFirst
));
1997 DBG_VOP_LOCKS_TEST(retval
);
2003 * Change a vnode's file id, parent id and name
2005 * Assumes the vnode is locked and is of type VREG
2008 hfs_chid(struct hfsnode
*hp
, u_int32_t fid
, u_int32_t pid
, char* name
)
2010 DBG_ASSERT(HTOV(hp
)->v_type
== VREG
);
2013 H_FILEID(hp
) = fid
; /* change h_nodeID */
2016 hfs_set_metaname(name
, hp
->h_meta
, HTOHFS(hp
));
2027 IN struct vnode *vp;
2028 IN struct ucred *cred;
2037 struct vop_fsync_args
/* {
2039 struct ucred *a_cred;
2044 struct vnode
*vp
= ap
->a_vp
;
2045 struct hfsnode
*hp
= VTOH(vp
);
2047 register struct buf
*bp
;
2052 DBG_FUNC_NAME("fsync");
2053 DBG_VOP_LOCKS_DECL(1);
2054 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
2055 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2056 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
2057 DBG_HFS_NODE_CHECK(ap
->a_vp
);
2060 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2065 * First of all, write out any clusters.
2070 * Flush all dirty buffers associated with a vnode.
2074 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2075 nbp
= bp
->b_vnbufs
.le_next
;
2076 if ((bp
->b_flags
& B_BUSY
))
2078 if ((bp
->b_flags
& B_DELWRI
) == 0)
2079 panic("hfs_fsync: not dirty");
2081 bp
->b_flags
|= B_BUSY
;
2082 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2085 * Wait for I/O associated with indirect blocks to complete,
2086 * since there is no way to quickly wait for them below.
2088 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp
->b_blkno
, bp
->b_lblkno
));
2089 if (bp
->b_vp
== vp
|| ap
->a_waitfor
== MNT_NOWAIT
) {
2092 (void) VOP_BWRITE(bp
);
2096 if (vp
->v_flag
& VHASDIRTY
)
2099 if (ap
->a_waitfor
== MNT_WAIT
) {
2100 while (vp
->v_numoutput
) {
2101 vp
->v_flag
|= VBWAIT
;
2102 tsleep((caddr_t
)&vp
->v_numoutput
, PRIBIO
+ 1, "hfs_fsync", 0);
2105 /* I have seen this happen for swapfile. So it is safer to
2106 * check for dirty buffers again. --Umesh
2108 if (vp
->v_dirtyblkhd
.lh_first
|| (vp
->v_flag
& VHASDIRTY
)) {
2109 vprint("hfs_fsync: dirty", vp
);
2117 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2121 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2122 BTSetLastSync(HTOFCB(hp
), tv
.tv_sec
);
2124 if (H_FORKTYPE(hp
) != kSysFile
) {
2125 retval
= VOP_UPDATE(ap
->a_vp
, &tv
, &tv
, ap
->a_waitfor
== MNT_WAIT
);
2127 if (retval
!= E_NONE
) {
2128 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname
, H_NAME(hp
)));
2132 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2134 if (ap
->a_waitfor
== MNT_WAIT
) {
2135 DBG_ASSERT(vp
->v_dirtyblkhd
.lh_first
== NULL
);
2137 DBG_VOP_LOCKS_TEST(retval
);
2138 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
2144 hfs_fsync_transaction(struct vnode
*vp
)
2146 struct hfsnode
*hp
= VTOH(vp
);
2147 register struct buf
*bp
;
2153 * Flush all dirty buffers associated with a vnode.
2158 for (bp
= vp
->v_dirtyblkhd
.lh_first
; bp
; bp
= nbp
) {
2159 nbp
= bp
->b_vnbufs
.le_next
;
2160 if ((bp
->b_flags
& B_BUSY
))
2162 if ((bp
->b_flags
& B_DELWRI
) == 0)
2163 panic("hfs_fsync: not dirty");
2164 if ( !(bp
->b_flags
& B_LOCKED
))
2168 bp
->b_flags
|= B_BUSY
;
2169 bp
->b_flags
&= ~B_LOCKED
; /* Clear flag, should only be set on meta files */
2179 if ((vp
->v_flag
& VSYSTEM
) && (hp
->fcbBTCBPtr
!=NULL
))
2180 (void) BTSetLastSync(VTOFCB(vp
), tv
.tv_sec
);
2181 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
2192 IN WILLRELE struct vnode *dvp;
2193 IN WILLRELE struct vnode *vp;
2194 IN struct componentname *cnp;
2200 struct vop_remove_args
/* {
2201 struct vnode *a_dvp;
2203 struct componentname *a_cnp;
2206 struct vnode
*vp
= ap
->a_vp
;
2207 struct vnode
*dvp
= ap
->a_dvp
;
2208 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
2209 struct hfsmount
*hfsmp
= HTOHFS(hp
);
2210 struct proc
*p
= current_proc();
2212 int retval
, use_count
;
2215 DBG_FUNC_NAME("remove");
2216 DBG_VOP_LOCKS_DECL(2);
2217 DBG_VOP_PRINT_FUNCNAME();
2218 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
2219 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2220 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2221 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2225 if ((hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2226 (VTOH(dvp
)->h_meta
->h_pflags
& APPEND
)) {
2231 if (vp
->v_usecount
> 1) {
2233 * the namei done for the rename took a reference on the
2234 * vnode. Hence set 1 in the tookref parameter
2237 if(UBCISVALID(vp
) && !ubc_isinuse(vp
, 1))
2239 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2240 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2241 /* Carbon semantics prohibits deleting busy files */
2250 tv
= time
; /* Done here, so all times are the same */
2252 /* Check other siblings for in use also */
2253 /* Uncache everything and make sure no other usecount */
2255 * This assumes the presence of the most 1 sibling
2257 * a. loop through the siblings looking for another
2258 * b. If we find ourselves...skip it
2259 * If there was a sibling:
2260 * a. Check for a positve usecount
2261 * b. uncache any pages
2262 * c. Write out and memory changes
2263 * The idea is to keep the h_siblinglock as little as possible
2265 if (hp
->h_meta
->h_usecount
> 1) {
2266 struct vnode
*sib_vp
= NULL
;
2267 struct hfsnode
*nhp
;
2269 DBG_ASSERT(hp
->h_meta
->h_siblinghead
.cqh_first
&&
2270 (hp
->h_meta
->h_siblinghead
.cqh_first
!= hp
->h_meta
->h_siblinghead
.cqh_last
));
2271 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
2273 /* Loop through all siblings, skipping ourselves */
2274 simple_lock(&hp
->h_meta
->h_siblinglock
);
2275 CIRCLEQ_FOREACH(nhp
, &hp
->h_meta
->h_siblinghead
, h_sibling
) {
2276 if (nhp
== hp
) /* skip ourselves */
2280 simple_unlock(&hp
->h_meta
->h_siblinglock
);
2282 /* Check to see if the other fork is in use */
2283 DBG_ASSERT(sib_vp
!= NULL
);
2284 simple_lock(&sib_vp
->v_interlock
);
2285 use_count
= sib_vp
->v_usecount
;
2286 simple_unlock(&sib_vp
->v_interlock
);
2287 if (use_count
> 0) {
2289 * This is a sibling vnode and we did not take
2290 * a reference on it.
2291 * Hence set 0 in the tookref parameter
2294 if(UBCISVALID(sib_vp
) && !ubc_isinuse(sib_vp
, 0))
2296 if ((ap
->a_cnp
->cn_flags
& NODELETEBUSY
)
2297 || (hfsmp
->hfs_private_metadata_dir
== 0)) {
2298 /* Carbon semantics prohibits deleting busy files */
2303 } /* use_count > 0 */
2307 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2308 if (vget(sib_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
2310 * XXX SER An intelligient person would ask, why flush out changes
2311 * that are going to be deleted? See the next comment.
2313 if ((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) || (VTOFCB(sib_vp
)->fcbFlags
2314 & fcbModifiedMask
)) {
2315 DBG_ASSERT((VTOH(sib_vp
)->h_nodeflags
& IN_MODIFIED
) != 0);
2316 VOP_UPDATE(sib_vp
, &tv
, &tv
, 0);
2319 /* Invalidate the buffers, ignore the results */
2320 (void) vinvalbuf(sib_vp
, 0, NOCRED
, p
, 0, 0);
2324 }; /* h_use_count > 1 */
2327 * remove the entry from the namei cache:
2328 * We do it early before any linking/busy file wierdness, make sure the
2333 /* Flush out any catalog changes */
2334 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2335 * and not from memory which is more correct
2337 if ((hp
->h_nodeflags
& IN_MODIFIED
) || (HTOFCB(hp
)->fcbFlags
& fcbModifiedMask
))
2339 DBG_ASSERT((hp
->h_nodeflags
& IN_MODIFIED
) != 0);
2340 VOP_UPDATE(vp
, &tv
, &tv
, 0);
2343 /* lock catalog b-tree */
2344 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2345 if (retval
!= E_NONE
) {
2351 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2356 * Multi-linked files just need their link node deleted from the catalog
2358 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
2360 if ((ap
->a_cnp
->cn_flags
& HASBUF
) == 0 ||
2361 ap
->a_cnp
->cn_nameptr
[0] == '\0') {
2362 retval
= ENOENT
; /* name missing */
2366 /* lock extents b-tree (also protects volume bitmap) */
2367 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2368 if (retval
!= E_NONE
) {
2370 goto out2
; /* unlock catalog b-tree on the way out */
2373 retval
= hfsDelete (HTOVCB(hp
), H_FILEID(VTOH(dvp
)),
2374 ap
->a_cnp
->cn_nameptr
, TRUE
, H_HINT(hp
));
2376 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2381 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2382 hp
->h_nodeflags
|= IN_CHANGE
;
2383 if (--hp
->h_meta
->h_nlink
< 1)
2384 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2386 /* name and parent fields are no longer valid so invalidate them */
2387 H_DIRID(hp
) = kUnknownID
;
2388 hfs_set_metaname("\0", hp
->h_meta
, HTOHFS(hp
));
2390 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVENAME
)) == (HASBUF
| SAVENAME
))
2391 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
2393 * This is a deleted file no new clients
2394 * would be able to look it up. Maked the VM object
2395 * not cachable so that it dies as soon as the last
2396 * mapping disappears. This will reclaim the disk
2397 * space as soon as possible.
2400 goto out2
; /* link deleted, all done */
2405 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2406 * an active vnode is translated to a move/rename so the file appears
2407 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2410 UInt32 hint
= H_HINT(hp
);
2413 MAKE_DELETED_NAME(nodeName
, H_FILEID(hp
));
2415 retval
= hfsMoveRename (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
),
2416 hfsmp
->hfs_private_metadata_dir
, nodeName
, &hint
);
2417 if (retval
) goto out2
;
2419 hp
->h_meta
->h_metaflags
|= IN_DELETED
;
2420 hp
->h_nodeflags
|= IN_CHANGE
;
2422 /* update name so Catalog lookups succeed */
2424 H_DIRID(hp
) = hfsmp
->hfs_private_metadata_dir
;
2425 hfs_set_metaname(nodeName
, hp
->h_meta
, HTOHFS(hp
));
2428 * This is an open deleted file no new clients
2429 * would be able to look it up. Maked the VM object
2430 * not cachable so that it dies as soon as the last
2431 * mapping disappears. This will reclaim the disk
2432 * space as soon as possible.
2435 goto out2
; /* all done, unlock the catalog */
2438 /* unlock the Catalog */
2439 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2441 /* Invalidate the buffers */
2442 if ((retval
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0)))
2445 if(UBCINFOEXISTS(vp
))
2446 (void)ubc_setsize(vp
, (off_t
)0);
2449 /* lock catalog b-tree */
2450 retval
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2451 if (retval
!= E_NONE
) {
2455 /* lock extents b-tree (also protects volume bitmap) */
2456 retval
= hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
2457 if (retval
!= E_NONE
) {
2459 goto out2
; /* unlock catalog b-tree on the way out */
2462 /* remove entry from catalog and free any blocks used */
2463 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
2466 (void) hfs_metafilelocking(hfsmp
, kHFSExtentsFileID
, LK_RELEASE
, p
);
2467 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2472 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2473 hp
->h_meta
->h_mode
= 0; /* Makes the node go away...see inactive */
2474 /* clear the block mappings */
2475 hp
->fcbPLen
= (u_int64_t
)0;
2476 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
2478 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2484 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
2489 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
2493 VOP_UNLOCK(vp
, 0, p
);
2501 DBG_VOP_LOCKS_TEST(retval
);
2508 #% rename sourcePar_vp U U U
2509 #% rename source_vp U U U
2510 #% rename targetPar_vp L U U
2511 #% rename target_vp X U U
2514 IN WILLRELE struct vnode *sourcePar_vp;
2515 IN WILLRELE struct vnode *source_vp;
2516 IN struct componentname *source_cnp;
2517 IN WILLRELE struct vnode *targetPar_vp;
2518 IN WILLRELE struct vnode *target_vp;
2519 IN struct componentname *target_cnp;
2525 * source's parent directory is unlocked
2526 * source file or directory is unlocked
2527 * destination's parent directory is locked
2528 * destination file or directory is locked if it exists
2531 * all denodes should be released
2537 struct vop_rename_args
/* {
2538 struct vnode *a_fdvp;
2539 struct vnode *a_fvp;
2540 struct componentname *a_fcnp;
2541 struct vnode *a_tdvp;
2542 struct vnode *a_tvp;
2543 struct componentname *a_tcnp;
2546 struct vnode
*target_vp
= ap
->a_tvp
;
2547 struct vnode
*targetPar_vp
= ap
->a_tdvp
;
2548 struct vnode
*source_vp
= ap
->a_fvp
;
2549 struct vnode
*sourcePar_vp
= ap
->a_fdvp
;
2550 struct componentname
*target_cnp
= ap
->a_tcnp
;
2551 struct componentname
*source_cnp
= ap
->a_fcnp
;
2552 struct proc
*p
= source_cnp
->cn_proc
;
2553 struct hfsnode
*target_hp
, *targetPar_hp
, *source_hp
, *sourcePar_hp
;
2554 u_int32_t oldparent
= 0, newparent
= 0;
2555 int doingdirectory
= 0;
2558 struct hfsCatalogInfo catInfo
;
2559 u_int32_t tehint
= 0;
2560 DBG_VOP_LOCKS_DECL(4);
2562 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2563 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fvp
);DBG_VOP_CONT(("\n"));
2564 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_fdvp
);DBG_VOP_CONT(("\n"));
2565 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tvp
);DBG_VOP_CONT(("\n"));
2566 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_tdvp
);DBG_VOP_CONT(("\n"));
2567 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_fcnp
);DBG_VOP_CONT(("\n"));
2568 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap
->a_tcnp
);DBG_VOP_CONT(("\n"));
2569 DBG_VOP_LOCKS_INIT(0,ap
->a_fdvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2570 DBG_VOP_LOCKS_INIT(1,ap
->a_fvp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2571 DBG_VOP_LOCKS_INIT(2,ap
->a_tdvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2572 DBG_VOP_LOCKS_INIT(3,ap
->a_tvp
, VOPDBG_LOCKNOTNIL
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2573 WRITE_CK(ap
->a_fdvp
, funcname
);
2574 DBG_HFS_NODE_CHECK(ap
->a_fdvp
);
2575 DBG_HFS_NODE_CHECK(ap
->a_tdvp
);
2578 if ((target_cnp
->cn_flags
& HASBUF
) == 0 ||
2579 (source_cnp
->cn_flags
& HASBUF
) == 0)
2580 panic("hfs_rename: no name");
2583 DBG_ASSERT((ap
->a_fdvp
->v_type
== VDIR
) && (ap
->a_tdvp
->v_type
== VDIR
));
2584 target_hp
= targetPar_hp
= source_hp
= sourcePar_hp
= 0;
2586 /* If fvp is the same as tvp...then we are just changing case, ignore target_vp */
2588 * This must be done now, since the value of target_vp is used to
2589 * determine wether to unlock it (for instance, goto abortit).
2590 * In this case, target_vp comes in unlocked
2592 if (source_vp
== target_vp
)
2596 * Check for cross-device rename.
2598 if ((source_vp
->v_mount
!= targetPar_vp
->v_mount
) ||
2599 (target_vp
&& (source_vp
->v_mount
!= target_vp
->v_mount
))) {
2605 * Check for access permissions
2607 if (target_vp
&& ((VTOH(target_vp
)->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) ||
2608 (VTOH(targetPar_vp
)->h_meta
->h_pflags
& APPEND
))) {
2614 * Force Carbon renames to have MacUnicode encoding
2616 if ((VTOH(targetPar_vp
)->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
2617 tehint
= kTextEncodingMacUnicode
;
2620 if ((retval
= vn_lock(source_vp
, LK_EXCLUSIVE
, p
)))
2623 sourcePar_hp
= VTOH(sourcePar_vp
);
2624 source_hp
= VTOH(source_vp
);
2625 oldparent
= H_FILEID(sourcePar_hp
);
2626 if ((source_hp
->h_meta
->h_pflags
& (IMMUTABLE
| APPEND
)) || (sourcePar_hp
->h_meta
->h_pflags
& APPEND
)) {
2627 VOP_UNLOCK(source_vp
, 0, p
);
2633 * Be sure we are not renaming ".", "..", or an alias of ".". This
2634 * leads to a crippled directory tree. It's pretty tough to do a
2635 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2636 * doesn't work if the ".." entry is missing.
2638 if ((source_hp
->h_meta
->h_mode
& IFMT
) == IFDIR
) {
2639 if ((source_cnp
->cn_namelen
== 1 && source_cnp
->cn_nameptr
[0] == '.')
2640 || sourcePar_hp
== source_hp
2641 || (source_cnp
->cn_flags
&ISDOTDOT
)
2642 || (source_hp
->h_nodeflags
& IN_RENAME
)) {
2643 VOP_UNLOCK(source_vp
, 0, p
);
2647 source_hp
->h_nodeflags
|= IN_RENAME
;
2648 doingdirectory
= TRUE
;
2653 * >>>> Transit between abort and bad <<<<
2657 targetPar_hp
= VTOH(targetPar_vp
);
2659 target_hp
= VTOH(target_vp
);
2661 DBG_ASSERT(target_hp
== NULL
);
2663 newparent
= H_FILEID(targetPar_hp
);
2665 /* Test to make sure we are not crossing devices */
2666 /* XXX SER Is this necesary, does catalog manager take care of this? */
2668 if (H_DEV(target_hp
) != H_DEV(targetPar_hp
) || H_DEV(target_hp
) != H_DEV(source_hp
))
2669 panic("rename: EXDEV");
2672 if (H_DEV(targetPar_hp
) != H_DEV(source_hp
))
2673 panic("rename: EXDEV");
2676 retval
= VOP_ACCESS(source_vp
, VWRITE
, target_cnp
->cn_cred
, target_cnp
->cn_proc
);
2677 if (doingdirectory
&& (newparent
!= oldparent
)) {
2678 if (retval
) /* write access check above */
2681 retval
= 0; /* Reset value from above, we dont care about it anymore */
2684 * If the destination exists, then be sure its type (file or dir)
2685 * matches that of the source. And, if it is a directory make sure
2686 * it is empty. Then delete the destination.
2691 * If the parent directory is "sticky", then the user must
2692 * own the parent directory, or the destination of the rename,
2693 * otherwise the destination may not be changed (except by
2694 * root). This implements append-only directories.
2696 if ((targetPar_hp
->h_meta
->h_mode
& S_ISTXT
) && (target_cnp
->cn_cred
->cr_uid
!= 0) &&
2697 target_cnp
->cn_cred
->cr_uid
!= targetPar_hp
->h_meta
->h_uid
&&
2698 target_cnp
->cn_cred
->cr_uid
!= target_hp
->h_meta
->h_uid
) {
2704 * VOP_REMOVE will vput targetPar_vp so we better bump
2705 * its ref count and relockit, always set target_vp to
2706 * NULL afterwards to indicate that were done with it.
2710 cache_purge(target_vp
);
2713 target_cnp
->cn_flags
&= ~SAVENAME
;
2716 retval
= VOP_REMOVE(targetPar_vp
, target_vp
, target_cnp
);
2717 (void) vn_lock(targetPar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2722 if (retval
) goto bad
;
2727 if (newparent
!= oldparent
)
2728 vn_lock(sourcePar_vp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
2730 /* remove the existing entry from the namei cache: */
2731 cache_purge(source_vp
);
2733 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
2735 /* lock catalog b-tree */
2736 retval
= hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2738 if (newparent
!= oldparent
) /* unlock the lock we just got */
2739 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2743 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2744 retval
= MoveRenameCatalogNode(HTOVCB(source_hp
), H_DIRID(source_hp
),
2745 source_cnp
->cn_nameptr
, H_HINT(source_hp
),
2746 H_FILEID(VTOH(targetPar_vp
)),
2747 target_cnp
->cn_nameptr
, &H_HINT(source_hp
), tehint
);
2748 retval
= MacToVFSError(retval
);
2751 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2752 catInfo
.hint
= H_HINT(source_hp
);
2753 retval
= hfs_getcatalog(HTOVCB(source_hp
), H_FILEID(targetPar_hp
), target_cnp
->cn_nameptr
, target_cnp
->cn_namelen
, &catInfo
);
2756 /* unlock catalog b-tree */
2757 (void) hfs_metafilelocking(VTOHFS(source_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2759 if (newparent
!= oldparent
)
2760 VOP_UNLOCK(sourcePar_vp
, 0, p
);
2762 if (retval
) goto bad
;
2764 H_DIRID(source_hp
) = H_FILEID(targetPar_hp
);
2766 hfs_name_CatToMeta(&catInfo
.nodeData
, source_hp
->h_meta
);
2768 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
2770 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2774 * Timestamp both parent directories.
2775 * Note that if this is a rename within the same directory,
2776 * (where targetPar_hp == sourcePar_hp)
2777 * the code below is still safe and correct.
2779 targetPar_hp
->h_nodeflags
|= IN_UPDATE
;
2780 sourcePar_hp
->h_nodeflags
|= IN_UPDATE
;
2782 HFSTIMES(targetPar_hp
, &tv
, &tv
);
2783 HFSTIMES(sourcePar_hp
, &tv
, &tv
);
2786 vrele(sourcePar_vp
);
2789 DBG_VOP_LOCKS_TEST(retval
);
2790 if (retval
!= E_NONE
) {
2791 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2796 if (retval
&& doingdirectory
)
2797 source_hp
->h_nodeflags
&= ~IN_RENAME
;
2799 if (targetPar_vp
== target_vp
)
2800 vrele(targetPar_vp
);
2807 vrele(sourcePar_vp
);
2809 if (VOP_ISLOCKED(source_vp
))
2814 DBG_VOP_LOCKS_TEST(retval
);
2815 if (retval
!= E_NONE
) {
2816 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2822 VOP_ABORTOP(targetPar_vp
, target_cnp
); /* XXX, why not in NFS? */
2824 if (targetPar_vp
== target_vp
)
2825 vrele(targetPar_vp
);
2832 VOP_ABORTOP(sourcePar_vp
, source_cnp
); /* XXX, why not in NFS? */
2834 vrele(sourcePar_vp
);
2837 DBG_VOP_LOCKS_TEST(retval
);
2838 if (retval
!= E_NONE
) {
2839 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval
));
2852 IN WILLRELE struct vnode *dvp;
2853 OUT struct vnode **vpp;
2854 IN struct componentname *cnp;
2855 IN struct vattr *vap;
2857 We are responsible for freeing the namei buffer,
2858 it is done in hfs_makenode()
2863 struct vop_mkdir_args
/* {
2864 struct vnode *a_dvp;
2865 struct vnode **a_vpp;
2866 struct componentname *a_cnp;
2867 struct vattr *a_vap;
2870 struct proc
*p
= current_proc();
2872 int mode
= MAKEIMODE(ap
->a_vap
->va_type
, ap
->a_vap
->va_mode
);
2874 DBG_FUNC_NAME("mkdir");
2875 DBG_VOP_LOCKS_DECL(2);
2876 DBG_VOP_PRINT_FUNCNAME();
2877 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
2878 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2880 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2881 DBG_VOP_LOCKS_INIT(1,*ap
->a_vpp
, VOPDBG_IGNORE
, VOPDBG_LOCKED
, VOPDBG_IGNORE
, VOPDBG_POS
);
2883 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
));
2884 WRITE_CK( ap
->a_dvp
, funcname
);
2885 DBG_HFS_NODE_CHECK(ap
->a_dvp
);
2886 DBG_ASSERT(ap
->a_dvp
->v_type
== VDIR
);
2888 /* Create the vnode */
2889 DBG_ASSERT((ap
->a_cnp
->cn_flags
& SAVESTART
) == 0);
2890 retval
= hfs_makenode(mode
, 0, ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, p
);
2891 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
2893 if (retval
!= E_NONE
) {
2894 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname
, ap
->a_cnp
->cn_nameptr
, H_NAME(VTOH(ap
->a_dvp
))));
2895 DBG_VOP_LOCKS_TEST(retval
);
2899 DBG_VOP_LOCKS_TEST(E_NONE
);
2904 * Rmdir system call.
2909 IN WILLRELE struct vnode *dvp;
2910 IN WILLRELE struct vnode *vp;
2911 IN struct componentname *cnp;
2917 struct vop_rmdir_args
/* {
2918 struct vnode *a_dvp;
2920 struct componentname *a_cnp;
2923 struct vnode
*vp
= ap
->a_vp
;
2924 struct vnode
*dvp
= ap
->a_dvp
;
2925 struct hfsnode
*hp
= VTOH(vp
);
2926 struct proc
*p
= current_proc();
2928 DBG_FUNC_NAME("rmdir");
2929 DBG_VOP_LOCKS_DECL(2);
2930 DBG_VOP_PRINT_FUNCNAME();
2931 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);DBG_VOP_CONT(("\n"));
2932 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
2933 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
2935 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2936 DBG_VOP_LOCKS_INIT(1,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
2941 DBG_VOP_LOCKS_TEST(EINVAL
);
2946 * HFS differs from UFS here in that we don't allow removing
2947 * a directory that in use by others - even if its empty.
2949 * In the future we might want to allow this just like we do
2950 * for files (by renaming the busy directory).
2953 if (vp
->v_usecount
> 1) {
2954 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname
, vp
->v_usecount
));
2959 /* remove the entry from the namei cache: */
2962 /* lock catalog b-tree */
2963 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
2964 if (retval
!= E_NONE
) {
2968 /* remove entry from catalog */
2969 retval
= hfsDelete (HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), FALSE
, H_HINT(hp
));
2971 /* unlock catalog b-tree */
2972 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
2975 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
; /* Set the parent to be updated */
2976 hp
->h_meta
->h_mode
= 0; /* Makes the vnode go away...see inactive */
2977 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
2985 DBG_VOP_LOCKS_TEST(retval
);
2990 * symlink -- make a symbolic link
2991 #% symlink dvp L U U
2992 #% symlink vpp - U -
2994 # XXX - note that the return vnode has already been VRELE'ed
2995 # by the filesystem layer. To use it you must use vget,
2996 # possibly with a further namei.
2999 IN WILLRELE struct vnode *dvp;
3000 OUT WILLRELE struct vnode **vpp;
3001 IN struct componentname *cnp;
3002 IN struct vattr *vap;
3005 We are responsible for freeing the namei buffer,
3006 it is done in hfs_makenode().
3012 struct vop_symlink_args
/* {
3013 struct vnode *a_dvp;
3014 struct vnode **a_vpp;
3015 struct componentname *a_cnp;
3016 struct vattr *a_vap;
3020 register struct vnode
*vp
, **vpp
= ap
->a_vpp
;
3021 struct proc
*p
= current_proc();
3024 struct buf
*bp
= NULL
;
3026 /* HFS standard disks don't support symbolic links */
3027 if (VTOVCB(ap
->a_dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
3028 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
3030 return (EOPNOTSUPP
);
3033 /* Check for empty target name */
3034 if (ap
->a_target
[0] == 0) {
3035 VOP_ABORTOP(ap
->a_dvp
, ap
->a_cnp
);
3040 /* Create the vnode */
3041 retval
= hfs_makenode(IFLNK
| ap
->a_vap
->va_mode
, 0, ap
->a_dvp
,
3043 DBG_VOP_UPDATE_VP(1, *ap
->a_vpp
);
3045 if (retval
!= E_NONE
) {
3050 len
= strlen(ap
->a_target
);
3052 hp
->fcbClmpSize
= VTOVCB(vp
)->blockSize
;
3054 /* Allocate space for the link */
3055 retval
= VOP_TRUNCATE(vp
, len
, IO_NOZEROFILL
,
3056 ap
->a_cnp
->cn_cred
, ap
->a_cnp
->cn_proc
);
3060 /* Write the link to disk */
3061 bp
= getblk(vp
, 0, roundup((int)hp
->fcbEOF
, VTOHFS(vp
)->hfs_phys_block_size
), 0, 0, BLK_META
);
3062 bzero(bp
->b_data
, bp
->b_bufsize
);
3063 bcopy(ap
->a_target
, bp
->b_data
, len
);
3064 bp
->b_flags
|= B_DIRTY
;
3074 * Dummy dirents to simulate the "." and ".." entries of the directory
3075 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
3076 * the size of these entries is the smallest needed to represent them
3077 * (only 12 byte each).
3079 static hfsdotentry rootdots
[2] = {
3082 sizeof(struct hfsdotentry
), /* d_reclen */
3083 DT_DIR
, /* d_type */
3089 sizeof(struct hfsdotentry
), /* d_reclen */
3090 DT_DIR
, /* d_type */
3096 static hfsdotentry emptyentry
= { 0 };
3099 * There is some confusion as to what the semantics of uio_offset are.
3100 * In ufs, it represents the actual byte offset within the directory
3101 * "file." HFS, however, just uses it as an entry counter - essentially
3102 * assuming that it has no meaning except to the hfs_readdir function.
3103 * This approach would be more efficient here, but some callers may
3104 * assume the uio_offset acts like a byte offset. NFS in fact
3105 * monkeys around with the offset field a lot between readdir calls.
3107 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3108 * fields is a mess as well. The libc function readdir() returns
3109 * NULL (indicating the end of a directory) when either
3110 * the getdirentries() syscall (which calls this and returns
3111 * the size of the buffer passed in less the value of uiop->uio_resid)
3112 * returns 0, or a direct record with a d_reclen of zero.
3113 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3114 * of the directory by testing uiop->uio_resid == 0. The solution
3115 * is to pad the size of the last struct direct in a given
3116 * block to fill the block if we are not at the end of the directory.
3119 struct callbackstate
{
3120 u_int32_t cbs_parentID
;
3121 u_int32_t cbs_hiddenDirID
;
3122 off_t cbs_lastoffset
;
3123 struct uio
* cbs_uio
;
3124 ExtendedVCB
* cbs_vcb
;
3125 int16_t cbs_hfsPlus
;
3131 ProcessCatalogEntry(const CatalogKey
*ckp
, const CatalogRecord
*crp
,
3132 u_int16_t recordLen
, struct callbackstate
*state
)
3138 struct dirent catent
;
3140 if (state
->cbs_hfsPlus
)
3141 curID
= ckp
->hfsPlus
.parentID
;
3143 curID
= ckp
->hfs
.parentID
;
3145 /* We're done when parent directory changes */
3146 if (state
->cbs_parentID
!= curID
) {
3149 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3150 * so remove padding for now...
3154 * Pad the end of list with an empty record.
3155 * This eliminates an extra call by readdir(3c).
3157 catent
.d_fileno
= 0;
3158 catent
.d_reclen
= 0;
3160 catent
.d_namlen
= 0;
3161 *(int32_t*)&catent
.d_name
[0] = 0;
3163 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3165 state
->cbs_result
= uiomove((caddr_t
) &catent
, 12, state
->cbs_uio
);
3166 if (state
->cbs_result
== 0)
3167 state
->cbs_result
= ENOENT
;
3169 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3170 state
->cbs_result
= ENOENT
;
3172 return (0); /* stop */
3175 if (state
->cbs_hfsPlus
) {
3176 switch(crp
->recordType
) {
3177 case kHFSPlusFolderRecord
:
3178 catent
.d_type
= DT_DIR
;
3179 catent
.d_fileno
= crp
->hfsPlusFolder
.folderID
;
3181 case kHFSPlusFileRecord
:
3182 catent
.d_type
= DT_REG
;
3183 catent
.d_fileno
= crp
->hfsPlusFile
.fileID
;
3186 return (0); /* stop */
3189 cnp
= (CatalogName
*) &ckp
->hfsPlus
.nodeName
;
3190 result
= utf8_encodestr(cnp
->ustr
.unicode
, cnp
->ustr
.length
* sizeof(UniChar
),
3191 catent
.d_name
, &utf8chars
, kdirentMaxNameBytes
+ 1, ':', 0);
3192 if (result
== ENAMETOOLONG
) {
3193 result
= ConvertUnicodeToUTF8Mangled(cnp
->ustr
.length
* sizeof(UniChar
),
3194 cnp
->ustr
.unicode
, kdirentMaxNameBytes
+ 1, (ByteCount
*)&utf8chars
, catent
.d_name
, catent
.d_fileno
);
3197 switch(crp
->recordType
) {
3198 case kHFSFolderRecord
:
3199 catent
.d_type
= DT_DIR
;
3200 catent
.d_fileno
= crp
->hfsFolder
.folderID
;
3202 case kHFSFileRecord
:
3203 catent
.d_type
= DT_REG
;
3204 catent
.d_fileno
= crp
->hfsFile
.fileID
;
3207 return (0); /* stop */
3210 cnp
= (CatalogName
*) ckp
->hfs
.nodeName
;
3211 result
= hfs_to_utf8(state
->cbs_vcb
, cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3212 (ByteCount
*)&utf8chars
, catent
.d_name
);
3214 * When an HFS name cannot be encoded with the current
3215 * volume encoding we use MacRoman as a fallback.
3218 result
= mac_roman_to_utf8(cnp
->pstr
, kdirentMaxNameBytes
+ 1,
3219 (ByteCount
*)&utf8chars
, catent
.d_name
);
3222 catent
.d_namlen
= utf8chars
;
3223 catent
.d_reclen
= DIRENTRY_SIZE(utf8chars
);
3225 /* hide our private meta data directory */
3226 if (curID
== kRootDirID
&&
3227 catent
.d_fileno
== state
->cbs_hiddenDirID
&&
3228 catent
.d_type
== DT_DIR
)
3231 state
->cbs_lastoffset
= state
->cbs_uio
->uio_offset
;
3233 /* if this entry won't fit then we're done */
3234 if (catent
.d_reclen
> state
->cbs_uio
->uio_resid
)
3235 return (0); /* stop */
3237 state
->cbs_result
= uiomove((caddr_t
) &catent
, catent
.d_reclen
, state
->cbs_uio
);
3239 /* continue iteration if there's room */
3240 return (state
->cbs_result
== 0 &&
3241 state
->cbs_uio
->uio_resid
>= AVERAGE_HFSDIRENTRY_SIZE
);
3245 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3246 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3247 * "Nbytes must be greater than or equal to the block size associated with the file,
3248 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3249 * room for the . and .. entries that have to added manually.
3256 IN struct vnode *vp;
3257 INOUT struct uio *uio;
3258 IN struct ucred *cred;
3261 INOUT u_long **cookies;
3265 struct vop_readdir_args
/* {
3274 register struct uio
*uio
= ap
->a_uio
;
3275 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
3276 struct proc
*p
= current_proc();
3277 ExtendedVCB
*vcb
= HTOVCB(hp
);
3278 off_t off
= uio
->uio_offset
;
3279 u_int32_t dirID
= H_FILEID(hp
);
3281 OSErr result
= noErr
;
3282 u_int32_t diroffset
;
3284 CatalogIterator
*cip
;
3286 struct callbackstate state
;
3289 DBG_FUNC_NAME("readdir");
3290 DBG_VOP_LOCKS_DECL(1);
3292 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
3293 DBG_VOP_PRINT_FUNCNAME();
3294 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3295 DBG_HFS_NODE_CHECK(ap
->a_vp
);
3297 /* We assume it's all one big buffer... */
3298 if (uio
->uio_iovcnt
> 1 || uio
->uio_resid
< AVERAGE_HFSDIRENTRY_SIZE
) {
3302 /* Create the entries for . and .. */
3303 if (uio
->uio_offset
< sizeof(rootdots
)) {
3307 rootdots
[0].d_fileno
= dirID
;
3308 rootdots
[1].d_fileno
= H_DIRID(hp
);
3310 if (uio
->uio_offset
== 0) {
3311 dep
= (caddr_t
) &rootdots
[0];
3312 dotsize
= 2* sizeof(struct hfsdotentry
);
3313 } else if (uio
->uio_offset
== sizeof(struct hfsdotentry
)) {
3314 dep
= (caddr_t
) &rootdots
[1];
3315 dotsize
= sizeof(struct hfsdotentry
);
3321 retval
= uiomove(dep
, dotsize
, uio
);
3326 diroffset
= uio
->uio_offset
;
3328 /* lock catalog b-tree */
3329 retval
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
3330 if (retval
!= E_NONE
)
3333 /* get an iterator and position it */
3334 cip
= GetCatalogIterator(vcb
, dirID
, diroffset
);
3336 result
= PositionIterator(cip
, diroffset
, &bi
, &op
);
3337 if (result
== cmNotFound
) {
3340 AgeCatalogIterator(cip
);
3342 } else if ((retval
= MacToVFSError(result
)))
3345 state
.cbs_hiddenDirID
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
3346 state
.cbs_lastoffset
= cip
->currentOffset
;
3347 state
.cbs_vcb
= vcb
;
3348 state
.cbs_uio
= uio
;
3349 state
.cbs_result
= 0;
3350 state
.cbs_parentID
= dirID
;
3352 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3353 state
.cbs_hfsPlus
= 1;
3355 state
.cbs_hfsPlus
= 0;
3357 /* process as many entries as possible... */
3358 result
= BTIterateRecords(GetFileControlBlock(vcb
->catalogRefNum
), op
, &bi
,
3359 (IterateCallBackProcPtr
)ProcessCatalogEntry
, &state
);
3361 if (state
.cbs_result
)
3362 retval
= state
.cbs_result
;
3364 retval
= MacToVFSError(result
);
3366 if (retval
== ENOENT
) {
3372 cip
->currentOffset
= state
.cbs_lastoffset
;
3373 cip
->nextOffset
= uio
->uio_offset
;
3374 UpdateCatalogIterator(&bi
, cip
);
3381 AgeCatalogIterator(cip
);
3384 (void) ReleaseCatalogIterator(cip
);
3386 /* unlock catalog b-tree */
3387 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3389 if (retval
!= E_NONE
) {
3390 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname
, retval
,
3391 H_FILEID(hp
), H_NAME(hp
)));
3396 /* were we already past eof ? */
3397 if (uio
->uio_offset
== off
) {
3402 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
3403 hp
->h_nodeflags
|= IN_ACCESS
;
3405 /* Bake any cookies */
3406 if (!retval
&& ap
->a_ncookies
!= NULL
) {
3407 struct dirent
* dpStart
;
3408 struct dirent
* dpEnd
;
3415 * Only the NFS server uses cookies, and it loads the
3416 * directory block into system space, so we can just look at
3419 if (uio
->uio_segflg
!= UIO_SYSSPACE
)
3420 panic("hfs_readdir: unexpected uio from NFS server");
3421 dpStart
= (struct dirent
*)(uio
->uio_iov
->iov_base
- (uio
->uio_offset
- off
));
3422 dpEnd
= (struct dirent
*) uio
->uio_iov
->iov_base
;
3423 for (dp
= dpStart
, ncookies
= 0;
3424 dp
< dpEnd
&& dp
->d_reclen
!= 0;
3425 dp
= (struct dirent
*)((caddr_t
)dp
+ dp
->d_reclen
))
3427 MALLOC(cookies
, u_long
*, ncookies
* sizeof(u_long
), M_TEMP
, M_WAITOK
);
3428 for (dp
= dpStart
, cookiep
= cookies
;
3430 dp
= (struct dirent
*)((caddr_t
) dp
+ dp
->d_reclen
)) {
3431 off
+= dp
->d_reclen
;
3432 *cookiep
++ = (u_long
) off
;
3434 *ap
->a_ncookies
= ncookies
;
3435 *ap
->a_cookies
= cookies
;
3441 *ap
->a_eofflag
= eofflag
;
3443 DBG_VOP_LOCKS_TEST(retval
);
3449 * readdirattr operation will return attributes for the items in the
3450 * directory specified.
3452 * It does not do . and .. entries. The problem is if you are at the root of the
3453 * hfs directory and go to .. you could be crossing a mountpoint into a
3454 * different (ufs) file system. The attributes that apply for it may not
3455 * apply for the file system you are doing the readdirattr on. To make life
3456 * simpler, this call will only return entries in its directory, hfs like.
3458 * 1.getattrlist creates a thread record if the objpermanentid attribute
3459 * is requested. Just do EINVAL for now and fix later.
3460 * 2. more than one for uiovcnt support.
3461 * 3. put knohint (hints) in state for next call in
3462 * 4. credentials checking when rest of hfs does it.
3463 * 5. Do return permissions concatenation ???
3468 #% readdirattr vp L L L
3471 IN struct vnode *vp;
3472 IN struct attrlist *alist;
3473 INOUT struct uio *uio;
3476 OUT u_long *newstate;
3478 OUT u_long *actualCount;
3479 OUT u_long **cookies;
3480 IN struct ucred *cred;
3485 struct vop_readdirattr_args
/* {
3487 struct attrlist *alist;
3493 u_long *actualcount;
3498 struct vnode
*vp
= ap
->a_vp
;
3499 struct attrlist
*alist
= ap
->a_alist
;
3500 register struct uio
*uio
= ap
->a_uio
;
3501 u_long maxcount
= ap
->a_maxcount
;
3503 ExtendedVCB
*vcb
= HTOVCB(VTOH(vp
));
3504 UInt32 dirID
= H_FILEID(VTOH(vp
));
3505 struct proc
*proc
= current_proc(); /* could get this out of uio */
3506 off_t startoffset
= uio
->uio_offset
;
3507 struct hfsCatalogInfo catInfo
;
3510 u_long fixedblocksize
;
3511 u_long maxattrblocksize
;
3512 u_long currattrbufsize
;
3513 void *attrbufptr
= NULL
;
3517 *(ap
->a_actualcount
) = 0;
3518 *(ap
->a_eofflag
) = 0;
3520 /* check for invalid options, check vnode, and buffer space */
3521 if (((ap
->a_options
& ~FSOPT_NOINMEMUPDATE
) != 0) ||
3523 (uio
->uio_resid
<= 0) || (uio
->uio_iovcnt
> 1))
3526 /* this call doesn't take volume attributes */
3527 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
3528 ((alist
->commonattr
& ~ATTR_CMN_VALIDMASK
) != 0) ||
3529 (alist
->volattr
!= 0) ||
3530 ((alist
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) ||
3531 ((alist
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) ||
3532 ((alist
->forkattr
& ~ATTR_FORK_VALIDMASK
) != 0))
3535 /* Reject requests for unsupported options for now: */
3536 if ((alist
->commonattr
& (ATTR_CMN_NAMEDATTRCOUNT
| ATTR_CMN_NAMEDATTRLIST
)) ||
3537 (alist
->fileattr
& (ATTR_FILE_FILETYPE
| ATTR_FILE_FORKCOUNT
| ATTR_FILE_FORKLIST
)) ||
3538 (alist
->commonattr
& ATTR_CMN_OBJPERMANENTID
) )
3541 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3542 * uiomove afterwards. It's an extra copy, but for now leave it alone
3544 fixedblocksize
= (sizeof(u_long
) + AttributeBlockSize(alist
)); /* u_long for length */
3545 maxattrblocksize
= fixedblocksize
;
3546 if (alist
->commonattr
& ATTR_CMN_NAME
)
3547 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
3548 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
3549 attrptr
= attrbufptr
;
3550 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3552 /* Since attributes passed back can contain variable ones (name), we can't just use
3553 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3554 * as hfs_readdir was originally doing. If this all we need to represent the current
3555 * state, then ap->a_state is not needed at all.
3557 /* index = ap->a_state; should not be less than 1 */
3558 index
= (uio
->uio_offset
/ sizeof(struct dirent
)) + 1;
3559 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
3562 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3563 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3564 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3566 catInfo
.hint
= kNoHint
; /* note, we may want to save the latest in state */
3567 while ((uio
->uio_resid
>= 0) && (maxcount
!=0 )) {
3568 /* better to check uio_resid against max or fixedblocksize, but won't work.
3569 * Depending on if dir or file, the attributes returned will be different.
3570 * Thus fixedblocksize is too large in some cases.Also, the variable
3571 * part (like name) could be between fixedblocksize and the max.
3575 /* Lock catalog b-tree */
3576 if ((retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_SHARED
, proc
)) != E_NONE
)
3579 catInfo
.nodeData
.cnd_iNodeNumCopy
= 0;
3580 result
= GetCatalogOffspring(vcb
, dirID
, index
, &catInfo
.nodeData
, NULL
, NULL
);
3582 hfs_resolvelink(vcb
, &catInfo
.nodeData
);
3584 /* Unlock catalog b-tree, unconditionally . Ties up the everything during enumeration */
3585 (void) hfs_metafilelocking( VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, proc
);
3587 if (result
!= noErr
) {
3588 if (result
== cmNotFound
) {
3589 *(ap
->a_eofflag
) = TRUE
;
3592 else retval
= MacToVFSError(result
);
3596 /* hide our private meta data directory as does hfs_readdir */
3597 if ((dirID
== kRootDirID
) &&
3598 catInfo
.nodeData
.cnd_nodeID
== VCBTOHFS(vcb
)->hfs_private_metadata_dir
&&
3599 catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) {
3602 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3606 *((u_long
*)attrptr
)++ = 0; /* move it past length */
3609 * Don't use data from cached vnodes when FSOPT_NOINMEMUPDATE
3610 * option is active or if this entry is a hard link.
3612 if ((ap
->a_options
& FSOPT_NOINMEMUPDATE
)
3613 || (catInfo
.nodeData
.cnd_iNodeNumCopy
!= 0)) {
3614 /* vp okay to use instead of root vp */
3615 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3617 struct vnode
*entry_vp
= NULL
;
3618 struct vnode
*rsrc_vp
= NULL
;
3623 * Flush out any in-memory state to the catalog record.
3625 * In the HFS locking hierarchy, the data fork vnode must
3626 * be acquired before the resource fork vnode.
3628 nodeid
= catInfo
.nodeData
.cnd_nodeID
;
3629 if (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
)
3630 nodetype
= kDirectory
;
3632 nodetype
= kDataFork
;
3634 /* Check for this entry's cached vnode: */
3635 entry_vp
= hfs_vhashget(H_DEV(VTOH(vp
)), nodeid
, nodetype
);
3637 /* Also check for a cached resource fork vnode: */
3638 if (nodetype
== kDataFork
) {
3639 rsrc_vp
= hfs_vhashget(H_DEV(VTOH(vp
)), nodeid
, kRsrcFork
);
3640 if ((rsrc_vp
!= NULL
)
3641 && (VTOH(rsrc_vp
)->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
))) {
3642 /* Pick up resource fork info */
3643 CopyVNodeToCatalogNode(rsrc_vp
, &catInfo
.nodeData
);
3647 if (entry_vp
!= NULL
)
3648 PackAttributeBlock(alist
, entry_vp
, &catInfo
, &attrptr
, &varptr
);
3649 else if (rsrc_vp
!= NULL
)
3650 PackAttributeBlock(alist
, rsrc_vp
, &catInfo
, &attrptr
, &varptr
);
3652 PackCatalogInfoAttributeBlock(alist
, vp
, &catInfo
, &attrptr
, &varptr
);
3659 currattrbufsize
= *((u_long
*)attrbufptr
) = ((char *)varptr
- (char *)attrbufptr
);
3661 /* now check if we can't fit in the buffer space remaining */
3662 if (currattrbufsize
> uio
->uio_resid
)
3665 retval
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, ap
->a_uio
);
3666 if (retval
!= E_NONE
)
3668 attrptr
= attrbufptr
;
3669 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
3671 *ap
->a_actualcount
+= 1;
3674 /* Clean for the next loop */
3675 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3677 *ap
->a_newstate
= VTOH(vp
)->h_meta
->h_mtime
;/* before we unlock, know the mod date */
3679 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
3681 if (!retval
&& ap
->a_cookies
!= NULL
) { /* CHECK THAT 0 wasn't passed in */
3688 /* Only the NFS server uses cookies, and it loads the
3689 * directory block into system space, so we can just look at
3692 if (uio
->uio_segflg
!= UIO_SYSSPACE
) /* || uio->uio_iovcnt != 1 checked earlier */
3693 panic("hfs_readdirattr: unexpected uio from NFS server");
3694 dpStart
= uio
->uio_iov
->iov_base
- (uio
->uio_offset
- startoffset
);
3695 dpEnd
= uio
->uio_iov
->iov_base
;
3696 MALLOC(cookies
, u_long
*, (*ap
->a_actualcount
)*sizeof(u_long
), M_TEMP
, M_WAITOK
);
3697 for (dp
= dpStart
, cookiep
= cookies
;
3699 dp
= ((caddr_t
) dp
+ *((u_long
*)dp
))) {
3700 *cookiep
++ = (u_long
)((caddr_t
)dp
+ sizeof(u_long
));
3702 *ap
->a_cookies
= cookies
;
3705 uio
->uio_offset
= startoffset
+ (*ap
->a_actualcount
)*sizeof(struct dirent
);
3708 if (attrbufptr
!= NULL
)
3709 FREE(attrbufptr
, M_TEMP
);
3715 * Return target name of a symbolic link
3716 #% readlink vp L L L
3719 IN struct vnode *vp;
3720 INOUT struct uio *uio;
3721 IN struct ucred *cred;
3726 struct vop_readlink_args
/* {
3729 struct ucred *a_cred;
3733 struct vnode
*vp
= ap
->a_vp
;
3734 struct hfsnode
*hp
= VTOH(vp
);
3736 if (vp
->v_type
!= VLNK
)
3739 /* Zero length sym links are not allowed */
3740 if (hp
->fcbEOF
== 0) {
3741 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
3745 /* Cache the path so we don't waste buffer cache resources */
3746 if (hp
->h_symlinkptr
== NULL
) {
3747 struct buf
*bp
= NULL
;
3749 if (H_ISBIGLINK(hp
))
3750 MALLOC(hp
->h_symlinkptr
, char *, hp
->fcbEOF
, M_TEMP
, M_WAITOK
);
3752 retval
= meta_bread(vp
, 0, roundup((int)hp
->fcbEOF
, VTOHFS(vp
)->hfs_phys_block_size
),
3757 if (hp
->h_symlinkptr
) {
3758 FREE(hp
->h_symlinkptr
, M_TEMP
);
3759 hp
->h_symlinkptr
= NULL
;
3764 bcopy(bp
->b_data
, H_SYMLINK(hp
), (size_t)hp
->fcbEOF
);
3767 bp
->b_flags
|= B_INVAL
; /* data no longer needed */
3772 retval
= uiomove((caddr_t
)H_SYMLINK(hp
), (int)hp
->fcbEOF
, ap
->a_uio
);
3779 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3780 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3781 #% abortop dvp = = =
3784 IN struct vnode *dvp;
3785 IN struct componentname *cnp;
3793 struct vop_abortop_args
/* {
3794 struct vnode *a_dvp;
3795 struct componentname *a_cnp;
3798 DBG_FUNC_NAME("abortop");
3799 DBG_VOP_LOCKS_DECL(1);
3800 DBG_VOP_PRINT_FUNCNAME();
3801 DBG_VOP_PRINT_VNODE_INFO(ap
->a_dvp
);
3802 DBG_VOP_PRINT_CPN_INFO(ap
->a_cnp
);DBG_VOP_CONT(("\n"));
3805 DBG_VOP_LOCKS_INIT(0,ap
->a_dvp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
3807 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
3808 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
3810 DBG_VOP_LOCKS_TEST(E_NONE
);
3814 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3817 #% inactive vp L U U
3820 IN struct vnode *vp;
3827 struct vop_inactive_args
/* {
3831 struct vnode
*vp
= ap
->a_vp
;
3832 struct hfsnode
*hp
= VTOH(vp
);
3833 struct proc
*p
= ap
->a_p
;
3836 extern int prtactive
;
3838 DBG_FUNC_NAME("inactive");
3839 DBG_VOP_LOCKS_DECL(1);
3840 DBG_VOP_PRINT_FUNCNAME();
3841 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3843 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
3846 if (prtactive
&& vp
->v_usecount
<= 0)
3847 vprint("hfs_inactive: pushing active", vp
);
3849 if (vp
->v_usecount
!= 0)
3850 DBG_VOP(("%s: bad usecount = %d\n",funcname
,vp
->v_usecount
));
3853 * Ignore nodes related to stale file handles.
3855 if (hp
->h_meta
->h_mode
== 0)
3859 * Check for a postponed deletion
3861 if (hp
->h_meta
->h_metaflags
& IN_DELETED
) {
3862 hp
->h_meta
->h_metaflags
&= ~IN_DELETED
;
3864 error
= vinvalbuf(vp
, 0, NOCRED
, p
, 0, 0);
3865 if (error
) goto out
;
3867 if(UBCINFOEXISTS(vp
))
3868 (void)ubc_setsize(vp
, (off_t
)0);
3871 * Note: we do not need a lock on the private metadata directory
3872 * since it never has a vnode associated with it.
3874 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3875 if (error
) goto out
;
3876 error
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
3878 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3882 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
3885 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
3886 error
= hfsDelete(HTOVCB(hp
), VTOHFS(vp
)->hfs_private_metadata_dir
, iNodeName
, TRUE
, H_HINT(hp
));
3888 /* XXX can we leave orphaned sibling? */
3889 error
= hfsDelete(HTOVCB(hp
), H_DIRID(hp
), H_NAME(hp
), TRUE
, H_HINT(hp
));
3890 if (error
== ENOENT
) {
3891 /* try by fileID as a backup */
3892 error
= hfsDelete(HTOVCB(hp
), H_FILEID(hp
), NULL
, TRUE
, H_HINT(hp
));
3896 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
3897 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
3898 if (error
) goto out
;
3900 hp
->h_meta
->h_metaflags
|= IN_NOEXISTS
;
3901 hp
->h_meta
->h_mode
= 0;
3902 /* clear the block mappings */
3903 hp
->fcbPLen
= (u_int64_t
)0;
3904 bzero(&hp
->fcbExtents
, sizeof(HFSPlusExtentRecord
));
3906 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
3909 if (hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) {
3911 VOP_UPDATE(vp
, &tv
, &tv
, 0);
3915 VOP_UNLOCK(vp
, 0, p
);
3917 * If we are done with the inode, reclaim it
3918 * so that it can be reused immediately.
3920 if (hp
->h_meta
->h_mode
== 0)
3921 vrecycle(vp
, (struct slock
*)0, p
);
3923 /* XXX SER Here we might want to get rid of any other forks
3924 * The problem is that if we call vrecycle(), our structure
3925 * disappear from under us, we would need to remember, and expect
3926 * things to go to null or to disappear
3927 * But it stillw would be a good thing to remove vnodes
3928 * referencing stale data
3931 DBG_VOP_LOCKS_TEST(E_NONE
);
3936 Ignored since the locks are gone......
3940 IN struct vnode *vp;
3947 struct vop_reclaim_args
/* {
3951 struct vnode
*vp
= ap
->a_vp
;
3952 struct hfsnode
*hp
= VTOH(vp
);
3953 void *tdata
= vp
->v_data
;
3955 Boolean freeMeta
= true;
3956 struct vnode
*devvp
= NULL
;
3958 extern int prtactive
;
3959 DBG_FUNC_NAME("reclaim");
3960 DBG_VOP_LOCKS_DECL(1);
3961 DBG_VOP_PRINT_FUNCNAME();
3962 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
3964 DBG_VOP_LOCKS_INIT(0, ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_ZERO
);
3967 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3968 created in their entirity could be getting cleaned up here.
3971 if (prtactive
&& vp
->v_usecount
!= 0)
3972 vprint("hfs_reclaim(): pushing active", vp
);
3974 hp
->h_nodeflags
|= IN_ALLOCATING
; /* Mark this as being incomplete */
3976 * This will remove the entry from the hash AND the sibling list
3977 * This will make sure everything is in a stable state to see if we can remove the meta
3978 * i.e. if this is the only fork...the sibling list will be empty
3982 DBG_ASSERT(tdata
!= NULL
);
3983 DBG_ASSERT(hp
->h_meta
!= NULL
);
3985 devvp
= hp
->h_meta
->h_devvp
; /* For later releasing */
3986 hp
->h_meta
->h_usecount
--;
3988 /* release the file meta if this is the last fork */
3989 if (H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
) {
3990 if (hp
->h_meta
->h_siblinghead
.cqh_first
!= (void *) &hp
->h_meta
->h_siblinghead
)
3995 DBG_ASSERT(hp
->h_meta
->h_usecount
== 0);
3996 if (hp
->h_meta
->h_metaflags
& IN_LONGNAME
) {
3998 DBG_ASSERT(tname
!= NULL
);
3999 FREE(tname
, M_TEMP
);
4001 FREE_ZONE(hp
->h_meta
, sizeof(struct hfsfilemeta
), M_HFSFMETA
);
4005 DBG_ASSERT(hp
->h_meta
->h_usecount
== 1);
4007 /* Dump cached symlink data */
4008 if ((vp
->v_type
== VLNK
) && (hp
->h_symlinkptr
!= NULL
)) {
4009 if (H_ISBIGLINK(hp
))
4010 FREE(hp
->h_symlinkptr
, M_TEMP
);
4011 hp
->h_symlinkptr
= NULL
;
4015 * Purge old data structures associated with the inode.
4022 /* Free our data structs */
4023 FREE_ZONE(tdata
, sizeof(struct hfsnode
), M_HFSNODE
);
4026 DBG_VOP_LOCKS_TEST(E_NONE
);
4032 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
4036 IN struct vnode *vp;
4043 struct vop_lock_args
/* {
4049 struct vnode
* vp
= ap
->a_vp
;
4050 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4053 DBG_FUNC_NAME("lock");
4054 DBG_VOP_LOCKS_DECL(1);
4055 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
4056 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
4057 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_ZERO
);
4059 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
, &vp
->v_interlock
, ap
->a_p
);
4060 if (retval
!= E_NONE
) {
4061 if ((ap
->a_flags
& LK_NOWAIT
) == 0)
4062 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval
, ap
->a_flags
));
4066 if (vp
->v_type
== VDIR
)
4067 hp
->h_nodeflags
&= ~IN_BYCNID
;
4070 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
4071 DBG_VOP_LOCKS_TEST(retval
);
4076 * Unlock an hfsnode.
4080 IN struct vnode *vp;
4087 struct vop_unlock_args
/* {
4093 struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4094 struct vnode
*vp
= ap
->a_vp
;
4095 int retval
= E_NONE
;
4097 DBG_FUNC_NAME("unlock");
4098 DBG_VOP_LOCKS_DECL(1);
4099 DBG_VOP_PRINT_FUNCNAME();
4100 DBG_VOP_PRINT_VNODE_INFO(vp
);DBG_VOP_CONT((" flags = 0x%08X.\n", ap
->a_flags
));
4101 DBG_VOP_LOCKS_INIT(0,vp
, VOPDBG_LOCKED
, VOPDBG_UNLOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4103 if (vp
->v_type
== VDIR
)
4104 hp
->h_nodeflags
&= ~IN_BYCNID
;
4106 DBG_ASSERT((ap
->a_flags
& (LK_EXCLUSIVE
|LK_SHARED
)) == 0);
4107 retval
= lockmgr(&hp
->h_lock
, ap
->a_flags
| LK_RELEASE
, &vp
->v_interlock
, ap
->a_p
);
4108 if (retval
!= E_NONE
) {
4109 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval
, H_FORKTYPE(hp
)));
4112 DBG_ASSERT(*((int*)&vp
->v_interlock
) == 0);
4113 DBG_VOP_LOCKS_TEST(retval
);
4119 * Print out the contents of an hfsnode.
4123 IN struct vnode *vp;
4127 struct vop_print_args
/* {
4131 register struct vnode
* vp
= ap
->a_vp
;
4132 register struct hfsnode
*hp
= VTOH( vp
);
4133 DBG_FUNC_NAME("print");
4134 DBG_VOP_LOCKS_DECL(1);
4135 DBG_VOP_PRINT_FUNCNAME();
4136 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4138 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_IGNORE
, VOPDBG_POS
);
4140 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp
),
4141 major(H_DEV(hp
)), minor(H_DEV(hp
)));
4142 /* lockmgr_printinfo(&hp->h_lock); */
4144 DBG_VOP_LOCKS_TEST(E_NONE
);
4150 * Check for a locked hfsnode.
4151 #% islocked vp = = =
4154 IN struct vnode *vp;
4159 struct vop_islocked_args
/* {
4164 //DBG_FUNC_NAME("islocked");
4165 //DBG_VOP_LOCKS_DECL(1);
4166 //DBG_VOP_PRINT_FUNCNAME();
4167 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
4169 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
4171 lockStatus
= lockstatus(&VTOH( ap
->a_vp
)->h_lock
);
4172 //DBG_VOP_LOCKS_TEST(E_NONE);
4173 return (lockStatus
);
4178 #% pathconf vp L L L
4181 IN struct vnode *vp;
4183 OUT register_t *retval;
4188 struct vop_pathconf_args
/* {
4194 int retval
= E_NONE
;
4195 DBG_FUNC_NAME("pathconf");
4196 DBG_VOP_LOCKS_DECL(1);
4197 DBG_VOP_PRINT_FUNCNAME();
4198 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);
4200 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_POS
);
4202 DBG_HFS_NODE_CHECK (ap
->a_vp
);
4204 switch (ap
->a_name
) {
4207 if (VTOVCB(ap
->a_vp
)->vcbSigWord
== kHFSPlusSigWord
)
4208 *ap
->a_retval
= HFS_LINK_MAX
;
4216 *ap
->a_retval
= kHFSPlusMaxFileNameBytes
; /* max # of characters x max utf8 representation */
4219 *ap
->a_retval
= PATH_MAX
; /* 1024 */
4221 case _PC_CHOWN_RESTRICTED
:
4227 case _PC_NAME_CHARS_MAX
:
4228 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
4230 case _PC_CASE_SENSITIVE
:
4233 case _PC_CASE_PRESERVING
:
4240 DBG_VOP_LOCKS_TEST(retval
);
4249 * Advisory record locking support
4253 IN struct vnode *vp;
4256 IN struct flock *fl;
4262 struct vop_advlock_args
/* {
4270 register struct hfsnode
*hp
= VTOH(ap
->a_vp
);
4271 register struct flock
*fl
= ap
->a_fl
;
4272 register struct hfslockf
*lock
;
4275 DBG_FUNC_NAME("advlock");
4276 DBG_VOP_LOCKS_DECL(1);
4277 DBG_VOP_PRINT_FUNCNAME();
4278 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP(("\n"));
4279 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_UNLOCKED
, VOPDBG_POS
);
4281 * Avoid the common case of unlocking when inode has no locks.
4283 if (hp
->h_lockf
== (struct hfslockf
*)0) {
4284 if (ap
->a_op
!= F_SETLK
) {
4285 fl
->l_type
= F_UNLCK
;
4290 * Convert the flock structure into a start and end.
4293 switch (fl
->l_whence
) {
4297 * Caller is responsible for adding any necessary offset
4298 * when SEEK_CUR is used.
4300 start
= fl
->l_start
;
4304 start
= HTOFCB(hp
)->fcbEOF
+ fl
->l_start
;
4316 end
= start
+ fl
->l_len
- 1;
4319 * Create the hfslockf structure
4321 MALLOC(lock
, struct hfslockf
*, sizeof *lock
, M_LOCKF
, M_WAITOK
);
4322 lock
->lf_start
= start
;
4324 lock
->lf_id
= ap
->a_id
;
4325 lock
->lf_hfsnode
= hp
;
4326 lock
->lf_type
= fl
->l_type
;
4327 lock
->lf_next
= (struct hfslockf
*)0;
4328 TAILQ_INIT(&lock
->lf_blkhd
);
4329 lock
->lf_flags
= ap
->a_flags
;
4331 * Do the requested operation.
4335 retval
= hfs_setlock(lock
);
4339 retval
= hfs_clearlock(lock
);
4340 FREE(lock
, M_LOCKF
);
4344 retval
= hfs_getlock(lock
, fl
);
4345 FREE(lock
, M_LOCKF
);
4350 _FREE(lock
, M_LOCKF
);
4354 DBG_VOP_LOCKS_TEST(retval
);
4361 * Update the access, modified, and node change times as specified by the
4362 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4363 * used to specify that the node needs to be updated but that the times have
4364 * already been set. The access and modified times are taken from the second
4365 * and third parameters; the node change time is always taken from the current
4366 * time. If waitfor is set, then wait for the disk write of the node to
4371 IN struct vnode *vp;
4372 IN struct timeval *access;
4373 IN struct timeval *modify;
4379 struct vop_update_args
/* {
4381 struct timeval *a_access;
4382 struct timeval *a_modify;
4388 hfsCatalogInfo catInfo
;
4394 DBG_FUNC_NAME("update");
4395 DBG_VOP_LOCKS_DECL(1);
4396 DBG_VOP_PRINT_FUNCNAME();
4397 DBG_VOP_PRINT_VNODE_INFO(ap
->a_vp
);DBG_VOP_CONT(("\n"));
4398 DBG_VOP_LOCKS_INIT(0,ap
->a_vp
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_LOCKED
, VOPDBG_ZERO
);
4400 hp
= VTOH(ap
->a_vp
);
4402 DBG_ASSERT(hp
&& hp
->h_meta
);
4403 DBG_ASSERT(*((int*)&ap
->a_vp
->v_interlock
) == 0);
4405 if ((H_FORKTYPE(hp
) == kSysFile
) ||
4406 (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) ||
4407 (hp
->h_meta
->h_mode
== 0)) {
4408 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4409 DBG_VOP_LOCKS_TEST(0);
4413 if (H_FORKTYPE(hp
) == kSysFile
) {
4414 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4415 DBG_VOP_LOCKS_TEST(0);
4419 if (VTOVFS(ap
->a_vp
)->mnt_flag
& MNT_RDONLY
) {
4420 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4421 DBG_VOP_LOCKS_TEST(0);
4425 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4426 if (HTOFCB(hp
)->fcbFlags
&fcbModifiedMask
)
4427 hp
->h_nodeflags
|= IN_MODIFIED
;
4429 if ((hp
->h_nodeflags
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
)) == 0) {
4430 DBG_VOP_LOCKS_TEST(0);
4434 if (hp
->h_nodeflags
& IN_ACCESS
)
4435 hp
->h_meta
->h_atime
= ap
->a_access
->tv_sec
;
4436 if (hp
->h_nodeflags
& IN_UPDATE
)
4437 hp
->h_meta
->h_mtime
= ap
->a_modify
->tv_sec
;
4438 if (hp
->h_nodeflags
& IN_CHANGE
) {
4439 hp
->h_meta
->h_ctime
= time
.tv_sec
;
4441 * HFS dates that WE set must be adjusted for DST
4443 if ((HTOVCB(hp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
4444 hp
->h_meta
->h_ctime
+= 3600;
4445 hp
->h_meta
->h_mtime
= hp
->h_meta
->h_ctime
;
4450 filename
= H_NAME(hp
);
4453 catInfo
.hint
= H_HINT(hp
);
4457 * Force an update of the indirect node instead of the link
4458 * by using the name and parent of the indirect node.
4460 if (hp
->h_meta
->h_metaflags
& IN_DATANODE
) {
4461 MAKE_INODE_NAME(iNodeName
, hp
->h_meta
->h_indnodeno
);
4462 filename
= iNodeName
;
4463 pid
= VCBTOHFS(vcb
)->hfs_private_metadata_dir
;
4467 INIT_CATALOGDATA(&catInfo
.nodeData
, kCatNameNoCopyName
);
4470 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4471 * the Catalog b-tree may aready be locked by the current thread. So we
4472 * allow recursive locking of the Catalog from within VOP_UPDATE.
4474 /* Lock the Catalog b-tree file */
4475 retval
= hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_EXCLUSIVE
| LK_CANRECURSE
, p
);
4477 DBG_VOP_LOCKS_TEST(retval
);
4481 retval
= hfs_getcatalog(vcb
, pid
, filename
, -1, &catInfo
);
4482 if (retval
!= noErr
) {
4483 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4484 retval
= MacToVFSError(retval
);
4488 H_HINT(hp
) = catInfo
.hint
;
4489 CopyVNodeToCatalogNode (HTOV(hp
), &catInfo
.nodeData
);
4491 retval
= UpdateCatalogNode(vcb
, pid
, filename
, H_HINT(hp
), &catInfo
.nodeData
);
4493 /* unlock the Catalog b-tree file */
4494 (void) hfs_metafilelocking(HTOHFS(hp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
4496 if (retval
!= noErr
) { /* from UpdateCatalogNode() */
4497 retval
= MacToVFSError(retval
);
4501 /* After the updates are finished, clear the flags */
4502 hp
->h_nodeflags
&= ~(IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
);
4503 HTOFCB(hp
)->fcbFlags
&= ~fcbModifiedMask
;
4505 /* Update general data */
4506 if (ap
->a_vp
->v_type
== VDIR
) {
4507 hp
->h_meta
->h_nlink
= 2 + catInfo
.nodeData
.cnd_valence
;
4508 hp
->h_meta
->h_size
= sizeof(rootdots
) +
4509 (catInfo
.nodeData
.cnd_valence
* AVERAGE_HFSDIRENTRY_SIZE
);
4510 if (hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
)
4511 hp
->h_meta
->h_size
< MAX_HFSDIRENTRY_SIZE
;
4513 hp
->h_meta
->h_size
= (off_t
)vcb
->blockSize
*
4514 (off_t
)(catInfo
.nodeData
.cnd_rsrcfork
.totalBlocks
+
4515 catInfo
.nodeData
.cnd_datafork
.totalBlocks
);
4521 CLEAN_CATALOGDATA(&catInfo
.nodeData
);
4523 DBG_VOP_LOCKS_TEST(retval
);
4529 * Initialize the vnode associated with a new hfsnode,
4530 * handle aliased vnodes.
4533 hfs_vinit(mntp
, specops
, fifoops
, vpp
)
4535 int (**specops
)(void *);
4536 int (**fifoops
)(void *);
4540 struct vnode
*vp
, *nvp
;
4544 /* vp->v_type set in CopyCatalogToHFSNode */
4545 switch(vp
->v_type
) {
4549 if ((nvp
= checkalias(vp
, hp
->h_meta
->h_rdev
, mntp
))) {
4551 * Discard unneeded vnode, but save its hfsnode.
4552 * Note that the lock is carried over in the hfsnode
4553 * to the replacement vnode.
4555 nvp
->v_data
= vp
->v_data
;
4557 vp
->v_op
= spec_vnodeop_p
;
4561 * Reinitialize aliased hfsnode.
4573 return (EOPNOTSUPP
);
4578 if (H_FILEID(hp
) == kRootDirID
)
4579 vp
->v_flag
|= VROOT
;
4586 * Allocate a new node
4588 * Upon leaving, namei buffer must be freed.
4592 hfs_makenode(mode
, rawdev
, dvp
, vpp
, cnp
, p
)
4597 struct componentname
*cnp
;
4600 register struct hfsnode
*hp
, *parhp
;
4603 struct hfsCatalogInfo catInfo
;
4607 int hasmetalock
= 0;
4608 u_int32_t tehint
= 0;
4609 DBG_FUNC_NAME("makenode");
4612 vcb
= HTOVCB(parhp
);
4615 if ((mode
& IFMT
) == 0)
4619 if ((cnp
->cn_flags
& HASBUF
) == 0)
4620 panic("hfs_makenode: no name");
4623 /* lock catalog b-tree */
4624 retval
= hfs_metafilelocking(VTOHFS(dvp
),
4625 kHFSCatalogFileID
, LK_EXCLUSIVE
, p
);
4626 if (retval
!= E_NONE
)
4632 * Force Carbon creates to have MacUnicode encoding
4634 if ((parhp
->h_nodeflags
& IN_BYCNID
) && (!ISSET(p
->p_flag
, P_TBE
))) {
4635 tehint
= kTextEncodingMacUnicode
;
4638 /* Create the Catalog B*-Tree entry */
4639 retval
= hfsCreate(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, mode
, tehint
);
4640 if (retval
!= E_NONE
) {
4641 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4645 /* Look up the catalog entry just created: */
4646 INIT_CATALOGDATA(&catInfo
.nodeData
, 0);
4647 catInfo
.hint
= kNoHint
;
4649 retval
= hfs_getcatalog(vcb
, H_FILEID(parhp
), cnp
->cn_nameptr
, cnp
->cn_namelen
, &catInfo
);
4650 if (retval
!= E_NONE
) {
4651 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname
, cnp
->cn_nameptr
, H_NAME(parhp
)));
4655 /* unlock catalog b-tree */
4657 (void) hfs_metafilelocking(VTOHFS(dvp
),
4658 kHFSCatalogFileID
, LK_RELEASE
, p
);
4660 /* hfs plus has additional metadata to initialize */
4661 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4665 if (VTOVFS(dvp
)->mnt_flag
& MNT_UNKNOWNPERMISSIONS
) {
4666 catInfo
.nodeData
.cnd_ownerID
= VTOHFS(dvp
)->hfs_uid
;
4667 catInfo
.nodeData
.cnd_groupID
= VTOHFS(dvp
)->hfs_gid
;
4670 catInfo
.nodeData
.cnd_ownerID
= cnp
->cn_cred
->cr_uid
;
4671 catInfo
.nodeData
.cnd_groupID
= parhp
->h_meta
->h_gid
;
4675 switch (catmode
& IFMT
) {
4677 catInfo
.nodeData
.cnd_ownerID
= parhp
->h_meta
->h_uid
;
4682 /* XXX should we move this to post hfsGet? */
4683 catInfo
.nodeData
.cnd_rawDevice
= rawdev
;
4685 * Don't tag as a special file (BLK or CHR) until *after*
4686 * hfsGet is called. This insures that the checkalias call
4687 * is defered until hfs_mknod completes.
4689 catmode
= (catmode
& ~IFMT
) | IFREG
;
4693 if ((catmode
& ISGID
) && !groupmember(parhp
->h_meta
->h_gid
, cnp
->cn_cred
) &&
4694 suser(cnp
->cn_cred
, NULL
))
4697 if (cnp
->cn_flags
& ISWHITEOUT
)
4703 * The 32-bit pflags field has two bytes of significance which
4704 * are stored separately as admin and owner flags.
4706 * +------------------------------------+
4707 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4708 * +------------------------------------+
4710 catInfo
.nodeData
.cnd_adminFlags
= (pflags
>> 16) & 0x00FF;
4711 catInfo
.nodeData
.cnd_ownerFlags
= pflags
& 0x00FF;
4712 catInfo
.nodeData
.cnd_mode
= catmode
;
4715 /* Create a vnode for the object just created: */
4716 forkType
= (catInfo
.nodeData
.cnd_type
== kCatalogFolderNode
) ? kDirectory
: kDataFork
;
4717 retval
= hfs_vcreate(vcb
, &catInfo
, forkType
, &tvp
);
4719 CLEAN_CATALOGDATA(&catInfo
.nodeData
); /* Should do nothing */
4721 if (retval
) goto bad1
; /* from hfs_vcreate() */
4723 /* flush out pflags, mode, gid, uid and rdev */
4725 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
4727 /* reset mode and v_type in case it was BLK/CHR */
4728 hp
->h_meta
->h_mode
= mode
;
4729 tvp
->v_type
= IFTOVT(mode
);
4730 hp
->h_meta
->h_metaflags
&= ~IN_UNSETACCESS
;
4731 hp
->h_nodeflags
|= IN_ACCESS
| IN_CHANGE
| IN_UPDATE
;
4732 if ((retval
= VOP_UPDATE(tvp
, &tv
, &tv
, 1)))
4736 VTOH(dvp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4737 if ((retval
= VOP_UPDATE(dvp
, &tv
, &tv
, 1)))
4740 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4741 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4744 if (UBCINFOMISSING(tvp
) || UBCINFORECLAIMED(tvp
))
4752 * Write retval occurred trying to update the node
4753 * or the directory so must deallocate the node.
4755 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4760 /* unlock catalog b-tree */
4762 (void) hfs_metafilelocking(VTOHFS(dvp
),
4763 kHFSCatalogFileID
, LK_RELEASE
, p
);
4765 if ((cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
4766 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
4774 #if DBG_VOP_TEST_LOCKS
4776 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4778 void DbgVopTest( int maxSlots
,
4780 VopDbgStoreRec
*VopDbgStore
,
4785 for (index
= 0; index
< maxSlots
; index
++)
4787 if (VopDbgStore
[index
].id
!= index
) {
4788 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname
, VopDbgStore
[index
].id
, index
));
4791 if ((VopDbgStore
[index
].vp
!= NULL
) &&
4792 ((VopDbgStore
[index
].vp
->v_data
==NULL
) || (VTOH(VopDbgStore
[index
].vp
)->h_valid
!= HFS_VNODE_MAGIC
)))
4795 if (VopDbgStore
[index
].vp
!= NULL
)
4796 debug_check_vnode(VopDbgStore
[index
].vp
, 0);
4798 switch (VopDbgStore
[index
].inState
)
4802 /* Do Nothing !!! */
4805 case VOPDBG_UNLOCKED
:
4806 case VOPDBG_LOCKNOTNIL
:
4808 if (VopDbgStore
[index
].vp
== NULL
&& (VopDbgStore
[index
].inState
!= VOPDBG_LOCKNOTNIL
)) {
4809 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4810 } else if (VopDbgStore
[index
].vp
!= NULL
) {
4811 switch (VopDbgStore
[index
].inState
)
4814 case VOPDBG_LOCKNOTNIL
:
4815 if (VopDbgStore
[index
].inValue
== 0)
4817 DBG_ERR (("%s: Entry: not LOCKED:", funcname
));
4818 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4822 case VOPDBG_UNLOCKED
:
4823 if (VopDbgStore
[index
].inValue
!= 0)
4825 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname
));
4826 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4835 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4841 switch (VopDbgStore
[index
].errState
)
4844 /* Do Nothing !!! */
4847 case VOPDBG_UNLOCKED
:
4850 if (VopDbgStore
[index
].vp
== NULL
) {
4851 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname
, index
));
4853 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4854 switch (VopDbgStore
[index
].errState
)
4857 if (VopDbgStore
[index
].outValue
== 0)
4859 DBG_ERR (("%s: Error: not LOCKED:", funcname
));
4860 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4864 case VOPDBG_UNLOCKED
:
4865 if (VopDbgStore
[index
].outValue
!= 0)
4867 DBG_ERR (("%s: Error: not UNLOCKED:", funcname
));
4868 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4873 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4874 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
));
4880 case VOPDBG_LOCKNOTNIL
:
4881 if (VopDbgStore
[index
].vp
!= NULL
) {
4882 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4883 if (VopDbgStore
[index
].outValue
== 0)
4884 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4888 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].errState
));
4893 switch (VopDbgStore
[index
].outState
)
4896 /* Do Nothing !!! */
4899 case VOPDBG_UNLOCKED
:
4901 if (VopDbgStore
[index
].vp
== NULL
) {
4902 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname
, index
));
4904 if (VopDbgStore
[index
].vp
!= NULL
)
4906 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4907 switch (VopDbgStore
[index
].outState
)
4910 if (VopDbgStore
[index
].outValue
== 0)
4912 DBG_ERR (("%s: Out: not LOCKED:", funcname
));
4913 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4917 case VOPDBG_UNLOCKED
:
4918 if (VopDbgStore
[index
].outValue
!= 0)
4920 DBG_ERR (("%s: Out: not UNLOCKED:", funcname
));
4921 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
);
4926 if (VopDbgStore
[index
].outValue
!= VopDbgStore
[index
].inValue
)
4927 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
));
4932 case VOPDBG_LOCKNOTNIL
:
4933 if (VopDbgStore
[index
].vp
!= NULL
) {
4934 if (&VTOH(VopDbgStore
[index
].vp
)->h_lock
== NULL
) {
4935 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname
, (u_int
)VopDbgStore
[index
].vp
));
4938 VopDbgStore
[index
].outValue
= lockstatus(&VTOH(VopDbgStore
[index
].vp
)->h_lock
);
4939 if (VopDbgStore
[index
].outValue
== 0)
4941 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname
));
4942 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore
[index
].vp
); DBG_ERR (("\n"));
4948 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname
, VopDbgStore
[index
].outState
));
4952 VopDbgStore
[index
].id
= -1; /* Invalidate the entry to allow panic-free re-use */
4956 #endif /* DBG_VOP_TEST_LOCKS */
4959 * Wrapper for special device reads
4963 struct vop_read_args
/* {
4967 struct ucred *a_cred;
4974 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
4975 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
4979 * Wrapper for special device writes
4983 struct vop_write_args
/* {
4987 struct ucred *a_cred;
4992 * Set update and change flags.
4994 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
4995 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
4999 * Wrapper for special device close
5001 * Update the times on the hfsnode then do device close.
5005 struct vop_close_args
/* {
5008 struct ucred *a_cred;
5012 struct vnode
*vp
= ap
->a_vp
;
5013 struct hfsnode
*hp
= VTOH(vp
);
5015 simple_lock(&vp
->v_interlock
);
5016 if (ap
->a_vp
->v_usecount
> 1)
5017 HFSTIMES(hp
, &time
, &time
);
5018 simple_unlock(&vp
->v_interlock
);
5019 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
5024 * Wrapper for fifo reads
5028 struct vop_read_args
/* {
5032 struct ucred *a_cred;
5035 extern int (**fifo_vnodeop_p
)(void *);
5040 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_ACCESS
;
5041 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_read
), ap
));
5045 * Wrapper for fifo writes
5049 struct vop_write_args
/* {
5053 struct ucred *a_cred;
5056 extern int (**fifo_vnodeop_p
)(void *);
5059 * Set update and change flags.
5061 VTOH(ap
->a_vp
)->h_nodeflags
|= IN_CHANGE
| IN_UPDATE
;
5062 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_write
), ap
));
5066 * Wrapper for fifo close
5068 * Update the times on the hfsnode then do device close.
5072 struct vop_close_args
/* {
5075 struct ucred *a_cred;
5079 extern int (**fifo_vnodeop_p
)(void *);
5080 struct vnode
*vp
= ap
->a_vp
;
5081 struct hfsnode
*hp
= VTOH(vp
);
5083 simple_lock(&vp
->v_interlock
);
5084 if (ap
->a_vp
->v_usecount
> 1)
5085 HFSTIMES(hp
, &time
, &time
);
5086 simple_unlock(&vp
->v_interlock
);
5087 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vop_close
), ap
));
5092 /*****************************************************************************
5096 *****************************************************************************/
5098 #define VOPFUNC int (*)(void *)
5100 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
5101 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5102 { &vop_lookup_desc
, (VOPFUNC
)hfs_cache_lookup
}, /* lookup */
5103 { &vop_create_desc
, (VOPFUNC
)hfs_create
}, /* create */
5104 { &vop_mknod_desc
, (VOPFUNC
)hfs_mknod
}, /* mknod */
5105 { &vop_open_desc
, (VOPFUNC
)hfs_open
}, /* open */
5106 { &vop_close_desc
, (VOPFUNC
)hfs_close
}, /* close */
5107 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5108 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5109 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5110 { &vop_read_desc
, (VOPFUNC
)hfs_read
}, /* read */
5111 { &vop_write_desc
, (VOPFUNC
)hfs_write
}, /* write */
5112 { &vop_ioctl_desc
, (VOPFUNC
)hfs_ioctl
}, /* ioctl */
5113 { &vop_select_desc
, (VOPFUNC
)hfs_select
}, /* select */
5114 { &vop_exchange_desc
, (VOPFUNC
)hfs_exchange
}, /* exchange */
5115 { &vop_mmap_desc
, (VOPFUNC
)hfs_mmap
}, /* mmap */
5116 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5117 { &vop_seek_desc
, (VOPFUNC
)hfs_seek
}, /* seek */
5118 { &vop_remove_desc
, (VOPFUNC
)hfs_remove
}, /* remove */
5120 { &vop_link_desc
, (VOPFUNC
)hfs_link
}, /* link */
5122 { &vop_link_desc
, (VOPFUNC
)err_link
}, /* link (NOT SUPPORTED) */
5124 { &vop_rename_desc
, (VOPFUNC
)hfs_rename
}, /* rename */
5125 { &vop_mkdir_desc
, (VOPFUNC
)hfs_mkdir
}, /* mkdir */
5126 { &vop_rmdir_desc
, (VOPFUNC
)hfs_rmdir
}, /* rmdir */
5127 { &vop_mkcomplex_desc
, (VOPFUNC
)hfs_mkcomplex
}, /* mkcomplex */
5128 { &vop_getattrlist_desc
, (VOPFUNC
)hfs_getattrlist
}, /* getattrlist */
5129 { &vop_setattrlist_desc
, (VOPFUNC
)hfs_setattrlist
}, /* setattrlist */
5130 { &vop_symlink_desc
, (VOPFUNC
)hfs_symlink
}, /* symlink */
5131 { &vop_readdir_desc
, (VOPFUNC
)hfs_readdir
}, /* readdir */
5132 { &vop_readdirattr_desc
, (VOPFUNC
)hfs_readdirattr
}, /* readdirattr */
5133 { &vop_readlink_desc
, (VOPFUNC
)hfs_readlink
}, /* readlink */
5134 { &vop_abortop_desc
, (VOPFUNC
)hfs_abortop
}, /* abortop */
5135 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5136 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5137 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5138 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5139 { &vop_bmap_desc
, (VOPFUNC
)hfs_bmap
}, /* bmap */
5140 { &vop_strategy_desc
, (VOPFUNC
)hfs_strategy
}, /* strategy */
5141 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5142 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5143 { &vop_pathconf_desc
, (VOPFUNC
)hfs_pathconf
}, /* pathconf */
5144 { &vop_advlock_desc
, (VOPFUNC
)hfs_advlock
}, /* advlock */
5145 { &vop_reallocblks_desc
, (VOPFUNC
)hfs_reallocblks
}, /* reallocblks */
5146 { &vop_truncate_desc
, (VOPFUNC
)hfs_truncate
}, /* truncate */
5147 { &vop_allocate_desc
, (VOPFUNC
)hfs_allocate
}, /* allocate */
5148 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5149 { &vop_searchfs_desc
, (VOPFUNC
)hfs_search
}, /* search fs */
5150 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
}, /* bwrite */
5151 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* pagein */
5152 { &vop_pageout_desc
,(VOPFUNC
) hfs_pageout
}, /* pageout */
5153 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5154 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5155 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5156 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5157 { NULL
, (VOPFUNC
)NULL
}
5160 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
5161 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
5163 int (**hfs_specop_p
)(void *);
5164 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
5165 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5166 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
5167 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
5168 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
5169 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
5170 { &vop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
5171 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5172 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5173 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5174 { &vop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
5175 { &vop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
5176 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
5177 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
5178 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
5179 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
5180 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
5181 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5182 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
5183 { &vop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
5184 { &vop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
5185 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
5186 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
5187 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
5188 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
5189 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
5190 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
5191 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
5192 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5193 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5194 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5195 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5196 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
5197 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
5198 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5199 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5200 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
5201 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
5202 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
5203 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
5204 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
5205 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5206 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
5207 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5208 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5209 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
5210 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5211 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5212 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5213 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5214 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5215 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5217 struct vnodeopv_desc hfs_specop_opv_desc
=
5218 { &hfs_specop_p
, hfs_specop_entries
};
5221 int (**hfs_fifoop_p
)(void *);
5222 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
5223 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
5224 { &vop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
5225 { &vop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
5226 { &vop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
5227 { &vop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
5228 { &vop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
5229 { &vop_access_desc
, (VOPFUNC
)hfs_access
}, /* access */
5230 { &vop_getattr_desc
, (VOPFUNC
)hfs_getattr
}, /* getattr */
5231 { &vop_setattr_desc
, (VOPFUNC
)hfs_setattr
}, /* setattr */
5232 { &vop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
5233 { &vop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
5234 { &vop_lease_desc
, (VOPFUNC
)fifo_lease_check
}, /* lease */
5235 { &vop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
5236 { &vop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
5237 { &vop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
5238 { &vop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
5239 { &vop_fsync_desc
, (VOPFUNC
)hfs_fsync
}, /* fsync */
5240 { &vop_seek_desc
, (VOPFUNC
)fifo_seek
}, /* seek */
5241 { &vop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
5242 { &vop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
5243 { &vop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
5244 { &vop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
5245 { &vop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
5246 { &vop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
5247 { &vop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
5248 { &vop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
5249 { &vop_abortop_desc
, (VOPFUNC
)fifo_abortop
}, /* abortop */
5250 { &vop_inactive_desc
, (VOPFUNC
)hfs_inactive
}, /* inactive */
5251 { &vop_reclaim_desc
, (VOPFUNC
)hfs_reclaim
}, /* reclaim */
5252 { &vop_lock_desc
, (VOPFUNC
)hfs_lock
}, /* lock */
5253 { &vop_unlock_desc
, (VOPFUNC
)hfs_unlock
}, /* unlock */
5254 { &vop_bmap_desc
, (VOPFUNC
)fifo_bmap
}, /* bmap */
5255 { &vop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
5256 { &vop_print_desc
, (VOPFUNC
)hfs_print
}, /* print */
5257 { &vop_islocked_desc
, (VOPFUNC
)hfs_islocked
}, /* islocked */
5258 { &vop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
5259 { &vop_advlock_desc
, (VOPFUNC
)fifo_advlock
}, /* advlock */
5260 { &vop_blkatoff_desc
, (VOPFUNC
)fifo_blkatoff
}, /* blkatoff */
5261 { &vop_valloc_desc
, (VOPFUNC
)fifo_valloc
}, /* valloc */
5262 { &vop_reallocblks_desc
, (VOPFUNC
)fifo_reallocblks
}, /* reallocblks */
5263 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
5264 { &vop_truncate_desc
, (VOPFUNC
)fifo_truncate
}, /* truncate */
5265 { &vop_update_desc
, (VOPFUNC
)hfs_update
}, /* update */
5266 { &vop_bwrite_desc
, (VOPFUNC
)hfs_bwrite
},
5267 { &vop_pagein_desc
, (VOPFUNC
)hfs_pagein
}, /* Pagein */
5268 { &vop_pageout_desc
, (VOPFUNC
)hfs_pageout
}, /* Pageout */
5269 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
5270 { &vop_blktooff_desc
, (VOPFUNC
)hfs_blktooff
}, /* blktooff */
5271 { &vop_offtoblk_desc
, (VOPFUNC
)hfs_offtoblk
}, /* offtoblk */
5272 { &vop_cmap_desc
, (VOPFUNC
)hfs_cmap
}, /* cmap */
5273 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
5275 struct vnodeopv_desc hfs_fifoop_opv_desc
=
5276 { &hfs_fifoop_p
, hfs_fifoop_entries
};