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