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