2 * Copyright (c) 1999-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 1991, 1993, 1994
30 * The Regents of the University of California. All rights reserved.
31 * (c) UNIX System Laboratories, Inc.
32 * All or some portions of this file are derived from material licensed
33 * to the University of California by American Telephone and Telegraph
34 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
35 * the permission of UNIX System Laboratories, Inc.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed by the University of
48 * California, Berkeley and its contributors.
49 * 4. Neither the name of the University nor the names of its contributors
50 * may be used to endorse or promote products derived from this software
51 * without specific prior written permission.
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
68 * (c) Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
70 * hfs_vfsops.c -- VFS layer for loadable HFS file system.
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/kauth.h>
78 #include <sys/ubc_internal.h>
79 #include <sys/vnode_internal.h>
80 #include <sys/mount_internal.h>
81 #include <sys/sysctl.h>
82 #include <sys/malloc.h>
84 #include <sys/quota.h>
86 #include <sys/paths.h>
87 #include <sys/utfconv.h>
88 #include <sys/kdebug.h>
89 #include <sys/fslog.h>
92 #include <kern/locks.h>
94 #include <vfs/vfs_journal.h>
96 #include <miscfs/specfs/specdev.h>
97 #include <hfs/hfs_mount.h>
99 #include <libkern/crypto/md5.h>
100 #include <uuid/uuid.h>
103 #include "hfs_catalog.h"
104 #include "hfs_cnode.h"
106 #include "hfs_endian.h"
107 #include "hfs_hotfiles.h"
108 #include "hfs_quota.h"
110 #include "hfscommon/headers/FileMgrInternal.h"
111 #include "hfscommon/headers/BTreesInternal.h"
114 #include <sys/cprotect.h>
117 #if CONFIG_HFS_ALLOC_RBTREE
118 #include "hfscommon/headers/HybridAllocator.h"
121 #define HFS_MOUNT_DEBUG 1
128 /* Enable/disable debugging code for live volume resizing */
129 int hfs_resize_debug
= 0;
131 lck_grp_attr_t
* hfs_group_attr
;
132 lck_attr_t
* hfs_lock_attr
;
133 lck_grp_t
* hfs_mutex_group
;
134 lck_grp_t
* hfs_rwlock_group
;
135 lck_grp_t
* hfs_spinlock_group
;
137 extern struct vnodeopv_desc hfs_vnodeop_opv_desc
;
138 extern struct vnodeopv_desc hfs_std_vnodeop_opv_desc
;
140 /* not static so we can re-use in hfs_readwrite.c for build_path calls */
141 int hfs_vfs_vget(struct mount
*mp
, ino64_t ino
, struct vnode
**vpp
, vfs_context_t context
);
143 static int hfs_changefs(struct mount
*mp
, struct hfs_mount_args
*args
);
144 static int hfs_fhtovp(struct mount
*mp
, int fhlen
, unsigned char *fhp
, struct vnode
**vpp
, vfs_context_t context
);
145 static int hfs_flushfiles(struct mount
*, int, struct proc
*);
146 static int hfs_flushMDB(struct hfsmount
*hfsmp
, int waitfor
, int altflush
);
147 static int hfs_getmountpoint(struct vnode
*vp
, struct hfsmount
**hfsmpp
);
148 static int hfs_init(struct vfsconf
*vfsp
);
149 static int hfs_vfs_root(struct mount
*mp
, struct vnode
**vpp
, vfs_context_t context
);
150 static int hfs_quotactl(struct mount
*, int, uid_t
, caddr_t
, vfs_context_t context
);
151 static int hfs_start(struct mount
*mp
, int flags
, vfs_context_t context
);
152 static int hfs_vptofh(struct vnode
*vp
, int *fhlenp
, unsigned char *fhp
, vfs_context_t context
);
153 static int hfs_file_extent_overlaps(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, struct HFSPlusCatalogFile
*filerec
);
154 static int hfs_journal_replay(vnode_t devvp
, vfs_context_t context
);
155 static int hfs_reclaimspace(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, u_int32_t reclaimblks
, vfs_context_t context
);
157 void hfs_initialize_allocator (struct hfsmount
*hfsmp
);
158 int hfs_teardown_allocator (struct hfsmount
*hfsmp
);
160 int hfs_mount(struct mount
*mp
, vnode_t devvp
, user_addr_t data
, vfs_context_t context
);
161 int hfs_mountfs(struct vnode
*devvp
, struct mount
*mp
, struct hfs_mount_args
*args
, int journal_replay_only
, vfs_context_t context
);
162 int hfs_reload(struct mount
*mp
);
163 int hfs_statfs(struct mount
*mp
, register struct vfsstatfs
*sbp
, vfs_context_t context
);
164 int hfs_sync(struct mount
*mp
, int waitfor
, vfs_context_t context
);
165 int hfs_sysctl(int *name
, u_int namelen
, user_addr_t oldp
, size_t *oldlenp
,
166 user_addr_t newp
, size_t newlen
, vfs_context_t context
);
167 int hfs_unmount(struct mount
*mp
, int mntflags
, vfs_context_t context
);
170 * Called by vfs_mountroot when mounting HFS Plus as root.
174 hfs_mountroot(mount_t mp
, vnode_t rvp
, vfs_context_t context
)
176 struct hfsmount
*hfsmp
;
178 struct vfsstatfs
*vfsp
;
181 if ((error
= hfs_mountfs(rvp
, mp
, NULL
, 0, context
))) {
182 if (HFS_MOUNT_DEBUG
) {
183 printf("hfs_mountroot: hfs_mountfs returned %d, rvp (%p) name (%s) \n",
184 error
, rvp
, (rvp
->v_name
? rvp
->v_name
: "unknown device"));
190 hfsmp
= VFSTOHFS(mp
);
192 hfsmp
->hfs_uid
= UNKNOWNUID
;
193 hfsmp
->hfs_gid
= UNKNOWNGID
;
194 hfsmp
->hfs_dir_mask
= (S_IRWXU
| S_IRGRP
|S_IXGRP
| S_IROTH
|S_IXOTH
); /* 0755 */
195 hfsmp
->hfs_file_mask
= (S_IRWXU
| S_IRGRP
|S_IXGRP
| S_IROTH
|S_IXOTH
); /* 0755 */
197 /* Establish the free block reserve. */
198 vcb
= HFSTOVCB(hfsmp
);
199 vcb
->reserveBlocks
= ((u_int64_t
)vcb
->totalBlocks
* HFS_MINFREE
) / 100;
200 vcb
->reserveBlocks
= MIN(vcb
->reserveBlocks
, HFS_MAXRESERVE
/ vcb
->blockSize
);
202 vfsp
= vfs_statfs(mp
);
203 (void)hfs_statfs(mp
, vfsp
, NULL
);
216 hfs_mount(struct mount
*mp
, vnode_t devvp
, user_addr_t data
, vfs_context_t context
)
218 struct proc
*p
= vfs_context_proc(context
);
219 struct hfsmount
*hfsmp
= NULL
;
220 struct hfs_mount_args args
;
224 if ((retval
= copyin(data
, (caddr_t
)&args
, sizeof(args
)))) {
225 if (HFS_MOUNT_DEBUG
) {
226 printf("hfs_mount: copyin returned %d for fs\n", retval
);
230 cmdflags
= (u_int32_t
)vfs_flags(mp
) & MNT_CMDFLAGS
;
231 if (cmdflags
& MNT_UPDATE
) {
232 hfsmp
= VFSTOHFS(mp
);
234 /* Reload incore data after an fsck. */
235 if (cmdflags
& MNT_RELOAD
) {
236 if (vfs_isrdonly(mp
)) {
237 int error
= hfs_reload(mp
);
238 if (error
&& HFS_MOUNT_DEBUG
) {
239 printf("hfs_mount: hfs_reload returned %d on %s \n", error
, hfsmp
->vcbVN
);
244 if (HFS_MOUNT_DEBUG
) {
245 printf("hfs_mount: MNT_RELOAD not supported on rdwr filesystem %s\n", hfsmp
->vcbVN
);
251 /* Change to a read-only file system. */
252 if (((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) &&
256 /* Set flag to indicate that a downgrade to read-only
257 * is in progress and therefore block any further
258 * modifications to the file system.
260 hfs_lock_global (hfsmp
, HFS_EXCLUSIVE_LOCK
);
261 hfsmp
->hfs_flags
|= HFS_RDONLY_DOWNGRADE
;
262 hfsmp
->hfs_downgrading_proc
= current_thread();
263 hfs_unlock_global (hfsmp
);
265 /* use VFS_SYNC to push out System (btree) files */
266 retval
= VFS_SYNC(mp
, MNT_WAIT
, context
);
267 if (retval
&& ((cmdflags
& MNT_FORCE
) == 0)) {
268 hfsmp
->hfs_flags
&= ~HFS_RDONLY_DOWNGRADE
;
269 hfsmp
->hfs_downgrading_proc
= NULL
;
270 if (HFS_MOUNT_DEBUG
) {
271 printf("hfs_mount: VFS_SYNC returned %d during b-tree sync of %s \n", retval
, hfsmp
->vcbVN
);
277 if (cmdflags
& MNT_FORCE
)
280 if ((retval
= hfs_flushfiles(mp
, flags
, p
))) {
281 hfsmp
->hfs_flags
&= ~HFS_RDONLY_DOWNGRADE
;
282 hfsmp
->hfs_downgrading_proc
= NULL
;
283 if (HFS_MOUNT_DEBUG
) {
284 printf("hfs_mount: hfs_flushfiles returned %d on %s \n", retval
, hfsmp
->vcbVN
);
289 /* mark the volume cleanly unmounted */
290 hfsmp
->vcbAtrb
|= kHFSVolumeUnmountedMask
;
291 retval
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
292 hfsmp
->hfs_flags
|= HFS_READ_ONLY
;
294 /* also get the volume bitmap blocks */
296 if (vnode_mount(hfsmp
->hfs_devvp
) == mp
) {
297 retval
= hfs_fsync(hfsmp
->hfs_devvp
, MNT_WAIT
, 0, p
);
299 vnode_get(hfsmp
->hfs_devvp
);
300 retval
= VNOP_FSYNC(hfsmp
->hfs_devvp
, MNT_WAIT
, context
);
301 vnode_put(hfsmp
->hfs_devvp
);
305 if (HFS_MOUNT_DEBUG
) {
306 printf("hfs_mount: FSYNC on devvp returned %d for fs %s\n", retval
, hfsmp
->vcbVN
);
308 hfsmp
->hfs_flags
&= ~HFS_RDONLY_DOWNGRADE
;
309 hfsmp
->hfs_downgrading_proc
= NULL
;
310 hfsmp
->hfs_flags
&= ~HFS_READ_ONLY
;
314 hfs_lock_global (hfsmp
, HFS_EXCLUSIVE_LOCK
);
316 journal_close(hfsmp
->jnl
);
319 // Note: we explicitly don't want to shutdown
320 // access to the jvp because we may need
321 // it later if we go back to being read-write.
323 hfs_unlock_global (hfsmp
);
326 #if CONFIG_HFS_ALLOC_RBTREE
327 (void) hfs_teardown_allocator(hfsmp
);
329 hfsmp
->hfs_downgrading_proc
= NULL
;
332 /* Change to a writable file system. */
333 if (vfs_iswriteupgrade(mp
)) {
334 #if CONFIG_HFS_ALLOC_RBTREE
335 thread_t allocator_thread
;
339 * On inconsistent disks, do not allow read-write mount
340 * unless it is the boot volume being mounted.
342 if (!(vfs_flags(mp
) & MNT_ROOTFS
) &&
343 (hfsmp
->vcbAtrb
& kHFSVolumeInconsistentMask
)) {
344 if (HFS_MOUNT_DEBUG
) {
345 printf("hfs_mount: attempting to mount inconsistent non-root volume %s\n", (hfsmp
->vcbVN
));
351 // If the journal was shut-down previously because we were
352 // asked to be read-only, let's start it back up again now
354 if ( (HFSTOVCB(hfsmp
)->vcbAtrb
& kHFSVolumeJournaledMask
)
355 && hfsmp
->jnl
== NULL
356 && hfsmp
->jvp
!= NULL
) {
359 if (hfsmp
->hfs_flags
& HFS_NEED_JNL_RESET
) {
360 jflags
= JOURNAL_RESET
;
365 hfs_lock_global (hfsmp
, HFS_EXCLUSIVE_LOCK
);
367 hfsmp
->jnl
= journal_open(hfsmp
->jvp
,
368 (hfsmp
->jnl_start
* HFSTOVCB(hfsmp
)->blockSize
) + (off_t
)HFSTOVCB(hfsmp
)->hfsPlusIOPosOffset
,
371 hfsmp
->hfs_logical_block_size
,
374 hfs_sync_metadata
, hfsmp
->hfs_mp
);
377 * Set up the trim callback function so that we can add
378 * recently freed extents to the free extent cache once
379 * the transaction that freed them is written to the
383 journal_trim_set_callback(hfsmp
->jnl
, hfs_trim_callback
, hfsmp
);
385 hfs_unlock_global (hfsmp
);
387 if (hfsmp
->jnl
== NULL
) {
388 if (HFS_MOUNT_DEBUG
) {
389 printf("hfs_mount: journal_open == NULL; couldn't be opened on %s \n", (hfsmp
->vcbVN
));
394 hfsmp
->hfs_flags
&= ~HFS_NEED_JNL_RESET
;
399 /* See if we need to erase unused Catalog nodes due to <rdar://problem/6947811>. */
400 retval
= hfs_erase_unused_nodes(hfsmp
);
401 if (retval
!= E_NONE
) {
402 if (HFS_MOUNT_DEBUG
) {
403 printf("hfs_mount: hfs_erase_unused_nodes returned %d for fs %s\n", retval
, hfsmp
->vcbVN
);
408 /* If this mount point was downgraded from read-write
409 * to read-only, clear that information as we are now
410 * moving back to read-write.
412 hfsmp
->hfs_flags
&= ~HFS_RDONLY_DOWNGRADE
;
413 hfsmp
->hfs_downgrading_proc
= NULL
;
415 /* mark the volume dirty (clear clean unmount bit) */
416 hfsmp
->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
418 retval
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
419 if (retval
!= E_NONE
) {
420 if (HFS_MOUNT_DEBUG
) {
421 printf("hfs_mount: hfs_flushvolumeheader returned %d for fs %s\n", retval
, hfsmp
->vcbVN
);
426 /* Only clear HFS_READ_ONLY after a successful write */
427 hfsmp
->hfs_flags
&= ~HFS_READ_ONLY
;
430 if (!(hfsmp
->hfs_flags
& (HFS_READ_ONLY
| HFS_STANDARD
))) {
431 /* Setup private/hidden directories for hardlinks. */
432 hfs_privatedir_init(hfsmp
, FILE_HARDLINKS
);
433 hfs_privatedir_init(hfsmp
, DIR_HARDLINKS
);
435 hfs_remove_orphans(hfsmp
);
438 * Allow hot file clustering if conditions allow.
440 if ((hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) &&
441 ((hfsmp
->hfs_flags
& HFS_SSD
) == 0)) {
442 (void) hfs_recording_init(hfsmp
);
444 /* Force ACLs on HFS+ file systems. */
445 if (vfs_extendedsecurity(HFSTOVFS(hfsmp
)) == 0) {
446 vfs_setextendedsecurity(HFSTOVFS(hfsmp
));
450 #if CONFIG_HFS_ALLOC_RBTREE
452 * Like the normal mount case, we need to handle creation of the allocation red-black tree
453 * if we're upgrading from read-only to read-write.
455 * We spawn a thread to create the pair of red-black trees for this volume.
456 * However, in so doing, we must be careful to ensure that if this thread is still
457 * running after mount has finished, it doesn't interfere with an unmount. Specifically,
458 * we'll need to set a bit that indicates we're in progress building the trees here.
459 * Unmount will check for this bit, and then if it's set, mark a corresponding bit that
460 * notifies the tree generation code that an unmount is waiting. Also, mark the extent
461 * tree flags that the allocator is enabled for use before we spawn the thread that will start
462 * scanning the RB tree.
464 * Only do this if we're operating on a read-write mount (we wouldn't care for read-only),
465 * which has not previously encountered a bad error on the red-black tree code. Also, don't
466 * try to re-build a tree that already exists.
469 if (hfsmp
->extent_tree_flags
== 0) {
470 hfsmp
->extent_tree_flags
|= (HFS_ALLOC_TREEBUILD_INFLIGHT
| HFS_ALLOC_RB_ENABLED
);
471 /* Initialize EOF counter so that the thread can assume it started at initial values */
472 hfsmp
->offset_block_end
= 0;
476 kernel_thread_start ((thread_continue_t
) hfs_initialize_allocator
, hfsmp
, &allocator_thread
);
477 thread_deallocate(allocator_thread
);
483 /* Update file system parameters. */
484 retval
= hfs_changefs(mp
, &args
);
485 if (retval
&& HFS_MOUNT_DEBUG
) {
486 printf("hfs_mount: hfs_changefs returned %d for %s\n", retval
, hfsmp
->vcbVN
);
489 } else /* not an update request */ {
491 /* Set the mount flag to indicate that we support volfs */
492 vfs_setflags(mp
, (u_int64_t
)((unsigned int)MNT_DOVOLFS
));
494 retval
= hfs_mountfs(devvp
, mp
, &args
, 0, context
);
495 if (retval
&& HFS_MOUNT_DEBUG
) {
496 printf("hfs_mount: hfs_mountfs returned %d\n", retval
);
500 * If above mount call was successful, and this mount is content protection
501 * enabled, then verify the on-disk EA on the root to ensure that the filesystem
502 * is of a suitable vintage to allow the mount to proceed.
504 if ((retval
== 0) && (cp_fs_protected (mp
))) {
506 struct cp_root_xattr xattr
;
507 bzero (&xattr
, sizeof(struct cp_root_xattr
));
508 hfsmp
= vfs_fsprivate(mp
);
510 /* go get the EA to get the version information */
511 err
= cp_getrootxattr (hfsmp
, &xattr
);
512 /* If there was no EA there, then write one out. */
513 if (err
== ENOATTR
) {
514 bzero(&xattr
, sizeof(struct cp_root_xattr
));
515 xattr
.major_version
= CP_CURRENT_MAJOR_VERS
;
516 xattr
.minor_version
= CP_CURRENT_MINOR_VERS
;
519 err
= cp_setrootxattr (hfsmp
, &xattr
);
522 * For any other error, including having an out of date CP version in the
523 * EA, or for an error out of cp_setrootxattr, deny the mount
524 * and do not proceed further.
526 if (err
|| xattr
.major_version
!= CP_CURRENT_MAJOR_VERS
) {
527 /* Deny the mount and tear down. */
529 (void) hfs_unmount (mp
, MNT_FORCE
, context
);
536 (void)hfs_statfs(mp
, vfs_statfs(mp
), context
);
542 struct hfs_changefs_cargs
{
543 struct hfsmount
*hfsmp
;
550 hfs_changefs_callback(struct vnode
*vp
, void *cargs
)
554 struct cat_desc cndesc
;
555 struct cat_attr cnattr
;
556 struct hfs_changefs_cargs
*args
;
560 args
= (struct hfs_changefs_cargs
*)cargs
;
563 vcb
= HFSTOVCB(args
->hfsmp
);
565 lockflags
= hfs_systemfile_lock(args
->hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
566 error
= cat_lookup(args
->hfsmp
, &cp
->c_desc
, 0, &cndesc
, &cnattr
, NULL
, NULL
);
567 hfs_systemfile_unlock(args
->hfsmp
, lockflags
);
570 * If we couldn't find this guy skip to the next one
575 return (VNODE_RETURNED
);
578 * Get the real uid/gid and perm mask from disk.
580 if (args
->permswitch
|| args
->permfix
) {
581 cp
->c_uid
= cnattr
.ca_uid
;
582 cp
->c_gid
= cnattr
.ca_gid
;
583 cp
->c_mode
= cnattr
.ca_mode
;
586 * If we're switching name converters then...
587 * Remove the existing entry from the namei cache.
588 * Update name to one based on new encoder.
592 replace_desc(cp
, &cndesc
);
594 if (cndesc
.cd_cnid
== kHFSRootFolderID
) {
595 strlcpy((char *)vcb
->vcbVN
, (const char *)cp
->c_desc
.cd_nameptr
, NAME_MAX
+1);
596 cp
->c_desc
.cd_encoding
= args
->hfsmp
->hfs_encoding
;
599 cat_releasedesc(&cndesc
);
601 return (VNODE_RETURNED
);
604 /* Change fs mount parameters */
606 hfs_changefs(struct mount
*mp
, struct hfs_mount_args
*args
)
609 int namefix
, permfix
, permswitch
;
610 struct hfsmount
*hfsmp
;
612 hfs_to_unicode_func_t get_unicode_func
;
613 unicode_to_hfs_func_t get_hfsname_func
;
614 u_int32_t old_encoding
= 0;
615 struct hfs_changefs_cargs cargs
;
616 u_int32_t mount_flags
;
618 hfsmp
= VFSTOHFS(mp
);
619 vcb
= HFSTOVCB(hfsmp
);
620 mount_flags
= (unsigned int)vfs_flags(mp
);
622 hfsmp
->hfs_flags
|= HFS_IN_CHANGEFS
;
624 permswitch
= (((hfsmp
->hfs_flags
& HFS_UNKNOWN_PERMS
) &&
625 ((mount_flags
& MNT_UNKNOWNPERMISSIONS
) == 0)) ||
626 (((hfsmp
->hfs_flags
& HFS_UNKNOWN_PERMS
) == 0) &&
627 (mount_flags
& MNT_UNKNOWNPERMISSIONS
)));
629 /* The root filesystem must operate with actual permissions: */
630 if (permswitch
&& (mount_flags
& MNT_ROOTFS
) && (mount_flags
& MNT_UNKNOWNPERMISSIONS
)) {
631 vfs_clearflags(mp
, (u_int64_t
)((unsigned int)MNT_UNKNOWNPERMISSIONS
)); /* Just say "No". */
635 if (mount_flags
& MNT_UNKNOWNPERMISSIONS
)
636 hfsmp
->hfs_flags
|= HFS_UNKNOWN_PERMS
;
638 hfsmp
->hfs_flags
&= ~HFS_UNKNOWN_PERMS
;
640 namefix
= permfix
= 0;
643 * Tracking of hot files requires up-to-date access times. So if
644 * access time updates are disabled, we must also disable hot files.
646 if (mount_flags
& MNT_NOATIME
) {
647 (void) hfs_recording_suspend(hfsmp
);
650 /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */
651 if (args
->hfs_timezone
.tz_minuteswest
!= VNOVAL
) {
652 gTimeZone
= args
->hfs_timezone
;
655 /* Change the default uid, gid and/or mask */
656 if ((args
->hfs_uid
!= (uid_t
)VNOVAL
) && (hfsmp
->hfs_uid
!= args
->hfs_uid
)) {
657 hfsmp
->hfs_uid
= args
->hfs_uid
;
658 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
661 if ((args
->hfs_gid
!= (gid_t
)VNOVAL
) && (hfsmp
->hfs_gid
!= args
->hfs_gid
)) {
662 hfsmp
->hfs_gid
= args
->hfs_gid
;
663 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
666 if (args
->hfs_mask
!= (mode_t
)VNOVAL
) {
667 if (hfsmp
->hfs_dir_mask
!= (args
->hfs_mask
& ALLPERMS
)) {
668 hfsmp
->hfs_dir_mask
= args
->hfs_mask
& ALLPERMS
;
669 hfsmp
->hfs_file_mask
= args
->hfs_mask
& ALLPERMS
;
670 if ((args
->flags
!= VNOVAL
) && (args
->flags
& HFSFSMNT_NOXONFILES
))
671 hfsmp
->hfs_file_mask
= (args
->hfs_mask
& DEFFILEMODE
);
672 if (vcb
->vcbSigWord
== kHFSPlusSigWord
)
677 /* Change the hfs encoding value (hfs only) */
678 if ((vcb
->vcbSigWord
== kHFSSigWord
) &&
679 (args
->hfs_encoding
!= (u_int32_t
)VNOVAL
) &&
680 (hfsmp
->hfs_encoding
!= args
->hfs_encoding
)) {
682 retval
= hfs_getconverter(args
->hfs_encoding
, &get_unicode_func
, &get_hfsname_func
);
687 * Connect the new hfs_get_unicode converter but leave
688 * the old hfs_get_hfsname converter in place so that
689 * we can lookup existing vnodes to get their correctly
692 * When we're all finished, we can then connect the new
693 * hfs_get_hfsname converter and release our interest
694 * in the old converters.
696 hfsmp
->hfs_get_unicode
= get_unicode_func
;
697 old_encoding
= hfsmp
->hfs_encoding
;
698 hfsmp
->hfs_encoding
= args
->hfs_encoding
;
702 if (!(namefix
|| permfix
|| permswitch
))
705 /* XXX 3762912 hack to support HFS filesystem 'owner' */
708 hfsmp
->hfs_uid
== UNKNOWNUID
? KAUTH_UID_NONE
: hfsmp
->hfs_uid
,
709 hfsmp
->hfs_gid
== UNKNOWNGID
? KAUTH_GID_NONE
: hfsmp
->hfs_gid
);
712 * For each active vnode fix things that changed
714 * Note that we can visit a vnode more than once
715 * and we can race with fsync.
717 * hfs_changefs_callback will be called for each vnode
718 * hung off of this mount point
720 * The vnode will be properly referenced and unreferenced
721 * around the callback
724 cargs
.namefix
= namefix
;
725 cargs
.permfix
= permfix
;
726 cargs
.permswitch
= permswitch
;
728 vnode_iterate(mp
, 0, hfs_changefs_callback
, (void *)&cargs
);
731 * If we're switching name converters we can now
732 * connect the new hfs_get_hfsname converter and
733 * release our interest in the old converters.
736 hfsmp
->hfs_get_hfsname
= get_hfsname_func
;
737 vcb
->volumeNameEncodingHint
= args
->hfs_encoding
;
738 (void) hfs_relconverter(old_encoding
);
741 hfsmp
->hfs_flags
&= ~HFS_IN_CHANGEFS
;
746 struct hfs_reload_cargs
{
747 struct hfsmount
*hfsmp
;
752 hfs_reload_callback(struct vnode
*vp
, void *cargs
)
755 struct hfs_reload_cargs
*args
;
758 args
= (struct hfs_reload_cargs
*)cargs
;
760 * flush all the buffers associated with this node
762 (void) buf_invalidateblks(vp
, 0, 0, 0);
766 * Remove any directory hints
769 hfs_reldirhints(cp
, 0);
772 * Re-read cnode data for all active vnodes (non-metadata files).
774 if (!vnode_issystem(vp
) && !VNODE_IS_RSRC(vp
) && (cp
->c_fileid
>= kHFSFirstUserCatalogNodeID
)) {
775 struct cat_fork
*datafork
;
776 struct cat_desc desc
;
778 datafork
= cp
->c_datafork
? &cp
->c_datafork
->ff_data
: NULL
;
780 /* lookup by fileID since name could have changed */
781 lockflags
= hfs_systemfile_lock(args
->hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
782 args
->error
= cat_idlookup(args
->hfsmp
, cp
->c_fileid
, 0, &desc
, &cp
->c_attr
, datafork
);
783 hfs_systemfile_unlock(args
->hfsmp
, lockflags
);
785 return (VNODE_RETURNED_DONE
);
788 /* update cnode's catalog descriptor */
789 (void) replace_desc(cp
, &desc
);
791 return (VNODE_RETURNED
);
795 * Reload all incore data for a filesystem (used after running fsck on
796 * the root filesystem and finding things to fix). The filesystem must
797 * be mounted read-only.
799 * Things to do to update the mount:
800 * invalidate all cached meta-data.
801 * invalidate all inactive vnodes.
802 * invalidate all cached file data.
803 * re-read volume header from disk.
804 * re-load meta-file info (extents, file size).
805 * re-load B-tree header data.
806 * re-read cnode data for all active vnodes.
809 hfs_reload(struct mount
*mountp
)
811 register struct vnode
*devvp
;
814 struct hfsmount
*hfsmp
;
815 struct HFSPlusVolumeHeader
*vhp
;
817 struct filefork
*forkp
;
818 struct cat_desc cndesc
;
819 struct hfs_reload_cargs args
;
820 daddr64_t priIDSector
;
822 hfsmp
= VFSTOHFS(mountp
);
823 vcb
= HFSTOVCB(hfsmp
);
825 if (vcb
->vcbSigWord
== kHFSSigWord
)
826 return (EINVAL
); /* rooting from HFS is not supported! */
829 * Invalidate all cached meta-data.
831 devvp
= hfsmp
->hfs_devvp
;
832 if (buf_invalidateblks(devvp
, 0, 0, 0))
833 panic("hfs_reload: dirty1");
838 * hfs_reload_callback will be called for each vnode
839 * hung off of this mount point that can't be recycled...
840 * vnode_iterate will recycle those that it can (the VNODE_RELOAD option)
841 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
842 * properly referenced and unreferenced around the callback
844 vnode_iterate(mountp
, VNODE_RELOAD
| VNODE_WAIT
, hfs_reload_callback
, (void *)&args
);
850 * Re-read VolumeHeader from disk.
852 priIDSector
= (daddr64_t
)((vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) +
853 HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
));
855 error
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
856 HFS_PHYSBLK_ROUNDDOWN(priIDSector
, hfsmp
->hfs_log_per_phys
),
857 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
);
864 vhp
= (HFSPlusVolumeHeader
*) (buf_dataptr(bp
) + HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
866 /* Do a quick sanity check */
867 if ((SWAP_BE16(vhp
->signature
) != kHFSPlusSigWord
&&
868 SWAP_BE16(vhp
->signature
) != kHFSXSigWord
) ||
869 (SWAP_BE16(vhp
->version
) != kHFSPlusVersion
&&
870 SWAP_BE16(vhp
->version
) != kHFSXVersion
) ||
871 SWAP_BE32(vhp
->blockSize
) != vcb
->blockSize
) {
876 vcb
->vcbLsMod
= to_bsd_time(SWAP_BE32(vhp
->modifyDate
));
877 vcb
->vcbAtrb
= SWAP_BE32 (vhp
->attributes
);
878 vcb
->vcbJinfoBlock
= SWAP_BE32(vhp
->journalInfoBlock
);
879 vcb
->vcbClpSiz
= SWAP_BE32 (vhp
->rsrcClumpSize
);
880 vcb
->vcbNxtCNID
= SWAP_BE32 (vhp
->nextCatalogID
);
881 vcb
->vcbVolBkUp
= to_bsd_time(SWAP_BE32(vhp
->backupDate
));
882 vcb
->vcbWrCnt
= SWAP_BE32 (vhp
->writeCount
);
883 vcb
->vcbFilCnt
= SWAP_BE32 (vhp
->fileCount
);
884 vcb
->vcbDirCnt
= SWAP_BE32 (vhp
->folderCount
);
885 HFS_UPDATE_NEXT_ALLOCATION(vcb
, SWAP_BE32 (vhp
->nextAllocation
));
886 vcb
->totalBlocks
= SWAP_BE32 (vhp
->totalBlocks
);
887 vcb
->freeBlocks
= SWAP_BE32 (vhp
->freeBlocks
);
888 vcb
->encodingsBitmap
= SWAP_BE64 (vhp
->encodingsBitmap
);
889 bcopy(vhp
->finderInfo
, vcb
->vcbFndrInfo
, sizeof(vhp
->finderInfo
));
890 vcb
->localCreateDate
= SWAP_BE32 (vhp
->createDate
); /* hfs+ create date is in local time */
893 * Re-load meta-file vnode data (extent info, file size, etc).
895 forkp
= VTOF((struct vnode
*)vcb
->extentsRefNum
);
896 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
897 forkp
->ff_extents
[i
].startBlock
=
898 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].startBlock
);
899 forkp
->ff_extents
[i
].blockCount
=
900 SWAP_BE32 (vhp
->extentsFile
.extents
[i
].blockCount
);
902 forkp
->ff_size
= SWAP_BE64 (vhp
->extentsFile
.logicalSize
);
903 forkp
->ff_blocks
= SWAP_BE32 (vhp
->extentsFile
.totalBlocks
);
904 forkp
->ff_clumpsize
= SWAP_BE32 (vhp
->extentsFile
.clumpSize
);
907 forkp
= VTOF((struct vnode
*)vcb
->catalogRefNum
);
908 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
909 forkp
->ff_extents
[i
].startBlock
=
910 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].startBlock
);
911 forkp
->ff_extents
[i
].blockCount
=
912 SWAP_BE32 (vhp
->catalogFile
.extents
[i
].blockCount
);
914 forkp
->ff_size
= SWAP_BE64 (vhp
->catalogFile
.logicalSize
);
915 forkp
->ff_blocks
= SWAP_BE32 (vhp
->catalogFile
.totalBlocks
);
916 forkp
->ff_clumpsize
= SWAP_BE32 (vhp
->catalogFile
.clumpSize
);
918 if (hfsmp
->hfs_attribute_vp
) {
919 forkp
= VTOF(hfsmp
->hfs_attribute_vp
);
920 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
921 forkp
->ff_extents
[i
].startBlock
=
922 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].startBlock
);
923 forkp
->ff_extents
[i
].blockCount
=
924 SWAP_BE32 (vhp
->attributesFile
.extents
[i
].blockCount
);
926 forkp
->ff_size
= SWAP_BE64 (vhp
->attributesFile
.logicalSize
);
927 forkp
->ff_blocks
= SWAP_BE32 (vhp
->attributesFile
.totalBlocks
);
928 forkp
->ff_clumpsize
= SWAP_BE32 (vhp
->attributesFile
.clumpSize
);
931 forkp
= VTOF((struct vnode
*)vcb
->allocationsRefNum
);
932 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
933 forkp
->ff_extents
[i
].startBlock
=
934 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].startBlock
);
935 forkp
->ff_extents
[i
].blockCount
=
936 SWAP_BE32 (vhp
->allocationFile
.extents
[i
].blockCount
);
938 forkp
->ff_size
= SWAP_BE64 (vhp
->allocationFile
.logicalSize
);
939 forkp
->ff_blocks
= SWAP_BE32 (vhp
->allocationFile
.totalBlocks
);
940 forkp
->ff_clumpsize
= SWAP_BE32 (vhp
->allocationFile
.clumpSize
);
946 * Re-load B-tree header data
948 forkp
= VTOF((struct vnode
*)vcb
->extentsRefNum
);
949 if ( (error
= MacToVFSError( BTReloadData((FCB
*)forkp
) )) )
952 forkp
= VTOF((struct vnode
*)vcb
->catalogRefNum
);
953 if ( (error
= MacToVFSError( BTReloadData((FCB
*)forkp
) )) )
956 if (hfsmp
->hfs_attribute_vp
) {
957 forkp
= VTOF(hfsmp
->hfs_attribute_vp
);
958 if ( (error
= MacToVFSError( BTReloadData((FCB
*)forkp
) )) )
962 /* Reload the volume name */
963 if ((error
= cat_idlookup(hfsmp
, kHFSRootFolderID
, 0, &cndesc
, NULL
, NULL
)))
965 vcb
->volumeNameEncodingHint
= cndesc
.cd_encoding
;
966 bcopy(cndesc
.cd_nameptr
, vcb
->vcbVN
, min(255, cndesc
.cd_namelen
));
967 cat_releasedesc(&cndesc
);
969 /* Re-establish private/hidden directories. */
970 hfs_privatedir_init(hfsmp
, FILE_HARDLINKS
);
971 hfs_privatedir_init(hfsmp
, DIR_HARDLINKS
);
973 /* In case any volume information changed to trigger a notification */
974 hfs_generate_volume_notifications(hfsmp
);
982 hfs_syncer(void *arg0
, void *unused
)
984 #pragma unused(unused)
986 struct hfsmount
*hfsmp
= arg0
;
989 uint32_t delay
= HFS_META_DELAY
;
993 clock_get_calendar_microtime(&secs
, &usecs
);
994 now
= ((uint64_t)secs
* 1000000ULL) + (uint64_t)usecs
;
997 // If the amount of pending writes is more than our limit, wait
998 // for 2/3 of it to drain and then flush the journal.
1000 if (hfsmp
->hfs_mp
->mnt_pending_write_size
> hfsmp
->hfs_max_pending_io
) {
1002 uint64_t pending_io
, start
, rate
;
1006 hfs_start_transaction(hfsmp
); // so we hold off any new i/o's
1008 pending_io
= hfsmp
->hfs_mp
->mnt_pending_write_size
;
1010 clock_get_calendar_microtime(&secs
, &usecs
);
1011 start
= ((uint64_t)secs
* 1000000ULL) + (uint64_t)usecs
;
1013 while(hfsmp
->hfs_mp
->mnt_pending_write_size
> (pending_io
/3) && counter
++ < 500) {
1014 tsleep((caddr_t
)hfsmp
, PRIBIO
, "hfs-wait-for-io-to-drain", 10);
1017 if (counter
>= 500) {
1018 printf("hfs: timed out waiting for io to drain (%lld)\n", (int64_t)hfsmp
->hfs_mp
->mnt_pending_write_size
);
1022 journal_flush(hfsmp
->jnl
, FALSE
);
1024 hfs_sync(hfsmp
->hfs_mp
, MNT_WAIT
, vfs_context_kernel());
1027 clock_get_calendar_microtime(&secs
, &usecs
);
1028 now
= ((uint64_t)secs
* 1000000ULL) + (uint64_t)usecs
;
1029 hfsmp
->hfs_last_sync_time
= now
;
1030 rate
= ((pending_io
* 1000000ULL) / (now
- start
)); // yields bytes per second
1032 hfs_end_transaction(hfsmp
);
1035 // If a reasonable amount of time elapsed then check the
1036 // i/o rate. If it's taking less than 1 second or more
1037 // than 2 seconds, adjust hfs_max_pending_io so that we
1038 // will allow about 1.5 seconds of i/o to queue up.
1040 if ((now
- start
) >= 300000) {
1041 uint64_t scale
= (pending_io
* 100) / rate
;
1043 if (scale
< 100 || scale
> 200) {
1044 // set it so that it should take about 1.5 seconds to drain
1045 hfsmp
->hfs_max_pending_io
= (rate
* 150ULL) / 100ULL;
1049 } else if ( ((now
- hfsmp
->hfs_last_sync_time
) >= 5000000ULL)
1050 || (((now
- hfsmp
->hfs_last_sync_time
) >= 100000LL)
1051 && ((now
- hfsmp
->hfs_last_sync_request_time
) >= 100000LL)
1052 && (hfsmp
->hfs_active_threads
== 0)
1053 && (hfsmp
->hfs_global_lock_nesting
== 0))) {
1056 // Flush the journal if more than 5 seconds elapsed since
1057 // the last sync OR we have not sync'ed recently and the
1058 // last sync request time was more than 100 milliseconds
1059 // ago and no one is in the middle of a transaction right
1060 // now. Else we defer the sync and reschedule it.
1063 hfs_lock_global (hfsmp
, HFS_SHARED_LOCK
);
1065 journal_flush(hfsmp
->jnl
, FALSE
);
1067 hfs_unlock_global (hfsmp
);
1069 hfs_sync(hfsmp
->hfs_mp
, MNT_WAIT
, vfs_context_kernel());
1072 clock_get_calendar_microtime(&secs
, &usecs
);
1073 now
= ((uint64_t)secs
* 1000000ULL) + (uint64_t)usecs
;
1074 hfsmp
->hfs_last_sync_time
= now
;
1076 } else if (hfsmp
->hfs_active_threads
== 0) {
1079 clock_interval_to_deadline(delay
, HFS_MILLISEC_SCALE
, &deadline
);
1080 thread_call_enter_delayed(hfsmp
->hfs_syncer
, deadline
);
1082 // note: we intentionally return early here and do not
1083 // decrement the sync_scheduled and sync_incomplete
1084 // variables because we rescheduled the timer.
1090 // NOTE: we decrement these *after* we're done the journal_flush() since
1091 // it can take a significant amount of time and so we don't want more
1092 // callbacks scheduled until we're done this one.
1094 OSDecrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_scheduled
);
1095 OSDecrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_incomplete
);
1096 wakeup((caddr_t
)&hfsmp
->hfs_sync_incomplete
);
1100 extern int IOBSDIsMediaEjectable( const char *cdev_name
);
1103 * Initialization code for Red-Black Tree Allocator
1105 * This function will build the two red-black trees necessary for allocating space
1106 * from the metadata zone as well as normal allocations. Currently, we use
1107 * an advisory read to get most of the data into the buffer cache.
1108 * This function is intended to be run in a separate thread so as not to slow down mount.
1113 hfs_initialize_allocator (struct hfsmount
*hfsmp
) {
1115 #if CONFIG_HFS_ALLOC_RBTREE
1119 * Take the allocation file lock. Journal transactions will block until
1122 int flags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1125 * GenerateTree assumes that the bitmap lock is held when you call the function.
1126 * It will drop and re-acquire the lock periodically as needed to let other allocations
1127 * through. It returns with the bitmap lock held. Since we only maintain one tree,
1128 * we don't need to specify a start block (always starts at 0).
1130 err
= GenerateTree(hfsmp
, hfsmp
->totalBlocks
, &flags
, 1);
1134 /* Mark offset tree as built */
1135 hfsmp
->extent_tree_flags
|= HFS_ALLOC_RB_ACTIVE
;
1139 * GenerateTree may drop the bitmap lock during operation in order to give other
1140 * threads a chance to allocate blocks, but it will always return with the lock held, so
1141 * we don't need to re-grab the lock in order to update the TREEBUILD_INFLIGHT bit.
1143 hfsmp
->extent_tree_flags
&= ~HFS_ALLOC_TREEBUILD_INFLIGHT
;
1145 /* Wakeup any waiters on the allocation bitmap lock */
1146 wakeup((caddr_t
)&hfsmp
->extent_tree_flags
);
1149 hfs_systemfile_unlock(hfsmp
, flags
);
1151 #pragma unused (hfsmp)
1157 * Teardown code for the Red-Black Tree allocator.
1158 * This function consolidates the code which serializes with respect
1159 * to a thread that may be potentially still building the tree when we need to begin
1160 * tearing it down. Since the red-black tree may not be live when we enter this function
1162 * 1 -> Tree was live.
1163 * 0 -> Tree was not active at time of call.
1167 hfs_teardown_allocator (struct hfsmount
*hfsmp
) {
1170 #if CONFIG_HFS_ALLOC_RBTREE
1175 * Check to see if the tree-generation is still on-going.
1176 * If it is, then block until it's done.
1179 flags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
1182 while (hfsmp
->extent_tree_flags
& HFS_ALLOC_TREEBUILD_INFLIGHT
) {
1183 hfsmp
->extent_tree_flags
|= HFS_ALLOC_TEARDOWN_INFLIGHT
;
1185 lck_rw_sleep(&(VTOC(hfsmp
->hfs_allocation_vp
))->c_rwlock
, LCK_SLEEP_EXCLUSIVE
,
1186 &hfsmp
->extent_tree_flags
, THREAD_UNINT
);
1189 if (hfs_isrbtree_active (hfsmp
)) {
1192 /* Tear down the RB Trees while we have the bitmap locked */
1193 DestroyTrees(hfsmp
);
1197 hfs_systemfile_unlock(hfsmp
, flags
);
1199 #pragma unused (hfsmp)
1206 static int hfs_root_unmounted_cleanly
= 0;
1208 SYSCTL_DECL(_vfs_generic
);
1209 SYSCTL_INT(_vfs_generic
, OID_AUTO
, root_unmounted_cleanly
, CTLFLAG_RD
, &hfs_root_unmounted_cleanly
, 0, "Root filesystem was unmounted cleanly");
1212 * Common code for mount and mountroot
1215 hfs_mountfs(struct vnode
*devvp
, struct mount
*mp
, struct hfs_mount_args
*args
,
1216 int journal_replay_only
, vfs_context_t context
)
1218 struct proc
*p
= vfs_context_proc(context
);
1219 int retval
= E_NONE
;
1220 struct hfsmount
*hfsmp
= NULL
;
1223 HFSMasterDirectoryBlock
*mdbp
= NULL
;
1231 daddr64_t log_blkcnt
;
1232 u_int32_t log_blksize
;
1233 u_int32_t phys_blksize
;
1234 u_int32_t minblksize
;
1235 u_int32_t iswritable
;
1236 daddr64_t mdb_offset
;
1240 #if CONFIG_HFS_ALLOC_RBTREE
1241 thread_t allocator_thread
;
1245 /* only hfs_mountroot passes us NULL as the 'args' argument */
1249 ronly
= vfs_isrdonly(mp
);
1250 dev
= vnode_specrdev(devvp
);
1251 cred
= p
? vfs_context_ucred(context
) : NOCRED
;
1257 minblksize
= kHFSBlockSize
;
1259 /* Advisory locking should be handled at the VFS layer */
1260 vfs_setlocklocal(mp
);
1262 /* Get the logical block size (treated as physical block size everywhere) */
1263 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKSIZE
, (caddr_t
)&log_blksize
, 0, context
)) {
1264 if (HFS_MOUNT_DEBUG
) {
1265 printf("hfs_mountfs: DKIOCGETBLOCKSIZE failed\n");
1270 if (log_blksize
== 0 || log_blksize
> 1024*1024*1024) {
1271 printf("hfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize
);
1276 /* Get the physical block size. */
1277 retval
= VNOP_IOCTL(devvp
, DKIOCGETPHYSICALBLOCKSIZE
, (caddr_t
)&phys_blksize
, 0, context
);
1279 if ((retval
!= ENOTSUP
) && (retval
!= ENOTTY
)) {
1280 if (HFS_MOUNT_DEBUG
) {
1281 printf("hfs_mountfs: DKIOCGETPHYSICALBLOCKSIZE failed\n");
1286 /* If device does not support this ioctl, assume that physical
1287 * block size is same as logical block size
1289 phys_blksize
= log_blksize
;
1291 if (phys_blksize
== 0 || phys_blksize
> 1024*1024*1024) {
1292 printf("hfs: physical block size 0x%x looks bad. Not mounting.\n", phys_blksize
);
1297 /* Switch to 512 byte sectors (temporarily) */
1298 if (log_blksize
> 512) {
1299 u_int32_t size512
= 512;
1301 if (VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
, (caddr_t
)&size512
, FWRITE
, context
)) {
1302 if (HFS_MOUNT_DEBUG
) {
1303 printf("hfs_mountfs: DKIOCSETBLOCKSIZE failed \n");
1309 /* Get the number of 512 byte physical blocks. */
1310 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
, (caddr_t
)&log_blkcnt
, 0, context
)) {
1311 /* resetting block size may fail if getting block count did */
1312 (void)VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
, (caddr_t
)&log_blksize
, FWRITE
, context
);
1313 if (HFS_MOUNT_DEBUG
) {
1314 printf("hfs_mountfs: DKIOCGETBLOCKCOUNT failed\n");
1319 /* Compute an accurate disk size (i.e. within 512 bytes) */
1320 disksize
= (u_int64_t
)log_blkcnt
* (u_int64_t
)512;
1323 * On Tiger it is not necessary to switch the device
1324 * block size to be 4k if there are more than 31-bits
1325 * worth of blocks but to insure compatibility with
1326 * pre-Tiger systems we have to do it.
1328 * If the device size is not a multiple of 4K (8 * 512), then
1329 * switching the logical block size isn't going to help because
1330 * we will be unable to write the alternate volume header.
1331 * In this case, just leave the logical block size unchanged.
1333 if (log_blkcnt
> 0x000000007fffffff && (log_blkcnt
& 7) == 0) {
1334 minblksize
= log_blksize
= 4096;
1335 if (phys_blksize
< log_blksize
)
1336 phys_blksize
= log_blksize
;
1340 * The cluster layer is not currently prepared to deal with a logical
1341 * block size larger than the system's page size. (It can handle
1342 * blocks per page, but not multiple pages per block.) So limit the
1343 * logical block size to the page size.
1345 if (log_blksize
> PAGE_SIZE
)
1346 log_blksize
= PAGE_SIZE
;
1348 /* Now switch to our preferred physical block size. */
1349 if (log_blksize
> 512) {
1350 if (VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
, (caddr_t
)&log_blksize
, FWRITE
, context
)) {
1351 if (HFS_MOUNT_DEBUG
) {
1352 printf("hfs_mountfs: DKIOCSETBLOCKSIZE (2) failed\n");
1357 /* Get the count of physical blocks. */
1358 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
, (caddr_t
)&log_blkcnt
, 0, context
)) {
1359 if (HFS_MOUNT_DEBUG
) {
1360 printf("hfs_mountfs: DKIOCGETBLOCKCOUNT (2) failed\n");
1368 * minblksize is the minimum physical block size
1369 * log_blksize has our preferred physical block size
1370 * log_blkcnt has the total number of physical blocks
1373 mdb_offset
= (daddr64_t
)HFS_PRI_SECTOR(log_blksize
);
1374 if ((retval
= (int)buf_meta_bread(devvp
,
1375 HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, (phys_blksize
/log_blksize
)),
1376 phys_blksize
, cred
, &bp
))) {
1377 if (HFS_MOUNT_DEBUG
) {
1378 printf("hfs_mountfs: buf_meta_bread failed with %d\n", retval
);
1382 MALLOC(mdbp
, HFSMasterDirectoryBlock
*, kMDBSize
, M_TEMP
, M_WAITOK
);
1385 if (HFS_MOUNT_DEBUG
) {
1386 printf("hfs_mountfs: MALLOC failed\n");
1390 bcopy((char *)buf_dataptr(bp
) + HFS_PRI_OFFSET(phys_blksize
), mdbp
, kMDBSize
);
1394 MALLOC(hfsmp
, struct hfsmount
*, sizeof(struct hfsmount
), M_HFSMNT
, M_WAITOK
);
1395 if (hfsmp
== NULL
) {
1396 if (HFS_MOUNT_DEBUG
) {
1397 printf("hfs_mountfs: MALLOC (2) failed\n");
1402 bzero(hfsmp
, sizeof(struct hfsmount
));
1404 hfs_chashinit_finish(hfsmp
);
1407 * See if the disk is a solid state device. We need this to decide what to do about
1410 if (VNOP_IOCTL(devvp
, DKIOCISSOLIDSTATE
, (caddr_t
)&isssd
, 0, context
) == 0) {
1412 hfsmp
->hfs_flags
|= HFS_SSD
;
1418 * Init the volume information structure
1421 lck_mtx_init(&hfsmp
->hfs_mutex
, hfs_mutex_group
, hfs_lock_attr
);
1422 lck_mtx_init(&hfsmp
->hfc_mutex
, hfs_mutex_group
, hfs_lock_attr
);
1423 lck_rw_init(&hfsmp
->hfs_global_lock
, hfs_rwlock_group
, hfs_lock_attr
);
1424 lck_rw_init(&hfsmp
->hfs_insync
, hfs_rwlock_group
, hfs_lock_attr
);
1425 lck_spin_init(&hfsmp
->vcbFreeExtLock
, hfs_spinlock_group
, hfs_lock_attr
);
1427 vfs_setfsprivate(mp
, hfsmp
);
1428 hfsmp
->hfs_mp
= mp
; /* Make VFSTOHFS work */
1429 hfsmp
->hfs_raw_dev
= vnode_specrdev(devvp
);
1430 hfsmp
->hfs_devvp
= devvp
;
1431 vnode_ref(devvp
); /* Hold a ref on the device, dropped when hfsmp is freed. */
1432 hfsmp
->hfs_logical_block_size
= log_blksize
;
1433 hfsmp
->hfs_logical_block_count
= log_blkcnt
;
1434 hfsmp
->hfs_physical_block_size
= phys_blksize
;
1435 hfsmp
->hfs_log_per_phys
= (phys_blksize
/ log_blksize
);
1436 hfsmp
->hfs_flags
|= HFS_WRITEABLE_MEDIA
;
1438 hfsmp
->hfs_flags
|= HFS_READ_ONLY
;
1439 if (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
)
1440 hfsmp
->hfs_flags
|= HFS_UNKNOWN_PERMS
;
1443 for (i
= 0; i
< MAXQUOTAS
; i
++)
1444 dqfileinit(&hfsmp
->hfs_qfiles
[i
]);
1448 hfsmp
->hfs_uid
= (args
->hfs_uid
== (uid_t
)VNOVAL
) ? UNKNOWNUID
: args
->hfs_uid
;
1449 if (hfsmp
->hfs_uid
== 0xfffffffd) hfsmp
->hfs_uid
= UNKNOWNUID
;
1450 hfsmp
->hfs_gid
= (args
->hfs_gid
== (gid_t
)VNOVAL
) ? UNKNOWNGID
: args
->hfs_gid
;
1451 if (hfsmp
->hfs_gid
== 0xfffffffd) hfsmp
->hfs_gid
= UNKNOWNGID
;
1452 vfs_setowner(mp
, hfsmp
->hfs_uid
, hfsmp
->hfs_gid
); /* tell the VFS */
1453 if (args
->hfs_mask
!= (mode_t
)VNOVAL
) {
1454 hfsmp
->hfs_dir_mask
= args
->hfs_mask
& ALLPERMS
;
1455 if (args
->flags
& HFSFSMNT_NOXONFILES
) {
1456 hfsmp
->hfs_file_mask
= (args
->hfs_mask
& DEFFILEMODE
);
1458 hfsmp
->hfs_file_mask
= args
->hfs_mask
& ALLPERMS
;
1461 hfsmp
->hfs_dir_mask
= UNKNOWNPERMISSIONS
& ALLPERMS
; /* 0777: rwx---rwx */
1462 hfsmp
->hfs_file_mask
= UNKNOWNPERMISSIONS
& DEFFILEMODE
; /* 0666: no --x by default? */
1464 if ((args
->flags
!= (int)VNOVAL
) && (args
->flags
& HFSFSMNT_WRAPPER
))
1467 /* Even w/o explicit mount arguments, MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */
1468 if (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
) {
1469 hfsmp
->hfs_uid
= UNKNOWNUID
;
1470 hfsmp
->hfs_gid
= UNKNOWNGID
;
1471 vfs_setowner(mp
, hfsmp
->hfs_uid
, hfsmp
->hfs_gid
); /* tell the VFS */
1472 hfsmp
->hfs_dir_mask
= UNKNOWNPERMISSIONS
& ALLPERMS
; /* 0777: rwx---rwx */
1473 hfsmp
->hfs_file_mask
= UNKNOWNPERMISSIONS
& DEFFILEMODE
; /* 0666: no --x by default? */
1477 /* Find out if disk media is writable. */
1478 if (VNOP_IOCTL(devvp
, DKIOCISWRITABLE
, (caddr_t
)&iswritable
, 0, context
) == 0) {
1480 hfsmp
->hfs_flags
|= HFS_WRITEABLE_MEDIA
;
1482 hfsmp
->hfs_flags
&= ~HFS_WRITEABLE_MEDIA
;
1485 // record the current time at which we're mounting this volume
1488 hfsmp
->hfs_mount_time
= tv
.tv_sec
;
1490 /* Mount a standard HFS disk */
1491 if ((SWAP_BE16(mdbp
->drSigWord
) == kHFSSigWord
) &&
1492 (mntwrapper
|| (SWAP_BE16(mdbp
->drEmbedSigWord
) != kHFSPlusSigWord
))) {
1494 /* On 10.6 and beyond, non read-only mounts for HFS standard vols get rejected */
1495 if (vfs_isrdwr(mp
)) {
1500 printf("hfs_mountfs: Mounting HFS Standard volumes was deprecated in Mac OS 10.7 \n");
1502 /* Treat it as if it's read-only and not writeable */
1503 hfsmp
->hfs_flags
|= HFS_READ_ONLY
;
1504 hfsmp
->hfs_flags
&= ~HFS_WRITEABLE_MEDIA
;
1506 /* If only journal replay is requested, exit immediately */
1507 if (journal_replay_only
) {
1512 if ((vfs_flags(mp
) & MNT_ROOTFS
)) {
1513 retval
= EINVAL
; /* Cannot root from HFS standard disks */
1516 /* HFS disks can only use 512 byte physical blocks */
1517 if (log_blksize
> kHFSBlockSize
) {
1518 log_blksize
= kHFSBlockSize
;
1519 if (VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
, (caddr_t
)&log_blksize
, FWRITE
, context
)) {
1523 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
, (caddr_t
)&log_blkcnt
, 0, context
)) {
1527 hfsmp
->hfs_logical_block_size
= log_blksize
;
1528 hfsmp
->hfs_logical_block_count
= log_blkcnt
;
1529 hfsmp
->hfs_physical_block_size
= log_blksize
;
1530 hfsmp
->hfs_log_per_phys
= 1;
1533 hfsmp
->hfs_encoding
= args
->hfs_encoding
;
1534 HFSTOVCB(hfsmp
)->volumeNameEncodingHint
= args
->hfs_encoding
;
1536 /* establish the timezone */
1537 gTimeZone
= args
->hfs_timezone
;
1540 retval
= hfs_getconverter(hfsmp
->hfs_encoding
, &hfsmp
->hfs_get_unicode
,
1541 &hfsmp
->hfs_get_hfsname
);
1545 retval
= hfs_MountHFSVolume(hfsmp
, mdbp
, p
);
1547 (void) hfs_relconverter(hfsmp
->hfs_encoding
);
1549 } else /* Mount an HFS Plus disk */ {
1550 HFSPlusVolumeHeader
*vhp
;
1551 off_t embeddedOffset
;
1552 int jnl_disable
= 0;
1554 /* Get the embedded Volume Header */
1555 if (SWAP_BE16(mdbp
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1556 embeddedOffset
= SWAP_BE16(mdbp
->drAlBlSt
) * kHFSBlockSize
;
1557 embeddedOffset
+= (u_int64_t
)SWAP_BE16(mdbp
->drEmbedExtent
.startBlock
) *
1558 (u_int64_t
)SWAP_BE32(mdbp
->drAlBlkSiz
);
1561 * If the embedded volume doesn't start on a block
1562 * boundary, then switch the device to a 512-byte
1563 * block size so everything will line up on a block
1566 if ((embeddedOffset
% log_blksize
) != 0) {
1567 printf("hfs_mountfs: embedded volume offset not"
1568 " a multiple of physical block size (%d);"
1569 " switching to 512\n", log_blksize
);
1571 if (VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
,
1572 (caddr_t
)&log_blksize
, FWRITE
, context
)) {
1574 if (HFS_MOUNT_DEBUG
) {
1575 printf("hfs_mountfs: DKIOCSETBLOCKSIZE (3) failed\n");
1580 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
,
1581 (caddr_t
)&log_blkcnt
, 0, context
)) {
1582 if (HFS_MOUNT_DEBUG
) {
1583 printf("hfs_mountfs: DKIOCGETBLOCKCOUNT (3) failed\n");
1588 /* Note: relative block count adjustment */
1589 hfsmp
->hfs_logical_block_count
*=
1590 hfsmp
->hfs_logical_block_size
/ log_blksize
;
1592 /* Update logical /physical block size */
1593 hfsmp
->hfs_logical_block_size
= log_blksize
;
1594 hfsmp
->hfs_physical_block_size
= log_blksize
;
1595 phys_blksize
= log_blksize
;
1596 hfsmp
->hfs_log_per_phys
= 1;
1599 disksize
= (u_int64_t
)SWAP_BE16(mdbp
->drEmbedExtent
.blockCount
) *
1600 (u_int64_t
)SWAP_BE32(mdbp
->drAlBlkSiz
);
1602 hfsmp
->hfs_logical_block_count
= disksize
/ log_blksize
;
1604 mdb_offset
= (daddr64_t
)((embeddedOffset
/ log_blksize
) + HFS_PRI_SECTOR(log_blksize
));
1605 retval
= (int)buf_meta_bread(devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, hfsmp
->hfs_log_per_phys
),
1606 phys_blksize
, cred
, &bp
);
1608 if (HFS_MOUNT_DEBUG
) {
1609 printf("hfs_mountfs: buf_meta_bread (2) failed with %d\n", retval
);
1613 bcopy((char *)buf_dataptr(bp
) + HFS_PRI_OFFSET(phys_blksize
), mdbp
, 512);
1616 vhp
= (HFSPlusVolumeHeader
*) mdbp
;
1618 } else /* pure HFS+ */ {
1620 vhp
= (HFSPlusVolumeHeader
*) mdbp
;
1624 hfs_root_unmounted_cleanly
= (SWAP_BE32(vhp
->attributes
) & kHFSVolumeUnmountedMask
) != 0;
1628 * On inconsistent disks, do not allow read-write mount
1629 * unless it is the boot volume being mounted. We also
1630 * always want to replay the journal if the journal_replay_only
1631 * flag is set because that will (most likely) get the
1632 * disk into a consistent state before fsck_hfs starts
1635 if ( !(vfs_flags(mp
) & MNT_ROOTFS
)
1636 && (SWAP_BE32(vhp
->attributes
) & kHFSVolumeInconsistentMask
)
1637 && !journal_replay_only
1638 && !(hfsmp
->hfs_flags
& HFS_READ_ONLY
)) {
1640 if (HFS_MOUNT_DEBUG
) {
1641 printf("hfs_mountfs: failed to mount non-root inconsistent disk\n");
1652 if (args
!= NULL
&& (args
->flags
& HFSFSMNT_EXTENDED_ARGS
) &&
1653 args
->journal_disable
) {
1658 // We only initialize the journal here if the last person
1659 // to mount this volume was journaling aware. Otherwise
1660 // we delay journal initialization until later at the end
1661 // of hfs_MountHFSPlusVolume() because the last person who
1662 // mounted it could have messed things up behind our back
1663 // (so we need to go find the .journal file, make sure it's
1664 // the right size, re-sync up if it was moved, etc).
1666 if ( (SWAP_BE32(vhp
->lastMountedVersion
) == kHFSJMountVersion
)
1667 && (SWAP_BE32(vhp
->attributes
) & kHFSVolumeJournaledMask
)
1670 // if we're able to init the journal, mark the mount
1671 // point as journaled.
1673 if ((retval
= hfs_early_journal_init(hfsmp
, vhp
, args
, embeddedOffset
, mdb_offset
, mdbp
, cred
)) == 0) {
1674 vfs_setflags(mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
1676 if (retval
== EROFS
) {
1677 // EROFS is a special error code that means the volume has an external
1678 // journal which we couldn't find. in that case we do not want to
1679 // rewrite the volume header - we'll just refuse to mount the volume.
1680 if (HFS_MOUNT_DEBUG
) {
1681 printf("hfs_mountfs: hfs_early_journal_init indicated external jnl \n");
1687 // if the journal failed to open, then set the lastMountedVersion
1688 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1689 // of just bailing out because the volume is journaled.
1691 if (HFS_MOUNT_DEBUG
) {
1692 printf("hfs_mountfs: hfs_early_journal_init failed, setting to FSK \n");
1695 HFSPlusVolumeHeader
*jvhp
;
1697 hfsmp
->hfs_flags
|= HFS_NEED_JNL_RESET
;
1699 if (mdb_offset
== 0) {
1700 mdb_offset
= (daddr64_t
)((embeddedOffset
/ log_blksize
) + HFS_PRI_SECTOR(log_blksize
));
1704 retval
= (int)buf_meta_bread(devvp
,
1705 HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, hfsmp
->hfs_log_per_phys
),
1706 phys_blksize
, cred
, &bp
);
1708 jvhp
= (HFSPlusVolumeHeader
*)(buf_dataptr(bp
) + HFS_PRI_OFFSET(phys_blksize
));
1710 if (SWAP_BE16(jvhp
->signature
) == kHFSPlusSigWord
|| SWAP_BE16(jvhp
->signature
) == kHFSXSigWord
) {
1711 printf ("hfs(1): Journal replay fail. Writing lastMountVersion as FSK!\n");
1712 jvhp
->lastMountedVersion
= SWAP_BE32(kFSKMountVersion
);
1720 // clear this so the error exit path won't try to use it
1725 // if this isn't the root device just bail out.
1726 // If it is the root device we just continue on
1727 // in the hopes that fsck_hfs will be able to
1728 // fix any damage that exists on the volume.
1729 if ( !(vfs_flags(mp
) & MNT_ROOTFS
)) {
1730 if (HFS_MOUNT_DEBUG
) {
1731 printf("hfs_mountfs: hfs_early_journal_init failed, erroring out \n");
1740 /* Either the journal is replayed successfully, or there
1741 * was nothing to replay, or no journal exists. In any case,
1744 if (journal_replay_only
) {
1749 (void) hfs_getconverter(0, &hfsmp
->hfs_get_unicode
, &hfsmp
->hfs_get_hfsname
);
1751 retval
= hfs_MountHFSPlusVolume(hfsmp
, vhp
, embeddedOffset
, disksize
, p
, args
, cred
);
1753 * If the backend didn't like our physical blocksize
1754 * then retry with physical blocksize of 512.
1756 if ((retval
== ENXIO
) && (log_blksize
> 512) && (log_blksize
!= minblksize
)) {
1757 printf("hfs_mountfs: could not use physical block size "
1758 "(%d) switching to 512\n", log_blksize
);
1760 if (VNOP_IOCTL(devvp
, DKIOCSETBLOCKSIZE
, (caddr_t
)&log_blksize
, FWRITE
, context
)) {
1761 if (HFS_MOUNT_DEBUG
) {
1762 printf("hfs_mountfs: DKIOCSETBLOCKSIZE (4) failed \n");
1767 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
, (caddr_t
)&log_blkcnt
, 0, context
)) {
1768 if (HFS_MOUNT_DEBUG
) {
1769 printf("hfs_mountfs: DKIOCGETBLOCKCOUNT (4) failed \n");
1774 devvp
->v_specsize
= log_blksize
;
1775 /* Note: relative block count adjustment (in case this is an embedded volume). */
1776 hfsmp
->hfs_logical_block_count
*= hfsmp
->hfs_logical_block_size
/ log_blksize
;
1777 hfsmp
->hfs_logical_block_size
= log_blksize
;
1778 hfsmp
->hfs_log_per_phys
= hfsmp
->hfs_physical_block_size
/ log_blksize
;
1780 if (hfsmp
->jnl
&& hfsmp
->jvp
== devvp
) {
1781 // close and re-open this with the new block size
1782 journal_close(hfsmp
->jnl
);
1784 if (hfs_early_journal_init(hfsmp
, vhp
, args
, embeddedOffset
, mdb_offset
, mdbp
, cred
) == 0) {
1785 vfs_setflags(mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
1787 // if the journal failed to open, then set the lastMountedVersion
1788 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1789 // of just bailing out because the volume is journaled.
1791 if (HFS_MOUNT_DEBUG
) {
1792 printf("hfs_mountfs: hfs_early_journal_init (2) resetting.. \n");
1794 HFSPlusVolumeHeader
*jvhp
;
1796 hfsmp
->hfs_flags
|= HFS_NEED_JNL_RESET
;
1798 if (mdb_offset
== 0) {
1799 mdb_offset
= (daddr64_t
)((embeddedOffset
/ log_blksize
) + HFS_PRI_SECTOR(log_blksize
));
1803 retval
= (int)buf_meta_bread(devvp
, HFS_PHYSBLK_ROUNDDOWN(mdb_offset
, hfsmp
->hfs_log_per_phys
),
1804 phys_blksize
, cred
, &bp
);
1806 jvhp
= (HFSPlusVolumeHeader
*)(buf_dataptr(bp
) + HFS_PRI_OFFSET(phys_blksize
));
1808 if (SWAP_BE16(jvhp
->signature
) == kHFSPlusSigWord
|| SWAP_BE16(jvhp
->signature
) == kHFSXSigWord
) {
1809 printf ("hfs(2): Journal replay fail. Writing lastMountVersion as FSK!\n");
1810 jvhp
->lastMountedVersion
= SWAP_BE32(kFSKMountVersion
);
1818 // clear this so the error exit path won't try to use it
1823 // if this isn't the root device just bail out.
1824 // If it is the root device we just continue on
1825 // in the hopes that fsck_hfs will be able to
1826 // fix any damage that exists on the volume.
1827 if ( !(vfs_flags(mp
) & MNT_ROOTFS
)) {
1828 if (HFS_MOUNT_DEBUG
) {
1829 printf("hfs_mountfs: hfs_early_journal_init (2) failed \n");
1837 /* Try again with a smaller block size... */
1838 retval
= hfs_MountHFSPlusVolume(hfsmp
, vhp
, embeddedOffset
, disksize
, p
, args
, cred
);
1839 if (retval
&& HFS_MOUNT_DEBUG
) {
1840 printf("hfs_MountHFSPlusVolume (late) returned %d\n",retval
);
1844 (void) hfs_relconverter(0);
1847 // save off a snapshot of the mtime from the previous mount
1849 hfsmp
->hfs_last_mounted_mtime
= hfsmp
->hfs_mtime
;
1852 if (HFS_MOUNT_DEBUG
) {
1853 printf("hfs_mountfs: encountered failure %d \n", retval
);
1858 mp
->mnt_vfsstat
.f_fsid
.val
[0] = (long)dev
;
1859 mp
->mnt_vfsstat
.f_fsid
.val
[1] = vfs_typenum(mp
);
1860 vfs_setmaxsymlen(mp
, 0);
1862 mp
->mnt_vtable
->vfc_vfsflags
|= VFC_VFSNATIVEXATTR
;
1864 mp
->mnt_kern_flag
|= MNTK_NAMED_STREAMS
;
1866 if (!(hfsmp
->hfs_flags
& HFS_STANDARD
)) {
1867 /* Tell VFS that we support directory hard links. */
1868 mp
->mnt_vtable
->vfc_vfsflags
|= VFC_VFSDIRLINKS
;
1870 /* HFS standard doesn't support extended readdir! */
1871 mount_set_noreaddirext (mp
);
1876 * Set the free space warning levels for a non-root volume:
1878 * Set the "danger" limit to 1% of the volume size or 100MB, whichever
1879 * is less. Set the "warning" limit to 2% of the volume size or 150MB,
1880 * whichever is less. And last, set the "desired" freespace level to
1881 * to 3% of the volume size or 200MB, whichever is less.
1883 hfsmp
->hfs_freespace_notify_dangerlimit
=
1884 MIN(HFS_VERYLOWDISKTRIGGERLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1885 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_VERYLOWDISKTRIGGERFRACTION
);
1886 hfsmp
->hfs_freespace_notify_warninglimit
=
1887 MIN(HFS_LOWDISKTRIGGERLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1888 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_LOWDISKTRIGGERFRACTION
);
1889 hfsmp
->hfs_freespace_notify_desiredlevel
=
1890 MIN(HFS_LOWDISKSHUTOFFLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1891 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_LOWDISKSHUTOFFFRACTION
);
1894 * Set the free space warning levels for the root volume:
1896 * Set the "danger" limit to 5% of the volume size or 512MB, whichever
1897 * is less. Set the "warning" limit to 10% of the volume size or 1GB,
1898 * whichever is less. And last, set the "desired" freespace level to
1899 * to 11% of the volume size or 1.25GB, whichever is less.
1901 hfsmp
->hfs_freespace_notify_dangerlimit
=
1902 MIN(HFS_ROOTVERYLOWDISKTRIGGERLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1903 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_ROOTVERYLOWDISKTRIGGERFRACTION
);
1904 hfsmp
->hfs_freespace_notify_warninglimit
=
1905 MIN(HFS_ROOTLOWDISKTRIGGERLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1906 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_ROOTLOWDISKTRIGGERFRACTION
);
1907 hfsmp
->hfs_freespace_notify_desiredlevel
=
1908 MIN(HFS_ROOTLOWDISKSHUTOFFLEVEL
/ HFSTOVCB(hfsmp
)->blockSize
,
1909 (HFSTOVCB(hfsmp
)->totalBlocks
/ 100) * HFS_ROOTLOWDISKSHUTOFFFRACTION
);
1912 /* Check if the file system exists on virtual device, like disk image */
1913 if (VNOP_IOCTL(devvp
, DKIOCISVIRTUAL
, (caddr_t
)&isvirtual
, 0, context
) == 0) {
1915 hfsmp
->hfs_flags
|= HFS_VIRTUAL_DEVICE
;
1919 /* do not allow ejectability checks on the root device */
1921 if ((hfsmp
->hfs_flags
& HFS_VIRTUAL_DEVICE
) == 0 &&
1922 IOBSDIsMediaEjectable(mp
->mnt_vfsstat
.f_mntfromname
)) {
1923 hfsmp
->hfs_max_pending_io
= 4096*1024; // a reasonable value to start with.
1924 hfsmp
->hfs_syncer
= thread_call_allocate(hfs_syncer
, hfsmp
);
1925 if (hfsmp
->hfs_syncer
== NULL
) {
1926 printf("hfs: failed to allocate syncer thread callback for %s (%s)\n",
1927 mp
->mnt_vfsstat
.f_mntfromname
, mp
->mnt_vfsstat
.f_mntonname
);
1932 #if CONFIG_HFS_ALLOC_RBTREE
1934 * We spawn a thread to create the pair of red-black trees for this volume.
1935 * However, in so doing, we must be careful to ensure that if this thread is still
1936 * running after mount has finished, it doesn't interfere with an unmount. Specifically,
1937 * we'll need to set a bit that indicates we're in progress building the trees here.
1938 * Unmount will check for this bit, and then if it's set, mark a corresponding bit that
1939 * notifies the tree generation code that an unmount is waiting. Also mark the bit that
1940 * indicates the tree is live and operating.
1942 * Only do this if we're operating on a read-write mount (we wouldn't care for read-only).
1945 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
1946 hfsmp
->extent_tree_flags
|= (HFS_ALLOC_TREEBUILD_INFLIGHT
| HFS_ALLOC_RB_ENABLED
);
1948 /* Initialize EOF counter so that the thread can assume it started at initial values */
1949 hfsmp
->offset_block_end
= 0;
1952 kernel_thread_start ((thread_continue_t
) hfs_initialize_allocator
, hfsmp
, &allocator_thread
);
1953 thread_deallocate(allocator_thread
);
1959 * Start looking for free space to drop below this level and generate a
1960 * warning immediately if needed:
1962 hfsmp
->hfs_notification_conditions
= 0;
1963 hfs_generate_volume_notifications(hfsmp
);
1966 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
1977 if (hfsmp
&& hfsmp
->jvp
&& hfsmp
->jvp
!= hfsmp
->hfs_devvp
) {
1978 vnode_clearmountedon(hfsmp
->jvp
);
1979 (void)VNOP_CLOSE(hfsmp
->jvp
, ronly
? FREAD
: FREAD
|FWRITE
, vfs_context_kernel());
1983 if (hfsmp
->hfs_devvp
) {
1984 vnode_rele(hfsmp
->hfs_devvp
);
1986 hfs_delete_chash(hfsmp
);
1988 FREE(hfsmp
, M_HFSMNT
);
1989 vfs_setfsprivate(mp
, NULL
);
1996 * Make a filesystem operational.
1997 * Nothing to do at the moment.
2001 hfs_start(__unused
struct mount
*mp
, __unused
int flags
, __unused vfs_context_t context
)
2008 * unmount system call
2011 hfs_unmount(struct mount
*mp
, int mntflags
, vfs_context_t context
)
2013 struct proc
*p
= vfs_context_proc(context
);
2014 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
2015 int retval
= E_NONE
;
2023 if (mntflags
& MNT_FORCE
) {
2024 flags
|= FORCECLOSE
;
2028 if ((retval
= hfs_flushfiles(mp
, flags
, p
)) && !force
)
2031 if (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
)
2032 (void) hfs_recording_suspend(hfsmp
);
2035 * Cancel any pending timers for this volume. Then wait for any timers
2036 * which have fired, but whose callbacks have not yet completed.
2038 if (hfsmp
->hfs_syncer
)
2040 struct timespec ts
= {0, 100000000}; /* 0.1 seconds */
2043 * Cancel any timers that have been scheduled, but have not
2044 * fired yet. NOTE: The kernel considers a timer complete as
2045 * soon as it starts your callback, so the kernel does not
2046 * keep track of the number of callbacks in progress.
2048 if (thread_call_cancel(hfsmp
->hfs_syncer
))
2049 OSDecrementAtomic((volatile SInt32
*)&hfsmp
->hfs_sync_incomplete
);
2050 thread_call_free(hfsmp
->hfs_syncer
);
2051 hfsmp
->hfs_syncer
= NULL
;
2054 * This waits for all of the callbacks that were entered before
2055 * we did thread_call_cancel above, but have not completed yet.
2057 while(hfsmp
->hfs_sync_incomplete
> 0)
2059 msleep((caddr_t
)&hfsmp
->hfs_sync_incomplete
, NULL
, PWAIT
, "hfs_unmount", &ts
);
2062 if (hfsmp
->hfs_sync_incomplete
< 0)
2063 panic("hfs_unmount: pm_sync_incomplete underflow!\n");
2066 #if CONFIG_HFS_ALLOC_RBTREE
2067 rb_used
= hfs_teardown_allocator(hfsmp
);
2071 * Flush out the b-trees, volume bitmap and Volume Header
2073 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) == 0) {
2074 retval
= hfs_start_transaction(hfsmp
);
2077 } else if (!force
) {
2081 if (hfsmp
->hfs_startup_vp
) {
2082 (void) hfs_lock(VTOC(hfsmp
->hfs_startup_vp
), HFS_EXCLUSIVE_LOCK
);
2083 retval
= hfs_fsync(hfsmp
->hfs_startup_vp
, MNT_WAIT
, 0, p
);
2084 hfs_unlock(VTOC(hfsmp
->hfs_startup_vp
));
2085 if (retval
&& !force
)
2089 if (hfsmp
->hfs_attribute_vp
) {
2090 (void) hfs_lock(VTOC(hfsmp
->hfs_attribute_vp
), HFS_EXCLUSIVE_LOCK
);
2091 retval
= hfs_fsync(hfsmp
->hfs_attribute_vp
, MNT_WAIT
, 0, p
);
2092 hfs_unlock(VTOC(hfsmp
->hfs_attribute_vp
));
2093 if (retval
&& !force
)
2097 (void) hfs_lock(VTOC(hfsmp
->hfs_catalog_vp
), HFS_EXCLUSIVE_LOCK
);
2098 retval
= hfs_fsync(hfsmp
->hfs_catalog_vp
, MNT_WAIT
, 0, p
);
2099 hfs_unlock(VTOC(hfsmp
->hfs_catalog_vp
));
2100 if (retval
&& !force
)
2103 (void) hfs_lock(VTOC(hfsmp
->hfs_extents_vp
), HFS_EXCLUSIVE_LOCK
);
2104 retval
= hfs_fsync(hfsmp
->hfs_extents_vp
, MNT_WAIT
, 0, p
);
2105 hfs_unlock(VTOC(hfsmp
->hfs_extents_vp
));
2106 if (retval
&& !force
)
2109 if (hfsmp
->hfs_allocation_vp
) {
2110 (void) hfs_lock(VTOC(hfsmp
->hfs_allocation_vp
), HFS_EXCLUSIVE_LOCK
);
2111 retval
= hfs_fsync(hfsmp
->hfs_allocation_vp
, MNT_WAIT
, 0, p
);
2112 hfs_unlock(VTOC(hfsmp
->hfs_allocation_vp
));
2113 if (retval
&& !force
)
2117 if (hfsmp
->hfc_filevp
&& vnode_issystem(hfsmp
->hfc_filevp
)) {
2118 retval
= hfs_fsync(hfsmp
->hfc_filevp
, MNT_WAIT
, 0, p
);
2119 if (retval
&& !force
)
2123 /* If runtime corruption was detected, indicate that the volume
2124 * was not unmounted cleanly.
2126 if (hfsmp
->vcbAtrb
& kHFSVolumeInconsistentMask
) {
2127 HFSTOVCB(hfsmp
)->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
2129 HFSTOVCB(hfsmp
)->vcbAtrb
|= kHFSVolumeUnmountedMask
;
2134 /* If the rb-tree was live, just set min_start to 0 */
2135 hfsmp
->nextAllocation
= 0;
2138 if (hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) {
2140 u_int32_t min_start
= hfsmp
->totalBlocks
;
2142 // set the nextAllocation pointer to the smallest free block number
2143 // we've seen so on the next mount we won't rescan unnecessarily
2144 lck_spin_lock(&hfsmp
->vcbFreeExtLock
);
2145 for(i
=0; i
< (int)hfsmp
->vcbFreeExtCnt
; i
++) {
2146 if (hfsmp
->vcbFreeExt
[i
].startBlock
< min_start
) {
2147 min_start
= hfsmp
->vcbFreeExt
[i
].startBlock
;
2150 lck_spin_unlock(&hfsmp
->vcbFreeExtLock
);
2151 if (min_start
< hfsmp
->nextAllocation
) {
2152 hfsmp
->nextAllocation
= min_start
;
2158 retval
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
2160 HFSTOVCB(hfsmp
)->vcbAtrb
&= ~kHFSVolumeUnmountedMask
;
2162 goto err_exit
; /* could not flush everything */
2166 hfs_end_transaction(hfsmp
);
2172 hfs_journal_flush(hfsmp
, FALSE
);
2176 * Invalidate our caches and release metadata vnodes
2178 (void) hfsUnmount(hfsmp
, p
);
2180 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
)
2181 (void) hfs_relconverter(hfsmp
->hfs_encoding
);
2185 journal_close(hfsmp
->jnl
);
2189 VNOP_FSYNC(hfsmp
->hfs_devvp
, MNT_WAIT
, context
);
2191 if (hfsmp
->jvp
&& hfsmp
->jvp
!= hfsmp
->hfs_devvp
) {
2192 vnode_clearmountedon(hfsmp
->jvp
);
2193 retval
= VNOP_CLOSE(hfsmp
->jvp
,
2194 hfsmp
->hfs_flags
& HFS_READ_ONLY
? FREAD
: FREAD
|FWRITE
,
2195 vfs_context_kernel());
2196 vnode_put(hfsmp
->jvp
);
2202 * Last chance to dump unreferenced system files.
2204 (void) vflush(mp
, NULLVP
, FORCECLOSE
);
2207 /* Drop our reference on the backing fs (if any). */
2208 if ((hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) && hfsmp
->hfs_backingfs_rootvp
) {
2209 struct vnode
* tmpvp
;
2211 hfsmp
->hfs_flags
&= ~HFS_HAS_SPARSE_DEVICE
;
2212 tmpvp
= hfsmp
->hfs_backingfs_rootvp
;
2213 hfsmp
->hfs_backingfs_rootvp
= NULLVP
;
2216 #endif /* HFS_SPARSE_DEV */
2217 lck_mtx_destroy(&hfsmp
->hfc_mutex
, hfs_mutex_group
);
2218 lck_spin_destroy(&hfsmp
->vcbFreeExtLock
, hfs_spinlock_group
);
2219 vnode_rele(hfsmp
->hfs_devvp
);
2221 hfs_delete_chash(hfsmp
);
2222 FREE(hfsmp
, M_HFSMNT
);
2228 hfs_end_transaction(hfsmp
);
2235 * Return the root of a filesystem.
2238 hfs_vfs_root(struct mount
*mp
, struct vnode
**vpp
, __unused vfs_context_t context
)
2240 return hfs_vget(VFSTOHFS(mp
), (cnid_t
)kHFSRootFolderID
, vpp
, 1, 0);
2245 * Do operations associated with quotas
2249 hfs_quotactl(__unused
struct mount
*mp
, __unused
int cmds
, __unused uid_t uid
, __unused caddr_t datap
, __unused vfs_context_t context
)
2255 hfs_quotactl(struct mount
*mp
, int cmds
, uid_t uid
, caddr_t datap
, vfs_context_t context
)
2257 struct proc
*p
= vfs_context_proc(context
);
2258 int cmd
, type
, error
;
2261 uid
= kauth_cred_getuid(vfs_context_ucred(context
));
2262 cmd
= cmds
>> SUBCMDSHIFT
;
2269 if (uid
== kauth_cred_getuid(vfs_context_ucred(context
)))
2273 if ( (error
= vfs_context_suser(context
)) )
2277 type
= cmds
& SUBCMDMASK
;
2278 if ((u_int
)type
>= MAXQUOTAS
)
2280 if (vfs_busy(mp
, LK_NOWAIT
))
2286 error
= hfs_quotaon(p
, mp
, type
, datap
);
2290 error
= hfs_quotaoff(p
, mp
, type
);
2294 error
= hfs_setquota(mp
, uid
, type
, datap
);
2298 error
= hfs_setuse(mp
, uid
, type
, datap
);
2302 error
= hfs_getquota(mp
, uid
, type
, datap
);
2306 error
= hfs_qsync(mp
);
2310 error
= hfs_quotastat(mp
, type
, datap
);
2323 /* Subtype is composite of bits */
2324 #define HFS_SUBTYPE_JOURNALED 0x01
2325 #define HFS_SUBTYPE_CASESENSITIVE 0x02
2326 /* bits 2 - 6 reserved */
2327 #define HFS_SUBTYPE_STANDARDHFS 0x80
2330 * Get file system statistics.
2333 hfs_statfs(struct mount
*mp
, register struct vfsstatfs
*sbp
, __unused vfs_context_t context
)
2335 ExtendedVCB
*vcb
= VFSTOVCB(mp
);
2336 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
2337 u_int32_t freeCNIDs
;
2338 u_int16_t subtype
= 0;
2340 freeCNIDs
= (u_int32_t
)0xFFFFFFFF - (u_int32_t
)vcb
->vcbNxtCNID
;
2342 sbp
->f_bsize
= (u_int32_t
)vcb
->blockSize
;
2343 sbp
->f_iosize
= (size_t)cluster_max_io_size(mp
, 0);
2344 sbp
->f_blocks
= (u_int64_t
)((u_int32_t
)vcb
->totalBlocks
);
2345 sbp
->f_bfree
= (u_int64_t
)((u_int32_t
)hfs_freeblks(hfsmp
, 0));
2346 sbp
->f_bavail
= (u_int64_t
)((u_int32_t
)hfs_freeblks(hfsmp
, 1));
2347 sbp
->f_files
= (u_int64_t
)((u_int32_t
)(vcb
->totalBlocks
- 2)); /* max files is constrained by total blocks */
2348 sbp
->f_ffree
= (u_int64_t
)((u_int32_t
)(MIN(freeCNIDs
, sbp
->f_bavail
)));
2351 * Subtypes (flavors) for HFS
2352 * 0: Mac OS Extended
2353 * 1: Mac OS Extended (Journaled)
2354 * 2: Mac OS Extended (Case Sensitive)
2355 * 3: Mac OS Extended (Case Sensitive, Journaled)
2357 * 128: Mac OS Standard
2360 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
2361 subtype
= HFS_SUBTYPE_STANDARDHFS
;
2362 } else /* HFS Plus */ {
2364 subtype
|= HFS_SUBTYPE_JOURNALED
;
2365 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
2366 subtype
|= HFS_SUBTYPE_CASESENSITIVE
;
2368 sbp
->f_fssubtype
= subtype
;
2375 // XXXdbg -- this is a callback to be used by the journal to
2376 // get meta data blocks flushed out to disk.
2378 // XXXdbg -- be smarter and don't flush *every* block on each
2379 // call. try to only flush some so we don't wind up
2380 // being too synchronous.
2384 hfs_sync_metadata(void *arg
)
2386 struct mount
*mp
= (struct mount
*)arg
;
2387 struct hfsmount
*hfsmp
;
2391 daddr64_t priIDSector
;
2392 hfsmp
= VFSTOHFS(mp
);
2393 vcb
= HFSTOVCB(hfsmp
);
2395 // now make sure the super block is flushed
2396 priIDSector
= (daddr64_t
)((vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) +
2397 HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
));
2399 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
2400 HFS_PHYSBLK_ROUNDDOWN(priIDSector
, hfsmp
->hfs_log_per_phys
),
2401 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
);
2402 if ((retval
!= 0 ) && (retval
!= ENXIO
)) {
2403 printf("hfs_sync_metadata: can't read volume header at %d! (retval 0x%x)\n",
2404 (int)priIDSector
, retval
);
2407 if (retval
== 0 && ((buf_flags(bp
) & (B_DELWRI
| B_LOCKED
)) == B_DELWRI
)) {
2413 // the alternate super block...
2414 // XXXdbg - we probably don't need to do this each and every time.
2415 // hfs_btreeio.c:FlushAlternate() should flag when it was
2417 if (hfsmp
->hfs_alt_id_sector
) {
2418 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
2419 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_alt_id_sector
, hfsmp
->hfs_log_per_phys
),
2420 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
);
2421 if (retval
== 0 && ((buf_flags(bp
) & (B_DELWRI
| B_LOCKED
)) == B_DELWRI
)) {
2430 struct hfs_sync_cargs
{
2439 hfs_sync_callback(struct vnode
*vp
, void *cargs
)
2442 struct hfs_sync_cargs
*args
;
2445 args
= (struct hfs_sync_cargs
*)cargs
;
2447 if (hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
) != 0) {
2448 return (VNODE_RETURNED
);
2452 if ((cp
->c_flag
& C_MODIFIED
) ||
2453 (cp
->c_touch_acctime
| cp
->c_touch_chgtime
| cp
->c_touch_modtime
) ||
2454 vnode_hasdirtyblks(vp
)) {
2455 error
= hfs_fsync(vp
, args
->waitfor
, 0, args
->p
);
2458 args
->error
= error
;
2461 return (VNODE_RETURNED
);
2467 * Go through the disk queues to initiate sandbagged IO;
2468 * go through the inodes to write those that have been modified;
2469 * initiate the writing of the super block if it has been modified.
2471 * Note: we are always called with the filesystem marked `MPBUSY'.
2474 hfs_sync(struct mount
*mp
, int waitfor
, vfs_context_t context
)
2476 struct proc
*p
= vfs_context_proc(context
);
2478 struct hfsmount
*hfsmp
;
2480 struct vnode
*meta_vp
[4];
2482 int error
, allerror
= 0;
2483 struct hfs_sync_cargs args
;
2485 hfsmp
= VFSTOHFS(mp
);
2488 * hfs_changefs might be manipulating vnodes so back off
2490 if (hfsmp
->hfs_flags
& HFS_IN_CHANGEFS
)
2493 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
)
2496 /* skip over frozen volumes */
2497 if (!lck_rw_try_lock_shared(&hfsmp
->hfs_insync
))
2500 args
.cred
= kauth_cred_get();
2501 args
.waitfor
= waitfor
;
2505 * hfs_sync_callback will be called for each vnode
2506 * hung off of this mount point... the vnode will be
2507 * properly referenced and unreferenced around the callback
2509 vnode_iterate(mp
, 0, hfs_sync_callback
, (void *)&args
);
2512 allerror
= args
.error
;
2514 vcb
= HFSTOVCB(hfsmp
);
2516 meta_vp
[0] = vcb
->extentsRefNum
;
2517 meta_vp
[1] = vcb
->catalogRefNum
;
2518 meta_vp
[2] = vcb
->allocationsRefNum
; /* This is NULL for standard HFS */
2519 meta_vp
[3] = hfsmp
->hfs_attribute_vp
; /* Optional file */
2521 /* Now sync our three metadata files */
2522 for (i
= 0; i
< 4; ++i
) {
2526 if ((btvp
==0) || (vnode_mount(btvp
) != mp
))
2529 /* XXX use hfs_systemfile_lock instead ? */
2530 (void) hfs_lock(VTOC(btvp
), HFS_EXCLUSIVE_LOCK
);
2533 if (((cp
->c_flag
& C_MODIFIED
) == 0) &&
2534 (cp
->c_touch_acctime
== 0) &&
2535 (cp
->c_touch_chgtime
== 0) &&
2536 (cp
->c_touch_modtime
== 0) &&
2537 vnode_hasdirtyblks(btvp
) == 0) {
2538 hfs_unlock(VTOC(btvp
));
2541 error
= vnode_get(btvp
);
2543 hfs_unlock(VTOC(btvp
));
2546 if ((error
= hfs_fsync(btvp
, waitfor
, 0, p
)))
2554 * Force stale file system control information to be flushed.
2556 if (vcb
->vcbSigWord
== kHFSSigWord
) {
2557 if ((error
= VNOP_FSYNC(hfsmp
->hfs_devvp
, waitfor
, context
))) {
2565 hfs_hotfilesync(hfsmp
, vfs_context_kernel());
2568 * Write back modified superblock.
2570 if (IsVCBDirty(vcb
)) {
2571 error
= hfs_flushvolumeheader(hfsmp
, waitfor
, 0);
2577 hfs_journal_flush(hfsmp
, FALSE
);
2585 clock_get_calendar_microtime(&secs
, &usecs
);
2586 now
= ((uint64_t)secs
* 1000000ULL) + (uint64_t)usecs
;
2587 hfsmp
->hfs_last_sync_time
= now
;
2590 lck_rw_unlock_shared(&hfsmp
->hfs_insync
);
2596 * File handle to vnode
2598 * Have to be really careful about stale file handles:
2599 * - check that the cnode id is valid
2600 * - call hfs_vget() to get the locked cnode
2601 * - check for an unallocated cnode (i_mode == 0)
2602 * - check that the given client host has export rights and return
2603 * those rights via. exflagsp and credanonp
2606 hfs_fhtovp(struct mount
*mp
, int fhlen
, unsigned char *fhp
, struct vnode
**vpp
, __unused vfs_context_t context
)
2608 struct hfsfid
*hfsfhp
;
2613 hfsfhp
= (struct hfsfid
*)fhp
;
2615 if (fhlen
< (int)sizeof(struct hfsfid
))
2618 result
= hfs_vget(VFSTOHFS(mp
), ntohl(hfsfhp
->hfsfid_cnid
), &nvp
, 0, 0);
2620 if (result
== ENOENT
)
2626 * We used to use the create time as the gen id of the file handle,
2627 * but it is not static enough because it can change at any point
2628 * via system calls. We still don't have another volume ID or other
2629 * unique identifier to use for a generation ID across reboots that
2630 * persists until the file is removed. Using only the CNID exposes
2631 * us to the potential wrap-around case, but as of 2/2008, it would take
2632 * over 2 months to wrap around if the machine did nothing but allocate
2633 * CNIDs. Using some kind of wrap counter would only be effective if
2634 * each file had the wrap counter associated with it. For now,
2635 * we use only the CNID to identify the file as it's good enough.
2640 hfs_unlock(VTOC(nvp
));
2646 * Vnode pointer to File handle
2650 hfs_vptofh(struct vnode
*vp
, int *fhlenp
, unsigned char *fhp
, __unused vfs_context_t context
)
2653 struct hfsfid
*hfsfhp
;
2655 if (ISHFS(VTOVCB(vp
)))
2656 return (ENOTSUP
); /* hfs standard is not exportable */
2658 if (*fhlenp
< (int)sizeof(struct hfsfid
))
2662 hfsfhp
= (struct hfsfid
*)fhp
;
2663 /* only the CNID is used to identify the file now */
2664 hfsfhp
->hfsfid_cnid
= htonl(cp
->c_fileid
);
2665 hfsfhp
->hfsfid_gen
= htonl(cp
->c_fileid
);
2666 *fhlenp
= sizeof(struct hfsfid
);
2673 * Initial HFS filesystems, done only once.
2676 hfs_init(__unused
struct vfsconf
*vfsp
)
2678 static int done
= 0;
2684 hfs_converterinit();
2689 hfs_lock_attr
= lck_attr_alloc_init();
2690 hfs_group_attr
= lck_grp_attr_alloc_init();
2691 hfs_mutex_group
= lck_grp_alloc_init("hfs-mutex", hfs_group_attr
);
2692 hfs_rwlock_group
= lck_grp_alloc_init("hfs-rwlock", hfs_group_attr
);
2693 hfs_spinlock_group
= lck_grp_alloc_init("hfs-spinlock", hfs_group_attr
);
2703 hfs_getmountpoint(struct vnode
*vp
, struct hfsmount
**hfsmpp
)
2705 struct hfsmount
* hfsmp
;
2706 char fstypename
[MFSNAMELEN
];
2711 if (!vnode_isvroot(vp
))
2714 vnode_vfsname(vp
, fstypename
);
2715 if (strncmp(fstypename
, "hfs", sizeof(fstypename
)) != 0)
2720 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
)
2729 #include <sys/filedesc.h>
2732 * HFS filesystem related variables.
2735 hfs_sysctl(int *name
, __unused u_int namelen
, user_addr_t oldp
, size_t *oldlenp
,
2736 user_addr_t newp
, size_t newlen
, vfs_context_t context
)
2738 struct proc
*p
= vfs_context_proc(context
);
2740 struct hfsmount
*hfsmp
;
2742 /* all sysctl names at this level are terminal */
2744 if (name
[0] == HFS_ENCODINGBIAS
) {
2747 bias
= hfs_getencodingbias();
2748 error
= sysctl_int(oldp
, oldlenp
, newp
, newlen
, &bias
);
2749 if (error
== 0 && newp
)
2750 hfs_setencodingbias(bias
);
2753 } else if (name
[0] == HFS_EXTEND_FS
) {
2755 vnode_t vp
= vfs_context_cwd(context
);
2757 if (newp
== USER_ADDR_NULL
|| vp
== NULLVP
)
2759 if ((error
= hfs_getmountpoint(vp
, &hfsmp
)))
2761 error
= sysctl_quad(oldp
, oldlenp
, newp
, newlen
, (quad_t
*)&newsize
);
2765 error
= hfs_extendfs(hfsmp
, newsize
, context
);
2768 } else if (name
[0] == HFS_ENCODINGHINT
) {
2772 u_int16_t
*unicode_name
= NULL
;
2773 char *filename
= NULL
;
2775 if ((newlen
<= 0) || (newlen
> MAXPATHLEN
))
2778 bufsize
= MAX(newlen
* 3, MAXPATHLEN
);
2779 MALLOC(filename
, char *, newlen
, M_TEMP
, M_WAITOK
);
2780 if (filename
== NULL
) {
2782 goto encodinghint_exit
;
2784 MALLOC(unicode_name
, u_int16_t
*, bufsize
, M_TEMP
, M_WAITOK
);
2785 if (filename
== NULL
) {
2787 goto encodinghint_exit
;
2790 error
= copyin(newp
, (caddr_t
)filename
, newlen
);
2792 error
= utf8_decodestr((u_int8_t
*)filename
, newlen
- 1, unicode_name
,
2793 &bytes
, bufsize
, 0, UTF_DECOMPOSED
);
2795 hint
= hfs_pickencoding(unicode_name
, bytes
/ 2);
2796 error
= sysctl_int(oldp
, oldlenp
, USER_ADDR_NULL
, 0, (int32_t *)&hint
);
2802 FREE(unicode_name
, M_TEMP
);
2804 FREE(filename
, M_TEMP
);
2807 } else if (name
[0] == HFS_ENABLE_JOURNALING
) {
2808 // make the file system journaled...
2809 vnode_t vp
= vfs_context_cwd(context
);
2812 struct cat_attr jnl_attr
, jinfo_attr
;
2813 struct cat_fork jnl_fork
, jinfo_fork
;
2817 /* Only root can enable journaling */
2825 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
2828 if (HFSTOVCB(hfsmp
)->vcbSigWord
== kHFSSigWord
) {
2829 printf("hfs: can't make a plain hfs volume journaled.\n");
2834 printf("hfs: volume @ mp %p is already journaled!\n", vnode_mount(vp
));
2838 vcb
= HFSTOVCB(hfsmp
);
2839 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_EXTENTS
, HFS_EXCLUSIVE_LOCK
);
2840 if (BTHasContiguousNodes(VTOF(vcb
->catalogRefNum
)) == 0 ||
2841 BTHasContiguousNodes(VTOF(vcb
->extentsRefNum
)) == 0) {
2843 printf("hfs: volume has a btree w/non-contiguous nodes. can not enable journaling.\n");
2844 hfs_systemfile_unlock(hfsmp
, lockflags
);
2847 hfs_systemfile_unlock(hfsmp
, lockflags
);
2849 // make sure these both exist!
2850 if ( GetFileInfo(vcb
, kHFSRootFolderID
, ".journal_info_block", &jinfo_attr
, &jinfo_fork
) == 0
2851 || GetFileInfo(vcb
, kHFSRootFolderID
, ".journal", &jnl_attr
, &jnl_fork
) == 0) {
2856 hfs_sync(hfsmp
->hfs_mp
, MNT_WAIT
, context
);
2858 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2859 (off_t
)name
[2], (off_t
)name
[3]);
2862 // XXXdbg - note that currently (Sept, 08) hfs_util does not support
2863 // enabling the journal on a separate device so it is safe
2864 // to just copy hfs_devvp here. If hfs_util gets the ability
2865 // to dynamically enable the journal on a separate device then
2866 // we will have to do the same thing as hfs_early_journal_init()
2867 // to locate and open the journal device.
2869 jvp
= hfsmp
->hfs_devvp
;
2870 jnl
= journal_create(jvp
,
2871 (off_t
)name
[2] * (off_t
)HFSTOVCB(hfsmp
)->blockSize
2872 + HFSTOVCB(hfsmp
)->hfsPlusIOPosOffset
,
2873 (off_t
)((unsigned)name
[3]),
2875 hfsmp
->hfs_logical_block_size
,
2878 hfs_sync_metadata
, hfsmp
->hfs_mp
);
2881 * Set up the trim callback function so that we can add
2882 * recently freed extents to the free extent cache once
2883 * the transaction that freed them is written to the
2887 journal_trim_set_callback(jnl
, hfs_trim_callback
, hfsmp
);
2890 printf("hfs: FAILED to create the journal!\n");
2891 if (jvp
&& jvp
!= hfsmp
->hfs_devvp
) {
2892 vnode_clearmountedon(jvp
);
2893 VNOP_CLOSE(jvp
, hfsmp
->hfs_flags
& HFS_READ_ONLY
? FREAD
: FREAD
|FWRITE
, vfs_context_kernel());
2900 hfs_lock_global (hfsmp
, HFS_EXCLUSIVE_LOCK
);
2903 * Flush all dirty metadata buffers.
2905 buf_flushdirtyblks(hfsmp
->hfs_devvp
, TRUE
, 0, "hfs_sysctl");
2906 buf_flushdirtyblks(hfsmp
->hfs_extents_vp
, TRUE
, 0, "hfs_sysctl");
2907 buf_flushdirtyblks(hfsmp
->hfs_catalog_vp
, TRUE
, 0, "hfs_sysctl");
2908 buf_flushdirtyblks(hfsmp
->hfs_allocation_vp
, TRUE
, 0, "hfs_sysctl");
2909 if (hfsmp
->hfs_attribute_vp
)
2910 buf_flushdirtyblks(hfsmp
->hfs_attribute_vp
, TRUE
, 0, "hfs_sysctl");
2912 HFSTOVCB(hfsmp
)->vcbJinfoBlock
= name
[1];
2913 HFSTOVCB(hfsmp
)->vcbAtrb
|= kHFSVolumeJournaledMask
;
2917 // save this off for the hack-y check in hfs_remove()
2918 hfsmp
->jnl_start
= (u_int32_t
)name
[2];
2919 hfsmp
->jnl_size
= (off_t
)((unsigned)name
[3]);
2920 hfsmp
->hfs_jnlinfoblkid
= jinfo_attr
.ca_fileid
;
2921 hfsmp
->hfs_jnlfileid
= jnl_attr
.ca_fileid
;
2923 vfs_setflags(hfsmp
->hfs_mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
2925 hfs_unlock_global (hfsmp
);
2926 hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 1);
2931 fsid
.val
[0] = (int32_t)hfsmp
->hfs_raw_dev
;
2932 fsid
.val
[1] = (int32_t)vfs_typenum(HFSTOVFS(hfsmp
));
2933 vfs_event_signal(&fsid
, VQ_UPDATE
, (intptr_t)NULL
);
2936 } else if (name
[0] == HFS_DISABLE_JOURNALING
) {
2937 // clear the journaling bit
2938 vnode_t vp
= vfs_context_cwd(context
);
2940 /* Only root can disable journaling */
2950 * Disabling journaling is disallowed on volumes with directory hard links
2951 * because we have not tested the relevant code path.
2953 if (hfsmp
->hfs_private_attr
[DIR_HARDLINKS
].ca_entries
!= 0){
2954 printf("hfs: cannot disable journaling on volumes with directory hardlinks\n");
2958 printf("hfs: disabling journaling for mount @ %p\n", vnode_mount(vp
));
2960 hfs_lock_global (hfsmp
, HFS_EXCLUSIVE_LOCK
);
2962 // Lights out for you buddy!
2963 journal_close(hfsmp
->jnl
);
2966 if (hfsmp
->jvp
&& hfsmp
->jvp
!= hfsmp
->hfs_devvp
) {
2967 vnode_clearmountedon(hfsmp
->jvp
);
2968 VNOP_CLOSE(hfsmp
->jvp
, hfsmp
->hfs_flags
& HFS_READ_ONLY
? FREAD
: FREAD
|FWRITE
, vfs_context_kernel());
2969 vnode_put(hfsmp
->jvp
);
2972 vfs_clearflags(hfsmp
->hfs_mp
, (u_int64_t
)((unsigned int)MNT_JOURNALED
));
2973 hfsmp
->jnl_start
= 0;
2974 hfsmp
->hfs_jnlinfoblkid
= 0;
2975 hfsmp
->hfs_jnlfileid
= 0;
2977 HFSTOVCB(hfsmp
)->vcbAtrb
&= ~kHFSVolumeJournaledMask
;
2979 hfs_unlock_global (hfsmp
);
2981 hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 1);
2986 fsid
.val
[0] = (int32_t)hfsmp
->hfs_raw_dev
;
2987 fsid
.val
[1] = (int32_t)vfs_typenum(HFSTOVFS(hfsmp
));
2988 vfs_event_signal(&fsid
, VQ_UPDATE
, (intptr_t)NULL
);
2991 } else if (name
[0] == HFS_GET_JOURNAL_INFO
) {
2992 vnode_t vp
= vfs_context_cwd(context
);
2993 off_t jnl_start
, jnl_size
;
2998 /* 64-bit processes won't work with this sysctl -- can't fit a pointer into an int! */
2999 if (proc_is64bit(current_proc()))
3003 if (hfsmp
->jnl
== NULL
) {
3007 jnl_start
= (off_t
)(hfsmp
->jnl_start
* HFSTOVCB(hfsmp
)->blockSize
) + (off_t
)HFSTOVCB(hfsmp
)->hfsPlusIOPosOffset
;
3008 jnl_size
= (off_t
)hfsmp
->jnl_size
;
3011 if ((error
= copyout((caddr_t
)&jnl_start
, CAST_USER_ADDR_T(name
[1]), sizeof(off_t
))) != 0) {
3014 if ((error
= copyout((caddr_t
)&jnl_size
, CAST_USER_ADDR_T(name
[2]), sizeof(off_t
))) != 0) {
3019 } else if (name
[0] == HFS_SET_PKG_EXTENSIONS
) {
3021 return set_package_extensions_table((user_addr_t
)((unsigned)name
[1]), name
[2], name
[3]);
3023 } else if (name
[0] == VFS_CTL_QUERY
) {
3024 struct sysctl_req
*req
;
3025 union union_vfsidctl vc
;
3029 req
= CAST_DOWN(struct sysctl_req
*, oldp
); /* we're new style vfs sysctl. */
3031 error
= SYSCTL_IN(req
, &vc
, proc_is64bit(p
)? sizeof(vc
.vc64
):sizeof(vc
.vc32
));
3032 if (error
) return (error
);
3034 mp
= vfs_getvfs(&vc
.vc32
.vc_fsid
); /* works for 32 and 64 */
3035 if (mp
== NULL
) return (ENOENT
);
3037 hfsmp
= VFSTOHFS(mp
);
3038 bzero(&vq
, sizeof(vq
));
3039 vq
.vq_flags
= hfsmp
->hfs_notification_conditions
;
3040 return SYSCTL_OUT(req
, &vq
, sizeof(vq
));;
3041 } else if (name
[0] == HFS_REPLAY_JOURNAL
) {
3042 vnode_t devvp
= NULL
;
3047 device_fd
= name
[1];
3048 error
= file_vnode(device_fd
, &devvp
);
3052 error
= vnode_getwithref(devvp
);
3054 file_drop(device_fd
);
3057 error
= hfs_journal_replay(devvp
, context
);
3058 file_drop(device_fd
);
3061 } else if (name
[0] == HFS_ENABLE_RESIZE_DEBUG
) {
3062 hfs_resize_debug
= 1;
3063 printf ("hfs_sysctl: Enabled volume resize debugging.\n");
3071 * hfs_vfs_vget is not static since it is used in hfs_readwrite.c to support
3072 * the build_path ioctl. We use it to leverage the code below that updates
3073 * the origin list cache if necessary
3077 hfs_vfs_vget(struct mount
*mp
, ino64_t ino
, struct vnode
**vpp
, __unused vfs_context_t context
)
3081 struct hfsmount
*hfsmp
;
3083 hfsmp
= VFSTOHFS(mp
);
3085 error
= hfs_vget(hfsmp
, (cnid_t
)ino
, vpp
, 1, 0);
3090 * ADLs may need to have their origin state updated
3091 * since build_path needs a valid parent. The same is true
3092 * for hardlinked files as well. There isn't a race window here
3093 * in re-acquiring the cnode lock since we aren't pulling any data
3094 * out of the cnode; instead, we're going to the catalog.
3096 if ((VTOC(*vpp
)->c_flag
& C_HARDLINK
) &&
3097 (hfs_lock(VTOC(*vpp
), HFS_EXCLUSIVE_LOCK
) == 0)) {
3098 cnode_t
*cp
= VTOC(*vpp
);
3099 struct cat_desc cdesc
;
3101 if (!hfs_haslinkorigin(cp
)) {
3102 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3103 error
= cat_findname(hfsmp
, (cnid_t
)ino
, &cdesc
);
3104 hfs_systemfile_unlock(hfsmp
, lockflags
);
3106 if ((cdesc
.cd_parentcnid
!= hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) &&
3107 (cdesc
.cd_parentcnid
!= hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
)) {
3108 hfs_savelinkorigin(cp
, cdesc
.cd_parentcnid
);
3110 cat_releasedesc(&cdesc
);
3120 * Look up an HFS object by ID.
3122 * The object is returned with an iocount reference and the cnode locked.
3124 * If the object is a file then it will represent the data fork.
3127 hfs_vget(struct hfsmount
*hfsmp
, cnid_t cnid
, struct vnode
**vpp
, int skiplock
, int allow_deleted
)
3129 struct vnode
*vp
= NULLVP
;
3130 struct cat_desc cndesc
;
3131 struct cat_attr cnattr
;
3132 struct cat_fork cnfork
;
3133 u_int32_t linkref
= 0;
3136 /* Check for cnids that should't be exported. */
3137 if ((cnid
< kHFSFirstUserCatalogNodeID
) &&
3138 (cnid
!= kHFSRootFolderID
&& cnid
!= kHFSRootParentID
)) {
3141 /* Don't export our private directories. */
3142 if (cnid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
3143 cnid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
3147 * Check the hash first
3149 vp
= hfs_chash_getvnode(hfsmp
, cnid
, 0, skiplock
, allow_deleted
);
3155 bzero(&cndesc
, sizeof(cndesc
));
3156 bzero(&cnattr
, sizeof(cnattr
));
3157 bzero(&cnfork
, sizeof(cnfork
));
3160 * Not in hash, lookup in catalog
3162 if (cnid
== kHFSRootParentID
) {
3163 static char hfs_rootname
[] = "/";
3165 cndesc
.cd_nameptr
= (const u_int8_t
*)&hfs_rootname
[0];
3166 cndesc
.cd_namelen
= 1;
3167 cndesc
.cd_parentcnid
= kHFSRootParentID
;
3168 cndesc
.cd_cnid
= kHFSRootFolderID
;
3169 cndesc
.cd_flags
= CD_ISDIR
;
3171 cnattr
.ca_fileid
= kHFSRootFolderID
;
3172 cnattr
.ca_linkcount
= 1;
3173 cnattr
.ca_entries
= 1;
3174 cnattr
.ca_dircount
= 1;
3175 cnattr
.ca_mode
= (S_IFDIR
| S_IRWXU
| S_IRWXG
| S_IRWXO
);
3179 const char *nameptr
;
3181 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3182 error
= cat_idlookup(hfsmp
, cnid
, 0, &cndesc
, &cnattr
, &cnfork
);
3183 hfs_systemfile_unlock(hfsmp
, lockflags
);
3191 * Check for a raw hardlink inode and save its linkref.
3193 pid
= cndesc
.cd_parentcnid
;
3194 nameptr
= (const char *)cndesc
.cd_nameptr
;
3196 if ((pid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3197 (bcmp(nameptr
, HFS_INODE_PREFIX
, HFS_INODE_PREFIX_LEN
) == 0)) {
3198 linkref
= strtoul(&nameptr
[HFS_INODE_PREFIX_LEN
], NULL
, 10);
3200 } else if ((pid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) &&
3201 (bcmp(nameptr
, HFS_DIRINODE_PREFIX
, HFS_DIRINODE_PREFIX_LEN
) == 0)) {
3202 linkref
= strtoul(&nameptr
[HFS_DIRINODE_PREFIX_LEN
], NULL
, 10);
3204 } else if ((pid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
3205 (bcmp(nameptr
, HFS_DELETE_PREFIX
, HFS_DELETE_PREFIX_LEN
) == 0)) {
3207 cat_releasedesc(&cndesc
);
3208 return (ENOENT
); /* open unlinked file */
3213 * Finish initializing cnode descriptor for hardlinks.
3215 * We need a valid name and parent for reverse lookups.
3220 struct cat_desc linkdesc
;
3223 cnattr
.ca_linkref
= linkref
;
3226 * Pick up the first link in the chain and get a descriptor for it.
3227 * This allows blind volfs paths to work for hardlinks.
3229 if ((hfs_lookup_siblinglinks(hfsmp
, linkref
, &prevlinkid
, &nextlinkid
) == 0) &&
3230 (nextlinkid
!= 0)) {
3231 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3232 error
= cat_findname(hfsmp
, nextlinkid
, &linkdesc
);
3233 hfs_systemfile_unlock(hfsmp
, lockflags
);
3235 cat_releasedesc(&cndesc
);
3236 bcopy(&linkdesc
, &cndesc
, sizeof(linkdesc
));
3242 int newvnode_flags
= 0;
3244 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cndesc
, 0, &cnattr
,
3245 &cnfork
, &vp
, &newvnode_flags
);
3247 VTOC(vp
)->c_flag
|= C_HARDLINK
;
3248 vnode_setmultipath(vp
);
3251 struct componentname cn
;
3252 int newvnode_flags
= 0;
3254 /* Supply hfs_getnewvnode with a component name. */
3255 MALLOC_ZONE(cn
.cn_pnbuf
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3256 cn
.cn_nameiop
= LOOKUP
;
3257 cn
.cn_flags
= ISLASTCN
| HASBUF
;
3258 cn
.cn_context
= NULL
;
3259 cn
.cn_pnlen
= MAXPATHLEN
;
3260 cn
.cn_nameptr
= cn
.cn_pnbuf
;
3261 cn
.cn_namelen
= cndesc
.cd_namelen
;
3264 bcopy(cndesc
.cd_nameptr
, cn
.cn_nameptr
, cndesc
.cd_namelen
+ 1);
3266 error
= hfs_getnewvnode(hfsmp
, NULLVP
, &cn
, &cndesc
, 0, &cnattr
,
3267 &cnfork
, &vp
, &newvnode_flags
);
3269 if (error
== 0 && (VTOC(vp
)->c_flag
& C_HARDLINK
)) {
3270 hfs_savelinkorigin(VTOC(vp
), cndesc
.cd_parentcnid
);
3272 FREE_ZONE(cn
.cn_pnbuf
, cn
.cn_pnlen
, M_NAMEI
);
3274 cat_releasedesc(&cndesc
);
3277 if (vp
&& skiplock
) {
3278 hfs_unlock(VTOC(vp
));
3285 * Flush out all the files in a filesystem.
3289 hfs_flushfiles(struct mount
*mp
, int flags
, struct proc
*p
)
3291 hfs_flushfiles(struct mount
*mp
, int flags
, __unused
struct proc
*p
)
3294 struct hfsmount
*hfsmp
;
3295 struct vnode
*skipvp
= NULLVP
;
3302 hfsmp
= VFSTOHFS(mp
);
3306 * The open quota files have an indirect reference on
3307 * the root directory vnode. We must account for this
3308 * extra reference when doing the intial vflush.
3311 if (((unsigned int)vfs_flags(mp
)) & MNT_QUOTA
) {
3313 /* Find out how many quota files we have open. */
3314 for (i
= 0; i
< MAXQUOTAS
; i
++) {
3315 if (hfsmp
->hfs_qfiles
[i
].qf_vp
!= NULLVP
)
3319 /* Obtain the root vnode so we can skip over it. */
3320 skipvp
= hfs_chash_getvnode(hfsmp
, kHFSRootFolderID
, 0, 0, 0);
3324 error
= vflush(mp
, skipvp
, SKIPSYSTEM
| SKIPSWAP
| flags
);
3328 error
= vflush(mp
, skipvp
, SKIPSYSTEM
| flags
);
3331 if (((unsigned int)vfs_flags(mp
)) & MNT_QUOTA
) {
3334 * See if there are additional references on the
3335 * root vp besides the ones obtained from the open
3336 * quota files and the hfs_chash_getvnode call above.
3339 (vnode_isinuse(skipvp
, quotafilecnt
))) {
3340 error
= EBUSY
; /* root directory is still open */
3342 hfs_unlock(VTOC(skipvp
));
3345 if (error
&& (flags
& FORCECLOSE
) == 0)
3348 for (i
= 0; i
< MAXQUOTAS
; i
++) {
3349 if (hfsmp
->hfs_qfiles
[i
].qf_vp
== NULLVP
)
3351 hfs_quotaoff(p
, mp
, i
);
3353 error
= vflush(mp
, NULLVP
, SKIPSYSTEM
| flags
);
3361 * Update volume encoding bitmap (HFS Plus only)
3365 hfs_setencodingbits(struct hfsmount
*hfsmp
, u_int32_t encoding
)
3367 #define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */
3368 #define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */
3373 case kTextEncodingMacUkrainian
:
3374 index
= kIndexMacUkrainian
;
3376 case kTextEncodingMacFarsi
:
3377 index
= kIndexMacFarsi
;
3384 if (index
< 64 && (hfsmp
->encodingsBitmap
& (u_int64_t
)(1ULL << index
)) == 0) {
3385 HFS_MOUNT_LOCK(hfsmp
, TRUE
)
3386 hfsmp
->encodingsBitmap
|= (u_int64_t
)(1ULL << index
);
3387 MarkVCBDirty(hfsmp
);
3388 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
3393 * Update volume stats
3395 * On journal volumes this will cause a volume header flush
3398 hfs_volupdate(struct hfsmount
*hfsmp
, enum volop op
, int inroot
)
3404 lck_mtx_lock(&hfsmp
->hfs_mutex
);
3406 MarkVCBDirty(hfsmp
);
3407 hfsmp
->hfs_mtime
= tv
.tv_sec
;
3413 if (hfsmp
->hfs_dircount
!= 0xFFFFFFFF)
3414 ++hfsmp
->hfs_dircount
;
3415 if (inroot
&& hfsmp
->vcbNmRtDirs
!= 0xFFFF)
3416 ++hfsmp
->vcbNmRtDirs
;
3419 if (hfsmp
->hfs_dircount
!= 0)
3420 --hfsmp
->hfs_dircount
;
3421 if (inroot
&& hfsmp
->vcbNmRtDirs
!= 0xFFFF)
3422 --hfsmp
->vcbNmRtDirs
;
3425 if (hfsmp
->hfs_filecount
!= 0xFFFFFFFF)
3426 ++hfsmp
->hfs_filecount
;
3427 if (inroot
&& hfsmp
->vcbNmFls
!= 0xFFFF)
3431 if (hfsmp
->hfs_filecount
!= 0)
3432 --hfsmp
->hfs_filecount
;
3433 if (inroot
&& hfsmp
->vcbNmFls
!= 0xFFFF)
3438 lck_mtx_unlock(&hfsmp
->hfs_mutex
);
3441 hfs_flushvolumeheader(hfsmp
, 0, 0);
3449 hfs_flushMDB(struct hfsmount
*hfsmp
, int waitfor
, int altflush
)
3451 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
3452 struct filefork
*fp
;
3453 HFSMasterDirectoryBlock
*mdb
;
3454 struct buf
*bp
= NULL
;
3459 sectorsize
= hfsmp
->hfs_logical_block_size
;
3460 retval
= (int)buf_bread(hfsmp
->hfs_devvp
, (daddr64_t
)HFS_PRI_SECTOR(sectorsize
), sectorsize
, NOCRED
, &bp
);
3467 lck_mtx_lock(&hfsmp
->hfs_mutex
);
3469 mdb
= (HFSMasterDirectoryBlock
*)(buf_dataptr(bp
) + HFS_PRI_OFFSET(sectorsize
));
3471 mdb
->drCrDate
= SWAP_BE32 (UTCToLocal(to_hfs_time(vcb
->hfs_itime
)));
3472 mdb
->drLsMod
= SWAP_BE32 (UTCToLocal(to_hfs_time(vcb
->vcbLsMod
)));
3473 mdb
->drAtrb
= SWAP_BE16 (vcb
->vcbAtrb
);
3474 mdb
->drNmFls
= SWAP_BE16 (vcb
->vcbNmFls
);
3475 mdb
->drAllocPtr
= SWAP_BE16 (vcb
->nextAllocation
);
3476 mdb
->drClpSiz
= SWAP_BE32 (vcb
->vcbClpSiz
);
3477 mdb
->drNxtCNID
= SWAP_BE32 (vcb
->vcbNxtCNID
);
3478 mdb
->drFreeBks
= SWAP_BE16 (vcb
->freeBlocks
);
3480 namelen
= strlen((char *)vcb
->vcbVN
);
3481 retval
= utf8_to_hfs(vcb
, namelen
, vcb
->vcbVN
, mdb
->drVN
);
3482 /* Retry with MacRoman in case that's how it was exported. */
3484 retval
= utf8_to_mac_roman(namelen
, vcb
->vcbVN
, mdb
->drVN
);
3486 mdb
->drVolBkUp
= SWAP_BE32 (UTCToLocal(to_hfs_time(vcb
->vcbVolBkUp
)));
3487 mdb
->drWrCnt
= SWAP_BE32 (vcb
->vcbWrCnt
);
3488 mdb
->drNmRtDirs
= SWAP_BE16 (vcb
->vcbNmRtDirs
);
3489 mdb
->drFilCnt
= SWAP_BE32 (vcb
->vcbFilCnt
);
3490 mdb
->drDirCnt
= SWAP_BE32 (vcb
->vcbDirCnt
);
3492 bcopy(vcb
->vcbFndrInfo
, mdb
->drFndrInfo
, sizeof(mdb
->drFndrInfo
));
3494 fp
= VTOF(vcb
->extentsRefNum
);
3495 mdb
->drXTExtRec
[0].startBlock
= SWAP_BE16 (fp
->ff_extents
[0].startBlock
);
3496 mdb
->drXTExtRec
[0].blockCount
= SWAP_BE16 (fp
->ff_extents
[0].blockCount
);
3497 mdb
->drXTExtRec
[1].startBlock
= SWAP_BE16 (fp
->ff_extents
[1].startBlock
);
3498 mdb
->drXTExtRec
[1].blockCount
= SWAP_BE16 (fp
->ff_extents
[1].blockCount
);
3499 mdb
->drXTExtRec
[2].startBlock
= SWAP_BE16 (fp
->ff_extents
[2].startBlock
);
3500 mdb
->drXTExtRec
[2].blockCount
= SWAP_BE16 (fp
->ff_extents
[2].blockCount
);
3501 mdb
->drXTFlSize
= SWAP_BE32 (fp
->ff_blocks
* vcb
->blockSize
);
3502 mdb
->drXTClpSiz
= SWAP_BE32 (fp
->ff_clumpsize
);
3503 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3505 fp
= VTOF(vcb
->catalogRefNum
);
3506 mdb
->drCTExtRec
[0].startBlock
= SWAP_BE16 (fp
->ff_extents
[0].startBlock
);
3507 mdb
->drCTExtRec
[0].blockCount
= SWAP_BE16 (fp
->ff_extents
[0].blockCount
);
3508 mdb
->drCTExtRec
[1].startBlock
= SWAP_BE16 (fp
->ff_extents
[1].startBlock
);
3509 mdb
->drCTExtRec
[1].blockCount
= SWAP_BE16 (fp
->ff_extents
[1].blockCount
);
3510 mdb
->drCTExtRec
[2].startBlock
= SWAP_BE16 (fp
->ff_extents
[2].startBlock
);
3511 mdb
->drCTExtRec
[2].blockCount
= SWAP_BE16 (fp
->ff_extents
[2].blockCount
);
3512 mdb
->drCTFlSize
= SWAP_BE32 (fp
->ff_blocks
* vcb
->blockSize
);
3513 mdb
->drCTClpSiz
= SWAP_BE32 (fp
->ff_clumpsize
);
3514 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3516 MarkVCBClean( vcb
);
3518 lck_mtx_unlock(&hfsmp
->hfs_mutex
);
3520 /* If requested, flush out the alternate MDB */
3522 struct buf
*alt_bp
= NULL
;
3524 if (buf_meta_bread(hfsmp
->hfs_devvp
, hfsmp
->hfs_alt_id_sector
, sectorsize
, NOCRED
, &alt_bp
) == 0) {
3525 bcopy(mdb
, (char *)buf_dataptr(alt_bp
) + HFS_ALT_OFFSET(sectorsize
), kMDBSize
);
3527 (void) VNOP_BWRITE(alt_bp
);
3532 if (waitfor
!= MNT_WAIT
)
3535 retval
= VNOP_BWRITE(bp
);
3541 * Flush any dirty in-memory mount data to the on-disk
3544 * Note: the on-disk volume signature is intentionally
3545 * not flushed since the on-disk "H+" and "HX" signatures
3546 * are always stored in-memory as "H+".
3549 hfs_flushvolumeheader(struct hfsmount
*hfsmp
, int waitfor
, int altflush
)
3551 ExtendedVCB
*vcb
= HFSTOVCB(hfsmp
);
3552 struct filefork
*fp
;
3553 HFSPlusVolumeHeader
*volumeHeader
, *altVH
;
3555 struct buf
*bp
, *alt_bp
;
3557 daddr64_t priIDSector
;
3559 u_int16_t signature
;
3560 u_int16_t hfsversion
;
3562 if (hfsmp
->hfs_flags
& HFS_READ_ONLY
) {
3565 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
3566 return hfs_flushMDB(hfsmp
, waitfor
, altflush
);
3568 critical
= altflush
;
3569 priIDSector
= (daddr64_t
)((vcb
->hfsPlusIOPosOffset
/ hfsmp
->hfs_logical_block_size
) +
3570 HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
));
3572 if (hfs_start_transaction(hfsmp
) != 0) {
3579 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
3580 HFS_PHYSBLK_ROUNDDOWN(priIDSector
, hfsmp
->hfs_log_per_phys
),
3581 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
);
3583 printf("hfs: err %d reading VH blk (%s)\n", retval
, vcb
->vcbVN
);
3587 volumeHeader
= (HFSPlusVolumeHeader
*)((char *)buf_dataptr(bp
) +
3588 HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
3591 * Sanity check what we just read. If it's bad, try the alternate
3594 signature
= SWAP_BE16 (volumeHeader
->signature
);
3595 hfsversion
= SWAP_BE16 (volumeHeader
->version
);
3596 if ((signature
!= kHFSPlusSigWord
&& signature
!= kHFSXSigWord
) ||
3597 (hfsversion
< kHFSPlusVersion
) || (hfsversion
> 100) ||
3598 (SWAP_BE32 (volumeHeader
->blockSize
) != vcb
->blockSize
)) {
3599 printf("hfs: corrupt VH on %s, sig 0x%04x, ver %d, blksize %d%s\n",
3600 vcb
->vcbVN
, signature
, hfsversion
,
3601 SWAP_BE32 (volumeHeader
->blockSize
),
3602 hfsmp
->hfs_alt_id_sector
? "; trying alternate" : "");
3603 hfs_mark_volume_inconsistent(hfsmp
);
3605 if (hfsmp
->hfs_alt_id_sector
) {
3606 retval
= buf_meta_bread(hfsmp
->hfs_devvp
,
3607 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_alt_id_sector
, hfsmp
->hfs_log_per_phys
),
3608 hfsmp
->hfs_physical_block_size
, NOCRED
, &alt_bp
);
3610 printf("hfs: err %d reading alternate VH (%s)\n", retval
, vcb
->vcbVN
);
3614 altVH
= (HFSPlusVolumeHeader
*)((char *)buf_dataptr(alt_bp
) +
3615 HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
));
3616 signature
= SWAP_BE16(altVH
->signature
);
3617 hfsversion
= SWAP_BE16(altVH
->version
);
3619 if ((signature
!= kHFSPlusSigWord
&& signature
!= kHFSXSigWord
) ||
3620 (hfsversion
< kHFSPlusVersion
) || (kHFSPlusVersion
> 100) ||
3621 (SWAP_BE32(altVH
->blockSize
) != vcb
->blockSize
)) {
3622 printf("hfs: corrupt alternate VH on %s, sig 0x%04x, ver %d, blksize %d\n",
3623 vcb
->vcbVN
, signature
, hfsversion
,
3624 SWAP_BE32(altVH
->blockSize
));
3629 /* The alternate is plausible, so use it. */
3630 bcopy(altVH
, volumeHeader
, kMDBSize
);
3634 /* No alternate VH, nothing more we can do. */
3641 journal_modify_block_start(hfsmp
->jnl
, bp
);
3645 * For embedded HFS+ volumes, update create date if it changed
3646 * (ie from a setattrlist call)
3648 if ((vcb
->hfsPlusIOPosOffset
!= 0) &&
3649 (SWAP_BE32 (volumeHeader
->createDate
) != vcb
->localCreateDate
)) {
3651 HFSMasterDirectoryBlock
*mdb
;
3653 retval
= (int)buf_meta_bread(hfsmp
->hfs_devvp
,
3654 HFS_PHYSBLK_ROUNDDOWN(HFS_PRI_SECTOR(hfsmp
->hfs_logical_block_size
), hfsmp
->hfs_log_per_phys
),
3655 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp2
);
3661 mdb
= (HFSMasterDirectoryBlock
*)(buf_dataptr(bp2
) +
3662 HFS_PRI_OFFSET(hfsmp
->hfs_physical_block_size
));
3664 if ( SWAP_BE32 (mdb
->drCrDate
) != vcb
->localCreateDate
)
3667 journal_modify_block_start(hfsmp
->jnl
, bp2
);
3670 mdb
->drCrDate
= SWAP_BE32 (vcb
->localCreateDate
); /* pick up the new create date */
3673 journal_modify_block_end(hfsmp
->jnl
, bp2
, NULL
, NULL
);
3675 (void) VNOP_BWRITE(bp2
); /* write out the changes */
3680 buf_brelse(bp2
); /* just release it */
3685 lck_mtx_lock(&hfsmp
->hfs_mutex
);
3687 /* Note: only update the lower 16 bits worth of attributes */
3688 volumeHeader
->attributes
= SWAP_BE32 (vcb
->vcbAtrb
);
3689 volumeHeader
->journalInfoBlock
= SWAP_BE32 (vcb
->vcbJinfoBlock
);
3691 volumeHeader
->lastMountedVersion
= SWAP_BE32 (kHFSJMountVersion
);
3693 volumeHeader
->lastMountedVersion
= SWAP_BE32 (kHFSPlusMountVersion
);
3695 volumeHeader
->createDate
= SWAP_BE32 (vcb
->localCreateDate
); /* volume create date is in local time */
3696 volumeHeader
->modifyDate
= SWAP_BE32 (to_hfs_time(vcb
->vcbLsMod
));
3697 volumeHeader
->backupDate
= SWAP_BE32 (to_hfs_time(vcb
->vcbVolBkUp
));
3698 volumeHeader
->fileCount
= SWAP_BE32 (vcb
->vcbFilCnt
);
3699 volumeHeader
->folderCount
= SWAP_BE32 (vcb
->vcbDirCnt
);
3700 volumeHeader
->totalBlocks
= SWAP_BE32 (vcb
->totalBlocks
);
3701 volumeHeader
->freeBlocks
= SWAP_BE32 (vcb
->freeBlocks
);
3702 volumeHeader
->nextAllocation
= SWAP_BE32 (vcb
->nextAllocation
);
3703 volumeHeader
->rsrcClumpSize
= SWAP_BE32 (vcb
->vcbClpSiz
);
3704 volumeHeader
->dataClumpSize
= SWAP_BE32 (vcb
->vcbClpSiz
);
3705 volumeHeader
->nextCatalogID
= SWAP_BE32 (vcb
->vcbNxtCNID
);
3706 volumeHeader
->writeCount
= SWAP_BE32 (vcb
->vcbWrCnt
);
3707 volumeHeader
->encodingsBitmap
= SWAP_BE64 (vcb
->encodingsBitmap
);
3709 if (bcmp(vcb
->vcbFndrInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
)) != 0) {
3710 bcopy(vcb
->vcbFndrInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
));
3715 * System files are only dirty when altflush is set.
3717 if (altflush
== 0) {
3721 /* Sync Extents over-flow file meta data */
3722 fp
= VTOF(vcb
->extentsRefNum
);
3723 if (FTOC(fp
)->c_flag
& C_MODIFIED
) {
3724 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
3725 volumeHeader
->extentsFile
.extents
[i
].startBlock
=
3726 SWAP_BE32 (fp
->ff_extents
[i
].startBlock
);
3727 volumeHeader
->extentsFile
.extents
[i
].blockCount
=
3728 SWAP_BE32 (fp
->ff_extents
[i
].blockCount
);
3730 volumeHeader
->extentsFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
3731 volumeHeader
->extentsFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
3732 volumeHeader
->extentsFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
3733 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3736 /* Sync Catalog file meta data */
3737 fp
= VTOF(vcb
->catalogRefNum
);
3738 if (FTOC(fp
)->c_flag
& C_MODIFIED
) {
3739 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
3740 volumeHeader
->catalogFile
.extents
[i
].startBlock
=
3741 SWAP_BE32 (fp
->ff_extents
[i
].startBlock
);
3742 volumeHeader
->catalogFile
.extents
[i
].blockCount
=
3743 SWAP_BE32 (fp
->ff_extents
[i
].blockCount
);
3745 volumeHeader
->catalogFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
3746 volumeHeader
->catalogFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
3747 volumeHeader
->catalogFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
3748 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3751 /* Sync Allocation file meta data */
3752 fp
= VTOF(vcb
->allocationsRefNum
);
3753 if (FTOC(fp
)->c_flag
& C_MODIFIED
) {
3754 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
3755 volumeHeader
->allocationFile
.extents
[i
].startBlock
=
3756 SWAP_BE32 (fp
->ff_extents
[i
].startBlock
);
3757 volumeHeader
->allocationFile
.extents
[i
].blockCount
=
3758 SWAP_BE32 (fp
->ff_extents
[i
].blockCount
);
3760 volumeHeader
->allocationFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
3761 volumeHeader
->allocationFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
3762 volumeHeader
->allocationFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
3763 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3766 /* Sync Attribute file meta data */
3767 if (hfsmp
->hfs_attribute_vp
) {
3768 fp
= VTOF(hfsmp
->hfs_attribute_vp
);
3769 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
3770 volumeHeader
->attributesFile
.extents
[i
].startBlock
=
3771 SWAP_BE32 (fp
->ff_extents
[i
].startBlock
);
3772 volumeHeader
->attributesFile
.extents
[i
].blockCount
=
3773 SWAP_BE32 (fp
->ff_extents
[i
].blockCount
);
3775 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3776 volumeHeader
->attributesFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
3777 volumeHeader
->attributesFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
3778 volumeHeader
->attributesFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
3781 /* Sync Startup file meta data */
3782 if (hfsmp
->hfs_startup_vp
) {
3783 fp
= VTOF(hfsmp
->hfs_startup_vp
);
3784 if (FTOC(fp
)->c_flag
& C_MODIFIED
) {
3785 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
3786 volumeHeader
->startupFile
.extents
[i
].startBlock
=
3787 SWAP_BE32 (fp
->ff_extents
[i
].startBlock
);
3788 volumeHeader
->startupFile
.extents
[i
].blockCount
=
3789 SWAP_BE32 (fp
->ff_extents
[i
].blockCount
);
3791 volumeHeader
->startupFile
.logicalSize
= SWAP_BE64 (fp
->ff_size
);
3792 volumeHeader
->startupFile
.totalBlocks
= SWAP_BE32 (fp
->ff_blocks
);
3793 volumeHeader
->startupFile
.clumpSize
= SWAP_BE32 (fp
->ff_clumpsize
);
3794 FTOC(fp
)->c_flag
&= ~C_MODIFIED
;
3799 MarkVCBClean(hfsmp
);
3800 lck_mtx_unlock(&hfsmp
->hfs_mutex
);
3802 /* If requested, flush out the alternate volume header */
3803 if (altflush
&& hfsmp
->hfs_alt_id_sector
) {
3804 if (buf_meta_bread(hfsmp
->hfs_devvp
,
3805 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_alt_id_sector
, hfsmp
->hfs_log_per_phys
),
3806 hfsmp
->hfs_physical_block_size
, NOCRED
, &alt_bp
) == 0) {
3808 journal_modify_block_start(hfsmp
->jnl
, alt_bp
);
3811 bcopy(volumeHeader
, (char *)buf_dataptr(alt_bp
) +
3812 HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
),
3816 journal_modify_block_end(hfsmp
->jnl
, alt_bp
, NULL
, NULL
);
3818 (void) VNOP_BWRITE(alt_bp
);
3825 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
3827 if (waitfor
!= MNT_WAIT
)
3830 retval
= VNOP_BWRITE(bp
);
3831 /* When critical data changes, flush the device cache */
3832 if (critical
&& (retval
== 0)) {
3833 (void) VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
,
3834 NULL
, FWRITE
, NULL
);
3838 hfs_end_transaction(hfsmp
);
3847 hfs_end_transaction(hfsmp
);
3853 * Extend a file system.
3856 hfs_extendfs(struct hfsmount
*hfsmp
, u_int64_t newsize
, vfs_context_t context
)
3858 struct proc
*p
= vfs_context_proc(context
);
3859 kauth_cred_t cred
= vfs_context_ucred(context
);
3861 struct vnode
*devvp
;
3863 struct filefork
*fp
= NULL
;
3865 struct cat_fork forkdata
;
3867 u_int64_t newblkcnt
;
3868 u_int64_t prev_phys_block_count
;
3870 u_int64_t sectorcnt
;
3871 u_int32_t sectorsize
;
3872 u_int32_t phys_sectorsize
;
3873 daddr64_t prev_alt_sector
;
3877 int64_t oldBitmapSize
;
3878 Boolean usedExtendFileC
= false;
3879 int transaction_begun
= 0;
3881 devvp
= hfsmp
->hfs_devvp
;
3882 vcb
= HFSTOVCB(hfsmp
);
3885 * - HFS Plus file systems only.
3886 * - Journaling must be enabled.
3887 * - No embedded volumes.
3889 if ((vcb
->vcbSigWord
== kHFSSigWord
) ||
3890 (hfsmp
->jnl
== NULL
) ||
3891 (vcb
->hfsPlusIOPosOffset
!= 0)) {
3895 * If extending file system by non-root, then verify
3896 * ownership and check permissions.
3898 if (suser(cred
, NULL
)) {
3899 error
= hfs_vget(hfsmp
, kHFSRootFolderID
, &vp
, 0, 0);
3903 error
= hfs_owner_rights(hfsmp
, VTOC(vp
)->c_uid
, cred
, p
, 0);
3905 error
= hfs_write_access(vp
, cred
, p
, false);
3907 hfs_unlock(VTOC(vp
));
3912 error
= vnode_authorize(devvp
, NULL
, KAUTH_VNODE_READ_DATA
| KAUTH_VNODE_WRITE_DATA
, context
);
3916 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKSIZE
, (caddr_t
)§orsize
, 0, context
)) {
3919 if (sectorsize
!= hfsmp
->hfs_logical_block_size
) {
3922 if (VNOP_IOCTL(devvp
, DKIOCGETBLOCKCOUNT
, (caddr_t
)§orcnt
, 0, context
)) {
3925 if ((sectorsize
* sectorcnt
) < newsize
) {
3926 printf("hfs_extendfs: not enough space on device\n");
3929 error
= VNOP_IOCTL(devvp
, DKIOCGETPHYSICALBLOCKSIZE
, (caddr_t
)&phys_sectorsize
, 0, context
);
3931 if ((error
!= ENOTSUP
) && (error
!= ENOTTY
)) {
3934 /* If ioctl is not supported, force physical and logical sector size to be same */
3935 phys_sectorsize
= sectorsize
;
3937 oldsize
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
3940 * Validate new size.
3942 if ((newsize
<= oldsize
) || (newsize
% sectorsize
) || (newsize
% phys_sectorsize
)) {
3943 printf("hfs_extendfs: invalid size\n");
3946 newblkcnt
= newsize
/ vcb
->blockSize
;
3947 if (newblkcnt
> (u_int64_t
)0xFFFFFFFF)
3950 addblks
= newblkcnt
- vcb
->totalBlocks
;
3952 if (hfs_resize_debug
) {
3953 printf ("hfs_extendfs: old: size=%qu, blkcnt=%u\n", oldsize
, hfsmp
->totalBlocks
);
3954 printf ("hfs_extendfs: new: size=%qu, blkcnt=%u, addblks=%u\n", newsize
, (u_int32_t
)newblkcnt
, addblks
);
3956 printf("hfs_extendfs: will extend \"%s\" by %d blocks\n", vcb
->vcbVN
, addblks
);
3958 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
3959 if (hfsmp
->hfs_flags
& HFS_RESIZE_IN_PROGRESS
) {
3960 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
3964 hfsmp
->hfs_flags
|= HFS_RESIZE_IN_PROGRESS
;
3965 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
3968 * Enclose changes inside a transaction.
3970 if (hfs_start_transaction(hfsmp
) != 0) {
3974 transaction_begun
= 1;
3977 * Note: we take the attributes lock in case we have an attribute data vnode
3978 * which needs to change size.
3980 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_EXTENTS
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
3981 vp
= vcb
->allocationsRefNum
;
3983 bcopy(&fp
->ff_data
, &forkdata
, sizeof(forkdata
));
3986 * Calculate additional space required (if any) by allocation bitmap.
3988 oldBitmapSize
= fp
->ff_size
;
3989 bitmapblks
= roundup((newblkcnt
+7) / 8, vcb
->vcbVBMIOSize
) / vcb
->blockSize
;
3990 if (bitmapblks
> (daddr_t
)fp
->ff_blocks
)
3991 bitmapblks
-= fp
->ff_blocks
;
3996 * The allocation bitmap can contain unused bits that are beyond end of
3997 * current volume's allocation blocks. Usually they are supposed to be
3998 * zero'ed out but there can be cases where they might be marked as used.
3999 * After extending the file system, those bits can represent valid
4000 * allocation blocks, so we mark all the bits from the end of current
4001 * volume to end of allocation bitmap as "free".
4003 BlockMarkFreeUnused(vcb
, vcb
->totalBlocks
,
4004 (fp
->ff_blocks
* vcb
->blockSize
* 8) - vcb
->totalBlocks
);
4006 if (bitmapblks
> 0) {
4012 * Get the bitmap's current size (in allocation blocks) so we know
4013 * where to start zero filling once the new space is added. We've
4014 * got to do this before the bitmap is grown.
4016 blkno
= (daddr64_t
)fp
->ff_blocks
;
4019 * Try to grow the allocation file in the normal way, using allocation
4020 * blocks already existing in the file system. This way, we might be
4021 * able to grow the bitmap contiguously, or at least in the metadata
4024 error
= ExtendFileC(vcb
, fp
, bitmapblks
* vcb
->blockSize
, 0,
4025 kEFAllMask
| kEFNoClumpMask
| kEFReserveMask
4026 | kEFMetadataMask
| kEFContigMask
, &bytesAdded
);
4029 usedExtendFileC
= true;
4032 * If the above allocation failed, fall back to allocating the new
4033 * extent of the bitmap from the space we're going to add. Since those
4034 * blocks don't yet belong to the file system, we have to update the
4035 * extent list directly, and manually adjust the file size.
4038 error
= AddFileExtent(vcb
, fp
, vcb
->totalBlocks
, bitmapblks
);
4040 printf("hfs_extendfs: error %d adding extents\n", error
);
4043 fp
->ff_blocks
+= bitmapblks
;
4044 VTOC(vp
)->c_blocks
= fp
->ff_blocks
;
4045 VTOC(vp
)->c_flag
|= C_MODIFIED
;
4049 * Update the allocation file's size to include the newly allocated
4050 * blocks. Note that ExtendFileC doesn't do this, which is why this
4051 * statement is outside the above "if" statement.
4053 fp
->ff_size
+= (u_int64_t
)bitmapblks
* (u_int64_t
)vcb
->blockSize
;
4056 * Zero out the new bitmap blocks.
4061 blkcnt
= bitmapblks
;
4062 while (blkcnt
> 0) {
4063 error
= (int)buf_meta_bread(vp
, blkno
, vcb
->blockSize
, NOCRED
, &bp
);
4070 bzero((char *)buf_dataptr(bp
), vcb
->blockSize
);
4072 error
= (int)buf_bwrite(bp
);
4080 printf("hfs_extendfs: error %d clearing blocks\n", error
);
4084 * Mark the new bitmap space as allocated.
4086 * Note that ExtendFileC will have marked any blocks it allocated, so
4087 * this is only needed if we used AddFileExtent. Also note that this
4088 * has to come *after* the zero filling of new blocks in the case where
4089 * we used AddFileExtent (since the part of the bitmap we're touching
4090 * is in those newly allocated blocks).
4092 if (!usedExtendFileC
) {
4093 error
= BlockMarkAllocated(vcb
, vcb
->totalBlocks
, bitmapblks
);
4095 printf("hfs_extendfs: error %d setting bitmap\n", error
);
4098 vcb
->freeBlocks
-= bitmapblks
;
4102 * Mark the new alternate VH as allocated.
4104 if (vcb
->blockSize
== 512)
4105 error
= BlockMarkAllocated(vcb
, vcb
->totalBlocks
+ addblks
- 2, 2);
4107 error
= BlockMarkAllocated(vcb
, vcb
->totalBlocks
+ addblks
- 1, 1);
4109 printf("hfs_extendfs: error %d setting bitmap (VH)\n", error
);
4113 * Mark the old alternate VH as free.
4115 if (vcb
->blockSize
== 512)
4116 (void) BlockMarkFree(vcb
, vcb
->totalBlocks
- 2, 2);
4118 (void) BlockMarkFree(vcb
, vcb
->totalBlocks
- 1, 1);
4120 * Adjust file system variables for new space.
4122 prev_phys_block_count
= hfsmp
->hfs_logical_block_count
;
4123 prev_alt_sector
= hfsmp
->hfs_alt_id_sector
;
4125 vcb
->totalBlocks
+= addblks
;
4126 vcb
->freeBlocks
+= addblks
;
4127 hfsmp
->hfs_logical_block_count
= newsize
/ sectorsize
;
4128 hfsmp
->hfs_alt_id_sector
= (hfsmp
->hfsPlusIOPosOffset
/ sectorsize
) +
4129 HFS_ALT_SECTOR(sectorsize
, hfsmp
->hfs_logical_block_count
);
4131 error
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
4133 printf("hfs_extendfs: couldn't flush volume headers (%d)", error
);
4135 * Restore to old state.
4137 if (usedExtendFileC
) {
4138 (void) TruncateFileC(vcb
, fp
, oldBitmapSize
, 0, FORK_IS_RSRC(fp
),
4139 FTOC(fp
)->c_fileid
, false);
4141 fp
->ff_blocks
-= bitmapblks
;
4142 fp
->ff_size
-= (u_int64_t
)bitmapblks
* (u_int64_t
)vcb
->blockSize
;
4144 * No need to mark the excess blocks free since those bitmap blocks
4145 * are no longer part of the bitmap. But we do need to undo the
4146 * effect of the "vcb->freeBlocks -= bitmapblks" above.
4148 vcb
->freeBlocks
+= bitmapblks
;
4150 vcb
->totalBlocks
-= addblks
;
4151 vcb
->freeBlocks
-= addblks
;
4152 hfsmp
->hfs_logical_block_count
= prev_phys_block_count
;
4153 hfsmp
->hfs_alt_id_sector
= prev_alt_sector
;
4155 if (vcb
->blockSize
== 512) {
4156 if (BlockMarkAllocated(vcb
, vcb
->totalBlocks
- 2, 2)) {
4157 hfs_mark_volume_inconsistent(hfsmp
);
4160 if (BlockMarkAllocated(vcb
, vcb
->totalBlocks
- 1, 1)) {
4161 hfs_mark_volume_inconsistent(hfsmp
);
4167 * Invalidate the old alternate volume header.
4170 if (prev_alt_sector
) {
4171 if (buf_meta_bread(hfsmp
->hfs_devvp
,
4172 HFS_PHYSBLK_ROUNDDOWN(prev_alt_sector
, hfsmp
->hfs_log_per_phys
),
4173 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
) == 0) {
4174 journal_modify_block_start(hfsmp
->jnl
, bp
);
4176 bzero((char *)buf_dataptr(bp
) + HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
), kMDBSize
);
4178 journal_modify_block_end(hfsmp
->jnl
, bp
, NULL
, NULL
);
4185 * Update the metadata zone size based on current volume size
4187 hfs_metadatazone_init(hfsmp
, false);
4190 * Adjust the size of hfsmp->hfs_attrdata_vp
4192 if (hfsmp
->hfs_attrdata_vp
) {
4193 struct cnode
*attr_cp
;
4194 struct filefork
*attr_fp
;
4196 if (vnode_get(hfsmp
->hfs_attrdata_vp
) == 0) {
4197 attr_cp
= VTOC(hfsmp
->hfs_attrdata_vp
);
4198 attr_fp
= VTOF(hfsmp
->hfs_attrdata_vp
);
4200 attr_cp
->c_blocks
= newblkcnt
;
4201 attr_fp
->ff_blocks
= newblkcnt
;
4202 attr_fp
->ff_extents
[0].blockCount
= newblkcnt
;
4203 attr_fp
->ff_size
= (off_t
) newblkcnt
* hfsmp
->blockSize
;
4204 ubc_setsize(hfsmp
->hfs_attrdata_vp
, attr_fp
->ff_size
);
4205 vnode_put(hfsmp
->hfs_attrdata_vp
);
4210 * Update the R/B Tree if necessary. Since we don't have to drop the systemfile
4211 * locks in the middle of these operations like we do in the truncate case
4212 * where we have to relocate files, we can only update the red-black tree
4213 * if there were actual changes made to the bitmap. Also, we can't really scan the
4214 * new portion of the bitmap before it has been allocated. The BlockMarkAllocated
4215 * routines are smart enough to avoid the r/b tree if the portion they are manipulating is
4216 * not currently controlled by the tree.
4218 * We only update hfsmp->allocLimit if totalBlocks actually increased.
4222 UpdateAllocLimit(hfsmp
, hfsmp
->totalBlocks
);
4225 /* Log successful extending */
4226 printf("hfs_extendfs: extended \"%s\" to %d blocks (was %d blocks)\n",
4227 hfsmp
->vcbVN
, hfsmp
->totalBlocks
, (u_int32_t
)(oldsize
/hfsmp
->blockSize
));
4231 /* Restore allocation fork. */
4232 bcopy(&forkdata
, &fp
->ff_data
, sizeof(forkdata
));
4233 VTOC(vp
)->c_blocks
= fp
->ff_blocks
;
4237 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
4238 hfsmp
->hfs_flags
&= ~HFS_RESIZE_IN_PROGRESS
;
4239 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
4241 hfs_systemfile_unlock(hfsmp
, lockflags
);
4243 if (transaction_begun
) {
4244 hfs_end_transaction(hfsmp
);
4247 return MacToVFSError(error
);
4250 #define HFS_MIN_SIZE (32LL * 1024LL * 1024LL)
4253 * Truncate a file system (while still mounted).
4256 hfs_truncatefs(struct hfsmount
*hfsmp
, u_int64_t newsize
, vfs_context_t context
)
4258 struct buf
*bp
= NULL
;
4260 u_int32_t newblkcnt
;
4261 u_int32_t reclaimblks
= 0;
4263 int transaction_begun
= 0;
4264 Boolean updateFreeBlocks
= false;
4265 Boolean disable_sparse
= false;
4268 lck_mtx_lock(&hfsmp
->hfs_mutex
);
4269 if (hfsmp
->hfs_flags
& HFS_RESIZE_IN_PROGRESS
) {
4270 lck_mtx_unlock(&hfsmp
->hfs_mutex
);
4273 hfsmp
->hfs_flags
|= HFS_RESIZE_IN_PROGRESS
;
4274 hfsmp
->hfs_resize_blocksmoved
= 0;
4275 hfsmp
->hfs_resize_totalblocks
= 0;
4276 hfsmp
->hfs_resize_progress
= 0;
4277 lck_mtx_unlock(&hfsmp
->hfs_mutex
);
4280 * - Journaled HFS Plus volumes only.
4281 * - No embedded volumes.
4283 if ((hfsmp
->jnl
== NULL
) ||
4284 (hfsmp
->hfsPlusIOPosOffset
!= 0)) {
4288 oldsize
= (u_int64_t
)hfsmp
->totalBlocks
* (u_int64_t
)hfsmp
->blockSize
;
4289 newblkcnt
= newsize
/ hfsmp
->blockSize
;
4290 reclaimblks
= hfsmp
->totalBlocks
- newblkcnt
;
4292 if (hfs_resize_debug
) {
4293 printf ("hfs_truncatefs: old: size=%qu, blkcnt=%u, freeblks=%u\n", oldsize
, hfsmp
->totalBlocks
, hfs_freeblks(hfsmp
, 1));
4294 printf ("hfs_truncatefs: new: size=%qu, blkcnt=%u, reclaimblks=%u\n", newsize
, newblkcnt
, reclaimblks
);
4297 /* Make sure new size is valid. */
4298 if ((newsize
< HFS_MIN_SIZE
) ||
4299 (newsize
>= oldsize
) ||
4300 (newsize
% hfsmp
->hfs_logical_block_size
) ||
4301 (newsize
% hfsmp
->hfs_physical_block_size
)) {
4302 printf ("hfs_truncatefs: invalid size (newsize=%qu, oldsize=%qu)\n", newsize
, oldsize
);
4308 * Make sure that the file system has enough free blocks reclaim.
4310 * Before resize, the disk is divided into four zones -
4311 * A. Allocated_Stationary - These are allocated blocks that exist
4312 * before the new end of disk. These blocks will not be
4313 * relocated or modified during resize.
4314 * B. Free_Stationary - These are free blocks that exist before the
4315 * new end of disk. These blocks can be used for any new
4316 * allocations during resize, including allocation for relocating
4317 * data from the area of disk being reclaimed.
4318 * C. Allocated_To-Reclaim - These are allocated blocks that exist
4319 * beyond the new end of disk. These blocks need to be reclaimed
4320 * during resize by allocating equal number of blocks in Free
4321 * Stationary zone and copying the data.
4322 * D. Free_To-Reclaim - These are free blocks that exist beyond the
4323 * new end of disk. Nothing special needs to be done to reclaim
4326 * Total number of blocks on the disk before resize:
4327 * ------------------------------------------------
4328 * Total Blocks = Allocated_Stationary + Free_Stationary +
4329 * Allocated_To-Reclaim + Free_To-Reclaim
4331 * Total number of blocks that need to be reclaimed:
4332 * ------------------------------------------------
4333 * Blocks to Reclaim = Allocated_To-Reclaim + Free_To-Reclaim
4335 * Note that the check below also makes sure that we have enough space
4336 * to relocate data from Allocated_To-Reclaim to Free_Stationary.
4337 * Therefore we do not need to check total number of blocks to relocate
4338 * later in the code.
4340 * The condition below gets converted to:
4342 * Allocated To-Reclaim + Free To-Reclaim >= Free Stationary + Free To-Reclaim
4344 * which is equivalent to:
4346 * Allocated To-Reclaim >= Free Stationary
4348 if (reclaimblks
>= hfs_freeblks(hfsmp
, 1)) {
4349 printf("hfs_truncatefs: insufficient space (need %u blocks; have %u free blocks)\n", reclaimblks
, hfs_freeblks(hfsmp
, 1));
4354 /* Start with a clean journal. */
4355 hfs_journal_flush(hfsmp
, TRUE
);
4357 if (hfs_start_transaction(hfsmp
) != 0) {
4361 transaction_begun
= 1;
4363 /* Take the bitmap lock to update the alloc limit field */
4364 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
4367 * Prevent new allocations from using the part we're trying to truncate.
4369 * NOTE: allocLimit is set to the allocation block number where the new
4370 * alternate volume header will be. That way there will be no files to
4371 * interfere with allocating the new alternate volume header, and no files
4372 * in the allocation blocks beyond (i.e. the blocks we're trying to
4375 * Also shrink the red-black tree if needed.
4377 if (hfsmp
->blockSize
== 512) {
4378 error
= UpdateAllocLimit (hfsmp
, newblkcnt
- 2);
4381 error
= UpdateAllocLimit (hfsmp
, newblkcnt
- 1);
4384 /* Sparse devices use first fit allocation which is not ideal
4385 * for volume resize which requires best fit allocation. If a
4386 * sparse device is being truncated, disable the sparse device
4387 * property temporarily for the duration of resize. Also reset
4388 * the free extent cache so that it is rebuilt as sorted by
4389 * totalBlocks instead of startBlock.
4391 * Note that this will affect all allocations on the volume and
4392 * ideal fix would be just to modify resize-related allocations,
4393 * but it will result in complexity like handling of two free
4394 * extent caches sorted differently, etc. So we stick to this
4397 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
4398 if (hfsmp
->hfs_flags
& HFS_HAS_SPARSE_DEVICE
) {
4399 hfsmp
->hfs_flags
&= ~HFS_HAS_SPARSE_DEVICE
;
4400 ResetVCBFreeExtCache(hfsmp
);
4401 disable_sparse
= true;
4405 * Update the volume free block count to reflect the total number
4406 * of free blocks that will exist after a successful resize.
4407 * Relocation of extents will result in no net change in the total
4408 * free space on the disk. Therefore the code that allocates
4409 * space for new extent and deallocates the old extent explicitly
4410 * prevents updating the volume free block count. It will also
4411 * prevent false disk full error when the number of blocks in
4412 * an extent being relocated is more than the free blocks that
4413 * will exist after the volume is resized.
4415 hfsmp
->freeBlocks
-= reclaimblks
;
4416 updateFreeBlocks
= true;
4417 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
4420 hfs_systemfile_unlock(hfsmp
, lockflags
);
4425 * Update the metadata zone size to match the new volume size,
4426 * and if it too less, metadata zone might be disabled.
4428 hfs_metadatazone_init(hfsmp
, false);
4431 * If some files have blocks at or beyond the location of the
4432 * new alternate volume header, recalculate free blocks and
4433 * reclaim blocks. Otherwise just update free blocks count.
4435 * The current allocLimit is set to the location of new alternate
4436 * volume header, and reclaimblks are the total number of blocks
4437 * that need to be reclaimed. So the check below is really
4438 * ignoring the blocks allocated for old alternate volume header.
4440 if (hfs_isallocated(hfsmp
, hfsmp
->allocLimit
, reclaimblks
)) {
4442 * hfs_reclaimspace will use separate transactions when
4443 * relocating files (so we don't overwhelm the journal).
4445 hfs_end_transaction(hfsmp
);
4446 transaction_begun
= 0;
4448 /* Attempt to reclaim some space. */
4449 error
= hfs_reclaimspace(hfsmp
, hfsmp
->allocLimit
, reclaimblks
, context
);
4451 printf("hfs_truncatefs: couldn't reclaim space on %s (error=%d)\n", hfsmp
->vcbVN
, error
);
4455 if (hfs_start_transaction(hfsmp
) != 0) {
4459 transaction_begun
= 1;
4461 /* Check if we're clear now. */
4462 error
= hfs_isallocated(hfsmp
, hfsmp
->allocLimit
, reclaimblks
);
4464 printf("hfs_truncatefs: didn't reclaim enough space on %s (error=%d)\n", hfsmp
->vcbVN
, error
);
4465 error
= EAGAIN
; /* tell client to try again */
4471 * Note: we take the attributes lock in case we have an attribute data vnode
4472 * which needs to change size.
4474 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
| SFL_EXTENTS
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
4477 * Allocate last 1KB for alternate volume header.
4479 error
= BlockMarkAllocated(hfsmp
, hfsmp
->allocLimit
, (hfsmp
->blockSize
== 512) ? 2 : 1);
4481 printf("hfs_truncatefs: Error %d allocating new alternate volume header\n", error
);
4486 * Mark the old alternate volume header as free.
4487 * We don't bother shrinking allocation bitmap file.
4489 if (hfsmp
->blockSize
== 512)
4490 (void) BlockMarkFree(hfsmp
, hfsmp
->totalBlocks
- 2, 2);
4492 (void) BlockMarkFree(hfsmp
, hfsmp
->totalBlocks
- 1, 1);
4495 * Invalidate the existing alternate volume header.
4497 * Don't include this in a transaction (don't call journal_modify_block)
4498 * since this block will be outside of the truncated file system!
4500 if (hfsmp
->hfs_alt_id_sector
) {
4501 error
= buf_meta_bread(hfsmp
->hfs_devvp
,
4502 HFS_PHYSBLK_ROUNDDOWN(hfsmp
->hfs_alt_id_sector
, hfsmp
->hfs_log_per_phys
),
4503 hfsmp
->hfs_physical_block_size
, NOCRED
, &bp
);
4505 bzero((void*)((char *)buf_dataptr(bp
) + HFS_ALT_OFFSET(hfsmp
->hfs_physical_block_size
)), kMDBSize
);
4506 (void) VNOP_BWRITE(bp
);
4515 /* Log successful shrinking. */
4516 printf("hfs_truncatefs: shrank \"%s\" to %d blocks (was %d blocks)\n",
4517 hfsmp
->vcbVN
, newblkcnt
, hfsmp
->totalBlocks
);
4520 * Adjust file system variables and flush them to disk.
4522 hfsmp
->totalBlocks
= newblkcnt
;
4523 hfsmp
->hfs_logical_block_count
= newsize
/ hfsmp
->hfs_logical_block_size
;
4524 hfsmp
->hfs_alt_id_sector
= HFS_ALT_SECTOR(hfsmp
->hfs_logical_block_size
, hfsmp
->hfs_logical_block_count
);
4525 MarkVCBDirty(hfsmp
);
4526 error
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
4528 panic("hfs_truncatefs: unexpected error flushing volume header (%d)\n", error
);
4531 * Adjust the size of hfsmp->hfs_attrdata_vp
4533 if (hfsmp
->hfs_attrdata_vp
) {
4535 struct filefork
*fp
;
4537 if (vnode_get(hfsmp
->hfs_attrdata_vp
) == 0) {
4538 cp
= VTOC(hfsmp
->hfs_attrdata_vp
);
4539 fp
= VTOF(hfsmp
->hfs_attrdata_vp
);
4541 cp
->c_blocks
= newblkcnt
;
4542 fp
->ff_blocks
= newblkcnt
;
4543 fp
->ff_extents
[0].blockCount
= newblkcnt
;
4544 fp
->ff_size
= (off_t
) newblkcnt
* hfsmp
->blockSize
;
4545 ubc_setsize(hfsmp
->hfs_attrdata_vp
, fp
->ff_size
);
4546 vnode_put(hfsmp
->hfs_attrdata_vp
);
4552 * Update the allocLimit to acknowledge the last one or two blocks now.
4553 * Add it to the tree as well if necessary.
4555 UpdateAllocLimit (hfsmp
, hfsmp
->totalBlocks
);
4557 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
4558 if (disable_sparse
== true) {
4559 /* Now that resize is completed, set the volume to be sparse
4560 * device again so that all further allocations will be first
4561 * fit instead of best fit. Reset free extent cache so that
4564 hfsmp
->hfs_flags
|= HFS_HAS_SPARSE_DEVICE
;
4565 ResetVCBFreeExtCache(hfsmp
);
4568 if (error
&& (updateFreeBlocks
== true)) {
4569 hfsmp
->freeBlocks
+= reclaimblks
;
4572 if (hfsmp
->nextAllocation
>= hfsmp
->allocLimit
) {
4573 hfsmp
->nextAllocation
= hfsmp
->hfs_metazone_end
+ 1;
4575 hfsmp
->hfs_flags
&= ~HFS_RESIZE_IN_PROGRESS
;
4576 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
4578 /* On error, reset the metadata zone for original volume size */
4579 if (error
&& (updateFreeBlocks
== true)) {
4580 hfs_metadatazone_init(hfsmp
, false);
4584 hfs_systemfile_unlock(hfsmp
, lockflags
);
4586 if (transaction_begun
) {
4587 hfs_end_transaction(hfsmp
);
4588 hfs_journal_flush(hfsmp
, FALSE
);
4589 /* Just to be sure, sync all data to the disk */
4590 (void) VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, context
);
4593 return MacToVFSError(error
);
4598 * Invalidate the physical block numbers associated with buffer cache blocks
4599 * in the given extent of the given vnode.
4601 struct hfs_inval_blk_no
{
4602 daddr64_t sectorStart
;
4603 daddr64_t sectorCount
;
4606 hfs_invalidate_block_numbers_callback(buf_t bp
, void *args_in
)
4609 struct hfs_inval_blk_no
*args
;
4611 blkno
= buf_blkno(bp
);
4614 if (blkno
>= args
->sectorStart
&& blkno
< args
->sectorStart
+args
->sectorCount
)
4615 buf_setblkno(bp
, buf_lblkno(bp
));
4617 return BUF_RETURNED
;
4620 hfs_invalidate_sectors(struct vnode
*vp
, daddr64_t sectorStart
, daddr64_t sectorCount
)
4622 struct hfs_inval_blk_no args
;
4623 args
.sectorStart
= sectorStart
;
4624 args
.sectorCount
= sectorCount
;
4626 buf_iterate(vp
, hfs_invalidate_block_numbers_callback
, BUF_SCAN_DIRTY
|BUF_SCAN_CLEAN
, &args
);
4631 * Copy the contents of an extent to a new location. Also invalidates the
4632 * physical block number of any buffer cache block in the copied extent
4633 * (so that if the block is written, it will go through VNOP_BLOCKMAP to
4634 * determine the new physical block number).
4638 struct hfsmount
*hfsmp
,
4639 struct vnode
*vp
, /* The file whose extent is being copied. */
4640 u_int32_t oldStart
, /* The start of the source extent. */
4641 u_int32_t newStart
, /* The start of the destination extent. */
4642 u_int32_t blockCount
, /* The number of allocation blocks to copy. */
4643 vfs_context_t context
)
4647 void *buffer
= NULL
;
4648 struct vfsioattr ioattr
;
4652 u_int32_t ioSizeSectors
; /* Device sectors in this I/O */
4653 daddr64_t srcSector
, destSector
;
4654 u_int32_t sectorsPerBlock
= hfsmp
->blockSize
/ hfsmp
->hfs_logical_block_size
;
4660 * Sanity check that we have locked the vnode of the file we're copying.
4662 * But since hfs_systemfile_lock() doesn't actually take the lock on
4663 * the allocation file if a journal is active, ignore the check if the
4664 * file being copied is the allocation file.
4666 struct cnode
*cp
= VTOC(vp
);
4667 if (cp
!= hfsmp
->hfs_allocation_cp
&& cp
->c_lockowner
!= current_thread())
4668 panic("hfs_copy_extent: vp=%p (cp=%p) not owned?\n", vp
, cp
);
4671 /* Prepare the CP blob and get it ready for use */
4672 if (!vnode_issystem (vp
) && vnode_isreg(vp
) &&
4673 cp_fs_protected (hfsmp
->hfs_mp
)) {
4675 cp_err
= cp_handle_relocate (cp
);
4678 * can't copy the file because we couldn't set up keys.
4690 * Determine the I/O size to use
4692 * NOTE: Many external drives will result in an ioSize of 128KB.
4693 * TODO: Should we use a larger buffer, doing several consecutive
4694 * reads, then several consecutive writes?
4696 vfs_ioattr(hfsmp
->hfs_mp
, &ioattr
);
4697 bufferSize
= MIN(ioattr
.io_maxreadcnt
, ioattr
.io_maxwritecnt
);
4698 if (kmem_alloc(kernel_map
, (vm_offset_t
*) &buffer
, bufferSize
))
4701 /* Get a buffer for doing the I/O */
4702 bp
= buf_alloc(hfsmp
->hfs_devvp
);
4703 buf_setdataptr(bp
, (uintptr_t)buffer
);
4705 resid
= (off_t
) blockCount
* (off_t
) hfsmp
->blockSize
;
4706 srcSector
= (daddr64_t
) oldStart
* hfsmp
->blockSize
/ hfsmp
->hfs_logical_block_size
;
4707 destSector
= (daddr64_t
) newStart
* hfsmp
->blockSize
/ hfsmp
->hfs_logical_block_size
;
4709 ioSize
= MIN(bufferSize
, (size_t) resid
);
4710 ioSizeSectors
= ioSize
/ hfsmp
->hfs_logical_block_size
;
4712 /* Prepare the buffer for reading */
4713 buf_reset(bp
, B_READ
);
4714 buf_setsize(bp
, ioSize
);
4715 buf_setcount(bp
, ioSize
);
4716 buf_setblkno(bp
, srcSector
);
4717 buf_setlblkno(bp
, srcSector
);
4719 /* Attach the CP to the buffer */
4722 buf_setcpaddr (bp
, cp
->c_cpentry
);
4727 err
= VNOP_STRATEGY(bp
);
4729 err
= buf_biowait(bp
);
4731 printf("hfs_copy_extent: Error %d from VNOP_STRATEGY (read)\n", err
);
4735 /* Prepare the buffer for writing */
4736 buf_reset(bp
, B_WRITE
);
4737 buf_setsize(bp
, ioSize
);
4738 buf_setcount(bp
, ioSize
);
4739 buf_setblkno(bp
, destSector
);
4740 buf_setlblkno(bp
, destSector
);
4741 if (vnode_issystem(vp
) && journal_uses_fua(hfsmp
->jnl
))
4745 /* Attach the CP to the buffer */
4747 buf_setcpaddr (bp
, cp
->c_cpentry
);
4752 vnode_startwrite(hfsmp
->hfs_devvp
);
4753 err
= VNOP_STRATEGY(bp
);
4755 err
= buf_biowait(bp
);
4757 printf("hfs_copy_extent: Error %d from VNOP_STRATEGY (write)\n", err
);
4762 srcSector
+= ioSizeSectors
;
4763 destSector
+= ioSizeSectors
;
4768 kmem_free(kernel_map
, (vm_offset_t
)buffer
, bufferSize
);
4770 /* Make sure all writes have been flushed to disk. */
4771 if (vnode_issystem(vp
) && !journal_uses_fua(hfsmp
->jnl
)) {
4772 err
= VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, context
);
4774 printf("hfs_copy_extent: DKIOCSYNCHRONIZECACHE failed (%d)\n", err
);
4775 err
= 0; /* Don't fail the copy. */
4780 hfs_invalidate_sectors(vp
, (daddr64_t
)oldStart
*sectorsPerBlock
, (daddr64_t
)blockCount
*sectorsPerBlock
);
4786 /* Structure to store state of reclaiming extents from a
4787 * given file. hfs_reclaim_file()/hfs_reclaim_xattr()
4788 * initializes the values in this structure which are then
4789 * used by code that reclaims and splits the extents.
4791 struct hfs_reclaim_extent_info
{
4795 u_int8_t is_dirlink
; /* Extent belongs to directory hard link */
4796 u_int8_t is_sysfile
; /* Extent belongs to system file */
4797 u_int8_t is_xattr
; /* Extent belongs to extent-based xattr */
4798 u_int8_t extent_index
;
4799 int lockflags
; /* Locks that reclaim and split code should grab before modifying the extent record */
4800 u_int32_t blocks_relocated
; /* Total blocks relocated for this file till now */
4801 u_int32_t recStartBlock
; /* File allocation block number (FABN) for current extent record */
4802 u_int32_t cur_blockCount
; /* Number of allocation blocks that have been checked for reclaim */
4803 struct filefork
*catalog_fp
; /* If non-NULL, extent is from catalog record */
4805 HFSPlusExtentRecord overflow
;/* Extent record from overflow extents btree */
4806 HFSPlusAttrRecord xattr
; /* Attribute record for large EAs */
4808 HFSPlusExtentDescriptor
*extents
; /* Pointer to current extent record being processed.
4809 * For catalog extent record, points to the correct
4810 * extent information in filefork. For overflow extent
4811 * record, or xattr record, points to extent record
4812 * in the structure above
4814 struct cat_desc
*dirlink_desc
;
4815 struct cat_attr
*dirlink_attr
;
4816 struct filefork
*dirlink_fork
; /* For directory hard links, fp points actually to this */
4817 struct BTreeIterator
*iterator
; /* Shared read/write iterator, hfs_reclaim_file/xattr()
4818 * use it for reading and hfs_reclaim_extent()/hfs_split_extent()
4819 * use it for writing updated extent record
4821 struct FSBufferDescriptor btdata
; /* Shared btdata for reading/writing extent record, same as iterator above */
4822 u_int16_t recordlen
;
4823 int overflow_count
; /* For debugging, counter for overflow extent record */
4824 FCB
*fcb
; /* Pointer to the current btree being traversed */
4828 * Split the current extent into two extents, with first extent
4829 * to contain given number of allocation blocks. Splitting of
4830 * extent creates one new extent entry which can result in
4831 * shifting of many entries through all the extent records of a
4832 * file, and/or creating a new extent record in the overflow
4836 * The diagram below represents two consecutive extent records,
4837 * for simplicity, lets call them record X and X+1 respectively.
4838 * Interesting extent entries have been denoted by letters.
4839 * If the letter is unchanged before and after split, it means
4840 * that the extent entry was not modified during the split.
4841 * A '.' means that the entry remains unchanged after the split
4842 * and is not relevant for our example. A '0' means that the
4843 * extent entry is empty.
4845 * If there isn't sufficient contiguous free space to relocate
4846 * an extent (extent "C" below), we will have to break the one
4847 * extent into multiple smaller extents, and relocate each of
4848 * the smaller extents individually. The way we do this is by
4849 * finding the largest contiguous free space that is currently
4850 * available (N allocation blocks), and then convert extent "C"
4851 * into two extents, C1 and C2, that occupy exactly the same
4852 * allocation blocks as extent C. Extent C1 is the first
4853 * N allocation blocks of extent C, and extent C2 is the remainder
4854 * of extent C. Then we can relocate extent C1 since we know
4855 * we have enough contiguous free space to relocate it in its
4856 * entirety. We then repeat the process starting with extent C2.
4858 * In record X, only the entries following entry C are shifted, and
4859 * the original entry C is replaced with two entries C1 and C2 which
4860 * are actually two extent entries for contiguous allocation blocks.
4862 * Note that the entry E from record X is shifted into record X+1 as
4863 * the new first entry. Since the first entry of record X+1 is updated,
4864 * the FABN will also get updated with the blockCount of entry E.
4865 * This also results in shifting of all extent entries in record X+1.
4866 * Note that the number of empty entries after the split has been
4867 * changed from 3 to 2.
4870 * record X record X+1
4871 * ---------------------===--------- ---------------------------------
4872 * | A | . | . | . | B | C | D | E | | F | . | . | . | G | 0 | 0 | 0 |
4873 * ---------------------===--------- ---------------------------------
4876 * ---------------------=======----- ---------------------------------
4877 * | A | . | . | . | B | C1| C2| D | | E | F | . | . | . | G | 0 | 0 |
4878 * ---------------------=======----- ---------------------------------
4880 * C1.startBlock = C.startBlock
4883 * C2.startBlock = C.startBlock + N
4884 * C2.blockCount = C.blockCount - N
4886 * FABN = old FABN - E.blockCount
4889 * extent_info - This is the structure that contains state about
4890 * the current file, extent, and extent record that
4891 * is being relocated. This structure is shared
4892 * among code that traverses through all the extents
4893 * of the file, code that relocates extents, and
4894 * code that splits the extent.
4896 * Zero on success, non-zero on failure.
4899 hfs_split_extent(struct hfs_reclaim_extent_info
*extent_info
, uint32_t newBlockCount
)
4902 int index
= extent_info
->extent_index
;
4904 HFSPlusExtentDescriptor shift_extent
;
4905 HFSPlusExtentDescriptor last_extent
;
4906 HFSPlusExtentDescriptor
*extents
; /* Pointer to current extent record being manipulated */
4907 HFSPlusExtentRecord
*extents_rec
= NULL
;
4908 HFSPlusExtentKey
*extents_key
= NULL
;
4909 HFSPlusAttrRecord
*xattr_rec
= NULL
;
4910 HFSPlusAttrKey
*xattr_key
= NULL
;
4911 struct BTreeIterator iterator
;
4912 struct FSBufferDescriptor btdata
;
4914 uint32_t read_recStartBlock
; /* Starting allocation block number to read old extent record */
4915 uint32_t write_recStartBlock
; /* Starting allocation block number to insert newly updated extent record */
4916 Boolean create_record
= false;
4919 is_xattr
= extent_info
->is_xattr
;
4920 extents
= extent_info
->extents
;
4922 if (hfs_resize_debug
) {
4923 printf ("hfs_split_extent: Split record:%u recStartBlock=%u %u:(%u,%u) for %u blocks\n", extent_info
->overflow_count
, extent_info
->recStartBlock
, index
, extents
[index
].startBlock
, extents
[index
].blockCount
, newBlockCount
);
4926 /* Determine the starting allocation block number for the following
4927 * overflow extent record, if any, before the current record
4930 read_recStartBlock
= extent_info
->recStartBlock
;
4931 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
4932 if (extents
[i
].blockCount
== 0) {
4935 read_recStartBlock
+= extents
[i
].blockCount
;
4938 /* Shift and split */
4939 if (index
== kHFSPlusExtentDensity
-1) {
4940 /* The new extent created after split will go into following overflow extent record */
4941 shift_extent
.startBlock
= extents
[index
].startBlock
+ newBlockCount
;
4942 shift_extent
.blockCount
= extents
[index
].blockCount
- newBlockCount
;
4944 /* Last extent in the record will be split, so nothing to shift */
4946 /* Splitting of extents can result in at most of one
4947 * extent entry to be shifted into following overflow extent
4948 * record. So, store the last extent entry for later.
4950 shift_extent
= extents
[kHFSPlusExtentDensity
-1];
4952 /* Start shifting extent information from the end of the extent
4953 * record to the index where we want to insert the new extent.
4954 * Note that kHFSPlusExtentDensity-1 is already saved above, and
4955 * does not need to be shifted. The extent entry that is being
4956 * split does not get shifted.
4958 for (i
= kHFSPlusExtentDensity
-2; i
> index
; i
--) {
4959 if (hfs_resize_debug
) {
4960 if (extents
[i
].blockCount
) {
4961 printf ("hfs_split_extent: Shift %u:(%u,%u) to %u:(%u,%u)\n", i
, extents
[i
].startBlock
, extents
[i
].blockCount
, i
+1, extents
[i
].startBlock
, extents
[i
].blockCount
);
4964 extents
[i
+1] = extents
[i
];
4968 if (index
== kHFSPlusExtentDensity
-1) {
4969 /* The second half of the extent being split will be the overflow
4970 * entry that will go into following overflow extent record. The
4971 * value has been stored in 'shift_extent' above, so there is
4972 * nothing to be done here.
4975 /* Update the values in the second half of the extent being split
4976 * before updating the first half of the split. Note that the
4977 * extent to split or first half of the split is at index 'index'
4978 * and a new extent or second half of the split will be inserted at
4979 * 'index+1' or into following overflow extent record.
4981 extents
[index
+1].startBlock
= extents
[index
].startBlock
+ newBlockCount
;
4982 extents
[index
+1].blockCount
= extents
[index
].blockCount
- newBlockCount
;
4984 /* Update the extent being split, only the block count will change */
4985 extents
[index
].blockCount
= newBlockCount
;
4987 if (hfs_resize_debug
) {
4988 printf ("hfs_split_extent: Split %u:(%u,%u) and ", index
, extents
[index
].startBlock
, extents
[index
].blockCount
);
4989 if (index
!= kHFSPlusExtentDensity
-1) {
4990 printf ("%u:(%u,%u)\n", index
+1, extents
[index
+1].startBlock
, extents
[index
+1].blockCount
);
4992 printf ("overflow:(%u,%u)\n", shift_extent
.startBlock
, shift_extent
.blockCount
);
4996 /* If the newly split extent is for large EAs or in overflow extent
4997 * record, so update it directly in the btree using the iterator
4998 * information from the shared extent_info structure
5000 if (extent_info
->catalog_fp
== NULL
) {
5001 error
= BTReplaceRecord(extent_info
->fcb
, extent_info
->iterator
,
5002 &(extent_info
->btdata
), extent_info
->recordlen
);
5004 printf ("hfs_split_extent: fileID=%u BTReplaceRecord returned error=%d\n", extent_info
->fileID
, error
);
5009 /* No extent entry to be shifted into another extent overflow record */
5010 if (shift_extent
.blockCount
== 0) {
5011 if (hfs_resize_debug
) {
5012 printf ("hfs_split_extent: No extent entry to be shifted into overflow records\n");
5018 /* The overflow extent entry has to be shifted into an extent
5019 * overflow record. This would mean that we have to shift
5020 * extent entries from all overflow records by one. We will
5021 * start iteration from the first record to the last record,
5022 * and shift the extent entry from one record to another.
5023 * We might have to create a new record for the last extent
5024 * entry for the file.
5027 /* Initialize iterator to search the next record */
5028 bzero(&iterator
, sizeof(iterator
));
5030 /* Copy the key from the iterator that was to update the modified attribute record. */
5031 xattr_key
= (HFSPlusAttrKey
*)&(iterator
.key
);
5032 bcopy((HFSPlusAttrKey
*)&(extent_info
->iterator
->key
), xattr_key
, sizeof(HFSPlusAttrKey
));
5033 /* Note: xattr_key->startBlock will be initialized later in the iteration loop */
5035 MALLOC(xattr_rec
, HFSPlusAttrRecord
*,
5036 sizeof(HFSPlusAttrRecord
), M_TEMP
, M_WAITOK
);
5037 if (xattr_rec
== NULL
) {
5041 btdata
.bufferAddress
= xattr_rec
;
5042 btdata
.itemSize
= sizeof(HFSPlusAttrRecord
);
5043 btdata
.itemCount
= 1;
5044 extents
= xattr_rec
->overflowExtents
.extents
;
5046 extents_key
= (HFSPlusExtentKey
*) &(iterator
.key
);
5047 extents_key
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
5048 extents_key
->forkType
= extent_info
->forkType
;
5049 extents_key
->fileID
= extent_info
->fileID
;
5050 /* Note: extents_key->startBlock will be initialized later in the iteration loop */
5052 MALLOC(extents_rec
, HFSPlusExtentRecord
*,
5053 sizeof(HFSPlusExtentRecord
), M_TEMP
, M_WAITOK
);
5054 if (extents_rec
== NULL
) {
5058 btdata
.bufferAddress
= extents_rec
;
5059 btdata
.itemSize
= sizeof(HFSPlusExtentRecord
);
5060 btdata
.itemCount
= 1;
5061 extents
= extents_rec
[0];
5064 /* An extent entry still needs to be shifted into following overflow
5065 * extent record. This will result in the starting allocation block
5066 * number of the extent record being changed which is part of the key
5067 * for the extent record. Since the extent record key is changing,
5068 * the record can not be updated, instead has to be deleted and
5071 while (shift_extent
.blockCount
) {
5072 if (hfs_resize_debug
) {
5073 printf ("hfs_split_extent: Will shift (%u,%u) into record with startBlock=%u\n", shift_extent
.startBlock
, shift_extent
.blockCount
, read_recStartBlock
);
5076 /* Search if there is any existing overflow extent record.
5077 * For this, the logical start block number in the key is
5078 * the value calculated based on the logical start block
5079 * number of the current extent record and the total number
5080 * of blocks existing in the current extent record.
5083 xattr_key
->startBlock
= read_recStartBlock
;
5085 extents_key
->startBlock
= read_recStartBlock
;
5087 error
= BTSearchRecord(extent_info
->fcb
, &iterator
, &btdata
, &reclen
, &iterator
);
5089 if (error
!= btNotFound
) {
5090 printf ("hfs_split_extent: fileID=%u startBlock=%u BTSearchRecord error=%d\n", extent_info
->fileID
, read_recStartBlock
, error
);
5093 create_record
= true;
5096 /* The extra extent entry from the previous record is being inserted
5097 * as the first entry in the current extent record. This will change
5098 * the file allocation block number (FABN) of the current extent
5099 * record, which is the startBlock value from the extent record key.
5100 * Since one extra entry is being inserted in the record, the new
5101 * FABN for the record will less than old FABN by the number of blocks
5102 * in the new extent entry being inserted at the start. We have to
5103 * do this before we update read_recStartBlock to point at the
5104 * startBlock of the following record.
5106 write_recStartBlock
= read_recStartBlock
- shift_extent
.blockCount
;
5107 if (hfs_resize_debug
) {
5108 if (create_record
) {
5109 printf ("hfs_split_extent: No records found for startBlock=%u, will create new with startBlock=%u\n", read_recStartBlock
, write_recStartBlock
);
5113 /* Now update the read_recStartBlock to account for total number
5114 * of blocks in this extent record. It will now point to the
5115 * starting allocation block number for the next extent record.
5117 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
5118 if (extents
[i
].blockCount
== 0) {
5121 read_recStartBlock
+= extents
[i
].blockCount
;
5124 if (create_record
== true) {
5125 /* Initialize new record content with only one extent entry */
5126 bzero(extents
, sizeof(HFSPlusExtentRecord
));
5127 /* The new record will contain only one extent entry */
5128 extents
[0] = shift_extent
;
5129 /* There are no more overflow extents to be shifted */
5130 shift_extent
.startBlock
= shift_extent
.blockCount
= 0;
5133 xattr_rec
->recordType
= kHFSPlusAttrExtents
;
5134 xattr_rec
->overflowExtents
.reserved
= 0;
5135 reclen
= sizeof(HFSPlusAttrExtents
);
5137 reclen
= sizeof(HFSPlusExtentRecord
);
5140 /* The overflow extent entry from previous record will be
5141 * the first entry in this extent record. If the last
5142 * extent entry in this record is valid, it will be shifted
5143 * into the following extent record as its first entry. So
5144 * save the last entry before shifting entries in current
5147 last_extent
= extents
[kHFSPlusExtentDensity
-1];
5149 /* Shift all entries by one index towards the end */
5150 for (i
= kHFSPlusExtentDensity
-2; i
>= 0; i
--) {
5151 extents
[i
+1] = extents
[i
];
5154 /* Overflow extent entry saved from previous record
5155 * is now the first entry in the current record.
5157 extents
[0] = shift_extent
;
5159 if (hfs_resize_debug
) {
5160 printf ("hfs_split_extent: Shift overflow=(%u,%u) to record with updated startBlock=%u\n", shift_extent
.startBlock
, shift_extent
.blockCount
, write_recStartBlock
);
5163 /* The last entry from current record will be the
5164 * overflow entry which will be the first entry for
5165 * the following extent record.
5167 shift_extent
= last_extent
;
5169 /* Since the key->startBlock is being changed for this record,
5170 * it should be deleted and inserted with the new key.
5172 error
= BTDeleteRecord(extent_info
->fcb
, &iterator
);
5174 printf ("hfs_split_extent: fileID=%u startBlock=%u BTDeleteRecord error=%d\n", extent_info
->fileID
, read_recStartBlock
, error
);
5177 if (hfs_resize_debug
) {
5178 printf ("hfs_split_extent: Deleted record with startBlock=%u\n", (is_xattr
? xattr_key
->startBlock
: extents_key
->startBlock
));
5182 /* Insert the newly created or modified extent record */
5183 bzero(&iterator
.hint
, sizeof(iterator
.hint
));
5185 xattr_key
->startBlock
= write_recStartBlock
;
5187 extents_key
->startBlock
= write_recStartBlock
;
5189 error
= BTInsertRecord(extent_info
->fcb
, &iterator
, &btdata
, reclen
);
5191 printf ("hfs_split_extent: fileID=%u, startBlock=%u BTInsertRecord error=%d\n", extent_info
->fileID
, write_recStartBlock
, error
);
5194 if (hfs_resize_debug
) {
5195 printf ("hfs_split_extent: Inserted extent record with startBlock=%u\n", write_recStartBlock
);
5198 BTFlushPath(extent_info
->fcb
);
5201 FREE (extents_rec
, M_TEMP
);
5204 FREE (xattr_rec
, M_TEMP
);
5211 * Relocate an extent if it lies beyond the expected end of volume.
5213 * This function is called for every extent of the file being relocated.
5214 * It allocates space for relocation, copies the data, deallocates
5215 * the old extent, and update corresponding on-disk extent. If the function
5216 * does not find contiguous space to relocate an extent, it splits the
5217 * extent in smaller size to be able to relocate it out of the area of
5218 * disk being reclaimed. As an optimization, if an extent lies partially
5219 * in the area of the disk being reclaimed, it is split so that we only
5220 * have to relocate the area that was overlapping with the area of disk
5223 * Note that every extent is relocated in its own transaction so that
5224 * they do not overwhelm the journal. This function handles the extent
5225 * record that exists in the catalog record, extent record from overflow
5226 * extents btree, and extents for large EAs.
5229 * extent_info - This is the structure that contains state about
5230 * the current file, extent, and extent record that
5231 * is being relocated. This structure is shared
5232 * among code that traverses through all the extents
5233 * of the file, code that relocates extents, and
5234 * code that splits the extent.
5237 hfs_reclaim_extent(struct hfsmount
*hfsmp
, const u_long allocLimit
, struct hfs_reclaim_extent_info
*extent_info
, vfs_context_t context
)
5242 u_int32_t oldStartBlock
;
5243 u_int32_t oldBlockCount
;
5244 u_int32_t newStartBlock
;
5245 u_int32_t newBlockCount
;
5246 u_int32_t alloc_flags
;
5247 int blocks_allocated
= false;
5249 index
= extent_info
->extent_index
;
5250 cp
= VTOC(extent_info
->vp
);
5252 oldStartBlock
= extent_info
->extents
[index
].startBlock
;
5253 oldBlockCount
= extent_info
->extents
[index
].blockCount
;
5255 if (0 && hfs_resize_debug
) {
5256 printf ("hfs_reclaim_extent: Examine record:%u recStartBlock=%u, %u:(%u,%u)\n", extent_info
->overflow_count
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
);
5259 /* Check if the current extent lies completely within allocLimit */
5260 if ((oldStartBlock
+ oldBlockCount
) <= allocLimit
) {
5261 extent_info
->cur_blockCount
+= oldBlockCount
;
5265 /* Every extent should be relocated in its own transaction
5266 * to make sure that we don't overflow the journal buffer.
5268 error
= hfs_start_transaction(hfsmp
);
5272 extent_info
->lockflags
= hfs_systemfile_lock(hfsmp
, extent_info
->lockflags
, HFS_EXCLUSIVE_LOCK
);
5274 /* Check if the extent lies partially in the area to reclaim,
5275 * i.e. it starts before allocLimit and ends beyond allocLimit.
5276 * We have already skipped extents that lie completely within
5277 * allocLimit in the check above, so we only check for the
5278 * startBlock. If it lies partially, split it so that we
5279 * only relocate part of the extent.
5281 if (oldStartBlock
< allocLimit
) {
5282 newBlockCount
= allocLimit
- oldStartBlock
;
5283 error
= hfs_split_extent(extent_info
, newBlockCount
);
5285 /* After successful split, the current extent does not
5286 * need relocation, so just return back.
5290 /* Ignore error and try relocating the entire extent instead */
5293 alloc_flags
= HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_SKIPFREEBLKS
;
5294 if (extent_info
->is_sysfile
) {
5295 alloc_flags
|= HFS_ALLOC_METAZONE
;
5298 error
= BlockAllocate(hfsmp
, 1, oldBlockCount
, oldBlockCount
, alloc_flags
,
5299 &newStartBlock
, &newBlockCount
);
5300 if ((extent_info
->is_sysfile
== false) &&
5301 ((error
== dskFulErr
) || (error
== ENOSPC
))) {
5302 /* For non-system files, try reallocating space in metadata zone */
5303 alloc_flags
|= HFS_ALLOC_METAZONE
;
5304 error
= BlockAllocate(hfsmp
, 1, oldBlockCount
, oldBlockCount
,
5305 alloc_flags
, &newStartBlock
, &newBlockCount
);
5307 if ((error
== dskFulErr
) || (error
== ENOSPC
)) {
5308 /* We did not find desired contiguous space for this extent.
5309 * So try to allocate the maximum contiguous space available.
5311 alloc_flags
&= ~HFS_ALLOC_FORCECONTIG
;
5313 error
= BlockAllocate(hfsmp
, 1, oldBlockCount
, oldBlockCount
,
5314 alloc_flags
, &newStartBlock
, &newBlockCount
);
5316 printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) BlockAllocate error=%d\n", extent_info
->fileID
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
, error
);
5319 blocks_allocated
= true;
5321 error
= hfs_split_extent(extent_info
, newBlockCount
);
5323 printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) split error=%d\n", extent_info
->fileID
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
, error
);
5326 oldBlockCount
= newBlockCount
;
5329 printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) contig BlockAllocate error=%d\n", extent_info
->fileID
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
, error
);
5332 blocks_allocated
= true;
5334 /* Copy data from old location to new location */
5335 error
= hfs_copy_extent(hfsmp
, extent_info
->vp
, oldStartBlock
,
5336 newStartBlock
, newBlockCount
, context
);
5338 printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u)=>(%u,%u) hfs_copy_extent error=%d\n", extent_info
->fileID
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
, newStartBlock
, newBlockCount
, error
);
5342 /* Update the extent record with the new start block information */
5343 extent_info
->extents
[index
].startBlock
= newStartBlock
;
5345 /* Sync the content back to the disk */
5346 if (extent_info
->catalog_fp
) {
5347 /* Update the extents in catalog record */
5348 if (extent_info
->is_dirlink
) {
5349 error
= cat_update_dirlink(hfsmp
, extent_info
->forkType
,
5350 extent_info
->dirlink_desc
, extent_info
->dirlink_attr
,
5351 &(extent_info
->dirlink_fork
->ff_data
));
5353 cp
->c_flag
|= C_MODIFIED
;
5354 /* If this is a system file, sync volume headers on disk */
5355 if (extent_info
->is_sysfile
) {
5356 error
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
5360 /* Replace record for extents overflow or extents-based xattrs */
5361 error
= BTReplaceRecord(extent_info
->fcb
, extent_info
->iterator
,
5362 &(extent_info
->btdata
), extent_info
->recordlen
);
5365 printf ("hfs_reclaim_extent: fileID=%u, update record error=%u\n", extent_info
->fileID
, error
);
5369 /* Deallocate the old extent */
5370 error
= BlockDeallocate(hfsmp
, oldStartBlock
, oldBlockCount
, HFS_ALLOC_SKIPFREEBLKS
);
5372 printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) BlockDeallocate error=%d\n", extent_info
->fileID
, extent_info
->recStartBlock
, index
, oldStartBlock
, oldBlockCount
, error
);
5375 extent_info
->blocks_relocated
+= newBlockCount
;
5377 if (hfs_resize_debug
) {
5378 printf ("hfs_reclaim_extent: Relocated record:%u %u:(%u,%u) to (%u,%u)\n", extent_info
->overflow_count
, index
, oldStartBlock
, oldBlockCount
, newStartBlock
, newBlockCount
);
5383 if (blocks_allocated
== true) {
5384 BlockDeallocate(hfsmp
, newStartBlock
, newBlockCount
, HFS_ALLOC_SKIPFREEBLKS
);
5387 /* On success, increment the total allocation blocks processed */
5388 extent_info
->cur_blockCount
+= newBlockCount
;
5391 hfs_systemfile_unlock(hfsmp
, extent_info
->lockflags
);
5393 /* For a non-system file, if an extent entry from catalog record
5394 * was modified, sync the in-memory changes to the catalog record
5395 * on disk before ending the transaction.
5398 (extent_info
->overflow_count
< kHFSPlusExtentDensity
) &&
5399 (extent_info
->is_sysfile
== false)) {
5400 (void) hfs_update(extent_info
->vp
, MNT_WAIT
);
5403 hfs_end_transaction(hfsmp
);
5408 /* Report intermediate progress during volume resize */
5410 hfs_truncatefs_progress(struct hfsmount
*hfsmp
)
5412 u_int32_t cur_progress
;
5414 hfs_resize_progress(hfsmp
, &cur_progress
);
5415 if (cur_progress
> (hfsmp
->hfs_resize_progress
+ 9)) {
5416 printf("hfs_truncatefs: %d%% done...\n", cur_progress
);
5417 hfsmp
->hfs_resize_progress
= cur_progress
;
5423 * Reclaim space at the end of a volume for given file and forktype.
5425 * This routine attempts to move any extent which contains allocation blocks
5426 * at or after "allocLimit." A separate transaction is used for every extent
5427 * that needs to be moved. If there is not contiguous space available for
5428 * moving an extent, it can be split into smaller extents. The contents of
5429 * any moved extents are read and written via the volume's device vnode --
5430 * NOT via "vp." During the move, moved blocks which are part of a transaction
5431 * have their physical block numbers invalidated so they will eventually be
5432 * written to their new locations.
5434 * This function is also called for directory hard links. Directory hard links
5435 * are regular files with no data fork and resource fork that contains alias
5436 * information for backward compatibility with pre-Leopard systems. However
5437 * non-Mac OS X implementation can add/modify data fork or resource fork
5438 * information to directory hard links, so we check, and if required, relocate
5439 * both data fork and resource fork.
5442 * hfsmp The volume being resized.
5443 * vp The vnode for the system file.
5444 * fileID ID of the catalog record that needs to be relocated
5445 * forktype The type of fork that needs relocated,
5446 * kHFSResourceForkType for resource fork,
5447 * kHFSDataForkType for data fork
5448 * allocLimit Allocation limit for the new volume size,
5449 * do not use this block or beyond. All extents
5450 * that use this block or any blocks beyond this limit
5451 * will be relocated.
5454 * hfsmp->hfs_resize_blocksmoved is incremented by the number of allocation
5455 * blocks that were relocated.
5458 hfs_reclaim_file(struct hfsmount
*hfsmp
, struct vnode
*vp
, u_int32_t fileID
,
5459 u_int8_t forktype
, u_long allocLimit
, vfs_context_t context
)
5462 struct hfs_reclaim_extent_info
*extent_info
;
5466 struct filefork
*fp
;
5467 int took_truncate_lock
= false;
5468 int release_desc
= false;
5469 HFSPlusExtentKey
*key
;
5471 /* If there is no vnode for this file, then there's nothing to do. */
5478 MALLOC(extent_info
, struct hfs_reclaim_extent_info
*,
5479 sizeof(struct hfs_reclaim_extent_info
), M_TEMP
, M_WAITOK
);
5480 if (extent_info
== NULL
) {
5483 bzero(extent_info
, sizeof(struct hfs_reclaim_extent_info
));
5484 extent_info
->vp
= vp
;
5485 extent_info
->fileID
= fileID
;
5486 extent_info
->forkType
= forktype
;
5487 extent_info
->is_sysfile
= vnode_issystem(vp
);
5488 if (vnode_isdir(vp
) && (cp
->c_flag
& C_HARDLINK
)) {
5489 extent_info
->is_dirlink
= true;
5491 /* We always need allocation bitmap and extent btree lock */
5492 lockflags
= SFL_BITMAP
| SFL_EXTENTS
;
5493 if ((fileID
== kHFSCatalogFileID
) || (extent_info
->is_dirlink
== true)) {
5494 lockflags
|= SFL_CATALOG
;
5495 } else if (fileID
== kHFSAttributesFileID
) {
5496 lockflags
|= SFL_ATTRIBUTE
;
5497 } else if (fileID
== kHFSStartupFileID
) {
5498 lockflags
|= SFL_STARTUP
;
5500 extent_info
->lockflags
= lockflags
;
5501 extent_info
->fcb
= VTOF(hfsmp
->hfs_extents_vp
);
5503 /* Flush data associated with current file on disk.
5505 * If the current vnode is directory hard link, no flushing of
5506 * journal or vnode is required. The current kernel does not
5507 * modify data/resource fork of directory hard links, so nothing
5508 * will be in the cache. If a directory hard link is newly created,
5509 * the resource fork data is written directly using devvp and
5510 * the code that actually relocates data (hfs_copy_extent()) also
5511 * uses devvp for its I/O --- so they will see a consistent copy.
5513 if (extent_info
->is_sysfile
) {
5514 /* If the current vnode is system vnode, flush journal
5515 * to make sure that all data is written to the disk.
5517 error
= hfs_journal_flush(hfsmp
, TRUE
);
5519 printf ("hfs_reclaim_file: journal_flush returned %d\n", error
);
5522 } else if (extent_info
->is_dirlink
== false) {
5523 /* Flush all blocks associated with this regular file vnode.
5524 * Normally there should not be buffer cache blocks for regular
5525 * files, but for objects like symlinks, we can have buffer cache
5526 * blocks associated with the vnode. Therefore we call
5527 * buf_flushdirtyblks() also.
5529 buf_flushdirtyblks(vp
, 0, BUF_SKIP_LOCKED
, "hfs_reclaim_file");
5532 hfs_lock_truncate(cp
, HFS_EXCLUSIVE_LOCK
);
5533 took_truncate_lock
= true;
5534 (void) cluster_push(vp
, 0);
5535 error
= hfs_lock(cp
, HFS_FORCE_LOCK
);
5540 /* If the file no longer exists, nothing left to do */
5541 if (cp
->c_flag
& C_NOEXISTS
) {
5546 /* Wait for any in-progress writes to this vnode to complete, so that we'll
5547 * be copying consistent bits. (Otherwise, it's possible that an async
5548 * write will complete to the old extent after we read from it. That
5549 * could lead to corruption.)
5551 error
= vnode_waitforwrites(vp
, 0, 0, 0, "hfs_reclaim_file");
5557 if (hfs_resize_debug
) {
5558 printf("hfs_reclaim_file: === Start reclaiming %sfork for %sid=%u ===\n", (forktype
? "rsrc" : "data"), (extent_info
->is_dirlink
? "dirlink" : "file"), fileID
);
5561 if (extent_info
->is_dirlink
) {
5562 MALLOC(extent_info
->dirlink_desc
, struct cat_desc
*,
5563 sizeof(struct cat_desc
), M_TEMP
, M_WAITOK
);
5564 MALLOC(extent_info
->dirlink_attr
, struct cat_attr
*,
5565 sizeof(struct cat_attr
), M_TEMP
, M_WAITOK
);
5566 MALLOC(extent_info
->dirlink_fork
, struct filefork
*,
5567 sizeof(struct filefork
), M_TEMP
, M_WAITOK
);
5568 if ((extent_info
->dirlink_desc
== NULL
) ||
5569 (extent_info
->dirlink_attr
== NULL
) ||
5570 (extent_info
->dirlink_fork
== NULL
)) {
5575 /* Lookup catalog record for directory hard link and
5576 * create a fake filefork for the value looked up from
5579 fp
= extent_info
->dirlink_fork
;
5580 bzero(extent_info
->dirlink_fork
, sizeof(struct filefork
));
5581 extent_info
->dirlink_fork
->ff_cp
= cp
;
5582 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
5583 error
= cat_lookup_dirlink(hfsmp
, fileID
, forktype
,
5584 extent_info
->dirlink_desc
, extent_info
->dirlink_attr
,
5585 &(extent_info
->dirlink_fork
->ff_data
));
5586 hfs_systemfile_unlock(hfsmp
, lockflags
);
5588 printf ("hfs_reclaim_file: cat_lookup_dirlink for fileID=%u returned error=%u\n", fileID
, error
);
5591 release_desc
= true;
5596 extent_info
->catalog_fp
= fp
;
5597 extent_info
->recStartBlock
= 0;
5598 extent_info
->extents
= extent_info
->catalog_fp
->ff_extents
;
5599 /* Relocate extents from the catalog record */
5600 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
5601 if (fp
->ff_extents
[i
].blockCount
== 0) {
5604 extent_info
->extent_index
= i
;
5605 error
= hfs_reclaim_extent(hfsmp
, allocLimit
, extent_info
, context
);
5607 printf ("hfs_reclaim_file: fileID=%u #%d %u:(%u,%u) hfs_reclaim_extent error=%d\n", fileID
, extent_info
->overflow_count
, i
, fp
->ff_extents
[i
].startBlock
, fp
->ff_extents
[i
].blockCount
, error
);
5612 /* If the number of allocation blocks processed for reclaiming
5613 * are less than total number of blocks for the file, continuing
5614 * working on overflow extents record.
5616 if (fp
->ff_blocks
<= extent_info
->cur_blockCount
) {
5617 if (0 && hfs_resize_debug
) {
5618 printf ("hfs_reclaim_file: Nothing more to relocate, offset=%d, ff_blocks=%u, cur_blockCount=%u\n", i
, fp
->ff_blocks
, extent_info
->cur_blockCount
);
5623 if (hfs_resize_debug
) {
5624 printf ("hfs_reclaim_file: Will check overflow records, offset=%d, ff_blocks=%u, cur_blockCount=%u\n", i
, fp
->ff_blocks
, extent_info
->cur_blockCount
);
5627 MALLOC(extent_info
->iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
5628 if (extent_info
->iterator
== NULL
) {
5632 bzero(extent_info
->iterator
, sizeof(struct BTreeIterator
));
5633 key
= (HFSPlusExtentKey
*) &(extent_info
->iterator
->key
);
5634 key
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
5635 key
->forkType
= forktype
;
5636 key
->fileID
= fileID
;
5637 key
->startBlock
= extent_info
->cur_blockCount
;
5639 extent_info
->btdata
.bufferAddress
= extent_info
->record
.overflow
;
5640 extent_info
->btdata
.itemSize
= sizeof(HFSPlusExtentRecord
);
5641 extent_info
->btdata
.itemCount
= 1;
5643 extent_info
->catalog_fp
= NULL
;
5645 /* Search the first overflow extent with expected startBlock as 'cur_blockCount' */
5646 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
5647 error
= BTSearchRecord(extent_info
->fcb
, extent_info
->iterator
,
5648 &(extent_info
->btdata
), &(extent_info
->recordlen
),
5649 extent_info
->iterator
);
5650 hfs_systemfile_unlock(hfsmp
, lockflags
);
5651 while (error
== 0) {
5652 extent_info
->overflow_count
++;
5653 extent_info
->recStartBlock
= key
->startBlock
;
5654 extent_info
->extents
= extent_info
->record
.overflow
;
5655 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
5656 if (extent_info
->record
.overflow
[i
].blockCount
== 0) {
5659 extent_info
->extent_index
= i
;
5660 error
= hfs_reclaim_extent(hfsmp
, allocLimit
, extent_info
, context
);
5662 printf ("hfs_reclaim_file: fileID=%u #%d %u:(%u,%u) hfs_reclaim_extent error=%d\n", fileID
, extent_info
->overflow_count
, i
, extent_info
->record
.overflow
[i
].startBlock
, extent_info
->record
.overflow
[i
].blockCount
, error
);
5667 /* Look for more overflow records */
5668 lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_EXCLUSIVE_LOCK
);
5669 error
= BTIterateRecord(extent_info
->fcb
, kBTreeNextRecord
,
5670 extent_info
->iterator
, &(extent_info
->btdata
),
5671 &(extent_info
->recordlen
));
5672 hfs_systemfile_unlock(hfsmp
, lockflags
);
5676 /* Stop when we encounter a different file or fork. */
5677 if ((key
->fileID
!= fileID
) || (key
->forkType
!= forktype
)) {
5681 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
5686 /* If any blocks were relocated, account them and report progress */
5687 if (extent_info
->blocks_relocated
) {
5688 hfsmp
->hfs_resize_blocksmoved
+= extent_info
->blocks_relocated
;
5689 hfs_truncatefs_progress(hfsmp
);
5690 if (fileID
< kHFSFirstUserCatalogNodeID
) {
5691 printf ("hfs_reclaim_file: Relocated %u blocks from fileID=%u on \"%s\"\n",
5692 extent_info
->blocks_relocated
, fileID
, hfsmp
->vcbVN
);
5695 if (extent_info
->iterator
) {
5696 FREE(extent_info
->iterator
, M_TEMP
);
5698 if (release_desc
== true) {
5699 cat_releasedesc(extent_info
->dirlink_desc
);
5701 if (extent_info
->dirlink_desc
) {
5702 FREE(extent_info
->dirlink_desc
, M_TEMP
);
5704 if (extent_info
->dirlink_attr
) {
5705 FREE(extent_info
->dirlink_attr
, M_TEMP
);
5707 if (extent_info
->dirlink_fork
) {
5708 FREE(extent_info
->dirlink_fork
, M_TEMP
);
5710 if ((extent_info
->blocks_relocated
!= 0) && (extent_info
->is_sysfile
== false)) {
5711 (void) hfs_update(vp
, MNT_WAIT
);
5713 if (took_truncate_lock
) {
5714 hfs_unlock_truncate(cp
, 0);
5717 FREE(extent_info
, M_TEMP
);
5719 if (hfs_resize_debug
) {
5720 printf("hfs_reclaim_file: === Finished relocating %sfork for fileid=%u (error=%d) ===\n", (forktype
? "rsrc" : "data"), fileID
, error
);
5728 * This journal_relocate callback updates the journal info block to point
5729 * at the new journal location. This write must NOT be done using the
5730 * transaction. We must write the block immediately. We must also force
5731 * it to get to the media so that the new journal location will be seen by
5732 * the replay code before we can safely let journaled blocks be written
5733 * to their normal locations.
5735 * The tests for journal_uses_fua below are mildly hacky. Since the journal
5736 * and the file system are both on the same device, I'm leveraging what
5737 * the journal has decided about FUA.
5739 struct hfs_journal_relocate_args
{
5740 struct hfsmount
*hfsmp
;
5741 vfs_context_t context
;
5742 u_int32_t newStartBlock
;
5746 hfs_journal_relocate_callback(void *_args
)
5749 struct hfs_journal_relocate_args
*args
= _args
;
5750 struct hfsmount
*hfsmp
= args
->hfsmp
;
5752 JournalInfoBlock
*jibp
;
5754 error
= buf_meta_bread(hfsmp
->hfs_devvp
,
5755 hfsmp
->vcbJinfoBlock
* (hfsmp
->blockSize
/hfsmp
->hfs_logical_block_size
),
5756 hfsmp
->blockSize
, vfs_context_ucred(args
->context
), &bp
);
5758 printf("hfs_reclaim_journal_file: failed to read JIB (%d)\n", error
);
5764 jibp
= (JournalInfoBlock
*) buf_dataptr(bp
);
5765 jibp
->offset
= SWAP_BE64((u_int64_t
)args
->newStartBlock
* hfsmp
->blockSize
);
5766 jibp
->size
= SWAP_BE64(hfsmp
->jnl_size
);
5767 if (journal_uses_fua(hfsmp
->jnl
))
5769 error
= buf_bwrite(bp
);
5771 printf("hfs_reclaim_journal_file: failed to write JIB (%d)\n", error
);
5774 if (!journal_uses_fua(hfsmp
->jnl
)) {
5775 error
= VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, args
->context
);
5777 printf("hfs_reclaim_journal_file: DKIOCSYNCHRONIZECACHE failed (%d)\n", error
);
5778 error
= 0; /* Don't fail the operation. */
5787 hfs_reclaim_journal_file(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, vfs_context_t context
)
5792 u_int32_t oldStartBlock
;
5793 u_int32_t newStartBlock
;
5794 u_int32_t oldBlockCount
;
5795 u_int32_t newBlockCount
;
5796 struct cat_desc journal_desc
;
5797 struct cat_attr journal_attr
;
5798 struct cat_fork journal_fork
;
5799 struct hfs_journal_relocate_args callback_args
;
5801 if (hfsmp
->jnl_start
+ (hfsmp
->jnl_size
/ hfsmp
->blockSize
) <= allocLimit
) {
5802 /* The journal does not require relocation */
5806 error
= hfs_start_transaction(hfsmp
);
5808 printf("hfs_reclaim_journal_file: hfs_start_transaction returned %d\n", error
);
5811 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
5813 oldBlockCount
= hfsmp
->jnl_size
/ hfsmp
->blockSize
;
5815 /* TODO: Allow the journal to change size based on the new volume size. */
5816 error
= BlockAllocate(hfsmp
, 1, oldBlockCount
, oldBlockCount
,
5817 HFS_ALLOC_METAZONE
| HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_SKIPFREEBLKS
,
5818 &newStartBlock
, &newBlockCount
);
5820 printf("hfs_reclaim_journal_file: BlockAllocate returned %d\n", error
);
5823 if (newBlockCount
!= oldBlockCount
) {
5824 printf("hfs_reclaim_journal_file: newBlockCount != oldBlockCount (%u, %u)\n", newBlockCount
, oldBlockCount
);
5828 error
= BlockDeallocate(hfsmp
, hfsmp
->jnl_start
, oldBlockCount
, HFS_ALLOC_SKIPFREEBLKS
);
5830 printf("hfs_reclaim_journal_file: BlockDeallocate returned %d\n", error
);
5834 /* Update the catalog record for .journal */
5835 error
= cat_idlookup(hfsmp
, hfsmp
->hfs_jnlfileid
, 1, &journal_desc
, &journal_attr
, &journal_fork
);
5837 printf("hfs_reclaim_journal_file: cat_idlookup returned %d\n", error
);
5840 oldStartBlock
= journal_fork
.cf_extents
[0].startBlock
;
5841 journal_fork
.cf_size
= newBlockCount
* hfsmp
->blockSize
;
5842 journal_fork
.cf_extents
[0].startBlock
= newStartBlock
;
5843 journal_fork
.cf_extents
[0].blockCount
= newBlockCount
;
5844 journal_fork
.cf_blocks
= newBlockCount
;
5845 error
= cat_update(hfsmp
, &journal_desc
, &journal_attr
, &journal_fork
, NULL
);
5846 cat_releasedesc(&journal_desc
); /* all done with cat descriptor */
5848 printf("hfs_reclaim_journal_file: cat_update returned %d\n", error
);
5851 callback_args
.hfsmp
= hfsmp
;
5852 callback_args
.context
= context
;
5853 callback_args
.newStartBlock
= newStartBlock
;
5855 error
= journal_relocate(hfsmp
->jnl
, (off_t
)newStartBlock
*hfsmp
->blockSize
,
5856 (off_t
)newBlockCount
*hfsmp
->blockSize
, 0,
5857 hfs_journal_relocate_callback
, &callback_args
);
5859 /* NOTE: journal_relocate will mark the journal invalid. */
5860 printf("hfs_reclaim_journal_file: journal_relocate returned %d\n", error
);
5863 hfsmp
->jnl_start
= newStartBlock
;
5864 hfsmp
->jnl_size
= (off_t
)newBlockCount
* hfsmp
->blockSize
;
5866 hfs_systemfile_unlock(hfsmp
, lockflags
);
5867 error
= hfs_end_transaction(hfsmp
);
5869 printf("hfs_reclaim_journal_file: hfs_end_transaction returned %d\n", error
);
5872 /* Account for the blocks relocated and print progress */
5873 hfsmp
->hfs_resize_blocksmoved
+= oldBlockCount
;
5874 hfs_truncatefs_progress(hfsmp
);
5876 printf ("hfs_reclaim_journal_file: Relocated %u blocks from journal on \"%s\"\n",
5877 oldBlockCount
, hfsmp
->vcbVN
);
5878 if (hfs_resize_debug
) {
5879 printf ("hfs_reclaim_journal_file: Successfully relocated journal from (%u,%u) to (%u,%u)\n", oldStartBlock
, oldBlockCount
, newStartBlock
, newBlockCount
);
5885 journal_err
= BlockDeallocate(hfsmp
, newStartBlock
, newBlockCount
, HFS_ALLOC_SKIPFREEBLKS
);
5887 printf("hfs_reclaim_journal_file: BlockDeallocate returned %d\n", error
);
5888 hfs_mark_volume_inconsistent(hfsmp
);
5891 hfs_systemfile_unlock(hfsmp
, lockflags
);
5892 (void) hfs_end_transaction(hfsmp
);
5893 if (hfs_resize_debug
) {
5894 printf ("hfs_reclaim_journal_file: Error relocating journal file (error=%d)\n", error
);
5901 * Move the journal info block to a new location. We have to make sure the
5902 * new copy of the journal info block gets to the media first, then change
5903 * the field in the volume header and the catalog record.
5906 hfs_reclaim_journal_info_block(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, vfs_context_t context
)
5913 u_int32_t blockCount
;
5914 struct cat_desc jib_desc
;
5915 struct cat_attr jib_attr
;
5916 struct cat_fork jib_fork
;
5917 buf_t old_bp
, new_bp
;
5919 if (hfsmp
->vcbJinfoBlock
<= allocLimit
) {
5920 /* The journal info block does not require relocation */
5924 error
= hfs_start_transaction(hfsmp
);
5926 printf("hfs_reclaim_journal_info_block: hfs_start_transaction returned %d\n", error
);
5929 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
| SFL_BITMAP
, HFS_EXCLUSIVE_LOCK
);
5931 error
= BlockAllocate(hfsmp
, 1, 1, 1,
5932 HFS_ALLOC_METAZONE
| HFS_ALLOC_FORCECONTIG
| HFS_ALLOC_SKIPFREEBLKS
,
5933 &newBlock
, &blockCount
);
5935 printf("hfs_reclaim_journal_info_block: BlockAllocate returned %d\n", error
);
5938 if (blockCount
!= 1) {
5939 printf("hfs_reclaim_journal_info_block: blockCount != 1 (%u)\n", blockCount
);
5942 error
= BlockDeallocate(hfsmp
, hfsmp
->vcbJinfoBlock
, 1, HFS_ALLOC_SKIPFREEBLKS
);
5944 printf("hfs_reclaim_journal_info_block: BlockDeallocate returned %d\n", error
);
5948 /* Copy the old journal info block content to the new location */
5949 error
= buf_meta_bread(hfsmp
->hfs_devvp
,
5950 hfsmp
->vcbJinfoBlock
* (hfsmp
->blockSize
/hfsmp
->hfs_logical_block_size
),
5951 hfsmp
->blockSize
, vfs_context_ucred(context
), &old_bp
);
5953 printf("hfs_reclaim_journal_info_block: failed to read JIB (%d)\n", error
);
5959 new_bp
= buf_getblk(hfsmp
->hfs_devvp
,
5960 newBlock
* (hfsmp
->blockSize
/hfsmp
->hfs_logical_block_size
),
5961 hfsmp
->blockSize
, 0, 0, BLK_META
);
5962 bcopy((char*)buf_dataptr(old_bp
), (char*)buf_dataptr(new_bp
), hfsmp
->blockSize
);
5964 if (journal_uses_fua(hfsmp
->jnl
))
5965 buf_markfua(new_bp
);
5966 error
= buf_bwrite(new_bp
);
5968 printf("hfs_reclaim_journal_info_block: failed to write new JIB (%d)\n", error
);
5971 if (!journal_uses_fua(hfsmp
->jnl
)) {
5972 error
= VNOP_IOCTL(hfsmp
->hfs_devvp
, DKIOCSYNCHRONIZECACHE
, NULL
, FWRITE
, context
);
5974 printf("hfs_reclaim_journal_info_block: DKIOCSYNCHRONIZECACHE failed (%d)\n", error
);
5975 /* Don't fail the operation. */
5979 /* Update the catalog record for .journal_info_block */
5980 error
= cat_idlookup(hfsmp
, hfsmp
->hfs_jnlinfoblkid
, 1, &jib_desc
, &jib_attr
, &jib_fork
);
5982 printf("hfs_reclaim_journal_file: cat_idlookup returned %d\n", error
);
5985 oldBlock
= jib_fork
.cf_extents
[0].startBlock
;
5986 jib_fork
.cf_size
= hfsmp
->blockSize
;
5987 jib_fork
.cf_extents
[0].startBlock
= newBlock
;
5988 jib_fork
.cf_extents
[0].blockCount
= 1;
5989 jib_fork
.cf_blocks
= 1;
5990 error
= cat_update(hfsmp
, &jib_desc
, &jib_attr
, &jib_fork
, NULL
);
5991 cat_releasedesc(&jib_desc
); /* all done with cat descriptor */
5993 printf("hfs_reclaim_journal_info_block: cat_update returned %d\n", error
);
5997 /* Update the pointer to the journal info block in the volume header. */
5998 hfsmp
->vcbJinfoBlock
= newBlock
;
5999 error
= hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, HFS_ALTFLUSH
);
6001 printf("hfs_reclaim_journal_info_block: hfs_flushvolumeheader returned %d\n", error
);
6004 hfs_systemfile_unlock(hfsmp
, lockflags
);
6005 error
= hfs_end_transaction(hfsmp
);
6007 printf("hfs_reclaim_journal_info_block: hfs_end_transaction returned %d\n", error
);
6009 error
= hfs_journal_flush(hfsmp
, FALSE
);
6011 printf("hfs_reclaim_journal_info_block: journal_flush returned %d\n", error
);
6014 /* Account for the block relocated and print progress */
6015 hfsmp
->hfs_resize_blocksmoved
+= 1;
6016 hfs_truncatefs_progress(hfsmp
);
6018 printf ("hfs_reclaim_journal_info: Relocated 1 block from journal info on \"%s\"\n",
6020 if (hfs_resize_debug
) {
6021 printf ("hfs_reclaim_journal_info_block: Successfully relocated journal info block from (%u,%u) to (%u,%u)\n", oldBlock
, blockCount
, newBlock
, blockCount
);
6027 journal_err
= BlockDeallocate(hfsmp
, newBlock
, blockCount
, HFS_ALLOC_SKIPFREEBLKS
);
6029 printf("hfs_reclaim_journal_info_block: BlockDeallocate returned %d\n", error
);
6030 hfs_mark_volume_inconsistent(hfsmp
);
6034 hfs_systemfile_unlock(hfsmp
, lockflags
);
6035 (void) hfs_end_transaction(hfsmp
);
6036 if (hfs_resize_debug
) {
6037 printf ("hfs_reclaim_journal_info_block: Error relocating journal info block (error=%d)\n", error
);
6044 * This function traverses through all extended attribute records for a given
6045 * fileID, and calls function that reclaims data blocks that exist in the
6046 * area of the disk being reclaimed which in turn is responsible for allocating
6047 * new space, copying extent data, deallocating new space, and if required,
6048 * splitting the extent.
6050 * Note: The caller has already acquired the cnode lock on the file. Therefore
6051 * we are assured that no other thread would be creating/deleting/modifying
6052 * extended attributes for this file.
6055 * hfsmp->hfs_resize_blocksmoved is incremented by the number of allocation
6056 * blocks that were relocated.
6059 * 0 on success, non-zero on failure.
6062 hfs_reclaim_xattr(struct hfsmount
*hfsmp
, struct vnode
*vp
, u_int32_t fileID
, u_int32_t allocLimit
, vfs_context_t context
)
6065 struct hfs_reclaim_extent_info
*extent_info
;
6067 HFSPlusAttrKey
*key
;
6070 if (hfs_resize_debug
) {
6071 printf("hfs_reclaim_xattr: === Start reclaiming xattr for id=%u ===\n", fileID
);
6074 MALLOC(extent_info
, struct hfs_reclaim_extent_info
*,
6075 sizeof(struct hfs_reclaim_extent_info
), M_TEMP
, M_WAITOK
);
6076 if (extent_info
== NULL
) {
6079 bzero(extent_info
, sizeof(struct hfs_reclaim_extent_info
));
6080 extent_info
->vp
= vp
;
6081 extent_info
->fileID
= fileID
;
6082 extent_info
->is_xattr
= true;
6083 extent_info
->is_sysfile
= vnode_issystem(vp
);
6084 extent_info
->fcb
= VTOF(hfsmp
->hfs_attribute_vp
);
6085 lockflags
= &(extent_info
->lockflags
);
6086 *lockflags
= SFL_ATTRIBUTE
| SFL_BITMAP
;
6088 /* Initialize iterator from the extent_info structure */
6089 MALLOC(extent_info
->iterator
, struct BTreeIterator
*,
6090 sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
6091 if (extent_info
->iterator
== NULL
) {
6095 bzero(extent_info
->iterator
, sizeof(struct BTreeIterator
));
6097 /* Build attribute key */
6098 key
= (HFSPlusAttrKey
*)&(extent_info
->iterator
->key
);
6099 error
= hfs_buildattrkey(fileID
, NULL
, key
);
6104 /* Initialize btdata from extent_info structure. Note that the
6105 * buffer pointer actually points to the xattr record from the
6106 * extent_info structure itself.
6108 extent_info
->btdata
.bufferAddress
= &(extent_info
->record
.xattr
);
6109 extent_info
->btdata
.itemSize
= sizeof(HFSPlusAttrRecord
);
6110 extent_info
->btdata
.itemCount
= 1;
6113 * Sync all extent-based attribute data to the disk.
6115 * All extent-based attribute data I/O is performed via cluster
6116 * I/O using a virtual file that spans across entire file system
6119 hfs_lock_truncate(VTOC(hfsmp
->hfs_attrdata_vp
), HFS_EXCLUSIVE_LOCK
);
6120 (void)cluster_push(hfsmp
->hfs_attrdata_vp
, 0);
6121 error
= vnode_waitforwrites(hfsmp
->hfs_attrdata_vp
, 0, 0, 0, "hfs_reclaim_xattr");
6122 hfs_unlock_truncate(VTOC(hfsmp
->hfs_attrdata_vp
), 0);
6127 /* Search for extended attribute for current file. This
6128 * will place the iterator before the first matching record.
6130 *lockflags
= hfs_systemfile_lock(hfsmp
, *lockflags
, HFS_EXCLUSIVE_LOCK
);
6131 error
= BTSearchRecord(extent_info
->fcb
, extent_info
->iterator
,
6132 &(extent_info
->btdata
), &(extent_info
->recordlen
),
6133 extent_info
->iterator
);
6134 hfs_systemfile_unlock(hfsmp
, *lockflags
);
6136 if (error
!= btNotFound
) {
6139 /* btNotFound is expected here, so just mask it */
6144 /* Iterate to the next record */
6145 *lockflags
= hfs_systemfile_lock(hfsmp
, *lockflags
, HFS_EXCLUSIVE_LOCK
);
6146 error
= BTIterateRecord(extent_info
->fcb
, kBTreeNextRecord
,
6147 extent_info
->iterator
, &(extent_info
->btdata
),
6148 &(extent_info
->recordlen
));
6149 hfs_systemfile_unlock(hfsmp
, *lockflags
);
6151 /* Stop the iteration if we encounter end of btree or xattr with different fileID */
6152 if (error
|| key
->fileID
!= fileID
) {
6153 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
6159 /* We only care about extent-based EAs */
6160 if ((extent_info
->record
.xattr
.recordType
!= kHFSPlusAttrForkData
) &&
6161 (extent_info
->record
.xattr
.recordType
!= kHFSPlusAttrExtents
)) {
6165 if (extent_info
->record
.xattr
.recordType
== kHFSPlusAttrForkData
) {
6166 extent_info
->overflow_count
= 0;
6167 extent_info
->extents
= extent_info
->record
.xattr
.forkData
.theFork
.extents
;
6168 } else if (extent_info
->record
.xattr
.recordType
== kHFSPlusAttrExtents
) {
6169 extent_info
->overflow_count
++;
6170 extent_info
->extents
= extent_info
->record
.xattr
.overflowExtents
.extents
;
6173 extent_info
->recStartBlock
= key
->startBlock
;
6174 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
6175 if (extent_info
->extents
[i
].blockCount
== 0) {
6178 extent_info
->extent_index
= i
;
6179 error
= hfs_reclaim_extent(hfsmp
, allocLimit
, extent_info
, context
);
6181 printf ("hfs_reclaim_xattr: fileID=%u hfs_reclaim_extent error=%d\n", fileID
, error
);
6188 /* If any blocks were relocated, account them and report progress */
6189 if (extent_info
->blocks_relocated
) {
6190 hfsmp
->hfs_resize_blocksmoved
+= extent_info
->blocks_relocated
;
6191 hfs_truncatefs_progress(hfsmp
);
6193 if (extent_info
->iterator
) {
6194 FREE(extent_info
->iterator
, M_TEMP
);
6197 FREE(extent_info
, M_TEMP
);
6199 if (hfs_resize_debug
) {
6200 printf("hfs_reclaim_xattr: === Finished relocating xattr for fileid=%u (error=%d) ===\n", fileID
, error
);
6206 * Reclaim any extent-based extended attributes allocation blocks from
6207 * the area of the disk that is being truncated.
6209 * The function traverses the attribute btree to find out the fileIDs
6210 * of the extended attributes that need to be relocated. For every
6211 * file whose large EA requires relocation, it looks up the cnode and
6212 * calls hfs_reclaim_xattr() to do all the work for allocating
6213 * new space, copying data, deallocating old space, and if required,
6214 * splitting the extents.
6217 * allocLimit - starting block of the area being reclaimed
6220 * returns 0 on success, non-zero on failure.
6223 hfs_reclaim_xattrspace(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, vfs_context_t context
)
6227 struct BTreeIterator
*iterator
= NULL
;
6228 struct FSBufferDescriptor btdata
;
6229 HFSPlusAttrKey
*key
;
6230 HFSPlusAttrRecord rec
;
6232 cnid_t prev_fileid
= 0;
6235 int btree_operation
;
6236 u_int32_t files_moved
= 0;
6237 u_int32_t prev_blocksmoved
;
6240 fcb
= VTOF(hfsmp
->hfs_attribute_vp
);
6241 /* Store the value to print total blocks moved by this function in end */
6242 prev_blocksmoved
= hfsmp
->hfs_resize_blocksmoved
;
6244 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&iterator
, sizeof(*iterator
))) {
6247 bzero(iterator
, sizeof(*iterator
));
6248 key
= (HFSPlusAttrKey
*)&iterator
->key
;
6249 btdata
.bufferAddress
= &rec
;
6250 btdata
.itemSize
= sizeof(rec
);
6251 btdata
.itemCount
= 1;
6253 need_relocate
= false;
6254 btree_operation
= kBTreeFirstRecord
;
6255 /* Traverse the attribute btree to find extent-based EAs to reclaim */
6257 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_ATTRIBUTE
, HFS_SHARED_LOCK
);
6258 error
= BTIterateRecord(fcb
, btree_operation
, iterator
, &btdata
, NULL
);
6259 hfs_systemfile_unlock(hfsmp
, lockflags
);
6261 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
6266 btree_operation
= kBTreeNextRecord
;
6268 /* If the extents of current fileID were already relocated, skip it */
6269 if (prev_fileid
== key
->fileID
) {
6273 /* Check if any of the extents in the current record need to be relocated */
6274 need_relocate
= false;
6275 switch(rec
.recordType
) {
6276 case kHFSPlusAttrForkData
:
6277 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
6278 if (rec
.forkData
.theFork
.extents
[i
].blockCount
== 0) {
6281 if ((rec
.forkData
.theFork
.extents
[i
].startBlock
+
6282 rec
.forkData
.theFork
.extents
[i
].blockCount
) > allocLimit
) {
6283 need_relocate
= true;
6289 case kHFSPlusAttrExtents
:
6290 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
6291 if (rec
.overflowExtents
.extents
[i
].blockCount
== 0) {
6294 if ((rec
.overflowExtents
.extents
[i
].startBlock
+
6295 rec
.overflowExtents
.extents
[i
].blockCount
) > allocLimit
) {
6296 need_relocate
= true;
6303 /* Continue iterating to next attribute record */
6304 if (need_relocate
== false) {
6308 /* Look up the vnode for corresponding file. The cnode
6309 * will be locked which will ensure that no one modifies
6310 * the xattrs when we are relocating them.
6312 * We want to allow open-unlinked files to be moved,
6313 * so provide allow_deleted == 1 for hfs_vget().
6315 if (hfs_vget(hfsmp
, key
->fileID
, &vp
, 0, 1) != 0) {
6319 error
= hfs_reclaim_xattr(hfsmp
, vp
, key
->fileID
, allocLimit
, context
);
6320 hfs_unlock(VTOC(vp
));
6323 printf ("hfs_reclaim_xattrspace: Error relocating xattrs for fileid=%u (error=%d)\n", key
->fileID
, error
);
6326 prev_fileid
= key
->fileID
;
6331 printf("hfs_reclaim_xattrspace: Relocated %u xattr blocks from %u files on \"%s\"\n",
6332 (hfsmp
->hfs_resize_blocksmoved
- prev_blocksmoved
),
6333 files_moved
, hfsmp
->vcbVN
);
6336 kmem_free(kernel_map
, (vm_offset_t
)iterator
, sizeof(*iterator
));
6341 * Reclaim blocks from regular files.
6343 * This function iterates over all the record in catalog btree looking
6344 * for files with extents that overlap into the space we're trying to
6345 * free up. If a file extent requires relocation, it looks up the vnode
6346 * and calls function to relocate the data.
6349 * Zero on success, non-zero on failure.
6352 hfs_reclaim_filespace(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, vfs_context_t context
)
6356 struct BTreeIterator
*iterator
= NULL
;
6357 struct FSBufferDescriptor btdata
;
6358 int btree_operation
;
6360 struct HFSPlusCatalogFile filerec
;
6363 struct filefork
*datafork
;
6364 u_int32_t files_moved
= 0;
6365 u_int32_t prev_blocksmoved
;
6367 fcb
= VTOF(hfsmp
->hfs_catalog_vp
);
6368 /* Store the value to print total blocks moved by this function at the end */
6369 prev_blocksmoved
= hfsmp
->hfs_resize_blocksmoved
;
6371 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&iterator
, sizeof(*iterator
))) {
6374 bzero(iterator
, sizeof(*iterator
));
6376 btdata
.bufferAddress
= &filerec
;
6377 btdata
.itemSize
= sizeof(filerec
);
6378 btdata
.itemCount
= 1;
6380 btree_operation
= kBTreeFirstRecord
;
6382 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
6383 error
= BTIterateRecord(fcb
, btree_operation
, iterator
, &btdata
, NULL
);
6384 hfs_systemfile_unlock(hfsmp
, lockflags
);
6386 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
6391 btree_operation
= kBTreeNextRecord
;
6393 if (filerec
.recordType
!= kHFSPlusFileRecord
) {
6397 /* Check if any of the extents require relocation */
6398 if (hfs_file_extent_overlaps(hfsmp
, allocLimit
, &filerec
) == false) {
6402 /* We want to allow open-unlinked files to be moved, so allow_deleted == 1 */
6403 if (hfs_vget(hfsmp
, filerec
.fileID
, &vp
, 0, 1) != 0) {
6407 /* If data fork exists or item is a directory hard link, relocate blocks */
6408 datafork
= VTOF(vp
);
6409 if ((datafork
&& datafork
->ff_blocks
> 0) || vnode_isdir(vp
)) {
6410 error
= hfs_reclaim_file(hfsmp
, vp
, filerec
.fileID
,
6411 kHFSDataForkType
, allocLimit
, context
);
6413 printf ("hfs_reclaimspace: Error reclaiming datafork blocks of fileid=%u (error=%d)\n", filerec
.fileID
, error
);
6414 hfs_unlock(VTOC(vp
));
6420 /* If resource fork exists or item is a directory hard link, relocate blocks */
6421 if (((VTOC(vp
)->c_blocks
- (datafork
? datafork
->ff_blocks
: 0)) > 0) || vnode_isdir(vp
)) {
6422 if (vnode_isdir(vp
)) {
6423 /* Resource fork vnode lookup is invalid for directory hard link.
6424 * So we fake data fork vnode as resource fork vnode.
6428 error
= hfs_vgetrsrc(hfsmp
, vp
, &rvp
, TRUE
, FALSE
);
6430 printf ("hfs_reclaimspace: Error looking up rvp for fileid=%u (error=%d)\n", filerec
.fileID
, error
);
6431 hfs_unlock(VTOC(vp
));
6435 VTOC(rvp
)->c_flag
|= C_NEED_RVNODE_PUT
;
6438 error
= hfs_reclaim_file(hfsmp
, rvp
, filerec
.fileID
,
6439 kHFSResourceForkType
, allocLimit
, context
);
6441 printf ("hfs_reclaimspace: Error reclaiming rsrcfork blocks of fileid=%u (error=%d)\n", filerec
.fileID
, error
);
6442 hfs_unlock(VTOC(vp
));
6448 /* The file forks were relocated successfully, now drop the
6449 * cnode lock and vnode reference, and continue iterating to
6450 * next catalog record.
6452 hfs_unlock(VTOC(vp
));
6458 printf("hfs_reclaim_filespace: Relocated %u blocks from %u files on \"%s\"\n",
6459 (hfsmp
->hfs_resize_blocksmoved
- prev_blocksmoved
),
6460 files_moved
, hfsmp
->vcbVN
);
6463 kmem_free(kernel_map
, (vm_offset_t
)iterator
, sizeof(*iterator
));
6468 * Reclaim space at the end of a file system.
6471 * allocLimit - start block of the space being reclaimed
6472 * reclaimblks - number of allocation blocks to reclaim
6475 hfs_reclaimspace(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, u_int32_t reclaimblks
, vfs_context_t context
)
6480 * Preflight the bitmap to find out total number of blocks that need
6483 * Note: Since allocLimit is set to the location of new alternate volume
6484 * header, the check below does not account for blocks allocated for old
6485 * alternate volume header.
6487 error
= hfs_count_allocated(hfsmp
, allocLimit
, reclaimblks
, &(hfsmp
->hfs_resize_totalblocks
));
6489 printf ("hfs_reclaimspace: Unable to determine total blocks to reclaim error=%d\n", error
);
6492 if (hfs_resize_debug
) {
6493 printf ("hfs_reclaimspace: Total number of blocks to reclaim = %u\n", hfsmp
->hfs_resize_totalblocks
);
6496 /* Relocate extents of the Allocation file if they're in the way. */
6497 error
= hfs_reclaim_file(hfsmp
, hfsmp
->hfs_allocation_vp
, kHFSAllocationFileID
,
6498 kHFSDataForkType
, allocLimit
, context
);
6500 printf("hfs_reclaimspace: reclaim allocation file returned %d\n", error
);
6504 /* Relocate extents of the Extents B-tree if they're in the way. */
6505 error
= hfs_reclaim_file(hfsmp
, hfsmp
->hfs_extents_vp
, kHFSExtentsFileID
,
6506 kHFSDataForkType
, allocLimit
, context
);
6508 printf("hfs_reclaimspace: reclaim extents b-tree returned %d\n", error
);
6512 /* Relocate extents of the Catalog B-tree if they're in the way. */
6513 error
= hfs_reclaim_file(hfsmp
, hfsmp
->hfs_catalog_vp
, kHFSCatalogFileID
,
6514 kHFSDataForkType
, allocLimit
, context
);
6516 printf("hfs_reclaimspace: reclaim catalog b-tree returned %d\n", error
);
6520 /* Relocate extents of the Attributes B-tree if they're in the way. */
6521 error
= hfs_reclaim_file(hfsmp
, hfsmp
->hfs_attribute_vp
, kHFSAttributesFileID
,
6522 kHFSDataForkType
, allocLimit
, context
);
6524 printf("hfs_reclaimspace: reclaim attribute b-tree returned %d\n", error
);
6528 /* Relocate extents of the Startup File if there is one and they're in the way. */
6529 error
= hfs_reclaim_file(hfsmp
, hfsmp
->hfs_startup_vp
, kHFSStartupFileID
,
6530 kHFSDataForkType
, allocLimit
, context
);
6532 printf("hfs_reclaimspace: reclaim startup file returned %d\n", error
);
6537 * We need to make sure the alternate volume header gets flushed if we moved
6538 * any extents in the volume header. But we need to do that before
6539 * shrinking the size of the volume, or else the journal code will panic
6540 * with an invalid (too large) block number.
6542 * Note that blks_moved will be set if ANY extent was moved, even
6543 * if it was just an overflow extent. In this case, the journal_flush isn't
6544 * strictly required, but shouldn't hurt.
6546 if (hfsmp
->hfs_resize_blocksmoved
) {
6547 hfs_journal_flush(hfsmp
, FALSE
);
6550 /* Relocate journal file blocks if they're in the way. */
6551 error
= hfs_reclaim_journal_file(hfsmp
, allocLimit
, context
);
6553 printf("hfs_reclaimspace: hfs_reclaim_journal_file failed (%d)\n", error
);
6557 /* Relocate journal info block blocks if they're in the way. */
6558 error
= hfs_reclaim_journal_info_block(hfsmp
, allocLimit
, context
);
6560 printf("hfs_reclaimspace: hfs_reclaim_journal_info_block failed (%d)\n", error
);
6564 /* Reclaim extents from catalog file records */
6565 error
= hfs_reclaim_filespace(hfsmp
, allocLimit
, context
);
6567 printf ("hfs_reclaimspace: hfs_reclaim_filespace returned error=%d\n", error
);
6571 /* Reclaim extents from extent-based extended attributes, if any */
6572 error
= hfs_reclaim_xattrspace(hfsmp
, allocLimit
, context
);
6574 printf ("hfs_reclaimspace: hfs_reclaim_xattrspace returned error=%d\n", error
);
6583 * Check if there are any extents (including overflow extents) that overlap
6584 * into the disk space that is being reclaimed.
6587 * true - One of the extents need to be relocated
6588 * false - No overflow extents need to be relocated, or there was an error
6591 hfs_file_extent_overlaps(struct hfsmount
*hfsmp
, u_int32_t allocLimit
, struct HFSPlusCatalogFile
*filerec
)
6593 struct BTreeIterator
* iterator
= NULL
;
6594 struct FSBufferDescriptor btdata
;
6595 HFSPlusExtentRecord extrec
;
6596 HFSPlusExtentKey
*extkeyptr
;
6598 int overlapped
= false;
6604 /* Check if data fork overlaps the target space */
6605 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
6606 if (filerec
->dataFork
.extents
[i
].blockCount
== 0) {
6609 endblock
= filerec
->dataFork
.extents
[i
].startBlock
+
6610 filerec
->dataFork
.extents
[i
].blockCount
;
6611 if (endblock
> allocLimit
) {
6617 /* Check if resource fork overlaps the target space */
6618 for (j
= 0; j
< kHFSPlusExtentDensity
; ++j
) {
6619 if (filerec
->resourceFork
.extents
[j
].blockCount
== 0) {
6622 endblock
= filerec
->resourceFork
.extents
[j
].startBlock
+
6623 filerec
->resourceFork
.extents
[j
].blockCount
;
6624 if (endblock
> allocLimit
) {
6630 /* Return back if there are no overflow extents for this file */
6631 if ((i
< kHFSPlusExtentDensity
) && (j
< kHFSPlusExtentDensity
)) {
6635 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&iterator
, sizeof(*iterator
))) {
6638 bzero(iterator
, sizeof(*iterator
));
6639 extkeyptr
= (HFSPlusExtentKey
*)&iterator
->key
;
6640 extkeyptr
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
6641 extkeyptr
->forkType
= 0;
6642 extkeyptr
->fileID
= filerec
->fileID
;
6643 extkeyptr
->startBlock
= 0;
6645 btdata
.bufferAddress
= &extrec
;
6646 btdata
.itemSize
= sizeof(extrec
);
6647 btdata
.itemCount
= 1;
6649 fcb
= VTOF(hfsmp
->hfs_extents_vp
);
6651 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_EXTENTS
, HFS_SHARED_LOCK
);
6653 /* This will position the iterator just before the first overflow
6654 * extent record for given fileID. It will always return btNotFound,
6655 * so we special case the error code.
6657 error
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, iterator
);
6658 if (error
&& (error
!= btNotFound
)) {
6662 /* BTIterateRecord() might return error if the btree is empty, and
6663 * therefore we return that the extent does not overflow to the caller
6665 error
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
6666 while (error
== 0) {
6667 /* Stop when we encounter a different file. */
6668 if (extkeyptr
->fileID
!= filerec
->fileID
) {
6671 /* Check if any of the forks exist in the target space. */
6672 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
6673 if (extrec
[i
].blockCount
== 0) {
6676 endblock
= extrec
[i
].startBlock
+ extrec
[i
].blockCount
;
6677 if (endblock
> allocLimit
) {
6682 /* Look for more records. */
6683 error
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
6688 hfs_systemfile_unlock(hfsmp
, lockflags
);
6691 kmem_free(kernel_map
, (vm_offset_t
)iterator
, sizeof(*iterator
));
6698 * Calculate the progress of a file system resize operation.
6702 hfs_resize_progress(struct hfsmount
*hfsmp
, u_int32_t
*progress
)
6704 if ((hfsmp
->hfs_flags
& HFS_RESIZE_IN_PROGRESS
) == 0) {
6708 if (hfsmp
->hfs_resize_totalblocks
> 0) {
6709 *progress
= (u_int32_t
)((hfsmp
->hfs_resize_blocksmoved
* 100ULL) / hfsmp
->hfs_resize_totalblocks
);
6719 * Creates a UUID from a unique "name" in the HFS UUID Name space.
6720 * See version 3 UUID.
6723 hfs_getvoluuid(struct hfsmount
*hfsmp
, uuid_t result
)
6728 ((uint32_t *)rawUUID
)[0] = hfsmp
->vcbFndrInfo
[6];
6729 ((uint32_t *)rawUUID
)[1] = hfsmp
->vcbFndrInfo
[7];
6732 MD5Update( &md5c
, HFS_UUID_NAMESPACE_ID
, sizeof( uuid_t
) );
6733 MD5Update( &md5c
, rawUUID
, sizeof (rawUUID
) );
6734 MD5Final( result
, &md5c
);
6736 result
[6] = 0x30 | ( result
[6] & 0x0F );
6737 result
[8] = 0x80 | ( result
[8] & 0x3F );
6741 * Get file system attributes.
6744 hfs_vfs_getattr(struct mount
*mp
, struct vfs_attr
*fsap
, __unused vfs_context_t context
)
6746 #define HFS_ATTR_CMN_VALIDMASK (ATTR_CMN_VALIDMASK & ~(ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST))
6747 #define HFS_ATTR_FILE_VALIDMASK (ATTR_FILE_VALIDMASK & ~(ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))
6748 #define HFS_ATTR_CMN_VOL_VALIDMASK (ATTR_CMN_VALIDMASK & ~(ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST | ATTR_CMN_ACCTIME))
6750 ExtendedVCB
*vcb
= VFSTOVCB(mp
);
6751 struct hfsmount
*hfsmp
= VFSTOHFS(mp
);
6752 u_int32_t freeCNIDs
;
6754 freeCNIDs
= (u_int32_t
)0xFFFFFFFF - (u_int32_t
)hfsmp
->vcbNxtCNID
;
6756 VFSATTR_RETURN(fsap
, f_objcount
, (u_int64_t
)hfsmp
->vcbFilCnt
+ (u_int64_t
)hfsmp
->vcbDirCnt
);
6757 VFSATTR_RETURN(fsap
, f_filecount
, (u_int64_t
)hfsmp
->vcbFilCnt
);
6758 VFSATTR_RETURN(fsap
, f_dircount
, (u_int64_t
)hfsmp
->vcbDirCnt
);
6759 VFSATTR_RETURN(fsap
, f_maxobjcount
, (u_int64_t
)0xFFFFFFFF);
6760 VFSATTR_RETURN(fsap
, f_iosize
, (size_t)cluster_max_io_size(mp
, 0));
6761 VFSATTR_RETURN(fsap
, f_blocks
, (u_int64_t
)hfsmp
->totalBlocks
);
6762 VFSATTR_RETURN(fsap
, f_bfree
, (u_int64_t
)hfs_freeblks(hfsmp
, 0));
6763 VFSATTR_RETURN(fsap
, f_bavail
, (u_int64_t
)hfs_freeblks(hfsmp
, 1));
6764 VFSATTR_RETURN(fsap
, f_bsize
, (u_int32_t
)vcb
->blockSize
);
6765 /* XXX needs clarification */
6766 VFSATTR_RETURN(fsap
, f_bused
, hfsmp
->totalBlocks
- hfs_freeblks(hfsmp
, 1));
6767 /* Maximum files is constrained by total blocks. */
6768 VFSATTR_RETURN(fsap
, f_files
, (u_int64_t
)(hfsmp
->totalBlocks
- 2));
6769 VFSATTR_RETURN(fsap
, f_ffree
, MIN((u_int64_t
)freeCNIDs
, (u_int64_t
)hfs_freeblks(hfsmp
, 1)));
6771 fsap
->f_fsid
.val
[0] = hfsmp
->hfs_raw_dev
;
6772 fsap
->f_fsid
.val
[1] = vfs_typenum(mp
);
6773 VFSATTR_SET_SUPPORTED(fsap
, f_fsid
);
6775 VFSATTR_RETURN(fsap
, f_signature
, vcb
->vcbSigWord
);
6776 VFSATTR_RETURN(fsap
, f_carbon_fsid
, 0);
6778 if (VFSATTR_IS_ACTIVE(fsap
, f_capabilities
)) {
6779 vol_capabilities_attr_t
*cap
;
6781 cap
= &fsap
->f_capabilities
;
6783 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
6784 cap
->capabilities
[VOL_CAPABILITIES_FORMAT
] =
6785 VOL_CAP_FMT_PERSISTENTOBJECTIDS
|
6786 VOL_CAP_FMT_CASE_PRESERVING
|
6787 VOL_CAP_FMT_FAST_STATFS
|
6788 VOL_CAP_FMT_HIDDEN_FILES
|
6789 VOL_CAP_FMT_PATH_FROM_ID
;
6791 cap
->capabilities
[VOL_CAPABILITIES_FORMAT
] =
6792 VOL_CAP_FMT_PERSISTENTOBJECTIDS
|
6793 VOL_CAP_FMT_SYMBOLICLINKS
|
6794 VOL_CAP_FMT_HARDLINKS
|
6795 VOL_CAP_FMT_JOURNAL
|
6796 VOL_CAP_FMT_ZERO_RUNS
|
6797 (hfsmp
->jnl
? VOL_CAP_FMT_JOURNAL_ACTIVE
: 0) |
6798 (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
? VOL_CAP_FMT_CASE_SENSITIVE
: 0) |
6799 VOL_CAP_FMT_CASE_PRESERVING
|
6800 VOL_CAP_FMT_FAST_STATFS
|
6801 VOL_CAP_FMT_2TB_FILESIZE
|
6802 VOL_CAP_FMT_HIDDEN_FILES
|
6804 VOL_CAP_FMT_PATH_FROM_ID
|
6805 VOL_CAP_FMT_DECMPFS_COMPRESSION
;
6807 VOL_CAP_FMT_PATH_FROM_ID
;
6810 cap
->capabilities
[VOL_CAPABILITIES_INTERFACES
] =
6811 VOL_CAP_INT_SEARCHFS
|
6812 VOL_CAP_INT_ATTRLIST
|
6813 VOL_CAP_INT_NFSEXPORT
|
6814 VOL_CAP_INT_READDIRATTR
|
6815 VOL_CAP_INT_EXCHANGEDATA
|
6816 VOL_CAP_INT_ALLOCATE
|
6817 VOL_CAP_INT_VOL_RENAME
|
6818 VOL_CAP_INT_ADVLOCK
|
6821 VOL_CAP_INT_EXTENDED_ATTR
|
6822 VOL_CAP_INT_NAMEDSTREAMS
;
6824 VOL_CAP_INT_EXTENDED_ATTR
;
6826 cap
->capabilities
[VOL_CAPABILITIES_RESERVED1
] = 0;
6827 cap
->capabilities
[VOL_CAPABILITIES_RESERVED2
] = 0;
6829 cap
->valid
[VOL_CAPABILITIES_FORMAT
] =
6830 VOL_CAP_FMT_PERSISTENTOBJECTIDS
|
6831 VOL_CAP_FMT_SYMBOLICLINKS
|
6832 VOL_CAP_FMT_HARDLINKS
|
6833 VOL_CAP_FMT_JOURNAL
|
6834 VOL_CAP_FMT_JOURNAL_ACTIVE
|
6835 VOL_CAP_FMT_NO_ROOT_TIMES
|
6836 VOL_CAP_FMT_SPARSE_FILES
|
6837 VOL_CAP_FMT_ZERO_RUNS
|
6838 VOL_CAP_FMT_CASE_SENSITIVE
|
6839 VOL_CAP_FMT_CASE_PRESERVING
|
6840 VOL_CAP_FMT_FAST_STATFS
|
6841 VOL_CAP_FMT_2TB_FILESIZE
|
6842 VOL_CAP_FMT_OPENDENYMODES
|
6843 VOL_CAP_FMT_HIDDEN_FILES
|
6845 VOL_CAP_FMT_PATH_FROM_ID
|
6846 VOL_CAP_FMT_DECMPFS_COMPRESSION
;
6848 VOL_CAP_FMT_PATH_FROM_ID
;
6850 cap
->valid
[VOL_CAPABILITIES_INTERFACES
] =
6851 VOL_CAP_INT_SEARCHFS
|
6852 VOL_CAP_INT_ATTRLIST
|
6853 VOL_CAP_INT_NFSEXPORT
|
6854 VOL_CAP_INT_READDIRATTR
|
6855 VOL_CAP_INT_EXCHANGEDATA
|
6856 VOL_CAP_INT_COPYFILE
|
6857 VOL_CAP_INT_ALLOCATE
|
6858 VOL_CAP_INT_VOL_RENAME
|
6859 VOL_CAP_INT_ADVLOCK
|
6861 VOL_CAP_INT_MANLOCK
|
6863 VOL_CAP_INT_EXTENDED_ATTR
|
6864 VOL_CAP_INT_NAMEDSTREAMS
;
6866 VOL_CAP_INT_EXTENDED_ATTR
;
6868 cap
->valid
[VOL_CAPABILITIES_RESERVED1
] = 0;
6869 cap
->valid
[VOL_CAPABILITIES_RESERVED2
] = 0;
6870 VFSATTR_SET_SUPPORTED(fsap
, f_capabilities
);
6872 if (VFSATTR_IS_ACTIVE(fsap
, f_attributes
)) {
6873 vol_attributes_attr_t
*attrp
= &fsap
->f_attributes
;
6875 attrp
->validattr
.commonattr
= HFS_ATTR_CMN_VOL_VALIDMASK
;
6876 attrp
->validattr
.volattr
= ATTR_VOL_VALIDMASK
& ~ATTR_VOL_INFO
;
6877 attrp
->validattr
.dirattr
= ATTR_DIR_VALIDMASK
;
6878 attrp
->validattr
.fileattr
= HFS_ATTR_FILE_VALIDMASK
;
6879 attrp
->validattr
.forkattr
= 0;
6881 attrp
->nativeattr
.commonattr
= HFS_ATTR_CMN_VOL_VALIDMASK
;
6882 attrp
->nativeattr
.volattr
= ATTR_VOL_VALIDMASK
& ~ATTR_VOL_INFO
;
6883 attrp
->nativeattr
.dirattr
= ATTR_DIR_VALIDMASK
;
6884 attrp
->nativeattr
.fileattr
= HFS_ATTR_FILE_VALIDMASK
;
6885 attrp
->nativeattr
.forkattr
= 0;
6886 VFSATTR_SET_SUPPORTED(fsap
, f_attributes
);
6888 fsap
->f_create_time
.tv_sec
= hfsmp
->hfs_itime
;
6889 fsap
->f_create_time
.tv_nsec
= 0;
6890 VFSATTR_SET_SUPPORTED(fsap
, f_create_time
);
6891 fsap
->f_modify_time
.tv_sec
= hfsmp
->vcbLsMod
;
6892 fsap
->f_modify_time
.tv_nsec
= 0;
6893 VFSATTR_SET_SUPPORTED(fsap
, f_modify_time
);
6895 fsap
->f_backup_time
.tv_sec
= hfsmp
->vcbVolBkUp
;
6896 fsap
->f_backup_time
.tv_nsec
= 0;
6897 VFSATTR_SET_SUPPORTED(fsap
, f_backup_time
);
6898 if (VFSATTR_IS_ACTIVE(fsap
, f_fssubtype
)) {
6899 u_int16_t subtype
= 0;
6902 * Subtypes (flavors) for HFS
6903 * 0: Mac OS Extended
6904 * 1: Mac OS Extended (Journaled)
6905 * 2: Mac OS Extended (Case Sensitive)
6906 * 3: Mac OS Extended (Case Sensitive, Journaled)
6908 * 128: Mac OS Standard
6911 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
6912 subtype
= HFS_SUBTYPE_STANDARDHFS
;
6913 } else /* HFS Plus */ {
6915 subtype
|= HFS_SUBTYPE_JOURNALED
;
6916 if (hfsmp
->hfs_flags
& HFS_CASE_SENSITIVE
)
6917 subtype
|= HFS_SUBTYPE_CASESENSITIVE
;
6919 fsap
->f_fssubtype
= subtype
;
6920 VFSATTR_SET_SUPPORTED(fsap
, f_fssubtype
);
6923 if (VFSATTR_IS_ACTIVE(fsap
, f_vol_name
)) {
6924 strlcpy(fsap
->f_vol_name
, (char *) hfsmp
->vcbVN
, MAXPATHLEN
);
6925 VFSATTR_SET_SUPPORTED(fsap
, f_vol_name
);
6927 if (VFSATTR_IS_ACTIVE(fsap
, f_uuid
)) {
6928 hfs_getvoluuid(hfsmp
, fsap
->f_uuid
);
6929 VFSATTR_SET_SUPPORTED(fsap
, f_uuid
);
6935 * Perform a volume rename. Requires the FS' root vp.
6938 hfs_rename_volume(struct vnode
*vp
, const char *name
, proc_t p
)
6940 ExtendedVCB
*vcb
= VTOVCB(vp
);
6941 struct cnode
*cp
= VTOC(vp
);
6942 struct hfsmount
*hfsmp
= VTOHFS(vp
);
6943 struct cat_desc to_desc
;
6944 struct cat_desc todir_desc
;
6945 struct cat_desc new_desc
;
6946 cat_cookie_t cookie
;
6949 char converted_volname
[256];
6950 size_t volname_length
= 0;
6951 size_t conv_volname_length
= 0;
6955 * Ignore attempts to rename a volume to a zero-length name.
6960 bzero(&to_desc
, sizeof(to_desc
));
6961 bzero(&todir_desc
, sizeof(todir_desc
));
6962 bzero(&new_desc
, sizeof(new_desc
));
6963 bzero(&cookie
, sizeof(cookie
));
6965 todir_desc
.cd_parentcnid
= kHFSRootParentID
;
6966 todir_desc
.cd_cnid
= kHFSRootFolderID
;
6967 todir_desc
.cd_flags
= CD_ISDIR
;
6969 to_desc
.cd_nameptr
= (const u_int8_t
*)name
;
6970 to_desc
.cd_namelen
= strlen(name
);
6971 to_desc
.cd_parentcnid
= kHFSRootParentID
;
6972 to_desc
.cd_cnid
= cp
->c_cnid
;
6973 to_desc
.cd_flags
= CD_ISDIR
;
6975 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
)) == 0) {
6976 if ((error
= hfs_start_transaction(hfsmp
)) == 0) {
6977 if ((error
= cat_preflight(hfsmp
, CAT_RENAME
, &cookie
, p
)) == 0) {
6978 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_EXCLUSIVE_LOCK
);
6980 error
= cat_rename(hfsmp
, &cp
->c_desc
, &todir_desc
, &to_desc
, &new_desc
);
6983 * If successful, update the name in the VCB, ensure it's terminated.
6986 strlcpy((char *)vcb
->vcbVN
, name
, sizeof(vcb
->vcbVN
));
6987 volname_length
= strlen ((const char*)vcb
->vcbVN
);
6988 #define DKIOCCSSETLVNAME _IOW('d', 198, char[1024])
6989 /* Send the volume name down to CoreStorage if necessary */
6990 error
= utf8_normalizestr(vcb
->vcbVN
, volname_length
, (u_int8_t
*)converted_volname
, &conv_volname_length
, 256, UTF_PRECOMPOSED
);
6992 (void) VNOP_IOCTL (hfsmp
->hfs_devvp
, DKIOCCSSETLVNAME
, converted_volname
, 0, vfs_context_current());
6997 hfs_systemfile_unlock(hfsmp
, lockflags
);
6998 cat_postflight(hfsmp
, &cookie
, p
);
7002 (void) hfs_flushvolumeheader(hfsmp
, MNT_WAIT
, 0);
7004 hfs_end_transaction(hfsmp
);
7007 /* Release old allocated name buffer */
7008 if (cp
->c_desc
.cd_flags
& CD_HASBUF
) {
7009 const char *tmp_name
= (const char *)cp
->c_desc
.cd_nameptr
;
7011 cp
->c_desc
.cd_nameptr
= 0;
7012 cp
->c_desc
.cd_namelen
= 0;
7013 cp
->c_desc
.cd_flags
&= ~CD_HASBUF
;
7014 vfs_removename(tmp_name
);
7016 /* Update cnode's catalog descriptor */
7017 replace_desc(cp
, &new_desc
);
7018 vcb
->volumeNameEncodingHint
= new_desc
.cd_encoding
;
7019 cp
->c_touch_chgtime
= TRUE
;
7029 * Get file system attributes.
7032 hfs_vfs_setattr(struct mount
*mp
, struct vfs_attr
*fsap
, __unused vfs_context_t context
)
7034 kauth_cred_t cred
= vfs_context_ucred(context
);
7038 * Must be superuser or owner of filesystem to change volume attributes
7040 if (!kauth_cred_issuser(cred
) && (kauth_cred_getuid(cred
) != vfs_statfs(mp
)->f_owner
))
7043 if (VFSATTR_IS_ACTIVE(fsap
, f_vol_name
)) {
7046 error
= hfs_vfs_root(mp
, &root_vp
, context
);
7050 error
= hfs_rename_volume(root_vp
, fsap
->f_vol_name
, vfs_context_proc(context
));
7051 (void) vnode_put(root_vp
);
7055 VFSATTR_SET_SUPPORTED(fsap
, f_vol_name
);
7062 /* If a runtime corruption is detected, set the volume inconsistent
7063 * bit in the volume attributes. The volume inconsistent bit is a persistent
7064 * bit which represents that the volume is corrupt and needs repair.
7065 * The volume inconsistent bit can be set from the kernel when it detects
7066 * runtime corruption or from file system repair utilities like fsck_hfs when
7067 * a repair operation fails. The bit should be cleared only from file system
7068 * verify/repair utility like fsck_hfs when a verify/repair succeeds.
7070 void hfs_mark_volume_inconsistent(struct hfsmount
*hfsmp
)
7072 HFS_MOUNT_LOCK(hfsmp
, TRUE
);
7073 if ((hfsmp
->vcbAtrb
& kHFSVolumeInconsistentMask
) == 0) {
7074 hfsmp
->vcbAtrb
|= kHFSVolumeInconsistentMask
;
7075 MarkVCBDirty(hfsmp
);
7077 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
)==0) {
7078 /* Log information to ASL log */
7079 fslog_fs_corrupt(hfsmp
->hfs_mp
);
7080 printf("hfs: Runtime corruption detected on %s, fsck will be forced on next mount.\n", hfsmp
->vcbVN
);
7082 HFS_MOUNT_UNLOCK(hfsmp
, TRUE
);
7085 /* Replay the journal on the device node provided. Returns zero if
7086 * journal replay succeeded or no journal was supposed to be replayed.
7088 static int hfs_journal_replay(vnode_t devvp
, vfs_context_t context
)
7091 struct mount
*mp
= NULL
;
7092 struct hfs_mount_args
*args
= NULL
;
7094 /* Replay allowed only on raw devices */
7095 if (!vnode_ischr(devvp
) && !vnode_isblk(devvp
)) {
7100 /* Create dummy mount structures */
7101 MALLOC(mp
, struct mount
*, sizeof(struct mount
), M_TEMP
, M_WAITOK
);
7106 bzero(mp
, sizeof(struct mount
));
7107 mount_lock_init(mp
);
7109 MALLOC(args
, struct hfs_mount_args
*, sizeof(struct hfs_mount_args
), M_TEMP
, M_WAITOK
);
7114 bzero(args
, sizeof(struct hfs_mount_args
));
7116 retval
= hfs_mountfs(devvp
, mp
, args
, 1, context
);
7117 buf_flushdirtyblks(devvp
, TRUE
, 0, "hfs_journal_replay");
7119 /* FSYNC the devnode to be sure all data has been flushed */
7120 retval
= VNOP_FSYNC(devvp
, MNT_WAIT
, context
);
7124 mount_lock_destroy(mp
);
7134 * hfs vfs operations.
7136 struct vfsops hfs_vfsops
= {
7142 hfs_vfs_getattr
, /* was hfs_statfs */