]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnodeops.c
e451aaab234909ec907367aebeaeb24cde56d35e
[apple/xnu.git] / bsd / hfs / hfs_vnodeops.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* @(#)hfs_vnodeops.c 3.0
23 *
24 * (c) 1997-1999 Apple Computer, Inc. All Rights Reserved
25 * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved
26 *
27 *
28 * hfs_vnodeops.c -- vnode layer for loadable Macintosh file system
29 *
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).
127 */
128
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>
134 #include <sys/buf.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>
140 #include <sys/ubc.h>
141 #include <sys/utfconv.h>
142 #include <miscfs/specfs/specdev.h>
143 #include <miscfs/fifofs/fifo.h>
144
145 #include <machine/spl.h>
146
147 #include <sys/kdebug.h>
148
149 #include "hfs.h"
150 #include "hfs_lockf.h"
151 #include "hfs_dbg.h"
152 #include "hfs_mount.h"
153
154 #include "hfscommon/headers/CatalogPrivate.h"
155 #include "hfscommon/headers/BTreesInternal.h"
156 #include "hfscommon/headers/FileMgrInternal.h"
157 #include "hfscommon/headers/HFSUnicodeWrappers.h"
158
159 #define OWNERSHIP_ONLY_ATTRS (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS)
160
161 #define MAKE_DELETED_NAME(NAME,FID) \
162 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
163
164
165 extern uid_t console_user;
166
167 /* Global vfs data structures for hfs */
168 int (**hfs_vnodeop_p)(void *);
169
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);
175
176 extern OSErr PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op);
177
178 extern void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm);
179
180 extern groupmember(gid_t gid, struct ucred *cred);
181
182 static int hfs_makenode( int mode,
183 dev_t rawdev, struct vnode *dvp, struct vnode **vpp,
184 struct componentname *cnp, struct proc *p);
185
186 static void hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name);
187
188 static int hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags);
189
190 static int hfs_chown( struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p);
191 static int hfs_chmod( struct vnode *vp, int mode, struct ucred *cred, struct proc *p);
192 static int hfs_chflags( struct vnode *vp, u_long flags, struct ucred *cred, struct proc *p);
193
194
195 int hfs_cache_lookup(); /* in hfs_lookup.c */
196 int hfs_lookup(); /* in hfs_lookup.c */
197 int hfs_read(); /* in hfs_readwrite.c */
198 int hfs_write(); /* in hfs_readwrite.c */
199 int hfs_ioctl(); /* in hfs_readwrite.c */
200 int hfs_select(); /* in hfs_readwrite.c */
201 int hfs_mmap(); /* in hfs_readwrite.c */
202 int hfs_seek(); /* in hfs_readwrite.c */
203 int hfs_bmap(); /* in hfs_readwrite.c */
204 int hfs_strategy(); /* in hfs_readwrite.c */
205 int hfs_reallocblks(); /* in hfs_readwrite.c */
206 int hfs_truncate(); /* in hfs_readwrite.c */
207 int hfs_allocate(); /* in hfs_readwrite.c */
208 int hfs_pagein(); /* in hfs_readwrite.c */
209 int hfs_pageout(); /* in hfs_readwrite.c */
210 int hfs_search(); /* in hfs_search.c */
211 int hfs_bwrite(); /* in hfs_readwrite.c */
212 int hfs_link(); /* in hfs_link.c */
213 int hfs_blktooff(); /* in hfs_readwrite.c */
214 int hfs_offtoblk(); /* in hfs_readwrite.c */
215 int hfs_cmap(); /* in hfs_readwrite.c */
216
217 /*****************************************************************************
218 *
219 * Operations on vnodes
220 *
221 *****************************************************************************/
222
223 /*
224 * Create a regular file
225 #% create dvp L U U
226 #% create vpp - L -
227 #
228 vop_create {
229 IN WILLRELE struct vnode *dvp;
230 OUT struct vnode **vpp;
231 IN struct componentname *cnp;
232 IN struct vattr *vap;
233
234 We are responsible for freeing the namei buffer,
235 it is done in hfs_makenode()
236 */
237
238 static int
239 hfs_create(ap)
240 struct vop_create_args /* {
241 struct vnode *a_dvp;
242 struct vnode **a_vpp;
243 struct componentname *a_cnp;
244 struct vattr *a_vap;
245 } */ *ap;
246 {
247 struct proc *p = current_proc();
248 int retval;
249 int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
250 DBG_FUNC_NAME("create");
251 DBG_VOP_LOCKS_DECL(2);
252 DBG_VOP_PRINT_FUNCNAME();
253 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
254 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);
255
256 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
257 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
258 DBG_VOP_CONT(("\tva_type %d va_mode 0x%x\n",
259 ap->a_vap->va_type, ap->a_vap->va_mode));
260
261 #if HFS_DIAGNOSTIC
262 DBG_HFS_NODE_CHECK(ap->a_dvp);
263 DBG_ASSERT(ap->a_dvp->v_type == VDIR);
264 if(ap->a_vap == NULL) {
265 panic("NULL attr on create");
266 }
267
268 switch(ap->a_vap->va_type) {
269 case VDIR:
270 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
271 vput(ap->a_dvp);
272 DBG_VOP_LOCKS_TEST(EISDIR);
273 return (EISDIR); /* use hfs_mkdir instead */
274 case VREG:
275 case VLNK:
276 break;
277 default:
278 DBG_ERR(("%s: INVALID va_type: %d, %s, %s\n", funcname, ap->a_vap->va_type, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
279 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
280 vput(ap->a_dvp);
281 DBG_VOP_LOCKS_TEST(EINVAL);
282 return (EINVAL);
283 }
284 // if(ap->a_vap->va_mode & (VSUID | VSGID | VSVTX)) {
285 // DBG_ERR(("%s: INVALID va_mode (%o): %s, %s\n", funcname, ap->a_vap->va_mode, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
286 // DBG_VOP_LOCKS_TEST(EINVAL);
287 // VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
288 // vput(ap->a_dvp);
289 // return (EINVAL); /* Can't do these */
290 // };
291 #endif
292
293 /* Create the vnode */
294 retval = hfs_makenode(mode, 0, ap->a_dvp, ap->a_vpp, ap->a_cnp, p);
295 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
296
297 if (retval != E_NONE) {
298 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
299 }
300 DBG_VOP_LOCKS_TEST(retval);
301 return (retval);
302 }
303
304
305 /*
306 * Mknod vnode call
307
308 #% mknod dvp L U U
309 #% mknod vpp - X -
310 #
311 vop_mknod {
312 IN WILLRELE struct vnode *dvp;
313 OUT WILLRELE struct vnode **vpp;
314 IN struct componentname *cnp;
315 IN struct vattr *vap;
316 */
317 /* ARGSUSED */
318
319 static int
320 hfs_mknod(ap)
321 struct vop_mknod_args /* {
322 struct vnode *a_dvp;
323 struct vnode **a_vpp;
324 struct componentname *a_cnp;
325 struct vattr *a_vap;
326 } */ *ap;
327 {
328 struct vattr *vap = ap->a_vap;
329 struct vnode **vpp = ap->a_vpp;
330 struct proc *p = current_proc();
331 dev_t rawdev = 0;
332 int error;
333
334 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
335 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
336 vput(ap->a_dvp);
337 return (EOPNOTSUPP);
338 }
339
340 if (vap->va_rdev != VNOVAL) {
341 /*
342 * Want to be able to use this to make badblock
343 * inodes, so don't truncate the dev number.
344 */
345 rawdev = vap->va_rdev;
346 }
347
348 /* Create the vnode */
349 error = hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
350 rawdev, ap->a_dvp, vpp, ap->a_cnp, p);
351
352 if (error != E_NONE) {
353 return (error);
354 }
355
356 /*
357 * Remove inode so that it will be reloaded by lookup and
358 * checked to see if it is an alias of an existing vnode.
359 * Note: unlike UFS, we don't bash v_type here.
360 */
361 vput(*vpp);
362 vgone(*vpp);
363 *vpp = 0;
364 return (0);
365 }
366
367
368 /*
369 * mkcomplex vnode call
370 *
371
372 #% mkcomplex dvp L U U
373 #% mkcomplex vpp - L -
374 #
375 vop_mkcomplex {
376 IN WILLRELE struct vnode *dvp;
377 OUT struct vnode **vpp;
378 IN struct componentname *cnp;
379 IN struct vattr *vap;
380 IN u_long type;
381 }
382
383 */
384
385 static int
386 hfs_mkcomplex(ap)
387 struct vop_mkcomplex_args /* {
388 struct vnode *a_dvp;
389 struct vnode **a_vpp;
390 struct componentname *a_cnp;
391 struct vattr *a_vap;
392 u_long a_type;
393 } */ *ap;
394 {
395 int retval = E_NONE;
396 DBG_FUNC_NAME("make_complex");
397 DBG_VOP_LOCKS_DECL(2);
398 DBG_VOP_PRINT_FUNCNAME();
399 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
400 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
401
402 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
403 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
404
405 retval = VOP_CREATE(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);
406
407 DBG_VOP_LOCKS_TEST(retval);
408 return retval;
409 }
410
411
412 /*
413 * Open called.
414 #% open vp L L L
415 #
416 vop_open {
417 IN struct vnode *vp;
418 IN int mode;
419 IN struct ucred *cred;
420 IN struct proc *p;
421 */
422
423
424 static int
425 hfs_open(ap)
426 struct vop_open_args /* {
427 struct vnode *a_vp;
428 int a_mode;
429 struct ucred *a_cred;
430 struct proc *a_p;
431 } */ *ap;
432 {
433 struct hfsnode *hp = VTOH(ap->a_vp);
434 int retval = E_NONE;
435 DBG_FUNC_NAME("open");
436 DBG_VOP_LOCKS_DECL(1);
437 DBG_VOP_PRINT_FUNCNAME();
438 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
439 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
440
441 if (ap->a_vp->v_type == VREG) /* Only files */
442 {
443 /*
444 * Files marked append-only must be opened for appending.
445 */
446 if ((hp->h_meta->h_pflags & APPEND) &&
447 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
448 retval = EPERM;
449 }
450
451
452 DBG_VOP_LOCKS_TEST(retval);
453 return (retval);
454 }
455
456 /*
457 * Close called.
458 *
459 * Update the times on the hfsnode.
460 #% close vp U U U
461 #
462 vop_close {
463 IN struct vnode *vp;
464 IN int fflag;
465 IN struct ucred *cred;
466 IN struct proc *p;
467 */
468
469
470 static int
471 hfs_close(ap)
472 struct vop_close_args /* {
473 struct vnode *a_vp;
474 int a_fflag;
475 struct ucred *a_cred;
476 struct proc *a_p;
477 } */ *ap;
478 {
479 register struct vnode *vp = ap->a_vp;
480 struct hfsnode *hp = VTOH(vp);
481 struct proc *p = ap->a_p;
482 FCB *fcb;
483 struct timeval tv;
484 off_t leof;
485 u_long blks, blocksize;
486 int retval = E_NONE;
487
488 DBG_FUNC_NAME("close");
489 DBG_VOP_LOCKS_DECL(1);
490 DBG_VOP_PRINT_FUNCNAME();
491 DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
492 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
493
494 simple_lock(&vp->v_interlock);
495 if (vp->v_usecount > (UBCINFOEXISTS(vp) ? 2 : 1)) {
496 tv = time;
497 HFSTIMES(hp, &tv, &tv);
498 }
499 simple_unlock(&vp->v_interlock);
500
501 /*
502 * VOP_CLOSE can be called with vp locked (from vclean).
503 * We check for this case using VOP_ISLOCKED and bail.
504 *
505 * also, ignore complex nodes; there's no data associated with them.
506 */
507 if (H_FORKTYPE(hp) == kDirectory || VOP_ISLOCKED(vp)) {
508 DBG_VOP_LOCKS_TEST(E_NONE);
509 return E_NONE;
510 };
511
512 fcb = HTOFCB(hp);
513 leof = fcb->fcbEOF;
514
515 if (leof != 0) {
516 enum vtype our_type = vp->v_type;
517 u_long our_id = vp->v_id;
518
519 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
520 /*
521 * Since we can contact switch in vn_lock our vnode
522 * could get recycled (eg umount -f). Double check
523 * that its still ours.
524 */
525 if (vp->v_type != our_type || vp->v_id != our_id) {
526 VOP_UNLOCK(vp, 0, p);
527 DBG_VOP_LOCKS_TEST(E_NONE);
528 return(E_NONE);
529 }
530
531 blocksize = HTOVCB(hp)->blockSize;
532 blks = leof / blocksize;
533 if (((off_t)blks * (off_t)blocksize) != leof)
534 blks++;
535
536 /*
537 * Shrink the peof to the smallest size neccessary to contain the leof.
538 */
539 if (((off_t)blks * (off_t)blocksize) < fcb->fcbPLen) {
540 retval = VOP_TRUNCATE(vp, leof, IO_NDELAY, ap->a_cred, p);
541 }
542 cluster_push(vp);
543 VOP_UNLOCK(vp, 0, p);
544 }
545
546 DBG_VOP_LOCKS_TEST(retval);
547 return (retval);
548 }
549
550 /*
551 #% access vp L L L
552 #
553 vop_access {
554 IN struct vnode *vp;
555 IN int mode;
556 IN struct ucred *cred;
557 IN struct proc *p;
558
559 */
560
561 static int
562 hfs_access(ap)
563 struct vop_access_args /* {
564 struct vnode *a_vp;
565 int a_mode;
566 struct ucred *a_cred;
567 struct proc *a_p;
568 } */ *ap;
569 {
570 struct vnode *vp = ap->a_vp;
571 struct ucred *cred = ap->a_cred;
572 struct hfsnode *hp = VTOH(vp);
573 ExtendedVCB *vcb = HTOVCB(hp);
574 register gid_t *gp;
575 mode_t mask, mode;
576 Boolean isHFSPlus;
577 int retval = E_NONE;
578 int i;
579 DBG_FUNC_NAME("access");
580 DBG_VOP_LOCKS_DECL(1);
581 // DBG_VOP_PRINT_FUNCNAME();
582 // DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
583
584 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
585
586 mode = ap->a_mode;
587 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord );
588
589 /*
590 * Disallow write attempts on read-only file systems;
591 * unless the file is a socket, fifo, or a block or
592 * character device resident on the file system.
593 */
594 if (mode & VWRITE) {
595 switch (vp->v_type) {
596 case VDIR:
597 case VLNK:
598 case VREG:
599 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
600 return (EROFS);
601 break;
602 default:
603 break;
604 }
605 }
606
607 /* If immutable bit set, nobody gets to write it. */
608 if ((mode & VWRITE) && (hp->h_meta->h_pflags & IMMUTABLE))
609 return (EPERM);
610
611 /* Otherwise, user id 0 always gets access. */
612 if (ap->a_cred->cr_uid == 0) {
613 retval = 0;
614 goto Exit;
615 };
616
617 mask = 0;
618
619 /* Otherwise, check the owner. */
620 if (hfs_owner_rights(vp, cred, ap->a_p, false) == 0) {
621 if (mode & VEXEC)
622 mask |= S_IXUSR;
623 if (mode & VREAD)
624 mask |= S_IRUSR;
625 if (mode & VWRITE)
626 mask |= S_IWUSR;
627 retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
628 goto Exit;
629 }
630
631 /* Otherwise, check the groups. */
632 if (! (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
633 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
634 if (hp->h_meta->h_gid == *gp) {
635 if (mode & VEXEC)
636 mask |= S_IXGRP;
637 if (mode & VREAD)
638 mask |= S_IRGRP;
639 if (mode & VWRITE)
640 mask |= S_IWGRP;
641 retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
642 goto Exit;
643 }
644 };
645 };
646
647 /* Otherwise, check everyone else. */
648 if (mode & VEXEC)
649 mask |= S_IXOTH;
650 if (mode & VREAD)
651 mask |= S_IROTH;
652 if (mode & VWRITE)
653 mask |= S_IWOTH;
654 retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
655
656 Exit:
657 DBG_VOP_LOCKS_TEST(retval);
658 return (retval);
659 }
660
661
662
663 /*
664 #% getattr vp = = =
665 #
666 vop_getattr {
667 IN struct vnode *vp;
668 IN struct vattr *vap;
669 IN struct ucred *cred;
670 IN struct proc *p;
671
672 */
673
674
675 /* ARGSUSED */
676 static int
677 hfs_getattr(ap)
678 struct vop_getattr_args /* {
679 struct vnode *a_vp;
680 struct vattr *a_vap;
681 struct ucred *a_cred;
682 struct proc *a_p;
683 } */ *ap;
684 {
685 register struct vnode *vp = ap->a_vp;
686 register struct hfsnode *hp = VTOH(vp);
687 register struct vattr *vap = ap->a_vap;
688 struct timeval tv;
689 DBG_FUNC_NAME("getattr");
690 DBG_VOP_LOCKS_DECL(1);
691 DBG_VOP_PRINT_FUNCNAME();
692 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
693
694 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
695
696 DBG_HFS_NODE_CHECK(ap->a_vp);
697
698 tv = time;
699 HFSTIMES(hp, &tv, &tv);
700
701 vap->va_fsid = H_DEV(hp);
702 vap->va_fileid = H_FILEID(hp);
703 vap->va_mode = hp->h_meta->h_mode;
704 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
705 vap->va_uid = (VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid;
706 } else {
707 vap->va_uid = (hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid;
708 };
709 vap->va_gid = hp->h_meta->h_gid;
710 if (vp->v_type == VDIR) {
711 vap->va_size = hp->h_meta->h_size;
712 vap->va_bytes = 0;
713 vap->va_rdev = 0;
714 vap->va_nlink = hp->h_meta->h_nlink;
715 /*
716 * account for hidden data nodes directory
717 */
718 if ((H_FILEID(hp) == kRootDirID) &&
719 (VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
720 vap->va_size -= AVERAGE_HFSDIRENTRY_SIZE;
721 vap->va_nlink--;
722 }
723 }
724 else {
725 vap->va_size = hp->fcbEOF;
726 vap->va_bytes = hp->h_meta->h_size;
727
728 if (vp->v_type == VBLK || vp->v_type == VCHR)
729 vap->va_rdev = hp->h_meta->h_rdev;
730 else
731 vap->va_rdev = 0;
732
733 if (hp->h_meta->h_metaflags & IN_DELETED)
734 vap->va_nlink = 0;
735 #if HFS_HARDLINKS
736 else if ((hp->h_meta->h_metaflags & IN_DATANODE) &&
737 (hp->h_meta->h_nlink > 0))
738 vap->va_nlink = hp->h_meta->h_nlink;
739 #endif
740 else
741 vap->va_nlink = 1;
742
743 }
744
745 vap->va_atime.tv_nsec = 0;
746 vap->va_atime.tv_sec = hp->h_meta->h_atime;
747 vap->va_mtime.tv_nsec = 0;
748 vap->va_mtime.tv_sec = hp->h_meta->h_mtime;
749 vap->va_ctime.tv_nsec = 0;
750 vap->va_ctime.tv_sec = hp->h_meta->h_ctime;
751 vap->va_flags = hp->h_meta->h_pflags;
752 vap->va_gen = 0;
753 /* this doesn't belong here */
754 if (vp->v_type == VBLK)
755 vap->va_blocksize = BLKDEV_IOSIZE;
756 else if (vp->v_type == VCHR)
757 vap->va_blocksize = MAXPHYSIO;
758 else
759 vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
760 vap->va_type = vp->v_type;
761 vap->va_filerev = 0;
762
763 DBG_VOP_LOCKS_TEST(E_NONE);
764 return (E_NONE);
765 }
766
767 /*
768 * Set attribute vnode op. called from several syscalls
769 #% setattr vp L L L
770 #
771 vop_setattr {
772 IN struct vnode *vp;
773 IN struct vattr *vap;
774 IN struct ucred *cred;
775 IN struct proc *p;
776
777 */
778
779 static int
780 hfs_setattr(ap)
781 struct vop_setattr_args /* {
782 struct vnode *a_vp;
783 struct vattr *a_vap;
784 struct ucred *a_cred;
785 struct proc *a_p;
786 } */ *ap;
787 {
788 struct vnode *vp = ap->a_vp;
789 struct hfsnode *hp = VTOH(vp);
790 struct vattr *vap = ap->a_vap;
791 struct ucred *cred = ap->a_cred;
792 struct proc *p = ap->a_p;
793 struct timeval atimeval, mtimeval;
794 int retval;
795 DBG_FUNC_NAME("setattr");
796 DBG_VOP_LOCKS_DECL(1);
797 DBG_VOP_PRINT_FUNCNAME();
798 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
799 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
800 WRITE_CK(vp, funcname);
801 DBG_HFS_NODE_CHECK(ap->a_vp);
802
803 /*
804 * Check for unsettable attributes.
805 */
806 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
807 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
808 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
809 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
810 retval = EINVAL;
811 goto ErrorExit;
812 }
813
814 if (vap->va_flags != VNOVAL) {
815 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
816 retval = EROFS;
817 goto ErrorExit;
818 };
819 if ((retval = hfs_chflags(vp, vap->va_flags, cred, p))) {
820 goto ErrorExit;
821 };
822 if (vap->va_flags & (IMMUTABLE | APPEND)) {
823 retval = 0;
824 goto ErrorExit;
825 };
826 }
827
828 if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
829 retval = EPERM;
830 goto ErrorExit;
831 };
832 /*
833 * Go through the fields and update iff not VNOVAL.
834 */
835 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
836 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
837 retval = EROFS;
838 goto ErrorExit;
839 };
840 if ((retval = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) {
841 goto ErrorExit;
842 };
843 }
844 if (vap->va_size != VNOVAL) {
845 /*
846 * Disallow write attempts on read-only file systems;
847 * unless the file is a socket, fifo, or a block or
848 * character device resident on the file system.
849 */
850 switch (vp->v_type) {
851 case VDIR:
852 retval = EISDIR;
853 goto ErrorExit;
854 case VLNK:
855 case VREG:
856 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
857 retval = EROFS;
858 goto ErrorExit;
859 };
860 break;
861 default:
862 break;
863 }
864 if ((retval = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) {
865 goto ErrorExit;
866 };
867 }
868 hp = VTOH(vp);
869 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
870 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
871 retval = EROFS;
872 goto ErrorExit;
873 };
874 if (((retval = hfs_owner_rights(vp, cred, p, true)) != 0) &&
875 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
876 (retval = VOP_ACCESS(vp, VWRITE, cred, p)))) {
877 goto ErrorExit;
878 };
879 if (vap->va_atime.tv_sec != VNOVAL)
880 hp->h_nodeflags |= IN_ACCESS;
881 if (vap->va_mtime.tv_sec != VNOVAL) {
882 hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
883 /*
884 * The utimes system call can reset the modification time
885 * but it doesn't know about the HFS+ create time. So we
886 * need to insure that the creation time is always at least
887 * as old as the modification time.
888 */
889 if (( VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord ) &&
890 ( H_FILEID(hp) != kRootDirID ) &&
891 ( vap->va_mtime.tv_sec < hp->h_meta->h_crtime ))
892 hp->h_meta->h_crtime = vap->va_mtime.tv_sec;
893 }
894 atimeval.tv_sec = vap->va_atime.tv_sec;
895 atimeval.tv_usec = 0;
896 mtimeval.tv_sec = vap->va_mtime.tv_sec;
897 mtimeval.tv_usec = 0;
898 if ((retval = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) {
899 goto ErrorExit;
900 };
901 }
902 retval = 0;
903 if (vap->va_mode != (mode_t)VNOVAL) {
904 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
905 retval = EROFS;
906 goto ErrorExit;
907 };
908 retval = hfs_chmod(vp, (int)vap->va_mode, cred, p);
909 };
910
911 ErrorExit: ;
912
913 DBG_VOP(("hfs_setattr: returning %d...\n", retval));
914 DBG_VOP_LOCKS_TEST(retval);
915 return (retval);
916 }
917
918
919 /*
920
921 #
922 #% getattrlist vp = = =
923 #
924 vop_getattrlist {
925 IN struct vnode *vp;
926 IN struct attrlist *alist;
927 INOUT struct uio *uio;
928 IN struct ucred *cred;
929 IN struct proc *p;
930 };
931
932 */
933
934 static int
935 hfs_getattrlist(ap)
936 struct vop_getattrlist_args /* {
937 struct vnode *a_vp;
938 struct attrlist *a_alist
939 struct uio *a_uio;
940 struct ucred *a_cred;
941 struct proc *a_p;
942 } */ *ap;
943 {
944 struct vnode *vp = ap->a_vp;
945 struct hfsnode *hp = VTOH(vp);
946 struct attrlist *alist = ap->a_alist;
947 int error = 0;
948 struct hfsCatalogInfo catInfo;
949 struct hfsCatalogInfo *catInfoPtr = NULL;
950 struct timeval tv;
951 int fixedblocksize;
952 int attrblocksize;
953 int attrbufsize;
954 void *attrbufptr;
955 void *attrptr;
956 void *varptr;
957 u_int32_t fileID;
958 DBG_FUNC_NAME("getattrlist");
959 DBG_VOP_LOCKS_DECL(1);
960
961 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
962 DBG_HFS_NODE_CHECK(ap->a_vp);
963 DBG_VOP(("%s: Common attr:0x%lx, buff size Ox%lX,\n",funcname, (u_long)alist->commonattr,(u_long)ap->a_uio->uio_resid));
964
965 DBG_ASSERT(ap->a_uio->uio_rw == UIO_READ);
966
967 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
968 ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
969 ((alist->volattr & ~ATTR_VOL_VALIDMASK) != 0) ||
970 ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
971 ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
972 ((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0)) {
973 DBG_ERR(("%s: bad attrlist\n", funcname));
974 DBG_VOP_LOCKS_TEST(EINVAL);
975 return EINVAL;
976 };
977
978 /* Requesting volume information requires setting the ATTR_VOL_INFO bit and
979 volume info requests are mutually exclusive with all other info requests: */
980 if ((alist->volattr != 0) && (((alist->volattr & ATTR_VOL_INFO) == 0) ||
981 (alist->dirattr != 0) || (alist->fileattr != 0) || (alist->forkattr != 0)
982 )) {
983 DBG_ERR(("%s: conflicting information requested\n", funcname));
984 DBG_VOP_LOCKS_TEST(EINVAL);
985 return EINVAL;
986 };
987
988 /* Reject requests for unsupported options for now: */
989 if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
990 (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))) {
991 DBG_ERR(("%s: illegal bits in attlist\n", funcname));
992 DBG_VOP_LOCKS_TEST(EINVAL);
993 return EINVAL;
994 };
995
996 /* Requesting volume information requires root vnode */
997 if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
998 DBG_ERR(("%s: not root vnode\n", funcname));
999 DBG_VOP_LOCKS_TEST(EINVAL);
1000 return EINVAL;
1001 };
1002
1003
1004 /* Update times if needed */
1005 tv = time;
1006 HFSTIMES(hp, &tv, &tv);
1007
1008 /* If a FileID (ATTR_CMN_OBJPERMANENTID) is requested on an HFS volume we must be sure
1009 to create the thread record before returning it:
1010 */
1011 if ((vp->v_type == VREG) &&
1012 (alist->commonattr & ATTR_CMN_OBJPERMANENTID)) {
1013 /* Only HFS-Plus volumes are guaranteed to have a thread record in place already: */
1014 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord) {
1015 /* Create a thread record and return the FileID [which is the file's fileNumber] */
1016 /* lock catalog b-tree */
1017 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
1018 error = hfsCreateFileID(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &fileID);
1019 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1020 if (error) {
1021 DBG_VOP_LOCKS_TEST(error);
1022 DBG_ERR(("hfs_getattrlist: error %d on CreateFileIDRef.\n", error));
1023 return error;
1024 };
1025 DBG_ASSERT(fileID == H_FILEID(hp));
1026 };
1027 };
1028
1029 /* Asking for data fork attributes from the rsrc fork is not supported */
1030 if ((H_FORKTYPE(hp) == kRsrcFork) && (alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK)) {
1031 return (EINVAL);
1032 }
1033
1034 /*
1035 * Avoid unnecessary catalog lookups for volume info which is available directly
1036 * in the VCB and root vnode, or can be synthesized.
1037 */
1038 INIT_CATALOGDATA(&catInfo.nodeData, 0);
1039 catInfo.hint = kNoHint;
1040
1041 if (((alist->volattr == 0) && ((alist->commonattr & HFS_ATTR_CMN_LOOKUPMASK) != 0)) ||
1042 ((alist->dirattr & HFS_ATTR_DIR_LOOKUPMASK) != 0) ||
1043 ((alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK) != 0)) {
1044
1045 /* lock catalog b-tree */
1046 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, ap->a_p);
1047 if (error) goto GetCatalogErr_Exit;
1048
1049 if (alist->volattr != 0) {
1050 /* Look up the root info, regardless of the vnode provided */
1051 error = hfs_getcatalog(VTOVCB(vp), 2, NULL, -1, &catInfo);
1052 } else {
1053 error = hfs_getcatalog(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), -1, &catInfo);
1054 if (error == 0) H_HINT(hp) = catInfo.hint; /* Remember the last valid hint */
1055 };
1056
1057 /* unlock catalog b-tree */
1058 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1059
1060 /*
1061 * If a data fork has an active sibling and we need
1062 * rsrc fork attributes then we need to lock the
1063 * sibling and make a copy of its attributes.
1064 */
1065 if ((hp->h_meta->h_usecount > 1) &&
1066 (H_FORKTYPE(hp) == kDataFork) &&
1067 (alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK)) {
1068 struct vnode *sib_vp = NULL;
1069 struct hfsnode *nhp;
1070 struct proc *p = current_proc();
1071
1072 DBG_ASSERT(hp->h_meta->h_siblinghead.cqh_first &&
1073 (hp->h_meta->h_siblinghead.cqh_first != hp->h_meta->h_siblinghead.cqh_last));
1074 DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
1075
1076 /* Loop through all siblings, skipping ourselves */
1077 simple_lock(&hp->h_meta->h_siblinglock);
1078 CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
1079 if (nhp == hp) /* skip ourselves */
1080 continue;
1081 sib_vp = HTOV(nhp);
1082 };
1083 simple_unlock(&hp->h_meta->h_siblinglock);
1084
1085 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1086 if (vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
1087 if (VTOH(sib_vp)->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
1088 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1089 CopyVNodeToCatalogNode (sib_vp, &catInfo.nodeData);
1090 };
1091
1092 vput(sib_vp);
1093 }; /* vget() */
1094 }; /* h_use_count > 1 */
1095
1096 /* Update to the in-memory state, if it has been modified...just to make sure */
1097 if (VTOH(vp)->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
1098 /* XXX SER No need to copy the whole thing over, just copy the fork info */
1099 CopyVNodeToCatalogNode (vp, &catInfo.nodeData);
1100 };
1101
1102 /* XXX What if hfs_getcatalog fails...we just continue??? */
1103 catInfoPtr = &catInfo;
1104
1105 };
1106
1107 fixedblocksize = AttributeBlockSize(alist);
1108 attrblocksize = fixedblocksize + (sizeof(u_long)); /* u_long for length longword */
1109 if (alist->commonattr & ATTR_CMN_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1;
1110 if (alist->commonattr & ATTR_CMN_NAMEDATTRLIST) attrblocksize += 0; /* XXX PPD */
1111 if (alist->volattr & ATTR_VOL_MOUNTPOINT) attrblocksize += PATH_MAX;
1112 if (alist->volattr & ATTR_VOL_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1;
1113 if (alist->fileattr & ATTR_FILE_FORKLIST) attrblocksize += 0; /* XXX PPD */
1114
1115 attrbufsize = MIN(ap->a_uio->uio_resid, attrblocksize);
1116 DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
1117 attrblocksize,
1118 fixedblocksize,
1119 attrblocksize - fixedblocksize));
1120 MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
1121 attrptr = attrbufptr;
1122 *((u_long *)attrptr) = 0; /* Set buffer length in case of errors */
1123 ++((u_long *)attrptr); /* Reserve space for length field */
1124 varptr = ((char *)attrptr) + fixedblocksize; /* Point to variable-length storage */
1125 DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int)attrptr, (u_int)varptr));
1126
1127 PackAttributeBlock(alist, vp, catInfoPtr, &attrptr, &varptr);
1128 attrbufsize = MIN(attrbufsize, (u_int)varptr - (u_int)attrbufptr); /* Don't copy out more data than was generated */
1129 *((u_long *)attrbufptr) = attrbufsize; /* Set actual buffer length for return to caller */
1130 DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize, (u_int)ap->a_uio->uio_iov->iov_base));
1131 error = uiomove((caddr_t)attrbufptr, attrbufsize, ap->a_uio);
1132 if (error != E_NONE) {
1133 DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error));
1134 };
1135
1136 FREE(attrbufptr, M_TEMP);
1137
1138
1139 GetCatalogErr_Exit:
1140 CLEAN_CATALOGDATA(&catInfo.nodeData);
1141 DBG_VOP_LOCKS_TEST(error);
1142 return error;
1143 }
1144
1145
1146
1147 /*
1148
1149 #
1150 #% setattrlist vp L L L
1151 #
1152 vop_setattrlist {
1153 IN struct vnode *vp;
1154 IN struct attrlist *alist;
1155 INOUT struct uio *uio;
1156 IN struct ucred *cred;
1157 IN struct proc *p;
1158 };
1159
1160 */
1161
1162 static int
1163 hfs_setattrlist(ap)
1164 struct vop_setattrlist_args /* {
1165 struct vnode *a_vp;
1166 struct attrlist *a_alist
1167 struct uio *a_uio;
1168 struct ucred *a_cred;
1169 struct proc *a_p;
1170 } */ *ap;
1171 {
1172 struct vnode *vp = ap->a_vp;
1173 struct hfsnode *hp = VTOH(vp);
1174 struct attrlist *alist = ap->a_alist;
1175 struct ucred *cred = ap->a_cred;
1176 struct proc *p = ap->a_p;
1177 int error;
1178 struct hfsCatalogInfo catInfo;
1179 int attrblocksize;
1180 void *attrbufptr = NULL;
1181 void *attrptr;
1182 void *varptr = NULL;
1183 uid_t saved_uid;
1184 gid_t saved_gid;
1185 mode_t saved_mode;
1186 u_long saved_flags;
1187 char * filename;
1188 char iNodeName[32];
1189 u_int32_t pid;
1190 int retval = 0;
1191
1192 DBG_FUNC_NAME("setattrlist");
1193 DBG_VOP_LOCKS_DECL(1);
1194
1195 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
1196 DBG_HFS_NODE_CHECK(ap->a_vp);
1197 DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname, (u_int)alist->commonattr,(u_int)ap->a_uio->uio_resid));
1198
1199 DBG_ASSERT(ap->a_uio->uio_rw == UIO_WRITE);
1200
1201 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
1202 ((alist->commonattr & ~ATTR_CMN_SETMASK) != 0) ||
1203 ((alist->volattr & ~ATTR_VOL_SETMASK) != 0) ||
1204 ((alist->dirattr & ~ATTR_DIR_SETMASK) != 0) ||
1205 ((alist->fileattr & ~ATTR_FILE_SETMASK) != 0) ||
1206 ((alist->forkattr & ~ATTR_FORK_SETMASK) != 0)) {
1207 DBG_ERR(("%s: Bad attrlist\n", funcname));
1208 DBG_VOP_LOCKS_TEST(EINVAL);
1209 return EINVAL;
1210 };
1211
1212 if ((alist->volattr != 0) && /* Setting volume info */
1213 (((alist->volattr & ATTR_VOL_INFO) == 0) || /* Not explicitly indicating this or ... */
1214 (alist->commonattr & ~ATTR_CMN_VOLSETMASK))) /* ... setting invalid attributes for volume */
1215 {
1216 DBG_ERR(("%s: Bad attrlist\n", funcname));
1217 DBG_VOP_LOCKS_TEST(EINVAL);
1218 return EINVAL;
1219 };
1220
1221 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
1222 DBG_VOP_LOCKS_TEST(EROFS);
1223 return EROFS;
1224 };
1225
1226 /*
1227 Ownership of the file (in addition to write access, checked below,
1228 is required in one of two classes of calls:
1229
1230 (a) When setting any ownership-requiring attribute other than ATTR_CMN_FLAGS, or
1231 (b) When setting ATTR_CMN_FLAGS on a volume that's not plain HFS (for which no
1232 real per-object ownership information is stored):
1233 */
1234 if ((alist->commonattr & (OWNERSHIP_ONLY_ATTRS & ~ATTR_CMN_FLAGS)) ||
1235 ((alist->commonattr & ATTR_CMN_FLAGS) && (VTOVCB(vp)->vcbSigWord != kHFSSigWord))) {
1236 /* NOTE: The following isn't ENTIRELY complete: even if you're the superuser
1237 you cannot change the flags as long as SF_IMMUTABLE or SF_APPEND is
1238 set and securelevel > 0. This is verified in hfs_chflags which gets
1239 invoked to do the actual flags field change so this check is sufficient
1240 for now.
1241 */
1242 /* Check to see if the user owns the object [or is superuser]: */
1243 if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0) {
1244 DBG_VOP_LOCKS_TEST(retval);
1245 return retval;
1246 };
1247 } else {
1248 DBG_ASSERT(((alist->commonattr & OWNERSHIP_ONLY_ATTRS) == 0) ||
1249 (((alist->commonattr & OWNERSHIP_ONLY_ATTRS) == ATTR_CMN_FLAGS) &&
1250 (VTOVCB(vp)->vcbSigWord == kHFSSigWord)));
1251 /* No ownership access is required: mere write access (checked below) will do... */
1252 };
1253
1254 /* For any other attributes, check to see if the user has write access to
1255 the object in question [unlike VOP_ACCESS, ignore IMMUTABLE here]: */
1256
1257 if ((((alist->commonattr & ~(OWNERSHIP_ONLY_ATTRS)) != 0) ||
1258 (alist->volattr != 0) ||
1259 (alist->dirattr != 0) ||
1260 (alist->fileattr != 0) ||
1261 (alist->forkattr != 0)) &&
1262 ((retval = hfs_write_access(vp, cred, p, false)) != 0)) {
1263 DBG_VOP_LOCKS_TEST(retval);
1264 return retval;
1265 }; /* end of if ownership attr */
1266
1267 /* Allocate the buffer now to minimize the time we might be blocked holding the catalog lock */
1268 attrblocksize = ap->a_uio->uio_resid;
1269 if (attrblocksize < AttributeBlockSize(alist)) {
1270 DBG_ERR(("%s: bad attrblocksize\n", funcname));
1271 DBG_VOP_LOCKS_TEST(EINVAL);
1272 return EINVAL;
1273 };
1274
1275 MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
1276
1277 INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
1278 catInfo.hint = kNoHint;
1279
1280 /* lock catalog b-tree */
1281 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
1282 if (error != E_NONE) {
1283 goto FreeBuffer;
1284 };
1285
1286 filename = H_NAME(hp);
1287 pid = H_DIRID(hp);
1288
1289 #if HFS_HARDLINKS
1290 /*
1291 * Force an update of the indirect node instead of the link
1292 * by using the name and parent of the indirect node.
1293 */
1294 if (hp->h_meta->h_metaflags & IN_DATANODE) {
1295 MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
1296 filename = iNodeName;
1297 pid = VTOHFS(vp)->hfs_private_metadata_dir;
1298 }
1299 #endif
1300
1301
1302 error = hfs_getcatalog(VTOVCB(vp), pid, filename, -1, &catInfo);
1303 if (error != E_NONE) {
1304 DBG_ERR(("%s: Lookup failed on file '%s'\n", funcname, filename));
1305 goto ErrorExit;
1306 };
1307 H_HINT(hp) = catInfo.hint; /* Remember the last valid hint */
1308
1309 error = uiomove((caddr_t)attrbufptr, attrblocksize, ap->a_uio);
1310 if (error) goto ErrorExit;
1311
1312 if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
1313 error = EINVAL;
1314 goto ErrorExit;
1315 };
1316
1317 /*
1318 * If we are going to change the times:
1319 * 1. do we have permission to change the dates?
1320 * 2. Is there another fork? If so then clear any flags associated with the times
1321 */
1322 if (alist->commonattr & (ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME)) {
1323 if (alist->commonattr & (ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME)) {
1324 if ((error = hfs_owner_rights(vp, cred, p, true)) != 0)
1325 goto ErrorExit;
1326 }
1327
1328 /* If there is another fork, clear the flags */
1329 if ((hp->h_meta->h_usecount > 1) && (H_FORKTYPE(hp) == kDataFork)) {
1330 struct vnode *sib_vp = NULL;
1331 struct hfsnode *nhp;
1332
1333 /* Loop through all siblings, skipping ourselves */
1334 simple_lock(&hp->h_meta->h_siblinglock);
1335 CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
1336 if (nhp == hp) /* skip ourselves */
1337 continue;
1338 sib_vp = HTOV(nhp);
1339 }
1340 simple_unlock(&hp->h_meta->h_siblinglock);
1341
1342 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
1343 if (sib_vp && vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
1344 if (VTOH(sib_vp)->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) {
1345 if (alist->commonattr & ATTR_CMN_MODTIME)
1346 VTOH(sib_vp)->h_nodeflags &= ~IN_UPDATE;
1347 if (alist->commonattr & ATTR_CMN_CHGTIME)
1348 VTOH(sib_vp)->h_nodeflags &= ~IN_CHANGE;
1349 if (alist->commonattr & ATTR_CMN_ACCTIME)
1350 VTOH(sib_vp)->h_nodeflags &= ~IN_ACCESS;
1351 }
1352 vput(sib_vp);
1353 } /* vget() */
1354 } /* h_use_count > 1 */
1355 }
1356
1357 /* save these in case hfs_chown() or hfs_chmod() fail */
1358 saved_uid = hp->h_meta->h_uid;
1359 saved_gid = hp->h_meta->h_gid;
1360 saved_mode = hp->h_meta->h_mode;
1361 saved_flags = hp->h_meta->h_pflags;
1362
1363 attrptr = attrbufptr;
1364 UnpackAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
1365
1366 /* if unpacking changed the owner or group then call hfs_chown() */
1367 if (saved_uid != hp->h_meta->h_uid || saved_gid != hp->h_meta->h_gid) {
1368 uid_t uid;
1369 gid_t gid;
1370
1371 uid = hp->h_meta->h_uid;
1372 hp->h_meta->h_uid = saved_uid;
1373 gid = hp->h_meta->h_gid;
1374 hp->h_meta->h_gid = saved_gid;
1375 if ((error = hfs_chown(vp, uid, gid, cred, p)))
1376 goto ErrorExit;
1377 }
1378
1379 /* if unpacking changed the mode then call hfs_chmod() */
1380 if (saved_mode != hp->h_meta->h_mode) {
1381 mode_t mode;
1382
1383 mode = hp->h_meta->h_mode;
1384 hp->h_meta->h_mode = saved_mode;
1385 if ((error = hfs_chmod(vp, mode, cred, p)))
1386 goto ErrorExit;
1387 };
1388
1389 /* if unpacking changed the flags then call hfs_chflags */
1390 if (saved_flags != hp->h_meta->h_pflags) {
1391 u_long flags;
1392
1393 flags = hp->h_meta->h_pflags;
1394 hp->h_meta->h_pflags = saved_flags;
1395 if ((error = hfs_chflags(vp, flags, cred, p)))
1396 goto ErrorExit;
1397 };
1398
1399
1400 /* Update Catalog Tree */
1401 if (alist->volattr == 0) {
1402 error = MacToVFSError( UpdateCatalogNode(HTOVCB(hp), pid, filename, H_HINT(hp), &catInfo.nodeData));
1403 }
1404
1405 /* Volume Rename */
1406 if (alist->volattr & ATTR_VOL_NAME) {
1407 ExtendedVCB *vcb = VTOVCB(vp);
1408 int namelen = strlen(vcb->vcbVN);
1409
1410 if (vcb->vcbVN[0] == 0) {
1411 /*
1412 * Ignore attempts to rename a volume to a zero-length name:
1413 * restore the original name from the metadata.
1414 */
1415 copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
1416 } else {
1417 error = MoveRenameCatalogNode(vcb, kRootParID, H_NAME(hp), H_HINT(hp),
1418 kRootParID, vcb->vcbVN, &H_HINT(hp));
1419 if (error) {
1420 VCB_LOCK(vcb);
1421 copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL); /* Restore the old name in the VCB */
1422 vcb->vcbFlags |= 0xFF00; // Mark the VCB dirty
1423 VCB_UNLOCK(vcb);
1424 goto ErrorExit;
1425 };
1426
1427 hfs_set_metaname(vcb->vcbVN, hp->h_meta, HTOHFS(hp));
1428 hp->h_nodeflags |= IN_CHANGE;
1429
1430 } /* vcb->vcbVN[0] == 0 ... else ... */
1431 } /* alist->volattr & ATTR_VOL_NAME */
1432
1433 ErrorExit:
1434 /* unlock catalog b-tree */
1435 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
1436
1437 CLEAN_CATALOGDATA(&catInfo.nodeData);
1438
1439 FreeBuffer:
1440 if (attrbufptr) FREE(attrbufptr, M_TEMP);
1441
1442 DBG_VOP_LOCKS_TEST(error);
1443 return error;
1444 }
1445
1446 /*
1447 * Change the mode on a file.
1448 * Inode must be locked before calling.
1449 */
1450 static int
1451 hfs_chmod(vp, mode, cred, p)
1452 register struct vnode *vp;
1453 register int mode;
1454 register struct ucred *cred;
1455 struct proc *p;
1456 {
1457 register struct hfsnode *hp = VTOH(vp);
1458 int retval;
1459
1460 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1461 return E_NONE;
1462
1463 #if OVERRIDE_UNKNOWN_PERMISSIONS
1464 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
1465 return E_NONE;
1466 };
1467 #endif
1468
1469 if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0)
1470 return (retval);
1471 if (cred->cr_uid) {
1472 if (vp->v_type != VDIR && (mode & S_ISTXT))
1473 return (EFTYPE);
1474 if (!groupmember(hp->h_meta->h_gid, cred) && (mode & ISGID))
1475 return (EPERM);
1476 }
1477 hp->h_meta->h_mode &= ~ALLPERMS;
1478 hp->h_meta->h_mode |= (mode & ALLPERMS);
1479 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1480 hp->h_nodeflags |= IN_CHANGE;
1481 return (0);
1482 }
1483
1484
1485 static int
1486 hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
1487 {
1488 struct hfsnode *hp = VTOH(vp);
1489 ExtendedVCB *vcb = HTOVCB(hp);
1490 gid_t *gp;
1491 Boolean isHFSPlus;
1492 int retval = E_NONE;
1493 int i;
1494
1495 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord );
1496
1497 /*
1498 * Disallow write attempts on read-only file systems;
1499 * unless the file is a socket, fifo, or a block or
1500 * character device resident on the file system.
1501 */
1502 switch (vp->v_type) {
1503 case VDIR:
1504 case VLNK:
1505 case VREG:
1506 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
1507 return (EROFS);
1508 break;
1509 default:
1510 break;
1511 }
1512
1513 /* If immutable bit set, nobody gets to write it. */
1514 if (considerFlags && (hp->h_meta->h_pflags & IMMUTABLE))
1515 return (EPERM);
1516
1517 /* Otherwise, user id 0 always gets access. */
1518 if (cred->cr_uid == 0) {
1519 retval = 0;
1520 goto Exit;
1521 };
1522
1523 /* Otherwise, check the owner. */
1524 if ((retval = hfs_owner_rights(vp, cred, p, false)) == 0) {
1525 retval = ((hp->h_meta->h_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
1526 goto Exit;
1527 }
1528
1529 /* Otherwise, check the groups. */
1530 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
1531 if (hp->h_meta->h_gid == *gp) {
1532 retval = ((hp->h_meta->h_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
1533 goto Exit;
1534 }
1535
1536 /* Otherwise, check everyone else. */
1537 retval = ((hp->h_meta->h_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
1538
1539 Exit:
1540 return (retval);
1541 }
1542
1543
1544
1545 /*
1546 * Change the flags on a file or directory.
1547 * Inode must be locked before calling.
1548 */
1549 static int
1550 hfs_chflags(vp, flags, cred, p)
1551 register struct vnode *vp;
1552 register u_long flags;
1553 register struct ucred *cred;
1554 struct proc *p;
1555 {
1556 register struct hfsnode *hp = VTOH(vp);
1557 int retval;
1558
1559 if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) {
1560 if ((retval = hfs_write_access(vp, cred, p, false)) != 0) {
1561 return retval;
1562 };
1563 } else if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0) {
1564 return retval;
1565 };
1566
1567 if (cred->cr_uid == 0) {
1568 if ((hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND)) &&
1569 securelevel > 0) {
1570 return EPERM;
1571 };
1572 hp->h_meta->h_pflags = flags;
1573 } else {
1574 if (hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND) ||
1575 (flags & UF_SETTABLE) != flags) {
1576 return EPERM;
1577 };
1578 hp->h_meta->h_pflags &= SF_SETTABLE;
1579 hp->h_meta->h_pflags |= (flags & UF_SETTABLE);
1580 }
1581 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1582 hp->h_nodeflags |= IN_CHANGE;
1583
1584 return 0;
1585 }
1586
1587
1588 /*
1589 * Perform chown operation on hfsnode hp;
1590 * hfsnode must be locked prior to call.
1591 */
1592 static int
1593 hfs_chown(vp, uid, gid, cred, p)
1594 register struct vnode *vp;
1595 uid_t uid;
1596 gid_t gid;
1597 struct ucred *cred;
1598 struct proc *p;
1599 {
1600 register struct hfsnode *hp = VTOH(vp);
1601 uid_t ouid;
1602 gid_t ogid;
1603 int retval = 0;
1604
1605 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1606 return EOPNOTSUPP;
1607
1608 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
1609 return E_NONE;
1610 };
1611
1612 if (uid == (uid_t)VNOVAL)
1613 uid = hp->h_meta->h_uid;
1614 if (gid == (gid_t)VNOVAL)
1615 gid = hp->h_meta->h_gid;
1616 /*
1617 * If we don't own the file, are trying to change the owner
1618 * of the file, or are not a member of the target group,
1619 * the caller must be superuser or the call fails.
1620 */
1621 if ((cred->cr_uid != hp->h_meta->h_uid || uid != hp->h_meta->h_uid ||
1622 (gid != hp->h_meta->h_gid && !groupmember((gid_t)gid, cred))) &&
1623 (retval = suser(cred, &p->p_acflag)))
1624 return (retval);
1625
1626 ogid = hp->h_meta->h_gid;
1627 ouid = hp->h_meta->h_uid;
1628
1629 hp->h_meta->h_gid = gid;
1630 hp->h_meta->h_uid = uid;
1631
1632 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1633 if (ouid != uid || ogid != gid)
1634 hp->h_nodeflags |= IN_CHANGE;
1635 if (ouid != uid && cred->cr_uid != 0)
1636 hp->h_meta->h_mode &= ~ISUID;
1637 if (ogid != gid && cred->cr_uid != 0)
1638 hp->h_meta->h_mode &= ~ISGID;
1639 return (0);
1640 }
1641
1642
1643
1644 /*
1645 #
1646 #% exchange fvp L L L
1647 #% exchange tvp L L L
1648 #
1649 vop_exchange {
1650 IN struct vnode *fvp;
1651 IN struct vnode *tvp;
1652 IN struct ucred *cred;
1653 IN struct proc *p;
1654 };
1655
1656 */
1657 /*
1658 * exchange is a very tricky routine, because we might have to unlock the
1659 * passed in vnode, and then retry locking it and all its siblings, and then
1660 * unlocking them in reverse.
1661 * Also the sibling list lock must be kept during the whole operation to
1662 * make sure nothing changes underneath us.
1663 * Also it depends on behavior of the sibling list and hash, so
1664 * careful if you change anything.
1665 */
1666
1667 static int
1668 hfs_exchange(ap)
1669 struct vop_exchange_args /* {
1670 struct vnode *a_fvp;
1671 struct vnode *a_tvp;
1672 struct ucred *a_cred;
1673 struct proc *a_p;
1674 } */ *ap;
1675 {
1676 struct hfsnode *from_hp, *to_hp, *nhp;
1677 struct hfsnode *fromFirst, *fromSecond, *toFirst, *toSecond;
1678 struct vnode *from_vp, *to_vp;
1679 struct hfsmount *hfsmp;
1680 u_char tmp_name[kHFSPlusMaxFileNameBytes+1]; /* 766 bytes! */
1681 ExtendedVCB *vcb;
1682 u_int32_t fromFileID, toFileID;
1683 u_int32_t fromParID;
1684 u_int32_t tmpLong;
1685 int retval = E_NONE;
1686 DBG_FUNC_NAME("exchange");
1687 DBG_VOP_LOCKS_DECL(2);
1688 DBG_VOP_LOCKS_INIT(0,ap->a_fvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1689 DBG_VOP_LOCKS_INIT(1,ap->a_tvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1690
1691 /* Set up variables and checks */
1692 from_vp = ap->a_fvp;
1693 to_vp = ap->a_tvp;
1694 from_hp = VTOH(from_vp);
1695 to_hp = VTOH(to_vp);
1696 hfsmp = VTOHFS(from_vp);
1697 vcb = HTOVCB(from_hp);
1698 toFileID = H_FILEID(to_hp);
1699 fromFileID = H_FILEID(from_hp);
1700 fromParID = H_DIRID(from_hp);
1701
1702 if (from_vp->v_mount != to_vp->v_mount) {
1703 DBG_VOP_LOCKS_TEST(EXDEV);
1704 return EXDEV;
1705 }
1706
1707 /* Can only exchange file objects */
1708 if (from_vp->v_type != VREG || to_vp->v_type != VREG) {
1709 DBG_VOP_LOCKS_TEST(EINVAL);
1710 return EINVAL;
1711 }
1712
1713 /*
1714 * Lock the siblink list
1715 * Check for multiple forks
1716 * If there are, we would need to:
1717 * 1. Unlock ourselves
1718 * 3. Traverse the list in a forward order...locking all vnodes
1719 * 4. Flush all buffers
1720 * 5. Perform the exchange
1721 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1722 * Notice that the sibling lock is kept during the whole operation. This quarentees
1723 * that no new forks are taken off or put on
1724 */
1725 DBG_ASSERT(H_FORKTYPE(from_hp)==kDataFork && H_FORKTYPE(to_hp)==kDataFork);
1726 fromFirst = fromSecond = toFirst = toSecond = NULL;
1727
1728 if (from_hp->h_meta->h_usecount > 1) {
1729 /*
1730 * This has siblings, so remember the passed-in vnode,
1731 * unlock it if it is not the 'first' sibling,
1732 * and then lock the rest of the vnodes by sibling order.
1733 * Notice that the passed-in vnode is not vrele(), this
1734 * keeps the usecount>0, so it wont go away.
1735 */
1736 simple_lock(&from_hp->h_meta->h_siblinglock);
1737 fromFirst = from_hp->h_meta->h_siblinghead.cqh_first;
1738 fromSecond = fromFirst->h_sibling.cqe_next;
1739 simple_unlock(&from_hp->h_meta->h_siblinglock);
1740
1741 if (fromFirst == from_hp) {
1742 if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1743 fromSecond = NULL; /* its going away */
1744 } else {
1745 VOP_UNLOCK(HTOV(from_hp), 0, ap->a_p);
1746 if (vget(HTOV(fromFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1747 fromFirst = NULL; /* its going away */
1748 if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1749 fromSecond = NULL; /* its going away */
1750 };
1751
1752 } else {
1753 fromFirst = from_hp;
1754 };
1755
1756 if (to_hp->h_meta->h_usecount > 1) {
1757
1758 simple_lock(&to_hp->h_meta->h_siblinglock);
1759 toFirst = to_hp->h_meta->h_siblinghead.cqh_first;
1760 toSecond = toFirst->h_sibling.cqe_next;
1761 simple_unlock(&to_hp->h_meta->h_siblinglock);
1762
1763 if (toFirst == to_hp) {
1764 if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1765 toSecond = NULL; /* its going away */
1766 } else {
1767 VOP_UNLOCK(HTOV(to_hp), 0, ap->a_p);
1768 if (vget(HTOV(toFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1769 toFirst = NULL; /* its going away */
1770 if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1771 toSecond = NULL; /* its going away */
1772 };
1773
1774 } else {
1775 toFirst = to_hp;
1776 };
1777
1778
1779 /* Ignore any errors, we are doing a 'best effort' on flushing */
1780 if (fromFirst)
1781 (void) vinvalbuf(HTOV(fromFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1782 if (fromSecond)
1783 (void) vinvalbuf(HTOV(fromSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1784 if (toFirst)
1785 (void) vinvalbuf(HTOV(toFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1786 if (toSecond)
1787 (void) vinvalbuf(HTOV(toSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1788
1789
1790 /* lock catalog b-tree */
1791 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
1792 if (retval) goto Err_Exit;
1793
1794 /* lock extents b-tree iff there are overflow extents */
1795 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1796 so we neeed the tree to be always locked.
1797 */
1798 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1799 if (retval) goto Err_Exit_Relse;
1800
1801 /* Do the exchange */
1802 retval = MacToVFSError( ExchangeFileIDs(vcb, H_NAME(from_hp), H_NAME(to_hp), H_DIRID(from_hp), H_DIRID(to_hp), H_HINT(from_hp), H_HINT(to_hp) ));
1803
1804 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1805
1806 if (retval != E_NONE) {
1807 DBG_ERR(("/tError trying to exchange: %d\n", retval));
1808 goto Err_Exit_Relse;
1809 }
1810
1811
1812 /* Purge the vnodes from the name */
1813 if (fromFirst)
1814 cache_purge(HTOV(fromFirst));
1815 if (fromSecond)
1816 cache_purge(HTOV(fromSecond));
1817 if (toFirst)
1818 cache_purge(HTOV(toFirst));
1819 if (toSecond)
1820 cache_purge(HTOV(toSecond));
1821
1822 /* Now exchange fileID, parID, name for the vnode itself */
1823 copystr(H_NAME(from_hp), (char*) tmp_name, strlen(H_NAME(from_hp))+1, NULL);
1824 hfs_chid(from_hp, toFileID, H_DIRID(to_hp), H_NAME(to_hp));
1825 hfs_chid(to_hp, fromFileID, fromParID, (char*) tmp_name);
1826
1827 /* copy rest */
1828 tmpLong = HTOFCB(from_hp)->fcbFlags;
1829 HTOFCB(from_hp)->fcbFlags = HTOFCB(to_hp)->fcbFlags;
1830 HTOFCB(to_hp)->fcbFlags = tmpLong;
1831
1832 tmpLong = from_hp->h_meta->h_crtime;
1833 from_hp->h_meta->h_crtime = to_hp->h_meta->h_crtime;
1834 to_hp->h_meta->h_crtime = tmpLong;
1835
1836 tmpLong = from_hp->h_meta->h_butime;
1837 from_hp->h_meta->h_butime = to_hp->h_meta->h_butime;
1838 to_hp->h_meta->h_butime = tmpLong;
1839
1840 tmpLong = from_hp->h_meta->h_atime;
1841 from_hp->h_meta->h_atime = to_hp->h_meta->h_atime;
1842 to_hp->h_meta->h_atime = tmpLong;
1843
1844 tmpLong = from_hp->h_meta->h_ctime;
1845 from_hp->h_meta->h_ctime = to_hp->h_meta->h_ctime;
1846 to_hp->h_meta->h_ctime = tmpLong;
1847
1848 tmpLong = from_hp->h_meta->h_gid;
1849 from_hp->h_meta->h_gid = to_hp->h_meta->h_gid;
1850 to_hp->h_meta->h_gid = tmpLong;
1851
1852 tmpLong = from_hp->h_meta->h_uid;
1853 from_hp->h_meta->h_uid = to_hp->h_meta->h_uid;
1854 to_hp->h_meta->h_uid = tmpLong;
1855
1856 tmpLong = from_hp->h_meta->h_pflags;
1857 from_hp->h_meta->h_pflags = to_hp->h_meta->h_pflags;
1858 to_hp->h_meta->h_pflags = tmpLong;
1859
1860 tmpLong = from_hp->h_meta->h_mode;
1861 from_hp->h_meta->h_mode = to_hp->h_meta->h_mode;
1862 to_hp->h_meta->h_mode = tmpLong;
1863
1864 tmpLong = from_hp->h_meta->h_rdev;
1865 from_hp->h_meta->h_rdev = to_hp->h_meta->h_rdev;
1866 to_hp->h_meta->h_rdev = tmpLong;
1867
1868 tmpLong = from_hp->h_meta->h_size;
1869 from_hp->h_meta->h_size = to_hp->h_meta->h_size;
1870 to_hp->h_meta->h_size = tmpLong;
1871
1872
1873
1874 Err_Exit_Relse:
1875
1876 /* unlock catalog b-tree */
1877 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1878
1879
1880 Err_Exit:
1881
1882
1883 /* XXX SER
1884 * At this point, the vnodes' data is switched, but are on the old hash list.
1885 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1886 * was being held.
1887 * Scenario:
1888 * A fork is trying to be added while exchanging...It got the hash lock,
1889 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1890 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1891 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1892 * Not very pretty, becarefull that this works ok
1893 * Scenario 2:
1894 * Same as the above, but before the move is made (like at this very spot), the new vnode
1895 * is added and a vget is requested for that new vnode, it would have old data
1896 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1897 * Scenario 3:
1898 * Hey! Same as above, but it is added after all the moving
1899 * So now there is a vnode with the old data, on the old hash...it will become
1900 * lost next time that a vget() is done
1901 *
1902 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1903 * opposite that we are doing now
1904 */
1905 hfs_vhashmove(from_hp, fromFileID);
1906 hfs_vhashmove(to_hp, toFileID);
1907
1908
1909 #if HFS_DIAGNOSTIC
1910 if (fromFirst)
1911 debug_check_vnode(HTOV(fromFirst), 0);
1912 if (fromSecond)
1913 debug_check_vnode(HTOV(fromSecond), 0);
1914 if (toFirst)
1915 debug_check_vnode(HTOV(toFirst), 0);
1916 if (toSecond)
1917 debug_check_vnode(HTOV(toSecond), 0);
1918 #endif
1919
1920
1921 /* Unlock any forks, and the sibling list */
1922 if (to_hp->h_meta->h_usecount > 1) {
1923 if (to_hp == toFirst) {
1924 if (toSecond)
1925 vput(HTOV(toSecond));
1926 } else {
1927 if (toSecond)
1928 vrele(HTOV(toSecond)); /* decrement, return it locked */
1929 if (toFirst)
1930 vput(HTOV(toFirst));
1931 }
1932 }
1933 if (from_hp->h_meta->h_usecount > 1) {
1934 if (from_hp == fromFirst) {
1935 if (fromSecond)
1936 vput(HTOV(fromSecond));
1937 } else {
1938 if (fromSecond)
1939 vrele(HTOV(fromSecond)); /* decrement, return it locked */
1940 if (fromFirst)
1941 vput(HTOV(fromFirst));
1942 }
1943 }
1944
1945 DBG_VOP_LOCKS_TEST(retval);
1946 return (retval);
1947 }
1948
1949
1950 /*
1951 * Change a vnode's file id, parent id and name
1952 *
1953 * Assumes the vnode is locked and is of type VREG
1954 */
1955 static void
1956 hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name)
1957 {
1958 DBG_ASSERT(HTOV(hp)->v_type == VREG);
1959
1960 H_HINT(hp) = 0;
1961 H_FILEID(hp) = fid; /* change h_nodeID */
1962 H_DIRID(hp) = pid;
1963
1964 hfs_set_metaname(name, hp->h_meta, HTOHFS(hp));
1965
1966
1967 }
1968
1969
1970 /*
1971
1972 #% fsync vp L L L
1973 #
1974 vop_fsync {
1975 IN struct vnode *vp;
1976 IN struct ucred *cred;
1977 IN int waitfor;
1978 IN struct proc *p;
1979
1980 */
1981
1982
1983 static int
1984 hfs_fsync(ap)
1985 struct vop_fsync_args /* {
1986 struct vnode *a_vp;
1987 struct ucred *a_cred;
1988 int a_waitfor;
1989 struct proc *a_p;
1990 } */ *ap;
1991 {
1992 struct vnode *vp = ap->a_vp ;
1993 struct hfsnode *hp = VTOH(vp);
1994 int retval = 0;
1995 register struct buf *bp;
1996 struct timeval tv;
1997 struct buf *nbp;
1998 int s;
1999
2000 DBG_FUNC_NAME("fsync");
2001 DBG_VOP_LOCKS_DECL(1);
2002 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
2003 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
2004 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
2005 DBG_HFS_NODE_CHECK(ap->a_vp);
2006
2007 #if HFS_DIAGNOSTIC
2008 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
2009 #endif
2010
2011
2012 /*
2013 * First of all, write out any clusters.
2014 */
2015 cluster_push(vp);
2016
2017 /*
2018 * Flush all dirty buffers associated with a vnode.
2019 */
2020 loop:
2021 s = splbio();
2022 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
2023 nbp = bp->b_vnbufs.le_next;
2024 if ((bp->b_flags & B_BUSY))
2025 continue;
2026 if ((bp->b_flags & B_DELWRI) == 0)
2027 panic("hfs_fsync: not dirty");
2028 bremfree(bp);
2029 bp->b_flags |= B_BUSY;
2030 bp->b_flags &= ~B_LOCKED; /* Clear flag, should only be set on meta files */
2031 splx(s);
2032 /*
2033 * Wait for I/O associated with indirect blocks to complete,
2034 * since there is no way to quickly wait for them below.
2035 */
2036 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp->b_blkno, bp->b_lblkno));
2037 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) {
2038 (void) bawrite(bp);
2039 } else {
2040 (void) VOP_BWRITE(bp);
2041 }
2042 goto loop;
2043 }
2044 if (vp->v_flag & VHASDIRTY)
2045 ubc_pushdirty(vp);
2046
2047 if (ap->a_waitfor == MNT_WAIT) {
2048 while (vp->v_numoutput) {
2049 vp->v_flag |= VBWAIT;
2050 tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
2051 }
2052
2053 /* I have seen this happen for swapfile. So it is safer to
2054 * check for dirty buffers again. --Umesh
2055 */
2056 if (vp->v_dirtyblkhd.lh_first || (vp->v_flag & VHASDIRTY)) {
2057 vprint("hfs_fsync: dirty", vp);
2058 splx(s);
2059 goto loop;
2060 }
2061 }
2062 splx(s);
2063
2064 #if HFS_DIAGNOSTIC
2065 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
2066 #endif
2067
2068 tv = time;
2069 if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
2070 BTSetLastSync(HTOFCB(hp), tv.tv_sec);
2071
2072 if (H_FORKTYPE(hp) != kSysFile) {
2073 retval = VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT);
2074
2075 if (retval != E_NONE) {
2076 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname, H_NAME(hp)));
2077 }
2078 }
2079 else
2080 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
2081
2082 if (ap->a_waitfor == MNT_WAIT) {
2083 DBG_ASSERT(vp->v_dirtyblkhd.lh_first == NULL);
2084 };
2085 DBG_VOP_LOCKS_TEST(retval);
2086 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
2087 return (retval);
2088 }
2089
2090
2091 int
2092 hfs_fsync_transaction(struct vnode *vp)
2093 {
2094 struct hfsnode *hp = VTOH(vp);
2095 register struct buf *bp;
2096 struct timeval tv;
2097 struct buf *nbp;
2098 int s;
2099
2100 /*
2101 * Flush all dirty buffers associated with a vnode.
2102 */
2103 loop:
2104 s = splbio();
2105
2106 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
2107 nbp = bp->b_vnbufs.le_next;
2108 if ((bp->b_flags & B_BUSY))
2109 continue;
2110 if ((bp->b_flags & B_DELWRI) == 0)
2111 panic("hfs_fsync: not dirty");
2112 if ( !(bp->b_flags & B_LOCKED))
2113 continue;
2114
2115 bremfree(bp);
2116 bp->b_flags |= B_BUSY;
2117 bp->b_flags &= ~B_LOCKED; /* Clear flag, should only be set on meta files */
2118 splx(s);
2119
2120 (void) bawrite(bp);
2121
2122 goto loop;
2123 }
2124 splx(s);
2125
2126 tv = time;
2127 if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
2128 (void) BTSetLastSync(VTOFCB(vp), tv.tv_sec);
2129 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
2130
2131 return 0;
2132 }
2133
2134 /*
2135
2136 #% remove dvp L U U
2137 #% remove vp L U U
2138 #
2139 vop_remove {
2140 IN WILLRELE struct vnode *dvp;
2141 IN WILLRELE struct vnode *vp;
2142 IN struct componentname *cnp;
2143
2144 */
2145
2146 int
2147 hfs_remove(ap)
2148 struct vop_remove_args /* {
2149 struct vnode *a_dvp;
2150 struct vnode *a_vp;
2151 struct componentname *a_cnp;
2152 } */ *ap;
2153 {
2154 struct vnode *vp = ap->a_vp;
2155 struct vnode *dvp = ap->a_dvp;
2156 struct hfsnode *hp = VTOH(ap->a_vp);
2157 struct hfsmount *hfsmp = HTOHFS(hp);
2158 struct proc *p = current_proc();
2159 struct timeval tv;
2160 int retval, use_count;
2161 int filebusy = 0;
2162 DBG_FUNC_NAME("remove");
2163 DBG_VOP_LOCKS_DECL(2);
2164 DBG_VOP_PRINT_FUNCNAME();
2165 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
2166 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2167 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2168 DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2169
2170 retval = E_NONE;
2171
2172 if ((hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
2173 (VTOH(dvp)->h_meta->h_pflags & APPEND)) {
2174 retval = EPERM;
2175 goto out;
2176 }
2177
2178 if (vp->v_usecount > 1) {
2179 /*
2180 * the namei done for the rename took a reference on the
2181 * vnode. Hence set 1 in the tookref parameter
2182 * of ubc_isinuse().
2183 */
2184 if(UBCISVALID(vp) && !ubc_isinuse(vp, 1))
2185 goto hfs_nobusy;
2186 if ((ap->a_cnp->cn_flags & NODELETEBUSY)
2187 || (hfsmp->hfs_private_metadata_dir == 0)) {
2188 /* Carbon semantics prohibits deleting busy files */
2189 retval = EBUSY;
2190 goto out;
2191 } else
2192 filebusy = 1;
2193 }
2194
2195 hfs_nobusy:
2196
2197 tv = time; /* Done here, so all times are the same */
2198
2199 /* Check other siblings for in use also */
2200 /* Uncache everything and make sure no other usecount */
2201 /*
2202 * This assumes the presence of the most 1 sibling
2203 *
2204 * a. loop through the siblings looking for another
2205 * b. If we find ourselves...skip it
2206 * If there was a sibling:
2207 * a. Check for a positve usecount
2208 * b. uncache any pages
2209 * c. Write out and memory changes
2210 * The idea is to keep the h_siblinglock as little as possible
2211 */
2212 if (hp->h_meta->h_usecount > 1) {
2213 struct vnode *sib_vp = NULL;
2214 struct hfsnode *nhp;
2215
2216 DBG_ASSERT(hp->h_meta->h_siblinghead.cqh_first &&
2217 (hp->h_meta->h_siblinghead.cqh_first != hp->h_meta->h_siblinghead.cqh_last));
2218 DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
2219
2220 /* Loop through all siblings, skipping ourselves */
2221 simple_lock(&hp->h_meta->h_siblinglock);
2222 CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
2223 if (nhp == hp) /* skip ourselves */
2224 continue;
2225 sib_vp = HTOV(nhp);
2226 };
2227 simple_unlock(&hp->h_meta->h_siblinglock);
2228
2229 /* Check to see if the other fork is in use */
2230 DBG_ASSERT(sib_vp != NULL);
2231 simple_lock(&sib_vp->v_interlock);
2232 use_count = sib_vp->v_usecount;
2233 simple_unlock(&sib_vp->v_interlock);
2234 if (use_count > 0) {
2235 /*
2236 * This is a sibling vnode and we did not take
2237 * a reference on it.
2238 * Hence set 0 in the tookref parameter
2239 * of ubc_isinuse().
2240 */
2241 if(UBCISVALID(sib_vp) && !ubc_isinuse(sib_vp, 0))
2242 goto hfs_nobusy2;
2243 if ((ap->a_cnp->cn_flags & NODELETEBUSY)
2244 || (hfsmp->hfs_private_metadata_dir == 0)) {
2245 /* Carbon semantics prohibits deleting busy files */
2246 retval = EBUSY;
2247 goto out;
2248 } else
2249 filebusy = 1;
2250 } /* use_count > 0 */
2251
2252 hfs_nobusy2:
2253
2254 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2255 if (vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
2256 /*
2257 * XXX SER An intelligient person would ask, why flush out changes
2258 * that are going to be deleted? See the next comment.
2259 */
2260 if ((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) || (VTOFCB(sib_vp)->fcbFlags
2261 & fcbModifiedMask)) {
2262 DBG_ASSERT((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) != 0);
2263 VOP_UPDATE(sib_vp, &tv, &tv, 0);
2264 };
2265
2266 /* Invalidate the buffers, ignore the results */
2267 (void) vinvalbuf(sib_vp, 0, NOCRED, p, 0, 0);
2268
2269 vput(sib_vp);
2270 }; /* vget() */
2271 }; /* h_use_count > 1 */
2272
2273 /*
2274 * remove the entry from the namei cache:
2275 * We do it early before any linking/busy file wierdness, make sure the
2276 * original is gone
2277 */
2278 cache_purge(vp);
2279
2280 /* Flush out any catalog changes */
2281 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2282 * and not from memory which is more correct
2283 */
2284 if ((hp->h_nodeflags & IN_MODIFIED) || (HTOFCB(hp)->fcbFlags & fcbModifiedMask))
2285 {
2286 DBG_ASSERT((hp->h_nodeflags & IN_MODIFIED) != 0);
2287 VOP_UPDATE(vp, &tv, &tv, 0);
2288 }
2289
2290 /* lock catalog b-tree */
2291 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
2292 if (retval != E_NONE) {
2293 retval = EBUSY;
2294 goto out;
2295 }
2296
2297 /*
2298 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2299 */
2300
2301 #if HFS_HARDLINKS
2302 /*
2303 * Multi-linked files just need their link node deleted from the catalog
2304 */
2305 if (hp->h_meta->h_metaflags & IN_DATANODE) {
2306
2307 if ((ap->a_cnp->cn_flags & HASBUF) == 0 ||
2308 ap->a_cnp->cn_nameptr[0] == '\0') {
2309 retval = ENOENT; /* name missing */
2310 goto out2;
2311 }
2312
2313 /* lock extents b-tree (also protects volume bitmap) */
2314 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
2315 if (retval != E_NONE) {
2316 retval = EBUSY;
2317 goto out2; /* unlock catalog b-tree on the way out */
2318 }
2319
2320 retval = hfsDelete (HTOVCB(hp), H_FILEID(VTOH(dvp)),
2321 ap->a_cnp->cn_nameptr, TRUE, H_HINT(hp));
2322
2323 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
2324
2325 if (retval != 0)
2326 goto out2;
2327
2328 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2329 hp->h_nodeflags |= IN_CHANGE;
2330 if (--hp->h_meta->h_nlink < 1)
2331 hp->h_meta->h_metaflags |= IN_DELETED;
2332
2333 /* name and parent fields are no longer valid so invalidate them */
2334 H_DIRID(hp) = kUnknownID;
2335 hfs_set_metaname("\0", hp->h_meta, HTOHFS(hp));
2336
2337 if ((ap->a_cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME))
2338 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
2339
2340 goto out2; /* link deleted, all done */
2341 }
2342 #endif
2343
2344 /*
2345 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2346 * an active vnode is translated to a move/rename so the file appears
2347 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2348 */
2349 if (filebusy) {
2350 UInt32 hint = H_HINT(hp);
2351 char nodeName[32];
2352
2353 MAKE_DELETED_NAME(nodeName, H_FILEID(hp));
2354
2355 retval = hfsMoveRename (HTOVCB(hp), H_DIRID(hp), H_NAME(hp),
2356 hfsmp->hfs_private_metadata_dir, nodeName, &hint);
2357 if (retval) goto out2;
2358
2359 hp->h_meta->h_metaflags |= IN_DELETED;
2360 hp->h_nodeflags |= IN_CHANGE;
2361
2362 /* update name so Catalog lookups succeed */
2363 H_HINT(hp) = hint;
2364 H_DIRID(hp) = hfsmp->hfs_private_metadata_dir;
2365 hfs_set_metaname(nodeName, hp->h_meta, HTOHFS(hp));
2366
2367 goto out2; /* all done, unlock the catalog */
2368 }
2369
2370 /* unlock the Catalog */
2371 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2372
2373 /* Invalidate the buffers */
2374 if ((retval= vinvalbuf(vp, 0, NOCRED, p, 0, 0)))
2375 goto out;
2376
2377 if(UBCINFOEXISTS(vp))
2378 (void)ubc_setsize(vp, (off_t)0);
2379
2380
2381 /* lock catalog b-tree */
2382 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
2383 if (retval != E_NONE) {
2384 retval = EBUSY;
2385 goto out;
2386 }
2387 /* lock extents b-tree (also protects volume bitmap) */
2388 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
2389 if (retval != E_NONE) {
2390 retval = EBUSY;
2391 goto out2; /* unlock catalog b-tree on the way out */
2392 }
2393
2394 /* remove entry from catalog and free any blocks used */
2395 retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
2396
2397 /* Clean up */
2398 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
2399 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2400
2401 if (retval != 0)
2402 goto out;
2403
2404 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2405 hp->h_meta->h_mode = 0; /* Makes the node go away...see inactive */
2406 /* clear the block mappings */
2407 hp->fcbPLen = (u_int64_t)0;
2408 bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
2409
2410 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
2411
2412 if (dvp == vp) {
2413 vrele(vp);
2414 } else {
2415 vput(vp);
2416 };
2417
2418 vput(dvp);
2419 DBG_VOP_LOCKS_TEST(retval);
2420
2421 if (UBCINFOEXISTS(vp)) {
2422 (void) ubc_uncache(vp);
2423 ubc_release(vp);
2424 /* WARNING vp may not be valid after this */
2425 }
2426 return (retval);
2427
2428 out2:
2429 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2430
2431 out:;
2432
2433 if (! retval)
2434 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
2435
2436 if (dvp == vp) {
2437 vrele(vp);
2438 } else {
2439 vput(vp);
2440 };
2441
2442 vput(dvp);
2443 DBG_VOP_LOCKS_TEST(retval);
2444 return (retval);
2445 }
2446
2447
2448 /*
2449
2450 #% rename sourcePar_vp U U U
2451 #% rename source_vp U U U
2452 #% rename targetPar_vp L U U
2453 #% rename target_vp X U U
2454 #
2455 vop_rename {
2456 IN WILLRELE struct vnode *sourcePar_vp;
2457 IN WILLRELE struct vnode *source_vp;
2458 IN struct componentname *source_cnp;
2459 IN WILLRELE struct vnode *targetPar_vp;
2460 IN WILLRELE struct vnode *target_vp;
2461 IN struct componentname *target_cnp;
2462
2463
2464 */
2465 /*
2466 * On entry:
2467 * source's parent directory is unlocked
2468 * source file or directory is unlocked
2469 * destination's parent directory is locked
2470 * destination file or directory is locked if it exists
2471 *
2472 * On exit:
2473 * all denodes should be released
2474 *
2475 */
2476
2477 static int
2478 hfs_rename(ap)
2479 struct vop_rename_args /* {
2480 struct vnode *a_fdvp;
2481 struct vnode *a_fvp;
2482 struct componentname *a_fcnp;
2483 struct vnode *a_tdvp;
2484 struct vnode *a_tvp;
2485 struct componentname *a_tcnp;
2486 } */ *ap;
2487 {
2488 struct vnode *target_vp = ap->a_tvp;
2489 struct vnode *targetPar_vp = ap->a_tdvp;
2490 struct vnode *source_vp = ap->a_fvp;
2491 struct vnode *sourcePar_vp = ap->a_fdvp;
2492 struct componentname *target_cnp = ap->a_tcnp;
2493 struct componentname *source_cnp = ap->a_fcnp;
2494 struct proc *p = source_cnp->cn_proc;
2495 struct hfsnode *target_hp, *targetPar_hp, *source_hp, *sourcePar_hp;
2496 u_int32_t oldparent = 0, newparent = 0;
2497 int doingdirectory = 0;
2498 int retval = 0;
2499 struct timeval tv;
2500 struct hfsCatalogInfo catInfo;
2501 DBG_VOP_LOCKS_DECL(4);
2502
2503 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2504 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_fvp);DBG_VOP_CONT(("\n"));
2505 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_fdvp);DBG_VOP_CONT(("\n"));
2506 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_tvp);DBG_VOP_CONT(("\n"));
2507 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_tdvp);DBG_VOP_CONT(("\n"));
2508 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_fcnp);DBG_VOP_CONT(("\n"));
2509 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_tcnp);DBG_VOP_CONT(("\n"));
2510 DBG_VOP_LOCKS_INIT(0,ap->a_fdvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2511 DBG_VOP_LOCKS_INIT(1,ap->a_fvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2512 DBG_VOP_LOCKS_INIT(2,ap->a_tdvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2513 DBG_VOP_LOCKS_INIT(3,ap->a_tvp, VOPDBG_LOCKNOTNIL, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2514 WRITE_CK(ap->a_fdvp, funcname);
2515 DBG_HFS_NODE_CHECK(ap->a_fdvp);
2516 DBG_HFS_NODE_CHECK(ap->a_tdvp);
2517
2518 #if HFS_DIAGNOSTIC
2519 if ((target_cnp->cn_flags & HASBUF) == 0 ||
2520 (source_cnp->cn_flags & HASBUF) == 0)
2521 panic("hfs_rename: no name");
2522 #endif
2523
2524 DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
2525 target_hp = targetPar_hp = source_hp = sourcePar_hp = 0;
2526
2527 /*
2528 * Check for cross-device rename.
2529 */
2530 if ((source_vp->v_mount != targetPar_vp->v_mount) ||
2531 (target_vp && (source_vp->v_mount != target_vp->v_mount))) {
2532 retval = EXDEV;
2533 goto abortit;
2534 }
2535
2536 /*
2537 * Check for access permissions
2538 */
2539 if (target_vp && ((VTOH(target_vp)->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
2540 (VTOH(targetPar_vp)->h_meta->h_pflags & APPEND))) {
2541 retval = EPERM;
2542 goto abortit;
2543 }
2544
2545 if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
2546 goto abortit;
2547
2548 sourcePar_hp = VTOH(sourcePar_vp);
2549 source_hp = VTOH(source_vp);
2550 oldparent = H_FILEID(sourcePar_hp);
2551 if ((source_hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) || (sourcePar_hp->h_meta->h_pflags & APPEND)) {
2552 VOP_UNLOCK(source_vp, 0, p);
2553 retval = EPERM;
2554 goto abortit;
2555 }
2556
2557 /*
2558 * Be sure we are not renaming ".", "..", or an alias of ".". This
2559 * leads to a crippled directory tree. It's pretty tough to do a
2560 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2561 * doesn't work if the ".." entry is missing.
2562 */
2563 if ((source_hp->h_meta->h_mode & IFMT) == IFDIR) {
2564 if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
2565 || sourcePar_hp == source_hp
2566 || (source_cnp->cn_flags&ISDOTDOT)
2567 || (source_hp->h_nodeflags & IN_RENAME)) {
2568 VOP_UNLOCK(source_vp, 0, p);
2569 retval = EINVAL;
2570 goto abortit;
2571 }
2572 source_hp->h_nodeflags |= IN_RENAME;
2573 doingdirectory = TRUE;
2574 }
2575
2576 /*
2577 *
2578 * >>>> Transit between abort and bad <<<<
2579 *
2580 */
2581
2582 targetPar_hp = VTOH(targetPar_vp);
2583 if (target_vp)
2584 target_hp = VTOH(target_vp);
2585 else
2586 DBG_ASSERT(target_hp == NULL);
2587
2588 newparent = H_FILEID(targetPar_hp);
2589
2590 /* Test to make sure we are not crossing devices */
2591 /* XXX SER Is this necesary, does catalog manager take care of this? */
2592 if (target_vp) {
2593 if (H_DEV(target_hp) != H_DEV(targetPar_hp) || H_DEV(target_hp) != H_DEV(source_hp))
2594 panic("rename: EXDEV");
2595 }
2596 else {
2597 if (H_DEV(targetPar_hp) != H_DEV(source_hp))
2598 panic("rename: EXDEV");
2599 };
2600
2601 retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
2602 if (doingdirectory && (newparent != oldparent)) {
2603 if (retval) /* write access check above */
2604 goto bad;
2605 }
2606 retval = 0; /* Reset value from above, we dont care about it anymore */
2607
2608 /*
2609 * If the destination exists, then be sure its type (file or dir)
2610 * matches that of the source. And, if it is a directory make sure
2611 * it is empty. Then delete the destination.
2612 */
2613 if (target_vp) {
2614
2615 /*
2616 * If the parent directory is "sticky", then the user must
2617 * own the parent directory, or the destination of the rename,
2618 * otherwise the destination may not be changed (except by
2619 * root). This implements append-only directories.
2620 */
2621 if ((targetPar_hp->h_meta->h_mode & S_ISTXT) && (target_cnp->cn_cred->cr_uid != 0) &&
2622 target_cnp->cn_cred->cr_uid != targetPar_hp->h_meta->h_uid &&
2623 target_cnp->cn_cred->cr_uid != target_hp->h_meta->h_uid) {
2624 retval = EPERM;
2625 goto bad;
2626 }
2627
2628 /*
2629 * VOP_REMOVE will vput targetPar_vp so we better bump
2630 * its ref count and relockit, always set target_vp to
2631 * NULL afterwards to indicate that were done with it.
2632 */
2633 VREF(targetPar_vp);
2634
2635 cache_purge(target_vp);
2636
2637 #if HFS_HARDLINKS
2638 target_cnp->cn_flags &= ~SAVENAME;
2639 #endif
2640
2641 retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
2642 (void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p);
2643
2644 target_vp = NULL;
2645 target_hp = NULL;
2646
2647 if (retval) goto bad;
2648
2649 };
2650
2651
2652 if (newparent != oldparent)
2653 vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);
2654
2655 /* remove the existing entry from the namei cache: */
2656 cache_purge(source_vp);
2657
2658 INIT_CATALOGDATA(&catInfo.nodeData, 0);
2659
2660 /* lock catalog b-tree */
2661 retval = hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
2662 if (retval) {
2663 if (newparent != oldparent) /* unlock the lock we just got */
2664 VOP_UNLOCK(sourcePar_vp, 0, p);
2665 goto bad;
2666 };
2667
2668 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2669 retval = hfsMoveRename( HTOVCB(source_hp), H_DIRID(source_hp), source_cnp->cn_nameptr,
2670 H_FILEID(VTOH(targetPar_vp)), target_cnp->cn_nameptr, &H_HINT(source_hp));
2671
2672 if (retval == 0) {
2673 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2674 catInfo.hint = H_HINT(source_hp);
2675 retval = hfs_getcatalog(HTOVCB(source_hp), H_FILEID(targetPar_hp), target_cnp->cn_nameptr, target_cnp->cn_namelen, &catInfo);
2676 }
2677
2678 /* unlock catalog b-tree */
2679 (void) hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_RELEASE, p);
2680
2681 if (newparent != oldparent)
2682 VOP_UNLOCK(sourcePar_vp, 0, p);
2683
2684 if (retval) goto bad;
2685
2686 H_DIRID(source_hp) = H_FILEID(targetPar_hp);
2687
2688 hfs_name_CatToMeta(&catInfo.nodeData, source_hp->h_meta);
2689
2690 CLEAN_CATALOGDATA(&catInfo.nodeData);
2691
2692 source_hp->h_nodeflags &= ~IN_RENAME;
2693
2694
2695 /*
2696 * Timestamp both parent directories.
2697 * Note that if this is a rename within the same directory,
2698 * (where targetPar_hp == sourcePar_hp)
2699 * the code below is still safe and correct.
2700 */
2701 targetPar_hp->h_nodeflags |= IN_UPDATE;
2702 sourcePar_hp->h_nodeflags |= IN_UPDATE;
2703 tv = time;
2704 HFSTIMES(targetPar_hp, &tv, &tv);
2705 HFSTIMES(sourcePar_hp, &tv, &tv);
2706
2707 vput(targetPar_vp);
2708 vrele(sourcePar_vp);
2709 vput(source_vp);
2710
2711 DBG_VOP_LOCKS_TEST(retval);
2712 if (retval != E_NONE) {
2713 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2714 }
2715 return (retval);
2716
2717 bad:;
2718 if (retval && doingdirectory)
2719 source_hp->h_nodeflags &= ~IN_RENAME;
2720
2721 if (targetPar_vp == target_vp)
2722 vrele(targetPar_vp);
2723 else
2724 vput(targetPar_vp);
2725
2726 if (target_vp)
2727 vput(target_vp);
2728
2729 vrele(sourcePar_vp);
2730
2731 if (VOP_ISLOCKED(source_vp))
2732 vput(source_vp);
2733 else
2734 vrele(source_vp);
2735
2736 DBG_VOP_LOCKS_TEST(retval);
2737 if (retval != E_NONE) {
2738 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2739 }
2740 return (retval);
2741
2742 abortit:;
2743
2744 VOP_ABORTOP(targetPar_vp, target_cnp); /* XXX, why not in NFS? */
2745
2746 if (targetPar_vp == target_vp)
2747 vrele(targetPar_vp);
2748 else
2749 vput(targetPar_vp);
2750
2751 if (target_vp)
2752 vput(target_vp);
2753
2754 VOP_ABORTOP(sourcePar_vp, source_cnp); /* XXX, why not in NFS? */
2755
2756 vrele(sourcePar_vp);
2757 vrele(source_vp);
2758
2759 DBG_VOP_LOCKS_TEST(retval);
2760 if (retval != E_NONE) {
2761 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2762 }
2763 return (retval);
2764 }
2765
2766
2767
2768 /*
2769 * Mkdir system call
2770 #% mkdir dvp L U U
2771 #% mkdir vpp - L -
2772 #
2773 vop_mkdir {
2774 IN WILLRELE struct vnode *dvp;
2775 OUT struct vnode **vpp;
2776 IN struct componentname *cnp;
2777 IN struct vattr *vap;
2778
2779 We are responsible for freeing the namei buffer,
2780 it is done in hfs_makenode()
2781 */
2782
2783 int
2784 hfs_mkdir(ap)
2785 struct vop_mkdir_args /* {
2786 struct vnode *a_dvp;
2787 struct vnode **a_vpp;
2788 struct componentname *a_cnp;
2789 struct vattr *a_vap;
2790 } */ *ap;
2791 {
2792 struct proc *p = current_proc();
2793 int retval;
2794 int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
2795
2796 DBG_FUNC_NAME("mkdir");
2797 DBG_VOP_LOCKS_DECL(2);
2798 DBG_VOP_PRINT_FUNCNAME();
2799 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
2800 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2801
2802 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2803 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
2804
2805 DBG_VOP(("%s: parent 0x%x (%s) ap->a_cnp->cn_nameptr %s\n", funcname, (u_int)VTOH(ap->a_dvp), H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
2806 WRITE_CK( ap->a_dvp, funcname);
2807 DBG_HFS_NODE_CHECK(ap->a_dvp);
2808 DBG_ASSERT(ap->a_dvp->v_type == VDIR);
2809
2810 /* Create the vnode */
2811 DBG_ASSERT((ap->a_cnp->cn_flags & SAVESTART) == 0);
2812 retval = hfs_makenode(mode, 0, ap->a_dvp, ap->a_vpp, ap->a_cnp, p);
2813 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
2814
2815 if (retval != E_NONE) {
2816 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
2817 DBG_VOP_LOCKS_TEST(retval);
2818 return (retval);
2819 }
2820
2821 DBG_VOP_LOCKS_TEST(E_NONE);
2822 return (E_NONE);
2823 }
2824
2825 /*
2826 * Rmdir system call.
2827 #% rmdir dvp L U U
2828 #% rmdir vp L U U
2829 #
2830 vop_rmdir {
2831 IN WILLRELE struct vnode *dvp;
2832 IN WILLRELE struct vnode *vp;
2833 IN struct componentname *cnp;
2834
2835 */
2836
2837 int
2838 hfs_rmdir(ap)
2839 struct vop_rmdir_args /* {
2840 struct vnode *a_dvp;
2841 struct vnode *a_vp;
2842 struct componentname *a_cnp;
2843 } */ *ap;
2844 {
2845 struct vnode *vp = ap->a_vp;
2846 struct vnode *dvp = ap->a_dvp;
2847 struct hfsnode *hp = VTOH(vp);
2848 struct proc *p = current_proc();
2849 int retval;
2850 DBG_FUNC_NAME("rmdir");
2851 DBG_VOP_LOCKS_DECL(2);
2852 DBG_VOP_PRINT_FUNCNAME();
2853 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);DBG_VOP_CONT(("\n"));
2854 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
2855 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2856
2857 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2858 DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2859
2860 if (dvp == vp) {
2861 vrele(vp);
2862 vput(vp);
2863 DBG_VOP_LOCKS_TEST(EINVAL);
2864 return (EINVAL);
2865 }
2866
2867 /*
2868 * HFS differs from UFS here in that we don't allow removing
2869 * a directory that in use by others - even if its empty.
2870 *
2871 * In the future we might want to allow this just like we do
2872 * for files (by renaming the busy directory).
2873 */
2874 #if 0
2875 if (vp->v_usecount > 1) {
2876 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname, vp->v_usecount ));
2877 retval = EBUSY;
2878 goto Err_Exit;
2879 }
2880 #endif
2881 /* remove the entry from the namei cache: */
2882 cache_purge(vp);
2883
2884 /* lock catalog b-tree */
2885 retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
2886 if (retval != E_NONE) {
2887 goto Err_Exit;
2888 }
2889
2890 /* remove entry from catalog */
2891 retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), FALSE, H_HINT(hp));
2892
2893 /* unlock catalog b-tree */
2894 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
2895
2896 if (! retval) {
2897 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE; /* Set the parent to be updated */
2898 hp->h_meta->h_mode = 0; /* Makes the vnode go away...see inactive */
2899 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2900 }
2901
2902 Err_Exit:;
2903 if (dvp != 0)
2904 vput(dvp);
2905 vput(vp);
2906
2907 DBG_VOP_LOCKS_TEST(retval);
2908 return (retval);
2909 }
2910
2911 /*
2912 * symlink -- make a symbolic link
2913 #% symlink dvp L U U
2914 #% symlink vpp - U -
2915 #
2916 # XXX - note that the return vnode has already been VRELE'ed
2917 # by the filesystem layer. To use it you must use vget,
2918 # possibly with a further namei.
2919 #
2920 vop_symlink {
2921 IN WILLRELE struct vnode *dvp;
2922 OUT WILLRELE struct vnode **vpp;
2923 IN struct componentname *cnp;
2924 IN struct vattr *vap;
2925 IN char *target;
2926
2927 We are responsible for freeing the namei buffer,
2928 it is done in hfs_makenode().
2929
2930 */
2931
2932 int
2933 hfs_symlink(ap)
2934 struct vop_symlink_args /* {
2935 struct vnode *a_dvp;
2936 struct vnode **a_vpp;
2937 struct componentname *a_cnp;
2938 struct vattr *a_vap;
2939 char *a_target;
2940 } */ *ap;
2941 {
2942 register struct vnode *vp, **vpp = ap->a_vpp;
2943 struct proc *p = current_proc();
2944 struct hfsnode *hp;
2945 u_int32_t dfltClump;
2946 int len, retval;
2947 DBG_FUNC_NAME("symlink");
2948 DBG_VOP_LOCKS_DECL(2);
2949 DBG_VOP_PRINT_FUNCNAME();
2950 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2951 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);
2952
2953 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
2954 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2955 vput(ap->a_dvp);
2956 DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
2957 DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
2958 return (EOPNOTSUPP);
2959 }
2960
2961 /* Create the vnode */
2962 retval = hfs_makenode(IFLNK | ap->a_vap->va_mode, 0, ap->a_dvp,
2963 vpp, ap->a_cnp, p);
2964 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
2965
2966 if (retval != E_NONE) {
2967 DBG_VOP_LOCKS_TEST(retval);
2968 return (retval);
2969 }
2970
2971
2972 vp = *vpp;
2973 len = strlen(ap->a_target);
2974 hp = VTOH(vp);
2975 dfltClump = hp->fcbClmpSize;
2976 /* make clump size minimal */
2977 hp->fcbClmpSize = VTOVCB(vp)->blockSize;
2978 retval = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
2979 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
2980 (struct proc *)0);
2981 hp->fcbClmpSize = dfltClump;
2982
2983
2984 vput(vp);
2985 DBG_VOP_LOCKS_TEST(retval);
2986 return (retval);
2987 }
2988
2989
2990 /*
2991 * Dummy dirents to simulate the "." and ".." entries of the directory
2992 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2993 * the size of these entries is the smallest needed to represent them
2994 * (only 12 byte each).
2995 */
2996 static hfsdotentry rootdots[2] = {
2997 {
2998 1, /* d_fileno */
2999 sizeof(struct hfsdotentry), /* d_reclen */
3000 DT_DIR, /* d_type */
3001 1, /* d_namlen */
3002 "." /* d_name */
3003 },
3004 {
3005 1, /* d_fileno */
3006 sizeof(struct hfsdotentry), /* d_reclen */
3007 DT_DIR, /* d_type */
3008 2, /* d_namlen */
3009 ".." /* d_name */
3010 }
3011 };
3012
3013 static hfsdotentry emptyentry = { 0 };
3014
3015 /* 4.3 Note:
3016 * There is some confusion as to what the semantics of uio_offset are.
3017 * In ufs, it represents the actual byte offset within the directory
3018 * "file." HFS, however, just uses it as an entry counter - essentially
3019 * assuming that it has no meaning except to the hfs_readdir function.
3020 * This approach would be more efficient here, but some callers may
3021 * assume the uio_offset acts like a byte offset. NFS in fact
3022 * monkeys around with the offset field a lot between readdir calls.
3023 *
3024 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3025 * fields is a mess as well. The libc function readdir() returns
3026 * NULL (indicating the end of a directory) when either
3027 * the getdirentries() syscall (which calls this and returns
3028 * the size of the buffer passed in less the value of uiop->uio_resid)
3029 * returns 0, or a direct record with a d_reclen of zero.
3030 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3031 * of the directory by testing uiop->uio_resid == 0. The solution
3032 * is to pad the size of the last struct direct in a given
3033 * block to fill the block if we are not at the end of the directory.
3034 */
3035
3036 struct callbackstate {
3037 u_int32_t cbs_parentID;
3038 u_int32_t cbs_hiddenDirID;
3039 off_t cbs_lastoffset;
3040 struct uio * cbs_uio;
3041 ExtendedVCB * cbs_vcb;
3042 int16_t cbs_hfsPlus;
3043 int16_t cbs_result;
3044 };
3045
3046
3047 SInt32
3048 ProcessCatalogEntry(const CatalogKey *ckp, const CatalogRecord *crp,
3049 u_int16_t recordLen, struct callbackstate *state)
3050 {
3051 CatalogName *cnp;
3052 size_t utf8chars;
3053 u_int32_t curID;
3054 OSErr result;
3055 struct dirent catent;
3056
3057 if (state->cbs_hfsPlus)
3058 curID = ckp->hfsPlus.parentID;
3059 else
3060 curID = ckp->hfs.parentID;
3061
3062 /* We're done when parent directory changes */
3063 if (state->cbs_parentID != curID) {
3064 lastitem:
3065 /*
3066 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3067 * so remove padding for now...
3068 */
3069 #if 0
3070 /*
3071 * Pad the end of list with an empty record.
3072 * This eliminates an extra call by readdir(3c).
3073 */
3074 catent.d_fileno = 0;
3075 catent.d_reclen = 0;
3076 catent.d_type = 0;
3077 catent.d_namlen = 0;
3078 *(int32_t*)&catent.d_name[0] = 0;
3079
3080 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3081
3082 state->cbs_result = uiomove((caddr_t) &catent, 12, state->cbs_uio);
3083 if (state->cbs_result == 0)
3084 state->cbs_result = ENOENT;
3085 #else
3086 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3087 state->cbs_result = ENOENT;
3088 #endif
3089 return (0); /* stop */
3090 }
3091
3092 if (state->cbs_hfsPlus) {
3093 switch(crp->recordType) {
3094 case kHFSPlusFolderRecord:
3095 catent.d_type = DT_DIR;
3096 catent.d_fileno = crp->hfsPlusFolder.folderID;
3097 break;
3098 case kHFSPlusFileRecord:
3099 catent.d_type = DT_REG;
3100 catent.d_fileno = crp->hfsPlusFile.fileID;
3101 break;
3102 default:
3103 return (0); /* stop */
3104 };
3105
3106 cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
3107 result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
3108 catent.d_name, &utf8chars, kdirentMaxNameBytes + 1, ':', 0);
3109 if (result == ENAMETOOLONG) {
3110 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
3111 cnp->ustr.unicode, kdirentMaxNameBytes + 1, (ByteCount*)&utf8chars, catent.d_name, catent.d_fileno);
3112 }
3113 } else { /* hfs */
3114 switch(crp->recordType) {
3115 case kHFSFolderRecord:
3116 catent.d_type = DT_DIR;
3117 catent.d_fileno = crp->hfsFolder.folderID;
3118 break;
3119 case kHFSFileRecord:
3120 catent.d_type = DT_REG;
3121 catent.d_fileno = crp->hfsFile.fileID;
3122 break;
3123 default:
3124 return (0); /* stop */
3125 };
3126
3127 cnp = (CatalogName*) ckp->hfs.nodeName;
3128 result = hfs_to_utf8(state->cbs_vcb, cnp->pstr, kdirentMaxNameBytes + 1,
3129 (ByteCount *)&utf8chars, catent.d_name);
3130 /*
3131 * When an HFS name cannot be encoded with the current
3132 * volume encoding we use MacRoman as a fallback.
3133 */
3134 if (result)
3135 result = mac_roman_to_utf8(cnp->pstr, kdirentMaxNameBytes + 1,
3136 (ByteCount *)&utf8chars, catent.d_name);
3137 }
3138
3139 catent.d_namlen = utf8chars;
3140 catent.d_reclen = DIRENTRY_SIZE(utf8chars);
3141
3142 /* hide our private meta data directory */
3143 if (curID == kRootDirID &&
3144 catent.d_fileno == state->cbs_hiddenDirID &&
3145 catent.d_type == DT_DIR)
3146 goto lastitem;
3147
3148 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3149
3150 /* if this entry won't fit then we're done */
3151 if (catent.d_reclen > state->cbs_uio->uio_resid)
3152 return (0); /* stop */
3153
3154 state->cbs_result = uiomove((caddr_t) &catent, catent.d_reclen, state->cbs_uio);
3155
3156 /* continue iteration if there's room */
3157 return (state->cbs_result == 0 &&
3158 state->cbs_uio->uio_resid >= AVERAGE_HFSDIRENTRY_SIZE);
3159 }
3160
3161 /*
3162 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3163 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3164 * "Nbytes must be greater than or equal to the block size associated with the file,
3165 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3166 * room for the . and .. entries that have to added manually.
3167 */
3168
3169 /*
3170 #% readdir vp L L L
3171 #
3172 vop_readdir {
3173 IN struct vnode *vp;
3174 INOUT struct uio *uio;
3175 IN struct ucred *cred;
3176 INOUT int *eofflag;
3177 OUT int *ncookies;
3178 INOUT u_long **cookies;
3179 */
3180 static int
3181 hfs_readdir(ap)
3182 struct vop_readdir_args /* {
3183 struct vnode *vp;
3184 struct uio *uio;
3185 struct ucred *cred;
3186 int *eofflag;
3187 int *ncookies;
3188 u_long **cookies;
3189 } */ *ap;
3190 {
3191 register struct uio *uio = ap->a_uio;
3192 struct hfsnode *hp = VTOH(ap->a_vp);
3193 struct proc *p = current_proc();
3194 ExtendedVCB *vcb = HTOVCB(hp);
3195 off_t off = uio->uio_offset;
3196 u_int32_t dirID = H_FILEID(hp);
3197 int retval = 0;
3198 OSErr result = noErr;
3199 u_int32_t diroffset;
3200 BTreeIterator bi;
3201 CatalogIterator *cip;
3202 u_int16_t op;
3203 struct callbackstate state;
3204 int eofflag = 0;
3205
3206 DBG_FUNC_NAME("readdir");
3207 DBG_VOP_LOCKS_DECL(1);
3208
3209 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
3210 DBG_VOP_PRINT_FUNCNAME();
3211 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3212 DBG_HFS_NODE_CHECK(ap->a_vp);
3213
3214 /* We assume it's all one big buffer... */
3215 if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE) {
3216 return EINVAL;
3217 };
3218
3219 /* Create the entries for . and .. */
3220 if (uio->uio_offset < sizeof(rootdots)) {
3221 caddr_t dep;
3222 size_t dotsize;
3223
3224 rootdots[0].d_fileno = dirID;
3225 rootdots[1].d_fileno = H_DIRID(hp);
3226
3227 if (uio->uio_offset == 0) {
3228 dep = (caddr_t) &rootdots[0];
3229 dotsize = 2* sizeof(struct hfsdotentry);
3230 } else if (uio->uio_offset == sizeof(struct hfsdotentry)) {
3231 dep = (caddr_t) &rootdots[1];
3232 dotsize = sizeof(struct hfsdotentry);
3233 } else {
3234 retval = EINVAL;
3235 goto Exit;
3236 }
3237
3238 retval = uiomove(dep, dotsize, uio);
3239 if (retval != 0)
3240 goto Exit;
3241 }
3242
3243 diroffset = uio->uio_offset;
3244
3245 /* lock catalog b-tree */
3246 retval = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
3247 if (retval != E_NONE)
3248 goto Exit;
3249
3250 /* get an iterator and position it */
3251 cip = GetCatalogIterator(vcb, dirID, diroffset);
3252
3253 result = PositionIterator(cip, diroffset, &bi, &op);
3254 if (result == cmNotFound) {
3255 eofflag = 1;
3256 retval = 0;
3257 AgeCatalogIterator(cip);
3258 goto cleanup;
3259 } else if ((retval = MacToVFSError(result)))
3260 goto cleanup;
3261
3262 state.cbs_hiddenDirID = VCBTOHFS(vcb)->hfs_private_metadata_dir;
3263 state.cbs_lastoffset = cip->currentOffset;
3264 state.cbs_vcb = vcb;
3265 state.cbs_uio = uio;
3266 state.cbs_result = 0;
3267 state.cbs_parentID = dirID;
3268
3269 if (vcb->vcbSigWord == kHFSPlusSigWord)
3270 state.cbs_hfsPlus = 1;
3271 else
3272 state.cbs_hfsPlus = 0;
3273
3274 /* process as many entries as possible... */
3275 result = BTIterateRecords(GetFileControlBlock(vcb->catalogRefNum), op, &bi,
3276 (IterateCallBackProcPtr)ProcessCatalogEntry, &state);
3277
3278 if (state.cbs_result)
3279 retval = state.cbs_result;
3280 else
3281 retval = MacToVFSError(result);
3282
3283 if (retval == ENOENT) {
3284 eofflag = 1;
3285 retval = 0;
3286 }
3287
3288 if (retval == 0) {
3289 cip->currentOffset = state.cbs_lastoffset;
3290 cip->nextOffset = uio->uio_offset;
3291 UpdateCatalogIterator(&bi, cip);
3292 }
3293
3294 cleanup:
3295 if (retval) {
3296 cip->volume = 0;
3297 cip->folderID = 0;
3298 AgeCatalogIterator(cip);
3299 }
3300
3301 (void) ReleaseCatalogIterator(cip);
3302
3303 /* unlock catalog b-tree */
3304 (void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);
3305
3306 if (retval != E_NONE) {
3307 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname, retval,
3308 H_FILEID(hp), H_NAME(hp)));
3309 goto Exit;
3310
3311 }
3312
3313 /* were we already past eof ? */
3314 if (uio->uio_offset == off) {
3315 retval = E_NONE;
3316 goto Exit;
3317 }
3318
3319 if (vcb->vcbSigWord == kHFSPlusSigWord)
3320 hp->h_nodeflags |= IN_ACCESS;
3321
3322 /* Bake any cookies */
3323 if (!retval && ap->a_ncookies != NULL) {
3324 struct dirent* dpStart;
3325 struct dirent* dpEnd;
3326 struct dirent* dp;
3327 int ncookies;
3328 u_long *cookies;
3329 u_long *cookiep;
3330
3331 /*
3332 * Only the NFS server uses cookies, and it loads the
3333 * directory block into system space, so we can just look at
3334 * it directly.
3335 */
3336 if (uio->uio_segflg != UIO_SYSSPACE)
3337 panic("hfs_readdir: unexpected uio from NFS server");
3338 dpStart = (struct dirent *)(uio->uio_iov->iov_base - (uio->uio_offset - off));
3339 dpEnd = (struct dirent *) uio->uio_iov->iov_base;
3340 for (dp = dpStart, ncookies = 0;
3341 dp < dpEnd && dp->d_reclen != 0;
3342 dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
3343 ncookies++;
3344 MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
3345 for (dp = dpStart, cookiep = cookies;
3346 dp < dpEnd;
3347 dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
3348 off += dp->d_reclen;
3349 *cookiep++ = (u_long) off;
3350 }
3351 *ap->a_ncookies = ncookies;
3352 *ap->a_cookies = cookies;
3353 }
3354
3355 Exit:;
3356
3357 if (ap->a_eofflag)
3358 *ap->a_eofflag = eofflag;
3359
3360 DBG_VOP_LOCKS_TEST(retval);
3361 return (retval);
3362 }
3363
3364
3365 /*
3366 * readdirattr operation will return attributes for the items in the
3367 * directory specified.
3368 *
3369 * It does not do . and .. entries. The problem is if you are at the root of the
3370 * hfs directory and go to .. you could be crossing a mountpoint into a
3371 * different (ufs) file system. The attributes that apply for it may not
3372 * apply for the file system you are doing the readdirattr on. To make life
3373 * simpler, this call will only return entries in its directory, hfs like.
3374 * TO DO LATER:
3375 * 1.getattrlist creates a thread record if the objpermanentid attribute
3376 * is requested. Just do EINVAL for now and fix later.
3377 * 2. more than one for uiovcnt support.
3378 * 3. put knohint (hints) in state for next call in
3379 * 4. credentials checking when rest of hfs does it.
3380 * 5. Do return permissions concatenation ???
3381 */
3382
3383 /*
3384 #
3385 #% readdirattr vp L L L
3386 #
3387 vop_readdirattr {
3388 IN struct vnode *vp;
3389 IN struct attrlist *alist;
3390 INOUT struct uio *uio;
3391 IN u_long maxcount:
3392 IN u_long options;
3393 OUT u_long *newstate;
3394 OUT int *eofflag;
3395 OUT u_long *actualCount;
3396 OUT u_long **cookies;
3397 IN struct ucred *cred;
3398 };
3399 */
3400 static int
3401 hfs_readdirattr(ap)
3402 struct vop_readdirattr_args /* {
3403 struct vnode *vp;
3404 struct attrlist *alist;
3405 struct uio *uio;
3406 u_long maxcount:
3407 u_long options;
3408 int *newstate;
3409 int *eofflag;
3410 u_long *actualcount;
3411 u_long **cookies;
3412 struct ucred *cred;
3413 } */ *ap;
3414 {
3415 struct vnode *vp = ap->a_vp;
3416 struct attrlist *alist = ap->a_alist;
3417 register struct uio *uio = ap->a_uio;
3418 u_long maxcount = ap->a_maxcount;
3419 u_long ncookies;
3420 ExtendedVCB *vcb = HTOVCB(VTOH(vp));
3421 UInt32 dirID = H_FILEID(VTOH(vp));
3422 struct proc *proc = current_proc(); /* could get this out of uio */
3423 off_t startoffset = uio->uio_offset;
3424 struct hfsCatalogInfo catInfo;
3425 UInt32 index;
3426 int retval = 0;
3427 u_long fixedblocksize;
3428 u_long maxattrblocksize;
3429 u_long currattrbufsize;
3430 void *attrbufptr = NULL;
3431 void *attrptr;
3432 void *varptr;
3433 struct vnode *entryvnode;
3434
3435
3436 *(ap->a_actualcount) = 0;
3437 *(ap->a_eofflag) = 0;
3438
3439 /* check for invalid options, check vnode, and buffer space */
3440 if (((ap->a_options & ~FSOPT_NOINMEMUPDATE) != 0) ||
3441 (vp == NULL) ||
3442 (uio->uio_resid <= 0) || (uio->uio_iovcnt > 1))
3443 return EINVAL;
3444
3445 /* this call doesn't take volume attributes */
3446 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
3447 ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
3448 (alist->volattr != 0) ||
3449 ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
3450 ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
3451 ((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0))
3452 return EINVAL;
3453
3454 /* Reject requests for unsupported options for now: */
3455 if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
3456 (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST)) ||
3457 (alist->commonattr & ATTR_CMN_OBJPERMANENTID) )
3458 return EINVAL;
3459
3460 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3461 * uiomove afterwards. It's an extra copy, but for now leave it alone
3462 */
3463 fixedblocksize = (sizeof(u_long) + AttributeBlockSize(alist)); /* u_long for length */
3464 maxattrblocksize = fixedblocksize;
3465 if (alist->commonattr & ATTR_CMN_NAME)
3466 maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
3467 MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
3468 attrptr = attrbufptr;
3469 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
3470
3471 /* Since attributes passed back can contain variable ones (name), we can't just use
3472 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3473 * as hfs_readdir was originally doing. If this all we need to represent the current
3474 * state, then ap->a_state is not needed at all.
3475 */
3476 /* index = ap->a_state; should not be less than 1 */
3477 index = (uio->uio_offset / sizeof(struct dirent)) + 1;
3478 INIT_CATALOGDATA(&catInfo.nodeData, 0);
3479
3480 /* Lock catalog b-tree */
3481 if ((retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, proc)) != E_NONE)
3482 goto exit;
3483
3484 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3485 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3486 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3487 */
3488 catInfo.hint = kNoHint; /* note, we may want to save the latest in state */
3489 while ((uio->uio_resid >= 0) && (maxcount !=0 )) {
3490 /* better to check uio_resid against max or fixedblocksize, but won't work.
3491 * Depending on if dir or file, the attributes returned will be different.
3492 * Thus fixedblocksize is too large in some cases.Also, the variable
3493 * part (like name) could be between fixedblocksize and the max.
3494 */
3495 OSErr result = GetCatalogOffspring(vcb, dirID, index, &catInfo.nodeData, NULL, NULL);
3496 if (result != noErr) {
3497 if (result == cmNotFound) {
3498 *(ap->a_eofflag) = TRUE;
3499 retval = E_NONE;
3500 }
3501 else retval = MacToVFSError(result);
3502 break;
3503 }
3504
3505 /* hide our private meta data directory as does hfs_readdir */
3506 if ((dirID == kRootDirID) &&
3507 catInfo.nodeData.cnd_nodeID == VCBTOHFS(vcb)->hfs_private_metadata_dir &&
3508 catInfo.nodeData.cnd_type == kCatalogFolderNode) {
3509
3510 ++index;
3511 continue;
3512 }
3513
3514 *((u_long *)attrptr)++ = 0; /* move it past length */
3515
3516 if (ap->a_options & FSOPT_NOINMEMUPDATE) {
3517 /* vp okay to use instead of root vp */
3518 PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
3519 } else {
3520 /* Check to see if there's a vnode for this item in the cache: */
3521 entryvnode = hfs_vhashget(H_DEV(VTOH(vp)), catInfo.nodeData.cnd_nodeID, kDefault);
3522 if (entryvnode != NULL) {
3523 PackAttributeBlock(alist, entryvnode, &catInfo, &attrptr, &varptr);
3524 vput(entryvnode);
3525 } else {
3526 /* vp okay to use instead of root vp */
3527 PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
3528 };
3529 };
3530 currattrbufsize = *((u_long *)attrbufptr) = ((char *)varptr - (char *)attrbufptr);
3531
3532 /* now check if we can't fit in the buffer space remaining */
3533 if (currattrbufsize > uio->uio_resid)
3534 break;
3535 else {
3536 retval = uiomove((caddr_t)attrbufptr, currattrbufsize, ap->a_uio);
3537 if (retval != E_NONE)
3538 break;
3539 attrptr = attrbufptr;
3540 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
3541 index++;
3542 *ap->a_actualcount += 1;
3543 maxcount--;
3544 }
3545 };
3546 *ap->a_newstate = VTOH(vp)->h_meta->h_mtime;/* before we unlock, know the mod date */
3547 /* Unlock catalog b-tree, finally. Ties up the everything during enumeration */
3548 (void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, proc );
3549
3550 CLEAN_CATALOGDATA(&catInfo.nodeData);
3551
3552 if (!retval && ap->a_cookies != NULL) { /* CHECK THAT 0 wasn't passed in */
3553 void* dpStart;
3554 void* dpEnd;
3555 void* dp;
3556 u_long *cookies;
3557 u_long *cookiep;
3558
3559 /* Only the NFS server uses cookies, and it loads the
3560 * directory block into system space, so we can just look at
3561 * it directly.
3562 */
3563 if (uio->uio_segflg != UIO_SYSSPACE) /* || uio->uio_iovcnt != 1 checked earlier */
3564 panic("hfs_readdirattr: unexpected uio from NFS server");
3565 dpStart = uio->uio_iov->iov_base - (uio->uio_offset - startoffset);
3566 dpEnd = uio->uio_iov->iov_base;
3567 MALLOC(cookies, u_long *, (*ap->a_actualcount)*sizeof(u_long), M_TEMP, M_WAITOK);
3568 for (dp = dpStart, cookiep = cookies;
3569 dp < dpEnd;
3570 dp = ((caddr_t) dp + *((u_long *)dp))) {
3571 *cookiep++ = (u_long)((caddr_t)dp + sizeof(u_long));
3572 }
3573 *ap->a_cookies = cookies;
3574 }
3575
3576 uio->uio_offset = startoffset + (*ap->a_actualcount)*sizeof(struct dirent);
3577
3578 exit:
3579 if (attrbufptr != NULL)
3580 FREE(attrbufptr, M_TEMP);
3581 return (retval);
3582 }
3583
3584
3585 /*
3586 * Return target name of a symbolic link
3587 #% readlink vp L L L
3588 #
3589 vop_readlink {
3590 IN struct vnode *vp;
3591 INOUT struct uio *uio;
3592 IN struct ucred *cred;
3593 */
3594
3595 int
3596 hfs_readlink(ap)
3597 struct vop_readlink_args /* {
3598 struct vnode *a_vp;
3599 struct uio *a_uio;
3600 struct ucred *a_cred;
3601 } */ *ap;
3602 {
3603 int retval;
3604 DBG_FUNC_NAME("readlink");
3605 DBG_VOP_LOCKS_DECL(1);
3606 DBG_VOP_PRINT_FUNCNAME();
3607 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3608
3609 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
3610 retval = VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred);
3611 /* clear IN_ACCESS to prevent needless update of symlink vnode */
3612 VTOH(ap->a_vp)->h_nodeflags &= ~IN_ACCESS;
3613
3614 DBG_VOP_LOCKS_TEST(retval);
3615 return (retval);
3616
3617 }
3618
3619
3620 /*
3621 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3622 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3623 #% abortop dvp = = =
3624 #
3625 vop_abortop {
3626 IN struct vnode *dvp;
3627 IN struct componentname *cnp;
3628
3629 */
3630
3631 /* ARGSUSED */
3632
3633 static int
3634 hfs_abortop(ap)
3635 struct vop_abortop_args /* {
3636 struct vnode *a_dvp;
3637 struct componentname *a_cnp;
3638 } */ *ap;
3639 {
3640 DBG_FUNC_NAME("abortop");
3641 DBG_VOP_LOCKS_DECL(1);
3642 DBG_VOP_PRINT_FUNCNAME();
3643 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
3644 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
3645
3646
3647 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
3648
3649 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
3650 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
3651 }
3652 DBG_VOP_LOCKS_TEST(E_NONE);
3653 return (E_NONE);
3654 }
3655
3656 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3657
3658 /*
3659 #% inactive vp L U U
3660 #
3661 vop_inactive {
3662 IN struct vnode *vp;
3663 IN struct proc *p;
3664
3665 */
3666
3667 static int
3668 hfs_inactive(ap)
3669 struct vop_inactive_args /* {
3670 struct vnode *a_vp;
3671 } */ *ap;
3672 {
3673 struct vnode *vp = ap->a_vp;
3674 struct hfsnode *hp = VTOH(vp);
3675 struct proc *p = ap->a_p;
3676 struct timeval tv;
3677 int error = 0;
3678 extern int prtactive;
3679
3680 DBG_FUNC_NAME("inactive");
3681 DBG_VOP_LOCKS_DECL(1);
3682 DBG_VOP_PRINT_FUNCNAME();
3683 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3684
3685 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
3686
3687
3688 if (prtactive && vp->v_usecount <= 0)
3689 vprint("hfs_inactive: pushing active", vp);
3690
3691 if (vp->v_usecount != 0)
3692 DBG_VOP(("%s: bad usecount = %d\n",funcname,vp->v_usecount ));
3693
3694 /*
3695 * Ignore nodes related to stale file handles.
3696 */
3697 if (hp->h_meta->h_mode == 0)
3698 goto out;
3699
3700 /*
3701 * Check for a postponed deletion
3702 */
3703 if (hp->h_meta->h_metaflags & IN_DELETED) {
3704 hp->h_meta->h_metaflags &= ~IN_DELETED;
3705
3706 error = vinvalbuf(vp, 0, NOCRED, p, 0, 0);
3707 if (error) goto out;
3708
3709 if(UBCINFOEXISTS(vp))
3710 (void)ubc_setsize(vp, (off_t)0);
3711
3712 /* Lock both trees
3713 * Note: we do not need a lock on the private metadata directory
3714 * since it never has a vnode associated with it.
3715 */
3716 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
3717 if (error) goto out;
3718 error = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
3719 if (error) {
3720 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
3721 goto out;
3722 }
3723
3724 if (hp->h_meta->h_metaflags & IN_DATANODE) {
3725 char iNodeName[32];
3726
3727 MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
3728 error = hfsDelete(HTOVCB(hp), VTOHFS(vp)->hfs_private_metadata_dir, iNodeName, TRUE, H_HINT(hp));
3729 } else {
3730 /* XXX can we leave orphaned sibling? */
3731 error = hfsDelete(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
3732 if (error == ENOENT) {
3733 /* try by fileID as a backup */
3734 error = hfsDelete(HTOVCB(hp), H_FILEID(hp), NULL, TRUE, H_HINT(hp));
3735 }
3736 }
3737
3738 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
3739 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
3740 if (error) goto out;
3741
3742 hp->h_meta->h_metaflags |= IN_NOEXISTS;
3743 hp->h_meta->h_mode = 0;
3744 /* clear the block mappings */
3745 hp->fcbPLen = (u_int64_t)0;
3746 bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
3747
3748 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
3749 }
3750
3751 if (hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
3752 tv = time;
3753 VOP_UPDATE(vp, &tv, &tv, 0);
3754 }
3755
3756 out:
3757 VOP_UNLOCK(vp, 0, p);
3758 /*
3759 * If we are done with the inode, reclaim it
3760 * so that it can be reused immediately.
3761 */
3762 if (hp->h_meta->h_mode == 0)
3763 vrecycle(vp, (struct slock *)0, p);
3764
3765 /* XXX SER Here we might want to get rid of any other forks
3766 * The problem is that if we call vrecycle(), our structure
3767 * disappear from under us, we would need to remember, and expect
3768 * things to go to null or to disappear
3769 * But it stillw would be a good thing to remove vnodes
3770 * referencing stale data
3771 */
3772
3773 DBG_VOP_LOCKS_TEST(E_NONE);
3774 return (E_NONE);
3775 }
3776
3777 /*
3778 Ignored since the locks are gone......
3779 #% reclaim vp U I I
3780 #
3781 vop_reclaim {
3782 IN struct vnode *vp;
3783 IN struct proc *p;
3784
3785 */
3786
3787 static int
3788 hfs_reclaim(ap)
3789 struct vop_reclaim_args /* {
3790 struct vnode *a_vp;
3791 } */ *ap;
3792 {
3793 struct vnode *vp = ap->a_vp;
3794 struct hfsnode *hp = VTOH(vp);
3795 void *tdata = vp->v_data;
3796 char *tname;
3797 Boolean freeMeta = true;
3798 struct vnode *devvp = NULL;
3799
3800 extern int prtactive;
3801 DBG_FUNC_NAME("reclaim");
3802 DBG_VOP_LOCKS_DECL(1);
3803 DBG_VOP_PRINT_FUNCNAME();
3804 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3805
3806 DBG_VOP_LOCKS_INIT(0, ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
3807
3808 /*
3809 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3810 created in their entirity could be getting cleaned up here.
3811 */
3812
3813 if (prtactive && vp->v_usecount != 0)
3814 vprint("hfs_reclaim(): pushing active", vp);
3815
3816 hp->h_nodeflags |= IN_ALLOCATING; /* Mark this as being incomplete */
3817 /*
3818 * This will remove the entry from the hash AND the sibling list
3819 * This will make sure everything is in a stable state to see if we can remove the meta
3820 * i.e. if this is the only fork...the sibling list will be empty
3821 */
3822 hfs_vhashrem(hp);
3823
3824 DBG_ASSERT(tdata != NULL);
3825 DBG_ASSERT(hp->h_meta != NULL);
3826
3827 devvp = hp->h_meta->h_devvp; /* For later releasing */
3828 hp->h_meta->h_usecount--;
3829
3830 /* release the file meta if this is the last fork */
3831 if (H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork) {
3832 if (hp->h_meta->h_siblinghead.cqh_first != (void *) &hp->h_meta->h_siblinghead)
3833 freeMeta = false;
3834 };
3835
3836 if (freeMeta) {
3837 DBG_ASSERT(hp->h_meta->h_usecount == 0);
3838 if (hp->h_meta->h_metaflags & IN_LONGNAME) {
3839 tname = H_NAME(hp);
3840 DBG_ASSERT(tname != NULL);
3841 FREE(tname, M_TEMP);
3842 }
3843 FREE_ZONE(hp->h_meta, sizeof(struct hfsfilemeta), M_HFSFMETA);
3844 hp->h_meta = NULL;
3845 }
3846 else
3847 DBG_ASSERT(hp->h_meta->h_usecount == 1);
3848
3849
3850 /*
3851 * Purge old data structures associated with the inode.
3852 */
3853 cache_purge(vp);
3854 if (devvp) {
3855 vrele(devvp);
3856 };
3857
3858 /* Free our data structs */
3859 FREE_ZONE(tdata, sizeof(struct hfsnode), M_HFSNODE);
3860 vp->v_data = NULL;
3861
3862 DBG_VOP_LOCKS_TEST(E_NONE);
3863 return (E_NONE);
3864 }
3865
3866
3867 /*
3868 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
3869 #% lock vp U L U
3870 #
3871 vop_lock {
3872 IN struct vnode *vp;
3873 IN int flags;
3874 IN struct proc *p;
3875 */
3876
3877 static int
3878 hfs_lock(ap)
3879 struct vop_lock_args /* {
3880 struct vnode *a_vp;
3881 int a_flags;
3882 struct proc *a_p;
3883 } */ *ap;
3884 {
3885 struct vnode * vp = ap->a_vp;
3886 struct hfsnode *hp = VTOH(ap->a_vp);
3887 int retval;
3888
3889 DBG_FUNC_NAME("lock");
3890 DBG_VOP_LOCKS_DECL(1);
3891 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
3892 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
3893 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
3894
3895 retval = lockmgr(&hp->h_lock, ap->a_flags, &vp->v_interlock, ap->a_p);
3896 if (retval != E_NONE) {
3897 if ((ap->a_flags & LK_NOWAIT) == 0)
3898 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval, ap->a_flags));
3899 goto Err_Exit;
3900 };
3901
3902 Err_Exit:;
3903 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
3904 DBG_VOP_LOCKS_TEST(retval);
3905 return (retval);
3906 }
3907
3908 /*
3909 * Unlock an hfsnode.
3910 #% unlock vp L U L
3911 #
3912 vop_unlock {
3913 IN struct vnode *vp;
3914 IN int flags;
3915 IN struct proc *p;
3916
3917 */
3918 int
3919 hfs_unlock(ap)
3920 struct vop_unlock_args /* {
3921 struct vnode *a_vp;
3922 int a_flags;
3923 struct proc *a_p;
3924 } */ *ap;
3925 {
3926 struct hfsnode *hp = VTOH(ap->a_vp);
3927 struct vnode *vp = ap->a_vp;
3928 int retval = E_NONE;
3929
3930 DBG_FUNC_NAME("unlock");
3931 DBG_VOP_LOCKS_DECL(1);
3932 DBG_VOP_PRINT_FUNCNAME();
3933 DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
3934 DBG_VOP_LOCKS_INIT(0,vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
3935
3936
3937 DBG_ASSERT((ap->a_flags & (LK_EXCLUSIVE|LK_SHARED)) == 0);
3938 retval = lockmgr(&hp->h_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p);
3939 if (retval != E_NONE) {
3940 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval, H_FORKTYPE(hp)));
3941 };
3942
3943 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
3944 DBG_VOP_LOCKS_TEST(retval);
3945 return (retval);
3946 }
3947
3948
3949 /*
3950 * Print out the contents of an hfsnode.
3951 #% print vp = = =
3952 #
3953 vop_print {
3954 IN struct vnode *vp;
3955 */
3956 int
3957 hfs_print(ap)
3958 struct vop_print_args /* {
3959 struct vnode *a_vp;
3960 } */ *ap;
3961 {
3962 register struct vnode * vp = ap->a_vp;
3963 register struct hfsnode *hp = VTOH( vp);
3964 DBG_FUNC_NAME("print");
3965 DBG_VOP_LOCKS_DECL(1);
3966 DBG_VOP_PRINT_FUNCNAME();
3967 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
3968
3969 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
3970
3971 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp),
3972 major(H_DEV(hp)), minor(H_DEV(hp)));
3973 /* lockmgr_printinfo(&hp->h_lock); */
3974 printf("\n");
3975 DBG_VOP_LOCKS_TEST(E_NONE);
3976 return (E_NONE);
3977 }
3978
3979
3980 /*
3981 * Check for a locked hfsnode.
3982 #% islocked vp = = =
3983 #
3984 vop_islocked {
3985 IN struct vnode *vp;
3986
3987 */
3988 int
3989 hfs_islocked(ap)
3990 struct vop_islocked_args /* {
3991 struct vnode *a_vp;
3992 } */ *ap;
3993 {
3994 int lockStatus;
3995 //DBG_FUNC_NAME("islocked");
3996 //DBG_VOP_LOCKS_DECL(1);
3997 //DBG_VOP_PRINT_FUNCNAME();
3998 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
3999
4000 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
4001
4002 lockStatus = lockstatus(&VTOH( ap->a_vp)->h_lock);
4003 //DBG_VOP_LOCKS_TEST(E_NONE);
4004 return (lockStatus);
4005 }
4006
4007 /*
4008
4009 #% pathconf vp L L L
4010 #
4011 vop_pathconf {
4012 IN struct vnode *vp;
4013 IN int name;
4014 OUT register_t *retval;
4015
4016 */
4017 static int
4018 hfs_pathconf(ap)
4019 struct vop_pathconf_args /* {
4020 struct vnode *a_vp;
4021 int a_name;
4022 int *a_retval;
4023 } */ *ap;
4024 {
4025 int retval = E_NONE;
4026 DBG_FUNC_NAME("pathconf");
4027 DBG_VOP_LOCKS_DECL(1);
4028 DBG_VOP_PRINT_FUNCNAME();
4029 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
4030
4031 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
4032
4033 DBG_HFS_NODE_CHECK (ap->a_vp);
4034
4035 switch (ap->a_name) {
4036 case _PC_LINK_MAX:
4037 #if HFS_HARDLINKS
4038 if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
4039 *ap->a_retval = HFS_LINK_MAX;
4040 else
4041 *ap->a_retval = 1;
4042 #else
4043 *ap->a_retval = 1;
4044 #endif
4045 break;
4046 case _PC_NAME_MAX:
4047 *ap->a_retval = kHFSPlusMaxFileNameBytes; /* max # of characters x max utf8 representation */
4048 break;
4049 case _PC_PATH_MAX:
4050 *ap->a_retval = PATH_MAX; /* 1024 */
4051 break;
4052 case _PC_CHOWN_RESTRICTED:
4053 *ap->a_retval = 1;
4054 break;
4055 case _PC_NO_TRUNC:
4056 *ap->a_retval = 0;
4057 break;
4058 default:
4059 retval = EINVAL;
4060 }
4061
4062 DBG_VOP_LOCKS_TEST(retval);
4063 return (retval);
4064 }
4065
4066
4067
4068
4069
4070 /*
4071 * Advisory record locking support
4072 #% advlock vp U U U
4073 #
4074 vop_advlock {
4075 IN struct vnode *vp;
4076 IN caddr_t id;
4077 IN int op;
4078 IN struct flock *fl;
4079 IN int flags;
4080
4081 */
4082 int
4083 hfs_advlock(ap)
4084 struct vop_advlock_args /* {
4085 struct vnode *a_vp;
4086 caddr_t a_id;
4087 int a_op;
4088 struct flock *a_fl;
4089 int a_flags;
4090 } */ *ap;
4091 {
4092 register struct hfsnode *hp = VTOH(ap->a_vp);
4093 register struct flock *fl = ap->a_fl;
4094 register struct hfslockf *lock;
4095 off_t start, end;
4096 int retval;
4097 DBG_FUNC_NAME("advlock");
4098 DBG_VOP_LOCKS_DECL(1);
4099 DBG_VOP_PRINT_FUNCNAME();
4100 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
4101 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
4102 /*
4103 * Avoid the common case of unlocking when inode has no locks.
4104 */
4105 if (hp->h_lockf == (struct hfslockf *)0) {
4106 if (ap->a_op != F_SETLK) {
4107 fl->l_type = F_UNLCK;
4108 return (0);
4109 }
4110 }
4111 /*
4112 * Convert the flock structure into a start and end.
4113 */
4114 start = 0;
4115 switch (fl->l_whence) {
4116 case SEEK_SET:
4117 case SEEK_CUR:
4118 /*
4119 * Caller is responsible for adding any necessary offset
4120 * when SEEK_CUR is used.
4121 */
4122 start = fl->l_start;
4123 break;
4124
4125 case SEEK_END:
4126 start = HTOFCB(hp)->fcbEOF + fl->l_start;
4127 break;
4128
4129 default:
4130 return (EINVAL);
4131 }
4132
4133 if (start < 0)
4134 return (EINVAL);
4135 if (fl->l_len == 0)
4136 end = -1;
4137 else
4138 end = start + fl->l_len - 1;
4139
4140 /*
4141 * Create the hfslockf structure
4142 */
4143 MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
4144 lock->lf_start = start;
4145 lock->lf_end = end;
4146 lock->lf_id = ap->a_id;
4147 lock->lf_hfsnode = hp;
4148 lock->lf_type = fl->l_type;
4149 lock->lf_next = (struct hfslockf *)0;
4150 TAILQ_INIT(&lock->lf_blkhd);
4151 lock->lf_flags = ap->a_flags;
4152 /*
4153 * Do the requested operation.
4154 */
4155 switch(ap->a_op) {
4156 case F_SETLK:
4157 retval = hfs_setlock(lock);
4158 break;
4159
4160 case F_UNLCK:
4161 retval = hfs_clearlock(lock);
4162 FREE(lock, M_LOCKF);
4163 break;
4164
4165 case F_GETLK:
4166 retval = hfs_getlock(lock, fl);
4167 FREE(lock, M_LOCKF);
4168 break;
4169
4170 default:
4171 retval = EINVAL;
4172 _FREE(lock, M_LOCKF);
4173 break;
4174 }
4175
4176 DBG_VOP_LOCKS_TEST(retval);
4177 return (retval);
4178 }
4179
4180
4181
4182 /*
4183 * Update the access, modified, and node change times as specified by the
4184 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4185 * used to specify that the node needs to be updated but that the times have
4186 * already been set. The access and modified times are taken from the second
4187 * and third parameters; the node change time is always taken from the current
4188 * time. If waitfor is set, then wait for the disk write of the node to
4189 * complete.
4190 */
4191 /*
4192 #% update vp L L L
4193 IN struct vnode *vp;
4194 IN struct timeval *access;
4195 IN struct timeval *modify;
4196 IN int waitfor;
4197 */
4198
4199 int
4200 hfs_update(ap)
4201 struct vop_update_args /* {
4202 struct vnode *a_vp;
4203 struct timeval *a_access;
4204 struct timeval *a_modify;
4205 int a_waitfor;
4206 } */ *ap;
4207 {
4208 struct hfsnode *hp;
4209 struct proc *p;
4210 hfsCatalogInfo catInfo;
4211 char *filename;
4212 char iNodeName[32];
4213 u_int32_t pid;
4214 int retval;
4215 ExtendedVCB *vcb;
4216 DBG_FUNC_NAME("update");
4217 DBG_VOP_LOCKS_DECL(1);
4218 DBG_VOP_PRINT_FUNCNAME();
4219 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
4220 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
4221
4222 hp = VTOH(ap->a_vp);
4223
4224 DBG_ASSERT(hp && hp->h_meta);
4225 DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
4226
4227 if ((H_FORKTYPE(hp) == kSysFile) ||
4228 (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) ||
4229 (hp->h_meta->h_mode == 0)) {
4230 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4231 DBG_VOP_LOCKS_TEST(0);
4232 return (0);
4233 }
4234
4235 if (H_FORKTYPE(hp) == kSysFile) {
4236 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4237 DBG_VOP_LOCKS_TEST(0);
4238 return (0);
4239 }
4240
4241 if (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) {
4242 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4243 DBG_VOP_LOCKS_TEST(0);
4244 return (0);
4245 }
4246
4247 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4248 if (HTOFCB(hp)->fcbFlags &fcbModifiedMask)
4249 hp->h_nodeflags |= IN_MODIFIED;
4250
4251 if ((hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) {
4252 DBG_VOP_LOCKS_TEST(0);
4253 return (0);
4254 };
4255
4256 if (hp->h_nodeflags & IN_ACCESS)
4257 hp->h_meta->h_atime = ap->a_access->tv_sec;
4258 if (hp->h_nodeflags & IN_UPDATE)
4259 hp->h_meta->h_mtime = ap->a_modify->tv_sec;
4260 if (hp->h_nodeflags & IN_CHANGE) {
4261 hp->h_meta->h_ctime = time.tv_sec;
4262 /*
4263 * HFS dates that WE set must be adjusted for DST
4264 */
4265 if ((HTOVCB(hp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
4266 hp->h_meta->h_ctime += 3600;
4267 hp->h_meta->h_mtime = hp->h_meta->h_ctime;
4268 }
4269 }
4270
4271 p = current_proc();
4272 filename = H_NAME(hp);
4273 pid = H_DIRID(hp);
4274 vcb = HTOVCB(hp);
4275 catInfo.hint = H_HINT(hp);
4276
4277 #if HFS_HARDLINKS
4278 /*
4279 * Force an update of the indirect node instead of the link
4280 * by using the name and parent of the indirect node.
4281 */
4282 if (hp->h_meta->h_metaflags & IN_DATANODE) {
4283 MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
4284 filename = iNodeName;
4285 pid = VCBTOHFS(vcb)->hfs_private_metadata_dir;
4286 }
4287 #endif
4288
4289 INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
4290
4291 /*
4292 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4293 * the Catalog b-tree may aready be locked by the current thread. So we
4294 * allow recursive locking of the Catalog from within VOP_UPDATE.
4295 */
4296 /* Lock the Catalog b-tree file */
4297 retval = hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
4298 if (retval) {
4299 DBG_VOP_LOCKS_TEST(retval);
4300 return (retval);
4301 };
4302
4303 retval = hfs_getcatalog(vcb, pid, filename, -1, &catInfo);
4304 if (retval != noErr) {
4305 (void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
4306 retval = MacToVFSError(retval);
4307 goto Err_Exit;
4308 };
4309
4310 H_HINT(hp) = catInfo.hint;
4311 CopyVNodeToCatalogNode (HTOV(hp), &catInfo.nodeData);
4312
4313 retval = UpdateCatalogNode(vcb, pid, filename, H_HINT(hp), &catInfo.nodeData);
4314
4315 /* unlock the Catalog b-tree file */
4316 (void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
4317
4318 if (retval != noErr) { /* from UpdateCatalogNode() */
4319 retval = MacToVFSError(retval);
4320 goto Err_Exit;
4321 };
4322
4323 /* After the updates are finished, clear the flags */
4324 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4325 HTOFCB(hp)->fcbFlags &= ~fcbModifiedMask;
4326
4327 /* Update general data */
4328 if (ap->a_vp->v_type == VDIR) {
4329 hp->h_meta->h_nlink = 2 + catInfo.nodeData.cnd_valence;
4330 hp->h_meta->h_size = sizeof(rootdots) +
4331 (catInfo.nodeData.cnd_valence * AVERAGE_HFSDIRENTRY_SIZE);
4332 if (hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE)
4333 hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE;
4334 } else {
4335 hp->h_meta->h_size = (off_t)vcb->blockSize *
4336 (off_t)(catInfo.nodeData.cnd_rsrcfork.totalBlocks +
4337 catInfo.nodeData.cnd_datafork.totalBlocks);
4338 }
4339
4340
4341 Err_Exit:
4342
4343 CLEAN_CATALOGDATA(&catInfo.nodeData);
4344
4345 DBG_VOP_LOCKS_TEST(retval);
4346 return (retval);
4347 }
4348
4349
4350 /*
4351 * Initialize the vnode associated with a new hfsnode,
4352 * handle aliased vnodes.
4353 */
4354 int
4355 hfs_vinit(mntp, specops, fifoops, vpp)
4356 struct mount *mntp;
4357 int (**specops)(void *);
4358 int (**fifoops)(void *);
4359 struct vnode **vpp;
4360 {
4361 struct hfsnode *hp;
4362 struct vnode *vp, *nvp;
4363
4364 vp = *vpp;
4365 hp = VTOH(vp);
4366 /* vp->v_type set in CopyCatalogToHFSNode */
4367 switch(vp->v_type) {
4368 case VCHR:
4369 case VBLK:
4370 vp->v_op = specops;
4371 if ((nvp = checkalias(vp, hp->h_meta->h_rdev, mntp))) {
4372 /*
4373 * Discard unneeded vnode, but save its hfsnode.
4374 * Note that the lock is carried over in the hfsnode
4375 * to the replacement vnode.
4376 */
4377 nvp->v_data = vp->v_data;
4378 vp->v_data = NULL;
4379 vp->v_op = spec_vnodeop_p;
4380 vrele(vp);
4381 vgone(vp);
4382 /*
4383 * Reinitialize aliased hfsnode.
4384 */
4385
4386 hp->h_vp = nvp;
4387 vp = nvp;
4388 }
4389 break;
4390 case VFIFO:
4391 #if FIFO
4392 vp->v_op = fifoops;
4393 break;
4394 #else
4395 return (EOPNOTSUPP);
4396 #endif
4397 default:
4398 break;
4399 }
4400 if (H_FILEID(hp) == kRootDirID)
4401 vp->v_flag |= VROOT;
4402
4403 *vpp = vp;
4404 return (0);
4405 }
4406
4407 /*
4408 * Allocate a new node
4409 *
4410 * Upon leaving, namei buffer must be freed.
4411 *
4412 */
4413 static int
4414 hfs_makenode(mode, rawdev, dvp, vpp, cnp, p)
4415 int mode;
4416 dev_t rawdev;
4417 struct vnode *dvp;
4418 struct vnode **vpp;
4419 struct componentname *cnp;
4420 struct proc *p;
4421 {
4422 register struct hfsnode *hp, *parhp;
4423 struct timeval tv;
4424 struct vnode *tvp;
4425 struct hfsCatalogInfo catInfo;
4426 ExtendedVCB *vcb;
4427 UInt8 forkType;
4428 int retval;
4429 int hasmetalock = 0;
4430 DBG_FUNC_NAME("makenode");
4431
4432 parhp = VTOH(dvp);
4433 vcb = HTOVCB(parhp);
4434 *vpp = NULL;
4435 tvp = NULL;
4436 if ((mode & IFMT) == 0)
4437 mode |= IFREG;
4438
4439 #if HFS_DIAGNOSTIC
4440 if ((cnp->cn_flags & HASBUF) == 0)
4441 panic("hfs_makenode: no name");
4442 #endif
4443
4444 /* lock catalog b-tree */
4445 retval = hfs_metafilelocking(VTOHFS(dvp),
4446 kHFSCatalogFileID, LK_EXCLUSIVE, p);
4447 if (retval != E_NONE)
4448 goto bad1;
4449 else
4450 hasmetalock = 1;
4451
4452 /* Create the Catalog B*-Tree entry */
4453 retval = hfsCreate(vcb, H_FILEID(parhp), cnp->cn_nameptr, mode);
4454 if (retval != E_NONE) {
4455 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
4456 goto bad1;
4457 }
4458
4459 /* Look up the catalog entry just created: */
4460 INIT_CATALOGDATA(&catInfo.nodeData, 0);
4461 catInfo.hint = kNoHint;
4462
4463 retval = hfs_getcatalog(vcb, H_FILEID(parhp), cnp->cn_nameptr, cnp->cn_namelen, &catInfo);
4464 if (retval != E_NONE) {
4465 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
4466 goto bad1;
4467 }
4468
4469 /* unlock catalog b-tree */
4470 hasmetalock = 0;
4471 (void) hfs_metafilelocking(VTOHFS(dvp),
4472 kHFSCatalogFileID, LK_RELEASE, p);
4473
4474 /* hfs plus has additional metadata to initialize */
4475 if (vcb->vcbSigWord == kHFSPlusSigWord) {
4476 u_int32_t pflags;
4477 int catmode;
4478
4479 if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
4480 catInfo.nodeData.cnd_ownerID = VTOHFS(dvp)->hfs_uid;
4481 catInfo.nodeData.cnd_groupID = VTOHFS(dvp)->hfs_gid;
4482 catmode = mode;
4483 } else {
4484 catInfo.nodeData.cnd_ownerID = cnp->cn_cred->cr_uid;
4485 catInfo.nodeData.cnd_groupID = parhp->h_meta->h_gid;
4486 catmode = mode;
4487 }
4488
4489 switch (catmode & IFMT) {
4490 case IFLNK:
4491 catInfo.nodeData.cnd_ownerID = parhp->h_meta->h_uid;
4492 break;
4493
4494 case IFCHR:
4495 case IFBLK:
4496 /* XXX should we move this to post hfsGet? */
4497 catInfo.nodeData.cnd_rawDevice = rawdev;
4498 /*
4499 * Don't tag as a special file (BLK or CHR) until *after*
4500 * hfsGet is called. This insures that the checkalias call
4501 * is defered until hfs_mknod completes.
4502 */
4503 catmode = (catmode & ~IFMT) | IFREG;
4504 break;
4505 }
4506
4507 if ((catmode & ISGID) && !groupmember(parhp->h_meta->h_gid, cnp->cn_cred) &&
4508 suser(cnp->cn_cred, NULL))
4509 catmode &= ~ISGID;
4510
4511 if (cnp->cn_flags & ISWHITEOUT)
4512 pflags = UF_OPAQUE;
4513 else
4514 pflags = 0;
4515
4516 /*
4517 * The 32-bit pflags field has two bytes of significance which
4518 * are stored separately as admin and owner flags.
4519 *
4520 * +------------------------------------+
4521 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4522 * +------------------------------------+
4523 */
4524 catInfo.nodeData.cnd_adminFlags = (pflags >> 16) & 0x00FF;
4525 catInfo.nodeData.cnd_ownerFlags = pflags & 0x00FF;
4526 catInfo.nodeData.cnd_mode = catmode;
4527 }
4528
4529 /* Create a vnode for the object just created: */
4530 forkType = (catInfo.nodeData.cnd_type == kCatalogFolderNode) ? kDirectory : kDataFork;
4531 retval = hfs_vcreate(vcb, &catInfo, forkType, &tvp);
4532
4533 CLEAN_CATALOGDATA(&catInfo.nodeData); /* Should do nothing */
4534
4535 if (retval) goto bad1; /* from hfs_vcreate() */
4536
4537 /* flush out pflags, mode, gid, uid and rdev */
4538 tv = time;
4539 if (vcb->vcbSigWord == kHFSPlusSigWord) {
4540 hp = VTOH(tvp);
4541 /* reset mode and v_type in case it was BLK/CHR */
4542 hp->h_meta->h_mode = mode;
4543 tvp->v_type = IFTOVT(mode);
4544 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
4545 hp->h_nodeflags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
4546 if ((retval = VOP_UPDATE(tvp, &tv, &tv, 1)))
4547 goto bad2;
4548 }
4549
4550 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4551 if ((retval = VOP_UPDATE(dvp, &tv, &tv, 1)))
4552 goto bad2;
4553
4554 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
4555 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
4556 };
4557 vput(dvp);
4558 if (UBCINFOMISSING(tvp) || UBCINFORECLAIMED(tvp))
4559 ubc_info_init(tvp);
4560
4561 *vpp = tvp;
4562 return (0);
4563
4564 bad2:
4565 /*
4566 * Write retval occurred trying to update the node
4567 * or the directory so must deallocate the node.
4568 */
4569 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4570 vput(tvp);
4571
4572 bad1:
4573 if (hasmetalock) {
4574 /* unlock catalog b-tree */
4575 hasmetalock = 0;
4576 (void) hfs_metafilelocking(VTOHFS(dvp),
4577 kHFSCatalogFileID, LK_RELEASE, p);
4578 }
4579 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
4580 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
4581 };
4582 vput(dvp);
4583
4584 return (retval);
4585 }
4586
4587
4588 #if DBG_VOP_TEST_LOCKS
4589
4590 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4591
4592 void DbgVopTest( int maxSlots,
4593 int retval,
4594 VopDbgStoreRec *VopDbgStore,
4595 char *funcname)
4596 {
4597 int index;
4598
4599 for (index = 0; index < maxSlots; index++)
4600 {
4601 if (VopDbgStore[index].id != index) {
4602 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index));
4603 };
4604
4605 if ((VopDbgStore[index].vp != NULL) &&
4606 ((VopDbgStore[index].vp->v_data==NULL) || (VTOH(VopDbgStore[index].vp)->h_valid != HFS_VNODE_MAGIC)))
4607 continue;
4608
4609 if (VopDbgStore[index].vp != NULL)
4610 debug_check_vnode(VopDbgStore[index].vp, 0);
4611
4612 switch (VopDbgStore[index].inState)
4613 {
4614 case VOPDBG_IGNORE:
4615 case VOPDBG_SAME:
4616 /* Do Nothing !!! */
4617 break;
4618 case VOPDBG_LOCKED:
4619 case VOPDBG_UNLOCKED:
4620 case VOPDBG_LOCKNOTNIL:
4621 {
4622 if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
4623 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index));
4624 } else if (VopDbgStore[index].vp != NULL) {
4625 switch (VopDbgStore[index].inState)
4626 {
4627 case VOPDBG_LOCKED:
4628 case VOPDBG_LOCKNOTNIL:
4629 if (VopDbgStore[index].inValue == 0)
4630 {
4631 DBG_ERR (("%s: Entry: not LOCKED:", funcname));
4632 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4633 DBG_ERR (("\n"));
4634 }
4635 break;
4636 case VOPDBG_UNLOCKED:
4637 if (VopDbgStore[index].inValue != 0)
4638 {
4639 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname));
4640 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4641 DBG_ERR (("\n"));
4642 }
4643 break;
4644 }
4645 }
4646 break;
4647 }
4648 default:
4649 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
4650 }
4651
4652
4653 if (retval != 0)
4654 {
4655 switch (VopDbgStore[index].errState)
4656 {
4657 case VOPDBG_IGNORE:
4658 /* Do Nothing !!! */
4659 break;
4660 case VOPDBG_LOCKED:
4661 case VOPDBG_UNLOCKED:
4662 case VOPDBG_SAME:
4663 {
4664 if (VopDbgStore[index].vp == NULL) {
4665 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index));
4666 } else {
4667 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4668 switch (VopDbgStore[index].errState)
4669 {
4670 case VOPDBG_LOCKED:
4671 if (VopDbgStore[index].outValue == 0)
4672 {
4673 DBG_ERR (("%s: Error: not LOCKED:", funcname));
4674 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4675 DBG_ERR(("\n"));
4676 }
4677 break;
4678 case VOPDBG_UNLOCKED:
4679 if (VopDbgStore[index].outValue != 0)
4680 {
4681 DBG_ERR (("%s: Error: not UNLOCKED:", funcname));
4682 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4683 DBG_ERR(("\n"));
4684 }
4685 break;
4686 case VOPDBG_SAME:
4687 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
4688 DBG_ERR (("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
4689 break;
4690 }
4691 }
4692 break;
4693 }
4694 case VOPDBG_LOCKNOTNIL:
4695 if (VopDbgStore[index].vp != NULL) {
4696 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4697 if (VopDbgStore[index].outValue == 0)
4698 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
4699 }
4700 break;
4701 default:
4702 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
4703 }
4704 }
4705 else
4706 {
4707 switch (VopDbgStore[index].outState)
4708 {
4709 case VOPDBG_IGNORE:
4710 /* Do Nothing !!! */
4711 break;
4712 case VOPDBG_LOCKED:
4713 case VOPDBG_UNLOCKED:
4714 case VOPDBG_SAME:
4715 if (VopDbgStore[index].vp == NULL) {
4716 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index));
4717 };
4718 if (VopDbgStore[index].vp != NULL)
4719 {
4720 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4721 switch (VopDbgStore[index].outState)
4722 {
4723 case VOPDBG_LOCKED:
4724 if (VopDbgStore[index].outValue == 0)
4725 {
4726 DBG_ERR (("%s: Out: not LOCKED:", funcname));
4727 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4728 DBG_ERR (("\n"));
4729 }
4730 break;
4731 case VOPDBG_UNLOCKED:
4732 if (VopDbgStore[index].outValue != 0)
4733 {
4734 DBG_ERR (("%s: Out: not UNLOCKED:", funcname));
4735 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4736 DBG_ERR (("\n"));
4737 }
4738 break;
4739 case VOPDBG_SAME:
4740 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
4741 DBG_ERR (("%s: Out: In/Out locks are DIFFERENT: 0x%x, in is %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
4742 break;
4743 }
4744 }
4745 break;
4746 case VOPDBG_LOCKNOTNIL:
4747 if (VopDbgStore[index].vp != NULL) {
4748 if (&VTOH(VopDbgStore[index].vp)->h_lock == NULL) {
4749 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
4750 }
4751 else {
4752 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4753 if (VopDbgStore[index].outValue == 0)
4754 {
4755 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname));
4756 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); DBG_ERR (("\n"));
4757 }
4758 }
4759 }
4760 break;
4761 default:
4762 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState));
4763 }
4764 }
4765
4766 VopDbgStore[index].id = -1; /* Invalidate the entry to allow panic-free re-use */
4767 }
4768 }
4769
4770 #endif /* DBG_VOP_TEST_LOCKS */
4771
4772 /*
4773 * Wrapper for special device reads
4774 */
4775 int
4776 hfsspec_read(ap)
4777 struct vop_read_args /* {
4778 struct vnode *a_vp;
4779 struct uio *a_uio;
4780 int a_ioflag;
4781 struct ucred *a_cred;
4782 } */ *ap;
4783 {
4784
4785 /*
4786 * Set access flag.
4787 */
4788 VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
4789 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
4790 }
4791
4792 /*
4793 * Wrapper for special device writes
4794 */
4795 int
4796 hfsspec_write(ap)
4797 struct vop_write_args /* {
4798 struct vnode *a_vp;
4799 struct uio *a_uio;
4800 int a_ioflag;
4801 struct ucred *a_cred;
4802 } */ *ap;
4803 {
4804
4805 /*
4806 * Set update and change flags.
4807 */
4808 VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4809 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
4810 }
4811
4812 /*
4813 * Wrapper for special device close
4814 *
4815 * Update the times on the hfsnode then do device close.
4816 */
4817 int
4818 hfsspec_close(ap)
4819 struct vop_close_args /* {
4820 struct vnode *a_vp;
4821 int a_fflag;
4822 struct ucred *a_cred;
4823 struct proc *a_p;
4824 } */ *ap;
4825 {
4826 struct vnode *vp = ap->a_vp;
4827 struct hfsnode *hp = VTOH(vp);
4828
4829 simple_lock(&vp->v_interlock);
4830 if (ap->a_vp->v_usecount > 1)
4831 HFSTIMES(hp, &time, &time);
4832 simple_unlock(&vp->v_interlock);
4833 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
4834 }
4835
4836 #if FIFO
4837 /*
4838 * Wrapper for fifo reads
4839 */
4840 int
4841 hfsfifo_read(ap)
4842 struct vop_read_args /* {
4843 struct vnode *a_vp;
4844 struct uio *a_uio;
4845 int a_ioflag;
4846 struct ucred *a_cred;
4847 } */ *ap;
4848 {
4849 extern int (**fifo_vnodeop_p)(void *);
4850
4851 /*
4852 * Set access flag.
4853 */
4854 VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
4855 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
4856 }
4857
4858 /*
4859 * Wrapper for fifo writes
4860 */
4861 int
4862 hfsfifo_write(ap)
4863 struct vop_write_args /* {
4864 struct vnode *a_vp;
4865 struct uio *a_uio;
4866 int a_ioflag;
4867 struct ucred *a_cred;
4868 } */ *ap;
4869 {
4870 extern int (**fifo_vnodeop_p)(void *);
4871
4872 /*
4873 * Set update and change flags.
4874 */
4875 VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4876 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
4877 }
4878
4879 /*
4880 * Wrapper for fifo close
4881 *
4882 * Update the times on the hfsnode then do device close.
4883 */
4884 int
4885 hfsfifo_close(ap)
4886 struct vop_close_args /* {
4887 struct vnode *a_vp;
4888 int a_fflag;
4889 struct ucred *a_cred;
4890 struct proc *a_p;
4891 } */ *ap;
4892 {
4893 extern int (**fifo_vnodeop_p)(void *);
4894 struct vnode *vp = ap->a_vp;
4895 struct hfsnode *hp = VTOH(vp);
4896
4897 simple_lock(&vp->v_interlock);
4898 if (ap->a_vp->v_usecount > 1)
4899 HFSTIMES(hp, &time, &time);
4900 simple_unlock(&vp->v_interlock);
4901 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
4902 }
4903 #endif /* FIFO */
4904
4905
4906 /*****************************************************************************
4907 *
4908 * VOP Tables
4909 *
4910 *****************************************************************************/
4911
4912 #define VOPFUNC int (*)(void *)
4913
4914 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
4915 { &vop_default_desc, (VOPFUNC)vn_default_error },
4916 { &vop_lookup_desc, (VOPFUNC)hfs_cache_lookup }, /* lookup */
4917 { &vop_create_desc, (VOPFUNC)hfs_create }, /* create */
4918 { &vop_mknod_desc, (VOPFUNC)hfs_mknod }, /* mknod */
4919 { &vop_open_desc, (VOPFUNC)hfs_open }, /* open */
4920 { &vop_close_desc, (VOPFUNC)hfs_close }, /* close */
4921 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4922 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4923 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4924 { &vop_read_desc, (VOPFUNC)hfs_read }, /* read */
4925 { &vop_write_desc, (VOPFUNC)hfs_write }, /* write */
4926 { &vop_ioctl_desc, (VOPFUNC)hfs_ioctl }, /* ioctl */
4927 { &vop_select_desc, (VOPFUNC)hfs_select }, /* select */
4928 { &vop_exchange_desc, (VOPFUNC)hfs_exchange }, /* exchange */
4929 { &vop_mmap_desc, (VOPFUNC)hfs_mmap }, /* mmap */
4930 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4931 { &vop_seek_desc, (VOPFUNC)hfs_seek }, /* seek */
4932 { &vop_remove_desc, (VOPFUNC)hfs_remove }, /* remove */
4933 #if HFS_HARDLINKS
4934 { &vop_link_desc, (VOPFUNC)hfs_link }, /* link */
4935 #else
4936 { &vop_link_desc, (VOPFUNC)err_link }, /* link (NOT SUPPORTED) */
4937 #endif
4938 { &vop_rename_desc, (VOPFUNC)hfs_rename }, /* rename */
4939 { &vop_mkdir_desc, (VOPFUNC)hfs_mkdir }, /* mkdir */
4940 { &vop_rmdir_desc, (VOPFUNC)hfs_rmdir }, /* rmdir */
4941 { &vop_mkcomplex_desc, (VOPFUNC)hfs_mkcomplex }, /* mkcomplex */
4942 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist }, /* getattrlist */
4943 { &vop_setattrlist_desc, (VOPFUNC)hfs_setattrlist }, /* setattrlist */
4944 { &vop_symlink_desc, (VOPFUNC)hfs_symlink }, /* symlink */
4945 { &vop_readdir_desc, (VOPFUNC)hfs_readdir }, /* readdir */
4946 { &vop_readdirattr_desc, (VOPFUNC)hfs_readdirattr }, /* readdirattr */
4947 { &vop_readlink_desc, (VOPFUNC)hfs_readlink }, /* readlink */
4948 { &vop_abortop_desc, (VOPFUNC)hfs_abortop }, /* abortop */
4949 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4950 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4951 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4952 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4953 { &vop_bmap_desc, (VOPFUNC)hfs_bmap }, /* bmap */
4954 { &vop_strategy_desc, (VOPFUNC)hfs_strategy }, /* strategy */
4955 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
4956 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
4957 { &vop_pathconf_desc, (VOPFUNC)hfs_pathconf }, /* pathconf */
4958 { &vop_advlock_desc, (VOPFUNC)hfs_advlock }, /* advlock */
4959 { &vop_reallocblks_desc, (VOPFUNC)hfs_reallocblks }, /* reallocblks */
4960 { &vop_truncate_desc, (VOPFUNC)hfs_truncate }, /* truncate */
4961 { &vop_allocate_desc, (VOPFUNC)hfs_allocate }, /* allocate */
4962 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
4963 { &vop_searchfs_desc, (VOPFUNC)hfs_search }, /* search fs */
4964 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite }, /* bwrite */
4965 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* pagein */
4966 { &vop_pageout_desc,(VOPFUNC) hfs_pageout }, /* pageout */
4967 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
4968 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
4969 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
4970 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
4971 { NULL, (VOPFUNC)NULL }
4972 };
4973
4974 struct vnodeopv_desc hfs_vnodeop_opv_desc =
4975 { &hfs_vnodeop_p, hfs_vnodeop_entries };
4976
4977 int (**hfs_specop_p)(void *);
4978 struct vnodeopv_entry_desc hfs_specop_entries[] = {
4979 { &vop_default_desc, (VOPFUNC)vn_default_error },
4980 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
4981 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
4982 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
4983 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
4984 { &vop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
4985 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4986 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4987 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4988 { &vop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
4989 { &vop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
4990 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
4991 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
4992 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
4993 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
4994 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
4995 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4996 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
4997 { &vop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
4998 { &vop_link_desc, (VOPFUNC)spec_link }, /* link */
4999 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
5000 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
5001 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
5002 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
5003 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
5004 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
5005 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
5006 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
5007 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
5008 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
5009 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
5010 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
5011 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
5012 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
5013 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
5014 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
5015 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
5016 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
5017 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
5018 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
5019 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
5020 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
5021 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
5022 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
5023 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
5024 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
5025 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
5026 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
5027 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
5028 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
5029 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
5030 };
5031 struct vnodeopv_desc hfs_specop_opv_desc =
5032 { &hfs_specop_p, hfs_specop_entries };
5033
5034 #if FIFO
5035 int (**hfs_fifoop_p)(void *);
5036 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
5037 { &vop_default_desc, (VOPFUNC)vn_default_error },
5038 { &vop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
5039 { &vop_create_desc, (VOPFUNC)fifo_create }, /* create */
5040 { &vop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
5041 { &vop_open_desc, (VOPFUNC)fifo_open }, /* open */
5042 { &vop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
5043 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
5044 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
5045 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
5046 { &vop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
5047 { &vop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
5048 { &vop_lease_desc, (VOPFUNC)fifo_lease_check }, /* lease */
5049 { &vop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
5050 { &vop_select_desc, (VOPFUNC)fifo_select }, /* select */
5051 { &vop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
5052 { &vop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
5053 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
5054 { &vop_seek_desc, (VOPFUNC)fifo_seek }, /* seek */
5055 { &vop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
5056 { &vop_link_desc, (VOPFUNC)fifo_link }, /* link */
5057 { &vop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
5058 { &vop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
5059 { &vop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
5060 { &vop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
5061 { &vop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
5062 { &vop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
5063 { &vop_abortop_desc, (VOPFUNC)fifo_abortop }, /* abortop */
5064 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
5065 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
5066 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
5067 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
5068 { &vop_bmap_desc, (VOPFUNC)fifo_bmap }, /* bmap */
5069 { &vop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
5070 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
5071 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
5072 { &vop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
5073 { &vop_advlock_desc, (VOPFUNC)fifo_advlock }, /* advlock */
5074 { &vop_blkatoff_desc, (VOPFUNC)fifo_blkatoff }, /* blkatoff */
5075 { &vop_valloc_desc, (VOPFUNC)fifo_valloc }, /* valloc */
5076 { &vop_reallocblks_desc, (VOPFUNC)fifo_reallocblks }, /* reallocblks */
5077 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
5078 { &vop_truncate_desc, (VOPFUNC)fifo_truncate }, /* truncate */
5079 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
5080 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
5081 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
5082 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
5083 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
5084 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
5085 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
5086 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
5087 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
5088 };
5089 struct vnodeopv_desc hfs_fifoop_opv_desc =
5090 { &hfs_fifoop_p, hfs_fifoop_entries };
5091 #endif /* FIFO */
5092
5093
5094