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