2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/file_internal.h>
26 #include <sys/dirent.h>
29 #include <sys/mount.h>
30 #include <sys/vnode_internal.h>
31 #include <sys/malloc.h>
33 #include <sys/paths.h>
34 #include <sys/quota.h>
37 #include <sys/kauth.h>
38 #include <sys/uio_internal.h>
40 #include <miscfs/specfs/specdev.h>
41 #include <miscfs/fifofs/fifo.h>
42 #include <vfs/vfs_support.h>
43 #include <machine/spl.h>
45 #include <sys/kdebug.h>
46 #include <sys/sysctl.h>
49 #include "hfs_catalog.h"
50 #include "hfs_cnode.h"
52 #include "hfs_mount.h"
53 #include "hfs_quota.h"
54 #include "hfs_endian.h"
56 #include "hfscommon/headers/BTreesInternal.h"
57 #include "hfscommon/headers/FileMgrInternal.h"
59 #define MAKE_DELETED_NAME(NAME,FID) \
60 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
62 #define KNDETACH_VNLOCKED 0x00000001
64 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
67 /* Global vfs data structures for hfs */
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");
73 extern unsigned long strtoul(const char *, char **, int);
75 static int hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
,
76 struct componentname
*cnp
, struct vnode_attr
*vap
,
79 static int hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
);
81 static int hfs_removedir(struct vnode
*, struct vnode
*, struct componentname
*,
84 static int hfs_removefile(struct vnode
*, struct vnode
*, struct componentname
*,
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
*);
102 /* Options for hfs_removedir and hfs_removefile */
103 #define HFSRM_SKIP_RESERVE 0x01
106 int hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
);
108 int hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
,
110 int hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
,
111 kauth_cred_t cred
, struct proc
*p
);
113 /*****************************************************************************
115 * Common Operations on vnodes
117 *****************************************************************************/
120 * Create a regular file.
123 hfs_vnop_create(struct vnop_create_args
*ap
)
125 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
129 * Make device special file.
132 hfs_vnop_mknod(struct vnop_mknod_args
*ap
)
134 struct vnode_attr
*vap
= ap
->a_vap
;
135 struct vnode
*dvp
= ap
->a_dvp
;
136 struct vnode
**vpp
= ap
->a_vpp
;
140 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
144 /* Create the vnode */
145 error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, vap
, ap
->a_context
);
150 cp
->c_touch_acctime
= TRUE
;
151 cp
->c_touch_chgtime
= TRUE
;
152 cp
->c_touch_modtime
= TRUE
;
154 if ((vap
->va_rdev
!= VNOVAL
) &&
155 (vap
->va_type
== VBLK
|| vap
->va_type
== VCHR
))
156 cp
->c_rdev
= vap
->va_rdev
;
162 * Open a file/directory.
165 hfs_vnop_open(struct vnop_open_args
*ap
)
167 struct vnode
*vp
= ap
->a_vp
;
173 * Files marked append-only must be opened for appending.
175 if ((VTOC(vp
)->c_flags
& APPEND
) && !vnode_isdir(vp
) &&
176 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
179 if (vnode_isreg(vp
) && !UBCINFOEXISTS(vp
))
180 return (EBUSY
); /* file is in use by the kernel */
182 /* Don't allow journal file to be opened externally. */
183 if (VTOC(vp
)->c_fileid
== VTOHFS(vp
)->hfs_jnlfileid
)
186 * On the first (non-busy) open of a fragmented
187 * file attempt to de-frag it (if its less than 20MB).
189 if ((VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
) ||
190 (VTOHFS(vp
)->jnl
== NULL
) ||
191 !vnode_isreg(vp
) || vnode_isinuse(vp
, 0)) {
195 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
199 fp
->ff_extents
[7].blockCount
!= 0 &&
200 fp
->ff_size
<= (20 * 1024 * 1024)) {
202 * Wait until system bootup is done (3 min).
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
));
211 hfs_unlock(VTOC(vp
));
218 * Close a file/directory.
222 struct vnop_close_args
/* {
225 vfs_context_t a_context;
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
;
234 if ( hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) != 0)
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
);
246 busy
= vnode_isinuse(vp
, 1);
249 hfs_touchtimes(VTOHFS(vp
), cp
);
251 if (vnode_isdir(vp
)) {
252 hfs_reldirhints(cp
, busy
);
253 } else if (vnode_issystem(vp
) && !busy
) {
262 * Get basic attributes.
265 hfs_vnop_getattr(struct vnop_getattr_args
*ap
)
267 struct vnode
*vp
= ap
->a_vp
;
268 struct vnode_attr
*vap
= ap
->a_vap
;
269 struct vnode
*rvp
= NULL
;
270 struct hfsmount
*hfsmp
;
275 if ((error
= hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
))) {
280 hfs_touchtimes(hfsmp
, cp
);
281 v_type
= vnode_vtype(vp
);
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
)) {
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 */
295 VATTR_RETURN(vap
, va_nlink
, (uint64_t)entries
);
298 if (VATTR_IS_ACTIVE(vap
, va_nchildren
)) {
301 entries
= cp
->c_entries
;
302 if (vnode_isvroot(vp
)) {
303 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
304 --entries
; /* hide private dir */
306 entries
-= 2; /* hide the journal files */
308 VATTR_RETURN(vap
, va_nchildren
, entries
);
311 VATTR_RETURN(vap
, va_nlink
, (uint64_t)cp
->c_nlink
);
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
);
319 uint64_t total_size
= 0;
322 if (cp
->c_datafork
) {
323 total_size
= cp
->c_datafork
->ff_size
;
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
);
334 if (rcp
&& rcp
->c_rsrcfork
) {
335 total_size
+= rcp
->c_rsrcfork
->ff_size
;
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
;
346 if (VATTR_IS_ACTIVE(vap
, va_total_alloc
)) {
347 if (v_type
== VDIR
) {
348 VATTR_RETURN(vap
, va_total_alloc
, 0);
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
;
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
);
361 VATTR_RETURN(vap
, va_data_size
, VTOF(vp
)->ff_size
);
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
);
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
);
373 /* XXX is S_IFXATTR still needed ??? */
374 if (VNODE_IS_RSRC(vp
))
375 vap
->va_mode
|= S_IFXATTR
;
377 VATTR_RETURN(vap
, va_flags
, cp
->c_flags
);
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.
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
);
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
);
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
) {
402 vap
->va_access_time
.tv_sec
= tv
.tv_sec
;
404 vap
->va_access_time
.tv_sec
= cp
->c_atime
;
406 vap
->va_access_time
.tv_nsec
= 0;
407 VATTR_SET_SUPPORTED(vap
, va_access_time
);
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
);
420 * Exporting file IDs from HFS Plus:
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).
427 * The stat call (getattr) uses va_fileid and the Carbon APIs,
428 * which are hardlink-ignorant, will ask for va_linkid.
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);
436 VATTR_RETURN(vap
, va_encoding
, cp
->c_encoding
);
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) {
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
);
461 struct vnop_setattr_args
/* {
463 struct vnode_attr *a_vap;
464 vfs_context_t a_context;
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
);
479 /* Don't allow modification of the journal file. */
480 if (hfsmp
->hfs_jnlfileid
== VTOC(vp
)->c_fileid
) {
485 * File size change request.
486 * We are guaranteed that this is not a directory, and that
487 * the filesystem object is writeable.
489 VATTR_SET_SUPPORTED(vap
, va_data_size
);
490 if (VATTR_IS_ACTIVE(vap
, va_data_size
) && !vnode_islnk(vp
)) {
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
));
500 error
= hfs_truncate(vp
, vap
->va_data_size
, vap
->va_vaflags
& 0xffff, 0, ap
->a_context
);
502 hfs_unlock_truncate(cp
);
507 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
513 * Owner/group change request.
514 * We are guaranteed that the new owner/group is valid and legal.
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))
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.
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))
536 * We are guaranteed that only flags allowed to change given the
537 * current securelevel are being changed.
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
;
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.
551 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
552 /* Remember if any ACL data was set or cleared. */
553 if (vap
->va_acl
== NULL
) {
555 if (cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) {
556 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
557 cp
->c_touch_chgtime
= TRUE
;
561 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
562 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
563 cp
->c_touch_chgtime
= TRUE
;
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
)) {
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
;
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
;
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.
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
;
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
;
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
);
620 if ((error
= hfs_update(vp
, TRUE
)) != 0)
622 HFS_KNOTE(vp
, NOTE_ATTRIB
);
631 * Change the mode on a file.
632 * cnode must be locked before calling.
636 hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
, struct proc
*p
)
638 register struct cnode
*cp
= VTOC(vp
);
641 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
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
;
648 extd
= &cp
->c_datafork
->ff_extents
[0];
649 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
654 #if OVERRIDE_UNKNOWN_PERMISSIONS
655 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
) {
659 cp
->c_mode
&= ~ALLPERMS
;
660 cp
->c_mode
|= (mode
& ALLPERMS
);
661 cp
->c_touch_chgtime
= TRUE
;
668 hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
)
670 struct cnode
*cp
= VTOC(vp
);
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.
679 switch (vnode_vtype(vp
)) {
683 if (VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
)
690 /* If immutable bit set, nobody gets to write it. */
691 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
694 /* Otherwise, user id 0 always gets access. */
695 if (!suser(cred
, NULL
))
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
);
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
);
707 /* Otherwise, check everyone else. */
708 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
713 * Perform chown operation on cnode cp;
714 * code must be locked prior to call.
718 hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
, kauth_cred_t cred
,
721 register struct cnode
*cp
= VTOC(vp
);
731 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
734 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
)
737 if (uid
== (uid_t
)VNOVAL
)
739 if (gid
== (gid_t
)VNOVAL
)
742 #if 0 /* we are guaranteed that this is already the case */
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.
748 if ((kauth_cred_getuid(cred
) != cp
->c_uid
|| uid
!= cp
->c_uid
||
750 (kauth_cred_ismember_gid(cred
, gid
, &is_member
) || !is_member
))) &&
751 (error
= suser(cred
, 0)))
758 if ((error
= hfs_getinoquota(cp
)))
761 dqrele(cp
->c_dquot
[USRQUOTA
]);
762 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
765 dqrele(cp
->c_dquot
[GRPQUOTA
]);
766 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
770 * Eventually need to account for (fake) a block per directory
771 * if (vnode_isdir(vp))
772 * change = VTOHFS(vp)->blockSize;
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
;
787 if ((error
= hfs_getinoquota(cp
)) == 0) {
789 dqrele(cp
->c_dquot
[USRQUOTA
]);
790 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
793 dqrele(cp
->c_dquot
[GRPQUOTA
]);
794 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
796 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
797 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
800 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
802 for (i
= 0; i
< MAXQUOTAS
; i
++) {
803 dqrele(cp
->c_dquot
[i
]);
804 cp
->c_dquot
[i
] = NODQUOT
;
809 if (hfs_getinoquota(cp
) == 0) {
811 dqrele(cp
->c_dquot
[USRQUOTA
]);
812 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
815 dqrele(cp
->c_dquot
[GRPQUOTA
]);
816 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
818 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
819 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
820 (void) hfs_getinoquota(cp
);
824 if (hfs_getinoquota(cp
))
825 panic("hfs_chown: lost quota");
828 if (ouid
!= uid
|| ogid
!= gid
)
829 cp
->c_touch_chgtime
= TRUE
;
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.
842 hfs_vnop_exchange(ap
)
843 struct vnop_exchange_args
/* {
847 vfs_context_t a_context;
850 struct vnode
*from_vp
= ap
->a_fvp
;
851 struct vnode
*to_vp
= ap
->a_tvp
;
852 struct cnode
*from_cp
;
854 struct hfsmount
*hfsmp
;
855 struct cat_desc tempdesc
;
856 struct cat_attr tempattr
;
858 int error
= 0, started_tr
= 0, got_cookie
= 0;
861 /* The files must be on the same volume. */
862 if (vnode_mount(from_vp
) != vnode_mount(to_vp
))
865 if (from_vp
== to_vp
)
868 if ((error
= hfs_lockpair(VTOC(from_vp
), VTOC(to_vp
), HFS_EXCLUSIVE_LOCK
)))
871 from_cp
= VTOC(from_vp
);
873 hfsmp
= VTOHFS(from_vp
);
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
)) {
883 // XXXdbg - don't allow modification of the journal or journal_info_block
885 struct HFSPlusExtentDescriptor
*extd
;
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
) {
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
) {
904 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
910 * Reserve some space in the Catalog file.
912 bzero(&cookie
, sizeof(cookie
));
913 if ((error
= cat_preflight(hfsmp
, CAT_EXCHANGE
, &cookie
, vfs_context_proc(ap
->a_context
)))) {
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.
922 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_EXTENTS
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
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
,
932 hfs_systemfile_unlock(hfsmp
, lockflags
);
935 * Note that we don't need to exchange any extended attributes
936 * since the attributes are keyed by file ID.
939 if (error
!= E_NONE
) {
940 error
= MacToVFSError(error
);
944 /* Purge the vnodes from the name cache */
946 cache_purge(from_vp
);
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
));
955 * Swap the descriptors and all non-fork related attributes.
956 * (except the modify date)
958 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
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);
973 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
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);
987 /* Rehash the cnodes using their new file IDs */
988 hfs_chash_rehash(from_cp
, to_cp
);
991 * When a file moves out of "Cleanup At Startup"
992 * we can drop its NODUMP status.
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
;
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
;
1005 HFS_KNOTE(from_vp
, NOTE_ATTRIB
);
1006 HFS_KNOTE(to_vp
, NOTE_ATTRIB
);
1010 cat_postflight(hfsmp
, &cookie
, vfs_context_proc(ap
->a_context
));
1013 hfs_end_transaction(hfsmp
);
1016 hfs_unlockpair(from_cp
, to_cp
);
1022 * cnode must be locked
1026 hfs_fsync(struct vnode
*vp
, int waitfor
, int fullsync
, struct proc
*p
)
1028 struct cnode
*cp
= VTOC(vp
);
1029 struct filefork
*fp
= NULL
;
1031 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1035 int took_trunc_lock
= 0;
1037 wait
= (waitfor
== MNT_WAIT
);
1039 /* HFS directories don't have any data blocks. */
1040 if (vnode_isdir(vp
))
1044 * For system files flush the B-tree header and
1045 * for regular files write out any clusters
1047 if (vnode_issystem(vp
)) {
1048 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1050 if (hfsmp
->jnl
== NULL
) {
1051 BTFlushPath(VTOF(vp
));
1054 } else if (UBCINFOEXISTS(vp
)) {
1056 hfs_lock_truncate(cp
, TRUE
);
1057 took_trunc_lock
= 1;
1059 /* Don't hold cnode lock when calling into cluster layer. */
1060 (void) cluster_push(vp
, 0);
1062 hfs_lock(cp
, HFS_FORCE_LOCK
);
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).
1069 * Files with NODUMP can bypass zero filling here.
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) {
1076 if (tv
.tv_sec
< cp
->c_zftimeout
) {
1077 /* Remember that a force sync was requested. */
1078 cp
->c_flag
|= C_ZFWANTSYNC
;
1081 if (!took_trunc_lock
) {
1083 hfs_lock_truncate(cp
, TRUE
);
1084 hfs_lock(cp
, HFS_FORCE_LOCK
);
1085 took_trunc_lock
= 1;
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
;
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:
1097 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1099 /* Don't hold cnode lock when calling into cluster layer. */
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
;
1108 (void) cluster_push(vp
, 0);
1109 hfs_lock(cp
, HFS_FORCE_LOCK
);
1111 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1112 cp
->c_zftimeout
= 0;
1115 if (took_trunc_lock
)
1116 hfs_unlock_truncate(cp
);
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.
1127 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1129 lockflag
= BUF_SKIP_LOCKED
;
1134 * Flush all dirty buffers associated with a vnode.
1136 buf_flushdirtyblks(vp
, wait
, lockflag
, "hfs_fsync");
1139 if (vnode_isreg(vp
) && vnode_issystem(vp
)) {
1140 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1142 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
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
);
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
);
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
1160 if (fullsync
|| always_do_fullfsync
) {
1162 journal_flush(hfsmp
->jnl
);
1164 /* XXX need to pass context! */
1165 VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, NULL
);
1174 /* Sync an hfs catalog b-tree node */
1176 hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
)
1182 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1184 // XXXdbg - don't need to do this on a journaled volume
1189 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1191 * Look for a matching node that has been delayed
1192 * but is not part of a set (B_LOCKED).
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
1200 bp
= buf_getblk(vp
, node
, 0, 0, 0, BLK_META
| BLK_ONLYVALID
);
1203 if ((buf_flags(bp
) & (B_LOCKED
| B_DELWRI
)) == B_DELWRI
)
1204 (void) VNOP_BWRITE(bp
);
1209 hfs_systemfile_unlock(hfsmp
, lockflags
);
1217 hfs_btsync_callback(struct buf
*bp
, void *dummy
)
1219 buf_clearflags(bp
, B_LOCKED
);
1220 (void) buf_bawrite(bp
);
1222 return(BUF_CLAIMED
);
1228 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1230 struct cnode
*cp
= VTOC(vp
);
1234 if (sync_transaction
)
1235 flags
|= BUF_SKIP_NONLOCKED
;
1237 * Flush all dirty buffers associated with b-tree.
1239 buf_iterate(vp
, hfs_btsync_callback
, flags
, 0);
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
;
1252 * Remove a directory.
1256 struct vnop_rmdir_args
/* {
1257 struct vnode *a_dvp;
1259 struct componentname *a_cnp;
1260 vfs_context_t a_context;
1263 struct vnode
*dvp
= ap
->a_dvp
;
1264 struct vnode
*vp
= ap
->a_vp
;
1267 if (!vnode_isdir(vp
)) {
1273 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1276 error
= hfs_removedir(dvp
, vp
, ap
->a_cnp
, 0);
1278 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1284 * Remove a directory
1286 * Both dvp and vp cnodes are locked
1289 hfs_removedir(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1292 vfs_context_t ctx
= cnp
->cn_context
;
1293 struct proc
*p
= vfs_context_proc(ctx
);
1296 struct hfsmount
* hfsmp
;
1297 struct cat_desc desc
;
1298 cat_cookie_t cookie
;
1300 int error
= 0, started_tr
= 0, got_cookie
= 0;
1307 return (EINVAL
); /* cannot remove "." */
1310 (void)hfs_getinoquota(cp
);
1312 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1317 if (!skip_reserve
) {
1319 * Reserve some space in the Catalog file.
1321 bzero(&cookie
, sizeof(cookie
));
1322 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
))) {
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
1335 if (cp
->c_entries
!= 0) {
1339 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1344 if (cp
->c_entries
> 0)
1345 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1347 /* Remove the entry from the namei cache: */
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).
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
;
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
);
1365 /* Delete any attributes, ignore errors */
1366 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1368 hfs_systemfile_unlock(hfsmp
, lockflags
);
1374 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1377 /* The parent lost a child */
1378 if (dcp
->c_entries
> 0)
1380 if (dcp
->c_nlink
> 0)
1382 dcp
->c_touch_chgtime
= TRUE
;
1383 dcp
->c_touch_modtime
= TRUE
;
1385 dcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - don't screw around, force this guy out
1387 (void) hfs_update(dvp
, 0);
1388 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
1390 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1392 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1393 cp
->c_flag
|= C_NOEXISTS
;
1395 HFS_KNOTE(vp
, NOTE_DELETE
);
1398 cat_postflight(hfsmp
, &cookie
, p
);
1401 hfs_end_transaction(hfsmp
);
1409 * Remove a file or link.
1413 struct vnop_remove_args
/* {
1414 struct vnode *a_dvp;
1416 struct componentname *a_cnp;
1418 vfs_context_t a_context;
1421 struct vnode
*dvp
= ap
->a_dvp
;
1422 struct vnode
*vp
= ap
->a_vp
;
1429 hfs_lock_truncate(VTOC(vp
), TRUE
);
1431 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1434 error
= hfs_removefile(dvp
, vp
, ap
->a_cnp
, ap
->a_flags
, 0);
1436 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1438 hfs_unlock_truncate(VTOC(vp
));
1444 hfs_removefile_callback(struct buf
*bp
, void *hfsmp
) {
1446 if ( !(buf_flags(bp
) & B_META
))
1447 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp
);
1449 * it's part of the current transaction, kill it.
1451 journal_kill_block(((struct hfsmount
*)hfsmp
)->jnl
, bp
);
1453 return (BUF_CLAIMED
);
1459 * Similar to hfs_vnop_remove except there are additional options.
1461 * Requires cnode and truncate locks to be held.
1464 hfs_removefile(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1465 int flags
, int skip_reserve
)
1467 struct vnode
*rvp
= NULL
;
1470 struct hfsmount
*hfsmp
;
1471 struct cat_desc desc
;
1473 vfs_context_t ctx
= cnp
->cn_context
;
1474 int dataforkbusy
= 0;
1475 int rsrcforkbusy
= 0;
1477 cat_cookie_t cookie
;
1480 int started_tr
= 0, got_cookie
= 0;
1482 cnid_t real_cnid
= 0;
1484 /* Directories should call hfs_rmdir! */
1485 if (vnode_isdir(vp
)) {
1493 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
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
)) {
1506 /* Make sure a remove is permitted */
1507 if (VNODE_IS_RSRC(vp
)) {
1513 * Aquire a vnode for a non-empty resource fork.
1514 * (needed for hfs_truncate)
1516 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1517 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, 0);
1522 // XXXdbg - don't allow deleting the journal or journal_info_block
1523 if (hfsmp
->jnl
&& cp
->c_datafork
) {
1524 struct HFSPlusExtentDescriptor
*extd
;
1526 extd
= &cp
->c_datafork
->ff_extents
[0];
1527 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
1534 * Check if this file is being used.
1536 if (vnode_isinuse(vp
, 0))
1538 if (rvp
&& vnode_isinuse(rvp
, 0))
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
);
1546 * Carbon semantics prohibit deleting busy files.
1547 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1549 if (dataforkbusy
|| rsrcforkbusy
) {
1550 if ((flags
& VNODE_REMOVE_NODELETEBUSY
) ||
1551 (hfsmp
->hfs_privdir_desc
.cd_cnid
== 0)) {
1558 (void)hfs_getinoquota(cp
);
1562 * We do the ubc_setsize before the hfs_truncate
1563 * since we'll be inside a transaction.
1565 if ((cp
->c_flag
& C_HARDLINK
) == 0 &&
1566 (!dataforkbusy
|| !rsrcforkbusy
)) {
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.
1573 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
&& !isbigfile
) {
1576 if (!rsrcforkbusy
&& rvp
) {
1577 ubc_setsize(rvp
, 0);
1579 hfs_lock(cp
, HFS_FORCE_LOCK
);
1581 struct cat_desc cndesc
;
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
;
1593 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1595 if (cat_lookup(hfsmp
, &cndesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
1596 hfs_systemfile_unlock(hfsmp
, lockflags
);
1601 hfs_systemfile_unlock(hfsmp
, lockflags
);
1604 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1609 if (!skip_reserve
) {
1611 * Reserve some space in the Catalog file.
1613 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, 0))) {
1619 /* Remove our entry from the namei cache. */
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
);
1627 * Truncate any non-busy forks. Busy forks will
1628 * get trucated when their vnode goes inactive.
1630 * Since we're already inside a transaction,
1631 * tell hfs_truncate to skip the ubc_setsize.
1633 * (Note: hard links are truncated in VOP_INACTIVE)
1635 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1636 int mode
= cp
->c_mode
;
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
);
1646 if (!rsrcforkbusy
&& rvp
) {
1647 cp
->c_mode
= 0; /* Suppress hfs_update */
1648 error
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
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).
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
;
1668 // if it was a hardlink we had to re-lookup the cnid
1669 desc
.cd_cnid
= real_cnid
;
1671 desc
.cd_cnid
= cp
->c_cnid
;
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
1682 if (cp
->c_flag
& C_HARDLINK
) {
1683 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1685 /* Delete the link record */
1686 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1688 /* Update the parent directory */
1689 if (dcp
->c_entries
> 0)
1691 if (dcp
->c_nlink
> 0)
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
);
1697 if (--cp
->c_nlink
< 1) {
1700 struct cat_desc to_desc
;
1701 struct cat_desc from_desc
;
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.
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
;
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
;
1725 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_privdir_desc
,
1726 &to_desc
, (struct cat_desc
*)NULL
);
1728 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1729 inodename
, delname
, cp
);
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
);
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
);
1743 hfs_systemfile_unlock(hfsmp
, lockflags
);
1747 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1749 } else if (dataforkbusy
|| rsrcforkbusy
|| isbigfile
) {
1751 struct cat_desc to_desc
;
1752 struct cat_desc todir_desc
;
1755 * Orphan this file (move to hidden directory).
1757 bzero(&todir_desc
, sizeof(todir_desc
));
1758 todir_desc
.cd_parentcnid
= 2;
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
;
1768 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1770 error
= cat_rename(hfsmp
, &desc
, &todir_desc
,
1771 &to_desc
, (struct cat_desc
*)NULL
);
1774 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1775 (void) cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1776 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1778 /* Update the parent directory */
1779 if (dcp
->c_entries
> 0)
1781 if (dcp
->c_nlink
> 0)
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
);
1787 /* Update the file's state */
1788 cp
->c_flag
|= C_DELETED
;
1789 cp
->c_ctime
= tv
.tv_sec
;
1791 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1793 hfs_systemfile_unlock(hfsmp
, lockflags
);
1797 } else /* Not busy */ {
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
);
1806 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1808 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
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
);
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
);
1821 /* Delete any attributes, ignore errors */
1822 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1824 /* Update the parent directory */
1825 if (dcp
->c_entries
> 0)
1827 if (dcp
->c_nlink
> 0)
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
);
1833 hfs_systemfile_unlock(hfsmp
, lockflags
);
1838 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1842 truncated
= 0; // because the catalog entry is gone
1843 cp
->c_flag
|= C_NOEXISTS
;
1844 cp
->c_touch_chgtime
= TRUE
; /* XXX needed ? */
1847 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1851 * All done with this cnode's descriptor...
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.
1858 cat_releasedesc(&cp
->c_desc
);
1860 HFS_KNOTE(dvp
, NOTE_WRITE
);
1864 cat_postflight(hfsmp
, &cookie
, 0);
1867 /* Commit the truncation to the catalog record */
1869 cp
->c_flag
|= C_FORCEUPDATE
;
1870 cp
->c_touch_chgtime
= TRUE
;
1871 cp
->c_touch_modtime
= TRUE
;
1872 (void) hfs_update(vp
, 0);
1876 hfs_end_transaction(hfsmp
);
1879 HFS_KNOTE(vp
, NOTE_DELETE
);
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
;
1890 __private_extern__
void
1891 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1893 if (&cp
->c_desc
== cdp
) {
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
;
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
);
1906 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1908 /* Cnode now owns the name buffer */
1909 cdp
->cd_nameptr
= 0;
1910 cdp
->cd_namelen
= 0;
1911 cdp
->cd_flags
&= ~CD_HASBUF
;
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
1923 * When the target is a directory, HFS must ensure that its empty.
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;
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
);
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;
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;
1965 error
= hfs_lockfour(VTOC(fdvp
), VTOC(fvp
), VTOC(tdvp
), tvp
? VTOC(tvp
) : NULL
,
1966 HFS_EXCLUSIVE_LOCK
);
1968 if (took_trunc_lock
)
1969 hfs_unlock_truncate(VTOC(tvp
));
1976 tcp
= tvp
? VTOC(tvp
) : NULL
;
1977 hfsmp
= VTOHFS(tdvp
);
1979 /* Check for a race against unlink. */
1980 if (fcp
->c_flag
& C_NOEXISTS
) {
1986 * The following edge case is caught here:
1987 * (to cannot be a descendent of from)
2000 if (tdcp
->c_parentcnid
== fcp
->c_cnid
) {
2006 * The following two edge cases are caught here:
2007 * (note tvp is not empty)
2020 if (tvp
&& vnode_isdir(tvp
) && (tcp
->c_entries
!= 0) && fvp
!= tvp
) {
2026 * The following edge case is caught here:
2027 * (the from child and parent are the same)
2040 * Make sure "from" vnode and its parent are changeable.
2042 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
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.
2053 * Note that checks for immutable and write access are done
2054 * by the call to hfs_removefile.
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)) ) {
2066 (void)hfs_getinoquota(tcp
);
2068 /* Preflighting done, take fvp out of the name space. */
2072 * When a file moves out of "Cleanup At Startup"
2073 * we can drop its NODUMP status.
2075 if ((fcp
->c_flags
& UF_NODUMP
) &&
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);
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
;
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
;
2099 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
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)
2108 if (fcp
->c_flag
& C_HARDLINK
) {
2109 struct cat_desc tmpdesc
;
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
;
2118 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2120 if (cat_lookup(hfsmp
, &tmpdesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
2121 hfs_systemfile_unlock(hfsmp
, lockflags
);
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
);
2131 * Reserve some space in the Catalog file.
2133 bzero(&cookie
, sizeof(cookie
));
2134 if ((error
= cat_preflight(hfsmp
, CAT_RENAME
+ CAT_DELETE
, &cookie
, p
))) {
2140 * If the destination exists then it may need to be removed.
2144 * When fvp matches tvp they must be case variants
2149 * If this a hard link with different parents
2150 * and its not a case variant then tvp should
2153 if (!((fcp
->c_flag
& C_HARDLINK
) &&
2155 (hfs_namecmp(fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
2156 tcnp
->cn_nameptr
, tcnp
->cn_namelen
) != 0)))) {
2163 if (vnode_isdir(tvp
))
2164 error
= hfs_removedir(tdvp
, tvp
, tcnp
, HFSRM_SKIP_RESERVE
);
2166 error
= hfs_removefile(tdvp
, tvp
, tcnp
, 0, HFSRM_SKIP_RESERVE
);
2175 * All done with tvp and fvp
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
);
2186 /* Invalidate negative cache entries in the destination directory */
2187 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
2188 cache_purge_negatives(tdvp
);
2190 /* Update cnode's catalog descriptor */
2191 replace_desc(fcp
, &out_desc
);
2192 fcp
->c_parentcnid
= tdcp
->c_cnid
;
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
));
2200 /* Update both parent directories. */
2204 if (fdcp
->c_nlink
> 0)
2206 if (fdcp
->c_entries
> 0)
2208 fdcp
->c_touch_chgtime
= TRUE
;
2209 fdcp
->c_touch_modtime
= TRUE
;
2211 fdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2212 (void) hfs_update(fdvp
, 0);
2214 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2215 tdcp
->c_touch_chgtime
= TRUE
;
2216 tdcp
->c_touch_modtime
= TRUE
;
2218 tdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2219 (void) hfs_update(tdvp
, 0);
2222 cat_postflight(hfsmp
, &cookie
, p
);
2225 hfs_end_transaction(hfsmp
);
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.
2232 HFS_KNOTE(fvp
, NOTE_RENAME
);
2233 HFS_KNOTE(fdvp
, NOTE_WRITE
);
2234 if (tdvp
!= fdvp
) HFS_KNOTE(tdvp
, NOTE_WRITE
);
2237 if (took_trunc_lock
)
2238 hfs_unlock_truncate(VTOC(tvp
));
2240 hfs_unlockfour(fdcp
, fcp
, tdcp
, tcp
);
2242 /* After tvp is removed the only acceptable error is EIO */
2243 if (error
&& tvp_deleted
)
2254 hfs_vnop_mkdir(struct vnop_mkdir_args
*ap
)
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
);
2263 * Create a symbolic link.
2266 hfs_vnop_symlink(struct vnop_symlink_args
*ap
)
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
;
2278 /* HFS standard disks don't support symbolic links */
2279 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
)
2282 /* Check for empty target name */
2283 if (ap
->a_target
[0] == 0)
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
))) {
2292 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2295 hfsmp
= VTOHFS(dvp
);
2296 len
= strlen(ap
->a_target
);
2299 (void)hfs_getinoquota(VTOC(vp
));
2302 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2308 * Allocate space for the link.
2310 * Since we're already inside a transaction,
2311 * tell hfs_truncate to skip the ubc_setsize.
2313 * Don't need truncate lock since a symlink is treated as a system file.
2315 error
= hfs_truncate(vp
, len
, IO_NOZEROFILL
, 1, ap
->a_context
);
2317 goto out
; /* XXX need to remove link */
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
),
2323 journal_modify_block_start(hfsmp
->jnl
, bp
);
2325 datap
= (char *)buf_dataptr(bp
);
2326 bzero(datap
, buf_size(bp
));
2327 bcopy(ap
->a_target
, datap
, len
);
2330 journal_modify_block_end(hfsmp
->jnl
, bp
);
2335 * We defered the ubc_setsize for hfs_truncate
2336 * since we were inside a transaction.
2338 * We don't need to drop the cnode lock here
2339 * since this is a symlink.
2341 ubc_setsize(vp
, len
);
2344 hfs_end_transaction(hfsmp
);
2346 hfs_unlock(VTOC(vp
));
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 ".." */
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 ".." */
2371 struct hfs_stddotentry std
;
2372 struct hfs_extdotentry ext
;
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:
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
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).
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.
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.
2403 hfs_vnop_readdir(ap
)
2404 struct vnop_readdir_args
/* {
2410 vfs_context_t a_context;
2413 struct vnode
*vp
= ap
->a_vp
;
2414 uio_t uio
= ap
->a_uio
;
2416 struct hfsmount
*hfsmp
;
2417 directoryhint_t
*dirhint
= NULL
;
2418 directoryhint_t localhint
;
2423 user_addr_t user_start
= 0;
2424 user_size_t user_len
= 0;
2432 cnid_t cnid_hint
= 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
);
2440 /* Sanity check the uio data. */
2441 if ((uio_iovcnt(uio
) > 1) ||
2442 (uio_resid(uio
) < (int)sizeof(struct dirent
))) {
2445 /* Note that the dirhint calls require an exclusive lock. */
2446 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2451 /* Pick up cnid hint (if any). */
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 */
2461 * Synthesize entries for "." and ".."
2464 hfs_dotentry_t dotentry
[2];
2468 struct hfs_extdotentry
*entry
= &dotentry
[0].ext
;
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;
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
);
2490 struct hfs_stddotentry
*entry
= &dotentry
[0].std
;
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] = '.';
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
);
2509 if ((error
= uiomove((caddr_t
)&dotentry
, uiosize
, uio
))) {
2515 /* If there are no real entries then we're done. */
2516 if (cp
->c_entries
== 0) {
2519 uio_setoffset(uio
, offset
);
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.
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.
2541 if (hfsmp
->jnl
&& uio_isuserspace(uio
)) {
2542 user_start
= uio_curriovbase(uio
);
2543 user_len
= uio_curriovlen(uio
);
2545 if ((error
= vslock(user_start
, user_len
)) != 0) {
2550 /* Convert offset into a catalog directory index. */
2551 index
= (offset
& HFS_INDEX_MASK
) - 2;
2552 tag
= offset
& ~HFS_INDEX_MASK
;
2554 /* Lock catalog during cat_findname and cat_getdirentries. */
2555 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
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 */
2566 cat_releasedesc(&localhint
.dh_desc
);
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
);
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;
2582 /* Pack the buffer with dirent entries. */
2583 error
= cat_getdirentries(hfsmp
, cp
->c_entries
, dirhint
, uio
, extended
, &items
, &eofflag
);
2585 hfs_systemfile_unlock(hfsmp
, lockflags
);
2591 /* Get index to the next item */
2594 if (items
>= (int)cp
->c_entries
) {
2598 /* Convert catalog directory index back into an offset. */
2600 tag
= (++cp
->c_dirhinttag
) << HFS_INDEX_BITS
;
2601 uio_setoffset(uio
, (index
+ 2) | tag
);
2602 dirhint
->dh_index
|= tag
;
2605 cp
->c_touch_acctime
= TRUE
;
2607 if (ap
->a_numdirent
) {
2608 if (startoffset
== 0)
2610 *ap
->a_numdirent
= items
;
2614 if (hfsmp
->jnl
&& user_start
) {
2615 vsunlock(user_start
, user_len
, TRUE
);
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
);
2624 if (ap
->a_eofflag
) {
2625 *ap
->a_eofflag
= eofflag
;
2627 if (dirhint
== &localhint
) {
2628 cat_releasedesc(&localhint
.dh_desc
);
2636 * Read contents of a symbolic link.
2639 hfs_vnop_readlink(ap
)
2640 struct vnop_readlink_args
/* {
2643 vfs_context_t a_context;
2646 struct vnode
*vp
= ap
->a_vp
;
2648 struct filefork
*fp
;
2651 if (!vnode_islnk(vp
))
2654 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2659 /* Zero length sym links are not allowed */
2660 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2661 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2666 /* Cache the path so we don't waste buffer cache resources */
2667 if (fp
->ff_symlinkptr
== NULL
) {
2668 struct buf
*bp
= NULL
;
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
);
2678 if (fp
->ff_symlinkptr
) {
2679 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2680 fp
->ff_symlinkptr
= NULL
;
2684 bcopy((char *)buf_dataptr(bp
), fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2686 if (VTOHFS(vp
)->jnl
&& (buf_flags(bp
) & B_LOCKED
) == 0) {
2687 buf_markinvalid(bp
); /* data no longer needed */
2691 error
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2694 * Keep track blocks read
2696 if ((VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) && (error
== 0)) {
2699 * If this file hasn't been seen since the start of
2700 * the current sampling period then start over.
2702 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
)
2703 VTOF(vp
)->ff_bytesread
= fp
->ff_size
;
2705 VTOF(vp
)->ff_bytesread
+= fp
->ff_size
;
2707 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2708 // cp->c_touch_acctime = TRUE;
2718 * Get configurable pathname variables.
2721 hfs_vnop_pathconf(ap
)
2722 struct vnop_pathconf_args
/* {
2726 vfs_context_t a_context;
2729 switch (ap
->a_name
) {
2731 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2734 *ap
->a_retval
= HFS_LINK_MAX
;
2737 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2738 *ap
->a_retval
= kHFSMaxFileNameChars
; /* 255 */
2740 *ap
->a_retval
= kHFSPlusMaxFileNameChars
; /* 31 */
2743 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2746 *ap
->a_retval
= PIPE_BUF
;
2748 case _PC_CHOWN_RESTRICTED
:
2754 case _PC_NAME_CHARS_MAX
:
2755 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2757 case _PC_CASE_SENSITIVE
:
2758 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_CASE_SENSITIVE
)
2763 case _PC_CASE_PRESERVING
:
2775 * Update a cnode's on-disk metadata.
2777 * If waitfor is set, then wait for the disk write of
2778 * the node to complete.
2780 * The cnode must be locked exclusive
2784 hfs_update(struct vnode
*vp
, __unused
int waitfor
)
2786 struct cnode
*cp
= VTOC(vp
);
2788 struct cat_fork
*dataforkp
= NULL
;
2789 struct cat_fork
*rsrcforkp
= NULL
;
2790 struct cat_fork datafork
;
2791 struct hfsmount
*hfsmp
;
2798 if (vnode_issystem(vp
) && (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)) {
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;
2809 hfs_touchtimes(hfsmp
, cp
);
2811 /* Nothing to update. */
2812 if ((cp
->c_flag
& (C_MODIFIED
| C_FORCEUPDATE
)) == 0) {
2817 dataforkp
= &cp
->c_datafork
->ff_data
;
2819 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
2822 * For delayed allocations updates are
2823 * postponed until an fsync or the file
2824 * gets written to disk.
2826 * Deleted files can defer meta data updates until inactive.
2828 * If we're ever called with the C_FORCEUPDATE flag though
2829 * we have to do the update.
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
;
2838 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2843 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
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.
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
);
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
;
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.
2876 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2878 /* XXX - waitfor is not enforced */
2879 error
= cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
2881 hfs_systemfile_unlock(hfsmp
, lockflags
);
2883 /* After the updates are finished, clear the flags */
2884 cp
->c_flag
&= ~(C_MODIFIED
| C_FORCEUPDATE
);
2886 hfs_end_transaction(hfsmp
);
2888 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2894 * Allocate a new node
2897 hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
, struct componentname
*cnp
,
2898 struct vnode_attr
*vap
, vfs_context_t ctx
)
2900 struct cnode
*cp
= NULL
;
2903 struct hfsmount
*hfsmp
;
2904 struct cat_desc in_desc
, out_desc
;
2905 struct cat_attr attr
;
2907 cat_cookie_t cookie
;
2909 int error
, started_tr
= 0, got_cookie
= 0;
2910 enum vtype vnodetype
;
2913 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
)))
2916 hfsmp
= VTOHFS(dvp
);
2919 out_desc
.cd_flags
= 0;
2920 out_desc
.cd_nameptr
= NULL
;
2922 mode
= MAKEIMODE(vap
->va_type
, vap
->va_mode
);
2924 if ((mode
& S_IFMT
) == 0)
2926 vnodetype
= IFTOVT(mode
);
2928 /* Check if were out of usable disk space. */
2929 if ((hfs_freeblks(hfsmp
, 1) <= 0) && (suser(vfs_context_ucred(ctx
), NULL
) != 0)) {
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 */
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
;
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
);
2956 /* Tag symlinks with a type and creator. */
2957 if (vnodetype
== VLNK
) {
2958 struct FndrFileInfo
*fip
;
2960 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
2961 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
2962 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
2964 if (cnp
->cn_flags
& ISWHITEOUT
)
2965 attr
.ca_flags
|= UF_OPAQUE
;
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;
2975 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2981 * Reserve some space in the Catalog file.
2983 * (we also add CAT_DELETE since our getnewvnode
2984 * request can cause an hfs_inactive call to
2985 * delete an unlinked file)
2987 if ((error
= cat_preflight(hfsmp
, CAT_CREATE
| CAT_DELETE
, &cookie
, 0))) {
2992 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2993 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
2995 /* Update the parent directory */
2996 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
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
);
3004 hfs_systemfile_unlock(hfsmp
, lockflags
);
3008 /* Invalidate negative cache entries in the directory */
3009 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
3010 cache_purge_negatives(dvp
);
3012 if (vnodetype
== VDIR
) {
3013 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
3015 HFS_KNOTE(dvp
, NOTE_WRITE
);
3018 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
3019 (dcp
->c_cnid
== kHFSRootFolderID
));
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).
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.
3034 hfs_end_transaction(hfsmp
);
3039 * Create a vnode for the object just created.
3041 * The cnode is locked on successful return.
3043 error
= hfs_getnewvnode(hfsmp
, dvp
, cnp
, &out_desc
, 0, &attr
, NULL
, &tvp
);
3048 //cache_enter(dvp, tvp, cnp);
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
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
))) {
3061 if (vnode_isdir(tvp
))
3062 (void) hfs_removedir(dvp
, tvp
, cnp
, 0);
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
);
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
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
);
3092 cat_releasedesc(&out_desc
);
3095 cat_postflight(hfsmp
, &cookie
, 0);
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.
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)) {
3111 * The parent of "Cleanup At Startup" should
3112 * have the ASCII name of the userid.
3114 if (hfs_vget(hfsmp
, dcp
->c_parentcnid
, &ddvp
, 0) == 0) {
3115 if (VTOC(ddvp
)->c_desc
.cd_nameptr
) {
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
;
3125 hfs_unlock(VTOC(ddvp
));
3132 if (error
== 0 && cp
!= NULL
) {
3136 hfs_end_transaction(hfsmp
);
3145 * WARNING - assumes caller has cnode lock.
3149 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, __unused
struct proc
*p
)
3152 struct cnode
*cp
= VTOC(vp
);
3156 if ((rvp
= cp
->c_rsrc_vp
)) {
3157 vid
= vnode_vid(rvp
);
3159 /* Use exising vnode */
3160 error
= vnode_getwithvid(rvp
, vid
);
3162 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
3165 printf("hfs_vgetrsrc: couldn't get"
3166 " resource fork for %s\n", name
);
3170 struct cat_fork rsrcfork
;
3171 struct componentname cn
;
3175 * Make sure cnode lock is exclusive, if not upgrade it.
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.
3180 if (cp
->c_lockowner
!= current_thread()) {
3182 * If the upgrade fails we loose the lock and
3183 * have to take the exclusive lock on our own.
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();
3190 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
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
);
3196 hfs_systemfile_unlock(hfsmp
, lockflags
);
3201 * Supply hfs_getnewvnode with a component name.
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
;
3213 cn
.cn_namelen
= sprintf(cn
.cn_nameptr
, "%s%s", cp
->c_desc
.cd_nameptr
, _PATH_RSRCFORKSPEC
);
3215 error
= hfs_getnewvnode(hfsmp
, vnode_parent(vp
), cn
.cn_pnbuf
? &cn
: NULL
,
3216 &cp
->c_desc
, 2, &cp
->c_attr
, &rsrcfork
, &rvp
);
3218 FREE_ZONE(cn
.cn_pnbuf
, cn
.cn_pnlen
, M_NAMEI
);
3229 filt_hfsdetach(struct knote
*kn
)
3233 vp
= (struct vnode
*)kn
->kn_hook
;
3234 if (vnode_getwithvid(vp
, kn
->kn_hookid
))
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
));
3249 filt_hfsread(struct knote
*kn
, long hint
)
3251 struct vnode
*vp
= (struct vnode
*)kn
->kn_hook
;
3255 if ((vnode_getwithvid(vp
, kn
->kn_hookid
) != 0)) {
3260 if (hint
== NOTE_REVOKE
) {
3262 * filesystem is gone, so set the EOF flag and schedule
3263 * the knote for deletion.
3265 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
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;
3276 return (kn
->kn_data
!= 0);
3281 filt_hfswrite(struct knote
*kn
, long hint
)
3286 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3289 vnode_put(kn
->kn_hook
);
3291 if (hint
== NOTE_REVOKE
) {
3293 * filesystem is gone, so set the EOF flag and schedule
3294 * the knote for deletion.
3297 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3305 filt_hfsvnode(struct knote
*kn
, long hint
)
3309 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3312 vnode_put(kn
->kn_hook
);
3314 if (kn
->kn_sfflags
& hint
)
3315 kn
->kn_fflags
|= hint
;
3316 if ((hint
== NOTE_REVOKE
)) {
3317 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3321 return (kn
->kn_fflags
!= 0);
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
};
3332 * Add a kqueue filter.
3336 struct vnop_kqfilt_add_args
/* {
3340 vfs_context_t a_context;
3343 struct vnode
*vp
= ap
->a_vp
;
3344 struct knote
*kn
= ap
->a_kn
;
3347 switch (kn
->kn_filter
) {
3349 if (vnode_isreg(vp
)) {
3350 kn
->kn_fop
= &hfsread_filtops
;
3356 if (vnode_isreg(vp
)) {
3357 kn
->kn_fop
= &hfswrite_filtops
;
3363 kn
->kn_fop
= &hfsvnode_filtops
;
3369 kn
->kn_hook
= (caddr_t
)vp
;
3370 kn
->kn_hookid
= vnode_vid(vp
);
3372 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
3374 KNOTE_ATTACH(&VTOC(vp
)->c_knotes
, kn
);
3375 hfs_unlock(VTOC(vp
));
3381 * Remove a kqueue filter
3384 hfs_vnop_kqfiltremove(ap
)
3385 struct vnop_kqfilt_remove_args
/* {
3388 vfs_context_t a_context;
3393 result
= ENOTSUP
; /* XXX */
3399 * Wrapper for special device reads
3403 struct vnop_read_args
/* {
3407 vfs_context_t a_context;
3413 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3414 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3418 * Wrapper for special device writes
3422 struct vnop_write_args
/* {
3426 vfs_context_t a_context;
3430 * Set update and change flags.
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
));
3438 * Wrapper for special device close
3440 * Update the times on the cnode then do device close.
3444 struct vnop_close_args
/* {
3447 vfs_context_t a_context;
3450 struct vnode
*vp
= ap
->a_vp
;
3453 if (vnode_isinuse(ap
->a_vp
, 1)) {
3454 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3456 hfs_touchtimes(VTOHFS(vp
), cp
);
3460 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3465 * Wrapper for fifo reads
3469 struct vnop_read_args
/* {
3473 vfs_context_t a_context;
3476 extern int (**fifo_vnodeop_p
)(void *);
3481 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3482 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3486 * Wrapper for fifo writes
3490 struct vnop_write_args
/* {
3494 vfs_context_t a_context;
3497 extern int (**fifo_vnodeop_p
)(void *);
3500 * Set update and change flags.
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
));
3508 * Wrapper for fifo close
3510 * Update the times on the cnode then do device close.
3514 struct vnop_close_args
/* {
3517 vfs_context_t a_context;
3520 extern int (**fifo_vnodeop_p
)(void *);
3521 struct vnode
*vp
= ap
->a_vp
;
3524 if (vnode_isinuse(ap
->a_vp
, 1)) {
3525 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3527 hfs_touchtimes(VTOHFS(vp
), cp
);
3531 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3535 * kqfilt_add wrapper for fifos.
3537 * Fall through to hfs kqfilt_add routines if needed
3540 hfsfifo_kqfilt_add(ap
)
3541 struct vnop_kqfilt_add_args
*ap
;
3543 extern int (**fifo_vnodeop_p
)(void *);
3546 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_add
), ap
);
3548 error
= hfs_vnop_kqfiltadd(ap
);
3553 * kqfilt_remove wrapper for fifos.
3555 * Fall through to hfs kqfilt_remove routines if needed
3558 hfsfifo_kqfilt_remove(ap
)
3559 struct vnop_kqfilt_remove_args
*ap
;
3561 extern int (**fifo_vnodeop_p
)(void *);
3564 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_remove
), ap
);
3566 error
= hfs_vnop_kqfiltremove(ap
);
3573 * Synchronize a file's in-core state with that on disk.
3577 struct vnop_fsync_args
/* {
3580 vfs_context_t a_context;
3583 struct vnode
* vp
= ap
->a_vp
;
3587 * We need to allow ENOENT lock errors since unlink
3588 * systenm call can call VNOP_FSYNC during vclean.
3590 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
3594 error
= hfs_fsync(vp
, ap
->a_waitfor
, 0, vfs_context_proc(ap
->a_context
));
3596 hfs_unlock(VTOC(vp
));
3600 /*****************************************************************************
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 */
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 */
3629 int (**hfs_vnodeop_p
)(void *);
3631 #define VOPFUNC int (*)(void *)
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
}
3682 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3683 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
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
}
3724 struct vnodeopv_desc hfs_specop_opv_desc
=
3725 { &hfs_specop_p
, hfs_specop_entries
};
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
}
3769 struct vnodeopv_desc hfs_fifoop_opv_desc
=
3770 { &hfs_fifoop_p
, hfs_fifoop_entries
};