]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vfsops.c
454db37ec6807b5e5f3aaacf58f939d5fd1e9090
[apple/xnu.git] / bsd / hfs / hfs_vfsops.c
1 /*
2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * Copyright (c) 1991, 1993, 1994
32 * The Regents of the University of California. All rights reserved.
33 * (c) UNIX System Laboratories, Inc.
34 * All or some portions of this file are derived from material licensed
35 * to the University of California by American Telephone and Telegraph
36 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
37 * the permission of UNIX System Laboratories, Inc.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * hfs_vfsops.c
68 * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
69 *
70 * (c) Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
71 *
72 * hfs_vfsops.c -- VFS layer for loadable HFS file system.
73 *
74 */
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/kauth.h>
78
79 #include <sys/ubc.h>
80 #include <sys/vnode_internal.h>
81 #include <sys/mount_internal.h>
82 #include <sys/sysctl.h>
83 #include <sys/malloc.h>
84 #include <sys/stat.h>
85 #include <sys/quota.h>
86 #include <sys/disk.h>
87 #include <sys/paths.h>
88 #include <sys/utfconv.h>
89 #include <sys/kdebug.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
110 #if HFS_DIAGNOSTIC
111 int hfs_dbg_all = 0;
112 int hfs_dbg_err = 0;
113 #endif
114
115
116 lck_grp_attr_t * hfs_group_attr;
117 lck_attr_t * hfs_lock_attr;
118 lck_grp_t * hfs_mutex_group;
119 lck_grp_t * hfs_rwlock_group;
120
121
122 extern struct vnodeopv_desc hfs_vnodeop_opv_desc;
123
124 extern void hfs_converterinit(void);
125
126 extern void inittodr(time_t base);
127
128 extern int hfs_write_access(struct vnode *, kauth_cred_t, struct proc *, Boolean);
129
130
131 static int hfs_changefs(struct mount *mp, struct hfs_mount_args *args);
132 static int hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp, vfs_context_t context);
133 static int hfs_flushfiles(struct mount *, int, struct proc *);
134 static int hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush);
135 static int hfs_getmountpoint(struct vnode *vp, struct hfsmount **hfsmpp);
136 static int hfs_init(struct vfsconf *vfsp);
137 static int hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context);
138 static int hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args, vfs_context_t context);
139 static int hfs_reload(struct mount *mp, kauth_cred_t cred, struct proc *p);
140 static int hfs_vfs_root(struct mount *mp, struct vnode **vpp, vfs_context_t context);
141 static int hfs_quotactl(struct mount *, int, uid_t, caddr_t, vfs_context_t context);
142 static int hfs_start(struct mount *mp, int flags, vfs_context_t context);
143 static int hfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, vfs_context_t context);
144 static int hfs_sync(struct mount *mp, int waitfor, vfs_context_t context);
145 static int hfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
146 user_addr_t newp, size_t newlen, vfs_context_t context);
147 static int hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context);
148 static int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
149 static int hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
150
151 static int hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks);
152 static int hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk,
153 u_int32_t catblks, u_int32_t fileID, int rsrcfork);
154
155
156 /*
157 * Called by vfs_mountroot when mounting HFS Plus as root.
158 */
159
160 __private_extern__
161 int
162 hfs_mountroot(mount_t mp, vnode_t rvp, vfs_context_t context)
163 {
164 struct hfsmount *hfsmp;
165 ExtendedVCB *vcb;
166 struct vfsstatfs *vfsp;
167 int error;
168
169 if ((error = hfs_mountfs(rvp, mp, NULL, context)))
170 return (error);
171
172 /* Init hfsmp */
173 hfsmp = VFSTOHFS(mp);
174
175 hfsmp->hfs_uid = UNKNOWNUID;
176 hfsmp->hfs_gid = UNKNOWNGID;
177 hfsmp->hfs_dir_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
178 hfsmp->hfs_file_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
179
180 /* Establish the free block reserve. */
181 vcb = HFSTOVCB(hfsmp);
182 vcb->reserveBlocks = ((u_int64_t)vcb->totalBlocks * HFS_MINFREE) / 100;
183 vcb->reserveBlocks = MIN(vcb->reserveBlocks, HFS_MAXRESERVE / vcb->blockSize);
184
185 vfsp = vfs_statfs(mp);
186 (void)hfs_statfs(mp, vfsp, NULL);
187
188 return (0);
189 }
190
191
192 /*
193 * VFS Operations.
194 *
195 * mount system call
196 */
197
198 static int
199 hfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context)
200 {
201 struct proc *p = vfs_context_proc(context);
202 struct hfsmount *hfsmp = NULL;
203 struct hfs_mount_args args;
204 int retval = E_NONE;
205 uint32_t cmdflags;
206
207 if ((retval = copyin(data, (caddr_t)&args, sizeof(args)))) {
208 return (retval);
209 }
210 cmdflags = (uint32_t)vfs_flags(mp) & MNT_CMDFLAGS;
211 if (cmdflags & MNT_UPDATE) {
212 hfsmp = VFSTOHFS(mp);
213
214 /* Reload incore data after an fsck. */
215 if (cmdflags & MNT_RELOAD) {
216 if (vfs_isrdonly(mp))
217 return hfs_reload(mp, vfs_context_ucred(context), p);
218 else
219 return (EINVAL);
220 }
221
222 /* Change to a read-only file system. */
223 if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
224 vfs_isrdonly(mp)) {
225 int flags;
226
227 /* use VFS_SYNC to push out System (btree) files */
228 retval = VFS_SYNC(mp, MNT_WAIT, context);
229 if (retval && ((cmdflags & MNT_FORCE) == 0))
230 goto out;
231
232 flags = WRITECLOSE;
233 if (cmdflags & MNT_FORCE)
234 flags |= FORCECLOSE;
235
236 if ((retval = hfs_flushfiles(mp, flags, p)))
237 goto out;
238 hfsmp->hfs_flags |= HFS_READ_ONLY;
239 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
240
241 /* also get the volume bitmap blocks */
242 if (!retval) {
243 if (vnode_mount(hfsmp->hfs_devvp) == mp) {
244 retval = hfs_fsync(hfsmp->hfs_devvp, MNT_WAIT, 0, p);
245 } else {
246 vnode_get(hfsmp->hfs_devvp);
247 retval = VNOP_FSYNC(hfsmp->hfs_devvp, MNT_WAIT, context);
248 vnode_put(hfsmp->hfs_devvp);
249 }
250 }
251 if (retval) {
252 hfsmp->hfs_flags &= ~HFS_READ_ONLY;
253 goto out;
254 }
255 if (hfsmp->jnl) {
256 hfs_global_exclusive_lock_acquire(hfsmp);
257
258 journal_close(hfsmp->jnl);
259 hfsmp->jnl = NULL;
260
261 // Note: we explicitly don't want to shutdown
262 // access to the jvp because we may need
263 // it later if we go back to being read-write.
264
265 hfs_global_exclusive_lock_release(hfsmp);
266 }
267 }
268
269 /* Change to a writable file system. */
270 if (vfs_iswriteupgrade(mp)) {
271 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
272 if (retval != E_NONE)
273 goto out;
274
275 // If the journal was shut-down previously because we were
276 // asked to be read-only, let's start it back up again now
277
278 if ( (HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask)
279 && hfsmp->jnl == NULL
280 && hfsmp->jvp != NULL) {
281 int jflags;
282
283 if (hfsmp->hfs_flags & HFS_NEED_JNL_RESET) {
284 jflags = JOURNAL_RESET;
285 } else {
286 jflags = 0;
287 }
288
289 hfs_global_exclusive_lock_acquire(hfsmp);
290
291 hfsmp->jnl = journal_open(hfsmp->jvp,
292 (hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset,
293 hfsmp->jnl_size,
294 hfsmp->hfs_devvp,
295 hfsmp->hfs_phys_block_size,
296 jflags,
297 0,
298 hfs_sync_metadata, hfsmp->hfs_mp);
299
300 hfs_global_exclusive_lock_release(hfsmp);
301
302 if (hfsmp->jnl == NULL) {
303 retval = EINVAL;
304 goto out;
305 } else {
306 hfsmp->hfs_flags &= ~HFS_NEED_JNL_RESET;
307 }
308
309 }
310
311 /* Only clear HFS_READ_ONLY after a successfull write */
312 hfsmp->hfs_flags &= ~HFS_READ_ONLY;
313
314 if (!(hfsmp->hfs_flags & (HFS_READ_ONLY & HFS_STANDARD))) {
315 /* setup private/hidden directory for unlinked files */
316 FindMetaDataDirectory(HFSTOVCB(hfsmp));
317 hfs_remove_orphans(hfsmp);
318
319 /*
320 * Allow hot file clustering if conditions allow.
321 */
322 if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
323 (void) hfs_recording_init(hfsmp);
324 }
325 }
326 }
327
328 /* Update file system parameters. */
329 retval = hfs_changefs(mp, &args);
330
331 } else /* not an update request */ {
332
333 /* Set the mount flag to indicate that we support volfs */
334 vfs_setflags(mp, (uint64_t)((unsigned int)MNT_DOVOLFS));
335
336 retval = hfs_mountfs(devvp, mp, &args, context);
337 }
338 out:
339 if (retval == 0) {
340 (void)hfs_statfs(mp, vfs_statfs(mp), context);
341 }
342 return (retval);
343 }
344
345
346 struct hfs_changefs_cargs {
347 struct hfsmount *hfsmp;
348 int namefix;
349 int permfix;
350 int permswitch;
351 };
352
353 static int
354 hfs_changefs_callback(struct vnode *vp, void *cargs)
355 {
356 ExtendedVCB *vcb;
357 struct cnode *cp;
358 struct cat_desc cndesc;
359 struct cat_attr cnattr;
360 struct hfs_changefs_cargs *args;
361
362 args = (struct hfs_changefs_cargs *)cargs;
363
364 cp = VTOC(vp);
365 vcb = HFSTOVCB(args->hfsmp);
366
367 if (cat_lookup(args->hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL, NULL)) {
368 /*
369 * If we couldn't find this guy skip to the next one
370 */
371 if (args->namefix)
372 cache_purge(vp);
373
374 return (VNODE_RETURNED);
375 }
376 /*
377 * Get the real uid/gid and perm mask from disk.
378 */
379 if (args->permswitch || args->permfix) {
380 cp->c_uid = cnattr.ca_uid;
381 cp->c_gid = cnattr.ca_gid;
382 cp->c_mode = cnattr.ca_mode;
383 }
384 /*
385 * If we're switching name converters then...
386 * Remove the existing entry from the namei cache.
387 * Update name to one based on new encoder.
388 */
389 if (args->namefix) {
390 cache_purge(vp);
391 replace_desc(cp, &cndesc);
392
393 if (cndesc.cd_cnid == kHFSRootFolderID) {
394 strncpy(vcb->vcbVN, cp->c_desc.cd_nameptr, NAME_MAX);
395 cp->c_desc.cd_encoding = args->hfsmp->hfs_encoding;
396 }
397 } else {
398 cat_releasedesc(&cndesc);
399 }
400 return (VNODE_RETURNED);
401 }
402
403 /* Change fs mount parameters */
404 static int
405 hfs_changefs(struct mount *mp, struct hfs_mount_args *args)
406 {
407 int retval = 0;
408 int namefix, permfix, permswitch;
409 struct hfsmount *hfsmp;
410 ExtendedVCB *vcb;
411 hfs_to_unicode_func_t get_unicode_func;
412 unicode_to_hfs_func_t get_hfsname_func;
413 u_long old_encoding = 0;
414 struct hfs_changefs_cargs cargs;
415 uint32_t mount_flags;
416
417 hfsmp = VFSTOHFS(mp);
418 vcb = HFSTOVCB(hfsmp);
419 mount_flags = (unsigned int)vfs_flags(mp);
420
421 permswitch = (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) &&
422 ((mount_flags & MNT_UNKNOWNPERMISSIONS) == 0)) ||
423 (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) == 0) &&
424 (mount_flags & MNT_UNKNOWNPERMISSIONS)));
425
426 /* The root filesystem must operate with actual permissions: */
427 if (permswitch && (mount_flags & MNT_ROOTFS) && (mount_flags & MNT_UNKNOWNPERMISSIONS)) {
428 vfs_clearflags(mp, (uint64_t)((unsigned int)MNT_UNKNOWNPERMISSIONS)); /* Just say "No". */
429 return EINVAL;
430 }
431 if (mount_flags & MNT_UNKNOWNPERMISSIONS)
432 hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS;
433 else
434 hfsmp->hfs_flags &= ~HFS_UNKNOWN_PERMS;
435
436 namefix = permfix = 0;
437
438 /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */
439 if (args->hfs_timezone.tz_minuteswest != VNOVAL) {
440 gTimeZone = args->hfs_timezone;
441 }
442
443 /* Change the default uid, gid and/or mask */
444 if ((args->hfs_uid != (uid_t)VNOVAL) && (hfsmp->hfs_uid != args->hfs_uid)) {
445 hfsmp->hfs_uid = args->hfs_uid;
446 if (vcb->vcbSigWord == kHFSPlusSigWord)
447 ++permfix;
448 }
449 if ((args->hfs_gid != (gid_t)VNOVAL) && (hfsmp->hfs_gid != args->hfs_gid)) {
450 hfsmp->hfs_gid = args->hfs_gid;
451 if (vcb->vcbSigWord == kHFSPlusSigWord)
452 ++permfix;
453 }
454 if (args->hfs_mask != (mode_t)VNOVAL) {
455 if (hfsmp->hfs_dir_mask != (args->hfs_mask & ALLPERMS)) {
456 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
457 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
458 if ((args->flags != VNOVAL) && (args->flags & HFSFSMNT_NOXONFILES))
459 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
460 if (vcb->vcbSigWord == kHFSPlusSigWord)
461 ++permfix;
462 }
463 }
464
465 /* Change the hfs encoding value (hfs only) */
466 if ((vcb->vcbSigWord == kHFSSigWord) &&
467 (args->hfs_encoding != (u_long)VNOVAL) &&
468 (hfsmp->hfs_encoding != args->hfs_encoding)) {
469
470 retval = hfs_getconverter(args->hfs_encoding, &get_unicode_func, &get_hfsname_func);
471 if (retval)
472 goto exit;
473
474 /*
475 * Connect the new hfs_get_unicode converter but leave
476 * the old hfs_get_hfsname converter in place so that
477 * we can lookup existing vnodes to get their correctly
478 * encoded names.
479 *
480 * When we're all finished, we can then connect the new
481 * hfs_get_hfsname converter and release our interest
482 * in the old converters.
483 */
484 hfsmp->hfs_get_unicode = get_unicode_func;
485 old_encoding = hfsmp->hfs_encoding;
486 hfsmp->hfs_encoding = args->hfs_encoding;
487 ++namefix;
488 }
489
490 if (!(namefix || permfix || permswitch))
491 goto exit;
492
493 /* XXX 3762912 hack to support HFS filesystem 'owner' */
494 if (permfix)
495 vfs_setowner(mp,
496 hfsmp->hfs_uid == UNKNOWNUID ? KAUTH_UID_NONE : hfsmp->hfs_uid,
497 hfsmp->hfs_gid == UNKNOWNGID ? KAUTH_GID_NONE : hfsmp->hfs_gid);
498
499 /*
500 * For each active vnode fix things that changed
501 *
502 * Note that we can visit a vnode more than once
503 * and we can race with fsync.
504 *
505 * hfs_changefs_callback will be called for each vnode
506 * hung off of this mount point
507 * the vnode will be
508 * properly referenced and unreferenced around the callback
509 */
510 cargs.hfsmp = hfsmp;
511 cargs.namefix = namefix;
512 cargs.permfix = permfix;
513 cargs.permswitch = permswitch;
514
515 vnode_iterate(mp, 0, hfs_changefs_callback, (void *)&cargs);
516
517 /*
518 * If we're switching name converters we can now
519 * connect the new hfs_get_hfsname converter and
520 * release our interest in the old converters.
521 */
522 if (namefix) {
523 hfsmp->hfs_get_hfsname = get_hfsname_func;
524 vcb->volumeNameEncodingHint = args->hfs_encoding;
525 (void) hfs_relconverter(old_encoding);
526 }
527 exit:
528 return (retval);
529 }
530
531
532 struct hfs_reload_cargs {
533 struct hfsmount *hfsmp;
534 kauth_cred_t cred;
535 struct proc *p;
536 int error;
537 };
538
539 static int
540 hfs_reload_callback(struct vnode *vp, void *cargs)
541 {
542 struct cnode *cp;
543 struct hfs_reload_cargs *args;
544
545 args = (struct hfs_reload_cargs *)cargs;
546 /*
547 * flush all the buffers associated with this node
548 */
549 (void) buf_invalidateblks(vp, 0, 0, 0);
550
551 cp = VTOC(vp);
552 /*
553 * Remove any directory hints
554 */
555 if (vnode_isdir(vp))
556 hfs_reldirhints(cp, 0);
557
558 /*
559 * Re-read cnode data for all active vnodes (non-metadata files).
560 */
561 if (!vnode_issystem(vp) && !VNODE_IS_RSRC(vp)) {
562 struct cat_fork *datafork;
563 struct cat_desc desc;
564
565 datafork = cp->c_datafork ? &cp->c_datafork->ff_data : NULL;
566
567 /* lookup by fileID since name could have changed */
568 if ((args->error = cat_idlookup(args->hfsmp, cp->c_fileid, &desc, &cp->c_attr, datafork)))
569 return (VNODE_RETURNED_DONE);
570
571 /* update cnode's catalog descriptor */
572 (void) replace_desc(cp, &desc);
573 }
574 return (VNODE_RETURNED);
575 }
576
577 /*
578 * Reload all incore data for a filesystem (used after running fsck on
579 * the root filesystem and finding things to fix). The filesystem must
580 * be mounted read-only.
581 *
582 * Things to do to update the mount:
583 * invalidate all cached meta-data.
584 * invalidate all inactive vnodes.
585 * invalidate all cached file data.
586 * re-read volume header from disk.
587 * re-load meta-file info (extents, file size).
588 * re-load B-tree header data.
589 * re-read cnode data for all active vnodes.
590 */
591 static int
592 hfs_reload(struct mount *mountp, kauth_cred_t cred, struct proc *p)
593 {
594 register struct vnode *devvp;
595 struct buf *bp;
596 int sectorsize;
597 int error, i;
598 struct hfsmount *hfsmp;
599 struct HFSPlusVolumeHeader *vhp;
600 ExtendedVCB *vcb;
601 struct filefork *forkp;
602 struct cat_desc cndesc;
603 struct hfs_reload_cargs args;
604
605 hfsmp = VFSTOHFS(mountp);
606 vcb = HFSTOVCB(hfsmp);
607
608 if (vcb->vcbSigWord == kHFSSigWord)
609 return (EINVAL); /* rooting from HFS is not supported! */
610
611 /*
612 * Invalidate all cached meta-data.
613 */
614 devvp = hfsmp->hfs_devvp;
615 if (buf_invalidateblks(devvp, 0, 0, 0))
616 panic("hfs_reload: dirty1");
617
618 args.hfsmp = hfsmp;
619 args.cred = cred;
620 args.p = p;
621 args.error = 0;
622 /*
623 * hfs_reload_callback will be called for each vnode
624 * hung off of this mount point that can't be recycled...
625 * vnode_iterate will recycle those that it can (the VNODE_RELOAD option)
626 * the vnode will be in an 'unbusy' state (VNODE_WAIT) and
627 * properly referenced and unreferenced around the callback
628 */
629 vnode_iterate(mountp, VNODE_RELOAD | VNODE_WAIT, hfs_reload_callback, (void *)&args);
630
631 if (args.error)
632 return (args.error);
633
634 /*
635 * Re-read VolumeHeader from disk.
636 */
637 sectorsize = hfsmp->hfs_phys_block_size;
638
639 error = (int)buf_meta_bread(hfsmp->hfs_devvp,
640 (daddr64_t)((vcb->hfsPlusIOPosOffset / sectorsize) + HFS_PRI_SECTOR(sectorsize)),
641 sectorsize, NOCRED, &bp);
642 if (error) {
643 if (bp != NULL)
644 buf_brelse(bp);
645 return (error);
646 }
647
648 vhp = (HFSPlusVolumeHeader *) (buf_dataptr(bp) + HFS_PRI_OFFSET(sectorsize));
649
650 /* Do a quick sanity check */
651 if ((SWAP_BE16(vhp->signature) != kHFSPlusSigWord &&
652 SWAP_BE16(vhp->signature) != kHFSXSigWord) ||
653 (SWAP_BE16(vhp->version) != kHFSPlusVersion &&
654 SWAP_BE16(vhp->version) != kHFSXVersion) ||
655 SWAP_BE32(vhp->blockSize) != vcb->blockSize) {
656 buf_brelse(bp);
657 return (EIO);
658 }
659
660 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
661 vcb->vcbAtrb = SWAP_BE32 (vhp->attributes);
662 vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
663 vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize);
664 vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID);
665 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
666 vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount);
667 vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount);
668 vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount);
669 vcb->nextAllocation = SWAP_BE32 (vhp->nextAllocation);
670 vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks);
671 vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks);
672 vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap);
673 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
674 vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* hfs+ create date is in local time */
675
676 /*
677 * Re-load meta-file vnode data (extent info, file size, etc).
678 */
679 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
680 for (i = 0; i < kHFSPlusExtentDensity; i++) {
681 forkp->ff_extents[i].startBlock =
682 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
683 forkp->ff_extents[i].blockCount =
684 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
685 }
686 forkp->ff_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
687 forkp->ff_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
688 forkp->ff_clumpsize = SWAP_BE32 (vhp->extentsFile.clumpSize);
689
690
691 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
692 for (i = 0; i < kHFSPlusExtentDensity; i++) {
693 forkp->ff_extents[i].startBlock =
694 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
695 forkp->ff_extents[i].blockCount =
696 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
697 }
698 forkp->ff_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
699 forkp->ff_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
700 forkp->ff_clumpsize = SWAP_BE32 (vhp->catalogFile.clumpSize);
701
702 if (hfsmp->hfs_attribute_vp) {
703 forkp = VTOF(hfsmp->hfs_attribute_vp);
704 for (i = 0; i < kHFSPlusExtentDensity; i++) {
705 forkp->ff_extents[i].startBlock =
706 SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
707 forkp->ff_extents[i].blockCount =
708 SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
709 }
710 forkp->ff_size = SWAP_BE64 (vhp->attributesFile.logicalSize);
711 forkp->ff_blocks = SWAP_BE32 (vhp->attributesFile.totalBlocks);
712 forkp->ff_clumpsize = SWAP_BE32 (vhp->attributesFile.clumpSize);
713 }
714
715 forkp = VTOF((struct vnode *)vcb->allocationsRefNum);
716 for (i = 0; i < kHFSPlusExtentDensity; i++) {
717 forkp->ff_extents[i].startBlock =
718 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
719 forkp->ff_extents[i].blockCount =
720 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
721 }
722 forkp->ff_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
723 forkp->ff_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
724 forkp->ff_clumpsize = SWAP_BE32 (vhp->allocationFile.clumpSize);
725
726 buf_brelse(bp);
727 vhp = NULL;
728
729 /*
730 * Re-load B-tree header data
731 */
732 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
733 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
734 return (error);
735
736 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
737 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
738 return (error);
739
740 if (hfsmp->hfs_attribute_vp) {
741 forkp = VTOF(hfsmp->hfs_attribute_vp);
742 if ( (error = MacToVFSError( BTReloadData((FCB*)forkp) )) )
743 return (error);
744 }
745
746 /* Reload the volume name */
747 if ((error = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, NULL, NULL)))
748 return (error);
749 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
750 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
751 cat_releasedesc(&cndesc);
752
753 /* Re-establish private/hidden directory for unlinked files */
754 FindMetaDataDirectory(vcb);
755
756 /* In case any volume information changed to trigger a notification */
757 hfs_generate_volume_notifications(hfsmp);
758
759 return (0);
760 }
761
762
763 /*
764 * Common code for mount and mountroot
765 */
766 static int
767 hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
768 vfs_context_t context)
769 {
770 struct proc *p = vfs_context_proc(context);
771 int retval = E_NONE;
772 struct hfsmount *hfsmp;
773 struct buf *bp;
774 dev_t dev;
775 HFSMasterDirectoryBlock *mdbp;
776 int ronly;
777 int i;
778 int mntwrapper;
779 kauth_cred_t cred;
780 u_int64_t disksize;
781 daddr64_t blkcnt;
782 u_int32_t blksize;
783 u_int32_t minblksize;
784 u_int32_t iswritable;
785 daddr64_t mdb_offset;
786
787 ronly = vfs_isrdonly(mp);
788 dev = vnode_specrdev(devvp);
789 cred = p ? vfs_context_ucred(context) : NOCRED;
790 mntwrapper = 0;
791
792 bp = NULL;
793 hfsmp = NULL;
794 mdbp = NULL;
795 minblksize = kHFSBlockSize;
796
797 /* Advisory locking should be handled at the VFS layer */
798 vfs_setlocklocal(mp);
799
800 /* Get the real physical block size. */
801 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&blksize, 0, context)) {
802 retval = ENXIO;
803 goto error_exit;
804 }
805 /* Switch to 512 byte sectors (temporarily) */
806 if (blksize > 512) {
807 u_int32_t size512 = 512;
808
809 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&size512, FWRITE, context)) {
810 retval = ENXIO;
811 goto error_exit;
812 }
813 }
814 /* Get the number of 512 byte physical blocks. */
815 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, context)) {
816 retval = ENXIO;
817 goto error_exit;
818 }
819 /* Compute an accurate disk size (i.e. within 512 bytes) */
820 disksize = (u_int64_t)blkcnt * (u_int64_t)512;
821
822 /*
823 * On Tiger it is not necessary to switch the device
824 * block size to be 4k if there are more than 31-bits
825 * worth of blocks but to insure compatibility with
826 * pre-Tiger systems we have to do it.
827 */
828 if (blkcnt > (u_int64_t)0x000000007fffffff) {
829 minblksize = blksize = 4096;
830 }
831
832 /* Now switch to our prefered physical block size. */
833 if (blksize > 512) {
834 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, context)) {
835 retval = ENXIO;
836 goto error_exit;
837 }
838 /* Get the count of physical blocks. */
839 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, context)) {
840 retval = ENXIO;
841 goto error_exit;
842 }
843 }
844 /*
845 * At this point:
846 * minblksize is the minimum physical block size
847 * blksize has our prefered physical block size
848 * blkcnt has the total number of physical blocks
849 */
850
851 mdb_offset = (daddr64_t)HFS_PRI_SECTOR(blksize);
852 if ((retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp))) {
853 goto error_exit;
854 }
855 MALLOC(mdbp, HFSMasterDirectoryBlock *, kMDBSize, M_TEMP, M_WAITOK);
856 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(blksize), mdbp, kMDBSize);
857 buf_brelse(bp);
858 bp = NULL;
859
860 MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK);
861 bzero(hfsmp, sizeof(struct hfsmount));
862
863 /*
864 * Init the volume information structure
865 */
866
867 lck_mtx_init(&hfsmp->hfs_mutex, hfs_mutex_group, hfs_lock_attr);
868 lck_mtx_init(&hfsmp->hfc_mutex, hfs_mutex_group, hfs_lock_attr);
869 lck_rw_init(&hfsmp->hfs_global_lock, hfs_rwlock_group, hfs_lock_attr);
870 lck_rw_init(&hfsmp->hfs_insync, hfs_rwlock_group, hfs_lock_attr);
871
872 vfs_setfsprivate(mp, hfsmp);
873 hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */
874 hfsmp->hfs_raw_dev = vnode_specrdev(devvp);
875 hfsmp->hfs_devvp = devvp;
876 hfsmp->hfs_phys_block_size = blksize;
877 hfsmp->hfs_phys_block_count = blkcnt;
878 hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA;
879 if (ronly)
880 hfsmp->hfs_flags |= HFS_READ_ONLY;
881 if (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)
882 hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS;
883 for (i = 0; i < MAXQUOTAS; i++)
884 dqfileinit(&hfsmp->hfs_qfiles[i]);
885
886 if (args) {
887 hfsmp->hfs_uid = (args->hfs_uid == (uid_t)VNOVAL) ? UNKNOWNUID : args->hfs_uid;
888 if (hfsmp->hfs_uid == 0xfffffffd) hfsmp->hfs_uid = UNKNOWNUID;
889 hfsmp->hfs_gid = (args->hfs_gid == (gid_t)VNOVAL) ? UNKNOWNGID : args->hfs_gid;
890 if (hfsmp->hfs_gid == 0xfffffffd) hfsmp->hfs_gid = UNKNOWNGID;
891 vfs_setowner(mp, hfsmp->hfs_uid, hfsmp->hfs_gid); /* tell the VFS */
892 if (args->hfs_mask != (mode_t)VNOVAL) {
893 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
894 if (args->flags & HFSFSMNT_NOXONFILES) {
895 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
896 } else {
897 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
898 }
899 } else {
900 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
901 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
902 }
903 if ((args->flags != (int)VNOVAL) && (args->flags & HFSFSMNT_WRAPPER))
904 mntwrapper = 1;
905 } else {
906 /* Even w/o explicit mount arguments, MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */
907 if (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS) {
908 hfsmp->hfs_uid = UNKNOWNUID;
909 hfsmp->hfs_gid = UNKNOWNGID;
910 vfs_setowner(mp, hfsmp->hfs_uid, hfsmp->hfs_gid); /* tell the VFS */
911 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
912 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
913 }
914 }
915
916 /* Find out if disk media is writable. */
917 if (VNOP_IOCTL(devvp, DKIOCISWRITABLE, (caddr_t)&iswritable, 0, context) == 0) {
918 if (iswritable)
919 hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA;
920 else
921 hfsmp->hfs_flags &= ~HFS_WRITEABLE_MEDIA;
922 }
923
924 // record the current time at which we're mounting this volume
925 {
926 struct timeval tv;
927 microtime(&tv);
928 hfsmp->hfs_mount_time = tv.tv_sec;
929 }
930
931 /* Mount a standard HFS disk */
932 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) &&
933 (mntwrapper || (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord))) {
934 if ((vfs_flags(mp) & MNT_ROOTFS)) {
935 retval = EINVAL; /* Cannot root from HFS standard disks */
936 goto error_exit;
937 }
938 /* HFS disks can only use 512 byte physical blocks */
939 if (blksize > kHFSBlockSize) {
940 blksize = kHFSBlockSize;
941 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, context)) {
942 retval = ENXIO;
943 goto error_exit;
944 }
945 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, context)) {
946 retval = ENXIO;
947 goto error_exit;
948 }
949 hfsmp->hfs_phys_block_size = blksize;
950 hfsmp->hfs_phys_block_count = blkcnt;
951 }
952 if (args) {
953 hfsmp->hfs_encoding = args->hfs_encoding;
954 HFSTOVCB(hfsmp)->volumeNameEncodingHint = args->hfs_encoding;
955
956 /* establish the timezone */
957 gTimeZone = args->hfs_timezone;
958 }
959
960 retval = hfs_getconverter(hfsmp->hfs_encoding, &hfsmp->hfs_get_unicode,
961 &hfsmp->hfs_get_hfsname);
962 if (retval)
963 goto error_exit;
964
965 retval = hfs_MountHFSVolume(hfsmp, mdbp, p);
966 if (retval)
967 (void) hfs_relconverter(hfsmp->hfs_encoding);
968
969 } else /* Mount an HFS Plus disk */ {
970 HFSPlusVolumeHeader *vhp;
971 off_t embeddedOffset;
972 int jnl_disable = 0;
973
974 /* Get the embedded Volume Header */
975 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
976 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * kHFSBlockSize;
977 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
978 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
979
980 /*
981 * If the embedded volume doesn't start on a block
982 * boundary, then switch the device to a 512-byte
983 * block size so everything will line up on a block
984 * boundary.
985 */
986 if ((embeddedOffset % blksize) != 0) {
987 printf("HFS Mount: embedded volume offset not"
988 " a multiple of physical block size (%d);"
989 " switching to 512\n", blksize);
990 blksize = 512;
991 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE,
992 (caddr_t)&blksize, FWRITE, context)) {
993 retval = ENXIO;
994 goto error_exit;
995 }
996 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT,
997 (caddr_t)&blkcnt, 0, context)) {
998 retval = ENXIO;
999 goto error_exit;
1000 }
1001 /* Note: relative block count adjustment */
1002 hfsmp->hfs_phys_block_count *=
1003 hfsmp->hfs_phys_block_size / blksize;
1004 hfsmp->hfs_phys_block_size = blksize;
1005 }
1006
1007 disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) *
1008 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
1009
1010 hfsmp->hfs_phys_block_count = disksize / blksize;
1011
1012 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
1013 retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1014 if (retval)
1015 goto error_exit;
1016 bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(blksize), mdbp, 512);
1017 buf_brelse(bp);
1018 bp = NULL;
1019 vhp = (HFSPlusVolumeHeader*) mdbp;
1020
1021 } else /* pure HFS+ */ {
1022 embeddedOffset = 0;
1023 vhp = (HFSPlusVolumeHeader*) mdbp;
1024 }
1025
1026 // XXXdbg
1027 //
1028 hfsmp->jnl = NULL;
1029 hfsmp->jvp = NULL;
1030 if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS) && args->journal_disable) {
1031 jnl_disable = 1;
1032 }
1033
1034 //
1035 // We only initialize the journal here if the last person
1036 // to mount this volume was journaling aware. Otherwise
1037 // we delay journal initialization until later at the end
1038 // of hfs_MountHFSPlusVolume() because the last person who
1039 // mounted it could have messed things up behind our back
1040 // (so we need to go find the .journal file, make sure it's
1041 // the right size, re-sync up if it was moved, etc).
1042 //
1043 if ( (SWAP_BE32(vhp->lastMountedVersion) == kHFSJMountVersion)
1044 && (SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask)
1045 && !jnl_disable) {
1046
1047 // if we're able to init the journal, mark the mount
1048 // point as journaled.
1049 //
1050 if (hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred) == 0) {
1051 vfs_setflags(mp, (uint64_t)((unsigned int)MNT_JOURNALED));
1052 } else {
1053 // if the journal failed to open, then set the lastMountedVersion
1054 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1055 // of just bailing out because the volume is journaled.
1056 if (!ronly) {
1057 HFSPlusVolumeHeader *jvhp;
1058
1059 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
1060
1061 if (mdb_offset == 0) {
1062 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
1063 }
1064
1065 bp = NULL;
1066 retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1067 if (retval == 0) {
1068 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(blksize));
1069
1070 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
1071 printf ("hfs(1): Journal replay fail. Writing lastMountVersion as FSK!\n");
1072 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
1073 buf_bwrite(bp);
1074 } else {
1075 buf_brelse(bp);
1076 }
1077 bp = NULL;
1078 } else if (bp) {
1079 buf_brelse(bp);
1080 // clear this so the error exit path won't try to use it
1081 bp = NULL;
1082 }
1083 }
1084
1085 // if this isn't the root device just bail out.
1086 // If it is the root device we just continue on
1087 // in the hopes that fsck_hfs will be able to
1088 // fix any damage that exists on the volume.
1089 if ( !(vfs_flags(mp) & MNT_ROOTFS)) {
1090 retval = EINVAL;
1091 goto error_exit;
1092 }
1093 }
1094 }
1095 // XXXdbg
1096
1097 (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
1098
1099 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args, cred);
1100 /*
1101 * If the backend didn't like our physical blocksize
1102 * then retry with physical blocksize of 512.
1103 */
1104 if ((retval == ENXIO) && (blksize > 512) && (blksize != minblksize)) {
1105 printf("HFS Mount: could not use physical block size "
1106 "(%d) switching to 512\n", blksize);
1107 blksize = 512;
1108 if (VNOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, context)) {
1109 retval = ENXIO;
1110 goto error_exit;
1111 }
1112 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, context)) {
1113 retval = ENXIO;
1114 goto error_exit;
1115 }
1116 devvp->v_specsize = blksize;
1117 /* Note: relative block count adjustment (in case this is an embedded volume). */
1118 hfsmp->hfs_phys_block_count *= hfsmp->hfs_phys_block_size / blksize;
1119 hfsmp->hfs_phys_block_size = blksize;
1120
1121 if (hfsmp->jnl) {
1122 // close and re-open this with the new block size
1123 journal_close(hfsmp->jnl);
1124 hfsmp->jnl = NULL;
1125 if (hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred) == 0) {
1126 vfs_setflags(mp, (uint64_t)((unsigned int)MNT_JOURNALED));
1127 } else {
1128 // if the journal failed to open, then set the lastMountedVersion
1129 // to be "FSK!" which fsck_hfs will see and force the fsck instead
1130 // of just bailing out because the volume is journaled.
1131 if (!ronly) {
1132 HFSPlusVolumeHeader *jvhp;
1133
1134 hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
1135
1136 if (mdb_offset == 0) {
1137 mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
1138 }
1139
1140 bp = NULL;
1141 retval = (int)buf_meta_bread(devvp, mdb_offset, blksize, cred, &bp);
1142 if (retval == 0) {
1143 jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(blksize));
1144
1145 if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
1146 printf ("hfs(2): Journal replay fail. Writing lastMountVersion as FSK!\n");
1147 jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
1148 buf_bwrite(bp);
1149 } else {
1150 buf_brelse(bp);
1151 }
1152 bp = NULL;
1153 } else if (bp) {
1154 buf_brelse(bp);
1155 // clear this so the error exit path won't try to use it
1156 bp = NULL;
1157 }
1158 }
1159
1160 // if this isn't the root device just bail out.
1161 // If it is the root device we just continue on
1162 // in the hopes that fsck_hfs will be able to
1163 // fix any damage that exists on the volume.
1164 if ( !(vfs_flags(mp) & MNT_ROOTFS)) {
1165 retval = EINVAL;
1166 goto error_exit;
1167 }
1168 }
1169 }
1170
1171 /* Try again with a smaller block size... */
1172 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args, cred);
1173 }
1174 if (retval)
1175 (void) hfs_relconverter(0);
1176 }
1177
1178 // save off a snapshot of the mtime from the previous mount
1179 // (for matador).
1180 hfsmp->hfs_last_mounted_mtime = hfsmp->hfs_mtime;
1181
1182 if ( retval ) {
1183 goto error_exit;
1184 }
1185
1186 mp->mnt_vfsstat.f_fsid.val[0] = (long)dev;
1187 mp->mnt_vfsstat.f_fsid.val[1] = vfs_typenum(mp);
1188 vfs_setmaxsymlen(mp, 0);
1189 mp->mnt_vtable->vfc_threadsafe = TRUE;
1190 mp->mnt_vtable->vfc_vfsflags |= VFC_VFSNATIVEXATTR;
1191
1192 if (args) {
1193 /*
1194 * Set the free space warning levels for a non-root volume:
1195 *
1196 * Set the lower freespace limit (the level that will trigger a warning)
1197 * to 5% of the volume size or 250MB, whichever is less, and the desired
1198 * level (which will cancel the alert request) to 1/2 above that limit.
1199 * Start looking for free space to drop below this level and generate a
1200 * warning immediately if needed:
1201 */
1202 hfsmp->hfs_freespace_notify_warninglimit =
1203 MIN(HFS_LOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1204 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKTRIGGERFRACTION);
1205 hfsmp->hfs_freespace_notify_desiredlevel =
1206 MIN(HFS_LOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
1207 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKSHUTOFFFRACTION);
1208 } else {
1209 /*
1210 * Set the free space warning levels for the root volume:
1211 *
1212 * Set the lower freespace limit (the level that will trigger a warning)
1213 * to 1% of the volume size or 50MB, whichever is less, and the desired
1214 * level (which will cancel the alert request) to 2% or 75MB, whichever is less.
1215 */
1216 hfsmp->hfs_freespace_notify_warninglimit =
1217 MIN(HFS_ROOTLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
1218 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKTRIGGERFRACTION);
1219 hfsmp->hfs_freespace_notify_desiredlevel =
1220 MIN(HFS_ROOTLOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
1221 (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKSHUTOFFFRACTION);
1222 };
1223
1224 /*
1225 * Start looking for free space to drop below this level and generate a
1226 * warning immediately if needed:
1227 */
1228 hfsmp->hfs_notification_conditions = 0;
1229 hfs_generate_volume_notifications(hfsmp);
1230
1231 if (ronly == 0) {
1232 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1233 }
1234 FREE(mdbp, M_TEMP);
1235 return (0);
1236
1237 error_exit:
1238 if (bp)
1239 buf_brelse(bp);
1240 if (mdbp)
1241 FREE(mdbp, M_TEMP);
1242
1243 if (hfsmp && hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
1244 (void)VNOP_CLOSE(hfsmp->jvp, ronly ? FREAD : FREAD|FWRITE, context);
1245 hfsmp->jvp = NULL;
1246 }
1247 if (hfsmp) {
1248 FREE(hfsmp, M_HFSMNT);
1249 vfs_setfsprivate(mp, NULL);
1250 }
1251 return (retval);
1252 }
1253
1254
1255 /*
1256 * Make a filesystem operational.
1257 * Nothing to do at the moment.
1258 */
1259 /* ARGSUSED */
1260 static int
1261 hfs_start(__unused struct mount *mp, __unused int flags, __unused vfs_context_t context)
1262 {
1263 return (0);
1264 }
1265
1266
1267 /*
1268 * unmount system call
1269 */
1270 static int
1271 hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context)
1272 {
1273 struct proc *p = vfs_context_proc(context);
1274 struct hfsmount *hfsmp = VFSTOHFS(mp);
1275 int retval = E_NONE;
1276 int flags;
1277 int force;
1278 int started_tr = 0;
1279
1280 flags = 0;
1281 force = 0;
1282 if (mntflags & MNT_FORCE) {
1283 flags |= FORCECLOSE;
1284 force = 1;
1285 }
1286
1287 if ((retval = hfs_flushfiles(mp, flags, p)) && !force)
1288 return (retval);
1289
1290 if (hfsmp->hfs_flags & HFS_METADATA_ZONE)
1291 (void) hfs_recording_suspend(hfsmp);
1292
1293 /*
1294 * Flush out the b-trees, volume bitmap and Volume Header
1295 */
1296 if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
1297 hfs_start_transaction(hfsmp);
1298 started_tr = 1;
1299
1300 if (hfsmp->hfs_attribute_vp) {
1301 (void) hfs_lock(VTOC(hfsmp->hfs_attribute_vp), HFS_EXCLUSIVE_LOCK);
1302 retval = hfs_fsync(hfsmp->hfs_attribute_vp, MNT_WAIT, 0, p);
1303 hfs_unlock(VTOC(hfsmp->hfs_attribute_vp));
1304 if (retval && !force)
1305 goto err_exit;
1306 }
1307
1308 (void) hfs_lock(VTOC(hfsmp->hfs_catalog_vp), HFS_EXCLUSIVE_LOCK);
1309 retval = hfs_fsync(hfsmp->hfs_catalog_vp, MNT_WAIT, 0, p);
1310 hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
1311 if (retval && !force)
1312 goto err_exit;
1313
1314 (void) hfs_lock(VTOC(hfsmp->hfs_extents_vp), HFS_EXCLUSIVE_LOCK);
1315 retval = hfs_fsync(hfsmp->hfs_extents_vp, MNT_WAIT, 0, p);
1316 hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
1317 if (retval && !force)
1318 goto err_exit;
1319
1320 if (hfsmp->hfs_allocation_vp) {
1321 (void) hfs_lock(VTOC(hfsmp->hfs_allocation_vp), HFS_EXCLUSIVE_LOCK);
1322 retval = hfs_fsync(hfsmp->hfs_allocation_vp, MNT_WAIT, 0, p);
1323 hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
1324 if (retval && !force)
1325 goto err_exit;
1326 }
1327
1328 if (hfsmp->hfc_filevp && vnode_issystem(hfsmp->hfc_filevp)) {
1329 retval = hfs_fsync(hfsmp->hfc_filevp, MNT_WAIT, 0, p);
1330 if (retval && !force)
1331 goto err_exit;
1332 }
1333 #if 0
1334 /* See if this volume is damaged, is so do not unmount cleanly */
1335 if (HFSTOVCB(hfsmp)->vcbFlags & kHFS_DamagedVolume) {
1336 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
1337 } else {
1338 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
1339 }
1340 #else
1341 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
1342 #endif
1343 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1344 if (retval) {
1345 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
1346 if (!force)
1347 goto err_exit; /* could not flush everything */
1348 }
1349
1350 hfs_end_transaction(hfsmp);
1351 started_tr = 0;
1352 }
1353
1354 if (hfsmp->jnl) {
1355 journal_flush(hfsmp->jnl);
1356 }
1357
1358 /*
1359 * Invalidate our caches and release metadata vnodes
1360 */
1361 (void) hfsUnmount(hfsmp, p);
1362
1363 /*
1364 * Last chance to dump unreferenced system files.
1365 */
1366 (void) vflush(mp, NULLVP, FORCECLOSE);
1367
1368 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
1369 (void) hfs_relconverter(hfsmp->hfs_encoding);
1370
1371 // XXXdbg
1372 if (hfsmp->jnl) {
1373 journal_close(hfsmp->jnl);
1374 hfsmp->jnl = NULL;
1375 }
1376
1377 VNOP_FSYNC(hfsmp->hfs_devvp, MNT_WAIT, context);
1378
1379 if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
1380 retval = VNOP_CLOSE(hfsmp->jvp,
1381 hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE,
1382 context);
1383 vnode_put(hfsmp->jvp);
1384 hfsmp->jvp = NULL;
1385 }
1386 // XXXdbg
1387
1388 #ifdef HFS_SPARSE_DEV
1389 /* Drop our reference on the backing fs (if any). */
1390 if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
1391 struct vnode * tmpvp;
1392
1393 hfsmp->hfs_flags &= ~HFS_HAS_SPARSE_DEVICE;
1394 tmpvp = hfsmp->hfs_backingfs_rootvp;
1395 hfsmp->hfs_backingfs_rootvp = NULLVP;
1396 vnode_rele(tmpvp);
1397 }
1398 #endif /* HFS_SPARSE_DEV */
1399 lck_mtx_destroy(&hfsmp->hfc_mutex, hfs_mutex_group);
1400 FREE(hfsmp, M_HFSMNT);
1401
1402 return (0);
1403
1404 err_exit:
1405 if (started_tr) {
1406 hfs_end_transaction(hfsmp);
1407 }
1408 return retval;
1409 }
1410
1411
1412 /*
1413 * Return the root of a filesystem.
1414 */
1415 static int
1416 hfs_vfs_root(struct mount *mp, struct vnode **vpp, __unused vfs_context_t context)
1417 {
1418 return hfs_vget(VFSTOHFS(mp), (cnid_t)kHFSRootFolderID, vpp, 1);
1419 }
1420
1421
1422 /*
1423 * Do operations associated with quotas
1424 */
1425 static int
1426 hfs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t datap, vfs_context_t context)
1427 {
1428 struct proc *p = vfs_context_proc(context);
1429 int cmd, type, error;
1430
1431 #if !QUOTA
1432 return (ENOTSUP);
1433 #else
1434 if (uid == -1)
1435 uid = vfs_context_ucred(context)->cr_ruid;
1436 cmd = cmds >> SUBCMDSHIFT;
1437
1438 switch (cmd) {
1439 case Q_SYNC:
1440 case Q_QUOTASTAT:
1441 break;
1442 case Q_GETQUOTA:
1443 if (uid == vfs_context_ucred(context)->cr_ruid)
1444 break;
1445 /* fall through */
1446 default:
1447 if ( (error = vfs_context_suser(context)) )
1448 return (error);
1449 }
1450
1451 type = cmds & SUBCMDMASK;
1452 if ((u_int)type >= MAXQUOTAS)
1453 return (EINVAL);
1454 if (vfs_busy(mp, LK_NOWAIT))
1455 return (0);
1456
1457 switch (cmd) {
1458
1459 case Q_QUOTAON:
1460 error = hfs_quotaon(p, mp, type, datap);
1461 break;
1462
1463 case Q_QUOTAOFF:
1464 error = hfs_quotaoff(p, mp, type);
1465 break;
1466
1467 case Q_SETQUOTA:
1468 error = hfs_setquota(mp, uid, type, datap);
1469 break;
1470
1471 case Q_SETUSE:
1472 error = hfs_setuse(mp, uid, type, datap);
1473 break;
1474
1475 case Q_GETQUOTA:
1476 error = hfs_getquota(mp, uid, type, datap);
1477 break;
1478
1479 case Q_SYNC:
1480 error = hfs_qsync(mp);
1481 break;
1482
1483 case Q_QUOTASTAT:
1484 error = hfs_quotastat(mp, type, datap);
1485 break;
1486
1487 default:
1488 error = EINVAL;
1489 break;
1490 }
1491 vfs_unbusy(mp);
1492
1493 return (error);
1494 #endif /* QUOTA */
1495 }
1496
1497 /* Subtype is composite of bits */
1498 #define HFS_SUBTYPE_JOURNALED 0x01
1499 #define HFS_SUBTYPE_CASESENSITIVE 0x02
1500 /* bits 2 - 6 reserved */
1501 #define HFS_SUBTYPE_STANDARDHFS 0x80
1502
1503 /*
1504 * Get file system statistics.
1505 */
1506 static int
1507 hfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, __unused vfs_context_t context)
1508 {
1509 ExtendedVCB *vcb = VFSTOVCB(mp);
1510 struct hfsmount *hfsmp = VFSTOHFS(mp);
1511 u_long freeCNIDs;
1512 uint16_t subtype = 0;
1513
1514 freeCNIDs = (u_long)0xFFFFFFFF - (u_long)vcb->vcbNxtCNID;
1515
1516 sbp->f_bsize = (uint32_t)vcb->blockSize;
1517 sbp->f_iosize = (size_t)(MAX_UPL_TRANSFER * PAGE_SIZE);
1518 sbp->f_blocks = (uint64_t)((unsigned long)vcb->totalBlocks);
1519 sbp->f_bfree = (uint64_t)((unsigned long )hfs_freeblks(hfsmp, 0));
1520 sbp->f_bavail = (uint64_t)((unsigned long )hfs_freeblks(hfsmp, 1));
1521 sbp->f_files = (uint64_t)((unsigned long )(vcb->totalBlocks - 2)); /* max files is constrained by total blocks */
1522 sbp->f_ffree = (uint64_t)((unsigned long )(MIN(freeCNIDs, sbp->f_bavail)));
1523
1524 /*
1525 * Subtypes (flavors) for HFS
1526 * 0: Mac OS Extended
1527 * 1: Mac OS Extended (Journaled)
1528 * 2: Mac OS Extended (Case Sensitive)
1529 * 3: Mac OS Extended (Case Sensitive, Journaled)
1530 * 4 - 127: Reserved
1531 * 128: Mac OS Standard
1532 *
1533 */
1534 if (hfsmp->hfs_flags & HFS_STANDARD) {
1535 subtype = HFS_SUBTYPE_STANDARDHFS;
1536 } else /* HFS Plus */ {
1537 if (hfsmp->jnl)
1538 subtype |= HFS_SUBTYPE_JOURNALED;
1539 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
1540 subtype |= HFS_SUBTYPE_CASESENSITIVE;
1541 }
1542 sbp->f_fssubtype = subtype;
1543
1544 return (0);
1545 }
1546
1547
1548 //
1549 // XXXdbg -- this is a callback to be used by the journal to
1550 // get meta data blocks flushed out to disk.
1551 //
1552 // XXXdbg -- be smarter and don't flush *every* block on each
1553 // call. try to only flush some so we don't wind up
1554 // being too synchronous.
1555 //
1556 __private_extern__
1557 void
1558 hfs_sync_metadata(void *arg)
1559 {
1560 struct mount *mp = (struct mount *)arg;
1561 struct hfsmount *hfsmp;
1562 ExtendedVCB *vcb;
1563 buf_t bp;
1564 int sectorsize, retval;
1565 daddr64_t priIDSector;
1566 hfsmp = VFSTOHFS(mp);
1567 vcb = HFSTOVCB(hfsmp);
1568
1569 // now make sure the super block is flushed
1570 sectorsize = hfsmp->hfs_phys_block_size;
1571 priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / sectorsize) +
1572 HFS_PRI_SECTOR(sectorsize));
1573 retval = (int)buf_meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp);
1574 if ((retval != 0) && (retval != ENXIO)) {
1575 printf("hfs_sync_metadata: can't read volume header at %d! (retval 0x%x)\n",
1576 priIDSector, retval);
1577 }
1578
1579 if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
1580 buf_bwrite(bp);
1581 } else if (bp) {
1582 buf_brelse(bp);
1583 }
1584
1585 // the alternate super block...
1586 // XXXdbg - we probably don't need to do this each and every time.
1587 // hfs_btreeio.c:FlushAlternate() should flag when it was
1588 // written...
1589 if (hfsmp->hfs_alt_id_sector) {
1590 retval = (int)buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector, sectorsize, NOCRED, &bp);
1591 if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
1592 buf_bwrite(bp);
1593 } else if (bp) {
1594 buf_brelse(bp);
1595 }
1596 }
1597 }
1598
1599
1600 struct hfs_sync_cargs {
1601 kauth_cred_t cred;
1602 struct proc *p;
1603 int waitfor;
1604 int error;
1605 };
1606
1607
1608 static int
1609 hfs_sync_callback(struct vnode *vp, void *cargs)
1610 {
1611 struct cnode *cp;
1612 struct hfs_sync_cargs *args;
1613 int error;
1614
1615 args = (struct hfs_sync_cargs *)cargs;
1616
1617 if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0) {
1618 return (VNODE_RETURNED);
1619 }
1620 cp = VTOC(vp);
1621
1622 if ((cp->c_flag & C_MODIFIED) ||
1623 (cp->c_touch_acctime | cp->c_touch_chgtime | cp->c_touch_modtime) ||
1624 vnode_hasdirtyblks(vp)) {
1625 error = hfs_fsync(vp, args->waitfor, 0, args->p);
1626
1627 if (error)
1628 args->error = error;
1629 }
1630 hfs_unlock(cp);
1631 return (VNODE_RETURNED);
1632 }
1633
1634
1635
1636 /*
1637 * Go through the disk queues to initiate sandbagged IO;
1638 * go through the inodes to write those that have been modified;
1639 * initiate the writing of the super block if it has been modified.
1640 *
1641 * Note: we are always called with the filesystem marked `MPBUSY'.
1642 */
1643 static int
1644 hfs_sync(struct mount *mp, int waitfor, vfs_context_t context)
1645 {
1646 struct proc *p = vfs_context_proc(context);
1647 struct cnode *cp;
1648 struct hfsmount *hfsmp;
1649 ExtendedVCB *vcb;
1650 struct vnode *meta_vp[4];
1651 int i;
1652 int error, allerror = 0;
1653 struct hfs_sync_cargs args;
1654
1655 /*
1656 * During MNT_UPDATE hfs_changefs might be manipulating
1657 * vnodes so back off
1658 */
1659 if (((uint32_t)vfs_flags(mp)) & MNT_UPDATE) /* XXX MNT_UPDATE may not be visible here */
1660 return (0);
1661
1662 hfsmp = VFSTOHFS(mp);
1663 if (hfsmp->hfs_flags & HFS_READ_ONLY)
1664 return (EROFS);
1665
1666 /* skip over frozen volumes */
1667 if (!lck_rw_try_lock_shared(&hfsmp->hfs_insync))
1668 return 0;
1669
1670 args.cred = vfs_context_proc(context);
1671 args.waitfor = waitfor;
1672 args.p = p;
1673 args.error = 0;
1674 /*
1675 * hfs_sync_callback will be called for each vnode
1676 * hung off of this mount point... the vnode will be
1677 * properly referenced and unreferenced around the callback
1678 */
1679 vnode_iterate(mp, 0, hfs_sync_callback, (void *)&args);
1680
1681 if (args.error)
1682 allerror = args.error;
1683
1684 vcb = HFSTOVCB(hfsmp);
1685
1686 meta_vp[0] = vcb->extentsRefNum;
1687 meta_vp[1] = vcb->catalogRefNum;
1688 meta_vp[2] = vcb->allocationsRefNum; /* This is NULL for standard HFS */
1689 meta_vp[3] = hfsmp->hfs_attribute_vp; /* Optional file */
1690
1691 /* Now sync our three metadata files */
1692 for (i = 0; i < 4; ++i) {
1693 struct vnode *btvp;
1694
1695 btvp = meta_vp[i];;
1696 if ((btvp==0) || (vnode_mount(btvp) != mp))
1697 continue;
1698
1699 /* XXX use hfs_systemfile_lock instead ? */
1700 (void) hfs_lock(VTOC(btvp), HFS_EXCLUSIVE_LOCK);
1701 cp = VTOC(btvp);
1702
1703 if (((cp->c_flag & C_MODIFIED) == 0) &&
1704 (cp->c_touch_acctime == 0) &&
1705 (cp->c_touch_chgtime == 0) &&
1706 (cp->c_touch_modtime == 0) &&
1707 vnode_hasdirtyblks(btvp) == 0) {
1708 hfs_unlock(VTOC(btvp));
1709 continue;
1710 }
1711 error = vnode_get(btvp);
1712 if (error) {
1713 hfs_unlock(VTOC(btvp));
1714 continue;
1715 }
1716 if ((error = hfs_fsync(btvp, waitfor, 0, p)))
1717 allerror = error;
1718
1719 hfs_unlock(cp);
1720 vnode_put(btvp);
1721 };
1722
1723 /*
1724 * Force stale file system control information to be flushed.
1725 */
1726 if (vcb->vcbSigWord == kHFSSigWord) {
1727 if ((error = VNOP_FSYNC(hfsmp->hfs_devvp, waitfor, context))) {
1728 allerror = error;
1729 }
1730 }
1731 #if QUOTA
1732 hfs_qsync(mp);
1733 #endif /* QUOTA */
1734
1735 hfs_hotfilesync(hfsmp, p);
1736 /*
1737 * Write back modified superblock.
1738 */
1739
1740 if (IsVCBDirty(vcb)) {
1741 error = hfs_flushvolumeheader(hfsmp, waitfor, 0);
1742 if (error)
1743 allerror = error;
1744 }
1745
1746 if (hfsmp->jnl) {
1747 journal_flush(hfsmp->jnl);
1748 }
1749
1750 lck_rw_unlock_shared(&hfsmp->hfs_insync);
1751 return (allerror);
1752 }
1753
1754
1755 /*
1756 * File handle to vnode
1757 *
1758 * Have to be really careful about stale file handles:
1759 * - check that the cnode id is valid
1760 * - call hfs_vget() to get the locked cnode
1761 * - check for an unallocated cnode (i_mode == 0)
1762 * - check that the given client host has export rights and return
1763 * those rights via. exflagsp and credanonp
1764 */
1765 static int
1766 hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp, vfs_context_t context)
1767 {
1768 struct hfsfid *hfsfhp;
1769 struct vnode *nvp;
1770 int result;
1771
1772 *vpp = NULL;
1773 hfsfhp = (struct hfsfid *)fhp;
1774
1775 if (fhlen < sizeof(struct hfsfid))
1776 return (EINVAL);
1777
1778 result = hfs_vget(VFSTOHFS(mp), ntohl(hfsfhp->hfsfid_cnid), &nvp, 0);
1779 if (result) {
1780 if (result == ENOENT)
1781 result = ESTALE;
1782 return result;
1783 }
1784
1785 /* The createtime can be changed by hfs_setattr or hfs_setattrlist.
1786 * For NFS, we are assuming that only if the createtime was moved
1787 * forward would it mean the fileID got reused in that session by
1788 * wrapping. We don't have a volume ID or other unique identifier to
1789 * to use here for a generation ID across reboots, crashes where
1790 * metadata noting lastFileID didn't make it to disk but client has
1791 * it, or volume erasures where fileIDs start over again. Lastly,
1792 * with HFS allowing "wraps" of fileIDs now, this becomes more
1793 * error prone. Future, would be change the "wrap bit" to a unique
1794 * wrap number and use that for generation number. For now do this.
1795 */
1796 if ((ntohl(hfsfhp->hfsfid_gen) < VTOC(nvp)->c_itime)) {
1797 hfs_unlock(VTOC(nvp));
1798 vnode_put(nvp);
1799 return (ESTALE);
1800 }
1801 *vpp = nvp;
1802
1803 hfs_unlock(VTOC(nvp));
1804 return (0);
1805 }
1806
1807
1808 /*
1809 * Vnode pointer to File handle
1810 */
1811 /* ARGSUSED */
1812 static int
1813 hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context)
1814 {
1815 struct cnode *cp;
1816 struct hfsfid *hfsfhp;
1817
1818 if (ISHFS(VTOVCB(vp)))
1819 return (ENOTSUP); /* hfs standard is not exportable */
1820
1821 if (*fhlenp < (int)sizeof(struct hfsfid))
1822 return (EOVERFLOW);
1823
1824 cp = VTOC(vp);
1825 hfsfhp = (struct hfsfid *)fhp;
1826 hfsfhp->hfsfid_cnid = htonl(cp->c_fileid);
1827 hfsfhp->hfsfid_gen = htonl(cp->c_itime);
1828 *fhlenp = sizeof(struct hfsfid);
1829
1830 return (0);
1831 }
1832
1833
1834 /*
1835 * Initial HFS filesystems, done only once.
1836 */
1837 static int
1838 hfs_init(__unused struct vfsconf *vfsp)
1839 {
1840 static int done = 0;
1841
1842 if (done)
1843 return (0);
1844 done = 1;
1845 hfs_chashinit();
1846 hfs_converterinit();
1847 #if QUOTA
1848 dqinit();
1849 #endif /* QUOTA */
1850
1851 BTReserveSetup();
1852
1853
1854 hfs_lock_attr = lck_attr_alloc_init();
1855 hfs_group_attr = lck_grp_attr_alloc_init();
1856 hfs_mutex_group = lck_grp_alloc_init("hfs-mutex", hfs_group_attr);
1857 hfs_rwlock_group = lck_grp_alloc_init("hfs-rwlock", hfs_group_attr);
1858
1859
1860 return (0);
1861 }
1862
1863 static int
1864 hfs_getmountpoint(vp, hfsmpp)
1865 struct vnode *vp;
1866 struct hfsmount **hfsmpp;
1867 {
1868 struct hfsmount * hfsmp;
1869 char fstypename[MFSNAMELEN];
1870
1871 if (vp == NULL)
1872 return (EINVAL);
1873
1874 if (!vnode_isvroot(vp))
1875 return (EINVAL);
1876
1877 vnode_vfsname(vp, fstypename);
1878 if (strcmp(fstypename, "hfs") != 0)
1879 return (EINVAL);
1880
1881 hfsmp = VTOHFS(vp);
1882
1883 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
1884 return (EINVAL);
1885
1886 *hfsmpp = hfsmp;
1887
1888 return (0);
1889 }
1890
1891 // XXXdbg
1892 #include <sys/filedesc.h>
1893
1894 /*
1895 * HFS filesystem related variables.
1896 */
1897 static int
1898 hfs_sysctl(int *name, __unused u_int namelen, user_addr_t oldp, size_t *oldlenp,
1899 user_addr_t newp, size_t newlen, vfs_context_t context)
1900 {
1901 struct proc *p = vfs_context_proc(context);
1902 int error;
1903 struct hfsmount *hfsmp;
1904
1905 /* all sysctl names at this level are terminal */
1906
1907 if (name[0] == HFS_ENCODINGBIAS) {
1908 u_int32_t bias;
1909
1910 bias = hfs_getencodingbias();
1911 error = sysctl_int(oldp, oldlenp, newp, newlen, &bias);
1912 if (error == 0 && newp)
1913 hfs_setencodingbias(bias);
1914 return (error);
1915
1916 } else if (name[0] == HFS_EXTEND_FS) {
1917 u_int64_t newsize;
1918 vnode_t vp = p->p_fd->fd_cdir;
1919
1920 if (newp == USER_ADDR_NULL || vp == NULL)
1921 return (EINVAL);
1922 if ((error = hfs_getmountpoint(vp, &hfsmp)))
1923 return (error);
1924 error = sysctl_quad(oldp, oldlenp, newp, newlen, &newsize);
1925 if (error)
1926 return (error);
1927
1928 error = hfs_extendfs(hfsmp, newsize, context);
1929 return (error);
1930
1931 } else if (name[0] == HFS_ENCODINGHINT) {
1932 size_t bufsize;
1933 size_t bytes;
1934 u_int32_t hint;
1935 u_int16_t *unicode_name;
1936 char *filename;
1937
1938 bufsize = MAX(newlen * 3, MAXPATHLEN);
1939 MALLOC(filename, char *, newlen, M_TEMP, M_WAITOK);
1940 MALLOC(unicode_name, u_int16_t *, bufsize, M_TEMP, M_WAITOK);
1941
1942 error = copyin(newp, (caddr_t)filename, newlen);
1943 if (error == 0) {
1944 error = utf8_decodestr(filename, newlen - 1, unicode_name,
1945 &bytes, bufsize, 0, UTF_DECOMPOSED);
1946 if (error == 0) {
1947 hint = hfs_pickencoding(unicode_name, bytes / 2);
1948 error = sysctl_int(oldp, oldlenp, USER_ADDR_NULL, 0, &hint);
1949 }
1950 }
1951 FREE(unicode_name, M_TEMP);
1952 FREE(filename, M_TEMP);
1953 return (error);
1954
1955 } else if (name[0] == HFS_ENABLE_JOURNALING) {
1956 // make the file system journaled...
1957 struct vnode *vp = p->p_fd->fd_cdir, *jvp;
1958 ExtendedVCB *vcb;
1959 struct cat_attr jnl_attr, jinfo_attr;
1960 struct cat_fork jnl_fork, jinfo_fork;
1961 void *jnl = NULL;
1962 int lockflags;
1963
1964 /* Only root can enable journaling */
1965 if (!is_suser()) {
1966 return (EPERM);
1967 }
1968 if (vp == NULL)
1969 return EINVAL;
1970
1971 hfsmp = VTOHFS(vp);
1972 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
1973 return EROFS;
1974 }
1975 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
1976 printf("hfs: can't make a plain hfs volume journaled.\n");
1977 return EINVAL;
1978 }
1979
1980 if (hfsmp->jnl) {
1981 printf("hfs: volume @ mp 0x%x is already journaled!\n", vnode_mount(vp));
1982 return EAGAIN;
1983 }
1984
1985 vcb = HFSTOVCB(hfsmp);
1986 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
1987 if (BTHasContiguousNodes(VTOF(vcb->catalogRefNum)) == 0 ||
1988 BTHasContiguousNodes(VTOF(vcb->extentsRefNum)) == 0) {
1989
1990 printf("hfs: volume has a btree w/non-contiguous nodes. can not enable journaling.\n");
1991 hfs_systemfile_unlock(hfsmp, lockflags);
1992 return EINVAL;
1993 }
1994 hfs_systemfile_unlock(hfsmp, lockflags);
1995
1996 // make sure these both exist!
1997 if ( GetFileInfo(vcb, kHFSRootFolderID, ".journal_info_block", &jinfo_attr, &jinfo_fork) == 0
1998 || GetFileInfo(vcb, kHFSRootFolderID, ".journal", &jnl_attr, &jnl_fork) == 0) {
1999
2000 return EINVAL;
2001 }
2002
2003 hfs_sync(hfsmp->hfs_mp, MNT_WAIT, context);
2004
2005 printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
2006 (off_t)name[2], (off_t)name[3]);
2007
2008 jvp = hfsmp->hfs_devvp;
2009 jnl = journal_create(jvp,
2010 (off_t)name[2] * (off_t)HFSTOVCB(hfsmp)->blockSize
2011 + HFSTOVCB(hfsmp)->hfsPlusIOPosOffset,
2012 (off_t)((unsigned)name[3]),
2013 hfsmp->hfs_devvp,
2014 hfsmp->hfs_phys_block_size,
2015 0,
2016 0,
2017 hfs_sync_metadata, hfsmp->hfs_mp);
2018
2019 if (jnl == NULL) {
2020 printf("hfs: FAILED to create the journal!\n");
2021 if (jvp && jvp != hfsmp->hfs_devvp) {
2022 VNOP_CLOSE(jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, context);
2023 }
2024 jvp = NULL;
2025
2026 return EINVAL;
2027 }
2028
2029 hfs_global_exclusive_lock_acquire(hfsmp);
2030
2031 /*
2032 * Flush all dirty metadata buffers.
2033 */
2034 buf_flushdirtyblks(hfsmp->hfs_devvp, MNT_WAIT, 0, "hfs_sysctl");
2035 buf_flushdirtyblks(hfsmp->hfs_extents_vp, MNT_WAIT, 0, "hfs_sysctl");
2036 buf_flushdirtyblks(hfsmp->hfs_catalog_vp, MNT_WAIT, 0, "hfs_sysctl");
2037 buf_flushdirtyblks(hfsmp->hfs_allocation_vp, MNT_WAIT, 0, "hfs_sysctl");
2038 if (hfsmp->hfs_attribute_vp)
2039 buf_flushdirtyblks(hfsmp->hfs_attribute_vp, MNT_WAIT, 0, "hfs_sysctl");
2040
2041 HFSTOVCB(hfsmp)->vcbJinfoBlock = name[1];
2042 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeJournaledMask;
2043 hfsmp->jvp = jvp;
2044 hfsmp->jnl = jnl;
2045
2046 // save this off for the hack-y check in hfs_remove()
2047 hfsmp->jnl_start = (u_int32_t)name[2];
2048 hfsmp->jnl_size = (off_t)((unsigned)name[3]);
2049 hfsmp->hfs_jnlinfoblkid = jinfo_attr.ca_fileid;
2050 hfsmp->hfs_jnlfileid = jnl_attr.ca_fileid;
2051
2052 vfs_setflags(hfsmp->hfs_mp, (uint64_t)((unsigned int)MNT_JOURNALED));
2053
2054 hfs_global_exclusive_lock_release(hfsmp);
2055 hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1);
2056
2057 return 0;
2058 } else if (name[0] == HFS_DISABLE_JOURNALING) {
2059 // clear the journaling bit
2060 struct vnode *vp = p->p_fd->fd_cdir;
2061
2062 /* Only root can disable journaling */
2063 if (!is_suser()) {
2064 return (EPERM);
2065 }
2066 if (vp == NULL)
2067 return EINVAL;
2068
2069 hfsmp = VTOHFS(vp);
2070
2071 printf("hfs: disabling journaling for mount @ 0x%x\n", vnode_mount(vp));
2072
2073 hfs_global_exclusive_lock_acquire(hfsmp);
2074
2075 // Lights out for you buddy!
2076 journal_close(hfsmp->jnl);
2077 hfsmp->jnl = NULL;
2078
2079 if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
2080 VNOP_CLOSE(hfsmp->jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, context);
2081 }
2082 hfsmp->jvp = NULL;
2083 vfs_clearflags(hfsmp->hfs_mp, (uint64_t)((unsigned int)MNT_JOURNALED));
2084 hfsmp->jnl_start = 0;
2085 hfsmp->hfs_jnlinfoblkid = 0;
2086 hfsmp->hfs_jnlfileid = 0;
2087
2088 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeJournaledMask;
2089
2090 hfs_global_exclusive_lock_release(hfsmp);
2091 hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1);
2092
2093 return 0;
2094 } else if (name[0] == HFS_GET_JOURNAL_INFO) {
2095 struct vnode *vp = p->p_fd->fd_cdir;
2096 off_t jnl_start, jnl_size;
2097
2098 if (vp == NULL)
2099 return EINVAL;
2100
2101 hfsmp = VTOHFS(vp);
2102 if (hfsmp->jnl == NULL) {
2103 jnl_start = 0;
2104 jnl_size = 0;
2105 } else {
2106 jnl_start = (off_t)(hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset;
2107 jnl_size = (off_t)hfsmp->jnl_size;
2108 }
2109
2110 if ((error = copyout((caddr_t)&jnl_start, CAST_USER_ADDR_T(name[1]), sizeof(off_t))) != 0) {
2111 return error;
2112 }
2113 if ((error = copyout((caddr_t)&jnl_size, CAST_USER_ADDR_T(name[2]), sizeof(off_t))) != 0) {
2114 return error;
2115 }
2116
2117 return 0;
2118 } else if (name[0] == HFS_SET_PKG_EXTENSIONS) {
2119
2120 return set_package_extensions_table((void *)name[1], name[2], name[3]);
2121
2122 } else if (name[0] == VFS_CTL_QUERY) {
2123 struct sysctl_req *req;
2124 struct vfsidctl vc;
2125 struct user_vfsidctl user_vc;
2126 struct mount *mp;
2127 struct vfsquery vq;
2128 boolean_t is_64_bit;
2129
2130 is_64_bit = proc_is64bit(p);
2131 req = CAST_DOWN(struct sysctl_req *, oldp); /* we're new style vfs sysctl. */
2132
2133 if (is_64_bit) {
2134 error = SYSCTL_IN(req, &user_vc, sizeof(user_vc));
2135 if (error) return (error);
2136
2137 mp = vfs_getvfs(&user_vc.vc_fsid);
2138 }
2139 else {
2140 error = SYSCTL_IN(req, &vc, sizeof(vc));
2141 if (error) return (error);
2142
2143 mp = vfs_getvfs(&vc.vc_fsid);
2144 }
2145 if (mp == NULL) return (ENOENT);
2146
2147 hfsmp = VFSTOHFS(mp);
2148 bzero(&vq, sizeof(vq));
2149 vq.vq_flags = hfsmp->hfs_notification_conditions;
2150 return SYSCTL_OUT(req, &vq, sizeof(vq));;
2151 };
2152
2153 return (ENOTSUP);
2154 }
2155
2156
2157 static int
2158 hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, __unused vfs_context_t context)
2159 {
2160 return hfs_vget(VFSTOHFS(mp), (cnid_t)ino, vpp, 1);
2161 }
2162
2163
2164 /*
2165 * Look up an HFS object by ID.
2166 *
2167 * The object is returned with an iocount reference and the cnode locked.
2168 *
2169 * If the object is a file then it will represent the data fork.
2170 */
2171 __private_extern__
2172 int
2173 hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
2174 {
2175 struct vnode *vp = NULL;
2176 struct cat_desc cndesc;
2177 struct cat_attr cnattr;
2178 struct cat_fork cnfork;
2179 struct componentname cn;
2180 u_int32_t linkref = 0;
2181 int error;
2182
2183 /* Check for cnids that should't be exported. */
2184 if ((cnid < kHFSFirstUserCatalogNodeID)
2185 && (cnid != kHFSRootFolderID && cnid != kHFSRootParentID))
2186 return (ENOENT);
2187
2188 /* Don't export HFS Private Data dir. */
2189 if (cnid == hfsmp->hfs_privdir_desc.cd_cnid)
2190 return (ENOENT);
2191
2192 /*
2193 * Check the hash first
2194 */
2195 vp = hfs_chash_getvnode(hfsmp->hfs_raw_dev, cnid, 0, skiplock);
2196 if (vp) {
2197 *vpp = vp;
2198 return(0);
2199 }
2200
2201 bzero(&cndesc, sizeof(cndesc));
2202 bzero(&cnattr, sizeof(cnattr));
2203 bzero(&cnfork, sizeof(cnfork));
2204
2205 /*
2206 * Not in hash, lookup in catalog
2207 */
2208 if (cnid == kHFSRootParentID) {
2209 static char hfs_rootname[] = "/";
2210
2211 cndesc.cd_nameptr = &hfs_rootname[0];
2212 cndesc.cd_namelen = 1;
2213 cndesc.cd_parentcnid = kHFSRootParentID;
2214 cndesc.cd_cnid = kHFSRootFolderID;
2215 cndesc.cd_flags = CD_ISDIR;
2216
2217 cnattr.ca_fileid = kHFSRootFolderID;
2218 cnattr.ca_nlink = 2;
2219 cnattr.ca_entries = 1;
2220 cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
2221 } else {
2222 int lockflags;
2223
2224 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
2225 error = cat_idlookup(hfsmp, cnid, &cndesc, &cnattr, &cnfork);
2226 hfs_systemfile_unlock(hfsmp, lockflags);
2227
2228 if (error) {
2229 *vpp = NULL;
2230 return (error);
2231 }
2232
2233 /*
2234 * If we just looked up a raw hardlink inode,
2235 * then finish initializing it.
2236 */
2237 if ((cndesc.cd_parentcnid == hfsmp->hfs_privdir_desc.cd_cnid) &&
2238 (bcmp(cndesc.cd_nameptr, HFS_INODE_PREFIX, HFS_INODE_PREFIX_LEN) == 0)) {
2239 linkref = strtoul((const char*)&cndesc.cd_nameptr[HFS_INODE_PREFIX_LEN], NULL, 10);
2240 cnattr.ca_rdev = linkref;
2241 }
2242 }
2243
2244 /*
2245 * Supply hfs_getnewvnode with a component name.
2246 */
2247 MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
2248 cn.cn_nameiop = LOOKUP;
2249 cn.cn_flags = ISLASTCN | HASBUF;
2250 cn.cn_context = NULL;
2251 cn.cn_pnlen = MAXPATHLEN;
2252 cn.cn_nameptr = cn.cn_pnbuf;
2253 cn.cn_namelen = cndesc.cd_namelen;
2254 cn.cn_hash = 0;
2255 cn.cn_consume = 0;
2256 bcopy(cndesc.cd_nameptr, cn.cn_nameptr, cndesc.cd_namelen + 1);
2257
2258 /* XXX should we supply the parent as well... ? */
2259 error = hfs_getnewvnode(hfsmp, NULLVP, &cn, &cndesc, 0, &cnattr, &cnfork, &vp);
2260 if (error == 0 && linkref != 0) {
2261 VTOC(vp)->c_flag |= C_HARDLINK;
2262 }
2263
2264 FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
2265
2266 cat_releasedesc(&cndesc);
2267 *vpp = vp;
2268 if (vp && skiplock)
2269 hfs_unlock(VTOC(vp));
2270 return (error);
2271 }
2272
2273
2274 /*
2275 * Flush out all the files in a filesystem.
2276 */
2277 static int
2278 hfs_flushfiles(struct mount *mp, int flags, struct proc *p)
2279 {
2280 struct hfsmount *hfsmp;
2281 struct vnode *skipvp = NULLVP;
2282 int quotafilecnt;
2283 int i;
2284 int error;
2285
2286 hfsmp = VFSTOHFS(mp);
2287
2288 #if QUOTA
2289 /*
2290 * The open quota files have an indirect reference on
2291 * the root directory vnode. We must account for this
2292 * extra reference when doing the intial vflush.
2293 */
2294 quotafilecnt = 0;
2295 if (((unsigned int)vfs_flags(mp)) & MNT_QUOTA) {
2296
2297 /* Find out how many quota files we have open. */
2298 for (i = 0; i < MAXQUOTAS; i++) {
2299 if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP)
2300 ++quotafilecnt;
2301 }
2302
2303 /* Obtain the root vnode so we can skip over it. */
2304 skipvp = hfs_chash_getvnode(hfsmp->hfs_raw_dev, kHFSRootFolderID, 0, 0);
2305 }
2306 #endif /* QUOTA */
2307
2308 error = vflush(mp, skipvp, SKIPSYSTEM | SKIPSWAP | flags);
2309 if (error != 0)
2310 return(error);
2311
2312 error = vflush(mp, skipvp, SKIPSYSTEM | flags);
2313
2314 #if QUOTA
2315 if (((unsigned int)vfs_flags(mp)) & MNT_QUOTA) {
2316 if (skipvp) {
2317 /*
2318 * See if there are additional references on the
2319 * root vp besides the ones obtained from the open
2320 * quota files and the hfs_chash_getvnode call above.
2321 */
2322 if ((error == 0) &&
2323 (vnode_isinuse(skipvp, quotafilecnt))) {
2324 error = EBUSY; /* root directory is still open */
2325 }
2326 hfs_unlock(VTOC(skipvp));
2327 vnode_put(skipvp);
2328 }
2329 if (error && (flags & FORCECLOSE) == 0)
2330 return (error);
2331
2332 for (i = 0; i < MAXQUOTAS; i++) {
2333 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
2334 continue;
2335 hfs_quotaoff(p, mp, i);
2336 }
2337 error = vflush(mp, NULLVP, SKIPSYSTEM | flags);
2338 }
2339 #endif /* QUOTA */
2340
2341 return (error);
2342 }
2343
2344 /*
2345 * Update volume encoding bitmap (HFS Plus only)
2346 */
2347 __private_extern__
2348 void
2349 hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding)
2350 {
2351 #define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */
2352 #define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */
2353
2354 UInt32 index;
2355
2356 switch (encoding) {
2357 case kTextEncodingMacUkrainian:
2358 index = kIndexMacUkrainian;
2359 break;
2360 case kTextEncodingMacFarsi:
2361 index = kIndexMacFarsi;
2362 break;
2363 default:
2364 index = encoding;
2365 break;
2366 }
2367
2368 if (index < 64) {
2369 HFS_MOUNT_LOCK(hfsmp, TRUE)
2370 hfsmp->encodingsBitmap |= (u_int64_t)(1ULL << index);
2371 hfsmp->vcbFlags |= 0xFF00;
2372 HFS_MOUNT_UNLOCK(hfsmp, TRUE);
2373 }
2374 }
2375
2376 /*
2377 * Update volume stats
2378 *
2379 * On journal volumes this will cause a volume header flush
2380 */
2381 __private_extern__
2382 int
2383 hfs_volupdate(struct hfsmount *hfsmp, enum volop op, int inroot)
2384 {
2385 struct timeval tv;
2386
2387 microtime(&tv);
2388
2389 lck_mtx_lock(&hfsmp->hfs_mutex);
2390
2391 hfsmp->vcbFlags |= 0xFF00;
2392 hfsmp->hfs_mtime = tv.tv_sec;
2393
2394 switch (op) {
2395 case VOL_UPDATE:
2396 break;
2397 case VOL_MKDIR:
2398 if (hfsmp->hfs_dircount != 0xFFFFFFFF)
2399 ++hfsmp->hfs_dircount;
2400 if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
2401 ++hfsmp->vcbNmRtDirs;
2402 break;
2403 case VOL_RMDIR:
2404 if (hfsmp->hfs_dircount != 0)
2405 --hfsmp->hfs_dircount;
2406 if (inroot && hfsmp->vcbNmRtDirs != 0xFFFF)
2407 --hfsmp->vcbNmRtDirs;
2408 break;
2409 case VOL_MKFILE:
2410 if (hfsmp->hfs_filecount != 0xFFFFFFFF)
2411 ++hfsmp->hfs_filecount;
2412 if (inroot && hfsmp->vcbNmFls != 0xFFFF)
2413 ++hfsmp->vcbNmFls;
2414 break;
2415 case VOL_RMFILE:
2416 if (hfsmp->hfs_filecount != 0)
2417 --hfsmp->hfs_filecount;
2418 if (inroot && hfsmp->vcbNmFls != 0xFFFF)
2419 --hfsmp->vcbNmFls;
2420 break;
2421 }
2422
2423 lck_mtx_unlock(&hfsmp->hfs_mutex);
2424
2425 if (hfsmp->jnl) {
2426 hfs_flushvolumeheader(hfsmp, 0, 0);
2427 }
2428
2429 return (0);
2430 }
2431
2432
2433 static int
2434 hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush)
2435 {
2436 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
2437 struct filefork *fp;
2438 HFSMasterDirectoryBlock *mdb;
2439 struct buf *bp = NULL;
2440 int retval;
2441 int sectorsize;
2442 ByteCount namelen;
2443
2444 sectorsize = hfsmp->hfs_phys_block_size;
2445 retval = (int)buf_bread(hfsmp->hfs_devvp, (daddr64_t)HFS_PRI_SECTOR(sectorsize), sectorsize, NOCRED, &bp);
2446 if (retval) {
2447 if (bp)
2448 buf_brelse(bp);
2449 return retval;
2450 }
2451
2452 lck_mtx_lock(&hfsmp->hfs_mutex);
2453
2454 mdb = (HFSMasterDirectoryBlock *)(buf_dataptr(bp) + HFS_PRI_OFFSET(sectorsize));
2455
2456 mdb->drCrDate = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbCrDate)));
2457 mdb->drLsMod = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbLsMod)));
2458 mdb->drAtrb = SWAP_BE16 (vcb->vcbAtrb);
2459 mdb->drNmFls = SWAP_BE16 (vcb->vcbNmFls);
2460 mdb->drAllocPtr = SWAP_BE16 (vcb->nextAllocation);
2461 mdb->drClpSiz = SWAP_BE32 (vcb->vcbClpSiz);
2462 mdb->drNxtCNID = SWAP_BE32 (vcb->vcbNxtCNID);
2463 mdb->drFreeBks = SWAP_BE16 (vcb->freeBlocks);
2464
2465 namelen = strlen(vcb->vcbVN);
2466 retval = utf8_to_hfs(vcb, namelen, vcb->vcbVN, mdb->drVN);
2467 /* Retry with MacRoman in case that's how it was exported. */
2468 if (retval)
2469 retval = utf8_to_mac_roman(namelen, vcb->vcbVN, mdb->drVN);
2470
2471 mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbVolBkUp)));
2472 mdb->drWrCnt = SWAP_BE32 (vcb->vcbWrCnt);
2473 mdb->drNmRtDirs = SWAP_BE16 (vcb->vcbNmRtDirs);
2474 mdb->drFilCnt = SWAP_BE32 (vcb->vcbFilCnt);
2475 mdb->drDirCnt = SWAP_BE32 (vcb->vcbDirCnt);
2476
2477 bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo));
2478
2479 fp = VTOF(vcb->extentsRefNum);
2480 mdb->drXTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
2481 mdb->drXTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
2482 mdb->drXTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
2483 mdb->drXTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
2484 mdb->drXTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
2485 mdb->drXTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
2486 mdb->drXTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
2487 mdb->drXTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
2488 FTOC(fp)->c_flag &= ~C_MODIFIED;
2489
2490 fp = VTOF(vcb->catalogRefNum);
2491 mdb->drCTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
2492 mdb->drCTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
2493 mdb->drCTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
2494 mdb->drCTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
2495 mdb->drCTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
2496 mdb->drCTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
2497 mdb->drCTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
2498 mdb->drCTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
2499 FTOC(fp)->c_flag &= ~C_MODIFIED;
2500
2501 MarkVCBClean( vcb );
2502
2503 lck_mtx_unlock(&hfsmp->hfs_mutex);
2504
2505 /* If requested, flush out the alternate MDB */
2506 if (altflush) {
2507 struct buf *alt_bp = NULL;
2508
2509 if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector, sectorsize, NOCRED, &alt_bp) == 0) {
2510 bcopy(mdb, (char *)buf_dataptr(alt_bp) + HFS_ALT_OFFSET(sectorsize), kMDBSize);
2511
2512 (void) VNOP_BWRITE(alt_bp);
2513 } else if (alt_bp)
2514 buf_brelse(alt_bp);
2515 }
2516
2517 if (waitfor != MNT_WAIT)
2518 buf_bawrite(bp);
2519 else
2520 retval = VNOP_BWRITE(bp);
2521
2522 return (retval);
2523 }
2524
2525 /*
2526 * Flush any dirty in-memory mount data to the on-disk
2527 * volume header.
2528 *
2529 * Note: the on-disk volume signature is intentionally
2530 * not flushed since the on-disk "H+" and "HX" signatures
2531 * are always stored in-memory as "H+".
2532 */
2533 __private_extern__
2534 int
2535 hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush)
2536 {
2537 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
2538 struct filefork *fp;
2539 HFSPlusVolumeHeader *volumeHeader;
2540 int retval;
2541 struct buf *bp;
2542 int i;
2543 int sectorsize;
2544 daddr64_t priIDSector;
2545 int critical = 0;
2546 u_int16_t signature;
2547 u_int16_t hfsversion;
2548
2549 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
2550 return(0);
2551 }
2552 if (vcb->vcbSigWord == kHFSSigWord)
2553 return hfs_flushMDB(hfsmp, waitfor, altflush);
2554
2555 if (altflush)
2556 critical = 1;
2557 sectorsize = hfsmp->hfs_phys_block_size;
2558 priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / sectorsize) +
2559 HFS_PRI_SECTOR(sectorsize));
2560
2561 if (hfs_start_transaction(hfsmp) != 0) {
2562 return EINVAL;
2563 }
2564
2565 retval = (int)buf_meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp);
2566 if (retval) {
2567 if (bp)
2568 buf_brelse(bp);
2569
2570 hfs_end_transaction(hfsmp);
2571
2572 printf("HFS: err %d reading VH blk (%s)\n", retval, vcb->vcbVN);
2573 return (retval);
2574 }
2575
2576 if (hfsmp->jnl) {
2577 journal_modify_block_start(hfsmp->jnl, bp);
2578 }
2579
2580 volumeHeader = (HFSPlusVolumeHeader *)((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(sectorsize));
2581
2582 /*
2583 * Sanity check what we just read.
2584 */
2585 signature = SWAP_BE16 (volumeHeader->signature);
2586 hfsversion = SWAP_BE16 (volumeHeader->version);
2587 if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) ||
2588 (hfsversion < kHFSPlusVersion) || (hfsversion > 100) ||
2589 (SWAP_BE32 (volumeHeader->blockSize) != vcb->blockSize)) {
2590 #if 1
2591 panic("HFS: corrupt VH on %s, sig 0x%04x, ver %d, blksize %d",
2592 vcb->vcbVN, signature, hfsversion,
2593 SWAP_BE32 (volumeHeader->blockSize));
2594 #endif
2595 printf("HFS: corrupt VH blk (%s)\n", vcb->vcbVN);
2596 buf_brelse(bp);
2597 return (EIO);
2598 }
2599
2600 /*
2601 * For embedded HFS+ volumes, update create date if it changed
2602 * (ie from a setattrlist call)
2603 */
2604 if ((vcb->hfsPlusIOPosOffset != 0) &&
2605 (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate)) {
2606 struct buf *bp2;
2607 HFSMasterDirectoryBlock *mdb;
2608
2609 retval = (int)buf_meta_bread(hfsmp->hfs_devvp, (daddr64_t)HFS_PRI_SECTOR(sectorsize),
2610 sectorsize, NOCRED, &bp2);
2611 if (retval) {
2612 if (bp2)
2613 buf_brelse(bp2);
2614 retval = 0;
2615 } else {
2616 mdb = (HFSMasterDirectoryBlock *)(buf_dataptr(bp2) +
2617 HFS_PRI_OFFSET(sectorsize));
2618
2619 if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate )
2620 {
2621 if (hfsmp->jnl) {
2622 journal_modify_block_start(hfsmp->jnl, bp2);
2623 }
2624
2625 mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate); /* pick up the new create date */
2626
2627 if (hfsmp->jnl) {
2628 journal_modify_block_end(hfsmp->jnl, bp2);
2629 } else {
2630 (void) VNOP_BWRITE(bp2); /* write out the changes */
2631 }
2632 }
2633 else
2634 {
2635 buf_brelse(bp2); /* just release it */
2636 }
2637 }
2638 }
2639
2640 if (1 /* hfsmp->jnl == 0 */) {
2641 lck_mtx_lock(&hfsmp->hfs_mutex);
2642 }
2643
2644 /* Note: only update the lower 16 bits worth of attributes */
2645 volumeHeader->attributes = SWAP_BE32 (vcb->vcbAtrb);
2646 volumeHeader->journalInfoBlock = SWAP_BE32 (vcb->vcbJinfoBlock);
2647 if (hfsmp->jnl) {
2648 volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSJMountVersion);
2649 } else {
2650 volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion);
2651 }
2652 volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate); /* volume create date is in local time */
2653 volumeHeader->modifyDate = SWAP_BE32 (to_hfs_time(vcb->vcbLsMod));
2654 volumeHeader->backupDate = SWAP_BE32 (to_hfs_time(vcb->vcbVolBkUp));
2655 volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt);
2656 volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt);
2657 volumeHeader->totalBlocks = SWAP_BE32 (vcb->totalBlocks);
2658 volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks);
2659 volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation);
2660 volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
2661 volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
2662 volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID);
2663 volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt);
2664 volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap);
2665
2666 if (bcmp(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)) != 0) {
2667 bcopy(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo));
2668 critical = 1;
2669 }
2670
2671 /* Sync Extents over-flow file meta data */
2672 fp = VTOF(vcb->extentsRefNum);
2673 if (FTOC(fp)->c_flag & C_MODIFIED) {
2674 for (i = 0; i < kHFSPlusExtentDensity; i++) {
2675 volumeHeader->extentsFile.extents[i].startBlock =
2676 SWAP_BE32 (fp->ff_extents[i].startBlock);
2677 volumeHeader->extentsFile.extents[i].blockCount =
2678 SWAP_BE32 (fp->ff_extents[i].blockCount);
2679 }
2680 volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fp->ff_size);
2681 volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
2682 volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
2683 FTOC(fp)->c_flag &= ~C_MODIFIED;
2684 }
2685
2686 /* Sync Catalog file meta data */
2687 fp = VTOF(vcb->catalogRefNum);
2688 if (FTOC(fp)->c_flag & C_MODIFIED) {
2689 for (i = 0; i < kHFSPlusExtentDensity; i++) {
2690 volumeHeader->catalogFile.extents[i].startBlock =
2691 SWAP_BE32 (fp->ff_extents[i].startBlock);
2692 volumeHeader->catalogFile.extents[i].blockCount =
2693 SWAP_BE32 (fp->ff_extents[i].blockCount);
2694 }
2695 volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fp->ff_size);
2696 volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
2697 volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
2698 FTOC(fp)->c_flag &= ~C_MODIFIED;
2699 }
2700
2701 /* Sync Allocation file meta data */
2702 fp = VTOF(vcb->allocationsRefNum);
2703 if (FTOC(fp)->c_flag & C_MODIFIED) {
2704 for (i = 0; i < kHFSPlusExtentDensity; i++) {
2705 volumeHeader->allocationFile.extents[i].startBlock =
2706 SWAP_BE32 (fp->ff_extents[i].startBlock);
2707 volumeHeader->allocationFile.extents[i].blockCount =
2708 SWAP_BE32 (fp->ff_extents[i].blockCount);
2709 }
2710 volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fp->ff_size);
2711 volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
2712 volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
2713 FTOC(fp)->c_flag &= ~C_MODIFIED;
2714 }
2715
2716 /* Sync Attribute file meta data */
2717 if (hfsmp->hfs_attribute_vp) {
2718 fp = VTOF(hfsmp->hfs_attribute_vp);
2719 for (i = 0; i < kHFSPlusExtentDensity; i++) {
2720 volumeHeader->attributesFile.extents[i].startBlock =
2721 SWAP_BE32 (fp->ff_extents[i].startBlock);
2722 volumeHeader->attributesFile.extents[i].blockCount =
2723 SWAP_BE32 (fp->ff_extents[i].blockCount);
2724 }
2725 FTOC(fp)->c_flag &= ~C_MODIFIED;
2726 volumeHeader->attributesFile.logicalSize = SWAP_BE64 (fp->ff_size);
2727 volumeHeader->attributesFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
2728 volumeHeader->attributesFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
2729 }
2730
2731 vcb->vcbFlags &= 0x00FF;
2732
2733 if (1 /* hfsmp->jnl == 0 */) {
2734 lck_mtx_unlock(&hfsmp->hfs_mutex);
2735 }
2736
2737 /* If requested, flush out the alternate volume header */
2738 if (altflush && hfsmp->hfs_alt_id_sector) {
2739 struct buf *alt_bp = NULL;
2740
2741 if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector, sectorsize, NOCRED, &alt_bp) == 0) {
2742 if (hfsmp->jnl) {
2743 journal_modify_block_start(hfsmp->jnl, alt_bp);
2744 }
2745
2746 bcopy(volumeHeader, (char *)buf_dataptr(alt_bp) + HFS_ALT_OFFSET(sectorsize), kMDBSize);
2747
2748 if (hfsmp->jnl) {
2749 journal_modify_block_end(hfsmp->jnl, alt_bp);
2750 } else {
2751 (void) VNOP_BWRITE(alt_bp);
2752 }
2753 } else if (alt_bp)
2754 buf_brelse(alt_bp);
2755 }
2756
2757 if (hfsmp->jnl) {
2758 journal_modify_block_end(hfsmp->jnl, bp);
2759 } else {
2760 if (waitfor != MNT_WAIT)
2761 buf_bawrite(bp);
2762 else {
2763 retval = VNOP_BWRITE(bp);
2764 /* When critical data changes, flush the device cache */
2765 if (critical && (retval == 0)) {
2766 (void) VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE,
2767 NULL, FWRITE, NULL);
2768 }
2769 }
2770 }
2771 hfs_end_transaction(hfsmp);
2772
2773 return (retval);
2774 }
2775
2776
2777 /*
2778 * Extend a file system.
2779 */
2780 __private_extern__
2781 int
2782 hfs_extendfs(struct hfsmount *hfsmp, u_int64_t newsize, vfs_context_t context)
2783 {
2784 struct proc *p = vfs_context_proc(context);
2785 kauth_cred_t cred = vfs_context_ucred(context);
2786 struct vnode *vp;
2787 struct vnode *devvp;
2788 struct buf *bp;
2789 struct filefork *fp = NULL;
2790 ExtendedVCB *vcb;
2791 struct cat_fork forkdata;
2792 u_int64_t oldsize;
2793 u_int64_t newblkcnt;
2794 u_int64_t prev_phys_block_count;
2795 u_int32_t addblks;
2796 u_int64_t sectorcnt;
2797 u_int32_t sectorsize;
2798 daddr64_t prev_alt_sector;
2799 daddr_t bitmapblks;
2800 int lockflags;
2801 int error;
2802
2803 devvp = hfsmp->hfs_devvp;
2804 vcb = HFSTOVCB(hfsmp);
2805
2806 /*
2807 * - HFS Plus file systems only.
2808 * - Journaling must be enabled.
2809 * - No embedded volumes.
2810 */
2811 if ((vcb->vcbSigWord == kHFSSigWord) ||
2812 (hfsmp->jnl == NULL) ||
2813 (vcb->hfsPlusIOPosOffset != 0)) {
2814 return (EPERM);
2815 }
2816 /*
2817 * If extending file system by non-root, then verify
2818 * ownership and check permissions.
2819 */
2820 if (suser(cred, NULL)) {
2821 error = hfs_vget(hfsmp, kHFSRootFolderID, &vp, 0);
2822
2823 if (error)
2824 return (error);
2825 error = hfs_owner_rights(hfsmp, VTOC(vp)->c_uid, cred, p, 0);
2826 if (error == 0) {
2827 error = hfs_write_access(vp, cred, p, false);
2828 }
2829 hfs_unlock(VTOC(vp));
2830 vnode_put(vp);
2831 if (error)
2832 return (error);
2833
2834 error = vnode_authorize(devvp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, context);
2835 if (error)
2836 return (error);
2837 }
2838 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&sectorsize, 0, context)) {
2839 return (ENXIO);
2840 }
2841 if (sectorsize != hfsmp->hfs_phys_block_size) {
2842 return (ENXIO);
2843 }
2844 if (VNOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&sectorcnt, 0, context)) {
2845 return (ENXIO);
2846 }
2847 if ((sectorsize * sectorcnt) < newsize) {
2848 printf("hfs_extendfs: not enough space on device\n");
2849 return (ENOSPC);
2850 }
2851 oldsize = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
2852
2853 /*
2854 * Validate new size.
2855 */
2856 if ((newsize <= oldsize) || (newsize % sectorsize)) {
2857 printf("hfs_extendfs: invalid size\n");
2858 return (EINVAL);
2859 }
2860 newblkcnt = newsize / vcb->blockSize;
2861 if (newblkcnt > (u_int64_t)0xFFFFFFFF)
2862 return (EOVERFLOW);
2863
2864 addblks = newblkcnt - vcb->totalBlocks;
2865
2866 printf("hfs_extendfs: growing %s by %d blocks\n", vcb->vcbVN, addblks);
2867 /*
2868 * Enclose changes inside a transaction.
2869 */
2870 if (hfs_start_transaction(hfsmp) != 0) {
2871 return (EINVAL);
2872 }
2873
2874 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
2875 vp = vcb->allocationsRefNum;
2876 fp = VTOF(vp);
2877 bcopy(&fp->ff_data, &forkdata, sizeof(forkdata));
2878
2879 /*
2880 * Calculate additional space required (if any) by allocation bitmap.
2881 */
2882 bitmapblks = roundup(newblkcnt / 8, vcb->vcbVBMIOSize) / vcb->blockSize;
2883 if (bitmapblks > (daddr_t)fp->ff_blocks)
2884 bitmapblks -= fp->ff_blocks;
2885 else
2886 bitmapblks = 0;
2887
2888 if (bitmapblks > 0) {
2889 daddr64_t blkno;
2890 daddr_t blkcnt;
2891
2892 /*
2893 * Add a new extent to the allocation bitmap file.
2894 */
2895 error = AddFileExtent(vcb, fp, vcb->totalBlocks, bitmapblks);
2896 if (error) {
2897 printf("hfs_extendfs: error %d adding extents\n", error);
2898 goto out;
2899 }
2900 blkcnt = bitmapblks;
2901 blkno = (daddr64_t)fp->ff_blocks;
2902 fp->ff_blocks += bitmapblks;
2903 fp->ff_size += (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize;
2904 VTOC(vp)->c_blocks = fp->ff_blocks;
2905 /*
2906 * Zero out the new bitmap blocks.
2907 */
2908 {
2909
2910 bp = NULL;
2911 while (blkcnt > 0) {
2912 error = (int)buf_meta_bread(vp, blkno, vcb->blockSize, NOCRED, &bp);
2913 if (error) {
2914 if (bp) {
2915 buf_brelse(bp);
2916 }
2917 break;
2918 }
2919 bzero((char *)buf_dataptr(bp), vcb->blockSize);
2920 buf_markaged(bp);
2921 error = (int)buf_bwrite(bp);
2922 if (error)
2923 break;
2924 --blkcnt;
2925 ++blkno;
2926 }
2927 }
2928 if (error) {
2929 printf("hfs_extendfs: error %d clearing blocks\n", error);
2930 goto out;
2931 }
2932 /*
2933 * Mark the new bitmap space as allocated.
2934 */
2935 error = BlockMarkAllocated(vcb, vcb->totalBlocks, bitmapblks);
2936 if (error) {
2937 printf("hfs_extendfs: error %d setting bitmap\n", error);
2938 goto out;
2939 }
2940 }
2941 /*
2942 * Mark the new alternate VH as allocated.
2943 */
2944 if (vcb->blockSize == 512)
2945 error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 2, 2);
2946 else
2947 error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 1, 1);
2948 if (error) {
2949 printf("hfs_extendfs: error %d setting bitmap (VH)\n", error);
2950 goto out;
2951 }
2952 /*
2953 * Mark the old alternate VH as free.
2954 */
2955 if (vcb->blockSize == 512)
2956 (void) BlockMarkFree(vcb, vcb->totalBlocks - 2, 2);
2957 else
2958 (void) BlockMarkFree(vcb, vcb->totalBlocks - 1, 1);
2959 /*
2960 * Adjust file system variables for new space.
2961 */
2962 prev_phys_block_count = hfsmp->hfs_phys_block_count;
2963 prev_alt_sector = hfsmp->hfs_alt_id_sector;
2964
2965 vcb->totalBlocks += addblks;
2966 vcb->freeBlocks += addblks - bitmapblks;
2967 hfsmp->hfs_phys_block_count = newsize / sectorsize;
2968 hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / sectorsize) +
2969 HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count);
2970 MarkVCBDirty(vcb);
2971 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
2972 if (error) {
2973 printf("hfs_extendfs: couldn't flush volume headers (%d)", error);
2974 /*
2975 * Restore to old state.
2976 */
2977 fp->ff_size -= (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize;
2978 vcb->totalBlocks -= addblks;
2979 vcb->freeBlocks -= addblks - bitmapblks;
2980 hfsmp->hfs_phys_block_count = prev_phys_block_count;
2981 hfsmp->hfs_alt_id_sector = prev_alt_sector;
2982 MarkVCBDirty(vcb);
2983 if (vcb->blockSize == 512)
2984 (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 2, 2);
2985 else
2986 (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 1, 1);
2987 goto out;
2988 }
2989 /*
2990 * Invalidate the old alternate volume header.
2991 */
2992 bp = NULL;
2993 if (prev_alt_sector) {
2994 if (buf_meta_bread(hfsmp->hfs_devvp, prev_alt_sector, sectorsize,
2995 NOCRED, &bp) == 0) {
2996 journal_modify_block_start(hfsmp->jnl, bp);
2997
2998 bzero((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(sectorsize), kMDBSize);
2999
3000 journal_modify_block_end(hfsmp->jnl, bp);
3001 } else if (bp) {
3002 buf_brelse(bp);
3003 }
3004 }
3005 out:
3006 if (error && fp) {
3007 /* Restore allocation fork. */
3008 bcopy(&forkdata, &fp->ff_data, sizeof(forkdata));
3009 VTOC(vp)->c_blocks = fp->ff_blocks;
3010
3011 }
3012 hfs_systemfile_unlock(hfsmp, lockflags);
3013 hfs_end_transaction(hfsmp);
3014
3015 return (error);
3016 }
3017
3018 #define HFS_MIN_SIZE (32LL * 1024LL * 1024LL)
3019
3020 /*
3021 * Truncate a file system (while still mounted).
3022 */
3023 __private_extern__
3024 int
3025 hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t context)
3026 {
3027 struct buf *bp = NULL;
3028 u_int64_t oldsize;
3029 u_int32_t newblkcnt;
3030 u_int32_t reclaimblks;
3031 int lockflags = 0;
3032 int transaction_begun = 0;
3033 int error;
3034
3035
3036 lck_mtx_lock(&hfsmp->hfs_mutex);
3037 if (hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) {
3038 lck_mtx_unlock(&hfsmp->hfs_mutex);
3039 return (EALREADY);
3040 }
3041 hfsmp->hfs_flags |= HFS_RESIZE_IN_PROGRESS;
3042 hfsmp->hfs_resize_filesmoved = 0;
3043 hfsmp->hfs_resize_totalfiles = 0;
3044 lck_mtx_unlock(&hfsmp->hfs_mutex);
3045
3046 /*
3047 * - Journaled HFS Plus volumes only.
3048 * - No embedded volumes.
3049 */
3050 if ((hfsmp->jnl == NULL) ||
3051 (hfsmp->hfsPlusIOPosOffset != 0)) {
3052 error = EPERM;
3053 goto out;
3054 }
3055 oldsize = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
3056 newblkcnt = newsize / hfsmp->blockSize;
3057 reclaimblks = hfsmp->totalBlocks - newblkcnt;
3058
3059 /* Make sure new size is valid. */
3060 if ((newsize < HFS_MIN_SIZE) ||
3061 (newsize >= oldsize) ||
3062 (newsize % hfsmp->hfs_phys_block_size)) {
3063 error = EINVAL;
3064 goto out;
3065 }
3066 /* Make sure there's enough space to work with. */
3067 if (reclaimblks >= hfs_freeblks(hfsmp, 1)) {
3068 error = ENOSPC;
3069 goto out;
3070 }
3071 /* Start with a clean journal. */
3072 journal_flush(hfsmp->jnl);
3073
3074 if (hfs_start_transaction(hfsmp) != 0) {
3075 error = EINVAL;
3076 goto out;
3077 }
3078 transaction_begun = 1;
3079
3080 /*
3081 * Look for files that have blocks beyond newblkcnt.
3082 */
3083 if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
3084 /*
3085 * hfs_reclaimspace will use separate transactions when
3086 * relocating files (so we don't overwhelm the journal).
3087 */
3088 hfs_end_transaction(hfsmp);
3089 transaction_begun = 0;
3090
3091 /* Attempt to reclaim some space. */
3092 if (hfs_reclaimspace(hfsmp, newblkcnt, reclaimblks) != 0) {
3093 printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
3094 error = ENOSPC;
3095 goto out;
3096 }
3097 if (hfs_start_transaction(hfsmp) != 0) {
3098 error = EINVAL;
3099 goto out;
3100 }
3101 transaction_begun = 1;
3102
3103 /* Check if we're clear now. */
3104 if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
3105 printf("hfs_truncatefs: didn't reclaim enough space on %s\n", hfsmp->vcbVN);
3106 error = EAGAIN; /* tell client to try again */
3107 goto out;
3108 }
3109 }
3110 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
3111
3112 /*
3113 * Mark the old alternate volume header as free.
3114 * We don't bother shrinking allocation bitmap file.
3115 */
3116 if (hfsmp->blockSize == 512)
3117 (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 2, 2);
3118 else
3119 (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 1, 1);
3120
3121 /*
3122 * Allocate last block for alternate volume header.
3123 */
3124 if (hfsmp->blockSize == 512)
3125 error = BlockMarkAllocated(hfsmp, newblkcnt - 2, 2);
3126 else
3127 error = BlockMarkAllocated(hfsmp, newblkcnt - 1, 1);
3128
3129 if (error) {
3130 goto out;
3131 }
3132
3133 /*
3134 * Invalidate the existing alternate volume header.
3135 *
3136 * Don't do this as a transaction (don't call journal_modify_block)
3137 * since this block will be outside of the truncated file system!
3138 */
3139 if (hfsmp->hfs_alt_id_sector) {
3140 if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector,
3141 hfsmp->hfs_phys_block_size, NOCRED, &bp) == 0) {
3142
3143 bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_phys_block_size)), kMDBSize);
3144 (void) VNOP_BWRITE(bp);
3145 } else if (bp) {
3146 buf_brelse(bp);
3147 }
3148 bp = NULL;
3149 }
3150
3151 /* Log successful shrinking. */
3152 printf("hfs_truncatefs: shrank \"%s\" to %d blocks (was %d blocks)\n",
3153 hfsmp->vcbVN, newblkcnt, hfsmp->totalBlocks);
3154
3155 /*
3156 * Adjust file system variables and flush them to disk.
3157 */
3158 hfsmp->freeBlocks -= hfsmp->totalBlocks - newblkcnt;
3159 hfsmp->totalBlocks = newblkcnt;
3160 hfsmp->hfs_phys_block_count = newsize / hfsmp->hfs_phys_block_size;
3161 hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_phys_block_size, hfsmp->hfs_phys_block_count);
3162 MarkVCBDirty(hfsmp);
3163 error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
3164 if (error)
3165 panic("hfs_truncatefs: unexpected error flushing volume header (%d)\n", error);
3166 out:
3167 if (lockflags) {
3168 hfs_systemfile_unlock(hfsmp, lockflags);
3169 }
3170 if (transaction_begun) {
3171 hfs_end_transaction(hfsmp);
3172 journal_flush(hfsmp->jnl);
3173 }
3174
3175 lck_mtx_lock(&hfsmp->hfs_mutex);
3176 hfsmp->hfs_flags &= ~HFS_RESIZE_IN_PROGRESS;
3177 lck_mtx_unlock(&hfsmp->hfs_mutex);
3178
3179 return (error);
3180 }
3181
3182
3183 /*
3184 * Reclaim space at the end of a file system.
3185 */
3186 static int
3187 hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks)
3188 {
3189 struct vnode *vp = NULL;
3190 FCB *fcb;
3191 struct BTreeIterator * iterator = NULL;
3192 struct FSBufferDescriptor btdata;
3193 struct HFSPlusCatalogFile filerec;
3194 struct filefork *fp;
3195 u_int32_t saved_next_allocation;
3196 cnid_t * cnidbufp;
3197 size_t cnidbufsize;
3198 int filecnt = 0;
3199 int maxfilecnt;
3200 u_long block;
3201 u_long datablks;
3202 u_long rsrcblks;
3203 u_long blkstomove = 0;
3204 int lockflags;
3205 int i;
3206 int error;
3207 int lastprogress = 0;
3208
3209
3210 /* Check if Attributes file overlaps reclaim area. */
3211 if (hfsmp->hfs_attribute_vp) {
3212 fp = VTOF(hfsmp->hfs_attribute_vp);
3213 datablks = 0;
3214 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
3215 if (fp->ff_extents[i].blockCount == 0) {
3216 break;
3217 }
3218 datablks += fp->ff_extents[i].blockCount;
3219 block = fp->ff_extents[i].startBlock + fp->ff_extents[i].blockCount;
3220 if (block >= startblk) {
3221 printf("hfs_reclaimspace: Attributes file can't move\n");
3222 return (EPERM);
3223 }
3224 }
3225 if ((i == kHFSPlusExtentDensity) && (fp->ff_blocks > datablks)) {
3226 if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, kHFSAttributesFileID, 0)) {
3227 printf("hfs_reclaimspace: Attributes file can't move\n");
3228 return (EPERM);
3229 }
3230 }
3231 }
3232 /* Check if Catalog file overlaps reclaim area. */
3233 fp = VTOF(hfsmp->hfs_catalog_vp);
3234 datablks = 0;
3235 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
3236 if (fp->ff_extents[i].blockCount == 0) {
3237 break;
3238 }
3239 datablks += fp->ff_extents[i].blockCount;
3240 block = fp->ff_extents[i].startBlock + fp->ff_extents[i].blockCount;
3241 if (block >= startblk) {
3242 printf("hfs_reclaimspace: Catalog file can't move\n");
3243 return (EPERM);
3244 }
3245 }
3246 if ((i == kHFSPlusExtentDensity) && (fp->ff_blocks > datablks)) {
3247 if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, kHFSCatalogFileID, 0)) {
3248 printf("hfs_reclaimspace: Catalog file can't move\n");
3249 return (EPERM);
3250 }
3251 }
3252
3253 /* For now move a maximum of 250,000 files. */
3254 maxfilecnt = MIN(hfsmp->hfs_filecount, 250000);
3255 maxfilecnt = MIN((u_long)maxfilecnt, reclaimblks);
3256 cnidbufsize = maxfilecnt * sizeof(cnid_t);
3257 if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
3258 return (ENOMEM);
3259 }
3260 if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
3261 kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
3262 return (ENOMEM);
3263 }
3264
3265 saved_next_allocation = hfsmp->nextAllocation;
3266 hfsmp->nextAllocation = hfsmp->hfs_metazone_start;
3267
3268 fcb = VTOF(hfsmp->hfs_catalog_vp);
3269 bzero(iterator, sizeof(*iterator));
3270
3271 btdata.bufferAddress = &filerec;
3272 btdata.itemSize = sizeof(filerec);
3273 btdata.itemCount = 1;
3274
3275 /* Keep the Catalog and extents files locked during iteration. */
3276 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_SHARED_LOCK);
3277
3278 error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
3279 if (error) {
3280 goto end_iteration;
3281 }
3282 /*
3283 * Iterate over all the catalog records looking for files
3284 * that overlap into the space we're trying to free up.
3285 */
3286 for (filecnt = 0; filecnt < maxfilecnt; ) {
3287 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
3288 if (error) {
3289 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
3290 error = 0;
3291 }
3292 break;
3293 }
3294 if (filerec.recordType != kHFSPlusFileRecord) {
3295 continue;
3296 }
3297 datablks = rsrcblks = 0;
3298 /*
3299 * Check if either fork overlaps target space.
3300 */
3301 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
3302 if (filerec.dataFork.extents[i].blockCount != 0) {
3303 datablks += filerec.dataFork.extents[i].blockCount;
3304 block = filerec.dataFork.extents[i].startBlock +
3305 filerec.dataFork.extents[i].blockCount;
3306 if (block >= startblk) {
3307 if ((filerec.fileID == hfsmp->hfs_jnlfileid) ||
3308 (filerec.fileID == hfsmp->hfs_jnlinfoblkid)) {
3309 printf("hfs_reclaimspace: cannot move active journal\n");
3310 error = EPERM;
3311 goto end_iteration;
3312 }
3313 cnidbufp[filecnt++] = filerec.fileID;
3314 blkstomove += filerec.dataFork.totalBlocks;
3315 break;
3316 }
3317 }
3318 if (filerec.resourceFork.extents[i].blockCount != 0) {
3319 rsrcblks += filerec.resourceFork.extents[i].blockCount;
3320 block = filerec.resourceFork.extents[i].startBlock +
3321 filerec.resourceFork.extents[i].blockCount;
3322 if (block >= startblk) {
3323 cnidbufp[filecnt++] = filerec.fileID;
3324 blkstomove += filerec.resourceFork.totalBlocks;
3325 break;
3326 }
3327 }
3328 }
3329 /*
3330 * Check for any overflow extents that overlap.
3331 */
3332 if (i == kHFSPlusExtentDensity) {
3333 if (filerec.dataFork.totalBlocks > datablks) {
3334 if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, filerec.fileID, 0)) {
3335 cnidbufp[filecnt++] = filerec.fileID;
3336 blkstomove += filerec.dataFork.totalBlocks;
3337 }
3338 } else if (filerec.resourceFork.totalBlocks > rsrcblks) {
3339 if (hfs_overlapped_overflow_extents(hfsmp, startblk, rsrcblks, filerec.fileID, 1)) {
3340 cnidbufp[filecnt++] = filerec.fileID;
3341 blkstomove += filerec.resourceFork.totalBlocks;
3342 }
3343 }
3344 }
3345 }
3346
3347 end_iteration:
3348 if (filecnt == 0) {
3349 error = ENOSPC;
3350 }
3351 /* All done with catalog. */
3352 hfs_systemfile_unlock(hfsmp, lockflags);
3353 if (error)
3354 goto out;
3355
3356 /*
3357 * Double check space requirements to make sure
3358 * there is enough space to relocate any files
3359 * that reside in the reclaim area.
3360 *
3361 * Blocks To Move --------------
3362 * | | |
3363 * V V V
3364 * ------------------------------------------------------------------------
3365 * | | / /// // |
3366 * | | / /// // |
3367 * | | / /// // |
3368 * ------------------------------------------------------------------------
3369 *
3370 * <------------------- New Total Blocks ------------------><-- Reclaim -->
3371 *
3372 * <------------------------ Original Total Blocks ----------------------->
3373 *
3374 */
3375 if ((reclaimblks + blkstomove) >= hfs_freeblks(hfsmp, 1)) {
3376 error = ENOSPC;
3377 goto out;
3378 }
3379 hfsmp->hfs_resize_filesmoved = 0;
3380 hfsmp->hfs_resize_totalfiles = filecnt;
3381
3382 /* Now move any files that are in the way. */
3383 for (i = 0; i < filecnt; ++i) {
3384 struct vnode * rvp;
3385
3386 if (hfs_vget(hfsmp, cnidbufp[i], &vp, 0) != 0)
3387 continue;
3388
3389 /* Relocate any data fork blocks. */
3390 if (VTOF(vp)->ff_blocks > 0) {
3391 error = hfs_relocate(vp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
3392 }
3393 if (error)
3394 break;
3395
3396 /* Relocate any resource fork blocks. */
3397 if ((VTOC((vp))->c_blocks - VTOF((vp))->ff_blocks) > 0) {
3398 error = hfs_vgetrsrc(hfsmp, vp, &rvp, current_proc());
3399 if (error)
3400 break;
3401 error = hfs_relocate(rvp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
3402 vnode_put(rvp);
3403 if (error)
3404 break;
3405 }
3406 hfs_unlock(VTOC(vp));
3407 vnode_put(vp);
3408 vp = NULL;
3409
3410 ++hfsmp->hfs_resize_filesmoved;
3411
3412 /* Report intermediate progress. */
3413 if (filecnt > 100) {
3414 int progress;
3415
3416 progress = (i * 100) / filecnt;
3417 if (progress > (lastprogress + 9)) {
3418 printf("hfs_reclaimspace: %d%% done...\n", progress);
3419 lastprogress = progress;
3420 }
3421 }
3422 }
3423 if (vp) {
3424 hfs_unlock(VTOC(vp));
3425 vnode_put(vp);
3426 vp = NULL;
3427 }
3428 if (hfsmp->hfs_resize_filesmoved != 0) {
3429 printf("hfs_reclaimspace: relocated %d files on \"%s\"\n",
3430 (int)hfsmp->hfs_resize_filesmoved, hfsmp->vcbVN);
3431 }
3432 out:
3433 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
3434 kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
3435
3436 /*
3437 * Restore the roving allocation pointer on errors.
3438 * (but only if we didn't move any files)
3439 */
3440 if (error && hfsmp->hfs_resize_filesmoved == 0) {
3441 hfsmp->nextAllocation = saved_next_allocation;
3442 }
3443 return (error);
3444 }
3445
3446
3447 /*
3448 * Check if there are any overflow extents that overlap.
3449 */
3450 static int
3451 hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk, u_int32_t catblks, u_int32_t fileID, int rsrcfork)
3452 {
3453 struct BTreeIterator * iterator = NULL;
3454 struct FSBufferDescriptor btdata;
3455 HFSPlusExtentRecord extrec;
3456 HFSPlusExtentKey *extkeyptr;
3457 FCB *fcb;
3458 u_int32_t block;
3459 u_int8_t forktype;
3460 int overlapped = 0;
3461 int i;
3462 int error;
3463
3464 forktype = rsrcfork ? 0xFF : 0;
3465 if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
3466 return (0);
3467 }
3468 bzero(iterator, sizeof(*iterator));
3469 extkeyptr = (HFSPlusExtentKey *)&iterator->key;
3470 extkeyptr->keyLength = kHFSPlusExtentKeyMaximumLength;
3471 extkeyptr->forkType = forktype;
3472 extkeyptr->fileID = fileID;
3473 extkeyptr->startBlock = catblks;
3474
3475 btdata.bufferAddress = &extrec;
3476 btdata.itemSize = sizeof(extrec);
3477 btdata.itemCount = 1;
3478
3479 fcb = VTOF(hfsmp->hfs_extents_vp);
3480
3481 error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
3482 while (error == 0) {
3483 /* Stop when we encounter a different file. */
3484 if ((extkeyptr->fileID != fileID) ||
3485 (extkeyptr->forkType != forktype)) {
3486 break;
3487 }
3488 /*
3489 * Check if the file overlaps target space.
3490 */
3491 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
3492 if (extrec[i].blockCount == 0) {
3493 break;
3494 }
3495 block = extrec[i].startBlock + extrec[i].blockCount;
3496 if (block >= startblk) {
3497 overlapped = 1;
3498 break;
3499 }
3500 }
3501 /* Look for more records. */
3502 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
3503 }
3504
3505 kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
3506 return (overlapped);
3507 }
3508
3509
3510 /*
3511 * Calculate the progress of a file system resize operation.
3512 */
3513 __private_extern__
3514 int
3515 hfs_resize_progress(struct hfsmount *hfsmp, u_int32_t *progress)
3516 {
3517 if ((hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) == 0) {
3518 return (ENXIO);
3519 }
3520
3521 if (hfsmp->hfs_resize_totalfiles > 0)
3522 *progress = (hfsmp->hfs_resize_filesmoved * 100) / hfsmp->hfs_resize_totalfiles;
3523 else
3524 *progress = 0;
3525
3526 return (0);
3527 }
3528
3529
3530 /*
3531 * Get file system attributes.
3532 */
3533 static int
3534 hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
3535 {
3536 ExtendedVCB *vcb = VFSTOVCB(mp);
3537 struct hfsmount *hfsmp = VFSTOHFS(mp);
3538 u_long freeCNIDs;
3539
3540 freeCNIDs = (u_long)0xFFFFFFFF - (u_long)hfsmp->vcbNxtCNID;
3541
3542 VFSATTR_RETURN(fsap, f_objcount, (uint64_t)hfsmp->vcbFilCnt + (uint64_t)hfsmp->vcbDirCnt);
3543 VFSATTR_RETURN(fsap, f_filecount, (uint64_t)hfsmp->vcbFilCnt);
3544 VFSATTR_RETURN(fsap, f_dircount, (uint64_t)hfsmp->vcbDirCnt);
3545 VFSATTR_RETURN(fsap, f_maxobjcount, (uint64_t)0xFFFFFFFF);
3546 VFSATTR_RETURN(fsap, f_iosize, (size_t)(MAX_UPL_TRANSFER * PAGE_SIZE));
3547 VFSATTR_RETURN(fsap, f_blocks, (uint64_t)hfsmp->totalBlocks);
3548 VFSATTR_RETURN(fsap, f_bfree, (uint64_t)hfs_freeblks(hfsmp, 0));
3549 VFSATTR_RETURN(fsap, f_bavail, (uint64_t)hfs_freeblks(hfsmp, 1));
3550 VFSATTR_RETURN(fsap, f_bsize, (uint32_t)vcb->blockSize);
3551 /* XXX needs clarification */
3552 VFSATTR_RETURN(fsap, f_bused, hfsmp->totalBlocks - hfs_freeblks(hfsmp, 1));
3553 /* Maximum files is constrained by total blocks. */
3554 VFSATTR_RETURN(fsap, f_files, (uint64_t)(hfsmp->totalBlocks - 2));
3555 VFSATTR_RETURN(fsap, f_ffree, MIN((uint64_t)freeCNIDs, (uint64_t)hfs_freeblks(hfsmp, 1)));
3556
3557 fsap->f_fsid.val[0] = hfsmp->hfs_raw_dev;
3558 fsap->f_fsid.val[1] = vfs_typenum(mp);
3559 VFSATTR_SET_SUPPORTED(fsap, f_fsid);
3560
3561 VFSATTR_RETURN(fsap, f_signature, vcb->vcbSigWord);
3562 VFSATTR_RETURN(fsap, f_carbon_fsid, 0);
3563
3564 if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
3565 vol_capabilities_attr_t *cap;
3566
3567 cap = &fsap->f_capabilities;
3568
3569 if (hfsmp->hfs_flags & HFS_STANDARD) {
3570 cap->capabilities[VOL_CAPABILITIES_FORMAT] =
3571 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
3572 VOL_CAP_FMT_CASE_PRESERVING |
3573 VOL_CAP_FMT_FAST_STATFS;
3574 } else {
3575 cap->capabilities[VOL_CAPABILITIES_FORMAT] =
3576 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
3577 VOL_CAP_FMT_SYMBOLICLINKS |
3578 VOL_CAP_FMT_HARDLINKS |
3579 VOL_CAP_FMT_JOURNAL |
3580 (hfsmp->jnl ? VOL_CAP_FMT_JOURNAL_ACTIVE : 0) |
3581 (hfsmp->hfs_flags & HFS_CASE_SENSITIVE ? VOL_CAP_FMT_CASE_SENSITIVE : 0) |
3582 VOL_CAP_FMT_CASE_PRESERVING |
3583 VOL_CAP_FMT_FAST_STATFS |
3584 VOL_CAP_FMT_2TB_FILESIZE;
3585 }
3586 cap->capabilities[VOL_CAPABILITIES_INTERFACES] =
3587 VOL_CAP_INT_SEARCHFS |
3588 VOL_CAP_INT_ATTRLIST |
3589 VOL_CAP_INT_NFSEXPORT |
3590 VOL_CAP_INT_READDIRATTR |
3591 VOL_CAP_INT_EXCHANGEDATA |
3592 VOL_CAP_INT_ALLOCATE |
3593 VOL_CAP_INT_VOL_RENAME |
3594 VOL_CAP_INT_ADVLOCK |
3595 VOL_CAP_INT_FLOCK;
3596 cap->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
3597 cap->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
3598
3599 cap->valid[VOL_CAPABILITIES_FORMAT] =
3600 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
3601 VOL_CAP_FMT_SYMBOLICLINKS |
3602 VOL_CAP_FMT_HARDLINKS |
3603 VOL_CAP_FMT_JOURNAL |
3604 VOL_CAP_FMT_JOURNAL_ACTIVE |
3605 VOL_CAP_FMT_NO_ROOT_TIMES |
3606 VOL_CAP_FMT_SPARSE_FILES |
3607 VOL_CAP_FMT_ZERO_RUNS |
3608 VOL_CAP_FMT_CASE_SENSITIVE |
3609 VOL_CAP_FMT_CASE_PRESERVING |
3610 VOL_CAP_FMT_FAST_STATFS |
3611 VOL_CAP_FMT_2TB_FILESIZE;
3612 cap->valid[VOL_CAPABILITIES_INTERFACES] =
3613 VOL_CAP_INT_SEARCHFS |
3614 VOL_CAP_INT_ATTRLIST |
3615 VOL_CAP_INT_NFSEXPORT |
3616 VOL_CAP_INT_READDIRATTR |
3617 VOL_CAP_INT_EXCHANGEDATA |
3618 VOL_CAP_INT_COPYFILE |
3619 VOL_CAP_INT_ALLOCATE |
3620 VOL_CAP_INT_VOL_RENAME |
3621 VOL_CAP_INT_ADVLOCK |
3622 VOL_CAP_INT_FLOCK;
3623 cap->valid[VOL_CAPABILITIES_RESERVED1] = 0;
3624 cap->valid[VOL_CAPABILITIES_RESERVED2] = 0;
3625 VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
3626 }
3627 if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
3628 vol_attributes_attr_t *attrp = &fsap->f_attributes;
3629
3630 attrp->validattr.commonattr = ATTR_CMN_VALIDMASK;
3631 attrp->validattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
3632 attrp->validattr.dirattr = ATTR_DIR_VALIDMASK;
3633 attrp->validattr.fileattr = ATTR_FILE_VALIDMASK;
3634 attrp->validattr.forkattr = 0;
3635
3636 attrp->nativeattr.commonattr = ATTR_CMN_VALIDMASK;
3637 attrp->nativeattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
3638 attrp->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
3639 attrp->nativeattr.fileattr = ATTR_FILE_VALIDMASK;
3640 attrp->nativeattr.forkattr = 0;
3641 VFSATTR_SET_SUPPORTED(fsap, f_attributes);
3642 }
3643 fsap->f_create_time.tv_sec = hfsmp->vcbCrDate;
3644 fsap->f_create_time.tv_nsec = 0;
3645 VFSATTR_SET_SUPPORTED(fsap, f_create_time);
3646 fsap->f_modify_time.tv_sec = hfsmp->vcbLsMod;
3647 fsap->f_modify_time.tv_nsec = 0;
3648 VFSATTR_SET_SUPPORTED(fsap, f_modify_time);
3649
3650 fsap->f_backup_time.tv_sec = hfsmp->vcbVolBkUp;
3651 fsap->f_backup_time.tv_nsec = 0;
3652 VFSATTR_SET_SUPPORTED(fsap, f_backup_time);
3653 if (VFSATTR_IS_ACTIVE(fsap, f_fssubtype)) {
3654 uint16_t subtype = 0;
3655
3656 /*
3657 * Subtypes (flavors) for HFS
3658 * 0: Mac OS Extended
3659 * 1: Mac OS Extended (Journaled)
3660 * 2: Mac OS Extended (Case Sensitive)
3661 * 3: Mac OS Extended (Case Sensitive, Journaled)
3662 * 4 - 127: Reserved
3663 * 128: Mac OS Standard
3664 *
3665 */
3666 if (hfsmp->hfs_flags & HFS_STANDARD) {
3667 subtype = HFS_SUBTYPE_STANDARDHFS;
3668 } else /* HFS Plus */ {
3669 if (hfsmp->jnl)
3670 subtype |= HFS_SUBTYPE_JOURNALED;
3671 if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
3672 subtype |= HFS_SUBTYPE_CASESENSITIVE;
3673 }
3674 fsap->f_fssubtype = subtype;
3675 VFSATTR_SET_SUPPORTED(fsap, f_fssubtype);
3676 }
3677
3678 if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
3679 strncpy(fsap->f_vol_name, hfsmp->vcbVN, MAXPATHLEN);
3680 fsap->f_vol_name[MAXPATHLEN - 1] = 0;
3681 VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
3682 }
3683 return (0);
3684 }
3685
3686 /*
3687 * Perform a volume rename. Requires the FS' root vp.
3688 */
3689 static int
3690 hfs_rename_volume(struct vnode *vp, const char *name, proc_t p)
3691 {
3692 ExtendedVCB *vcb = VTOVCB(vp);
3693 struct cnode *cp = VTOC(vp);
3694 struct hfsmount *hfsmp = VTOHFS(vp);
3695 struct cat_desc to_desc;
3696 struct cat_desc todir_desc;
3697 struct cat_desc new_desc;
3698 cat_cookie_t cookie;
3699 int lockflags;
3700 int error = 0;
3701
3702 /*
3703 * Ignore attempts to rename a volume to a zero-length name.
3704 */
3705 if (name[0] == 0)
3706 return(0);
3707
3708 bzero(&to_desc, sizeof(to_desc));
3709 bzero(&todir_desc, sizeof(todir_desc));
3710 bzero(&new_desc, sizeof(new_desc));
3711 bzero(&cookie, sizeof(cookie));
3712
3713 todir_desc.cd_parentcnid = kHFSRootParentID;
3714 todir_desc.cd_cnid = kHFSRootFolderID;
3715 todir_desc.cd_flags = CD_ISDIR;
3716
3717 to_desc.cd_nameptr = name;
3718 to_desc.cd_namelen = strlen(name);
3719 to_desc.cd_parentcnid = kHFSRootParentID;
3720 to_desc.cd_cnid = cp->c_cnid;
3721 to_desc.cd_flags = CD_ISDIR;
3722
3723 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)) == 0) {
3724 if ((error = hfs_start_transaction(hfsmp)) == 0) {
3725 if ((error = cat_preflight(hfsmp, CAT_RENAME, &cookie, p)) == 0) {
3726 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
3727
3728 error = cat_rename(hfsmp, &cp->c_desc, &todir_desc, &to_desc, &new_desc);
3729
3730 /*
3731 * If successful, update the name in the VCB, ensure it's terminated.
3732 */
3733 if (!error) {
3734 strncpy(vcb->vcbVN, name, sizeof(vcb->vcbVN));
3735 vcb->vcbVN[sizeof(vcb->vcbVN) - 1] = 0;
3736 }
3737
3738 hfs_systemfile_unlock(hfsmp, lockflags);
3739 cat_postflight(hfsmp, &cookie, p);
3740
3741 if (error)
3742 vcb->vcbFlags |= 0xFF00;
3743 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
3744 }
3745 hfs_end_transaction(hfsmp);
3746 }
3747 if (!error) {
3748 /* Release old allocated name buffer */
3749 if (cp->c_desc.cd_flags & CD_HASBUF) {
3750 char *name = cp->c_desc.cd_nameptr;
3751
3752 cp->c_desc.cd_nameptr = 0;
3753 cp->c_desc.cd_namelen = 0;
3754 cp->c_desc.cd_flags &= ~CD_HASBUF;
3755 vfs_removename(name);
3756 }
3757 /* Update cnode's catalog descriptor */
3758 replace_desc(cp, &new_desc);
3759 vcb->volumeNameEncodingHint = new_desc.cd_encoding;
3760 cp->c_touch_chgtime = TRUE;
3761 }
3762
3763 hfs_unlock(cp);
3764 }
3765
3766 return(error);
3767 }
3768
3769 /*
3770 * Get file system attributes.
3771 */
3772 static int
3773 hfs_vfs_setattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
3774 {
3775 kauth_cred_t cred = vfs_context_ucred(context);
3776 int error = 0;
3777
3778 /*
3779 * Must be superuser or owner of filesystem to change volume attributes
3780 */
3781 if (!kauth_cred_issuser(cred) && (kauth_cred_getuid(cred) != vfs_statfs(mp)->f_owner))
3782 return(EACCES);
3783
3784 if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
3785 vnode_t root_vp;
3786
3787 error = hfs_vfs_root(mp, &root_vp, context);
3788 if (error)
3789 goto out;
3790
3791 error = hfs_rename_volume(root_vp, fsap->f_vol_name, vfs_context_proc(context));
3792 (void) vnode_put(root_vp);
3793 if (error)
3794 goto out;
3795
3796 VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
3797 }
3798
3799 out:
3800 return error;
3801 }
3802
3803
3804 /*
3805 * hfs vfs operations.
3806 */
3807 struct vfsops hfs_vfsops = {
3808 hfs_mount,
3809 hfs_start,
3810 hfs_unmount,
3811 hfs_vfs_root,
3812 hfs_quotactl,
3813 hfs_vfs_getattr, /* was hfs_statfs */
3814 hfs_sync,
3815 hfs_vfs_vget,
3816 hfs_fhtovp,
3817 hfs_vptofh,
3818 hfs_init,
3819 hfs_sysctl,
3820 hfs_vfs_setattr
3821 };