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