2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/file_internal.h>
34 #include <sys/dirent.h>
37 #include <sys/mount.h>
38 #include <sys/vnode_internal.h>
39 #include <sys/malloc.h>
41 #include <sys/paths.h>
42 #include <sys/quota.h>
45 #include <sys/kauth.h>
46 #include <sys/uio_internal.h>
48 #include <miscfs/specfs/specdev.h>
49 #include <miscfs/fifofs/fifo.h>
50 #include <vfs/vfs_support.h>
51 #include <machine/spl.h>
53 #include <sys/kdebug.h>
54 #include <sys/sysctl.h>
57 #include "hfs_catalog.h"
58 #include "hfs_cnode.h"
60 #include "hfs_mount.h"
61 #include "hfs_quota.h"
62 #include "hfs_endian.h"
64 #include "hfscommon/headers/BTreesInternal.h"
65 #include "hfscommon/headers/FileMgrInternal.h"
67 #define MAKE_DELETED_NAME(NAME,FID) \
68 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
70 #define KNDETACH_VNLOCKED 0x00000001
72 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
75 /* Global vfs data structures for hfs */
77 /* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */
78 int always_do_fullfsync
= 0;
79 SYSCTL_INT (_kern
, OID_AUTO
, always_do_fullfsync
, CTLFLAG_RW
, &always_do_fullfsync
, 0, "always F_FULLFSYNC when fsync is called");
81 extern unsigned long strtoul(const char *, char **, int);
83 static int hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
,
84 struct componentname
*cnp
, struct vnode_attr
*vap
,
87 static int hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
);
89 static int hfs_removedir(struct vnode
*, struct vnode
*, struct componentname
*,
92 static int hfs_removefile(struct vnode
*, struct vnode
*, struct componentname
*,
95 static int hfs_vnop_close(struct vnop_close_args
*);
96 static int hfs_vnop_create(struct vnop_create_args
*);
97 static int hfs_vnop_exchange(struct vnop_exchange_args
*);
98 static int hfs_vnop_fsync(struct vnop_fsync_args
*);
99 static int hfs_vnop_mkdir(struct vnop_mkdir_args
*);
100 static int hfs_vnop_mknod(struct vnop_mknod_args
*);
101 static int hfs_vnop_getattr(struct vnop_getattr_args
*);
102 static int hfs_vnop_open(struct vnop_open_args
*);
103 static int hfs_vnop_readdir(struct vnop_readdir_args
*);
104 static int hfs_vnop_remove(struct vnop_remove_args
*);
105 static int hfs_vnop_rename(struct vnop_rename_args
*);
106 static int hfs_vnop_rmdir(struct vnop_rmdir_args
*);
107 static int hfs_vnop_symlink(struct vnop_symlink_args
*);
108 static int hfs_vnop_setattr(struct vnop_setattr_args
*);
110 /* Options for hfs_removedir and hfs_removefile */
111 #define HFSRM_SKIP_RESERVE 0x01
114 int hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
);
116 int hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
,
118 int hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
,
119 kauth_cred_t cred
, struct proc
*p
);
121 /*****************************************************************************
123 * Common Operations on vnodes
125 *****************************************************************************/
128 * Create a regular file.
131 hfs_vnop_create(struct vnop_create_args
*ap
)
133 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
137 * Make device special file.
140 hfs_vnop_mknod(struct vnop_mknod_args
*ap
)
142 struct vnode_attr
*vap
= ap
->a_vap
;
143 struct vnode
*dvp
= ap
->a_dvp
;
144 struct vnode
**vpp
= ap
->a_vpp
;
148 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
152 /* Create the vnode */
153 error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, vap
, ap
->a_context
);
158 cp
->c_touch_acctime
= TRUE
;
159 cp
->c_touch_chgtime
= TRUE
;
160 cp
->c_touch_modtime
= TRUE
;
162 if ((vap
->va_rdev
!= VNOVAL
) &&
163 (vap
->va_type
== VBLK
|| vap
->va_type
== VCHR
))
164 cp
->c_rdev
= vap
->va_rdev
;
170 * Open a file/directory.
173 hfs_vnop_open(struct vnop_open_args
*ap
)
175 struct vnode
*vp
= ap
->a_vp
;
181 * Files marked append-only must be opened for appending.
183 if ((VTOC(vp
)->c_flags
& APPEND
) && !vnode_isdir(vp
) &&
184 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
187 if (vnode_isreg(vp
) && !UBCINFOEXISTS(vp
))
188 return (EBUSY
); /* file is in use by the kernel */
190 /* Don't allow journal file to be opened externally. */
191 if (VTOC(vp
)->c_fileid
== VTOHFS(vp
)->hfs_jnlfileid
)
194 * On the first (non-busy) open of a fragmented
195 * file attempt to de-frag it (if its less than 20MB).
197 if ((VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
) ||
198 (VTOHFS(vp
)->jnl
== NULL
) ||
199 !vnode_isreg(vp
) || vnode_isinuse(vp
, 0)) {
203 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
207 fp
->ff_extents
[7].blockCount
!= 0 &&
208 fp
->ff_size
<= (20 * 1024 * 1024)) {
210 * Wait until system bootup is done (3 min).
213 if (tv
.tv_sec
> (60 * 3)) {
214 (void) hfs_relocate(vp
, VTOVCB(vp
)->nextAllocation
+ 4096,
215 vfs_context_ucred(ap
->a_context
),
216 vfs_context_proc(ap
->a_context
));
219 hfs_unlock(VTOC(vp
));
226 * Close a file/directory.
230 struct vnop_close_args
/* {
233 vfs_context_t a_context;
236 register struct vnode
*vp
= ap
->a_vp
;
237 register struct cnode
*cp
;
238 struct proc
*p
= vfs_context_proc(ap
->a_context
);
239 struct hfsmount
*hfsmp
;
242 if ( hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) != 0)
247 // if we froze the fs and we're exiting, then "thaw" the fs
248 if (hfsmp
->hfs_freezing_proc
== p
&& proc_exiting(p
)) {
249 hfsmp
->hfs_freezing_proc
= NULL
;
250 hfs_global_exclusive_lock_release(hfsmp
);
251 lck_rw_unlock_exclusive(&hfsmp
->hfs_insync
);
254 busy
= vnode_isinuse(vp
, 1);
257 hfs_touchtimes(VTOHFS(vp
), cp
);
259 if (vnode_isdir(vp
)) {
260 hfs_reldirhints(cp
, busy
);
261 } else if (vnode_issystem(vp
) && !busy
) {
270 * Get basic attributes.
273 hfs_vnop_getattr(struct vnop_getattr_args
*ap
)
275 struct vnode
*vp
= ap
->a_vp
;
276 struct vnode_attr
*vap
= ap
->a_vap
;
277 struct vnode
*rvp
= NULL
;
278 struct hfsmount
*hfsmp
;
283 if ((error
= hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
))) {
288 hfs_touchtimes(hfsmp
, cp
);
289 v_type
= vnode_vtype(vp
);
291 VATTR_RETURN(vap
, va_rdev
, (v_type
== VBLK
|| v_type
== VCHR
) ? cp
->c_rdev
: 0);
292 if (v_type
== VDIR
) {
293 if (VATTR_IS_ACTIVE(vap
, va_nlink
)) {
296 entries
= cp
->c_nlink
;
297 if (vnode_isvroot(vp
)) {
298 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
299 --entries
; /* hide private dir */
300 if (hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
301 entries
-= 2; /* hide the journal files */
303 VATTR_RETURN(vap
, va_nlink
, (uint64_t)entries
);
306 if (VATTR_IS_ACTIVE(vap
, va_nchildren
)) {
309 entries
= cp
->c_entries
;
310 if (vnode_isvroot(vp
)) {
311 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
312 --entries
; /* hide private dir */
314 entries
-= 2; /* hide the journal files */
316 VATTR_RETURN(vap
, va_nchildren
, entries
);
319 VATTR_RETURN(vap
, va_nlink
, (uint64_t)cp
->c_nlink
);
322 /* conditional because 64-bit arithmetic can be expensive */
323 if (VATTR_IS_ACTIVE(vap
, va_total_size
)) {
324 if (v_type
== VDIR
) {
325 VATTR_RETURN(vap
, va_total_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
327 uint64_t total_size
= 0;
330 if (cp
->c_datafork
) {
331 total_size
= cp
->c_datafork
->ff_size
;
334 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
335 /* hfs_vgetrsrc does not use struct proc - therefore passing NULL */
336 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, NULL
);
342 if (rcp
&& rcp
->c_rsrcfork
) {
343 total_size
+= rcp
->c_rsrcfork
->ff_size
;
347 VATTR_RETURN(vap
, va_total_size
, total_size
);
348 /* Include size of attibute data (extents), if any */
349 if (cp
->c_attrblks
) {
350 vap
->va_total_size
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
354 if (VATTR_IS_ACTIVE(vap
, va_total_alloc
)) {
355 if (v_type
== VDIR
) {
356 VATTR_RETURN(vap
, va_total_alloc
, 0);
358 VATTR_RETURN(vap
, va_total_alloc
, (uint64_t)cp
->c_blocks
* (uint64_t)hfsmp
->blockSize
);
359 /* Include size of attibute data (extents), if any */
360 if (cp
->c_attrblks
) {
361 vap
->va_total_alloc
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
365 /* XXX broken... if ask for "data size" of rsrc fork vp you get rsrc fork size! */
366 if (v_type
== VDIR
) {
367 VATTR_RETURN(vap
, va_data_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
369 VATTR_RETURN(vap
, va_data_size
, VTOF(vp
)->ff_size
);
371 if (VATTR_IS_ACTIVE(vap
, va_data_alloc
) && (v_type
!= VDIR
)) {
372 /* XXX do we need to account for ff_unallocblocks ? */
373 VATTR_RETURN(vap
, va_data_alloc
, (uint64_t)VTOF(vp
)->ff_blocks
* (uint64_t)hfsmp
->blockSize
);
375 /* XXX is this really a good 'optimal I/O size'? */
376 VATTR_RETURN(vap
, va_iosize
, hfsmp
->hfs_logBlockSize
);
377 VATTR_RETURN(vap
, va_uid
, cp
->c_uid
);
378 VATTR_RETURN(vap
, va_gid
, cp
->c_gid
);
379 VATTR_RETURN(vap
, va_mode
, cp
->c_mode
);
381 /* XXX is S_IFXATTR still needed ??? */
382 if (VNODE_IS_RSRC(vp
))
383 vap
->va_mode
|= S_IFXATTR
;
385 VATTR_RETURN(vap
, va_flags
, cp
->c_flags
);
388 * If the VFS wants extended security data, and we know that we
389 * don't have any (because it never told us it was setting any)
390 * then we can return the supported bit and no data. If we do
391 * have extended security, we can just leave the bit alone and
392 * the VFS will use the fallback path to fetch it.
394 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
395 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
396 vap
->va_acl
= KAUTH_FILESEC_NONE
;
397 VATTR_SET_SUPPORTED(vap
, va_acl
);
400 vap
->va_create_time
.tv_sec
= cp
->c_itime
;
401 vap
->va_create_time
.tv_nsec
= 0;
402 VATTR_SET_SUPPORTED(vap
, va_create_time
);
404 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
405 /* Access times are lazyily updated, get current time if needed */
406 if (cp
->c_touch_acctime
) {
410 vap
->va_access_time
.tv_sec
= tv
.tv_sec
;
412 vap
->va_access_time
.tv_sec
= cp
->c_atime
;
414 vap
->va_access_time
.tv_nsec
= 0;
415 VATTR_SET_SUPPORTED(vap
, va_access_time
);
417 vap
->va_modify_time
.tv_sec
= cp
->c_mtime
;
418 vap
->va_modify_time
.tv_nsec
= 0;
419 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
420 vap
->va_change_time
.tv_sec
= cp
->c_ctime
;
421 vap
->va_change_time
.tv_nsec
= 0;
422 VATTR_SET_SUPPORTED(vap
, va_change_time
);
423 vap
->va_backup_time
.tv_sec
= cp
->c_btime
;
424 vap
->va_backup_time
.tv_nsec
= 0;
425 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
428 * Exporting file IDs from HFS Plus:
430 * For "normal" files the c_fileid is the same value as the
431 * c_cnid. But for hard link files, they are different - the
432 * c_cnid belongs to the active directory entry (ie the link)
433 * and the c_fileid is for the actual inode (ie the data file).
435 * The stat call (getattr) uses va_fileid and the Carbon APIs,
436 * which are hardlink-ignorant, will ask for va_linkid.
438 VATTR_RETURN(vap
, va_fileid
, (uint64_t)cp
->c_fileid
);
439 VATTR_RETURN(vap
, va_linkid
, (uint64_t)cp
->c_cnid
);
440 VATTR_RETURN(vap
, va_parentid
, (uint64_t)cp
->c_parentcnid
);
441 VATTR_RETURN(vap
, va_fsid
, cp
->c_dev
);
442 VATTR_RETURN(vap
, va_filerev
, 0);
444 VATTR_RETURN(vap
, va_encoding
, cp
->c_encoding
);
446 /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
447 if (VATTR_IS_ACTIVE(vap
, va_name
) && !vnode_isvroot(vp
)) {
448 /* Return the name for ATTR_CMN_NAME */
449 if (cp
->c_desc
.cd_namelen
== 0) {
454 strncpy(vap
->va_name
, cp
->c_desc
.cd_nameptr
, MAXPATHLEN
);
455 vap
->va_name
[MAXPATHLEN
-1] = '\0';
456 VATTR_SET_SUPPORTED(vap
, va_name
);
469 struct vnop_setattr_args
/* {
471 struct vnode_attr *a_vap;
472 vfs_context_t a_context;
475 struct vnode_attr
*vap
= ap
->a_vap
;
476 struct vnode
*vp
= ap
->a_vp
;
477 struct cnode
*cp
= NULL
;
478 struct hfsmount
*hfsmp
;
479 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
480 struct proc
*p
= vfs_context_proc(ap
->a_context
);
487 /* Don't allow modification of the journal file. */
488 if (hfsmp
->hfs_jnlfileid
== VTOC(vp
)->c_fileid
) {
493 * File size change request.
494 * We are guaranteed that this is not a directory, and that
495 * the filesystem object is writeable.
497 VATTR_SET_SUPPORTED(vap
, va_data_size
);
498 if (VATTR_IS_ACTIVE(vap
, va_data_size
) && !vnode_islnk(vp
)) {
500 /* Take truncate lock before taking cnode lock. */
501 hfs_lock_truncate(VTOC(vp
), TRUE
);
502 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
503 hfs_unlock_truncate(VTOC(vp
));
508 error
= hfs_truncate(vp
, vap
->va_data_size
, vap
->va_vaflags
& 0xffff, 0, ap
->a_context
);
510 hfs_unlock_truncate(cp
);
515 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
521 * Owner/group change request.
522 * We are guaranteed that the new owner/group is valid and legal.
524 VATTR_SET_SUPPORTED(vap
, va_uid
);
525 VATTR_SET_SUPPORTED(vap
, va_gid
);
526 nuid
= VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: (uid_t
)VNOVAL
;
527 ngid
= VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: (gid_t
)VNOVAL
;
528 if (((nuid
!= (uid_t
)VNOVAL
) || (ngid
!= (gid_t
)VNOVAL
)) &&
529 ((error
= hfs_chown(vp
, nuid
, ngid
, cred
, p
)) != 0))
533 * Mode change request.
534 * We are guaranteed that the mode value is valid and that in
535 * conjunction with the owner and group, this change is legal.
537 VATTR_SET_SUPPORTED(vap
, va_mode
);
538 if (VATTR_IS_ACTIVE(vap
, va_mode
) &&
539 ((error
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
)) != 0))
544 * We are guaranteed that only flags allowed to change given the
545 * current securelevel are being changed.
547 VATTR_SET_SUPPORTED(vap
, va_flags
);
548 if (VATTR_IS_ACTIVE(vap
, va_flags
)) {
549 cp
->c_flags
= vap
->va_flags
;
550 cp
->c_touch_chgtime
= TRUE
;
554 * If the file's extended security data is being changed, we
555 * need to note the change. Note that because we don't store
556 * the data, we do not set the SUPPORTED bit; this will cause
557 * the VFS to use a fallback strategy.
559 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
560 /* Remember if any ACL data was set or cleared. */
561 if (vap
->va_acl
== NULL
) {
563 if (cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) {
564 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
565 cp
->c_touch_chgtime
= TRUE
;
569 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
570 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
571 cp
->c_touch_chgtime
= TRUE
;
579 VATTR_SET_SUPPORTED(vap
, va_create_time
);
580 VATTR_SET_SUPPORTED(vap
, va_access_time
);
581 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
582 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
583 VATTR_SET_SUPPORTED(vap
, va_change_time
);
584 if (VATTR_IS_ACTIVE(vap
, va_create_time
) ||
585 VATTR_IS_ACTIVE(vap
, va_access_time
) ||
586 VATTR_IS_ACTIVE(vap
, va_modify_time
) ||
587 VATTR_IS_ACTIVE(vap
, va_backup_time
)) {
590 if (VATTR_IS_ACTIVE(vap
, va_create_time
))
591 cp
->c_itime
= vap
->va_create_time
.tv_sec
;
592 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
593 cp
->c_atime
= vap
->va_access_time
.tv_sec
;
594 cp
->c_touch_acctime
= FALSE
;
596 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
597 cp
->c_mtime
= vap
->va_modify_time
.tv_sec
;
598 cp
->c_touch_modtime
= FALSE
;
599 cp
->c_touch_chgtime
= TRUE
;
602 * The utimes system call can reset the modification
603 * time but it doesn't know about HFS create times.
604 * So we need to ensure that the creation time is
605 * always at least as old as the modification time.
607 if ((VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
608 (cp
->c_cnid
!= kHFSRootFolderID
) &&
609 (cp
->c_mtime
< cp
->c_itime
)) {
610 cp
->c_itime
= cp
->c_mtime
;
613 if (VATTR_IS_ACTIVE(vap
, va_backup_time
))
614 cp
->c_btime
= vap
->va_backup_time
.tv_sec
;
615 cp
->c_flag
|= C_MODIFIED
;
621 VATTR_SET_SUPPORTED(vap
, va_encoding
);
622 if (VATTR_IS_ACTIVE(vap
, va_encoding
)) {
623 cp
->c_encoding
= vap
->va_encoding
;
624 hfs_setencodingbits(hfsmp
, cp
->c_encoding
);
628 if ((error
= hfs_update(vp
, TRUE
)) != 0)
630 HFS_KNOTE(vp
, NOTE_ATTRIB
);
639 * Change the mode on a file.
640 * cnode must be locked before calling.
644 hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
, struct proc
*p
)
646 register struct cnode
*cp
= VTOC(vp
);
649 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
652 // XXXdbg - don't allow modification of the journal or journal_info_block
653 if (VTOHFS(vp
)->jnl
&& cp
&& cp
->c_datafork
) {
654 struct HFSPlusExtentDescriptor
*extd
;
656 extd
= &cp
->c_datafork
->ff_extents
[0];
657 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
662 #if OVERRIDE_UNKNOWN_PERMISSIONS
663 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
) {
667 cp
->c_mode
&= ~ALLPERMS
;
668 cp
->c_mode
|= (mode
& ALLPERMS
);
669 cp
->c_touch_chgtime
= TRUE
;
676 hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
)
678 struct cnode
*cp
= VTOC(vp
);
683 * Disallow write attempts on read-only file systems;
684 * unless the file is a socket, fifo, or a block or
685 * character device resident on the file system.
687 switch (vnode_vtype(vp
)) {
691 if (VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
)
698 /* If immutable bit set, nobody gets to write it. */
699 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
702 /* Otherwise, user id 0 always gets access. */
703 if (!suser(cred
, NULL
))
706 /* Otherwise, check the owner. */
707 if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, false)) == 0)
708 return ((cp
->c_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
710 /* Otherwise, check the groups. */
711 if (kauth_cred_ismember_gid(cred
, cp
->c_gid
, &is_member
) == 0 && is_member
) {
712 return ((cp
->c_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
715 /* Otherwise, check everyone else. */
716 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
721 * Perform chown operation on cnode cp;
722 * code must be locked prior to call.
726 hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
, kauth_cred_t cred
,
729 register struct cnode
*cp
= VTOC(vp
);
739 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
742 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
)
745 if (uid
== (uid_t
)VNOVAL
)
747 if (gid
== (gid_t
)VNOVAL
)
750 #if 0 /* we are guaranteed that this is already the case */
752 * If we don't own the file, are trying to change the owner
753 * of the file, or are not a member of the target group,
754 * the caller must be superuser or the call fails.
756 if ((kauth_cred_getuid(cred
) != cp
->c_uid
|| uid
!= cp
->c_uid
||
758 (kauth_cred_ismember_gid(cred
, gid
, &is_member
) || !is_member
))) &&
759 (error
= suser(cred
, 0)))
766 if ((error
= hfs_getinoquota(cp
)))
769 dqrele(cp
->c_dquot
[USRQUOTA
]);
770 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
773 dqrele(cp
->c_dquot
[GRPQUOTA
]);
774 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
778 * Eventually need to account for (fake) a block per directory
779 * if (vnode_isdir(vp))
780 * change = VTOHFS(vp)->blockSize;
784 change
= (int64_t)(cp
->c_blocks
) * (int64_t)VTOVCB(vp
)->blockSize
;
785 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
);
786 (void) hfs_chkiq(cp
, -1, cred
, CHOWN
);
787 for (i
= 0; i
< MAXQUOTAS
; i
++) {
788 dqrele(cp
->c_dquot
[i
]);
789 cp
->c_dquot
[i
] = NODQUOT
;
795 if ((error
= hfs_getinoquota(cp
)) == 0) {
797 dqrele(cp
->c_dquot
[USRQUOTA
]);
798 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
801 dqrele(cp
->c_dquot
[GRPQUOTA
]);
802 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
804 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
805 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
808 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
810 for (i
= 0; i
< MAXQUOTAS
; i
++) {
811 dqrele(cp
->c_dquot
[i
]);
812 cp
->c_dquot
[i
] = NODQUOT
;
817 if (hfs_getinoquota(cp
) == 0) {
819 dqrele(cp
->c_dquot
[USRQUOTA
]);
820 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
823 dqrele(cp
->c_dquot
[GRPQUOTA
]);
824 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
826 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
827 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
828 (void) hfs_getinoquota(cp
);
832 if (hfs_getinoquota(cp
))
833 panic("hfs_chown: lost quota");
836 if (ouid
!= uid
|| ogid
!= gid
)
837 cp
->c_touch_chgtime
= TRUE
;
843 * The hfs_exchange routine swaps the fork data in two files by
844 * exchanging some of the information in the cnode. It is used
845 * to preserve the file ID when updating an existing file, in
846 * case the file is being tracked through its file ID. Typically
847 * its used after creating a new file during a safe-save.
850 hfs_vnop_exchange(ap
)
851 struct vnop_exchange_args
/* {
855 vfs_context_t a_context;
858 struct vnode
*from_vp
= ap
->a_fvp
;
859 struct vnode
*to_vp
= ap
->a_tvp
;
860 struct cnode
*from_cp
;
862 struct hfsmount
*hfsmp
;
863 struct cat_desc tempdesc
;
864 struct cat_attr tempattr
;
866 int error
= 0, started_tr
= 0, got_cookie
= 0;
869 /* The files must be on the same volume. */
870 if (vnode_mount(from_vp
) != vnode_mount(to_vp
))
873 if (from_vp
== to_vp
)
876 if ((error
= hfs_lockpair(VTOC(from_vp
), VTOC(to_vp
), HFS_EXCLUSIVE_LOCK
)))
879 from_cp
= VTOC(from_vp
);
881 hfsmp
= VTOHFS(from_vp
);
883 /* Only normal files can be exchanged. */
884 if (!vnode_isreg(from_vp
) || !vnode_isreg(to_vp
) ||
885 (from_cp
->c_flag
& C_HARDLINK
) || (to_cp
->c_flag
& C_HARDLINK
) ||
886 VNODE_IS_RSRC(from_vp
) || VNODE_IS_RSRC(to_vp
)) {
891 // XXXdbg - don't allow modification of the journal or journal_info_block
893 struct HFSPlusExtentDescriptor
*extd
;
895 if (from_cp
->c_datafork
) {
896 extd
= &from_cp
->c_datafork
->ff_extents
[0];
897 if (extd
->startBlock
== VTOVCB(from_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
903 if (to_cp
->c_datafork
) {
904 extd
= &to_cp
->c_datafork
->ff_extents
[0];
905 if (extd
->startBlock
== VTOVCB(to_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
912 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
918 * Reserve some space in the Catalog file.
920 bzero(&cookie
, sizeof(cookie
));
921 if ((error
= cat_preflight(hfsmp
, CAT_EXCHANGE
, &cookie
, vfs_context_proc(ap
->a_context
)))) {
926 /* The backend code always tries to delete the virtual
927 * extent id for exchanging files so we neeed to lock
928 * the extents b-tree.
930 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_EXTENTS
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
932 /* Do the exchange */
933 error
= ExchangeFileIDs(hfsmp
,
934 from_cp
->c_desc
.cd_nameptr
,
935 to_cp
->c_desc
.cd_nameptr
,
936 from_cp
->c_parentcnid
,
940 hfs_systemfile_unlock(hfsmp
, lockflags
);
943 * Note that we don't need to exchange any extended attributes
944 * since the attributes are keyed by file ID.
947 if (error
!= E_NONE
) {
948 error
= MacToVFSError(error
);
952 /* Purge the vnodes from the name cache */
954 cache_purge(from_vp
);
958 /* Save a copy of from attributes before swapping. */
959 bcopy(&from_cp
->c_desc
, &tempdesc
, sizeof(struct cat_desc
));
960 bcopy(&from_cp
->c_attr
, &tempattr
, sizeof(struct cat_attr
));
963 * Swap the descriptors and all non-fork related attributes.
964 * (except the modify date)
966 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
969 from_cp
->c_fileid
= from_cp
->c_cnid
;
970 from_cp
->c_itime
= to_cp
->c_itime
;
971 from_cp
->c_btime
= to_cp
->c_btime
;
972 from_cp
->c_atime
= to_cp
->c_atime
;
973 from_cp
->c_ctime
= to_cp
->c_ctime
;
974 from_cp
->c_gid
= to_cp
->c_gid
;
975 from_cp
->c_uid
= to_cp
->c_uid
;
976 from_cp
->c_flags
= to_cp
->c_flags
;
977 from_cp
->c_mode
= to_cp
->c_mode
;
978 from_cp
->c_attr
.ca_recflags
= to_cp
->c_attr
.ca_recflags
;
979 bcopy(to_cp
->c_finderinfo
, from_cp
->c_finderinfo
, 32);
981 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
983 to_cp
->c_fileid
= to_cp
->c_cnid
;
984 to_cp
->c_itime
= tempattr
.ca_itime
;
985 to_cp
->c_btime
= tempattr
.ca_btime
;
986 to_cp
->c_atime
= tempattr
.ca_atime
;
987 to_cp
->c_ctime
= tempattr
.ca_ctime
;
988 to_cp
->c_gid
= tempattr
.ca_gid
;
989 to_cp
->c_uid
= tempattr
.ca_uid
;
990 to_cp
->c_flags
= tempattr
.ca_flags
;
991 to_cp
->c_mode
= tempattr
.ca_mode
;
992 to_cp
->c_attr
.ca_recflags
= tempattr
.ca_recflags
;
993 bcopy(tempattr
.ca_finderinfo
, to_cp
->c_finderinfo
, 32);
995 /* Rehash the cnodes using their new file IDs */
996 hfs_chash_rehash(from_cp
, to_cp
);
999 * When a file moves out of "Cleanup At Startup"
1000 * we can drop its NODUMP status.
1002 if ((from_cp
->c_flags
& UF_NODUMP
) &&
1003 (from_cp
->c_parentcnid
!= to_cp
->c_parentcnid
)) {
1004 from_cp
->c_flags
&= ~UF_NODUMP
;
1005 from_cp
->c_touch_chgtime
= TRUE
;
1007 if ((to_cp
->c_flags
& UF_NODUMP
) &&
1008 (to_cp
->c_parentcnid
!= from_cp
->c_parentcnid
)) {
1009 to_cp
->c_flags
&= ~UF_NODUMP
;
1010 to_cp
->c_touch_chgtime
= TRUE
;
1013 HFS_KNOTE(from_vp
, NOTE_ATTRIB
);
1014 HFS_KNOTE(to_vp
, NOTE_ATTRIB
);
1018 cat_postflight(hfsmp
, &cookie
, vfs_context_proc(ap
->a_context
));
1021 hfs_end_transaction(hfsmp
);
1024 hfs_unlockpair(from_cp
, to_cp
);
1030 * cnode must be locked
1034 hfs_fsync(struct vnode
*vp
, int waitfor
, int fullsync
, struct proc
*p
)
1036 struct cnode
*cp
= VTOC(vp
);
1037 struct filefork
*fp
= NULL
;
1039 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1043 int took_trunc_lock
= 0;
1045 wait
= (waitfor
== MNT_WAIT
);
1047 /* HFS directories don't have any data blocks. */
1048 if (vnode_isdir(vp
))
1052 * For system files flush the B-tree header and
1053 * for regular files write out any clusters
1055 if (vnode_issystem(vp
)) {
1056 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1058 if (hfsmp
->jnl
== NULL
) {
1059 BTFlushPath(VTOF(vp
));
1062 } else if (UBCINFOEXISTS(vp
)) {
1064 hfs_lock_truncate(cp
, TRUE
);
1065 took_trunc_lock
= 1;
1067 /* Don't hold cnode lock when calling into cluster layer. */
1068 (void) cluster_push(vp
, 0);
1070 hfs_lock(cp
, HFS_FORCE_LOCK
);
1073 * When MNT_WAIT is requested and the zero fill timeout
1074 * has expired then we must explicitly zero out any areas
1075 * that are currently marked invalid (holes).
1077 * Files with NODUMP can bypass zero filling here.
1079 if ((wait
|| (cp
->c_flag
& C_ZFWANTSYNC
)) &&
1080 ((cp
->c_flags
& UF_NODUMP
) == 0) &&
1081 UBCINFOEXISTS(vp
) && (fp
= VTOF(vp
)) &&
1082 cp
->c_zftimeout
!= 0) {
1084 if (tv
.tv_sec
< cp
->c_zftimeout
) {
1085 /* Remember that a force sync was requested. */
1086 cp
->c_flag
|= C_ZFWANTSYNC
;
1089 if (!took_trunc_lock
) {
1091 hfs_lock_truncate(cp
, TRUE
);
1092 hfs_lock(cp
, HFS_FORCE_LOCK
);
1093 took_trunc_lock
= 1;
1096 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
1097 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
1098 off_t start
= invalid_range
->rl_start
;
1099 off_t end
= invalid_range
->rl_end
;
1101 /* The range about to be written must be validated
1102 * first, so that VNOP_BLOCKMAP() will return the
1103 * appropriate mapping for the cluster code:
1105 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1107 /* Don't hold cnode lock when calling into cluster layer. */
1109 (void) cluster_write(vp
, (struct uio
*) 0,
1110 fp
->ff_size
, end
+ 1, start
, (off_t
)0,
1111 IO_HEADZEROFILL
| IO_NOZERODIRTY
| IO_NOCACHE
);
1112 hfs_lock(cp
, HFS_FORCE_LOCK
);
1113 cp
->c_flag
|= C_MODIFIED
;
1116 (void) cluster_push(vp
, 0);
1117 hfs_lock(cp
, HFS_FORCE_LOCK
);
1119 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1120 cp
->c_zftimeout
= 0;
1123 if (took_trunc_lock
)
1124 hfs_unlock_truncate(cp
);
1127 * if we have a journal and if journal_active() returns != 0 then the
1128 * we shouldn't do anything to a locked block (because it is part
1129 * of a transaction). otherwise we'll just go through the normal
1130 * code path and flush the buffer. note journal_active() can return
1131 * -1 if the journal is invalid -- however we still need to skip any
1132 * locked blocks as they get cleaned up when we finish the transaction
1133 * or close the journal.
1135 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1137 lockflag
= BUF_SKIP_LOCKED
;
1142 * Flush all dirty buffers associated with a vnode.
1144 buf_flushdirtyblks(vp
, wait
, lockflag
, "hfs_fsync");
1147 if (vnode_isreg(vp
) && vnode_issystem(vp
)) {
1148 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1150 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1152 cp
->c_touch_acctime
= FALSE
;
1153 cp
->c_touch_chgtime
= FALSE
;
1154 cp
->c_touch_modtime
= FALSE
;
1155 } else if ( !(vp
->v_flag
& VSWAP
) ) /* User file */ {
1156 retval
= hfs_update(vp
, wait
);
1158 /* When MNT_WAIT is requested push out any delayed meta data */
1159 if ((retval
== 0) && wait
&& cp
->c_hint
&&
1160 !ISSET(cp
->c_flag
, C_DELETED
| C_NOEXISTS
)) {
1161 hfs_metasync(VTOHFS(vp
), (daddr64_t
)cp
->c_hint
, p
);
1164 // make sure that we've really been called from the user
1165 // fsync() and if so push out any pending transactions
1166 // that this file might is a part of (and get them on
1168 if (fullsync
|| always_do_fullfsync
) {
1170 journal_flush(hfsmp
->jnl
);
1172 /* XXX need to pass context! */
1173 VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, NULL
);
1182 /* Sync an hfs catalog b-tree node */
1184 hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
)
1190 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1192 // XXXdbg - don't need to do this on a journaled volume
1197 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1199 * Look for a matching node that has been delayed
1200 * but is not part of a set (B_LOCKED).
1202 * BLK_ONLYVALID causes buf_getblk to return a
1203 * buf_t for the daddr64_t specified only if it's
1204 * currently resident in the cache... the size
1205 * parameter to buf_getblk is ignored when this flag
1208 bp
= buf_getblk(vp
, node
, 0, 0, 0, BLK_META
| BLK_ONLYVALID
);
1211 if ((buf_flags(bp
) & (B_LOCKED
| B_DELWRI
)) == B_DELWRI
)
1212 (void) VNOP_BWRITE(bp
);
1217 hfs_systemfile_unlock(hfsmp
, lockflags
);
1225 hfs_btsync_callback(struct buf
*bp
, void *dummy
)
1227 buf_clearflags(bp
, B_LOCKED
);
1228 (void) buf_bawrite(bp
);
1230 return(BUF_CLAIMED
);
1236 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1238 struct cnode
*cp
= VTOC(vp
);
1242 if (sync_transaction
)
1243 flags
|= BUF_SKIP_NONLOCKED
;
1245 * Flush all dirty buffers associated with b-tree.
1247 buf_iterate(vp
, hfs_btsync_callback
, flags
, 0);
1250 if (vnode_issystem(vp
) && (VTOF(vp
)->fcbBTCBPtr
!= NULL
))
1251 (void) BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1252 cp
->c_touch_acctime
= FALSE
;
1253 cp
->c_touch_chgtime
= FALSE
;
1254 cp
->c_touch_modtime
= FALSE
;
1260 * Remove a directory.
1264 struct vnop_rmdir_args
/* {
1265 struct vnode *a_dvp;
1267 struct componentname *a_cnp;
1268 vfs_context_t a_context;
1271 struct vnode
*dvp
= ap
->a_dvp
;
1272 struct vnode
*vp
= ap
->a_vp
;
1275 if (!vnode_isdir(vp
)) {
1281 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1284 error
= hfs_removedir(dvp
, vp
, ap
->a_cnp
, 0);
1286 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1292 * Remove a directory
1294 * Both dvp and vp cnodes are locked
1297 hfs_removedir(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1300 vfs_context_t ctx
= cnp
->cn_context
;
1301 struct proc
*p
= vfs_context_proc(ctx
);
1304 struct hfsmount
* hfsmp
;
1305 struct cat_desc desc
;
1306 cat_cookie_t cookie
;
1308 int error
= 0, started_tr
= 0, got_cookie
= 0;
1315 return (EINVAL
); /* cannot remove "." */
1318 (void)hfs_getinoquota(cp
);
1320 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1325 if (!skip_reserve
) {
1327 * Reserve some space in the Catalog file.
1329 bzero(&cookie
, sizeof(cookie
));
1330 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
))) {
1337 * Verify the directory is empty (and valid).
1338 * (Rmdir ".." won't be valid since
1339 * ".." will contain a reference to
1340 * the current directory and thus be
1343 if (cp
->c_entries
!= 0) {
1347 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1352 if (cp
->c_entries
> 0)
1353 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1355 /* Remove the entry from the namei cache: */
1359 * Protect against a race with rename by using the component
1360 * name passed in and parent id from dvp (instead of using
1361 * the cp->c_desc which may have changed).
1363 bzero(&desc
, sizeof(desc
));
1364 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1365 desc
.cd_namelen
= cnp
->cn_namelen
;
1366 desc
.cd_parentcnid
= dcp
->c_cnid
;
1367 desc
.cd_cnid
= cp
->c_cnid
;
1369 /* Remove entry from catalog */
1370 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1371 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1373 /* Delete any attributes, ignore errors */
1374 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1376 hfs_systemfile_unlock(hfsmp
, lockflags
);
1382 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1385 /* The parent lost a child */
1386 if (dcp
->c_entries
> 0)
1388 if (dcp
->c_nlink
> 0)
1390 dcp
->c_touch_chgtime
= TRUE
;
1391 dcp
->c_touch_modtime
= TRUE
;
1393 dcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - don't screw around, force this guy out
1395 (void) hfs_update(dvp
, 0);
1396 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
1398 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1400 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1401 cp
->c_flag
|= C_NOEXISTS
;
1403 HFS_KNOTE(vp
, NOTE_DELETE
);
1406 cat_postflight(hfsmp
, &cookie
, p
);
1409 hfs_end_transaction(hfsmp
);
1417 * Remove a file or link.
1421 struct vnop_remove_args
/* {
1422 struct vnode *a_dvp;
1424 struct componentname *a_cnp;
1426 vfs_context_t a_context;
1429 struct vnode
*dvp
= ap
->a_dvp
;
1430 struct vnode
*vp
= ap
->a_vp
;
1437 hfs_lock_truncate(VTOC(vp
), TRUE
);
1439 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1442 error
= hfs_removefile(dvp
, vp
, ap
->a_cnp
, ap
->a_flags
, 0);
1444 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1446 hfs_unlock_truncate(VTOC(vp
));
1452 hfs_removefile_callback(struct buf
*bp
, void *hfsmp
) {
1454 if ( !(buf_flags(bp
) & B_META
))
1455 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp
);
1457 * it's part of the current transaction, kill it.
1459 journal_kill_block(((struct hfsmount
*)hfsmp
)->jnl
, bp
);
1461 return (BUF_CLAIMED
);
1467 * Similar to hfs_vnop_remove except there are additional options.
1469 * Requires cnode and truncate locks to be held.
1472 hfs_removefile(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1473 int flags
, int skip_reserve
)
1475 struct vnode
*rvp
= NULL
;
1478 struct hfsmount
*hfsmp
;
1479 struct cat_desc desc
;
1481 vfs_context_t ctx
= cnp
->cn_context
;
1482 int dataforkbusy
= 0;
1483 int rsrcforkbusy
= 0;
1485 cat_cookie_t cookie
;
1488 int started_tr
= 0, got_cookie
= 0;
1490 cnid_t real_cnid
= 0;
1492 /* Directories should call hfs_rmdir! */
1493 if (vnode_isdir(vp
)) {
1501 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
1505 // if it's not a hardlink, check that the parent
1506 // cnid is the same as the directory cnid
1507 if ( (cp
->c_flag
& C_HARDLINK
) == 0
1508 && (cp
->c_parentcnid
!= hfsmp
->hfs_privdir_desc
.cd_cnid
)
1509 && (cp
->c_parentcnid
!= dcp
->c_cnid
)) {
1514 /* Make sure a remove is permitted */
1515 if (VNODE_IS_RSRC(vp
)) {
1521 * Aquire a vnode for a non-empty resource fork.
1522 * (needed for hfs_truncate)
1524 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1525 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, 0);
1530 // XXXdbg - don't allow deleting the journal or journal_info_block
1531 if (hfsmp
->jnl
&& cp
->c_datafork
) {
1532 struct HFSPlusExtentDescriptor
*extd
;
1534 extd
= &cp
->c_datafork
->ff_extents
[0];
1535 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
1542 * Check if this file is being used.
1544 if (vnode_isinuse(vp
, 0))
1546 if (rvp
&& vnode_isinuse(rvp
, 0))
1549 // need this to check if we have to break the deletion
1550 // into multiple pieces
1551 isbigfile
= (VTOC(vp
)->c_datafork
->ff_size
>= HFS_BIGFILE_SIZE
);
1554 * Carbon semantics prohibit deleting busy files.
1555 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1557 if (dataforkbusy
|| rsrcforkbusy
) {
1558 if ((flags
& VNODE_REMOVE_NODELETEBUSY
) ||
1559 (hfsmp
->hfs_privdir_desc
.cd_cnid
== 0)) {
1566 (void)hfs_getinoquota(cp
);
1570 * We do the ubc_setsize before the hfs_truncate
1571 * since we'll be inside a transaction.
1573 if ((cp
->c_flag
& C_HARDLINK
) == 0 &&
1574 (!dataforkbusy
|| !rsrcforkbusy
)) {
1576 * A ubc_setsize can cause a pagein so defer it
1577 * until after the cnode lock is dropped. The
1578 * cnode lock cannot be dropped/reacquired here
1579 * since we might already hold the journal lock.
1581 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
&& !isbigfile
) {
1582 cp
->c_flag
|= C_NEED_DATA_SETSIZE
;
1584 if (!rsrcforkbusy
&& rvp
) {
1585 cp
->c_flag
|= C_NEED_RSRC_SETSIZE
;
1588 struct cat_desc cndesc
;
1590 // for hard links, re-lookup the name that was passed
1591 // in so we get the correct cnid for the name (as
1592 // opposed to the c_cnid in the cnode which could have
1593 // been changed before this node got locked).
1594 bzero(&cndesc
, sizeof(cndesc
));
1595 cndesc
.cd_nameptr
= cnp
->cn_nameptr
;
1596 cndesc
.cd_namelen
= cnp
->cn_namelen
;
1597 cndesc
.cd_parentcnid
= VTOC(dvp
)->c_cnid
;
1598 cndesc
.cd_hint
= VTOC(dvp
)->c_childhint
;
1600 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1602 if (cat_lookup(hfsmp
, &cndesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
1603 hfs_systemfile_unlock(hfsmp
, lockflags
);
1608 hfs_systemfile_unlock(hfsmp
, lockflags
);
1611 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1616 if (!skip_reserve
) {
1618 * Reserve some space in the Catalog file.
1620 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, 0))) {
1626 /* Remove our entry from the namei cache. */
1629 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1630 if (hfsmp
->jnl
&& vnode_islnk(vp
))
1631 buf_iterate(vp
, hfs_removefile_callback
, BUF_SKIP_NONLOCKED
, (void *)hfsmp
);
1634 * Truncate any non-busy forks. Busy forks will
1635 * get trucated when their vnode goes inactive.
1637 * Since we're already inside a transaction,
1638 * tell hfs_truncate to skip the ubc_setsize.
1640 * (Note: hard links are truncated in VOP_INACTIVE)
1642 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1643 int mode
= cp
->c_mode
;
1645 if (!dataforkbusy
&& !isbigfile
&& cp
->c_datafork
->ff_blocks
!= 0) {
1646 cp
->c_mode
= 0; /* Suppress hfs_update */
1647 error
= hfs_truncate(vp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1653 if (!rsrcforkbusy
&& rvp
) {
1654 cp
->c_mode
= 0; /* Suppress hfs_update */
1655 error
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1664 * Protect against a race with rename by using the component
1665 * name passed in and parent id from dvp (instead of using
1666 * the cp->c_desc which may have changed).
1669 desc
.cd_encoding
= cp
->c_desc
.cd_encoding
;
1670 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1671 desc
.cd_namelen
= cnp
->cn_namelen
;
1672 desc
.cd_parentcnid
= dcp
->c_cnid
;
1673 desc
.cd_hint
= cp
->c_desc
.cd_hint
;
1675 // if it was a hardlink we had to re-lookup the cnid
1676 desc
.cd_cnid
= real_cnid
;
1678 desc
.cd_cnid
= cp
->c_cnid
;
1683 * There are 3 remove cases to consider:
1684 * 1. File is a hardlink ==> remove the link
1685 * 2. File is busy (in use) ==> move/rename the file
1686 * 3. File is not in use ==> remove the file
1689 if (cp
->c_flag
& C_HARDLINK
) {
1690 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1692 /* Delete the link record */
1693 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1695 /* Update the parent directory */
1696 if (dcp
->c_entries
> 0)
1698 if (dcp
->c_nlink
> 0)
1700 dcp
->c_ctime
= tv
.tv_sec
;
1701 dcp
->c_mtime
= tv
.tv_sec
;
1702 (void ) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1704 if (--cp
->c_nlink
< 1) {
1707 struct cat_desc to_desc
;
1708 struct cat_desc from_desc
;
1711 * This is now esentially an open deleted file.
1712 * Rename it to reflect this state which makes
1713 * orphan file cleanup easier (see hfs_remove_orphans).
1714 * Note: a rename failure here is not fatal.
1716 MAKE_INODE_NAME(inodename
, cp
->c_rdev
);
1717 bzero(&from_desc
, sizeof(from_desc
));
1718 from_desc
.cd_nameptr
= inodename
;
1719 from_desc
.cd_namelen
= strlen(inodename
);
1720 from_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1721 from_desc
.cd_flags
= 0;
1722 from_desc
.cd_cnid
= cp
->c_fileid
;
1724 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1725 bzero(&to_desc
, sizeof(to_desc
));
1726 to_desc
.cd_nameptr
= delname
;
1727 to_desc
.cd_namelen
= strlen(delname
);
1728 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1729 to_desc
.cd_flags
= 0;
1730 to_desc
.cd_cnid
= cp
->c_fileid
;
1732 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_privdir_desc
,
1733 &to_desc
, (struct cat_desc
*)NULL
);
1735 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1736 inodename
, delname
, cp
);
1739 /* Update the file's state */
1740 cp
->c_flag
|= C_DELETED
;
1741 cp
->c_ctime
= tv
.tv_sec
;
1742 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1745 /* Update the file's state */
1746 cp
->c_ctime
= tv
.tv_sec
;
1747 (void) cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, NULL
, NULL
);
1750 hfs_systemfile_unlock(hfsmp
, lockflags
);
1754 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1756 } else if (dataforkbusy
|| rsrcforkbusy
|| isbigfile
) {
1758 struct cat_desc to_desc
;
1759 struct cat_desc todir_desc
;
1762 * Orphan this file (move to hidden directory).
1764 bzero(&todir_desc
, sizeof(todir_desc
));
1765 todir_desc
.cd_parentcnid
= 2;
1767 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1768 bzero(&to_desc
, sizeof(to_desc
));
1769 to_desc
.cd_nameptr
= delname
;
1770 to_desc
.cd_namelen
= strlen(delname
);
1771 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1772 to_desc
.cd_flags
= 0;
1773 to_desc
.cd_cnid
= cp
->c_cnid
;
1775 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1777 error
= cat_rename(hfsmp
, &desc
, &todir_desc
,
1778 &to_desc
, (struct cat_desc
*)NULL
);
1781 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1782 (void) cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1783 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1785 /* Update the parent directory */
1786 if (dcp
->c_entries
> 0)
1788 if (dcp
->c_nlink
> 0)
1790 dcp
->c_ctime
= tv
.tv_sec
;
1791 dcp
->c_mtime
= tv
.tv_sec
;
1792 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1794 /* Update the file's state */
1795 cp
->c_flag
|= C_DELETED
;
1796 cp
->c_ctime
= tv
.tv_sec
;
1798 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1800 hfs_systemfile_unlock(hfsmp
, lockflags
);
1804 } else /* Not busy */ {
1806 if (cp
->c_blocks
> 0) {
1807 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1808 cp
->c_desc
.cd_nameptr
);
1813 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1815 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1817 if (error
&& error
!= ENXIO
&& error
!= ENOENT
&& truncated
) {
1818 if ((cp
->c_datafork
&& cp
->c_datafork
->ff_size
!= 0) ||
1819 (cp
->c_rsrcfork
&& cp
->c_rsrcfork
->ff_size
!= 0)) {
1820 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1821 error
, cp
->c_datafork
->ff_size
, cp
->c_rsrcfork
->ff_size
);
1823 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1824 cp
->c_desc
.cd_nameptr
, cp
->c_attr
.ca_fileid
, error
);
1828 /* Delete any attributes, ignore errors */
1829 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1831 /* Update the parent directory */
1832 if (dcp
->c_entries
> 0)
1834 if (dcp
->c_nlink
> 0)
1836 dcp
->c_ctime
= tv
.tv_sec
;
1837 dcp
->c_mtime
= tv
.tv_sec
;
1838 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1840 hfs_systemfile_unlock(hfsmp
, lockflags
);
1845 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1849 truncated
= 0; // because the catalog entry is gone
1850 cp
->c_flag
|= C_NOEXISTS
;
1851 cp
->c_touch_chgtime
= TRUE
; /* XXX needed ? */
1854 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1858 * All done with this cnode's descriptor...
1860 * Note: all future catalog calls for this cnode must be
1861 * by fileid only. This is OK for HFS (which doesn't have
1862 * file thread records) since HFS doesn't support hard
1863 * links or the removal of busy files.
1865 cat_releasedesc(&cp
->c_desc
);
1867 HFS_KNOTE(dvp
, NOTE_WRITE
);
1871 cat_postflight(hfsmp
, &cookie
, 0);
1874 /* Commit the truncation to the catalog record */
1876 cp
->c_flag
|= C_FORCEUPDATE
;
1877 cp
->c_touch_chgtime
= TRUE
;
1878 cp
->c_touch_modtime
= TRUE
;
1879 (void) hfs_update(vp
, 0);
1883 hfs_end_transaction(hfsmp
);
1886 HFS_KNOTE(vp
, NOTE_DELETE
);
1888 HFS_KNOTE(rvp
, NOTE_DELETE
);
1889 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1890 cp
->c_flag
|= C_NEED_RVNODE_PUT
;
1897 __private_extern__
void
1898 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1900 if (&cp
->c_desc
== cdp
) {
1904 /* First release allocated name buffer */
1905 if (cp
->c_desc
.cd_flags
& CD_HASBUF
&& cp
->c_desc
.cd_nameptr
!= 0) {
1906 char *name
= cp
->c_desc
.cd_nameptr
;
1908 cp
->c_desc
.cd_nameptr
= 0;
1909 cp
->c_desc
.cd_namelen
= 0;
1910 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
1911 vfs_removename(name
);
1913 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1915 /* Cnode now owns the name buffer */
1916 cdp
->cd_nameptr
= 0;
1917 cdp
->cd_namelen
= 0;
1918 cdp
->cd_flags
&= ~CD_HASBUF
;
1925 * The VFS layer guarantees that:
1926 * - source and destination will either both be directories, or
1927 * both not be directories.
1928 * - all the vnodes are from the same file system
1930 * When the target is a directory, HFS must ensure that its empty.
1934 struct vnop_rename_args
/* {
1935 struct vnode *a_fdvp;
1936 struct vnode *a_fvp;
1937 struct componentname *a_fcnp;
1938 struct vnode *a_tdvp;
1939 struct vnode *a_tvp;
1940 struct componentname *a_tcnp;
1941 vfs_context_t a_context;
1944 struct vnode
*tvp
= ap
->a_tvp
;
1945 struct vnode
*tdvp
= ap
->a_tdvp
;
1946 struct vnode
*fvp
= ap
->a_fvp
;
1947 struct vnode
*fdvp
= ap
->a_fdvp
;
1948 struct componentname
*tcnp
= ap
->a_tcnp
;
1949 struct componentname
*fcnp
= ap
->a_fcnp
;
1950 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1955 struct cat_desc from_desc
;
1956 struct cat_desc to_desc
;
1957 struct cat_desc out_desc
;
1958 struct hfsmount
*hfsmp
;
1959 cat_cookie_t cookie
;
1960 int tvp_deleted
= 0;
1961 int started_tr
= 0, got_cookie
= 0;
1962 int took_trunc_lock
= 0;
1966 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1967 if (tvp
&& vnode_isreg(tvp
)) {
1968 hfs_lock_truncate(VTOC(tvp
), TRUE
);
1969 took_trunc_lock
= 1;
1972 error
= hfs_lockfour(VTOC(fdvp
), VTOC(fvp
), VTOC(tdvp
), tvp
? VTOC(tvp
) : NULL
,
1973 HFS_EXCLUSIVE_LOCK
);
1975 if (took_trunc_lock
)
1976 hfs_unlock_truncate(VTOC(tvp
));
1983 tcp
= tvp
? VTOC(tvp
) : NULL
;
1984 hfsmp
= VTOHFS(tdvp
);
1986 /* Check for a race against unlink. */
1987 if (fcp
->c_flag
& C_NOEXISTS
) {
1993 * The following edge case is caught here:
1994 * (to cannot be a descendent of from)
2007 if (tdcp
->c_parentcnid
== fcp
->c_cnid
) {
2013 * The following two edge cases are caught here:
2014 * (note tvp is not empty)
2027 if (tvp
&& vnode_isdir(tvp
) && (tcp
->c_entries
!= 0) && fvp
!= tvp
) {
2033 * The following edge case is caught here:
2034 * (the from child and parent are the same)
2047 * Make sure "from" vnode and its parent are changeable.
2049 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
2055 * If the destination parent directory is "sticky", then the
2056 * user must own the parent directory, or the destination of
2057 * the rename, otherwise the destination may not be changed
2058 * (except by root). This implements append-only directories.
2060 * Note that checks for immutable and write access are done
2061 * by the call to hfs_removefile.
2063 if (tvp
&& (tdcp
->c_mode
& S_ISTXT
) &&
2064 (suser(vfs_context_ucred(tcnp
->cn_context
), NULL
)) &&
2065 (kauth_cred_getuid(vfs_context_ucred(tcnp
->cn_context
)) != tdcp
->c_uid
) &&
2066 (hfs_owner_rights(hfsmp
, tcp
->c_uid
, vfs_context_ucred(tcnp
->cn_context
), p
, false)) ) {
2073 (void)hfs_getinoquota(tcp
);
2075 /* Preflighting done, take fvp out of the name space. */
2079 * When a file moves out of "Cleanup At Startup"
2080 * we can drop its NODUMP status.
2082 if ((fcp
->c_flags
& UF_NODUMP
) &&
2085 (fdcp
->c_desc
.cd_nameptr
!= NULL
) &&
2086 (strcmp(fdcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
2087 fcp
->c_flags
&= ~UF_NODUMP
;
2088 fcp
->c_touch_chgtime
= TRUE
;
2089 (void) hfs_update(fvp
, 0);
2092 bzero(&from_desc
, sizeof(from_desc
));
2093 from_desc
.cd_nameptr
= fcnp
->cn_nameptr
;
2094 from_desc
.cd_namelen
= fcnp
->cn_namelen
;
2095 from_desc
.cd_parentcnid
= fdcp
->c_cnid
;
2096 from_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2097 from_desc
.cd_cnid
= fcp
->c_cnid
;
2099 bzero(&to_desc
, sizeof(to_desc
));
2100 to_desc
.cd_nameptr
= tcnp
->cn_nameptr
;
2101 to_desc
.cd_namelen
= tcnp
->cn_namelen
;
2102 to_desc
.cd_parentcnid
= tdcp
->c_cnid
;
2103 to_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2104 to_desc
.cd_cnid
= fcp
->c_cnid
;
2106 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2111 // if it's a hardlink then re-lookup the name so
2112 // that we get the correct cnid in from_desc (see
2113 // the comment in hfs_removefile for more details)
2115 if (fcp
->c_flag
& C_HARDLINK
) {
2116 struct cat_desc tmpdesc
;
2119 bzero(&tmpdesc
, sizeof(tmpdesc
));
2120 tmpdesc
.cd_nameptr
= fcnp
->cn_nameptr
;
2121 tmpdesc
.cd_namelen
= fcnp
->cn_namelen
;
2122 tmpdesc
.cd_parentcnid
= fdcp
->c_cnid
;
2123 tmpdesc
.cd_hint
= fdcp
->c_childhint
;
2125 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2127 if (cat_lookup(hfsmp
, &tmpdesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
2128 hfs_systemfile_unlock(hfsmp
, lockflags
);
2132 // use the real cnid instead of whatever happened to be there
2133 from_desc
.cd_cnid
= real_cnid
;
2134 hfs_systemfile_unlock(hfsmp
, lockflags
);
2138 * Reserve some space in the Catalog file.
2140 bzero(&cookie
, sizeof(cookie
));
2141 if ((error
= cat_preflight(hfsmp
, CAT_RENAME
+ CAT_DELETE
, &cookie
, p
))) {
2147 * If the destination exists then it may need to be removed.
2151 * When fvp matches tvp they must be case variants
2156 * If this a hard link with different parents
2157 * and its not a case variant then tvp should
2160 if (!((fcp
->c_flag
& C_HARDLINK
) &&
2162 (hfs_namecmp(fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
2163 tcnp
->cn_nameptr
, tcnp
->cn_namelen
) != 0)))) {
2170 if (vnode_isdir(tvp
))
2171 error
= hfs_removedir(tdvp
, tvp
, tcnp
, HFSRM_SKIP_RESERVE
);
2173 error
= hfs_removefile(tdvp
, tvp
, tcnp
, 0, HFSRM_SKIP_RESERVE
);
2182 * All done with tvp and fvp
2185 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2186 error
= cat_rename(hfsmp
, &from_desc
, &tdcp
->c_desc
, &to_desc
, &out_desc
);
2187 hfs_systemfile_unlock(hfsmp
, lockflags
);
2193 /* Invalidate negative cache entries in the destination directory */
2194 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
2195 cache_purge_negatives(tdvp
);
2197 /* Update cnode's catalog descriptor */
2198 replace_desc(fcp
, &out_desc
);
2199 fcp
->c_parentcnid
= tdcp
->c_cnid
;
2202 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_RMDIR
: VOL_RMFILE
,
2203 (fdcp
->c_cnid
== kHFSRootFolderID
));
2204 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_MKDIR
: VOL_MKFILE
,
2205 (tdcp
->c_cnid
== kHFSRootFolderID
));
2207 /* Update both parent directories. */
2211 if (fdcp
->c_nlink
> 0)
2213 if (fdcp
->c_entries
> 0)
2215 fdcp
->c_touch_chgtime
= TRUE
;
2216 fdcp
->c_touch_modtime
= TRUE
;
2218 fdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2219 (void) hfs_update(fdvp
, 0);
2221 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2222 tdcp
->c_touch_chgtime
= TRUE
;
2223 tdcp
->c_touch_modtime
= TRUE
;
2225 tdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2226 (void) hfs_update(tdvp
, 0);
2229 cat_postflight(hfsmp
, &cookie
, p
);
2232 hfs_end_transaction(hfsmp
);
2235 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2236 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2239 HFS_KNOTE(fvp
, NOTE_RENAME
);
2240 HFS_KNOTE(fdvp
, NOTE_WRITE
);
2241 if (tdvp
!= fdvp
) HFS_KNOTE(tdvp
, NOTE_WRITE
);
2244 if (took_trunc_lock
)
2245 hfs_unlock_truncate(VTOC(tvp
));
2247 hfs_unlockfour(fdcp
, fcp
, tdcp
, tcp
);
2249 /* After tvp is removed the only acceptable error is EIO */
2250 if (error
&& tvp_deleted
)
2261 hfs_vnop_mkdir(struct vnop_mkdir_args
*ap
)
2263 /***** HACK ALERT ********/
2264 ap
->a_cnp
->cn_flags
|= MAKEENTRY
;
2265 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
2270 * Create a symbolic link.
2273 hfs_vnop_symlink(struct vnop_symlink_args
*ap
)
2275 struct vnode
**vpp
= ap
->a_vpp
;
2276 struct vnode
*dvp
= ap
->a_dvp
;
2277 struct vnode
*vp
= NULL
;
2278 struct hfsmount
*hfsmp
;
2279 struct filefork
*fp
;
2280 struct buf
*bp
= NULL
;
2285 /* HFS standard disks don't support symbolic links */
2286 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
)
2289 /* Check for empty target name */
2290 if (ap
->a_target
[0] == 0)
2293 /* Create the vnode */
2294 ap
->a_vap
->va_mode
|= S_IFLNK
;
2295 if ((error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
))) {
2299 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2302 hfsmp
= VTOHFS(dvp
);
2303 len
= strlen(ap
->a_target
);
2306 (void)hfs_getinoquota(VTOC(vp
));
2309 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2315 * Allocate space for the link.
2317 * Since we're already inside a transaction,
2318 * tell hfs_truncate to skip the ubc_setsize.
2320 * Don't need truncate lock since a symlink is treated as a system file.
2322 error
= hfs_truncate(vp
, len
, IO_NOZEROFILL
, 1, ap
->a_context
);
2324 goto out
; /* XXX need to remove link */
2326 /* Write the link to disk */
2327 bp
= buf_getblk(vp
, (daddr64_t
)0, roundup((int)fp
->ff_size
, VTOHFS(vp
)->hfs_phys_block_size
),
2330 journal_modify_block_start(hfsmp
->jnl
, bp
);
2332 datap
= (char *)buf_dataptr(bp
);
2333 bzero(datap
, buf_size(bp
));
2334 bcopy(ap
->a_target
, datap
, len
);
2337 journal_modify_block_end(hfsmp
->jnl
, bp
);
2342 * We defered the ubc_setsize for hfs_truncate
2343 * since we were inside a transaction.
2345 * We don't need to drop the cnode lock here
2346 * since this is a symlink.
2348 ubc_setsize(vp
, len
);
2351 hfs_end_transaction(hfsmp
);
2353 hfs_unlock(VTOC(vp
));
2359 /* structures to hold a "." or ".." directory entry */
2360 struct hfs_stddotentry
{
2361 u_int32_t d_fileno
; /* unique file number */
2362 u_int16_t d_reclen
; /* length of this structure */
2363 u_int8_t d_type
; /* dirent file type */
2364 u_int8_t d_namlen
; /* len of filename */
2365 char d_name
[4]; /* "." or ".." */
2368 struct hfs_extdotentry
{
2369 u_int64_t d_fileno
; /* unique file number */
2370 u_int64_t d_seekoff
; /* seek offset (optional, used by servers) */
2371 u_int16_t d_reclen
; /* length of this structure */
2372 u_int16_t d_namlen
; /* len of filename */
2373 u_int8_t d_type
; /* dirent file type */
2374 u_char d_name
[3]; /* "." or ".." */
2378 struct hfs_stddotentry std
;
2379 struct hfs_extdotentry ext
;
2383 * hfs_vnop_readdir reads directory entries into the buffer pointed
2384 * to by uio, in a filesystem independent format. Up to uio_resid
2385 * bytes of data can be transferred. The data in the buffer is a
2386 * series of packed dirent structures where each one contains the
2387 * following entries:
2389 * u_int32_t d_fileno; // file number of entry
2390 * u_int16_t d_reclen; // length of this record
2391 * u_int8_t d_type; // file type
2392 * u_int8_t d_namlen; // length of string in d_name
2393 * char d_name[MAXNAMELEN+1]; // null terminated file name
2395 * The current position (uio_offset) refers to the next block of
2396 * entries. The offset can only be set to a value previously
2397 * returned by hfs_vnop_readdir or zero. This offset does not have
2398 * to match the number of bytes returned (in uio_resid).
2400 * In fact, the offset used by HFS is essentially an index (26 bits)
2401 * with a tag (6 bits). The tag is for associating the next request
2402 * with the current request. This enables us to have multiple threads
2403 * reading the directory while the directory is also being modified.
2405 * Each tag/index pair is tied to a unique directory hint. The hint
2406 * contains information (filename) needed to build the catalog b-tree
2407 * key for finding the next set of entries.
2410 hfs_vnop_readdir(ap
)
2411 struct vnop_readdir_args
/* {
2417 vfs_context_t a_context;
2420 struct vnode
*vp
= ap
->a_vp
;
2421 uio_t uio
= ap
->a_uio
;
2423 struct hfsmount
*hfsmp
;
2424 directoryhint_t
*dirhint
= NULL
;
2425 directoryhint_t localhint
;
2430 user_addr_t user_start
= 0;
2431 user_size_t user_len
= 0;
2439 cnid_t cnid_hint
= 0;
2442 startoffset
= offset
= uio_offset(uio
);
2443 bufstart
= CAST_DOWN(caddr_t
, uio_iov_base(uio
));
2444 extended
= (ap
->a_flags
& VNODE_READDIR_EXTENDED
);
2445 nfs_cookies
= extended
&& (ap
->a_flags
& VNODE_READDIR_REQSEEKOFF
);
2447 /* Sanity check the uio data. */
2448 if ((uio_iovcnt(uio
) > 1) ||
2449 (uio_resid(uio
) < (int)sizeof(struct dirent
))) {
2452 /* Note that the dirhint calls require an exclusive lock. */
2453 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2458 /* Pick up cnid hint (if any). */
2460 cnid_hint
= (cnid_t
)(uio_offset(uio
) >> 32);
2461 uio_setoffset(uio
, uio_offset(uio
) & 0x00000000ffffffffLL
);
2462 if (cnid_hint
== INT_MAX
) { /* searching pass the last item */
2468 * Synthesize entries for "." and ".."
2471 hfs_dotentry_t dotentry
[2];
2475 struct hfs_extdotentry
*entry
= &dotentry
[0].ext
;
2477 entry
->d_fileno
= cp
->c_cnid
;
2478 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2479 entry
->d_type
= DT_DIR
;
2480 entry
->d_namlen
= 1;
2481 entry
->d_name
[0] = '.';
2482 entry
->d_name
[1] = '\0';
2483 entry
->d_name
[2] = '\0';
2484 entry
->d_seekoff
= 1;
2487 entry
->d_fileno
= cp
->c_parentcnid
;
2488 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2489 entry
->d_type
= DT_DIR
;
2490 entry
->d_namlen
= 2;
2491 entry
->d_name
[0] = '.';
2492 entry
->d_name
[1] = '.';
2493 entry
->d_name
[2] = '\0';
2494 entry
->d_seekoff
= 2;
2495 uiosize
= 2 * sizeof(struct hfs_extdotentry
);
2497 struct hfs_stddotentry
*entry
= &dotentry
[0].std
;
2499 entry
->d_fileno
= cp
->c_cnid
;
2500 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2501 entry
->d_type
= DT_DIR
;
2502 entry
->d_namlen
= 1;
2503 *(int *)&entry
->d_name
[0] = 0;
2504 entry
->d_name
[0] = '.';
2507 entry
->d_fileno
= cp
->c_parentcnid
;
2508 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2509 entry
->d_type
= DT_DIR
;
2510 entry
->d_namlen
= 2;
2511 *(int *)&entry
->d_name
[0] = 0;
2512 entry
->d_name
[0] = '.';
2513 entry
->d_name
[1] = '.';
2514 uiosize
= 2 * sizeof(struct hfs_stddotentry
);
2516 if ((error
= uiomove((caddr_t
)&dotentry
, uiosize
, uio
))) {
2522 /* If there are no real entries then we're done. */
2523 if (cp
->c_entries
== 0) {
2526 uio_setoffset(uio
, offset
);
2531 // We have to lock the user's buffer here so that we won't
2532 // fault on it after we've acquired a shared lock on the
2533 // catalog file. The issue is that you can get a 3-way
2534 // deadlock if someone else starts a transaction and then
2535 // tries to lock the catalog file but can't because we're
2536 // here and we can't service our page fault because VM is
2537 // blocked trying to start a transaction as a result of
2538 // trying to free up pages for our page fault. It's messy
2539 // but it does happen on dual-procesors that are paging
2540 // heavily (see radar 3082639 for more info). By locking
2541 // the buffer up-front we prevent ourselves from faulting
2542 // while holding the shared catalog file lock.
2544 // Fortunately this and hfs_search() are the only two places
2545 // currently (10/30/02) that can fault on user data with a
2546 // shared lock on the catalog file.
2548 if (hfsmp
->jnl
&& uio_isuserspace(uio
)) {
2549 user_start
= uio_curriovbase(uio
);
2550 user_len
= uio_curriovlen(uio
);
2552 if ((error
= vslock(user_start
, user_len
)) != 0) {
2557 /* Convert offset into a catalog directory index. */
2558 index
= (offset
& HFS_INDEX_MASK
) - 2;
2559 tag
= offset
& ~HFS_INDEX_MASK
;
2561 /* Lock catalog during cat_findname and cat_getdirentries. */
2562 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2564 /* When called from NFS, try and resolve a cnid hint. */
2565 if (nfs_cookies
&& cnid_hint
!= 0) {
2566 if (cat_findname(hfsmp
, cnid_hint
, &localhint
.dh_desc
) == 0) {
2567 if ( localhint
.dh_desc
.cd_parentcnid
== cp
->c_cnid
) {
2568 localhint
.dh_index
= index
- 1;
2569 localhint
.dh_time
= 0;
2570 bzero(&localhint
.dh_link
, sizeof(localhint
.dh_link
));
2571 dirhint
= &localhint
; /* don't forget to release the descriptor */
2573 cat_releasedesc(&localhint
.dh_desc
);
2578 /* Get a directory hint (cnode must be locked exclusive) */
2579 if (dirhint
== NULL
) {
2580 dirhint
= hfs_getdirhint(cp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
);
2582 /* Hide tag from catalog layer. */
2583 dirhint
->dh_index
&= HFS_INDEX_MASK
;
2584 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
2585 dirhint
->dh_index
= -1;
2589 /* Pack the buffer with dirent entries. */
2590 error
= cat_getdirentries(hfsmp
, cp
->c_entries
, dirhint
, uio
, extended
, &items
, &eofflag
);
2592 hfs_systemfile_unlock(hfsmp
, lockflags
);
2598 /* Get index to the next item */
2601 if (items
>= (int)cp
->c_entries
) {
2605 /* Convert catalog directory index back into an offset. */
2607 tag
= (++cp
->c_dirhinttag
) << HFS_INDEX_BITS
;
2608 uio_setoffset(uio
, (index
+ 2) | tag
);
2609 dirhint
->dh_index
|= tag
;
2612 cp
->c_touch_acctime
= TRUE
;
2614 if (ap
->a_numdirent
) {
2615 if (startoffset
== 0)
2617 *ap
->a_numdirent
= items
;
2621 if (hfsmp
->jnl
&& user_start
) {
2622 vsunlock(user_start
, user_len
, TRUE
);
2624 /* If we didn't do anything then go ahead and dump the hint. */
2625 if ((dirhint
!= NULL
) &&
2626 (dirhint
!= &localhint
) &&
2627 (uio_offset(uio
) == startoffset
)) {
2628 hfs_reldirhint(cp
, dirhint
);
2631 if (ap
->a_eofflag
) {
2632 *ap
->a_eofflag
= eofflag
;
2634 if (dirhint
== &localhint
) {
2635 cat_releasedesc(&localhint
.dh_desc
);
2643 * Read contents of a symbolic link.
2646 hfs_vnop_readlink(ap
)
2647 struct vnop_readlink_args
/* {
2650 vfs_context_t a_context;
2653 struct vnode
*vp
= ap
->a_vp
;
2655 struct filefork
*fp
;
2658 if (!vnode_islnk(vp
))
2661 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2666 /* Zero length sym links are not allowed */
2667 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2668 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2673 /* Cache the path so we don't waste buffer cache resources */
2674 if (fp
->ff_symlinkptr
== NULL
) {
2675 struct buf
*bp
= NULL
;
2677 MALLOC(fp
->ff_symlinkptr
, char *, fp
->ff_size
, M_TEMP
, M_WAITOK
);
2678 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0,
2679 roundup((int)fp
->ff_size
,
2680 VTOHFS(vp
)->hfs_phys_block_size
),
2681 vfs_context_ucred(ap
->a_context
), &bp
);
2685 if (fp
->ff_symlinkptr
) {
2686 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2687 fp
->ff_symlinkptr
= NULL
;
2691 bcopy((char *)buf_dataptr(bp
), fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2693 if (VTOHFS(vp
)->jnl
&& (buf_flags(bp
) & B_LOCKED
) == 0) {
2694 buf_markinvalid(bp
); /* data no longer needed */
2698 error
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2701 * Keep track blocks read
2703 if ((VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) && (error
== 0)) {
2706 * If this file hasn't been seen since the start of
2707 * the current sampling period then start over.
2709 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
)
2710 VTOF(vp
)->ff_bytesread
= fp
->ff_size
;
2712 VTOF(vp
)->ff_bytesread
+= fp
->ff_size
;
2714 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2715 // cp->c_touch_acctime = TRUE;
2725 * Get configurable pathname variables.
2728 hfs_vnop_pathconf(ap
)
2729 struct vnop_pathconf_args
/* {
2733 vfs_context_t a_context;
2736 switch (ap
->a_name
) {
2738 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2741 *ap
->a_retval
= HFS_LINK_MAX
;
2744 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2745 *ap
->a_retval
= kHFSMaxFileNameChars
; /* 255 */
2747 *ap
->a_retval
= kHFSPlusMaxFileNameChars
; /* 31 */
2750 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2753 *ap
->a_retval
= PIPE_BUF
;
2755 case _PC_CHOWN_RESTRICTED
:
2761 case _PC_NAME_CHARS_MAX
:
2762 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2764 case _PC_CASE_SENSITIVE
:
2765 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_CASE_SENSITIVE
)
2770 case _PC_CASE_PRESERVING
:
2782 * Update a cnode's on-disk metadata.
2784 * If waitfor is set, then wait for the disk write of
2785 * the node to complete.
2787 * The cnode must be locked exclusive
2791 hfs_update(struct vnode
*vp
, __unused
int waitfor
)
2793 struct cnode
*cp
= VTOC(vp
);
2795 struct cat_fork
*dataforkp
= NULL
;
2796 struct cat_fork
*rsrcforkp
= NULL
;
2797 struct cat_fork datafork
;
2798 struct hfsmount
*hfsmp
;
2805 if (vnode_issystem(vp
) && (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)) {
2808 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) || (cp
->c_mode
== 0)) {
2809 cp
->c_flag
&= ~C_MODIFIED
;
2810 cp
->c_touch_acctime
= 0;
2811 cp
->c_touch_chgtime
= 0;
2812 cp
->c_touch_modtime
= 0;
2816 hfs_touchtimes(hfsmp
, cp
);
2818 /* Nothing to update. */
2819 if ((cp
->c_flag
& (C_MODIFIED
| C_FORCEUPDATE
)) == 0) {
2824 dataforkp
= &cp
->c_datafork
->ff_data
;
2826 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
2829 * For delayed allocations updates are
2830 * postponed until an fsync or the file
2831 * gets written to disk.
2833 * Deleted files can defer meta data updates until inactive.
2835 * If we're ever called with the C_FORCEUPDATE flag though
2836 * we have to do the update.
2838 if (ISSET(cp
->c_flag
, C_FORCEUPDATE
) == 0 &&
2839 (ISSET(cp
->c_flag
, C_DELETED
) ||
2840 (dataforkp
&& cp
->c_datafork
->ff_unallocblocks
) ||
2841 (rsrcforkp
&& cp
->c_rsrcfork
->ff_unallocblocks
))) {
2842 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2843 cp
->c_flag
|= C_MODIFIED
;
2845 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2850 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2855 * For files with invalid ranges (holes) the on-disk
2856 * field representing the size of the file (cf_size)
2857 * must be no larger than the start of the first hole.
2859 if (dataforkp
&& !CIRCLEQ_EMPTY(&cp
->c_datafork
->ff_invalidranges
)) {
2860 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2861 datafork
.cf_size
= CIRCLEQ_FIRST(&cp
->c_datafork
->ff_invalidranges
)->rl_start
;
2862 dataforkp
= &datafork
;
2863 } else if (dataforkp
&& (cp
->c_datafork
->ff_unallocblocks
!= 0)) {
2864 // always make sure the block count and the size
2865 // of the file match the number of blocks actually
2866 // allocated to the file on disk
2867 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2868 // make sure that we don't assign a negative block count
2869 if (cp
->c_datafork
->ff_blocks
< cp
->c_datafork
->ff_unallocblocks
) {
2870 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2871 cp
->c_datafork
->ff_blocks
, cp
->c_datafork
->ff_unallocblocks
);
2873 datafork
.cf_blocks
= (cp
->c_datafork
->ff_blocks
- cp
->c_datafork
->ff_unallocblocks
);
2874 datafork
.cf_size
= datafork
.cf_blocks
* HFSTOVCB(hfsmp
)->blockSize
;
2875 dataforkp
= &datafork
;
2879 * Lock the Catalog b-tree file.
2880 * A shared lock is sufficient since an update doesn't change
2881 * the tree and the lock on vp protects the cnode.
2883 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2885 /* XXX - waitfor is not enforced */
2886 error
= cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
2888 hfs_systemfile_unlock(hfsmp
, lockflags
);
2890 /* After the updates are finished, clear the flags */
2891 cp
->c_flag
&= ~(C_MODIFIED
| C_FORCEUPDATE
);
2893 hfs_end_transaction(hfsmp
);
2895 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2901 * Allocate a new node
2904 hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
, struct componentname
*cnp
,
2905 struct vnode_attr
*vap
, vfs_context_t ctx
)
2907 struct cnode
*cp
= NULL
;
2910 struct hfsmount
*hfsmp
;
2911 struct cat_desc in_desc
, out_desc
;
2912 struct cat_attr attr
;
2914 cat_cookie_t cookie
;
2916 int error
, started_tr
= 0, got_cookie
= 0;
2917 enum vtype vnodetype
;
2920 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
)))
2923 hfsmp
= VTOHFS(dvp
);
2926 out_desc
.cd_flags
= 0;
2927 out_desc
.cd_nameptr
= NULL
;
2929 mode
= MAKEIMODE(vap
->va_type
, vap
->va_mode
);
2931 if ((mode
& S_IFMT
) == 0)
2933 vnodetype
= IFTOVT(mode
);
2935 /* Check if were out of usable disk space. */
2936 if ((hfs_freeblks(hfsmp
, 1) <= 0) && (suser(vfs_context_ucred(ctx
), NULL
) != 0)) {
2943 /* Setup the default attributes */
2944 bzero(&attr
, sizeof(attr
));
2945 attr
.ca_mode
= mode
;
2946 attr
.ca_nlink
= vnodetype
== VDIR
? 2 : 1;
2947 attr
.ca_mtime
= tv
.tv_sec
;
2948 if ((VTOVCB(dvp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
2949 attr
.ca_mtime
+= 3600; /* Same as what hfs_update does */
2951 attr
.ca_atime
= attr
.ca_ctime
= attr
.ca_itime
= attr
.ca_mtime
;
2952 attr
.ca_atimeondisk
= attr
.ca_atime
;
2953 /* On HFS+ the ThreadExists flag must always be set for files. */
2954 if (vnodetype
!= VDIR
&& (hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
2955 attr
.ca_recflags
= kHFSThreadExistsMask
;
2957 attr
.ca_uid
= vap
->va_uid
;
2958 attr
.ca_gid
= vap
->va_gid
;
2959 VATTR_SET_SUPPORTED(vap
, va_mode
);
2960 VATTR_SET_SUPPORTED(vap
, va_uid
);
2961 VATTR_SET_SUPPORTED(vap
, va_gid
);
2963 /* Tag symlinks with a type and creator. */
2964 if (vnodetype
== VLNK
) {
2965 struct FndrFileInfo
*fip
;
2967 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
2968 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
2969 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
2971 if (cnp
->cn_flags
& ISWHITEOUT
)
2972 attr
.ca_flags
|= UF_OPAQUE
;
2974 /* Setup the descriptor */
2975 in_desc
.cd_nameptr
= cnp
->cn_nameptr
;
2976 in_desc
.cd_namelen
= cnp
->cn_namelen
;
2977 in_desc
.cd_parentcnid
= dcp
->c_cnid
;
2978 in_desc
.cd_flags
= S_ISDIR(mode
) ? CD_ISDIR
: 0;
2979 in_desc
.cd_hint
= dcp
->c_childhint
;
2980 in_desc
.cd_encoding
= 0;
2982 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2988 * Reserve some space in the Catalog file.
2990 * (we also add CAT_DELETE since our getnewvnode
2991 * request can cause an hfs_inactive call to
2992 * delete an unlinked file)
2994 if ((error
= cat_preflight(hfsmp
, CAT_CREATE
| CAT_DELETE
, &cookie
, 0))) {
2999 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
3000 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
3002 /* Update the parent directory */
3003 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
3006 dcp
->c_ctime
= tv
.tv_sec
;
3007 dcp
->c_mtime
= tv
.tv_sec
;
3008 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
3009 HFS_KNOTE(dvp
, NOTE_ATTRIB
);
3011 hfs_systemfile_unlock(hfsmp
, lockflags
);
3015 /* Invalidate negative cache entries in the directory */
3016 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
3017 cache_purge_negatives(dvp
);
3019 if (vnodetype
== VDIR
) {
3020 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
3022 HFS_KNOTE(dvp
, NOTE_WRITE
);
3025 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
3026 (dcp
->c_cnid
== kHFSRootFolderID
));
3029 // have to end the transaction here before we call hfs_getnewvnode()
3030 // because that can cause us to try and reclaim a vnode on a different
3031 // file system which could cause us to start a transaction which can
3032 // deadlock with someone on that other file system (since we could be
3033 // holding two transaction locks as well as various vnodes and we did
3034 // not obtain the locks on them in the proper order).
3036 // NOTE: this means that if the quota check fails or we have to update
3037 // the change time on a block-special device that those changes
3038 // will happen as part of independent transactions.
3041 hfs_end_transaction(hfsmp
);
3046 * Create a vnode for the object just created.
3048 * The cnode is locked on successful return.
3050 error
= hfs_getnewvnode(hfsmp
, dvp
, cnp
, &out_desc
, 0, &attr
, NULL
, &tvp
);
3055 //cache_enter(dvp, tvp, cnp);
3060 * We call hfs_chkiq with FORCE flag so that if we
3061 * fall through to the rmdir we actually have
3062 * accounted for the inode
3064 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_QUOTA
) {
3065 if ((error
= hfs_getinoquota(cp
)) ||
3066 (error
= hfs_chkiq(cp
, 1, vfs_context_ucred(ctx
), FORCE
))) {
3068 if (vnode_isdir(tvp
))
3069 (void) hfs_removedir(dvp
, tvp
, cnp
, 0);
3072 hfs_lock_truncate(cp
, TRUE
);
3073 hfs_lock(cp
, HFS_FORCE_LOCK
);
3074 (void) hfs_removefile(dvp
, tvp
, cnp
, 0, 0);
3075 hfs_unlock_truncate(cp
);
3078 * we successfully allocated a new vnode, but
3079 * the quota check is telling us we're beyond
3080 * our limit, so we need to dump our lock + reference
3090 /* Remember if any ACL data was set. */
3091 if (VATTR_IS_ACTIVE(vap
, va_acl
) &&
3092 (vap
->va_acl
!= NULL
)) {
3093 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
3094 cp
->c_touch_chgtime
= TRUE
;
3095 (void) hfs_update(tvp
, TRUE
);
3099 cat_releasedesc(&out_desc
);
3102 cat_postflight(hfsmp
, &cookie
, 0);
3105 * Check if a file is located in the "Cleanup At Startup"
3106 * directory. If it is then tag it as NODUMP so that we
3107 * can be lazy about zero filling data holes.
3109 if ((error
== 0) && dvp
&& (vnodetype
== VREG
) &&
3110 (dcp
->c_desc
.cd_nameptr
!= NULL
) &&
3111 (strcmp(dcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
3118 * The parent of "Cleanup At Startup" should
3119 * have the ASCII name of the userid.
3121 if (hfs_vget(hfsmp
, dcp
->c_parentcnid
, &ddvp
, 0) == 0) {
3122 if (VTOC(ddvp
)->c_desc
.cd_nameptr
) {
3125 uid
= strtoul(VTOC(ddvp
)->c_desc
.cd_nameptr
, 0, 0);
3126 if ((uid
== cp
->c_uid
) ||
3127 (uid
== vfs_context_ucred(ctx
)->cr_uid
)) {
3128 cp
->c_flags
|= UF_NODUMP
;
3129 cp
->c_touch_chgtime
= TRUE
;
3132 hfs_unlock(VTOC(ddvp
));
3139 if (error
== 0 && cp
!= NULL
) {
3143 hfs_end_transaction(hfsmp
);
3152 * WARNING - assumes caller has cnode lock.
3156 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, __unused
struct proc
*p
)
3159 struct cnode
*cp
= VTOC(vp
);
3163 if ((rvp
= cp
->c_rsrc_vp
)) {
3164 vid
= vnode_vid(rvp
);
3166 /* Use exising vnode */
3167 error
= vnode_getwithvid(rvp
, vid
);
3169 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
3172 printf("hfs_vgetrsrc: couldn't get"
3173 " resource fork for %s\n", name
);
3177 struct cat_fork rsrcfork
;
3178 struct componentname cn
;
3182 * Make sure cnode lock is exclusive, if not upgrade it.
3184 * We assume that we were called from a read-only VNOP (getattr)
3185 * and that its safe to have the cnode lock dropped and reacquired.
3187 if (cp
->c_lockowner
!= current_thread()) {
3189 * If the upgrade fails we loose the lock and
3190 * have to take the exclusive lock on our own.
3192 if (lck_rw_lock_shared_to_exclusive(&cp
->c_rwlock
) != 0)
3193 lck_rw_lock_exclusive(&cp
->c_rwlock
);
3194 cp
->c_lockowner
= current_thread();
3197 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3199 /* Get resource fork data */
3200 error
= cat_lookup(hfsmp
, &cp
->c_desc
, 1, (struct cat_desc
*)0,
3201 (struct cat_attr
*)0, &rsrcfork
, NULL
);
3203 hfs_systemfile_unlock(hfsmp
, lockflags
);
3208 * Supply hfs_getnewvnode with a component name.
3211 if (cp
->c_desc
.cd_nameptr
) {
3212 MALLOC_ZONE(cn
.cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3213 cn
.cn_nameiop
= LOOKUP
;
3214 cn
.cn_flags
= ISLASTCN
| HASBUF
;
3215 cn
.cn_context
= NULL
;
3216 cn
.cn_pnlen
= MAXPATHLEN
;
3217 cn
.cn_nameptr
= cn
.cn_pnbuf
;
3220 cn
.cn_namelen
= sprintf(cn
.cn_nameptr
, "%s%s", cp
->c_desc
.cd_nameptr
, _PATH_RSRCFORKSPEC
);
3222 error
= hfs_getnewvnode(hfsmp
, vnode_parent(vp
), cn
.cn_pnbuf
? &cn
: NULL
,
3223 &cp
->c_desc
, 2, &cp
->c_attr
, &rsrcfork
, &rvp
);
3225 FREE_ZONE(cn
.cn_pnbuf
, cn
.cn_pnlen
, M_NAMEI
);
3236 filt_hfsdetach(struct knote
*kn
)
3240 vp
= (struct vnode
*)kn
->kn_hook
;
3241 if (vnode_getwithvid(vp
, kn
->kn_hookid
))
3244 if (1) { /* ! KNDETACH_VNLOCKED */
3245 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3246 (void) KNOTE_DETACH(&VTOC(vp
)->c_knotes
, kn
);
3247 hfs_unlock(VTOC(vp
));
3256 filt_hfsread(struct knote
*kn
, long hint
)
3258 struct vnode
*vp
= (struct vnode
*)kn
->kn_hook
;
3262 if ((vnode_getwithvid(vp
, kn
->kn_hookid
) != 0)) {
3267 if (hint
== NOTE_REVOKE
) {
3269 * filesystem is gone, so set the EOF flag and schedule
3270 * the knote for deletion.
3272 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3276 /* poll(2) semantics dictate always saying there is data */
3277 kn
->kn_data
= (!(kn
->kn_flags
& EV_POLL
)) ?
3278 VTOF(vp
)->ff_size
- kn
->kn_fp
->f_fglob
->fg_offset
: 1;
3283 return (kn
->kn_data
!= 0);
3288 filt_hfswrite(struct knote
*kn
, long hint
)
3293 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3296 vnode_put(kn
->kn_hook
);
3298 if (hint
== NOTE_REVOKE
) {
3300 * filesystem is gone, so set the EOF flag and schedule
3301 * the knote for deletion.
3304 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3312 filt_hfsvnode(struct knote
*kn
, long hint
)
3316 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3319 vnode_put(kn
->kn_hook
);
3321 if (kn
->kn_sfflags
& hint
)
3322 kn
->kn_fflags
|= hint
;
3323 if ((hint
== NOTE_REVOKE
)) {
3324 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3328 return (kn
->kn_fflags
!= 0);
3331 static struct filterops hfsread_filtops
=
3332 { 1, NULL
, filt_hfsdetach
, filt_hfsread
};
3333 static struct filterops hfswrite_filtops
=
3334 { 1, NULL
, filt_hfsdetach
, filt_hfswrite
};
3335 static struct filterops hfsvnode_filtops
=
3336 { 1, NULL
, filt_hfsdetach
, filt_hfsvnode
};
3339 * Add a kqueue filter.
3343 struct vnop_kqfilt_add_args
/* {
3347 vfs_context_t a_context;
3350 struct vnode
*vp
= ap
->a_vp
;
3351 struct knote
*kn
= ap
->a_kn
;
3354 switch (kn
->kn_filter
) {
3356 if (vnode_isreg(vp
)) {
3357 kn
->kn_fop
= &hfsread_filtops
;
3363 if (vnode_isreg(vp
)) {
3364 kn
->kn_fop
= &hfswrite_filtops
;
3370 kn
->kn_fop
= &hfsvnode_filtops
;
3376 kn
->kn_hook
= (caddr_t
)vp
;
3377 kn
->kn_hookid
= vnode_vid(vp
);
3379 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
3381 KNOTE_ATTACH(&VTOC(vp
)->c_knotes
, kn
);
3382 hfs_unlock(VTOC(vp
));
3388 * Remove a kqueue filter
3391 hfs_vnop_kqfiltremove(ap
)
3392 struct vnop_kqfilt_remove_args
/* {
3395 vfs_context_t a_context;
3400 result
= ENOTSUP
; /* XXX */
3406 * Wrapper for special device reads
3410 struct vnop_read_args
/* {
3414 vfs_context_t a_context;
3420 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3421 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3425 * Wrapper for special device writes
3429 struct vnop_write_args
/* {
3433 vfs_context_t a_context;
3437 * Set update and change flags.
3439 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3440 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3441 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3445 * Wrapper for special device close
3447 * Update the times on the cnode then do device close.
3451 struct vnop_close_args
/* {
3454 vfs_context_t a_context;
3457 struct vnode
*vp
= ap
->a_vp
;
3460 if (vnode_isinuse(ap
->a_vp
, 1)) {
3461 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3463 hfs_touchtimes(VTOHFS(vp
), cp
);
3467 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3472 * Wrapper for fifo reads
3476 struct vnop_read_args
/* {
3480 vfs_context_t a_context;
3483 extern int (**fifo_vnodeop_p
)(void *);
3488 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3489 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3493 * Wrapper for fifo writes
3497 struct vnop_write_args
/* {
3501 vfs_context_t a_context;
3504 extern int (**fifo_vnodeop_p
)(void *);
3507 * Set update and change flags.
3509 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3510 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3511 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3515 * Wrapper for fifo close
3517 * Update the times on the cnode then do device close.
3521 struct vnop_close_args
/* {
3524 vfs_context_t a_context;
3527 extern int (**fifo_vnodeop_p
)(void *);
3528 struct vnode
*vp
= ap
->a_vp
;
3531 if (vnode_isinuse(ap
->a_vp
, 1)) {
3532 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3534 hfs_touchtimes(VTOHFS(vp
), cp
);
3538 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3542 * kqfilt_add wrapper for fifos.
3544 * Fall through to hfs kqfilt_add routines if needed
3547 hfsfifo_kqfilt_add(ap
)
3548 struct vnop_kqfilt_add_args
*ap
;
3550 extern int (**fifo_vnodeop_p
)(void *);
3553 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_add
), ap
);
3555 error
= hfs_vnop_kqfiltadd(ap
);
3560 * kqfilt_remove wrapper for fifos.
3562 * Fall through to hfs kqfilt_remove routines if needed
3565 hfsfifo_kqfilt_remove(ap
)
3566 struct vnop_kqfilt_remove_args
*ap
;
3568 extern int (**fifo_vnodeop_p
)(void *);
3571 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_remove
), ap
);
3573 error
= hfs_vnop_kqfiltremove(ap
);
3580 * Synchronize a file's in-core state with that on disk.
3584 struct vnop_fsync_args
/* {
3587 vfs_context_t a_context;
3590 struct vnode
* vp
= ap
->a_vp
;
3594 * We need to allow ENOENT lock errors since unlink
3595 * systenm call can call VNOP_FSYNC during vclean.
3597 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
3601 error
= hfs_fsync(vp
, ap
->a_waitfor
, 0, vfs_context_proc(ap
->a_context
));
3603 hfs_unlock(VTOC(vp
));
3607 /*****************************************************************************
3611 *****************************************************************************/
3612 int hfs_vnop_readdirattr(struct vnop_readdirattr_args
*); /* in hfs_attrlist.c */
3613 int hfs_vnop_inactive(struct vnop_inactive_args
*); /* in hfs_cnode.c */
3614 int hfs_vnop_reclaim(struct vnop_reclaim_args
*); /* in hfs_cnode.c */
3615 int hfs_vnop_link(struct vnop_link_args
*); /* in hfs_link.c */
3616 int hfs_vnop_lookup(struct vnop_lookup_args
*); /* in hfs_lookup.c */
3617 int hfs_vnop_search(struct vnop_searchfs_args
*); /* in hfs_search.c */
3619 int hfs_vnop_read(struct vnop_read_args
*); /* in hfs_readwrite.c */
3620 int hfs_vnop_write(struct vnop_write_args
*); /* in hfs_readwrite.c */
3621 int hfs_vnop_ioctl(struct vnop_ioctl_args
*); /* in hfs_readwrite.c */
3622 int hfs_vnop_select(struct vnop_select_args
*); /* in hfs_readwrite.c */
3623 int hfs_vnop_strategy(struct vnop_strategy_args
*); /* in hfs_readwrite.c */
3624 int hfs_vnop_allocate(struct vnop_allocate_args
*); /* in hfs_readwrite.c */
3625 int hfs_vnop_pagein(struct vnop_pagein_args
*); /* in hfs_readwrite.c */
3626 int hfs_vnop_pageout(struct vnop_pageout_args
*); /* in hfs_readwrite.c */
3627 int hfs_vnop_bwrite(struct vnop_bwrite_args
*); /* in hfs_readwrite.c */
3628 int hfs_vnop_blktooff(struct vnop_blktooff_args
*); /* in hfs_readwrite.c */
3629 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*); /* in hfs_readwrite.c */
3630 int hfs_vnop_blockmap(struct vnop_blockmap_args
*); /* in hfs_readwrite.c */
3631 int hfs_vnop_getxattr(struct vnop_getxattr_args
*); /* in hfs_xattr.c */
3632 int hfs_vnop_setxattr(struct vnop_setxattr_args
*); /* in hfs_xattr.c */
3633 int hfs_vnop_removexattr(struct vnop_removexattr_args
*); /* in hfs_xattr.c */
3634 int hfs_vnop_listxattr(struct vnop_listxattr_args
*); /* in hfs_xattr.c */
3636 int (**hfs_vnodeop_p
)(void *);
3638 #define VOPFUNC int (*)(void *)
3640 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
3641 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3642 { &vnop_lookup_desc
, (VOPFUNC
)hfs_vnop_lookup
}, /* lookup */
3643 { &vnop_create_desc
, (VOPFUNC
)hfs_vnop_create
}, /* create */
3644 { &vnop_mknod_desc
, (VOPFUNC
)hfs_vnop_mknod
}, /* mknod */
3645 { &vnop_open_desc
, (VOPFUNC
)hfs_vnop_open
}, /* open */
3646 { &vnop_close_desc
, (VOPFUNC
)hfs_vnop_close
}, /* close */
3647 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3648 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3649 { &vnop_read_desc
, (VOPFUNC
)hfs_vnop_read
}, /* read */
3650 { &vnop_write_desc
, (VOPFUNC
)hfs_vnop_write
}, /* write */
3651 { &vnop_ioctl_desc
, (VOPFUNC
)hfs_vnop_ioctl
}, /* ioctl */
3652 { &vnop_select_desc
, (VOPFUNC
)hfs_vnop_select
}, /* select */
3653 { &vnop_revoke_desc
, (VOPFUNC
)nop_revoke
}, /* revoke */
3654 { &vnop_exchange_desc
, (VOPFUNC
)hfs_vnop_exchange
}, /* exchange */
3655 { &vnop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
3656 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3657 { &vnop_remove_desc
, (VOPFUNC
)hfs_vnop_remove
}, /* remove */
3658 { &vnop_link_desc
, (VOPFUNC
)hfs_vnop_link
}, /* link */
3659 { &vnop_rename_desc
, (VOPFUNC
)hfs_vnop_rename
}, /* rename */
3660 { &vnop_mkdir_desc
, (VOPFUNC
)hfs_vnop_mkdir
}, /* mkdir */
3661 { &vnop_rmdir_desc
, (VOPFUNC
)hfs_vnop_rmdir
}, /* rmdir */
3662 { &vnop_symlink_desc
, (VOPFUNC
)hfs_vnop_symlink
}, /* symlink */
3663 { &vnop_readdir_desc
, (VOPFUNC
)hfs_vnop_readdir
}, /* readdir */
3664 { &vnop_readdirattr_desc
, (VOPFUNC
)hfs_vnop_readdirattr
}, /* readdirattr */
3665 { &vnop_readlink_desc
, (VOPFUNC
)hfs_vnop_readlink
}, /* readlink */
3666 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3667 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3668 { &vnop_strategy_desc
, (VOPFUNC
)hfs_vnop_strategy
}, /* strategy */
3669 { &vnop_pathconf_desc
, (VOPFUNC
)hfs_vnop_pathconf
}, /* pathconf */
3670 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3671 { &vnop_allocate_desc
, (VOPFUNC
)hfs_vnop_allocate
}, /* allocate */
3672 { &vnop_searchfs_desc
, (VOPFUNC
)hfs_vnop_search
}, /* search fs */
3673 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
}, /* bwrite */
3674 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* pagein */
3675 { &vnop_pageout_desc
,(VOPFUNC
) hfs_vnop_pageout
}, /* pageout */
3676 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3677 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3678 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3679 { &vnop_blockmap_desc
, (VOPFUNC
)hfs_vnop_blockmap
}, /* blockmap */
3680 { &vnop_kqfilt_add_desc
, (VOPFUNC
)hfs_vnop_kqfiltadd
}, /* kqfilt_add */
3681 { &vnop_kqfilt_remove_desc
, (VOPFUNC
)hfs_vnop_kqfiltremove
}, /* kqfilt_remove */
3682 { &vnop_getxattr_desc
, (VOPFUNC
)hfs_vnop_getxattr
},
3683 { &vnop_setxattr_desc
, (VOPFUNC
)hfs_vnop_setxattr
},
3684 { &vnop_removexattr_desc
, (VOPFUNC
)hfs_vnop_removexattr
},
3685 { &vnop_listxattr_desc
, (VOPFUNC
)hfs_vnop_listxattr
},
3686 { NULL
, (VOPFUNC
)NULL
}
3689 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3690 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
3692 int (**hfs_specop_p
)(void *);
3693 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
3694 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3695 { &vnop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
3696 { &vnop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
3697 { &vnop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
3698 { &vnop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
3699 { &vnop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
3700 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3701 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3702 { &vnop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
3703 { &vnop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
3704 { &vnop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
3705 { &vnop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
3706 { &vnop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
3707 { &vnop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
3708 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3709 { &vnop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
3710 { &vnop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
3711 { &vnop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
3712 { &vnop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
3713 { &vnop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
3714 { &vnop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
3715 { &vnop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
3716 { &vnop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
3717 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3718 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3719 { &vnop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
3720 { &vnop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
3721 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3722 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
},
3723 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* Pagein */
3724 { &vnop_pageout_desc
, (VOPFUNC
)hfs_vnop_pageout
}, /* Pageout */
3725 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3726 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3727 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3728 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3730 struct vnodeopv_desc hfs_specop_opv_desc
=
3731 { &hfs_specop_p
, hfs_specop_entries
};
3734 int (**hfs_fifoop_p
)(void *);
3735 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
3736 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3737 { &vnop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
3738 { &vnop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
3739 { &vnop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
3740 { &vnop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
3741 { &vnop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
3742 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3743 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3744 { &vnop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
3745 { &vnop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
3746 { &vnop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
3747 { &vnop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
3748 { &vnop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
3749 { &vnop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
3750 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3751 { &vnop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
3752 { &vnop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
3753 { &vnop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
3754 { &vnop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
3755 { &vnop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
3756 { &vnop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
3757 { &vnop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
3758 { &vnop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
3759 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3760 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3761 { &vnop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
3762 { &vnop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
3763 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3764 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
},
3765 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* Pagein */
3766 { &vnop_pageout_desc
, (VOPFUNC
)hfs_vnop_pageout
}, /* Pageout */
3767 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3768 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3769 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3770 { &vnop_blockmap_desc
, (VOPFUNC
)hfs_vnop_blockmap
}, /* blockmap */
3771 { &vnop_kqfilt_add_desc
, (VOPFUNC
)hfsfifo_kqfilt_add
}, /* kqfilt_add */
3772 { &vnop_kqfilt_remove_desc
, (VOPFUNC
)hfsfifo_kqfilt_remove
}, /* kqfilt_remove */
3773 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3775 struct vnodeopv_desc hfs_fifoop_opv_desc
=
3776 { &hfs_fifoop_p
, hfs_fifoop_entries
};