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