]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsops.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsops.c
1 /*
2 * Copyright (c) 1999-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
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.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
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.
52 *
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
63 * SUCH DAMAGE.
64 *
65 * hfs_vfsops.c
66 * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
67 *
68 * (c) Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
69 *
70 * hfs_vfsops.c -- VFS layer for loadable HFS file system.
71 *
72 */
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/kauth.h>
76
77 #include <sys/ubc.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>
83 #include <sys/stat.h>
84 #include <sys/quota.h>
85 #include <sys/disk.h>
86 #include <sys/paths.h>
87 #include <sys/utfconv.h>
88 #include <sys/kdebug.h>
89 #include <sys/fslog.h>
90
91 #include <kern/locks.h>
92
93 #include <vfs/vfs_journal.h>
94
95 #include <miscfs/specfs/specdev.h>
96 #include <hfs/hfs_mount.h>
97
98 #include <libkern/crypto/md5.h>
99 #include <uuid/uuid.h>
100
101 #include "hfs.h"
102 #include "hfs_catalog.h"
103 #include "hfs_cnode.h"
104 #include "hfs_dbg.h"
105 #include "hfs_endian.h"
106 #include "hfs_hotfiles.h"
107 #include "hfs_quota.h"
108
109 #include "hfscommon/headers/FileMgrInternal.h"
110 #include "hfscommon/headers/BTreesInternal.h"
111
112 #if HFS_DIAGNOSTIC
113 int hfs_dbg_all = 0;
114 int hfs_dbg_err = 0;
115 #endif
116
117
118 lck_grp_attr_t * hfs_group_attr;
119 lck_attr_t * hfs_lock_attr;
120 lck_grp_t * hfs_mutex_group;
121 lck_grp_t * hfs_rwlock_group;
122
123 extern struct vnodeopv_desc hfs_vnodeop_opv_desc;
124 extern struct vnodeopv_desc hfs_std_vnodeop_opv_desc;
125
126 /* not static so we can re-use in hfs_readwrite.c for build_path calls */
127 int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
128
129 static int hfs_changefs(struct mount *mp, struct hfs_mount_args *args);
130 static int hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp, vfs_context_t context);
131 static int hfs_flushfiles(struct mount *, int, struct proc *);
132 static int hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush);
133 static int hfs_getmountpoint(struct vnode *vp, struct hfsmount **hfsmpp);
134 static int hfs_init(struct vfsconf *vfsp);
135 static int hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context);
136 static int hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args, int journal_replay_only, vfs_context_t context);
137 static int hfs_reload(struct mount *mp);
138 static int hfs_vfs_root(struct mount *mp, struct vnode **vpp, vfs_context_t context);
139 static int hfs_quotactl(struct mount *, int, uid_t, caddr_t, vfs_context_t context);
140 static int hfs_start(struct mount *mp, int flags, vfs_context_t context);
141 static int hfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, vfs_context_t context);
142 static int hfs_sync(struct mount *mp, int waitfor, vfs_context_t context);
143 static int hfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
144 user_addr_t newp, size_t newlen, vfs_context_t context);
145 static int hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context);
146 static int hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
147
148 static int hfs_reclaimspace(struct hfsmount *hfsmp, u_int32_t startblk, u_int32_t reclaimblks, vfs_context_t context);
149 static int hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk,
150 u_int32_t catblks, u_int32_t fileID, int rsrcfork);
151 static int hfs_journal_replay(vnode_t devvp, vfs_context_t context);
152
153
154 /*
155 * Called by vfs_mountroot when mounting HFS Plus as root.
156 */
157
158 __private_extern__
159 int
160 hfs_mountroot(mount_t mp, vnode_t rvp, vfs_context_t context)
161 {
162 struct hfsmount *hfsmp;
163 ExtendedVCB *vcb;
164 struct vfsstatfs *vfsp;
165 int error;
166
167 if ((error = hfs_mountfs(rvp, mp, NULL, 0, context)))
168 return (error);
169
170 /* Init hfsmp */
171 hfsmp = VFSTOHFS(mp);
172
173 hfsmp->hfs_uid = UNKNOWNUID;
174 hfsmp->hfs_gid = UNKNOWNGID;
175 hfsmp->hfs_dir_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
176 hfsmp->hfs_file_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
177
178 /* Establish the free block reserve. */
179 vcb = HFSTOVCB(hfsmp);
180 vcb->reserveBlocks = ((u_int64_t)vcb->totalBlocks * HFS_MINFREE) / 100;
181 vcb->reserveBlocks = MIN(vcb->reserveBlocks, HFS_MAXRESERVE / vcb->blockSize);
182
183 vfsp = vfs_statfs(mp);
184 (void)hfs_statfs(mp, vfsp, NULL);
185
186 return (0);
187 }
188
189
190 /*
191 * VFS Operations.
192 *
193 * mount system call
194 */
195
196 static int
197 hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context)
198 {
199 struct proc *p = vfs_context_proc(context);
200 struct hfsmount *hfsmp = NULL;
201 struct hfs_mount_args args;
202 int retval = E_NONE;
203 u_int32_t cmdflags;
204
205 if ((retval = copyin(data, (caddr_t)&args, sizeof(args)))) {
206 return (retval);
207 }
208 cmdflags = (u_int32_t)vfs_flags(mp) & MNT_CMDFLAGS;
209 if (cmdflags & MNT_UPDATE) {
210 hfsmp = VFSTOHFS(mp);
211
212 /* Reload incore data after an fsck. */
213 if (cmdflags & MNT_RELOAD) {
214 if (vfs_isrdonly(mp))
215 return hfs_reload(mp);
216 else
217 return (EINVAL);
218 }
219
220 /* Change to a read-only file system. */
221 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
222 vfs_isrdonly(mp)) {
223 int flags;
224
225 /* Set flag to indicate that a downgrade to read-only
226 * is in progress and therefore block any further
227 * modifications to the file system.
228 */
229 hfs_global_exclusive_lock_acquire(hfsmp);
230 hfsmp->hfs_flags |= HFS_RDONLY_DOWNGRADE;
231 hfsmp->hfs_downgrading_proc = current_thread();
232 hfs_global_exclusive_lock_release(hfsmp);
233
234 /* use VFS_SYNC to push out System (btree) files */
235 retval = VFS_SYNC(mp, MNT_WAIT, context);
236 if (retval && ((cmdflags & MNT_FORCE) == 0)) {
237 hfsmp->hfs_flags &= ~HFS_RDONLY_DOWNGRADE;
238 hfsmp->hfs_downgrading_proc = NULL;
239 goto out;
240 }
241
242 flags = WRITECLOSE;
243 if (cmdflags & MNT_FORCE)
244 flags |= FORCECLOSE;
245
246 if ((retval = hfs_flushfiles(mp, flags, p))) {
247 hfsmp->hfs_flags &= ~HFS_RDONLY_DOWNGRADE;
248 hfsmp->hfs_downgrading_proc = NULL;
249 goto out;
250 }
251
252 /* mark the volume cleanly unmounted */
253 hfsmp->vcbAtrb |= kHFSVolumeUnmountedMask;
254 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
255 hfsmp->hfs_flags |= HFS_READ_ONLY;
256
257 /* also get the volume bitmap blocks */
258 if (!retval) {
259 if (vnode_mount(hfsmp->hfs_devvp) == mp) {
260 retval = hfs_fsync(hfsmp->hfs_devvp, MNT_WAIT, 0, p);
261 } else {
262 vnode_get(hfsmp->hfs_devvp);
263 retval = VNOP_FSYNC(hfsmp->hfs_devvp, MNT_WAIT, context);
264 vnode_put(hfsmp->hfs_devvp);
265 }
266 }
267 if (retval) {
268 hfsmp->hfs_flags &= ~HFS_RDONLY_DOWNGRADE;
269 hfsmp->hfs_downgrading_proc = NULL;
270 hfsmp->hfs_flags &= ~HFS_READ_ONLY;
271 goto out;
272 }
273 if (hfsmp->jnl) {
274 hfs_global_exclusive_lock_acquire(hfsmp);
275
276 journal_close(hfsmp->jnl);
277 hfsmp->jnl = NULL;
278
279 // Note: we explicitly don't want to shutdown
280 // access to the jvp because we may need
281 // it later if we go back to being read-write.
282
283 hfs_global_exclusive_lock_release(hfsmp);
284 }
285
286 hfsmp->hfs_downgrading_proc = NULL;
287 }
288
289 /* Change to a writable file system. */
290 if (vfs_iswriteupgrade(mp)) {
291
292 /*
293 * On inconsistent disks, do not allow read-write mount
294 * unless it is the boot volume being mounted.
295 */
296 if (!(vfs_flags(mp) & MNT_ROOTFS) &&
297 (hfsmp->vcbAtrb & kHFSVolumeInconsistentMask)) {
298 retval = EINVAL;
299 goto out;
300 }
301
302 // If the journal was shut-down previously because we were
303 // asked to be read-only, let's start it back up again now
304
305 if ( (HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask)
306 && hfsmp->jnl == NULL
307 && hfsmp->jvp != NULL) {
308 int jflags;
309
310 if (hfsmp->hfs_flags & HFS_NEED_JNL_RESET) {
311 jflags = JOURNAL_RESET;
312 } else {
313 jflags = 0;
314 }
315
316 hfs_global_exclusive_lock_acquire(hfsmp);
317
318 hfsmp->jnl = journal_open(hfsmp->jvp,
319 (hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset,
320 hfsmp->jnl_size,
321 hfsmp->hfs_devvp,
322 hfsmp->hfs_logical_block_size,
323 jflags,
324 0,
325 hfs_sync_metadata, hfsmp->hfs_mp);
326
327 hfs_global_exclusive_lock_release(hfsmp);
328
329 if (hfsmp->jnl == NULL) {
330 retval = EINVAL;
331 goto out;
332 } else {
333 hfsmp->hfs_flags &= ~HFS_NEED_JNL_RESET;
334 }
335
336 }
337
338 /* See if we need to erase unused Catalog nodes due to <rdar://problem/6947811>. */
339 retval = hfs_erase_unused_nodes(hfsmp);
340 if (retval != E_NONE)
341 goto out;
342
343 /* Only clear HFS_READ_ONLY after a successful write */
344 hfsmp->hfs_flags &= ~HFS_READ_ONLY;
345
346 /* If this mount point was downgraded from read-write
347 * to read-only, clear that information as we are now
348 * moving back to read-write.
349 */
350 hfsmp->hfs_flags &= ~HFS_RDONLY_DOWNGRADE;
351 hfsmp->hfs_downgrading_proc = NULL;
352
353 /* mark the volume dirty (clear clean unmount bit) */
354 hfsmp->vcbAtrb &= ~kHFSVolumeUnmountedMask;
355
356 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
357 if (retval != E_NONE)
358 goto out;
359
360 if (!(hfsmp->hfs_flags & (HFS_READ_ONLY | HFS_STANDARD))) {
361 /* Setup private/hidden directories for hardlinks. */
362 hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
363 hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
364
365 hfs_remove_orphans(hfsmp);
366
367 /*
368 * Allow hot file clustering if conditions allow.
369 */
370 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
371 (void) hfs_recording_init(hfsmp);
372 }
373 /* Force ACLs on HFS+ file systems. */
374 if (vfs_extendedsecurity(HFSTOVFS(hfsmp)) == 0) {
375 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
376 }
377 }
378 }
379
380 /* Update file system parameters. */
381 retval = hfs_changefs(mp, &args);
382
383 } else /* not an update request */ {
384
385 /* Set the mount flag to indicate that we support volfs */
386 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_DOVOLFS));
387
388 retval = hfs_mountfs(devvp, mp, &args, 0, context);
389 }
390 out:
391 if (retval == 0) {
392 (void)hfs_statfs(mp, vfs_statfs(mp), context);
393 }
394 return (retval);
395 }
396
397
398 struct hfs_changefs_cargs {
399 struct hfsmount *hfsmp;
400 int namefix;
401 int permfix;
402 int permswitch;
403 };
404
405 static int
406 hfs_changefs_callback(struct vnode *vp, void *cargs)
407 {
408 ExtendedVCB *vcb;
409 struct cnode *cp;
410 struct cat_desc cndesc;
411 struct cat_attr cnattr;
412 struct hfs_changefs_cargs *args;
413 int lockflags;
414 int error;
415
416 args = (struct hfs_changefs_cargs *)cargs;
417
418 cp = VTOC(vp);
419 vcb = HFSTOVCB(args->hfsmp);
420
421 lockflags = hfs_systemfile_lock(args->hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
422 error = cat_lookup(args->hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL, NULL);
423 hfs_systemfile_unlock(args->hfsmp, lockflags);
424 if (error) {
425 /*
426 * If we couldn't find this guy skip to the next one
427 */
428 if (args->namefix)
429 cache_purge(vp);
430
431 return (VNODE_RETURNED);
432 }
433 /*
434 * Get the real uid/gid and perm mask from disk.
435 */
436 if (args->permswitch || args->permfix) {
437 cp->c_uid = cnattr.ca_uid;
438 cp->c_gid = cnattr.ca_gid;
439 cp->c_mode = cnattr.ca_mode;
440 }
441 /*
442 * If we're switching name converters then...
443 * Remove the existing entry from the namei cache.
444 * Update name to one based on new encoder.
445 */
446 if (args->namefix) {
447 cache_purge(vp);
448 replace_desc(cp, &cndesc);
449
450 if (cndesc.cd_cnid == kHFSRootFolderID) {
451 strlcpy((char *)vcb->vcbVN, (const char *)cp->c_desc.cd_nameptr, NAME_MAX+1);
452 cp->c_desc.cd_encoding = args->hfsmp->hfs_encoding;
453 }
454 } else {
455 cat_releasedesc(&cndesc);
456 }
457 return (VNODE_RETURNED);
458 }
459
460 /* Change fs mount parameters */
461 static int
462 hfs_changefs(struct mount *mp, struct hfs_mount_args *args)
463 {
464 int retval = 0;
465 int namefix, permfix, permswitch;
466 struct hfsmount *hfsmp;
467 ExtendedVCB *vcb;
468 hfs_to_unicode_func_t get_unicode_func;
469 unicode_to_hfs_func_t get_hfsname_func;
470 u_int32_t old_encoding = 0;
471 struct hfs_changefs_cargs cargs;
472 u_int32_t mount_flags;
473
474 hfsmp = VFSTOHFS(mp);
475 vcb = HFSTOVCB(hfsmp);
476 mount_flags = (unsigned int)vfs_flags(mp);
477
478 hfsmp->hfs_flags |= HFS_IN_CHANGEFS;
479
480 permswitch = (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) &&
481 ((mount_flags & MNT_UNKNOWNPERMISSIONS) == 0)) ||
482 (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) == 0) &&
483 (mount_flags & MNT_UNKNOWNPERMISSIONS)));
484
485 /* The root filesystem must operate with actual permissions: */
486 if (permswitch && (mount_flags & MNT_ROOTFS) && (mount_flags & MNT_UNKNOWNPERMISSIONS)) {
487 vfs_clearflags(mp, (u_int64_t)((unsigned int)MNT_UNKNOWNPERMISSIONS)); /* Just say "No". */
488 retval = EINVAL;
489 goto exit;
490 }
491 if (mount_flags & MNT_UNKNOWNPERMISSIONS)
492 hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS;
493 else
494 hfsmp->hfs_flags &= ~HFS_UNKNOWN_PERMS;
495
496 namefix = permfix = 0;
497
498 /*
499 * Tracking of hot files requires up-to-date access times. So if
500 * access time updates are disabled, we must also disable hot files.
501 */
502 if (mount_flags & MNT_NOATIME) {
503 (void) hfs_recording_suspend(hfsmp);
504 }
505
506 /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */
507 if (args->hfs_timezone.tz_minuteswest != VNOVAL) {
508 gTimeZone = args->hfs_timezone;
509 }
510
511 /* Change the default uid, gid and/or mask */
512 if ((args->hfs_uid != (uid_t)VNOVAL) && (hfsmp->hfs_uid != args->hfs_uid)) {
513 hfsmp->hfs_uid = args->hfs_uid;
514 if (vcb->vcbSigWord == kHFSPlusSigWord)
515 ++permfix;
516 }
517 if ((args->hfs_gid != (gid_t)VNOVAL) && (hfsmp->hfs_gid != args->hfs_gid)) {
518 hfsmp->hfs_gid = args->hfs_gid;
519 if (vcb->vcbSigWord == kHFSPlusSigWord)
520 ++permfix;
521 }
522 if (args->hfs_mask != (mode_t)VNOVAL) {
523 if (hfsmp->hfs_dir_mask != (args->hfs_mask & ALLPERMS)) {
524 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
525 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
526 if ((args->flags != VNOVAL) && (args->flags & HFSFSMNT_NOXONFILES))
527 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
528 if (vcb->vcbSigWord == kHFSPlusSigWord)
529 ++permfix;
530 }
531 }
532
533 /* Change the hfs encoding value (hfs only) */
534 if ((vcb->vcbSigWord == kHFSSigWord) &&
535 (args->hfs_encoding != (u_int32_t)VNOVAL) &&
536 (hfsmp->hfs_encoding != args->hfs_encoding)) {
537
538 retval = hfs_getconverter(args->hfs_encoding, &get_unicode_func, &get_hfsname_func);
539 if (retval)
540 goto exit;
541
542 /*
543 * Connect the new hfs_get_unicode converter but leave
544 * the old hfs_get_hfsname converter in place so that
545 * we can lookup existing vnodes to get their correctly
546 * encoded names.
547 *
548 * When we're all finished, we can then connect the new
549 * hfs_get_hfsname converter and release our interest
550 * in the old converters.
551 */
552 hfsmp->hfs_get_unicode = get_unicode_func;
553 old_encoding = hfsmp->hfs_encoding;
554 hfsmp->hfs_encoding = args->hfs_encoding;
555 ++namefix;
556 }
557
558 if (!(namefix || permfix || permswitch))
559 goto exit;
560
561 /* XXX 3762912 hack to support HFS filesystem 'owner' */
562 if (permfix)
563 vfs_setowner(mp,
564 hfsmp->hfs_uid == UNKNOWNUID ? KAUTH_UID_NONE : hfsmp->hfs_uid,
565 hfsmp->hfs_gid == UNKNOWNGID ? KAUTH_GID_NONE : hfsmp->hfs_gid);
566
567 /*
568 * For each active vnode fix things that changed
569 *
570 * Note that we can visit a vnode more than once
571 * and we can race with fsync.
572 *
573 * hfs_changefs_callback will be called for each vnode
574 * hung off of this mount point
575 *
576 * The vnode will be properly referenced and unreferenced
577 * around the callback
578 */
579 cargs.hfsmp = hfsmp;
580 cargs.namefix = namefix;
581 cargs.permfix = permfix;
582 cargs.permswitch = permswitch;
583
584 vnode_iterate(mp, 0, hfs_changefs_callback, (void *)&cargs);
585
586 /*
587 * If we're switching name converters we can now
588 * connect the new hfs_get_hfsname converter and
589 * release our interest in the old converters.
590 */
591 if (namefix) {
592 hfsmp->hfs_get_hfsname = get_hfsname_func;
593 vcb->volumeNameEncodingHint = args->hfs_encoding;
594 (void) hfs_relconverter(old_encoding);
595 }
596 exit:
597 hfsmp->hfs_flags &= ~HFS_IN_CHANGEFS;
598 return (retval);
599 }
600
601
602 struct hfs_reload_cargs {
603 struct hfsmount *hfsmp;
604 int error;
605 };
606
607 static int
608 hfs_reload_callback(struct vnode *vp, void *cargs)
609 {
610 struct cnode *cp;
611 struct hfs_reload_cargs *args;
612 int lockflags;
613
614 args = (struct hfs_reload_cargs *)cargs;
615 /*
616 * flush all the buffers associated with this node
617 */
618 (void) buf_invalidateblks(vp, 0, 0, 0);
619
620 cp = VTOC(vp);
621 /*
622 * Remove any directory hints
623 */
624 if (vnode_isdir(vp))
625 hfs_reldirhints(cp, 0);
626
627 /*
628 * Re-read cnode data for all active vnodes (non-metadata files).
629 */
630 if (!vnode_issystem(vp) && !VNODE_IS_RSRC(vp)) {
631 struct cat_fork *datafork;
632 struct cat_desc desc;
633
634 datafork = cp->c_datafork ? &cp->c_datafork->ff_data : NULL;
635
636 /* lookup by fileID since name could have changed */
637 lockflags = hfs_systemfile_lock(args->hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
638 args->error = cat_idlookup(args->hfsmp, cp->c_fileid, 0, &desc, &cp->c_attr, datafork);
639 hfs_systemfile_unlock(args->hfsmp, lockflags);
640 if (args->error) {
641 return (VNODE_RETURNED_DONE);
642 }
643
644 /* update cnode's catalog descriptor */
645 (void) replace_desc(cp, &desc);
646 }
647 return (VNODE_RETURNED);
648 }
649
650 /*
651 * Reload all incore data for a filesystem (used after running fsck on
652 * the root filesystem and finding things to fix). The filesystem must
653 * be mounted read-only.
654 *
655 * Things to do to update the mount:
656 * invalidate all cached meta-data.
657 * invalidate all inactive vnodes.
658 * invalidate all cached file data.
659 * re-read volume header from disk.
660 * re-load meta-file info (extents, file size).
661 * re-load B-tree header data.
662 * re-read cnode data for all active vnodes.
663 */
664 static int
665 hfs_reload(struct mount *mountp)
666 {
667 register struct vnode *devvp;
668 struct buf *bp;
669 int error, i;
670 struct hfsmount *hfsmp;
671 struct HFSPlusVolumeHeader *vhp;
672 ExtendedVCB *vcb;
673 struct filefork *forkp;
674 struct cat_desc cndesc;
675 struct hfs_reload_cargs args;
676 daddr64_t priIDSector;
677
678 hfsmp = VFSTOHFS(mountp);
679 vcb = HFSTOVCB(hfsmp);
680
681 if (vcb->vcbSigWord == kHFSSigWord)
682 return (EINVAL); /* rooting from HFS is not supported! */
683
684 /*
685 * Invalidate all cached meta-data.
686 */
687 devvp = hfsmp->hfs_devvp;
688 if (buf_invalidateblks(devvp, 0, 0, 0))
689 panic("hfs_reload: dirty1");
690
691 args.hfsmp = hfsmp;
692 args.error = 0;
693 /*
694 * hfs_reload_callback will be called for each vnode
695 * hung off of this mount point that can't be recycled...
696 * vnode_iterate will recycle those that it can (the VNODE_RELOAD option)
697 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
698 * properly referenced and unreferenced around the callback
699 */
700 vnode_iterate(mountp, VNODE_RELOAD | VNODE_WAIT, hfs_reload_callback, (void *)&args);
701
702 if (args.error)
703 return (args.error);
704
705 /*
706 * Re-read VolumeHeader from disk.
707 */
708 priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
709 HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size));
710
711 error = (int)buf_meta_bread(hfsmp->hfs_devvp,
712 HFS_PHYSBLK_ROUNDDOWN(priIDSector, hfsmp->hfs_log_per_phys),
713 hfsmp->hfs_physical_block_size, NOCRED, &bp);
714 if (error) {
715 if (bp != NULL)
716 buf_brelse(bp);
717 return (error);
718 }
719
720 vhp = (HFSPlusVolumeHeader *) (buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
721
722 /* Do a quick sanity check */
723 if ((SWAP_BE16(vhp->signature) != kHFSPlusSigWord &&
724 SWAP_BE16(vhp->signature) != kHFSXSigWord) ||
725 (SWAP_BE16(vhp->version) != kHFSPlusVersion &&
726 SWAP_BE16(vhp->version) != kHFSXVersion) ||
727 SWAP_BE32(vhp->blockSize) != vcb->blockSize) {
728 buf_brelse(bp);
729 return (EIO);
730 }
731
732 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
733 vcb->vcbAtrb = SWAP_BE32 (vhp->attributes);
734 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
735 vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize);
736 vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID);
737 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
738 vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount);
739 vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount);
740 vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount);
741 HFS_UPDATE_NEXT_ALLOCATION(vcb, SWAP_BE32 (vhp->nextAllocation));
742 vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks);
743 vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks);
744 vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap);
745 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
746 vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* hfs+ create date is in local time */
747
748 /*
749 * Re-load meta-file vnode data (extent info, file size, etc).
750 */
751 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
752 for (i = 0; i < kHFSPlusExtentDensity; i++) {
753 forkp->ff_extents[i].startBlock =
754 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
755 forkp->ff_extents[i].blockCount =
756 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
757 }
758 forkp->ff_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
759 forkp->ff_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
760 forkp->ff_clumpsize = SWAP_BE32 (vhp->extentsFile.clumpSize);
761
762
763 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
764 for (i = 0; i < kHFSPlusExtentDensity; i++) {
765 forkp->ff_extents[i].startBlock =
766 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
767 forkp->ff_extents[i].blockCount =
768 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
769 }
770 forkp->ff_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
771 forkp->ff_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
772 forkp->ff_clumpsize = SWAP_BE32 (vhp->catalogFile.clumpSize);
773
774 if (hfsmp->hfs_attribute_vp) {
775 forkp = VTOF(hfsmp->hfs_attribute_vp);
776 for (i = 0; i < kHFSPlusExtentDensity; i++) {
777 forkp->ff_extents[i].startBlock =
778 SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
779 forkp->ff_extents[i].blockCount =
780 SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
781 }
782 forkp->ff_size = SWAP_BE64 (vhp->attributesFile.logicalSize);
783 forkp->ff_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks);
784 forkp->ff_clumpsize = SWAP_BE32 (vhp->attributesFile.clumpSize);
785 }
786
787 forkp = VTOF((struct vnode *)vcb->allocationsRefNum);
788 for (i = 0; i < kHFSPlusExtentDensity; i++) {
789 forkp->ff_extents[i].startBlock =
790 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
791 forkp->ff_extents[i].blockCount =
792 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
793 }
794 forkp->ff_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
795 forkp->ff_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
796 forkp->ff_clumpsize = SWAP_BE32 (vhp->allocationFile.clumpSize);
797
798 buf_brelse(bp);
799 vhp = NULL;
800
801 /*
802 * Re-load B-tree header data
803 */
804 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
805 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
806 return (error);
807
808 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
809 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
810 return (error);
811
812 if (hfsmp->hfs_attribute_vp) {
813 forkp = VTOF(hfsmp->hfs_attribute_vp);
814 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
815 return (error);
816 }
817
818 /* Reload the volume name */
819 if ((error = cat_idlookup(hfsmp, kHFSRootFolderID, 0, &cndesc, NULL, NULL)))
820 return (error);
821 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
822 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
823 cat_releasedesc(&cndesc);
824
825 /* Re-establish private/hidden directories. */
826 hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
827 hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
828
829 /* In case any volume information changed to trigger a notification */
830 hfs_generate_volume_notifications(hfsmp);
831
832 return (0);
833 }
834
835
836
837 static void
838 hfs_syncer(void *arg0, void *unused)
839 {
840 #pragma unused(unused)
841
842 struct hfsmount *hfsmp = arg0;
843 clock_sec_t secs;
844 clock_usec_t usecs;
845 uint32_t delay = HFS_META_DELAY;
846 uint64_t now;
847 static int no_max=1;
848
849 clock_get_calendar_microtime(&secs, &usecs);
850 now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
851
852 //
853 // If the amount of pending writes is more than our limit, wait
854 // for 2/3 of it to drain and then flush the journal.
855 //
856 if (hfsmp->hfs_mp->mnt_pending_write_size > hfsmp->hfs_max_pending_io) {
857 int counter=0;
858 uint64_t pending_io, start, rate;
859
860 no_max = 0;
861
862 hfs_start_transaction(hfsmp); // so we hold off any new i/o's
863
864 pending_io = hfsmp->hfs_mp->mnt_pending_write_size;
865
866 clock_get_calendar_microtime(&secs, &usecs);
867 start = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
868
869 while(hfsmp->hfs_mp->mnt_pending_write_size > (pending_io/3) && counter++ < 500) {
870 tsleep((caddr_t)hfsmp, PRIBIO, "hfs-wait-for-io-to-drain", 10);
871 }
872
873 if (counter >= 500) {
874 printf("hfs: timed out waiting for io to drain (%lld)\n", (int64_t)hfsmp->hfs_mp->mnt_pending_write_size);
875 }
876
877 if (hfsmp->jnl) {
878 journal_flush(hfsmp->jnl);
879 } else {
880 hfs_sync(hfsmp->hfs_mp, MNT_WAIT, vfs_context_kernel());
881 }
882
883 clock_get_calendar_microtime(&secs, &usecs);
884 now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
885 hfsmp->hfs_last_sync_time = now;
886 rate = ((pending_io * 1000000ULL) / (now - start)); // yields bytes per second
887
888 hfs_end_transaction(hfsmp);
889
890 //
891 // If a reasonable amount of time elapsed then check the
892 // i/o rate. If it's taking less than 1 second or more
893 // than 2 seconds, adjust hfs_max_pending_io so that we
894 // will allow about 1.5 seconds of i/o to queue up.
895 //
896 if ((now - start) >= 300000) {
897 uint64_t scale = (pending_io * 100) / rate;
898
899 if (scale < 100 || scale > 200) {
900 // set it so that it should take about 1.5 seconds to drain
901 hfsmp->hfs_max_pending_io = (rate * 150ULL) / 100ULL;
902 }
903 }
904
905 } else if ( ((now - hfsmp->hfs_last_sync_time) >= 5000000ULL)
906 || (((now - hfsmp->hfs_last_sync_time) >= 100000LL)
907 && ((now - hfsmp->hfs_last_sync_request_time) >= 100000LL)
908 && (hfsmp->hfs_active_threads == 0)
909 && (hfsmp->hfs_global_lock_nesting == 0))) {
910
911 //
912 // Flush the journal if more than 5 seconds elapsed since
913 // the last sync OR we have not sync'ed recently and the
914 // last sync request time was more than 100 milliseconds
915 // ago and no one is in the middle of a transaction right
916 // now. Else we defer the sync and reschedule it.
917 //
918 if (hfsmp->jnl) {
919 lck_rw_lock_shared(&hfsmp->hfs_global_lock);
920
921 journal_flush(hfsmp->jnl);
922
923 lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
924 } else {
925 hfs_sync(hfsmp->hfs_mp, MNT_WAIT, vfs_context_kernel());
926 }
927
928 clock_get_calendar_microtime(&secs, &usecs);
929 now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
930 hfsmp->hfs_last_sync_time = now;
931
932 } else if (hfsmp->hfs_active_threads == 0) {
933 uint64_t deadline;
934
935 clock_interval_to_deadline(delay, HFS_MILLISEC_SCALE, &deadline);
936 thread_call_enter_delayed(hfsmp->hfs_syncer, deadline);
937
938 // note: we intentionally return early here and do not
939 // decrement the sync_scheduled and sync_incomplete
940 // variables because we rescheduled the timer.
941
942 return;
943 }
944
945 //
946 // NOTE: we decrement these *after* we're done the journal_flush() since
947 // it can take a significant amount of time and so we don't want more
948 // callbacks scheduled until we're done this one.
949 //
950 OSDecrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_scheduled);
951 OSDecrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_incomplete);
952 wakeup((caddr_t)&hfsmp->hfs_sync_incomplete);
953 }
954
955
956 extern int IOBSDIsMediaEjectable( const char *cdev_name );
957
958 /*
959 * Common code for mount and mountroot
960 */
961 static int
962 hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
963 int journal_replay_only, vfs_context_t context)
964 {
965 struct proc *p = vfs_context_proc(context);
966 int retval = E_NONE;
967 struct hfsmount *hfsmp = NULL;
968 struct buf *bp;
969 dev_t dev;
970 HFSMasterDirectoryBlock *mdbp = NULL;
971 int ronly;
972 #if QUOTA
973 int i;
974 #endif
975 int mntwrapper;
976 kauth_cred_t cred;
977 u_int64_t disksize;
978 daddr64_t log_blkcnt;
979 u_int32_t log_blksize;
980 u_int32_t phys_blksize;
981 u_int32_t minblksize;
982 u_int32_t iswritable;
983 daddr64_t mdb_offset;
984 int isvirtual = 0;
985 int isroot = 0;
986
987 if (args == NULL) {
988 /* only hfs_mountroot passes us NULL as the 'args' argument */
989 isroot = 1;
990 }
991
992 ronly = vfs_isrdonly(mp);
993 dev = vnode_specrdev(devvp);
994 cred = p ? vfs_context_ucred(context) : NOCRED;
995 mntwrapper = 0;
996
997 bp = NULL;
998 hfsmp = NULL;
999 mdbp = NULL;
1000 minblksize = kHFSBlockSize;
1001
1002 /* Advisory locking should be handled at the VFS layer */
1003 vfs_setlocklocal(mp);
1004
1005 /* Get the logical block size (treated as physical block size everywhere) */
1006 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&log_blksize, 0, context)) {
1007 retval = ENXIO;
1008 goto error_exit;
1009 }
1010 if (log_blksize == 0 || log_blksize > 1024*1024*1024) {
1011 printf("hfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize);
1012 retval = ENXIO;
1013 goto error_exit;
1014 }
1015
1016 /* Get the physical block size. */
1017 retval = VNOP_IOCTL(devvp, DKIOCGETPHYSICALBLOCKSIZE, (caddr_t)&phys_blksize, 0, context);
1018 if (retval) {
1019 if ((retval != ENOTSUP) && (retval != ENOTTY)) {
1020 retval = ENXIO;
1021 goto error_exit;
1022 }
1023 /* If device does not support this ioctl, assume that physical
1024 * block size is same as logical block size
1025 */
1026 phys_blksize = log_blksize;
1027 }
1028 if (phys_blksize == 0 || phys_blksize > 1024*1024*1024) {
1029 printf("hfs: physical block size 0x%x looks bad. Not mounting.\n", phys_blksize);
1030 retval = ENXIO;
1031 goto error_exit;
1032 }
1033
1034 /* Switch to 512 byte sectors (temporarily) */
1035 if (log_blksize > 512) {
1036 u_int32_t size512 = 512;
1037
1038 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&size512, FWRITE, context)) {
1039 retval = ENXIO;
1040 goto error_exit;
1041 }
1042 }
1043 /* Get the number of 512 byte physical blocks. */
1044 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&log_blkcnt, 0, context)) {
1045 /* resetting block size may fail if getting block count did */
1046 (void)VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&log_blksize, FWRITE, context);
1047
1048 retval = ENXIO;
1049 goto error_exit;
1050 }
1051 /* Compute an accurate disk size (i.e. within 512 bytes) */
1052 disksize = (u_int64_t)log_blkcnt * (u_int64_t)512;
1053
1054 /*
1055 * On Tiger it is not necessary to switch the device
1056 * block size to be 4k if there are more than 31-bits
1057 * worth of blocks but to insure compatibility with
1058 * pre-Tiger systems we have to do it.
1059 *
1060 * If the device size is not a multiple of 4K (8 * 512), then
1061 * switching the logical block size isn't going to help because
1062 * we will be unable to write the alternate volume header.
1063 * In this case, just leave the logical block size unchanged.
1064 */
1065 if (log_blkcnt > 0x000000007fffffff && (log_blkcnt & 7) == 0) {
1066 minblksize = log_blksize = 4096;
1067 if (phys_blksize < log_blksize)
1068 phys_blksize = log_blksize;
1069 }
1070
1071 /*
1072 * The cluster layer is not currently prepared to deal with a logical
1073 * block size larger than the system's page size. (It can handle
1074 * blocks per page, but not multiple pages per block.) So limit the
1075 * logical block size to the page size.
1076 */
1077 if (log_blksize > PAGE_SIZE)
1078 log_blksize = PAGE_SIZE;
1079
1080 /* Now switch to our preferred physical block size. */
1081 if (log_blksize > 512) {
1082 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&log_blksize, FWRITE, context)) {
1083 retval = ENXIO;
1084 goto error_exit;
1085 }
1086 /* Get the count of physical blocks. */
1087 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&log_blkcnt, 0, context)) {
1088 retval = ENXIO;
1089 goto error_exit;
1090 }
1091 }
1092 /*
1093 * At this point:
1094 * minblksize is the minimum physical block size
1095 * log_blksize has our preferred physical block size
1096 * log_blkcnt has the total number of physical blocks
1097 */
1098
1099 mdb_offset = (daddr64_t)HFS_PRI_SECTOR(log_blksize);
1100 if ((retval = (int)buf_meta_bread(devvp,
1101 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, (phys_blksize/log_blksize)),
1102 phys_blksize, cred, &bp))) {
1103 goto error_exit;
1104 }
1105 MALLOC(mdbp, HFSMasterDirectoryBlock *, kMDBSize, M_TEMP, M_WAITOK);
1106 if (mdbp == NULL) {
1107 retval = ENOMEM;
1108 goto error_exit;
1109 }
1110 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(phys_blksize), mdbp, kMDBSize);
1111 buf_brelse(bp);
1112 bp = NULL;
1113
1114 MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK);
1115 if (hfsmp == NULL) {
1116 retval = ENOMEM;
1117 goto error_exit;
1118 }
1119 bzero(hfsmp, sizeof(struct hfsmount));
1120
1121 hfs_chashinit_finish(hfsmp);
1122
1123 /*
1124 * Init the volume information structure
1125 */
1126
1127 lck_mtx_init(&hfsmp->hfs_mutex, hfs_mutex_group, hfs_lock_attr);
1128 lck_mtx_init(&hfsmp->hfc_mutex, hfs_mutex_group, hfs_lock_attr);
1129 lck_rw_init(&hfsmp->hfs_global_lock, hfs_rwlock_group, hfs_lock_attr);
1130 lck_rw_init(&hfsmp->hfs_insync, hfs_rwlock_group, hfs_lock_attr);
1131
1132 vfs_setfsprivate(mp, hfsmp);
1133 hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */
1134 hfsmp->hfs_raw_dev = vnode_specrdev(devvp);
1135 hfsmp->hfs_devvp = devvp;
1136 vnode_ref(devvp); /* Hold a ref on the device, dropped when hfsmp is freed. */
1137 hfsmp->hfs_logical_block_size = log_blksize;
1138 hfsmp->hfs_logical_block_count = log_blkcnt;
1139 hfsmp->hfs_physical_block_size = phys_blksize;
1140 hfsmp->hfs_log_per_phys = (phys_blksize / log_blksize);
1141 hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA;
1142 if (ronly)
1143 hfsmp->hfs_flags |= HFS_READ_ONLY;
1144 if (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)
1145 hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS;
1146
1147 #if QUOTA
1148 for (i = 0; i < MAXQUOTAS; i++)
1149 dqfileinit(&hfsmp->hfs_qfiles[i]);
1150 #endif
1151
1152 if (args) {
1153 hfsmp->hfs_uid = (args->hfs_uid == (uid_t)VNOVAL) ? UNKNOWNUID : args->hfs_uid;
1154 if (hfsmp->hfs_uid == 0xfffffffd) hfsmp->hfs_uid = UNKNOWNUID;
1155 hfsmp->hfs_gid = (args->hfs_gid == (gid_t)VNOVAL) ? UNKNOWNGID : args->hfs_gid;
1156 if (hfsmp->hfs_gid == 0xfffffffd) hfsmp->hfs_gid = UNKNOWNGID;
1157 vfs_setowner(mp, hfsmp->hfs_uid, hfsmp->hfs_gid); /* tell the VFS */
1158 if (args->hfs_mask != (mode_t)VNOVAL) {
1159 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
1160 if (args->flags & HFSFSMNT_NOXONFILES) {
1161 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
1162 } else {
1163 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
1164 }
1165 } else {
1166 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
1167 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
1168 }
1169 if ((args->flags != (int)VNOVAL) && (args->flags & HFSFSMNT_WRAPPER))
1170 mntwrapper = 1;
1171 } else {
1172 /* Even w/o explicit mount arguments, MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */
1173 if (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS) {
1174 hfsmp->hfs_uid = UNKNOWNUID;
1175 hfsmp->hfs_gid = UNKNOWNGID;
1176 vfs_setowner(mp, hfsmp->hfs_uid, hfsmp->hfs_gid); /* tell the VFS */
1177 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
1178 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
1179 }
1180 }
1181
1182 /* Find out if disk media is writable. */
1183 if (VNOP_IOCTL(devvp, DKIOCISWRITABLE, (caddr_t)&iswritable, 0, context) == 0) {
1184 if (iswritable)
1185 hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA;
1186 else
1187 hfsmp->hfs_flags &= ~HFS_WRITEABLE_MEDIA;
1188 }
1189
1190 // record the current time at which we're mounting this volume
1191 struct timeval tv;
1192 microtime(&tv);
1193 hfsmp->hfs_mount_time = tv.tv_sec;
1194
1195 /* Mount a standard HFS disk */
1196 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) &&
1197 (mntwrapper || (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord))) {
1198
1199 /* On 10.6 and beyond, non read-only mounts for HFS standard vols get rejected */
1200 if (vfs_isrdwr(mp)) {
1201 retval = EROFS;
1202 goto error_exit;
1203 }
1204 /* Treat it as if it's read-only and not writeable */
1205 hfsmp->hfs_flags |= HFS_READ_ONLY;
1206 hfsmp->hfs_flags &= ~HFS_WRITEABLE_MEDIA;
1207
1208 /* If only journal replay is requested, exit immediately */
1209 if (journal_replay_only) {
1210 retval = 0;
1211 goto error_exit;
1212 }
1213
1214 if ((vfs_flags(mp) & MNT_ROOTFS)) {
1215 retval = EINVAL; /* Cannot root from HFS standard disks */
1216 goto error_exit;
1217 }
1218 /* HFS disks can only use 512 byte physical blocks */
1219 if (log_blksize > kHFSBlockSize) {
1220 log_blksize = kHFSBlockSize;
1221 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&log_blksize, FWRITE, context)) {
1222 retval = ENXIO;
1223 goto error_exit;
1224 }
1225 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&log_blkcnt, 0, context)) {
1226 retval = ENXIO;
1227 goto error_exit;
1228 }
1229 hfsmp->hfs_logical_block_size = log_blksize;
1230 hfsmp->hfs_logical_block_count = log_blkcnt;
1231 hfsmp->hfs_physical_block_size = log_blksize;
1232 hfsmp->hfs_log_per_phys = 1;
1233 }
1234 if (args) {
1235 hfsmp->hfs_encoding = args->hfs_encoding;
1236 HFSTOVCB(hfsmp)->volumeNameEncodingHint = args->hfs_encoding;
1237
1238 /* establish the timezone */
1239 gTimeZone = args->hfs_timezone;
1240 }
1241
1242 retval = hfs_getconverter(hfsmp->hfs_encoding, &hfsmp->hfs_get_unicode,
1243 &hfsmp->hfs_get_hfsname);
1244 if (retval)
1245 goto error_exit;
1246
1247 retval = hfs_MountHFSVolume(hfsmp, mdbp, p);
1248 if (retval)
1249 (void) hfs_relconverter(hfsmp->hfs_encoding);
1250
1251 } else /* Mount an HFS Plus disk */ {
1252 HFSPlusVolumeHeader *vhp;
1253 off_t embeddedOffset;
1254 int jnl_disable = 0;
1255
1256 /* Get the embedded Volume Header */
1257 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
1258 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * kHFSBlockSize;
1259 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
1260 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1261
1262 /*
1263 * If the embedded volume doesn't start on a block
1264 * boundary, then switch the device to a 512-byte
1265 * block size so everything will line up on a block
1266 * boundary.
1267 */
1268 if ((embeddedOffset % log_blksize) != 0) {
1269 printf("hfs_mountfs: embedded volume offset not"
1270 " a multiple of physical block size (%d);"
1271 " switching to 512\n", log_blksize);
1272 log_blksize = 512;
1273 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE,
1274 (caddr_t)&log_blksize, FWRITE, context)) {
1275 retval = ENXIO;
1276 goto error_exit;
1277 }
1278 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT,
1279 (caddr_t)&log_blkcnt, 0, context)) {
1280 retval = ENXIO;
1281 goto error_exit;
1282 }
1283 /* Note: relative block count adjustment */
1284 hfsmp->hfs_logical_block_count *=
1285 hfsmp->hfs_logical_block_size / log_blksize;
1286
1287 /* Update logical /physical block size */
1288 hfsmp->hfs_logical_block_size = log_blksize;
1289 hfsmp->hfs_physical_block_size = log_blksize;
1290 phys_blksize = log_blksize;
1291 hfsmp->hfs_log_per_phys = 1;
1292 }
1293
1294 disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) *
1295 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1296
1297 hfsmp->hfs_logical_block_count = disksize / log_blksize;
1298
1299 mdb_offset = (daddr64_t)((embeddedOffset / log_blksize) + HFS_PRI_SECTOR(log_blksize));
1300 retval = (int)buf_meta_bread(devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
1301 phys_blksize, cred, &bp);
1302 if (retval)
1303 goto error_exit;
1304 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(phys_blksize), mdbp, 512);
1305 buf_brelse(bp);
1306 bp = NULL;
1307 vhp = (HFSPlusVolumeHeader*) mdbp;
1308
1309 } else /* pure HFS+ */ {
1310 embeddedOffset = 0;
1311 vhp = (HFSPlusVolumeHeader*) mdbp;
1312 }
1313
1314 /*
1315 * On inconsistent disks, do not allow read-write mount
1316 * unless it is the boot volume being mounted. We also
1317 * always want to replay the journal if the journal_replay_only
1318 * flag is set because that will (most likely) get the
1319 * disk into a consistent state before fsck_hfs starts
1320 * looking at it.
1321 */
1322 if ( !(vfs_flags(mp) & MNT_ROOTFS)
1323 && (SWAP_BE32(vhp->attributes) & kHFSVolumeInconsistentMask)
1324 && !journal_replay_only
1325 && !(hfsmp->hfs_flags & HFS_READ_ONLY)) {
1326 retval = EINVAL;
1327 goto error_exit;
1328 }
1329
1330
1331 // XXXdbg
1332 //
1333 hfsmp->jnl = NULL;
1334 hfsmp->jvp = NULL;
1335 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS) &&
1336 args->journal_disable) {
1337 jnl_disable = 1;
1338 }
1339
1340 //
1341 // We only initialize the journal here if the last person
1342 // to mount this volume was journaling aware. Otherwise
1343 // we delay journal initialization until later at the end
1344 // of hfs_MountHFSPlusVolume() because the last person who
1345 // mounted it could have messed things up behind our back
1346 // (so we need to go find the .journal file, make sure it's
1347 // the right size, re-sync up if it was moved, etc).
1348 //
1349 if ( (SWAP_BE32(vhp->lastMountedVersion) == kHFSJMountVersion)
1350 && (SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask)
1351 && !jnl_disable) {
1352
1353 // if we're able to init the journal, mark the mount
1354 // point as journaled.
1355 //
1356 if ((retval = hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred)) == 0) {
1357 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
1358 } else {
1359 if (retval == EROFS) {
1360 // EROFS is a special error code that means the volume has an external
1361 // journal which we couldn't find. in that case we do not want to
1362 // rewrite the volume header - we'll just refuse to mount the volume.
1363 retval = EINVAL;
1364 goto error_exit;
1365 }
1366
1367 // if the journal failed to open, then set the lastMountedVersion
1368 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1369 // of just bailing out because the volume is journaled.
1370 if (!ronly) {
1371 HFSPlusVolumeHeader *jvhp;
1372
1373 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
1374
1375 if (mdb_offset == 0) {
1376 mdb_offset = (daddr64_t)((embeddedOffset / log_blksize) + HFS_PRI_SECTOR(log_blksize));
1377 }
1378
1379 bp = NULL;
1380 retval = (int)buf_meta_bread(devvp,
1381 HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
1382 phys_blksize, cred, &bp);
1383 if (retval == 0) {
1384 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(phys_blksize));
1385
1386 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
1387 printf ("hfs(1): Journal replay fail. Writing lastMountVersion as FSK!\n");
1388 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
1389 buf_bwrite(bp);
1390 } else {
1391 buf_brelse(bp);
1392 }
1393 bp = NULL;
1394 } else if (bp) {
1395 buf_brelse(bp);
1396 // clear this so the error exit path won't try to use it
1397 bp = NULL;
1398 }
1399 }
1400
1401 // if this isn't the root device just bail out.
1402 // If it is the root device we just continue on
1403 // in the hopes that fsck_hfs will be able to
1404 // fix any damage that exists on the volume.
1405 if ( !(vfs_flags(mp) & MNT_ROOTFS)) {
1406 retval = EINVAL;
1407 goto error_exit;
1408 }
1409 }
1410 }
1411 // XXXdbg
1412
1413 /* Either the journal is replayed successfully, or there
1414 * was nothing to replay, or no journal exists. In any case,
1415 * return success.
1416 */
1417 if (journal_replay_only) {
1418 retval = 0;
1419 goto error_exit;
1420 }
1421
1422 (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
1423
1424 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args, cred);
1425 /*
1426 * If the backend didn't like our physical blocksize
1427 * then retry with physical blocksize of 512.
1428 */
1429 if ((retval == ENXIO) && (log_blksize > 512) && (log_blksize != minblksize)) {
1430 printf("hfs_mountfs: could not use physical block size "
1431 "(%d) switching to 512\n", log_blksize);
1432 log_blksize = 512;
1433 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&log_blksize, FWRITE, context)) {
1434 retval = ENXIO;
1435 goto error_exit;
1436 }
1437 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&log_blkcnt, 0, context)) {
1438 retval = ENXIO;
1439 goto error_exit;
1440 }
1441 devvp->v_specsize = log_blksize;
1442 /* Note: relative block count adjustment (in case this is an embedded volume). */
1443 hfsmp->hfs_logical_block_count *= hfsmp->hfs_logical_block_size / log_blksize;
1444 hfsmp->hfs_logical_block_size = log_blksize;
1445 hfsmp->hfs_log_per_phys = hfsmp->hfs_physical_block_size / log_blksize;
1446
1447 if (hfsmp->jnl && hfsmp->jvp == devvp) {
1448 // close and re-open this with the new block size
1449 journal_close(hfsmp->jnl);
1450 hfsmp->jnl = NULL;
1451 if (hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred) == 0) {
1452 vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
1453 } else {
1454 // if the journal failed to open, then set the lastMountedVersion
1455 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1456 // of just bailing out because the volume is journaled.
1457 if (!ronly) {
1458 HFSPlusVolumeHeader *jvhp;
1459
1460 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
1461
1462 if (mdb_offset == 0) {
1463 mdb_offset = (daddr64_t)((embeddedOffset / log_blksize) + HFS_PRI_SECTOR(log_blksize));
1464 }
1465
1466 bp = NULL;
1467 retval = (int)buf_meta_bread(devvp, HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
1468 phys_blksize, cred, &bp);
1469 if (retval == 0) {
1470 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(phys_blksize));
1471
1472 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
1473 printf ("hfs(2): Journal replay fail. Writing lastMountVersion as FSK!\n");
1474 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
1475 buf_bwrite(bp);
1476 } else {
1477 buf_brelse(bp);
1478 }
1479 bp = NULL;
1480 } else if (bp) {
1481 buf_brelse(bp);
1482 // clear this so the error exit path won't try to use it
1483 bp = NULL;
1484 }
1485 }
1486
1487 // if this isn't the root device just bail out.
1488 // If it is the root device we just continue on
1489 // in the hopes that fsck_hfs will be able to
1490 // fix any damage that exists on the volume.
1491 if ( !(vfs_flags(mp) & MNT_ROOTFS)) {
1492 retval = EINVAL;
1493 goto error_exit;
1494 }
1495 }
1496 }
1497
1498 /* Try again with a smaller block size... */
1499 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args, cred);
1500 }
1501 if (retval)
1502 (void) hfs_relconverter(0);
1503 }
1504
1505 // save off a snapshot of the mtime from the previous mount
1506 // (for matador).
1507 hfsmp->hfs_last_mounted_mtime = hfsmp->hfs_mtime;
1508
1509 if ( retval ) {
1510 goto error_exit;
1511 }
1512
1513 mp->mnt_vfsstat.f_fsid.val[0] = (long)dev;
1514 mp->mnt_vfsstat.f_fsid.val[1] = vfs_typenum(mp);
1515 vfs_setmaxsymlen(mp, 0);
1516
1517 mp->mnt_vtable->vfc_vfsflags |= VFC_VFSNATIVEXATTR;
1518 #if NAMEDSTREAMS
1519 mp->mnt_kern_flag |= MNTK_NAMED_STREAMS;
1520 #endif
1521 if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
1522 /* Tell VFS that we support directory hard links. */
1523 mp->mnt_vtable->vfc_vfsflags |= VFC_VFSDIRLINKS;
1524 } else {
1525 /* HFS standard doesn't support extended readdir! */
1526 mp->mnt_vtable->vfc_vfsflags &= ~VFC_VFSREADDIR_EXTENDED;
1527 }
1528
1529 if (args) {
1530 /*
1531 * Set the free space warning levels for a non-root volume:
1532 *
1533 * Set the "danger" limit to 1% of the volume size or 100MB, whichever
1534 * is less. Set the "warning" limit to 2% of the volume size or 150MB,
1535 * whichever is less. And last, set the "desired" freespace level to
1536 * to 3% of the volume size or 200MB, whichever is less.
1537 */
1538 hfsmp->hfs_freespace_notify_dangerlimit =
1539 MIN(HFS_VERYLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1540 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_VERYLOWDISKTRIGGERFRACTION);
1541 hfsmp->hfs_freespace_notify_warninglimit =
1542 MIN(HFS_LOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1543 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKTRIGGERFRACTION);
1544 hfsmp->hfs_freespace_notify_desiredlevel =
1545 MIN(HFS_LOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
1546 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKSHUTOFFFRACTION);
1547 } else {
1548 /*
1549 * Set the free space warning levels for the root volume:
1550 *
1551 * Set the "danger" limit to 5% of the volume size or 125MB, whichever
1552 * is less. Set the "warning" limit to 10% of the volume size or 250MB,
1553 * whichever is less. And last, set the "desired" freespace level to
1554 * to 11% of the volume size or 375MB, whichever is less.
1555 */
1556 hfsmp->hfs_freespace_notify_dangerlimit =
1557 MIN(HFS_ROOTVERYLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1558 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTVERYLOWDISKTRIGGERFRACTION);
1559 hfsmp->hfs_freespace_notify_warninglimit =
1560 MIN(HFS_ROOTLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1561 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKTRIGGERFRACTION);
1562 hfsmp->hfs_freespace_notify_desiredlevel =
1563 MIN(HFS_ROOTLOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
1564 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKSHUTOFFFRACTION);
1565 };
1566
1567 /* Check if the file system exists on virtual device, like disk image */
1568 if (VNOP_IOCTL(devvp, DKIOCISVIRTUAL, (caddr_t)&isvirtual, 0, context) == 0) {
1569 if (isvirtual) {
1570 hfsmp->hfs_flags |= HFS_VIRTUAL_DEVICE;
1571 }
1572 }
1573
1574 /* do not allow ejectability checks on the root device */
1575 if (isroot == 0) {
1576 if ((hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) == 0 &&
1577 IOBSDIsMediaEjectable(mp->mnt_vfsstat.f_mntfromname)) {
1578 hfsmp->hfs_max_pending_io = 4096*1024; // a reasonable value to start with.
1579 hfsmp->hfs_syncer = thread_call_allocate(hfs_syncer, hfsmp);
1580 if (hfsmp->hfs_syncer == NULL) {
1581 printf("hfs: failed to allocate syncer thread callback for %s (%s)\n",
1582 mp->mnt_vfsstat.f_mntfromname, mp->mnt_vfsstat.f_mntonname);
1583 }
1584 }
1585 }
1586
1587 /*
1588 * Start looking for free space to drop below this level and generate a
1589 * warning immediately if needed:
1590 */
1591 hfsmp->hfs_notification_conditions = 0;
1592 hfs_generate_volume_notifications(hfsmp);
1593
1594 if (ronly == 0) {
1595 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1596 }
1597 FREE(mdbp, M_TEMP);
1598 return (0);
1599
1600 error_exit:
1601 if (bp)
1602 buf_brelse(bp);
1603 if (mdbp)
1604 FREE(mdbp, M_TEMP);
1605
1606 if (hfsmp && hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
1607 vnode_clearmountedon(hfsmp->jvp);
1608 (void)VNOP_CLOSE(hfsmp->jvp, ronly ? FREAD : FREAD|FWRITE, vfs_context_kernel());
1609 hfsmp->jvp = NULL;
1610 }
1611 if (hfsmp) {
1612 if (hfsmp->hfs_devvp) {
1613 vnode_rele(hfsmp->hfs_devvp);
1614 }
1615 hfs_delete_chash(hfsmp);
1616
1617 FREE(hfsmp, M_HFSMNT);
1618 vfs_setfsprivate(mp, NULL);
1619 }
1620 return (retval);
1621 }
1622
1623
1624 /*
1625 * Make a filesystem operational.
1626 * Nothing to do at the moment.
1627 */
1628 /* ARGSUSED */
1629 static int
1630 hfs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t context)
1631 {
1632 return (0);
1633 }
1634
1635
1636 /*
1637 * unmount system call
1638 */
1639 static int
1640 hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context)
1641 {
1642 struct proc *p = vfs_context_proc(context);
1643 struct hfsmount *hfsmp = VFSTOHFS(mp);
1644 int retval = E_NONE;
1645 int flags;
1646 int force;
1647 int started_tr = 0;
1648
1649 flags = 0;
1650 force = 0;
1651 if (mntflags & MNT_FORCE) {
1652 flags |= FORCECLOSE;
1653 force = 1;
1654 }
1655
1656 if ((retval = hfs_flushfiles(mp, flags, p)) && !force)
1657 return (retval);
1658
1659 if (hfsmp->hfs_flags & HFS_METADATA_ZONE)
1660 (void) hfs_recording_suspend(hfsmp);
1661
1662 /*
1663 * Cancel any pending timers for this volume. Then wait for any timers
1664 * which have fired, but whose callbacks have not yet completed.
1665 */
1666 if (hfsmp->hfs_syncer)
1667 {
1668 struct timespec ts = {0, 100000000}; /* 0.1 seconds */
1669
1670 /*
1671 * Cancel any timers that have been scheduled, but have not
1672 * fired yet. NOTE: The kernel considers a timer complete as
1673 * soon as it starts your callback, so the kernel does not
1674 * keep track of the number of callbacks in progress.
1675 */
1676 if (thread_call_cancel(hfsmp->hfs_syncer))
1677 OSDecrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_incomplete);
1678 thread_call_free(hfsmp->hfs_syncer);
1679 hfsmp->hfs_syncer = NULL;
1680
1681 /*
1682 * This waits for all of the callbacks that were entered before
1683 * we did thread_call_cancel above, but have not completed yet.
1684 */
1685 while(hfsmp->hfs_sync_incomplete > 0)
1686 {
1687 msleep((caddr_t)&hfsmp->hfs_sync_incomplete, NULL, PWAIT, "hfs_unmount", &ts);
1688 }
1689
1690 if (hfsmp->hfs_sync_incomplete < 0)
1691 panic("hfs_unmount: pm_sync_incomplete underflow!\n");
1692 }
1693
1694 /*
1695 * Flush out the b-trees, volume bitmap and Volume Header
1696 */
1697 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
1698 retval = hfs_start_transaction(hfsmp);
1699 if (retval == 0) {
1700 started_tr = 1;
1701 } else if (!force) {
1702 goto err_exit;
1703 }
1704
1705 if (hfsmp->hfs_startup_vp) {
1706 (void) hfs_lock(VTOC(hfsmp->hfs_startup_vp), HFS_EXCLUSIVE_LOCK);
1707 retval = hfs_fsync(hfsmp->hfs_startup_vp, MNT_WAIT, 0, p);
1708 hfs_unlock(VTOC(hfsmp->hfs_startup_vp));
1709 if (retval && !force)
1710 goto err_exit;
1711 }
1712
1713 if (hfsmp->hfs_attribute_vp) {
1714 (void) hfs_lock(VTOC(hfsmp->hfs_attribute_vp), HFS_EXCLUSIVE_LOCK);
1715 retval = hfs_fsync(hfsmp->hfs_attribute_vp, MNT_WAIT, 0, p);
1716 hfs_unlock(VTOC(hfsmp->hfs_attribute_vp));
1717 if (retval && !force)
1718 goto err_exit;
1719 }
1720
1721 (void) hfs_lock(VTOC(hfsmp->hfs_catalog_vp), HFS_EXCLUSIVE_LOCK);
1722 retval = hfs_fsync(hfsmp->hfs_catalog_vp, MNT_WAIT, 0, p);
1723 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
1724 if (retval && !force)
1725 goto err_exit;
1726
1727 (void) hfs_lock(VTOC(hfsmp->hfs_extents_vp), HFS_EXCLUSIVE_LOCK);
1728 retval = hfs_fsync(hfsmp->hfs_extents_vp, MNT_WAIT, 0, p);
1729 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
1730 if (retval && !force)
1731 goto err_exit;
1732
1733 if (hfsmp->hfs_allocation_vp) {
1734 (void) hfs_lock(VTOC(hfsmp->hfs_allocation_vp), HFS_EXCLUSIVE_LOCK);
1735 retval = hfs_fsync(hfsmp->hfs_allocation_vp, MNT_WAIT, 0, p);
1736 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
1737 if (retval && !force)
1738 goto err_exit;
1739 }
1740
1741 if (hfsmp->hfc_filevp && vnode_issystem(hfsmp->hfc_filevp)) {
1742 retval = hfs_fsync(hfsmp->hfc_filevp, MNT_WAIT, 0, p);
1743 if (retval && !force)
1744 goto err_exit;
1745 }
1746
1747 /* If runtime corruption was detected, indicate that the volume
1748 * was not unmounted cleanly.
1749 */
1750 if (hfsmp->vcbAtrb & kHFSVolumeInconsistentMask) {
1751 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
1752 } else {
1753 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
1754 }
1755
1756 if (hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) {
1757 int i;
1758 u_int32_t min_start = hfsmp->totalBlocks;
1759
1760 // set the nextAllocation pointer to the smallest free block number
1761 // we've seen so on the next mount we won't rescan unnecessarily
1762 for(i=0; i < (int)hfsmp->vcbFreeExtCnt; i++) {
1763 if (hfsmp->vcbFreeExt[i].startBlock < min_start) {
1764 min_start = hfsmp->vcbFreeExt[i].startBlock;
1765 }
1766 }
1767 if (min_start < hfsmp->nextAllocation) {
1768 hfsmp->nextAllocation = min_start;
1769 }
1770 }
1771
1772
1773 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1774 if (retval) {
1775 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
1776 if (!force)
1777 goto err_exit; /* could not flush everything */
1778 }
1779
1780 if (started_tr) {
1781 hfs_end_transaction(hfsmp);
1782 started_tr = 0;
1783 }
1784 }
1785
1786 if (hfsmp->jnl) {
1787 hfs_journal_flush(hfsmp);
1788 }
1789
1790 /*
1791 * Invalidate our caches and release metadata vnodes
1792 */
1793 (void) hfsUnmount(hfsmp, p);
1794
1795 /*
1796 * Last chance to dump unreferenced system files.
1797 */
1798 (void) vflush(mp, NULLVP, FORCECLOSE);
1799
1800 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
1801 (void) hfs_relconverter(hfsmp->hfs_encoding);
1802
1803 // XXXdbg
1804 if (hfsmp->jnl) {
1805 journal_close(hfsmp->jnl);
1806 hfsmp->jnl = NULL;
1807 }
1808
1809 VNOP_FSYNC(hfsmp->hfs_devvp, MNT_WAIT, context);
1810
1811 if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
1812 vnode_clearmountedon(hfsmp->jvp);
1813 retval = VNOP_CLOSE(hfsmp->jvp,
1814 hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE,
1815 vfs_context_kernel());
1816 vnode_put(hfsmp->jvp);
1817 hfsmp->jvp = NULL;
1818 }
1819 // XXXdbg
1820
1821 #ifdef HFS_SPARSE_DEV
1822 /* Drop our reference on the backing fs (if any). */
1823 if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
1824 struct vnode * tmpvp;
1825
1826 hfsmp->hfs_flags &= ~HFS_HAS_SPARSE_DEVICE;
1827 tmpvp = hfsmp->hfs_backingfs_rootvp;
1828 hfsmp->hfs_backingfs_rootvp = NULLVP;
1829 vnode_rele(tmpvp);
1830 }
1831 #endif /* HFS_SPARSE_DEV */
1832 lck_mtx_destroy(&hfsmp->hfc_mutex, hfs_mutex_group);
1833 vnode_rele(hfsmp->hfs_devvp);
1834
1835 hfs_delete_chash(hfsmp);
1836 FREE(hfsmp, M_HFSMNT);
1837
1838 return (0);
1839
1840 err_exit:
1841 if (started_tr) {
1842 hfs_end_transaction(hfsmp);
1843 }
1844 return retval;
1845 }
1846
1847
1848 /*
1849 * Return the root of a filesystem.
1850 */
1851 static int
1852 hfs_vfs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t context)
1853 {
1854 return hfs_vget(VFSTOHFS(mp), (cnid_t)kHFSRootFolderID, vpp, 1);
1855 }
1856
1857
1858 /*
1859 * Do operations associated with quotas
1860 */
1861 #if !QUOTA
1862 static int
1863 hfs_quotactl(__unused struct mount *mp, __unused int cmds, __unused uid_t uid, __unused caddr_t datap, __unused vfs_context_t context)
1864 {
1865 return (ENOTSUP);
1866 }
1867 #else
1868 static int
1869 hfs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t datap, vfs_context_t context)
1870 {
1871 struct proc *p = vfs_context_proc(context);
1872 int cmd, type, error;
1873
1874 if (uid == ~0U)
1875 uid = vfs_context_ucred(context)->cr_ruid;
1876 cmd = cmds >> SUBCMDSHIFT;
1877
1878 switch (cmd) {
1879 case Q_SYNC:
1880 case Q_QUOTASTAT:
1881 break;
1882 case Q_GETQUOTA:
1883 if (uid == vfs_context_ucred(context)->cr_ruid)
1884 break;
1885 /* fall through */
1886 default:
1887 if ( (error = vfs_context_suser(context)) )
1888 return (error);
1889 }
1890
1891 type = cmds & SUBCMDMASK;
1892 if ((u_int)type >= MAXQUOTAS)
1893 return (EINVAL);
1894 if (vfs_busy(mp, LK_NOWAIT))
1895 return (0);
1896
1897 switch (cmd) {
1898
1899 case Q_QUOTAON:
1900 error = hfs_quotaon(p, mp, type, datap);
1901 break;
1902
1903 case Q_QUOTAOFF:
1904 error = hfs_quotaoff(p, mp, type);
1905 break;
1906
1907 case Q_SETQUOTA:
1908 error = hfs_setquota(mp, uid, type, datap);
1909 break;
1910
1911 case Q_SETUSE:
1912 error = hfs_setuse(mp, uid, type, datap);
1913 break;
1914
1915 case Q_GETQUOTA:
1916 error = hfs_getquota(mp, uid, type, datap);
1917 break;
1918
1919 case Q_SYNC:
1920 error = hfs_qsync(mp);
1921 break;
1922
1923 case Q_QUOTASTAT:
1924 error = hfs_quotastat(mp, type, datap);
1925 break;
1926
1927 default:
1928 error = EINVAL;
1929 break;
1930 }
1931 vfs_unbusy(mp);
1932
1933 return (error);
1934 }
1935 #endif /* QUOTA */
1936
1937 /* Subtype is composite of bits */
1938 #define HFS_SUBTYPE_JOURNALED 0x01
1939 #define HFS_SUBTYPE_CASESENSITIVE 0x02
1940 /* bits 2 - 6 reserved */
1941 #define HFS_SUBTYPE_STANDARDHFS 0x80
1942
1943 /*
1944 * Get file system statistics.
1945 */
1946 static int
1947 hfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, __unused vfs_context_t context)
1948 {
1949 ExtendedVCB *vcb = VFSTOVCB(mp);
1950 struct hfsmount *hfsmp = VFSTOHFS(mp);
1951 u_int32_t freeCNIDs;
1952 u_int16_t subtype = 0;
1953
1954 freeCNIDs = (u_int32_t)0xFFFFFFFF - (u_int32_t)vcb->vcbNxtCNID;
1955
1956 sbp->f_bsize = (u_int32_t)vcb->blockSize;
1957 sbp->f_iosize = (size_t)cluster_max_io_size(mp, 0);
1958 sbp->f_blocks = (u_int64_t)((u_int32_t)vcb->totalBlocks);
1959 sbp->f_bfree = (u_int64_t)((u_int32_t )hfs_freeblks(hfsmp, 0));
1960 sbp->f_bavail = (u_int64_t)((u_int32_t )hfs_freeblks(hfsmp, 1));
1961 sbp->f_files = (u_int64_t)((u_int32_t )(vcb->totalBlocks - 2)); /* max files is constrained by total blocks */
1962 sbp->f_ffree = (u_int64_t)((u_int32_t )(MIN(freeCNIDs, sbp->f_bavail)));
1963
1964 /*
1965 * Subtypes (flavors) for HFS
1966 * 0: Mac OS Extended
1967 * 1: Mac OS Extended (Journaled)
1968 * 2: Mac OS Extended (Case Sensitive)
1969 * 3: Mac OS Extended (Case Sensitive, Journaled)
1970 * 4 - 127: Reserved
1971 * 128: Mac OS Standard
1972 *
1973 */
1974 if (hfsmp->hfs_flags & HFS_STANDARD) {
1975 subtype = HFS_SUBTYPE_STANDARDHFS;
1976 } else /* HFS Plus */ {
1977 if (hfsmp->jnl)
1978 subtype |= HFS_SUBTYPE_JOURNALED;
1979 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
1980 subtype |= HFS_SUBTYPE_CASESENSITIVE;
1981 }
1982 sbp->f_fssubtype = subtype;
1983
1984 return (0);
1985 }
1986
1987
1988 //
1989 // XXXdbg -- this is a callback to be used by the journal to
1990 // get meta data blocks flushed out to disk.
1991 //
1992 // XXXdbg -- be smarter and don't flush *every* block on each
1993 // call. try to only flush some so we don't wind up
1994 // being too synchronous.
1995 //
1996 __private_extern__
1997 void
1998 hfs_sync_metadata(void *arg)
1999 {
2000 struct mount *mp = (struct mount *)arg;
2001 struct hfsmount *hfsmp;
2002 ExtendedVCB *vcb;
2003 buf_t bp;
2004 int retval;
2005 daddr64_t priIDSector;
2006 hfsmp = VFSTOHFS(mp);
2007 vcb = HFSTOVCB(hfsmp);
2008
2009 // now make sure the super block is flushed
2010 priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
2011 HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size));
2012
2013 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
2014 HFS_PHYSBLK_ROUNDDOWN(priIDSector, hfsmp->hfs_log_per_phys),
2015 hfsmp->hfs_physical_block_size, NOCRED, &bp);
2016 if ((retval != 0 ) && (retval != ENXIO)) {
2017 printf("hfs_sync_metadata: can't read volume header at %d! (retval 0x%x)\n",
2018 (int)priIDSector, retval);
2019 }
2020
2021 if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
2022 buf_bwrite(bp);
2023 } else if (bp) {
2024 buf_brelse(bp);
2025 }
2026
2027 // the alternate super block...
2028 // XXXdbg - we probably don't need to do this each and every time.
2029 // hfs_btreeio.c:FlushAlternate() should flag when it was
2030 // written...
2031 if (hfsmp->hfs_alt_id_sector) {
2032 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
2033 HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_alt_id_sector, hfsmp->hfs_log_per_phys),
2034 hfsmp->hfs_physical_block_size, NOCRED, &bp);
2035 if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
2036 buf_bwrite(bp);
2037 } else if (bp) {
2038 buf_brelse(bp);
2039 }
2040 }
2041 }
2042
2043
2044 struct hfs_sync_cargs {
2045 kauth_cred_t cred;
2046 struct proc *p;
2047 int waitfor;
2048 int error;
2049 };
2050
2051
2052 static int
2053 hfs_sync_callback(struct vnode *vp, void *cargs)
2054 {
2055 struct cnode *cp;
2056 struct hfs_sync_cargs *args;
2057 int error;
2058
2059 args = (struct hfs_sync_cargs *)cargs;
2060
2061 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0) {
2062 return (VNODE_RETURNED);
2063 }
2064 cp = VTOC(vp);
2065
2066 if ((cp->c_flag & C_MODIFIED) ||
2067 (cp->c_touch_acctime | cp->c_touch_chgtime | cp->c_touch_modtime) ||
2068 vnode_hasdirtyblks(vp)) {
2069 error = hfs_fsync(vp, args->waitfor, 0, args->p);
2070
2071 if (error)
2072 args->error = error;
2073 }
2074 hfs_unlock(cp);
2075 return (VNODE_RETURNED);
2076 }
2077
2078
2079
2080 /*
2081 * Go through the disk queues to initiate sandbagged IO;
2082 * go through the inodes to write those that have been modified;
2083 * initiate the writing of the super block if it has been modified.
2084 *
2085 * Note: we are always called with the filesystem marked `MPBUSY'.
2086 */
2087 static int
2088 hfs_sync(struct mount *mp, int waitfor, vfs_context_t context)
2089 {
2090 struct proc *p = vfs_context_proc(context);
2091 struct cnode *cp;
2092 struct hfsmount *hfsmp;
2093 ExtendedVCB *vcb;
2094 struct vnode *meta_vp[4];
2095 int i;
2096 int error, allerror = 0;
2097 struct hfs_sync_cargs args;
2098
2099 hfsmp = VFSTOHFS(mp);
2100
2101 /*
2102 * hfs_changefs might be manipulating vnodes so back off
2103 */
2104 if (hfsmp->hfs_flags & HFS_IN_CHANGEFS)
2105 return (0);
2106
2107 if (hfsmp->hfs_flags & HFS_READ_ONLY)
2108 return (EROFS);
2109
2110 /* skip over frozen volumes */
2111 if (!lck_rw_try_lock_shared(&hfsmp->hfs_insync))
2112 return 0;
2113
2114 args.cred = kauth_cred_get();
2115 args.waitfor = waitfor;
2116 args.p = p;
2117 args.error = 0;
2118 /*
2119 * hfs_sync_callback will be called for each vnode
2120 * hung off of this mount point... the vnode will be
2121 * properly referenced and unreferenced around the callback
2122 */
2123 vnode_iterate(mp, 0, hfs_sync_callback, (void *)&args);
2124
2125 if (args.error)
2126 allerror = args.error;
2127
2128 vcb = HFSTOVCB(hfsmp);
2129
2130 meta_vp[0] = vcb->extentsRefNum;
2131 meta_vp[1] = vcb->catalogRefNum;
2132 meta_vp[2] = vcb->allocationsRefNum; /* This is NULL for standard HFS */
2133 meta_vp[3] = hfsmp->hfs_attribute_vp; /* Optional file */
2134
2135 /* Now sync our three metadata files */
2136 for (i = 0; i < 4; ++i) {
2137 struct vnode *btvp;
2138
2139 btvp = meta_vp[i];;
2140 if ((btvp==0) || (vnode_mount(btvp) != mp))
2141 continue;
2142
2143 /* XXX use hfs_systemfile_lock instead ? */
2144 (void) hfs_lock(VTOC(btvp), HFS_EXCLUSIVE_LOCK);
2145 cp = VTOC(btvp);
2146
2147 if (((cp->c_flag & C_MODIFIED) == 0) &&
2148 (cp->c_touch_acctime == 0) &&
2149 (cp->c_touch_chgtime == 0) &&
2150 (cp->c_touch_modtime == 0) &&
2151 vnode_hasdirtyblks(btvp) == 0) {
2152 hfs_unlock(VTOC(btvp));
2153 continue;
2154 }
2155 error = vnode_get(btvp);
2156 if (error) {
2157 hfs_unlock(VTOC(btvp));
2158 continue;
2159 }
2160 if ((error = hfs_fsync(btvp, waitfor, 0, p)))
2161 allerror = error;
2162
2163 hfs_unlock(cp);
2164 vnode_put(btvp);
2165 };
2166
2167 /*
2168 * Force stale file system control information to be flushed.
2169 */
2170 if (vcb->vcbSigWord == kHFSSigWord) {
2171 if ((error = VNOP_FSYNC(hfsmp->hfs_devvp, waitfor, context))) {
2172 allerror = error;
2173 }
2174 }
2175 #if QUOTA
2176 hfs_qsync(mp);
2177 #endif /* QUOTA */
2178
2179 hfs_hotfilesync(hfsmp, vfs_context_kernel());
2180
2181 /*
2182 * Write back modified superblock.
2183 */
2184 if (IsVCBDirty(vcb)) {
2185 error = hfs_flushvolumeheader(hfsmp, waitfor, 0);
2186 if (error)
2187 allerror = error;
2188 }
2189
2190 if (hfsmp->jnl) {
2191 hfs_journal_flush(hfsmp);
2192 }
2193
2194 {
2195 clock_sec_t secs;
2196 clock_usec_t usecs;
2197 uint64_t now;
2198
2199 clock_get_calendar_microtime(&secs, &usecs);
2200 now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
2201 hfsmp->hfs_last_sync_time = now;
2202 }
2203
2204 lck_rw_unlock_shared(&hfsmp->hfs_insync);
2205 return (allerror);
2206 }
2207
2208
2209 /*
2210 * File handle to vnode
2211 *
2212 * Have to be really careful about stale file handles:
2213 * - check that the cnode id is valid
2214 * - call hfs_vget() to get the locked cnode
2215 * - check for an unallocated cnode (i_mode == 0)
2216 * - check that the given client host has export rights and return
2217 * those rights via. exflagsp and credanonp
2218 */
2219 static int
2220 hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp, __unused vfs_context_t context)
2221 {
2222 struct hfsfid *hfsfhp;
2223 struct vnode *nvp;
2224 int result;
2225
2226 *vpp = NULL;
2227 hfsfhp = (struct hfsfid *)fhp;
2228
2229 if (fhlen < (int)sizeof(struct hfsfid))
2230 return (EINVAL);
2231
2232 result = hfs_vget(VFSTOHFS(mp), ntohl(hfsfhp->hfsfid_cnid), &nvp, 0);
2233 if (result) {
2234 if (result == ENOENT)
2235 result = ESTALE;
2236 return result;
2237 }
2238
2239 /*
2240 * We used to use the create time as the gen id of the file handle,
2241 * but it is not static enough because it can change at any point
2242 * via system calls. We still don't have another volume ID or other
2243 * unique identifier to use for a generation ID across reboots that
2244 * persists until the file is removed. Using only the CNID exposes
2245 * us to the potential wrap-around case, but as of 2/2008, it would take
2246 * over 2 months to wrap around if the machine did nothing but allocate
2247 * CNIDs. Using some kind of wrap counter would only be effective if
2248 * each file had the wrap counter associated with it. For now,
2249 * we use only the CNID to identify the file as it's good enough.
2250 */
2251
2252 *vpp = nvp;
2253
2254 hfs_unlock(VTOC(nvp));
2255 return (0);
2256 }
2257
2258
2259 /*
2260 * Vnode pointer to File handle
2261 */
2262 /* ARGSUSED */
2263 static int
2264 hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, __unused vfs_context_t context)
2265 {
2266 struct cnode *cp;
2267 struct hfsfid *hfsfhp;
2268
2269 if (ISHFS(VTOVCB(vp)))
2270 return (ENOTSUP); /* hfs standard is not exportable */
2271
2272 if (*fhlenp < (int)sizeof(struct hfsfid))
2273 return (EOVERFLOW);
2274
2275 cp = VTOC(vp);
2276 hfsfhp = (struct hfsfid *)fhp;
2277 /* only the CNID is used to identify the file now */
2278 hfsfhp->hfsfid_cnid = htonl(cp->c_fileid);
2279 hfsfhp->hfsfid_gen = htonl(cp->c_fileid);
2280 *fhlenp = sizeof(struct hfsfid);
2281
2282 return (0);
2283 }
2284
2285
2286 /*
2287 * Initial HFS filesystems, done only once.
2288 */
2289 static int
2290 hfs_init(__unused struct vfsconf *vfsp)
2291 {
2292 static int done = 0;
2293
2294 if (done)
2295 return (0);
2296 done = 1;
2297 hfs_chashinit();
2298 hfs_converterinit();
2299
2300 BTReserveSetup();
2301
2302
2303 hfs_lock_attr = lck_attr_alloc_init();
2304 hfs_group_attr = lck_grp_attr_alloc_init();
2305 hfs_mutex_group = lck_grp_alloc_init("hfs-mutex", hfs_group_attr);
2306 hfs_rwlock_group = lck_grp_alloc_init("hfs-rwlock", hfs_group_attr);
2307
2308 #if HFS_COMPRESSION
2309 decmpfs_init();
2310 #endif
2311
2312 return (0);
2313 }
2314
2315 static int
2316 hfs_getmountpoint(struct vnode *vp, struct hfsmount **hfsmpp)
2317 {
2318 struct hfsmount * hfsmp;
2319 char fstypename[MFSNAMELEN];
2320
2321 if (vp == NULL)
2322 return (EINVAL);
2323
2324 if (!vnode_isvroot(vp))
2325 return (EINVAL);
2326
2327 vnode_vfsname(vp, fstypename);
2328 if (strncmp(fstypename, "hfs", sizeof(fstypename)) != 0)
2329 return (EINVAL);
2330
2331 hfsmp = VTOHFS(vp);
2332
2333 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
2334 return (EINVAL);
2335
2336 *hfsmpp = hfsmp;
2337
2338 return (0);
2339 }
2340
2341 // XXXdbg
2342 #include <sys/filedesc.h>
2343
2344 /*
2345 * HFS filesystem related variables.
2346 */
2347 static int
2348 hfs_sysctl(int *name, __unused u_int namelen, user_addr_t oldp, size_t *oldlenp,
2349 user_addr_t newp, size_t newlen, vfs_context_t context)
2350 {
2351 struct proc *p = vfs_context_proc(context);
2352 int error;
2353 struct hfsmount *hfsmp;
2354
2355 /* all sysctl names at this level are terminal */
2356
2357 if (name[0] == HFS_ENCODINGBIAS) {
2358 int bias;
2359
2360 bias = hfs_getencodingbias();
2361 error = sysctl_int(oldp, oldlenp, newp, newlen, &bias);
2362 if (error == 0 && newp)
2363 hfs_setencodingbias(bias);
2364 return (error);
2365
2366 } else if (name[0] == HFS_EXTEND_FS) {
2367 u_int64_t newsize;
2368 vnode_t vp = vfs_context_cwd(context);
2369
2370 if (newp == USER_ADDR_NULL || vp == NULLVP)
2371 return (EINVAL);
2372 if ((error = hfs_getmountpoint(vp, &hfsmp)))
2373 return (error);
2374 error = sysctl_quad(oldp, oldlenp, newp, newlen, (quad_t *)&newsize);
2375 if (error)
2376 return (error);
2377
2378 error = hfs_extendfs(hfsmp, newsize, context);
2379 return (error);
2380
2381 } else if (name[0] == HFS_ENCODINGHINT) {
2382 size_t bufsize;
2383 size_t bytes;
2384 u_int32_t hint;
2385 u_int16_t *unicode_name = NULL;
2386 char *filename = NULL;
2387
2388 if ((newlen <= 0) || (newlen > MAXPATHLEN))
2389 return (EINVAL);
2390
2391 bufsize = MAX(newlen * 3, MAXPATHLEN);
2392 MALLOC(filename, char *, newlen, M_TEMP, M_WAITOK);
2393 if (filename == NULL) {
2394 error = ENOMEM;
2395 goto encodinghint_exit;
2396 }
2397 MALLOC(unicode_name, u_int16_t *, bufsize, M_TEMP, M_WAITOK);
2398 if (filename == NULL) {
2399 error = ENOMEM;
2400 goto encodinghint_exit;
2401 }
2402
2403 error = copyin(newp, (caddr_t)filename, newlen);
2404 if (error == 0) {
2405 error = utf8_decodestr((u_int8_t *)filename, newlen - 1, unicode_name,
2406 &bytes, bufsize, 0, UTF_DECOMPOSED);
2407 if (error == 0) {
2408 hint = hfs_pickencoding(unicode_name, bytes / 2);
2409 error = sysctl_int(oldp, oldlenp, USER_ADDR_NULL, 0, (int32_t *)&hint);
2410 }
2411 }
2412
2413 encodinghint_exit:
2414 if (unicode_name)
2415 FREE(unicode_name, M_TEMP);
2416 if (filename)
2417 FREE(filename, M_TEMP);
2418 return (error);
2419
2420 } else if (name[0] == HFS_ENABLE_JOURNALING) {
2421 // make the file system journaled...
2422 vnode_t vp = vfs_context_cwd(context);
2423 vnode_t jvp;
2424 ExtendedVCB *vcb;
2425 struct cat_attr jnl_attr, jinfo_attr;
2426 struct cat_fork jnl_fork, jinfo_fork;
2427 void *jnl = NULL;
2428 int lockflags;
2429
2430 /* Only root can enable journaling */
2431 if (!is_suser()) {
2432 return (EPERM);
2433 }
2434 if (vp == NULLVP)
2435 return EINVAL;
2436
2437 hfsmp = VTOHFS(vp);
2438 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
2439 return EROFS;
2440 }
2441 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
2442 printf("hfs: can't make a plain hfs volume journaled.\n");
2443 return EINVAL;
2444 }
2445
2446 if (hfsmp->jnl) {
2447 printf("hfs: volume @ mp %p is already journaled!\n", vnode_mount(vp));
2448 return EAGAIN;
2449 }
2450
2451 vcb = HFSTOVCB(hfsmp);
2452 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
2453 if (BTHasContiguousNodes(VTOF(vcb->catalogRefNum)) == 0 ||
2454 BTHasContiguousNodes(VTOF(vcb->extentsRefNum)) == 0) {
2455
2456 printf("hfs: volume has a btree w/non-contiguous nodes. can not enable journaling.\n");
2457 hfs_systemfile_unlock(hfsmp, lockflags);
2458 return EINVAL;
2459 }
2460 hfs_systemfile_unlock(hfsmp, lockflags);
2461
2462 // make sure these both exist!
2463 if ( GetFileInfo(vcb, kHFSRootFolderID, ".journal_info_block", &jinfo_attr, &jinfo_fork) == 0
2464 || GetFileInfo(vcb, kHFSRootFolderID, ".journal", &jnl_attr, &jnl_fork) == 0) {
2465
2466 return EINVAL;
2467 }
2468
2469 hfs_sync(hfsmp->hfs_mp, MNT_WAIT, context);
2470
2471 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2472 (off_t)name[2], (off_t)name[3]);
2473
2474 //
2475 // XXXdbg - note that currently (Sept, 08) hfs_util does not support
2476 // enabling the journal on a separate device so it is safe
2477 // to just copy hfs_devvp here. If hfs_util gets the ability
2478 // to dynamically enable the journal on a separate device then
2479 // we will have to do the same thing as hfs_early_journal_init()
2480 // to locate and open the journal device.
2481 //
2482 jvp = hfsmp->hfs_devvp;
2483 jnl = journal_create(jvp,
2484 (off_t)name[2] * (off_t)HFSTOVCB(hfsmp)->blockSize
2485 + HFSTOVCB(hfsmp)->hfsPlusIOPosOffset,
2486 (off_t)((unsigned)name[3]),
2487 hfsmp->hfs_devvp,
2488 hfsmp->hfs_logical_block_size,
2489 0,
2490 0,
2491 hfs_sync_metadata, hfsmp->hfs_mp);
2492
2493 if (jnl == NULL) {
2494 printf("hfs: FAILED to create the journal!\n");
2495 if (jvp && jvp != hfsmp->hfs_devvp) {
2496 vnode_clearmountedon(jvp);
2497 VNOP_CLOSE(jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, vfs_context_kernel());
2498 }
2499 jvp = NULL;
2500
2501 return EINVAL;
2502 }
2503
2504 hfs_global_exclusive_lock_acquire(hfsmp);
2505
2506 /*
2507 * Flush all dirty metadata buffers.
2508 */
2509 buf_flushdirtyblks(hfsmp->hfs_devvp, MNT_WAIT, 0, "hfs_sysctl");
2510 buf_flushdirtyblks(hfsmp->hfs_extents_vp, MNT_WAIT, 0, "hfs_sysctl");
2511 buf_flushdirtyblks(hfsmp->hfs_catalog_vp, MNT_WAIT, 0, "hfs_sysctl");
2512 buf_flushdirtyblks(hfsmp->hfs_allocation_vp, MNT_WAIT, 0, "hfs_sysctl");
2513 if (hfsmp->hfs_attribute_vp)
2514 buf_flushdirtyblks(hfsmp->hfs_attribute_vp, MNT_WAIT, 0, "hfs_sysctl");
2515
2516 HFSTOVCB(hfsmp)->vcbJinfoBlock = name[1];
2517 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeJournaledMask;
2518 hfsmp->jvp = jvp;
2519 hfsmp->jnl = jnl;
2520
2521 // save this off for the hack-y check in hfs_remove()
2522 hfsmp->jnl_start = (u_int32_t)name[2];
2523 hfsmp->jnl_size = (off_t)((unsigned)name[3]);
2524 hfsmp->hfs_jnlinfoblkid = jinfo_attr.ca_fileid;
2525 hfsmp->hfs_jnlfileid = jnl_attr.ca_fileid;
2526
2527 vfs_setflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
2528
2529 hfs_global_exclusive_lock_release(hfsmp);
2530 hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1);
2531
2532 {
2533 fsid_t fsid;
2534
2535 fsid.val[0] = (int32_t)hfsmp->hfs_raw_dev;
2536 fsid.val[1] = (int32_t)vfs_typenum(HFSTOVFS(hfsmp));
2537 vfs_event_signal(&fsid, VQ_UPDATE, (intptr_t)NULL);
2538 }
2539 return 0;
2540 } else if (name[0] == HFS_DISABLE_JOURNALING) {
2541 // clear the journaling bit
2542 vnode_t vp = vfs_context_cwd(context);
2543
2544 /* Only root can disable journaling */
2545 if (!is_suser()) {
2546 return (EPERM);
2547 }
2548 if (vp == NULLVP)
2549 return EINVAL;
2550
2551 hfsmp = VTOHFS(vp);
2552
2553 /*
2554 * Disabling journaling is disallowed on volumes with directory hard links
2555 * because we have not tested the relevant code path.
2556 */
2557 if (hfsmp->hfs_private_attr[DIR_HARDLINKS].ca_entries != 0){
2558 printf("hfs: cannot disable journaling on volumes with directory hardlinks\n");
2559 return EPERM;
2560 }
2561
2562 printf("hfs: disabling journaling for mount @ %p\n", vnode_mount(vp));
2563
2564 hfs_global_exclusive_lock_acquire(hfsmp);
2565
2566 // Lights out for you buddy!
2567 journal_close(hfsmp->jnl);
2568 hfsmp->jnl = NULL;
2569
2570 if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
2571 vnode_clearmountedon(hfsmp->jvp);
2572 VNOP_CLOSE(hfsmp->jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, vfs_context_kernel());
2573 vnode_put(hfsmp->jvp);
2574 }
2575 hfsmp->jvp = NULL;
2576 vfs_clearflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
2577 hfsmp->jnl_start = 0;
2578 hfsmp->hfs_jnlinfoblkid = 0;
2579 hfsmp->hfs_jnlfileid = 0;
2580
2581 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeJournaledMask;
2582
2583 hfs_global_exclusive_lock_release(hfsmp);
2584 hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1);
2585
2586 {
2587 fsid_t fsid;
2588
2589 fsid.val[0] = (int32_t)hfsmp->hfs_raw_dev;
2590 fsid.val[1] = (int32_t)vfs_typenum(HFSTOVFS(hfsmp));
2591 vfs_event_signal(&fsid, VQ_UPDATE, (intptr_t)NULL);
2592 }
2593 return 0;
2594 } else if (name[0] == HFS_GET_JOURNAL_INFO) {
2595 vnode_t vp = vfs_context_cwd(context);
2596 off_t jnl_start, jnl_size;
2597
2598 if (vp == NULLVP)
2599 return EINVAL;
2600
2601 /* 64-bit processes won't work with this sysctl -- can't fit a pointer into an int! */
2602 if (proc_is64bit(current_proc()))
2603 return EINVAL;
2604
2605 hfsmp = VTOHFS(vp);
2606 if (hfsmp->jnl == NULL) {
2607 jnl_start = 0;
2608 jnl_size = 0;
2609 } else {
2610 jnl_start = (off_t)(hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset;
2611 jnl_size = (off_t)hfsmp->jnl_size;
2612 }
2613
2614 if ((error = copyout((caddr_t)&jnl_start, CAST_USER_ADDR_T(name[1]), sizeof(off_t))) != 0) {
2615 return error;
2616 }
2617 if ((error = copyout((caddr_t)&jnl_size, CAST_USER_ADDR_T(name[2]), sizeof(off_t))) != 0) {
2618 return error;
2619 }
2620
2621 return 0;
2622 } else if (name[0] == HFS_SET_PKG_EXTENSIONS) {
2623
2624 return set_package_extensions_table((user_addr_t)((unsigned)name[1]), name[2], name[3]);
2625
2626 } else if (name[0] == VFS_CTL_QUERY) {
2627 struct sysctl_req *req;
2628 union union_vfsidctl vc;
2629 struct mount *mp;
2630 struct vfsquery vq;
2631
2632 req = CAST_DOWN(struct sysctl_req *, oldp); /* we're new style vfs sysctl. */
2633
2634 error = SYSCTL_IN(req, &vc, proc_is64bit(p)? sizeof(vc.vc64):sizeof(vc.vc32));
2635 if (error) return (error);
2636
2637 mp = vfs_getvfs(&vc.vc32.vc_fsid); /* works for 32 and 64 */
2638 if (mp == NULL) return (ENOENT);
2639
2640 hfsmp = VFSTOHFS(mp);
2641 bzero(&vq, sizeof(vq));
2642 vq.vq_flags = hfsmp->hfs_notification_conditions;
2643 return SYSCTL_OUT(req, &vq, sizeof(vq));;
2644 } else if (name[0] == HFS_REPLAY_JOURNAL) {
2645 vnode_t devvp = NULL;
2646 int device_fd;
2647 if (namelen != 2) {
2648 return (EINVAL);
2649 }
2650 device_fd = name[1];
2651 error = file_vnode(device_fd, &devvp);
2652 if (error) {
2653 return error;
2654 }
2655 error = vnode_getwithref(devvp);
2656 if (error) {
2657 file_drop(device_fd);
2658 return error;
2659 }
2660 error = hfs_journal_replay(devvp, context);
2661 file_drop(device_fd);
2662 vnode_put(devvp);
2663 return error;
2664 }
2665
2666 return (ENOTSUP);
2667 }
2668
2669 /*
2670 * hfs_vfs_vget is not static since it is used in hfs_readwrite.c to support
2671 * the build_path ioctl. We use it to leverage the code below that updates
2672 * the origin list cache if necessary
2673 */
2674
2675 int
2676 hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, __unused vfs_context_t context)
2677 {
2678 int error;
2679 int lockflags;
2680 struct hfsmount *hfsmp;
2681
2682 hfsmp = VFSTOHFS(mp);
2683
2684 error = hfs_vget(hfsmp, (cnid_t)ino, vpp, 1);
2685 if (error)
2686 return (error);
2687
2688 /*
2689 * ADLs may need to have their origin state updated
2690 * since build_path needs a valid parent. The same is true
2691 * for hardlinked files as well. There isn't a race window here
2692 * in re-acquiring the cnode lock since we aren't pulling any data
2693 * out of the cnode; instead, we're going to the catalog.
2694 */
2695 if ((VTOC(*vpp)->c_flag & C_HARDLINK) &&
2696 (hfs_lock(VTOC(*vpp), HFS_EXCLUSIVE_LOCK) == 0)) {
2697 cnode_t *cp = VTOC(*vpp);
2698 struct cat_desc cdesc;
2699
2700 if (!hfs_haslinkorigin(cp)) {
2701 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2702 error = cat_findname(hfsmp, (cnid_t)ino, &cdesc);
2703 hfs_systemfile_unlock(hfsmp, lockflags);
2704 if (error == 0) {
2705 if ((cdesc.cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) &&
2706 (cdesc.cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)) {
2707 hfs_savelinkorigin(cp, cdesc.cd_parentcnid);
2708 }
2709 cat_releasedesc(&cdesc);
2710 }
2711 }
2712 hfs_unlock(cp);
2713 }
2714 return (0);
2715 }
2716
2717
2718 /*
2719 * Look up an HFS object by ID.
2720 *
2721 * The object is returned with an iocount reference and the cnode locked.
2722 *
2723 * If the object is a file then it will represent the data fork.
2724 */
2725 __private_extern__
2726 int
2727 hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
2728 {
2729 struct vnode *vp = NULLVP;
2730 struct cat_desc cndesc;
2731 struct cat_attr cnattr;
2732 struct cat_fork cnfork;
2733 u_int32_t linkref = 0;
2734 int error;
2735
2736 /* Check for cnids that should't be exported. */
2737 if ((cnid < kHFSFirstUserCatalogNodeID) &&
2738 (cnid != kHFSRootFolderID && cnid != kHFSRootParentID)) {
2739 return (ENOENT);
2740 }
2741 /* Don't export our private directories. */
2742 if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
2743 cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2744 return (ENOENT);
2745 }
2746 /*
2747 * Check the hash first
2748 */
2749 vp = hfs_chash_getvnode(hfsmp, cnid, 0, skiplock);
2750 if (vp) {
2751 *vpp = vp;
2752 return(0);
2753 }
2754
2755 bzero(&cndesc, sizeof(cndesc));
2756 bzero(&cnattr, sizeof(cnattr));
2757 bzero(&cnfork, sizeof(cnfork));
2758
2759 /*
2760 * Not in hash, lookup in catalog
2761 */
2762 if (cnid == kHFSRootParentID) {
2763 static char hfs_rootname[] = "/";
2764
2765 cndesc.cd_nameptr = (const u_int8_t *)&hfs_rootname[0];
2766 cndesc.cd_namelen = 1;
2767 cndesc.cd_parentcnid = kHFSRootParentID;
2768 cndesc.cd_cnid = kHFSRootFolderID;
2769 cndesc.cd_flags = CD_ISDIR;
2770
2771 cnattr.ca_fileid = kHFSRootFolderID;
2772 cnattr.ca_linkcount = 1;
2773 cnattr.ca_entries = 1;
2774 cnattr.ca_dircount = 1;
2775 cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
2776 } else {
2777 int lockflags;
2778 cnid_t pid;
2779 const char *nameptr;
2780
2781 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2782 error = cat_idlookup(hfsmp, cnid, 0, &cndesc, &cnattr, &cnfork);
2783 hfs_systemfile_unlock(hfsmp, lockflags);
2784
2785 if (error) {
2786 *vpp = NULL;
2787 return (error);
2788 }
2789
2790 /*
2791 * Check for a raw hardlink inode and save its linkref.
2792 */
2793 pid = cndesc.cd_parentcnid;
2794 nameptr = (const char *)cndesc.cd_nameptr;
2795
2796 if ((pid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
2797 (bcmp(nameptr, HFS_INODE_PREFIX, HFS_INODE_PREFIX_LEN) == 0)) {
2798 linkref = strtoul(&nameptr[HFS_INODE_PREFIX_LEN], NULL, 10);
2799
2800 } else if ((pid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) &&
2801 (bcmp(nameptr, HFS_DIRINODE_PREFIX, HFS_DIRINODE_PREFIX_LEN) == 0)) {
2802 linkref = strtoul(&nameptr[HFS_DIRINODE_PREFIX_LEN], NULL, 10);
2803
2804 } else if ((pid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
2805 (bcmp(nameptr, HFS_DELETE_PREFIX, HFS_DELETE_PREFIX_LEN) == 0)) {
2806 *vpp = NULL;
2807 cat_releasedesc(&cndesc);
2808 return (ENOENT); /* open unlinked file */
2809 }
2810 }
2811
2812 /*
2813 * Finish initializing cnode descriptor for hardlinks.
2814 *
2815 * We need a valid name and parent for reverse lookups.
2816 */
2817 if (linkref) {
2818 cnid_t nextlinkid;
2819 cnid_t prevlinkid;
2820 struct cat_desc linkdesc;
2821 int lockflags;
2822
2823 cnattr.ca_linkref = linkref;
2824
2825 /*
2826 * Pick up the first link in the chain and get a descriptor for it.
2827 * This allows blind volfs paths to work for hardlinks.
2828 */
2829 if ((hfs_lookuplink(hfsmp, linkref, &prevlinkid, &nextlinkid) == 0) &&
2830 (nextlinkid != 0)) {
2831 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2832 error = cat_findname(hfsmp, nextlinkid, &linkdesc);
2833 hfs_systemfile_unlock(hfsmp, lockflags);
2834 if (error == 0) {
2835 cat_releasedesc(&cndesc);
2836 bcopy(&linkdesc, &cndesc, sizeof(linkdesc));
2837 }
2838 }
2839 }
2840
2841 if (linkref) {
2842 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cnfork, &vp);
2843 if (error == 0) {
2844 VTOC(vp)->c_flag |= C_HARDLINK;
2845 vnode_setmultipath(vp);
2846 }
2847 } else {
2848 struct componentname cn;
2849
2850 /* Supply hfs_getnewvnode with a component name. */
2851 MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
2852 cn.cn_nameiop = LOOKUP;
2853 cn.cn_flags = ISLASTCN | HASBUF;
2854 cn.cn_context = NULL;
2855 cn.cn_pnlen = MAXPATHLEN;
2856 cn.cn_nameptr = cn.cn_pnbuf;
2857 cn.cn_namelen = cndesc.cd_namelen;
2858 cn.cn_hash = 0;
2859 cn.cn_consume = 0;
2860 bcopy(cndesc.cd_nameptr, cn.cn_nameptr, cndesc.cd_namelen + 1);
2861
2862 error = hfs_getnewvnode(hfsmp, NULLVP, &cn, &cndesc, 0, &cnattr, &cnfork, &vp);
2863
2864 if (error == 0 && (VTOC(vp)->c_flag & C_HARDLINK)) {
2865 hfs_savelinkorigin(VTOC(vp), cndesc.cd_parentcnid);
2866 }
2867 FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
2868 }
2869 cat_releasedesc(&cndesc);
2870
2871 *vpp = vp;
2872 if (vp && skiplock) {
2873 hfs_unlock(VTOC(vp));
2874 }
2875 return (error);
2876 }
2877
2878
2879 /*
2880 * Flush out all the files in a filesystem.
2881 */
2882 static int
2883 #if QUOTA
2884 hfs_flushfiles(struct mount *mp, int flags, struct proc *p)
2885 #else
2886 hfs_flushfiles(struct mount *mp, int flags, __unused struct proc *p)
2887 #endif /* QUOTA */
2888 {
2889 struct hfsmount *hfsmp;
2890 struct vnode *skipvp = NULLVP;
2891 int error;
2892 #if QUOTA
2893 int quotafilecnt;
2894 int i;
2895 #endif
2896
2897 hfsmp = VFSTOHFS(mp);
2898
2899 #if QUOTA
2900 /*
2901 * The open quota files have an indirect reference on
2902 * the root directory vnode. We must account for this
2903 * extra reference when doing the intial vflush.
2904 */
2905 quotafilecnt = 0;
2906 if (((unsigned int)vfs_flags(mp)) & MNT_QUOTA) {
2907
2908 /* Find out how many quota files we have open. */
2909 for (i = 0; i < MAXQUOTAS; i++) {
2910 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
2911 ++quotafilecnt;
2912 }
2913
2914 /* Obtain the root vnode so we can skip over it. */
2915 skipvp = hfs_chash_getvnode(hfsmp, kHFSRootFolderID, 0, 0);
2916 }
2917 #endif /* QUOTA */
2918
2919 error = vflush(mp, skipvp, SKIPSYSTEM | SKIPSWAP | flags);
2920 if (error != 0)
2921 return(error);
2922
2923 error = vflush(mp, skipvp, SKIPSYSTEM | flags);
2924
2925 #if QUOTA
2926 if (((unsigned int)vfs_flags(mp)) & MNT_QUOTA) {
2927 if (skipvp) {
2928 /*
2929 * See if there are additional references on the
2930 * root vp besides the ones obtained from the open
2931 * quota files and the hfs_chash_getvnode call above.
2932 */
2933 if ((error == 0) &&
2934 (vnode_isinuse(skipvp, quotafilecnt))) {
2935 error = EBUSY; /* root directory is still open */
2936 }
2937 hfs_unlock(VTOC(skipvp));
2938 vnode_put(skipvp);
2939 }
2940 if (error && (flags & FORCECLOSE) == 0)
2941 return (error);
2942
2943 for (i = 0; i < MAXQUOTAS; i++) {
2944 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
2945 continue;
2946 hfs_quotaoff(p, mp, i);
2947 }
2948 error = vflush(mp, NULLVP, SKIPSYSTEM | flags);
2949 }
2950 #endif /* QUOTA */
2951
2952 return (error);
2953 }
2954
2955 /*
2956 * Update volume encoding bitmap (HFS Plus only)
2957 */
2958 __private_extern__
2959 void
2960 hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding)
2961 {
2962 #define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */
2963 #define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */
2964
2965 u_int32_t index;
2966
2967 switch (encoding) {
2968 case kTextEncodingMacUkrainian:
2969 index = kIndexMacUkrainian;
2970 break;
2971 case kTextEncodingMacFarsi:
2972 index = kIndexMacFarsi;
2973 break;
2974 default:
2975 index = encoding;
2976 break;
2977 }
2978
2979 if (index < 64 && (hfsmp->encodingsBitmap & (u_int64_t)(1ULL << index)) == 0) {
2980 HFS_MOUNT_LOCK(hfsmp, TRUE)
2981 hfsmp->encodingsBitmap |= (u_int64_t)(1ULL << index);
2982 MarkVCBDirty(hfsmp);
2983 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
2984 }
2985 }
2986
2987 /*
2988 * Update volume stats
2989 *
2990 * On journal volumes this will cause a volume header flush
2991 */
2992 __private_extern__
2993 int
2994 hfs_volupdate(struct hfsmount *hfsmp, enum volop op, int inroot)
2995 {
2996 struct timeval tv;
2997
2998 microtime(&tv);
2999
3000 lck_mtx_lock(&hfsmp->hfs_mutex);
3001
3002 MarkVCBDirty(hfsmp);
3003 hfsmp->hfs_mtime = tv.tv_sec;
3004
3005 switch (op) {
3006 case VOL_UPDATE:
3007 break;
3008 case VOL_MKDIR:
3009 if (hfsmp->hfs_dircount != 0xFFFFFFFF)
3010 ++hfsmp->hfs_dircount;
3011 if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
3012 ++hfsmp->vcbNmRtDirs;
3013 break;
3014 case VOL_RMDIR:
3015 if (hfsmp->hfs_dircount != 0)
3016 --hfsmp->hfs_dircount;
3017 if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
3018 --hfsmp->vcbNmRtDirs;
3019 break;
3020 case VOL_MKFILE:
3021 if (hfsmp->hfs_filecount != 0xFFFFFFFF)
3022 ++hfsmp->hfs_filecount;
3023 if (inroot && hfsmp->vcbNmFls != 0xFFFF)
3024 ++hfsmp->vcbNmFls;
3025 break;
3026 case VOL_RMFILE:
3027 if (hfsmp->hfs_filecount != 0)
3028 --hfsmp->hfs_filecount;
3029 if (inroot && hfsmp->vcbNmFls != 0xFFFF)
3030 --hfsmp->vcbNmFls;
3031 break;
3032 }
3033
3034 lck_mtx_unlock(&hfsmp->hfs_mutex);
3035
3036 if (hfsmp->jnl) {
3037 hfs_flushvolumeheader(hfsmp, 0, 0);
3038 }
3039
3040 return (0);
3041 }
3042
3043
3044 static int
3045 hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush)
3046 {
3047 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
3048 struct filefork *fp;
3049 HFSMasterDirectoryBlock *mdb;
3050 struct buf *bp = NULL;
3051 int retval;
3052 int sectorsize;
3053 ByteCount namelen;
3054
3055 sectorsize = hfsmp->hfs_logical_block_size;
3056 retval = (int)buf_bread(hfsmp->hfs_devvp, (daddr64_t)HFS_PRI_SECTOR(sectorsize), sectorsize, NOCRED, &bp);
3057 if (retval) {
3058 if (bp)
3059 buf_brelse(bp);
3060 return retval;
3061 }
3062
3063 lck_mtx_lock(&hfsmp->hfs_mutex);
3064
3065 mdb = (HFSMasterDirectoryBlock *)(buf_dataptr(bp) + HFS_PRI_OFFSET(sectorsize));
3066
3067 mdb->drCrDate = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbCrDate)));
3068 mdb->drLsMod = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbLsMod)));
3069 mdb->drAtrb = SWAP_BE16 (vcb->vcbAtrb);
3070 mdb->drNmFls = SWAP_BE16 (vcb->vcbNmFls);
3071 mdb->drAllocPtr = SWAP_BE16 (vcb->nextAllocation);
3072 mdb->drClpSiz = SWAP_BE32 (vcb->vcbClpSiz);
3073 mdb->drNxtCNID = SWAP_BE32 (vcb->vcbNxtCNID);
3074 mdb->drFreeBks = SWAP_BE16 (vcb->freeBlocks);
3075
3076 namelen = strlen((char *)vcb->vcbVN);
3077 retval = utf8_to_hfs(vcb, namelen, vcb->vcbVN, mdb->drVN);
3078 /* Retry with MacRoman in case that's how it was exported. */
3079 if (retval)
3080 retval = utf8_to_mac_roman(namelen, vcb->vcbVN, mdb->drVN);
3081
3082 mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbVolBkUp)));
3083 mdb->drWrCnt = SWAP_BE32 (vcb->vcbWrCnt);
3084 mdb->drNmRtDirs = SWAP_BE16 (vcb->vcbNmRtDirs);
3085 mdb->drFilCnt = SWAP_BE32 (vcb->vcbFilCnt);
3086 mdb->drDirCnt = SWAP_BE32 (vcb->vcbDirCnt);
3087
3088 bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo));
3089
3090 fp = VTOF(vcb->extentsRefNum);
3091 mdb->drXTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
3092 mdb->drXTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
3093 mdb->drXTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
3094 mdb->drXTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
3095 mdb->drXTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
3096 mdb->drXTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
3097 mdb->drXTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
3098 mdb->drXTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
3099 FTOC(fp)->c_flag &= ~C_MODIFIED;
3100
3101 fp = VTOF(vcb->catalogRefNum);
3102 mdb->drCTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
3103 mdb->drCTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
3104 mdb->drCTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
3105 mdb->drCTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
3106 mdb->drCTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
3107 mdb->drCTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
3108 mdb->drCTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
3109 mdb->drCTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
3110 FTOC(fp)->c_flag &= ~C_MODIFIED;
3111
3112 MarkVCBClean( vcb );
3113
3114 lck_mtx_unlock(&hfsmp->hfs_mutex);
3115
3116 /* If requested, flush out the alternate MDB */
3117 if (altflush) {
3118 struct buf *alt_bp = NULL;
3119
3120 if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector, sectorsize, NOCRED, &alt_bp) == 0) {
3121 bcopy(mdb, (char *)buf_dataptr(alt_bp) + HFS_ALT_OFFSET(sectorsize), kMDBSize);
3122
3123 (void) VNOP_BWRITE(alt_bp);
3124 } else if (alt_bp)
3125 buf_brelse(alt_bp);
3126 }
3127
3128 if (waitfor != MNT_WAIT)
3129 buf_bawrite(bp);
3130 else
3131 retval = VNOP_BWRITE(bp);
3132
3133 return (retval);
3134 }
3135
3136 /*
3137 * Flush any dirty in-memory mount data to the on-disk
3138 * volume header.
3139 *
3140 * Note: the on-disk volume signature is intentionally
3141 * not flushed since the on-disk "H+" and "HX" signatures
3142 * are always stored in-memory as "H+".
3143 */
3144 __private_extern__
3145 int
3146 hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush)
3147 {
3148 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
3149 struct filefork *fp;
3150 HFSPlusVolumeHeader *volumeHeader, *altVH;
3151 int retval;
3152 struct buf *bp, *alt_bp;
3153 int i;
3154 daddr64_t priIDSector;
3155 int critical;
3156 u_int16_t signature;
3157 u_int16_t hfsversion;
3158
3159 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
3160 return(0);
3161 }
3162 if (hfsmp->hfs_flags & HFS_STANDARD) {
3163 return hfs_flushMDB(hfsmp, waitfor, altflush);
3164 }
3165 critical = altflush;
3166 priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
3167 HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size));
3168
3169 if (hfs_start_transaction(hfsmp) != 0) {
3170 return EINVAL;
3171 }
3172
3173 bp = NULL;
3174 alt_bp = NULL;
3175
3176 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
3177 HFS_PHYSBLK_ROUNDDOWN(priIDSector, hfsmp->hfs_log_per_phys),
3178 hfsmp->hfs_physical_block_size, NOCRED, &bp);
3179 if (retval) {
3180 printf("hfs: err %d reading VH blk (%s)\n", retval, vcb->vcbVN);
3181 goto err_exit;
3182 }
3183
3184 volumeHeader = (HFSPlusVolumeHeader *)((char *)buf_dataptr(bp) +
3185 HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
3186
3187 /*
3188 * Sanity check what we just read. If it's bad, try the alternate
3189 * instead.
3190 */
3191 signature = SWAP_BE16 (volumeHeader->signature);
3192 hfsversion = SWAP_BE16 (volumeHeader->version);
3193 if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) ||
3194 (hfsversion < kHFSPlusVersion) || (hfsversion > 100) ||
3195 (SWAP_BE32 (volumeHeader->blockSize) != vcb->blockSize)) {
3196 printf("hfs: corrupt VH on %s, sig 0x%04x, ver %d, blksize %d%s\n",
3197 vcb->vcbVN, signature, hfsversion,
3198 SWAP_BE32 (volumeHeader->blockSize),
3199 hfsmp->hfs_alt_id_sector ? "; trying alternate" : "");
3200 hfs_mark_volume_inconsistent(hfsmp);
3201
3202 if (hfsmp->hfs_alt_id_sector) {
3203 retval = buf_meta_bread(hfsmp->hfs_devvp,
3204 HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_alt_id_sector, hfsmp->hfs_log_per_phys),
3205 hfsmp->hfs_physical_block_size, NOCRED, &alt_bp);
3206 if (retval) {
3207 printf("hfs: err %d reading alternate VH (%s)\n", retval, vcb->vcbVN);
3208 goto err_exit;
3209 }
3210
3211 altVH = (HFSPlusVolumeHeader *)((char *)buf_dataptr(alt_bp) +
3212 HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size));
3213 signature = SWAP_BE16(altVH->signature);
3214 hfsversion = SWAP_BE16(altVH->version);
3215
3216 if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) ||
3217 (hfsversion < kHFSPlusVersion) || (kHFSPlusVersion > 100) ||
3218 (SWAP_BE32(altVH->blockSize) != vcb->blockSize)) {
3219 printf("hfs: corrupt alternate VH on %s, sig 0x%04x, ver %d, blksize %d\n",
3220 vcb->vcbVN, signature, hfsversion,
3221 SWAP_BE32(altVH->blockSize));
3222 retval = EIO;
3223 goto err_exit;
3224 }
3225
3226 /* The alternate is plausible, so use it. */
3227 bcopy(altVH, volumeHeader, kMDBSize);
3228 buf_brelse(alt_bp);
3229 alt_bp = NULL;
3230 } else {
3231 /* No alternate VH, nothing more we can do. */
3232 retval = EIO;
3233 goto err_exit;
3234 }
3235 }
3236
3237 if (hfsmp->jnl) {
3238 journal_modify_block_start(hfsmp->jnl, bp);
3239 }
3240
3241 /*
3242 * For embedded HFS+ volumes, update create date if it changed
3243 * (ie from a setattrlist call)
3244 */
3245 if ((vcb->hfsPlusIOPosOffset != 0) &&
3246 (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate)) {
3247 struct buf *bp2;
3248 HFSMasterDirectoryBlock *mdb;
3249
3250 retval = (int)buf_meta_bread(hfsmp->hfs_devvp,
3251 HFS_PHYSBLK_ROUNDDOWN(HFS_PRI_SECTOR(hfsmp->hfs_logical_block_size), hfsmp->hfs_log_per_phys),
3252 hfsmp->hfs_physical_block_size, NOCRED, &bp2);
3253 if (retval) {
3254 if (bp2)
3255 buf_brelse(bp2);
3256 retval = 0;
3257 } else {
3258 mdb = (HFSMasterDirectoryBlock *)(buf_dataptr(bp2) +
3259 HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
3260
3261 if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate )
3262 {
3263 if (hfsmp->jnl) {
3264 journal_modify_block_start(hfsmp->jnl, bp2);
3265 }
3266
3267 mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate); /* pick up the new create date */
3268
3269 if (hfsmp->jnl) {
3270 journal_modify_block_end(hfsmp->jnl, bp2, NULL, NULL);
3271 } else {
3272 (void) VNOP_BWRITE(bp2); /* write out the changes */
3273 }
3274 }
3275 else
3276 {
3277 buf_brelse(bp2); /* just release it */
3278 }
3279 }
3280 }
3281
3282 lck_mtx_lock(&hfsmp->hfs_mutex);
3283
3284 /* Note: only update the lower 16 bits worth of attributes */
3285 volumeHeader->attributes = SWAP_BE32 (vcb->vcbAtrb);
3286 volumeHeader->journalInfoBlock = SWAP_BE32 (vcb->vcbJinfoBlock);
3287 if (hfsmp->jnl) {
3288 volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSJMountVersion);
3289 } else {
3290 volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion);
3291 }
3292 volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate); /* volume create date is in local time */
3293 volumeHeader->modifyDate = SWAP_BE32 (to_hfs_time(vcb->vcbLsMod));
3294 volumeHeader->backupDate = SWAP_BE32 (to_hfs_time(vcb->vcbVolBkUp));
3295 volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt);
3296 volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt);
3297 volumeHeader->totalBlocks = SWAP_BE32 (vcb->totalBlocks);
3298 volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks);
3299 volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation);
3300 volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
3301 volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
3302 volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID);
3303 volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt);
3304 volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap);
3305
3306 if (bcmp(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)) != 0) {
3307 bcopy(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo));
3308 critical = 1;
3309 }
3310
3311 /*
3312 * System files are only dirty when altflush is set.
3313 */
3314 if (altflush == 0) {
3315 goto done;
3316 }
3317
3318 /* Sync Extents over-flow file meta data */
3319 fp = VTOF(vcb->extentsRefNum);
3320 if (FTOC(fp)->c_flag & C_MODIFIED) {
3321 for (i = 0; i < kHFSPlusExtentDensity; i++) {
3322 volumeHeader->extentsFile.extents[i].startBlock =
3323 SWAP_BE32 (fp->ff_extents[i].startBlock);
3324 volumeHeader->extentsFile.extents[i].blockCount =
3325 SWAP_BE32 (fp->ff_extents[i].blockCount);
3326 }
3327 volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fp->ff_size);
3328 volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
3329 volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
3330 FTOC(fp)->c_flag &= ~C_MODIFIED;
3331 }
3332
3333 /* Sync Catalog file meta data */
3334 fp = VTOF(vcb->catalogRefNum);
3335 if (FTOC(fp)->c_flag & C_MODIFIED) {
3336 for (i = 0; i < kHFSPlusExtentDensity; i++) {
3337 volumeHeader->catalogFile.extents[i].startBlock =
3338 SWAP_BE32 (fp->ff_extents[i].startBlock);
3339 volumeHeader->catalogFile.extents[i].blockCount =
3340 SWAP_BE32 (fp->ff_extents[i].blockCount);
3341 }
3342 volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fp->ff_size);
3343 volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
3344 volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
3345 FTOC(fp)->c_flag &= ~C_MODIFIED;
3346 }
3347
3348 /* Sync Allocation file meta data */
3349 fp = VTOF(vcb->allocationsRefNum);
3350 if (FTOC(fp)->c_flag & C_MODIFIED) {
3351 for (i = 0; i < kHFSPlusExtentDensity; i++) {
3352 volumeHeader->allocationFile.extents[i].startBlock =
3353 SWAP_BE32 (fp->ff_extents[i].startBlock);
3354 volumeHeader->allocationFile.extents[i].blockCount =
3355 SWAP_BE32 (fp->ff_extents[i].blockCount);
3356 }
3357 volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fp->ff_size);
3358 volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
3359 volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
3360 FTOC(fp)->c_flag &= ~C_MODIFIED;
3361 }
3362
3363 /* Sync Attribute file meta data */
3364 if (hfsmp->hfs_attribute_vp) {
3365 fp = VTOF(hfsmp->hfs_attribute_vp);
3366 for (i = 0; i < kHFSPlusExtentDensity; i++) {
3367 volumeHeader->attributesFile.extents[i].startBlock =
3368 SWAP_BE32 (fp->ff_extents[i].startBlock);
3369 volumeHeader->attributesFile.extents[i].blockCount =
3370 SWAP_BE32 (fp->ff_extents[i].blockCount);
3371 }
3372 FTOC(fp)->c_flag &= ~C_MODIFIED;
3373 volumeHeader->attributesFile.logicalSize = SWAP_BE64 (fp->ff_size);
3374 volumeHeader->attributesFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
3375 volumeHeader->attributesFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
3376 }
3377
3378 /* Sync Startup file meta data */
3379 if (hfsmp->hfs_startup_vp) {
3380 fp = VTOF(hfsmp->hfs_startup_vp);
3381 if (FTOC(fp)->c_flag & C_MODIFIED) {
3382 for (i = 0; i < kHFSPlusExtentDensity; i++) {
3383 volumeHeader->startupFile.extents[i].startBlock =
3384 SWAP_BE32 (fp->ff_extents[i].startBlock);
3385 volumeHeader->startupFile.extents[i].blockCount =
3386 SWAP_BE32 (fp->ff_extents[i].blockCount);
3387 }
3388 volumeHeader->startupFile.logicalSize = SWAP_BE64 (fp->ff_size);
3389 volumeHeader->startupFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
3390 volumeHeader->startupFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
3391 FTOC(fp)->c_flag &= ~C_MODIFIED;
3392 }
3393 }
3394
3395 done:
3396 MarkVCBClean(hfsmp);
3397 lck_mtx_unlock(&hfsmp->hfs_mutex);
3398
3399 /* If requested, flush out the alternate volume header */
3400 if (altflush && hfsmp->hfs_alt_id_sector) {
3401 if (buf_meta_bread(hfsmp->hfs_devvp,
3402 HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_alt_id_sector, hfsmp->hfs_log_per_phys),
3403 hfsmp->hfs_physical_block_size, NOCRED, &alt_bp) == 0) {
3404 if (hfsmp->jnl) {
3405 journal_modify_block_start(hfsmp->jnl, alt_bp);
3406 }
3407
3408 bcopy(volumeHeader, (char *)buf_dataptr(alt_bp) +
3409 HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size),
3410 kMDBSize);
3411
3412 if (hfsmp->jnl) {
3413 journal_modify_block_end(hfsmp->jnl, alt_bp, NULL, NULL);
3414 } else {
3415 (void) VNOP_BWRITE(alt_bp);
3416 }
3417 } else if (alt_bp)
3418 buf_brelse(alt_bp);
3419 }
3420
3421 if (hfsmp->jnl) {
3422 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
3423 } else {
3424 if (waitfor != MNT_WAIT)
3425 buf_bawrite(bp);
3426 else {
3427 retval = VNOP_BWRITE(bp);
3428 /* When critical data changes, flush the device cache */
3429 if (critical && (retval == 0)) {
3430 (void) VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE,
3431 NULL, FWRITE, NULL);
3432 }
3433 }
3434 }
3435 hfs_end_transaction(hfsmp);
3436
3437 return (retval);
3438
3439 err_exit:
3440 if (alt_bp)
3441 buf_brelse(alt_bp);
3442 if (bp)
3443 buf_brelse(bp);
3444 hfs_end_transaction(hfsmp);
3445 return retval;
3446 }
3447
3448
3449 /*
3450 * Extend a file system.
3451 */
3452 __private_extern__
3453 int
3454 hfs_extendfs(struct hfsmount *hfsmp, u_int64_t newsize, vfs_context_t context)
3455 {
3456 struct proc *p = vfs_context_proc(context);
3457 kauth_cred_t cred = vfs_context_ucred(context);
3458 struct vnode *vp;
3459 struct vnode *devvp;
3460 struct buf *bp;
3461 struct filefork *fp = NULL;
3462 ExtendedVCB *vcb;
3463 struct cat_fork forkdata;
3464 u_int64_t oldsize;
3465 u_int64_t newblkcnt;
3466 u_int64_t prev_phys_block_count;
3467 u_int32_t addblks;
3468 u_int64_t sectorcnt;
3469 u_int32_t sectorsize;
3470 u_int32_t phys_sectorsize;
3471 daddr64_t prev_alt_sector;
3472 daddr_t bitmapblks;
3473 int lockflags;
3474 int error;
3475 int64_t oldBitmapSize;
3476 Boolean usedExtendFileC = false;
3477
3478 devvp = hfsmp->hfs_devvp;
3479 vcb = HFSTOVCB(hfsmp);
3480
3481 /*
3482 * - HFS Plus file systems only.
3483 * - Journaling must be enabled.
3484 * - No embedded volumes.
3485 */
3486 if ((vcb->vcbSigWord == kHFSSigWord) ||
3487 (hfsmp->jnl == NULL) ||
3488 (vcb->hfsPlusIOPosOffset != 0)) {
3489 return (EPERM);
3490 }
3491 /*
3492 * If extending file system by non-root, then verify
3493 * ownership and check permissions.
3494 */
3495 if (suser(cred, NULL)) {
3496 error = hfs_vget(hfsmp, kHFSRootFolderID, &vp, 0);
3497
3498 if (error)
3499 return (error);
3500 error = hfs_owner_rights(hfsmp, VTOC(vp)->c_uid, cred, p, 0);
3501 if (error == 0) {
3502 error = hfs_write_access(vp, cred, p, false);
3503 }
3504 hfs_unlock(VTOC(vp));
3505 vnode_put(vp);
3506 if (error)
3507 return (error);
3508
3509 error = vnode_authorize(devvp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, context);
3510 if (error)
3511 return (error);
3512 }
3513 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&sectorsize, 0, context)) {
3514 return (ENXIO);
3515 }
3516 if (sectorsize != hfsmp->hfs_logical_block_size) {
3517 return (ENXIO);
3518 }
3519 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&sectorcnt, 0, context)) {
3520 return (ENXIO);
3521 }
3522 if ((sectorsize * sectorcnt) < newsize) {
3523 printf("hfs_extendfs: not enough space on device\n");
3524 return (ENOSPC);
3525 }
3526 error = VNOP_IOCTL(devvp, DKIOCGETPHYSICALBLOCKSIZE, (caddr_t)&phys_sectorsize, 0, context);
3527 if (error) {
3528 if ((error != ENOTSUP) && (error != ENOTTY)) {
3529 return (ENXIO);
3530 }
3531 /* If ioctl is not supported, force physical and logical sector size to be same */
3532 phys_sectorsize = sectorsize;
3533 }
3534 oldsize = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
3535
3536 /*
3537 * Validate new size.
3538 */
3539 if ((newsize <= oldsize) || (newsize % sectorsize) || (newsize % phys_sectorsize)) {
3540 printf("hfs_extendfs: invalid size\n");
3541 return (EINVAL);
3542 }
3543 newblkcnt = newsize / vcb->blockSize;
3544 if (newblkcnt > (u_int64_t)0xFFFFFFFF)
3545 return (EOVERFLOW);
3546
3547 addblks = newblkcnt - vcb->totalBlocks;
3548
3549 printf("hfs_extendfs: growing %s by %d blocks\n", vcb->vcbVN, addblks);
3550 /*
3551 * Enclose changes inside a transaction.
3552 */
3553 if (hfs_start_transaction(hfsmp) != 0) {
3554 return (EINVAL);
3555 }
3556
3557 /*
3558 * Note: we take the attributes lock in case we have an attribute data vnode
3559 * which needs to change size.
3560 */
3561 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
3562 vp = vcb->allocationsRefNum;
3563 fp = VTOF(vp);
3564 bcopy(&fp->ff_data, &forkdata, sizeof(forkdata));
3565
3566 /*
3567 * Calculate additional space required (if any) by allocation bitmap.
3568 */
3569 oldBitmapSize = fp->ff_size;
3570 bitmapblks = roundup((newblkcnt+7) / 8, vcb->vcbVBMIOSize) / vcb->blockSize;
3571 if (bitmapblks > (daddr_t)fp->ff_blocks)
3572 bitmapblks -= fp->ff_blocks;
3573 else
3574 bitmapblks = 0;
3575
3576 if (bitmapblks > 0) {
3577 daddr64_t blkno;
3578 daddr_t blkcnt;
3579 off_t bytesAdded;
3580
3581 /*
3582 * Get the bitmap's current size (in allocation blocks) so we know
3583 * where to start zero filling once the new space is added. We've
3584 * got to do this before the bitmap is grown.
3585 */
3586 blkno = (daddr64_t)fp->ff_blocks;
3587
3588 /*
3589 * Try to grow the allocation file in the normal way, using allocation
3590 * blocks already existing in the file system. This way, we might be
3591 * able to grow the bitmap contiguously, or at least in the metadata
3592 * zone.
3593 */
3594 error = ExtendFileC(vcb, fp, bitmapblks * vcb->blockSize, 0,
3595 kEFAllMask | kEFNoClumpMask | kEFReserveMask | kEFMetadataMask,
3596 &bytesAdded);
3597
3598 if (error == 0) {
3599 usedExtendFileC = true;
3600 } else {
3601 /*
3602 * If the above allocation failed, fall back to allocating the new
3603 * extent of the bitmap from the space we're going to add. Since those
3604 * blocks don't yet belong to the file system, we have to update the
3605 * extent list directly, and manually adjust the file size.
3606 */
3607 bytesAdded = 0;
3608 error = AddFileExtent(vcb, fp, vcb->totalBlocks, bitmapblks);
3609 if (error) {
3610 printf("hfs_extendfs: error %d adding extents\n", error);
3611 goto out;
3612 }
3613 fp->ff_blocks += bitmapblks;
3614 VTOC(vp)->c_blocks = fp->ff_blocks;
3615 VTOC(vp)->c_flag |= C_MODIFIED;
3616 }
3617
3618 /*
3619 * Update the allocation file's size to include the newly allocated
3620 * blocks. Note that ExtendFileC doesn't do this, which is why this
3621 * statement is outside the above "if" statement.
3622 */
3623 fp->ff_size += (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize;
3624
3625 /*
3626 * Zero out the new bitmap blocks.
3627 */
3628 {
3629
3630 bp = NULL;
3631 blkcnt = bitmapblks;
3632 while (blkcnt > 0) {
3633 error = (int)buf_meta_bread(vp, blkno, vcb->blockSize, NOCRED, &bp);
3634 if (error) {
3635 if (bp) {
3636 buf_brelse(bp);
3637 }
3638 break;
3639 }
3640 bzero((char *)buf_dataptr(bp), vcb->blockSize);
3641 buf_markaged(bp);
3642 error = (int)buf_bwrite(bp);
3643 if (error)
3644 break;
3645 --blkcnt;
3646 ++blkno;
3647 }
3648 }
3649 if (error) {
3650 printf("hfs_extendfs: error %d clearing blocks\n", error);
3651 goto out;
3652 }
3653 /*
3654 * Mark the new bitmap space as allocated.
3655 *
3656 * Note that ExtendFileC will have marked any blocks it allocated, so
3657 * this is only needed if we used AddFileExtent. Also note that this
3658 * has to come *after* the zero filling of new blocks in the case where
3659 * we used AddFileExtent (since the part of the bitmap we're touching
3660 * is in those newly allocated blocks).
3661 */
3662 if (!usedExtendFileC) {
3663 error = BlockMarkAllocated(vcb, vcb->totalBlocks, bitmapblks);
3664 if (error) {
3665 printf("hfs_extendfs: error %d setting bitmap\n", error);
3666 goto out;
3667 }
3668 vcb->freeBlocks -= bitmapblks;
3669 }
3670 }
3671 /*
3672 * Mark the new alternate VH as allocated.
3673 */
3674 if (vcb->blockSize == 512)
3675 error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 2, 2);
3676 else
3677 error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 1, 1);
3678 if (error) {
3679 printf("hfs_extendfs: error %d setting bitmap (VH)\n", error);
3680 goto out;
3681 }
3682 /*
3683 * Mark the old alternate VH as free.
3684 */
3685 if (vcb->blockSize == 512)
3686 (void) BlockMarkFree(vcb, vcb->totalBlocks - 2, 2);
3687 else
3688 (void) BlockMarkFree(vcb, vcb->totalBlocks - 1, 1);
3689 /*
3690 * Adjust file system variables for new space.
3691 */
3692 prev_phys_block_count = hfsmp->hfs_logical_block_count;
3693 prev_alt_sector = hfsmp->hfs_alt_id_sector;
3694
3695 vcb->totalBlocks += addblks;
3696 vcb->freeBlocks += addblks;
3697 hfsmp->hfs_logical_block_count = newsize / sectorsize;
3698 hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / sectorsize) +
3699 HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_logical_block_count);
3700 MarkVCBDirty(vcb);
3701 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
3702 if (error) {
3703 printf("hfs_extendfs: couldn't flush volume headers (%d)", error);
3704 /*
3705 * Restore to old state.
3706 */
3707 if (usedExtendFileC) {
3708 (void) TruncateFileC(vcb, fp, oldBitmapSize, false);
3709 } else {
3710 fp->ff_blocks -= bitmapblks;
3711 fp->ff_size -= (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize;
3712 /*
3713 * No need to mark the excess blocks free since those bitmap blocks
3714 * are no longer part of the bitmap. But we do need to undo the
3715 * effect of the "vcb->freeBlocks -= bitmapblks" above.
3716 */
3717 vcb->freeBlocks += bitmapblks;
3718 }
3719 vcb->totalBlocks -= addblks;
3720 vcb->freeBlocks -= addblks;
3721 hfsmp->hfs_logical_block_count = prev_phys_block_count;
3722 hfsmp->hfs_alt_id_sector = prev_alt_sector;
3723 MarkVCBDirty(vcb);
3724 if (vcb->blockSize == 512)
3725 (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 2, 2);
3726 else
3727 (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 1, 1);
3728 goto out;
3729 }
3730 /*
3731 * Invalidate the old alternate volume header.
3732 */
3733 bp = NULL;
3734 if (prev_alt_sector) {
3735 if (buf_meta_bread(hfsmp->hfs_devvp,
3736 HFS_PHYSBLK_ROUNDDOWN(prev_alt_sector, hfsmp->hfs_log_per_phys),
3737 hfsmp->hfs_physical_block_size, NOCRED, &bp) == 0) {
3738 journal_modify_block_start(hfsmp->jnl, bp);
3739
3740 bzero((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size), kMDBSize);
3741
3742 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
3743 } else if (bp) {
3744 buf_brelse(bp);
3745 }
3746 }
3747
3748 /*
3749 * TODO: Adjust the size of the metadata zone based on new volume size?
3750 */
3751
3752 /*
3753 * Adjust the size of hfsmp->hfs_attrdata_vp
3754 */
3755 if (hfsmp->hfs_attrdata_vp) {
3756 struct cnode *attr_cp;
3757 struct filefork *attr_fp;
3758
3759 if (vnode_get(hfsmp->hfs_attrdata_vp) == 0) {
3760 attr_cp = VTOC(hfsmp->hfs_attrdata_vp);
3761 attr_fp = VTOF(hfsmp->hfs_attrdata_vp);
3762
3763 attr_cp->c_blocks = newblkcnt;
3764 attr_fp->ff_blocks = newblkcnt;
3765 attr_fp->ff_extents[0].blockCount = newblkcnt;
3766 attr_fp->ff_size = (off_t) newblkcnt * hfsmp->blockSize;
3767 ubc_setsize(hfsmp->hfs_attrdata_vp, attr_fp->ff_size);
3768 vnode_put(hfsmp->hfs_attrdata_vp);
3769 }
3770 }
3771
3772 out:
3773 if (error && fp) {
3774 /* Restore allocation fork. */
3775 bcopy(&forkdata, &fp->ff_data, sizeof(forkdata));
3776 VTOC(vp)->c_blocks = fp->ff_blocks;
3777
3778 }
3779 /*
3780 Regardless of whether or not the totalblocks actually increased,
3781 we should reset the allocLimit field. If it changed, it will
3782 get updated; if not, it will remain the same.
3783 */
3784 hfsmp->allocLimit = vcb->totalBlocks;
3785 hfs_systemfile_unlock(hfsmp, lockflags);
3786 hfs_end_transaction(hfsmp);
3787
3788 return (error);
3789 }
3790
3791 #define HFS_MIN_SIZE (32LL * 1024LL * 1024LL)
3792
3793 /*
3794 * Truncate a file system (while still mounted).
3795 */
3796 __private_extern__
3797 int
3798 hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, vfs_context_t context)
3799 {
3800 struct buf *bp = NULL;
3801 u_int64_t oldsize;
3802 u_int32_t newblkcnt;
3803 u_int32_t reclaimblks = 0;
3804 int lockflags = 0;
3805 int transaction_begun = 0;
3806 int error;
3807
3808 lck_mtx_lock(&hfsmp->hfs_mutex);
3809 if (hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) {
3810 lck_mtx_unlock(&hfsmp->hfs_mutex);
3811 return (EALREADY);
3812 }
3813 hfsmp->hfs_flags |= HFS_RESIZE_IN_PROGRESS;
3814 hfsmp->hfs_resize_filesmoved = 0;
3815 hfsmp->hfs_resize_totalfiles = 0;
3816 lck_mtx_unlock(&hfsmp->hfs_mutex);
3817
3818 /*
3819 * - Journaled HFS Plus volumes only.
3820 * - No embedded volumes.
3821 */
3822 if ((hfsmp->jnl == NULL) ||
3823 (hfsmp->hfsPlusIOPosOffset != 0)) {
3824 error = EPERM;
3825 goto out;
3826 }
3827 oldsize = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
3828 newblkcnt = newsize / hfsmp->blockSize;
3829 reclaimblks = hfsmp->totalBlocks - newblkcnt;
3830
3831 /* Make sure new size is valid. */
3832 if ((newsize < HFS_MIN_SIZE) ||
3833 (newsize >= oldsize) ||
3834 (newsize % hfsmp->hfs_logical_block_size) ||
3835 (newsize % hfsmp->hfs_physical_block_size)) {
3836 printf ("hfs_truncatefs: invalid size\n");
3837 error = EINVAL;
3838 goto out;
3839 }
3840 /* Make sure there's enough space to work with. */
3841 if (reclaimblks >= hfs_freeblks(hfsmp, 1)) {
3842 printf("hfs_truncatefs: insufficient space (need %u blocks; have %u blocks)\n", reclaimblks, hfs_freeblks(hfsmp, 1));
3843 error = ENOSPC;
3844 goto out;
3845 }
3846
3847 /* Start with a clean journal. */
3848 hfs_journal_flush(hfsmp);
3849
3850 if (hfs_start_transaction(hfsmp) != 0) {
3851 error = EINVAL;
3852 goto out;
3853 }
3854 transaction_begun = 1;
3855
3856 /*
3857 * Prevent new allocations from using the part we're trying to truncate.
3858 *
3859 * NOTE: allocLimit is set to the allocation block number where the new
3860 * alternate volume header will be. That way there will be no files to
3861 * interfere with allocating the new alternate volume header, and no files
3862 * in the allocation blocks beyond (i.e. the blocks we're trying to
3863 * truncate away.
3864 */
3865 lck_mtx_lock(&hfsmp->hfs_mutex);
3866 if (hfsmp->blockSize == 512)
3867 hfsmp->allocLimit = newblkcnt - 2;
3868 else
3869 hfsmp->allocLimit = newblkcnt - 1;
3870 hfsmp->freeBlocks -= reclaimblks;
3871 lck_mtx_unlock(&hfsmp->hfs_mutex);
3872
3873 /*
3874 * Look for files that have blocks at or beyond the location of the
3875 * new alternate volume header.
3876 */
3877 if (hfs_isallocated(hfsmp, hfsmp->allocLimit, reclaimblks)) {
3878 /*
3879 * hfs_reclaimspace will use separate transactions when
3880 * relocating files (so we don't overwhelm the journal).
3881 */
3882 hfs_end_transaction(hfsmp);
3883 transaction_begun = 0;
3884
3885 /* Attempt to reclaim some space. */
3886 if (hfs_reclaimspace(hfsmp, hfsmp->allocLimit, reclaimblks, context) != 0) {
3887 printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
3888 error = ENOSPC;
3889 goto out;
3890 }
3891 if (hfs_start_transaction(hfsmp) != 0) {
3892 error = EINVAL;
3893 goto out;
3894 }
3895 transaction_begun = 1;
3896
3897 /* Check if we're clear now. */
3898 if (hfs_isallocated(hfsmp, hfsmp->allocLimit, reclaimblks)) {
3899 printf("hfs_truncatefs: didn't reclaim enough space on %s\n", hfsmp->vcbVN);
3900 error = EAGAIN; /* tell client to try again */
3901 goto out;
3902 }
3903 }
3904
3905 /*
3906 * Note: we take the attributes lock in case we have an attribute data vnode
3907 * which needs to change size.
3908 */
3909 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
3910
3911 /*
3912 * Mark the old alternate volume header as free.
3913 * We don't bother shrinking allocation bitmap file.
3914 */
3915 if (hfsmp->blockSize == 512)
3916 (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 2, 2);
3917 else
3918 (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 1, 1);
3919
3920 /*
3921 * Allocate last 1KB for alternate volume header.
3922 */
3923 error = BlockMarkAllocated(hfsmp, hfsmp->allocLimit, (hfsmp->blockSize == 512) ? 2 : 1);
3924 if (error) {
3925 printf("hfs_truncatefs: Error %d allocating new alternate volume header\n", error);
3926 goto out;
3927 }
3928
3929 /*
3930 * Invalidate the existing alternate volume header.
3931 *
3932 * Don't include this in a transaction (don't call journal_modify_block)
3933 * since this block will be outside of the truncated file system!
3934 */
3935 if (hfsmp->hfs_alt_id_sector) {
3936 if (buf_meta_bread(hfsmp->hfs_devvp,
3937 HFS_PHYSBLK_ROUNDDOWN(hfsmp->hfs_alt_id_sector, hfsmp->hfs_log_per_phys),
3938 hfsmp->hfs_physical_block_size, NOCRED, &bp) == 0) {
3939
3940 bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_physical_block_size)), kMDBSize);
3941 (void) VNOP_BWRITE(bp);
3942 } else if (bp) {
3943 buf_brelse(bp);
3944 }
3945 bp = NULL;
3946 }
3947
3948 /* Log successful shrinking. */
3949 printf("hfs_truncatefs: shrank \"%s\" to %d blocks (was %d blocks)\n",
3950 hfsmp->vcbVN, newblkcnt, hfsmp->totalBlocks);
3951
3952 /*
3953 * Adjust file system variables and flush them to disk.
3954 */
3955 hfsmp->totalBlocks = newblkcnt;
3956 hfsmp->hfs_logical_block_count = newsize / hfsmp->hfs_logical_block_size;
3957 hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size, hfsmp->hfs_logical_block_count);
3958 MarkVCBDirty(hfsmp);
3959 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
3960 if (error)
3961 panic("hfs_truncatefs: unexpected error flushing volume header (%d)\n", error);
3962
3963 /*
3964 * TODO: Adjust the size of the metadata zone based on new volume size?
3965 */
3966
3967 /*
3968 * Adjust the size of hfsmp->hfs_attrdata_vp
3969 */
3970 if (hfsmp->hfs_attrdata_vp) {
3971 struct cnode *cp;
3972 struct filefork *fp;
3973
3974 if (vnode_get(hfsmp->hfs_attrdata_vp) == 0) {
3975 cp = VTOC(hfsmp->hfs_attrdata_vp);
3976 fp = VTOF(hfsmp->hfs_attrdata_vp);
3977
3978 cp->c_blocks = newblkcnt;
3979 fp->ff_blocks = newblkcnt;
3980 fp->ff_extents[0].blockCount = newblkcnt;
3981 fp->ff_size = (off_t) newblkcnt * hfsmp->blockSize;
3982 ubc_setsize(hfsmp->hfs_attrdata_vp, fp->ff_size);
3983 vnode_put(hfsmp->hfs_attrdata_vp);
3984 }
3985 }
3986
3987 out:
3988 if (error)
3989 hfsmp->freeBlocks += reclaimblks;
3990
3991 lck_mtx_lock(&hfsmp->hfs_mutex);
3992 hfsmp->allocLimit = hfsmp->totalBlocks;
3993 if (hfsmp->nextAllocation >= hfsmp->allocLimit)
3994 hfsmp->nextAllocation = hfsmp->hfs_metazone_end + 1;
3995 hfsmp->hfs_flags &= ~HFS_RESIZE_IN_PROGRESS;
3996 lck_mtx_unlock(&hfsmp->hfs_mutex);
3997
3998 if (lockflags) {
3999 hfs_systemfile_unlock(hfsmp, lockflags);
4000 }
4001 if (transaction_begun) {
4002 hfs_end_transaction(hfsmp);
4003 hfs_journal_flush(hfsmp);
4004 }
4005
4006 return (error);
4007 }
4008
4009
4010 /*
4011 * Invalidate the physical block numbers associated with buffer cache blocks
4012 * in the given extent of the given vnode.
4013 */
4014 struct hfs_inval_blk_no {
4015 daddr64_t sectorStart;
4016 daddr64_t sectorCount;
4017 };
4018 static int
4019 hfs_invalidate_block_numbers_callback(buf_t bp, void *args_in)
4020 {
4021 daddr64_t blkno;
4022 struct hfs_inval_blk_no *args;
4023
4024 blkno = buf_blkno(bp);
4025 args = args_in;
4026
4027 if (blkno >= args->sectorStart && blkno < args->sectorStart+args->sectorCount)
4028 buf_setblkno(bp, buf_lblkno(bp));
4029
4030 return BUF_RETURNED;
4031 }
4032 static void
4033 hfs_invalidate_sectors(struct vnode *vp, daddr64_t sectorStart, daddr64_t sectorCount)
4034 {
4035 struct hfs_inval_blk_no args;
4036 args.sectorStart = sectorStart;
4037 args.sectorCount = sectorCount;
4038
4039 buf_iterate(vp, hfs_invalidate_block_numbers_callback, BUF_SCAN_DIRTY|BUF_SCAN_CLEAN, &args);
4040 }
4041
4042
4043 /*
4044 * Copy the contents of an extent to a new location. Also invalidates the
4045 * physical block number of any buffer cache block in the copied extent
4046 * (so that if the block is written, it will go through VNOP_BLOCKMAP to
4047 * determine the new physical block number).
4048 */
4049 static int
4050 hfs_copy_extent(
4051 struct hfsmount *hfsmp,
4052 struct vnode *vp, /* The file whose extent is being copied. */
4053 u_int32_t oldStart, /* The start of the source extent. */
4054 u_int32_t newStart, /* The start of the destination extent. */
4055 u_int32_t blockCount, /* The number of allocation blocks to copy. */
4056 vfs_context_t context)
4057 {
4058 int err = 0;
4059 size_t bufferSize;
4060 void *buffer = NULL;
4061 struct vfsioattr ioattr;
4062 buf_t bp = NULL;
4063 off_t resid;
4064 size_t ioSize;
4065 u_int32_t ioSizeSectors; /* Device sectors in this I/O */
4066 daddr64_t srcSector, destSector;
4067 u_int32_t sectorsPerBlock = hfsmp->blockSize / hfsmp->hfs_logical_block_size;
4068
4069 /*
4070 * Sanity check that we have locked the vnode of the file we're copying.
4071 *
4072 * But since hfs_systemfile_lock() doesn't actually take the lock on
4073 * the allocation file if a journal is active, ignore the check if the
4074 * file being copied is the allocation file.
4075 */
4076 struct cnode *cp = VTOC(vp);
4077 if (cp != hfsmp->hfs_allocation_cp && cp->c_lockowner != current_thread())
4078 panic("hfs_copy_extent: vp=%p (cp=%p) not owned?\n", vp, cp);
4079
4080 /*
4081 * Wait for any in-progress writes to this vnode to complete, so that we'll
4082 * be copying consistent bits. (Otherwise, it's possible that an async
4083 * write will complete to the old extent after we read from it. That
4084 * could lead to corruption.)
4085 */
4086 err = vnode_waitforwrites(vp, 0, 0, 0, "hfs_copy_extent");
4087 if (err) {
4088 printf("hfs_copy_extent: Error %d from vnode_waitforwrites\n", err);
4089 return err;
4090 }
4091
4092 /*
4093 * Determine the I/O size to use
4094 *
4095 * NOTE: Many external drives will result in an ioSize of 128KB.
4096 * TODO: Should we use a larger buffer, doing several consecutive
4097 * reads, then several consecutive writes?
4098 */
4099 vfs_ioattr(hfsmp->hfs_mp, &ioattr);
4100 bufferSize = MIN(ioattr.io_maxreadcnt, ioattr.io_maxwritecnt);
4101 if (kmem_alloc(kernel_map, (vm_offset_t*) &buffer, bufferSize))
4102 return ENOMEM;
4103
4104 /* Get a buffer for doing the I/O */
4105 bp = buf_alloc(hfsmp->hfs_devvp);
4106 buf_setdataptr(bp, (uintptr_t)buffer);
4107
4108 resid = (off_t) blockCount * (off_t) hfsmp->blockSize;
4109 srcSector = (daddr64_t) oldStart * hfsmp->blockSize / hfsmp->hfs_logical_block_size;
4110 destSector = (daddr64_t) newStart * hfsmp->blockSize / hfsmp->hfs_logical_block_size;
4111 while (resid > 0) {
4112 ioSize = MIN(bufferSize, (size_t) resid);
4113 ioSizeSectors = ioSize / hfsmp->hfs_logical_block_size;
4114
4115 /* Prepare the buffer for reading */
4116 buf_reset(bp, B_READ);
4117 buf_setsize(bp, ioSize);
4118 buf_setcount(bp, ioSize);
4119 buf_setblkno(bp, srcSector);
4120 buf_setlblkno(bp, srcSector);
4121
4122 /* Do the read */
4123 err = VNOP_STRATEGY(bp);
4124 if (!err)
4125 err = buf_biowait(bp);
4126 if (err) {
4127 printf("hfs_copy_extent: Error %d from VNOP_STRATEGY (read)\n", err);
4128 break;
4129 }
4130
4131 /* Prepare the buffer for writing */
4132 buf_reset(bp, B_WRITE);
4133 buf_setsize(bp, ioSize);
4134 buf_setcount(bp, ioSize);
4135 buf_setblkno(bp, destSector);
4136 buf_setlblkno(bp, destSector);
4137 if (journal_uses_fua(hfsmp->jnl))
4138 buf_markfua(bp);
4139
4140 /* Do the write */
4141 vnode_startwrite(hfsmp->hfs_devvp);
4142 err = VNOP_STRATEGY(bp);
4143 if (!err)
4144 err = buf_biowait(bp);
4145 if (err) {
4146 printf("hfs_copy_extent: Error %d from VNOP_STRATEGY (write)\n", err);
4147 break;
4148 }
4149
4150 resid -= ioSize;
4151 srcSector += ioSizeSectors;
4152 destSector += ioSizeSectors;
4153 }
4154 if (bp)
4155 buf_free(bp);
4156 if (buffer)
4157 kmem_free(kernel_map, (vm_offset_t)buffer, bufferSize);
4158
4159 /* Make sure all writes have been flushed to disk. */
4160 if (!journal_uses_fua(hfsmp->jnl)) {
4161 err = VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, context);
4162 if (err) {
4163 printf("hfs_copy_extent: DKIOCSYNCHRONIZECACHE failed (%d)\n", err);
4164 err = 0; /* Don't fail the copy. */
4165 }
4166 }
4167
4168 if (!err)
4169 hfs_invalidate_sectors(vp, (daddr64_t)oldStart*sectorsPerBlock, (daddr64_t)blockCount*sectorsPerBlock);
4170
4171 return err;
4172 }
4173
4174
4175 /*
4176 * Reclaim space at the end of a volume, used by a given system file.
4177 *
4178 * This routine attempts to move any extent which contains allocation blocks
4179 * at or after "startblk." A separate transaction is used to do the move.
4180 * The contents of any moved extents are read and written via the volume's
4181 * device vnode -- NOT via "vp." During the move, moved blocks which are part
4182 * of a transaction have their physical block numbers invalidated so they will
4183 * eventually be written to their new locations.
4184 *
4185 * This routine can be used to move overflow extents for the allocation file.
4186 *
4187 * Inputs:
4188 * hfsmp The volume being resized.
4189 * startblk Blocks >= this allocation block need to be moved.
4190 * locks Which locks need to be taken for the given system file.
4191 * vp The vnode for the system file.
4192 *
4193 * Outputs:
4194 * moved Set to true if any extents were moved.
4195 */
4196 static int
4197 hfs_relocate_callback(__unused HFSPlusExtentKey *key, HFSPlusExtentRecord *record, HFSPlusExtentRecord *state)
4198 {
4199 bcopy(state, record, sizeof(HFSPlusExtentRecord));
4200 return 0;
4201 }
4202 static int
4203 hfs_reclaim_sys_file(struct hfsmount *hfsmp, struct vnode *vp, u_long startblk, int locks, Boolean *moved, vfs_context_t context)
4204 {
4205 int error;
4206 int lockflags;
4207 int i;
4208 u_long datablks;
4209 u_long block;
4210 u_int32_t oldStartBlock;
4211 u_int32_t newStartBlock;
4212 u_int32_t blockCount;
4213 struct filefork *fp;
4214
4215 /* If there is no vnode for this file, then there's nothing to do. */
4216 if (vp == NULL)
4217 return 0;
4218
4219 /* printf("hfs_reclaim_sys_file: %.*s\n", VTOC(vp)->c_desc.cd_namelen, VTOC(vp)->c_desc.cd_nameptr); */
4220
4221 /* We always need the allocation bitmap and extents B-tree */
4222 locks |= SFL_BITMAP | SFL_EXTENTS;
4223
4224 error = hfs_start_transaction(hfsmp);
4225 if (error) {
4226 printf("hfs_reclaim_sys_file: hfs_start_transaction returned %d\n", error);
4227 return error;
4228 }
4229 lockflags = hfs_systemfile_lock(hfsmp, locks, HFS_EXCLUSIVE_LOCK);
4230 fp = VTOF(vp);
4231 datablks = 0;
4232
4233 /* Relocate non-overflow extents */
4234 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
4235 if (fp->ff_extents[i].blockCount == 0)
4236 break;
4237 oldStartBlock = fp->ff_extents[i].startBlock;
4238 blockCount = fp->ff_extents[i].blockCount;
4239 datablks += blockCount;
4240 block = oldStartBlock + blockCount;
4241 if (block > startblk) {
4242 error = BlockAllocate(hfsmp, 1, blockCount, blockCount, true, true, &newStartBlock, &blockCount);
4243 if (error) {
4244 printf("hfs_reclaim_sys_file: BlockAllocate returned %d\n", error);
4245 goto fail;
4246 }
4247 if (blockCount != fp->ff_extents[i].blockCount) {
4248 printf("hfs_reclaim_sys_file: new blockCount=%u, original blockCount=%u", blockCount, fp->ff_extents[i].blockCount);
4249 goto free_fail;
4250 }
4251 error = hfs_copy_extent(hfsmp, vp, oldStartBlock, newStartBlock, blockCount, context);
4252 if (error) {
4253 printf("hfs_reclaim_sys_file: hfs_copy_extent returned %d\n", error);
4254 goto free_fail;
4255 }
4256 fp->ff_extents[i].startBlock = newStartBlock;
4257 VTOC(vp)->c_flag |= C_MODIFIED;
4258 *moved = true;
4259 error = BlockDeallocate(hfsmp, oldStartBlock, blockCount);
4260 if (error) {
4261 /* TODO: Mark volume inconsistent? */
4262 printf("hfs_reclaim_sys_file: BlockDeallocate returned %d\n", error);
4263 goto fail;
4264 }
4265 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
4266 if (error) {
4267 /* TODO: Mark volume inconsistent? */
4268 printf("hfs_reclaim_sys_file: hfs_flushvolumeheader returned %d\n", error);
4269 goto fail;
4270 }
4271 }
4272 }
4273
4274 /* Relocate overflow extents (if any) */
4275 if (i == kHFSPlusExtentDensity && fp->ff_blocks > datablks) {
4276 struct BTreeIterator *iterator = NULL;
4277 struct FSBufferDescriptor btdata;
4278 HFSPlusExtentRecord record;
4279 HFSPlusExtentKey *key;
4280 FCB *fcb;
4281 u_int32_t fileID;
4282 u_int8_t forktype;
4283
4284 forktype = VNODE_IS_RSRC(vp) ? 0xFF : 0;
4285 fileID = VTOC(vp)->c_cnid;
4286 if (kmem_alloc(kernel_map, (vm_offset_t*) &iterator, sizeof(*iterator))) {
4287 printf("hfs_reclaim_sys_file: kmem_alloc failed!\n");
4288 error = ENOMEM;
4289 goto fail;
4290 }
4291
4292 bzero(iterator, sizeof(*iterator));
4293 key = (HFSPlusExtentKey *) &iterator->key;
4294 key->keyLength = kHFSPlusExtentKeyMaximumLength;
4295 key->forkType = forktype;
4296 key->fileID = fileID;
4297 key->startBlock = datablks;
4298
4299 btdata.bufferAddress = &record;
4300 btdata.itemSize = sizeof(record);
4301 btdata.itemCount = 1;
4302
4303 fcb = VTOF(hfsmp->hfs_extents_vp);
4304
4305 error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
4306 while (error == 0) {
4307 /* Stop when we encounter a different file or fork. */
4308 if ((key->fileID != fileID) ||
4309 (key->forkType != forktype)) {
4310 break;
4311 }
4312 /*
4313 * Check if the file overlaps target space.
4314 */
4315 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
4316 if (record[i].blockCount == 0) {
4317 goto overflow_done;
4318 }
4319 oldStartBlock = record[i].startBlock;
4320 blockCount = record[i].blockCount;
4321 block = oldStartBlock + blockCount;
4322 if (block > startblk) {
4323 error = BlockAllocate(hfsmp, 1, blockCount, blockCount, true, true, &newStartBlock, &blockCount);
4324 if (error) {
4325 printf("hfs_reclaim_sys_file: BlockAllocate returned %d\n", error);
4326 goto overflow_done;
4327 }
4328 if (blockCount != record[i].blockCount) {
4329 printf("hfs_reclaim_sys_file: new blockCount=%u, original blockCount=%u", blockCount, fp->ff_extents[i].blockCount);
4330 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
4331 goto free_fail;
4332 }
4333 error = hfs_copy_extent(hfsmp, vp, oldStartBlock, newStartBlock, blockCount, context);
4334 if (error) {
4335 printf("hfs_reclaim_sys_file: hfs_copy_extent returned %d\n", error);
4336 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
4337 goto free_fail;
4338 }
4339 record[i].startBlock = newStartBlock;
4340 VTOC(vp)->c_flag |= C_MODIFIED;
4341 *moved = true;
4342 /*
4343 * NOTE: To support relocating overflow extents of the
4344 * allocation file, we must update the BTree record BEFORE
4345 * deallocating the old extent so that BlockDeallocate will
4346 * use the extent's new location to calculate physical block
4347 * numbers. (This is for the case where the old extent's
4348 * bitmap bits actually reside in the extent being moved.)
4349 */
4350 error = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr) hfs_relocate_callback, &record);
4351 if (error) {
4352 /* TODO: Mark volume inconsistent? */
4353 printf("hfs_reclaim_sys_file: BTUpdateRecord returned %d\n", error);
4354 goto overflow_done;
4355 }
4356 error = BlockDeallocate(hfsmp, oldStartBlock, blockCount);
4357 if (error) {
4358 /* TODO: Mark volume inconsistent? */
4359 printf("hfs_reclaim_sys_file: BlockDeallocate returned %d\n", error);
4360 goto overflow_done;
4361 }
4362 }
4363 }
4364 /* Look for more records. */
4365 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
4366 if (error == btNotFound) {
4367 error = 0;
4368 break;
4369 }
4370 }
4371 overflow_done:
4372 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
4373 if (error) {
4374 goto fail;
4375 }
4376 }
4377
4378 hfs_systemfile_unlock(hfsmp, lockflags);
4379 error = hfs_end_transaction(hfsmp);
4380 if (error) {
4381 printf("hfs_reclaim_sys_file: hfs_end_transaction returned %d\n", error);
4382 }
4383
4384 return error;
4385
4386 free_fail:
4387 (void) BlockDeallocate(hfsmp, newStartBlock, blockCount);
4388 fail:
4389 (void) hfs_systemfile_unlock(hfsmp, lockflags);
4390 (void) hfs_end_transaction(hfsmp);
4391 return error;
4392 }
4393
4394
4395 /*
4396 * This journal_relocate callback updates the journal info block to point
4397 * at the new journal location. This write must NOT be done using the
4398 * transaction. We must write the block immediately. We must also force
4399 * it to get to the media so that the new journal location will be seen by
4400 * the replay code before we can safely let journaled blocks be written
4401 * to their normal locations.
4402 *
4403 * The tests for journal_uses_fua below are mildly hacky. Since the journal
4404 * and the file system are both on the same device, I'm leveraging what
4405 * the journal has decided about FUA.
4406 */
4407 struct hfs_journal_relocate_args {
4408 struct hfsmount *hfsmp;
4409 vfs_context_t context;
4410 u_int32_t newStartBlock;
4411 };
4412
4413 static errno_t
4414 hfs_journal_relocate_callback(void *_args)
4415 {
4416 int error;
4417 struct hfs_journal_relocate_args *args = _args;
4418 struct hfsmount *hfsmp = args->hfsmp;
4419 buf_t bp;
4420 JournalInfoBlock *jibp;
4421
4422 error = buf_meta_bread(hfsmp->hfs_devvp,
4423 hfsmp->vcbJinfoBlock * (hfsmp->blockSize/hfsmp->hfs_logical_block_size),
4424 hfsmp->blockSize, vfs_context_ucred(args->context), &bp);
4425 if (error) {
4426 printf("hfs_reclaim_journal_file: failed to read JIB (%d)\n", error);
4427 return error;
4428 }
4429 jibp = (JournalInfoBlock*) buf_dataptr(bp);
4430 jibp->offset = SWAP_BE64((u_int64_t)args->newStartBlock * hfsmp->blockSize);
4431 jibp->size = SWAP_BE64(hfsmp->jnl_size);
4432 if (journal_uses_fua(hfsmp->jnl))
4433 buf_markfua(bp);
4434 error = buf_bwrite(bp);
4435 if (error) {
4436 printf("hfs_reclaim_journal_file: failed to write JIB (%d)\n", error);
4437 return error;
4438 }
4439 if (!journal_uses_fua(hfsmp->jnl)) {
4440 error = VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, args->context);
4441 if (error) {
4442 printf("hfs_reclaim_journal_file: DKIOCSYNCHRONIZECACHE failed (%d)\n", error);
4443 error = 0; /* Don't fail the operation. */
4444 }
4445 }
4446
4447 return error;
4448 }
4449
4450
4451 static int
4452 hfs_reclaim_journal_file(struct hfsmount *hfsmp, vfs_context_t context)
4453 {
4454 int error;
4455 int lockflags;
4456 u_int32_t newStartBlock;
4457 u_int32_t oldBlockCount;
4458 u_int32_t newBlockCount;
4459 struct cat_desc journal_desc;
4460 struct cat_attr journal_attr;
4461 struct cat_fork journal_fork;
4462 struct hfs_journal_relocate_args callback_args;
4463
4464 error = hfs_start_transaction(hfsmp);
4465 if (error) {
4466 printf("hfs_reclaim_journal_file: hfs_start_transaction returned %d\n", error);
4467 return error;
4468 }
4469 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
4470
4471 oldBlockCount = hfsmp->jnl_size / hfsmp->blockSize;
4472
4473 /* TODO: Allow the journal to change size based on the new volume size. */
4474 error = BlockAllocate(hfsmp, 1, oldBlockCount, oldBlockCount, true, true, &newStartBlock, &newBlockCount);
4475 if (error) {
4476 printf("hfs_reclaim_journal_file: BlockAllocate returned %d\n", error);
4477 goto fail;
4478 }
4479 if (newBlockCount != oldBlockCount) {
4480 printf("hfs_reclaim_journal_file: newBlockCount != oldBlockCount (%u, %u)\n", newBlockCount, oldBlockCount);
4481 goto free_fail;
4482 }
4483
4484 error = BlockDeallocate(hfsmp, hfsmp->jnl_start, oldBlockCount);
4485 if (error) {
4486 printf("hfs_reclaim_journal_file: BlockDeallocate returned %d\n", error);
4487 goto free_fail;
4488 }
4489
4490 /* Update the catalog record for .journal */
4491 error = cat_idlookup(hfsmp, hfsmp->hfs_jnlfileid, 1, &journal_desc, &journal_attr, &journal_fork);
4492 if (error) {
4493 printf("hfs_reclaim_journal_file: cat_idlookup returned %d\n", error);
4494 goto free_fail;
4495 }
4496 journal_fork.cf_size = newBlockCount * hfsmp->blockSize;
4497 journal_fork.cf_extents[0].startBlock = newStartBlock;
4498 journal_fork.cf_extents[0].blockCount = newBlockCount;
4499 journal_fork.cf_blocks = newBlockCount;
4500 error = cat_update(hfsmp, &journal_desc, &journal_attr, &journal_fork, NULL);
4501 cat_releasedesc(&journal_desc); /* all done with cat descriptor */
4502 if (error) {
4503 printf("hfs_reclaim_journal_file: cat_update returned %d\n", error);
4504 goto free_fail;
4505 }
4506 callback_args.hfsmp = hfsmp;
4507 callback_args.context = context;
4508 callback_args.newStartBlock = newStartBlock;
4509
4510 error = journal_relocate(hfsmp->jnl, (off_t)newStartBlock*hfsmp->blockSize,
4511 (off_t)newBlockCount*hfsmp->blockSize, 0,
4512 hfs_journal_relocate_callback, &callback_args);
4513 if (error) {
4514 /* NOTE: journal_relocate will mark the journal invalid. */
4515 printf("hfs_reclaim_journal_file: journal_relocate returned %d\n", error);
4516 goto fail;
4517 }
4518 hfsmp->jnl_start = newStartBlock;
4519 hfsmp->jnl_size = (off_t)newBlockCount * hfsmp->blockSize;
4520
4521 hfs_systemfile_unlock(hfsmp, lockflags);
4522 error = hfs_end_transaction(hfsmp);
4523 if (error) {
4524 printf("hfs_reclaim_journal_file: hfs_end_transaction returned %d\n", error);
4525 }
4526
4527 return error;
4528
4529 free_fail:
4530 (void) BlockDeallocate(hfsmp, newStartBlock, newBlockCount);
4531 fail:
4532 hfs_systemfile_unlock(hfsmp, lockflags);
4533 (void) hfs_end_transaction(hfsmp);
4534 return error;
4535 }
4536
4537
4538 /*
4539 * Move the journal info block to a new location. We have to make sure the
4540 * new copy of the journal info block gets to the media first, then change
4541 * the field in the volume header and the catalog record.
4542 */
4543 static int
4544 hfs_reclaim_journal_info_block(struct hfsmount *hfsmp, vfs_context_t context)
4545 {
4546 int error;
4547 int lockflags;
4548 u_int32_t newBlock;
4549 u_int32_t blockCount;
4550 struct cat_desc jib_desc;
4551 struct cat_attr jib_attr;
4552 struct cat_fork jib_fork;
4553 buf_t old_bp, new_bp;
4554
4555 error = hfs_start_transaction(hfsmp);
4556 if (error) {
4557 printf("hfs_reclaim_journal_info_block: hfs_start_transaction returned %d\n", error);
4558 return error;
4559 }
4560 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
4561
4562 error = BlockAllocate(hfsmp, 1, 1, 1, true, true, &newBlock, &blockCount);
4563 if (error) {
4564 printf("hfs_reclaim_journal_info_block: BlockAllocate returned %d\n", error);
4565 goto fail;
4566 }
4567 if (blockCount != 1) {
4568 printf("hfs_reclaim_journal_info_block: blockCount != 1 (%u)\n", blockCount);
4569 goto free_fail;
4570 }
4571 error = BlockDeallocate(hfsmp, hfsmp->vcbJinfoBlock, 1);
4572 if (error) {
4573 printf("hfs_reclaim_journal_info_block: BlockDeallocate returned %d\n", error);
4574 goto free_fail;
4575 }
4576
4577 /* Copy the old journal info block content to the new location */
4578 error = buf_meta_bread(hfsmp->hfs_devvp,
4579 hfsmp->vcbJinfoBlock * (hfsmp->blockSize/hfsmp->hfs_logical_block_size),
4580 hfsmp->blockSize, vfs_context_ucred(context), &old_bp);
4581 if (error) {
4582 printf("hfs_reclaim_journal_info_block: failed to read JIB (%d)\n", error);
4583 goto free_fail;
4584 }
4585 new_bp = buf_getblk(hfsmp->hfs_devvp,
4586 newBlock * (hfsmp->blockSize/hfsmp->hfs_logical_block_size),
4587 hfsmp->blockSize, 0, 0, BLK_META);
4588 bcopy((char*)buf_dataptr(old_bp), (char*)buf_dataptr(new_bp), hfsmp->blockSize);
4589 buf_brelse(old_bp);
4590 if (journal_uses_fua(hfsmp->jnl))
4591 buf_markfua(new_bp);
4592 error = buf_bwrite(new_bp);
4593 if (error) {
4594 printf("hfs_reclaim_journal_info_block: failed to write new JIB (%d)\n", error);
4595 goto free_fail;
4596 }
4597 if (!journal_uses_fua(hfsmp->jnl)) {
4598 error = VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, context);
4599 if (error) {
4600 printf("hfs_reclaim_journal_info_block: DKIOCSYNCHRONIZECACHE failed (%d)\n", error);
4601 /* Don't fail the operation. */
4602 }
4603 }
4604
4605 /* Update the catalog record for .journal_info_block */
4606 error = cat_idlookup(hfsmp, hfsmp->hfs_jnlinfoblkid, 1, &jib_desc, &jib_attr, &jib_fork);
4607 if (error) {
4608 printf("hfs_reclaim_journal_file: cat_idlookup returned %d\n", error);
4609 goto fail;
4610 }
4611 jib_fork.cf_size = hfsmp->blockSize;
4612 jib_fork.cf_extents[0].startBlock = newBlock;
4613 jib_fork.cf_extents[0].blockCount = 1;
4614 jib_fork.cf_blocks = 1;
4615 error = cat_update(hfsmp, &jib_desc, &jib_attr, &jib_fork, NULL);
4616 cat_releasedesc(&jib_desc); /* all done with cat descriptor */
4617 if (error) {
4618 printf("hfs_reclaim_journal_info_block: cat_update returned %d\n", error);
4619 goto fail;
4620 }
4621
4622 /* Update the pointer to the journal info block in the volume header. */
4623 hfsmp->vcbJinfoBlock = newBlock;
4624 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
4625 if (error) {
4626 printf("hfs_reclaim_journal_info_block: hfs_flushvolumeheader returned %d\n", error);
4627 goto fail;
4628 }
4629 hfs_systemfile_unlock(hfsmp, lockflags);
4630 error = hfs_end_transaction(hfsmp);
4631 if (error) {
4632 printf("hfs_reclaim_journal_info_block: hfs_end_transaction returned %d\n", error);
4633 }
4634 error = hfs_journal_flush(hfsmp);
4635 if (error) {
4636 printf("hfs_reclaim_journal_info_block: journal_flush returned %d\n", error);
4637 }
4638 return error;
4639
4640 free_fail:
4641 (void) BlockDeallocate(hfsmp, newBlock, blockCount);
4642 fail:
4643 hfs_systemfile_unlock(hfsmp, lockflags);
4644 (void) hfs_end_transaction(hfsmp);
4645 return error;
4646 }
4647
4648
4649 /*
4650 * Reclaim space at the end of a file system.
4651 */
4652 static int
4653 hfs_reclaimspace(struct hfsmount *hfsmp, u_int32_t startblk, u_int32_t reclaimblks, vfs_context_t context)
4654 {
4655 struct vnode *vp = NULL;
4656 FCB *fcb;
4657 struct BTreeIterator * iterator = NULL;
4658 struct FSBufferDescriptor btdata;
4659 struct HFSPlusCatalogFile filerec;
4660 u_int32_t saved_next_allocation;
4661 cnid_t * cnidbufp;
4662 size_t cnidbufsize;
4663 int filecnt = 0;
4664 int maxfilecnt;
4665 u_int32_t block;
4666 u_int32_t datablks;
4667 u_int32_t rsrcblks;
4668 u_int32_t blkstomove = 0;
4669 int lockflags;
4670 int i;
4671 int error;
4672 int lastprogress = 0;
4673 Boolean system_file_moved = false;
4674
4675 /* Relocate extents of the Allocation file if they're in the way. */
4676 error = hfs_reclaim_sys_file(hfsmp, hfsmp->hfs_allocation_vp, startblk, SFL_BITMAP, &system_file_moved, context);
4677 if (error) {
4678 printf("hfs_reclaimspace: reclaim allocation file returned %d\n", error);
4679 return error;
4680 }
4681 /* Relocate extents of the Extents B-tree if they're in the way. */
4682 error = hfs_reclaim_sys_file(hfsmp, hfsmp->hfs_extents_vp, startblk, SFL_EXTENTS, &system_file_moved, context);
4683 if (error) {
4684 printf("hfs_reclaimspace: reclaim extents b-tree returned %d\n", error);
4685 return error;
4686 }
4687 /* Relocate extents of the Catalog B-tree if they're in the way. */
4688 error = hfs_reclaim_sys_file(hfsmp, hfsmp->hfs_catalog_vp, startblk, SFL_CATALOG, &system_file_moved, context);
4689 if (error) {
4690 printf("hfs_reclaimspace: reclaim catalog b-tree returned %d\n", error);
4691 return error;
4692 }
4693 /* Relocate extents of the Attributes B-tree if they're in the way. */
4694 error = hfs_reclaim_sys_file(hfsmp, hfsmp->hfs_attribute_vp, startblk, SFL_ATTRIBUTE, &system_file_moved, context);
4695 if (error) {
4696 printf("hfs_reclaimspace: reclaim attribute b-tree returned %d\n", error);
4697 return error;
4698 }
4699 /* Relocate extents of the Startup File if there is one and they're in the way. */
4700 error = hfs_reclaim_sys_file(hfsmp, hfsmp->hfs_startup_vp, startblk, SFL_STARTUP, &system_file_moved, context);
4701 if (error) {
4702 printf("hfs_reclaimspace: reclaim startup file returned %d\n", error);
4703 return error;
4704 }
4705
4706 /*
4707 * We need to make sure the alternate volume header gets flushed if we moved
4708 * any extents in the volume header. But we need to do that before
4709 * shrinking the size of the volume, or else the journal code will panic
4710 * with an invalid (too large) block number.
4711 *
4712 * Note that system_file_moved will be set if ANY extent was moved, even
4713 * if it was just an overflow extent. In this case, the journal_flush isn't
4714 * strictly required, but shouldn't hurt.
4715 */
4716 if (system_file_moved)
4717 hfs_journal_flush(hfsmp);
4718
4719 if (hfsmp->jnl_start + (hfsmp->jnl_size / hfsmp->blockSize) > startblk) {
4720 error = hfs_reclaim_journal_file(hfsmp, context);
4721 if (error) {
4722 printf("hfs_reclaimspace: hfs_reclaim_journal_file failed (%d)\n", error);
4723 return error;
4724 }
4725 }
4726
4727 if (hfsmp->vcbJinfoBlock >= startblk) {
4728 error = hfs_reclaim_journal_info_block(hfsmp, context);
4729 if (error) {
4730 printf("hfs_reclaimspace: hfs_reclaim_journal_info_block failed (%d)\n", error);
4731 return error;
4732 }
4733 }
4734
4735 /* For now move a maximum of 250,000 files. */
4736 maxfilecnt = MIN(hfsmp->hfs_filecount, 250000);
4737 maxfilecnt = MIN((u_int32_t)maxfilecnt, reclaimblks);
4738 cnidbufsize = maxfilecnt * sizeof(cnid_t);
4739 if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
4740 return (ENOMEM);
4741 }
4742 if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
4743 kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
4744 return (ENOMEM);
4745 }
4746
4747 saved_next_allocation = hfsmp->nextAllocation;
4748 HFS_UPDATE_NEXT_ALLOCATION(hfsmp, hfsmp->hfs_metazone_start);
4749
4750 fcb = VTOF(hfsmp->hfs_catalog_vp);
4751 bzero(iterator, sizeof(*iterator));
4752
4753 btdata.bufferAddress = &filerec;
4754 btdata.itemSize = sizeof(filerec);
4755 btdata.itemCount = 1;
4756
4757 /* Keep the Catalog and extents files locked during iteration. */
4758 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_SHARED_LOCK);
4759
4760 error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
4761 if (error) {
4762 goto end_iteration;
4763 }
4764 /*
4765 * Iterate over all the catalog records looking for files
4766 * that overlap into the space we're trying to free up.
4767 */
4768 for (filecnt = 0; filecnt < maxfilecnt; ) {
4769 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
4770 if (error) {
4771 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
4772 error = 0;
4773 }
4774 break;
4775 }
4776 if (filerec.recordType != kHFSPlusFileRecord) {
4777 continue;
4778 }
4779 datablks = rsrcblks = 0;
4780 /*
4781 * Check if either fork overlaps target space.
4782 */
4783 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
4784 if (filerec.dataFork.extents[i].blockCount != 0) {
4785 datablks += filerec.dataFork.extents[i].blockCount;
4786 block = filerec.dataFork.extents[i].startBlock +
4787 filerec.dataFork.extents[i].blockCount;
4788 if (block >= startblk) {
4789 if ((filerec.fileID == hfsmp->hfs_jnlfileid) ||
4790 (filerec.fileID == hfsmp->hfs_jnlinfoblkid)) {
4791 printf("hfs_reclaimspace: cannot move active journal\n");
4792 error = EPERM;
4793 goto end_iteration;
4794 }
4795 cnidbufp[filecnt++] = filerec.fileID;
4796 blkstomove += filerec.dataFork.totalBlocks;
4797 break;
4798 }
4799 }
4800 if (filerec.resourceFork.extents[i].blockCount != 0) {
4801 rsrcblks += filerec.resourceFork.extents[i].blockCount;
4802 block = filerec.resourceFork.extents[i].startBlock +
4803 filerec.resourceFork.extents[i].blockCount;
4804 if (block >= startblk) {
4805 cnidbufp[filecnt++] = filerec.fileID;
4806 blkstomove += filerec.resourceFork.totalBlocks;
4807 break;
4808 }
4809 }
4810 }
4811 /*
4812 * Check for any overflow extents that overlap.
4813 */
4814 if (i == kHFSPlusExtentDensity) {
4815 if (filerec.dataFork.totalBlocks > datablks) {
4816 if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, filerec.fileID, 0)) {
4817 cnidbufp[filecnt++] = filerec.fileID;
4818 blkstomove += filerec.dataFork.totalBlocks;
4819 }
4820 } else if (filerec.resourceFork.totalBlocks > rsrcblks) {
4821 if (hfs_overlapped_overflow_extents(hfsmp, startblk, rsrcblks, filerec.fileID, 1)) {
4822 cnidbufp[filecnt++] = filerec.fileID;
4823 blkstomove += filerec.resourceFork.totalBlocks;
4824 }
4825 }
4826 }
4827 }
4828
4829 end_iteration:
4830 if (filecnt == 0 && !system_file_moved) {
4831 printf("hfs_reclaimspace: no files moved\n");
4832 error = ENOSPC;
4833 }
4834 /* All done with catalog. */
4835 hfs_systemfile_unlock(hfsmp, lockflags);
4836 if (error || filecnt == 0)
4837 goto out;
4838
4839 /*
4840 * Double check space requirements to make sure
4841 * there is enough space to relocate any files
4842 * that reside in the reclaim area.
4843 *
4844 * Blocks To Move --------------
4845 * | | |
4846 * V V V
4847 * ------------------------------------------------------------------------
4848 * | | / /// // |
4849 * | | / /// // |
4850 * | | / /// // |
4851 * ------------------------------------------------------------------------
4852 *
4853 * <------------------- New Total Blocks ------------------><-- Reclaim -->
4854 *
4855 * <------------------------ Original Total Blocks ----------------------->
4856 *
4857 */
4858 if (blkstomove >= hfs_freeblks(hfsmp, 1)) {
4859 printf("hfs_truncatefs: insufficient space (need %u blocks; have %u blocks)\n", blkstomove, hfs_freeblks(hfsmp, 1));
4860 error = ENOSPC;
4861 goto out;
4862 }
4863 hfsmp->hfs_resize_filesmoved = 0;
4864 hfsmp->hfs_resize_totalfiles = filecnt;
4865
4866 /* Now move any files that are in the way. */
4867 for (i = 0; i < filecnt; ++i) {
4868 struct vnode * rvp;
4869 struct cnode * cp;
4870
4871 if (hfs_vget(hfsmp, cnidbufp[i], &vp, 0) != 0)
4872 continue;
4873
4874 /* Relocating directory hard links is not supported, so we
4875 * punt (see radar 6217026). */
4876 cp = VTOC(vp);
4877 if ((cp->c_flag & C_HARDLINK) && vnode_isdir(vp)) {
4878 printf("hfs_reclaimspace: unable to relocate directory hard link %d\n", cp->c_cnid);
4879 error = EINVAL;
4880 goto out;
4881 }
4882
4883 /* Relocate any data fork blocks. */
4884 if (VTOF(vp) && VTOF(vp)->ff_blocks > 0) {
4885 error = hfs_relocate(vp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
4886 }
4887 if (error)
4888 break;
4889
4890 /* Relocate any resource fork blocks. */
4891 if ((cp->c_blocks - (VTOF(vp) ? VTOF((vp))->ff_blocks : 0)) > 0) {
4892 error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
4893 if (error)
4894 break;
4895 error = hfs_relocate(rvp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
4896 VTOC(rvp)->c_flag |= C_NEED_RVNODE_PUT;
4897 if (error)
4898 break;
4899 }
4900 hfs_unlock(cp);
4901 vnode_put(vp);
4902 vp = NULL;
4903
4904 ++hfsmp->hfs_resize_filesmoved;
4905
4906 /* Report intermediate progress. */
4907 if (filecnt > 100) {
4908 int progress;
4909
4910 progress = (i * 100) / filecnt;
4911 if (progress > (lastprogress + 9)) {
4912 printf("hfs_reclaimspace: %d%% done...\n", progress);
4913 lastprogress = progress;
4914 }
4915 }
4916 }
4917 if (vp) {
4918 hfs_unlock(VTOC(vp));
4919 vnode_put(vp);
4920 vp = NULL;
4921 }
4922 if (hfsmp->hfs_resize_filesmoved != 0) {
4923 printf("hfs_reclaimspace: relocated %d files on \"%s\"\n",
4924 (int)hfsmp->hfs_resize_filesmoved, hfsmp->vcbVN);
4925 }
4926 out:
4927 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
4928 kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
4929
4930 /*
4931 * Restore the roving allocation pointer on errors.
4932 * (but only if we didn't move any files)
4933 */
4934 if (error && hfsmp->hfs_resize_filesmoved == 0) {
4935 HFS_UPDATE_NEXT_ALLOCATION(hfsmp, saved_next_allocation);
4936 }
4937 return (error);
4938 }
4939
4940
4941 /*
4942 * Check if there are any overflow extents that overlap.
4943 */
4944 static int
4945 hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk, u_int32_t catblks, u_int32_t fileID, int rsrcfork)
4946 {
4947 struct BTreeIterator * iterator = NULL;
4948 struct FSBufferDescriptor btdata;
4949 HFSPlusExtentRecord extrec;
4950 HFSPlusExtentKey *extkeyptr;
4951 FCB *fcb;
4952 u_int32_t block;
4953 u_int8_t forktype;
4954 int overlapped = 0;
4955 int i;
4956 int error;
4957
4958 forktype = rsrcfork ? 0xFF : 0;
4959 if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
4960 return (0);
4961 }
4962 bzero(iterator, sizeof(*iterator));
4963 extkeyptr = (HFSPlusExtentKey *)&iterator->key;
4964 extkeyptr->keyLength = kHFSPlusExtentKeyMaximumLength;
4965 extkeyptr->forkType = forktype;
4966 extkeyptr->fileID = fileID;
4967 extkeyptr->startBlock = catblks;
4968
4969 btdata.bufferAddress = &extrec;
4970 btdata.itemSize = sizeof(extrec);
4971 btdata.itemCount = 1;
4972
4973 fcb = VTOF(hfsmp->hfs_extents_vp);
4974
4975 error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
4976 while (error == 0) {
4977 /* Stop when we encounter a different file. */
4978 if ((extkeyptr->fileID != fileID) ||
4979 (extkeyptr->forkType != forktype)) {
4980 break;
4981 }
4982 /*
4983 * Check if the file overlaps target space.
4984 */
4985 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
4986 if (extrec[i].blockCount == 0) {
4987 break;
4988 }
4989 block = extrec[i].startBlock + extrec[i].blockCount;
4990 if (block >= startblk) {
4991 overlapped = 1;
4992 break;
4993 }
4994 }
4995 /* Look for more records. */
4996 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
4997 }
4998
4999 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
5000 return (overlapped);
5001 }
5002
5003
5004 /*
5005 * Calculate the progress of a file system resize operation.
5006 */
5007 __private_extern__
5008 int
5009 hfs_resize_progress(struct hfsmount *hfsmp, u_int32_t *progress)
5010 {
5011 if ((hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) == 0) {
5012 return (ENXIO);
5013 }
5014
5015 if (hfsmp->hfs_resize_totalfiles > 0)
5016 *progress = (hfsmp->hfs_resize_filesmoved * 100) / hfsmp->hfs_resize_totalfiles;
5017 else
5018 *progress = 0;
5019
5020 return (0);
5021 }
5022
5023
5024 /*
5025 * Creates a UUID from a unique "name" in the HFS UUID Name space.
5026 * See version 3 UUID.
5027 */
5028 static void
5029 hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result)
5030 {
5031 MD5_CTX md5c;
5032 uint8_t rawUUID[8];
5033
5034 ((uint32_t *)rawUUID)[0] = hfsmp->vcbFndrInfo[6];
5035 ((uint32_t *)rawUUID)[1] = hfsmp->vcbFndrInfo[7];
5036
5037 MD5Init( &md5c );
5038 MD5Update( &md5c, HFS_UUID_NAMESPACE_ID, sizeof( uuid_t ) );
5039 MD5Update( &md5c, rawUUID, sizeof (rawUUID) );
5040 MD5Final( result, &md5c );
5041
5042 result[6] = 0x30 | ( result[6] & 0x0F );
5043 result[8] = 0x80 | ( result[8] & 0x3F );
5044 }
5045
5046 /*
5047 * Get file system attributes.
5048 */
5049 static int
5050 hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
5051 {
5052 #define HFS_ATTR_CMN_VALIDMASK (ATTR_CMN_VALIDMASK & ~(ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST))
5053 #define HFS_ATTR_FILE_VALIDMASK (ATTR_FILE_VALIDMASK & ~(ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))
5054
5055 ExtendedVCB *vcb = VFSTOVCB(mp);
5056 struct hfsmount *hfsmp = VFSTOHFS(mp);
5057 u_int32_t freeCNIDs;
5058
5059 freeCNIDs = (u_int32_t)0xFFFFFFFF - (u_int32_t)hfsmp->vcbNxtCNID;
5060
5061 VFSATTR_RETURN(fsap, f_objcount, (u_int64_t)hfsmp->vcbFilCnt + (u_int64_t)hfsmp->vcbDirCnt);
5062 VFSATTR_RETURN(fsap, f_filecount, (u_int64_t)hfsmp->vcbFilCnt);
5063 VFSATTR_RETURN(fsap, f_dircount, (u_int64_t)hfsmp->vcbDirCnt);
5064 VFSATTR_RETURN(fsap, f_maxobjcount, (u_int64_t)0xFFFFFFFF);
5065 VFSATTR_RETURN(fsap, f_iosize, (size_t)cluster_max_io_size(mp, 0));
5066 VFSATTR_RETURN(fsap, f_blocks, (u_int64_t)hfsmp->totalBlocks);
5067 VFSATTR_RETURN(fsap, f_bfree, (u_int64_t)hfs_freeblks(hfsmp, 0));
5068 VFSATTR_RETURN(fsap, f_bavail, (u_int64_t)hfs_freeblks(hfsmp, 1));
5069 VFSATTR_RETURN(fsap, f_bsize, (u_int32_t)vcb->blockSize);
5070 /* XXX needs clarification */
5071 VFSATTR_RETURN(fsap, f_bused, hfsmp->totalBlocks - hfs_freeblks(hfsmp, 1));
5072 /* Maximum files is constrained by total blocks. */
5073 VFSATTR_RETURN(fsap, f_files, (u_int64_t)(hfsmp->totalBlocks - 2));
5074 VFSATTR_RETURN(fsap, f_ffree, MIN((u_int64_t)freeCNIDs, (u_int64_t)hfs_freeblks(hfsmp, 1)));
5075
5076 fsap->f_fsid.val[0] = hfsmp->hfs_raw_dev;
5077 fsap->f_fsid.val[1] = vfs_typenum(mp);
5078 VFSATTR_SET_SUPPORTED(fsap, f_fsid);
5079
5080 VFSATTR_RETURN(fsap, f_signature, vcb->vcbSigWord);
5081 VFSATTR_RETURN(fsap, f_carbon_fsid, 0);
5082
5083 if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
5084 vol_capabilities_attr_t *cap;
5085
5086 cap = &fsap->f_capabilities;
5087
5088 if (hfsmp->hfs_flags & HFS_STANDARD) {
5089 cap->capabilities[VOL_CAPABILITIES_FORMAT] =
5090 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
5091 VOL_CAP_FMT_CASE_PRESERVING |
5092 VOL_CAP_FMT_FAST_STATFS |
5093 VOL_CAP_FMT_HIDDEN_FILES |
5094 VOL_CAP_FMT_PATH_FROM_ID;
5095 } else {
5096 cap->capabilities[VOL_CAPABILITIES_FORMAT] =
5097 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
5098 VOL_CAP_FMT_SYMBOLICLINKS |
5099 VOL_CAP_FMT_HARDLINKS |
5100 VOL_CAP_FMT_JOURNAL |
5101 VOL_CAP_FMT_ZERO_RUNS |
5102 (hfsmp->jnl ? VOL_CAP_FMT_JOURNAL_ACTIVE : 0) |
5103 (hfsmp->hfs_flags & HFS_CASE_SENSITIVE ? VOL_CAP_FMT_CASE_SENSITIVE : 0) |
5104 VOL_CAP_FMT_CASE_PRESERVING |
5105 VOL_CAP_FMT_FAST_STATFS |
5106 VOL_CAP_FMT_2TB_FILESIZE |
5107 VOL_CAP_FMT_HIDDEN_FILES |
5108 #if HFS_COMPRESSION
5109 VOL_CAP_FMT_PATH_FROM_ID |
5110 VOL_CAP_FMT_DECMPFS_COMPRESSION;
5111 #else
5112 VOL_CAP_FMT_PATH_FROM_ID;
5113 #endif
5114 }
5115 cap->capabilities[VOL_CAPABILITIES_INTERFACES] =
5116 VOL_CAP_INT_SEARCHFS |
5117 VOL_CAP_INT_ATTRLIST |
5118 VOL_CAP_INT_NFSEXPORT |
5119 VOL_CAP_INT_READDIRATTR |
5120 VOL_CAP_INT_EXCHANGEDATA |
5121 VOL_CAP_INT_ALLOCATE |
5122 VOL_CAP_INT_VOL_RENAME |
5123 VOL_CAP_INT_ADVLOCK |
5124 VOL_CAP_INT_FLOCK |
5125 #if NAMEDSTREAMS
5126 VOL_CAP_INT_EXTENDED_ATTR |
5127 VOL_CAP_INT_NAMEDSTREAMS;
5128 #else
5129 VOL_CAP_INT_EXTENDED_ATTR;
5130 #endif
5131 cap->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
5132 cap->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
5133
5134 cap->valid[VOL_CAPABILITIES_FORMAT] =
5135 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
5136 VOL_CAP_FMT_SYMBOLICLINKS |
5137 VOL_CAP_FMT_HARDLINKS |
5138 VOL_CAP_FMT_JOURNAL |
5139 VOL_CAP_FMT_JOURNAL_ACTIVE |
5140 VOL_CAP_FMT_NO_ROOT_TIMES |
5141 VOL_CAP_FMT_SPARSE_FILES |
5142 VOL_CAP_FMT_ZERO_RUNS |
5143 VOL_CAP_FMT_CASE_SENSITIVE |
5144 VOL_CAP_FMT_CASE_PRESERVING |
5145 VOL_CAP_FMT_FAST_STATFS |
5146 VOL_CAP_FMT_2TB_FILESIZE |
5147 VOL_CAP_FMT_OPENDENYMODES |
5148 VOL_CAP_FMT_HIDDEN_FILES |
5149 #if HFS_COMPRESSION
5150 VOL_CAP_FMT_PATH_FROM_ID |
5151 VOL_CAP_FMT_DECMPFS_COMPRESSION;
5152 #else
5153 VOL_CAP_FMT_PATH_FROM_ID;
5154 #endif
5155 cap->valid[VOL_CAPABILITIES_INTERFACES] =
5156 VOL_CAP_INT_SEARCHFS |
5157 VOL_CAP_INT_ATTRLIST |
5158 VOL_CAP_INT_NFSEXPORT |
5159 VOL_CAP_INT_READDIRATTR |
5160 VOL_CAP_INT_EXCHANGEDATA |
5161 VOL_CAP_INT_COPYFILE |
5162 VOL_CAP_INT_ALLOCATE |
5163 VOL_CAP_INT_VOL_RENAME |
5164 VOL_CAP_INT_ADVLOCK |
5165 VOL_CAP_INT_FLOCK |
5166 VOL_CAP_INT_MANLOCK |
5167 #if NAMEDSTREAMS
5168 VOL_CAP_INT_EXTENDED_ATTR |
5169 VOL_CAP_INT_NAMEDSTREAMS;
5170 #else
5171 VOL_CAP_INT_EXTENDED_ATTR;
5172 #endif
5173 cap->valid[VOL_CAPABILITIES_RESERVED1] = 0;
5174 cap->valid[VOL_CAPABILITIES_RESERVED2] = 0;
5175 VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
5176 }
5177 if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
5178 vol_attributes_attr_t *attrp = &fsap->f_attributes;
5179
5180 attrp->validattr.commonattr = HFS_ATTR_CMN_VALIDMASK;
5181 attrp->validattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
5182 attrp->validattr.dirattr = ATTR_DIR_VALIDMASK;
5183 attrp->validattr.fileattr = HFS_ATTR_FILE_VALIDMASK;
5184 attrp->validattr.forkattr = 0;
5185
5186 attrp->nativeattr.commonattr = HFS_ATTR_CMN_VALIDMASK;
5187 attrp->nativeattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
5188 attrp->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
5189 attrp->nativeattr.fileattr = HFS_ATTR_FILE_VALIDMASK;
5190 attrp->nativeattr.forkattr = 0;
5191 VFSATTR_SET_SUPPORTED(fsap, f_attributes);
5192 }
5193 fsap->f_create_time.tv_sec = hfsmp->vcbCrDate;
5194 fsap->f_create_time.tv_nsec = 0;
5195 VFSATTR_SET_SUPPORTED(fsap, f_create_time);
5196 fsap->f_modify_time.tv_sec = hfsmp->vcbLsMod;
5197 fsap->f_modify_time.tv_nsec = 0;
5198 VFSATTR_SET_SUPPORTED(fsap, f_modify_time);
5199
5200 fsap->f_backup_time.tv_sec = hfsmp->vcbVolBkUp;
5201 fsap->f_backup_time.tv_nsec = 0;
5202 VFSATTR_SET_SUPPORTED(fsap, f_backup_time);
5203 if (VFSATTR_IS_ACTIVE(fsap, f_fssubtype)) {
5204 u_int16_t subtype = 0;
5205
5206 /*
5207 * Subtypes (flavors) for HFS
5208 * 0: Mac OS Extended
5209 * 1: Mac OS Extended (Journaled)
5210 * 2: Mac OS Extended (Case Sensitive)
5211 * 3: Mac OS Extended (Case Sensitive, Journaled)
5212 * 4 - 127: Reserved
5213 * 128: Mac OS Standard
5214 *
5215 */
5216 if (hfsmp->hfs_flags & HFS_STANDARD) {
5217 subtype = HFS_SUBTYPE_STANDARDHFS;
5218 } else /* HFS Plus */ {
5219 if (hfsmp->jnl)
5220 subtype |= HFS_SUBTYPE_JOURNALED;
5221 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
5222 subtype |= HFS_SUBTYPE_CASESENSITIVE;
5223 }
5224 fsap->f_fssubtype = subtype;
5225 VFSATTR_SET_SUPPORTED(fsap, f_fssubtype);
5226 }
5227
5228 if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
5229 strlcpy(fsap->f_vol_name, (char *) hfsmp->vcbVN, MAXPATHLEN);
5230 VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
5231 }
5232 if (VFSATTR_IS_ACTIVE(fsap, f_uuid)) {
5233 hfs_getvoluuid(hfsmp, fsap->f_uuid);
5234 VFSATTR_SET_SUPPORTED(fsap, f_uuid);
5235 }
5236 return (0);
5237 }
5238
5239 /*
5240 * Perform a volume rename. Requires the FS' root vp.
5241 */
5242 static int
5243 hfs_rename_volume(struct vnode *vp, const char *name, proc_t p)
5244 {
5245 ExtendedVCB *vcb = VTOVCB(vp);
5246 struct cnode *cp = VTOC(vp);
5247 struct hfsmount *hfsmp = VTOHFS(vp);
5248 struct cat_desc to_desc;
5249 struct cat_desc todir_desc;
5250 struct cat_desc new_desc;
5251 cat_cookie_t cookie;
5252 int lockflags;
5253 int error = 0;
5254
5255 /*
5256 * Ignore attempts to rename a volume to a zero-length name.
5257 */
5258 if (name[0] == 0)
5259 return(0);
5260
5261 bzero(&to_desc, sizeof(to_desc));
5262 bzero(&todir_desc, sizeof(todir_desc));
5263 bzero(&new_desc, sizeof(new_desc));
5264 bzero(&cookie, sizeof(cookie));
5265
5266 todir_desc.cd_parentcnid = kHFSRootParentID;
5267 todir_desc.cd_cnid = kHFSRootFolderID;
5268 todir_desc.cd_flags = CD_ISDIR;
5269
5270 to_desc.cd_nameptr = (const u_int8_t *)name;
5271 to_desc.cd_namelen = strlen(name);
5272 to_desc.cd_parentcnid = kHFSRootParentID;
5273 to_desc.cd_cnid = cp->c_cnid;
5274 to_desc.cd_flags = CD_ISDIR;
5275
5276 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)) == 0) {
5277 if ((error = hfs_start_transaction(hfsmp)) == 0) {
5278 if ((error = cat_preflight(hfsmp, CAT_RENAME, &cookie, p)) == 0) {
5279 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
5280
5281 error = cat_rename(hfsmp, &cp->c_desc, &todir_desc, &to_desc, &new_desc);
5282
5283 /*
5284 * If successful, update the name in the VCB, ensure it's terminated.
5285 */
5286 if (!error) {
5287 strlcpy((char *)vcb->vcbVN, name, sizeof(vcb->vcbVN));
5288 }
5289
5290 hfs_systemfile_unlock(hfsmp, lockflags);
5291 cat_postflight(hfsmp, &cookie, p);
5292
5293 if (error)
5294 MarkVCBDirty(vcb);
5295 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
5296 }
5297 hfs_end_transaction(hfsmp);
5298 }
5299 if (!error) {
5300 /* Release old allocated name buffer */
5301 if (cp->c_desc.cd_flags & CD_HASBUF) {
5302 const char *tmp_name = (const char *)cp->c_desc.cd_nameptr;
5303
5304 cp->c_desc.cd_nameptr = 0;
5305 cp->c_desc.cd_namelen = 0;
5306 cp->c_desc.cd_flags &= ~CD_HASBUF;
5307 vfs_removename(tmp_name);
5308 }
5309 /* Update cnode's catalog descriptor */
5310 replace_desc(cp, &new_desc);
5311 vcb->volumeNameEncodingHint = new_desc.cd_encoding;
5312 cp->c_touch_chgtime = TRUE;
5313 }
5314
5315 hfs_unlock(cp);
5316 }
5317
5318 return(error);
5319 }
5320
5321 /*
5322 * Get file system attributes.
5323 */
5324 static int
5325 hfs_vfs_setattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
5326 {
5327 kauth_cred_t cred = vfs_context_ucred(context);
5328 int error = 0;
5329
5330 /*
5331 * Must be superuser or owner of filesystem to change volume attributes
5332 */
5333 if (!kauth_cred_issuser(cred) && (kauth_cred_getuid(cred) != vfs_statfs(mp)->f_owner))
5334 return(EACCES);
5335
5336 if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
5337 vnode_t root_vp;
5338
5339 error = hfs_vfs_root(mp, &root_vp, context);
5340 if (error)
5341 goto out;
5342
5343 error = hfs_rename_volume(root_vp, fsap->f_vol_name, vfs_context_proc(context));
5344 (void) vnode_put(root_vp);
5345 if (error)
5346 goto out;
5347
5348 VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
5349 }
5350
5351 out:
5352 return error;
5353 }
5354
5355 /* If a runtime corruption is detected, set the volume inconsistent
5356 * bit in the volume attributes. The volume inconsistent bit is a persistent
5357 * bit which represents that the volume is corrupt and needs repair.
5358 * The volume inconsistent bit can be set from the kernel when it detects
5359 * runtime corruption or from file system repair utilities like fsck_hfs when
5360 * a repair operation fails. The bit should be cleared only from file system
5361 * verify/repair utility like fsck_hfs when a verify/repair succeeds.
5362 */
5363 void hfs_mark_volume_inconsistent(struct hfsmount *hfsmp)
5364 {
5365 HFS_MOUNT_LOCK(hfsmp, TRUE);
5366 if ((hfsmp->vcbAtrb & kHFSVolumeInconsistentMask) == 0) {
5367 hfsmp->vcbAtrb |= kHFSVolumeInconsistentMask;
5368 MarkVCBDirty(hfsmp);
5369 }
5370 if ((hfsmp->hfs_flags & HFS_READ_ONLY)==0) {
5371 /* Log information to ASL log */
5372 fslog_fs_corrupt(hfsmp->hfs_mp);
5373 printf("hfs: Runtime corruption detected on %s, fsck will be forced on next mount.\n", hfsmp->vcbVN);
5374 }
5375 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
5376 }
5377
5378 /* Replay the journal on the device node provided. Returns zero if
5379 * journal replay succeeded or no journal was supposed to be replayed.
5380 */
5381 static int hfs_journal_replay(vnode_t devvp, vfs_context_t context)
5382 {
5383 int retval = 0;
5384 struct mount *mp = NULL;
5385 struct hfs_mount_args *args = NULL;
5386
5387 /* Replay allowed only on raw devices */
5388 if (!vnode_ischr(devvp)) {
5389 retval = EINVAL;
5390 goto out;
5391 }
5392
5393 /* Create dummy mount structures */
5394 MALLOC(mp, struct mount *, sizeof(struct mount), M_TEMP, M_WAITOK);
5395 if (mp == NULL) {
5396 retval = ENOMEM;
5397 goto out;
5398 }
5399 bzero(mp, sizeof(struct mount));
5400 mount_lock_init(mp);
5401
5402 MALLOC(args, struct hfs_mount_args *, sizeof(struct hfs_mount_args), M_TEMP, M_WAITOK);
5403 if (args == NULL) {
5404 retval = ENOMEM;
5405 goto out;
5406 }
5407 bzero(args, sizeof(struct hfs_mount_args));
5408
5409 retval = hfs_mountfs(devvp, mp, args, 1, context);
5410 buf_flushdirtyblks(devvp, MNT_WAIT, 0, "hfs_journal_replay");
5411
5412 out:
5413 if (mp) {
5414 mount_lock_destroy(mp);
5415 FREE(mp, M_TEMP);
5416 }
5417 if (args) {
5418 FREE(args, M_TEMP);
5419 }
5420 return retval;
5421 }
5422
5423 /*
5424 * hfs vfs operations.
5425 */
5426 struct vfsops hfs_vfsops = {
5427 hfs_mount,
5428 hfs_start,
5429 hfs_unmount,
5430 hfs_vfs_root,
5431 hfs_quotactl,
5432 hfs_vfs_getattr, /* was hfs_statfs */
5433 hfs_sync,
5434 hfs_vfs_vget,
5435 hfs_fhtovp,
5436 hfs_vptofh,
5437 hfs_init,
5438 hfs_sysctl,
5439 hfs_vfs_setattr,
5440 {NULL}
5441 };