]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vnops.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vnops.c
1 /*
2 * Copyright (c) 2000-2005 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
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/file_internal.h>
26 #include <sys/dirent.h>
27 #include <sys/stat.h>
28 #include <sys/buf.h>
29 #include <sys/mount.h>
30 #include <sys/vnode_internal.h>
31 #include <sys/malloc.h>
32 #include <sys/ubc.h>
33 #include <sys/paths.h>
34 #include <sys/quota.h>
35 #include <sys/time.h>
36 #include <sys/disk.h>
37 #include <sys/kauth.h>
38 #include <sys/uio_internal.h>
39
40 #include <miscfs/specfs/specdev.h>
41 #include <miscfs/fifofs/fifo.h>
42 #include <vfs/vfs_support.h>
43 #include <machine/spl.h>
44
45 #include <sys/kdebug.h>
46 #include <sys/sysctl.h>
47
48 #include "hfs.h"
49 #include "hfs_catalog.h"
50 #include "hfs_cnode.h"
51 #include "hfs_dbg.h"
52 #include "hfs_mount.h"
53 #include "hfs_quota.h"
54 #include "hfs_endian.h"
55
56 #include "hfscommon/headers/BTreesInternal.h"
57 #include "hfscommon/headers/FileMgrInternal.h"
58
59 #define MAKE_DELETED_NAME(NAME,FID) \
60 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
61
62 #define KNDETACH_VNLOCKED 0x00000001
63
64 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
65
66
67 /* Global vfs data structures for hfs */
68
69 /* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */
70 int always_do_fullfsync = 0;
71 SYSCTL_INT (_kern, OID_AUTO, always_do_fullfsync, CTLFLAG_RW, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
72
73 extern unsigned long strtoul(const char *, char **, int);
74
75 static int hfs_makenode(struct vnode *dvp, struct vnode **vpp,
76 struct componentname *cnp, struct vnode_attr *vap,
77 vfs_context_t ctx);
78
79 static int hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, struct proc *p);
80
81 static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
82 int);
83
84 static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
85 int, int);
86
87 static int hfs_vnop_close(struct vnop_close_args*);
88 static int hfs_vnop_create(struct vnop_create_args*);
89 static int hfs_vnop_exchange(struct vnop_exchange_args*);
90 static int hfs_vnop_fsync(struct vnop_fsync_args*);
91 static int hfs_vnop_mkdir(struct vnop_mkdir_args*);
92 static int hfs_vnop_mknod(struct vnop_mknod_args*);
93 static int hfs_vnop_getattr(struct vnop_getattr_args*);
94 static int hfs_vnop_open(struct vnop_open_args*);
95 static int hfs_vnop_readdir(struct vnop_readdir_args*);
96 static int hfs_vnop_remove(struct vnop_remove_args*);
97 static int hfs_vnop_rename(struct vnop_rename_args*);
98 static int hfs_vnop_rmdir(struct vnop_rmdir_args*);
99 static int hfs_vnop_symlink(struct vnop_symlink_args*);
100 static int hfs_vnop_setattr(struct vnop_setattr_args*);
101
102 /* Options for hfs_removedir and hfs_removefile */
103 #define HFSRM_SKIP_RESERVE 0x01
104
105
106 int hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags);
107
108 int hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred,
109 struct proc *p);
110 int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
111 kauth_cred_t cred, struct proc *p);
112
113 /*****************************************************************************
114 *
115 * Common Operations on vnodes
116 *
117 *****************************************************************************/
118
119 /*
120 * Create a regular file.
121 */
122 static int
123 hfs_vnop_create(struct vnop_create_args *ap)
124 {
125 return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
126 }
127
128 /*
129 * Make device special file.
130 */
131 static int
132 hfs_vnop_mknod(struct vnop_mknod_args *ap)
133 {
134 struct vnode_attr *vap = ap->a_vap;
135 struct vnode *dvp = ap->a_dvp;
136 struct vnode **vpp = ap->a_vpp;
137 struct cnode *cp;
138 int error;
139
140 if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) {
141 return (ENOTSUP);
142 }
143
144 /* Create the vnode */
145 error = hfs_makenode(dvp, vpp, ap->a_cnp, vap, ap->a_context);
146 if (error)
147 return (error);
148
149 cp = VTOC(*vpp);
150 cp->c_touch_acctime = TRUE;
151 cp->c_touch_chgtime = TRUE;
152 cp->c_touch_modtime = TRUE;
153
154 if ((vap->va_rdev != VNOVAL) &&
155 (vap->va_type == VBLK || vap->va_type == VCHR))
156 cp->c_rdev = vap->va_rdev;
157
158 return (0);
159 }
160
161 /*
162 * Open a file/directory.
163 */
164 static int
165 hfs_vnop_open(struct vnop_open_args *ap)
166 {
167 struct vnode *vp = ap->a_vp;
168 struct filefork *fp;
169 struct timeval tv;
170 int error;
171
172 /*
173 * Files marked append-only must be opened for appending.
174 */
175 if ((VTOC(vp)->c_flags & APPEND) && !vnode_isdir(vp) &&
176 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
177 return (EPERM);
178
179 if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
180 return (EBUSY); /* file is in use by the kernel */
181
182 /* Don't allow journal file to be opened externally. */
183 if (VTOC(vp)->c_fileid == VTOHFS(vp)->hfs_jnlfileid)
184 return (EPERM);
185 /*
186 * On the first (non-busy) open of a fragmented
187 * file attempt to de-frag it (if its less than 20MB).
188 */
189 if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
190 (VTOHFS(vp)->jnl == NULL) ||
191 !vnode_isreg(vp) || vnode_isinuse(vp, 0)) {
192 return (0);
193 }
194
195 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
196 return (error);
197 fp = VTOF(vp);
198 if (fp->ff_blocks &&
199 fp->ff_extents[7].blockCount != 0 &&
200 fp->ff_size <= (20 * 1024 * 1024)) {
201 /*
202 * Wait until system bootup is done (3 min).
203 */
204 microuptime(&tv);
205 if (tv.tv_sec > (60 * 3)) {
206 (void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096,
207 vfs_context_ucred(ap->a_context),
208 vfs_context_proc(ap->a_context));
209 }
210 }
211 hfs_unlock(VTOC(vp));
212
213 return (0);
214 }
215
216
217 /*
218 * Close a file/directory.
219 */
220 static int
221 hfs_vnop_close(ap)
222 struct vnop_close_args /* {
223 struct vnode *a_vp;
224 int a_fflag;
225 vfs_context_t a_context;
226 } */ *ap;
227 {
228 register struct vnode *vp = ap->a_vp;
229 register struct cnode *cp;
230 struct proc *p = vfs_context_proc(ap->a_context);
231 struct hfsmount *hfsmp;
232 int busy;
233
234 if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0)
235 return (0);
236 cp = VTOC(vp);
237 hfsmp = VTOHFS(vp);
238
239 // if we froze the fs and we're exiting, then "thaw" the fs
240 if (hfsmp->hfs_freezing_proc == p && proc_exiting(p)) {
241 hfsmp->hfs_freezing_proc = NULL;
242 hfs_global_exclusive_lock_release(hfsmp);
243 lck_rw_unlock_exclusive(&hfsmp->hfs_insync);
244 }
245
246 busy = vnode_isinuse(vp, 1);
247
248 if (busy) {
249 hfs_touchtimes(VTOHFS(vp), cp);
250 }
251 if (vnode_isdir(vp)) {
252 hfs_reldirhints(cp, busy);
253 } else if (vnode_issystem(vp) && !busy) {
254 vnode_recycle(vp);
255 }
256
257 hfs_unlock(cp);
258 return (0);
259 }
260
261 /*
262 * Get basic attributes.
263 */
264 static int
265 hfs_vnop_getattr(struct vnop_getattr_args *ap)
266 {
267 struct vnode *vp = ap->a_vp;
268 struct vnode_attr *vap = ap->a_vap;
269 struct vnode *rvp = NULL;
270 struct hfsmount *hfsmp;
271 struct cnode *cp;
272 enum vtype v_type;
273 int error = 0;
274
275 if ((error = hfs_lock(VTOC(vp), HFS_SHARED_LOCK))) {
276 return (error);
277 }
278 cp = VTOC(vp);
279 hfsmp = VTOHFS(vp);
280 hfs_touchtimes(hfsmp, cp);
281 v_type = vnode_vtype(vp);
282
283 VATTR_RETURN(vap, va_rdev, (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0);
284 if (v_type == VDIR) {
285 if (VATTR_IS_ACTIVE(vap, va_nlink)) {
286 int entries;
287
288 entries = cp->c_nlink;
289 if (vnode_isvroot(vp)) {
290 if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
291 --entries; /* hide private dir */
292 if (hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))
293 entries -= 2; /* hide the journal files */
294 }
295 VATTR_RETURN(vap, va_nlink, (uint64_t)entries);
296 }
297
298 if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
299 int entries;
300
301 entries = cp->c_entries;
302 if (vnode_isvroot(vp)) {
303 if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
304 --entries; /* hide private dir */
305 if (hfsmp->jnl)
306 entries -= 2; /* hide the journal files */
307 }
308 VATTR_RETURN(vap, va_nchildren, entries);
309 }
310 } else {
311 VATTR_RETURN(vap, va_nlink, (uint64_t)cp->c_nlink);
312 }
313
314 /* conditional because 64-bit arithmetic can be expensive */
315 if (VATTR_IS_ACTIVE(vap, va_total_size)) {
316 if (v_type == VDIR) {
317 VATTR_RETURN(vap, va_total_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
318 } else {
319 uint64_t total_size = 0;
320 struct cnode *rcp;
321
322 if (cp->c_datafork) {
323 total_size = cp->c_datafork->ff_size;
324 }
325
326 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
327 /* hfs_vgetrsrc does not use struct proc - therefore passing NULL */
328 error = hfs_vgetrsrc(hfsmp, vp, &rvp, NULL);
329 if (error) {
330 goto out;
331 }
332
333 rcp = VTOC(rvp);
334 if (rcp && rcp->c_rsrcfork) {
335 total_size += rcp->c_rsrcfork->ff_size;
336 }
337 }
338
339 VATTR_RETURN(vap, va_total_size, total_size);
340 /* Include size of attibute data (extents), if any */
341 if (cp->c_attrblks) {
342 vap->va_total_size += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
343 }
344 }
345 }
346 if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
347 if (v_type == VDIR) {
348 VATTR_RETURN(vap, va_total_alloc, 0);
349 } else {
350 VATTR_RETURN(vap, va_total_alloc, (uint64_t)cp->c_blocks * (uint64_t)hfsmp->blockSize);
351 /* Include size of attibute data (extents), if any */
352 if (cp->c_attrblks) {
353 vap->va_total_alloc += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
354 }
355 }
356 }
357 /* XXX broken... if ask for "data size" of rsrc fork vp you get rsrc fork size! */
358 if (v_type == VDIR) {
359 VATTR_RETURN(vap, va_data_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
360 } else {
361 VATTR_RETURN(vap, va_data_size, VTOF(vp)->ff_size);
362 }
363 if (VATTR_IS_ACTIVE(vap, va_data_alloc) && (v_type != VDIR)) {
364 /* XXX do we need to account for ff_unallocblocks ? */
365 VATTR_RETURN(vap, va_data_alloc, (uint64_t)VTOF(vp)->ff_blocks * (uint64_t)hfsmp->blockSize);
366 }
367 /* XXX is this really a good 'optimal I/O size'? */
368 VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize);
369 VATTR_RETURN(vap, va_uid, cp->c_uid);
370 VATTR_RETURN(vap, va_gid, cp->c_gid);
371 VATTR_RETURN(vap, va_mode, cp->c_mode);
372 #if 0
373 /* XXX is S_IFXATTR still needed ??? */
374 if (VNODE_IS_RSRC(vp))
375 vap->va_mode |= S_IFXATTR;
376 #endif
377 VATTR_RETURN(vap, va_flags, cp->c_flags);
378
379 /*
380 * If the VFS wants extended security data, and we know that we
381 * don't have any (because it never told us it was setting any)
382 * then we can return the supported bit and no data. If we do
383 * have extended security, we can just leave the bit alone and
384 * the VFS will use the fallback path to fetch it.
385 */
386 if (VATTR_IS_ACTIVE(vap, va_acl)) {
387 if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
388 vap->va_acl = KAUTH_FILESEC_NONE;
389 VATTR_SET_SUPPORTED(vap, va_acl);
390 }
391 }
392 vap->va_create_time.tv_sec = cp->c_itime;
393 vap->va_create_time.tv_nsec = 0;
394 VATTR_SET_SUPPORTED(vap, va_create_time);
395
396 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
397 /* Access times are lazyily updated, get current time if needed */
398 if (cp->c_touch_acctime) {
399 struct timeval tv;
400
401 microtime(&tv);
402 vap->va_access_time.tv_sec = tv.tv_sec;
403 } else {
404 vap->va_access_time.tv_sec = cp->c_atime;
405 }
406 vap->va_access_time.tv_nsec = 0;
407 VATTR_SET_SUPPORTED(vap, va_access_time);
408 }
409 vap->va_modify_time.tv_sec = cp->c_mtime;
410 vap->va_modify_time.tv_nsec = 0;
411 VATTR_SET_SUPPORTED(vap, va_modify_time);
412 vap->va_change_time.tv_sec = cp->c_ctime;
413 vap->va_change_time.tv_nsec = 0;
414 VATTR_SET_SUPPORTED(vap, va_change_time);
415 vap->va_backup_time.tv_sec = cp->c_btime;
416 vap->va_backup_time.tv_nsec = 0;
417 VATTR_SET_SUPPORTED(vap, va_backup_time);
418
419 /*
420 * Exporting file IDs from HFS Plus:
421 *
422 * For "normal" files the c_fileid is the same value as the
423 * c_cnid. But for hard link files, they are different - the
424 * c_cnid belongs to the active directory entry (ie the link)
425 * and the c_fileid is for the actual inode (ie the data file).
426 *
427 * The stat call (getattr) uses va_fileid and the Carbon APIs,
428 * which are hardlink-ignorant, will ask for va_linkid.
429 */
430 VATTR_RETURN(vap, va_fileid, (uint64_t)cp->c_fileid);
431 VATTR_RETURN(vap, va_linkid, (uint64_t)cp->c_cnid);
432 VATTR_RETURN(vap, va_parentid, (uint64_t)cp->c_parentcnid);
433 VATTR_RETURN(vap, va_fsid, cp->c_dev);
434 VATTR_RETURN(vap, va_filerev, 0);
435
436 VATTR_RETURN(vap, va_encoding, cp->c_encoding);
437
438 /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
439 if (VATTR_IS_ACTIVE(vap, va_name) && !vnode_isvroot(vp)) {
440 /* Return the name for ATTR_CMN_NAME */
441 if (cp->c_desc.cd_namelen == 0) {
442 error = ENOENT;
443 goto out;
444 }
445
446 strncpy(vap->va_name, cp->c_desc.cd_nameptr, MAXPATHLEN);
447 vap->va_name[MAXPATHLEN-1] = '\0';
448 VATTR_SET_SUPPORTED(vap, va_name);
449 }
450
451 out:
452 hfs_unlock(cp);
453 if (rvp) {
454 vnode_put(rvp);
455 }
456 return (error);
457 }
458
459 static int
460 hfs_vnop_setattr(ap)
461 struct vnop_setattr_args /* {
462 struct vnode *a_vp;
463 struct vnode_attr *a_vap;
464 vfs_context_t a_context;
465 } */ *ap;
466 {
467 struct vnode_attr *vap = ap->a_vap;
468 struct vnode *vp = ap->a_vp;
469 struct cnode *cp = NULL;
470 struct hfsmount *hfsmp;
471 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
472 struct proc *p = vfs_context_proc(ap->a_context);
473 int error = 0;
474 uid_t nuid;
475 gid_t ngid;
476
477 hfsmp = VTOHFS(vp);
478
479 /* Don't allow modification of the journal file. */
480 if (hfsmp->hfs_jnlfileid == VTOC(vp)->c_fileid) {
481 return (EPERM);
482 }
483
484 /*
485 * File size change request.
486 * We are guaranteed that this is not a directory, and that
487 * the filesystem object is writeable.
488 */
489 VATTR_SET_SUPPORTED(vap, va_data_size);
490 if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) {
491
492 /* Take truncate lock before taking cnode lock. */
493 hfs_lock_truncate(VTOC(vp), TRUE);
494 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
495 hfs_unlock_truncate(VTOC(vp));
496 return (error);
497 }
498 cp = VTOC(vp);
499
500 error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 0, ap->a_context);
501
502 hfs_unlock_truncate(cp);
503 if (error)
504 goto out;
505 }
506 if (cp == NULL) {
507 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
508 return (error);
509 cp = VTOC(vp);
510 }
511
512 /*
513 * Owner/group change request.
514 * We are guaranteed that the new owner/group is valid and legal.
515 */
516 VATTR_SET_SUPPORTED(vap, va_uid);
517 VATTR_SET_SUPPORTED(vap, va_gid);
518 nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
519 ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
520 if (((nuid != (uid_t)VNOVAL) || (ngid != (gid_t)VNOVAL)) &&
521 ((error = hfs_chown(vp, nuid, ngid, cred, p)) != 0))
522 goto out;
523
524 /*
525 * Mode change request.
526 * We are guaranteed that the mode value is valid and that in
527 * conjunction with the owner and group, this change is legal.
528 */
529 VATTR_SET_SUPPORTED(vap, va_mode);
530 if (VATTR_IS_ACTIVE(vap, va_mode) &&
531 ((error = hfs_chmod(vp, (int)vap->va_mode, cred, p)) != 0))
532 goto out;
533
534 /*
535 * File flags change.
536 * We are guaranteed that only flags allowed to change given the
537 * current securelevel are being changed.
538 */
539 VATTR_SET_SUPPORTED(vap, va_flags);
540 if (VATTR_IS_ACTIVE(vap, va_flags)) {
541 cp->c_flags = vap->va_flags;
542 cp->c_touch_chgtime = TRUE;
543 }
544
545 /*
546 * If the file's extended security data is being changed, we
547 * need to note the change. Note that because we don't store
548 * the data, we do not set the SUPPORTED bit; this will cause
549 * the VFS to use a fallback strategy.
550 */
551 if (VATTR_IS_ACTIVE(vap, va_acl)) {
552 /* Remember if any ACL data was set or cleared. */
553 if (vap->va_acl == NULL) {
554 /* being cleared */
555 if (cp->c_attr.ca_recflags & kHFSHasSecurityMask) {
556 cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
557 cp->c_touch_chgtime = TRUE;
558 }
559 } else {
560 /* being set */
561 if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
562 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
563 cp->c_touch_chgtime = TRUE;
564 }
565 }
566 }
567
568 /*
569 * Timestamp updates.
570 */
571 VATTR_SET_SUPPORTED(vap, va_create_time);
572 VATTR_SET_SUPPORTED(vap, va_access_time);
573 VATTR_SET_SUPPORTED(vap, va_modify_time);
574 VATTR_SET_SUPPORTED(vap, va_backup_time);
575 VATTR_SET_SUPPORTED(vap, va_change_time);
576 if (VATTR_IS_ACTIVE(vap, va_create_time) ||
577 VATTR_IS_ACTIVE(vap, va_access_time) ||
578 VATTR_IS_ACTIVE(vap, va_modify_time) ||
579 VATTR_IS_ACTIVE(vap, va_backup_time)) {
580 if (vnode_islnk(vp))
581 goto done;
582 if (VATTR_IS_ACTIVE(vap, va_create_time))
583 cp->c_itime = vap->va_create_time.tv_sec;
584 if (VATTR_IS_ACTIVE(vap, va_access_time)) {
585 cp->c_atime = vap->va_access_time.tv_sec;
586 cp->c_touch_acctime = FALSE;
587 }
588 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
589 cp->c_mtime = vap->va_modify_time.tv_sec;
590 cp->c_touch_modtime = FALSE;
591 cp->c_touch_chgtime = TRUE;
592
593 /*
594 * The utimes system call can reset the modification
595 * time but it doesn't know about HFS create times.
596 * So we need to ensure that the creation time is
597 * always at least as old as the modification time.
598 */
599 if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
600 (cp->c_cnid != kHFSRootFolderID) &&
601 (cp->c_mtime < cp->c_itime)) {
602 cp->c_itime = cp->c_mtime;
603 }
604 }
605 if (VATTR_IS_ACTIVE(vap, va_backup_time))
606 cp->c_btime = vap->va_backup_time.tv_sec;
607 cp->c_flag |= C_MODIFIED;
608 }
609
610 /*
611 * Set name encoding.
612 */
613 VATTR_SET_SUPPORTED(vap, va_encoding);
614 if (VATTR_IS_ACTIVE(vap, va_encoding)) {
615 cp->c_encoding = vap->va_encoding;
616 hfs_setencodingbits(hfsmp, cp->c_encoding);
617 }
618
619 done:
620 if ((error = hfs_update(vp, TRUE)) != 0)
621 goto out;
622 HFS_KNOTE(vp, NOTE_ATTRIB);
623 out:
624 if (cp)
625 hfs_unlock(cp);
626 return (error);
627 }
628
629
630 /*
631 * Change the mode on a file.
632 * cnode must be locked before calling.
633 */
634 __private_extern__
635 int
636 hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p)
637 {
638 register struct cnode *cp = VTOC(vp);
639 int error;
640
641 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
642 return (0);
643
644 // XXXdbg - don't allow modification of the journal or journal_info_block
645 if (VTOHFS(vp)->jnl && cp && cp->c_datafork) {
646 struct HFSPlusExtentDescriptor *extd;
647
648 extd = &cp->c_datafork->ff_extents[0];
649 if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
650 return EPERM;
651 }
652 }
653
654 #if OVERRIDE_UNKNOWN_PERMISSIONS
655 if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) {
656 return (0);
657 };
658 #endif
659 cp->c_mode &= ~ALLPERMS;
660 cp->c_mode |= (mode & ALLPERMS);
661 cp->c_touch_chgtime = TRUE;
662 return (0);
663 }
664
665
666 __private_extern__
667 int
668 hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags)
669 {
670 struct cnode *cp = VTOC(vp);
671 int retval = 0;
672 int is_member;
673
674 /*
675 * Disallow write attempts on read-only file systems;
676 * unless the file is a socket, fifo, or a block or
677 * character device resident on the file system.
678 */
679 switch (vnode_vtype(vp)) {
680 case VDIR:
681 case VLNK:
682 case VREG:
683 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
684 return (EROFS);
685 break;
686 default:
687 break;
688 }
689
690 /* If immutable bit set, nobody gets to write it. */
691 if (considerFlags && (cp->c_flags & IMMUTABLE))
692 return (EPERM);
693
694 /* Otherwise, user id 0 always gets access. */
695 if (!suser(cred, NULL))
696 return (0);
697
698 /* Otherwise, check the owner. */
699 if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
700 return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
701
702 /* Otherwise, check the groups. */
703 if (kauth_cred_ismember_gid(cred, cp->c_gid, &is_member) == 0 && is_member) {
704 return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
705 }
706
707 /* Otherwise, check everyone else. */
708 return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
709 }
710
711
712 /*
713 * Perform chown operation on cnode cp;
714 * code must be locked prior to call.
715 */
716 __private_extern__
717 int
718 hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
719 struct proc *p)
720 {
721 register struct cnode *cp = VTOC(vp);
722 uid_t ouid;
723 gid_t ogid;
724 int error = 0;
725 int is_member;
726 #if QUOTA
727 register int i;
728 int64_t change;
729 #endif /* QUOTA */
730
731 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
732 return (ENOTSUP);
733
734 if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS)
735 return (0);
736
737 if (uid == (uid_t)VNOVAL)
738 uid = cp->c_uid;
739 if (gid == (gid_t)VNOVAL)
740 gid = cp->c_gid;
741
742 #if 0 /* we are guaranteed that this is already the case */
743 /*
744 * If we don't own the file, are trying to change the owner
745 * of the file, or are not a member of the target group,
746 * the caller must be superuser or the call fails.
747 */
748 if ((kauth_cred_getuid(cred) != cp->c_uid || uid != cp->c_uid ||
749 (gid != cp->c_gid &&
750 (kauth_cred_ismember_gid(cred, gid, &is_member) || !is_member))) &&
751 (error = suser(cred, 0)))
752 return (error);
753 #endif
754
755 ogid = cp->c_gid;
756 ouid = cp->c_uid;
757 #if QUOTA
758 if ((error = hfs_getinoquota(cp)))
759 return (error);
760 if (ouid == uid) {
761 dqrele(cp->c_dquot[USRQUOTA]);
762 cp->c_dquot[USRQUOTA] = NODQUOT;
763 }
764 if (ogid == gid) {
765 dqrele(cp->c_dquot[GRPQUOTA]);
766 cp->c_dquot[GRPQUOTA] = NODQUOT;
767 }
768
769 /*
770 * Eventually need to account for (fake) a block per directory
771 * if (vnode_isdir(vp))
772 * change = VTOHFS(vp)->blockSize;
773 * else
774 */
775
776 change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
777 (void) hfs_chkdq(cp, -change, cred, CHOWN);
778 (void) hfs_chkiq(cp, -1, cred, CHOWN);
779 for (i = 0; i < MAXQUOTAS; i++) {
780 dqrele(cp->c_dquot[i]);
781 cp->c_dquot[i] = NODQUOT;
782 }
783 #endif /* QUOTA */
784 cp->c_gid = gid;
785 cp->c_uid = uid;
786 #if QUOTA
787 if ((error = hfs_getinoquota(cp)) == 0) {
788 if (ouid == uid) {
789 dqrele(cp->c_dquot[USRQUOTA]);
790 cp->c_dquot[USRQUOTA] = NODQUOT;
791 }
792 if (ogid == gid) {
793 dqrele(cp->c_dquot[GRPQUOTA]);
794 cp->c_dquot[GRPQUOTA] = NODQUOT;
795 }
796 if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
797 if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
798 goto good;
799 else
800 (void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
801 }
802 for (i = 0; i < MAXQUOTAS; i++) {
803 dqrele(cp->c_dquot[i]);
804 cp->c_dquot[i] = NODQUOT;
805 }
806 }
807 cp->c_gid = ogid;
808 cp->c_uid = ouid;
809 if (hfs_getinoquota(cp) == 0) {
810 if (ouid == uid) {
811 dqrele(cp->c_dquot[USRQUOTA]);
812 cp->c_dquot[USRQUOTA] = NODQUOT;
813 }
814 if (ogid == gid) {
815 dqrele(cp->c_dquot[GRPQUOTA]);
816 cp->c_dquot[GRPQUOTA] = NODQUOT;
817 }
818 (void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
819 (void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
820 (void) hfs_getinoquota(cp);
821 }
822 return (error);
823 good:
824 if (hfs_getinoquota(cp))
825 panic("hfs_chown: lost quota");
826 #endif /* QUOTA */
827
828 if (ouid != uid || ogid != gid)
829 cp->c_touch_chgtime = TRUE;
830 return (0);
831 }
832
833
834 /*
835 * The hfs_exchange routine swaps the fork data in two files by
836 * exchanging some of the information in the cnode. It is used
837 * to preserve the file ID when updating an existing file, in
838 * case the file is being tracked through its file ID. Typically
839 * its used after creating a new file during a safe-save.
840 */
841 static int
842 hfs_vnop_exchange(ap)
843 struct vnop_exchange_args /* {
844 struct vnode *a_fvp;
845 struct vnode *a_tvp;
846 int a_options;
847 vfs_context_t a_context;
848 } */ *ap;
849 {
850 struct vnode *from_vp = ap->a_fvp;
851 struct vnode *to_vp = ap->a_tvp;
852 struct cnode *from_cp;
853 struct cnode *to_cp;
854 struct hfsmount *hfsmp;
855 struct cat_desc tempdesc;
856 struct cat_attr tempattr;
857 int lockflags;
858 int error = 0, started_tr = 0, got_cookie = 0;
859 cat_cookie_t cookie;
860
861 /* The files must be on the same volume. */
862 if (vnode_mount(from_vp) != vnode_mount(to_vp))
863 return (EXDEV);
864
865 if (from_vp == to_vp)
866 return (EINVAL);
867
868 if ((error = hfs_lockpair(VTOC(from_vp), VTOC(to_vp), HFS_EXCLUSIVE_LOCK)))
869 return (error);
870
871 from_cp = VTOC(from_vp);
872 to_cp = VTOC(to_vp);
873 hfsmp = VTOHFS(from_vp);
874
875 /* Only normal files can be exchanged. */
876 if (!vnode_isreg(from_vp) || !vnode_isreg(to_vp) ||
877 (from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
878 VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp)) {
879 error = EINVAL;
880 goto exit;
881 }
882
883 // XXXdbg - don't allow modification of the journal or journal_info_block
884 if (hfsmp->jnl) {
885 struct HFSPlusExtentDescriptor *extd;
886
887 if (from_cp->c_datafork) {
888 extd = &from_cp->c_datafork->ff_extents[0];
889 if (extd->startBlock == VTOVCB(from_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
890 error = EPERM;
891 goto exit;
892 }
893 }
894
895 if (to_cp->c_datafork) {
896 extd = &to_cp->c_datafork->ff_extents[0];
897 if (extd->startBlock == VTOVCB(to_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
898 error = EPERM;
899 goto exit;
900 }
901 }
902 }
903
904 if ((error = hfs_start_transaction(hfsmp)) != 0) {
905 goto exit;
906 }
907 started_tr = 1;
908
909 /*
910 * Reserve some space in the Catalog file.
911 */
912 bzero(&cookie, sizeof(cookie));
913 if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, vfs_context_proc(ap->a_context)))) {
914 goto exit;
915 }
916 got_cookie = 1;
917
918 /* The backend code always tries to delete the virtual
919 * extent id for exchanging files so we neeed to lock
920 * the extents b-tree.
921 */
922 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
923
924 /* Do the exchange */
925 error = ExchangeFileIDs(hfsmp,
926 from_cp->c_desc.cd_nameptr,
927 to_cp->c_desc.cd_nameptr,
928 from_cp->c_parentcnid,
929 to_cp->c_parentcnid,
930 from_cp->c_hint,
931 to_cp->c_hint);
932 hfs_systemfile_unlock(hfsmp, lockflags);
933
934 /*
935 * Note that we don't need to exchange any extended attributes
936 * since the attributes are keyed by file ID.
937 */
938
939 if (error != E_NONE) {
940 error = MacToVFSError(error);
941 goto exit;
942 }
943
944 /* Purge the vnodes from the name cache */
945 if (from_vp)
946 cache_purge(from_vp);
947 if (to_vp)
948 cache_purge(to_vp);
949
950 /* Save a copy of from attributes before swapping. */
951 bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
952 bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
953
954 /*
955 * Swap the descriptors and all non-fork related attributes.
956 * (except the modify date)
957 */
958 bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
959
960 from_cp->c_hint = 0;
961 from_cp->c_fileid = from_cp->c_cnid;
962 from_cp->c_itime = to_cp->c_itime;
963 from_cp->c_btime = to_cp->c_btime;
964 from_cp->c_atime = to_cp->c_atime;
965 from_cp->c_ctime = to_cp->c_ctime;
966 from_cp->c_gid = to_cp->c_gid;
967 from_cp->c_uid = to_cp->c_uid;
968 from_cp->c_flags = to_cp->c_flags;
969 from_cp->c_mode = to_cp->c_mode;
970 from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags;
971 bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
972
973 bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
974 to_cp->c_hint = 0;
975 to_cp->c_fileid = to_cp->c_cnid;
976 to_cp->c_itime = tempattr.ca_itime;
977 to_cp->c_btime = tempattr.ca_btime;
978 to_cp->c_atime = tempattr.ca_atime;
979 to_cp->c_ctime = tempattr.ca_ctime;
980 to_cp->c_gid = tempattr.ca_gid;
981 to_cp->c_uid = tempattr.ca_uid;
982 to_cp->c_flags = tempattr.ca_flags;
983 to_cp->c_mode = tempattr.ca_mode;
984 to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
985 bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
986
987 /* Rehash the cnodes using their new file IDs */
988 hfs_chash_rehash(from_cp, to_cp);
989
990 /*
991 * When a file moves out of "Cleanup At Startup"
992 * we can drop its NODUMP status.
993 */
994 if ((from_cp->c_flags & UF_NODUMP) &&
995 (from_cp->c_parentcnid != to_cp->c_parentcnid)) {
996 from_cp->c_flags &= ~UF_NODUMP;
997 from_cp->c_touch_chgtime = TRUE;
998 }
999 if ((to_cp->c_flags & UF_NODUMP) &&
1000 (to_cp->c_parentcnid != from_cp->c_parentcnid)) {
1001 to_cp->c_flags &= ~UF_NODUMP;
1002 to_cp->c_touch_chgtime = TRUE;
1003 }
1004
1005 HFS_KNOTE(from_vp, NOTE_ATTRIB);
1006 HFS_KNOTE(to_vp, NOTE_ATTRIB);
1007
1008 exit:
1009 if (got_cookie) {
1010 cat_postflight(hfsmp, &cookie, vfs_context_proc(ap->a_context));
1011 }
1012 if (started_tr) {
1013 hfs_end_transaction(hfsmp);
1014 }
1015
1016 hfs_unlockpair(from_cp, to_cp);
1017 return (error);
1018 }
1019
1020
1021 /*
1022 * cnode must be locked
1023 */
1024 __private_extern__
1025 int
1026 hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p)
1027 {
1028 struct cnode *cp = VTOC(vp);
1029 struct filefork *fp = NULL;
1030 int retval = 0;
1031 struct hfsmount *hfsmp = VTOHFS(vp);
1032 struct timeval tv;
1033 int wait;
1034 int lockflag;
1035 int took_trunc_lock = 0;
1036
1037 wait = (waitfor == MNT_WAIT);
1038
1039 /* HFS directories don't have any data blocks. */
1040 if (vnode_isdir(vp))
1041 goto metasync;
1042
1043 /*
1044 * For system files flush the B-tree header and
1045 * for regular files write out any clusters
1046 */
1047 if (vnode_issystem(vp)) {
1048 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1049 // XXXdbg
1050 if (hfsmp->jnl == NULL) {
1051 BTFlushPath(VTOF(vp));
1052 }
1053 }
1054 } else if (UBCINFOEXISTS(vp)) {
1055 hfs_unlock(cp);
1056 hfs_lock_truncate(cp, TRUE);
1057 took_trunc_lock = 1;
1058
1059 /* Don't hold cnode lock when calling into cluster layer. */
1060 (void) cluster_push(vp, 0);
1061
1062 hfs_lock(cp, HFS_FORCE_LOCK);
1063 }
1064 /*
1065 * When MNT_WAIT is requested and the zero fill timeout
1066 * has expired then we must explicitly zero out any areas
1067 * that are currently marked invalid (holes).
1068 *
1069 * Files with NODUMP can bypass zero filling here.
1070 */
1071 if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
1072 ((cp->c_flags & UF_NODUMP) == 0) &&
1073 UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
1074 cp->c_zftimeout != 0) {
1075 microuptime(&tv);
1076 if (tv.tv_sec < cp->c_zftimeout) {
1077 /* Remember that a force sync was requested. */
1078 cp->c_flag |= C_ZFWANTSYNC;
1079 goto datasync;
1080 }
1081 if (!took_trunc_lock) {
1082 hfs_unlock(cp);
1083 hfs_lock_truncate(cp, TRUE);
1084 hfs_lock(cp, HFS_FORCE_LOCK);
1085 took_trunc_lock = 1;
1086 }
1087
1088 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
1089 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
1090 off_t start = invalid_range->rl_start;
1091 off_t end = invalid_range->rl_end;
1092
1093 /* The range about to be written must be validated
1094 * first, so that VNOP_BLOCKMAP() will return the
1095 * appropriate mapping for the cluster code:
1096 */
1097 rl_remove(start, end, &fp->ff_invalidranges);
1098
1099 /* Don't hold cnode lock when calling into cluster layer. */
1100 hfs_unlock(cp);
1101 (void) cluster_write(vp, (struct uio *) 0,
1102 fp->ff_size, end + 1, start, (off_t)0,
1103 IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
1104 hfs_lock(cp, HFS_FORCE_LOCK);
1105 cp->c_flag |= C_MODIFIED;
1106 }
1107 hfs_unlock(cp);
1108 (void) cluster_push(vp, 0);
1109 hfs_lock(cp, HFS_FORCE_LOCK);
1110
1111 cp->c_flag &= ~C_ZFWANTSYNC;
1112 cp->c_zftimeout = 0;
1113 }
1114 datasync:
1115 if (took_trunc_lock)
1116 hfs_unlock_truncate(cp);
1117
1118 /*
1119 * if we have a journal and if journal_active() returns != 0 then the
1120 * we shouldn't do anything to a locked block (because it is part
1121 * of a transaction). otherwise we'll just go through the normal
1122 * code path and flush the buffer. note journal_active() can return
1123 * -1 if the journal is invalid -- however we still need to skip any
1124 * locked blocks as they get cleaned up when we finish the transaction
1125 * or close the journal.
1126 */
1127 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1128 if (hfsmp->jnl)
1129 lockflag = BUF_SKIP_LOCKED;
1130 else
1131 lockflag = 0;
1132
1133 /*
1134 * Flush all dirty buffers associated with a vnode.
1135 */
1136 buf_flushdirtyblks(vp, wait, lockflag, "hfs_fsync");
1137
1138 metasync:
1139 if (vnode_isreg(vp) && vnode_issystem(vp)) {
1140 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1141 microuptime(&tv);
1142 BTSetLastSync(VTOF(vp), tv.tv_sec);
1143 }
1144 cp->c_touch_acctime = FALSE;
1145 cp->c_touch_chgtime = FALSE;
1146 cp->c_touch_modtime = FALSE;
1147 } else if ( !(vp->v_flag & VSWAP) ) /* User file */ {
1148 retval = hfs_update(vp, wait);
1149
1150 /* When MNT_WAIT is requested push out any delayed meta data */
1151 if ((retval == 0) && wait && cp->c_hint &&
1152 !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
1153 hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
1154 }
1155
1156 // make sure that we've really been called from the user
1157 // fsync() and if so push out any pending transactions
1158 // that this file might is a part of (and get them on
1159 // stable storage).
1160 if (fullsync || always_do_fullfsync) {
1161 if (hfsmp->jnl) {
1162 journal_flush(hfsmp->jnl);
1163 } else {
1164 /* XXX need to pass context! */
1165 VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
1166 }
1167 }
1168 }
1169
1170 return (retval);
1171 }
1172
1173
1174 /* Sync an hfs catalog b-tree node */
1175 static int
1176 hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, struct proc *p)
1177 {
1178 vnode_t vp;
1179 buf_t bp;
1180 int lockflags;
1181
1182 vp = HFSTOVCB(hfsmp)->catalogRefNum;
1183
1184 // XXXdbg - don't need to do this on a journaled volume
1185 if (hfsmp->jnl) {
1186 return 0;
1187 }
1188
1189 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1190 /*
1191 * Look for a matching node that has been delayed
1192 * but is not part of a set (B_LOCKED).
1193 *
1194 * BLK_ONLYVALID causes buf_getblk to return a
1195 * buf_t for the daddr64_t specified only if it's
1196 * currently resident in the cache... the size
1197 * parameter to buf_getblk is ignored when this flag
1198 * is set
1199 */
1200 bp = buf_getblk(vp, node, 0, 0, 0, BLK_META | BLK_ONLYVALID);
1201
1202 if (bp) {
1203 if ((buf_flags(bp) & (B_LOCKED | B_DELWRI)) == B_DELWRI)
1204 (void) VNOP_BWRITE(bp);
1205 else
1206 buf_brelse(bp);
1207 }
1208
1209 hfs_systemfile_unlock(hfsmp, lockflags);
1210
1211 return (0);
1212 }
1213
1214
1215 /*ARGSUSED 1*/
1216 static int
1217 hfs_btsync_callback(struct buf *bp, void *dummy)
1218 {
1219 buf_clearflags(bp, B_LOCKED);
1220 (void) buf_bawrite(bp);
1221
1222 return(BUF_CLAIMED);
1223 }
1224
1225
1226 __private_extern__
1227 int
1228 hfs_btsync(struct vnode *vp, int sync_transaction)
1229 {
1230 struct cnode *cp = VTOC(vp);
1231 struct timeval tv;
1232 int flags = 0;
1233
1234 if (sync_transaction)
1235 flags |= BUF_SKIP_NONLOCKED;
1236 /*
1237 * Flush all dirty buffers associated with b-tree.
1238 */
1239 buf_iterate(vp, hfs_btsync_callback, flags, 0);
1240
1241 microuptime(&tv);
1242 if (vnode_issystem(vp) && (VTOF(vp)->fcbBTCBPtr != NULL))
1243 (void) BTSetLastSync(VTOF(vp), tv.tv_sec);
1244 cp->c_touch_acctime = FALSE;
1245 cp->c_touch_chgtime = FALSE;
1246 cp->c_touch_modtime = FALSE;
1247
1248 return 0;
1249 }
1250
1251 /*
1252 * Remove a directory.
1253 */
1254 static int
1255 hfs_vnop_rmdir(ap)
1256 struct vnop_rmdir_args /* {
1257 struct vnode *a_dvp;
1258 struct vnode *a_vp;
1259 struct componentname *a_cnp;
1260 vfs_context_t a_context;
1261 } */ *ap;
1262 {
1263 struct vnode *dvp = ap->a_dvp;
1264 struct vnode *vp = ap->a_vp;
1265 int error;
1266
1267 if (!vnode_isdir(vp)) {
1268 return (ENOTDIR);
1269 }
1270 if (dvp == vp) {
1271 return (EINVAL);
1272 }
1273 if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
1274 return (error);
1275
1276 error = hfs_removedir(dvp, vp, ap->a_cnp, 0);
1277
1278 hfs_unlockpair(VTOC(dvp), VTOC(vp));
1279
1280 return (error);
1281 }
1282
1283 /*
1284 * Remove a directory
1285 *
1286 * Both dvp and vp cnodes are locked
1287 */
1288 static int
1289 hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
1290 int skip_reserve)
1291 {
1292 vfs_context_t ctx = cnp->cn_context;
1293 struct proc *p = vfs_context_proc(ctx);
1294 struct cnode *cp;
1295 struct cnode *dcp;
1296 struct hfsmount * hfsmp;
1297 struct cat_desc desc;
1298 cat_cookie_t cookie;
1299 int lockflags;
1300 int error = 0, started_tr = 0, got_cookie = 0;
1301
1302 cp = VTOC(vp);
1303 dcp = VTOC(dvp);
1304 hfsmp = VTOHFS(vp);
1305
1306 if (dcp == cp)
1307 return (EINVAL); /* cannot remove "." */
1308
1309 #if QUOTA
1310 (void)hfs_getinoquota(cp);
1311 #endif
1312 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1313 goto out;
1314 }
1315 started_tr = 1;
1316
1317 if (!skip_reserve) {
1318 /*
1319 * Reserve some space in the Catalog file.
1320 */
1321 bzero(&cookie, sizeof(cookie));
1322 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
1323 goto out;
1324 }
1325 got_cookie = 1;
1326 }
1327
1328 /*
1329 * Verify the directory is empty (and valid).
1330 * (Rmdir ".." won't be valid since
1331 * ".." will contain a reference to
1332 * the current directory and thus be
1333 * non-empty.)
1334 */
1335 if (cp->c_entries != 0) {
1336 error = ENOTEMPTY;
1337 goto out;
1338 }
1339 if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
1340 error = EPERM;
1341 goto out;
1342 }
1343
1344 if (cp->c_entries > 0)
1345 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1346
1347 /* Remove the entry from the namei cache: */
1348 cache_purge(vp);
1349
1350 /*
1351 * Protect against a race with rename by using the component
1352 * name passed in and parent id from dvp (instead of using
1353 * the cp->c_desc which may have changed).
1354 */
1355 bzero(&desc, sizeof(desc));
1356 desc.cd_nameptr = cnp->cn_nameptr;
1357 desc.cd_namelen = cnp->cn_namelen;
1358 desc.cd_parentcnid = dcp->c_cnid;
1359 desc.cd_cnid = cp->c_cnid;
1360
1361 /* Remove entry from catalog */
1362 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1363 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1364 if (error == 0) {
1365 /* Delete any attributes, ignore errors */
1366 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
1367 }
1368 hfs_systemfile_unlock(hfsmp, lockflags);
1369
1370 if (error)
1371 goto out;
1372
1373 #if QUOTA
1374 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1375 #endif /* QUOTA */
1376
1377 /* The parent lost a child */
1378 if (dcp->c_entries > 0)
1379 dcp->c_entries--;
1380 if (dcp->c_nlink > 0)
1381 dcp->c_nlink--;
1382 dcp->c_touch_chgtime = TRUE;
1383 dcp->c_touch_modtime = TRUE;
1384
1385 dcp->c_flag |= C_FORCEUPDATE; // XXXdbg - don't screw around, force this guy out
1386
1387 (void) hfs_update(dvp, 0);
1388 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1389
1390 hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
1391
1392 cp->c_mode = 0; /* Makes the vnode go away...see inactive */
1393 cp->c_flag |= C_NOEXISTS;
1394 out:
1395 HFS_KNOTE(vp, NOTE_DELETE);
1396
1397 if (got_cookie) {
1398 cat_postflight(hfsmp, &cookie, p);
1399 }
1400 if (started_tr) {
1401 hfs_end_transaction(hfsmp);
1402 }
1403
1404 return (error);
1405 }
1406
1407
1408 /*
1409 * Remove a file or link.
1410 */
1411 static int
1412 hfs_vnop_remove(ap)
1413 struct vnop_remove_args /* {
1414 struct vnode *a_dvp;
1415 struct vnode *a_vp;
1416 struct componentname *a_cnp;
1417 int a_flags;
1418 vfs_context_t a_context;
1419 } */ *ap;
1420 {
1421 struct vnode *dvp = ap->a_dvp;
1422 struct vnode *vp = ap->a_vp;
1423 int error;
1424
1425 if (dvp == vp) {
1426 return (EINVAL);
1427 }
1428
1429 hfs_lock_truncate(VTOC(vp), TRUE);
1430
1431 if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
1432 goto out;
1433
1434 error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0);
1435
1436 hfs_unlockpair(VTOC(dvp), VTOC(vp));
1437 out:
1438 hfs_unlock_truncate(VTOC(vp));
1439 return (error);
1440 }
1441
1442
1443 static int
1444 hfs_removefile_callback(struct buf *bp, void *hfsmp) {
1445
1446 if ( !(buf_flags(bp) & B_META))
1447 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp);
1448 /*
1449 * it's part of the current transaction, kill it.
1450 */
1451 journal_kill_block(((struct hfsmount *)hfsmp)->jnl, bp);
1452
1453 return (BUF_CLAIMED);
1454 }
1455
1456 /*
1457 * hfs_removefile
1458 *
1459 * Similar to hfs_vnop_remove except there are additional options.
1460 *
1461 * Requires cnode and truncate locks to be held.
1462 */
1463 static int
1464 hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
1465 int flags, int skip_reserve)
1466 {
1467 struct vnode *rvp = NULL;
1468 struct cnode *cp;
1469 struct cnode *dcp;
1470 struct hfsmount *hfsmp;
1471 struct cat_desc desc;
1472 struct timeval tv;
1473 vfs_context_t ctx = cnp->cn_context;
1474 int dataforkbusy = 0;
1475 int rsrcforkbusy = 0;
1476 int truncated = 0;
1477 cat_cookie_t cookie;
1478 int lockflags;
1479 int error = 0;
1480 int started_tr = 0, got_cookie = 0;
1481 int isbigfile = 0;
1482 cnid_t real_cnid = 0;
1483
1484 /* Directories should call hfs_rmdir! */
1485 if (vnode_isdir(vp)) {
1486 return (EISDIR);
1487 }
1488
1489 cp = VTOC(vp);
1490 dcp = VTOC(dvp);
1491 hfsmp = VTOHFS(vp);
1492
1493 if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
1494 return 0;
1495 }
1496
1497 // if it's not a hardlink, check that the parent
1498 // cnid is the same as the directory cnid
1499 if ( (cp->c_flag & C_HARDLINK) == 0
1500 && (cp->c_parentcnid != hfsmp->hfs_privdir_desc.cd_cnid)
1501 && (cp->c_parentcnid != dcp->c_cnid)) {
1502 error = EINVAL;
1503 goto out;
1504 }
1505
1506 /* Make sure a remove is permitted */
1507 if (VNODE_IS_RSRC(vp)) {
1508 error = EPERM;
1509 goto out;
1510 }
1511
1512 /*
1513 * Aquire a vnode for a non-empty resource fork.
1514 * (needed for hfs_truncate)
1515 */
1516 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
1517 error = hfs_vgetrsrc(hfsmp, vp, &rvp, 0);
1518 if (error)
1519 goto out;
1520 }
1521
1522 // XXXdbg - don't allow deleting the journal or journal_info_block
1523 if (hfsmp->jnl && cp->c_datafork) {
1524 struct HFSPlusExtentDescriptor *extd;
1525
1526 extd = &cp->c_datafork->ff_extents[0];
1527 if (extd->startBlock == HFSTOVCB(hfsmp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
1528 error = EPERM;
1529 goto out;
1530 }
1531 }
1532
1533 /*
1534 * Check if this file is being used.
1535 */
1536 if (vnode_isinuse(vp, 0))
1537 dataforkbusy = 1;
1538 if (rvp && vnode_isinuse(rvp, 0))
1539 rsrcforkbusy = 1;
1540
1541 // need this to check if we have to break the deletion
1542 // into multiple pieces
1543 isbigfile = (VTOC(vp)->c_datafork->ff_size >= HFS_BIGFILE_SIZE);
1544
1545 /*
1546 * Carbon semantics prohibit deleting busy files.
1547 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1548 */
1549 if (dataforkbusy || rsrcforkbusy) {
1550 if ((flags & VNODE_REMOVE_NODELETEBUSY) ||
1551 (hfsmp->hfs_privdir_desc.cd_cnid == 0)) {
1552 error = EBUSY;
1553 goto out;
1554 }
1555 }
1556
1557 #if QUOTA
1558 (void)hfs_getinoquota(cp);
1559 #endif /* QUOTA */
1560
1561 /*
1562 * We do the ubc_setsize before the hfs_truncate
1563 * since we'll be inside a transaction.
1564 */
1565 if ((cp->c_flag & C_HARDLINK) == 0 &&
1566 (!dataforkbusy || !rsrcforkbusy)) {
1567 /*
1568 * A ubc_setsize can cause a pagein here
1569 * so we need to the drop cnode lock. Note
1570 * that we still hold the truncate lock.
1571 */
1572 hfs_unlock(cp);
1573 if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
1574 ubc_setsize(vp, 0);
1575 }
1576 if (!rsrcforkbusy && rvp) {
1577 ubc_setsize(rvp, 0);
1578 }
1579 hfs_lock(cp, HFS_FORCE_LOCK);
1580 } else {
1581 struct cat_desc cndesc;
1582
1583 // for hard links, re-lookup the name that was passed
1584 // in so we get the correct cnid for the name (as
1585 // opposed to the c_cnid in the cnode which could have
1586 // been changed before this node got locked).
1587 bzero(&cndesc, sizeof(cndesc));
1588 cndesc.cd_nameptr = cnp->cn_nameptr;
1589 cndesc.cd_namelen = cnp->cn_namelen;
1590 cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
1591 cndesc.cd_hint = VTOC(dvp)->c_childhint;
1592
1593 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1594
1595 if (cat_lookup(hfsmp, &cndesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
1596 hfs_systemfile_unlock(hfsmp, lockflags);
1597 error = ENOENT;
1598 goto out;
1599 }
1600
1601 hfs_systemfile_unlock(hfsmp, lockflags);
1602 }
1603
1604 if ((error = hfs_start_transaction(hfsmp)) != 0) {
1605 goto out;
1606 }
1607 started_tr = 1;
1608
1609 if (!skip_reserve) {
1610 /*
1611 * Reserve some space in the Catalog file.
1612 */
1613 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, 0))) {
1614 goto out;
1615 }
1616 got_cookie = 1;
1617 }
1618
1619 /* Remove our entry from the namei cache. */
1620 cache_purge(vp);
1621
1622 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1623 if (hfsmp->jnl && vnode_islnk(vp))
1624 buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
1625
1626 /*
1627 * Truncate any non-busy forks. Busy forks will
1628 * get trucated when their vnode goes inactive.
1629 *
1630 * Since we're already inside a transaction,
1631 * tell hfs_truncate to skip the ubc_setsize.
1632 *
1633 * (Note: hard links are truncated in VOP_INACTIVE)
1634 */
1635 if ((cp->c_flag & C_HARDLINK) == 0) {
1636 int mode = cp->c_mode;
1637
1638 if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
1639 cp->c_mode = 0; /* Suppress hfs_update */
1640 error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ctx);
1641 cp->c_mode = mode;
1642 if (error)
1643 goto out;
1644 truncated = 1;
1645 }
1646 if (!rsrcforkbusy && rvp) {
1647 cp->c_mode = 0; /* Suppress hfs_update */
1648 error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ctx);
1649 cp->c_mode = mode;
1650 if (error)
1651 goto out;
1652 truncated = 1;
1653 }
1654 }
1655
1656 /*
1657 * Protect against a race with rename by using the component
1658 * name passed in and parent id from dvp (instead of using
1659 * the cp->c_desc which may have changed).
1660 */
1661 desc.cd_flags = 0;
1662 desc.cd_encoding = cp->c_desc.cd_encoding;
1663 desc.cd_nameptr = cnp->cn_nameptr;
1664 desc.cd_namelen = cnp->cn_namelen;
1665 desc.cd_parentcnid = dcp->c_cnid;
1666 desc.cd_hint = cp->c_desc.cd_hint;
1667 if (real_cnid) {
1668 // if it was a hardlink we had to re-lookup the cnid
1669 desc.cd_cnid = real_cnid;
1670 } else {
1671 desc.cd_cnid = cp->c_cnid;
1672 }
1673 microtime(&tv);
1674
1675 /*
1676 * There are 3 remove cases to consider:
1677 * 1. File is a hardlink ==> remove the link
1678 * 2. File is busy (in use) ==> move/rename the file
1679 * 3. File is not in use ==> remove the file
1680 */
1681
1682 if (cp->c_flag & C_HARDLINK) {
1683 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1684
1685 /* Delete the link record */
1686 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1687 if (error == 0) {
1688 /* Update the parent directory */
1689 if (dcp->c_entries > 0)
1690 dcp->c_entries--;
1691 if (dcp->c_nlink > 0)
1692 dcp->c_nlink--;
1693 dcp->c_ctime = tv.tv_sec;
1694 dcp->c_mtime = tv.tv_sec;
1695 (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1696
1697 if (--cp->c_nlink < 1) {
1698 char inodename[32];
1699 char delname[32];
1700 struct cat_desc to_desc;
1701 struct cat_desc from_desc;
1702
1703 /*
1704 * This is now esentially an open deleted file.
1705 * Rename it to reflect this state which makes
1706 * orphan file cleanup easier (see hfs_remove_orphans).
1707 * Note: a rename failure here is not fatal.
1708 */
1709 MAKE_INODE_NAME(inodename, cp->c_rdev);
1710 bzero(&from_desc, sizeof(from_desc));
1711 from_desc.cd_nameptr = inodename;
1712 from_desc.cd_namelen = strlen(inodename);
1713 from_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1714 from_desc.cd_flags = 0;
1715 from_desc.cd_cnid = cp->c_fileid;
1716
1717 MAKE_DELETED_NAME(delname, cp->c_fileid);
1718 bzero(&to_desc, sizeof(to_desc));
1719 to_desc.cd_nameptr = delname;
1720 to_desc.cd_namelen = strlen(delname);
1721 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1722 to_desc.cd_flags = 0;
1723 to_desc.cd_cnid = cp->c_fileid;
1724
1725 error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
1726 &to_desc, (struct cat_desc *)NULL);
1727 if (error != 0) {
1728 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1729 inodename, delname, cp);
1730 }
1731 if (error == 0) {
1732 /* Update the file's state */
1733 cp->c_flag |= C_DELETED;
1734 cp->c_ctime = tv.tv_sec;
1735 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1736 }
1737 } else {
1738 /* Update the file's state */
1739 cp->c_ctime = tv.tv_sec;
1740 (void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
1741 }
1742 }
1743 hfs_systemfile_unlock(hfsmp, lockflags);
1744 if (error != 0)
1745 goto out;
1746
1747 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1748
1749 } else if (dataforkbusy || rsrcforkbusy || isbigfile) {
1750 char delname[32];
1751 struct cat_desc to_desc;
1752 struct cat_desc todir_desc;
1753
1754 /*
1755 * Orphan this file (move to hidden directory).
1756 */
1757 bzero(&todir_desc, sizeof(todir_desc));
1758 todir_desc.cd_parentcnid = 2;
1759
1760 MAKE_DELETED_NAME(delname, cp->c_fileid);
1761 bzero(&to_desc, sizeof(to_desc));
1762 to_desc.cd_nameptr = delname;
1763 to_desc.cd_namelen = strlen(delname);
1764 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
1765 to_desc.cd_flags = 0;
1766 to_desc.cd_cnid = cp->c_cnid;
1767
1768 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
1769
1770 error = cat_rename(hfsmp, &desc, &todir_desc,
1771 &to_desc, (struct cat_desc *)NULL);
1772
1773 if (error == 0) {
1774 hfsmp->hfs_privdir_attr.ca_entries++;
1775 (void) cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1776 &hfsmp->hfs_privdir_attr, NULL, NULL);
1777
1778 /* Update the parent directory */
1779 if (dcp->c_entries > 0)
1780 dcp->c_entries--;
1781 if (dcp->c_nlink > 0)
1782 dcp->c_nlink--;
1783 dcp->c_ctime = tv.tv_sec;
1784 dcp->c_mtime = tv.tv_sec;
1785 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1786
1787 /* Update the file's state */
1788 cp->c_flag |= C_DELETED;
1789 cp->c_ctime = tv.tv_sec;
1790 --cp->c_nlink;
1791 (void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
1792 }
1793 hfs_systemfile_unlock(hfsmp, lockflags);
1794 if (error)
1795 goto out;
1796
1797 } else /* Not busy */ {
1798
1799 if (cp->c_blocks > 0) {
1800 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1801 cp->c_desc.cd_nameptr);
1802 error = EBUSY;
1803 goto out;
1804 }
1805
1806 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
1807
1808 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1809
1810 if (error && error != ENXIO && error != ENOENT && truncated) {
1811 if ((cp->c_datafork && cp->c_datafork->ff_size != 0) ||
1812 (cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) {
1813 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1814 error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size);
1815 } else {
1816 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1817 cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error);
1818 }
1819 }
1820 if (error == 0) {
1821 /* Delete any attributes, ignore errors */
1822 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
1823
1824 /* Update the parent directory */
1825 if (dcp->c_entries > 0)
1826 dcp->c_entries--;
1827 if (dcp->c_nlink > 0)
1828 dcp->c_nlink--;
1829 dcp->c_ctime = tv.tv_sec;
1830 dcp->c_mtime = tv.tv_sec;
1831 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
1832 }
1833 hfs_systemfile_unlock(hfsmp, lockflags);
1834 if (error)
1835 goto out;
1836
1837 #if QUOTA
1838 (void)hfs_chkiq(cp, -1, NOCRED, 0);
1839 #endif /* QUOTA */
1840
1841 cp->c_mode = 0;
1842 truncated = 0; // because the catalog entry is gone
1843 cp->c_flag |= C_NOEXISTS;
1844 cp->c_touch_chgtime = TRUE; /* XXX needed ? */
1845 --cp->c_nlink;
1846
1847 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1848 }
1849
1850 /*
1851 * All done with this cnode's descriptor...
1852 *
1853 * Note: all future catalog calls for this cnode must be
1854 * by fileid only. This is OK for HFS (which doesn't have
1855 * file thread records) since HFS doesn't support hard
1856 * links or the removal of busy files.
1857 */
1858 cat_releasedesc(&cp->c_desc);
1859
1860 HFS_KNOTE(dvp, NOTE_WRITE);
1861
1862 out:
1863 if (got_cookie) {
1864 cat_postflight(hfsmp, &cookie, 0);
1865 }
1866
1867 /* Commit the truncation to the catalog record */
1868 if (truncated) {
1869 cp->c_flag |= C_FORCEUPDATE;
1870 cp->c_touch_chgtime = TRUE;
1871 cp->c_touch_modtime = TRUE;
1872 (void) hfs_update(vp, 0);
1873 }
1874
1875 if (started_tr) {
1876 hfs_end_transaction(hfsmp);
1877 }
1878
1879 HFS_KNOTE(vp, NOTE_DELETE);
1880 if (rvp) {
1881 HFS_KNOTE(rvp, NOTE_DELETE);
1882 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1883 cp->c_flag |= C_NEED_RVNODE_PUT;
1884 };
1885
1886 return (error);
1887 }
1888
1889
1890 __private_extern__ void
1891 replace_desc(struct cnode *cp, struct cat_desc *cdp)
1892 {
1893 if (&cp->c_desc == cdp) {
1894 return;
1895 }
1896
1897 /* First release allocated name buffer */
1898 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
1899 char *name = cp->c_desc.cd_nameptr;
1900
1901 cp->c_desc.cd_nameptr = 0;
1902 cp->c_desc.cd_namelen = 0;
1903 cp->c_desc.cd_flags &= ~CD_HASBUF;
1904 vfs_removename(name);
1905 }
1906 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
1907
1908 /* Cnode now owns the name buffer */
1909 cdp->cd_nameptr = 0;
1910 cdp->cd_namelen = 0;
1911 cdp->cd_flags &= ~CD_HASBUF;
1912 }
1913
1914
1915 /*
1916 * Rename a cnode.
1917 *
1918 * The VFS layer guarantees that:
1919 * - source and destination will either both be directories, or
1920 * both not be directories.
1921 * - all the vnodes are from the same file system
1922 *
1923 * When the target is a directory, HFS must ensure that its empty.
1924 */
1925 static int
1926 hfs_vnop_rename(ap)
1927 struct vnop_rename_args /* {
1928 struct vnode *a_fdvp;
1929 struct vnode *a_fvp;
1930 struct componentname *a_fcnp;
1931 struct vnode *a_tdvp;
1932 struct vnode *a_tvp;
1933 struct componentname *a_tcnp;
1934 vfs_context_t a_context;
1935 } */ *ap;
1936 {
1937 struct vnode *tvp = ap->a_tvp;
1938 struct vnode *tdvp = ap->a_tdvp;
1939 struct vnode *fvp = ap->a_fvp;
1940 struct vnode *fdvp = ap->a_fdvp;
1941 struct componentname *tcnp = ap->a_tcnp;
1942 struct componentname *fcnp = ap->a_fcnp;
1943 struct proc *p = vfs_context_proc(ap->a_context);
1944 struct cnode *fcp;
1945 struct cnode *fdcp;
1946 struct cnode *tdcp;
1947 struct cnode *tcp;
1948 struct cat_desc from_desc;
1949 struct cat_desc to_desc;
1950 struct cat_desc out_desc;
1951 struct hfsmount *hfsmp;
1952 cat_cookie_t cookie;
1953 int tvp_deleted = 0;
1954 int started_tr = 0, got_cookie = 0;
1955 int took_trunc_lock = 0;
1956 int lockflags;
1957 int error;
1958
1959 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1960 if (tvp && vnode_isreg(tvp)) {
1961 hfs_lock_truncate(VTOC(tvp), TRUE);
1962 took_trunc_lock = 1;
1963 }
1964
1965 error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
1966 HFS_EXCLUSIVE_LOCK);
1967 if (error) {
1968 if (took_trunc_lock)
1969 hfs_unlock_truncate(VTOC(tvp));
1970 return (error);
1971 }
1972
1973 fdcp = VTOC(fdvp);
1974 fcp = VTOC(fvp);
1975 tdcp = VTOC(tdvp);
1976 tcp = tvp ? VTOC(tvp) : NULL;
1977 hfsmp = VTOHFS(tdvp);
1978
1979 /* Check for a race against unlink. */
1980 if (fcp->c_flag & C_NOEXISTS) {
1981 error = ENOENT;
1982 goto out;
1983 }
1984
1985 /*
1986 * The following edge case is caught here:
1987 * (to cannot be a descendent of from)
1988 *
1989 * o fdvp
1990 * /
1991 * /
1992 * o fvp
1993 * \
1994 * \
1995 * o tdvp
1996 * /
1997 * /
1998 * o tvp
1999 */
2000 if (tdcp->c_parentcnid == fcp->c_cnid) {
2001 error = EINVAL;
2002 goto out;
2003 }
2004
2005 /*
2006 * The following two edge cases are caught here:
2007 * (note tvp is not empty)
2008 *
2009 * o tdvp o tdvp
2010 * / /
2011 * / /
2012 * o tvp tvp o fdvp
2013 * \ \
2014 * \ \
2015 * o fdvp o fvp
2016 * /
2017 * /
2018 * o fvp
2019 */
2020 if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) {
2021 error = ENOTEMPTY;
2022 goto out;
2023 }
2024
2025 /*
2026 * The following edge case is caught here:
2027 * (the from child and parent are the same)
2028 *
2029 * o tdvp
2030 * /
2031 * /
2032 * fdvp o fvp
2033 */
2034 if (fdvp == fvp) {
2035 error = EINVAL;
2036 goto out;
2037 }
2038
2039 /*
2040 * Make sure "from" vnode and its parent are changeable.
2041 */
2042 if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) {
2043 error = EPERM;
2044 goto out;
2045 }
2046
2047 /*
2048 * If the destination parent directory is "sticky", then the
2049 * user must own the parent directory, or the destination of
2050 * the rename, otherwise the destination may not be changed
2051 * (except by root). This implements append-only directories.
2052 *
2053 * Note that checks for immutable and write access are done
2054 * by the call to hfs_removefile.
2055 */
2056 if (tvp && (tdcp->c_mode & S_ISTXT) &&
2057 (suser(vfs_context_ucred(tcnp->cn_context), NULL)) &&
2058 (kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) &&
2059 (hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) {
2060 error = EPERM;
2061 goto out;
2062 }
2063
2064 #if QUOTA
2065 if (tvp)
2066 (void)hfs_getinoquota(tcp);
2067 #endif
2068 /* Preflighting done, take fvp out of the name space. */
2069 cache_purge(fvp);
2070
2071 /*
2072 * When a file moves out of "Cleanup At Startup"
2073 * we can drop its NODUMP status.
2074 */
2075 if ((fcp->c_flags & UF_NODUMP) &&
2076 vnode_isreg(fvp) &&
2077 (fdvp != tdvp) &&
2078 (fdcp->c_desc.cd_nameptr != NULL) &&
2079 (strcmp(fdcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
2080 fcp->c_flags &= ~UF_NODUMP;
2081 fcp->c_touch_chgtime = TRUE;
2082 (void) hfs_update(fvp, 0);
2083 }
2084
2085 bzero(&from_desc, sizeof(from_desc));
2086 from_desc.cd_nameptr = fcnp->cn_nameptr;
2087 from_desc.cd_namelen = fcnp->cn_namelen;
2088 from_desc.cd_parentcnid = fdcp->c_cnid;
2089 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2090 from_desc.cd_cnid = fcp->c_cnid;
2091
2092 bzero(&to_desc, sizeof(to_desc));
2093 to_desc.cd_nameptr = tcnp->cn_nameptr;
2094 to_desc.cd_namelen = tcnp->cn_namelen;
2095 to_desc.cd_parentcnid = tdcp->c_cnid;
2096 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2097 to_desc.cd_cnid = fcp->c_cnid;
2098
2099 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2100 goto out;
2101 }
2102 started_tr = 1;
2103
2104 // if it's a hardlink then re-lookup the name so
2105 // that we get the correct cnid in from_desc (see
2106 // the comment in hfs_removefile for more details)
2107 //
2108 if (fcp->c_flag & C_HARDLINK) {
2109 struct cat_desc tmpdesc;
2110 cnid_t real_cnid;
2111
2112 bzero(&tmpdesc, sizeof(tmpdesc));
2113 tmpdesc.cd_nameptr = fcnp->cn_nameptr;
2114 tmpdesc.cd_namelen = fcnp->cn_namelen;
2115 tmpdesc.cd_parentcnid = fdcp->c_cnid;
2116 tmpdesc.cd_hint = fdcp->c_childhint;
2117
2118 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2119
2120 if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
2121 hfs_systemfile_unlock(hfsmp, lockflags);
2122 goto out;
2123 }
2124
2125 // use the real cnid instead of whatever happened to be there
2126 from_desc.cd_cnid = real_cnid;
2127 hfs_systemfile_unlock(hfsmp, lockflags);
2128 }
2129
2130 /*
2131 * Reserve some space in the Catalog file.
2132 */
2133 bzero(&cookie, sizeof(cookie));
2134 if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
2135 goto out;
2136 }
2137 got_cookie = 1;
2138
2139 /*
2140 * If the destination exists then it may need to be removed.
2141 */
2142 if (tvp) {
2143 /*
2144 * When fvp matches tvp they must be case variants
2145 * or hard links.
2146 */
2147 if (fvp == tvp) {
2148 /*
2149 * If this a hard link with different parents
2150 * and its not a case variant then tvp should
2151 * be removed.
2152 */
2153 if (!((fcp->c_flag & C_HARDLINK) &&
2154 ((fdvp != tdvp) ||
2155 (hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
2156 tcnp->cn_nameptr, tcnp->cn_namelen) != 0)))) {
2157 goto skip;
2158 }
2159 } else {
2160 cache_purge(tvp);
2161 }
2162
2163 if (vnode_isdir(tvp))
2164 error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE);
2165 else {
2166 error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE);
2167 }
2168
2169 if (error)
2170 goto out;
2171 tvp_deleted = 1;
2172 }
2173 skip:
2174 /*
2175 * All done with tvp and fvp
2176 */
2177
2178 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2179 error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
2180 hfs_systemfile_unlock(hfsmp, lockflags);
2181
2182 if (error) {
2183 goto out;
2184 }
2185
2186 /* Invalidate negative cache entries in the destination directory */
2187 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
2188 cache_purge_negatives(tdvp);
2189
2190 /* Update cnode's catalog descriptor */
2191 replace_desc(fcp, &out_desc);
2192 fcp->c_parentcnid = tdcp->c_cnid;
2193 fcp->c_hint = 0;
2194
2195 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE,
2196 (fdcp->c_cnid == kHFSRootFolderID));
2197 hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE,
2198 (tdcp->c_cnid == kHFSRootFolderID));
2199
2200 /* Update both parent directories. */
2201 if (fdvp != tdvp) {
2202 tdcp->c_nlink++;
2203 tdcp->c_entries++;
2204 if (fdcp->c_nlink > 0)
2205 fdcp->c_nlink--;
2206 if (fdcp->c_entries > 0)
2207 fdcp->c_entries--;
2208 fdcp->c_touch_chgtime = TRUE;
2209 fdcp->c_touch_modtime = TRUE;
2210
2211 fdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2212 (void) hfs_update(fdvp, 0);
2213 }
2214 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2215 tdcp->c_touch_chgtime = TRUE;
2216 tdcp->c_touch_modtime = TRUE;
2217
2218 tdcp->c_flag |= C_FORCEUPDATE; // XXXdbg - force it out!
2219 (void) hfs_update(tdvp, 0);
2220 out:
2221 if (got_cookie) {
2222 cat_postflight(hfsmp, &cookie, p);
2223 }
2224 if (started_tr) {
2225 hfs_end_transaction(hfsmp);
2226 }
2227
2228 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2229 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2230 */
2231 if (error == 0) {
2232 HFS_KNOTE(fvp, NOTE_RENAME);
2233 HFS_KNOTE(fdvp, NOTE_WRITE);
2234 if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
2235 };
2236
2237 if (took_trunc_lock)
2238 hfs_unlock_truncate(VTOC(tvp));
2239
2240 hfs_unlockfour(fdcp, fcp, tdcp, tcp);
2241
2242 /* After tvp is removed the only acceptable error is EIO */
2243 if (error && tvp_deleted)
2244 error = EIO;
2245
2246 return (error);
2247 }
2248
2249
2250 /*
2251 * Make a directory.
2252 */
2253 static int
2254 hfs_vnop_mkdir(struct vnop_mkdir_args *ap)
2255 {
2256 /***** HACK ALERT ********/
2257 ap->a_cnp->cn_flags |= MAKEENTRY;
2258 return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
2259 }
2260
2261
2262 /*
2263 * Create a symbolic link.
2264 */
2265 static int
2266 hfs_vnop_symlink(struct vnop_symlink_args *ap)
2267 {
2268 struct vnode **vpp = ap->a_vpp;
2269 struct vnode *dvp = ap->a_dvp;
2270 struct vnode *vp = NULL;
2271 struct hfsmount *hfsmp;
2272 struct filefork *fp;
2273 struct buf *bp = NULL;
2274 char *datap;
2275 int started_tr = 0;
2276 int len, error;
2277
2278 /* HFS standard disks don't support symbolic links */
2279 if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
2280 return (ENOTSUP);
2281
2282 /* Check for empty target name */
2283 if (ap->a_target[0] == 0)
2284 return (EINVAL);
2285
2286 /* Create the vnode */
2287 ap->a_vap->va_mode |= S_IFLNK;
2288 if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) {
2289 goto out;
2290 }
2291 vp = *vpp;
2292 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2293 return (error);
2294 fp = VTOF(vp);
2295 hfsmp = VTOHFS(dvp);
2296 len = strlen(ap->a_target);
2297
2298 #if QUOTA
2299 (void)hfs_getinoquota(VTOC(vp));
2300 #endif /* QUOTA */
2301
2302 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2303 goto out;
2304 }
2305 started_tr = 1;
2306
2307 /*
2308 * Allocate space for the link.
2309 *
2310 * Since we're already inside a transaction,
2311 * tell hfs_truncate to skip the ubc_setsize.
2312 *
2313 * Don't need truncate lock since a symlink is treated as a system file.
2314 */
2315 error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, ap->a_context);
2316 if (error)
2317 goto out; /* XXX need to remove link */
2318
2319 /* Write the link to disk */
2320 bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
2321 0, 0, BLK_META);
2322 if (hfsmp->jnl) {
2323 journal_modify_block_start(hfsmp->jnl, bp);
2324 }
2325 datap = (char *)buf_dataptr(bp);
2326 bzero(datap, buf_size(bp));
2327 bcopy(ap->a_target, datap, len);
2328
2329 if (hfsmp->jnl) {
2330 journal_modify_block_end(hfsmp->jnl, bp);
2331 } else {
2332 buf_bawrite(bp);
2333 }
2334 /*
2335 * We defered the ubc_setsize for hfs_truncate
2336 * since we were inside a transaction.
2337 *
2338 * We don't need to drop the cnode lock here
2339 * since this is a symlink.
2340 */
2341 ubc_setsize(vp, len);
2342 out:
2343 if (started_tr)
2344 hfs_end_transaction(hfsmp);
2345 if (vp) {
2346 hfs_unlock(VTOC(vp));
2347 }
2348 return (error);
2349 }
2350
2351
2352 /* structures to hold a "." or ".." directory entry */
2353 struct hfs_stddotentry {
2354 u_int32_t d_fileno; /* unique file number */
2355 u_int16_t d_reclen; /* length of this structure */
2356 u_int8_t d_type; /* dirent file type */
2357 u_int8_t d_namlen; /* len of filename */
2358 char d_name[4]; /* "." or ".." */
2359 };
2360
2361 struct hfs_extdotentry {
2362 u_int64_t d_fileno; /* unique file number */
2363 u_int64_t d_seekoff; /* seek offset (optional, used by servers) */
2364 u_int16_t d_reclen; /* length of this structure */
2365 u_int16_t d_namlen; /* len of filename */
2366 u_int8_t d_type; /* dirent file type */
2367 u_char d_name[3]; /* "." or ".." */
2368 };
2369
2370 typedef union {
2371 struct hfs_stddotentry std;
2372 struct hfs_extdotentry ext;
2373 } hfs_dotentry_t;
2374
2375 /*
2376 * hfs_vnop_readdir reads directory entries into the buffer pointed
2377 * to by uio, in a filesystem independent format. Up to uio_resid
2378 * bytes of data can be transferred. The data in the buffer is a
2379 * series of packed dirent structures where each one contains the
2380 * following entries:
2381 *
2382 * u_int32_t d_fileno; // file number of entry
2383 * u_int16_t d_reclen; // length of this record
2384 * u_int8_t d_type; // file type
2385 * u_int8_t d_namlen; // length of string in d_name
2386 * char d_name[MAXNAMELEN+1]; // null terminated file name
2387 *
2388 * The current position (uio_offset) refers to the next block of
2389 * entries. The offset can only be set to a value previously
2390 * returned by hfs_vnop_readdir or zero. This offset does not have
2391 * to match the number of bytes returned (in uio_resid).
2392 *
2393 * In fact, the offset used by HFS is essentially an index (26 bits)
2394 * with a tag (6 bits). The tag is for associating the next request
2395 * with the current request. This enables us to have multiple threads
2396 * reading the directory while the directory is also being modified.
2397 *
2398 * Each tag/index pair is tied to a unique directory hint. The hint
2399 * contains information (filename) needed to build the catalog b-tree
2400 * key for finding the next set of entries.
2401 */
2402 static int
2403 hfs_vnop_readdir(ap)
2404 struct vnop_readdir_args /* {
2405 vnode_t a_vp;
2406 uio_t a_uio;
2407 int a_flags;
2408 int *a_eofflag;
2409 int *a_numdirent;
2410 vfs_context_t a_context;
2411 } */ *ap;
2412 {
2413 struct vnode *vp = ap->a_vp;
2414 uio_t uio = ap->a_uio;
2415 struct cnode *cp;
2416 struct hfsmount *hfsmp;
2417 directoryhint_t *dirhint = NULL;
2418 directoryhint_t localhint;
2419 off_t offset;
2420 off_t startoffset;
2421 int error = 0;
2422 int eofflag = 0;
2423 user_addr_t user_start = 0;
2424 user_size_t user_len = 0;
2425 int index;
2426 unsigned int tag;
2427 int items;
2428 int lockflags;
2429 int extended;
2430 int nfs_cookies;
2431 caddr_t bufstart;
2432 cnid_t cnid_hint = 0;
2433
2434 items = 0;
2435 startoffset = offset = uio_offset(uio);
2436 bufstart = CAST_DOWN(caddr_t, uio_iov_base(uio));
2437 extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
2438 nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
2439
2440 /* Sanity check the uio data. */
2441 if ((uio_iovcnt(uio) > 1) ||
2442 (uio_resid(uio) < (int)sizeof(struct dirent))) {
2443 return (EINVAL);
2444 }
2445 /* Note that the dirhint calls require an exclusive lock. */
2446 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2447 return (error);
2448 cp = VTOC(vp);
2449 hfsmp = VTOHFS(vp);
2450
2451 /* Pick up cnid hint (if any). */
2452 if (nfs_cookies) {
2453 cnid_hint = (cnid_t)(uio_offset(uio) >> 32);
2454 uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL);
2455 if (cnid_hint == INT_MAX) { /* searching pass the last item */
2456 eofflag = 1;
2457 goto out;
2458 }
2459 }
2460 /*
2461 * Synthesize entries for "." and ".."
2462 */
2463 if (offset == 0) {
2464 hfs_dotentry_t dotentry[2];
2465 size_t uiosize;
2466
2467 if (extended) {
2468 struct hfs_extdotentry *entry = &dotentry[0].ext;
2469
2470 entry->d_fileno = cp->c_cnid;
2471 entry->d_reclen = sizeof(struct hfs_extdotentry);
2472 entry->d_type = DT_DIR;
2473 entry->d_namlen = 1;
2474 entry->d_name[0] = '.';
2475 entry->d_name[1] = '\0';
2476 entry->d_name[2] = '\0';
2477 entry->d_seekoff = 1;
2478
2479 ++entry;
2480 entry->d_fileno = cp->c_parentcnid;
2481 entry->d_reclen = sizeof(struct hfs_extdotentry);
2482 entry->d_type = DT_DIR;
2483 entry->d_namlen = 2;
2484 entry->d_name[0] = '.';
2485 entry->d_name[1] = '.';
2486 entry->d_name[2] = '\0';
2487 entry->d_seekoff = 2;
2488 uiosize = 2 * sizeof(struct hfs_extdotentry);
2489 } else {
2490 struct hfs_stddotentry *entry = &dotentry[0].std;
2491
2492 entry->d_fileno = cp->c_cnid;
2493 entry->d_reclen = sizeof(struct hfs_stddotentry);
2494 entry->d_type = DT_DIR;
2495 entry->d_namlen = 1;
2496 *(int *)&entry->d_name[0] = 0;
2497 entry->d_name[0] = '.';
2498
2499 ++entry;
2500 entry->d_fileno = cp->c_parentcnid;
2501 entry->d_reclen = sizeof(struct hfs_stddotentry);
2502 entry->d_type = DT_DIR;
2503 entry->d_namlen = 2;
2504 *(int *)&entry->d_name[0] = 0;
2505 entry->d_name[0] = '.';
2506 entry->d_name[1] = '.';
2507 uiosize = 2 * sizeof(struct hfs_stddotentry);
2508 }
2509 if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) {
2510 goto out;
2511 }
2512 offset += 2;
2513 }
2514
2515 /* If there are no real entries then we're done. */
2516 if (cp->c_entries == 0) {
2517 error = 0;
2518 eofflag = 1;
2519 uio_setoffset(uio, offset);
2520 goto seekoffcalc;
2521 }
2522
2523 //
2524 // We have to lock the user's buffer here so that we won't
2525 // fault on it after we've acquired a shared lock on the
2526 // catalog file. The issue is that you can get a 3-way
2527 // deadlock if someone else starts a transaction and then
2528 // tries to lock the catalog file but can't because we're
2529 // here and we can't service our page fault because VM is
2530 // blocked trying to start a transaction as a result of
2531 // trying to free up pages for our page fault. It's messy
2532 // but it does happen on dual-procesors that are paging
2533 // heavily (see radar 3082639 for more info). By locking
2534 // the buffer up-front we prevent ourselves from faulting
2535 // while holding the shared catalog file lock.
2536 //
2537 // Fortunately this and hfs_search() are the only two places
2538 // currently (10/30/02) that can fault on user data with a
2539 // shared lock on the catalog file.
2540 //
2541 if (hfsmp->jnl && uio_isuserspace(uio)) {
2542 user_start = uio_curriovbase(uio);
2543 user_len = uio_curriovlen(uio);
2544
2545 if ((error = vslock(user_start, user_len)) != 0) {
2546 user_start = 0;
2547 goto out;
2548 }
2549 }
2550 /* Convert offset into a catalog directory index. */
2551 index = (offset & HFS_INDEX_MASK) - 2;
2552 tag = offset & ~HFS_INDEX_MASK;
2553
2554 /* Lock catalog during cat_findname and cat_getdirentries. */
2555 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2556
2557 /* When called from NFS, try and resolve a cnid hint. */
2558 if (nfs_cookies && cnid_hint != 0) {
2559 if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) {
2560 if ( localhint.dh_desc.cd_parentcnid == cp->c_cnid) {
2561 localhint.dh_index = index - 1;
2562 localhint.dh_time = 0;
2563 bzero(&localhint.dh_link, sizeof(localhint.dh_link));
2564 dirhint = &localhint; /* don't forget to release the descriptor */
2565 } else {
2566 cat_releasedesc(&localhint.dh_desc);
2567 }
2568 }
2569 }
2570
2571 /* Get a directory hint (cnode must be locked exclusive) */
2572 if (dirhint == NULL) {
2573 dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag);
2574
2575 /* Hide tag from catalog layer. */
2576 dirhint->dh_index &= HFS_INDEX_MASK;
2577 if (dirhint->dh_index == HFS_INDEX_MASK) {
2578 dirhint->dh_index = -1;
2579 }
2580 }
2581
2582 /* Pack the buffer with dirent entries. */
2583 error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag);
2584
2585 hfs_systemfile_unlock(hfsmp, lockflags);
2586
2587 if (error != 0) {
2588 goto out;
2589 }
2590
2591 /* Get index to the next item */
2592 index += items;
2593
2594 if (items >= (int)cp->c_entries) {
2595 eofflag = 1;
2596 }
2597
2598 /* Convert catalog directory index back into an offset. */
2599 while (tag == 0)
2600 tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
2601 uio_setoffset(uio, (index + 2) | tag);
2602 dirhint->dh_index |= tag;
2603
2604 seekoffcalc:
2605 cp->c_touch_acctime = TRUE;
2606
2607 if (ap->a_numdirent) {
2608 if (startoffset == 0)
2609 items += 2;
2610 *ap->a_numdirent = items;
2611 }
2612
2613 out:
2614 if (hfsmp->jnl && user_start) {
2615 vsunlock(user_start, user_len, TRUE);
2616 }
2617 /* If we didn't do anything then go ahead and dump the hint. */
2618 if ((dirhint != NULL) &&
2619 (dirhint != &localhint) &&
2620 (uio_offset(uio) == startoffset)) {
2621 hfs_reldirhint(cp, dirhint);
2622 eofflag = 1;
2623 }
2624 if (ap->a_eofflag) {
2625 *ap->a_eofflag = eofflag;
2626 }
2627 if (dirhint == &localhint) {
2628 cat_releasedesc(&localhint.dh_desc);
2629 }
2630 hfs_unlock(cp);
2631 return (error);
2632 }
2633
2634
2635 /*
2636 * Read contents of a symbolic link.
2637 */
2638 static int
2639 hfs_vnop_readlink(ap)
2640 struct vnop_readlink_args /* {
2641 struct vnode *a_vp;
2642 struct uio *a_uio;
2643 vfs_context_t a_context;
2644 } */ *ap;
2645 {
2646 struct vnode *vp = ap->a_vp;
2647 struct cnode *cp;
2648 struct filefork *fp;
2649 int error;
2650
2651 if (!vnode_islnk(vp))
2652 return (EINVAL);
2653
2654 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
2655 return (error);
2656 cp = VTOC(vp);
2657 fp = VTOF(vp);
2658
2659 /* Zero length sym links are not allowed */
2660 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
2661 VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
2662 error = EINVAL;
2663 goto exit;
2664 }
2665
2666 /* Cache the path so we don't waste buffer cache resources */
2667 if (fp->ff_symlinkptr == NULL) {
2668 struct buf *bp = NULL;
2669
2670 MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
2671 error = (int)buf_meta_bread(vp, (daddr64_t)0,
2672 roundup((int)fp->ff_size,
2673 VTOHFS(vp)->hfs_phys_block_size),
2674 vfs_context_ucred(ap->a_context), &bp);
2675 if (error) {
2676 if (bp)
2677 buf_brelse(bp);
2678 if (fp->ff_symlinkptr) {
2679 FREE(fp->ff_symlinkptr, M_TEMP);
2680 fp->ff_symlinkptr = NULL;
2681 }
2682 goto exit;
2683 }
2684 bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size);
2685
2686 if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) {
2687 buf_markinvalid(bp); /* data no longer needed */
2688 }
2689 buf_brelse(bp);
2690 }
2691 error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
2692
2693 /*
2694 * Keep track blocks read
2695 */
2696 if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) {
2697
2698 /*
2699 * If this file hasn't been seen since the start of
2700 * the current sampling period then start over.
2701 */
2702 if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
2703 VTOF(vp)->ff_bytesread = fp->ff_size;
2704 else
2705 VTOF(vp)->ff_bytesread += fp->ff_size;
2706
2707 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2708 // cp->c_touch_acctime = TRUE;
2709 }
2710
2711 exit:
2712 hfs_unlock(cp);
2713 return (error);
2714 }
2715
2716
2717 /*
2718 * Get configurable pathname variables.
2719 */
2720 static int
2721 hfs_vnop_pathconf(ap)
2722 struct vnop_pathconf_args /* {
2723 struct vnode *a_vp;
2724 int a_name;
2725 int *a_retval;
2726 vfs_context_t a_context;
2727 } */ *ap;
2728 {
2729 switch (ap->a_name) {
2730 case _PC_LINK_MAX:
2731 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2732 *ap->a_retval = 1;
2733 else
2734 *ap->a_retval = HFS_LINK_MAX;
2735 break;
2736 case _PC_NAME_MAX:
2737 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
2738 *ap->a_retval = kHFSMaxFileNameChars; /* 255 */
2739 else
2740 *ap->a_retval = kHFSPlusMaxFileNameChars; /* 31 */
2741 break;
2742 case _PC_PATH_MAX:
2743 *ap->a_retval = PATH_MAX; /* 1024 */
2744 break;
2745 case _PC_PIPE_BUF:
2746 *ap->a_retval = PIPE_BUF;
2747 break;
2748 case _PC_CHOWN_RESTRICTED:
2749 *ap->a_retval = 1;
2750 break;
2751 case _PC_NO_TRUNC:
2752 *ap->a_retval = 0;
2753 break;
2754 case _PC_NAME_CHARS_MAX:
2755 *ap->a_retval = kHFSPlusMaxFileNameChars;
2756 break;
2757 case _PC_CASE_SENSITIVE:
2758 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
2759 *ap->a_retval = 1;
2760 else
2761 *ap->a_retval = 0;
2762 break;
2763 case _PC_CASE_PRESERVING:
2764 *ap->a_retval = 1;
2765 break;
2766 default:
2767 return (EINVAL);
2768 }
2769
2770 return (0);
2771 }
2772
2773
2774 /*
2775 * Update a cnode's on-disk metadata.
2776 *
2777 * If waitfor is set, then wait for the disk write of
2778 * the node to complete.
2779 *
2780 * The cnode must be locked exclusive
2781 */
2782 __private_extern__
2783 int
2784 hfs_update(struct vnode *vp, __unused int waitfor)
2785 {
2786 struct cnode *cp = VTOC(vp);
2787 struct proc *p;
2788 struct cat_fork *dataforkp = NULL;
2789 struct cat_fork *rsrcforkp = NULL;
2790 struct cat_fork datafork;
2791 struct hfsmount *hfsmp;
2792 int lockflags;
2793 int error;
2794
2795 p = current_proc();
2796 hfsmp = VTOHFS(vp);
2797
2798 if (vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID)) {
2799 return (0);
2800 }
2801 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
2802 cp->c_flag &= ~C_MODIFIED;
2803 cp->c_touch_acctime = 0;
2804 cp->c_touch_chgtime = 0;
2805 cp->c_touch_modtime = 0;
2806 return (0);
2807 }
2808
2809 hfs_touchtimes(hfsmp, cp);
2810
2811 /* Nothing to update. */
2812 if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) {
2813 return (0);
2814 }
2815
2816 if (cp->c_datafork)
2817 dataforkp = &cp->c_datafork->ff_data;
2818 if (cp->c_rsrcfork)
2819 rsrcforkp = &cp->c_rsrcfork->ff_data;
2820
2821 /*
2822 * For delayed allocations updates are
2823 * postponed until an fsync or the file
2824 * gets written to disk.
2825 *
2826 * Deleted files can defer meta data updates until inactive.
2827 *
2828 * If we're ever called with the C_FORCEUPDATE flag though
2829 * we have to do the update.
2830 */
2831 if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
2832 (ISSET(cp->c_flag, C_DELETED) ||
2833 (dataforkp && cp->c_datafork->ff_unallocblocks) ||
2834 (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
2835 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2836 cp->c_flag |= C_MODIFIED;
2837
2838 HFS_KNOTE(vp, NOTE_ATTRIB);
2839
2840 return (0);
2841 }
2842
2843 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2844 return error;
2845 }
2846
2847 /*
2848 * For files with invalid ranges (holes) the on-disk
2849 * field representing the size of the file (cf_size)
2850 * must be no larger than the start of the first hole.
2851 */
2852 if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
2853 bcopy(dataforkp, &datafork, sizeof(datafork));
2854 datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
2855 dataforkp = &datafork;
2856 } else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) {
2857 // always make sure the block count and the size
2858 // of the file match the number of blocks actually
2859 // allocated to the file on disk
2860 bcopy(dataforkp, &datafork, sizeof(datafork));
2861 // make sure that we don't assign a negative block count
2862 if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
2863 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2864 cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
2865 }
2866 datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
2867 datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
2868 dataforkp = &datafork;
2869 }
2870
2871 /*
2872 * Lock the Catalog b-tree file.
2873 * A shared lock is sufficient since an update doesn't change
2874 * the tree and the lock on vp protects the cnode.
2875 */
2876 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2877
2878 /* XXX - waitfor is not enforced */
2879 error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
2880
2881 hfs_systemfile_unlock(hfsmp, lockflags);
2882
2883 /* After the updates are finished, clear the flags */
2884 cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
2885
2886 hfs_end_transaction(hfsmp);
2887
2888 HFS_KNOTE(vp, NOTE_ATTRIB);
2889
2890 return (error);
2891 }
2892
2893 /*
2894 * Allocate a new node
2895 */
2896 static int
2897 hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
2898 struct vnode_attr *vap, vfs_context_t ctx)
2899 {
2900 struct cnode *cp = NULL;
2901 struct cnode *dcp;
2902 struct vnode *tvp;
2903 struct hfsmount *hfsmp;
2904 struct cat_desc in_desc, out_desc;
2905 struct cat_attr attr;
2906 struct timeval tv;
2907 cat_cookie_t cookie;
2908 int lockflags;
2909 int error, started_tr = 0, got_cookie = 0;
2910 enum vtype vnodetype;
2911 int mode;
2912
2913 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK)))
2914 return (error);
2915 dcp = VTOC(dvp);
2916 hfsmp = VTOHFS(dvp);
2917 *vpp = NULL;
2918 tvp = NULL;
2919 out_desc.cd_flags = 0;
2920 out_desc.cd_nameptr = NULL;
2921
2922 mode = MAKEIMODE(vap->va_type, vap->va_mode);
2923
2924 if ((mode & S_IFMT) == 0)
2925 mode |= S_IFREG;
2926 vnodetype = IFTOVT(mode);
2927
2928 /* Check if were out of usable disk space. */
2929 if ((hfs_freeblks(hfsmp, 1) <= 0) && (suser(vfs_context_ucred(ctx), NULL) != 0)) {
2930 error = ENOSPC;
2931 goto exit;
2932 }
2933
2934 microtime(&tv);
2935
2936 /* Setup the default attributes */
2937 bzero(&attr, sizeof(attr));
2938 attr.ca_mode = mode;
2939 attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
2940 attr.ca_mtime = tv.tv_sec;
2941 if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
2942 attr.ca_mtime += 3600; /* Same as what hfs_update does */
2943 }
2944 attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
2945 attr.ca_atimeondisk = attr.ca_atime;
2946 /* On HFS+ the ThreadExists flag must always be set for files. */
2947 if (vnodetype != VDIR && (hfsmp->hfs_flags & HFS_STANDARD) == 0)
2948 attr.ca_recflags = kHFSThreadExistsMask;
2949
2950 attr.ca_uid = vap->va_uid;
2951 attr.ca_gid = vap->va_gid;
2952 VATTR_SET_SUPPORTED(vap, va_mode);
2953 VATTR_SET_SUPPORTED(vap, va_uid);
2954 VATTR_SET_SUPPORTED(vap, va_gid);
2955
2956 /* Tag symlinks with a type and creator. */
2957 if (vnodetype == VLNK) {
2958 struct FndrFileInfo *fip;
2959
2960 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
2961 fip->fdType = SWAP_BE32(kSymLinkFileType);
2962 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
2963 }
2964 if (cnp->cn_flags & ISWHITEOUT)
2965 attr.ca_flags |= UF_OPAQUE;
2966
2967 /* Setup the descriptor */
2968 in_desc.cd_nameptr = cnp->cn_nameptr;
2969 in_desc.cd_namelen = cnp->cn_namelen;
2970 in_desc.cd_parentcnid = dcp->c_cnid;
2971 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
2972 in_desc.cd_hint = dcp->c_childhint;
2973 in_desc.cd_encoding = 0;
2974
2975 if ((error = hfs_start_transaction(hfsmp)) != 0) {
2976 goto exit;
2977 }
2978 started_tr = 1;
2979
2980 /*
2981 * Reserve some space in the Catalog file.
2982 *
2983 * (we also add CAT_DELETE since our getnewvnode
2984 * request can cause an hfs_inactive call to
2985 * delete an unlinked file)
2986 */
2987 if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, 0))) {
2988 goto exit;
2989 }
2990 got_cookie = 1;
2991
2992 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2993 error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
2994 if (error == 0) {
2995 /* Update the parent directory */
2996 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2997 dcp->c_nlink++;
2998 dcp->c_entries++;
2999 dcp->c_ctime = tv.tv_sec;
3000 dcp->c_mtime = tv.tv_sec;
3001 (void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3002 HFS_KNOTE(dvp, NOTE_ATTRIB);
3003 }
3004 hfs_systemfile_unlock(hfsmp, lockflags);
3005 if (error)
3006 goto exit;
3007
3008 /* Invalidate negative cache entries in the directory */
3009 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
3010 cache_purge_negatives(dvp);
3011
3012 if (vnodetype == VDIR) {
3013 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
3014 } else {
3015 HFS_KNOTE(dvp, NOTE_WRITE);
3016 };
3017
3018 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
3019 (dcp->c_cnid == kHFSRootFolderID));
3020
3021 // XXXdbg
3022 // have to end the transaction here before we call hfs_getnewvnode()
3023 // because that can cause us to try and reclaim a vnode on a different
3024 // file system which could cause us to start a transaction which can
3025 // deadlock with someone on that other file system (since we could be
3026 // holding two transaction locks as well as various vnodes and we did
3027 // not obtain the locks on them in the proper order).
3028 //
3029 // NOTE: this means that if the quota check fails or we have to update
3030 // the change time on a block-special device that those changes
3031 // will happen as part of independent transactions.
3032 //
3033 if (started_tr) {
3034 hfs_end_transaction(hfsmp);
3035 started_tr = 0;
3036 }
3037
3038 /*
3039 * Create a vnode for the object just created.
3040 *
3041 * The cnode is locked on successful return.
3042 */
3043 error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, 0, &attr, NULL, &tvp);
3044 if (error)
3045 goto exit;
3046
3047 // XXXdbg
3048 //cache_enter(dvp, tvp, cnp);
3049
3050 cp = VTOC(tvp);
3051 #if QUOTA
3052 /*
3053 * We call hfs_chkiq with FORCE flag so that if we
3054 * fall through to the rmdir we actually have
3055 * accounted for the inode
3056 */
3057 if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_QUOTA) {
3058 if ((error = hfs_getinoquota(cp)) ||
3059 (error = hfs_chkiq(cp, 1, vfs_context_ucred(ctx), FORCE))) {
3060
3061 if (vnode_isdir(tvp))
3062 (void) hfs_removedir(dvp, tvp, cnp, 0);
3063 else {
3064 hfs_unlock(cp);
3065 hfs_lock_truncate(cp, TRUE);
3066 hfs_lock(cp, HFS_FORCE_LOCK);
3067 (void) hfs_removefile(dvp, tvp, cnp, 0, 0);
3068 hfs_unlock_truncate(cp);
3069 }
3070 /*
3071 * we successfully allocated a new vnode, but
3072 * the quota check is telling us we're beyond
3073 * our limit, so we need to dump our lock + reference
3074 */
3075 hfs_unlock(cp);
3076 vnode_put(tvp);
3077
3078 goto exit;
3079 }
3080 }
3081 #endif /* QUOTA */
3082
3083 /* Remember if any ACL data was set. */
3084 if (VATTR_IS_ACTIVE(vap, va_acl) &&
3085 (vap->va_acl != NULL)) {
3086 cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
3087 cp->c_touch_chgtime = TRUE;
3088 (void) hfs_update(tvp, TRUE);
3089 }
3090 *vpp = tvp;
3091 exit:
3092 cat_releasedesc(&out_desc);
3093
3094 if (got_cookie) {
3095 cat_postflight(hfsmp, &cookie, 0);
3096 }
3097 /*
3098 * Check if a file is located in the "Cleanup At Startup"
3099 * directory. If it is then tag it as NODUMP so that we
3100 * can be lazy about zero filling data holes.
3101 */
3102 if ((error == 0) && dvp && (vnodetype == VREG) &&
3103 (dcp->c_desc.cd_nameptr != NULL) &&
3104 (strcmp(dcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
3105 struct vnode *ddvp;
3106
3107 hfs_unlock(dcp);
3108 dvp = NULL;
3109
3110 /*
3111 * The parent of "Cleanup At Startup" should
3112 * have the ASCII name of the userid.
3113 */
3114 if (hfs_vget(hfsmp, dcp->c_parentcnid, &ddvp, 0) == 0) {
3115 if (VTOC(ddvp)->c_desc.cd_nameptr) {
3116 uid_t uid;
3117
3118 uid = strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0);
3119 if ((uid == cp->c_uid) ||
3120 (uid == vfs_context_ucred(ctx)->cr_uid)) {
3121 cp->c_flags |= UF_NODUMP;
3122 cp->c_touch_chgtime = TRUE;
3123 }
3124 }
3125 hfs_unlock(VTOC(ddvp));
3126 vnode_put(ddvp);
3127 }
3128 }
3129 if (dvp) {
3130 hfs_unlock(dcp);
3131 }
3132 if (error == 0 && cp != NULL) {
3133 hfs_unlock(cp);
3134 }
3135 if (started_tr) {
3136 hfs_end_transaction(hfsmp);
3137 started_tr = 0;
3138 }
3139
3140 return (error);
3141 }
3142
3143
3144 /*
3145 * WARNING - assumes caller has cnode lock.
3146 */
3147 __private_extern__
3148 int
3149 hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, __unused struct proc *p)
3150 {
3151 struct vnode *rvp;
3152 struct cnode *cp = VTOC(vp);
3153 int error;
3154 int vid;
3155
3156 if ((rvp = cp->c_rsrc_vp)) {
3157 vid = vnode_vid(rvp);
3158
3159 /* Use exising vnode */
3160 error = vnode_getwithvid(rvp, vid);
3161 if (error) {
3162 char * name = VTOC(vp)->c_desc.cd_nameptr;
3163
3164 if (name)
3165 printf("hfs_vgetrsrc: couldn't get"
3166 " resource fork for %s\n", name);
3167 return (error);
3168 }
3169 } else {
3170 struct cat_fork rsrcfork;
3171 struct componentname cn;
3172 int lockflags;
3173
3174 /*
3175 * Make sure cnode lock is exclusive, if not upgrade it.
3176 *
3177 * We assume that we were called from a read-only VNOP (getattr)
3178 * and that its safe to have the cnode lock dropped and reacquired.
3179 */
3180 if (cp->c_lockowner != current_thread()) {
3181 /*
3182 * If the upgrade fails we loose the lock and
3183 * have to take the exclusive lock on our own.
3184 */
3185 if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) != 0)
3186 lck_rw_lock_exclusive(&cp->c_rwlock);
3187 cp->c_lockowner = current_thread();
3188 }
3189
3190 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3191
3192 /* Get resource fork data */
3193 error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
3194 (struct cat_attr *)0, &rsrcfork, NULL);
3195
3196 hfs_systemfile_unlock(hfsmp, lockflags);
3197 if (error)
3198 return (error);
3199
3200 /*
3201 * Supply hfs_getnewvnode with a component name.
3202 */
3203 cn.cn_pnbuf = NULL;
3204 if (cp->c_desc.cd_nameptr) {
3205 MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
3206 cn.cn_nameiop = LOOKUP;
3207 cn.cn_flags = ISLASTCN | HASBUF;
3208 cn.cn_context = NULL;
3209 cn.cn_pnlen = MAXPATHLEN;
3210 cn.cn_nameptr = cn.cn_pnbuf;
3211 cn.cn_hash = 0;
3212 cn.cn_consume = 0;
3213 cn.cn_namelen = sprintf(cn.cn_nameptr, "%s%s", cp->c_desc.cd_nameptr, _PATH_RSRCFORKSPEC);
3214 }
3215 error = hfs_getnewvnode(hfsmp, vnode_parent(vp), cn.cn_pnbuf ? &cn : NULL,
3216 &cp->c_desc, 2, &cp->c_attr, &rsrcfork, &rvp);
3217 if (cn.cn_pnbuf)
3218 FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
3219 if (error)
3220 return (error);
3221 }
3222
3223 *rvpp = rvp;
3224 return (0);
3225 }
3226
3227
3228 static void
3229 filt_hfsdetach(struct knote *kn)
3230 {
3231 struct vnode *vp;
3232
3233 vp = (struct vnode *)kn->kn_hook;
3234 if (vnode_getwithvid(vp, kn->kn_hookid))
3235 return;
3236
3237 if (1) { /* ! KNDETACH_VNLOCKED */
3238 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3239 (void) KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
3240 hfs_unlock(VTOC(vp));
3241 }
3242 }
3243
3244 vnode_put(vp);
3245 }
3246
3247 /*ARGSUSED*/
3248 static int
3249 filt_hfsread(struct knote *kn, long hint)
3250 {
3251 struct vnode *vp = (struct vnode *)kn->kn_hook;
3252 int dropvp = 0;
3253
3254 if (hint == 0) {
3255 if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) {
3256 hint = NOTE_REVOKE;
3257 } else
3258 dropvp = 1;
3259 }
3260 if (hint == NOTE_REVOKE) {
3261 /*
3262 * filesystem is gone, so set the EOF flag and schedule
3263 * the knote for deletion.
3264 */
3265 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3266 return (1);
3267 }
3268
3269 /* poll(2) semantics dictate always saying there is data */
3270 kn->kn_data = (!(kn->kn_flags & EV_POLL)) ?
3271 VTOF(vp)->ff_size - kn->kn_fp->f_fglob->fg_offset : 1;
3272
3273 if (dropvp)
3274 vnode_put(vp);
3275
3276 return (kn->kn_data != 0);
3277 }
3278
3279 /*ARGSUSED*/
3280 static int
3281 filt_hfswrite(struct knote *kn, long hint)
3282 {
3283 int dropvp = 0;
3284
3285 if (hint == 0) {
3286 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3287 hint = NOTE_REVOKE;
3288 } else
3289 vnode_put(kn->kn_hook);
3290 }
3291 if (hint == NOTE_REVOKE) {
3292 /*
3293 * filesystem is gone, so set the EOF flag and schedule
3294 * the knote for deletion.
3295 */
3296 kn->kn_data = 0;
3297 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3298 return (1);
3299 }
3300 kn->kn_data = 0;
3301 return (1);
3302 }
3303
3304 static int
3305 filt_hfsvnode(struct knote *kn, long hint)
3306 {
3307
3308 if (hint == 0) {
3309 if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
3310 hint = NOTE_REVOKE;
3311 } else
3312 vnode_put(kn->kn_hook);
3313 }
3314 if (kn->kn_sfflags & hint)
3315 kn->kn_fflags |= hint;
3316 if ((hint == NOTE_REVOKE)) {
3317 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3318 return (1);
3319 }
3320
3321 return (kn->kn_fflags != 0);
3322 }
3323
3324 static struct filterops hfsread_filtops =
3325 { 1, NULL, filt_hfsdetach, filt_hfsread };
3326 static struct filterops hfswrite_filtops =
3327 { 1, NULL, filt_hfsdetach, filt_hfswrite };
3328 static struct filterops hfsvnode_filtops =
3329 { 1, NULL, filt_hfsdetach, filt_hfsvnode };
3330
3331 /*
3332 * Add a kqueue filter.
3333 */
3334 static int
3335 hfs_vnop_kqfiltadd(
3336 struct vnop_kqfilt_add_args /* {
3337 struct vnode *a_vp;
3338 struct knote *a_kn;
3339 struct proc *p;
3340 vfs_context_t a_context;
3341 } */ *ap)
3342 {
3343 struct vnode *vp = ap->a_vp;
3344 struct knote *kn = ap->a_kn;
3345 int error;
3346
3347 switch (kn->kn_filter) {
3348 case EVFILT_READ:
3349 if (vnode_isreg(vp)) {
3350 kn->kn_fop = &hfsread_filtops;
3351 } else {
3352 return EINVAL;
3353 };
3354 break;
3355 case EVFILT_WRITE:
3356 if (vnode_isreg(vp)) {
3357 kn->kn_fop = &hfswrite_filtops;
3358 } else {
3359 return EINVAL;
3360 };
3361 break;
3362 case EVFILT_VNODE:
3363 kn->kn_fop = &hfsvnode_filtops;
3364 break;
3365 default:
3366 return (1);
3367 }
3368
3369 kn->kn_hook = (caddr_t)vp;
3370 kn->kn_hookid = vnode_vid(vp);
3371
3372 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
3373 return (error);
3374 KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
3375 hfs_unlock(VTOC(vp));
3376
3377 return (0);
3378 }
3379
3380 /*
3381 * Remove a kqueue filter
3382 */
3383 static int
3384 hfs_vnop_kqfiltremove(ap)
3385 struct vnop_kqfilt_remove_args /* {
3386 struct vnode *a_vp;
3387 uintptr_t ident;
3388 vfs_context_t a_context;
3389 } */ *ap;
3390 {
3391 int result;
3392
3393 result = ENOTSUP; /* XXX */
3394
3395 return (result);
3396 }
3397
3398 /*
3399 * Wrapper for special device reads
3400 */
3401 static int
3402 hfsspec_read(ap)
3403 struct vnop_read_args /* {
3404 struct vnode *a_vp;
3405 struct uio *a_uio;
3406 int a_ioflag;
3407 vfs_context_t a_context;
3408 } */ *ap;
3409 {
3410 /*
3411 * Set access flag.
3412 */
3413 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3414 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
3415 }
3416
3417 /*
3418 * Wrapper for special device writes
3419 */
3420 static int
3421 hfsspec_write(ap)
3422 struct vnop_write_args /* {
3423 struct vnode *a_vp;
3424 struct uio *a_uio;
3425 int a_ioflag;
3426 vfs_context_t a_context;
3427 } */ *ap;
3428 {
3429 /*
3430 * Set update and change flags.
3431 */
3432 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3433 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3434 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
3435 }
3436
3437 /*
3438 * Wrapper for special device close
3439 *
3440 * Update the times on the cnode then do device close.
3441 */
3442 static int
3443 hfsspec_close(ap)
3444 struct vnop_close_args /* {
3445 struct vnode *a_vp;
3446 int a_fflag;
3447 vfs_context_t a_context;
3448 } */ *ap;
3449 {
3450 struct vnode *vp = ap->a_vp;
3451 struct cnode *cp;
3452
3453 if (vnode_isinuse(ap->a_vp, 1)) {
3454 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3455 cp = VTOC(vp);
3456 hfs_touchtimes(VTOHFS(vp), cp);
3457 hfs_unlock(cp);
3458 }
3459 }
3460 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
3461 }
3462
3463 #if FIFO
3464 /*
3465 * Wrapper for fifo reads
3466 */
3467 static int
3468 hfsfifo_read(ap)
3469 struct vnop_read_args /* {
3470 struct vnode *a_vp;
3471 struct uio *a_uio;
3472 int a_ioflag;
3473 vfs_context_t a_context;
3474 } */ *ap;
3475 {
3476 extern int (**fifo_vnodeop_p)(void *);
3477
3478 /*
3479 * Set access flag.
3480 */
3481 VTOC(ap->a_vp)->c_touch_acctime = TRUE;
3482 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
3483 }
3484
3485 /*
3486 * Wrapper for fifo writes
3487 */
3488 static int
3489 hfsfifo_write(ap)
3490 struct vnop_write_args /* {
3491 struct vnode *a_vp;
3492 struct uio *a_uio;
3493 int a_ioflag;
3494 vfs_context_t a_context;
3495 } */ *ap;
3496 {
3497 extern int (**fifo_vnodeop_p)(void *);
3498
3499 /*
3500 * Set update and change flags.
3501 */
3502 VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
3503 VTOC(ap->a_vp)->c_touch_modtime = TRUE;
3504 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
3505 }
3506
3507 /*
3508 * Wrapper for fifo close
3509 *
3510 * Update the times on the cnode then do device close.
3511 */
3512 static int
3513 hfsfifo_close(ap)
3514 struct vnop_close_args /* {
3515 struct vnode *a_vp;
3516 int a_fflag;
3517 vfs_context_t a_context;
3518 } */ *ap;
3519 {
3520 extern int (**fifo_vnodeop_p)(void *);
3521 struct vnode *vp = ap->a_vp;
3522 struct cnode *cp;
3523
3524 if (vnode_isinuse(ap->a_vp, 1)) {
3525 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
3526 cp = VTOC(vp);
3527 hfs_touchtimes(VTOHFS(vp), cp);
3528 hfs_unlock(cp);
3529 }
3530 }
3531 return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
3532 }
3533
3534 /*
3535 * kqfilt_add wrapper for fifos.
3536 *
3537 * Fall through to hfs kqfilt_add routines if needed
3538 */
3539 int
3540 hfsfifo_kqfilt_add(ap)
3541 struct vnop_kqfilt_add_args *ap;
3542 {
3543 extern int (**fifo_vnodeop_p)(void *);
3544 int error;
3545
3546 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap);
3547 if (error)
3548 error = hfs_vnop_kqfiltadd(ap);
3549 return (error);
3550 }
3551
3552 /*
3553 * kqfilt_remove wrapper for fifos.
3554 *
3555 * Fall through to hfs kqfilt_remove routines if needed
3556 */
3557 int
3558 hfsfifo_kqfilt_remove(ap)
3559 struct vnop_kqfilt_remove_args *ap;
3560 {
3561 extern int (**fifo_vnodeop_p)(void *);
3562 int error;
3563
3564 error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap);
3565 if (error)
3566 error = hfs_vnop_kqfiltremove(ap);
3567 return (error);
3568 }
3569
3570 #endif /* FIFO */
3571
3572 /*
3573 * Synchronize a file's in-core state with that on disk.
3574 */
3575 static int
3576 hfs_vnop_fsync(ap)
3577 struct vnop_fsync_args /* {
3578 struct vnode *a_vp;
3579 int a_waitfor;
3580 vfs_context_t a_context;
3581 } */ *ap;
3582 {
3583 struct vnode* vp = ap->a_vp;
3584 int error;
3585
3586 /*
3587 * We need to allow ENOENT lock errors since unlink
3588 * systenm call can call VNOP_FSYNC during vclean.
3589 */
3590 error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
3591 if (error)
3592 return (0);
3593
3594 error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context));
3595
3596 hfs_unlock(VTOC(vp));
3597 return (error);
3598 }
3599
3600 /*****************************************************************************
3601 *
3602 * VOP Tables
3603 *
3604 *****************************************************************************/
3605 int hfs_vnop_readdirattr(struct vnop_readdirattr_args *); /* in hfs_attrlist.c */
3606 int hfs_vnop_inactive(struct vnop_inactive_args *); /* in hfs_cnode.c */
3607 int hfs_vnop_reclaim(struct vnop_reclaim_args *); /* in hfs_cnode.c */
3608 int hfs_vnop_link(struct vnop_link_args *); /* in hfs_link.c */
3609 int hfs_vnop_lookup(struct vnop_lookup_args *); /* in hfs_lookup.c */
3610 int hfs_vnop_search(struct vnop_searchfs_args *); /* in hfs_search.c */
3611
3612 int hfs_vnop_read(struct vnop_read_args *); /* in hfs_readwrite.c */
3613 int hfs_vnop_write(struct vnop_write_args *); /* in hfs_readwrite.c */
3614 int hfs_vnop_ioctl(struct vnop_ioctl_args *); /* in hfs_readwrite.c */
3615 int hfs_vnop_select(struct vnop_select_args *); /* in hfs_readwrite.c */
3616 int hfs_vnop_strategy(struct vnop_strategy_args *); /* in hfs_readwrite.c */
3617 int hfs_vnop_allocate(struct vnop_allocate_args *); /* in hfs_readwrite.c */
3618 int hfs_vnop_pagein(struct vnop_pagein_args *); /* in hfs_readwrite.c */
3619 int hfs_vnop_pageout(struct vnop_pageout_args *); /* in hfs_readwrite.c */
3620 int hfs_vnop_bwrite(struct vnop_bwrite_args *); /* in hfs_readwrite.c */
3621 int hfs_vnop_blktooff(struct vnop_blktooff_args *); /* in hfs_readwrite.c */
3622 int hfs_vnop_offtoblk(struct vnop_offtoblk_args *); /* in hfs_readwrite.c */
3623 int hfs_vnop_blockmap(struct vnop_blockmap_args *); /* in hfs_readwrite.c */
3624 int hfs_vnop_getxattr(struct vnop_getxattr_args *); /* in hfs_xattr.c */
3625 int hfs_vnop_setxattr(struct vnop_setxattr_args *); /* in hfs_xattr.c */
3626 int hfs_vnop_removexattr(struct vnop_removexattr_args *); /* in hfs_xattr.c */
3627 int hfs_vnop_listxattr(struct vnop_listxattr_args *); /* in hfs_xattr.c */
3628
3629 int (**hfs_vnodeop_p)(void *);
3630
3631 #define VOPFUNC int (*)(void *)
3632
3633 struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
3634 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3635 { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup }, /* lookup */
3636 { &vnop_create_desc, (VOPFUNC)hfs_vnop_create }, /* create */
3637 { &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod }, /* mknod */
3638 { &vnop_open_desc, (VOPFUNC)hfs_vnop_open }, /* open */
3639 { &vnop_close_desc, (VOPFUNC)hfs_vnop_close }, /* close */
3640 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3641 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3642 { &vnop_read_desc, (VOPFUNC)hfs_vnop_read }, /* read */
3643 { &vnop_write_desc, (VOPFUNC)hfs_vnop_write }, /* write */
3644 { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl }, /* ioctl */
3645 { &vnop_select_desc, (VOPFUNC)hfs_vnop_select }, /* select */
3646 { &vnop_revoke_desc, (VOPFUNC)nop_revoke }, /* revoke */
3647 { &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange }, /* exchange */
3648 { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
3649 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3650 { &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove }, /* remove */
3651 { &vnop_link_desc, (VOPFUNC)hfs_vnop_link }, /* link */
3652 { &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename }, /* rename */
3653 { &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir }, /* mkdir */
3654 { &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir }, /* rmdir */
3655 { &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink }, /* symlink */
3656 { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir }, /* readdir */
3657 { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr }, /* readdirattr */
3658 { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink }, /* readlink */
3659 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3660 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3661 { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy }, /* strategy */
3662 { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf }, /* pathconf */
3663 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3664 { &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate }, /* allocate */
3665 { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search }, /* search fs */
3666 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite }, /* bwrite */
3667 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* pagein */
3668 { &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout }, /* pageout */
3669 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3670 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3671 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3672 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3673 { &vnop_kqfilt_add_desc, (VOPFUNC)hfs_vnop_kqfiltadd }, /* kqfilt_add */
3674 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfs_vnop_kqfiltremove }, /* kqfilt_remove */
3675 { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
3676 { &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
3677 { &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
3678 { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
3679 { NULL, (VOPFUNC)NULL }
3680 };
3681
3682 struct vnodeopv_desc hfs_vnodeop_opv_desc =
3683 { &hfs_vnodeop_p, hfs_vnodeop_entries };
3684
3685 int (**hfs_specop_p)(void *);
3686 struct vnodeopv_entry_desc hfs_specop_entries[] = {
3687 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3688 { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
3689 { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */
3690 { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
3691 { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */
3692 { &vnop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
3693 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3694 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3695 { &vnop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
3696 { &vnop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
3697 { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
3698 { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */
3699 { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
3700 { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
3701 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3702 { &vnop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
3703 { &vnop_link_desc, (VOPFUNC)spec_link }, /* link */
3704 { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
3705 { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
3706 { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
3707 { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
3708 { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
3709 { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
3710 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3711 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3712 { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
3713 { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
3714 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3715 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3716 { &vnop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
3717 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3718 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3719 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3720 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3721 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3722 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3723 };
3724 struct vnodeopv_desc hfs_specop_opv_desc =
3725 { &hfs_specop_p, hfs_specop_entries };
3726
3727 #if FIFO
3728 int (**hfs_fifoop_p)(void *);
3729 struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
3730 { &vnop_default_desc, (VOPFUNC)vn_default_error },
3731 { &vnop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
3732 { &vnop_create_desc, (VOPFUNC)fifo_create }, /* create */
3733 { &vnop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
3734 { &vnop_open_desc, (VOPFUNC)fifo_open }, /* open */
3735 { &vnop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
3736 { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr }, /* getattr */
3737 { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr }, /* setattr */
3738 { &vnop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
3739 { &vnop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
3740 { &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
3741 { &vnop_select_desc, (VOPFUNC)fifo_select }, /* select */
3742 { &vnop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
3743 { &vnop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
3744 { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync }, /* fsync */
3745 { &vnop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
3746 { &vnop_link_desc, (VOPFUNC)fifo_link }, /* link */
3747 { &vnop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
3748 { &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
3749 { &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
3750 { &vnop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
3751 { &vnop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
3752 { &vnop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
3753 { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive }, /* inactive */
3754 { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim }, /* reclaim */
3755 { &vnop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
3756 { &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
3757 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
3758 { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
3759 { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein }, /* Pagein */
3760 { &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout }, /* Pageout */
3761 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
3762 { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff }, /* blktooff */
3763 { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk }, /* offtoblk */
3764 { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap }, /* blockmap */
3765 { &vnop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add }, /* kqfilt_add */
3766 { &vnop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove }, /* kqfilt_remove */
3767 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
3768 };
3769 struct vnodeopv_desc hfs_fifoop_opv_desc =
3770 { &hfs_fifoop_p, hfs_fifoop_entries };
3771 #endif /* FIFO */
3772
3773
3774