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