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