2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/file_internal.h>
32 #include <sys/dirent.h>
35 #include <sys/mount.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/malloc.h>
39 #include <sys/paths.h>
40 #include <sys/quota.h>
43 #include <sys/kauth.h>
44 #include <sys/uio_internal.h>
46 #include <miscfs/specfs/specdev.h>
47 #include <miscfs/fifofs/fifo.h>
48 #include <vfs/vfs_support.h>
49 #include <machine/spl.h>
51 #include <sys/kdebug.h>
52 #include <sys/sysctl.h>
55 #include "hfs_catalog.h"
56 #include "hfs_cnode.h"
58 #include "hfs_mount.h"
59 #include "hfs_quota.h"
60 #include "hfs_endian.h"
62 #include "hfscommon/headers/BTreesInternal.h"
63 #include "hfscommon/headers/FileMgrInternal.h"
65 #define MAKE_DELETED_NAME(NAME,FID) \
66 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
68 #define KNDETACH_VNLOCKED 0x00000001
70 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
73 /* Global vfs data structures for hfs */
75 /* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */
76 int always_do_fullfsync
= 0;
77 SYSCTL_INT (_kern
, OID_AUTO
, always_do_fullfsync
, CTLFLAG_RW
, &always_do_fullfsync
, 0, "always F_FULLFSYNC when fsync is called");
79 extern unsigned long strtoul(const char *, char **, int);
81 static int hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
,
82 struct componentname
*cnp
, struct vnode_attr
*vap
,
85 static int hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
);
87 static int hfs_removedir(struct vnode
*, struct vnode
*, struct componentname
*,
90 static int hfs_removefile(struct vnode
*, struct vnode
*, struct componentname
*,
93 static int hfs_vnop_close(struct vnop_close_args
*);
94 static int hfs_vnop_create(struct vnop_create_args
*);
95 static int hfs_vnop_exchange(struct vnop_exchange_args
*);
96 static int hfs_vnop_fsync(struct vnop_fsync_args
*);
97 static int hfs_vnop_mkdir(struct vnop_mkdir_args
*);
98 static int hfs_vnop_mknod(struct vnop_mknod_args
*);
99 static int hfs_vnop_getattr(struct vnop_getattr_args
*);
100 static int hfs_vnop_open(struct vnop_open_args
*);
101 static int hfs_vnop_readdir(struct vnop_readdir_args
*);
102 static int hfs_vnop_remove(struct vnop_remove_args
*);
103 static int hfs_vnop_rename(struct vnop_rename_args
*);
104 static int hfs_vnop_rmdir(struct vnop_rmdir_args
*);
105 static int hfs_vnop_symlink(struct vnop_symlink_args
*);
106 static int hfs_vnop_setattr(struct vnop_setattr_args
*);
108 /* Options for hfs_removedir and hfs_removefile */
109 #define HFSRM_SKIP_RESERVE 0x01
112 int hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
);
114 int hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
,
116 int hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
,
117 kauth_cred_t cred
, struct proc
*p
);
119 /*****************************************************************************
121 * Common Operations on vnodes
123 *****************************************************************************/
126 * Create a regular file.
129 hfs_vnop_create(struct vnop_create_args
*ap
)
131 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
135 * Make device special file.
138 hfs_vnop_mknod(struct vnop_mknod_args
*ap
)
140 struct vnode_attr
*vap
= ap
->a_vap
;
141 struct vnode
*dvp
= ap
->a_dvp
;
142 struct vnode
**vpp
= ap
->a_vpp
;
146 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
150 /* Create the vnode */
151 error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, vap
, ap
->a_context
);
156 cp
->c_touch_acctime
= TRUE
;
157 cp
->c_touch_chgtime
= TRUE
;
158 cp
->c_touch_modtime
= TRUE
;
160 if ((vap
->va_rdev
!= VNOVAL
) &&
161 (vap
->va_type
== VBLK
|| vap
->va_type
== VCHR
))
162 cp
->c_rdev
= vap
->va_rdev
;
168 * Open a file/directory.
171 hfs_vnop_open(struct vnop_open_args
*ap
)
173 struct vnode
*vp
= ap
->a_vp
;
179 * Files marked append-only must be opened for appending.
181 if ((VTOC(vp
)->c_flags
& APPEND
) && !vnode_isdir(vp
) &&
182 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
185 if (vnode_isreg(vp
) && !UBCINFOEXISTS(vp
))
186 return (EBUSY
); /* file is in use by the kernel */
188 /* Don't allow journal file to be opened externally. */
189 if (VTOC(vp
)->c_fileid
== VTOHFS(vp
)->hfs_jnlfileid
)
192 * On the first (non-busy) open of a fragmented
193 * file attempt to de-frag it (if its less than 20MB).
195 if ((VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
) ||
196 (VTOHFS(vp
)->jnl
== NULL
) ||
197 !vnode_isreg(vp
) || vnode_isinuse(vp
, 0)) {
201 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
205 fp
->ff_extents
[7].blockCount
!= 0 &&
206 fp
->ff_size
<= (20 * 1024 * 1024)) {
208 * Wait until system bootup is done (3 min).
211 if (tv
.tv_sec
> (60 * 3)) {
212 (void) hfs_relocate(vp
, VTOVCB(vp
)->nextAllocation
+ 4096,
213 vfs_context_ucred(ap
->a_context
),
214 vfs_context_proc(ap
->a_context
));
217 hfs_unlock(VTOC(vp
));
224 * Close a file/directory.
228 struct vnop_close_args
/* {
231 vfs_context_t a_context;
234 register struct vnode
*vp
= ap
->a_vp
;
235 register struct cnode
*cp
;
236 struct proc
*p
= vfs_context_proc(ap
->a_context
);
237 struct hfsmount
*hfsmp
;
240 if ( hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) != 0)
245 // if we froze the fs and we're exiting, then "thaw" the fs
246 if (hfsmp
->hfs_freezing_proc
== p
&& proc_exiting(p
)) {
247 hfsmp
->hfs_freezing_proc
= NULL
;
248 hfs_global_exclusive_lock_release(hfsmp
);
249 lck_rw_unlock_exclusive(&hfsmp
->hfs_insync
);
252 busy
= vnode_isinuse(vp
, 1);
255 hfs_touchtimes(VTOHFS(vp
), cp
);
257 if (vnode_isdir(vp
)) {
258 hfs_reldirhints(cp
, busy
);
259 } else if (vnode_issystem(vp
) && !busy
) {
268 * Get basic attributes.
271 hfs_vnop_getattr(struct vnop_getattr_args
*ap
)
273 struct vnode
*vp
= ap
->a_vp
;
274 struct vnode_attr
*vap
= ap
->a_vap
;
275 struct vnode
*rvp
= NULL
;
276 struct hfsmount
*hfsmp
;
281 if ((error
= hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
))) {
286 hfs_touchtimes(hfsmp
, cp
);
287 v_type
= vnode_vtype(vp
);
289 VATTR_RETURN(vap
, va_rdev
, (v_type
== VBLK
|| v_type
== VCHR
) ? cp
->c_rdev
: 0);
290 if (v_type
== VDIR
) {
291 if (VATTR_IS_ACTIVE(vap
, va_nlink
)) {
294 entries
= cp
->c_nlink
;
295 if (vnode_isvroot(vp
)) {
296 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
297 --entries
; /* hide private dir */
298 if (hfsmp
->jnl
|| ((HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
) && (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
299 entries
-= 2; /* hide the journal files */
301 VATTR_RETURN(vap
, va_nlink
, (uint64_t)entries
);
304 if (VATTR_IS_ACTIVE(vap
, va_nchildren
)) {
307 entries
= cp
->c_entries
;
308 if (vnode_isvroot(vp
)) {
309 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
310 --entries
; /* hide private dir */
312 entries
-= 2; /* hide the journal files */
314 VATTR_RETURN(vap
, va_nchildren
, entries
);
317 VATTR_RETURN(vap
, va_nlink
, (uint64_t)cp
->c_nlink
);
320 /* conditional because 64-bit arithmetic can be expensive */
321 if (VATTR_IS_ACTIVE(vap
, va_total_size
)) {
322 if (v_type
== VDIR
) {
323 VATTR_RETURN(vap
, va_total_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
325 uint64_t total_size
= 0;
328 if (cp
->c_datafork
) {
329 total_size
= cp
->c_datafork
->ff_size
;
332 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
333 /* hfs_vgetrsrc does not use struct proc - therefore passing NULL */
334 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, NULL
);
340 if (rcp
&& rcp
->c_rsrcfork
) {
341 total_size
+= rcp
->c_rsrcfork
->ff_size
;
345 VATTR_RETURN(vap
, va_total_size
, total_size
);
346 /* Include size of attibute data (extents), if any */
347 if (cp
->c_attrblks
) {
348 vap
->va_total_size
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
352 if (VATTR_IS_ACTIVE(vap
, va_total_alloc
)) {
353 if (v_type
== VDIR
) {
354 VATTR_RETURN(vap
, va_total_alloc
, 0);
356 VATTR_RETURN(vap
, va_total_alloc
, (uint64_t)cp
->c_blocks
* (uint64_t)hfsmp
->blockSize
);
357 /* Include size of attibute data (extents), if any */
358 if (cp
->c_attrblks
) {
359 vap
->va_total_alloc
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
363 /* XXX broken... if ask for "data size" of rsrc fork vp you get rsrc fork size! */
364 if (v_type
== VDIR
) {
365 VATTR_RETURN(vap
, va_data_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
367 VATTR_RETURN(vap
, va_data_size
, VTOF(vp
)->ff_size
);
369 if (VATTR_IS_ACTIVE(vap
, va_data_alloc
) && (v_type
!= VDIR
)) {
370 /* XXX do we need to account for ff_unallocblocks ? */
371 VATTR_RETURN(vap
, va_data_alloc
, (uint64_t)VTOF(vp
)->ff_blocks
* (uint64_t)hfsmp
->blockSize
);
373 /* XXX is this really a good 'optimal I/O size'? */
374 VATTR_RETURN(vap
, va_iosize
, hfsmp
->hfs_logBlockSize
);
375 VATTR_RETURN(vap
, va_uid
, cp
->c_uid
);
376 VATTR_RETURN(vap
, va_gid
, cp
->c_gid
);
377 VATTR_RETURN(vap
, va_mode
, cp
->c_mode
);
379 /* XXX is S_IFXATTR still needed ??? */
380 if (VNODE_IS_RSRC(vp
))
381 vap
->va_mode
|= S_IFXATTR
;
383 VATTR_RETURN(vap
, va_flags
, cp
->c_flags
);
386 * If the VFS wants extended security data, and we know that we
387 * don't have any (because it never told us it was setting any)
388 * then we can return the supported bit and no data. If we do
389 * have extended security, we can just leave the bit alone and
390 * the VFS will use the fallback path to fetch it.
392 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
393 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
394 vap
->va_acl
= KAUTH_FILESEC_NONE
;
395 VATTR_SET_SUPPORTED(vap
, va_acl
);
398 vap
->va_create_time
.tv_sec
= cp
->c_itime
;
399 vap
->va_create_time
.tv_nsec
= 0;
400 VATTR_SET_SUPPORTED(vap
, va_create_time
);
402 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
403 /* Access times are lazyily updated, get current time if needed */
404 if (cp
->c_touch_acctime
) {
408 vap
->va_access_time
.tv_sec
= tv
.tv_sec
;
410 vap
->va_access_time
.tv_sec
= cp
->c_atime
;
412 vap
->va_access_time
.tv_nsec
= 0;
413 VATTR_SET_SUPPORTED(vap
, va_access_time
);
415 vap
->va_modify_time
.tv_sec
= cp
->c_mtime
;
416 vap
->va_modify_time
.tv_nsec
= 0;
417 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
418 vap
->va_change_time
.tv_sec
= cp
->c_ctime
;
419 vap
->va_change_time
.tv_nsec
= 0;
420 VATTR_SET_SUPPORTED(vap
, va_change_time
);
421 vap
->va_backup_time
.tv_sec
= cp
->c_btime
;
422 vap
->va_backup_time
.tv_nsec
= 0;
423 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
426 * Exporting file IDs from HFS Plus:
428 * For "normal" files the c_fileid is the same value as the
429 * c_cnid. But for hard link files, they are different - the
430 * c_cnid belongs to the active directory entry (ie the link)
431 * and the c_fileid is for the actual inode (ie the data file).
433 * The stat call (getattr) uses va_fileid and the Carbon APIs,
434 * which are hardlink-ignorant, will ask for va_linkid.
436 VATTR_RETURN(vap
, va_fileid
, (uint64_t)cp
->c_fileid
);
437 VATTR_RETURN(vap
, va_linkid
, (uint64_t)cp
->c_cnid
);
438 VATTR_RETURN(vap
, va_parentid
, (uint64_t)cp
->c_parentcnid
);
439 VATTR_RETURN(vap
, va_fsid
, cp
->c_dev
);
440 VATTR_RETURN(vap
, va_filerev
, 0);
442 VATTR_RETURN(vap
, va_encoding
, cp
->c_encoding
);
444 /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
445 if (VATTR_IS_ACTIVE(vap
, va_name
) && !vnode_isvroot(vp
)) {
446 /* Return the name for ATTR_CMN_NAME */
447 if (cp
->c_desc
.cd_namelen
== 0) {
452 strncpy(vap
->va_name
, cp
->c_desc
.cd_nameptr
, MAXPATHLEN
);
453 vap
->va_name
[MAXPATHLEN
-1] = '\0';
454 VATTR_SET_SUPPORTED(vap
, va_name
);
467 struct vnop_setattr_args
/* {
469 struct vnode_attr *a_vap;
470 vfs_context_t a_context;
473 struct vnode_attr
*vap
= ap
->a_vap
;
474 struct vnode
*vp
= ap
->a_vp
;
475 struct cnode
*cp
= NULL
;
476 struct hfsmount
*hfsmp
;
477 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
478 struct proc
*p
= vfs_context_proc(ap
->a_context
);
485 /* Don't allow modification of the journal file. */
486 if (hfsmp
->hfs_jnlfileid
== VTOC(vp
)->c_fileid
) {
491 * File size change request.
492 * We are guaranteed that this is not a directory, and that
493 * the filesystem object is writeable.
495 VATTR_SET_SUPPORTED(vap
, va_data_size
);
496 if (VATTR_IS_ACTIVE(vap
, va_data_size
) && !vnode_islnk(vp
)) {
498 /* Take truncate lock before taking cnode lock. */
499 hfs_lock_truncate(VTOC(vp
), TRUE
);
500 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
501 hfs_unlock_truncate(VTOC(vp
));
506 error
= hfs_truncate(vp
, vap
->va_data_size
, vap
->va_vaflags
& 0xffff, 0, ap
->a_context
);
508 hfs_unlock_truncate(cp
);
513 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
519 * Owner/group change request.
520 * We are guaranteed that the new owner/group is valid and legal.
522 VATTR_SET_SUPPORTED(vap
, va_uid
);
523 VATTR_SET_SUPPORTED(vap
, va_gid
);
524 nuid
= VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: (uid_t
)VNOVAL
;
525 ngid
= VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: (gid_t
)VNOVAL
;
526 if (((nuid
!= (uid_t
)VNOVAL
) || (ngid
!= (gid_t
)VNOVAL
)) &&
527 ((error
= hfs_chown(vp
, nuid
, ngid
, cred
, p
)) != 0))
531 * Mode change request.
532 * We are guaranteed that the mode value is valid and that in
533 * conjunction with the owner and group, this change is legal.
535 VATTR_SET_SUPPORTED(vap
, va_mode
);
536 if (VATTR_IS_ACTIVE(vap
, va_mode
) &&
537 ((error
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
)) != 0))
542 * We are guaranteed that only flags allowed to change given the
543 * current securelevel are being changed.
545 VATTR_SET_SUPPORTED(vap
, va_flags
);
546 if (VATTR_IS_ACTIVE(vap
, va_flags
)) {
547 cp
->c_flags
= vap
->va_flags
;
548 cp
->c_touch_chgtime
= TRUE
;
552 * If the file's extended security data is being changed, we
553 * need to note the change. Note that because we don't store
554 * the data, we do not set the SUPPORTED bit; this will cause
555 * the VFS to use a fallback strategy.
557 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
558 /* Remember if any ACL data was set or cleared. */
559 if (vap
->va_acl
== NULL
) {
561 if (cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) {
562 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
563 cp
->c_touch_chgtime
= TRUE
;
567 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
568 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
569 cp
->c_touch_chgtime
= TRUE
;
577 VATTR_SET_SUPPORTED(vap
, va_create_time
);
578 VATTR_SET_SUPPORTED(vap
, va_access_time
);
579 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
580 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
581 VATTR_SET_SUPPORTED(vap
, va_change_time
);
582 if (VATTR_IS_ACTIVE(vap
, va_create_time
) ||
583 VATTR_IS_ACTIVE(vap
, va_access_time
) ||
584 VATTR_IS_ACTIVE(vap
, va_modify_time
) ||
585 VATTR_IS_ACTIVE(vap
, va_backup_time
)) {
588 if (VATTR_IS_ACTIVE(vap
, va_create_time
))
589 cp
->c_itime
= vap
->va_create_time
.tv_sec
;
590 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
591 cp
->c_atime
= vap
->va_access_time
.tv_sec
;
592 cp
->c_touch_acctime
= FALSE
;
594 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
595 cp
->c_mtime
= vap
->va_modify_time
.tv_sec
;
596 cp
->c_touch_modtime
= FALSE
;
597 cp
->c_touch_chgtime
= TRUE
;
600 * The utimes system call can reset the modification
601 * time but it doesn't know about HFS create times.
602 * So we need to ensure that the creation time is
603 * always at least as old as the modification time.
605 if ((VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
606 (cp
->c_cnid
!= kHFSRootFolderID
) &&
607 (cp
->c_mtime
< cp
->c_itime
)) {
608 cp
->c_itime
= cp
->c_mtime
;
611 if (VATTR_IS_ACTIVE(vap
, va_backup_time
))
612 cp
->c_btime
= vap
->va_backup_time
.tv_sec
;
613 cp
->c_flag
|= C_MODIFIED
;
619 VATTR_SET_SUPPORTED(vap
, va_encoding
);
620 if (VATTR_IS_ACTIVE(vap
, va_encoding
)) {
621 cp
->c_encoding
= vap
->va_encoding
;
622 hfs_setencodingbits(hfsmp
, cp
->c_encoding
);
626 if ((error
= hfs_update(vp
, TRUE
)) != 0)
628 HFS_KNOTE(vp
, NOTE_ATTRIB
);
637 * Change the mode on a file.
638 * cnode must be locked before calling.
642 hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
, struct proc
*p
)
644 register struct cnode
*cp
= VTOC(vp
);
647 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
650 // XXXdbg - don't allow modification of the journal or journal_info_block
651 if (VTOHFS(vp
)->jnl
&& cp
&& cp
->c_datafork
) {
652 struct HFSPlusExtentDescriptor
*extd
;
654 extd
= &cp
->c_datafork
->ff_extents
[0];
655 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
660 #if OVERRIDE_UNKNOWN_PERMISSIONS
661 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
) {
665 cp
->c_mode
&= ~ALLPERMS
;
666 cp
->c_mode
|= (mode
& ALLPERMS
);
667 cp
->c_touch_chgtime
= TRUE
;
674 hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
)
676 struct cnode
*cp
= VTOC(vp
);
681 * Disallow write attempts on read-only file systems;
682 * unless the file is a socket, fifo, or a block or
683 * character device resident on the file system.
685 switch (vnode_vtype(vp
)) {
689 if (VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
)
696 /* If immutable bit set, nobody gets to write it. */
697 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
700 /* Otherwise, user id 0 always gets access. */
701 if (!suser(cred
, NULL
))
704 /* Otherwise, check the owner. */
705 if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, false)) == 0)
706 return ((cp
->c_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
708 /* Otherwise, check the groups. */
709 if (kauth_cred_ismember_gid(cred
, cp
->c_gid
, &is_member
) == 0 && is_member
) {
710 return ((cp
->c_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
713 /* Otherwise, check everyone else. */
714 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
719 * Perform chown operation on cnode cp;
720 * code must be locked prior to call.
724 hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
, kauth_cred_t cred
,
727 register struct cnode
*cp
= VTOC(vp
);
737 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
740 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
)
743 if (uid
== (uid_t
)VNOVAL
)
745 if (gid
== (gid_t
)VNOVAL
)
748 #if 0 /* we are guaranteed that this is already the case */
750 * If we don't own the file, are trying to change the owner
751 * of the file, or are not a member of the target group,
752 * the caller must be superuser or the call fails.
754 if ((kauth_cred_getuid(cred
) != cp
->c_uid
|| uid
!= cp
->c_uid
||
756 (kauth_cred_ismember_gid(cred
, gid
, &is_member
) || !is_member
))) &&
757 (error
= suser(cred
, 0)))
764 if ((error
= hfs_getinoquota(cp
)))
767 dqrele(cp
->c_dquot
[USRQUOTA
]);
768 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
771 dqrele(cp
->c_dquot
[GRPQUOTA
]);
772 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
776 * Eventually need to account for (fake) a block per directory
777 * if (vnode_isdir(vp))
778 * change = VTOHFS(vp)->blockSize;
782 change
= (int64_t)(cp
->c_blocks
) * (int64_t)VTOVCB(vp
)->blockSize
;
783 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
);
784 (void) hfs_chkiq(cp
, -1, cred
, CHOWN
);
785 for (i
= 0; i
< MAXQUOTAS
; i
++) {
786 dqrele(cp
->c_dquot
[i
]);
787 cp
->c_dquot
[i
] = NODQUOT
;
793 if ((error
= hfs_getinoquota(cp
)) == 0) {
795 dqrele(cp
->c_dquot
[USRQUOTA
]);
796 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
799 dqrele(cp
->c_dquot
[GRPQUOTA
]);
800 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
802 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
803 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
806 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
808 for (i
= 0; i
< MAXQUOTAS
; i
++) {
809 dqrele(cp
->c_dquot
[i
]);
810 cp
->c_dquot
[i
] = NODQUOT
;
815 if (hfs_getinoquota(cp
) == 0) {
817 dqrele(cp
->c_dquot
[USRQUOTA
]);
818 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
821 dqrele(cp
->c_dquot
[GRPQUOTA
]);
822 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
824 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
825 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
826 (void) hfs_getinoquota(cp
);
830 if (hfs_getinoquota(cp
))
831 panic("hfs_chown: lost quota");
834 if (ouid
!= uid
|| ogid
!= gid
)
835 cp
->c_touch_chgtime
= TRUE
;
841 * The hfs_exchange routine swaps the fork data in two files by
842 * exchanging some of the information in the cnode. It is used
843 * to preserve the file ID when updating an existing file, in
844 * case the file is being tracked through its file ID. Typically
845 * its used after creating a new file during a safe-save.
848 hfs_vnop_exchange(ap
)
849 struct vnop_exchange_args
/* {
853 vfs_context_t a_context;
856 struct vnode
*from_vp
= ap
->a_fvp
;
857 struct vnode
*to_vp
= ap
->a_tvp
;
858 struct cnode
*from_cp
;
860 struct hfsmount
*hfsmp
;
861 struct cat_desc tempdesc
;
862 struct cat_attr tempattr
;
864 int error
= 0, started_tr
= 0, got_cookie
= 0;
867 /* The files must be on the same volume. */
868 if (vnode_mount(from_vp
) != vnode_mount(to_vp
))
871 if (from_vp
== to_vp
)
874 if ((error
= hfs_lockpair(VTOC(from_vp
), VTOC(to_vp
), HFS_EXCLUSIVE_LOCK
)))
877 from_cp
= VTOC(from_vp
);
879 hfsmp
= VTOHFS(from_vp
);
881 /* Only normal files can be exchanged. */
882 if (!vnode_isreg(from_vp
) || !vnode_isreg(to_vp
) ||
883 (from_cp
->c_flag
& C_HARDLINK
) || (to_cp
->c_flag
& C_HARDLINK
) ||
884 VNODE_IS_RSRC(from_vp
) || VNODE_IS_RSRC(to_vp
)) {
889 // XXXdbg - don't allow modification of the journal or journal_info_block
891 struct HFSPlusExtentDescriptor
*extd
;
893 if (from_cp
->c_datafork
) {
894 extd
= &from_cp
->c_datafork
->ff_extents
[0];
895 if (extd
->startBlock
== VTOVCB(from_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
901 if (to_cp
->c_datafork
) {
902 extd
= &to_cp
->c_datafork
->ff_extents
[0];
903 if (extd
->startBlock
== VTOVCB(to_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
910 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
916 * Reserve some space in the Catalog file.
918 bzero(&cookie
, sizeof(cookie
));
919 if ((error
= cat_preflight(hfsmp
, CAT_EXCHANGE
, &cookie
, vfs_context_proc(ap
->a_context
)))) {
924 /* The backend code always tries to delete the virtual
925 * extent id for exchanging files so we neeed to lock
926 * the extents b-tree.
928 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_EXTENTS
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
930 /* Do the exchange */
931 error
= ExchangeFileIDs(hfsmp
,
932 from_cp
->c_desc
.cd_nameptr
,
933 to_cp
->c_desc
.cd_nameptr
,
934 from_cp
->c_parentcnid
,
938 hfs_systemfile_unlock(hfsmp
, lockflags
);
941 * Note that we don't need to exchange any extended attributes
942 * since the attributes are keyed by file ID.
945 if (error
!= E_NONE
) {
946 error
= MacToVFSError(error
);
950 /* Purge the vnodes from the name cache */
952 cache_purge(from_vp
);
956 /* Save a copy of from attributes before swapping. */
957 bcopy(&from_cp
->c_desc
, &tempdesc
, sizeof(struct cat_desc
));
958 bcopy(&from_cp
->c_attr
, &tempattr
, sizeof(struct cat_attr
));
961 * Swap the descriptors and all non-fork related attributes.
962 * (except the modify date)
964 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
967 from_cp
->c_fileid
= from_cp
->c_cnid
;
968 from_cp
->c_itime
= to_cp
->c_itime
;
969 from_cp
->c_btime
= to_cp
->c_btime
;
970 from_cp
->c_atime
= to_cp
->c_atime
;
971 from_cp
->c_ctime
= to_cp
->c_ctime
;
972 from_cp
->c_gid
= to_cp
->c_gid
;
973 from_cp
->c_uid
= to_cp
->c_uid
;
974 from_cp
->c_flags
= to_cp
->c_flags
;
975 from_cp
->c_mode
= to_cp
->c_mode
;
976 from_cp
->c_attr
.ca_recflags
= to_cp
->c_attr
.ca_recflags
;
977 bcopy(to_cp
->c_finderinfo
, from_cp
->c_finderinfo
, 32);
979 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
981 to_cp
->c_fileid
= to_cp
->c_cnid
;
982 to_cp
->c_itime
= tempattr
.ca_itime
;
983 to_cp
->c_btime
= tempattr
.ca_btime
;
984 to_cp
->c_atime
= tempattr
.ca_atime
;
985 to_cp
->c_ctime
= tempattr
.ca_ctime
;
986 to_cp
->c_gid
= tempattr
.ca_gid
;
987 to_cp
->c_uid
= tempattr
.ca_uid
;
988 to_cp
->c_flags
= tempattr
.ca_flags
;
989 to_cp
->c_mode
= tempattr
.ca_mode
;
990 to_cp
->c_attr
.ca_recflags
= tempattr
.ca_recflags
;
991 bcopy(tempattr
.ca_finderinfo
, to_cp
->c_finderinfo
, 32);
993 /* Rehash the cnodes using their new file IDs */
994 hfs_chash_rehash(from_cp
, to_cp
);
997 * When a file moves out of "Cleanup At Startup"
998 * we can drop its NODUMP status.
1000 if ((from_cp
->c_flags
& UF_NODUMP
) &&
1001 (from_cp
->c_parentcnid
!= to_cp
->c_parentcnid
)) {
1002 from_cp
->c_flags
&= ~UF_NODUMP
;
1003 from_cp
->c_touch_chgtime
= TRUE
;
1005 if ((to_cp
->c_flags
& UF_NODUMP
) &&
1006 (to_cp
->c_parentcnid
!= from_cp
->c_parentcnid
)) {
1007 to_cp
->c_flags
&= ~UF_NODUMP
;
1008 to_cp
->c_touch_chgtime
= TRUE
;
1011 HFS_KNOTE(from_vp
, NOTE_ATTRIB
);
1012 HFS_KNOTE(to_vp
, NOTE_ATTRIB
);
1016 cat_postflight(hfsmp
, &cookie
, vfs_context_proc(ap
->a_context
));
1019 hfs_end_transaction(hfsmp
);
1022 hfs_unlockpair(from_cp
, to_cp
);
1028 * cnode must be locked
1032 hfs_fsync(struct vnode
*vp
, int waitfor
, int fullsync
, struct proc
*p
)
1034 struct cnode
*cp
= VTOC(vp
);
1035 struct filefork
*fp
= NULL
;
1037 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1041 int took_trunc_lock
= 0;
1043 wait
= (waitfor
== MNT_WAIT
);
1045 /* HFS directories don't have any data blocks. */
1046 if (vnode_isdir(vp
))
1050 * For system files flush the B-tree header and
1051 * for regular files write out any clusters
1053 if (vnode_issystem(vp
)) {
1054 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1056 if (hfsmp
->jnl
== NULL
) {
1057 BTFlushPath(VTOF(vp
));
1060 } else if (UBCINFOEXISTS(vp
)) {
1062 hfs_lock_truncate(cp
, TRUE
);
1063 took_trunc_lock
= 1;
1065 /* Don't hold cnode lock when calling into cluster layer. */
1066 (void) cluster_push(vp
, 0);
1068 hfs_lock(cp
, HFS_FORCE_LOCK
);
1071 * When MNT_WAIT is requested and the zero fill timeout
1072 * has expired then we must explicitly zero out any areas
1073 * that are currently marked invalid (holes).
1075 * Files with NODUMP can bypass zero filling here.
1077 if ((wait
|| (cp
->c_flag
& C_ZFWANTSYNC
)) &&
1078 ((cp
->c_flags
& UF_NODUMP
) == 0) &&
1079 UBCINFOEXISTS(vp
) && (fp
= VTOF(vp
)) &&
1080 cp
->c_zftimeout
!= 0) {
1082 if (tv
.tv_sec
< cp
->c_zftimeout
) {
1083 /* Remember that a force sync was requested. */
1084 cp
->c_flag
|= C_ZFWANTSYNC
;
1087 if (!took_trunc_lock
) {
1089 hfs_lock_truncate(cp
, TRUE
);
1090 hfs_lock(cp
, HFS_FORCE_LOCK
);
1091 took_trunc_lock
= 1;
1094 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
1095 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
1096 off_t start
= invalid_range
->rl_start
;
1097 off_t end
= invalid_range
->rl_end
;
1099 /* The range about to be written must be validated
1100 * first, so that VNOP_BLOCKMAP() will return the
1101 * appropriate mapping for the cluster code:
1103 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1105 /* Don't hold cnode lock when calling into cluster layer. */
1107 (void) cluster_write(vp
, (struct uio
*) 0,
1108 fp
->ff_size
, end
+ 1, start
, (off_t
)0,
1109 IO_HEADZEROFILL
| IO_NOZERODIRTY
| IO_NOCACHE
);
1110 hfs_lock(cp
, HFS_FORCE_LOCK
);
1111 cp
->c_flag
|= C_MODIFIED
;
1114 (void) cluster_push(vp
, 0);
1115 hfs_lock(cp
, HFS_FORCE_LOCK
);
1117 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1118 cp
->c_zftimeout
= 0;
1121 if (took_trunc_lock
)
1122 hfs_unlock_truncate(cp
);
1125 * if we have a journal and if journal_active() returns != 0 then the
1126 * we shouldn't do anything to a locked block (because it is part
1127 * of a transaction). otherwise we'll just go through the normal
1128 * code path and flush the buffer. note journal_active() can return
1129 * -1 if the journal is invalid -- however we still need to skip any
1130 * locked blocks as they get cleaned up when we finish the transaction
1131 * or close the journal.
1133 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1135 lockflag
= BUF_SKIP_LOCKED
;
1140 * Flush all dirty buffers associated with a vnode.
1142 buf_flushdirtyblks(vp
, wait
, lockflag
, "hfs_fsync");
1145 if (vnode_isreg(vp
) && vnode_issystem(vp
)) {
1146 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1148 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1150 cp
->c_touch_acctime
= FALSE
;
1151 cp
->c_touch_chgtime
= FALSE
;
1152 cp
->c_touch_modtime
= FALSE
;
1153 } else if ( !(vp
->v_flag
& VSWAP
) ) /* User file */ {
1154 retval
= hfs_update(vp
, wait
);
1156 /* When MNT_WAIT is requested push out any delayed meta data */
1157 if ((retval
== 0) && wait
&& cp
->c_hint
&&
1158 !ISSET(cp
->c_flag
, C_DELETED
| C_NOEXISTS
)) {
1159 hfs_metasync(VTOHFS(vp
), (daddr64_t
)cp
->c_hint
, p
);
1162 // make sure that we've really been called from the user
1163 // fsync() and if so push out any pending transactions
1164 // that this file might is a part of (and get them on
1166 if (fullsync
|| always_do_fullfsync
) {
1168 journal_flush(hfsmp
->jnl
);
1170 /* XXX need to pass context! */
1171 VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, NULL
);
1180 /* Sync an hfs catalog b-tree node */
1182 hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
)
1188 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1190 // XXXdbg - don't need to do this on a journaled volume
1195 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1197 * Look for a matching node that has been delayed
1198 * but is not part of a set (B_LOCKED).
1200 * BLK_ONLYVALID causes buf_getblk to return a
1201 * buf_t for the daddr64_t specified only if it's
1202 * currently resident in the cache... the size
1203 * parameter to buf_getblk is ignored when this flag
1206 bp
= buf_getblk(vp
, node
, 0, 0, 0, BLK_META
| BLK_ONLYVALID
);
1209 if ((buf_flags(bp
) & (B_LOCKED
| B_DELWRI
)) == B_DELWRI
)
1210 (void) VNOP_BWRITE(bp
);
1215 hfs_systemfile_unlock(hfsmp
, lockflags
);
1223 hfs_btsync_callback(struct buf
*bp
, void *dummy
)
1225 buf_clearflags(bp
, B_LOCKED
);
1226 (void) buf_bawrite(bp
);
1228 return(BUF_CLAIMED
);
1234 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1236 struct cnode
*cp
= VTOC(vp
);
1240 if (sync_transaction
)
1241 flags
|= BUF_SKIP_NONLOCKED
;
1243 * Flush all dirty buffers associated with b-tree.
1245 buf_iterate(vp
, hfs_btsync_callback
, flags
, 0);
1248 if (vnode_issystem(vp
) && (VTOF(vp
)->fcbBTCBPtr
!= NULL
))
1249 (void) BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1250 cp
->c_touch_acctime
= FALSE
;
1251 cp
->c_touch_chgtime
= FALSE
;
1252 cp
->c_touch_modtime
= FALSE
;
1258 * Remove a directory.
1262 struct vnop_rmdir_args
/* {
1263 struct vnode *a_dvp;
1265 struct componentname *a_cnp;
1266 vfs_context_t a_context;
1269 struct vnode
*dvp
= ap
->a_dvp
;
1270 struct vnode
*vp
= ap
->a_vp
;
1273 if (!vnode_isdir(vp
)) {
1279 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1282 error
= hfs_removedir(dvp
, vp
, ap
->a_cnp
, 0);
1284 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1290 * Remove a directory
1292 * Both dvp and vp cnodes are locked
1295 hfs_removedir(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1298 vfs_context_t ctx
= cnp
->cn_context
;
1299 struct proc
*p
= vfs_context_proc(ctx
);
1302 struct hfsmount
* hfsmp
;
1303 struct cat_desc desc
;
1304 cat_cookie_t cookie
;
1306 int error
= 0, started_tr
= 0, got_cookie
= 0;
1313 return (EINVAL
); /* cannot remove "." */
1316 (void)hfs_getinoquota(cp
);
1318 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1323 if (!skip_reserve
) {
1325 * Reserve some space in the Catalog file.
1327 bzero(&cookie
, sizeof(cookie
));
1328 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
))) {
1335 * Verify the directory is empty (and valid).
1336 * (Rmdir ".." won't be valid since
1337 * ".." will contain a reference to
1338 * the current directory and thus be
1341 if (cp
->c_entries
!= 0) {
1345 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1350 if (cp
->c_entries
> 0)
1351 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1353 /* Remove the entry from the namei cache: */
1357 * Protect against a race with rename by using the component
1358 * name passed in and parent id from dvp (instead of using
1359 * the cp->c_desc which may have changed).
1361 bzero(&desc
, sizeof(desc
));
1362 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1363 desc
.cd_namelen
= cnp
->cn_namelen
;
1364 desc
.cd_parentcnid
= dcp
->c_cnid
;
1365 desc
.cd_cnid
= cp
->c_cnid
;
1367 /* Remove entry from catalog */
1368 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1369 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1371 /* Delete any attributes, ignore errors */
1372 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1374 hfs_systemfile_unlock(hfsmp
, lockflags
);
1380 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1383 /* The parent lost a child */
1384 if (dcp
->c_entries
> 0)
1386 if (dcp
->c_nlink
> 0)
1388 dcp
->c_touch_chgtime
= TRUE
;
1389 dcp
->c_touch_modtime
= TRUE
;
1391 dcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - don't screw around, force this guy out
1393 (void) hfs_update(dvp
, 0);
1394 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
1396 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1398 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1399 cp
->c_flag
|= C_NOEXISTS
;
1401 HFS_KNOTE(vp
, NOTE_DELETE
);
1404 cat_postflight(hfsmp
, &cookie
, p
);
1407 hfs_end_transaction(hfsmp
);
1415 * Remove a file or link.
1419 struct vnop_remove_args
/* {
1420 struct vnode *a_dvp;
1422 struct componentname *a_cnp;
1424 vfs_context_t a_context;
1427 struct vnode
*dvp
= ap
->a_dvp
;
1428 struct vnode
*vp
= ap
->a_vp
;
1435 hfs_lock_truncate(VTOC(vp
), TRUE
);
1437 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1440 error
= hfs_removefile(dvp
, vp
, ap
->a_cnp
, ap
->a_flags
, 0);
1442 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1444 hfs_unlock_truncate(VTOC(vp
));
1450 hfs_removefile_callback(struct buf
*bp
, void *hfsmp
) {
1452 if ( !(buf_flags(bp
) & B_META
))
1453 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp
);
1455 * it's part of the current transaction, kill it.
1457 journal_kill_block(((struct hfsmount
*)hfsmp
)->jnl
, bp
);
1459 return (BUF_CLAIMED
);
1465 * Similar to hfs_vnop_remove except there are additional options.
1467 * Requires cnode and truncate locks to be held.
1470 hfs_removefile(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1471 int flags
, int skip_reserve
)
1473 struct vnode
*rvp
= NULL
;
1476 struct hfsmount
*hfsmp
;
1477 struct cat_desc desc
;
1479 vfs_context_t ctx
= cnp
->cn_context
;
1480 int dataforkbusy
= 0;
1481 int rsrcforkbusy
= 0;
1483 cat_cookie_t cookie
;
1486 int started_tr
= 0, got_cookie
= 0;
1488 cnid_t real_cnid
= 0;
1490 /* Directories should call hfs_rmdir! */
1491 if (vnode_isdir(vp
)) {
1499 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
1503 // if it's not a hardlink, check that the parent
1504 // cnid is the same as the directory cnid
1505 if ( (cp
->c_flag
& C_HARDLINK
) == 0
1506 && (cp
->c_parentcnid
!= hfsmp
->hfs_privdir_desc
.cd_cnid
)
1507 && (cp
->c_parentcnid
!= dcp
->c_cnid
)) {
1512 /* Make sure a remove is permitted */
1513 if (VNODE_IS_RSRC(vp
)) {
1519 * Aquire a vnode for a non-empty resource fork.
1520 * (needed for hfs_truncate)
1522 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1523 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, 0);
1528 // XXXdbg - don't allow deleting the journal or journal_info_block
1529 if (hfsmp
->jnl
&& cp
->c_datafork
) {
1530 struct HFSPlusExtentDescriptor
*extd
;
1532 extd
= &cp
->c_datafork
->ff_extents
[0];
1533 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
1540 * Check if this file is being used.
1542 if (vnode_isinuse(vp
, 0))
1544 if (rvp
&& vnode_isinuse(rvp
, 0))
1547 // need this to check if we have to break the deletion
1548 // into multiple pieces
1549 isbigfile
= (VTOC(vp
)->c_datafork
->ff_size
>= HFS_BIGFILE_SIZE
);
1552 * Carbon semantics prohibit deleting busy files.
1553 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1555 if (dataforkbusy
|| rsrcforkbusy
) {
1556 if ((flags
& VNODE_REMOVE_NODELETEBUSY
) ||
1557 (hfsmp
->hfs_privdir_desc
.cd_cnid
== 0)) {
1564 (void)hfs_getinoquota(cp
);
1568 * We do the ubc_setsize before the hfs_truncate
1569 * since we'll be inside a transaction.
1571 if ((cp
->c_flag
& C_HARDLINK
) == 0 &&
1572 (!dataforkbusy
|| !rsrcforkbusy
)) {
1574 * A ubc_setsize can cause a pagein here
1575 * so we need to the drop cnode lock. Note
1576 * that we still hold the truncate lock.
1579 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
&& !isbigfile
) {
1582 if (!rsrcforkbusy
&& rvp
) {
1583 ubc_setsize(rvp
, 0);
1585 hfs_lock(cp
, HFS_FORCE_LOCK
);
1587 struct cat_desc cndesc
;
1589 // for hard links, re-lookup the name that was passed
1590 // in so we get the correct cnid for the name (as
1591 // opposed to the c_cnid in the cnode which could have
1592 // been changed before this node got locked).
1593 bzero(&cndesc
, sizeof(cndesc
));
1594 cndesc
.cd_nameptr
= cnp
->cn_nameptr
;
1595 cndesc
.cd_namelen
= cnp
->cn_namelen
;
1596 cndesc
.cd_parentcnid
= VTOC(dvp
)->c_cnid
;
1597 cndesc
.cd_hint
= VTOC(dvp
)->c_childhint
;
1599 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1601 if (cat_lookup(hfsmp
, &cndesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
1602 hfs_systemfile_unlock(hfsmp
, lockflags
);
1607 hfs_systemfile_unlock(hfsmp
, lockflags
);
1610 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1615 if (!skip_reserve
) {
1617 * Reserve some space in the Catalog file.
1619 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, 0))) {
1625 /* Remove our entry from the namei cache. */
1628 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1629 if (hfsmp
->jnl
&& vnode_islnk(vp
))
1630 buf_iterate(vp
, hfs_removefile_callback
, BUF_SKIP_NONLOCKED
, (void *)hfsmp
);
1633 * Truncate any non-busy forks. Busy forks will
1634 * get trucated when their vnode goes inactive.
1636 * Since we're already inside a transaction,
1637 * tell hfs_truncate to skip the ubc_setsize.
1639 * (Note: hard links are truncated in VOP_INACTIVE)
1641 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1642 int mode
= cp
->c_mode
;
1644 if (!dataforkbusy
&& !isbigfile
&& cp
->c_datafork
->ff_blocks
!= 0) {
1645 cp
->c_mode
= 0; /* Suppress hfs_update */
1646 error
= hfs_truncate(vp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1652 if (!rsrcforkbusy
&& rvp
) {
1653 cp
->c_mode
= 0; /* Suppress hfs_update */
1654 error
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1663 * Protect against a race with rename by using the component
1664 * name passed in and parent id from dvp (instead of using
1665 * the cp->c_desc which may have changed).
1668 desc
.cd_encoding
= cp
->c_desc
.cd_encoding
;
1669 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1670 desc
.cd_namelen
= cnp
->cn_namelen
;
1671 desc
.cd_parentcnid
= dcp
->c_cnid
;
1672 desc
.cd_hint
= cp
->c_desc
.cd_hint
;
1674 // if it was a hardlink we had to re-lookup the cnid
1675 desc
.cd_cnid
= real_cnid
;
1677 desc
.cd_cnid
= cp
->c_cnid
;
1682 * There are 3 remove cases to consider:
1683 * 1. File is a hardlink ==> remove the link
1684 * 2. File is busy (in use) ==> move/rename the file
1685 * 3. File is not in use ==> remove the file
1688 if (cp
->c_flag
& C_HARDLINK
) {
1689 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1691 /* Delete the link record */
1692 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1694 /* Update the parent directory */
1695 if (dcp
->c_entries
> 0)
1697 if (dcp
->c_nlink
> 0)
1699 dcp
->c_ctime
= tv
.tv_sec
;
1700 dcp
->c_mtime
= tv
.tv_sec
;
1701 (void ) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1703 if (--cp
->c_nlink
< 1) {
1706 struct cat_desc to_desc
;
1707 struct cat_desc from_desc
;
1710 * This is now esentially an open deleted file.
1711 * Rename it to reflect this state which makes
1712 * orphan file cleanup easier (see hfs_remove_orphans).
1713 * Note: a rename failure here is not fatal.
1715 MAKE_INODE_NAME(inodename
, cp
->c_rdev
);
1716 bzero(&from_desc
, sizeof(from_desc
));
1717 from_desc
.cd_nameptr
= inodename
;
1718 from_desc
.cd_namelen
= strlen(inodename
);
1719 from_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1720 from_desc
.cd_flags
= 0;
1721 from_desc
.cd_cnid
= cp
->c_fileid
;
1723 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1724 bzero(&to_desc
, sizeof(to_desc
));
1725 to_desc
.cd_nameptr
= delname
;
1726 to_desc
.cd_namelen
= strlen(delname
);
1727 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1728 to_desc
.cd_flags
= 0;
1729 to_desc
.cd_cnid
= cp
->c_fileid
;
1731 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_privdir_desc
,
1732 &to_desc
, (struct cat_desc
*)NULL
);
1734 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1735 inodename
, delname
, cp
);
1738 /* Update the file's state */
1739 cp
->c_flag
|= C_DELETED
;
1740 cp
->c_ctime
= tv
.tv_sec
;
1741 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1744 /* Update the file's state */
1745 cp
->c_ctime
= tv
.tv_sec
;
1746 (void) cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, NULL
, NULL
);
1749 hfs_systemfile_unlock(hfsmp
, lockflags
);
1753 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1755 } else if (dataforkbusy
|| rsrcforkbusy
|| isbigfile
) {
1757 struct cat_desc to_desc
;
1758 struct cat_desc todir_desc
;
1761 * Orphan this file (move to hidden directory).
1763 bzero(&todir_desc
, sizeof(todir_desc
));
1764 todir_desc
.cd_parentcnid
= 2;
1766 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1767 bzero(&to_desc
, sizeof(to_desc
));
1768 to_desc
.cd_nameptr
= delname
;
1769 to_desc
.cd_namelen
= strlen(delname
);
1770 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1771 to_desc
.cd_flags
= 0;
1772 to_desc
.cd_cnid
= cp
->c_cnid
;
1774 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1776 error
= cat_rename(hfsmp
, &desc
, &todir_desc
,
1777 &to_desc
, (struct cat_desc
*)NULL
);
1780 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1781 (void) cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1782 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1784 /* Update the parent directory */
1785 if (dcp
->c_entries
> 0)
1787 if (dcp
->c_nlink
> 0)
1789 dcp
->c_ctime
= tv
.tv_sec
;
1790 dcp
->c_mtime
= tv
.tv_sec
;
1791 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1793 /* Update the file's state */
1794 cp
->c_flag
|= C_DELETED
;
1795 cp
->c_ctime
= tv
.tv_sec
;
1797 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1799 hfs_systemfile_unlock(hfsmp
, lockflags
);
1803 } else /* Not busy */ {
1805 if (cp
->c_blocks
> 0) {
1806 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1807 cp
->c_desc
.cd_nameptr
);
1812 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1814 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1816 if (error
&& error
!= ENXIO
&& error
!= ENOENT
&& truncated
) {
1817 if ((cp
->c_datafork
&& cp
->c_datafork
->ff_size
!= 0) ||
1818 (cp
->c_rsrcfork
&& cp
->c_rsrcfork
->ff_size
!= 0)) {
1819 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1820 error
, cp
->c_datafork
->ff_size
, cp
->c_rsrcfork
->ff_size
);
1822 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1823 cp
->c_desc
.cd_nameptr
, cp
->c_attr
.ca_fileid
, error
);
1827 /* Delete any attributes, ignore errors */
1828 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1830 /* Update the parent directory */
1831 if (dcp
->c_entries
> 0)
1833 if (dcp
->c_nlink
> 0)
1835 dcp
->c_ctime
= tv
.tv_sec
;
1836 dcp
->c_mtime
= tv
.tv_sec
;
1837 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1839 hfs_systemfile_unlock(hfsmp
, lockflags
);
1844 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1848 truncated
= 0; // because the catalog entry is gone
1849 cp
->c_flag
|= C_NOEXISTS
;
1850 cp
->c_touch_chgtime
= TRUE
; /* XXX needed ? */
1853 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1857 * All done with this cnode's descriptor...
1859 * Note: all future catalog calls for this cnode must be
1860 * by fileid only. This is OK for HFS (which doesn't have
1861 * file thread records) since HFS doesn't support hard
1862 * links or the removal of busy files.
1864 cat_releasedesc(&cp
->c_desc
);
1866 HFS_KNOTE(dvp
, NOTE_WRITE
);
1870 cat_postflight(hfsmp
, &cookie
, 0);
1873 /* Commit the truncation to the catalog record */
1875 cp
->c_flag
|= C_FORCEUPDATE
;
1876 cp
->c_touch_chgtime
= TRUE
;
1877 cp
->c_touch_modtime
= TRUE
;
1878 (void) hfs_update(vp
, 0);
1882 hfs_end_transaction(hfsmp
);
1885 HFS_KNOTE(vp
, NOTE_DELETE
);
1887 HFS_KNOTE(rvp
, NOTE_DELETE
);
1888 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1889 cp
->c_flag
|= C_NEED_RVNODE_PUT
;
1896 __private_extern__
void
1897 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1899 if (&cp
->c_desc
== cdp
) {
1903 /* First release allocated name buffer */
1904 if (cp
->c_desc
.cd_flags
& CD_HASBUF
&& cp
->c_desc
.cd_nameptr
!= 0) {
1905 char *name
= cp
->c_desc
.cd_nameptr
;
1907 cp
->c_desc
.cd_nameptr
= 0;
1908 cp
->c_desc
.cd_namelen
= 0;
1909 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
1910 vfs_removename(name
);
1912 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1914 /* Cnode now owns the name buffer */
1915 cdp
->cd_nameptr
= 0;
1916 cdp
->cd_namelen
= 0;
1917 cdp
->cd_flags
&= ~CD_HASBUF
;
1924 * The VFS layer guarantees that:
1925 * - source and destination will either both be directories, or
1926 * both not be directories.
1927 * - all the vnodes are from the same file system
1929 * When the target is a directory, HFS must ensure that its empty.
1933 struct vnop_rename_args
/* {
1934 struct vnode *a_fdvp;
1935 struct vnode *a_fvp;
1936 struct componentname *a_fcnp;
1937 struct vnode *a_tdvp;
1938 struct vnode *a_tvp;
1939 struct componentname *a_tcnp;
1940 vfs_context_t a_context;
1943 struct vnode
*tvp
= ap
->a_tvp
;
1944 struct vnode
*tdvp
= ap
->a_tdvp
;
1945 struct vnode
*fvp
= ap
->a_fvp
;
1946 struct vnode
*fdvp
= ap
->a_fdvp
;
1947 struct componentname
*tcnp
= ap
->a_tcnp
;
1948 struct componentname
*fcnp
= ap
->a_fcnp
;
1949 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1954 struct cat_desc from_desc
;
1955 struct cat_desc to_desc
;
1956 struct cat_desc out_desc
;
1957 struct hfsmount
*hfsmp
;
1958 cat_cookie_t cookie
;
1959 int tvp_deleted
= 0;
1960 int started_tr
= 0, got_cookie
= 0;
1961 int took_trunc_lock
= 0;
1965 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1966 if (tvp
&& vnode_isreg(tvp
)) {
1967 hfs_lock_truncate(VTOC(tvp
), TRUE
);
1968 took_trunc_lock
= 1;
1971 error
= hfs_lockfour(VTOC(fdvp
), VTOC(fvp
), VTOC(tdvp
), tvp
? VTOC(tvp
) : NULL
,
1972 HFS_EXCLUSIVE_LOCK
);
1974 if (took_trunc_lock
)
1975 hfs_unlock_truncate(VTOC(tvp
));
1982 tcp
= tvp
? VTOC(tvp
) : NULL
;
1983 hfsmp
= VTOHFS(tdvp
);
1985 /* Check for a race against unlink. */
1986 if (fcp
->c_flag
& C_NOEXISTS
) {
1992 * The following edge case is caught here:
1993 * (to cannot be a descendent of from)
2006 if (tdcp
->c_parentcnid
== fcp
->c_cnid
) {
2012 * The following two edge cases are caught here:
2013 * (note tvp is not empty)
2026 if (tvp
&& vnode_isdir(tvp
) && (tcp
->c_entries
!= 0) && fvp
!= tvp
) {
2032 * The following edge case is caught here:
2033 * (the from child and parent are the same)
2046 * Make sure "from" vnode and its parent are changeable.
2048 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
2054 * If the destination parent directory is "sticky", then the
2055 * user must own the parent directory, or the destination of
2056 * the rename, otherwise the destination may not be changed
2057 * (except by root). This implements append-only directories.
2059 * Note that checks for immutable and write access are done
2060 * by the call to hfs_removefile.
2062 if (tvp
&& (tdcp
->c_mode
& S_ISTXT
) &&
2063 (suser(vfs_context_ucred(tcnp
->cn_context
), NULL
)) &&
2064 (kauth_cred_getuid(vfs_context_ucred(tcnp
->cn_context
)) != tdcp
->c_uid
) &&
2065 (hfs_owner_rights(hfsmp
, tcp
->c_uid
, vfs_context_ucred(tcnp
->cn_context
), p
, false)) ) {
2072 (void)hfs_getinoquota(tcp
);
2074 /* Preflighting done, take fvp out of the name space. */
2078 * When a file moves out of "Cleanup At Startup"
2079 * we can drop its NODUMP status.
2081 if ((fcp
->c_flags
& UF_NODUMP
) &&
2084 (fdcp
->c_desc
.cd_nameptr
!= NULL
) &&
2085 (strcmp(fdcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
2086 fcp
->c_flags
&= ~UF_NODUMP
;
2087 fcp
->c_touch_chgtime
= TRUE
;
2088 (void) hfs_update(fvp
, 0);
2091 bzero(&from_desc
, sizeof(from_desc
));
2092 from_desc
.cd_nameptr
= fcnp
->cn_nameptr
;
2093 from_desc
.cd_namelen
= fcnp
->cn_namelen
;
2094 from_desc
.cd_parentcnid
= fdcp
->c_cnid
;
2095 from_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2096 from_desc
.cd_cnid
= fcp
->c_cnid
;
2098 bzero(&to_desc
, sizeof(to_desc
));
2099 to_desc
.cd_nameptr
= tcnp
->cn_nameptr
;
2100 to_desc
.cd_namelen
= tcnp
->cn_namelen
;
2101 to_desc
.cd_parentcnid
= tdcp
->c_cnid
;
2102 to_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2103 to_desc
.cd_cnid
= fcp
->c_cnid
;
2105 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2110 // if it's a hardlink then re-lookup the name so
2111 // that we get the correct cnid in from_desc (see
2112 // the comment in hfs_removefile for more details)
2114 if (fcp
->c_flag
& C_HARDLINK
) {
2115 struct cat_desc tmpdesc
;
2118 bzero(&tmpdesc
, sizeof(tmpdesc
));
2119 tmpdesc
.cd_nameptr
= fcnp
->cn_nameptr
;
2120 tmpdesc
.cd_namelen
= fcnp
->cn_namelen
;
2121 tmpdesc
.cd_parentcnid
= fdcp
->c_cnid
;
2122 tmpdesc
.cd_hint
= fdcp
->c_childhint
;
2124 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2126 if (cat_lookup(hfsmp
, &tmpdesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
2127 hfs_systemfile_unlock(hfsmp
, lockflags
);
2131 // use the real cnid instead of whatever happened to be there
2132 from_desc
.cd_cnid
= real_cnid
;
2133 hfs_systemfile_unlock(hfsmp
, lockflags
);
2137 * Reserve some space in the Catalog file.
2139 bzero(&cookie
, sizeof(cookie
));
2140 if ((error
= cat_preflight(hfsmp
, CAT_RENAME
+ CAT_DELETE
, &cookie
, p
))) {
2146 * If the destination exists then it may need to be removed.
2150 * When fvp matches tvp they must be case variants
2155 * If this a hard link with different parents
2156 * and its not a case variant then tvp should
2159 if (!((fcp
->c_flag
& C_HARDLINK
) &&
2161 (hfs_namecmp(fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
2162 tcnp
->cn_nameptr
, tcnp
->cn_namelen
) != 0)))) {
2169 if (vnode_isdir(tvp
))
2170 error
= hfs_removedir(tdvp
, tvp
, tcnp
, HFSRM_SKIP_RESERVE
);
2172 error
= hfs_removefile(tdvp
, tvp
, tcnp
, 0, HFSRM_SKIP_RESERVE
);
2181 * All done with tvp and fvp
2184 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2185 error
= cat_rename(hfsmp
, &from_desc
, &tdcp
->c_desc
, &to_desc
, &out_desc
);
2186 hfs_systemfile_unlock(hfsmp
, lockflags
);
2192 /* Invalidate negative cache entries in the destination directory */
2193 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
2194 cache_purge_negatives(tdvp
);
2196 /* Update cnode's catalog descriptor */
2197 replace_desc(fcp
, &out_desc
);
2198 fcp
->c_parentcnid
= tdcp
->c_cnid
;
2201 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_RMDIR
: VOL_RMFILE
,
2202 (fdcp
->c_cnid
== kHFSRootFolderID
));
2203 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_MKDIR
: VOL_MKFILE
,
2204 (tdcp
->c_cnid
== kHFSRootFolderID
));
2206 /* Update both parent directories. */
2210 if (fdcp
->c_nlink
> 0)
2212 if (fdcp
->c_entries
> 0)
2214 fdcp
->c_touch_chgtime
= TRUE
;
2215 fdcp
->c_touch_modtime
= TRUE
;
2217 fdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2218 (void) hfs_update(fdvp
, 0);
2220 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2221 tdcp
->c_touch_chgtime
= TRUE
;
2222 tdcp
->c_touch_modtime
= TRUE
;
2224 tdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2225 (void) hfs_update(tdvp
, 0);
2228 cat_postflight(hfsmp
, &cookie
, p
);
2231 hfs_end_transaction(hfsmp
);
2234 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2235 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2238 HFS_KNOTE(fvp
, NOTE_RENAME
);
2239 HFS_KNOTE(fdvp
, NOTE_WRITE
);
2240 if (tdvp
!= fdvp
) HFS_KNOTE(tdvp
, NOTE_WRITE
);
2243 if (took_trunc_lock
)
2244 hfs_unlock_truncate(VTOC(tvp
));
2246 hfs_unlockfour(fdcp
, fcp
, tdcp
, tcp
);
2248 /* After tvp is removed the only acceptable error is EIO */
2249 if (error
&& tvp_deleted
)
2260 hfs_vnop_mkdir(struct vnop_mkdir_args
*ap
)
2262 /***** HACK ALERT ********/
2263 ap
->a_cnp
->cn_flags
|= MAKEENTRY
;
2264 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
2269 * Create a symbolic link.
2272 hfs_vnop_symlink(struct vnop_symlink_args
*ap
)
2274 struct vnode
**vpp
= ap
->a_vpp
;
2275 struct vnode
*dvp
= ap
->a_dvp
;
2276 struct vnode
*vp
= NULL
;
2277 struct hfsmount
*hfsmp
;
2278 struct filefork
*fp
;
2279 struct buf
*bp
= NULL
;
2284 /* HFS standard disks don't support symbolic links */
2285 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
)
2288 /* Check for empty target name */
2289 if (ap
->a_target
[0] == 0)
2292 /* Create the vnode */
2293 ap
->a_vap
->va_mode
|= S_IFLNK
;
2294 if ((error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
))) {
2298 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2301 hfsmp
= VTOHFS(dvp
);
2302 len
= strlen(ap
->a_target
);
2305 (void)hfs_getinoquota(VTOC(vp
));
2308 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2314 * Allocate space for the link.
2316 * Since we're already inside a transaction,
2317 * tell hfs_truncate to skip the ubc_setsize.
2319 * Don't need truncate lock since a symlink is treated as a system file.
2321 error
= hfs_truncate(vp
, len
, IO_NOZEROFILL
, 1, ap
->a_context
);
2323 goto out
; /* XXX need to remove link */
2325 /* Write the link to disk */
2326 bp
= buf_getblk(vp
, (daddr64_t
)0, roundup((int)fp
->ff_size
, VTOHFS(vp
)->hfs_phys_block_size
),
2329 journal_modify_block_start(hfsmp
->jnl
, bp
);
2331 datap
= (char *)buf_dataptr(bp
);
2332 bzero(datap
, buf_size(bp
));
2333 bcopy(ap
->a_target
, datap
, len
);
2336 journal_modify_block_end(hfsmp
->jnl
, bp
);
2341 * We defered the ubc_setsize for hfs_truncate
2342 * since we were inside a transaction.
2344 * We don't need to drop the cnode lock here
2345 * since this is a symlink.
2347 ubc_setsize(vp
, len
);
2350 hfs_end_transaction(hfsmp
);
2352 hfs_unlock(VTOC(vp
));
2358 /* structures to hold a "." or ".." directory entry */
2359 struct hfs_stddotentry
{
2360 u_int32_t d_fileno
; /* unique file number */
2361 u_int16_t d_reclen
; /* length of this structure */
2362 u_int8_t d_type
; /* dirent file type */
2363 u_int8_t d_namlen
; /* len of filename */
2364 char d_name
[4]; /* "." or ".." */
2367 struct hfs_extdotentry
{
2368 u_int64_t d_fileno
; /* unique file number */
2369 u_int64_t d_seekoff
; /* seek offset (optional, used by servers) */
2370 u_int16_t d_reclen
; /* length of this structure */
2371 u_int16_t d_namlen
; /* len of filename */
2372 u_int8_t d_type
; /* dirent file type */
2373 u_char d_name
[3]; /* "." or ".." */
2377 struct hfs_stddotentry std
;
2378 struct hfs_extdotentry ext
;
2382 * hfs_vnop_readdir reads directory entries into the buffer pointed
2383 * to by uio, in a filesystem independent format. Up to uio_resid
2384 * bytes of data can be transferred. The data in the buffer is a
2385 * series of packed dirent structures where each one contains the
2386 * following entries:
2388 * u_int32_t d_fileno; // file number of entry
2389 * u_int16_t d_reclen; // length of this record
2390 * u_int8_t d_type; // file type
2391 * u_int8_t d_namlen; // length of string in d_name
2392 * char d_name[MAXNAMELEN+1]; // null terminated file name
2394 * The current position (uio_offset) refers to the next block of
2395 * entries. The offset can only be set to a value previously
2396 * returned by hfs_vnop_readdir or zero. This offset does not have
2397 * to match the number of bytes returned (in uio_resid).
2399 * In fact, the offset used by HFS is essentially an index (26 bits)
2400 * with a tag (6 bits). The tag is for associating the next request
2401 * with the current request. This enables us to have multiple threads
2402 * reading the directory while the directory is also being modified.
2404 * Each tag/index pair is tied to a unique directory hint. The hint
2405 * contains information (filename) needed to build the catalog b-tree
2406 * key for finding the next set of entries.
2409 hfs_vnop_readdir(ap
)
2410 struct vnop_readdir_args
/* {
2416 vfs_context_t a_context;
2419 struct vnode
*vp
= ap
->a_vp
;
2420 uio_t uio
= ap
->a_uio
;
2422 struct hfsmount
*hfsmp
;
2423 directoryhint_t
*dirhint
= NULL
;
2424 directoryhint_t localhint
;
2429 user_addr_t user_start
= 0;
2430 user_size_t user_len
= 0;
2438 cnid_t cnid_hint
= 0;
2441 startoffset
= offset
= uio_offset(uio
);
2442 bufstart
= CAST_DOWN(caddr_t
, uio_iov_base(uio
));
2443 extended
= (ap
->a_flags
& VNODE_READDIR_EXTENDED
);
2444 nfs_cookies
= extended
&& (ap
->a_flags
& VNODE_READDIR_REQSEEKOFF
);
2446 /* Sanity check the uio data. */
2447 if ((uio_iovcnt(uio
) > 1) ||
2448 (uio_resid(uio
) < (int)sizeof(struct dirent
))) {
2451 /* Note that the dirhint calls require an exclusive lock. */
2452 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2457 /* Pick up cnid hint (if any). */
2459 cnid_hint
= (cnid_t
)(uio_offset(uio
) >> 32);
2460 uio_setoffset(uio
, uio_offset(uio
) & 0x00000000ffffffffLL
);
2461 if (cnid_hint
== INT_MAX
) { /* searching pass the last item */
2467 * Synthesize entries for "." and ".."
2470 hfs_dotentry_t dotentry
[2];
2474 struct hfs_extdotentry
*entry
= &dotentry
[0].ext
;
2476 entry
->d_fileno
= cp
->c_cnid
;
2477 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2478 entry
->d_type
= DT_DIR
;
2479 entry
->d_namlen
= 1;
2480 entry
->d_name
[0] = '.';
2481 entry
->d_name
[1] = '\0';
2482 entry
->d_name
[2] = '\0';
2483 entry
->d_seekoff
= 1;
2486 entry
->d_fileno
= cp
->c_parentcnid
;
2487 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2488 entry
->d_type
= DT_DIR
;
2489 entry
->d_namlen
= 2;
2490 entry
->d_name
[0] = '.';
2491 entry
->d_name
[1] = '.';
2492 entry
->d_name
[2] = '\0';
2493 entry
->d_seekoff
= 2;
2494 uiosize
= 2 * sizeof(struct hfs_extdotentry
);
2496 struct hfs_stddotentry
*entry
= &dotentry
[0].std
;
2498 entry
->d_fileno
= cp
->c_cnid
;
2499 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2500 entry
->d_type
= DT_DIR
;
2501 entry
->d_namlen
= 1;
2502 *(int *)&entry
->d_name
[0] = 0;
2503 entry
->d_name
[0] = '.';
2506 entry
->d_fileno
= cp
->c_parentcnid
;
2507 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2508 entry
->d_type
= DT_DIR
;
2509 entry
->d_namlen
= 2;
2510 *(int *)&entry
->d_name
[0] = 0;
2511 entry
->d_name
[0] = '.';
2512 entry
->d_name
[1] = '.';
2513 uiosize
= 2 * sizeof(struct hfs_stddotentry
);
2515 if ((error
= uiomove((caddr_t
)&dotentry
, uiosize
, uio
))) {
2521 /* If there are no real entries then we're done. */
2522 if (cp
->c_entries
== 0) {
2525 uio_setoffset(uio
, offset
);
2530 // We have to lock the user's buffer here so that we won't
2531 // fault on it after we've acquired a shared lock on the
2532 // catalog file. The issue is that you can get a 3-way
2533 // deadlock if someone else starts a transaction and then
2534 // tries to lock the catalog file but can't because we're
2535 // here and we can't service our page fault because VM is
2536 // blocked trying to start a transaction as a result of
2537 // trying to free up pages for our page fault. It's messy
2538 // but it does happen on dual-procesors that are paging
2539 // heavily (see radar 3082639 for more info). By locking
2540 // the buffer up-front we prevent ourselves from faulting
2541 // while holding the shared catalog file lock.
2543 // Fortunately this and hfs_search() are the only two places
2544 // currently (10/30/02) that can fault on user data with a
2545 // shared lock on the catalog file.
2547 if (hfsmp
->jnl
&& uio_isuserspace(uio
)) {
2548 user_start
= uio_curriovbase(uio
);
2549 user_len
= uio_curriovlen(uio
);
2551 if ((error
= vslock(user_start
, user_len
)) != 0) {
2556 /* Convert offset into a catalog directory index. */
2557 index
= (offset
& HFS_INDEX_MASK
) - 2;
2558 tag
= offset
& ~HFS_INDEX_MASK
;
2560 /* Lock catalog during cat_findname and cat_getdirentries. */
2561 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2563 /* When called from NFS, try and resolve a cnid hint. */
2564 if (nfs_cookies
&& cnid_hint
!= 0) {
2565 if (cat_findname(hfsmp
, cnid_hint
, &localhint
.dh_desc
) == 0) {
2566 if ( localhint
.dh_desc
.cd_parentcnid
== cp
->c_cnid
) {
2567 localhint
.dh_index
= index
- 1;
2568 localhint
.dh_time
= 0;
2569 bzero(&localhint
.dh_link
, sizeof(localhint
.dh_link
));
2570 dirhint
= &localhint
; /* don't forget to release the descriptor */
2572 cat_releasedesc(&localhint
.dh_desc
);
2577 /* Get a directory hint (cnode must be locked exclusive) */
2578 if (dirhint
== NULL
) {
2579 dirhint
= hfs_getdirhint(cp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
);
2581 /* Hide tag from catalog layer. */
2582 dirhint
->dh_index
&= HFS_INDEX_MASK
;
2583 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
2584 dirhint
->dh_index
= -1;
2588 /* Pack the buffer with dirent entries. */
2589 error
= cat_getdirentries(hfsmp
, cp
->c_entries
, dirhint
, uio
, extended
, &items
, &eofflag
);
2591 hfs_systemfile_unlock(hfsmp
, lockflags
);
2597 /* Get index to the next item */
2600 if (items
>= (int)cp
->c_entries
) {
2604 /* Convert catalog directory index back into an offset. */
2606 tag
= (++cp
->c_dirhinttag
) << HFS_INDEX_BITS
;
2607 uio_setoffset(uio
, (index
+ 2) | tag
);
2608 dirhint
->dh_index
|= tag
;
2611 cp
->c_touch_acctime
= TRUE
;
2613 if (ap
->a_numdirent
) {
2614 if (startoffset
== 0)
2616 *ap
->a_numdirent
= items
;
2620 if (hfsmp
->jnl
&& user_start
) {
2621 vsunlock(user_start
, user_len
, TRUE
);
2623 /* If we didn't do anything then go ahead and dump the hint. */
2624 if ((dirhint
!= NULL
) &&
2625 (dirhint
!= &localhint
) &&
2626 (uio_offset(uio
) == startoffset
)) {
2627 hfs_reldirhint(cp
, dirhint
);
2630 if (ap
->a_eofflag
) {
2631 *ap
->a_eofflag
= eofflag
;
2633 if (dirhint
== &localhint
) {
2634 cat_releasedesc(&localhint
.dh_desc
);
2642 * Read contents of a symbolic link.
2645 hfs_vnop_readlink(ap
)
2646 struct vnop_readlink_args
/* {
2649 vfs_context_t a_context;
2652 struct vnode
*vp
= ap
->a_vp
;
2654 struct filefork
*fp
;
2657 if (!vnode_islnk(vp
))
2660 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2665 /* Zero length sym links are not allowed */
2666 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2667 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2672 /* Cache the path so we don't waste buffer cache resources */
2673 if (fp
->ff_symlinkptr
== NULL
) {
2674 struct buf
*bp
= NULL
;
2676 MALLOC(fp
->ff_symlinkptr
, char *, fp
->ff_size
, M_TEMP
, M_WAITOK
);
2677 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0,
2678 roundup((int)fp
->ff_size
,
2679 VTOHFS(vp
)->hfs_phys_block_size
),
2680 vfs_context_ucred(ap
->a_context
), &bp
);
2684 if (fp
->ff_symlinkptr
) {
2685 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2686 fp
->ff_symlinkptr
= NULL
;
2690 bcopy((char *)buf_dataptr(bp
), fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2692 if (VTOHFS(vp
)->jnl
&& (buf_flags(bp
) & B_LOCKED
) == 0) {
2693 buf_markinvalid(bp
); /* data no longer needed */
2697 error
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2700 * Keep track blocks read
2702 if ((VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) && (error
== 0)) {
2705 * If this file hasn't been seen since the start of
2706 * the current sampling period then start over.
2708 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
)
2709 VTOF(vp
)->ff_bytesread
= fp
->ff_size
;
2711 VTOF(vp
)->ff_bytesread
+= fp
->ff_size
;
2713 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2714 // cp->c_touch_acctime = TRUE;
2724 * Get configurable pathname variables.
2727 hfs_vnop_pathconf(ap
)
2728 struct vnop_pathconf_args
/* {
2732 vfs_context_t a_context;
2735 switch (ap
->a_name
) {
2737 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2740 *ap
->a_retval
= HFS_LINK_MAX
;
2743 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2744 *ap
->a_retval
= kHFSMaxFileNameChars
; /* 255 */
2746 *ap
->a_retval
= kHFSPlusMaxFileNameChars
; /* 31 */
2749 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2752 *ap
->a_retval
= PIPE_BUF
;
2754 case _PC_CHOWN_RESTRICTED
:
2760 case _PC_NAME_CHARS_MAX
:
2761 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2763 case _PC_CASE_SENSITIVE
:
2764 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_CASE_SENSITIVE
)
2769 case _PC_CASE_PRESERVING
:
2781 * Update a cnode's on-disk metadata.
2783 * If waitfor is set, then wait for the disk write of
2784 * the node to complete.
2786 * The cnode must be locked exclusive
2790 hfs_update(struct vnode
*vp
, __unused
int waitfor
)
2792 struct cnode
*cp
= VTOC(vp
);
2794 struct cat_fork
*dataforkp
= NULL
;
2795 struct cat_fork
*rsrcforkp
= NULL
;
2796 struct cat_fork datafork
;
2797 struct hfsmount
*hfsmp
;
2804 if (vnode_issystem(vp
) && (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)) {
2807 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) || (cp
->c_mode
== 0)) {
2808 cp
->c_flag
&= ~C_MODIFIED
;
2809 cp
->c_touch_acctime
= 0;
2810 cp
->c_touch_chgtime
= 0;
2811 cp
->c_touch_modtime
= 0;
2815 hfs_touchtimes(hfsmp
, cp
);
2817 /* Nothing to update. */
2818 if ((cp
->c_flag
& (C_MODIFIED
| C_FORCEUPDATE
)) == 0) {
2823 dataforkp
= &cp
->c_datafork
->ff_data
;
2825 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
2828 * For delayed allocations updates are
2829 * postponed until an fsync or the file
2830 * gets written to disk.
2832 * Deleted files can defer meta data updates until inactive.
2834 * If we're ever called with the C_FORCEUPDATE flag though
2835 * we have to do the update.
2837 if (ISSET(cp
->c_flag
, C_FORCEUPDATE
) == 0 &&
2838 (ISSET(cp
->c_flag
, C_DELETED
) ||
2839 (dataforkp
&& cp
->c_datafork
->ff_unallocblocks
) ||
2840 (rsrcforkp
&& cp
->c_rsrcfork
->ff_unallocblocks
))) {
2841 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2842 cp
->c_flag
|= C_MODIFIED
;
2844 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2849 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2854 * For files with invalid ranges (holes) the on-disk
2855 * field representing the size of the file (cf_size)
2856 * must be no larger than the start of the first hole.
2858 if (dataforkp
&& !CIRCLEQ_EMPTY(&cp
->c_datafork
->ff_invalidranges
)) {
2859 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2860 datafork
.cf_size
= CIRCLEQ_FIRST(&cp
->c_datafork
->ff_invalidranges
)->rl_start
;
2861 dataforkp
= &datafork
;
2862 } else if (dataforkp
&& (cp
->c_datafork
->ff_unallocblocks
!= 0)) {
2863 // always make sure the block count and the size
2864 // of the file match the number of blocks actually
2865 // allocated to the file on disk
2866 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2867 // make sure that we don't assign a negative block count
2868 if (cp
->c_datafork
->ff_blocks
< cp
->c_datafork
->ff_unallocblocks
) {
2869 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2870 cp
->c_datafork
->ff_blocks
, cp
->c_datafork
->ff_unallocblocks
);
2872 datafork
.cf_blocks
= (cp
->c_datafork
->ff_blocks
- cp
->c_datafork
->ff_unallocblocks
);
2873 datafork
.cf_size
= datafork
.cf_blocks
* HFSTOVCB(hfsmp
)->blockSize
;
2874 dataforkp
= &datafork
;
2878 * Lock the Catalog b-tree file.
2879 * A shared lock is sufficient since an update doesn't change
2880 * the tree and the lock on vp protects the cnode.
2882 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2884 /* XXX - waitfor is not enforced */
2885 error
= cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
2887 hfs_systemfile_unlock(hfsmp
, lockflags
);
2889 /* After the updates are finished, clear the flags */
2890 cp
->c_flag
&= ~(C_MODIFIED
| C_FORCEUPDATE
);
2892 hfs_end_transaction(hfsmp
);
2894 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2900 * Allocate a new node
2903 hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
, struct componentname
*cnp
,
2904 struct vnode_attr
*vap
, vfs_context_t ctx
)
2906 struct cnode
*cp
= NULL
;
2909 struct hfsmount
*hfsmp
;
2910 struct cat_desc in_desc
, out_desc
;
2911 struct cat_attr attr
;
2913 cat_cookie_t cookie
;
2915 int error
, started_tr
= 0, got_cookie
= 0;
2916 enum vtype vnodetype
;
2919 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
)))
2922 hfsmp
= VTOHFS(dvp
);
2925 out_desc
.cd_flags
= 0;
2926 out_desc
.cd_nameptr
= NULL
;
2928 mode
= MAKEIMODE(vap
->va_type
, vap
->va_mode
);
2930 if ((mode
& S_IFMT
) == 0)
2932 vnodetype
= IFTOVT(mode
);
2934 /* Check if were out of usable disk space. */
2935 if ((hfs_freeblks(hfsmp
, 1) <= 0) && (suser(vfs_context_ucred(ctx
), NULL
) != 0)) {
2942 /* Setup the default attributes */
2943 bzero(&attr
, sizeof(attr
));
2944 attr
.ca_mode
= mode
;
2945 attr
.ca_nlink
= vnodetype
== VDIR
? 2 : 1;
2946 attr
.ca_mtime
= tv
.tv_sec
;
2947 if ((VTOVCB(dvp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
2948 attr
.ca_mtime
+= 3600; /* Same as what hfs_update does */
2950 attr
.ca_atime
= attr
.ca_ctime
= attr
.ca_itime
= attr
.ca_mtime
;
2951 attr
.ca_atimeondisk
= attr
.ca_atime
;
2952 /* On HFS+ the ThreadExists flag must always be set for files. */
2953 if (vnodetype
!= VDIR
&& (hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
2954 attr
.ca_recflags
= kHFSThreadExistsMask
;
2956 attr
.ca_uid
= vap
->va_uid
;
2957 attr
.ca_gid
= vap
->va_gid
;
2958 VATTR_SET_SUPPORTED(vap
, va_mode
);
2959 VATTR_SET_SUPPORTED(vap
, va_uid
);
2960 VATTR_SET_SUPPORTED(vap
, va_gid
);
2962 /* Tag symlinks with a type and creator. */
2963 if (vnodetype
== VLNK
) {
2964 struct FndrFileInfo
*fip
;
2966 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
2967 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
2968 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
2970 if (cnp
->cn_flags
& ISWHITEOUT
)
2971 attr
.ca_flags
|= UF_OPAQUE
;
2973 /* Setup the descriptor */
2974 in_desc
.cd_nameptr
= cnp
->cn_nameptr
;
2975 in_desc
.cd_namelen
= cnp
->cn_namelen
;
2976 in_desc
.cd_parentcnid
= dcp
->c_cnid
;
2977 in_desc
.cd_flags
= S_ISDIR(mode
) ? CD_ISDIR
: 0;
2978 in_desc
.cd_hint
= dcp
->c_childhint
;
2979 in_desc
.cd_encoding
= 0;
2981 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2987 * Reserve some space in the Catalog file.
2989 * (we also add CAT_DELETE since our getnewvnode
2990 * request can cause an hfs_inactive call to
2991 * delete an unlinked file)
2993 if ((error
= cat_preflight(hfsmp
, CAT_CREATE
| CAT_DELETE
, &cookie
, 0))) {
2998 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2999 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
3001 /* Update the parent directory */
3002 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
3005 dcp
->c_ctime
= tv
.tv_sec
;
3006 dcp
->c_mtime
= tv
.tv_sec
;
3007 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
3008 HFS_KNOTE(dvp
, NOTE_ATTRIB
);
3010 hfs_systemfile_unlock(hfsmp
, lockflags
);
3014 /* Invalidate negative cache entries in the directory */
3015 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
3016 cache_purge_negatives(dvp
);
3018 if (vnodetype
== VDIR
) {
3019 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
3021 HFS_KNOTE(dvp
, NOTE_WRITE
);
3024 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
3025 (dcp
->c_cnid
== kHFSRootFolderID
));
3028 // have to end the transaction here before we call hfs_getnewvnode()
3029 // because that can cause us to try and reclaim a vnode on a different
3030 // file system which could cause us to start a transaction which can
3031 // deadlock with someone on that other file system (since we could be
3032 // holding two transaction locks as well as various vnodes and we did
3033 // not obtain the locks on them in the proper order).
3035 // NOTE: this means that if the quota check fails or we have to update
3036 // the change time on a block-special device that those changes
3037 // will happen as part of independent transactions.
3040 hfs_end_transaction(hfsmp
);
3045 * Create a vnode for the object just created.
3047 * The cnode is locked on successful return.
3049 error
= hfs_getnewvnode(hfsmp
, dvp
, cnp
, &out_desc
, 0, &attr
, NULL
, &tvp
);
3054 //cache_enter(dvp, tvp, cnp);
3059 * We call hfs_chkiq with FORCE flag so that if we
3060 * fall through to the rmdir we actually have
3061 * accounted for the inode
3063 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_QUOTA
) {
3064 if ((error
= hfs_getinoquota(cp
)) ||
3065 (error
= hfs_chkiq(cp
, 1, vfs_context_ucred(ctx
), FORCE
))) {
3067 if (vnode_isdir(tvp
))
3068 (void) hfs_removedir(dvp
, tvp
, cnp
, 0);
3071 hfs_lock_truncate(cp
, TRUE
);
3072 hfs_lock(cp
, HFS_FORCE_LOCK
);
3073 (void) hfs_removefile(dvp
, tvp
, cnp
, 0, 0);
3074 hfs_unlock_truncate(cp
);
3077 * we successfully allocated a new vnode, but
3078 * the quota check is telling us we're beyond
3079 * our limit, so we need to dump our lock + reference
3089 /* Remember if any ACL data was set. */
3090 if (VATTR_IS_ACTIVE(vap
, va_acl
) &&
3091 (vap
->va_acl
!= NULL
)) {
3092 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
3093 cp
->c_touch_chgtime
= TRUE
;
3094 (void) hfs_update(tvp
, TRUE
);
3098 cat_releasedesc(&out_desc
);
3101 cat_postflight(hfsmp
, &cookie
, 0);
3104 * Check if a file is located in the "Cleanup At Startup"
3105 * directory. If it is then tag it as NODUMP so that we
3106 * can be lazy about zero filling data holes.
3108 if ((error
== 0) && dvp
&& (vnodetype
== VREG
) &&
3109 (dcp
->c_desc
.cd_nameptr
!= NULL
) &&
3110 (strcmp(dcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
3117 * The parent of "Cleanup At Startup" should
3118 * have the ASCII name of the userid.
3120 if (hfs_vget(hfsmp
, dcp
->c_parentcnid
, &ddvp
, 0) == 0) {
3121 if (VTOC(ddvp
)->c_desc
.cd_nameptr
) {
3124 uid
= strtoul(VTOC(ddvp
)->c_desc
.cd_nameptr
, 0, 0);
3125 if ((uid
== cp
->c_uid
) ||
3126 (uid
== vfs_context_ucred(ctx
)->cr_uid
)) {
3127 cp
->c_flags
|= UF_NODUMP
;
3128 cp
->c_touch_chgtime
= TRUE
;
3131 hfs_unlock(VTOC(ddvp
));
3138 if (error
== 0 && cp
!= NULL
) {
3142 hfs_end_transaction(hfsmp
);
3151 * WARNING - assumes caller has cnode lock.
3155 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, __unused
struct proc
*p
)
3158 struct cnode
*cp
= VTOC(vp
);
3162 if ((rvp
= cp
->c_rsrc_vp
)) {
3163 vid
= vnode_vid(rvp
);
3165 /* Use exising vnode */
3166 error
= vnode_getwithvid(rvp
, vid
);
3168 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
3171 printf("hfs_vgetrsrc: couldn't get"
3172 " resource fork for %s\n", name
);
3176 struct cat_fork rsrcfork
;
3177 struct componentname cn
;
3181 * Make sure cnode lock is exclusive, if not upgrade it.
3183 * We assume that we were called from a read-only VNOP (getattr)
3184 * and that its safe to have the cnode lock dropped and reacquired.
3186 if (cp
->c_lockowner
!= current_thread()) {
3188 * If the upgrade fails we loose the lock and
3189 * have to take the exclusive lock on our own.
3191 if (lck_rw_lock_shared_to_exclusive(&cp
->c_rwlock
) != 0)
3192 lck_rw_lock_exclusive(&cp
->c_rwlock
);
3193 cp
->c_lockowner
= current_thread();
3196 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3198 /* Get resource fork data */
3199 error
= cat_lookup(hfsmp
, &cp
->c_desc
, 1, (struct cat_desc
*)0,
3200 (struct cat_attr
*)0, &rsrcfork
, NULL
);
3202 hfs_systemfile_unlock(hfsmp
, lockflags
);
3207 * Supply hfs_getnewvnode with a component name.
3210 if (cp
->c_desc
.cd_nameptr
) {
3211 MALLOC_ZONE(cn
.cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3212 cn
.cn_nameiop
= LOOKUP
;
3213 cn
.cn_flags
= ISLASTCN
| HASBUF
;
3214 cn
.cn_context
= NULL
;
3215 cn
.cn_pnlen
= MAXPATHLEN
;
3216 cn
.cn_nameptr
= cn
.cn_pnbuf
;
3219 cn
.cn_namelen
= sprintf(cn
.cn_nameptr
, "%s%s", cp
->c_desc
.cd_nameptr
, _PATH_RSRCFORKSPEC
);
3221 error
= hfs_getnewvnode(hfsmp
, vnode_parent(vp
), cn
.cn_pnbuf
? &cn
: NULL
,
3222 &cp
->c_desc
, 2, &cp
->c_attr
, &rsrcfork
, &rvp
);
3224 FREE_ZONE(cn
.cn_pnbuf
, cn
.cn_pnlen
, M_NAMEI
);
3235 filt_hfsdetach(struct knote
*kn
)
3239 vp
= (struct vnode
*)kn
->kn_hook
;
3240 if (vnode_getwithvid(vp
, kn
->kn_hookid
))
3243 if (1) { /* ! KNDETACH_VNLOCKED */
3244 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3245 (void) KNOTE_DETACH(&VTOC(vp
)->c_knotes
, kn
);
3246 hfs_unlock(VTOC(vp
));
3255 filt_hfsread(struct knote
*kn
, long hint
)
3257 struct vnode
*vp
= (struct vnode
*)kn
->kn_hook
;
3261 if ((vnode_getwithvid(vp
, kn
->kn_hookid
) != 0)) {
3266 if (hint
== NOTE_REVOKE
) {
3268 * filesystem is gone, so set the EOF flag and schedule
3269 * the knote for deletion.
3271 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3275 /* poll(2) semantics dictate always saying there is data */
3276 kn
->kn_data
= (!(kn
->kn_flags
& EV_POLL
)) ?
3277 VTOF(vp
)->ff_size
- kn
->kn_fp
->f_fglob
->fg_offset
: 1;
3282 return (kn
->kn_data
!= 0);
3287 filt_hfswrite(struct knote
*kn
, long hint
)
3292 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3295 vnode_put(kn
->kn_hook
);
3297 if (hint
== NOTE_REVOKE
) {
3299 * filesystem is gone, so set the EOF flag and schedule
3300 * the knote for deletion.
3303 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3311 filt_hfsvnode(struct knote
*kn
, long hint
)
3315 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3318 vnode_put(kn
->kn_hook
);
3320 if (kn
->kn_sfflags
& hint
)
3321 kn
->kn_fflags
|= hint
;
3322 if ((hint
== NOTE_REVOKE
)) {
3323 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3327 return (kn
->kn_fflags
!= 0);
3330 static struct filterops hfsread_filtops
=
3331 { 1, NULL
, filt_hfsdetach
, filt_hfsread
};
3332 static struct filterops hfswrite_filtops
=
3333 { 1, NULL
, filt_hfsdetach
, filt_hfswrite
};
3334 static struct filterops hfsvnode_filtops
=
3335 { 1, NULL
, filt_hfsdetach
, filt_hfsvnode
};
3338 * Add a kqueue filter.
3342 struct vnop_kqfilt_add_args
/* {
3346 vfs_context_t a_context;
3349 struct vnode
*vp
= ap
->a_vp
;
3350 struct knote
*kn
= ap
->a_kn
;
3353 switch (kn
->kn_filter
) {
3355 if (vnode_isreg(vp
)) {
3356 kn
->kn_fop
= &hfsread_filtops
;
3362 if (vnode_isreg(vp
)) {
3363 kn
->kn_fop
= &hfswrite_filtops
;
3369 kn
->kn_fop
= &hfsvnode_filtops
;
3375 kn
->kn_hook
= (caddr_t
)vp
;
3376 kn
->kn_hookid
= vnode_vid(vp
);
3378 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
3380 KNOTE_ATTACH(&VTOC(vp
)->c_knotes
, kn
);
3381 hfs_unlock(VTOC(vp
));
3387 * Remove a kqueue filter
3390 hfs_vnop_kqfiltremove(ap
)
3391 struct vnop_kqfilt_remove_args
/* {
3394 vfs_context_t a_context;
3399 result
= ENOTSUP
; /* XXX */
3405 * Wrapper for special device reads
3409 struct vnop_read_args
/* {
3413 vfs_context_t a_context;
3419 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3420 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3424 * Wrapper for special device writes
3428 struct vnop_write_args
/* {
3432 vfs_context_t a_context;
3436 * Set update and change flags.
3438 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3439 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3440 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3444 * Wrapper for special device close
3446 * Update the times on the cnode then do device close.
3450 struct vnop_close_args
/* {
3453 vfs_context_t a_context;
3456 struct vnode
*vp
= ap
->a_vp
;
3459 if (vnode_isinuse(ap
->a_vp
, 1)) {
3460 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3462 hfs_touchtimes(VTOHFS(vp
), cp
);
3466 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3471 * Wrapper for fifo reads
3475 struct vnop_read_args
/* {
3479 vfs_context_t a_context;
3482 extern int (**fifo_vnodeop_p
)(void *);
3487 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3488 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3492 * Wrapper for fifo writes
3496 struct vnop_write_args
/* {
3500 vfs_context_t a_context;
3503 extern int (**fifo_vnodeop_p
)(void *);
3506 * Set update and change flags.
3508 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3509 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3510 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3514 * Wrapper for fifo close
3516 * Update the times on the cnode then do device close.
3520 struct vnop_close_args
/* {
3523 vfs_context_t a_context;
3526 extern int (**fifo_vnodeop_p
)(void *);
3527 struct vnode
*vp
= ap
->a_vp
;
3530 if (vnode_isinuse(ap
->a_vp
, 1)) {
3531 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3533 hfs_touchtimes(VTOHFS(vp
), cp
);
3537 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3541 * kqfilt_add wrapper for fifos.
3543 * Fall through to hfs kqfilt_add routines if needed
3546 hfsfifo_kqfilt_add(ap
)
3547 struct vnop_kqfilt_add_args
*ap
;
3549 extern int (**fifo_vnodeop_p
)(void *);
3552 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_add
), ap
);
3554 error
= hfs_vnop_kqfiltadd(ap
);
3559 * kqfilt_remove wrapper for fifos.
3561 * Fall through to hfs kqfilt_remove routines if needed
3564 hfsfifo_kqfilt_remove(ap
)
3565 struct vnop_kqfilt_remove_args
*ap
;
3567 extern int (**fifo_vnodeop_p
)(void *);
3570 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_remove
), ap
);
3572 error
= hfs_vnop_kqfiltremove(ap
);
3579 * Synchronize a file's in-core state with that on disk.
3583 struct vnop_fsync_args
/* {
3586 vfs_context_t a_context;
3589 struct vnode
* vp
= ap
->a_vp
;
3593 * We need to allow ENOENT lock errors since unlink
3594 * systenm call can call VNOP_FSYNC during vclean.
3596 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
3600 error
= hfs_fsync(vp
, ap
->a_waitfor
, 0, vfs_context_proc(ap
->a_context
));
3602 hfs_unlock(VTOC(vp
));
3606 /*****************************************************************************
3610 *****************************************************************************/
3611 int hfs_vnop_readdirattr(struct vnop_readdirattr_args
*); /* in hfs_attrlist.c */
3612 int hfs_vnop_inactive(struct vnop_inactive_args
*); /* in hfs_cnode.c */
3613 int hfs_vnop_reclaim(struct vnop_reclaim_args
*); /* in hfs_cnode.c */
3614 int hfs_vnop_link(struct vnop_link_args
*); /* in hfs_link.c */
3615 int hfs_vnop_lookup(struct vnop_lookup_args
*); /* in hfs_lookup.c */
3616 int hfs_vnop_search(struct vnop_searchfs_args
*); /* in hfs_search.c */
3618 int hfs_vnop_read(struct vnop_read_args
*); /* in hfs_readwrite.c */
3619 int hfs_vnop_write(struct vnop_write_args
*); /* in hfs_readwrite.c */
3620 int hfs_vnop_ioctl(struct vnop_ioctl_args
*); /* in hfs_readwrite.c */
3621 int hfs_vnop_select(struct vnop_select_args
*); /* in hfs_readwrite.c */
3622 int hfs_vnop_strategy(struct vnop_strategy_args
*); /* in hfs_readwrite.c */
3623 int hfs_vnop_allocate(struct vnop_allocate_args
*); /* in hfs_readwrite.c */
3624 int hfs_vnop_pagein(struct vnop_pagein_args
*); /* in hfs_readwrite.c */
3625 int hfs_vnop_pageout(struct vnop_pageout_args
*); /* in hfs_readwrite.c */
3626 int hfs_vnop_bwrite(struct vnop_bwrite_args
*); /* in hfs_readwrite.c */
3627 int hfs_vnop_blktooff(struct vnop_blktooff_args
*); /* in hfs_readwrite.c */
3628 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*); /* in hfs_readwrite.c */
3629 int hfs_vnop_blockmap(struct vnop_blockmap_args
*); /* in hfs_readwrite.c */
3630 int hfs_vnop_getxattr(struct vnop_getxattr_args
*); /* in hfs_xattr.c */
3631 int hfs_vnop_setxattr(struct vnop_setxattr_args
*); /* in hfs_xattr.c */
3632 int hfs_vnop_removexattr(struct vnop_removexattr_args
*); /* in hfs_xattr.c */
3633 int hfs_vnop_listxattr(struct vnop_listxattr_args
*); /* in hfs_xattr.c */
3635 int (**hfs_vnodeop_p
)(void *);
3637 #define VOPFUNC int (*)(void *)
3639 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
3640 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3641 { &vnop_lookup_desc
, (VOPFUNC
)hfs_vnop_lookup
}, /* lookup */
3642 { &vnop_create_desc
, (VOPFUNC
)hfs_vnop_create
}, /* create */
3643 { &vnop_mknod_desc
, (VOPFUNC
)hfs_vnop_mknod
}, /* mknod */
3644 { &vnop_open_desc
, (VOPFUNC
)hfs_vnop_open
}, /* open */
3645 { &vnop_close_desc
, (VOPFUNC
)hfs_vnop_close
}, /* close */
3646 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3647 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3648 { &vnop_read_desc
, (VOPFUNC
)hfs_vnop_read
}, /* read */
3649 { &vnop_write_desc
, (VOPFUNC
)hfs_vnop_write
}, /* write */
3650 { &vnop_ioctl_desc
, (VOPFUNC
)hfs_vnop_ioctl
}, /* ioctl */
3651 { &vnop_select_desc
, (VOPFUNC
)hfs_vnop_select
}, /* select */
3652 { &vnop_revoke_desc
, (VOPFUNC
)nop_revoke
}, /* revoke */
3653 { &vnop_exchange_desc
, (VOPFUNC
)hfs_vnop_exchange
}, /* exchange */
3654 { &vnop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
3655 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3656 { &vnop_remove_desc
, (VOPFUNC
)hfs_vnop_remove
}, /* remove */
3657 { &vnop_link_desc
, (VOPFUNC
)hfs_vnop_link
}, /* link */
3658 { &vnop_rename_desc
, (VOPFUNC
)hfs_vnop_rename
}, /* rename */
3659 { &vnop_mkdir_desc
, (VOPFUNC
)hfs_vnop_mkdir
}, /* mkdir */
3660 { &vnop_rmdir_desc
, (VOPFUNC
)hfs_vnop_rmdir
}, /* rmdir */
3661 { &vnop_symlink_desc
, (VOPFUNC
)hfs_vnop_symlink
}, /* symlink */
3662 { &vnop_readdir_desc
, (VOPFUNC
)hfs_vnop_readdir
}, /* readdir */
3663 { &vnop_readdirattr_desc
, (VOPFUNC
)hfs_vnop_readdirattr
}, /* readdirattr */
3664 { &vnop_readlink_desc
, (VOPFUNC
)hfs_vnop_readlink
}, /* readlink */
3665 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3666 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3667 { &vnop_strategy_desc
, (VOPFUNC
)hfs_vnop_strategy
}, /* strategy */
3668 { &vnop_pathconf_desc
, (VOPFUNC
)hfs_vnop_pathconf
}, /* pathconf */
3669 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3670 { &vnop_allocate_desc
, (VOPFUNC
)hfs_vnop_allocate
}, /* allocate */
3671 { &vnop_searchfs_desc
, (VOPFUNC
)hfs_vnop_search
}, /* search fs */
3672 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
}, /* bwrite */
3673 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* pagein */
3674 { &vnop_pageout_desc
,(VOPFUNC
) hfs_vnop_pageout
}, /* pageout */
3675 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3676 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3677 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3678 { &vnop_blockmap_desc
, (VOPFUNC
)hfs_vnop_blockmap
}, /* blockmap */
3679 { &vnop_kqfilt_add_desc
, (VOPFUNC
)hfs_vnop_kqfiltadd
}, /* kqfilt_add */
3680 { &vnop_kqfilt_remove_desc
, (VOPFUNC
)hfs_vnop_kqfiltremove
}, /* kqfilt_remove */
3681 { &vnop_getxattr_desc
, (VOPFUNC
)hfs_vnop_getxattr
},
3682 { &vnop_setxattr_desc
, (VOPFUNC
)hfs_vnop_setxattr
},
3683 { &vnop_removexattr_desc
, (VOPFUNC
)hfs_vnop_removexattr
},
3684 { &vnop_listxattr_desc
, (VOPFUNC
)hfs_vnop_listxattr
},
3685 { NULL
, (VOPFUNC
)NULL
}
3688 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3689 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
3691 int (**hfs_specop_p
)(void *);
3692 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
3693 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3694 { &vnop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
3695 { &vnop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
3696 { &vnop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
3697 { &vnop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
3698 { &vnop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
3699 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3700 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3701 { &vnop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
3702 { &vnop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
3703 { &vnop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
3704 { &vnop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
3705 { &vnop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
3706 { &vnop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
3707 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3708 { &vnop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
3709 { &vnop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
3710 { &vnop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
3711 { &vnop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
3712 { &vnop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
3713 { &vnop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
3714 { &vnop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
3715 { &vnop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
3716 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3717 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3718 { &vnop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
3719 { &vnop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
3720 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3721 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
},
3722 { &vnop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
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
};