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