2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/file_internal.h>
26 #include <sys/dirent.h>
29 #include <sys/mount.h>
30 #include <sys/vnode_internal.h>
31 #include <sys/malloc.h>
33 #include <sys/paths.h>
34 #include <sys/quota.h>
37 #include <sys/kauth.h>
38 #include <sys/uio_internal.h>
40 #include <miscfs/specfs/specdev.h>
41 #include <miscfs/fifofs/fifo.h>
42 #include <vfs/vfs_support.h>
43 #include <machine/spl.h>
45 #include <sys/kdebug.h>
48 #include "hfs_catalog.h"
49 #include "hfs_cnode.h"
51 #include "hfs_mount.h"
52 #include "hfs_quota.h"
53 #include "hfs_endian.h"
55 #include "hfscommon/headers/BTreesInternal.h"
56 #include "hfscommon/headers/FileMgrInternal.h"
58 #define MAKE_DELETED_NAME(NAME,FID) \
59 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
61 #define KNDETACH_VNLOCKED 0x00000001
63 #define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
66 /* Global vfs data structures for hfs */
69 extern unsigned long strtoul(const char *, char **, int);
71 static int hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
,
72 struct componentname
*cnp
, struct vnode_attr
*vap
,
75 static int hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
);
77 static int hfs_removedir(struct vnode
*, struct vnode
*, struct componentname
*,
80 static int hfs_removefile(struct vnode
*, struct vnode
*, struct componentname
*,
83 static int hfs_vnop_close(struct vnop_close_args
*);
84 static int hfs_vnop_create(struct vnop_create_args
*);
85 static int hfs_vnop_exchange(struct vnop_exchange_args
*);
86 static int hfs_vnop_fsync(struct vnop_fsync_args
*);
87 static int hfs_vnop_mkdir(struct vnop_mkdir_args
*);
88 static int hfs_vnop_mknod(struct vnop_mknod_args
*);
89 static int hfs_vnop_getattr(struct vnop_getattr_args
*);
90 static int hfs_vnop_open(struct vnop_open_args
*);
91 static int hfs_vnop_readdir(struct vnop_readdir_args
*);
92 static int hfs_vnop_remove(struct vnop_remove_args
*);
93 static int hfs_vnop_rename(struct vnop_rename_args
*);
94 static int hfs_vnop_rmdir(struct vnop_rmdir_args
*);
95 static int hfs_vnop_symlink(struct vnop_symlink_args
*);
96 static int hfs_vnop_setattr(struct vnop_setattr_args
*);
98 /* Options for hfs_removedir and hfs_removefile */
99 #define HFSRM_SKIP_RESERVE 0x01
102 int hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
);
104 int hfs_chflags(struct vnode
*vp
, uint32_t flags
, kauth_cred_t cred
,
106 int hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
,
108 int hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
,
109 kauth_cred_t cred
, struct proc
*p
);
111 /*****************************************************************************
113 * Common Operations on vnodes
115 *****************************************************************************/
118 * Create a regular file.
121 hfs_vnop_create(struct vnop_create_args
*ap
)
123 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
127 * Make device special file.
130 hfs_vnop_mknod(struct vnop_mknod_args
*ap
)
132 struct vnode_attr
*vap
= ap
->a_vap
;
133 struct vnode
*dvp
= ap
->a_dvp
;
134 struct vnode
**vpp
= ap
->a_vpp
;
138 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
) {
142 /* Create the vnode */
143 error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, vap
, ap
->a_context
);
148 cp
->c_touch_acctime
= TRUE
;
149 cp
->c_touch_chgtime
= TRUE
;
150 cp
->c_touch_modtime
= TRUE
;
152 if ((vap
->va_rdev
!= VNOVAL
) &&
153 (vap
->va_type
== VBLK
|| vap
->va_type
== VCHR
))
154 cp
->c_rdev
= vap
->va_rdev
;
160 * Open a file/directory.
163 hfs_vnop_open(struct vnop_open_args
*ap
)
165 struct vnode
*vp
= ap
->a_vp
;
171 * Files marked append-only must be opened for appending.
173 if ((VTOC(vp
)->c_flags
& APPEND
) && !vnode_isdir(vp
) &&
174 (ap
->a_mode
& (FWRITE
| O_APPEND
)) == FWRITE
)
177 if (vnode_isreg(vp
) && !UBCINFOEXISTS(vp
))
178 return (EBUSY
); /* file is in use by the kernel */
180 /* Don't allow journal file to be opened externally. */
181 if (VTOC(vp
)->c_fileid
== VTOHFS(vp
)->hfs_jnlfileid
)
184 * On the first (non-busy) open of a fragmented
185 * file attempt to de-frag it (if its less than 20MB).
187 if ((VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
) ||
188 (VTOHFS(vp
)->jnl
== NULL
) ||
189 !vnode_isreg(vp
) || vnode_isinuse(vp
, 0)) {
193 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
197 fp
->ff_extents
[7].blockCount
!= 0 &&
198 fp
->ff_size
<= (20 * 1024 * 1024)) {
200 * Wait until system bootup is done (3 min).
203 if (tv
.tv_sec
> (60 * 3)) {
204 (void) hfs_relocate(vp
, VTOVCB(vp
)->nextAllocation
+ 4096,
205 vfs_context_ucred(ap
->a_context
),
206 vfs_context_proc(ap
->a_context
));
209 hfs_unlock(VTOC(vp
));
216 * Close a file/directory.
220 struct vnop_close_args
/* {
223 vfs_context_t a_context;
226 register struct vnode
*vp
= ap
->a_vp
;
227 register struct cnode
*cp
;
228 struct proc
*p
= vfs_context_proc(ap
->a_context
);
229 struct hfsmount
*hfsmp
;
232 if ( hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) != 0)
237 // if we froze the fs and we're exiting, then "thaw" the fs
238 if (hfsmp
->hfs_freezing_proc
== p
&& proc_exiting(p
)) {
239 hfsmp
->hfs_freezing_proc
= NULL
;
240 hfs_global_exclusive_lock_release(hfsmp
);
243 busy
= vnode_isinuse(vp
, 1);
246 hfs_touchtimes(VTOHFS(vp
), cp
);
248 if (vnode_isdir(vp
)) {
249 hfs_reldirhints(cp
, busy
);
250 } else if (vnode_issystem(vp
) && !busy
) {
259 * Get basic attributes.
262 hfs_vnop_getattr(struct vnop_getattr_args
*ap
)
264 struct vnode
*vp
= ap
->a_vp
;
265 struct vnode_attr
*vap
= ap
->a_vap
;
266 struct vnode
*rvp
= NULL
;
267 struct hfsmount
*hfsmp
;
272 if ((error
= hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
))) {
277 hfs_touchtimes(hfsmp
, cp
);
278 v_type
= vnode_vtype(vp
);
280 VATTR_RETURN(vap
, va_rdev
, (v_type
== VBLK
|| v_type
== VCHR
) ? cp
->c_rdev
: 0);
281 if (v_type
== VDIR
) {
282 if (VATTR_IS_ACTIVE(vap
, va_nlink
)) {
285 entries
= cp
->c_nlink
;
286 if (vnode_isvroot(vp
)) {
287 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
288 --entries
; /* hide private dir */
290 entries
-= 2; /* hide the journal files */
292 VATTR_RETURN(vap
, va_nlink
, (uint64_t)entries
);
295 if (VATTR_IS_ACTIVE(vap
, va_nchildren
)) {
298 entries
= cp
->c_entries
;
299 if (vnode_isvroot(vp
)) {
300 if (hfsmp
->hfs_privdir_desc
.cd_cnid
!= 0)
301 --entries
; /* hide private dir */
303 entries
-= 2; /* hide the journal files */
305 VATTR_RETURN(vap
, va_nchildren
, entries
);
308 VATTR_RETURN(vap
, va_nlink
, (uint64_t)cp
->c_nlink
);
311 /* conditional because 64-bit arithmetic can be expensive */
312 if (VATTR_IS_ACTIVE(vap
, va_total_size
)) {
313 if (v_type
== VDIR
) {
314 VATTR_RETURN(vap
, va_total_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
316 uint64_t total_size
= 0;
319 if (cp
->c_datafork
) {
320 total_size
= cp
->c_datafork
->ff_size
;
323 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
324 /* hfs_vgetrsrc does not use struct proc - therefore passing NULL */
325 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, NULL
);
331 if (rcp
&& rcp
->c_rsrcfork
) {
332 total_size
+= rcp
->c_rsrcfork
->ff_size
;
336 VATTR_RETURN(vap
, va_total_size
, total_size
);
337 /* Include size of attibute data (extents), if any */
338 if (cp
->c_attrblks
) {
339 vap
->va_total_size
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
343 if (VATTR_IS_ACTIVE(vap
, va_total_alloc
)) {
344 if (v_type
== VDIR
) {
345 VATTR_RETURN(vap
, va_total_alloc
, 0);
347 VATTR_RETURN(vap
, va_total_alloc
, (uint64_t)cp
->c_blocks
* (uint64_t)hfsmp
->blockSize
);
348 /* Include size of attibute data (extents), if any */
349 if (cp
->c_attrblks
) {
350 vap
->va_total_alloc
+= (uint64_t)cp
->c_attrblks
* (uint64_t)hfsmp
->blockSize
;
354 /* XXX broken... if ask for "data size" of rsrc fork vp you get rsrc fork size! */
355 if (v_type
== VDIR
) {
356 VATTR_RETURN(vap
, va_data_size
, cp
->c_nlink
* AVERAGE_HFSDIRENTRY_SIZE
);
358 VATTR_RETURN(vap
, va_data_size
, VTOF(vp
)->ff_size
);
360 if (VATTR_IS_ACTIVE(vap
, va_data_alloc
) && (v_type
!= VDIR
)) {
361 /* XXX do we need to account for ff_unallocblocks ? */
362 VATTR_RETURN(vap
, va_data_alloc
, (uint64_t)VTOF(vp
)->ff_blocks
* (uint64_t)hfsmp
->blockSize
);
364 /* XXX is this really a good 'optimal I/O size'? */
365 VATTR_RETURN(vap
, va_iosize
, hfsmp
->hfs_logBlockSize
);
366 VATTR_RETURN(vap
, va_uid
, cp
->c_uid
);
367 VATTR_RETURN(vap
, va_gid
, cp
->c_gid
);
368 VATTR_RETURN(vap
, va_mode
, cp
->c_mode
);
370 /* XXX is S_IFXATTR still needed ??? */
371 if (VNODE_IS_RSRC(vp
))
372 vap
->va_mode
|= S_IFXATTR
;
374 VATTR_RETURN(vap
, va_flags
, cp
->c_flags
);
377 * If the VFS wants extended security data, and we know that we
378 * don't have any (because it never told us it was setting any)
379 * then we can return the supported bit and no data. If we do
380 * have extended security, we can just leave the bit alone and
381 * the VFS will use the fallback path to fetch it.
383 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
384 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
385 vap
->va_acl
= KAUTH_FILESEC_NONE
;
386 VATTR_SET_SUPPORTED(vap
, va_acl
);
389 vap
->va_create_time
.tv_sec
= cp
->c_itime
;
390 vap
->va_create_time
.tv_nsec
= 0;
391 VATTR_SET_SUPPORTED(vap
, va_create_time
);
393 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
394 /* Access times are lazyily updated, get current time if needed */
395 if (cp
->c_touch_acctime
) {
399 vap
->va_access_time
.tv_sec
= tv
.tv_sec
;
401 vap
->va_access_time
.tv_sec
= cp
->c_atime
;
403 vap
->va_access_time
.tv_nsec
= 0;
404 VATTR_SET_SUPPORTED(vap
, va_access_time
);
406 vap
->va_modify_time
.tv_sec
= cp
->c_mtime
;
407 vap
->va_modify_time
.tv_nsec
= 0;
408 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
409 vap
->va_change_time
.tv_sec
= cp
->c_ctime
;
410 vap
->va_change_time
.tv_nsec
= 0;
411 VATTR_SET_SUPPORTED(vap
, va_change_time
);
412 vap
->va_backup_time
.tv_sec
= cp
->c_btime
;
413 vap
->va_backup_time
.tv_nsec
= 0;
414 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
417 * Exporting file IDs from HFS Plus:
419 * For "normal" files the c_fileid is the same value as the
420 * c_cnid. But for hard link files, they are different - the
421 * c_cnid belongs to the active directory entry (ie the link)
422 * and the c_fileid is for the actual inode (ie the data file).
424 * The stat call (getattr) uses va_fileid and the Carbon APIs,
425 * which are hardlink-ignorant, will ask for va_linkid.
427 VATTR_RETURN(vap
, va_fileid
, (uint64_t)cp
->c_fileid
);
428 VATTR_RETURN(vap
, va_linkid
, (uint64_t)cp
->c_cnid
);
429 VATTR_RETURN(vap
, va_parentid
, (uint64_t)cp
->c_parentcnid
);
430 VATTR_RETURN(vap
, va_fsid
, cp
->c_dev
);
431 VATTR_RETURN(vap
, va_filerev
, 0);
433 VATTR_RETURN(vap
, va_encoding
, cp
->c_encoding
);
435 /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
436 if (VATTR_IS_ACTIVE(vap
, va_name
) && !vnode_isvroot(vp
)) {
437 /* Return the name for ATTR_CMN_NAME */
438 if (cp
->c_desc
.cd_namelen
== 0) {
443 strncpy(vap
->va_name
, cp
->c_desc
.cd_nameptr
, MAXPATHLEN
);
444 vap
->va_name
[MAXPATHLEN
-1] = '\0';
445 VATTR_SET_SUPPORTED(vap
, va_name
);
458 struct vnop_setattr_args
/* {
460 struct vnode_attr *a_vap;
461 vfs_context_t a_context;
464 struct vnode_attr
*vap
= ap
->a_vap
;
465 struct vnode
*vp
= ap
->a_vp
;
466 struct cnode
*cp
= NULL
;
467 struct hfsmount
*hfsmp
;
468 kauth_cred_t cred
= vfs_context_ucred(ap
->a_context
);
469 struct proc
*p
= vfs_context_proc(ap
->a_context
);
476 /* Don't allow modification of the journal file. */
477 if (hfsmp
->hfs_jnlfileid
== VTOC(vp
)->c_fileid
) {
482 * File size change request.
483 * We are guaranteed that this is not a directory, and that
484 * the filesystem object is writeable.
486 VATTR_SET_SUPPORTED(vap
, va_data_size
);
487 if (VATTR_IS_ACTIVE(vap
, va_data_size
) && !vnode_islnk(vp
)) {
489 /* Take truncate lock before taking cnode lock. */
490 hfs_lock_truncate(VTOC(vp
), TRUE
);
491 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
492 hfs_unlock_truncate(VTOC(vp
));
497 error
= hfs_truncate(vp
, vap
->va_data_size
, vap
->va_vaflags
& 0xffff, 0, ap
->a_context
);
499 hfs_unlock_truncate(cp
);
504 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
510 * Owner/group change request.
511 * We are guaranteed that the new owner/group is valid and legal.
513 VATTR_SET_SUPPORTED(vap
, va_uid
);
514 VATTR_SET_SUPPORTED(vap
, va_gid
);
515 nuid
= VATTR_IS_ACTIVE(vap
, va_uid
) ? vap
->va_uid
: (uid_t
)VNOVAL
;
516 ngid
= VATTR_IS_ACTIVE(vap
, va_gid
) ? vap
->va_gid
: (gid_t
)VNOVAL
;
517 if (((nuid
!= (uid_t
)VNOVAL
) || (ngid
!= (gid_t
)VNOVAL
)) &&
518 ((error
= hfs_chown(vp
, nuid
, ngid
, cred
, p
)) != 0))
522 * Mode change request.
523 * We are guaranteed that the mode value is valid and that in
524 * conjunction with the owner and group, this change is legal.
526 VATTR_SET_SUPPORTED(vap
, va_mode
);
527 if (VATTR_IS_ACTIVE(vap
, va_mode
) &&
528 ((error
= hfs_chmod(vp
, (int)vap
->va_mode
, cred
, p
)) != 0))
533 * We are guaranteed that only flags allowed to change given the
534 * current securelevel are being changed.
536 VATTR_SET_SUPPORTED(vap
, va_flags
);
537 if (VATTR_IS_ACTIVE(vap
, va_flags
) &&
538 ((error
= hfs_chflags(vp
, vap
->va_flags
, cred
, p
)) != 0))
542 * If the file's extended security data is being changed, we
543 * need to note the change. Note that because we don't store
544 * the data, we do not set the SUPPORTED bit; this will cause
545 * the VFS to use a fallback strategy.
547 if (VATTR_IS_ACTIVE(vap
, va_acl
)) {
548 /* Remember if any ACL data was set or cleared. */
549 if (vap
->va_acl
== NULL
) {
551 if (cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) {
552 cp
->c_attr
.ca_recflags
&= ~kHFSHasSecurityMask
;
553 cp
->c_touch_chgtime
= TRUE
;
557 if ((cp
->c_attr
.ca_recflags
& kHFSHasSecurityMask
) == 0) {
558 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
559 cp
->c_touch_chgtime
= TRUE
;
567 VATTR_SET_SUPPORTED(vap
, va_create_time
);
568 VATTR_SET_SUPPORTED(vap
, va_access_time
);
569 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
570 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
571 VATTR_SET_SUPPORTED(vap
, va_change_time
);
572 if (VATTR_IS_ACTIVE(vap
, va_create_time
) ||
573 VATTR_IS_ACTIVE(vap
, va_access_time
) ||
574 VATTR_IS_ACTIVE(vap
, va_modify_time
) ||
575 VATTR_IS_ACTIVE(vap
, va_backup_time
)) {
578 if (VATTR_IS_ACTIVE(vap
, va_create_time
))
579 cp
->c_itime
= vap
->va_create_time
.tv_sec
;
580 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
581 cp
->c_atime
= vap
->va_access_time
.tv_sec
;
582 cp
->c_touch_acctime
= FALSE
;
584 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
585 cp
->c_mtime
= vap
->va_modify_time
.tv_sec
;
586 cp
->c_touch_modtime
= FALSE
;
587 cp
->c_touch_chgtime
= TRUE
;
590 * The utimes system call can reset the modification
591 * time but it doesn't know about HFS create times.
592 * So we need to ensure that the creation time is
593 * always at least as old as the modification time.
595 if ((VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) &&
596 (cp
->c_cnid
!= kHFSRootFolderID
) &&
597 (cp
->c_mtime
< cp
->c_itime
)) {
598 cp
->c_itime
= cp
->c_mtime
;
601 if (VATTR_IS_ACTIVE(vap
, va_backup_time
))
602 cp
->c_btime
= vap
->va_backup_time
.tv_sec
;
603 cp
->c_flag
|= C_MODIFIED
;
609 VATTR_SET_SUPPORTED(vap
, va_encoding
);
610 if (VATTR_IS_ACTIVE(vap
, va_encoding
)) {
611 cp
->c_encoding
= vap
->va_encoding
;
612 hfs_setencodingbits(hfsmp
, cp
->c_encoding
);
616 if ((error
= hfs_update(vp
, TRUE
)) != 0)
618 HFS_KNOTE(vp
, NOTE_ATTRIB
);
627 * Change the mode on a file.
628 * cnode must be locked before calling.
632 hfs_chmod(struct vnode
*vp
, int mode
, kauth_cred_t cred
, struct proc
*p
)
634 register struct cnode
*cp
= VTOC(vp
);
637 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
640 // XXXdbg - don't allow modification of the journal or journal_info_block
641 if (VTOHFS(vp
)->jnl
&& cp
&& cp
->c_datafork
) {
642 struct HFSPlusExtentDescriptor
*extd
;
644 extd
= &cp
->c_datafork
->ff_extents
[0];
645 if (extd
->startBlock
== VTOVCB(vp
)->vcbJinfoBlock
|| extd
->startBlock
== VTOHFS(vp
)->jnl_start
) {
650 #if OVERRIDE_UNKNOWN_PERMISSIONS
651 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
) {
655 cp
->c_mode
&= ~ALLPERMS
;
656 cp
->c_mode
|= (mode
& ALLPERMS
);
657 cp
->c_touch_chgtime
= TRUE
;
664 hfs_write_access(struct vnode
*vp
, kauth_cred_t cred
, struct proc
*p
, Boolean considerFlags
)
666 struct cnode
*cp
= VTOC(vp
);
671 * Disallow write attempts on read-only file systems;
672 * unless the file is a socket, fifo, or a block or
673 * character device resident on the file system.
675 switch (vnode_vtype(vp
)) {
679 if (VTOHFS(vp
)->hfs_flags
& HFS_READ_ONLY
)
686 /* If immutable bit set, nobody gets to write it. */
687 if (considerFlags
&& (cp
->c_flags
& IMMUTABLE
))
690 /* Otherwise, user id 0 always gets access. */
691 if (!suser(cred
, NULL
))
694 /* Otherwise, check the owner. */
695 if ((retval
= hfs_owner_rights(VTOHFS(vp
), cp
->c_uid
, cred
, p
, false)) == 0)
696 return ((cp
->c_mode
& S_IWUSR
) == S_IWUSR
? 0 : EACCES
);
698 /* Otherwise, check the groups. */
699 if (kauth_cred_ismember_gid(cred
, cp
->c_gid
, &is_member
) == 0 && is_member
) {
700 return ((cp
->c_mode
& S_IWGRP
) == S_IWGRP
? 0 : EACCES
);
703 /* Otherwise, check everyone else. */
704 return ((cp
->c_mode
& S_IWOTH
) == S_IWOTH
? 0 : EACCES
);
710 * Change the flags on a file or directory.
711 * cnode must be locked before calling.
715 hfs_chflags(struct vnode
*vp
, uint32_t flags
, __unused kauth_cred_t cred
, __unused
struct proc
*p
)
717 register struct cnode
*cp
= VTOC(vp
);
719 cp
->c_flags
&= SF_SETTABLE
;
720 cp
->c_flags
|= (flags
& UF_SETTABLE
);
721 cp
->c_touch_chgtime
= TRUE
;
728 * Perform chown operation on cnode cp;
729 * code must be locked prior to call.
733 hfs_chown(struct vnode
*vp
, uid_t uid
, gid_t gid
, kauth_cred_t cred
,
736 register struct cnode
*cp
= VTOC(vp
);
746 if (VTOVCB(vp
)->vcbSigWord
!= kHFSPlusSigWord
)
749 if (((unsigned int)vfs_flags(VTOVFS(vp
))) & MNT_UNKNOWNPERMISSIONS
)
752 if (uid
== (uid_t
)VNOVAL
)
754 if (gid
== (gid_t
)VNOVAL
)
757 #if 0 /* we are guaranteed that this is already the case */
759 * If we don't own the file, are trying to change the owner
760 * of the file, or are not a member of the target group,
761 * the caller must be superuser or the call fails.
763 if ((kauth_cred_getuid(cred
) != cp
->c_uid
|| uid
!= cp
->c_uid
||
765 (kauth_cred_ismember_gid(cred
, gid
, &is_member
) || !is_member
))) &&
766 (error
= suser(cred
, 0)))
773 if ((error
= hfs_getinoquota(cp
)))
776 dqrele(cp
->c_dquot
[USRQUOTA
]);
777 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
780 dqrele(cp
->c_dquot
[GRPQUOTA
]);
781 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
785 * Eventually need to account for (fake) a block per directory
786 * if (vnode_isdir(vp))
787 * change = VTOHFS(vp)->blockSize;
791 change
= (int64_t)(cp
->c_blocks
) * (int64_t)VTOVCB(vp
)->blockSize
;
792 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
);
793 (void) hfs_chkiq(cp
, -1, cred
, CHOWN
);
794 for (i
= 0; i
< MAXQUOTAS
; i
++) {
795 dqrele(cp
->c_dquot
[i
]);
796 cp
->c_dquot
[i
] = NODQUOT
;
802 if ((error
= hfs_getinoquota(cp
)) == 0) {
804 dqrele(cp
->c_dquot
[USRQUOTA
]);
805 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
808 dqrele(cp
->c_dquot
[GRPQUOTA
]);
809 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
811 if ((error
= hfs_chkdq(cp
, change
, cred
, CHOWN
)) == 0) {
812 if ((error
= hfs_chkiq(cp
, 1, cred
, CHOWN
)) == 0)
815 (void) hfs_chkdq(cp
, -change
, cred
, CHOWN
|FORCE
);
817 for (i
= 0; i
< MAXQUOTAS
; i
++) {
818 dqrele(cp
->c_dquot
[i
]);
819 cp
->c_dquot
[i
] = NODQUOT
;
824 if (hfs_getinoquota(cp
) == 0) {
826 dqrele(cp
->c_dquot
[USRQUOTA
]);
827 cp
->c_dquot
[USRQUOTA
] = NODQUOT
;
830 dqrele(cp
->c_dquot
[GRPQUOTA
]);
831 cp
->c_dquot
[GRPQUOTA
] = NODQUOT
;
833 (void) hfs_chkdq(cp
, change
, cred
, FORCE
|CHOWN
);
834 (void) hfs_chkiq(cp
, 1, cred
, FORCE
|CHOWN
);
835 (void) hfs_getinoquota(cp
);
839 if (hfs_getinoquota(cp
))
840 panic("hfs_chown: lost quota");
843 if (ouid
!= uid
|| ogid
!= gid
)
844 cp
->c_touch_chgtime
= TRUE
;
850 * The hfs_exchange routine swaps the fork data in two files by
851 * exchanging some of the information in the cnode. It is used
852 * to preserve the file ID when updating an existing file, in
853 * case the file is being tracked through its file ID. Typically
854 * its used after creating a new file during a safe-save.
857 hfs_vnop_exchange(ap
)
858 struct vnop_exchange_args
/* {
862 vfs_context_t a_context;
865 struct vnode
*from_vp
= ap
->a_fvp
;
866 struct vnode
*to_vp
= ap
->a_tvp
;
867 struct cnode
*from_cp
;
869 struct hfsmount
*hfsmp
;
870 struct cat_desc tempdesc
;
871 struct cat_attr tempattr
;
873 int error
= 0, started_tr
= 0, got_cookie
= 0;
876 /* The files must be on the same volume. */
877 if (vnode_mount(from_vp
) != vnode_mount(to_vp
))
880 if (from_vp
== to_vp
)
883 if ((error
= hfs_lockpair(VTOC(from_vp
), VTOC(to_vp
), HFS_EXCLUSIVE_LOCK
)))
886 from_cp
= VTOC(from_vp
);
888 hfsmp
= VTOHFS(from_vp
);
890 /* Only normal files can be exchanged. */
891 if (!vnode_isreg(from_vp
) || !vnode_isreg(to_vp
) ||
892 (from_cp
->c_flag
& C_HARDLINK
) || (to_cp
->c_flag
& C_HARDLINK
) ||
893 VNODE_IS_RSRC(from_vp
) || VNODE_IS_RSRC(to_vp
)) {
898 // XXXdbg - don't allow modification of the journal or journal_info_block
900 struct HFSPlusExtentDescriptor
*extd
;
902 if (from_cp
->c_datafork
) {
903 extd
= &from_cp
->c_datafork
->ff_extents
[0];
904 if (extd
->startBlock
== VTOVCB(from_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
910 if (to_cp
->c_datafork
) {
911 extd
= &to_cp
->c_datafork
->ff_extents
[0];
912 if (extd
->startBlock
== VTOVCB(to_vp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
919 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
925 * Reserve some space in the Catalog file.
927 bzero(&cookie
, sizeof(cookie
));
928 if ((error
= cat_preflight(hfsmp
, CAT_EXCHANGE
, &cookie
, vfs_context_proc(ap
->a_context
)))) {
933 /* The backend code always tries to delete the virtual
934 * extent id for exchanging files so we neeed to lock
935 * the extents b-tree.
937 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_EXTENTS
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
939 /* Do the exchange */
940 error
= ExchangeFileIDs(hfsmp
,
941 from_cp
->c_desc
.cd_nameptr
,
942 to_cp
->c_desc
.cd_nameptr
,
943 from_cp
->c_parentcnid
,
947 hfs_systemfile_unlock(hfsmp
, lockflags
);
950 * Note that we don't need to exchange any extended attributes
951 * since the attributes are keyed by file ID.
954 if (error
!= E_NONE
) {
955 error
= MacToVFSError(error
);
959 /* Purge the vnodes from the name cache */
961 cache_purge(from_vp
);
965 /* Save a copy of from attributes before swapping. */
966 bcopy(&from_cp
->c_desc
, &tempdesc
, sizeof(struct cat_desc
));
967 bcopy(&from_cp
->c_attr
, &tempattr
, sizeof(struct cat_attr
));
970 * Swap the descriptors and all non-fork related attributes.
971 * (except the modify date)
973 bcopy(&to_cp
->c_desc
, &from_cp
->c_desc
, sizeof(struct cat_desc
));
976 from_cp
->c_fileid
= from_cp
->c_cnid
;
977 from_cp
->c_itime
= to_cp
->c_itime
;
978 from_cp
->c_btime
= to_cp
->c_btime
;
979 from_cp
->c_atime
= to_cp
->c_atime
;
980 from_cp
->c_ctime
= to_cp
->c_ctime
;
981 from_cp
->c_gid
= to_cp
->c_gid
;
982 from_cp
->c_uid
= to_cp
->c_uid
;
983 from_cp
->c_flags
= to_cp
->c_flags
;
984 from_cp
->c_mode
= to_cp
->c_mode
;
985 bcopy(to_cp
->c_finderinfo
, from_cp
->c_finderinfo
, 32);
987 bcopy(&tempdesc
, &to_cp
->c_desc
, sizeof(struct cat_desc
));
989 to_cp
->c_fileid
= to_cp
->c_cnid
;
990 to_cp
->c_itime
= tempattr
.ca_itime
;
991 to_cp
->c_btime
= tempattr
.ca_btime
;
992 to_cp
->c_atime
= tempattr
.ca_atime
;
993 to_cp
->c_ctime
= tempattr
.ca_ctime
;
994 to_cp
->c_gid
= tempattr
.ca_gid
;
995 to_cp
->c_uid
= tempattr
.ca_uid
;
996 to_cp
->c_flags
= tempattr
.ca_flags
;
997 to_cp
->c_mode
= tempattr
.ca_mode
;
998 bcopy(tempattr
.ca_finderinfo
, to_cp
->c_finderinfo
, 32);
1000 /* Rehash the cnodes using their new file IDs */
1001 hfs_chash_rehash(from_cp
, to_cp
);
1004 * When a file moves out of "Cleanup At Startup"
1005 * we can drop its NODUMP status.
1007 if ((from_cp
->c_flags
& UF_NODUMP
) &&
1008 (from_cp
->c_parentcnid
!= to_cp
->c_parentcnid
)) {
1009 from_cp
->c_flags
&= ~UF_NODUMP
;
1010 from_cp
->c_touch_chgtime
= TRUE
;
1012 if ((to_cp
->c_flags
& UF_NODUMP
) &&
1013 (to_cp
->c_parentcnid
!= from_cp
->c_parentcnid
)) {
1014 to_cp
->c_flags
&= ~UF_NODUMP
;
1015 to_cp
->c_touch_chgtime
= TRUE
;
1018 HFS_KNOTE(from_vp
, NOTE_ATTRIB
);
1019 HFS_KNOTE(to_vp
, NOTE_ATTRIB
);
1023 cat_postflight(hfsmp
, &cookie
, vfs_context_proc(ap
->a_context
));
1026 hfs_end_transaction(hfsmp
);
1029 hfs_unlockpair(from_cp
, to_cp
);
1035 * cnode must be locked
1039 hfs_fsync(struct vnode
*vp
, int waitfor
, int fullsync
, struct proc
*p
)
1041 struct cnode
*cp
= VTOC(vp
);
1042 struct filefork
*fp
= NULL
;
1044 struct hfsmount
*hfsmp
= VTOHFS(vp
);
1048 int took_trunc_lock
= 0;
1050 wait
= (waitfor
== MNT_WAIT
);
1052 /* HFS directories don't have any data blocks. */
1053 if (vnode_isdir(vp
))
1057 * For system files flush the B-tree header and
1058 * for regular files write out any clusters
1060 if (vnode_issystem(vp
)) {
1061 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1063 if (hfsmp
->jnl
== NULL
) {
1064 BTFlushPath(VTOF(vp
));
1067 } else if (UBCINFOEXISTS(vp
)) {
1069 hfs_lock_truncate(cp
, TRUE
);
1070 took_trunc_lock
= 1;
1072 /* Don't hold cnode lock when calling into cluster layer. */
1073 (void) cluster_push(vp
, 0);
1075 hfs_lock(cp
, HFS_FORCE_LOCK
);
1078 * When MNT_WAIT is requested and the zero fill timeout
1079 * has expired then we must explicitly zero out any areas
1080 * that are currently marked invalid (holes).
1082 * Files with NODUMP can bypass zero filling here.
1084 if ((wait
|| (cp
->c_flag
& C_ZFWANTSYNC
)) &&
1085 ((cp
->c_flags
& UF_NODUMP
) == 0) &&
1086 UBCINFOEXISTS(vp
) && (fp
= VTOF(vp
)) &&
1087 cp
->c_zftimeout
!= 0) {
1089 if (tv
.tv_sec
< cp
->c_zftimeout
) {
1090 /* Remember that a force sync was requested. */
1091 cp
->c_flag
|= C_ZFWANTSYNC
;
1094 if (!took_trunc_lock
) {
1096 hfs_lock_truncate(cp
, TRUE
);
1097 hfs_lock(cp
, HFS_FORCE_LOCK
);
1098 took_trunc_lock
= 1;
1101 while (!CIRCLEQ_EMPTY(&fp
->ff_invalidranges
)) {
1102 struct rl_entry
*invalid_range
= CIRCLEQ_FIRST(&fp
->ff_invalidranges
);
1103 off_t start
= invalid_range
->rl_start
;
1104 off_t end
= invalid_range
->rl_end
;
1106 /* The range about to be written must be validated
1107 * first, so that VNOP_BLOCKMAP() will return the
1108 * appropriate mapping for the cluster code:
1110 rl_remove(start
, end
, &fp
->ff_invalidranges
);
1112 /* Don't hold cnode lock when calling into cluster layer. */
1114 (void) cluster_write(vp
, (struct uio
*) 0,
1115 fp
->ff_size
, end
+ 1, start
, (off_t
)0,
1116 IO_HEADZEROFILL
| IO_NOZERODIRTY
| IO_NOCACHE
);
1117 hfs_lock(cp
, HFS_FORCE_LOCK
);
1118 cp
->c_flag
|= C_MODIFIED
;
1121 (void) cluster_push(vp
, 0);
1122 hfs_lock(cp
, HFS_FORCE_LOCK
);
1124 cp
->c_flag
&= ~C_ZFWANTSYNC
;
1125 cp
->c_zftimeout
= 0;
1128 if (took_trunc_lock
)
1129 hfs_unlock_truncate(cp
);
1132 * if we have a journal and if journal_active() returns != 0 then the
1133 * we shouldn't do anything to a locked block (because it is part
1134 * of a transaction). otherwise we'll just go through the normal
1135 * code path and flush the buffer. note journal_active() can return
1136 * -1 if the journal is invalid -- however we still need to skip any
1137 * locked blocks as they get cleaned up when we finish the transaction
1138 * or close the journal.
1140 // if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
1142 lockflag
= BUF_SKIP_LOCKED
;
1147 * Flush all dirty buffers associated with a vnode.
1149 buf_flushdirtyblks(vp
, wait
, lockflag
, "hfs_fsync");
1152 if (vnode_isreg(vp
) && vnode_issystem(vp
)) {
1153 if (VTOF(vp
)->fcbBTCBPtr
!= NULL
) {
1155 BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1157 cp
->c_touch_acctime
= FALSE
;
1158 cp
->c_touch_chgtime
= FALSE
;
1159 cp
->c_touch_modtime
= FALSE
;
1160 } else /* User file */ {
1161 retval
= hfs_update(vp
, wait
);
1163 /* When MNT_WAIT is requested push out any delayed meta data */
1164 if ((retval
== 0) && wait
&& cp
->c_hint
&&
1165 !ISSET(cp
->c_flag
, C_DELETED
| C_NOEXISTS
)) {
1166 hfs_metasync(VTOHFS(vp
), (daddr64_t
)cp
->c_hint
, p
);
1169 // make sure that we've really been called from the user
1170 // fsync() and if so push out any pending transactions
1171 // that this file might is a part of (and get them on
1175 journal_flush(hfsmp
->jnl
);
1177 /* XXX need to pass context! */
1178 VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, NULL
);
1187 /* Sync an hfs catalog b-tree node */
1189 hfs_metasync(struct hfsmount
*hfsmp
, daddr64_t node
, struct proc
*p
)
1195 vp
= HFSTOVCB(hfsmp
)->catalogRefNum
;
1197 // XXXdbg - don't need to do this on a journaled volume
1202 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1204 * Look for a matching node that has been delayed
1205 * but is not part of a set (B_LOCKED).
1207 * BLK_ONLYVALID causes buf_getblk to return a
1208 * buf_t for the daddr64_t specified only if it's
1209 * currently resident in the cache... the size
1210 * parameter to buf_getblk is ignored when this flag
1213 bp
= buf_getblk(vp
, node
, 0, 0, 0, BLK_META
| BLK_ONLYVALID
);
1216 if ((buf_flags(bp
) & (B_LOCKED
| B_DELWRI
)) == B_DELWRI
)
1217 (void) VNOP_BWRITE(bp
);
1222 hfs_systemfile_unlock(hfsmp
, lockflags
);
1230 hfs_btsync_callback(struct buf
*bp
, void *dummy
)
1232 buf_clearflags(bp
, B_LOCKED
);
1233 (void) buf_bawrite(bp
);
1235 return(BUF_CLAIMED
);
1241 hfs_btsync(struct vnode
*vp
, int sync_transaction
)
1243 struct cnode
*cp
= VTOC(vp
);
1247 if (sync_transaction
)
1248 flags
|= BUF_SKIP_NONLOCKED
;
1250 * Flush all dirty buffers associated with b-tree.
1252 buf_iterate(vp
, hfs_btsync_callback
, flags
, 0);
1255 if (vnode_issystem(vp
) && (VTOF(vp
)->fcbBTCBPtr
!= NULL
))
1256 (void) BTSetLastSync(VTOF(vp
), tv
.tv_sec
);
1257 cp
->c_touch_acctime
= FALSE
;
1258 cp
->c_touch_chgtime
= FALSE
;
1259 cp
->c_touch_modtime
= FALSE
;
1265 * Remove a directory.
1269 struct vnop_rmdir_args
/* {
1270 struct vnode *a_dvp;
1272 struct componentname *a_cnp;
1273 vfs_context_t a_context;
1276 struct vnode
*dvp
= ap
->a_dvp
;
1277 struct vnode
*vp
= ap
->a_vp
;
1280 if (!vnode_isdir(vp
)) {
1286 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1289 error
= hfs_removedir(dvp
, vp
, ap
->a_cnp
, 0);
1291 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1297 * Remove a directory
1299 * Both dvp and vp cnodes are locked
1302 hfs_removedir(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1305 vfs_context_t ctx
= cnp
->cn_context
;
1306 struct proc
*p
= vfs_context_proc(ctx
);
1309 struct hfsmount
* hfsmp
;
1310 struct cat_desc desc
;
1311 cat_cookie_t cookie
;
1313 int error
= 0, started_tr
= 0, got_cookie
= 0;
1320 return (EINVAL
); /* cannot remove "." */
1323 (void)hfs_getinoquota(cp
);
1325 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1330 if (!skip_reserve
) {
1332 * Reserve some space in the Catalog file.
1334 bzero(&cookie
, sizeof(cookie
));
1335 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, p
))) {
1342 * Verify the directory is empty (and valid).
1343 * (Rmdir ".." won't be valid since
1344 * ".." will contain a reference to
1345 * the current directory and thus be
1348 if (cp
->c_entries
!= 0) {
1352 if ((dcp
->c_flags
& APPEND
) || (cp
->c_flags
& (IMMUTABLE
| APPEND
))) {
1357 if (cp
->c_entries
> 0)
1358 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1360 /* Remove the entry from the namei cache: */
1364 * Protect against a race with rename by using the component
1365 * name passed in and parent id from dvp (instead of using
1366 * the cp->c_desc which may have changed).
1368 bzero(&desc
, sizeof(desc
));
1369 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1370 desc
.cd_namelen
= cnp
->cn_namelen
;
1371 desc
.cd_parentcnid
= dcp
->c_cnid
;
1372 desc
.cd_cnid
= cp
->c_cnid
;
1374 /* Remove entry from catalog */
1375 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1376 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1378 /* Delete any attributes, ignore errors */
1379 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1381 hfs_systemfile_unlock(hfsmp
, lockflags
);
1387 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1390 /* The parent lost a child */
1391 if (dcp
->c_entries
> 0)
1393 if (dcp
->c_nlink
> 0)
1395 dcp
->c_touch_chgtime
= TRUE
;
1396 dcp
->c_touch_modtime
= TRUE
;
1398 dcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - don't screw around, force this guy out
1400 (void) hfs_update(dvp
, 0);
1401 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
1403 hfs_volupdate(hfsmp
, VOL_RMDIR
, (dcp
->c_cnid
== kHFSRootFolderID
));
1405 cp
->c_mode
= 0; /* Makes the vnode go away...see inactive */
1406 cp
->c_flag
|= C_NOEXISTS
;
1408 HFS_KNOTE(vp
, NOTE_DELETE
);
1411 cat_postflight(hfsmp
, &cookie
, p
);
1414 hfs_end_transaction(hfsmp
);
1422 * Remove a file or link.
1426 struct vnop_remove_args
/* {
1427 struct vnode *a_dvp;
1429 struct componentname *a_cnp;
1431 vfs_context_t a_context;
1434 struct vnode
*dvp
= ap
->a_dvp
;
1435 struct vnode
*vp
= ap
->a_vp
;
1442 hfs_lock_truncate(VTOC(vp
), TRUE
);
1444 if ((error
= hfs_lockpair(VTOC(dvp
), VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
1447 error
= hfs_removefile(dvp
, vp
, ap
->a_cnp
, ap
->a_flags
, 0);
1449 hfs_unlockpair(VTOC(dvp
), VTOC(vp
));
1451 hfs_unlock_truncate(VTOC(vp
));
1457 hfs_removefile_callback(struct buf
*bp
, void *hfsmp
) {
1459 if ( !(buf_flags(bp
) & B_META
))
1460 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp
);
1462 * it's part of the current transaction, kill it.
1464 journal_kill_block(((struct hfsmount
*)hfsmp
)->jnl
, bp
);
1466 return (BUF_CLAIMED
);
1472 * Similar to hfs_vnop_remove except there are additional options.
1474 * Requires cnode and truncate locks to be held.
1477 hfs_removefile(struct vnode
*dvp
, struct vnode
*vp
, struct componentname
*cnp
,
1478 int flags
, int skip_reserve
)
1480 struct vnode
*rvp
= NULL
;
1483 struct hfsmount
*hfsmp
;
1484 struct cat_desc desc
;
1486 vfs_context_t ctx
= cnp
->cn_context
;
1487 int dataforkbusy
= 0;
1488 int rsrcforkbusy
= 0;
1490 cat_cookie_t cookie
;
1493 int started_tr
= 0, got_cookie
= 0;
1495 cnid_t real_cnid
= 0;
1497 /* Directories should call hfs_rmdir! */
1498 if (vnode_isdir(vp
)) {
1506 if (cp
->c_flag
& (C_NOEXISTS
| C_DELETED
)) {
1510 // if it's not a hardlink, check that the parent
1511 // cnid is the same as the directory cnid
1512 if ( (cp
->c_flag
& C_HARDLINK
) == 0
1513 && (cp
->c_parentcnid
!= hfsmp
->hfs_privdir_desc
.cd_cnid
)
1514 && (cp
->c_parentcnid
!= dcp
->c_cnid
)) {
1519 /* Make sure a remove is permitted */
1520 if (VNODE_IS_RSRC(vp
)) {
1526 * Aquire a vnode for a non-empty resource fork.
1527 * (needed for hfs_truncate)
1529 if (cp
->c_blocks
- VTOF(vp
)->ff_blocks
) {
1530 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, 0);
1535 // XXXdbg - don't allow deleting the journal or journal_info_block
1536 if (hfsmp
->jnl
&& cp
->c_datafork
) {
1537 struct HFSPlusExtentDescriptor
*extd
;
1539 extd
= &cp
->c_datafork
->ff_extents
[0];
1540 if (extd
->startBlock
== HFSTOVCB(hfsmp
)->vcbJinfoBlock
|| extd
->startBlock
== hfsmp
->jnl_start
) {
1547 * Check if this file is being used.
1549 if (vnode_isinuse(vp
, 0))
1551 if (rvp
&& vnode_isinuse(rvp
, 0))
1554 // need this to check if we have to break the deletion
1555 // into multiple pieces
1556 isbigfile
= (VTOC(vp
)->c_datafork
->ff_size
>= HFS_BIGFILE_SIZE
);
1559 * Carbon semantics prohibit deleting busy files.
1560 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
1562 if (dataforkbusy
|| rsrcforkbusy
) {
1563 if ((flags
& VNODE_REMOVE_NODELETEBUSY
) ||
1564 (hfsmp
->hfs_privdir_desc
.cd_cnid
== 0)) {
1571 (void)hfs_getinoquota(cp
);
1575 * We do the ubc_setsize before the hfs_truncate
1576 * since we'll be inside a transaction.
1578 if ((cp
->c_flag
& C_HARDLINK
) == 0 &&
1579 (!dataforkbusy
|| !rsrcforkbusy
)) {
1581 * A ubc_setsize can cause a pagein here
1582 * so we need to the drop cnode lock. Note
1583 * that we still hold the truncate lock.
1586 if (!dataforkbusy
&& cp
->c_datafork
->ff_blocks
&& !isbigfile
) {
1589 if (!rsrcforkbusy
&& rvp
) {
1590 ubc_setsize(rvp
, 0);
1592 hfs_lock(cp
, HFS_FORCE_LOCK
);
1594 struct cat_desc cndesc
;
1596 // for hard links, re-lookup the name that was passed
1597 // in so we get the correct cnid for the name (as
1598 // opposed to the c_cnid in the cnode which could have
1599 // been changed before this node got locked).
1600 bzero(&cndesc
, sizeof(cndesc
));
1601 cndesc
.cd_nameptr
= cnp
->cn_nameptr
;
1602 cndesc
.cd_namelen
= cnp
->cn_namelen
;
1603 cndesc
.cd_parentcnid
= VTOC(dvp
)->c_cnid
;
1604 cndesc
.cd_hint
= VTOC(dvp
)->c_childhint
;
1606 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1608 if (cat_lookup(hfsmp
, &cndesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
1609 hfs_systemfile_unlock(hfsmp
, lockflags
);
1614 hfs_systemfile_unlock(hfsmp
, lockflags
);
1617 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
1622 if (!skip_reserve
) {
1624 * Reserve some space in the Catalog file.
1626 if ((error
= cat_preflight(hfsmp
, CAT_DELETE
, &cookie
, 0))) {
1632 /* Remove our entry from the namei cache. */
1635 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1636 if (hfsmp
->jnl
&& vnode_islnk(vp
))
1637 buf_iterate(vp
, hfs_removefile_callback
, BUF_SKIP_NONLOCKED
, (void *)hfsmp
);
1640 * Truncate any non-busy forks. Busy forks will
1641 * get trucated when their vnode goes inactive.
1643 * Since we're already inside a transaction,
1644 * tell hfs_truncate to skip the ubc_setsize.
1646 * (Note: hard links are truncated in VOP_INACTIVE)
1648 if ((cp
->c_flag
& C_HARDLINK
) == 0) {
1649 int mode
= cp
->c_mode
;
1651 if (!dataforkbusy
&& !isbigfile
&& cp
->c_datafork
->ff_blocks
!= 0) {
1652 cp
->c_mode
= 0; /* Suppress hfs_update */
1653 error
= hfs_truncate(vp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1659 if (!rsrcforkbusy
&& rvp
) {
1660 cp
->c_mode
= 0; /* Suppress hfs_update */
1661 error
= hfs_truncate(rvp
, (off_t
)0, IO_NDELAY
, 1, ctx
);
1670 * Protect against a race with rename by using the component
1671 * name passed in and parent id from dvp (instead of using
1672 * the cp->c_desc which may have changed).
1675 desc
.cd_encoding
= cp
->c_desc
.cd_encoding
;
1676 desc
.cd_nameptr
= cnp
->cn_nameptr
;
1677 desc
.cd_namelen
= cnp
->cn_namelen
;
1678 desc
.cd_parentcnid
= dcp
->c_cnid
;
1679 desc
.cd_hint
= cp
->c_desc
.cd_hint
;
1681 // if it was a hardlink we had to re-lookup the cnid
1682 desc
.cd_cnid
= real_cnid
;
1684 desc
.cd_cnid
= cp
->c_cnid
;
1689 * There are 3 remove cases to consider:
1690 * 1. File is a hardlink ==> remove the link
1691 * 2. File is busy (in use) ==> move/rename the file
1692 * 3. File is not in use ==> remove the file
1695 if (cp
->c_flag
& C_HARDLINK
) {
1696 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1698 /* Delete the link record */
1699 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1701 /* Update the parent directory */
1702 if (dcp
->c_entries
> 0)
1704 if (dcp
->c_nlink
> 0)
1706 dcp
->c_ctime
= tv
.tv_sec
;
1707 dcp
->c_mtime
= tv
.tv_sec
;
1708 (void ) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1710 if (--cp
->c_nlink
< 1) {
1713 struct cat_desc to_desc
;
1714 struct cat_desc from_desc
;
1717 * This is now esentially an open deleted file.
1718 * Rename it to reflect this state which makes
1719 * orphan file cleanup easier (see hfs_remove_orphans).
1720 * Note: a rename failure here is not fatal.
1722 MAKE_INODE_NAME(inodename
, cp
->c_rdev
);
1723 bzero(&from_desc
, sizeof(from_desc
));
1724 from_desc
.cd_nameptr
= inodename
;
1725 from_desc
.cd_namelen
= strlen(inodename
);
1726 from_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1727 from_desc
.cd_flags
= 0;
1728 from_desc
.cd_cnid
= cp
->c_fileid
;
1730 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1731 bzero(&to_desc
, sizeof(to_desc
));
1732 to_desc
.cd_nameptr
= delname
;
1733 to_desc
.cd_namelen
= strlen(delname
);
1734 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1735 to_desc
.cd_flags
= 0;
1736 to_desc
.cd_cnid
= cp
->c_fileid
;
1738 error
= cat_rename(hfsmp
, &from_desc
, &hfsmp
->hfs_privdir_desc
,
1739 &to_desc
, (struct cat_desc
*)NULL
);
1741 panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
1742 inodename
, delname
, cp
);
1745 /* Update the file's state */
1746 cp
->c_flag
|= C_DELETED
;
1747 cp
->c_ctime
= tv
.tv_sec
;
1748 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1751 /* Update the file's state */
1752 cp
->c_ctime
= tv
.tv_sec
;
1753 (void) cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, NULL
, NULL
);
1756 hfs_systemfile_unlock(hfsmp
, lockflags
);
1760 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1762 } else if (dataforkbusy
|| rsrcforkbusy
|| isbigfile
) {
1764 struct cat_desc to_desc
;
1765 struct cat_desc todir_desc
;
1768 * Orphan this file (move to hidden directory).
1770 bzero(&todir_desc
, sizeof(todir_desc
));
1771 todir_desc
.cd_parentcnid
= 2;
1773 MAKE_DELETED_NAME(delname
, cp
->c_fileid
);
1774 bzero(&to_desc
, sizeof(to_desc
));
1775 to_desc
.cd_nameptr
= delname
;
1776 to_desc
.cd_namelen
= strlen(delname
);
1777 to_desc
.cd_parentcnid
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1778 to_desc
.cd_flags
= 0;
1779 to_desc
.cd_cnid
= cp
->c_cnid
;
1781 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
1783 error
= cat_rename(hfsmp
, &desc
, &todir_desc
,
1784 &to_desc
, (struct cat_desc
*)NULL
);
1787 hfsmp
->hfs_privdir_attr
.ca_entries
++;
1788 (void) cat_update(hfsmp
, &hfsmp
->hfs_privdir_desc
,
1789 &hfsmp
->hfs_privdir_attr
, NULL
, NULL
);
1791 /* Update the parent directory */
1792 if (dcp
->c_entries
> 0)
1794 if (dcp
->c_nlink
> 0)
1796 dcp
->c_ctime
= tv
.tv_sec
;
1797 dcp
->c_mtime
= tv
.tv_sec
;
1798 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1800 /* Update the file's state */
1801 cp
->c_flag
|= C_DELETED
;
1802 cp
->c_ctime
= tv
.tv_sec
;
1804 (void) cat_update(hfsmp
, &to_desc
, &cp
->c_attr
, NULL
, NULL
);
1806 hfs_systemfile_unlock(hfsmp
, lockflags
);
1810 } else /* Not busy */ {
1812 if (cp
->c_blocks
> 0) {
1813 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1814 cp
->c_desc
.cd_nameptr
);
1819 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_ATTRIBUTE
, HFS_EXCLUSIVE_LOCK
);
1821 error
= cat_delete(hfsmp
, &desc
, &cp
->c_attr
);
1823 if (error
&& error
!= ENXIO
&& error
!= ENOENT
&& truncated
) {
1824 if ((cp
->c_datafork
&& cp
->c_datafork
->ff_size
!= 0) ||
1825 (cp
->c_rsrcfork
&& cp
->c_rsrcfork
->ff_size
!= 0)) {
1826 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
1827 error
, cp
->c_datafork
->ff_size
, cp
->c_rsrcfork
->ff_size
);
1829 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1830 cp
->c_desc
.cd_nameptr
, cp
->c_attr
.ca_fileid
, error
);
1834 /* Delete any attributes, ignore errors */
1835 (void) hfs_removeallattr(hfsmp
, cp
->c_fileid
);
1837 /* Update the parent directory */
1838 if (dcp
->c_entries
> 0)
1840 if (dcp
->c_nlink
> 0)
1842 dcp
->c_ctime
= tv
.tv_sec
;
1843 dcp
->c_mtime
= tv
.tv_sec
;
1844 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
1846 hfs_systemfile_unlock(hfsmp
, lockflags
);
1851 (void)hfs_chkiq(cp
, -1, NOCRED
, 0);
1855 truncated
= 0; // because the catalog entry is gone
1856 cp
->c_flag
|= C_NOEXISTS
;
1857 cp
->c_touch_chgtime
= TRUE
; /* XXX needed ? */
1860 hfs_volupdate(hfsmp
, VOL_RMFILE
, (dcp
->c_cnid
== kHFSRootFolderID
));
1864 * All done with this cnode's descriptor...
1866 * Note: all future catalog calls for this cnode must be
1867 * by fileid only. This is OK for HFS (which doesn't have
1868 * file thread records) since HFS doesn't support hard
1869 * links or the removal of busy files.
1871 cat_releasedesc(&cp
->c_desc
);
1873 HFS_KNOTE(dvp
, NOTE_WRITE
);
1877 cat_postflight(hfsmp
, &cookie
, 0);
1880 /* Commit the truncation to the catalog record */
1882 cp
->c_flag
|= C_FORCEUPDATE
;
1883 cp
->c_touch_chgtime
= TRUE
;
1884 cp
->c_touch_modtime
= TRUE
;
1885 (void) hfs_update(vp
, 0);
1889 hfs_end_transaction(hfsmp
);
1892 HFS_KNOTE(vp
, NOTE_DELETE
);
1894 HFS_KNOTE(rvp
, NOTE_DELETE
);
1895 /* Defer the vnode_put on rvp until the hfs_unlock(). */
1896 cp
->c_flag
|= C_NEED_RVNODE_PUT
;
1903 __private_extern__
void
1904 replace_desc(struct cnode
*cp
, struct cat_desc
*cdp
)
1906 /* First release allocated name buffer */
1907 if (cp
->c_desc
.cd_flags
& CD_HASBUF
&& cp
->c_desc
.cd_nameptr
!= 0) {
1908 char *name
= cp
->c_desc
.cd_nameptr
;
1910 cp
->c_desc
.cd_nameptr
= 0;
1911 cp
->c_desc
.cd_namelen
= 0;
1912 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
1913 vfs_removename(name
);
1915 bcopy(cdp
, &cp
->c_desc
, sizeof(cp
->c_desc
));
1917 /* Cnode now owns the name buffer */
1918 cdp
->cd_nameptr
= 0;
1919 cdp
->cd_namelen
= 0;
1920 cdp
->cd_flags
&= ~CD_HASBUF
;
1927 * The VFS layer guarantees that:
1928 * - source and destination will either both be directories, or
1929 * both not be directories.
1930 * - all the vnodes are from the same file system
1932 * When the target is a directory, HFS must ensure that its empty.
1936 struct vnop_rename_args
/* {
1937 struct vnode *a_fdvp;
1938 struct vnode *a_fvp;
1939 struct componentname *a_fcnp;
1940 struct vnode *a_tdvp;
1941 struct vnode *a_tvp;
1942 struct componentname *a_tcnp;
1943 vfs_context_t a_context;
1946 struct vnode
*tvp
= ap
->a_tvp
;
1947 struct vnode
*tdvp
= ap
->a_tdvp
;
1948 struct vnode
*fvp
= ap
->a_fvp
;
1949 struct vnode
*fdvp
= ap
->a_fdvp
;
1950 struct componentname
*tcnp
= ap
->a_tcnp
;
1951 struct componentname
*fcnp
= ap
->a_fcnp
;
1952 struct proc
*p
= vfs_context_proc(ap
->a_context
);
1957 struct cat_desc from_desc
;
1958 struct cat_desc to_desc
;
1959 struct cat_desc out_desc
;
1960 struct hfsmount
*hfsmp
;
1961 cat_cookie_t cookie
;
1962 int tvp_deleted
= 0;
1963 int started_tr
= 0, got_cookie
= 0;
1964 int took_trunc_lock
= 0;
1968 /* When tvp exist, take the truncate lock for the hfs_removefile(). */
1969 if (tvp
&& vnode_isreg(tvp
)) {
1970 hfs_lock_truncate(VTOC(tvp
), TRUE
);
1971 took_trunc_lock
= 1;
1974 error
= hfs_lockfour(VTOC(fdvp
), VTOC(fvp
), VTOC(tdvp
), tvp
? VTOC(tvp
) : NULL
,
1975 HFS_EXCLUSIVE_LOCK
);
1977 if (took_trunc_lock
)
1978 hfs_unlock_truncate(VTOC(tvp
));
1985 tcp
= tvp
? VTOC(tvp
) : NULL
;
1986 hfsmp
= VTOHFS(tdvp
);
1988 /* Check for a race against unlink. */
1989 if (fcp
->c_flag
& C_NOEXISTS
) {
1995 * The following edge case is caught here:
1996 * (to cannot be a descendent of from)
2009 if (tdcp
->c_parentcnid
== fcp
->c_cnid
) {
2015 * The following two edge cases are caught here:
2016 * (note tvp is not empty)
2029 if (tvp
&& vnode_isdir(tvp
) && (tcp
->c_entries
!= 0) && fvp
!= tvp
) {
2035 * The following edge case is caught here:
2036 * (the from child and parent are the same)
2049 * Make sure "from" vnode and its parent are changeable.
2051 if ((fcp
->c_flags
& (IMMUTABLE
| APPEND
)) || (fdcp
->c_flags
& APPEND
)) {
2057 * If the destination parent directory is "sticky", then the
2058 * user must own the parent directory, or the destination of
2059 * the rename, otherwise the destination may not be changed
2060 * (except by root). This implements append-only directories.
2062 * Note that checks for immutable and write access are done
2063 * by the call to hfs_removefile.
2065 if (tvp
&& (tdcp
->c_mode
& S_ISTXT
) &&
2066 (suser(vfs_context_ucred(tcnp
->cn_context
), NULL
)) &&
2067 (kauth_cred_getuid(vfs_context_ucred(tcnp
->cn_context
)) != tdcp
->c_uid
) &&
2068 (hfs_owner_rights(hfsmp
, tcp
->c_uid
, vfs_context_ucred(tcnp
->cn_context
), p
, false)) ) {
2075 (void)hfs_getinoquota(tcp
);
2077 /* Preflighting done, take fvp out of the name space. */
2081 * When a file moves out of "Cleanup At Startup"
2082 * we can drop its NODUMP status.
2084 if ((fcp
->c_flags
& UF_NODUMP
) &&
2087 (fdcp
->c_desc
.cd_nameptr
!= NULL
) &&
2088 (strcmp(fdcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
2089 fcp
->c_flags
&= ~UF_NODUMP
;
2090 fcp
->c_touch_chgtime
= TRUE
;
2091 (void) hfs_update(fvp
, 0);
2094 bzero(&from_desc
, sizeof(from_desc
));
2095 from_desc
.cd_nameptr
= fcnp
->cn_nameptr
;
2096 from_desc
.cd_namelen
= fcnp
->cn_namelen
;
2097 from_desc
.cd_parentcnid
= fdcp
->c_cnid
;
2098 from_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2099 from_desc
.cd_cnid
= fcp
->c_cnid
;
2101 bzero(&to_desc
, sizeof(to_desc
));
2102 to_desc
.cd_nameptr
= tcnp
->cn_nameptr
;
2103 to_desc
.cd_namelen
= tcnp
->cn_namelen
;
2104 to_desc
.cd_parentcnid
= tdcp
->c_cnid
;
2105 to_desc
.cd_flags
= fcp
->c_desc
.cd_flags
& ~(CD_HASBUF
| CD_DECOMPOSED
);
2106 to_desc
.cd_cnid
= fcp
->c_cnid
;
2108 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2113 // if it's a hardlink then re-lookup the name so
2114 // that we get the correct cnid in from_desc (see
2115 // the comment in hfs_removefile for more details)
2117 if (fcp
->c_flag
& C_HARDLINK
) {
2118 struct cat_desc tmpdesc
;
2121 bzero(&tmpdesc
, sizeof(tmpdesc
));
2122 tmpdesc
.cd_nameptr
= fcnp
->cn_nameptr
;
2123 tmpdesc
.cd_namelen
= fcnp
->cn_namelen
;
2124 tmpdesc
.cd_parentcnid
= fdcp
->c_cnid
;
2125 tmpdesc
.cd_hint
= fdcp
->c_childhint
;
2127 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2129 if (cat_lookup(hfsmp
, &tmpdesc
, 0, NULL
, NULL
, NULL
, &real_cnid
) != 0) {
2130 hfs_systemfile_unlock(hfsmp
, lockflags
);
2134 // use the real cnid instead of whatever happened to be there
2135 from_desc
.cd_cnid
= real_cnid
;
2136 hfs_systemfile_unlock(hfsmp
, lockflags
);
2140 * Reserve some space in the Catalog file.
2142 bzero(&cookie
, sizeof(cookie
));
2143 if ((error
= cat_preflight(hfsmp
, CAT_RENAME
+ CAT_DELETE
, &cookie
, p
))) {
2149 * If the destination exists then it may need to be removed.
2153 * When fvp matches tvp they must be case variants
2158 * If this a hard link with different parents
2159 * and its not a case variant then tvp should
2162 if (!((fcp
->c_flag
& C_HARDLINK
) &&
2164 (hfs_namecmp(fcnp
->cn_nameptr
, fcnp
->cn_namelen
,
2165 tcnp
->cn_nameptr
, tcnp
->cn_namelen
) != 0)))) {
2172 if (vnode_isdir(tvp
))
2173 error
= hfs_removedir(tdvp
, tvp
, tcnp
, HFSRM_SKIP_RESERVE
);
2175 error
= hfs_removefile(tdvp
, tvp
, tcnp
, 0, HFSRM_SKIP_RESERVE
);
2184 * All done with tvp and fvp
2187 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2188 error
= cat_rename(hfsmp
, &from_desc
, &tdcp
->c_desc
, &to_desc
, &out_desc
);
2189 hfs_systemfile_unlock(hfsmp
, lockflags
);
2195 /* Invalidate negative cache entries in the destination directory */
2196 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
2197 cache_purge_negatives(tdvp
);
2199 /* Update cnode's catalog descriptor */
2200 replace_desc(fcp
, &out_desc
);
2201 fcp
->c_parentcnid
= tdcp
->c_cnid
;
2204 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_RMDIR
: VOL_RMFILE
,
2205 (fdcp
->c_cnid
== kHFSRootFolderID
));
2206 hfs_volupdate(hfsmp
, vnode_isdir(fvp
) ? VOL_MKDIR
: VOL_MKFILE
,
2207 (tdcp
->c_cnid
== kHFSRootFolderID
));
2209 /* Update both parent directories. */
2213 if (fdcp
->c_nlink
> 0)
2215 if (fdcp
->c_entries
> 0)
2217 fdcp
->c_touch_chgtime
= TRUE
;
2218 fdcp
->c_touch_modtime
= TRUE
;
2220 fdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2221 (void) hfs_update(fdvp
, 0);
2223 tdcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
2224 tdcp
->c_touch_chgtime
= TRUE
;
2225 tdcp
->c_touch_modtime
= TRUE
;
2227 tdcp
->c_flag
|= C_FORCEUPDATE
; // XXXdbg - force it out!
2228 (void) hfs_update(tdvp
, 0);
2231 cat_postflight(hfsmp
, &cookie
, p
);
2234 hfs_end_transaction(hfsmp
);
2237 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2238 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2241 HFS_KNOTE(fvp
, NOTE_RENAME
);
2242 HFS_KNOTE(fdvp
, NOTE_WRITE
);
2243 if (tdvp
!= fdvp
) HFS_KNOTE(tdvp
, NOTE_WRITE
);
2246 if (took_trunc_lock
)
2247 hfs_unlock_truncate(VTOC(tvp
));
2249 hfs_unlockfour(fdcp
, fcp
, tdcp
, tcp
);
2251 /* After tvp is removed the only acceptable error is EIO */
2252 if (error
&& tvp_deleted
)
2263 hfs_vnop_mkdir(struct vnop_mkdir_args
*ap
)
2265 /***** HACK ALERT ********/
2266 ap
->a_cnp
->cn_flags
|= MAKEENTRY
;
2267 return hfs_makenode(ap
->a_dvp
, ap
->a_vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
);
2272 * Create a symbolic link.
2275 hfs_vnop_symlink(struct vnop_symlink_args
*ap
)
2277 struct vnode
**vpp
= ap
->a_vpp
;
2278 struct vnode
*dvp
= ap
->a_dvp
;
2279 struct vnode
*vp
= NULL
;
2280 struct hfsmount
*hfsmp
;
2281 struct filefork
*fp
;
2282 struct buf
*bp
= NULL
;
2287 /* HFS standard disks don't support symbolic links */
2288 if (VTOVCB(dvp
)->vcbSigWord
!= kHFSPlusSigWord
)
2291 /* Check for empty target name */
2292 if (ap
->a_target
[0] == 0)
2295 /* Create the vnode */
2296 ap
->a_vap
->va_mode
|= S_IFLNK
;
2297 if ((error
= hfs_makenode(dvp
, vpp
, ap
->a_cnp
, ap
->a_vap
, ap
->a_context
))) {
2301 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2304 hfsmp
= VTOHFS(dvp
);
2305 len
= strlen(ap
->a_target
);
2308 (void)hfs_getinoquota(VTOC(vp
));
2311 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2317 * Allocate space for the link.
2319 * Since we're already inside a transaction,
2320 * tell hfs_truncate to skip the ubc_setsize.
2322 * Don't need truncate lock since a symlink is treated as a system file.
2324 error
= hfs_truncate(vp
, len
, IO_NOZEROFILL
, 1, ap
->a_context
);
2326 goto out
; /* XXX need to remove link */
2328 /* Write the link to disk */
2329 bp
= buf_getblk(vp
, (daddr64_t
)0, roundup((int)fp
->ff_size
, VTOHFS(vp
)->hfs_phys_block_size
),
2332 journal_modify_block_start(hfsmp
->jnl
, bp
);
2334 datap
= (char *)buf_dataptr(bp
);
2335 bzero(datap
, buf_size(bp
));
2336 bcopy(ap
->a_target
, datap
, len
);
2339 journal_modify_block_end(hfsmp
->jnl
, bp
);
2344 * We defered the ubc_setsize for hfs_truncate
2345 * since we were inside a transaction.
2347 * We don't need to drop the cnode lock here
2348 * since this is a symlink.
2350 ubc_setsize(vp
, len
);
2353 hfs_end_transaction(hfsmp
);
2355 hfs_unlock(VTOC(vp
));
2361 /* structures to hold a "." or ".." directory entry */
2362 struct hfs_stddotentry
{
2363 u_int32_t d_fileno
; /* unique file number */
2364 u_int16_t d_reclen
; /* length of this structure */
2365 u_int8_t d_type
; /* dirent file type */
2366 u_int8_t d_namlen
; /* len of filename */
2367 char d_name
[4]; /* "." or ".." */
2370 struct hfs_extdotentry
{
2371 u_int64_t d_fileno
; /* unique file number */
2372 u_int64_t d_seekoff
; /* seek offset (optional, used by servers) */
2373 u_int16_t d_reclen
; /* length of this structure */
2374 u_int16_t d_namlen
; /* len of filename */
2375 u_int8_t d_type
; /* dirent file type */
2376 u_char d_name
[3]; /* "." or ".." */
2380 struct hfs_stddotentry std
;
2381 struct hfs_extdotentry ext
;
2385 * hfs_vnop_readdir reads directory entries into the buffer pointed
2386 * to by uio, in a filesystem independent format. Up to uio_resid
2387 * bytes of data can be transferred. The data in the buffer is a
2388 * series of packed dirent structures where each one contains the
2389 * following entries:
2391 * u_int32_t d_fileno; // file number of entry
2392 * u_int16_t d_reclen; // length of this record
2393 * u_int8_t d_type; // file type
2394 * u_int8_t d_namlen; // length of string in d_name
2395 * char d_name[MAXNAMELEN+1]; // null terminated file name
2397 * The current position (uio_offset) refers to the next block of
2398 * entries. The offset can only be set to a value previously
2399 * returned by hfs_vnop_readdir or zero. This offset does not have
2400 * to match the number of bytes returned (in uio_resid).
2402 * In fact, the offset used by HFS is essentially an index (26 bits)
2403 * with a tag (6 bits). The tag is for associating the next request
2404 * with the current request. This enables us to have multiple threads
2405 * reading the directory while the directory is also being modified.
2407 * Each tag/index pair is tied to a unique directory hint. The hint
2408 * contains information (filename) needed to build the catalog b-tree
2409 * key for finding the next set of entries.
2412 hfs_vnop_readdir(ap
)
2413 struct vnop_readdir_args
/* {
2419 vfs_context_t a_context;
2422 struct vnode
*vp
= ap
->a_vp
;
2423 uio_t uio
= ap
->a_uio
;
2425 struct hfsmount
*hfsmp
;
2426 directoryhint_t
*dirhint
= NULL
;
2427 directoryhint_t localhint
;
2432 user_addr_t user_start
= 0;
2433 user_size_t user_len
= 0;
2441 cnid_t cnid_hint
= 0;
2444 startoffset
= offset
= uio_offset(uio
);
2445 bufstart
= CAST_DOWN(caddr_t
, uio_iov_base(uio
));
2446 extended
= (ap
->a_flags
& VNODE_READDIR_EXTENDED
);
2447 nfs_cookies
= extended
&& (ap
->a_flags
& VNODE_READDIR_REQSEEKOFF
);
2449 /* Sanity check the uio data. */
2450 if ((uio_iovcnt(uio
) > 1) ||
2451 (uio_resid(uio
) < (int)sizeof(struct dirent
))) {
2454 /* Note that the dirhint calls require an exclusive lock. */
2455 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2460 /* Pick up cnid hint (if any). */
2462 cnid_hint
= (cnid_t
)(uio_offset(uio
) >> 32);
2463 uio_setoffset(uio
, uio_offset(uio
) & 0x00000000ffffffffLL
);
2466 * Synthesize entries for "." and ".."
2469 hfs_dotentry_t dotentry
[2];
2473 struct hfs_extdotentry
*entry
= &dotentry
[0].ext
;
2475 entry
->d_fileno
= cp
->c_cnid
;
2476 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2477 entry
->d_type
= DT_DIR
;
2478 entry
->d_namlen
= 1;
2479 entry
->d_name
[0] = '.';
2480 entry
->d_name
[1] = '\0';
2481 entry
->d_name
[2] = '\0';
2482 entry
->d_seekoff
= 1;
2485 entry
->d_fileno
= cp
->c_parentcnid
;
2486 entry
->d_reclen
= sizeof(struct hfs_extdotentry
);
2487 entry
->d_type
= DT_DIR
;
2488 entry
->d_namlen
= 2;
2489 entry
->d_name
[0] = '.';
2490 entry
->d_name
[1] = '.';
2491 entry
->d_name
[2] = '\0';
2492 entry
->d_seekoff
= 2;
2493 uiosize
= 2 * sizeof(struct hfs_extdotentry
);
2495 struct hfs_stddotentry
*entry
= &dotentry
[0].std
;
2497 entry
->d_fileno
= cp
->c_cnid
;
2498 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2499 entry
->d_type
= DT_DIR
;
2500 entry
->d_namlen
= 1;
2501 *(int *)&entry
->d_name
[0] = 0;
2502 entry
->d_name
[0] = '.';
2505 entry
->d_fileno
= cp
->c_parentcnid
;
2506 entry
->d_reclen
= sizeof(struct hfs_stddotentry
);
2507 entry
->d_type
= DT_DIR
;
2508 entry
->d_namlen
= 2;
2509 *(int *)&entry
->d_name
[0] = 0;
2510 entry
->d_name
[0] = '.';
2511 entry
->d_name
[1] = '.';
2512 uiosize
= 2 * sizeof(struct hfs_stddotentry
);
2514 if ((error
= uiomove((caddr_t
)&dotentry
, uiosize
, uio
))) {
2520 /* If there are no real entries then we're done. */
2521 if (cp
->c_entries
== 0) {
2524 uio_setoffset(uio
, offset
);
2529 // We have to lock the user's buffer here so that we won't
2530 // fault on it after we've acquired a shared lock on the
2531 // catalog file. The issue is that you can get a 3-way
2532 // deadlock if someone else starts a transaction and then
2533 // tries to lock the catalog file but can't because we're
2534 // here and we can't service our page fault because VM is
2535 // blocked trying to start a transaction as a result of
2536 // trying to free up pages for our page fault. It's messy
2537 // but it does happen on dual-procesors that are paging
2538 // heavily (see radar 3082639 for more info). By locking
2539 // the buffer up-front we prevent ourselves from faulting
2540 // while holding the shared catalog file lock.
2542 // Fortunately this and hfs_search() are the only two places
2543 // currently (10/30/02) that can fault on user data with a
2544 // shared lock on the catalog file.
2546 if (hfsmp
->jnl
&& uio_isuserspace(uio
)) {
2547 user_start
= uio_curriovbase(uio
);
2548 user_len
= uio_curriovlen(uio
);
2550 if ((error
= vslock(user_start
, user_len
)) != 0) {
2555 /* Convert offset into a catalog directory index. */
2556 index
= (offset
& HFS_INDEX_MASK
) - 2;
2557 tag
= offset
& ~HFS_INDEX_MASK
;
2559 /* Lock catalog during cat_findname and cat_getdirentries. */
2560 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2562 /* When called from NFS, try and resolve a cnid hint. */
2563 if (nfs_cookies
&& cnid_hint
!= 0) {
2564 if (cat_findname(hfsmp
, cnid_hint
, &localhint
.dh_desc
) == 0) {
2565 if ( localhint
.dh_desc
.cd_parentcnid
== cp
->c_cnid
) {
2566 localhint
.dh_index
= index
- 1;
2567 localhint
.dh_time
= 0;
2568 localhint
.dh_link
.sle_next
= 0;
2569 dirhint
= &localhint
; /* don't forget to release the descriptor */
2571 cat_releasedesc(&localhint
.dh_desc
);
2576 /* Get a directory hint (cnode must be locked exclusive) */
2577 if (dirhint
== NULL
) {
2578 dirhint
= hfs_getdirhint(cp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
);
2580 /* Hide tag from catalog layer. */
2581 dirhint
->dh_index
&= HFS_INDEX_MASK
;
2582 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
2583 dirhint
->dh_index
= -1;
2587 /* Pack the buffer with dirent entries. */
2588 error
= cat_getdirentries(hfsmp
, cp
->c_entries
, dirhint
, uio
, extended
, &items
);
2590 hfs_systemfile_unlock(hfsmp
, lockflags
);
2596 /* Get index to the next item */
2599 if (items
>= (int)cp
->c_entries
) {
2603 /* Convert catalog directory index back into an offset. */
2605 tag
= (++cp
->c_dirhinttag
) << HFS_INDEX_BITS
;
2606 uio_setoffset(uio
, (index
+ 2) | tag
);
2607 dirhint
->dh_index
|= tag
;
2610 cp
->c_touch_acctime
= TRUE
;
2612 if (ap
->a_numdirent
) {
2613 if (startoffset
== 0)
2615 *ap
->a_numdirent
= items
;
2619 if (hfsmp
->jnl
&& user_start
) {
2620 vsunlock(user_start
, user_len
, TRUE
);
2622 /* If we didn't do anything then go ahead and dump the hint. */
2623 if ((dirhint
!= NULL
) &&
2624 (dirhint
!= &localhint
) &&
2625 (uio_offset(uio
) == startoffset
)) {
2626 hfs_reldirhint(cp
, dirhint
);
2629 if (ap
->a_eofflag
) {
2630 *ap
->a_eofflag
= eofflag
;
2632 if (dirhint
== &localhint
) {
2633 cat_releasedesc(&localhint
.dh_desc
);
2641 * Read contents of a symbolic link.
2644 hfs_vnop_readlink(ap
)
2645 struct vnop_readlink_args
/* {
2648 vfs_context_t a_context;
2651 struct vnode
*vp
= ap
->a_vp
;
2653 struct filefork
*fp
;
2656 if (!vnode_islnk(vp
))
2659 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
2664 /* Zero length sym links are not allowed */
2665 if (fp
->ff_size
== 0 || fp
->ff_size
> MAXPATHLEN
) {
2666 VTOVCB(vp
)->vcbFlags
|= kHFS_DamagedVolume
;
2671 /* Cache the path so we don't waste buffer cache resources */
2672 if (fp
->ff_symlinkptr
== NULL
) {
2673 struct buf
*bp
= NULL
;
2675 MALLOC(fp
->ff_symlinkptr
, char *, fp
->ff_size
, M_TEMP
, M_WAITOK
);
2676 error
= (int)buf_meta_bread(vp
, (daddr64_t
)0,
2677 roundup((int)fp
->ff_size
,
2678 VTOHFS(vp
)->hfs_phys_block_size
),
2679 vfs_context_ucred(ap
->a_context
), &bp
);
2683 if (fp
->ff_symlinkptr
) {
2684 FREE(fp
->ff_symlinkptr
, M_TEMP
);
2685 fp
->ff_symlinkptr
= NULL
;
2689 bcopy((char *)buf_dataptr(bp
), fp
->ff_symlinkptr
, (size_t)fp
->ff_size
);
2691 if (VTOHFS(vp
)->jnl
&& (buf_flags(bp
) & B_LOCKED
) == 0) {
2692 buf_markinvalid(bp
); /* data no longer needed */
2696 error
= uiomove((caddr_t
)fp
->ff_symlinkptr
, (int)fp
->ff_size
, ap
->a_uio
);
2699 * Keep track blocks read
2701 if ((VTOHFS(vp
)->hfc_stage
== HFC_RECORDING
) && (error
== 0)) {
2704 * If this file hasn't been seen since the start of
2705 * the current sampling period then start over.
2707 if (cp
->c_atime
< VTOHFS(vp
)->hfc_timebase
)
2708 VTOF(vp
)->ff_bytesread
= fp
->ff_size
;
2710 VTOF(vp
)->ff_bytesread
+= fp
->ff_size
;
2712 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
2713 // cp->c_touch_acctime = TRUE;
2723 * Get configurable pathname variables.
2726 hfs_vnop_pathconf(ap
)
2727 struct vnop_pathconf_args
/* {
2731 vfs_context_t a_context;
2734 switch (ap
->a_name
) {
2736 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2739 *ap
->a_retval
= HFS_LINK_MAX
;
2742 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_STANDARD
)
2743 *ap
->a_retval
= kHFSMaxFileNameChars
; /* 255 */
2745 *ap
->a_retval
= kHFSPlusMaxFileNameChars
; /* 31 */
2748 *ap
->a_retval
= PATH_MAX
; /* 1024 */
2751 *ap
->a_retval
= PIPE_BUF
;
2753 case _PC_CHOWN_RESTRICTED
:
2759 case _PC_NAME_CHARS_MAX
:
2760 *ap
->a_retval
= kHFSPlusMaxFileNameChars
;
2762 case _PC_CASE_SENSITIVE
:
2763 if (VTOHFS(ap
->a_vp
)->hfs_flags
& HFS_CASE_SENSITIVE
)
2768 case _PC_CASE_PRESERVING
:
2780 * Update a cnode's on-disk metadata.
2782 * If waitfor is set, then wait for the disk write of
2783 * the node to complete.
2785 * The cnode must be locked exclusive
2789 hfs_update(struct vnode
*vp
, __unused
int waitfor
)
2791 struct cnode
*cp
= VTOC(vp
);
2793 struct cat_fork
*dataforkp
= NULL
;
2794 struct cat_fork
*rsrcforkp
= NULL
;
2795 struct cat_fork datafork
;
2796 struct hfsmount
*hfsmp
;
2803 if (vnode_issystem(vp
) && (cp
->c_cnid
< kHFSFirstUserCatalogNodeID
)) {
2806 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) || (cp
->c_mode
== 0)) {
2807 cp
->c_flag
&= ~C_MODIFIED
;
2808 cp
->c_touch_acctime
= 0;
2809 cp
->c_touch_chgtime
= 0;
2810 cp
->c_touch_modtime
= 0;
2814 hfs_touchtimes(hfsmp
, cp
);
2816 /* Nothing to update. */
2817 if ((cp
->c_flag
& (C_MODIFIED
| C_FORCEUPDATE
)) == 0) {
2822 dataforkp
= &cp
->c_datafork
->ff_data
;
2824 rsrcforkp
= &cp
->c_rsrcfork
->ff_data
;
2827 * For delayed allocations updates are
2828 * postponed until an fsync or the file
2829 * gets written to disk.
2831 * Deleted files can defer meta data updates until inactive.
2833 * If we're ever called with the C_FORCEUPDATE flag though
2834 * we have to do the update.
2836 if (ISSET(cp
->c_flag
, C_FORCEUPDATE
) == 0 &&
2837 (ISSET(cp
->c_flag
, C_DELETED
) ||
2838 (dataforkp
&& cp
->c_datafork
->ff_unallocblocks
) ||
2839 (rsrcforkp
&& cp
->c_rsrcfork
->ff_unallocblocks
))) {
2840 // cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
2841 cp
->c_flag
|= C_MODIFIED
;
2843 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2848 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2853 * For files with invalid ranges (holes) the on-disk
2854 * field representing the size of the file (cf_size)
2855 * must be no larger than the start of the first hole.
2857 if (dataforkp
&& !CIRCLEQ_EMPTY(&cp
->c_datafork
->ff_invalidranges
)) {
2858 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2859 datafork
.cf_size
= CIRCLEQ_FIRST(&cp
->c_datafork
->ff_invalidranges
)->rl_start
;
2860 dataforkp
= &datafork
;
2861 } else if (dataforkp
&& (cp
->c_datafork
->ff_unallocblocks
!= 0)) {
2862 // always make sure the block count and the size
2863 // of the file match the number of blocks actually
2864 // allocated to the file on disk
2865 bcopy(dataforkp
, &datafork
, sizeof(datafork
));
2866 // make sure that we don't assign a negative block count
2867 if (cp
->c_datafork
->ff_blocks
< cp
->c_datafork
->ff_unallocblocks
) {
2868 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
2869 cp
->c_datafork
->ff_blocks
, cp
->c_datafork
->ff_unallocblocks
);
2871 datafork
.cf_blocks
= (cp
->c_datafork
->ff_blocks
- cp
->c_datafork
->ff_unallocblocks
);
2872 datafork
.cf_size
= datafork
.cf_blocks
* HFSTOVCB(hfsmp
)->blockSize
;
2873 dataforkp
= &datafork
;
2877 * Lock the Catalog b-tree file.
2878 * A shared lock is sufficient since an update doesn't change
2879 * the tree and the lock on vp protects the cnode.
2881 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
2883 /* XXX - waitfor is not enforced */
2884 error
= cat_update(hfsmp
, &cp
->c_desc
, &cp
->c_attr
, dataforkp
, rsrcforkp
);
2886 hfs_systemfile_unlock(hfsmp
, lockflags
);
2888 /* After the updates are finished, clear the flags */
2889 cp
->c_flag
&= ~(C_MODIFIED
| C_FORCEUPDATE
);
2891 hfs_end_transaction(hfsmp
);
2893 HFS_KNOTE(vp
, NOTE_ATTRIB
);
2899 * Allocate a new node
2902 hfs_makenode(struct vnode
*dvp
, struct vnode
**vpp
, struct componentname
*cnp
,
2903 struct vnode_attr
*vap
, vfs_context_t ctx
)
2905 struct cnode
*cp
= NULL
;
2908 struct hfsmount
*hfsmp
;
2909 struct cat_desc in_desc
, out_desc
;
2910 struct cat_attr attr
;
2912 cat_cookie_t cookie
;
2914 int error
, started_tr
= 0, got_cookie
= 0;
2915 enum vtype vnodetype
;
2918 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
)))
2921 hfsmp
= VTOHFS(dvp
);
2924 out_desc
.cd_flags
= 0;
2925 out_desc
.cd_nameptr
= NULL
;
2927 mode
= MAKEIMODE(vap
->va_type
, vap
->va_mode
);
2929 if ((mode
& S_IFMT
) == 0)
2931 vnodetype
= IFTOVT(mode
);
2933 /* Check if were out of usable disk space. */
2934 if ((hfs_freeblks(hfsmp
, 1) <= 0) && (suser(vfs_context_ucred(ctx
), NULL
) != 0)) {
2941 /* Setup the default attributes */
2942 bzero(&attr
, sizeof(attr
));
2943 attr
.ca_mode
= mode
;
2944 attr
.ca_nlink
= vnodetype
== VDIR
? 2 : 1;
2945 attr
.ca_mtime
= tv
.tv_sec
;
2946 if ((VTOVCB(dvp
)->vcbSigWord
== kHFSSigWord
) && gTimeZone
.tz_dsttime
) {
2947 attr
.ca_mtime
+= 3600; /* Same as what hfs_update does */
2949 attr
.ca_atime
= attr
.ca_ctime
= attr
.ca_itime
= attr
.ca_mtime
;
2950 attr
.ca_atimeondisk
= attr
.ca_atime
;
2952 attr
.ca_uid
= vap
->va_uid
;
2953 attr
.ca_gid
= vap
->va_gid
;
2954 VATTR_SET_SUPPORTED(vap
, va_mode
);
2955 VATTR_SET_SUPPORTED(vap
, va_uid
);
2956 VATTR_SET_SUPPORTED(vap
, va_gid
);
2958 /* Tag symlinks with a type and creator. */
2959 if (vnodetype
== VLNK
) {
2960 struct FndrFileInfo
*fip
;
2962 fip
= (struct FndrFileInfo
*)&attr
.ca_finderinfo
;
2963 fip
->fdType
= SWAP_BE32(kSymLinkFileType
);
2964 fip
->fdCreator
= SWAP_BE32(kSymLinkCreator
);
2966 if (cnp
->cn_flags
& ISWHITEOUT
)
2967 attr
.ca_flags
|= UF_OPAQUE
;
2969 /* Setup the descriptor */
2970 in_desc
.cd_nameptr
= cnp
->cn_nameptr
;
2971 in_desc
.cd_namelen
= cnp
->cn_namelen
;
2972 in_desc
.cd_parentcnid
= dcp
->c_cnid
;
2973 in_desc
.cd_flags
= S_ISDIR(mode
) ? CD_ISDIR
: 0;
2974 in_desc
.cd_hint
= dcp
->c_childhint
;
2975 in_desc
.cd_encoding
= 0;
2977 if ((error
= hfs_start_transaction(hfsmp
)) != 0) {
2983 * Reserve some space in the Catalog file.
2985 * (we also add CAT_DELETE since our getnewvnode
2986 * request can cause an hfs_inactive call to
2987 * delete an unlinked file)
2989 if ((error
= cat_preflight(hfsmp
, CAT_CREATE
| CAT_DELETE
, &cookie
, 0))) {
2994 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
2995 error
= cat_create(hfsmp
, &in_desc
, &attr
, &out_desc
);
2997 /* Update the parent directory */
2998 dcp
->c_childhint
= out_desc
.cd_hint
; /* Cache directory's location */
3001 dcp
->c_ctime
= tv
.tv_sec
;
3002 dcp
->c_mtime
= tv
.tv_sec
;
3003 (void) cat_update(hfsmp
, &dcp
->c_desc
, &dcp
->c_attr
, NULL
, NULL
);
3004 HFS_KNOTE(dvp
, NOTE_ATTRIB
);
3006 hfs_systemfile_unlock(hfsmp
, lockflags
);
3010 /* Invalidate negative cache entries in the directory */
3011 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
3012 cache_purge_negatives(dvp
);
3014 if (vnodetype
== VDIR
) {
3015 HFS_KNOTE(dvp
, NOTE_WRITE
| NOTE_LINK
);
3017 HFS_KNOTE(dvp
, NOTE_WRITE
);
3020 hfs_volupdate(hfsmp
, vnodetype
== VDIR
? VOL_MKDIR
: VOL_MKFILE
,
3021 (dcp
->c_cnid
== kHFSRootFolderID
));
3024 // have to end the transaction here before we call hfs_getnewvnode()
3025 // because that can cause us to try and reclaim a vnode on a different
3026 // file system which could cause us to start a transaction which can
3027 // deadlock with someone on that other file system (since we could be
3028 // holding two transaction locks as well as various vnodes and we did
3029 // not obtain the locks on them in the proper order).
3031 // NOTE: this means that if the quota check fails or we have to update
3032 // the change time on a block-special device that those changes
3033 // will happen as part of independent transactions.
3036 hfs_end_transaction(hfsmp
);
3041 * Create a vnode for the object just created.
3043 * The cnode is locked on successful return.
3045 error
= hfs_getnewvnode(hfsmp
, dvp
, cnp
, &out_desc
, 0, &attr
, NULL
, &tvp
);
3050 //cache_enter(dvp, tvp, cnp);
3055 * We call hfs_chkiq with FORCE flag so that if we
3056 * fall through to the rmdir we actually have
3057 * accounted for the inode
3059 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_QUOTA
) {
3060 if ((error
= hfs_getinoquota(cp
)) ||
3061 (error
= hfs_chkiq(cp
, 1, vfs_context_ucred(ctx
), FORCE
))) {
3063 if (vnode_isdir(tvp
))
3064 (void) hfs_removedir(dvp
, tvp
, cnp
, 0);
3067 hfs_lock_truncate(cp
, TRUE
);
3068 hfs_lock(cp
, HFS_FORCE_LOCK
);
3069 (void) hfs_removefile(dvp
, tvp
, cnp
, 0, 0);
3070 hfs_unlock_truncate(cp
);
3073 * we successfully allocated a new vnode, but
3074 * the quota check is telling us we're beyond
3075 * our limit, so we need to dump our lock + reference
3085 /* Remember if any ACL data was set. */
3086 if (VATTR_IS_ACTIVE(vap
, va_acl
) &&
3087 (vap
->va_acl
!= NULL
)) {
3088 cp
->c_attr
.ca_recflags
|= kHFSHasSecurityMask
;
3089 cp
->c_touch_chgtime
= TRUE
;
3090 (void) hfs_update(tvp
, TRUE
);
3094 cat_releasedesc(&out_desc
);
3097 cat_postflight(hfsmp
, &cookie
, 0);
3100 * Check if a file is located in the "Cleanup At Startup"
3101 * directory. If it is then tag it as NODUMP so that we
3102 * can be lazy about zero filling data holes.
3104 if ((error
== 0) && dvp
&& (vnodetype
== VREG
) &&
3105 (dcp
->c_desc
.cd_nameptr
!= NULL
) &&
3106 (strcmp(dcp
->c_desc
.cd_nameptr
, CARBON_TEMP_DIR_NAME
) == 0)) {
3113 * The parent of "Cleanup At Startup" should
3114 * have the ASCII name of the userid.
3116 if (hfs_vget(hfsmp
, dcp
->c_parentcnid
, &ddvp
, 0) == 0) {
3117 if (VTOC(ddvp
)->c_desc
.cd_nameptr
) {
3120 uid
= strtoul(VTOC(ddvp
)->c_desc
.cd_nameptr
, 0, 0);
3121 if ((uid
== cp
->c_uid
) ||
3122 (uid
== vfs_context_ucred(ctx
)->cr_uid
)) {
3123 cp
->c_flags
|= UF_NODUMP
;
3124 cp
->c_touch_chgtime
= TRUE
;
3127 hfs_unlock(VTOC(ddvp
));
3134 if (error
== 0 && cp
!= NULL
) {
3138 hfs_end_transaction(hfsmp
);
3147 * WARNING - assumes caller has cnode lock.
3151 hfs_vgetrsrc(struct hfsmount
*hfsmp
, struct vnode
*vp
, struct vnode
**rvpp
, __unused
struct proc
*p
)
3154 struct cnode
*cp
= VTOC(vp
);
3158 if ((rvp
= cp
->c_rsrc_vp
)) {
3159 vid
= vnode_vid(rvp
);
3161 /* Use exising vnode */
3162 error
= vnode_getwithvid(rvp
, vid
);
3164 char * name
= VTOC(vp
)->c_desc
.cd_nameptr
;
3167 printf("hfs_vgetrsrc: couldn't get"
3168 " resource fork for %s\n", name
);
3172 struct cat_fork rsrcfork
;
3173 struct componentname cn
;
3176 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3178 /* Get resource fork data */
3179 error
= cat_lookup(hfsmp
, &cp
->c_desc
, 1, (struct cat_desc
*)0,
3180 (struct cat_attr
*)0, &rsrcfork
, NULL
);
3182 hfs_systemfile_unlock(hfsmp
, lockflags
);
3187 * Supply hfs_getnewvnode with a component name.
3190 if (cp
->c_desc
.cd_nameptr
) {
3191 MALLOC_ZONE(cn
.cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3192 cn
.cn_nameiop
= LOOKUP
;
3193 cn
.cn_flags
= ISLASTCN
| HASBUF
;
3194 cn
.cn_context
= NULL
;
3195 cn
.cn_pnlen
= MAXPATHLEN
;
3196 cn
.cn_nameptr
= cn
.cn_pnbuf
;
3199 cn
.cn_namelen
= sprintf(cn
.cn_nameptr
, "%s%s", cp
->c_desc
.cd_nameptr
, _PATH_RSRCFORKSPEC
);
3201 error
= hfs_getnewvnode(hfsmp
, vnode_parent(vp
), cn
.cn_pnbuf
? &cn
: NULL
,
3202 &cp
->c_desc
, 2, &cp
->c_attr
, &rsrcfork
, &rvp
);
3204 FREE_ZONE(cn
.cn_pnbuf
, cn
.cn_pnlen
, M_NAMEI
);
3215 filt_hfsdetach(struct knote
*kn
)
3219 vp
= (struct vnode
*)kn
->kn_hook
;
3220 if (vnode_getwithvid(vp
, kn
->kn_hookid
))
3223 if (1) { /* ! KNDETACH_VNLOCKED */
3224 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3225 (void) KNOTE_DETACH(&VTOC(vp
)->c_knotes
, kn
);
3226 hfs_unlock(VTOC(vp
));
3235 filt_hfsread(struct knote
*kn
, long hint
)
3237 struct vnode
*vp
= (struct vnode
*)kn
->kn_hook
;
3241 if ((vnode_getwithvid(vp
, kn
->kn_hookid
) != 0)) {
3246 if (hint
== NOTE_REVOKE
) {
3248 * filesystem is gone, so set the EOF flag and schedule
3249 * the knote for deletion.
3251 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3255 /* poll(2) semantics dictate always saying there is data */
3256 kn
->kn_data
= (!(kn
->kn_flags
& EV_POLL
)) ?
3257 VTOF(vp
)->ff_size
- kn
->kn_fp
->f_fglob
->fg_offset
: 1;
3262 return (kn
->kn_data
!= 0);
3267 filt_hfswrite(struct knote
*kn
, long hint
)
3272 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3275 vnode_put(kn
->kn_hook
);
3277 if (hint
== NOTE_REVOKE
) {
3279 * filesystem is gone, so set the EOF flag and schedule
3280 * the knote for deletion.
3283 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3291 filt_hfsvnode(struct knote
*kn
, long hint
)
3295 if ((vnode_getwithvid(kn
->kn_hook
, kn
->kn_hookid
) != 0)) {
3298 vnode_put(kn
->kn_hook
);
3300 if (kn
->kn_sfflags
& hint
)
3301 kn
->kn_fflags
|= hint
;
3302 if ((hint
== NOTE_REVOKE
)) {
3303 kn
->kn_flags
|= (EV_EOF
| EV_ONESHOT
);
3307 return (kn
->kn_fflags
!= 0);
3310 static struct filterops hfsread_filtops
=
3311 { 1, NULL
, filt_hfsdetach
, filt_hfsread
};
3312 static struct filterops hfswrite_filtops
=
3313 { 1, NULL
, filt_hfsdetach
, filt_hfswrite
};
3314 static struct filterops hfsvnode_filtops
=
3315 { 1, NULL
, filt_hfsdetach
, filt_hfsvnode
};
3318 * Add a kqueue filter.
3322 struct vnop_kqfilt_add_args
/* {
3326 vfs_context_t a_context;
3329 struct vnode
*vp
= ap
->a_vp
;
3330 struct knote
*kn
= ap
->a_kn
;
3333 switch (kn
->kn_filter
) {
3335 if (vnode_isreg(vp
)) {
3336 kn
->kn_fop
= &hfsread_filtops
;
3342 if (vnode_isreg(vp
)) {
3343 kn
->kn_fop
= &hfswrite_filtops
;
3349 kn
->kn_fop
= &hfsvnode_filtops
;
3355 kn
->kn_hook
= (caddr_t
)vp
;
3356 kn
->kn_hookid
= vnode_vid(vp
);
3358 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
)))
3360 KNOTE_ATTACH(&VTOC(vp
)->c_knotes
, kn
);
3361 hfs_unlock(VTOC(vp
));
3367 * Remove a kqueue filter
3370 hfs_vnop_kqfiltremove(ap
)
3371 struct vnop_kqfilt_remove_args
/* {
3374 vfs_context_t a_context;
3379 result
= ENOTSUP
; /* XXX */
3385 * Wrapper for special device reads
3389 struct vnop_read_args
/* {
3393 vfs_context_t a_context;
3399 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3400 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3404 * Wrapper for special device writes
3408 struct vnop_write_args
/* {
3412 vfs_context_t a_context;
3416 * Set update and change flags.
3418 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3419 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3420 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3424 * Wrapper for special device close
3426 * Update the times on the cnode then do device close.
3430 struct vnop_close_args
/* {
3433 vfs_context_t a_context;
3436 struct vnode
*vp
= ap
->a_vp
;
3439 if (vnode_isinuse(ap
->a_vp
, 1)) {
3440 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3442 hfs_touchtimes(VTOHFS(vp
), cp
);
3446 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3451 * Wrapper for fifo reads
3455 struct vnop_read_args
/* {
3459 vfs_context_t a_context;
3462 extern int (**fifo_vnodeop_p
)(void *);
3467 VTOC(ap
->a_vp
)->c_touch_acctime
= TRUE
;
3468 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_read
), ap
));
3472 * Wrapper for fifo writes
3476 struct vnop_write_args
/* {
3480 vfs_context_t a_context;
3483 extern int (**fifo_vnodeop_p
)(void *);
3486 * Set update and change flags.
3488 VTOC(ap
->a_vp
)->c_touch_chgtime
= TRUE
;
3489 VTOC(ap
->a_vp
)->c_touch_modtime
= TRUE
;
3490 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_write
), ap
));
3494 * Wrapper for fifo close
3496 * Update the times on the cnode then do device close.
3500 struct vnop_close_args
/* {
3503 vfs_context_t a_context;
3506 extern int (**fifo_vnodeop_p
)(void *);
3507 struct vnode
*vp
= ap
->a_vp
;
3510 if (vnode_isinuse(ap
->a_vp
, 1)) {
3511 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) == 0) {
3513 hfs_touchtimes(VTOHFS(vp
), cp
);
3517 return (VOCALL (fifo_vnodeop_p
, VOFFSET(vnop_close
), ap
));
3521 * kqfilt_add wrapper for fifos.
3523 * Fall through to hfs kqfilt_add routines if needed
3526 hfsfifo_kqfilt_add(ap
)
3527 struct vnop_kqfilt_add_args
*ap
;
3529 extern int (**fifo_vnodeop_p
)(void *);
3532 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_add
), ap
);
3534 error
= hfs_vnop_kqfiltadd(ap
);
3539 * kqfilt_remove wrapper for fifos.
3541 * Fall through to hfs kqfilt_remove routines if needed
3544 hfsfifo_kqfilt_remove(ap
)
3545 struct vnop_kqfilt_remove_args
*ap
;
3547 extern int (**fifo_vnodeop_p
)(void *);
3550 error
= VOCALL(fifo_vnodeop_p
, VOFFSET(vnop_kqfilt_remove
), ap
);
3552 error
= hfs_vnop_kqfiltremove(ap
);
3559 * Synchronize a file's in-core state with that on disk.
3563 struct vnop_fsync_args
/* {
3566 vfs_context_t a_context;
3569 struct vnode
* vp
= ap
->a_vp
;
3573 * We need to allow ENOENT lock errors since unlink
3574 * systenm call can call VNOP_FSYNC during vclean.
3576 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
3580 error
= hfs_fsync(vp
, ap
->a_waitfor
, 0, vfs_context_proc(ap
->a_context
));
3582 hfs_unlock(VTOC(vp
));
3586 /*****************************************************************************
3590 *****************************************************************************/
3591 int hfs_vnop_readdirattr(struct vnop_readdirattr_args
*); /* in hfs_attrlist.c */
3592 int hfs_vnop_inactive(struct vnop_inactive_args
*); /* in hfs_cnode.c */
3593 int hfs_vnop_reclaim(struct vnop_reclaim_args
*); /* in hfs_cnode.c */
3594 int hfs_vnop_link(struct vnop_link_args
*); /* in hfs_link.c */
3595 int hfs_vnop_lookup(struct vnop_lookup_args
*); /* in hfs_lookup.c */
3596 int hfs_vnop_search(struct vnop_searchfs_args
*); /* in hfs_search.c */
3598 int hfs_vnop_read(struct vnop_read_args
*); /* in hfs_readwrite.c */
3599 int hfs_vnop_write(struct vnop_write_args
*); /* in hfs_readwrite.c */
3600 int hfs_vnop_ioctl(struct vnop_ioctl_args
*); /* in hfs_readwrite.c */
3601 int hfs_vnop_select(struct vnop_select_args
*); /* in hfs_readwrite.c */
3602 int hfs_vnop_strategy(struct vnop_strategy_args
*); /* in hfs_readwrite.c */
3603 int hfs_vnop_allocate(struct vnop_allocate_args
*); /* in hfs_readwrite.c */
3604 int hfs_vnop_pagein(struct vnop_pagein_args
*); /* in hfs_readwrite.c */
3605 int hfs_vnop_pageout(struct vnop_pageout_args
*); /* in hfs_readwrite.c */
3606 int hfs_vnop_bwrite(struct vnop_bwrite_args
*); /* in hfs_readwrite.c */
3607 int hfs_vnop_blktooff(struct vnop_blktooff_args
*); /* in hfs_readwrite.c */
3608 int hfs_vnop_offtoblk(struct vnop_offtoblk_args
*); /* in hfs_readwrite.c */
3609 int hfs_vnop_blockmap(struct vnop_blockmap_args
*); /* in hfs_readwrite.c */
3610 int hfs_vnop_getxattr(struct vnop_getxattr_args
*); /* in hfs_xattr.c */
3611 int hfs_vnop_setxattr(struct vnop_setxattr_args
*); /* in hfs_xattr.c */
3612 int hfs_vnop_removexattr(struct vnop_removexattr_args
*); /* in hfs_xattr.c */
3613 int hfs_vnop_listxattr(struct vnop_listxattr_args
*); /* in hfs_xattr.c */
3615 int (**hfs_vnodeop_p
)(void *);
3617 #define VOPFUNC int (*)(void *)
3619 struct vnodeopv_entry_desc hfs_vnodeop_entries
[] = {
3620 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3621 { &vnop_lookup_desc
, (VOPFUNC
)hfs_vnop_lookup
}, /* lookup */
3622 { &vnop_create_desc
, (VOPFUNC
)hfs_vnop_create
}, /* create */
3623 { &vnop_mknod_desc
, (VOPFUNC
)hfs_vnop_mknod
}, /* mknod */
3624 { &vnop_open_desc
, (VOPFUNC
)hfs_vnop_open
}, /* open */
3625 { &vnop_close_desc
, (VOPFUNC
)hfs_vnop_close
}, /* close */
3626 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3627 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3628 { &vnop_read_desc
, (VOPFUNC
)hfs_vnop_read
}, /* read */
3629 { &vnop_write_desc
, (VOPFUNC
)hfs_vnop_write
}, /* write */
3630 { &vnop_ioctl_desc
, (VOPFUNC
)hfs_vnop_ioctl
}, /* ioctl */
3631 { &vnop_select_desc
, (VOPFUNC
)hfs_vnop_select
}, /* select */
3632 { &vnop_revoke_desc
, (VOPFUNC
)nop_revoke
}, /* revoke */
3633 { &vnop_exchange_desc
, (VOPFUNC
)hfs_vnop_exchange
}, /* exchange */
3634 { &vnop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
3635 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3636 { &vnop_remove_desc
, (VOPFUNC
)hfs_vnop_remove
}, /* remove */
3637 { &vnop_link_desc
, (VOPFUNC
)hfs_vnop_link
}, /* link */
3638 { &vnop_rename_desc
, (VOPFUNC
)hfs_vnop_rename
}, /* rename */
3639 { &vnop_mkdir_desc
, (VOPFUNC
)hfs_vnop_mkdir
}, /* mkdir */
3640 { &vnop_rmdir_desc
, (VOPFUNC
)hfs_vnop_rmdir
}, /* rmdir */
3641 { &vnop_symlink_desc
, (VOPFUNC
)hfs_vnop_symlink
}, /* symlink */
3642 { &vnop_readdir_desc
, (VOPFUNC
)hfs_vnop_readdir
}, /* readdir */
3643 { &vnop_readdirattr_desc
, (VOPFUNC
)hfs_vnop_readdirattr
}, /* readdirattr */
3644 { &vnop_readlink_desc
, (VOPFUNC
)hfs_vnop_readlink
}, /* readlink */
3645 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3646 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3647 { &vnop_strategy_desc
, (VOPFUNC
)hfs_vnop_strategy
}, /* strategy */
3648 { &vnop_pathconf_desc
, (VOPFUNC
)hfs_vnop_pathconf
}, /* pathconf */
3649 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3650 { &vnop_allocate_desc
, (VOPFUNC
)hfs_vnop_allocate
}, /* allocate */
3651 { &vnop_searchfs_desc
, (VOPFUNC
)hfs_vnop_search
}, /* search fs */
3652 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
}, /* bwrite */
3653 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* pagein */
3654 { &vnop_pageout_desc
,(VOPFUNC
) hfs_vnop_pageout
}, /* pageout */
3655 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3656 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3657 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3658 { &vnop_blockmap_desc
, (VOPFUNC
)hfs_vnop_blockmap
}, /* blockmap */
3659 { &vnop_kqfilt_add_desc
, (VOPFUNC
)hfs_vnop_kqfiltadd
}, /* kqfilt_add */
3660 { &vnop_kqfilt_remove_desc
, (VOPFUNC
)hfs_vnop_kqfiltremove
}, /* kqfilt_remove */
3661 { &vnop_getxattr_desc
, (VOPFUNC
)hfs_vnop_getxattr
},
3662 { &vnop_setxattr_desc
, (VOPFUNC
)hfs_vnop_setxattr
},
3663 { &vnop_removexattr_desc
, (VOPFUNC
)hfs_vnop_removexattr
},
3664 { &vnop_listxattr_desc
, (VOPFUNC
)hfs_vnop_listxattr
},
3665 { NULL
, (VOPFUNC
)NULL
}
3668 struct vnodeopv_desc hfs_vnodeop_opv_desc
=
3669 { &hfs_vnodeop_p
, hfs_vnodeop_entries
};
3671 int (**hfs_specop_p
)(void *);
3672 struct vnodeopv_entry_desc hfs_specop_entries
[] = {
3673 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3674 { &vnop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
3675 { &vnop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
3676 { &vnop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
3677 { &vnop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
3678 { &vnop_close_desc
, (VOPFUNC
)hfsspec_close
}, /* close */
3679 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3680 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3681 { &vnop_read_desc
, (VOPFUNC
)hfsspec_read
}, /* read */
3682 { &vnop_write_desc
, (VOPFUNC
)hfsspec_write
}, /* write */
3683 { &vnop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
3684 { &vnop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
3685 { &vnop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
3686 { &vnop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
3687 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3688 { &vnop_remove_desc
, (VOPFUNC
)spec_remove
}, /* remove */
3689 { &vnop_link_desc
, (VOPFUNC
)spec_link
}, /* link */
3690 { &vnop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
3691 { &vnop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
3692 { &vnop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
3693 { &vnop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
3694 { &vnop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
3695 { &vnop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
3696 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3697 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3698 { &vnop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
3699 { &vnop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
3700 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3701 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
},
3702 { &vnop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
3703 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* Pagein */
3704 { &vnop_pageout_desc
, (VOPFUNC
)hfs_vnop_pageout
}, /* Pageout */
3705 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3706 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3707 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3708 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3710 struct vnodeopv_desc hfs_specop_opv_desc
=
3711 { &hfs_specop_p
, hfs_specop_entries
};
3714 int (**hfs_fifoop_p
)(void *);
3715 struct vnodeopv_entry_desc hfs_fifoop_entries
[] = {
3716 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
3717 { &vnop_lookup_desc
, (VOPFUNC
)fifo_lookup
}, /* lookup */
3718 { &vnop_create_desc
, (VOPFUNC
)fifo_create
}, /* create */
3719 { &vnop_mknod_desc
, (VOPFUNC
)fifo_mknod
}, /* mknod */
3720 { &vnop_open_desc
, (VOPFUNC
)fifo_open
}, /* open */
3721 { &vnop_close_desc
, (VOPFUNC
)hfsfifo_close
}, /* close */
3722 { &vnop_getattr_desc
, (VOPFUNC
)hfs_vnop_getattr
}, /* getattr */
3723 { &vnop_setattr_desc
, (VOPFUNC
)hfs_vnop_setattr
}, /* setattr */
3724 { &vnop_read_desc
, (VOPFUNC
)hfsfifo_read
}, /* read */
3725 { &vnop_write_desc
, (VOPFUNC
)hfsfifo_write
}, /* write */
3726 { &vnop_ioctl_desc
, (VOPFUNC
)fifo_ioctl
}, /* ioctl */
3727 { &vnop_select_desc
, (VOPFUNC
)fifo_select
}, /* select */
3728 { &vnop_revoke_desc
, (VOPFUNC
)fifo_revoke
}, /* revoke */
3729 { &vnop_mmap_desc
, (VOPFUNC
)fifo_mmap
}, /* mmap */
3730 { &vnop_fsync_desc
, (VOPFUNC
)hfs_vnop_fsync
}, /* fsync */
3731 { &vnop_remove_desc
, (VOPFUNC
)fifo_remove
}, /* remove */
3732 { &vnop_link_desc
, (VOPFUNC
)fifo_link
}, /* link */
3733 { &vnop_rename_desc
, (VOPFUNC
)fifo_rename
}, /* rename */
3734 { &vnop_mkdir_desc
, (VOPFUNC
)fifo_mkdir
}, /* mkdir */
3735 { &vnop_rmdir_desc
, (VOPFUNC
)fifo_rmdir
}, /* rmdir */
3736 { &vnop_symlink_desc
, (VOPFUNC
)fifo_symlink
}, /* symlink */
3737 { &vnop_readdir_desc
, (VOPFUNC
)fifo_readdir
}, /* readdir */
3738 { &vnop_readlink_desc
, (VOPFUNC
)fifo_readlink
}, /* readlink */
3739 { &vnop_inactive_desc
, (VOPFUNC
)hfs_vnop_inactive
}, /* inactive */
3740 { &vnop_reclaim_desc
, (VOPFUNC
)hfs_vnop_reclaim
}, /* reclaim */
3741 { &vnop_strategy_desc
, (VOPFUNC
)fifo_strategy
}, /* strategy */
3742 { &vnop_pathconf_desc
, (VOPFUNC
)fifo_pathconf
}, /* pathconf */
3743 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
3744 { &vnop_bwrite_desc
, (VOPFUNC
)hfs_vnop_bwrite
},
3745 { &vnop_pagein_desc
, (VOPFUNC
)hfs_vnop_pagein
}, /* Pagein */
3746 { &vnop_pageout_desc
, (VOPFUNC
)hfs_vnop_pageout
}, /* Pageout */
3747 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* copyfile */
3748 { &vnop_blktooff_desc
, (VOPFUNC
)hfs_vnop_blktooff
}, /* blktooff */
3749 { &vnop_offtoblk_desc
, (VOPFUNC
)hfs_vnop_offtoblk
}, /* offtoblk */
3750 { &vnop_blockmap_desc
, (VOPFUNC
)hfs_vnop_blockmap
}, /* blockmap */
3751 { &vnop_kqfilt_add_desc
, (VOPFUNC
)hfsfifo_kqfilt_add
}, /* kqfilt_add */
3752 { &vnop_kqfilt_remove_desc
, (VOPFUNC
)hfsfifo_kqfilt_remove
}, /* kqfilt_remove */
3753 { (struct vnodeop_desc
*)NULL
, (VOPFUNC
)NULL
}
3755 struct vnodeopv_desc hfs_fifoop_opv_desc
=
3756 { &hfs_fifoop_p
, hfs_fifoop_entries
};