]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnodeops.c
14a784ee927516ce2a06de0d4e7b399066dc2f71
[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 /* do we have permission to change the dates? */
1318 // if (alist->commonattr & (ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME | ATTR_CMN_BKUPTIME)) {
1319 if (alist->commonattr & (ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME)) {
1320 if ((error = hfs_owner_rights(vp, cred, p, true)) != 0) {
1321 goto ErrorExit;
1322 };
1323 };
1324
1325 /* save these in case hfs_chown() or hfs_chmod() fail */
1326 saved_uid = hp->h_meta->h_uid;
1327 saved_gid = hp->h_meta->h_gid;
1328 saved_mode = hp->h_meta->h_mode;
1329 saved_flags = hp->h_meta->h_pflags;
1330
1331 attrptr = attrbufptr;
1332 UnpackAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
1333
1334 /* if unpacking changed the owner or group then call hfs_chown() */
1335 if (saved_uid != hp->h_meta->h_uid || saved_gid != hp->h_meta->h_gid) {
1336 uid_t uid;
1337 gid_t gid;
1338
1339 uid = hp->h_meta->h_uid;
1340 hp->h_meta->h_uid = saved_uid;
1341 gid = hp->h_meta->h_gid;
1342 hp->h_meta->h_gid = saved_gid;
1343 if ((error = hfs_chown(vp, uid, gid, cred, p)))
1344 goto ErrorExit;
1345 }
1346
1347 /* if unpacking changed the mode then call hfs_chmod() */
1348 if (saved_mode != hp->h_meta->h_mode) {
1349 mode_t mode;
1350
1351 mode = hp->h_meta->h_mode;
1352 hp->h_meta->h_mode = saved_mode;
1353 if ((error = hfs_chmod(vp, mode, cred, p)))
1354 goto ErrorExit;
1355 };
1356
1357 /* if unpacking changed the flags then call hfs_chflags */
1358 if (saved_flags != hp->h_meta->h_pflags) {
1359 u_long flags;
1360
1361 flags = hp->h_meta->h_pflags;
1362 hp->h_meta->h_pflags = saved_flags;
1363 if ((error = hfs_chflags(vp, flags, cred, p)))
1364 goto ErrorExit;
1365 };
1366
1367 if (alist->volattr == 0) {
1368 error = MacToVFSError( UpdateCatalogNode(HTOVCB(hp), pid, filename, H_HINT(hp), &catInfo.nodeData));
1369 }
1370
1371 if (alist->volattr & ATTR_VOL_NAME) {
1372 ExtendedVCB *vcb = VTOVCB(vp);
1373 int namelen = strlen(vcb->vcbVN);
1374
1375 if (vcb->vcbVN[0] == 0) {
1376 /*
1377 Ignore attempts to rename a volume to a zero-length name:
1378 restore the original name from the metadata.
1379 */
1380 copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
1381 } else {
1382 error = MoveRenameCatalogNode(vcb, kRootParID, H_NAME(hp), H_HINT(hp), kRootParID, vcb->vcbVN, &H_HINT(hp));
1383 if (error) {
1384 VCB_LOCK(vcb);
1385 copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL); /* Restore the old name in the VCB */
1386 vcb->vcbFlags |= 0xFF00; // Mark the VCB dirty
1387 VCB_UNLOCK(vcb);
1388 goto ErrorExit;
1389 };
1390
1391 hfs_set_metaname(vcb->vcbVN, hp->h_meta, HTOHFS(hp));
1392 hp->h_nodeflags |= IN_CHANGE;
1393
1394 #if 0
1395 /* if hfs wrapper exists, update its name too */
1396 if (vcb->vcbSigWord == kHFSPlusSigWord && vcb->vcbAlBlSt != 0) {
1397 HFSMasterDirectoryBlock *mdb;
1398 struct buf *bp = NULL;
1399 int size = kMDBSize; /* 512 */
1400 int volnamelen = MIN(sizeof(Str27), namelen);
1401
1402 if ( bread(VTOHFS(vp)->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size),
1403 IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp) == 0) {
1404
1405 mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size));
1406 if (SWAP_BE16 (mdb->drSigWord) == kHFSSigWord) {
1407 /* Convert the string to MacRoman, ignoring any errors, */
1408 (void) utf8_to_hfs(vcb, volnamelen, vcb->vcbVN, Str31 mdb->drVN)
1409 bawrite(bp);
1410 bp = NULL;
1411 }
1412 }
1413
1414 if (bp) brelse(bp);
1415 }
1416 #endif
1417 }; /* vcb->vcbVN[0] == 0 ... else ... */
1418 }; /* alist->volattr & ATTR_VOL_NAME */
1419
1420 ErrorExit:
1421 /* unlock catalog b-tree */
1422 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
1423
1424 CLEAN_CATALOGDATA(&catInfo.nodeData);
1425
1426 FreeBuffer:
1427 if (attrbufptr) FREE(attrbufptr, M_TEMP);
1428
1429 DBG_VOP_LOCKS_TEST(error);
1430 return error;
1431 }
1432
1433 /*
1434 * Change the mode on a file.
1435 * Inode must be locked before calling.
1436 */
1437 static int
1438 hfs_chmod(vp, mode, cred, p)
1439 register struct vnode *vp;
1440 register int mode;
1441 register struct ucred *cred;
1442 struct proc *p;
1443 {
1444 register struct hfsnode *hp = VTOH(vp);
1445 int retval;
1446
1447 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1448 return E_NONE;
1449
1450 #if OVERRIDE_UNKNOWN_PERMISSIONS
1451 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
1452 return E_NONE;
1453 };
1454 #endif
1455
1456 if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0)
1457 return (retval);
1458 if (cred->cr_uid) {
1459 if (vp->v_type != VDIR && (mode & S_ISTXT))
1460 return (EFTYPE);
1461 if (!groupmember(hp->h_meta->h_gid, cred) && (mode & ISGID))
1462 return (EPERM);
1463 }
1464 hp->h_meta->h_mode &= ~ALLPERMS;
1465 hp->h_meta->h_mode |= (mode & ALLPERMS);
1466 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1467 hp->h_nodeflags |= IN_CHANGE;
1468 return (0);
1469 }
1470
1471
1472 static int
1473 hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
1474 {
1475 struct hfsnode *hp = VTOH(vp);
1476 ExtendedVCB *vcb = HTOVCB(hp);
1477 gid_t *gp;
1478 Boolean isHFSPlus;
1479 int retval = E_NONE;
1480 int i;
1481
1482 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord );
1483
1484 /*
1485 * Disallow write attempts on read-only file systems;
1486 * unless the file is a socket, fifo, or a block or
1487 * character device resident on the file system.
1488 */
1489 switch (vp->v_type) {
1490 case VDIR:
1491 case VLNK:
1492 case VREG:
1493 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
1494 return (EROFS);
1495 break;
1496 default:
1497 break;
1498 }
1499
1500 /* If immutable bit set, nobody gets to write it. */
1501 if (considerFlags && (hp->h_meta->h_pflags & IMMUTABLE))
1502 return (EPERM);
1503
1504 /* Otherwise, user id 0 always gets access. */
1505 if (cred->cr_uid == 0) {
1506 retval = 0;
1507 goto Exit;
1508 };
1509
1510 /* Otherwise, check the owner. */
1511 if ((retval = hfs_owner_rights(vp, cred, p, false)) == 0) {
1512 retval = ((hp->h_meta->h_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
1513 goto Exit;
1514 }
1515
1516 /* Otherwise, check the groups. */
1517 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
1518 if (hp->h_meta->h_gid == *gp) {
1519 retval = ((hp->h_meta->h_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
1520 goto Exit;
1521 }
1522
1523 /* Otherwise, check everyone else. */
1524 retval = ((hp->h_meta->h_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
1525
1526 Exit:
1527 return (retval);
1528 }
1529
1530
1531
1532 /*
1533 * Change the flags on a file or directory.
1534 * Inode must be locked before calling.
1535 */
1536 static int
1537 hfs_chflags(vp, flags, cred, p)
1538 register struct vnode *vp;
1539 register u_long flags;
1540 register struct ucred *cred;
1541 struct proc *p;
1542 {
1543 register struct hfsnode *hp = VTOH(vp);
1544 int retval;
1545
1546 if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) {
1547 if ((retval = hfs_write_access(vp, cred, p, false)) != 0) {
1548 return retval;
1549 };
1550 } else if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0) {
1551 return retval;
1552 };
1553
1554 if (cred->cr_uid == 0) {
1555 if ((hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND)) &&
1556 securelevel > 0) {
1557 return EPERM;
1558 };
1559 hp->h_meta->h_pflags = flags;
1560 } else {
1561 if (hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND) ||
1562 (flags & UF_SETTABLE) != flags) {
1563 return EPERM;
1564 };
1565 hp->h_meta->h_pflags &= SF_SETTABLE;
1566 hp->h_meta->h_pflags |= (flags & UF_SETTABLE);
1567 }
1568 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1569 hp->h_nodeflags |= IN_CHANGE;
1570
1571 return 0;
1572 }
1573
1574
1575 /*
1576 * Perform chown operation on hfsnode hp;
1577 * hfsnode must be locked prior to call.
1578 */
1579 static int
1580 hfs_chown(vp, uid, gid, cred, p)
1581 register struct vnode *vp;
1582 uid_t uid;
1583 gid_t gid;
1584 struct ucred *cred;
1585 struct proc *p;
1586 {
1587 register struct hfsnode *hp = VTOH(vp);
1588 uid_t ouid;
1589 gid_t ogid;
1590 int retval = 0;
1591
1592 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1593 return EOPNOTSUPP;
1594
1595 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
1596 return E_NONE;
1597 };
1598
1599 if (uid == (uid_t)VNOVAL)
1600 uid = hp->h_meta->h_uid;
1601 if (gid == (gid_t)VNOVAL)
1602 gid = hp->h_meta->h_gid;
1603 /*
1604 * If we don't own the file, are trying to change the owner
1605 * of the file, or are not a member of the target group,
1606 * the caller must be superuser or the call fails.
1607 */
1608 if ((cred->cr_uid != hp->h_meta->h_uid || uid != hp->h_meta->h_uid ||
1609 (gid != hp->h_meta->h_gid && !groupmember((gid_t)gid, cred))) &&
1610 (retval = suser(cred, &p->p_acflag)))
1611 return (retval);
1612
1613 ogid = hp->h_meta->h_gid;
1614 ouid = hp->h_meta->h_uid;
1615
1616 hp->h_meta->h_gid = gid;
1617 hp->h_meta->h_uid = uid;
1618
1619 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
1620 if (ouid != uid || ogid != gid)
1621 hp->h_nodeflags |= IN_CHANGE;
1622 if (ouid != uid && cred->cr_uid != 0)
1623 hp->h_meta->h_mode &= ~ISUID;
1624 if (ogid != gid && cred->cr_uid != 0)
1625 hp->h_meta->h_mode &= ~ISGID;
1626 return (0);
1627 }
1628
1629
1630
1631 /*
1632 #
1633 #% exchange fvp L L L
1634 #% exchange tvp L L L
1635 #
1636 vop_exchange {
1637 IN struct vnode *fvp;
1638 IN struct vnode *tvp;
1639 IN struct ucred *cred;
1640 IN struct proc *p;
1641 };
1642
1643 */
1644 /*
1645 * exchange is a very tricky routine, because we might have to unlock the
1646 * passed in vnode, and then retry locking it and all its siblings, and then
1647 * unlocking them in reverse.
1648 * Also the sibling list lock must be kept during the whole operation to
1649 * make sure nothing changes underneath us.
1650 * Also it depends on behavior of the sibling list and hash, so
1651 * careful if you change anything.
1652 */
1653
1654 static int
1655 hfs_exchange(ap)
1656 struct vop_exchange_args /* {
1657 struct vnode *a_fvp;
1658 struct vnode *a_tvp;
1659 struct ucred *a_cred;
1660 struct proc *a_p;
1661 } */ *ap;
1662 {
1663 struct hfsnode *from_hp, *to_hp, *nhp;
1664 struct hfsnode *fromFirst, *fromSecond, *toFirst, *toSecond;
1665 struct vnode *from_vp, *to_vp;
1666 struct hfsmount *hfsmp;
1667 u_char tmp_name[kHFSPlusMaxFileNameBytes+1]; /* 766 bytes! */
1668 ExtendedVCB *vcb;
1669 u_int32_t fromFileID, toFileID;
1670 u_int32_t fromParID;
1671 u_int32_t tmpLong;
1672 int retval = E_NONE;
1673 DBG_FUNC_NAME("exchange");
1674 DBG_VOP_LOCKS_DECL(2);
1675 DBG_VOP_LOCKS_INIT(0,ap->a_fvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1676 DBG_VOP_LOCKS_INIT(1,ap->a_tvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
1677
1678 /* Set up variables and checks */
1679 from_vp = ap->a_fvp;
1680 to_vp = ap->a_tvp;
1681 from_hp = VTOH(from_vp);
1682 to_hp = VTOH(to_vp);
1683 hfsmp = VTOHFS(from_vp);
1684 vcb = HTOVCB(from_hp);
1685 toFileID = H_FILEID(to_hp);
1686 fromFileID = H_FILEID(from_hp);
1687 fromParID = H_DIRID(from_hp);
1688
1689 if (from_vp->v_mount != to_vp->v_mount) {
1690 DBG_VOP_LOCKS_TEST(EXDEV);
1691 return EXDEV;
1692 }
1693
1694 /* Can only exchange file objects */
1695 if (from_vp->v_type != VREG || to_vp->v_type != VREG) {
1696 DBG_VOP_LOCKS_TEST(EINVAL);
1697 return EINVAL;
1698 }
1699
1700 /*
1701 * Lock the siblink list
1702 * Check for multiple forks
1703 * If there are, we would need to:
1704 * 1. Unlock ourselves
1705 * 3. Traverse the list in a forward order...locking all vnodes
1706 * 4. Flush all buffers
1707 * 5. Perform the exchange
1708 * 6. Traverse the list in a reverse order...unlocking all vnodes, except orignal
1709 * Notice that the sibling lock is kept during the whole operation. This quarentees
1710 * that no new forks are taken off or put on
1711 */
1712 DBG_ASSERT(H_FORKTYPE(from_hp)==kDataFork && H_FORKTYPE(to_hp)==kDataFork);
1713 fromFirst = fromSecond = toFirst = toSecond = NULL;
1714
1715 if (from_hp->h_meta->h_usecount > 1) {
1716 /*
1717 * This has siblings, so remember the passed-in vnode,
1718 * unlock it if it is not the 'first' sibling,
1719 * and then lock the rest of the vnodes by sibling order.
1720 * Notice that the passed-in vnode is not vrele(), this
1721 * keeps the usecount>0, so it wont go away.
1722 */
1723 simple_lock(&from_hp->h_meta->h_siblinglock);
1724 fromFirst = from_hp->h_meta->h_siblinghead.cqh_first;
1725 fromSecond = fromFirst->h_sibling.cqe_next;
1726 simple_unlock(&from_hp->h_meta->h_siblinglock);
1727
1728 if (fromFirst == from_hp) {
1729 if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1730 fromSecond = NULL; /* its going away */
1731 } else {
1732 VOP_UNLOCK(HTOV(from_hp), 0, ap->a_p);
1733 if (vget(HTOV(fromFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1734 fromFirst = NULL; /* its going away */
1735 if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1736 fromSecond = NULL; /* its going away */
1737 };
1738
1739 } else {
1740 fromFirst = from_hp;
1741 };
1742
1743 if (to_hp->h_meta->h_usecount > 1) {
1744
1745 simple_lock(&to_hp->h_meta->h_siblinglock);
1746 toFirst = to_hp->h_meta->h_siblinghead.cqh_first;
1747 toSecond = toFirst->h_sibling.cqe_next;
1748 simple_unlock(&to_hp->h_meta->h_siblinglock);
1749
1750 if (toFirst == to_hp) {
1751 if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1752 toSecond = NULL; /* its going away */
1753 } else {
1754 VOP_UNLOCK(HTOV(to_hp), 0, ap->a_p);
1755 if (vget(HTOV(toFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1756 toFirst = NULL; /* its going away */
1757 if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
1758 toSecond = NULL; /* its going away */
1759 };
1760
1761 } else {
1762 toFirst = to_hp;
1763 };
1764
1765
1766 /* Ignore any errors, we are doing a 'best effort' on flushing */
1767 if (fromFirst)
1768 (void) vinvalbuf(HTOV(fromFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1769 if (fromSecond)
1770 (void) vinvalbuf(HTOV(fromSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1771 if (toFirst)
1772 (void) vinvalbuf(HTOV(toFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1773 if (toSecond)
1774 (void) vinvalbuf(HTOV(toSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
1775
1776
1777 /* lock catalog b-tree */
1778 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
1779 if (retval) goto Err_Exit;
1780
1781 /* lock extents b-tree iff there are overflow extents */
1782 /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
1783 so we neeed the tree to be always locked.
1784 */
1785 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1786 if (retval) goto Err_Exit_Relse;
1787
1788 /* Do the exchange */
1789 retval = MacToVFSError( ExchangeFileIDs(vcb, H_NAME(from_hp), H_NAME(to_hp), H_DIRID(from_hp), H_DIRID(to_hp), H_HINT(from_hp), H_HINT(to_hp) ));
1790
1791 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1792
1793 if (retval != E_NONE) {
1794 DBG_ERR(("/tError trying to exchange: %d\n", retval));
1795 goto Err_Exit_Relse;
1796 }
1797
1798
1799 /* Purge the vnodes from the name */
1800 if (fromFirst)
1801 cache_purge(HTOV(fromFirst));
1802 if (fromSecond)
1803 cache_purge(HTOV(fromSecond));
1804 if (toFirst)
1805 cache_purge(HTOV(toFirst));
1806 if (toSecond)
1807 cache_purge(HTOV(toSecond));
1808
1809 /* Now exchange fileID, parID, name for the vnode itself */
1810 copystr(H_NAME(from_hp), (char*) tmp_name, strlen(H_NAME(from_hp))+1, NULL);
1811 hfs_chid(from_hp, toFileID, H_DIRID(to_hp), H_NAME(to_hp));
1812 hfs_chid(to_hp, fromFileID, fromParID, (char*) tmp_name);
1813
1814 /* copy rest */
1815 tmpLong = HTOFCB(from_hp)->fcbFlags;
1816 HTOFCB(from_hp)->fcbFlags = HTOFCB(to_hp)->fcbFlags;
1817 HTOFCB(to_hp)->fcbFlags = tmpLong;
1818
1819 tmpLong = from_hp->h_meta->h_crtime;
1820 from_hp->h_meta->h_crtime = to_hp->h_meta->h_crtime;
1821 to_hp->h_meta->h_crtime = tmpLong;
1822
1823 tmpLong = from_hp->h_meta->h_butime;
1824 from_hp->h_meta->h_butime = to_hp->h_meta->h_butime;
1825 to_hp->h_meta->h_butime = tmpLong;
1826
1827 tmpLong = from_hp->h_meta->h_atime;
1828 from_hp->h_meta->h_atime = to_hp->h_meta->h_atime;
1829 to_hp->h_meta->h_atime = tmpLong;
1830
1831 tmpLong = from_hp->h_meta->h_ctime;
1832 from_hp->h_meta->h_ctime = to_hp->h_meta->h_ctime;
1833 to_hp->h_meta->h_ctime = tmpLong;
1834
1835 tmpLong = from_hp->h_meta->h_gid;
1836 from_hp->h_meta->h_gid = to_hp->h_meta->h_gid;
1837 to_hp->h_meta->h_gid = tmpLong;
1838
1839 tmpLong = from_hp->h_meta->h_uid;
1840 from_hp->h_meta->h_uid = to_hp->h_meta->h_uid;
1841 to_hp->h_meta->h_uid = tmpLong;
1842
1843 tmpLong = from_hp->h_meta->h_pflags;
1844 from_hp->h_meta->h_pflags = to_hp->h_meta->h_pflags;
1845 to_hp->h_meta->h_pflags = tmpLong;
1846
1847 tmpLong = from_hp->h_meta->h_mode;
1848 from_hp->h_meta->h_mode = to_hp->h_meta->h_mode;
1849 to_hp->h_meta->h_mode = tmpLong;
1850
1851 tmpLong = from_hp->h_meta->h_rdev;
1852 from_hp->h_meta->h_rdev = to_hp->h_meta->h_rdev;
1853 to_hp->h_meta->h_rdev = tmpLong;
1854
1855 tmpLong = from_hp->h_meta->h_size;
1856 from_hp->h_meta->h_size = to_hp->h_meta->h_size;
1857 to_hp->h_meta->h_size = tmpLong;
1858
1859
1860
1861 Err_Exit_Relse:
1862
1863 /* unlock catalog b-tree */
1864 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1865
1866
1867 Err_Exit:
1868
1869
1870 /* XXX SER
1871 * At this point, the vnodes' data is switched, but are on the old hash list.
1872 * so move them to the right bucket. This couldnt be done until now, because the h_siblinglock
1873 * was being held.
1874 * Scenario:
1875 * A fork is trying to be added while exchanging...It got the hash lock,
1876 * but is waiting for the h_siblinglock. So we cannot try get the hash lock
1877 * until we release h_siblinglock, so it could continue, so it adds to the sibling list
1878 * and at the old place, so hfs_vhashmove has to move all vnodes with the old file id.
1879 * Not very pretty, becarefull that this works ok
1880 * Scenario 2:
1881 * Same as the above, but before the move is made (like at this very spot), the new vnode
1882 * is added and a vget is requested for that new vnode, it would have old data
1883 * WE MIGHT NEED TO LOCK THE HASH BECAUSE OF THIS !!!
1884 * Scenario 3:
1885 * Hey! Same as above, but it is added after all the moving
1886 * So now there is a vnode with the old data, on the old hash...it will become
1887 * lost next time that a vget() is done
1888 *
1889 * XXX SER A solution might be to NOT move the hash, but the data (extents) or the
1890 * opposite that we are doing now
1891 */
1892 hfs_vhashmove(from_hp, fromFileID);
1893 hfs_vhashmove(to_hp, toFileID);
1894
1895
1896 #if HFS_DIAGNOSTIC
1897 if (fromFirst)
1898 debug_check_vnode(HTOV(fromFirst), 0);
1899 if (fromSecond)
1900 debug_check_vnode(HTOV(fromSecond), 0);
1901 if (toFirst)
1902 debug_check_vnode(HTOV(toFirst), 0);
1903 if (toSecond)
1904 debug_check_vnode(HTOV(toSecond), 0);
1905 #endif
1906
1907
1908 /* Unlock any forks, and the sibling list */
1909 if (to_hp->h_meta->h_usecount > 1) {
1910 if (to_hp == toFirst) {
1911 if (toSecond)
1912 vput(HTOV(toSecond));
1913 } else {
1914 if (toSecond)
1915 vrele(HTOV(toSecond)); /* decrement, return it locked */
1916 if (toFirst)
1917 vput(HTOV(toFirst));
1918 }
1919 }
1920 if (from_hp->h_meta->h_usecount > 1) {
1921 if (from_hp == fromFirst) {
1922 if (fromSecond)
1923 vput(HTOV(fromSecond));
1924 } else {
1925 if (fromSecond)
1926 vrele(HTOV(fromSecond)); /* decrement, return it locked */
1927 if (fromFirst)
1928 vput(HTOV(fromFirst));
1929 }
1930 }
1931
1932 DBG_VOP_LOCKS_TEST(retval);
1933 return (retval);
1934 }
1935
1936
1937 /*
1938 * Change a vnode's file id, parent id and name
1939 *
1940 * Assumes the vnode is locked and is of type VREG
1941 */
1942 static void
1943 hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name)
1944 {
1945 DBG_ASSERT(HTOV(hp)->v_type == VREG);
1946
1947 H_HINT(hp) = 0;
1948 H_FILEID(hp) = fid; /* change h_nodeID */
1949 H_DIRID(hp) = pid;
1950
1951 hfs_set_metaname(name, hp->h_meta, HTOHFS(hp));
1952
1953
1954 }
1955
1956
1957 /*
1958
1959 #% fsync vp L L L
1960 #
1961 vop_fsync {
1962 IN struct vnode *vp;
1963 IN struct ucred *cred;
1964 IN int waitfor;
1965 IN struct proc *p;
1966
1967 */
1968
1969
1970 static int
1971 hfs_fsync(ap)
1972 struct vop_fsync_args /* {
1973 struct vnode *a_vp;
1974 struct ucred *a_cred;
1975 int a_waitfor;
1976 struct proc *a_p;
1977 } */ *ap;
1978 {
1979 struct vnode *vp = ap->a_vp ;
1980 struct hfsnode *hp = VTOH(vp);
1981 int retval = 0;
1982 register struct buf *bp;
1983 struct timeval tv;
1984 struct buf *nbp;
1985 int s;
1986
1987 DBG_FUNC_NAME("fsync");
1988 DBG_VOP_LOCKS_DECL(1);
1989 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
1990 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
1991 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
1992 DBG_HFS_NODE_CHECK(ap->a_vp);
1993
1994 #if HFS_DIAGNOSTIC
1995 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
1996 #endif
1997
1998
1999 /*
2000 * First of all, write out any clusters.
2001 */
2002 cluster_push(vp);
2003
2004 /*
2005 * Flush all dirty buffers associated with a vnode.
2006 */
2007 loop:
2008 s = splbio();
2009 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
2010 nbp = bp->b_vnbufs.le_next;
2011 if ((bp->b_flags & B_BUSY))
2012 continue;
2013 if ((bp->b_flags & B_DELWRI) == 0)
2014 panic("hfs_fsync: not dirty");
2015 bremfree(bp);
2016 bp->b_flags |= B_BUSY;
2017 bp->b_flags &= ~B_LOCKED; /* Clear flag, should only be set on meta files */
2018 splx(s);
2019 /*
2020 * Wait for I/O associated with indirect blocks to complete,
2021 * since there is no way to quickly wait for them below.
2022 */
2023 DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp->b_blkno, bp->b_lblkno));
2024 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) {
2025 (void) bawrite(bp);
2026 } else {
2027 (void) VOP_BWRITE(bp);
2028 }
2029 goto loop;
2030 }
2031 if (vp->v_flag & VHASDIRTY)
2032 ubc_pushdirty(vp);
2033
2034 if (ap->a_waitfor == MNT_WAIT) {
2035 while (vp->v_numoutput) {
2036 vp->v_flag |= VBWAIT;
2037 tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
2038 }
2039
2040 /* I have seen this happen for swapfile. So it is safer to
2041 * check for dirty buffers again. --Umesh
2042 */
2043 if (vp->v_dirtyblkhd.lh_first || (vp->v_flag & VHASDIRTY)) {
2044 vprint("hfs_fsync: dirty", vp);
2045 splx(s);
2046 goto loop;
2047 }
2048 }
2049 splx(s);
2050
2051 #if HFS_DIAGNOSTIC
2052 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
2053 #endif
2054
2055 tv = time;
2056 if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
2057 BTSetLastSync(HTOFCB(hp), tv.tv_sec);
2058
2059 if (H_FORKTYPE(hp) != kSysFile) {
2060 retval = VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT);
2061
2062 if (retval != E_NONE) {
2063 DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname, H_NAME(hp)));
2064 }
2065 }
2066 else
2067 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
2068
2069 if (ap->a_waitfor == MNT_WAIT) {
2070 DBG_ASSERT(vp->v_dirtyblkhd.lh_first == NULL);
2071 };
2072 DBG_VOP_LOCKS_TEST(retval);
2073 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
2074 return (retval);
2075 }
2076
2077
2078 int
2079 hfs_fsync_transaction(struct vnode *vp)
2080 {
2081 struct hfsnode *hp = VTOH(vp);
2082 register struct buf *bp;
2083 struct timeval tv;
2084 struct buf *nbp;
2085 int s;
2086
2087 /*
2088 * Flush all dirty buffers associated with a vnode.
2089 */
2090 loop:
2091 s = splbio();
2092
2093 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
2094 nbp = bp->b_vnbufs.le_next;
2095 if ((bp->b_flags & B_BUSY))
2096 continue;
2097 if ((bp->b_flags & B_DELWRI) == 0)
2098 panic("hfs_fsync: not dirty");
2099 if ( !(bp->b_flags & B_LOCKED))
2100 continue;
2101
2102 bremfree(bp);
2103 bp->b_flags |= B_BUSY;
2104 bp->b_flags &= ~B_LOCKED; /* Clear flag, should only be set on meta files */
2105 splx(s);
2106
2107 (void) bawrite(bp);
2108
2109 goto loop;
2110 }
2111 splx(s);
2112
2113 tv = time;
2114 if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
2115 (void) BTSetLastSync(VTOFCB(vp), tv.tv_sec);
2116 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
2117
2118 return 0;
2119 }
2120
2121 /*
2122
2123 #% remove dvp L U U
2124 #% remove vp L U U
2125 #
2126 vop_remove {
2127 IN WILLRELE struct vnode *dvp;
2128 IN WILLRELE struct vnode *vp;
2129 IN struct componentname *cnp;
2130
2131 */
2132
2133 int
2134 hfs_remove(ap)
2135 struct vop_remove_args /* {
2136 struct vnode *a_dvp;
2137 struct vnode *a_vp;
2138 struct componentname *a_cnp;
2139 } */ *ap;
2140 {
2141 struct vnode *vp = ap->a_vp;
2142 struct vnode *dvp = ap->a_dvp;
2143 struct hfsnode *hp = VTOH(ap->a_vp);
2144 struct hfsmount *hfsmp = HTOHFS(hp);
2145 struct proc *p = current_proc();
2146 struct timeval tv;
2147 int retval, use_count;
2148 int filebusy = 0;
2149 DBG_FUNC_NAME("remove");
2150 DBG_VOP_LOCKS_DECL(2);
2151 DBG_VOP_PRINT_FUNCNAME();
2152 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
2153 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2154 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2155 DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2156
2157 retval = E_NONE;
2158
2159 if ((hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
2160 (VTOH(dvp)->h_meta->h_pflags & APPEND)) {
2161 retval = EPERM;
2162 goto out;
2163 }
2164
2165 if (vp->v_usecount > 1) {
2166 /*
2167 * the namei done for the rename took a reference on the
2168 * vnode. Hence set 1 in the tookref parameter
2169 * of ubc_isinuse().
2170 */
2171 if(UBCISVALID(vp) && !ubc_isinuse(vp, 1))
2172 goto hfs_nobusy;
2173 if ((ap->a_cnp->cn_flags & NODELETEBUSY)
2174 || (hfsmp->hfs_private_metadata_dir == 0)) {
2175 /* Carbon semantics prohibits deleting busy files */
2176 retval = EBUSY;
2177 goto out;
2178 } else
2179 filebusy = 1;
2180 }
2181
2182 hfs_nobusy:
2183
2184 tv = time; /* Done here, so all times are the same */
2185
2186 /* Check other siblings for in use also */
2187 /* Uncache everything and make sure no other usecount */
2188 /*
2189 * This assumes the presence of the most 1 sibling
2190 *
2191 * a. loop through the siblings looking for another
2192 * b. If we find ourselves...skip it
2193 * If there was a sibling:
2194 * a. Check for a positve usecount
2195 * b. uncache any pages
2196 * c. Write out and memory changes
2197 * The idea is to keep the h_siblinglock as little as possible
2198 */
2199 if (hp->h_meta->h_usecount > 1) {
2200 struct vnode *sib_vp = NULL;
2201 struct hfsnode *nhp;
2202
2203 DBG_ASSERT(hp->h_meta->h_siblinghead.cqh_first &&
2204 (hp->h_meta->h_siblinghead.cqh_first != hp->h_meta->h_siblinghead.cqh_last));
2205 DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
2206
2207 /* Loop through all siblings, skipping ourselves */
2208 simple_lock(&hp->h_meta->h_siblinglock);
2209 CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
2210 if (nhp == hp) /* skip ourselves */
2211 continue;
2212 sib_vp = HTOV(nhp);
2213 };
2214 simple_unlock(&hp->h_meta->h_siblinglock);
2215
2216 /* Check to see if the other fork is in use */
2217 DBG_ASSERT(sib_vp != NULL);
2218 simple_lock(&sib_vp->v_interlock);
2219 use_count = sib_vp->v_usecount;
2220 simple_unlock(&sib_vp->v_interlock);
2221 if (use_count > 0) {
2222 /*
2223 * This is a sibling vnode and we did not take
2224 * a reference on it.
2225 * Hence set 0 in the tookref parameter
2226 * of ubc_isinuse().
2227 */
2228 if(UBCISVALID(sib_vp) && !ubc_isinuse(sib_vp, 0))
2229 goto hfs_nobusy2;
2230 if ((ap->a_cnp->cn_flags & NODELETEBUSY)
2231 || (hfsmp->hfs_private_metadata_dir == 0)) {
2232 /* Carbon semantics prohibits deleting busy files */
2233 retval = EBUSY;
2234 goto out;
2235 } else
2236 filebusy = 1;
2237 } /* use_count > 0 */
2238
2239 hfs_nobusy2:
2240
2241 /* The only error that vget returns is when the vnode is going away, so ignore the vnode */
2242 if (vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
2243 /*
2244 * XXX SER An intelligient person would ask, why flush out changes
2245 * that are going to be deleted? See the next comment.
2246 */
2247 if ((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) || (VTOFCB(sib_vp)->fcbFlags
2248 & fcbModifiedMask)) {
2249 DBG_ASSERT((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) != 0);
2250 VOP_UPDATE(sib_vp, &tv, &tv, 0);
2251 };
2252
2253 /* Invalidate the buffers, ignore the results */
2254 (void) vinvalbuf(sib_vp, 0, NOCRED, p, 0, 0);
2255
2256 vput(sib_vp);
2257 }; /* vget() */
2258 }; /* h_use_count > 1 */
2259
2260 /*
2261 * remove the entry from the namei cache:
2262 * We do it early before any linking/busy file wierdness, make sure the
2263 * original is gone
2264 */
2265 cache_purge(vp);
2266
2267 /* Flush out any catalog changes */
2268 /* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
2269 * and not from memory which is more correct
2270 */
2271 if ((hp->h_nodeflags & IN_MODIFIED) || (HTOFCB(hp)->fcbFlags & fcbModifiedMask))
2272 {
2273 DBG_ASSERT((hp->h_nodeflags & IN_MODIFIED) != 0);
2274 VOP_UPDATE(vp, &tv, &tv, 0);
2275 }
2276
2277 /* lock catalog b-tree */
2278 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
2279 if (retval != E_NONE) {
2280 retval = EBUSY;
2281 goto out;
2282 }
2283
2284 /*
2285 * After this point, any errors must goto out2, so the Catalog Tree gets unlocked
2286 */
2287
2288 #if HFS_HARDLINKS
2289 /*
2290 * Multi-linked files just need their link node deleted from the catalog
2291 */
2292 if (hp->h_meta->h_metaflags & IN_DATANODE) {
2293
2294 if ((ap->a_cnp->cn_flags & HASBUF) == 0 ||
2295 ap->a_cnp->cn_nameptr[0] == '\0') {
2296 retval = ENOENT; /* name missing */
2297 goto out2;
2298 }
2299
2300 /* lock extents b-tree (also protects volume bitmap) */
2301 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
2302 if (retval != E_NONE) {
2303 retval = EBUSY;
2304 goto out2; /* unlock catalog b-tree on the way out */
2305 }
2306
2307 retval = hfsDelete (HTOVCB(hp), H_FILEID(VTOH(dvp)),
2308 ap->a_cnp->cn_nameptr, TRUE, H_HINT(hp));
2309
2310 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
2311
2312 if (retval != 0)
2313 goto out2;
2314
2315 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2316 hp->h_nodeflags |= IN_CHANGE;
2317 if (--hp->h_meta->h_nlink < 1)
2318 hp->h_meta->h_metaflags |= IN_DELETED;
2319
2320 /* name and parent fields are no longer valid so invalidate them */
2321 H_DIRID(hp) = kUnknownID;
2322 hfs_set_metaname("\0", hp->h_meta, HTOHFS(hp));
2323
2324 if ((ap->a_cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME))
2325 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
2326
2327 goto out2; /* link deleted, all done */
2328 }
2329 #endif
2330
2331 /*
2332 * To make the HFS filesystem follow UFS unlink semantics, a remove of
2333 * an active vnode is translated to a move/rename so the file appears
2334 * deleted. Later, the file is removed by hfs_inactive on the hfsnode.
2335 */
2336 if (filebusy) {
2337 UInt32 hint = H_HINT(hp);
2338 char nodeName[32];
2339
2340 MAKE_DELETED_NAME(nodeName, H_FILEID(hp));
2341
2342 retval = hfsMoveRename (HTOVCB(hp), H_DIRID(hp), H_NAME(hp),
2343 hfsmp->hfs_private_metadata_dir, nodeName, &hint);
2344 if (retval) goto out2;
2345
2346 hp->h_meta->h_metaflags |= IN_DELETED;
2347 hp->h_nodeflags |= IN_CHANGE;
2348
2349 /* update name so Catalog lookups succeed */
2350 H_HINT(hp) = hint;
2351 H_DIRID(hp) = hfsmp->hfs_private_metadata_dir;
2352 hfs_set_metaname(nodeName, hp->h_meta, HTOHFS(hp));
2353
2354 goto out2; /* all done, unlock the catalog */
2355 }
2356
2357 /* unlock the Catalog */
2358 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2359
2360 /* Invalidate the buffers */
2361 if ((retval= vinvalbuf(vp, 0, NOCRED, p, 0, 0)))
2362 goto out;
2363
2364 if(UBCINFOEXISTS(vp))
2365 (void)ubc_setsize(vp, (off_t)0);
2366
2367
2368 /* lock catalog b-tree */
2369 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
2370 if (retval != E_NONE) {
2371 retval = EBUSY;
2372 goto out;
2373 }
2374 /* lock extents b-tree (also protects volume bitmap) */
2375 retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
2376 if (retval != E_NONE) {
2377 retval = EBUSY;
2378 goto out2; /* unlock catalog b-tree on the way out */
2379 }
2380
2381 /* remove entry from catalog and free any blocks used */
2382 retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
2383
2384 /* Clean up */
2385 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
2386 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2387
2388 if (retval != 0)
2389 goto out;
2390
2391 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2392 hp->h_meta->h_mode = 0; /* Makes the node go away...see inactive */
2393 /* clear the block mappings */
2394 hp->fcbPLen = (u_int64_t)0;
2395 bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
2396
2397 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
2398
2399 if (dvp == vp) {
2400 vrele(vp);
2401 } else {
2402 vput(vp);
2403 };
2404
2405 vput(dvp);
2406 DBG_VOP_LOCKS_TEST(retval);
2407
2408 if (UBCINFOEXISTS(vp)) {
2409 (void) ubc_uncache(vp);
2410 ubc_release(vp);
2411 /* WARNING vp may not be valid after this */
2412 }
2413 return (retval);
2414
2415 out2:
2416 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2417
2418 out:;
2419
2420 if (! retval)
2421 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
2422
2423 if (dvp == vp) {
2424 vrele(vp);
2425 } else {
2426 vput(vp);
2427 };
2428
2429 vput(dvp);
2430 DBG_VOP_LOCKS_TEST(retval);
2431 return (retval);
2432 }
2433
2434
2435 /*
2436
2437 #% rename sourcePar_vp U U U
2438 #% rename source_vp U U U
2439 #% rename targetPar_vp L U U
2440 #% rename target_vp X U U
2441 #
2442 vop_rename {
2443 IN WILLRELE struct vnode *sourcePar_vp;
2444 IN WILLRELE struct vnode *source_vp;
2445 IN struct componentname *source_cnp;
2446 IN WILLRELE struct vnode *targetPar_vp;
2447 IN WILLRELE struct vnode *target_vp;
2448 IN struct componentname *target_cnp;
2449
2450
2451 */
2452 /*
2453 * On entry:
2454 * source's parent directory is unlocked
2455 * source file or directory is unlocked
2456 * destination's parent directory is locked
2457 * destination file or directory is locked if it exists
2458 *
2459 * On exit:
2460 * all denodes should be released
2461 *
2462 */
2463
2464 static int
2465 hfs_rename(ap)
2466 struct vop_rename_args /* {
2467 struct vnode *a_fdvp;
2468 struct vnode *a_fvp;
2469 struct componentname *a_fcnp;
2470 struct vnode *a_tdvp;
2471 struct vnode *a_tvp;
2472 struct componentname *a_tcnp;
2473 } */ *ap;
2474 {
2475 struct vnode *target_vp = ap->a_tvp;
2476 struct vnode *targetPar_vp = ap->a_tdvp;
2477 struct vnode *source_vp = ap->a_fvp;
2478 struct vnode *sourcePar_vp = ap->a_fdvp;
2479 struct componentname *target_cnp = ap->a_tcnp;
2480 struct componentname *source_cnp = ap->a_fcnp;
2481 struct proc *p = source_cnp->cn_proc;
2482 struct hfsnode *target_hp, *targetPar_hp, *source_hp, *sourcePar_hp;
2483 u_int32_t oldparent = 0, newparent = 0;
2484 int doingdirectory = 0;
2485 int retval = 0;
2486 struct timeval tv;
2487 struct hfsCatalogInfo catInfo;
2488 DBG_VOP_LOCKS_DECL(4);
2489
2490 DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
2491 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_fvp);DBG_VOP_CONT(("\n"));
2492 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_fdvp);DBG_VOP_CONT(("\n"));
2493 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_tvp);DBG_VOP_CONT(("\n"));
2494 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_tdvp);DBG_VOP_CONT(("\n"));
2495 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_fcnp);DBG_VOP_CONT(("\n"));
2496 DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_tcnp);DBG_VOP_CONT(("\n"));
2497 DBG_VOP_LOCKS_INIT(0,ap->a_fdvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2498 DBG_VOP_LOCKS_INIT(1,ap->a_fvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2499 DBG_VOP_LOCKS_INIT(2,ap->a_tdvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2500 DBG_VOP_LOCKS_INIT(3,ap->a_tvp, VOPDBG_LOCKNOTNIL, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2501 WRITE_CK(ap->a_fdvp, funcname);
2502 DBG_HFS_NODE_CHECK(ap->a_fdvp);
2503 DBG_HFS_NODE_CHECK(ap->a_tdvp);
2504
2505 #if HFS_DIAGNOSTIC
2506 if ((target_cnp->cn_flags & HASBUF) == 0 ||
2507 (source_cnp->cn_flags & HASBUF) == 0)
2508 panic("hfs_rename: no name");
2509 #endif
2510
2511 DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
2512 target_hp = targetPar_hp = source_hp = sourcePar_hp = 0;
2513
2514 /*
2515 * Check for cross-device rename.
2516 */
2517 if ((source_vp->v_mount != targetPar_vp->v_mount) ||
2518 (target_vp && (source_vp->v_mount != target_vp->v_mount))) {
2519 retval = EXDEV;
2520 goto abortit;
2521 }
2522
2523 /*
2524 * Check for access permissions
2525 */
2526 if (target_vp && ((VTOH(target_vp)->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
2527 (VTOH(targetPar_vp)->h_meta->h_pflags & APPEND))) {
2528 retval = EPERM;
2529 goto abortit;
2530 }
2531
2532 if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
2533 goto abortit;
2534
2535 sourcePar_hp = VTOH(sourcePar_vp);
2536 source_hp = VTOH(source_vp);
2537 oldparent = H_FILEID(sourcePar_hp);
2538 if ((source_hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) || (sourcePar_hp->h_meta->h_pflags & APPEND)) {
2539 VOP_UNLOCK(source_vp, 0, p);
2540 retval = EPERM;
2541 goto abortit;
2542 }
2543
2544 /*
2545 * Be sure we are not renaming ".", "..", or an alias of ".". This
2546 * leads to a crippled directory tree. It's pretty tough to do a
2547 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
2548 * doesn't work if the ".." entry is missing.
2549 */
2550 if ((source_hp->h_meta->h_mode & IFMT) == IFDIR) {
2551 if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
2552 || sourcePar_hp == source_hp
2553 || (source_cnp->cn_flags&ISDOTDOT)
2554 || (source_hp->h_nodeflags & IN_RENAME)) {
2555 VOP_UNLOCK(source_vp, 0, p);
2556 retval = EINVAL;
2557 goto abortit;
2558 }
2559 source_hp->h_nodeflags |= IN_RENAME;
2560 doingdirectory = TRUE;
2561 }
2562
2563 /*
2564 *
2565 * >>>> Transit between abort and bad <<<<
2566 *
2567 */
2568
2569 targetPar_hp = VTOH(targetPar_vp);
2570 if (target_vp)
2571 target_hp = VTOH(target_vp);
2572 else
2573 DBG_ASSERT(target_hp == NULL);
2574
2575 newparent = H_FILEID(targetPar_hp);
2576
2577 /* Test to make sure we are not crossing devices */
2578 /* XXX SER Is this necesary, does catalog manager take care of this? */
2579 if (target_vp) {
2580 if (H_DEV(target_hp) != H_DEV(targetPar_hp) || H_DEV(target_hp) != H_DEV(source_hp))
2581 panic("rename: EXDEV");
2582 }
2583 else {
2584 if (H_DEV(targetPar_hp) != H_DEV(source_hp))
2585 panic("rename: EXDEV");
2586 };
2587
2588 retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
2589 if (doingdirectory && (newparent != oldparent)) {
2590 if (retval) /* write access check above */
2591 goto bad;
2592 }
2593 retval = 0; /* Reset value from above, we dont care about it anymore */
2594
2595 /*
2596 * If the destination exists, then be sure its type (file or dir)
2597 * matches that of the source. And, if it is a directory make sure
2598 * it is empty. Then delete the destination.
2599 */
2600 if (target_vp) {
2601
2602 /*
2603 * If the parent directory is "sticky", then the user must
2604 * own the parent directory, or the destination of the rename,
2605 * otherwise the destination may not be changed (except by
2606 * root). This implements append-only directories.
2607 */
2608 if ((targetPar_hp->h_meta->h_mode & S_ISTXT) && (target_cnp->cn_cred->cr_uid != 0) &&
2609 target_cnp->cn_cred->cr_uid != targetPar_hp->h_meta->h_uid &&
2610 target_cnp->cn_cred->cr_uid != target_hp->h_meta->h_uid) {
2611 retval = EPERM;
2612 goto bad;
2613 }
2614
2615 /*
2616 * VOP_REMOVE will vput targetPar_vp so we better bump
2617 * its ref count and relockit, always set target_vp to
2618 * NULL afterwards to indicate that were done with it.
2619 */
2620 VREF(targetPar_vp);
2621
2622 cache_purge(target_vp);
2623
2624 #if HFS_HARDLINKS
2625 target_cnp->cn_flags &= ~SAVENAME;
2626 #endif
2627
2628 retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
2629 (void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p);
2630
2631 target_vp = NULL;
2632 target_hp = NULL;
2633
2634 if (retval) goto bad;
2635
2636 };
2637
2638
2639 if (newparent != oldparent)
2640 vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);
2641
2642 /* remove the existing entry from the namei cache: */
2643 cache_purge(source_vp);
2644
2645 INIT_CATALOGDATA(&catInfo.nodeData, 0);
2646
2647 /* lock catalog b-tree */
2648 retval = hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
2649 if (retval) {
2650 if (newparent != oldparent) /* unlock the lock we just got */
2651 VOP_UNLOCK(sourcePar_vp, 0, p);
2652 goto bad;
2653 };
2654
2655 /* use source_cnp instead of H_NAME(source_hp) in case source is a hard link */
2656 retval = hfsMoveRename( HTOVCB(source_hp), H_DIRID(source_hp), source_cnp->cn_nameptr,
2657 H_FILEID(VTOH(targetPar_vp)), target_cnp->cn_nameptr, &H_HINT(source_hp));
2658
2659 if (retval == 0) {
2660 /* Look up the catalog entry just renamed since it might have been auto-decomposed */
2661 catInfo.hint = H_HINT(source_hp);
2662 retval = hfs_getcatalog(HTOVCB(source_hp), H_FILEID(targetPar_hp), target_cnp->cn_nameptr, target_cnp->cn_namelen, &catInfo);
2663 }
2664
2665 /* unlock catalog b-tree */
2666 (void) hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_RELEASE, p);
2667
2668 if (newparent != oldparent)
2669 VOP_UNLOCK(sourcePar_vp, 0, p);
2670
2671 if (retval) goto bad;
2672
2673 H_DIRID(source_hp) = H_FILEID(targetPar_hp);
2674
2675 hfs_name_CatToMeta(&catInfo.nodeData, source_hp->h_meta);
2676
2677 CLEAN_CATALOGDATA(&catInfo.nodeData);
2678
2679 source_hp->h_nodeflags &= ~IN_RENAME;
2680
2681
2682 /*
2683 * Timestamp both parent directories.
2684 * Note that if this is a rename within the same directory,
2685 * (where targetPar_hp == sourcePar_hp)
2686 * the code below is still safe and correct.
2687 */
2688 targetPar_hp->h_nodeflags |= IN_UPDATE;
2689 sourcePar_hp->h_nodeflags |= IN_UPDATE;
2690 tv = time;
2691 HFSTIMES(targetPar_hp, &tv, &tv);
2692 HFSTIMES(sourcePar_hp, &tv, &tv);
2693
2694 vput(targetPar_vp);
2695 vrele(sourcePar_vp);
2696 vput(source_vp);
2697
2698 DBG_VOP_LOCKS_TEST(retval);
2699 if (retval != E_NONE) {
2700 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2701 }
2702 return (retval);
2703
2704 bad:;
2705 if (retval && doingdirectory)
2706 source_hp->h_nodeflags &= ~IN_RENAME;
2707
2708 if (targetPar_vp == target_vp)
2709 vrele(targetPar_vp);
2710 else
2711 vput(targetPar_vp);
2712
2713 if (target_vp)
2714 vput(target_vp);
2715
2716 vrele(sourcePar_vp);
2717
2718 if (VOP_ISLOCKED(source_vp))
2719 vput(source_vp);
2720 else
2721 vrele(source_vp);
2722
2723 DBG_VOP_LOCKS_TEST(retval);
2724 if (retval != E_NONE) {
2725 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2726 }
2727 return (retval);
2728
2729 abortit:;
2730
2731 VOP_ABORTOP(targetPar_vp, target_cnp); /* XXX, why not in NFS? */
2732
2733 if (targetPar_vp == target_vp)
2734 vrele(targetPar_vp);
2735 else
2736 vput(targetPar_vp);
2737
2738 if (target_vp)
2739 vput(target_vp);
2740
2741 VOP_ABORTOP(sourcePar_vp, source_cnp); /* XXX, why not in NFS? */
2742
2743 vrele(sourcePar_vp);
2744 vrele(source_vp);
2745
2746 DBG_VOP_LOCKS_TEST(retval);
2747 if (retval != E_NONE) {
2748 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
2749 }
2750 return (retval);
2751 }
2752
2753
2754
2755 /*
2756 * Mkdir system call
2757 #% mkdir dvp L U U
2758 #% mkdir vpp - L -
2759 #
2760 vop_mkdir {
2761 IN WILLRELE struct vnode *dvp;
2762 OUT struct vnode **vpp;
2763 IN struct componentname *cnp;
2764 IN struct vattr *vap;
2765
2766 We are responsible for freeing the namei buffer,
2767 it is done in hfs_makenode()
2768 */
2769
2770 int
2771 hfs_mkdir(ap)
2772 struct vop_mkdir_args /* {
2773 struct vnode *a_dvp;
2774 struct vnode **a_vpp;
2775 struct componentname *a_cnp;
2776 struct vattr *a_vap;
2777 } */ *ap;
2778 {
2779 struct proc *p = current_proc();
2780 int retval;
2781 int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
2782
2783 DBG_FUNC_NAME("mkdir");
2784 DBG_VOP_LOCKS_DECL(2);
2785 DBG_VOP_PRINT_FUNCNAME();
2786 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
2787 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2788
2789 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2790 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
2791
2792 DBG_VOP(("%s: parent 0x%x (%s) ap->a_cnp->cn_nameptr %s\n", funcname, (u_int)VTOH(ap->a_dvp), H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
2793 WRITE_CK( ap->a_dvp, funcname);
2794 DBG_HFS_NODE_CHECK(ap->a_dvp);
2795 DBG_ASSERT(ap->a_dvp->v_type == VDIR);
2796
2797 /* Create the vnode */
2798 DBG_ASSERT((ap->a_cnp->cn_flags & SAVESTART) == 0);
2799 retval = hfs_makenode(mode, 0, ap->a_dvp, ap->a_vpp, ap->a_cnp, p);
2800 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
2801
2802 if (retval != E_NONE) {
2803 DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
2804 DBG_VOP_LOCKS_TEST(retval);
2805 return (retval);
2806 }
2807
2808 DBG_VOP_LOCKS_TEST(E_NONE);
2809 return (E_NONE);
2810 }
2811
2812 /*
2813 * Rmdir system call.
2814 #% rmdir dvp L U U
2815 #% rmdir vp L U U
2816 #
2817 vop_rmdir {
2818 IN WILLRELE struct vnode *dvp;
2819 IN WILLRELE struct vnode *vp;
2820 IN struct componentname *cnp;
2821
2822 */
2823
2824 int
2825 hfs_rmdir(ap)
2826 struct vop_rmdir_args /* {
2827 struct vnode *a_dvp;
2828 struct vnode *a_vp;
2829 struct componentname *a_cnp;
2830 } */ *ap;
2831 {
2832 struct vnode *vp = ap->a_vp;
2833 struct vnode *dvp = ap->a_dvp;
2834 struct hfsnode *hp = VTOH(vp);
2835 struct proc *p = current_proc();
2836 int retval;
2837 DBG_FUNC_NAME("rmdir");
2838 DBG_VOP_LOCKS_DECL(2);
2839 DBG_VOP_PRINT_FUNCNAME();
2840 DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);DBG_VOP_CONT(("\n"));
2841 DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
2842 DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
2843
2844 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2845 DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2846
2847 if (dvp == vp) {
2848 vrele(vp);
2849 vput(vp);
2850 DBG_VOP_LOCKS_TEST(EINVAL);
2851 return (EINVAL);
2852 }
2853
2854 /*
2855 * HFS differs from UFS here in that we don't allow removing
2856 * a directory that in use by others - even if its empty.
2857 *
2858 * In the future we might want to allow this just like we do
2859 * for files (by renaming the busy directory).
2860 */
2861 #if 0
2862 if (vp->v_usecount > 1) {
2863 DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname, vp->v_usecount ));
2864 retval = EBUSY;
2865 goto Err_Exit;
2866 }
2867 #endif
2868 /* remove the entry from the namei cache: */
2869 cache_purge(vp);
2870
2871 /* lock catalog b-tree */
2872 retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
2873 if (retval != E_NONE) {
2874 goto Err_Exit;
2875 }
2876
2877 /* remove entry from catalog */
2878 retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), FALSE, H_HINT(hp));
2879
2880 /* unlock catalog b-tree */
2881 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
2882
2883 if (! retval) {
2884 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE; /* Set the parent to be updated */
2885 hp->h_meta->h_mode = 0; /* Makes the vnode go away...see inactive */
2886 hp->h_meta->h_metaflags |= IN_NOEXISTS;
2887 }
2888
2889 Err_Exit:;
2890 if (dvp != 0)
2891 vput(dvp);
2892 vput(vp);
2893
2894 DBG_VOP_LOCKS_TEST(retval);
2895 return (retval);
2896 }
2897
2898 /*
2899 * symlink -- make a symbolic link
2900 #% symlink dvp L U U
2901 #% symlink vpp - U -
2902 #
2903 # XXX - note that the return vnode has already been VRELE'ed
2904 # by the filesystem layer. To use it you must use vget,
2905 # possibly with a further namei.
2906 #
2907 vop_symlink {
2908 IN WILLRELE struct vnode *dvp;
2909 OUT WILLRELE struct vnode **vpp;
2910 IN struct componentname *cnp;
2911 IN struct vattr *vap;
2912 IN char *target;
2913
2914 We are responsible for freeing the namei buffer,
2915 it is done in hfs_makenode().
2916
2917 */
2918
2919 int
2920 hfs_symlink(ap)
2921 struct vop_symlink_args /* {
2922 struct vnode *a_dvp;
2923 struct vnode **a_vpp;
2924 struct componentname *a_cnp;
2925 struct vattr *a_vap;
2926 char *a_target;
2927 } */ *ap;
2928 {
2929 register struct vnode *vp, **vpp = ap->a_vpp;
2930 struct proc *p = current_proc();
2931 struct hfsnode *hp;
2932 u_int32_t dfltClump;
2933 int len, retval;
2934 DBG_FUNC_NAME("symlink");
2935 DBG_VOP_LOCKS_DECL(2);
2936 DBG_VOP_PRINT_FUNCNAME();
2937 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
2938 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);
2939
2940 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
2941 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2942 vput(ap->a_dvp);
2943 DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
2944 DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
2945 return (EOPNOTSUPP);
2946 }
2947
2948 /* Create the vnode */
2949 retval = hfs_makenode(IFLNK | ap->a_vap->va_mode, 0, ap->a_dvp,
2950 vpp, ap->a_cnp, p);
2951 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
2952
2953 if (retval != E_NONE) {
2954 DBG_VOP_LOCKS_TEST(retval);
2955 return (retval);
2956 }
2957
2958
2959 vp = *vpp;
2960 len = strlen(ap->a_target);
2961 hp = VTOH(vp);
2962 dfltClump = hp->fcbClmpSize;
2963 /* make clump size minimal */
2964 hp->fcbClmpSize = VTOVCB(vp)->blockSize;
2965 retval = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
2966 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
2967 (struct proc *)0);
2968 hp->fcbClmpSize = dfltClump;
2969
2970
2971 vput(vp);
2972 DBG_VOP_LOCKS_TEST(retval);
2973 return (retval);
2974 }
2975
2976
2977 /*
2978 * Dummy dirents to simulate the "." and ".." entries of the directory
2979 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2980 * the size of these entries is the smallest needed to represent them
2981 * (only 12 byte each).
2982 */
2983 static hfsdotentry rootdots[2] = {
2984 {
2985 1, /* d_fileno */
2986 sizeof(struct hfsdotentry), /* d_reclen */
2987 DT_DIR, /* d_type */
2988 1, /* d_namlen */
2989 "." /* d_name */
2990 },
2991 {
2992 1, /* d_fileno */
2993 sizeof(struct hfsdotentry), /* d_reclen */
2994 DT_DIR, /* d_type */
2995 2, /* d_namlen */
2996 ".." /* d_name */
2997 }
2998 };
2999
3000 static hfsdotentry emptyentry = { 0 };
3001
3002 /* 4.3 Note:
3003 * There is some confusion as to what the semantics of uio_offset are.
3004 * In ufs, it represents the actual byte offset within the directory
3005 * "file." HFS, however, just uses it as an entry counter - essentially
3006 * assuming that it has no meaning except to the hfs_readdir function.
3007 * This approach would be more efficient here, but some callers may
3008 * assume the uio_offset acts like a byte offset. NFS in fact
3009 * monkeys around with the offset field a lot between readdir calls.
3010 *
3011 * The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
3012 * fields is a mess as well. The libc function readdir() returns
3013 * NULL (indicating the end of a directory) when either
3014 * the getdirentries() syscall (which calls this and returns
3015 * the size of the buffer passed in less the value of uiop->uio_resid)
3016 * returns 0, or a direct record with a d_reclen of zero.
3017 * nfs_server.c:rfs_readdir(), on the other hand, checks for the end
3018 * of the directory by testing uiop->uio_resid == 0. The solution
3019 * is to pad the size of the last struct direct in a given
3020 * block to fill the block if we are not at the end of the directory.
3021 */
3022
3023 struct callbackstate {
3024 u_int32_t cbs_parentID;
3025 u_int32_t cbs_hiddenDirID;
3026 off_t cbs_lastoffset;
3027 struct uio * cbs_uio;
3028 ExtendedVCB * cbs_vcb;
3029 int16_t cbs_hfsPlus;
3030 int16_t cbs_result;
3031 };
3032
3033
3034 SInt32
3035 ProcessCatalogEntry(const CatalogKey *ckp, const CatalogRecord *crp,
3036 u_int16_t recordLen, struct callbackstate *state)
3037 {
3038 CatalogName *cnp;
3039 size_t utf8chars;
3040 u_int32_t curID;
3041 OSErr result;
3042 struct dirent catent;
3043
3044 if (state->cbs_hfsPlus)
3045 curID = ckp->hfsPlus.parentID;
3046 else
3047 curID = ckp->hfs.parentID;
3048
3049 /* We're done when parent directory changes */
3050 if (state->cbs_parentID != curID) {
3051 lastitem:
3052 /*
3053 * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
3054 * so remove padding for now...
3055 */
3056 #if 0
3057 /*
3058 * Pad the end of list with an empty record.
3059 * This eliminates an extra call by readdir(3c).
3060 */
3061 catent.d_fileno = 0;
3062 catent.d_reclen = 0;
3063 catent.d_type = 0;
3064 catent.d_namlen = 0;
3065 *(int32_t*)&catent.d_name[0] = 0;
3066
3067 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3068
3069 state->cbs_result = uiomove((caddr_t) &catent, 12, state->cbs_uio);
3070 if (state->cbs_result == 0)
3071 state->cbs_result = ENOENT;
3072 #else
3073 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3074 state->cbs_result = ENOENT;
3075 #endif
3076 return (0); /* stop */
3077 }
3078
3079 if (state->cbs_hfsPlus) {
3080 switch(crp->recordType) {
3081 case kHFSPlusFolderRecord:
3082 catent.d_type = DT_DIR;
3083 catent.d_fileno = crp->hfsPlusFolder.folderID;
3084 break;
3085 case kHFSPlusFileRecord:
3086 catent.d_type = DT_REG;
3087 catent.d_fileno = crp->hfsPlusFile.fileID;
3088 break;
3089 default:
3090 return (0); /* stop */
3091 };
3092
3093 cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
3094 result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
3095 catent.d_name, &utf8chars, kdirentMaxNameBytes + 1, ':', 0);
3096 if (result == ENAMETOOLONG) {
3097 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
3098 cnp->ustr.unicode, kdirentMaxNameBytes + 1, (ByteCount*)&utf8chars, catent.d_name, catent.d_fileno);
3099 }
3100 } else { /* hfs */
3101 switch(crp->recordType) {
3102 case kHFSFolderRecord:
3103 catent.d_type = DT_DIR;
3104 catent.d_fileno = crp->hfsFolder.folderID;
3105 break;
3106 case kHFSFileRecord:
3107 catent.d_type = DT_REG;
3108 catent.d_fileno = crp->hfsFile.fileID;
3109 break;
3110 default:
3111 return (0); /* stop */
3112 };
3113
3114 cnp = (CatalogName*) ckp->hfs.nodeName;
3115 result = hfs_to_utf8(state->cbs_vcb, cnp->pstr, kdirentMaxNameBytes + 1,
3116 (ByteCount *)&utf8chars, catent.d_name);
3117 /*
3118 * When an HFS name cannot be encoded with the current
3119 * volume encoding we use MacRoman as a fallback.
3120 */
3121 if (result)
3122 result = mac_roman_to_utf8(cnp->pstr, kdirentMaxNameBytes + 1,
3123 (ByteCount *)&utf8chars, catent.d_name);
3124 }
3125
3126 catent.d_namlen = utf8chars;
3127 catent.d_reclen = DIRENTRY_SIZE(utf8chars);
3128
3129 /* hide our private meta data directory */
3130 if (curID == kRootDirID &&
3131 catent.d_fileno == state->cbs_hiddenDirID &&
3132 catent.d_type == DT_DIR)
3133 goto lastitem;
3134
3135 state->cbs_lastoffset = state->cbs_uio->uio_offset;
3136
3137 /* if this entry won't fit then we're done */
3138 if (catent.d_reclen > state->cbs_uio->uio_resid)
3139 return (0); /* stop */
3140
3141 state->cbs_result = uiomove((caddr_t) &catent, catent.d_reclen, state->cbs_uio);
3142
3143 /* continue iteration if there's room */
3144 return (state->cbs_result == 0 &&
3145 state->cbs_uio->uio_resid >= AVERAGE_HFSDIRENTRY_SIZE);
3146 }
3147
3148 /*
3149 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
3150 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
3151 * "Nbytes must be greater than or equal to the block size associated with the file,
3152 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
3153 * room for the . and .. entries that have to added manually.
3154 */
3155
3156 /*
3157 #% readdir vp L L L
3158 #
3159 vop_readdir {
3160 IN struct vnode *vp;
3161 INOUT struct uio *uio;
3162 IN struct ucred *cred;
3163 INOUT int *eofflag;
3164 OUT int *ncookies;
3165 INOUT u_long **cookies;
3166 */
3167 static int
3168 hfs_readdir(ap)
3169 struct vop_readdir_args /* {
3170 struct vnode *vp;
3171 struct uio *uio;
3172 struct ucred *cred;
3173 int *eofflag;
3174 int *ncookies;
3175 u_long **cookies;
3176 } */ *ap;
3177 {
3178 register struct uio *uio = ap->a_uio;
3179 struct hfsnode *hp = VTOH(ap->a_vp);
3180 struct proc *p = current_proc();
3181 ExtendedVCB *vcb = HTOVCB(hp);
3182 off_t off = uio->uio_offset;
3183 u_int32_t dirID = H_FILEID(hp);
3184 int retval = 0;
3185 OSErr result = noErr;
3186 u_int32_t diroffset;
3187 BTreeIterator bi;
3188 CatalogIterator *cip;
3189 u_int16_t op;
3190 struct callbackstate state;
3191 int eofflag = 0;
3192
3193 DBG_FUNC_NAME("readdir");
3194 DBG_VOP_LOCKS_DECL(1);
3195
3196 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
3197 DBG_VOP_PRINT_FUNCNAME();
3198 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3199 DBG_HFS_NODE_CHECK(ap->a_vp);
3200
3201 /* We assume it's all one big buffer... */
3202 if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE) {
3203 return EINVAL;
3204 };
3205
3206 /* Create the entries for . and .. */
3207 if (uio->uio_offset < sizeof(rootdots)) {
3208 caddr_t dep;
3209 size_t dotsize;
3210
3211 rootdots[0].d_fileno = dirID;
3212 rootdots[1].d_fileno = H_DIRID(hp);
3213
3214 if (uio->uio_offset == 0) {
3215 dep = (caddr_t) &rootdots[0];
3216 dotsize = 2* sizeof(struct hfsdotentry);
3217 } else if (uio->uio_offset == sizeof(struct hfsdotentry)) {
3218 dep = (caddr_t) &rootdots[1];
3219 dotsize = sizeof(struct hfsdotentry);
3220 } else {
3221 retval = EINVAL;
3222 goto Exit;
3223 }
3224
3225 retval = uiomove(dep, dotsize, uio);
3226 if (retval != 0)
3227 goto Exit;
3228 }
3229
3230 diroffset = uio->uio_offset;
3231
3232 /* lock catalog b-tree */
3233 retval = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
3234 if (retval != E_NONE)
3235 goto Exit;
3236
3237 /* get an iterator and position it */
3238 cip = GetCatalogIterator(vcb, dirID, diroffset);
3239
3240 result = PositionIterator(cip, diroffset, &bi, &op);
3241 if (result == cmNotFound) {
3242 eofflag = 1;
3243 retval = 0;
3244 AgeCatalogIterator(cip);
3245 goto cleanup;
3246 } else if ((retval = MacToVFSError(result)))
3247 goto cleanup;
3248
3249 state.cbs_hiddenDirID = VCBTOHFS(vcb)->hfs_private_metadata_dir;
3250 state.cbs_lastoffset = cip->currentOffset;
3251 state.cbs_vcb = vcb;
3252 state.cbs_uio = uio;
3253 state.cbs_result = 0;
3254 state.cbs_parentID = dirID;
3255
3256 if (vcb->vcbSigWord == kHFSPlusSigWord)
3257 state.cbs_hfsPlus = 1;
3258 else
3259 state.cbs_hfsPlus = 0;
3260
3261 /* process as many entries as possible... */
3262 result = BTIterateRecords(GetFileControlBlock(vcb->catalogRefNum), op, &bi,
3263 (IterateCallBackProcPtr)ProcessCatalogEntry, &state);
3264
3265 if (state.cbs_result)
3266 retval = state.cbs_result;
3267 else
3268 retval = MacToVFSError(result);
3269
3270 if (retval == ENOENT) {
3271 eofflag = 1;
3272 retval = 0;
3273 }
3274
3275 if (retval == 0) {
3276 cip->currentOffset = state.cbs_lastoffset;
3277 cip->nextOffset = uio->uio_offset;
3278 UpdateCatalogIterator(&bi, cip);
3279 }
3280
3281 cleanup:
3282 if (retval) {
3283 cip->volume = 0;
3284 cip->folderID = 0;
3285 AgeCatalogIterator(cip);
3286 }
3287
3288 (void) ReleaseCatalogIterator(cip);
3289
3290 /* unlock catalog b-tree */
3291 (void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);
3292
3293 if (retval != E_NONE) {
3294 DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname, retval,
3295 H_FILEID(hp), H_NAME(hp)));
3296 goto Exit;
3297
3298 }
3299
3300 /* were we already past eof ? */
3301 if (uio->uio_offset == off) {
3302 retval = E_NONE;
3303 goto Exit;
3304 }
3305
3306 if (vcb->vcbSigWord == kHFSPlusSigWord)
3307 hp->h_nodeflags |= IN_ACCESS;
3308
3309 /* Bake any cookies */
3310 if (!retval && ap->a_ncookies != NULL) {
3311 struct dirent* dpStart;
3312 struct dirent* dpEnd;
3313 struct dirent* dp;
3314 int ncookies;
3315 u_long *cookies;
3316 u_long *cookiep;
3317
3318 /*
3319 * Only the NFS server uses cookies, and it loads the
3320 * directory block into system space, so we can just look at
3321 * it directly.
3322 */
3323 if (uio->uio_segflg != UIO_SYSSPACE)
3324 panic("hfs_readdir: unexpected uio from NFS server");
3325 dpStart = (struct dirent *)(uio->uio_iov->iov_base - (uio->uio_offset - off));
3326 dpEnd = (struct dirent *) uio->uio_iov->iov_base;
3327 for (dp = dpStart, ncookies = 0;
3328 dp < dpEnd && dp->d_reclen != 0;
3329 dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
3330 ncookies++;
3331 MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
3332 for (dp = dpStart, cookiep = cookies;
3333 dp < dpEnd;
3334 dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
3335 off += dp->d_reclen;
3336 *cookiep++ = (u_long) off;
3337 }
3338 *ap->a_ncookies = ncookies;
3339 *ap->a_cookies = cookies;
3340 }
3341
3342 Exit:;
3343
3344 if (ap->a_eofflag)
3345 *ap->a_eofflag = eofflag;
3346
3347 DBG_VOP_LOCKS_TEST(retval);
3348 return (retval);
3349 }
3350
3351
3352 /*
3353 * readdirattr operation will return attributes for the items in the
3354 * directory specified.
3355 *
3356 * It does not do . and .. entries. The problem is if you are at the root of the
3357 * hfs directory and go to .. you could be crossing a mountpoint into a
3358 * different (ufs) file system. The attributes that apply for it may not
3359 * apply for the file system you are doing the readdirattr on. To make life
3360 * simpler, this call will only return entries in its directory, hfs like.
3361 * TO DO LATER:
3362 * 1.getattrlist creates a thread record if the objpermanentid attribute
3363 * is requested. Just do EINVAL for now and fix later.
3364 * 2. more than one for uiovcnt support.
3365 * 3. put knohint (hints) in state for next call in
3366 * 4. credentials checking when rest of hfs does it.
3367 * 5. Do return permissions concatenation ???
3368 */
3369
3370 /*
3371 #
3372 #% readdirattr vp L L L
3373 #
3374 vop_readdirattr {
3375 IN struct vnode *vp;
3376 IN struct attrlist *alist;
3377 INOUT struct uio *uio;
3378 IN u_long maxcount:
3379 IN u_long options;
3380 OUT u_long *newstate;
3381 OUT int *eofflag;
3382 OUT u_long *actualCount;
3383 OUT u_long **cookies;
3384 IN struct ucred *cred;
3385 };
3386 */
3387 static int
3388 hfs_readdirattr(ap)
3389 struct vop_readdirattr_args /* {
3390 struct vnode *vp;
3391 struct attrlist *alist;
3392 struct uio *uio;
3393 u_long maxcount:
3394 u_long options;
3395 int *newstate;
3396 int *eofflag;
3397 u_long *actualcount;
3398 u_long **cookies;
3399 struct ucred *cred;
3400 } */ *ap;
3401 {
3402 struct vnode *vp = ap->a_vp;
3403 struct attrlist *alist = ap->a_alist;
3404 register struct uio *uio = ap->a_uio;
3405 u_long maxcount = ap->a_maxcount;
3406 u_long ncookies;
3407 ExtendedVCB *vcb = HTOVCB(VTOH(vp));
3408 UInt32 dirID = H_FILEID(VTOH(vp));
3409 struct proc *proc = current_proc(); /* could get this out of uio */
3410 off_t startoffset = uio->uio_offset;
3411 struct hfsCatalogInfo catInfo;
3412 UInt32 index;
3413 int retval = 0;
3414 u_long fixedblocksize;
3415 u_long maxattrblocksize;
3416 u_long currattrbufsize;
3417 void *attrbufptr = NULL;
3418 void *attrptr;
3419 void *varptr;
3420 struct vnode *entryvnode;
3421
3422
3423 *(ap->a_actualcount) = 0;
3424 *(ap->a_eofflag) = 0;
3425
3426 /* check for invalid options, check vnode, and buffer space */
3427 if (((ap->a_options & ~FSOPT_NOINMEMUPDATE) != 0) ||
3428 (vp == NULL) ||
3429 (uio->uio_resid <= 0) || (uio->uio_iovcnt > 1))
3430 return EINVAL;
3431
3432 /* this call doesn't take volume attributes */
3433 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
3434 ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
3435 (alist->volattr != 0) ||
3436 ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
3437 ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
3438 ((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0))
3439 return EINVAL;
3440
3441 /* Reject requests for unsupported options for now: */
3442 if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
3443 (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST)) ||
3444 (alist->commonattr & ATTR_CMN_OBJPERMANENTID) )
3445 return EINVAL;
3446
3447 /* getattrlist and searchfs use a secondary buffer to malloc and then use
3448 * uiomove afterwards. It's an extra copy, but for now leave it alone
3449 */
3450 fixedblocksize = (sizeof(u_long) + AttributeBlockSize(alist)); /* u_long for length */
3451 maxattrblocksize = fixedblocksize;
3452 if (alist->commonattr & ATTR_CMN_NAME)
3453 maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
3454 MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
3455 attrptr = attrbufptr;
3456 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
3457
3458 /* Since attributes passed back can contain variable ones (name), we can't just use
3459 * uio_offset as is. We thus force it to represent fixed size of hfsdirentries
3460 * as hfs_readdir was originally doing. If this all we need to represent the current
3461 * state, then ap->a_state is not needed at all.
3462 */
3463 /* index = ap->a_state; should not be less than 1 */
3464 index = (uio->uio_offset / sizeof(struct dirent)) + 1;
3465 INIT_CATALOGDATA(&catInfo.nodeData, 0);
3466
3467 /* Lock catalog b-tree */
3468 if ((retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, proc)) != E_NONE)
3469 goto exit;
3470
3471 /* HFS Catalog does not have a bulk directory enumeration call. Do it one at
3472 * time, using hints. GetCatalogOffspring takes care of hfsplus and name issues
3473 * for us, so that's a win. Later, implement GetCatalogOffspringBulk.
3474 */
3475 catInfo.hint = kNoHint; /* note, we may want to save the latest in state */
3476 while ((uio->uio_resid >= 0) && (maxcount !=0 )) {
3477 /* better to check uio_resid against max or fixedblocksize, but won't work.
3478 * Depending on if dir or file, the attributes returned will be different.
3479 * Thus fixedblocksize is too large in some cases.Also, the variable
3480 * part (like name) could be between fixedblocksize and the max.
3481 */
3482 OSErr result = GetCatalogOffspring(vcb, dirID, index, &catInfo.nodeData, NULL, NULL);
3483 if (result != noErr) {
3484 if (result == cmNotFound) {
3485 *(ap->a_eofflag) = TRUE;
3486 retval = E_NONE;
3487 }
3488 else retval = MacToVFSError(result);
3489 break;
3490 }
3491
3492 /* hide our private meta data directory as does hfs_readdir */
3493 if ((dirID == kRootDirID) &&
3494 catInfo.nodeData.cnd_nodeID == VCBTOHFS(vcb)->hfs_private_metadata_dir &&
3495 catInfo.nodeData.cnd_type == kCatalogFolderNode) {
3496
3497 ++index;
3498 continue;
3499 }
3500
3501 *((u_long *)attrptr)++ = 0; /* move it past length */
3502
3503 if (ap->a_options & FSOPT_NOINMEMUPDATE) {
3504 /* vp okay to use instead of root vp */
3505 PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
3506 } else {
3507 /* Check to see if there's a vnode for this item in the cache: */
3508 entryvnode = hfs_vhashget(H_DEV(VTOH(vp)), catInfo.nodeData.cnd_nodeID, kDefault);
3509 if (entryvnode != NULL) {
3510 PackAttributeBlock(alist, entryvnode, &catInfo, &attrptr, &varptr);
3511 vput(entryvnode);
3512 } else {
3513 /* vp okay to use instead of root vp */
3514 PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
3515 };
3516 };
3517 currattrbufsize = *((u_long *)attrbufptr) = ((char *)varptr - (char *)attrbufptr);
3518
3519 /* now check if we can't fit in the buffer space remaining */
3520 if (currattrbufsize > uio->uio_resid)
3521 break;
3522 else {
3523 retval = uiomove((caddr_t)attrbufptr, currattrbufsize, ap->a_uio);
3524 if (retval != E_NONE)
3525 break;
3526 attrptr = attrbufptr;
3527 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
3528 index++;
3529 *ap->a_actualcount += 1;
3530 maxcount--;
3531 }
3532 };
3533 *ap->a_newstate = VTOH(vp)->h_meta->h_mtime;/* before we unlock, know the mod date */
3534 /* Unlock catalog b-tree, finally. Ties up the everything during enumeration */
3535 (void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, proc );
3536
3537 CLEAN_CATALOGDATA(&catInfo.nodeData);
3538
3539 if (!retval && ap->a_cookies != NULL) { /* CHECK THAT 0 wasn't passed in */
3540 void* dpStart;
3541 void* dpEnd;
3542 void* dp;
3543 u_long *cookies;
3544 u_long *cookiep;
3545
3546 /* Only the NFS server uses cookies, and it loads the
3547 * directory block into system space, so we can just look at
3548 * it directly.
3549 */
3550 if (uio->uio_segflg != UIO_SYSSPACE) /* || uio->uio_iovcnt != 1 checked earlier */
3551 panic("hfs_readdirattr: unexpected uio from NFS server");
3552 dpStart = uio->uio_iov->iov_base - (uio->uio_offset - startoffset);
3553 dpEnd = uio->uio_iov->iov_base;
3554 MALLOC(cookies, u_long *, (*ap->a_actualcount)*sizeof(u_long), M_TEMP, M_WAITOK);
3555 for (dp = dpStart, cookiep = cookies;
3556 dp < dpEnd;
3557 dp = ((caddr_t) dp + *((u_long *)dp))) {
3558 *cookiep++ = (u_long)((caddr_t)dp + sizeof(u_long));
3559 }
3560 *ap->a_cookies = cookies;
3561 }
3562
3563 uio->uio_offset = startoffset + (*ap->a_actualcount)*sizeof(struct dirent);
3564
3565 exit:
3566 if (attrbufptr != NULL)
3567 FREE(attrbufptr, M_TEMP);
3568 return (retval);
3569 }
3570
3571
3572 /*
3573 * Return target name of a symbolic link
3574 #% readlink vp L L L
3575 #
3576 vop_readlink {
3577 IN struct vnode *vp;
3578 INOUT struct uio *uio;
3579 IN struct ucred *cred;
3580 */
3581
3582 int
3583 hfs_readlink(ap)
3584 struct vop_readlink_args /* {
3585 struct vnode *a_vp;
3586 struct uio *a_uio;
3587 struct ucred *a_cred;
3588 } */ *ap;
3589 {
3590 int retval;
3591 DBG_FUNC_NAME("readlink");
3592 DBG_VOP_LOCKS_DECL(1);
3593 DBG_VOP_PRINT_FUNCNAME();
3594 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3595
3596 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
3597 retval = VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred);
3598 /* clear IN_ACCESS to prevent needless update of symlink vnode */
3599 VTOH(ap->a_vp)->h_nodeflags &= ~IN_ACCESS;
3600
3601 DBG_VOP_LOCKS_TEST(retval);
3602 return (retval);
3603
3604 }
3605
3606
3607 /*
3608 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
3609 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
3610 #% abortop dvp = = =
3611 #
3612 vop_abortop {
3613 IN struct vnode *dvp;
3614 IN struct componentname *cnp;
3615
3616 */
3617
3618 /* ARGSUSED */
3619
3620 static int
3621 hfs_abortop(ap)
3622 struct vop_abortop_args /* {
3623 struct vnode *a_dvp;
3624 struct componentname *a_cnp;
3625 } */ *ap;
3626 {
3627 DBG_FUNC_NAME("abortop");
3628 DBG_VOP_LOCKS_DECL(1);
3629 DBG_VOP_PRINT_FUNCNAME();
3630 DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
3631 DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
3632
3633
3634 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
3635
3636 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
3637 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
3638 }
3639 DBG_VOP_LOCKS_TEST(E_NONE);
3640 return (E_NONE);
3641 }
3642
3643 // int prthfsactive = 0; /* 1 => print out reclaim of active vnodes */
3644
3645 /*
3646 #% inactive vp L U U
3647 #
3648 vop_inactive {
3649 IN struct vnode *vp;
3650 IN struct proc *p;
3651
3652 */
3653
3654 static int
3655 hfs_inactive(ap)
3656 struct vop_inactive_args /* {
3657 struct vnode *a_vp;
3658 } */ *ap;
3659 {
3660 struct vnode *vp = ap->a_vp;
3661 struct hfsnode *hp = VTOH(vp);
3662 struct proc *p = ap->a_p;
3663 struct timeval tv;
3664 int error = 0;
3665 extern int prtactive;
3666
3667 DBG_FUNC_NAME("inactive");
3668 DBG_VOP_LOCKS_DECL(1);
3669 DBG_VOP_PRINT_FUNCNAME();
3670 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3671
3672 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
3673
3674
3675 if (prtactive && vp->v_usecount <= 0)
3676 vprint("hfs_inactive: pushing active", vp);
3677
3678 if (vp->v_usecount != 0)
3679 DBG_VOP(("%s: bad usecount = %d\n",funcname,vp->v_usecount ));
3680
3681 /*
3682 * Ignore nodes related to stale file handles.
3683 */
3684 if (hp->h_meta->h_mode == 0)
3685 goto out;
3686
3687 /*
3688 * Check for a postponed deletion
3689 */
3690 if (hp->h_meta->h_metaflags & IN_DELETED) {
3691 hp->h_meta->h_metaflags &= ~IN_DELETED;
3692
3693 error = vinvalbuf(vp, 0, NOCRED, p, 0, 0);
3694 if (error) goto out;
3695
3696 if(UBCINFOEXISTS(vp))
3697 (void)ubc_setsize(vp, (off_t)0);
3698
3699 /* Lock both trees
3700 * Note: we do not need a lock on the private metadata directory
3701 * since it never has a vnode associated with it.
3702 */
3703 error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
3704 if (error) goto out;
3705 error = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
3706 if (error) {
3707 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
3708 goto out;
3709 }
3710
3711 if (hp->h_meta->h_metaflags & IN_DATANODE) {
3712 char iNodeName[32];
3713
3714 MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
3715 error = hfsDelete(HTOVCB(hp), VTOHFS(vp)->hfs_private_metadata_dir, iNodeName, TRUE, H_HINT(hp));
3716 } else {
3717 /* XXX can we leave orphaned sibling? */
3718 error = hfsDelete(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
3719 if (error == ENOENT) {
3720 /* try by fileID as a backup */
3721 error = hfsDelete(HTOVCB(hp), H_FILEID(hp), NULL, TRUE, H_HINT(hp));
3722 }
3723 }
3724
3725 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
3726 (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
3727 if (error) goto out;
3728
3729 hp->h_meta->h_metaflags |= IN_NOEXISTS;
3730 hp->h_meta->h_mode = 0;
3731 /* clear the block mappings */
3732 hp->fcbPLen = (u_int64_t)0;
3733 bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
3734
3735 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
3736 }
3737
3738 if (hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
3739 tv = time;
3740 VOP_UPDATE(vp, &tv, &tv, 0);
3741 }
3742
3743 out:
3744 VOP_UNLOCK(vp, 0, p);
3745 /*
3746 * If we are done with the inode, reclaim it
3747 * so that it can be reused immediately.
3748 */
3749 if (hp->h_meta->h_mode == 0)
3750 vrecycle(vp, (struct slock *)0, p);
3751
3752 /* XXX SER Here we might want to get rid of any other forks
3753 * The problem is that if we call vrecycle(), our structure
3754 * disappear from under us, we would need to remember, and expect
3755 * things to go to null or to disappear
3756 * But it stillw would be a good thing to remove vnodes
3757 * referencing stale data
3758 */
3759
3760 DBG_VOP_LOCKS_TEST(E_NONE);
3761 return (E_NONE);
3762 }
3763
3764 /*
3765 Ignored since the locks are gone......
3766 #% reclaim vp U I I
3767 #
3768 vop_reclaim {
3769 IN struct vnode *vp;
3770 IN struct proc *p;
3771
3772 */
3773
3774 static int
3775 hfs_reclaim(ap)
3776 struct vop_reclaim_args /* {
3777 struct vnode *a_vp;
3778 } */ *ap;
3779 {
3780 struct vnode *vp = ap->a_vp;
3781 struct hfsnode *hp = VTOH(vp);
3782 void *tdata = vp->v_data;
3783 char *tname;
3784 Boolean freeMeta = true;
3785 struct vnode *devvp = NULL;
3786
3787 extern int prtactive;
3788 DBG_FUNC_NAME("reclaim");
3789 DBG_VOP_LOCKS_DECL(1);
3790 DBG_VOP_PRINT_FUNCNAME();
3791 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
3792
3793 DBG_VOP_LOCKS_INIT(0, ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
3794
3795 /*
3796 NOTE: XXX vnodes need careful handling because fork vnodes that failed to be
3797 created in their entirity could be getting cleaned up here.
3798 */
3799
3800 if (prtactive && vp->v_usecount != 0)
3801 vprint("hfs_reclaim(): pushing active", vp);
3802
3803 hp->h_nodeflags |= IN_ALLOCATING; /* Mark this as being incomplete */
3804 /*
3805 * This will remove the entry from the hash AND the sibling list
3806 * This will make sure everything is in a stable state to see if we can remove the meta
3807 * i.e. if this is the only fork...the sibling list will be empty
3808 */
3809 hfs_vhashrem(hp);
3810
3811 DBG_ASSERT(tdata != NULL);
3812 DBG_ASSERT(hp->h_meta != NULL);
3813
3814 devvp = hp->h_meta->h_devvp; /* For later releasing */
3815 hp->h_meta->h_usecount--;
3816
3817 /* release the file meta if this is the last fork */
3818 if (H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork) {
3819 if (hp->h_meta->h_siblinghead.cqh_first != (void *) &hp->h_meta->h_siblinghead)
3820 freeMeta = false;
3821 };
3822
3823 if (freeMeta) {
3824 DBG_ASSERT(hp->h_meta->h_usecount == 0);
3825 if (hp->h_meta->h_metaflags & IN_LONGNAME) {
3826 tname = H_NAME(hp);
3827 DBG_ASSERT(tname != NULL);
3828 FREE(tname, M_TEMP);
3829 }
3830 FREE_ZONE(hp->h_meta, sizeof(struct hfsfilemeta), M_HFSFMETA);
3831 hp->h_meta = NULL;
3832 }
3833 else
3834 DBG_ASSERT(hp->h_meta->h_usecount == 1);
3835
3836
3837 /*
3838 * Purge old data structures associated with the inode.
3839 */
3840 cache_purge(vp);
3841 if (devvp) {
3842 vrele(devvp);
3843 };
3844
3845 /* Free our data structs */
3846 FREE_ZONE(tdata, sizeof(struct hfsnode), M_HFSNODE);
3847 vp->v_data = NULL;
3848
3849 DBG_VOP_LOCKS_TEST(E_NONE);
3850 return (E_NONE);
3851 }
3852
3853
3854 /*
3855 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
3856 #% lock vp U L U
3857 #
3858 vop_lock {
3859 IN struct vnode *vp;
3860 IN int flags;
3861 IN struct proc *p;
3862 */
3863
3864 static int
3865 hfs_lock(ap)
3866 struct vop_lock_args /* {
3867 struct vnode *a_vp;
3868 int a_flags;
3869 struct proc *a_p;
3870 } */ *ap;
3871 {
3872 struct vnode * vp = ap->a_vp;
3873 struct hfsnode *hp = VTOH(ap->a_vp);
3874 int retval;
3875
3876 DBG_FUNC_NAME("lock");
3877 DBG_VOP_LOCKS_DECL(1);
3878 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
3879 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
3880 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
3881
3882 retval = lockmgr(&hp->h_lock, ap->a_flags, &vp->v_interlock, ap->a_p);
3883 if (retval != E_NONE) {
3884 if ((ap->a_flags & LK_NOWAIT) == 0)
3885 DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval, ap->a_flags));
3886 goto Err_Exit;
3887 };
3888
3889 Err_Exit:;
3890 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
3891 DBG_VOP_LOCKS_TEST(retval);
3892 return (retval);
3893 }
3894
3895 /*
3896 * Unlock an hfsnode.
3897 #% unlock vp L U L
3898 #
3899 vop_unlock {
3900 IN struct vnode *vp;
3901 IN int flags;
3902 IN struct proc *p;
3903
3904 */
3905 int
3906 hfs_unlock(ap)
3907 struct vop_unlock_args /* {
3908 struct vnode *a_vp;
3909 int a_flags;
3910 struct proc *a_p;
3911 } */ *ap;
3912 {
3913 struct hfsnode *hp = VTOH(ap->a_vp);
3914 struct vnode *vp = ap->a_vp;
3915 int retval = E_NONE;
3916
3917 DBG_FUNC_NAME("unlock");
3918 DBG_VOP_LOCKS_DECL(1);
3919 DBG_VOP_PRINT_FUNCNAME();
3920 DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
3921 DBG_VOP_LOCKS_INIT(0,vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
3922
3923
3924 DBG_ASSERT((ap->a_flags & (LK_EXCLUSIVE|LK_SHARED)) == 0);
3925 retval = lockmgr(&hp->h_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p);
3926 if (retval != E_NONE) {
3927 DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval, H_FORKTYPE(hp)));
3928 };
3929
3930 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
3931 DBG_VOP_LOCKS_TEST(retval);
3932 return (retval);
3933 }
3934
3935
3936 /*
3937 * Print out the contents of an hfsnode.
3938 #% print vp = = =
3939 #
3940 vop_print {
3941 IN struct vnode *vp;
3942 */
3943 int
3944 hfs_print(ap)
3945 struct vop_print_args /* {
3946 struct vnode *a_vp;
3947 } */ *ap;
3948 {
3949 register struct vnode * vp = ap->a_vp;
3950 register struct hfsnode *hp = VTOH( vp);
3951 DBG_FUNC_NAME("print");
3952 DBG_VOP_LOCKS_DECL(1);
3953 DBG_VOP_PRINT_FUNCNAME();
3954 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
3955
3956 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
3957
3958 printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp),
3959 major(H_DEV(hp)), minor(H_DEV(hp)));
3960 /* lockmgr_printinfo(&hp->h_lock); */
3961 printf("\n");
3962 DBG_VOP_LOCKS_TEST(E_NONE);
3963 return (E_NONE);
3964 }
3965
3966
3967 /*
3968 * Check for a locked hfsnode.
3969 #% islocked vp = = =
3970 #
3971 vop_islocked {
3972 IN struct vnode *vp;
3973
3974 */
3975 int
3976 hfs_islocked(ap)
3977 struct vop_islocked_args /* {
3978 struct vnode *a_vp;
3979 } */ *ap;
3980 {
3981 int lockStatus;
3982 //DBG_FUNC_NAME("islocked");
3983 //DBG_VOP_LOCKS_DECL(1);
3984 //DBG_VOP_PRINT_FUNCNAME();
3985 //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
3986
3987 //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
3988
3989 lockStatus = lockstatus(&VTOH( ap->a_vp)->h_lock);
3990 //DBG_VOP_LOCKS_TEST(E_NONE);
3991 return (lockStatus);
3992 }
3993
3994 /*
3995
3996 #% pathconf vp L L L
3997 #
3998 vop_pathconf {
3999 IN struct vnode *vp;
4000 IN int name;
4001 OUT register_t *retval;
4002
4003 */
4004 static int
4005 hfs_pathconf(ap)
4006 struct vop_pathconf_args /* {
4007 struct vnode *a_vp;
4008 int a_name;
4009 int *a_retval;
4010 } */ *ap;
4011 {
4012 int retval = E_NONE;
4013 DBG_FUNC_NAME("pathconf");
4014 DBG_VOP_LOCKS_DECL(1);
4015 DBG_VOP_PRINT_FUNCNAME();
4016 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
4017
4018 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
4019
4020 DBG_HFS_NODE_CHECK (ap->a_vp);
4021
4022 switch (ap->a_name) {
4023 case _PC_LINK_MAX:
4024 #if HFS_HARDLINKS
4025 if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
4026 *ap->a_retval = HFS_LINK_MAX;
4027 else
4028 *ap->a_retval = 1;
4029 #else
4030 *ap->a_retval = 1;
4031 #endif
4032 break;
4033 case _PC_NAME_MAX:
4034 *ap->a_retval = kHFSPlusMaxFileNameBytes; /* max # of characters x max utf8 representation */
4035 break;
4036 case _PC_PATH_MAX:
4037 *ap->a_retval = PATH_MAX; /* 1024 */
4038 break;
4039 case _PC_CHOWN_RESTRICTED:
4040 *ap->a_retval = 1;
4041 break;
4042 case _PC_NO_TRUNC:
4043 *ap->a_retval = 0;
4044 break;
4045 default:
4046 retval = EINVAL;
4047 }
4048
4049 DBG_VOP_LOCKS_TEST(retval);
4050 return (retval);
4051 }
4052
4053
4054
4055
4056
4057 /*
4058 * Advisory record locking support
4059 #% advlock vp U U U
4060 #
4061 vop_advlock {
4062 IN struct vnode *vp;
4063 IN caddr_t id;
4064 IN int op;
4065 IN struct flock *fl;
4066 IN int flags;
4067
4068 */
4069 int
4070 hfs_advlock(ap)
4071 struct vop_advlock_args /* {
4072 struct vnode *a_vp;
4073 caddr_t a_id;
4074 int a_op;
4075 struct flock *a_fl;
4076 int a_flags;
4077 } */ *ap;
4078 {
4079 register struct hfsnode *hp = VTOH(ap->a_vp);
4080 register struct flock *fl = ap->a_fl;
4081 register struct hfslockf *lock;
4082 off_t start, end;
4083 int retval;
4084 DBG_FUNC_NAME("advlock");
4085 DBG_VOP_LOCKS_DECL(1);
4086 DBG_VOP_PRINT_FUNCNAME();
4087 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
4088 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
4089 /*
4090 * Avoid the common case of unlocking when inode has no locks.
4091 */
4092 if (hp->h_lockf == (struct hfslockf *)0) {
4093 if (ap->a_op != F_SETLK) {
4094 fl->l_type = F_UNLCK;
4095 return (0);
4096 }
4097 }
4098 /*
4099 * Convert the flock structure into a start and end.
4100 */
4101 start = 0;
4102 switch (fl->l_whence) {
4103 case SEEK_SET:
4104 case SEEK_CUR:
4105 /*
4106 * Caller is responsible for adding any necessary offset
4107 * when SEEK_CUR is used.
4108 */
4109 start = fl->l_start;
4110 break;
4111
4112 case SEEK_END:
4113 start = HTOFCB(hp)->fcbEOF + fl->l_start;
4114 break;
4115
4116 default:
4117 return (EINVAL);
4118 }
4119
4120 if (start < 0)
4121 return (EINVAL);
4122 if (fl->l_len == 0)
4123 end = -1;
4124 else
4125 end = start + fl->l_len - 1;
4126
4127 /*
4128 * Create the hfslockf structure
4129 */
4130 MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
4131 lock->lf_start = start;
4132 lock->lf_end = end;
4133 lock->lf_id = ap->a_id;
4134 lock->lf_hfsnode = hp;
4135 lock->lf_type = fl->l_type;
4136 lock->lf_next = (struct hfslockf *)0;
4137 TAILQ_INIT(&lock->lf_blkhd);
4138 lock->lf_flags = ap->a_flags;
4139 /*
4140 * Do the requested operation.
4141 */
4142 switch(ap->a_op) {
4143 case F_SETLK:
4144 retval = hfs_setlock(lock);
4145 break;
4146
4147 case F_UNLCK:
4148 retval = hfs_clearlock(lock);
4149 FREE(lock, M_LOCKF);
4150 break;
4151
4152 case F_GETLK:
4153 retval = hfs_getlock(lock, fl);
4154 FREE(lock, M_LOCKF);
4155 break;
4156
4157 default:
4158 retval = EINVAL;
4159 _FREE(lock, M_LOCKF);
4160 break;
4161 }
4162
4163 DBG_VOP_LOCKS_TEST(retval);
4164 return (retval);
4165 }
4166
4167
4168
4169 /*
4170 * Update the access, modified, and node change times as specified by the
4171 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
4172 * used to specify that the node needs to be updated but that the times have
4173 * already been set. The access and modified times are taken from the second
4174 * and third parameters; the node change time is always taken from the current
4175 * time. If waitfor is set, then wait for the disk write of the node to
4176 * complete.
4177 */
4178 /*
4179 #% update vp L L L
4180 IN struct vnode *vp;
4181 IN struct timeval *access;
4182 IN struct timeval *modify;
4183 IN int waitfor;
4184 */
4185
4186 int
4187 hfs_update(ap)
4188 struct vop_update_args /* {
4189 struct vnode *a_vp;
4190 struct timeval *a_access;
4191 struct timeval *a_modify;
4192 int a_waitfor;
4193 } */ *ap;
4194 {
4195 struct hfsnode *hp;
4196 struct proc *p;
4197 hfsCatalogInfo catInfo;
4198 char *filename;
4199 char iNodeName[32];
4200 u_int32_t pid;
4201 int retval;
4202 ExtendedVCB *vcb;
4203 DBG_FUNC_NAME("update");
4204 DBG_VOP_LOCKS_DECL(1);
4205 DBG_VOP_PRINT_FUNCNAME();
4206 DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
4207 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
4208
4209 hp = VTOH(ap->a_vp);
4210
4211 DBG_ASSERT(hp && hp->h_meta);
4212 DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
4213
4214 if ((H_FORKTYPE(hp) == kSysFile) ||
4215 (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) ||
4216 (hp->h_meta->h_mode == 0)) {
4217 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4218 DBG_VOP_LOCKS_TEST(0);
4219 return (0);
4220 }
4221
4222 if (H_FORKTYPE(hp) == kSysFile) {
4223 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4224 DBG_VOP_LOCKS_TEST(0);
4225 return (0);
4226 }
4227
4228 if (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) {
4229 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4230 DBG_VOP_LOCKS_TEST(0);
4231 return (0);
4232 }
4233
4234 /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
4235 if (HTOFCB(hp)->fcbFlags &fcbModifiedMask)
4236 hp->h_nodeflags |= IN_MODIFIED;
4237
4238 if ((hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) {
4239 DBG_VOP_LOCKS_TEST(0);
4240 return (0);
4241 };
4242
4243 if (hp->h_nodeflags & IN_ACCESS)
4244 hp->h_meta->h_atime = ap->a_access->tv_sec;
4245 if (hp->h_nodeflags & IN_UPDATE)
4246 hp->h_meta->h_mtime = ap->a_modify->tv_sec;
4247 if (hp->h_nodeflags & IN_CHANGE) {
4248 hp->h_meta->h_ctime = time.tv_sec;
4249 /*
4250 * HFS dates that WE set must be adjusted for DST
4251 */
4252 if ((HTOVCB(hp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
4253 hp->h_meta->h_ctime += 3600;
4254 hp->h_meta->h_mtime = hp->h_meta->h_ctime;
4255 }
4256 }
4257
4258 p = current_proc();
4259 filename = H_NAME(hp);
4260 pid = H_DIRID(hp);
4261 vcb = HTOVCB(hp);
4262 catInfo.hint = H_HINT(hp);
4263
4264 #if HFS_HARDLINKS
4265 /*
4266 * Force an update of the indirect node instead of the link
4267 * by using the name and parent of the indirect node.
4268 */
4269 if (hp->h_meta->h_metaflags & IN_DATANODE) {
4270 MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
4271 filename = iNodeName;
4272 pid = VCBTOHFS(vcb)->hfs_private_metadata_dir;
4273 }
4274 #endif
4275
4276 INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
4277
4278 /*
4279 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
4280 * the Catalog b-tree may aready be locked by the current thread. So we
4281 * allow recursive locking of the Catalog from within VOP_UPDATE.
4282 */
4283 /* Lock the Catalog b-tree file */
4284 retval = hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
4285 if (retval) {
4286 DBG_VOP_LOCKS_TEST(retval);
4287 return (retval);
4288 };
4289
4290 retval = hfs_getcatalog(vcb, pid, filename, -1, &catInfo);
4291 if (retval != noErr) {
4292 (void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
4293 retval = MacToVFSError(retval);
4294 goto Err_Exit;
4295 };
4296
4297 H_HINT(hp) = catInfo.hint;
4298 CopyVNodeToCatalogNode (HTOV(hp), &catInfo.nodeData);
4299
4300 retval = UpdateCatalogNode(vcb, pid, filename, H_HINT(hp), &catInfo.nodeData);
4301
4302 /* unlock the Catalog b-tree file */
4303 (void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
4304
4305 if (retval != noErr) { /* from UpdateCatalogNode() */
4306 retval = MacToVFSError(retval);
4307 goto Err_Exit;
4308 };
4309
4310 /* After the updates are finished, clear the flags */
4311 hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
4312 HTOFCB(hp)->fcbFlags &= ~fcbModifiedMask;
4313
4314 /* Update general data */
4315 if (ap->a_vp->v_type == VDIR) {
4316 hp->h_meta->h_nlink = 2 + catInfo.nodeData.cnd_valence;
4317 hp->h_meta->h_size = sizeof(rootdots) +
4318 (catInfo.nodeData.cnd_valence * AVERAGE_HFSDIRENTRY_SIZE);
4319 if (hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE)
4320 hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE;
4321 } else {
4322 hp->h_meta->h_size = (off_t)vcb->blockSize *
4323 (off_t)(catInfo.nodeData.cnd_rsrcfork.totalBlocks +
4324 catInfo.nodeData.cnd_datafork.totalBlocks);
4325 }
4326
4327
4328 Err_Exit:
4329
4330 CLEAN_CATALOGDATA(&catInfo.nodeData);
4331
4332 DBG_VOP_LOCKS_TEST(retval);
4333 return (retval);
4334 }
4335
4336
4337 /*
4338 * Initialize the vnode associated with a new hfsnode,
4339 * handle aliased vnodes.
4340 */
4341 int
4342 hfs_vinit(mntp, specops, fifoops, vpp)
4343 struct mount *mntp;
4344 int (**specops)(void *);
4345 int (**fifoops)(void *);
4346 struct vnode **vpp;
4347 {
4348 struct hfsnode *hp;
4349 struct vnode *vp, *nvp;
4350
4351 vp = *vpp;
4352 hp = VTOH(vp);
4353 /* vp->v_type set in CopyCatalogToHFSNode */
4354 switch(vp->v_type) {
4355 case VCHR:
4356 case VBLK:
4357 vp->v_op = specops;
4358 if ((nvp = checkalias(vp, hp->h_meta->h_rdev, mntp))) {
4359 /*
4360 * Discard unneeded vnode, but save its hfsnode.
4361 * Note that the lock is carried over in the hfsnode
4362 * to the replacement vnode.
4363 */
4364 nvp->v_data = vp->v_data;
4365 vp->v_data = NULL;
4366 vp->v_op = spec_vnodeop_p;
4367 vrele(vp);
4368 vgone(vp);
4369 /*
4370 * Reinitialize aliased hfsnode.
4371 */
4372
4373 hp->h_vp = nvp;
4374 vp = nvp;
4375 }
4376 break;
4377 case VFIFO:
4378 #if FIFO
4379 vp->v_op = fifoops;
4380 break;
4381 #else
4382 return (EOPNOTSUPP);
4383 #endif
4384 default:
4385 break;
4386 }
4387 if (H_FILEID(hp) == kRootDirID)
4388 vp->v_flag |= VROOT;
4389
4390 *vpp = vp;
4391 return (0);
4392 }
4393
4394 /*
4395 * Allocate a new node
4396 *
4397 * Upon leaving, namei buffer must be freed.
4398 *
4399 */
4400 static int
4401 hfs_makenode(mode, rawdev, dvp, vpp, cnp, p)
4402 int mode;
4403 dev_t rawdev;
4404 struct vnode *dvp;
4405 struct vnode **vpp;
4406 struct componentname *cnp;
4407 struct proc *p;
4408 {
4409 register struct hfsnode *hp, *parhp;
4410 struct timeval tv;
4411 struct vnode *tvp;
4412 struct hfsCatalogInfo catInfo;
4413 ExtendedVCB *vcb;
4414 UInt8 forkType;
4415 int retval;
4416 int hasmetalock = 0;
4417 DBG_FUNC_NAME("makenode");
4418
4419 parhp = VTOH(dvp);
4420 vcb = HTOVCB(parhp);
4421 *vpp = NULL;
4422 tvp = NULL;
4423 if ((mode & IFMT) == 0)
4424 mode |= IFREG;
4425
4426 #if HFS_DIAGNOSTIC
4427 if ((cnp->cn_flags & HASBUF) == 0)
4428 panic("hfs_makenode: no name");
4429 #endif
4430
4431 /* lock catalog b-tree */
4432 retval = hfs_metafilelocking(VTOHFS(dvp),
4433 kHFSCatalogFileID, LK_EXCLUSIVE, p);
4434 if (retval != E_NONE)
4435 goto bad1;
4436 else
4437 hasmetalock = 1;
4438
4439 /* Create the Catalog B*-Tree entry */
4440 retval = hfsCreate(vcb, H_FILEID(parhp), cnp->cn_nameptr, mode);
4441 if (retval != E_NONE) {
4442 DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
4443 goto bad1;
4444 }
4445
4446 /* Look up the catalog entry just created: */
4447 INIT_CATALOGDATA(&catInfo.nodeData, 0);
4448 catInfo.hint = kNoHint;
4449
4450 retval = hfs_getcatalog(vcb, H_FILEID(parhp), cnp->cn_nameptr, cnp->cn_namelen, &catInfo);
4451 if (retval != E_NONE) {
4452 DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
4453 goto bad1;
4454 }
4455
4456 /* unlock catalog b-tree */
4457 hasmetalock = 0;
4458 (void) hfs_metafilelocking(VTOHFS(dvp),
4459 kHFSCatalogFileID, LK_RELEASE, p);
4460
4461 /* hfs plus has additional metadata to initialize */
4462 if (vcb->vcbSigWord == kHFSPlusSigWord) {
4463 u_int32_t pflags;
4464 int catmode;
4465
4466 if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
4467 catInfo.nodeData.cnd_ownerID = VTOHFS(dvp)->hfs_uid;
4468 catInfo.nodeData.cnd_groupID = VTOHFS(dvp)->hfs_gid;
4469 catmode = mode;
4470 } else {
4471 catInfo.nodeData.cnd_ownerID = cnp->cn_cred->cr_uid;
4472 catInfo.nodeData.cnd_groupID = parhp->h_meta->h_gid;
4473 catmode = mode;
4474 }
4475
4476 switch (catmode & IFMT) {
4477 case IFLNK:
4478 catInfo.nodeData.cnd_ownerID = parhp->h_meta->h_uid;
4479 break;
4480
4481 case IFCHR:
4482 case IFBLK:
4483 /* XXX should we move this to post hfsGet? */
4484 catInfo.nodeData.cnd_rawDevice = rawdev;
4485 /*
4486 * Don't tag as a special file (BLK or CHR) until *after*
4487 * hfsGet is called. This insures that the checkalias call
4488 * is defered until hfs_mknod completes.
4489 */
4490 catmode = (catmode & ~IFMT) | IFREG;
4491 break;
4492 }
4493
4494 if ((catmode & ISGID) && !groupmember(parhp->h_meta->h_gid, cnp->cn_cred) &&
4495 suser(cnp->cn_cred, NULL))
4496 catmode &= ~ISGID;
4497
4498 if (cnp->cn_flags & ISWHITEOUT)
4499 pflags = UF_OPAQUE;
4500 else
4501 pflags = 0;
4502
4503 /*
4504 * The 32-bit pflags field has two bytes of significance which
4505 * are stored separately as admin and owner flags.
4506 *
4507 * +------------------------------------+
4508 * pflags: |XXXXXXXX| SF |XXXXXXXX| UF |
4509 * +------------------------------------+
4510 */
4511 catInfo.nodeData.cnd_adminFlags = (pflags >> 16) & 0x00FF;
4512 catInfo.nodeData.cnd_ownerFlags = pflags & 0x00FF;
4513 catInfo.nodeData.cnd_mode = catmode;
4514 }
4515
4516 /* Create a vnode for the object just created: */
4517 forkType = (catInfo.nodeData.cnd_type == kCatalogFolderNode) ? kDirectory : kDataFork;
4518 retval = hfs_vcreate(vcb, &catInfo, forkType, &tvp);
4519
4520 CLEAN_CATALOGDATA(&catInfo.nodeData); /* Should do nothing */
4521
4522 if (retval) goto bad1; /* from hfs_vcreate() */
4523
4524 /* flush out pflags, mode, gid, uid and rdev */
4525 tv = time;
4526 if (vcb->vcbSigWord == kHFSPlusSigWord) {
4527 hp = VTOH(tvp);
4528 /* reset mode and v_type in case it was BLK/CHR */
4529 hp->h_meta->h_mode = mode;
4530 tvp->v_type = IFTOVT(mode);
4531 hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
4532 hp->h_nodeflags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
4533 if ((retval = VOP_UPDATE(tvp, &tv, &tv, 1)))
4534 goto bad2;
4535 }
4536
4537 VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4538 if ((retval = VOP_UPDATE(dvp, &tv, &tv, 1)))
4539 goto bad2;
4540
4541 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
4542 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
4543 };
4544 vput(dvp);
4545 if (UBCINFOMISSING(tvp) || UBCINFORECLAIMED(tvp))
4546 ubc_info_init(tvp);
4547
4548 *vpp = tvp;
4549 return (0);
4550
4551 bad2:
4552 /*
4553 * Write retval occurred trying to update the node
4554 * or the directory so must deallocate the node.
4555 */
4556 /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
4557 vput(tvp);
4558
4559 bad1:
4560 if (hasmetalock) {
4561 /* unlock catalog b-tree */
4562 hasmetalock = 0;
4563 (void) hfs_metafilelocking(VTOHFS(dvp),
4564 kHFSCatalogFileID, LK_RELEASE, p);
4565 }
4566 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
4567 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
4568 };
4569 vput(dvp);
4570
4571 return (retval);
4572 }
4573
4574
4575 #if DBG_VOP_TEST_LOCKS
4576
4577 /* XXX SER Add passing in the flags...might not be a serious error if locked */
4578
4579 void DbgVopTest( int maxSlots,
4580 int retval,
4581 VopDbgStoreRec *VopDbgStore,
4582 char *funcname)
4583 {
4584 int index;
4585
4586 for (index = 0; index < maxSlots; index++)
4587 {
4588 if (VopDbgStore[index].id != index) {
4589 DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index));
4590 };
4591
4592 if ((VopDbgStore[index].vp != NULL) &&
4593 ((VopDbgStore[index].vp->v_data==NULL) || (VTOH(VopDbgStore[index].vp)->h_valid != HFS_VNODE_MAGIC)))
4594 continue;
4595
4596 if (VopDbgStore[index].vp != NULL)
4597 debug_check_vnode(VopDbgStore[index].vp, 0);
4598
4599 switch (VopDbgStore[index].inState)
4600 {
4601 case VOPDBG_IGNORE:
4602 case VOPDBG_SAME:
4603 /* Do Nothing !!! */
4604 break;
4605 case VOPDBG_LOCKED:
4606 case VOPDBG_UNLOCKED:
4607 case VOPDBG_LOCKNOTNIL:
4608 {
4609 if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
4610 DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index));
4611 } else if (VopDbgStore[index].vp != NULL) {
4612 switch (VopDbgStore[index].inState)
4613 {
4614 case VOPDBG_LOCKED:
4615 case VOPDBG_LOCKNOTNIL:
4616 if (VopDbgStore[index].inValue == 0)
4617 {
4618 DBG_ERR (("%s: Entry: not LOCKED:", funcname));
4619 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4620 DBG_ERR (("\n"));
4621 }
4622 break;
4623 case VOPDBG_UNLOCKED:
4624 if (VopDbgStore[index].inValue != 0)
4625 {
4626 DBG_ERR (("%s: Entry: not UNLOCKED:", funcname));
4627 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4628 DBG_ERR (("\n"));
4629 }
4630 break;
4631 }
4632 }
4633 break;
4634 }
4635 default:
4636 DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
4637 }
4638
4639
4640 if (retval != 0)
4641 {
4642 switch (VopDbgStore[index].errState)
4643 {
4644 case VOPDBG_IGNORE:
4645 /* Do Nothing !!! */
4646 break;
4647 case VOPDBG_LOCKED:
4648 case VOPDBG_UNLOCKED:
4649 case VOPDBG_SAME:
4650 {
4651 if (VopDbgStore[index].vp == NULL) {
4652 DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index));
4653 } else {
4654 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4655 switch (VopDbgStore[index].errState)
4656 {
4657 case VOPDBG_LOCKED:
4658 if (VopDbgStore[index].outValue == 0)
4659 {
4660 DBG_ERR (("%s: Error: not LOCKED:", funcname));
4661 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4662 DBG_ERR(("\n"));
4663 }
4664 break;
4665 case VOPDBG_UNLOCKED:
4666 if (VopDbgStore[index].outValue != 0)
4667 {
4668 DBG_ERR (("%s: Error: not UNLOCKED:", funcname));
4669 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4670 DBG_ERR(("\n"));
4671 }
4672 break;
4673 case VOPDBG_SAME:
4674 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
4675 DBG_ERR (("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
4676 break;
4677 }
4678 }
4679 break;
4680 }
4681 case VOPDBG_LOCKNOTNIL:
4682 if (VopDbgStore[index].vp != NULL) {
4683 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4684 if (VopDbgStore[index].outValue == 0)
4685 DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
4686 }
4687 break;
4688 default:
4689 DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
4690 }
4691 }
4692 else
4693 {
4694 switch (VopDbgStore[index].outState)
4695 {
4696 case VOPDBG_IGNORE:
4697 /* Do Nothing !!! */
4698 break;
4699 case VOPDBG_LOCKED:
4700 case VOPDBG_UNLOCKED:
4701 case VOPDBG_SAME:
4702 if (VopDbgStore[index].vp == NULL) {
4703 DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index));
4704 };
4705 if (VopDbgStore[index].vp != NULL)
4706 {
4707 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4708 switch (VopDbgStore[index].outState)
4709 {
4710 case VOPDBG_LOCKED:
4711 if (VopDbgStore[index].outValue == 0)
4712 {
4713 DBG_ERR (("%s: Out: not LOCKED:", funcname));
4714 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4715 DBG_ERR (("\n"));
4716 }
4717 break;
4718 case VOPDBG_UNLOCKED:
4719 if (VopDbgStore[index].outValue != 0)
4720 {
4721 DBG_ERR (("%s: Out: not UNLOCKED:", funcname));
4722 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
4723 DBG_ERR (("\n"));
4724 }
4725 break;
4726 case VOPDBG_SAME:
4727 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
4728 DBG_ERR (("%s: Out: In/Out locks are DIFFERENT: 0x%x, in is %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
4729 break;
4730 }
4731 }
4732 break;
4733 case VOPDBG_LOCKNOTNIL:
4734 if (VopDbgStore[index].vp != NULL) {
4735 if (&VTOH(VopDbgStore[index].vp)->h_lock == NULL) {
4736 DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
4737 }
4738 else {
4739 VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
4740 if (VopDbgStore[index].outValue == 0)
4741 {
4742 DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname));
4743 DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); DBG_ERR (("\n"));
4744 }
4745 }
4746 }
4747 break;
4748 default:
4749 DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState));
4750 }
4751 }
4752
4753 VopDbgStore[index].id = -1; /* Invalidate the entry to allow panic-free re-use */
4754 }
4755 }
4756
4757 #endif /* DBG_VOP_TEST_LOCKS */
4758
4759 /*
4760 * Wrapper for special device reads
4761 */
4762 int
4763 hfsspec_read(ap)
4764 struct vop_read_args /* {
4765 struct vnode *a_vp;
4766 struct uio *a_uio;
4767 int a_ioflag;
4768 struct ucred *a_cred;
4769 } */ *ap;
4770 {
4771
4772 /*
4773 * Set access flag.
4774 */
4775 VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
4776 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
4777 }
4778
4779 /*
4780 * Wrapper for special device writes
4781 */
4782 int
4783 hfsspec_write(ap)
4784 struct vop_write_args /* {
4785 struct vnode *a_vp;
4786 struct uio *a_uio;
4787 int a_ioflag;
4788 struct ucred *a_cred;
4789 } */ *ap;
4790 {
4791
4792 /*
4793 * Set update and change flags.
4794 */
4795 VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4796 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
4797 }
4798
4799 /*
4800 * Wrapper for special device close
4801 *
4802 * Update the times on the hfsnode then do device close.
4803 */
4804 int
4805 hfsspec_close(ap)
4806 struct vop_close_args /* {
4807 struct vnode *a_vp;
4808 int a_fflag;
4809 struct ucred *a_cred;
4810 struct proc *a_p;
4811 } */ *ap;
4812 {
4813 struct vnode *vp = ap->a_vp;
4814 struct hfsnode *hp = VTOH(vp);
4815
4816 simple_lock(&vp->v_interlock);
4817 if (ap->a_vp->v_usecount > 1)
4818 HFSTIMES(hp, &time, &time);
4819 simple_unlock(&vp->v_interlock);
4820 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
4821 }
4822
4823 #if FIFO
4824 /*
4825 * Wrapper for fifo reads
4826 */
4827 int
4828 hfsfifo_read(ap)
4829 struct vop_read_args /* {
4830 struct vnode *a_vp;
4831 struct uio *a_uio;
4832 int a_ioflag;
4833 struct ucred *a_cred;
4834 } */ *ap;
4835 {
4836 extern int (**fifo_vnodeop_p)(void *);
4837
4838 /*
4839 * Set access flag.
4840 */
4841 VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
4842 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
4843 }
4844
4845 /*
4846 * Wrapper for fifo writes
4847 */
4848 int
4849 hfsfifo_write(ap)
4850 struct vop_write_args /* {
4851 struct vnode *a_vp;
4852 struct uio *a_uio;
4853 int a_ioflag;
4854 struct ucred *a_cred;
4855 } */ *ap;
4856 {
4857 extern int (**fifo_vnodeop_p)(void *);
4858
4859 /*
4860 * Set update and change flags.
4861 */
4862 VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
4863 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
4864 }
4865
4866 /*
4867 * Wrapper for fifo close
4868 *
4869 * Update the times on the hfsnode then do device close.
4870 */
4871 int
4872 hfsfifo_close(ap)
4873 struct vop_close_args /* {
4874 struct vnode *a_vp;
4875 int a_fflag;
4876 struct ucred *a_cred;
4877 struct proc *a_p;
4878 } */ *ap;
4879 {
4880 extern int (**fifo_vnodeop_p)(void *);
4881 struct vnode *vp = ap->a_vp;
4882 struct hfsnode *hp = VTOH(vp);
4883
4884 simple_lock(&vp->v_interlock);
4885 if (ap->a_vp->v_usecount > 1)
4886 HFSTIMES(hp, &time, &time);
4887 simple_unlock(&vp->v_interlock);
4888 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
4889 }
4890 #endif /* FIFO */
4891
4892
4893 /*****************************************************************************
4894 *
4895 * VOP Tables
4896 *
4897 *****************************************************************************/
4898
4899 #define VOPFUNC int (*)(void *)
4900
4901 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
4902 { &vop_default_desc, (VOPFUNC)vn_default_error },
4903 { &vop_lookup_desc, (VOPFUNC)hfs_cache_lookup }, /* lookup */
4904 { &vop_create_desc, (VOPFUNC)hfs_create }, /* create */
4905 { &vop_mknod_desc, (VOPFUNC)hfs_mknod }, /* mknod */
4906 { &vop_open_desc, (VOPFUNC)hfs_open }, /* open */
4907 { &vop_close_desc, (VOPFUNC)hfs_close }, /* close */
4908 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4909 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4910 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4911 { &vop_read_desc, (VOPFUNC)hfs_read }, /* read */
4912 { &vop_write_desc, (VOPFUNC)hfs_write }, /* write */
4913 { &vop_ioctl_desc, (VOPFUNC)hfs_ioctl }, /* ioctl */
4914 { &vop_select_desc, (VOPFUNC)hfs_select }, /* select */
4915 { &vop_exchange_desc, (VOPFUNC)hfs_exchange }, /* exchange */
4916 { &vop_mmap_desc, (VOPFUNC)hfs_mmap }, /* mmap */
4917 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4918 { &vop_seek_desc, (VOPFUNC)hfs_seek }, /* seek */
4919 { &vop_remove_desc, (VOPFUNC)hfs_remove }, /* remove */
4920 #if HFS_HARDLINKS
4921 { &vop_link_desc, (VOPFUNC)hfs_link }, /* link */
4922 #else
4923 { &vop_link_desc, (VOPFUNC)err_link }, /* link (NOT SUPPORTED) */
4924 #endif
4925 { &vop_rename_desc, (VOPFUNC)hfs_rename }, /* rename */
4926 { &vop_mkdir_desc, (VOPFUNC)hfs_mkdir }, /* mkdir */
4927 { &vop_rmdir_desc, (VOPFUNC)hfs_rmdir }, /* rmdir */
4928 { &vop_mkcomplex_desc, (VOPFUNC)hfs_mkcomplex }, /* mkcomplex */
4929 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist }, /* getattrlist */
4930 { &vop_setattrlist_desc, (VOPFUNC)hfs_setattrlist }, /* setattrlist */
4931 { &vop_symlink_desc, (VOPFUNC)hfs_symlink }, /* symlink */
4932 { &vop_readdir_desc, (VOPFUNC)hfs_readdir }, /* readdir */
4933 { &vop_readdirattr_desc, (VOPFUNC)hfs_readdirattr }, /* readdirattr */
4934 { &vop_readlink_desc, (VOPFUNC)hfs_readlink }, /* readlink */
4935 { &vop_abortop_desc, (VOPFUNC)hfs_abortop }, /* abortop */
4936 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4937 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4938 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4939 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4940 { &vop_bmap_desc, (VOPFUNC)hfs_bmap }, /* bmap */
4941 { &vop_strategy_desc, (VOPFUNC)hfs_strategy }, /* strategy */
4942 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
4943 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
4944 { &vop_pathconf_desc, (VOPFUNC)hfs_pathconf }, /* pathconf */
4945 { &vop_advlock_desc, (VOPFUNC)hfs_advlock }, /* advlock */
4946 { &vop_reallocblks_desc, (VOPFUNC)hfs_reallocblks }, /* reallocblks */
4947 { &vop_truncate_desc, (VOPFUNC)hfs_truncate }, /* truncate */
4948 { &vop_allocate_desc, (VOPFUNC)hfs_allocate }, /* allocate */
4949 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
4950 { &vop_searchfs_desc, (VOPFUNC)hfs_search }, /* search fs */
4951 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite }, /* bwrite */
4952 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* pagein */
4953 { &vop_pageout_desc,(VOPFUNC) hfs_pageout }, /* pageout */
4954 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
4955 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
4956 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
4957 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
4958 { NULL, (VOPFUNC)NULL }
4959 };
4960
4961 struct vnodeopv_desc hfs_vnodeop_opv_desc =
4962 { &hfs_vnodeop_p, hfs_vnodeop_entries };
4963
4964 int (**hfs_specop_p)(void *);
4965 struct vnodeopv_entry_desc hfs_specop_entries[] = {
4966 { &vop_default_desc, (VOPFUNC)vn_default_error },
4967 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
4968 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
4969 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
4970 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
4971 { &vop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
4972 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4973 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4974 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4975 { &vop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
4976 { &vop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
4977 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
4978 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
4979 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
4980 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
4981 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
4982 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4983 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
4984 { &vop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
4985 { &vop_link_desc, (VOPFUNC)spec_link }, /* link */
4986 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
4987 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
4988 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
4989 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
4990 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
4991 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
4992 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
4993 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4994 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4995 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4996 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4997 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
4998 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
4999 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
5000 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
5001 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
5002 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
5003 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
5004 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
5005 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
5006 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
5007 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
5008 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
5009 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
5010 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
5011 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
5012 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
5013 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
5014 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
5015 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
5016 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
5017 };
5018 struct vnodeopv_desc hfs_specop_opv_desc =
5019 { &hfs_specop_p, hfs_specop_entries };
5020
5021 #if FIFO
5022 int (**hfs_fifoop_p)(void *);
5023 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
5024 { &vop_default_desc, (VOPFUNC)vn_default_error },
5025 { &vop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
5026 { &vop_create_desc, (VOPFUNC)fifo_create }, /* create */
5027 { &vop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
5028 { &vop_open_desc, (VOPFUNC)fifo_open }, /* open */
5029 { &vop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
5030 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
5031 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
5032 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
5033 { &vop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
5034 { &vop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
5035 { &vop_lease_desc, (VOPFUNC)fifo_lease_check }, /* lease */
5036 { &vop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
5037 { &vop_select_desc, (VOPFUNC)fifo_select }, /* select */
5038 { &vop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
5039 { &vop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
5040 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
5041 { &vop_seek_desc, (VOPFUNC)fifo_seek }, /* seek */
5042 { &vop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
5043 { &vop_link_desc, (VOPFUNC)fifo_link }, /* link */
5044 { &vop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
5045 { &vop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
5046 { &vop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
5047 { &vop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
5048 { &vop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
5049 { &vop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
5050 { &vop_abortop_desc, (VOPFUNC)fifo_abortop }, /* abortop */
5051 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
5052 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
5053 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
5054 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
5055 { &vop_bmap_desc, (VOPFUNC)fifo_bmap }, /* bmap */
5056 { &vop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
5057 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
5058 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
5059 { &vop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
5060 { &vop_advlock_desc, (VOPFUNC)fifo_advlock }, /* advlock */
5061 { &vop_blkatoff_desc, (VOPFUNC)fifo_blkatoff }, /* blkatoff */
5062 { &vop_valloc_desc, (VOPFUNC)fifo_valloc }, /* valloc */
5063 { &vop_reallocblks_desc, (VOPFUNC)fifo_reallocblks }, /* reallocblks */
5064 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
5065 { &vop_truncate_desc, (VOPFUNC)fifo_truncate }, /* truncate */
5066 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
5067 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
5068 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
5069 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
5070 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
5071 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
5072 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
5073 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
5074 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
5075 };
5076 struct vnodeopv_desc hfs_fifoop_opv_desc =
5077 { &hfs_fifoop_p, hfs_fifoop_entries };
5078 #endif /* FIFO */
5079
5080
5081