]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vfsops.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsops.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1991, 1993, 1994
24 * The Regents of the University of California. All rights reserved.
25 * (c) UNIX System Laboratories, Inc.
26 * All or some portions of this file are derived from material licensed
27 * to the University of California by American Telephone and Telegraph
28 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
29 * the permission of UNIX System Laboratories, Inc.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 *
59 * hfs_vfsops.c
60 * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95
61 *
9bccf70c 62 * (c) Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
63 *
64 * hfs_vfsops.c -- VFS layer for loadable HFS file system.
65 *
1c79356b
A
66 */
67#include <sys/param.h>
68#include <sys/systm.h>
69
70#include <sys/ubc.h>
71#include <sys/namei.h>
72#include <sys/vnode.h>
73#include <sys/mount.h>
74#include <sys/malloc.h>
75#include <sys/stat.h>
1c79356b 76#include <sys/lock.h>
9bccf70c
A
77#include <sys/quota.h>
78#include <sys/disk.h>
79
1c79356b
A
80#include <miscfs/specfs/specdev.h>
81#include <hfs/hfs_mount.h>
82
83#include "hfs.h"
9bccf70c
A
84#include "hfs_catalog.h"
85#include "hfs_cnode.h"
1c79356b
A
86#include "hfs_dbg.h"
87#include "hfs_endian.h"
9bccf70c 88#include "hfs_quota.h"
1c79356b
A
89
90#include "hfscommon/headers/FileMgrInternal.h"
91#include "hfscommon/headers/BTreesInternal.h"
92
9bccf70c 93
1c79356b
A
94#if HFS_DIAGNOSTIC
95int hfs_dbg_all = 0;
1c79356b 96int hfs_dbg_err = 0;
1c79356b
A
97#endif
98
d52fe63f 99
1c79356b
A
100extern struct vnodeopv_desc hfs_vnodeop_opv_desc;
101
9bccf70c 102extern void hfs_converterinit(void);
1c79356b
A
103
104extern void inittodr( time_t base);
1c79356b 105
1c79356b 106
9bccf70c
A
107static int hfs_changefs __P((struct mount *mp, struct hfs_mount_args *args,
108 struct proc *p));
109static int hfs_reload __P((struct mount *mp, struct ucred *cred, struct proc *p));
1c79356b 110
9bccf70c
A
111static int hfs_mountfs __P((struct vnode *devvp, struct mount *mp, struct proc *p,
112 struct hfs_mount_args *args));
113static int hfs_statfs __P((struct mount *mp, register struct statfs *sbp,
114 struct proc *p));
1c79356b
A
115
116
117/*
118 * Called by vfs_mountroot when mounting HFS Plus as root.
119 */
120int
121hfs_mountroot()
122{
123 extern struct vnode *rootvp;
124 struct mount *mp;
125 struct proc *p = current_proc(); /* XXX */
126 struct hfsmount *hfsmp;
9bccf70c 127 ExtendedVCB *vcb;
1c79356b
A
128 int error;
129
130 /*
131 * Get vnode for rootdev.
132 */
133 if ((error = bdevvp(rootdev, &rootvp))) {
134 printf("hfs_mountroot: can't setup bdevvp");
135 return (error);
136 }
9bccf70c
A
137 if ((error = vfs_rootmountalloc("hfs", "root_device", &mp))) {
138 vrele(rootvp); /* release the reference from bdevvp() */
1c79356b 139 return (error);
9bccf70c 140 }
1c79356b
A
141 if ((error = hfs_mountfs(rootvp, mp, p, NULL))) {
142 mp->mnt_vfc->vfc_refcount--;
143 vfs_unbusy(mp, p);
9bccf70c 144 vrele(rootvp); /* release the reference from bdevvp() */
1c79356b
A
145 _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
146 return (error);
147 }
148 simple_lock(&mountlist_slock);
149 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
150 simple_unlock(&mountlist_slock);
151
152 /* Init hfsmp */
153 hfsmp = VFSTOHFS(mp);
154
0b4e3aa0
A
155 hfsmp->hfs_uid = UNKNOWNUID;
156 hfsmp->hfs_gid = UNKNOWNGID;
157 hfsmp->hfs_dir_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
158 hfsmp->hfs_file_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */
1c79356b 159
9bccf70c
A
160 /* Establish the free block reserve. */
161 vcb = HFSTOVCB(hfsmp);
162 vcb->reserveBlocks = ((u_int64_t)vcb->totalBlocks * HFS_MINFREE) / 100;
163 vcb->reserveBlocks = MIN(vcb->reserveBlocks, HFS_MAXRESERVE / vcb->blockSize);
164
1c79356b
A
165 (void)hfs_statfs(mp, &mp->mnt_stat, p);
166
167 vfs_unbusy(mp, p);
9bccf70c 168 inittodr(HFSTOVCB(hfsmp)->vcbLsMod);
1c79356b
A
169 return (0);
170}
171
172
173/*
174 * VFS Operations.
175 *
176 * mount system call
177 */
178
9bccf70c
A
179static int
180hfs_mount(mp, path, data, ndp, p)
1c79356b
A
181 register struct mount *mp;
182 char *path;
183 caddr_t data;
184 struct nameidata *ndp;
185 struct proc *p;
186{
187 struct hfsmount *hfsmp = NULL;
188 struct vnode *devvp;
189 struct hfs_mount_args args;
190 size_t size;
191 int retval = E_NONE;
192 int flags;
193 mode_t accessmode;
1c79356b
A
194
195 if ((retval = copyin(data, (caddr_t)&args, sizeof(args))))
196 goto error_exit;
197
198 /*
199 * If updating, check whether changing from read-only to
200 * read/write; if there is no device name, that's all we do.
201 */
202 if (mp->mnt_flag & MNT_UPDATE) {
0b4e3aa0 203
1c79356b
A
204 hfsmp = VFSTOHFS(mp);
205 if ((hfsmp->hfs_fs_ronly == 0) && (mp->mnt_flag & MNT_RDONLY)) {
206
207 /* use VFS_SYNC to push out System (btree) files */
208 retval = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p);
209 if (retval && ((mp->mnt_flag & MNT_FORCE) == 0))
210 goto error_exit;
211
212 flags = WRITECLOSE;
213 if (mp->mnt_flag & MNT_FORCE)
214 flags |= FORCECLOSE;
215
9bccf70c 216 if ((retval = hfs_flushfiles(mp, flags, p)))
1c79356b 217 goto error_exit;
1c79356b 218 hfsmp->hfs_fs_ronly = 1;
9bccf70c 219 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1c79356b
A
220
221 /* also get the volume bitmap blocks */
222 if (!retval)
223 retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p);
224
225 if (retval) {
1c79356b
A
226 hfsmp->hfs_fs_ronly = 0;
227 goto error_exit;
228 }
229 }
230
231 if ((mp->mnt_flag & MNT_RELOAD) &&
232 (retval = hfs_reload(mp, ndp->ni_cnd.cn_cred, p)))
233 goto error_exit;
234
235 if (hfsmp->hfs_fs_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
236 /*
237 * If upgrade to read-write by non-root, then verify
238 * that user has necessary permissions on the device.
239 */
240 if (p->p_ucred->cr_uid != 0) {
241 devvp = hfsmp->hfs_devvp;
242 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
243 if ((retval = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p))) {
244 VOP_UNLOCK(devvp, 0, p);
245 goto error_exit;
246 }
247 VOP_UNLOCK(devvp, 0, p);
248 }
9bccf70c 249 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1c79356b
A
250
251 if (retval != E_NONE)
252 goto error_exit;
253
254 /* only change hfs_fs_ronly after a successfull write */
255 hfsmp->hfs_fs_ronly = 0;
1c79356b
A
256 }
257
258 if ((hfsmp->hfs_fs_ronly == 0) &&
259 (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)) {
260 /* setup private/hidden directory for unlinked files */
261 hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(HFSTOVCB(hfsmp));
262 }
263
264 if (args.fspec == 0) {
265 /*
266 * Process export requests.
267 */
268 return vfs_export(mp, &hfsmp->hfs_export, &args.export);
269 }
270 }
271
272 /*
273 * Not an update, or updating the name: look up the name
274 * and verify that it refers to a sensible block device.
275 */
276 NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
277 retval = namei(ndp);
278 if (retval != E_NONE) {
279 DBG_ERR(("hfs_mount: CAN'T GET DEVICE: %s, %x\n", args.fspec, ndp->ni_vp->v_rdev));
280 goto error_exit;
281 }
282
283 devvp = ndp->ni_vp;
284
285 if (devvp->v_type != VBLK) {
286 vrele(devvp);
287 retval = ENOTBLK;
288 goto error_exit;
289 }
290 if (major(devvp->v_rdev) >= nblkdev) {
291 vrele(devvp);
292 retval = ENXIO;
293 goto error_exit;
294 }
295
296 /*
297 * If mount by non-root, then verify that user has necessary
298 * permissions on the device.
299 */
300 if (p->p_ucred->cr_uid != 0) {
301 accessmode = VREAD;
302 if ((mp->mnt_flag & MNT_RDONLY) == 0)
303 accessmode |= VWRITE;
304 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
305 if ((retval = VOP_ACCESS(devvp, accessmode, p->p_ucred, p))) {
306 vput(devvp);
307 goto error_exit;
308 }
309 VOP_UNLOCK(devvp, 0, p);
310 }
311
312 if ((mp->mnt_flag & MNT_UPDATE) == 0) {
313 retval = hfs_mountfs(devvp, mp, p, &args);
314 if (retval != E_NONE)
315 vrele(devvp);
316 } else {
317 if (devvp != hfsmp->hfs_devvp)
318 retval = EINVAL; /* needs translation */
319 else
320 retval = hfs_changefs(mp, &args, p);
321 vrele(devvp);
322 }
323
324 if (retval != E_NONE) {
325 goto error_exit;
326 }
327
328
329 /* Set the mount flag to indicate that we support volfs */
330 mp->mnt_flag |= MNT_DOVOLFS;
331 if (VFSTOVCB(mp)->vcbSigWord == kHFSSigWord) {
332 /* HFS volumes only want roman-encoded names: */
333 mp->mnt_flag |= MNT_FIXEDSCRIPTENCODING;
334 }
335 (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN-1, &size);
336 bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
337 (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
338 bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
339 (void)hfs_statfs(mp, &mp->mnt_stat, p);
340 return (E_NONE);
341
342error_exit:
343
344 return (retval);
345}
346
347
9bccf70c
A
348/* Change fs mount parameters */
349static int
1c79356b
A
350hfs_changefs(mp, args, p)
351 struct mount *mp;
352 struct hfs_mount_args *args;
353 struct proc *p;
354{
9bccf70c 355 int retval = 0;
1c79356b
A
356 int namefix, permfix, permswitch;
357 struct hfsmount *hfsmp;
9bccf70c 358 struct cnode *cp;
1c79356b 359 ExtendedVCB *vcb;
1c79356b
A
360 register struct vnode *vp, *nvp;
361 hfs_to_unicode_func_t get_unicode_func;
362 unicode_to_hfs_func_t get_hfsname_func;
9bccf70c
A
363 struct cat_desc cndesc;
364 struct cat_attr cnattr;
365 u_long old_encoding;
1c79356b
A
366
367 hfsmp = VFSTOHFS(mp);
368 vcb = HFSTOVCB(hfsmp);
369 permswitch = (((hfsmp->hfs_unknownpermissions != 0) && ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) == 0)) ||
370 ((hfsmp->hfs_unknownpermissions == 0) && ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0)));
0b4e3aa0
A
371 /* The root filesystem must operate with actual permissions: */
372 if (permswitch && (mp->mnt_flag & MNT_ROOTFS) && (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
373 mp->mnt_flag &= ~MNT_UNKNOWNPERMISSIONS; /* Just say "No". */
374 return EINVAL;
375 };
1c79356b
A
376 hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0);
377 namefix = permfix = 0;
378
9bccf70c 379 /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */
1c79356b
A
380 if (args->hfs_timezone.tz_minuteswest != VNOVAL) {
381 gTimeZone = args->hfs_timezone;
382 }
383
9bccf70c 384 /* Change the default uid, gid and/or mask */
1c79356b
A
385 if ((args->hfs_uid != (uid_t)VNOVAL) && (hfsmp->hfs_uid != args->hfs_uid)) {
386 hfsmp->hfs_uid = args->hfs_uid;
9bccf70c
A
387 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
388 ++permfix;
1c79356b
A
389 }
390 if ((args->hfs_gid != (gid_t)VNOVAL) && (hfsmp->hfs_gid != args->hfs_gid)) {
391 hfsmp->hfs_gid = args->hfs_gid;
9bccf70c
A
392 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
393 ++permfix;
1c79356b
A
394 }
395 if (args->hfs_mask != (mode_t)VNOVAL) {
396 if (hfsmp->hfs_dir_mask != (args->hfs_mask & ALLPERMS)) {
397 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
398 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
399 if ((args->flags != VNOVAL) && (args->flags & HFSFSMNT_NOXONFILES))
400 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
9bccf70c
A
401 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
402 ++permfix;
1c79356b
A
403 }
404 }
405
9bccf70c 406 /* Change the hfs encoding value (hfs only) */
1c79356b
A
407 if ((HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) &&
408 (hfsmp->hfs_encoding != (u_long)VNOVAL) &&
409 (hfsmp->hfs_encoding != args->hfs_encoding)) {
410
411 retval = hfs_getconverter(args->hfs_encoding, &get_unicode_func, &get_hfsname_func);
9bccf70c
A
412 if (retval)
413 goto exit;
1c79356b
A
414
415 /*
416 * Connect the new hfs_get_unicode converter but leave
417 * the old hfs_get_hfsname converter in place so that
418 * we can lookup existing vnodes to get their correctly
419 * encoded names.
420 *
421 * When we're all finished, we can then connect the new
422 * hfs_get_hfsname converter and release our interest
423 * in the old converters.
424 */
425 hfsmp->hfs_get_unicode = get_unicode_func;
9bccf70c
A
426 old_encoding = hfsmp->hfs_encoding;
427 hfsmp->hfs_encoding = args->hfs_encoding;
1c79356b
A
428 ++namefix;
429 }
430
9bccf70c
A
431 if (!(namefix || permfix || permswitch))
432 goto exit;
1c79356b
A
433
434 /*
435 * For each active vnode fix things that changed
436 *
437 * Note that we can visit a vnode more than once
438 * and we can race with fsync.
439 */
440 simple_lock(&mntvnode_slock);
441loop:
442 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
443 /*
444 * If the vnode that we are about to fix is no longer
445 * associated with this mount point, start over.
446 */
447 if (vp->v_mount != mp)
448 goto loop;
449
450 simple_lock(&vp->v_interlock);
451 nvp = vp->v_mntvnodes.le_next;
452 if (vp->v_flag & VSYSTEM) {
453 simple_unlock(&vp->v_interlock);
454 continue;
455 }
456 simple_unlock(&mntvnode_slock);
457 retval = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
458 if (retval) {
459 simple_lock(&mntvnode_slock);
460 if (retval == ENOENT)
461 goto loop;
462 continue;
463 }
464
9bccf70c 465 cp = VTOC(vp);
1c79356b 466
9bccf70c 467 retval = cat_lookup(hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL);
1c79356b
A
468 /* If we couldn't find this guy skip to the next one */
469 if (retval) {
470 if (namefix)
471 cache_purge(vp);
472 vput(vp);
473 simple_lock(&mntvnode_slock);
474 continue;
475 }
476
9bccf70c
A
477 if (permswitch || permfix) {
478 cp->c_uid = cnattr.ca_uid;
479 cp->c_gid = cnattr.ca_gid;
480 cp->c_mode = cnattr.ca_mode;
481 }
1c79356b
A
482
483 /*
484 * If we're switching name converters then...
485 * Remove the existing entry from the namei cache.
486 * Update name to one based on new encoder.
487 */
488 if (namefix) {
489 cache_purge(vp);
9bccf70c 490 replace_desc(cp, &cndesc);
1c79356b 491
9bccf70c
A
492 if (cndesc.cd_cnid == kHFSRootFolderID) {
493 strncpy(vcb->vcbVN, cp->c_desc.cd_nameptr, NAME_MAX);
494 cp->c_desc.cd_encoding = hfsmp->hfs_encoding;
495 }
496 } else {
497 cat_releasedesc(&cndesc);
1c79356b 498 }
1c79356b
A
499 vput(vp);
500 simple_lock(&mntvnode_slock);
501
9bccf70c
A
502 } /* end for (vp...) */
503 simple_unlock(&mntvnode_slock);
1c79356b
A
504 /*
505 * If we're switching name converters we can now
506 * connect the new hfs_get_hfsname converter and
507 * release our interest in the old converters.
508 */
509 if (namefix) {
1c79356b 510 hfsmp->hfs_get_hfsname = get_hfsname_func;
1c79356b 511 vcb->volumeNameEncodingHint = args->hfs_encoding;
1c79356b
A
512 (void) hfs_relconverter(old_encoding);
513 }
9bccf70c 514exit:
1c79356b
A
515 return (retval);
516}
517
518
519/*
520 * Reload all incore data for a filesystem (used after running fsck on
521 * the root filesystem and finding things to fix). The filesystem must
522 * be mounted read-only.
523 *
524 * Things to do to update the mount:
9bccf70c
A
525 * invalidate all cached meta-data.
526 * invalidate all inactive vnodes.
527 * invalidate all cached file data.
528 * re-read volume header from disk.
529 * re-load meta-file info (extents, file size).
530 * re-load B-tree header data.
531 * re-read cnode data for all active vnodes.
1c79356b 532 */
9bccf70c 533static int
1c79356b
A
534hfs_reload(mountp, cred, p)
535 register struct mount *mountp;
536 struct ucred *cred;
537 struct proc *p;
538{
539 register struct vnode *vp, *nvp, *devvp;
9bccf70c 540 struct cnode *cp;
1c79356b 541 struct buf *bp;
d52fe63f
A
542 int sectorsize;
543 int error, i;
1c79356b
A
544 struct hfsmount *hfsmp;
545 struct HFSPlusVolumeHeader *vhp;
546 ExtendedVCB *vcb;
9bccf70c
A
547 struct filefork *forkp;
548 struct cat_desc cndesc;
1c79356b
A
549
550 if ((mountp->mnt_flag & MNT_RDONLY) == 0)
551 return (EINVAL);
552
553 hfsmp = VFSTOHFS(mountp);
554 vcb = HFSTOVCB(hfsmp);
555
556 if (vcb->vcbSigWord == kHFSSigWord)
557 return (EINVAL); /* rooting from HFS is not supported! */
558
559 /*
560 * Invalidate all cached meta-data.
561 */
562 devvp = hfsmp->hfs_devvp;
563 if (vinvalbuf(devvp, 0, cred, p, 0, 0))
564 panic("hfs_reload: dirty1");
565 InvalidateCatalogCache(vcb);
566
9bccf70c
A
567loop:
568 simple_lock(&mntvnode_slock);
569 for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
570 if (vp->v_mount != mountp) {
571 simple_unlock(&mntvnode_slock);
572 goto loop;
573 }
574 nvp = vp->v_mntvnodes.le_next;
575
576 /*
577 * Invalidate all inactive vnodes.
578 */
579 if (vrecycle(vp, &mntvnode_slock, p))
580 goto loop;
581
582 /*
583 * Invalidate all cached file data.
584 */
585 simple_lock(&vp->v_interlock);
586 simple_unlock(&mntvnode_slock);
587 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) {
588 goto loop;
589 }
590 if (vinvalbuf(vp, 0, cred, p, 0, 0))
591 panic("hfs_reload: dirty2");
592
593 /*
594 * Re-read cnode data for all active vnodes (non-metadata files).
595 */
596 cp = VTOC(vp);
597 if ((vp->v_flag & VSYSTEM) == 0 && !VNODE_IS_RSRC(vp)) {
598 struct cat_fork *datafork;
599 struct cat_desc desc;
600
601 datafork = cp->c_datafork ? &cp->c_datafork->ff_data : NULL;
602
603 /* lookup by fileID since name could have changed */
604 if ((error = cat_idlookup(hfsmp, cp->c_fileid, &desc, &cp->c_attr, datafork))) {
605 vput(vp);
606 return (error);
607 }
608
609
610 /* update cnode's catalog descriptor */
611 (void) replace_desc(cp, &desc);
612 }
613 vput(vp);
614 simple_lock(&mntvnode_slock);
615 }
616 simple_unlock(&mntvnode_slock);
617
1c79356b
A
618 /*
619 * Re-read VolumeHeader from disk.
620 */
d52fe63f
A
621 sectorsize = hfsmp->hfs_phys_block_size;
622
623 error = meta_bread(hfsmp->hfs_devvp,
624 (vcb->hfsPlusIOPosOffset / sectorsize) + HFS_PRI_SECTOR(sectorsize),
625 sectorsize, NOCRED, &bp);
1c79356b
A
626 if (error) {
627 if (bp != NULL)
628 brelse(bp);
629 return (error);
630 }
631
d52fe63f 632 vhp = (HFSPlusVolumeHeader *) (bp->b_data + HFS_PRI_OFFSET(sectorsize));
1c79356b 633
9bccf70c
A
634 /* Do a quick sanity check */
635 if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord ||
636 SWAP_BE16(vhp->version) != kHFSPlusVersion ||
637 SWAP_BE32(vhp->blockSize) != vcb->blockSize) {
1c79356b 638 brelse(bp);
9bccf70c 639 return (EIO);
1c79356b
A
640 }
641
9bccf70c
A
642 vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate));
643 vcb->vcbAtrb = (UInt16) SWAP_BE32 (vhp->attributes); /* VCB only uses lower 16 bits */
644 vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize);
645 vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID);
646 vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
647 vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount);
648 vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount);
649 vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount);
1c79356b 650 vcb->nextAllocation = SWAP_BE32 (vhp->nextAllocation);
9bccf70c
A
651 vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks);
652 vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks);
1c79356b
A
653 vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap);
654 bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
655 vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* hfs+ create date is in local time */
656
657 /*
658 * Re-load meta-file vnode data (extent info, file size, etc).
659 */
9bccf70c
A
660 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
661 for (i = 0; i < kHFSPlusExtentDensity; i++) {
662 forkp->ff_extents[i].startBlock =
663 SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
664 forkp->ff_extents[i].blockCount =
665 SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
666 }
667 forkp->ff_size = SWAP_BE64 (vhp->extentsFile.logicalSize);
668 forkp->ff_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks);
669 forkp->ff_clumpsize = SWAP_BE32 (vhp->extentsFile.clumpSize);
670
671
672 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
673 for (i = 0; i < kHFSPlusExtentDensity; i++) {
674 forkp->ff_extents[i].startBlock =
675 SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
676 forkp->ff_extents[i].blockCount =
677 SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
678 }
679 forkp->ff_size = SWAP_BE64 (vhp->catalogFile.logicalSize);
680 forkp->ff_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks);
681 forkp->ff_clumpsize = SWAP_BE32 (vhp->catalogFile.clumpSize);
682
683
684 forkp = VTOF((struct vnode *)vcb->allocationsRefNum);
685 for (i = 0; i < kHFSPlusExtentDensity; i++) {
686 forkp->ff_extents[i].startBlock =
687 SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
688 forkp->ff_extents[i].blockCount =
689 SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
690 }
691 forkp->ff_size = SWAP_BE64 (vhp->allocationFile.logicalSize);
692 forkp->ff_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks);
693 forkp->ff_clumpsize = SWAP_BE32 (vhp->allocationFile.clumpSize);
1c79356b
A
694
695 brelse(bp);
696 vhp = NULL;
697
698 /*
699 * Re-load B-tree header data
700 */
9bccf70c
A
701 forkp = VTOF((struct vnode *)vcb->extentsRefNum);
702 if (error = MacToVFSError( BTReloadData((FCB*)forkp) ))
1c79356b
A
703 return (error);
704
9bccf70c
A
705 forkp = VTOF((struct vnode *)vcb->catalogRefNum);
706 if (error = MacToVFSError( BTReloadData((FCB*)forkp) ))
1c79356b
A
707 return (error);
708
9bccf70c
A
709 /* Reload the volume name */
710 if ((error = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, NULL, NULL)))
1c79356b 711 return (error);
9bccf70c
A
712 vcb->volumeNameEncodingHint = cndesc.cd_encoding;
713 bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
714 cat_releasedesc(&cndesc);
1c79356b
A
715
716 /* Re-establish private/hidden directory for unlinked files */
717 hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
718
1c79356b
A
719 return (0);
720}
721
722
723/*
724 * Common code for mount and mountroot
725 */
9bccf70c
A
726static int
727hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p,
728 struct hfs_mount_args *args)
1c79356b 729{
9bccf70c
A
730 int retval = E_NONE;
731 struct hfsmount *hfsmp;
732 struct buf *bp;
733 dev_t dev;
734 HFSMasterDirectoryBlock *mdbp;
735 int ronly;
736 int i;
737 int mntwrapper;
738 struct ucred *cred;
d52fe63f
A
739 u_int64_t disksize;
740 u_int64_t blkcnt;
741 u_int32_t blksize;
742 u_int32_t minblksize;
9bccf70c 743 u_int32_t iswritable;
1c79356b 744
9bccf70c
A
745 dev = devvp->v_rdev;
746 cred = p ? p->p_ucred : NOCRED;
747 mntwrapper = 0;
1c79356b
A
748 /*
749 * Disallow multiple mounts of the same device.
750 * Disallow mounting of a device that is currently in use
751 * (except for root, which might share swap device for miniroot).
752 * Flush out any old buffers remaining from a previous use.
753 */
754 if ((retval = vfs_mountedon(devvp)))
755 return (retval);
756 if ((vcount(devvp) > 1) && (devvp != rootvp))
757 return (EBUSY);
758 if ((retval = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0)))
759 return (retval);
760
761 ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
1c79356b
A
762 if ((retval = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p)))
763 return (retval);
764
d52fe63f
A
765 bp = NULL;
766 hfsmp = NULL;
9bccf70c 767 mdbp = NULL;
d52fe63f 768 minblksize = kHFSBlockSize;
1c79356b 769
d52fe63f
A
770 /* Get the real physical block size. */
771 if (VOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&blksize, 0, cred, p)) {
772 retval = ENXIO;
773 goto error_exit;
774 }
775 /* Switch to 512 byte sectors (temporarily) */
776 if (blksize > 512) {
777 u_int32_t size512 = 512;
778
779 if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&size512, FWRITE, cred, p)) {
780 retval = ENXIO;
781 goto error_exit;
782 }
783 }
784 /* Get the number of 512 byte physical blocks. */
785 if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) {
786 retval = ENXIO;
787 goto error_exit;
788 }
789 /* Compute an accurate disk size (i.e. within 512 bytes) */
790 disksize = blkcnt * (u_int64_t)512;
1c79356b 791
d52fe63f 792 /*
9bccf70c
A
793 * There are only 31 bits worth of block count in
794 * the buffer cache. So for large volumes a 4K
795 * physical block size is needed.
1c79356b 796 */
d52fe63f
A
797 if (blkcnt > (u_int64_t)0x000000007fffffff) {
798 minblksize = blksize = 4096;
799 }
d52fe63f
A
800 /* Now switch to our prefered physical block size. */
801 if (blksize > 512) {
802 if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) {
803 retval = ENXIO;
804 goto error_exit;
805 }
806 /* Get the count of physical blocks. */
807 if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) {
808 retval = ENXIO;
809 goto error_exit;
810 }
811 }
812
813 /*
814 * At this point:
815 * minblksize is the minimum physical block size
816 * blksize has our prefered physical block size
817 * blkcnt has the total number of physical blocks
1c79356b 818 */
1c79356b
A
819 devvp->v_specsize = blksize;
820
0b4e3aa0
A
821 /* cache the IO attributes */
822 if ((retval = vfs_init_io_attributes(devvp, mp))) {
823 printf("hfs_mountfs: vfs_init_io_attributes returned %d\n",
824 retval);
825 return (retval);
826 }
827
d52fe63f
A
828 if ((retval = meta_bread(devvp, HFS_PRI_SECTOR(blksize), blksize, cred, &bp))) {
829 goto error_exit;
830 }
9bccf70c
A
831 MALLOC(mdbp, HFSMasterDirectoryBlock *, kMDBSize, M_TEMP, M_WAITOK);
832 bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, kMDBSize);
833 brelse(bp);
834 bp = NULL;
1c79356b 835
d52fe63f
A
836 MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK);
837 bzero(hfsmp, sizeof(struct hfsmount));
1c79356b
A
838
839 simple_lock_init(&hfsmp->hfs_renamelock);
840
9bccf70c
A
841 /*
842 * Init the volume information structure
843 */
844 mp->mnt_data = (qaddr_t)hfsmp;
845 hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */
846 hfsmp->hfs_vcb.vcb_hfsmp = hfsmp; /* Make VCBTOHFS work */
847 hfsmp->hfs_raw_dev = devvp->v_rdev;
848 hfsmp->hfs_devvp = devvp;
849 hfsmp->hfs_phys_block_size = blksize;
850 hfsmp->hfs_phys_block_count = blkcnt;
851 hfsmp->hfs_media_writeable = 1;
852 hfsmp->hfs_fs_ronly = ronly;
853 hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0);
854 for (i = 0; i < MAXQUOTAS; i++)
855 hfsmp->hfs_qfiles[i].qf_vp = NULLVP;
856
1c79356b
A
857 if (args) {
858 hfsmp->hfs_uid = (args->hfs_uid == (uid_t)VNOVAL) ? UNKNOWNUID : args->hfs_uid;
859 if (hfsmp->hfs_uid == 0xfffffffd) hfsmp->hfs_uid = UNKNOWNUID;
860 hfsmp->hfs_gid = (args->hfs_gid == (gid_t)VNOVAL) ? UNKNOWNGID : args->hfs_gid;
861 if (hfsmp->hfs_gid == 0xfffffffd) hfsmp->hfs_gid = UNKNOWNGID;
862 if (args->hfs_mask != (mode_t)VNOVAL) {
863 hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
864 if (args->flags & HFSFSMNT_NOXONFILES) {
865 hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
866 } else {
867 hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
868 }
869 } else {
870 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
871 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
9bccf70c
A
872 }
873 if ((args->flags != (int)VNOVAL) && (args->flags & HFSFSMNT_WRAPPER))
874 mntwrapper = 1;
1c79356b
A
875 } else {
876 /* Even w/o explicit mount arguments, MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */
877 if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
878 hfsmp->hfs_uid = UNKNOWNUID;
879 hfsmp->hfs_gid = UNKNOWNGID;
880 hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */
881 hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */
9bccf70c
A
882 }
883 }
884
885 /* Find out if disk media is writable. */
886 if (VOP_IOCTL(devvp, DKIOCISWRITABLE, (caddr_t)&iswritable, 0, cred, p) == 0) {
887 if (iswritable)
888 hfsmp->hfs_media_writeable = 1;
889 else
890 hfsmp->hfs_media_writeable = 0;
891 }
1c79356b 892
d52fe63f
A
893 /* Mount a standard HFS disk */
894 if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) &&
9bccf70c 895 (mntwrapper || (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord))) {
d52fe63f
A
896 if (devvp == rootvp) {
897 retval = EINVAL; /* Cannot root from HFS standard disks */
1c79356b 898 goto error_exit;
d52fe63f
A
899 }
900 /* HFS disks can only use 512 byte physical blocks */
901 if (blksize > kHFSBlockSize) {
902 blksize = kHFSBlockSize;
903 if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) {
904 retval = ENXIO;
905 goto error_exit;
906 }
907 if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) {
908 retval = ENXIO;
909 goto error_exit;
910 }
d52fe63f
A
911 devvp->v_specsize = blksize;
912 hfsmp->hfs_phys_block_size = blksize;
913 hfsmp->hfs_phys_block_count = blkcnt;
914 }
1c79356b
A
915 if (args) {
916 hfsmp->hfs_encoding = args->hfs_encoding;
917 HFSTOVCB(hfsmp)->volumeNameEncodingHint = args->hfs_encoding;
918
1c79356b
A
919 /* establish the timezone */
920 gTimeZone = args->hfs_timezone;
921 }
922
9bccf70c
A
923 retval = hfs_getconverter(hfsmp->hfs_encoding, &hfsmp->hfs_get_unicode,
924 &hfsmp->hfs_get_hfsname);
d52fe63f
A
925 if (retval)
926 goto error_exit;
1c79356b 927
d52fe63f 928 retval = hfs_MountHFSVolume(hfsmp, mdbp, p);
1c79356b
A
929 if (retval)
930 (void) hfs_relconverter(hfsmp->hfs_encoding);
931
d52fe63f
A
932 } else /* Mount an HFS Plus disk */ {
933 HFSPlusVolumeHeader *vhp;
934 off_t embeddedOffset;
935
936 /* Get the embedded Volume Header */
937 if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
938 embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * kHFSBlockSize;
939 embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
940 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
941
d52fe63f
A
942 /*
943 * If the embedded volume doesn't start on a block
944 * boundary, then switch the device to a 512-byte
945 * block size so everything will line up on a block
946 * boundary.
947 */
948 if ((embeddedOffset % blksize) != 0) {
949 printf("HFS Mount: embedded volume offset not"
950 " a multiple of physical block size (%d);"
951 " switching to 512\n", blksize);
952 blksize = 512;
953 if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE,
954 (caddr_t)&blksize, FWRITE, cred, p)) {
955 retval = ENXIO;
956 goto error_exit;
957 }
958 if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT,
959 (caddr_t)&blkcnt, 0, cred, p)) {
960 retval = ENXIO;
961 goto error_exit;
962 }
963 /* XXX do we need to call vfs_init_io_attributes again? */
964 devvp->v_specsize = blksize;
965 /* Note: relative block count adjustment */
966 hfsmp->hfs_phys_block_count *=
967 hfsmp->hfs_phys_block_size / blksize;
968 hfsmp->hfs_phys_block_size = blksize;
969 }
970
9bccf70c
A
971 disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) *
972 (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
973
974 hfsmp->hfs_phys_block_count = disksize / blksize;
975
976 retval = meta_bread(devvp, (embeddedOffset / blksize) +
977 HFS_PRI_SECTOR(blksize), blksize, cred, &bp);
d52fe63f
A
978 if (retval)
979 goto error_exit;
9bccf70c
A
980 bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512);
981 brelse(bp);
982 bp = NULL;
983 vhp = (HFSPlusVolumeHeader*) mdbp;
d52fe63f
A
984
985 } else /* pure HFS+ */ {
986 embeddedOffset = 0;
987 vhp = (HFSPlusVolumeHeader*) mdbp;
988 }
989
990 (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
991
992 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p);
993 /*
994 * If the backend didn't like our physical blocksize
995 * then retry with physical blocksize of 512.
996 */
997 if ((retval == ENXIO) && (blksize > 512) && (blksize != minblksize)) {
998 printf("HFS Mount: could not use physical block size "
999 "(%d) switching to 512\n", blksize);
1000 blksize = 512;
1001 if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) {
1002 retval = ENXIO;
1003 goto error_exit;
1004 }
1005 if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) {
1006 retval = ENXIO;
1007 goto error_exit;
1008 }
d52fe63f
A
1009 devvp->v_specsize = blksize;
1010 /* Note: relative block count adjustment (in case this is an embedded volume). */
1011 hfsmp->hfs_phys_block_count *= hfsmp->hfs_phys_block_size / blksize;
1012 hfsmp->hfs_phys_block_size = blksize;
1013
1014 /* Try again with a smaller block size... */
1015 retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p);
1016 }
1017 if (retval)
1018 (void) hfs_relconverter(0);
1019 }
1c79356b
A
1020
1021 if ( retval ) {
1022 goto error_exit;
1023 }
1024
9bccf70c
A
1025 mp->mnt_stat.f_fsid.val[0] = (long)dev;
1026 mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
1027 mp->mnt_maxsymlinklen = 0;
1028 devvp->v_specflags |= SI_MOUNTEDON;
1c79356b 1029
9bccf70c
A
1030 if (ronly == 0) {
1031 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1032 }
1033 FREE(mdbp, M_TEMP);
1034 return (0);
1c79356b 1035
9bccf70c
A
1036error_exit:
1037 if (bp)
1038 brelse(bp);
1039 if (mdbp)
1040 FREE(mdbp, M_TEMP);
1041 (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
1042 if (hfsmp) {
1043 FREE(hfsmp, M_HFSMNT);
1044 mp->mnt_data = (qaddr_t)0;
1045 }
1c79356b
A
1046 return (retval);
1047}
1048
1049
1050/*
1051 * Make a filesystem operational.
1052 * Nothing to do at the moment.
1053 */
1054/* ARGSUSED */
9bccf70c
A
1055static int
1056hfs_start(mp, flags, p)
1057 struct mount *mp;
1058 int flags;
1059 struct proc *p;
1c79356b 1060{
9bccf70c 1061 return (0);
1c79356b
A
1062}
1063
1064
1065/*
1066 * unmount system call
1067 */
9bccf70c 1068static int
1c79356b
A
1069hfs_unmount(mp, mntflags, p)
1070 struct mount *mp;
1071 int mntflags;
1072 struct proc *p;
1073{
1074 struct hfsmount *hfsmp = VFSTOHFS(mp);
1075 int retval = E_NONE;
1076 int flags;
9bccf70c 1077 int force;
1c79356b
A
1078
1079 flags = 0;
9bccf70c
A
1080 force = 0;
1081 if (mntflags & MNT_FORCE) {
1c79356b 1082 flags |= FORCECLOSE;
9bccf70c
A
1083 force = 1;
1084 }
1c79356b 1085
9bccf70c 1086 if ((retval = hfs_flushfiles(mp, flags, p)) && !force)
1c79356b
A
1087 return (retval);
1088
1089 /*
1090 * Flush out the b-trees, volume bitmap and Volume Header
1091 */
1092 if (hfsmp->hfs_fs_ronly == 0) {
1093 retval = VOP_FSYNC(HFSTOVCB(hfsmp)->catalogRefNum, NOCRED, MNT_WAIT, p);
9bccf70c 1094 if (retval && !force)
1c79356b
A
1095 return (retval);
1096
1097 retval = VOP_FSYNC(HFSTOVCB(hfsmp)->extentsRefNum, NOCRED, MNT_WAIT, p);
9bccf70c 1098 if (retval && !force)
1c79356b
A
1099 return (retval);
1100
1101 if (retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p)) {
9bccf70c 1102 if (!force)
1c79356b
A
1103 return (retval);
1104 }
1105
1106 /* See if this volume is damaged, is so do not unmount cleanly */
1107 if (HFSTOVCB(hfsmp)->vcbFlags & kHFS_DamagedVolume) {
1c79356b
A
1108 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
1109 } else {
9bccf70c 1110 HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
1c79356b 1111 }
9bccf70c
A
1112
1113 retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
1c79356b 1114 if (retval) {
1c79356b 1115 HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
9bccf70c 1116 if (!force)
1c79356b
A
1117 return (retval); /* could not flush everything */
1118 }
1119 }
1120
1121 /*
1122 * Invalidate our caches and release metadata vnodes
1123 */
1124 (void) hfsUnmount(hfsmp, p);
1125
1126 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
1127 (void) hfs_relconverter(hfsmp->hfs_encoding);
1128
1129 hfsmp->hfs_devvp->v_specflags &= ~SI_MOUNTEDON;
9bccf70c
A
1130 retval = VOP_CLOSE(hfsmp->hfs_devvp,
1131 hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE,
1132 NOCRED, p);
1133 if (retval && !force)
1134 return(retval);
1c79356b 1135
9bccf70c 1136 vrele(hfsmp->hfs_devvp);
1c79356b
A
1137 FREE(hfsmp, M_HFSMNT);
1138 mp->mnt_data = (qaddr_t)0;
9bccf70c 1139 return (0);
1c79356b
A
1140}
1141
1142
1143/*
1144 * Return the root of a filesystem.
1145 *
1146 * OUT - vpp, should be locked and vget()'d (to increment usecount and lock)
1147 */
9bccf70c
A
1148static int
1149hfs_root(mp, vpp)
1150 struct mount *mp;
1151 struct vnode **vpp;
1c79356b 1152{
9bccf70c
A
1153 struct vnode *nvp;
1154 int retval;
1155 UInt32 rootObjID = kRootDirID;
1c79356b 1156
9bccf70c
A
1157 if ((retval = VFS_VGET(mp, &rootObjID, &nvp)))
1158 return (retval);
1c79356b 1159
9bccf70c
A
1160 *vpp = nvp;
1161 return (0);
1c79356b
A
1162}
1163
1164
1165/*
1166 * Do operations associated with quotas
1167 */
9bccf70c
A
1168int
1169hfs_quotactl(mp, cmds, uid, arg, p)
1170 struct mount *mp;
1171 int cmds;
1172 uid_t uid;
1173 caddr_t arg;
1174 struct proc *p;
1c79356b 1175{
9bccf70c
A
1176 int cmd, type, error;
1177
1178#if !QUOTA
1179 return (EOPNOTSUPP);
1180#else
1181 if (uid == -1)
1182 uid = p->p_cred->p_ruid;
1183 cmd = cmds >> SUBCMDSHIFT;
1184
1185 switch (cmd) {
1186 case Q_SYNC:
1187 case Q_QUOTASTAT:
1188 break;
1189 case Q_GETQUOTA:
1190 if (uid == p->p_cred->p_ruid)
1191 break;
1192 /* fall through */
1193 default:
1194 if (error = suser(p->p_ucred, &p->p_acflag))
1195 return (error);
1196 }
1197
1198 type = cmds & SUBCMDMASK;
1199 if ((u_int)type >= MAXQUOTAS)
1200 return (EINVAL);
1201 if (vfs_busy(mp, LK_NOWAIT, 0, p))
1202 return (0);
1203
1204 switch (cmd) {
1c79356b 1205
9bccf70c
A
1206 case Q_QUOTAON:
1207 error = hfs_quotaon(p, mp, type, arg, UIO_USERSPACE);
1208 break;
1209
1210 case Q_QUOTAOFF:
1211 error = hfs_quotaoff(p, mp, type);
1212 break;
1213
1214 case Q_SETQUOTA:
1215 error = hfs_setquota(mp, uid, type, arg);
1216 break;
1217
1218 case Q_SETUSE:
1219 error = hfs_setuse(mp, uid, type, arg);
1220 break;
1221
1222 case Q_GETQUOTA:
1223 error = hfs_getquota(mp, uid, type, arg);
1224 break;
1225
1226 case Q_SYNC:
1227 error = hfs_qsync(mp);
1228 break;
1229
1230 case Q_QUOTASTAT:
1231 error = hfs_quotastat(mp, type, arg);
1232 break;
1233
1234 default:
1235 error = EINVAL;
1236 break;
1237 }
1238 vfs_unbusy(mp, p);
1239 return (error);
1240#endif /* QUOTA */
1c79356b
A
1241}
1242
1243
1244/*
1245 * Get file system statistics.
1246 */
1247static int
1248hfs_statfs(mp, sbp, p)
1249 struct mount *mp;
1250 register struct statfs *sbp;
1251 struct proc *p;
1252{
1253 ExtendedVCB *vcb = VFSTOVCB(mp);
1254 struct hfsmount *hfsmp = VFSTOHFS(mp);
1255 u_long freeCNIDs;
1256
1c79356b
A
1257 freeCNIDs = (u_long)0xFFFFFFFF - (u_long)vcb->vcbNxtCNID;
1258
1259 sbp->f_bsize = vcb->blockSize;
1260 sbp->f_iosize = hfsmp->hfs_logBlockSize;
1261 sbp->f_blocks = vcb->totalBlocks;
9bccf70c
A
1262 sbp->f_bfree = hfs_freeblks(hfsmp, 0);
1263 sbp->f_bavail = hfs_freeblks(hfsmp, 1);
1c79356b 1264 sbp->f_files = vcb->totalBlocks - 2; /* max files is constrained by total blocks */
9bccf70c 1265 sbp->f_ffree = MIN(freeCNIDs, sbp->f_bavail);
1c79356b
A
1266
1267 sbp->f_type = 0;
1268 if (sbp != &mp->mnt_stat) {
1269 sbp->f_type = mp->mnt_vfc->vfc_typenum;
1270 bcopy((caddr_t)mp->mnt_stat.f_mntonname,
1271 (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
1272 bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
1273 (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
1274 }
1275 return (0);
1276}
1277
1278
1279/*
1280 * Go through the disk queues to initiate sandbagged IO;
1281 * go through the inodes to write those that have been modified;
1282 * initiate the writing of the super block if it has been modified.
1283 *
1284 * Note: we are always called with the filesystem marked `MPBUSY'.
1285 */
9bccf70c
A
1286static int
1287hfs_sync(mp, waitfor, cred, p)
1288 struct mount *mp;
1289 int waitfor;
1290 struct ucred *cred;
1291 struct proc *p;
1c79356b 1292{
9bccf70c
A
1293 struct vnode *nvp, *vp;
1294 struct cnode *cp;
1295 struct hfsmount *hfsmp;
1296 ExtendedVCB *vcb;
1297 struct vnode *meta_vp[3];
1298 int i;
1299 int error, allerror = 0;
1c79356b
A
1300
1301 /*
1302 * During MNT_UPDATE hfs_changefs might be manipulating
1303 * vnodes so back off
1304 */
1305 if (mp->mnt_flag & MNT_UPDATE)
1306 return (0);
1307
9bccf70c
A
1308 hfsmp = VFSTOHFS(mp);
1309 if (hfsmp->hfs_fs_ronly != 0) {
1310 panic("update: rofs mod");
1311 };
1c79356b 1312
9bccf70c
A
1313 /*
1314 * Write back each 'modified' vnode
1315 */
1c79356b 1316
9bccf70c
A
1317loop:
1318 simple_lock(&mntvnode_slock);
1319 for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
0b4e3aa0 1320 int didhold;
9bccf70c
A
1321 /*
1322 * If the vnode that we are about to sync is no longer
1323 * associated with this mount point, start over.
1324 */
1325 if (vp->v_mount != mp) {
1326 simple_unlock(&mntvnode_slock);
1327 goto loop;
1328 }
1329 simple_lock(&vp->v_interlock);
1330 nvp = vp->v_mntvnodes.le_next;
1331 cp = VTOC(vp);
1332
1333 if ((vp->v_flag & VSYSTEM) || (vp->v_type == VNON) ||
1334 (((cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) == 0) &&
1335 (vp->v_dirtyblkhd.lh_first == NULL) && !(vp->v_flag & VHASDIRTY))) {
1336 simple_unlock(&vp->v_interlock);
1337 simple_unlock(&mntvnode_slock);
1338 simple_lock(&mntvnode_slock);
1339 continue;
1340 }
1341
1342 simple_unlock(&mntvnode_slock);
1343 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
1344 if (error) {
1345 if (error == ENOENT)
1346 goto loop;
1347 simple_lock(&mntvnode_slock);
1348 continue;
1349 }
1c79356b 1350
0b4e3aa0 1351 didhold = ubc_hold(vp);
9bccf70c
A
1352 if ((error = VOP_FSYNC(vp, cred, waitfor, p))) {
1353 allerror = error;
1354 };
1355 VOP_UNLOCK(vp, 0, p);
0b4e3aa0
A
1356 if (didhold)
1357 ubc_rele(vp);
9bccf70c
A
1358 vrele(vp);
1359 simple_lock(&mntvnode_slock);
1360 };
1c79356b 1361
9bccf70c
A
1362 vcb = HFSTOVCB(hfsmp);
1363
1364 meta_vp[0] = vcb->extentsRefNum;
1365 meta_vp[1] = vcb->catalogRefNum;
1366 meta_vp[2] = vcb->allocationsRefNum; /* This is NULL for standard HFS */
1367
1368 /* Now sync our three metadata files */
1369 for (i = 0; i < 3; ++i) {
1370 struct vnode *btvp;
1371
1372 btvp = btvp = meta_vp[i];;
1373 if ((btvp==0) || (btvp->v_type == VNON) || (btvp->v_mount != mp))
1374 continue;
1375 simple_lock(&btvp->v_interlock);
1376 cp = VTOC(btvp);
1377 if (((cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) == 0) &&
1378 (btvp->v_dirtyblkhd.lh_first == NULL) && !(btvp->v_flag & VHASDIRTY)) {
1379 simple_unlock(&btvp->v_interlock);
1380 continue;
1381 }
1382 simple_unlock(&mntvnode_slock);
1383 error = vget(btvp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
1384 if (error) {
1385 simple_lock(&mntvnode_slock);
1386 continue;
1387 }
1388 if ((error = VOP_FSYNC(btvp, cred, waitfor, p)))
1389 allerror = error;
1390 VOP_UNLOCK(btvp, 0, p);
1391 vrele(btvp);
1392 simple_lock(&mntvnode_slock);
1393 };
1394
1395 simple_unlock(&mntvnode_slock);
1396
1397 /*
1398 * Force stale file system control information to be flushed.
1399 */
1400 if (vcb->vcbSigWord == kHFSSigWord) {
1401 if ((error = VOP_FSYNC(hfsmp->hfs_devvp, cred, waitfor, p)))
1402 allerror = error;
1403 }
1404#if QUOTA
1405 hfs_qsync(mp);
1406#endif /* QUOTA */
1407 /*
1408 * Write back modified superblock.
1409 */
1410
1411 if (IsVCBDirty(vcb)) {
1412 error = hfs_flushvolumeheader(hfsmp, waitfor, 0);
1413 if (error)
1414 allerror = error;
1415 }
1c79356b 1416
9bccf70c 1417 return (allerror);
1c79356b
A
1418}
1419
1420
1421/*
1422 * File handle to vnode
1423 *
1424 * Have to be really careful about stale file handles:
9bccf70c
A
1425 * - check that the cnode id is valid
1426 * - call hfs_vget() to get the locked cnode
1427 * - check for an unallocated cnode (i_mode == 0)
1c79356b
A
1428 * - check that the given client host has export rights and return
1429 * those rights via. exflagsp and credanonp
1430 */
9bccf70c 1431static int
1c79356b 1432hfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
9bccf70c
A
1433 register struct mount *mp;
1434 struct fid *fhp;
1435 struct mbuf *nam;
1436 struct vnode **vpp;
1437 int *exflagsp;
1438 struct ucred **credanonp;
1c79356b
A
1439{
1440 struct hfsfid *hfsfhp;
1441 struct vnode *nvp;
1442 int result;
1443 struct netcred *np;
1c79356b
A
1444
1445 *vpp = NULL;
1446 hfsfhp = (struct hfsfid *)fhp;
1447
1448 /*
1449 * Get the export permission structure for this <mp, client> tuple.
1450 */
1451 np = vfs_export_lookup(mp, &VFSTOHFS(mp)->hfs_export, nam);
1452 if (np == NULL) {
1453 return EACCES;
1454 };
1455
1456 result = VFS_VGET(mp, &hfsfhp->hfsfid_cnid, &nvp);
1457 if (result) return result;
1458 if (nvp == NULL) return ESTALE;
1459
0b4e3aa0
A
1460 /* The createtime can be changed by hfs_setattr or hfs_setattrlist.
1461 * For NFS, we are assuming that only if the createtime was moved
1462 * forward would it mean the fileID got reused in that session by
1463 * wrapping. We don't have a volume ID or other unique identifier to
1464 * to use here for a generation ID across reboots, crashes where
1465 * metadata noting lastFileID didn't make it to disk but client has
1466 * it, or volume erasures where fileIDs start over again. Lastly,
1467 * with HFS allowing "wraps" of fileIDs now, this becomes more
1468 * error prone. Future, would be change the "wrap bit" to a unique
1469 * wrap number and use that for generation number. For now do this.
1470 */
9bccf70c 1471 if ((hfsfhp->hfsfid_gen < VTOC(nvp)->c_itime)) {
1c79356b 1472 vput(nvp);
9bccf70c 1473 return (ESTALE);
1c79356b
A
1474 };
1475
1476 *vpp = nvp;
1477 *exflagsp = np->netc_exflags;
1478 *credanonp = &np->netc_anon;
1479
9bccf70c 1480 return (0);
1c79356b
A
1481}
1482
1483
1484/*
1485 * Vnode pointer to File handle
1486 */
1487/* ARGSUSED */
9bccf70c
A
1488static int
1489hfs_vptofh(vp, fhp)
1490 struct vnode *vp;
1491 struct fid *fhp;
1c79356b 1492{
9bccf70c 1493 struct cnode *cp;
1c79356b 1494 struct hfsfid *hfsfhp;
1c79356b 1495
9bccf70c
A
1496 if (ISHFS(VTOVCB(vp)))
1497 return (EOPNOTSUPP); /* hfs standard is not exportable */
1c79356b 1498
9bccf70c
A
1499 cp = VTOC(vp);
1500 hfsfhp = (struct hfsfid *)fhp;
1c79356b
A
1501 hfsfhp->hfsfid_len = sizeof(struct hfsfid);
1502 hfsfhp->hfsfid_pad = 0;
9bccf70c
A
1503 hfsfhp->hfsfid_cnid = cp->c_cnid;
1504 hfsfhp->hfsfid_gen = cp->c_itime;
1c79356b 1505
9bccf70c 1506 return (0);
1c79356b
A
1507}
1508
1509
1510/*
1511 * Initial HFS filesystems, done only once.
1512 */
9bccf70c 1513static int
1c79356b 1514hfs_init(vfsp)
9bccf70c 1515 struct vfsconf *vfsp;
1c79356b 1516{
9bccf70c 1517 static int done = 0;
1c79356b 1518
9bccf70c
A
1519 if (done)
1520 return (0);
1521 done = 1;
1522 hfs_chashinit();
1523 hfs_converterinit();
1524#if QUOTA
1525 dqinit();
1526#endif /* QUOTA */
1c79356b 1527
1c79356b
A
1528 /*
1529 * Allocate Catalog Iterator cache...
1530 */
9bccf70c 1531 (void) InitCatalogCache();
1c79356b 1532
9bccf70c 1533 return (0);
1c79356b
A
1534}
1535
1536
1537/*
9bccf70c 1538 * HFS filesystem related variables.
1c79356b 1539 */
9bccf70c
A
1540static int
1541hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
1542 int *name;
1543 u_int namelen;
1544 void *oldp;
1545 size_t *oldlenp;
1546 void *newp;
1547 size_t newlen;
1548 struct proc *p;
1c79356b 1549{
9bccf70c
A
1550 extern u_int32_t hfs_encodingbias;
1551
1552 /* all sysctl names at this level are terminal */
1553 if (namelen != 1)
1554 return (ENOTDIR); /* overloaded */
1c79356b 1555
9bccf70c
A
1556 if (name[0] == HFS_ENCODINGBIAS)
1557 return (sysctl_int(oldp, oldlenp, newp, newlen,
1558 &hfs_encodingbias));
1559
1560 return (EOPNOTSUPP);
1c79356b
A
1561}
1562
1563
1564/* This will return a vnode of either a directory or a data vnode based on an object id. If
1565 * it is a file id, its data fork will be returned.
1566 */
9bccf70c
A
1567static int
1568hfs_vget(mp, ino, vpp)
1569 struct mount *mp;
1570 void *ino;
1571 struct vnode **vpp;
1572{
1573 cnid_t cnid = *(cnid_t *)ino;
1574
1575 /* Check for cnids that should't be exported. */
1576 if ((cnid < kHFSFirstUserCatalogNodeID)
1577 && (cnid != kHFSRootFolderID && cnid != kHFSRootParentID))
1578 return (ENOENT);
1579 /* Don't export HFS Private Data dir. */
1580 if (cnid == VFSTOHFS(mp)->hfs_privdir_desc.cd_cnid)
1581 return (ENOENT);
1582
1583 return (hfs_getcnode(VFSTOHFS(mp), cnid, NULL, 0, NULL, NULL, vpp));
1584}
1585
1586/*
1587 * Flush out all the files in a filesystem.
1588 */
1c79356b 1589int
9bccf70c 1590hfs_flushfiles(struct mount *mp, int flags, struct proc *p)
1c79356b 1591{
9bccf70c
A
1592 register struct hfsmount *hfsmp;
1593 int i;
1594 int error;
1c79356b 1595
9bccf70c
A
1596#if QUOTA
1597 hfsmp = VFSTOHFS(mp);
1c79356b 1598
9bccf70c
A
1599 if (mp->mnt_flag & MNT_QUOTA) {
1600 if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags))
1601 return (error);
1602 for (i = 0; i < MAXQUOTAS; i++) {
1603 if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP)
1604 continue;
1605 hfs_quotaoff(p, mp, i);
1606 }
1607 /*
1608 * Here we fall through to vflush again to ensure
1609 * that we have gotten rid of all the system vnodes.
1610 */
1c79356b 1611 }
9bccf70c 1612#endif /* QUOTA */
1c79356b 1613
9bccf70c
A
1614 error = vflush(mp, NULLVP, (SKIPSYSTEM | SKIPSWAP | flags));
1615 error = vflush(mp, NULLVP, (SKIPSYSTEM | flags));
1c79356b 1616
9bccf70c
A
1617 return (error);
1618}
1c79356b 1619
9bccf70c
A
1620/*
1621 * Update volume encoding bitmap (HFS Plus only)
1622 */
1623__private_extern__
1624void
1625hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding)
1626{
1627#define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */
1628#define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */
1629
1630 UInt32 index;
1631
1632 switch (encoding) {
1633 case kTextEncodingMacUkrainian:
1634 index = kIndexMacUkrainian;
1635 break;
1636 case kTextEncodingMacFarsi:
1637 index = kIndexMacFarsi;
1638 break;
1639 default:
1640 index = encoding;
1641 break;
1642 }
1c79356b 1643
9bccf70c
A
1644 if (index < 128) {
1645 HFSTOVCB(hfsmp)->encodingsBitmap |= (1 << index);
1646 HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00;
1647 }
1c79356b
A
1648}
1649
1650/*
9bccf70c 1651 * Update volume stats
1c79356b 1652 */
9bccf70c 1653__private_extern__
1c79356b 1654int
9bccf70c 1655hfs_volupdate(struct hfsmount *hfsmp, enum volop op, int inroot)
1c79356b 1656{
9bccf70c 1657 ExtendedVCB *vcb;
1c79356b 1658
9bccf70c
A
1659 vcb = HFSTOVCB(hfsmp);
1660 vcb->vcbFlags |= 0xFF00;
1661 vcb->vcbLsMod = time.tv_sec;
1662
1663 switch (op) {
1664 case VOL_UPDATE:
1665 break;
1666 case VOL_MKDIR:
1667 if (vcb->vcbDirCnt != 0xFFFFFFFF)
1668 ++vcb->vcbDirCnt;
1669 if (inroot && vcb->vcbNmRtDirs != 0xFFFF)
1670 ++vcb->vcbNmRtDirs;
1671 break;
1672 case VOL_RMDIR:
1673 if (vcb->vcbDirCnt != 0)
1674 --vcb->vcbDirCnt;
1675 if (inroot && vcb->vcbNmRtDirs != 0xFFFF)
1676 --vcb->vcbNmRtDirs;
1677 break;
1678 case VOL_MKFILE:
1679 if (vcb->vcbFilCnt != 0xFFFFFFFF)
1680 ++vcb->vcbFilCnt;
1681 if (inroot && vcb->vcbNmFls != 0xFFFF)
1682 ++vcb->vcbNmFls;
1683 break;
1684 case VOL_RMFILE:
1685 if (vcb->vcbFilCnt != 0)
1686 --vcb->vcbFilCnt;
1687 if (inroot && vcb->vcbNmFls != 0xFFFF)
1688 --vcb->vcbNmFls;
1689 break;
1690 }
1691 return (0);
1c79356b
A
1692}
1693
9bccf70c
A
1694
1695static int
1696hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush)
1c79356b 1697{
9bccf70c
A
1698 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
1699 struct filefork *fp;
1c79356b 1700 HFSMasterDirectoryBlock *mdb;
9bccf70c
A
1701 struct buf *bp = NULL;
1702 int retval;
1703 int sectorsize;
1704 ByteCount namelen;
1c79356b 1705
9bccf70c 1706 sectorsize = hfsmp->hfs_phys_block_size;
1c79356b 1707
9bccf70c 1708 retval = bread(hfsmp->hfs_devvp, HFS_PRI_SECTOR(sectorsize), sectorsize, NOCRED, &bp);
1c79356b 1709 if (retval) {
9bccf70c
A
1710 if (bp)
1711 brelse(bp);
1c79356b
A
1712 return retval;
1713 }
1714
9bccf70c
A
1715 DBG_ASSERT(bp != NULL);
1716 DBG_ASSERT(bp->b_data != NULL);
1717 DBG_ASSERT(bp->b_bcount == size);
1c79356b 1718
9bccf70c 1719 mdb = (HFSMasterDirectoryBlock *)(bp->b_data + HFS_PRI_OFFSET(sectorsize));
1c79356b 1720
9bccf70c
A
1721 mdb->drCrDate = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbCrDate)));
1722 mdb->drLsMod = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbLsMod)));
1723 mdb->drAtrb = SWAP_BE16 (vcb->vcbAtrb);
1c79356b
A
1724 mdb->drNmFls = SWAP_BE16 (vcb->vcbNmFls);
1725 mdb->drAllocPtr = SWAP_BE16 (vcb->nextAllocation);
1726 mdb->drClpSiz = SWAP_BE32 (vcb->vcbClpSiz);
1727 mdb->drNxtCNID = SWAP_BE32 (vcb->vcbNxtCNID);
1728 mdb->drFreeBks = SWAP_BE16 (vcb->freeBlocks);
1729
1730 namelen = strlen(vcb->vcbVN);
1731 retval = utf8_to_hfs(vcb, namelen, vcb->vcbVN, mdb->drVN);
1732 /* Retry with MacRoman in case that's how it was exported. */
1733 if (retval)
1734 retval = utf8_to_mac_roman(namelen, vcb->vcbVN, mdb->drVN);
1735
9bccf70c 1736 mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbVolBkUp)));
1c79356b
A
1737 mdb->drWrCnt = SWAP_BE32 (vcb->vcbWrCnt);
1738 mdb->drNmRtDirs = SWAP_BE16 (vcb->vcbNmRtDirs);
1739 mdb->drFilCnt = SWAP_BE32 (vcb->vcbFilCnt);
1740 mdb->drDirCnt = SWAP_BE32 (vcb->vcbDirCnt);
1741
1742 bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo));
1743
9bccf70c
A
1744 fp = VTOF(vcb->extentsRefNum);
1745 mdb->drXTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
1746 mdb->drXTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
1747 mdb->drXTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
1748 mdb->drXTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
1749 mdb->drXTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
1750 mdb->drXTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
1751 mdb->drXTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
1752 mdb->drXTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
1c79356b 1753
9bccf70c
A
1754 fp = VTOF(vcb->catalogRefNum);
1755 mdb->drCTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock);
1756 mdb->drCTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount);
1757 mdb->drCTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock);
1758 mdb->drCTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount);
1759 mdb->drCTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock);
1760 mdb->drCTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount);
1761 mdb->drCTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize);
1762 mdb->drCTClpSiz = SWAP_BE32 (fp->ff_clumpsize);
1763
1764 /* If requested, flush out the alternate MDB */
1765 if (altflush) {
1766 struct buf *alt_bp = NULL;
1767 u_long altIDSector;
1768
1769 altIDSector = HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count);
1770
1771 if (meta_bread(hfsmp->hfs_devvp, altIDSector, sectorsize, NOCRED, &alt_bp) == 0) {
1772 bcopy(mdb, alt_bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize);
1773 (void) VOP_BWRITE(alt_bp);
1774 } else if (alt_bp)
1775 brelse(alt_bp);
1776 }
1c79356b 1777
9bccf70c 1778 if (waitfor != MNT_WAIT)
1c79356b 1779 bawrite(bp);
9bccf70c 1780 else
1c79356b
A
1781 retval = VOP_BWRITE(bp);
1782
1783 MarkVCBClean( vcb );
1784
1785 return (retval);
1786}
1787
1788
9bccf70c
A
1789__private_extern__
1790int
1791hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush)
1c79356b 1792{
9bccf70c
A
1793 ExtendedVCB *vcb = HFSTOVCB(hfsmp);
1794 struct filefork *fp;
1795 HFSPlusVolumeHeader *volumeHeader;
1796 int retval;
1797 struct buf *bp;
1798 int i;
d52fe63f
A
1799 int sectorsize;
1800 int priIDSector;
9bccf70c 1801 int critical = 0;
1c79356b 1802
9bccf70c
A
1803 if (vcb->vcbSigWord == kHFSSigWord)
1804 return hfs_flushMDB(hfsmp, waitfor, altflush);
1c79356b 1805
9bccf70c
A
1806 if (altflush)
1807 critical = 1;
d52fe63f
A
1808 sectorsize = hfsmp->hfs_phys_block_size;
1809 priIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) +
1810 HFS_PRI_SECTOR(sectorsize);
1811
1812 retval = meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp);
1c79356b 1813 if (retval) {
9bccf70c
A
1814 if (bp)
1815 brelse(bp);
1816 return (retval);
1c79356b
A
1817 }
1818
d52fe63f 1819 volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data + HFS_PRI_OFFSET(sectorsize));
1c79356b
A
1820
1821 /*
1822 * For embedded HFS+ volumes, update create date if it changed
1823 * (ie from a setattrlist call)
1824 */
9bccf70c
A
1825 if ((vcb->hfsPlusIOPosOffset != 0) &&
1826 (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate)) {
1827 struct buf *bp2;
1c79356b
A
1828 HFSMasterDirectoryBlock *mdb;
1829
9bccf70c
A
1830 retval = meta_bread(hfsmp->hfs_devvp, HFS_PRI_SECTOR(sectorsize),
1831 sectorsize, NOCRED, &bp2);
1832 if (retval) {
1833 if (bp2)
1834 brelse(bp2);
1835 retval = 0;
1c79356b 1836 } else {
9bccf70c
A
1837 mdb = (HFSMasterDirectoryBlock *)(bp2->b_data +
1838 HFS_PRI_OFFSET(sectorsize));
1c79356b
A
1839
1840 if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate )
1841 {
1842 mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate); /* pick up the new create date */
1843
1844 (void) VOP_BWRITE(bp2); /* write out the changes */
1845 }
1846 else
1847 {
1848 brelse(bp2); /* just release it */
1849 }
1850 }
9bccf70c 1851 }
1c79356b 1852
1c79356b 1853 /* Note: only update the lower 16 bits worth of attributes */
9bccf70c
A
1854 volumeHeader->attributes = SWAP_BE32 ((SWAP_BE32 (volumeHeader->attributes) & 0xFFFF0000) + (UInt16) vcb->vcbAtrb);
1855 volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion);
1856 volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate); /* volume create date is in local time */
1857 volumeHeader->modifyDate = SWAP_BE32 (to_hfs_time(vcb->vcbLsMod));
1858 volumeHeader->backupDate = SWAP_BE32 (to_hfs_time(vcb->vcbVolBkUp));
1859 volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt);
1860 volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt);
1861 volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks);
1862 volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation);
1863 volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
1864 volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz);
1865 volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID);
1866 volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt);
1867 volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap);
1868
1869 if (bcmp(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)) != 0)
1870 critical = 1;
1871 bcopy(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo));
1872
1873 /* Sync Extents over-flow file meta data */
1874 fp = VTOF(vcb->extentsRefNum);
1875 for (i = 0; i < kHFSPlusExtentDensity; i++) {
1876 volumeHeader->extentsFile.extents[i].startBlock =
1877 SWAP_BE32 (fp->ff_extents[i].startBlock);
1878 volumeHeader->extentsFile.extents[i].blockCount =
1879 SWAP_BE32 (fp->ff_extents[i].blockCount);
1880 }
1881 FTOC(fp)->c_flag &= ~C_MODIFIED;
1882 volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fp->ff_size);
1883 volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
1884 volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
1885
1886 /* Sync Catalog file meta data */
1887 fp = VTOF(vcb->catalogRefNum);
1888 for (i = 0; i < kHFSPlusExtentDensity; i++) {
1889 volumeHeader->catalogFile.extents[i].startBlock =
1890 SWAP_BE32 (fp->ff_extents[i].startBlock);
1891 volumeHeader->catalogFile.extents[i].blockCount =
1892 SWAP_BE32 (fp->ff_extents[i].blockCount);
1893 }
1894 FTOC(fp)->c_flag &= ~C_MODIFIED;
1895 volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fp->ff_size);
1896 volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
1897 volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
1898
1899 /* Sync Allocation file meta data */
1900 fp = VTOF(vcb->allocationsRefNum);
1901 for (i = 0; i < kHFSPlusExtentDensity; i++) {
1902 volumeHeader->allocationFile.extents[i].startBlock =
1903 SWAP_BE32 (fp->ff_extents[i].startBlock);
1904 volumeHeader->allocationFile.extents[i].blockCount =
1905 SWAP_BE32 (fp->ff_extents[i].blockCount);
1906 }
1907 FTOC(fp)->c_flag &= ~C_MODIFIED;
1908 volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fp->ff_size);
1909 volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fp->ff_blocks);
1910 volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize);
1911
1912 /* If requested, flush out the alternate volume header */
1913 if (altflush) {
1914 struct buf *alt_bp = NULL;
1915 u_long altIDSector;
1916
1917 altIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) +
1918 HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count);
1919
1920 if (meta_bread(hfsmp->hfs_devvp, altIDSector, sectorsize, NOCRED, &alt_bp) == 0) {
1921 bcopy(volumeHeader, alt_bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize);
1922 (void) VOP_BWRITE(alt_bp);
1923 } else if (alt_bp)
1924 brelse(alt_bp);
1925 }
1926
1927 if (waitfor != MNT_WAIT)
1928 bawrite(bp);
1929 else {
1c79356b 1930 retval = VOP_BWRITE(bp);
9bccf70c
A
1931 /* When critical data changes, flush the device cache */
1932 if (critical && (retval == 0)) {
1933 (void) VOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE,
1934 NULL, FWRITE, NOCRED, current_proc());
1935 }
1936 }
1c79356b 1937
9bccf70c 1938 vcb->vcbFlags &= 0x00FF;
1c79356b
A
1939 return (retval);
1940}
1941
1942
1c79356b
A
1943/*
1944 * hfs vfs operations.
1945 */
1946struct vfsops hfs_vfsops = {
9bccf70c
A
1947 hfs_mount,
1948 hfs_start,
1949 hfs_unmount,
1950 hfs_root,
1951 hfs_quotactl,
1952 hfs_statfs,
1953 hfs_sync,
1954 hfs_vget,
1955 hfs_fhtovp,
1956 hfs_vptofh,
1957 hfs_init,
1958 hfs_sysctl
1c79356b 1959};