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