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