X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fa4905b191e0d16b0fffd53bd565eca71d01fae0..d7e50217d7adf6e52786a38bcaa4cd698cb9a79e:/bsd/hfs/hfs_vfsops.c diff --git a/bsd/hfs/hfs_vfsops.c b/bsd/hfs/hfs_vfsops.c index 6329bd207..513f49ca6 100644 --- a/bsd/hfs/hfs_vfsops.c +++ b/bsd/hfs/hfs_vfsops.c @@ -1,21 +1,24 @@ /* - * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -59,46 +62,10 @@ * hfs_vfsops.c * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95 * - * (c) Copyright 1997-1998 Apple Computer, Inc. All rights reserved. + * (c) Copyright 1997-2002 Apple Computer, Inc. All rights reserved. * * hfs_vfsops.c -- VFS layer for loadable HFS file system. * - * HISTORY - * 9-Nov-1999 Don Brady Fix error handling in hfs_unmount [2399157]. - * 9-Sep-1999 Don Brady Clear system file fcbModified flags in hfs_flushvolumeheader/hfs_flushMDB. - * 5-Aug-1999 Pat Dirks Moved special HFS flag from f_fsid.val[0][0] to mount flags (#2293117). - * 23-Jul-1999 Pat Dirks Added special-case code for root's parent directory in hfs_vget (#2263664). - * 9-Jun-1999 Don Brady Fix hfs_mount for reload and read-only downgrade cases. - * 2-Jun-1999 Don Brady Fix hfs_statfs to return correct f_files value. - * 4-May-1999 Don Brady Remove obsolete loadable module code. - * 22-Mar-1999 Don Brady Hide our private meta data in hfs_vget. - * 18-May-1999 Don Brady Add hfs_mountroot for HFS Plus rooting. - * 22-Mar-1999 Don Brady Hide our private meta data in hfs_vget. - * 12-Nov-1998 Pat Dirks Changed hfs_statfs to return volume's actual log. block size (#2286198). - * 22-Aug-1998 Scott Roberts Assign uid,gid, and mask for default on objects. - * 29-Jul-1998 Pat Dirks Fixed changed hfs_vget() to release complex node when retrying for data fork node. - * 27-Jul-1998 Scott Roberts Changes hfs_vget() to return data forks instead of complex. - * 14-Jul-1998 CHW Added check for use count of device node in hfs_mountfs - * 1-Jul-1998 Don Brady Always set kHFSVolumeUnmountedMask bit of vcb->vcbAtrb in hfs_unmount. - * 30-Jun-1998 Don Brady Removed hard-coded EINVAL error in hfs_mountfs (for radar #2249539). - * 24-Jun-1998 Don Brady Added setting of timezone to hfs_mount (radar #2226387). - * 4-Jun-1998 Don Brady Use VPUT/VRELE macros instead of vput/vrele. - * 6-May-1998 Scott Roberts Updated hfs_vget with kernel changes. - * 29-Apr-1998 Don Brady Update hfs_statfs to actually fill in statfs fields (radar #2227092). - * 23-Apr-1998 Pat Dirks Cleaned up code to call brelse() on errors from bread(). - * 4/20/1998 Don Brady Remove course-grained hfs metadata locking. - * 4/18/1998 Don Brady Add VCB locking. - * 4/16/1998 Don Brady hfs_unmount now flushes the volume bitmap. Add b-tree locking to hfs_vget. - * 4/8/1998 Don Brady Replace hfs_mdbupdate with hfs_flushvolumeheader and hfs_flushMDB. - * 4/8/1998 Don Brady In hfs_unmount call hfs_mdbupdate before trashing metafiles! - * 4/3/1998 Don Brady Call InitCatalogCache instead of PostInitFS. - * 4/1/1998 Don Brady Get rid of gHFSFlags, gReqstVol and gFlushOnlyFlag globals (not used). - * 3/30/1998 Don Brady In hfs_unmount use SKIPSYSTEM option on first vflush. - * 3/26/1998 Don Brady Changed hfs_unmount to call vflush before calling hfsUnmount. - * In hfs_mountfs don't mount hfs-wrapper. - * 3/19/1998 Pat Dirks Fixed bug in hfs_mount where device vnode was being - * released on way out. - * 11/14/1997 Pat Dirks Derived from hfs_vfsops.c */ #include #include @@ -109,69 +76,48 @@ #include #include #include -#include #include +#include +#include + +// XXXdbg +#include + #include #include #include "hfs.h" +#include "hfs_catalog.h" +#include "hfs_cnode.h" #include "hfs_dbg.h" #include "hfs_endian.h" +#include "hfs_quota.h" #include "hfscommon/headers/FileMgrInternal.h" #include "hfscommon/headers/BTreesInternal.h" + #if HFS_DIAGNOSTIC int hfs_dbg_all = 0; -int hfs_dbg_vfs = 0; -int hfs_dbg_vop = 0; -int hfs_dbg_load = 0; -int hfs_dbg_io = 0; -int hfs_dbg_utils = 0; -int hfs_dbg_rw = 0; -int hfs_dbg_lookup = 0; -int hfs_dbg_tree = 0; int hfs_dbg_err = 0; -int hfs_dbg_test = 0; #endif -/* - * HFS File System globals: - */ -Ptr gBufferAddress[BUFFERPTRLISTSIZE]; -struct buf *gBufferHeaderPtr[BUFFERPTRLISTSIZE]; -int gBufferListIndex; -simple_lock_data_t gBufferPtrListLock; - -//static char hfs_fs_name[MFSNAMELEN] = "hfs"; - -/* - * Global variables defined in other modules: - */ extern struct vnodeopv_desc hfs_vnodeop_opv_desc; -extern struct vnode *hfs_vhashget(dev_t dev, UInt32 nodeID, UInt8 forkType); - -extern OSErr HFSPlusToHFSExtents( const HFSPlusExtentRecord oldExtents, HFSExtentRecord newExtents); - +extern void hfs_converterinit(void); extern void inittodr( time_t base); -extern OSErr GetVolumeNameFromCatalog(ExtendedVCB *vcb); -extern void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catInfo, struct vnode *vp, struct hfsfilemeta *fm); -extern void CopyCatalogToFCB(struct hfsCatalogInfo *catInfo, struct vnode *vp); -extern void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm); -int hfs_changefs(struct mount *mp, struct hfs_mount_args *args, struct proc *p); -int hfs_reload(struct mount *mp, struct ucred *cred, struct proc *p); -int hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mount_args *args); -int hfs_vget(struct mount *mp, void *objID, struct vnode **vpp); -void hfs_vhashinit(); -void hfs_converterinit(void); +static int hfs_changefs __P((struct mount *mp, struct hfs_mount_args *args, + struct proc *p)); +static int hfs_reload __P((struct mount *mp, struct ucred *cred, struct proc *p)); - -static int hfs_statfs(); +static int hfs_mountfs __P((struct vnode *devvp, struct mount *mp, struct proc *p, + struct hfs_mount_args *args)); +static int hfs_statfs __P((struct mount *mp, register struct statfs *sbp, + struct proc *p)); /* @@ -184,6 +130,7 @@ hfs_mountroot() struct mount *mp; struct proc *p = current_proc(); /* XXX */ struct hfsmount *hfsmp; + ExtendedVCB *vcb; int error; /* @@ -193,11 +140,14 @@ hfs_mountroot() printf("hfs_mountroot: can't setup bdevvp"); return (error); } - if ((error = vfs_rootmountalloc("hfs", "root_device", &mp))) + if ((error = vfs_rootmountalloc("hfs", "root_device", &mp))) { + vrele(rootvp); /* release the reference from bdevvp() */ return (error); + } if ((error = hfs_mountfs(rootvp, mp, p, NULL))) { mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); + vrele(rootvp); /* release the reference from bdevvp() */ _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT); return (error); } @@ -213,10 +163,15 @@ hfs_mountroot() hfsmp->hfs_dir_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */ hfsmp->hfs_file_mask = (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); /* 0755 */ + /* Establish the free block reserve. */ + vcb = HFSTOVCB(hfsmp); + vcb->reserveBlocks = ((u_int64_t)vcb->totalBlocks * HFS_MINFREE) / 100; + vcb->reserveBlocks = MIN(vcb->reserveBlocks, HFS_MAXRESERVE / vcb->blockSize); + (void)hfs_statfs(mp, &mp->mnt_stat, p); vfs_unbusy(mp, p); - inittodr(to_bsd_time(HFSTOVCB(hfsmp)->vcbLsMod)); + inittodr(HFSTOVCB(hfsmp)->vcbLsMod); return (0); } @@ -227,8 +182,8 @@ hfs_mountroot() * mount system call */ -int -hfs_mount (mp, path, data, ndp, p) +static int +hfs_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; @@ -242,7 +197,6 @@ hfs_mount (mp, path, data, ndp, p) int retval = E_NONE; int flags; mode_t accessmode; - int loadconv = 0; if ((retval = copyin(data, (caddr_t)&args, sizeof(args)))) goto error_exit; @@ -265,21 +219,16 @@ hfs_mount (mp, path, data, ndp, p) if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; - if ((retval = hfs_flushfiles(mp, flags))) + if ((retval = hfs_flushfiles(mp, flags, p))) goto error_exit; - hfsmp->hfs_fs_clean = 1; hfsmp->hfs_fs_ronly = 1; - if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) - retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); - else - retval = hfs_flushMDB(hfsmp, MNT_WAIT); + retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); /* also get the volume bitmap blocks */ if (!retval) retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p); if (retval) { - hfsmp->hfs_fs_clean = 0; hfsmp->hfs_fs_ronly = 0; goto error_exit; } @@ -303,23 +252,21 @@ hfs_mount (mp, path, data, ndp, p) } VOP_UNLOCK(devvp, 0, p); } - if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) - retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); - else - retval = hfs_flushMDB(hfsmp, MNT_WAIT); + retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); if (retval != E_NONE) goto error_exit; /* only change hfs_fs_ronly after a successfull write */ hfsmp->hfs_fs_ronly = 0; - hfsmp->hfs_fs_clean = 0; } if ((hfsmp->hfs_fs_ronly == 0) && (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)) { /* setup private/hidden directory for unlinked files */ hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(HFSTOVCB(hfsmp)); + if (hfsmp->jnl) + hfs_remove_orphans(hfsmp); } if (args.fspec == 0) { @@ -386,7 +333,6 @@ hfs_mount (mp, path, data, ndp, p) goto error_exit; } - /* Set the mount flag to indicate that we support volfs */ mp->mnt_flag |= MNT_DOVOLFS; if (VFSTOVCB(mp)->vcbSigWord == kHFSSigWord) { @@ -394,6 +340,7 @@ hfs_mount (mp, path, data, ndp, p) mp->mnt_flag |= MNT_FIXEDSCRIPTENCODING; } (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN-1, &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); @@ -406,23 +353,24 @@ error_exit: } -/* change fs mount parameters */ -int +/* Change fs mount parameters */ +static int hfs_changefs(mp, args, p) struct mount *mp; struct hfs_mount_args *args; struct proc *p; { - int retval; + int retval = 0; int namefix, permfix, permswitch; struct hfsmount *hfsmp; - struct hfsnode *hp; - mode_t hfs_file_mask; + struct cnode *cp; ExtendedVCB *vcb; - hfsCatalogInfo catInfo; register struct vnode *vp, *nvp; hfs_to_unicode_func_t get_unicode_func; unicode_to_hfs_func_t get_hfsname_func; + struct cat_desc cndesc; + struct cat_attr cnattr; + u_long old_encoding; hfsmp = VFSTOHFS(mp); vcb = HFSTOVCB(hfsmp); @@ -436,19 +384,21 @@ hfs_changefs(mp, args, p) hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0); namefix = permfix = 0; - /* change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */ + /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */ if (args->hfs_timezone.tz_minuteswest != VNOVAL) { gTimeZone = args->hfs_timezone; } - /* change the default uid, gid and/or mask */ + /* Change the default uid, gid and/or mask */ if ((args->hfs_uid != (uid_t)VNOVAL) && (hfsmp->hfs_uid != args->hfs_uid)) { hfsmp->hfs_uid = args->hfs_uid; - ++permfix; + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) + ++permfix; } if ((args->hfs_gid != (gid_t)VNOVAL) && (hfsmp->hfs_gid != args->hfs_gid)) { hfsmp->hfs_gid = args->hfs_gid; - ++permfix; + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) + ++permfix; } if (args->hfs_mask != (mode_t)VNOVAL) { if (hfsmp->hfs_dir_mask != (args->hfs_mask & ALLPERMS)) { @@ -456,17 +406,19 @@ hfs_changefs(mp, args, p) hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS; if ((args->flags != VNOVAL) && (args->flags & HFSFSMNT_NOXONFILES)) hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE); - ++permfix; + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) + ++permfix; } } - /* change the hfs encoding value (hfs only) */ + /* Change the hfs encoding value (hfs only) */ if ((HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) && (hfsmp->hfs_encoding != (u_long)VNOVAL) && (hfsmp->hfs_encoding != args->hfs_encoding)) { retval = hfs_getconverter(args->hfs_encoding, &get_unicode_func, &get_hfsname_func); - if (retval) goto error_exit; + if (retval) + goto exit; /* * Connect the new hfs_get_unicode converter but leave @@ -479,11 +431,13 @@ hfs_changefs(mp, args, p) * in the old converters. */ hfsmp->hfs_get_unicode = get_unicode_func; + old_encoding = hfsmp->hfs_encoding; + hfsmp->hfs_encoding = args->hfs_encoding; ++namefix; } - - if (!(namefix || permfix || permswitch)) goto exit; + if (!(namefix || permfix || permswitch)) + goto exit; /* * For each active vnode fix things that changed @@ -516,12 +470,9 @@ loop: continue; } - hp = VTOH(vp); + cp = VTOC(vp); - INIT_CATALOGDATA(&catInfo.nodeData, 0); - - catInfo.hint = kNoHint; - retval = hfs_getcatalog(vcb, H_DIRID(hp), H_NAME(hp), hp->h_meta->h_namelen, &catInfo); + retval = cat_lookup(hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL); /* If we couldn't find this guy skip to the next one */ if (retval) { if (namefix) @@ -531,45 +482,11 @@ loop: continue; } - H_HINT(hp) = catInfo.hint; - if (permswitch || (permfix && (hp->h_meta->h_metaflags & IN_UNSETACCESS))) { - if ((vcb->vcbSigWord == kHFSPlusSigWord) && (catInfo.nodeData.cnd_mode & IFMT)) { - if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) { - /* - * Override the permissions as determined by the mount auguments - * in ALMOST the same way unset permissions are treated but keep - * track of whether or not the file or folder is hfs locked - * by leaving the h_pflags field unchanged from what was unpacked - * out of the catalog. - */ - hp->h_meta->h_metaflags |= IN_UNSETACCESS; - hp->h_meta->h_uid = VTOHFS(vp)->hfs_uid; - hp->h_meta->h_gid = VTOHFS(vp)->hfs_gid; - } else { - hp->h_meta->h_uid = catInfo.nodeData.cnd_ownerID; - hp->h_meta->h_gid = catInfo.nodeData.cnd_groupID; - }; - hp->h_meta->h_mode = (mode_t)catInfo.nodeData.cnd_mode; - } else { - /* - * Set the permissions as determined by the mount auguments - * but keep in account if the file or folder is hfs locked - */ - hp->h_meta->h_metaflags |= IN_UNSETACCESS; - hp->h_meta->h_uid = VTOHFS(vp)->hfs_uid; - hp->h_meta->h_gid = VTOHFS(vp)->hfs_gid; - - /* Default access is full read/write/execute: */ - hp->h_meta->h_mode &= IFMT; - hp->h_meta->h_mode |= ACCESSPERMS; /* 0777: rwxrwxrwx */ - /* ... but no more than that permitted by the mount point's: */ - if ((hp->h_meta->h_mode & IFMT) == IFDIR) { - hp->h_meta->h_mode &= IFMT | VTOHFS(vp)->hfs_dir_mask; - } else { - hp->h_meta->h_mode &= IFMT | VTOHFS(vp)->hfs_file_mask; - } - }; - }; + if (permswitch || permfix) { + cp->c_uid = cnattr.ca_uid; + cp->c_gid = cnattr.ca_gid; + cp->c_mode = cnattr.ca_mode; + } /* * If we're switching name converters then... @@ -578,41 +495,31 @@ loop: */ if (namefix) { cache_purge(vp); - hfs_name_CatToMeta(&catInfo.nodeData, hp->h_meta); + replace_desc(cp, &cndesc); - if (catInfo.nodeData.cnd_nodeID == kHFSRootFolderID) - strncpy(vcb->vcbVN, H_NAME(hp), NAME_MAX); + if (cndesc.cd_cnid == kHFSRootFolderID) { + strncpy(vcb->vcbVN, cp->c_desc.cd_nameptr, NAME_MAX); + cp->c_desc.cd_encoding = hfsmp->hfs_encoding; + } + } else { + cat_releasedesc(&cndesc); } - - CLEAN_CATALOGDATA(&catInfo.nodeData); - vput(vp); simple_lock(&mntvnode_slock); - } /* end for (vp...) */ - simple_unlock(&mntvnode_slock); - - -exit: + } /* end for (vp...) */ + simple_unlock(&mntvnode_slock); /* * If we're switching name converters we can now * connect the new hfs_get_hfsname converter and * release our interest in the old converters. */ if (namefix) { - u_long old_encoding = hfsmp->hfs_encoding; - hfsmp->hfs_get_hfsname = get_hfsname_func; - hfsmp->hfs_encoding = args->hfs_encoding; vcb->volumeNameEncodingHint = args->hfs_encoding; - (void) hfs_relconverter(old_encoding); } - - return (0); - -error_exit: - +exit: return (retval); } @@ -623,28 +530,30 @@ error_exit: * be mounted read-only. * * Things to do to update the mount: - * 1) invalidate all cached meta-data. - * 2) re-read volume header from disk. - * 3) re-load meta-file info (extents, file size). - * 4) re-load B-tree header data. - * 5) invalidate all inactive vnodes. - * 6) invalidate all cached file data. - * 7) re-read hfsnode data for all active vnodes. + * invalidate all cached meta-data. + * invalidate all inactive vnodes. + * invalidate all cached file data. + * re-read volume header from disk. + * re-load meta-file info (extents, file size). + * re-load B-tree header data. + * re-read cnode data for all active vnodes. */ -int +static int hfs_reload(mountp, cred, p) register struct mount *mountp; struct ucred *cred; struct proc *p; { register struct vnode *vp, *nvp, *devvp; - struct hfsnode *hp; + struct cnode *cp; struct buf *bp; - int size, error, i; + int sectorsize; + int error, i; struct hfsmount *hfsmp; struct HFSPlusVolumeHeader *vhp; ExtendedVCB *vcb; - FCB *fcb; + struct filefork *forkp; + struct cat_desc cndesc; if ((mountp->mnt_flag & MNT_RDONLY) == 0) return (EINVAL); @@ -663,41 +572,93 @@ hfs_reload(mountp, cred, p) panic("hfs_reload: dirty1"); InvalidateCatalogCache(vcb); +loop: + simple_lock(&mntvnode_slock); + for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { + if (vp->v_mount != mountp) { + simple_unlock(&mntvnode_slock); + goto loop; + } + nvp = vp->v_mntvnodes.le_next; + + /* + * Invalidate all inactive vnodes. + */ + if (vrecycle(vp, &mntvnode_slock, p)) + goto loop; + + /* + * Invalidate all cached file data. + */ + simple_lock(&vp->v_interlock); + simple_unlock(&mntvnode_slock); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) { + goto loop; + } + if (vinvalbuf(vp, 0, cred, p, 0, 0)) + panic("hfs_reload: dirty2"); + + /* + * Re-read cnode data for all active vnodes (non-metadata files). + */ + cp = VTOC(vp); + if ((vp->v_flag & VSYSTEM) == 0 && !VNODE_IS_RSRC(vp)) { + struct cat_fork *datafork; + struct cat_desc desc; + + datafork = cp->c_datafork ? &cp->c_datafork->ff_data : NULL; + + /* lookup by fileID since name could have changed */ + if ((error = cat_idlookup(hfsmp, cp->c_fileid, &desc, &cp->c_attr, datafork))) { + vput(vp); + return (error); + } + + + /* update cnode's catalog descriptor */ + (void) replace_desc(cp, &desc); + } + vput(vp); + simple_lock(&mntvnode_slock); + } + simple_unlock(&mntvnode_slock); + /* * Re-read VolumeHeader from disk. */ - size = kMDBSize; - error = bread( hfsmp->hfs_devvp, - IOBLKNOFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), - NOCRED, - &bp); + sectorsize = hfsmp->hfs_phys_block_size; + + error = meta_bread(hfsmp->hfs_devvp, + (vcb->hfsPlusIOPosOffset / sectorsize) + HFS_PRI_SECTOR(sectorsize), + sectorsize, NOCRED, &bp); if (error) { if (bp != NULL) brelse(bp); return (error); } - vhp = (HFSPlusVolumeHeader *) ((char *)bp->b_data + - IOBYTEOFFSETFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size)); + vhp = (HFSPlusVolumeHeader *) (bp->b_data + HFS_PRI_OFFSET(sectorsize)); - if ((ValidVolumeHeader(vhp) != 0) || (vcb->blockSize != SWAP_BE32 (vhp->blockSize))) { + /* Do a quick sanity check */ + if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord || + SWAP_BE16(vhp->version) != kHFSPlusVersion || + SWAP_BE32(vhp->blockSize) != vcb->blockSize) { brelse(bp); - return (EIO); /* XXX needs translation */ + return (EIO); } - vcb->vcbLsMod = SWAP_BE32 (vhp->modifyDate); - vcb->vcbAtrb = (UInt16) SWAP_BE32 (vhp->attributes); /* VCB only uses lower 16 bits */ - vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize); - vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID); - vcb->vcbVolBkUp = SWAP_BE32 (vhp->backupDate); - vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount); - vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount); - vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount); + vcb->vcbLsMod = to_bsd_time(SWAP_BE32(vhp->modifyDate)); + vcb->vcbAtrb = (UInt16) SWAP_BE32 (vhp->attributes); /* VCB only uses lower 16 bits */ + vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock); + vcb->vcbClpSiz = SWAP_BE32 (vhp->rsrcClumpSize); + vcb->vcbNxtCNID = SWAP_BE32 (vhp->nextCatalogID); + vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate)); + vcb->vcbWrCnt = SWAP_BE32 (vhp->writeCount); + vcb->vcbFilCnt = SWAP_BE32 (vhp->fileCount); + vcb->vcbDirCnt = SWAP_BE32 (vhp->folderCount); vcb->nextAllocation = SWAP_BE32 (vhp->nextAllocation); - vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks); - vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks); - vcb->checkedDate = SWAP_BE32 (vhp->checkedDate); + vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks); + vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks); vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap); bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo)); vcb->localCreateDate = SWAP_BE32 (vhp->createDate); /* hfs+ create date is in local time */ @@ -705,35 +666,40 @@ hfs_reload(mountp, cred, p) /* * Re-load meta-file vnode data (extent info, file size, etc). */ - fcb = VTOFCB((struct vnode *)vcb->extentsRefNum); - /* bcopy(vhp->extentsFile.extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord)); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - fcb->fcbExtents[i].startBlock = SWAP_BE32 (vhp->extentsFile.extents[i].startBlock); - fcb->fcbExtents[i].blockCount = SWAP_BE32 (vhp->extentsFile.extents[i].blockCount); - } - fcb->fcbEOF = SWAP_BE64 (vhp->extentsFile.logicalSize); - fcb->fcbPLen = SWAP_BE32 (vhp->extentsFile.totalBlocks) * vcb->blockSize; - fcb->fcbClmpSize = SWAP_BE32 (vhp->extentsFile.clumpSize); - - fcb = VTOFCB((struct vnode *)vcb->catalogRefNum); - /* bcopy(vhp->catalogFile.extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord)); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - fcb->fcbExtents[i].startBlock = SWAP_BE32 (vhp->catalogFile.extents[i].startBlock); - fcb->fcbExtents[i].blockCount = SWAP_BE32 (vhp->catalogFile.extents[i].blockCount); - } - fcb->fcbPLen = SWAP_BE64 (vhp->catalogFile.logicalSize); - fcb->fcbPLen = SWAP_BE32 (vhp->catalogFile.totalBlocks) * vcb->blockSize; - fcb->fcbClmpSize = SWAP_BE32 (vhp->catalogFile.clumpSize); - - fcb = VTOFCB((struct vnode *)vcb->allocationsRefNum); - /* bcopy(vhp->allocationFile.extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord)); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - fcb->fcbExtents[i].startBlock = SWAP_BE32 (vhp->allocationFile.extents[i].startBlock); - fcb->fcbExtents[i].blockCount = SWAP_BE32 (vhp->allocationFile.extents[i].blockCount); - } - fcb->fcbEOF = SWAP_BE64 (vhp->allocationFile.logicalSize); - fcb->fcbPLen = SWAP_BE32 (vhp->allocationFile.totalBlocks) * vcb->blockSize; - fcb->fcbClmpSize = SWAP_BE32 (vhp->allocationFile.clumpSize); + forkp = VTOF((struct vnode *)vcb->extentsRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + forkp->ff_extents[i].startBlock = + SWAP_BE32 (vhp->extentsFile.extents[i].startBlock); + forkp->ff_extents[i].blockCount = + SWAP_BE32 (vhp->extentsFile.extents[i].blockCount); + } + forkp->ff_size = SWAP_BE64 (vhp->extentsFile.logicalSize); + forkp->ff_blocks = SWAP_BE32 (vhp->extentsFile.totalBlocks); + forkp->ff_clumpsize = SWAP_BE32 (vhp->extentsFile.clumpSize); + + + forkp = VTOF((struct vnode *)vcb->catalogRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + forkp->ff_extents[i].startBlock = + SWAP_BE32 (vhp->catalogFile.extents[i].startBlock); + forkp->ff_extents[i].blockCount = + SWAP_BE32 (vhp->catalogFile.extents[i].blockCount); + } + forkp->ff_size = SWAP_BE64 (vhp->catalogFile.logicalSize); + forkp->ff_blocks = SWAP_BE32 (vhp->catalogFile.totalBlocks); + forkp->ff_clumpsize = SWAP_BE32 (vhp->catalogFile.clumpSize); + + + forkp = VTOF((struct vnode *)vcb->allocationsRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + forkp->ff_extents[i].startBlock = + SWAP_BE32 (vhp->allocationFile.extents[i].startBlock); + forkp->ff_extents[i].blockCount = + SWAP_BE32 (vhp->allocationFile.extents[i].blockCount); + } + forkp->ff_size = SWAP_BE64 (vhp->allocationFile.logicalSize); + forkp->ff_blocks = SWAP_BE32 (vhp->allocationFile.totalBlocks); + forkp->ff_clumpsize = SWAP_BE32 (vhp->allocationFile.clumpSize); brelse(bp); vhp = NULL; @@ -741,104 +707,132 @@ hfs_reload(mountp, cred, p) /* * Re-load B-tree header data */ - fcb = VTOFCB((struct vnode *)vcb->extentsRefNum); - if (error = MacToVFSError( BTReloadData(fcb) )) + forkp = VTOF((struct vnode *)vcb->extentsRefNum); + if (error = MacToVFSError( BTReloadData((FCB*)forkp) )) return (error); - fcb = VTOFCB((struct vnode *)vcb->catalogRefNum); - if (error = MacToVFSError( BTReloadData(fcb) )) + forkp = VTOF((struct vnode *)vcb->catalogRefNum); + if (error = MacToVFSError( BTReloadData((FCB*)forkp) )) return (error); - /* Now that the catalog is ready, get the volume name */ - /* also picks up the create date in GMT */ - if ((error = MacToVFSError( GetVolumeNameFromCatalog(vcb) ))) + /* Reload the volume name */ + if ((error = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, NULL, NULL))) return (error); + vcb->volumeNameEncodingHint = cndesc.cd_encoding; + bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen)); + cat_releasedesc(&cndesc); /* Re-establish private/hidden directory for unlinked files */ hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb); -loop: - simple_lock(&mntvnode_slock); - for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { - if (vp->v_mount != mountp) { - simple_unlock(&mntvnode_slock); - goto loop; - } - nvp = vp->v_mntvnodes.le_next; + return (0); +} - /* - * Invalidate all inactive vnodes. - */ - if (vrecycle(vp, &mntvnode_slock, p)) - goto loop; - /* - * Invalidate all cached file data. - */ - simple_lock(&vp->v_interlock); - simple_unlock(&mntvnode_slock); - if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) { - goto loop; - } - if (vinvalbuf(vp, 0, cred, p, 0, 0)) - panic("hfs_reload: dirty2"); +static int +get_raw_device(char *fspec, int is_user, int ronly, struct vnode **rvp, struct ucred *cred, struct proc *p) +{ + char *rawbuf; + char *dp; + size_t namelen; + struct nameidata nd; + int retval; - /* - * Re-read hfsnode data for all active vnodes (non-metadata files). - */ - hp = VTOH(vp); - if ((vp->v_flag & VSYSTEM) == 0) { - hfsCatalogInfo catInfo; + *rvp = NULL; - /* lookup by fileID since name could have changed */ - catInfo.hint = kNoHint; - INIT_CATALOGDATA(&catInfo.nodeData, 0); + MALLOC(rawbuf, char *, MAXPATHLEN, M_HFSMNT, M_WAITOK); + if (rawbuf == NULL) { + retval = ENOMEM; + goto error_exit; + } - if ((error = hfs_getcatalog(vcb, H_FILEID(hp), NULL, -1, &catInfo))) { - vput(vp); - CLEAN_CATALOGDATA(&catInfo.nodeData); - return (error); - } + if (is_user) { + retval = copyinstr(fspec, rawbuf, MAXPATHLEN - 1, &namelen); + if (retval != E_NONE) { + FREE(rawbuf, M_HFSMNT); + goto error_exit; + } + } else { + strcpy(rawbuf, fspec); + namelen = strlen(rawbuf); + } + + /* make sure it's null terminated */ + rawbuf[MAXPATHLEN-1] = '\0'; - H_HINT(hp) = catInfo.hint; - if (hp->h_meta->h_metaflags & IN_LONGNAME) - FREE(H_NAME(hp), M_TEMP); - H_NAME(hp) = NULL; - hp->h_meta->h_namelen = 0; - CopyCatalogToObjectMeta(&catInfo, vp, hp->h_meta); - CopyCatalogToFCB(&catInfo, vp); + dp = &rawbuf[namelen-1]; + while(dp >= rawbuf && *dp != '/') { + dp--; + } - CLEAN_CATALOGDATA(&catInfo.nodeData); - } + if (dp != NULL) { + dp++; + } else { + dp = rawbuf; + } + + /* make room for and insert the 'r' for the raw device */ + memmove(dp+1, dp, strlen(dp)+1); + *dp = 'r'; - vput(vp); - simple_lock(&mntvnode_slock); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, rawbuf, p); + retval = namei(&nd); + if (retval != E_NONE) { + DBG_ERR(("hfs_mountfs: can't open raw device for journal: %s, %x\n", rawbuf, nd.ni_vp->v_rdev)); + FREE(rawbuf, M_HFSMNT); + goto error_exit; } - simple_unlock(&mntvnode_slock); - return (0); + *rvp = nd.ni_vp; + if ((retval = VOP_OPEN(*rvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))) { + *rvp = NULL; + goto error_exit; + } + + // don't need this any more + FREE(rawbuf, M_HFSMNT); + + return 0; + + error_exit: + if (*rvp) { + (void)VOP_CLOSE(*rvp, ronly ? FREAD : FREAD|FWRITE, cred, p); + } + + if (rawbuf) { + FREE(rawbuf, M_HFSMNT); + } + return retval; } + /* * Common code for mount and mountroot */ -int -hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mount_args *args) +static int +hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, + struct hfs_mount_args *args) { - int retval = E_NONE; - register struct hfsmount *hfsmp; - struct buf *bp; - dev_t dev; - HFSMasterDirectoryBlock *mdbp; - int ronly; - struct ucred *cred; - u_long diskBlks; - u_long blksize; - DBG_VFS(("hfs_mountfs: mp = 0x%lX\n", (u_long)mp)); - - dev = devvp->v_rdev; - cred = p ? p->p_ucred : NOCRED; + int retval = E_NONE; + struct hfsmount *hfsmp; + struct buf *bp; + dev_t dev; + HFSMasterDirectoryBlock *mdbp; + int ronly; + int i; + int mntwrapper; + struct ucred *cred; + u_int64_t disksize; + u_int64_t blkcnt; + u_int32_t blksize; + u_int32_t minblksize; + u_int32_t iswritable; + daddr_t mdb_offset; + + dev = devvp->v_rdev; + cred = p ? p->p_ucred : NOCRED; + mntwrapper = 0; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use @@ -853,29 +847,63 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mo return (retval); ronly = (mp->mnt_flag & MNT_RDONLY) != 0; - DBG_VFS(("hfs_mountfs: opening device...\n")); if ((retval = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))) return (retval); - blksize = kHFSBlockSize; - DBG_VFS(("hfs_mountfs: size = %d (DEV_BSIZE = %d).\n", blksize, DEV_BSIZE)); + bp = NULL; + hfsmp = NULL; + mdbp = NULL; + minblksize = kHFSBlockSize; + + /* Get the real physical block size. */ + if (VOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)&blksize, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + /* Switch to 512 byte sectors (temporarily) */ + if (blksize > 512) { + u_int32_t size512 = 512; - bp = NULL; - hfsmp = NULL; + if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&size512, FWRITE, cred, p)) { + retval = ENXIO; + goto error_exit; + } + } + /* Get the number of 512 byte physical blocks. */ + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + /* Compute an accurate disk size (i.e. within 512 bytes) */ + disksize = blkcnt * (u_int64_t)512; - /* - * XXX SER Currently we only support 512 block size systems. This might change - * So this is a place holder to remind us that the mdb might not be 512 aligned - * retval = VOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, &blksize, FWRITE, cred, p); - * if (retval) return retval; + /* + * There are only 31 bits worth of block count in + * the buffer cache. So for large volumes a 4K + * physical block size is needed. */ + if (blkcnt > (u_int64_t)0x000000007fffffff) { + minblksize = blksize = 4096; + } + /* Now switch to our prefered physical block size. */ + if (blksize > 512) { + if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) { + retval = ENXIO; + goto error_exit; + } + /* Get the count of physical blocks. */ + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + } - /* - * the next three lines should probably be replaced - * with a call to the yet unimplemented function VOP_SETBLOCKSIZE + /* + * At this point: + * minblksize is the minimum physical block size + * blksize has our prefered physical block size + * blkcnt has the total number of physical blocks */ - retval = VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, &blksize, FWRITE, cred, p); - if (retval) return retval; devvp->v_specsize = blksize; /* cache the IO attributes */ @@ -885,37 +913,36 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mo return (retval); } - DBG_VFS(("hfs_mountfs: reading MDB [block no. %d + %d bytes, size %d bytes]...\n", - IOBLKNOFORBLK(kMasterDirectoryBlock, blksize), - IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, blksize), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, blksize))); - - if ((retval = bread(devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, blksize), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, blksize), cred, &bp))) { - goto error_exit; - }; - mdbp = (HFSMasterDirectoryBlock*) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, blksize)); + mdb_offset = HFS_PRI_SECTOR(blksize); + if ((retval = meta_bread(devvp, HFS_PRI_SECTOR(blksize), blksize, cred, &bp))) { + goto error_exit; + } + MALLOC(mdbp, HFSMasterDirectoryBlock *, kMDBSize, M_TEMP, M_WAITOK); + bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, kMDBSize); + brelse(bp); + bp = NULL; - MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK); - bzero(hfsmp, sizeof(struct hfsmount)); + MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK); + bzero(hfsmp, sizeof(struct hfsmount)); simple_lock_init(&hfsmp->hfs_renamelock); + + /* + * Init the volume information structure + */ + mp->mnt_data = (qaddr_t)hfsmp; + hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */ + hfsmp->hfs_vcb.vcb_hfsmp = hfsmp; /* Make VCBTOHFS work */ + hfsmp->hfs_raw_dev = devvp->v_rdev; + hfsmp->hfs_devvp = devvp; + hfsmp->hfs_phys_block_size = blksize; + hfsmp->hfs_phys_block_count = blkcnt; + hfsmp->hfs_media_writeable = 1; + hfsmp->hfs_fs_ronly = ronly; + hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0); + for (i = 0; i < MAXQUOTAS; i++) + hfsmp->hfs_qfiles[i].qf_vp = NULLVP; - DBG_VFS(("hfs_mountfs: Initializing hfsmount structure at 0x%lX...\n", (u_long)hfsmp)); - /* - * Init the volume information structure - */ - mp->mnt_data = (qaddr_t)hfsmp; - hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */ - hfsmp->hfs_vcb.vcb_hfsmp = hfsmp; /* Make VCBTOHFS work */ - hfsmp->hfs_raw_dev = devvp->v_rdev; - hfsmp->hfs_devvp = devvp; - hfsmp->hfs_phys_block_size = blksize; - - /* The hfs_log_block_size field is updated in the respective hfs_MountHFS[Plus]Volume routine */ - hfsmp->hfs_logBlockSize = BestBlockSizeFit(SWAP_BE32 (mdbp->drAlBlkSiz), MAXBSIZE, hfsmp->hfs_phys_block_size); - hfsmp->hfs_fs_ronly = ronly; - hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0); if (args) { hfsmp->hfs_uid = (args->hfs_uid == (uid_t)VNOVAL) ? UNKNOWNUID : args->hfs_uid; if (hfsmp->hfs_uid == 0xfffffffd) hfsmp->hfs_uid = UNKNOWNUID; @@ -931,7 +958,9 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mo } else { hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */ hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */ - }; + } + if ((args->flags != (int)VNOVAL) && (args->flags & HFSFSMNT_WRAPPER)) + mntwrapper = 1; } else { /* Even w/o explicit mount arguments, MNT_UNKNOWNPERMISSIONS requires setting up uid, gid, and mask: */ if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) { @@ -939,118 +968,208 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, struct hfs_mo hfsmp->hfs_gid = UNKNOWNGID; hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS; /* 0777: rwx---rwx */ hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE; /* 0666: no --x by default? */ - }; - }; - - /* See above comment for DKIOCGETBLOCKSIZE - * retval = VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, &blksize, FWRITE, cred, p); - * if (retval) return retval; - */ - - retval = VOP_IOCTL(devvp, DKIOCNUMBLKS, (caddr_t)&diskBlks, 0, cred, p); - if (retval) return retval; - - if (SWAP_BE16 (mdbp->drSigWord) == kHFSPlusSigWord) { - /* Enidan swap volume header in place */ - /* SWAP_HFS_PLUS_VOLUME_HEADER ((HFSPlusVolumeHeader *)bp->b_data); */ - - /* mount wrapper-less HFS-Plus volume */ - (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname); - retval = hfs_MountHFSPlusVolume(hfsmp, (HFSPlusVolumeHeader*) bp->b_data, 0, diskBlks, p); - - /* Enidan un-swap volume header in place */ - /* SWAP_HFS_PLUS_VOLUME_HEADER ((HFSPlusVolumeHeader *)bp->b_data); */ - - } else if (SWAP_BE16 (mdbp->drEmbedSigWord) == kHFSPlusSigWord) { - u_long embBlkOffset; - HFSPlusVolumeHeader *vhp; + } + } - embBlkOffset = SWAP_BE16 (mdbp->drAlBlSt) + - (SWAP_BE16 (mdbp->drEmbedExtent.startBlock) * (SWAP_BE32 (mdbp->drAlBlkSiz)/kHFSBlockSize)); - /* calculate virtual number of 512-byte sectors */ - diskBlks = SWAP_BE16 (mdbp->drEmbedExtent.blockCount) * (SWAP_BE32 (mdbp->drAlBlkSiz)/kHFSBlockSize); + /* Find out if disk media is writable. */ + if (VOP_IOCTL(devvp, DKIOCISWRITABLE, (caddr_t)&iswritable, 0, cred, p) == 0) { + if (iswritable) + hfsmp->hfs_media_writeable = 1; + else + hfsmp->hfs_media_writeable = 0; + } - brelse(bp); - bp = NULL; /* done with MDB, go grab Volume Header */ - mdbp = NULL; - - retval = bread( devvp, - IOBLKNOFORBLK(kMasterDirectoryBlock+embBlkOffset, blksize), - IOBYTECCNTFORBLK(kMasterDirectoryBlock+embBlkOffset, kMDBSize, blksize), - cred, - &bp); - if (retval) { + /* Mount a standard HFS disk */ + if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && + (mntwrapper || (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord))) { + if (devvp == rootvp) { + retval = EINVAL; /* Cannot root from HFS standard disks */ goto error_exit; - }; - vhp = (HFSPlusVolumeHeader*) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, blksize)); - - /* Enidan swap volume header in place */ - /* SWAP_HFS_PLUS_VOLUME_HEADER (vhp); */ - - /* mount embedded HFS Plus volume */ - (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname); - retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embBlkOffset, diskBlks, p); - - /* Enidan un-swap volume header in place */ - /* SWAP_HFS_PLUS_VOLUME_HEADER (vhp); */ - - } else if (devvp != rootvp) { + } + /* HFS disks can only use 512 byte physical blocks */ + if (blksize > kHFSBlockSize) { + blksize = kHFSBlockSize; + if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) { + retval = ENXIO; + goto error_exit; + } + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + devvp->v_specsize = blksize; + hfsmp->hfs_phys_block_size = blksize; + hfsmp->hfs_phys_block_count = blkcnt; + } if (args) { hfsmp->hfs_encoding = args->hfs_encoding; HFSTOVCB(hfsmp)->volumeNameEncodingHint = args->hfs_encoding; - /* establish the timezone */ gTimeZone = args->hfs_timezone; } - retval = hfs_getconverter(hfsmp->hfs_encoding, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname); - if (retval) goto error_exit; + retval = hfs_getconverter(hfsmp->hfs_encoding, &hfsmp->hfs_get_unicode, + &hfsmp->hfs_get_hfsname); + if (retval) + goto error_exit; - /* mount HFS volume */ - retval = hfs_MountHFSVolume( hfsmp, mdbp, diskBlks, p); - + retval = hfs_MountHFSVolume(hfsmp, mdbp, p); if (retval) (void) hfs_relconverter(hfsmp->hfs_encoding); - } else { - /* sorry, we cannot root from HFS */ - retval = EINVAL; - } - - if ( retval ) { - goto error_exit; - } - - brelse(bp); - bp = NULL; + } else /* Mount an HFS Plus disk */ { + HFSPlusVolumeHeader *vhp; + off_t embeddedOffset; + int jnl_disable = 0; - mp->mnt_stat.f_fsid.val[0] = (long)dev; - mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; - mp->mnt_maxsymlinklen = 0; - devvp->v_specflags |= SI_MOUNTEDON; - - if (ronly == 0) { - hfsmp->hfs_fs_clean = 0; - if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) - (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT); - else - (void) hfs_flushMDB(hfsmp, MNT_WAIT); - } - goto std_exit; + /* Get the embedded Volume Header */ + if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) { + embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * kHFSBlockSize; + embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * + (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); -error_exit: - DBG_VFS(("hfs_mountfs: exiting with error %d...\n", retval)); - - if (bp) - brelse(bp); - (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p); - if (hfsmp) { - FREE(hfsmp, M_HFSMNT); - mp->mnt_data = (qaddr_t)0; - } + /* + * If the embedded volume doesn't start on a block + * boundary, then switch the device to a 512-byte + * block size so everything will line up on a block + * boundary. + */ + if ((embeddedOffset % blksize) != 0) { + printf("HFS Mount: embedded volume offset not" + " a multiple of physical block size (%d);" + " switching to 512\n", blksize); + blksize = 512; + if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, + (caddr_t)&blksize, FWRITE, cred, p)) { + retval = ENXIO; + goto error_exit; + } + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, + (caddr_t)&blkcnt, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + /* XXX do we need to call vfs_init_io_attributes again? */ + devvp->v_specsize = blksize; + /* Note: relative block count adjustment */ + hfsmp->hfs_phys_block_count *= + hfsmp->hfs_phys_block_size / blksize; + hfsmp->hfs_phys_block_size = blksize; + } + + disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) * + (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz); + + hfsmp->hfs_phys_block_count = disksize / blksize; + + mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); + retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp); + if (retval) + goto error_exit; + bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512); + brelse(bp); + bp = NULL; + vhp = (HFSPlusVolumeHeader*) mdbp; + + } else /* pure HFS+ */ { + embeddedOffset = 0; + vhp = (HFSPlusVolumeHeader*) mdbp; + } + + // XXXdbg + // + hfsmp->jnl = NULL; + hfsmp->jvp = NULL; + if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS) && args->journal_disable) { + jnl_disable = 1; + } + + // + // We only initialize the journal here if the last person + // to mount this volume was journaling aware. Otherwise + // we delay journal initialization until later at the end + // of hfs_MountHFSPlusVolume() because the last person who + // mounted it could have messed things up behind our back + // (so we need to go find the .journal file, make sure it's + // the right size, re-sync up if it was moved, etc). + // + if ( (SWAP_BE32(vhp->lastMountedVersion) == kHFSJMountVersion) + && (SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) + && !jnl_disable) { + + // if we're able to init the journal, mark the mount + // point as journaled. + // + if (hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred) == 0) { + mp->mnt_flag |= MNT_JOURNALED; + } else { + retval = EINVAL; + goto error_exit; + } + } + // XXXdbg + + (void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname); + + retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args); + /* + * If the backend didn't like our physical blocksize + * then retry with physical blocksize of 512. + */ + if ((retval == ENXIO) && (blksize > 512) && (blksize != minblksize)) { + printf("HFS Mount: could not use physical block size " + "(%d) switching to 512\n", blksize); + blksize = 512; + if (VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, (caddr_t)&blksize, FWRITE, cred, p)) { + retval = ENXIO; + goto error_exit; + } + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt, 0, cred, p)) { + retval = ENXIO; + goto error_exit; + } + devvp->v_specsize = blksize; + /* Note: relative block count adjustment (in case this is an embedded volume). */ + hfsmp->hfs_phys_block_count *= hfsmp->hfs_phys_block_size / blksize; + hfsmp->hfs_phys_block_size = blksize; + + /* Try again with a smaller block size... */ + retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args); + } + if (retval) + (void) hfs_relconverter(0); + } -std_exit: + if ( retval ) { + goto error_exit; + } + + mp->mnt_stat.f_fsid.val[0] = (long)dev; + mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; + mp->mnt_maxsymlinklen = 0; + devvp->v_specflags |= SI_MOUNTEDON; + + if (ronly == 0) { + (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); + } + FREE(mdbp, M_TEMP); + return (0); + +error_exit: + if (bp) + brelse(bp); + if (mdbp) + FREE(mdbp, M_TEMP); + (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p); + if (hfsmp && hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) { + (void)VOP_CLOSE(hfsmp->jvp, ronly ? FREAD : FREAD|FWRITE, cred, p); + hfsmp->jvp = NULL; + } + if (hfsmp) { + FREE(hfsmp, M_HFSMNT); + mp->mnt_data = (qaddr_t)0; + } return (retval); } @@ -1060,22 +1179,20 @@ std_exit: * Nothing to do at the moment. */ /* ARGSUSED */ -int hfs_start(mp, flags, p) -struct mount *mp; -int flags; -struct proc *p; +static int +hfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; { - DBG_FUNC_NAME("hfs_start"); - DBG_PRINT_FUNC_NAME(); - - return (0); + return (0); } /* * unmount system call */ -int +static int hfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; @@ -1084,52 +1201,80 @@ hfs_unmount(mp, mntflags, p) struct hfsmount *hfsmp = VFSTOHFS(mp); int retval = E_NONE; int flags; + int force; + int started_tr = 0, grabbed_lock = 0; flags = 0; - if (mntflags & MNT_FORCE) + force = 0; + if (mntflags & MNT_FORCE) { flags |= FORCECLOSE; + force = 1; + } - if ((retval = hfs_flushfiles(mp, flags))) + if ((retval = hfs_flushfiles(mp, flags, p)) && !force) return (retval); /* * Flush out the b-trees, volume bitmap and Volume Header */ if (hfsmp->hfs_fs_ronly == 0) { + hfs_global_shared_lock_acquire(hfsmp); + grabbed_lock = 1; + if (hfsmp->jnl) { + journal_start_transaction(hfsmp->jnl); + started_tr = 1; + } + retval = VOP_FSYNC(HFSTOVCB(hfsmp)->catalogRefNum, NOCRED, MNT_WAIT, p); - if (retval && ((mntflags & MNT_FORCE) == 0)) - return (retval); - + if (retval && !force) + goto err_exit; + retval = VOP_FSYNC(HFSTOVCB(hfsmp)->extentsRefNum, NOCRED, MNT_WAIT, p); - if (retval && ((mntflags & MNT_FORCE) == 0)) - return (retval); + if (retval && !force) + goto err_exit; + + // if we have an allocation file, sync it too so we don't leave dirty + // blocks around + if (HFSTOVCB(hfsmp)->allocationsRefNum) { + if (retval = VOP_FSYNC(HFSTOVCB(hfsmp)->allocationsRefNum, NOCRED, MNT_WAIT, p)) { + if (!force) + goto err_exit; + } + } if (retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p)) { - if ((mntflags & MNT_FORCE) == 0) - return (retval); + if (!force) + goto err_exit; } /* See if this volume is damaged, is so do not unmount cleanly */ if (HFSTOVCB(hfsmp)->vcbFlags & kHFS_DamagedVolume) { - hfsmp->hfs_fs_clean = 0; HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask; } else { - hfsmp->hfs_fs_clean = 1; - HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask; + HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask; } - if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) - retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); - else - retval = hfs_flushMDB(hfsmp, MNT_WAIT); - + + retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); if (retval) { - hfsmp->hfs_fs_clean = 0; HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask; - if ((mntflags & MNT_FORCE) == 0) - return (retval); /* could not flush everything */ + if (!force) + goto err_exit; /* could not flush everything */ + } + + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); + started_tr = 0; + } + if (grabbed_lock) { + hfs_global_shared_lock_release(hfsmp); + grabbed_lock = 0; } } + if (hfsmp->jnl) { + journal_flush(hfsmp->jnl); + } + /* * Invalidate our caches and release metadata vnodes */ @@ -1138,15 +1283,39 @@ hfs_unmount(mp, mntflags, p) if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) (void) hfs_relconverter(hfsmp->hfs_encoding); + // XXXdbg + if (hfsmp->jnl) { + journal_close(hfsmp->jnl); + } + + if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) { + retval = VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, + NOCRED, p); + vrele(hfsmp->jvp); + hfsmp->jvp = NULL; + } + // XXXdbg + hfsmp->hfs_devvp->v_specflags &= ~SI_MOUNTEDON; - retval = VOP_CLOSE(hfsmp->hfs_devvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, - NOCRED, p); - vrele(hfsmp->hfs_devvp); + retval = VOP_CLOSE(hfsmp->hfs_devvp, + hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, + NOCRED, p); + if (retval && !force) + return(retval); + vrele(hfsmp->hfs_devvp); FREE(hfsmp, M_HFSMNT); mp->mnt_data = (qaddr_t)0; + return (0); - return (retval); + err_exit: + if (hfsmp->jnl && started_tr) { + journal_end_transaction(hfsmp->jnl); + } + if (grabbed_lock) { + hfs_global_shared_lock_release(hfsmp); + } + return retval; } @@ -1155,42 +1324,104 @@ hfs_unmount(mp, mntflags, p) * * OUT - vpp, should be locked and vget()'d (to increment usecount and lock) */ -int hfs_root(mp, vpp) -struct mount *mp; -struct vnode **vpp; +static int +hfs_root(mp, vpp) + struct mount *mp; + struct vnode **vpp; { - struct vnode *nvp; - int retval; - UInt32 rootObjID = kRootDirID; - - DBG_FUNC_NAME("hfs_root"); - DBG_PRINT_FUNC_NAME(); + struct vnode *nvp; + int retval; + UInt32 rootObjID = kRootDirID; - if ((retval = VFS_VGET(mp, &rootObjID, &nvp))) - return (retval); + if ((retval = VFS_VGET(mp, &rootObjID, &nvp))) + return (retval); - *vpp = nvp; - return (0); + *vpp = nvp; + return (0); } /* * Do operations associated with quotas */ -int hfs_quotactl(mp, cmds, uid, arg, p) -struct mount *mp; -int cmds; -uid_t uid; -caddr_t arg; -struct proc *p; +int +hfs_quotactl(mp, cmds, uid, arg, p) + struct mount *mp; + int cmds; + uid_t uid; + caddr_t arg; + struct proc *p; { - DBG_FUNC_NAME("hfs_quotactl"); - DBG_PRINT_FUNC_NAME(); + int cmd, type, error; + +#if !QUOTA + return (EOPNOTSUPP); +#else + if (uid == -1) + uid = p->p_cred->p_ruid; + cmd = cmds >> SUBCMDSHIFT; + + switch (cmd) { + case Q_SYNC: + case Q_QUOTASTAT: + break; + case Q_GETQUOTA: + if (uid == p->p_cred->p_ruid) + break; + /* fall through */ + default: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + } - return (EOPNOTSUPP); + type = cmds & SUBCMDMASK; + if ((u_int)type >= MAXQUOTAS) + return (EINVAL); + if (vfs_busy(mp, LK_NOWAIT, 0, p)) + return (0); + + switch (cmd) { + + case Q_QUOTAON: + error = hfs_quotaon(p, mp, type, arg, UIO_USERSPACE); + break; + + case Q_QUOTAOFF: + error = hfs_quotaoff(p, mp, type); + break; + + case Q_SETQUOTA: + error = hfs_setquota(mp, uid, type, arg); + break; + + case Q_SETUSE: + error = hfs_setuse(mp, uid, type, arg); + break; + + case Q_GETQUOTA: + error = hfs_getquota(mp, uid, type, arg); + break; + + case Q_SYNC: + error = hfs_qsync(mp); + break; + + case Q_QUOTASTAT: + error = hfs_quotastat(mp, type, arg); + break; + + default: + error = EINVAL; + break; + } + vfs_unbusy(mp, p); + return (error); +#endif /* QUOTA */ } + + /* * Get file system statistics. */ @@ -1204,18 +1435,15 @@ hfs_statfs(mp, sbp, p) struct hfsmount *hfsmp = VFSTOHFS(mp); u_long freeCNIDs; - DBG_FUNC_NAME("hfs_statfs"); - DBG_PRINT_FUNC_NAME(); - freeCNIDs = (u_long)0xFFFFFFFF - (u_long)vcb->vcbNxtCNID; sbp->f_bsize = vcb->blockSize; sbp->f_iosize = hfsmp->hfs_logBlockSize; sbp->f_blocks = vcb->totalBlocks; - sbp->f_bfree = vcb->freeBlocks; - sbp->f_bavail = vcb->freeBlocks; + sbp->f_bfree = hfs_freeblks(hfsmp, 0); + sbp->f_bavail = hfs_freeblks(hfsmp, 1); sbp->f_files = vcb->totalBlocks - 2; /* max files is constrained by total blocks */ - sbp->f_ffree = MIN(freeCNIDs, vcb->freeBlocks); + sbp->f_ffree = MIN(freeCNIDs, sbp->f_bavail); sbp->f_type = 0; if (sbp != &mp->mnt_stat) { @@ -1229,6 +1457,70 @@ hfs_statfs(mp, sbp, p) } +// +// XXXdbg -- this is a callback to be used by the journal to +// get meta data blocks flushed out to disk. +// +// XXXdbg -- be smarter and don't flush *every* block on each +// call. try to only flush some so we don't wind up +// being too synchronous. +// +__private_extern__ +void +hfs_sync_metadata(void *arg) +{ + struct mount *mp = (struct mount *)arg; + struct cnode *cp; + struct hfsmount *hfsmp; + ExtendedVCB *vcb; + struct vnode *meta_vp[3]; + struct buf *bp; + int i, sectorsize, priIDSector, altIDSector, retval; + int error, allerror = 0; + + hfsmp = VFSTOHFS(mp); + vcb = HFSTOVCB(hfsmp); + + bflushq(BQ_META, mp); + + +#if 1 // XXXdbg - I do not believe this is necessary... + // but if I pull it out, then the journal + // does not seem to get flushed properly + // when it is closed.... + + // now make sure the super block is flushed + sectorsize = hfsmp->hfs_phys_block_size; + priIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) + + HFS_PRI_SECTOR(sectorsize); + retval = meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp); + if (retval != 0) { + panic("hfs: sync_metadata: can't read super-block?! (retval 0x%x, priIDSector)\n", + retval, priIDSector); + } + + if (retval == 0 && (bp->b_flags & B_DELWRI) && (bp->b_flags & B_LOCKED) == 0) { + bwrite(bp); + } else if (bp) { + brelse(bp); + } + + // the alternate super block... + // XXXdbg - we probably don't need to do this each and every time. + // hfs_btreeio.c:FlushAlternate() should flag when it was + // written... + altIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) + + HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count); + retval = meta_bread(hfsmp->hfs_devvp, altIDSector, sectorsize, NOCRED, &bp); + if (retval == 0 && (bp->b_flags & B_DELWRI) && (bp->b_flags & B_LOCKED) == 0) { + bwrite(bp); + } else if (bp) { + brelse(bp); + } +#endif + +} + /* * Go through the disk queues to initiate sandbagged IO; * go through the inodes to write those that have been modified; @@ -1236,22 +1528,20 @@ hfs_statfs(mp, sbp, p) * * Note: we are always called with the filesystem marked `MPBUSY'. */ -static int hfs_sync(mp, waitfor, cred, p) -struct mount *mp; -int waitfor; -struct ucred *cred; -struct proc *p; +static int +hfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; { - struct vnode *nvp, *vp; - struct hfsnode *hp; - struct hfsmount *hfsmp = VFSTOHFS(mp); - ExtendedVCB *vcb; - struct vnode *meta_vp[3]; - int i; - int error, allerror = 0; - - DBG_FUNC_NAME("hfs_sync"); - DBG_PRINT_FUNC_NAME(); + struct vnode *nvp, *vp; + struct cnode *cp; + struct hfsmount *hfsmp; + ExtendedVCB *vcb; + struct vnode *meta_vp[3]; + int i; + int error, allerror = 0; /* * During MNT_UPDATE hfs_changefs might be manipulating @@ -1260,121 +1550,153 @@ struct proc *p; if (mp->mnt_flag & MNT_UPDATE) return (0); - hfsmp = VFSTOHFS(mp); - if (hfsmp->hfs_fs_ronly != 0) { - panic("update: rofs mod"); - }; + hfsmp = VFSTOHFS(mp); + if (hfsmp->hfs_fs_ronly != 0) { + panic("update: rofs mod"); + }; - /* - * Write back each 'modified' vnode - */ +#if 0 + // XXXdbg first go through and flush out any modified + // meta data blocks so they go out in order... + bflushq(BQ_META, mp); + bflushq(BQ_LRU, mp); + // only flush locked blocks if we're not doing journaling + if (hfsmp->jnl == NULL) { + bflushq(BQ_LOCKED, mp); + } +#endif + + /* + * Write back each 'modified' vnode + */ -loop:; - simple_lock(&mntvnode_slock); - for (vp = mp->mnt_vnodelist.lh_first; - vp != NULL; - vp = nvp) { +loop: + simple_lock(&mntvnode_slock); + for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { int didhold; - /* - * If the vnode that we are about to sync is no longer - * associated with this mount point, start over. - */ - if (vp->v_mount != mp) { - simple_unlock(&mntvnode_slock); - goto loop; - } - simple_lock(&vp->v_interlock); - nvp = vp->v_mntvnodes.le_next; - hp = VTOH(vp); - - if ((vp->v_flag & VSYSTEM) || (vp->v_type == VNON) || - (((hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) && - (vp->v_dirtyblkhd.lh_first == NULL) && !(vp->v_flag & VHASDIRTY))) { - simple_unlock(&vp->v_interlock); - simple_unlock(&mntvnode_slock); - simple_lock(&mntvnode_slock); - continue; - } - - simple_unlock(&mntvnode_slock); - error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); - if (error) { - if (error == ENOENT) - goto loop; - simple_lock(&mntvnode_slock); - continue; - } + /* + * If the vnode that we are about to sync is no longer + * associated with this mount point, start over. + */ + if (vp->v_mount != mp) { + simple_unlock(&mntvnode_slock); + goto loop; + } + + simple_lock(&vp->v_interlock); + nvp = vp->v_mntvnodes.le_next; + + cp = VTOC(vp); + + // restart our whole search if this guy is locked + // or being reclaimed. + // XXXdbg - at some point this should go away or we + // need to change all file systems to have + // this same code. vget() should never return + // success if either of these conditions is + // true. + if (vp->v_tag != VT_HFS || cp == NULL) { + simple_unlock(&vp->v_interlock); + continue; + } + + if ((vp->v_flag & VSYSTEM) || (vp->v_type == VNON) || + (((cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) == 0) && + (vp->v_dirtyblkhd.lh_first == NULL) && !(vp->v_flag & VHASDIRTY))) { + simple_unlock(&vp->v_interlock); + simple_unlock(&mntvnode_slock); + simple_lock(&mntvnode_slock); + continue; + } + + simple_unlock(&mntvnode_slock); + error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); + if (error) { + if (error == ENOENT) + goto loop; + simple_lock(&mntvnode_slock); + continue; + } didhold = ubc_hold(vp); - if ((error = VOP_FSYNC(vp, cred, waitfor, p))) { - DBG_ERR(("hfs_sync: error %d calling fsync on vnode 0x%X.\n", error, (u_int)vp)); - allerror = error; - }; - DBG_ASSERT(*((volatile int *)(&(vp)->v_interlock))==0); - VOP_UNLOCK(vp, 0, p); + if ((error = VOP_FSYNC(vp, cred, waitfor, p))) { + allerror = error; + }; + VOP_UNLOCK(vp, 0, p); if (didhold) ubc_rele(vp); - vrele(vp); - simple_lock(&mntvnode_slock); - }; - - vcb = HFSTOVCB(hfsmp); - meta_vp[0] = vcb->extentsRefNum; - meta_vp[1] = vcb->catalogRefNum; - meta_vp[2] = vcb->allocationsRefNum; /* This is NULL for standard HFS */ - - /* Now sync our three metadata files */ - for (i = 0; i < 3; ++i) { - struct vnode *btvp; - - btvp = meta_vp[i]; - - if ((btvp==0) || (btvp->v_type == VNON) || (btvp->v_mount != mp)) - continue; - simple_lock(&btvp->v_interlock); - hp = VTOH(btvp); - if (((hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) && - (btvp->v_dirtyblkhd.lh_first == NULL) && !(btvp->v_flag & VHASDIRTY)) { - simple_unlock(&btvp->v_interlock); - continue; - } - simple_unlock(&mntvnode_slock); - error = vget(btvp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); - if (error) { - simple_lock(&mntvnode_slock); - continue; - } - if ((error = VOP_FSYNC(btvp, cred, waitfor, p))) - allerror = error; - VOP_UNLOCK(btvp, 0, p); - vrele(btvp); - simple_lock(&mntvnode_slock); - }; - - simple_unlock(&mntvnode_slock); + vrele(vp); + simple_lock(&mntvnode_slock); + }; - /* - * Force stale file system control information to be flushed. - */ - if (vcb->vcbSigWord == kHFSSigWord) { - if ((error = VOP_FSYNC(hfsmp->hfs_devvp, cred, waitfor, p))) - allerror = error; - } - /* - * Write back modified superblock. - */ + vcb = HFSTOVCB(hfsmp); + + meta_vp[0] = vcb->extentsRefNum; + meta_vp[1] = vcb->catalogRefNum; + meta_vp[2] = vcb->allocationsRefNum; /* This is NULL for standard HFS */ + + /* Now sync our three metadata files */ + for (i = 0; i < 3; ++i) { + struct vnode *btvp; - if (IsVCBDirty(vcb)) { - if (vcb->vcbSigWord == kHFSPlusSigWord) - error = hfs_flushvolumeheader(hfsmp, waitfor); - else - error = hfs_flushMDB(hfsmp, waitfor); - - if (error) - allerror = error; - }; - - return (allerror); + btvp = btvp = meta_vp[i];; + if ((btvp==0) || (btvp->v_type == VNON) || (btvp->v_mount != mp)) + continue; + + simple_lock(&btvp->v_interlock); + cp = VTOC(btvp); + if (((cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) == 0) && + (btvp->v_dirtyblkhd.lh_first == NULL) && !(btvp->v_flag & VHASDIRTY)) { + simple_unlock(&btvp->v_interlock); + continue; + } + simple_unlock(&mntvnode_slock); + error = vget(btvp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); + if (error) { + simple_lock(&mntvnode_slock); + continue; + } + if ((error = VOP_FSYNC(btvp, cred, waitfor, p))) + allerror = error; + VOP_UNLOCK(btvp, 0, p); + vrele(btvp); + simple_lock(&mntvnode_slock); + }; + + simple_unlock(&mntvnode_slock); + + /* + * Force stale file system control information to be flushed. + */ + if (vcb->vcbSigWord == kHFSSigWord) { + if ((error = VOP_FSYNC(hfsmp->hfs_devvp, cred, waitfor, p))) + allerror = error; + } +#if QUOTA + hfs_qsync(mp); +#endif /* QUOTA */ + /* + * Write back modified superblock. + */ + + if (IsVCBDirty(vcb)) { + // XXXdbg - debugging, remove + if (hfsmp->jnl) { + //printf("hfs: sync: strange, a journaled volume w/dirty VCB? jnl 0x%x hfsmp 0x%x\n", + // hfsmp->jnl, hfsmp); + } + + error = hfs_flushvolumeheader(hfsmp, waitfor, 0); + if (error) + allerror = error; + } + + if (hfsmp->jnl) { + journal_flush(hfsmp->jnl); + } + + err_exit: + return (allerror); } @@ -1382,27 +1704,25 @@ loop:; * File handle to vnode * * Have to be really careful about stale file handles: - * - check that the hfsnode number is valid - * - call hfs_vget() to get the locked hfsnode - * - check for an unallocated hfsnode (i_mode == 0) + * - check that the cnode id is valid + * - call hfs_vget() to get the locked cnode + * - check for an unallocated cnode (i_mode == 0) * - check that the given client host has export rights and return * those rights via. exflagsp and credanonp */ -int +static int hfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) -register struct mount *mp; -struct fid *fhp; -struct mbuf *nam; -struct vnode **vpp; -int *exflagsp; -struct ucred **credanonp; + register struct mount *mp; + struct fid *fhp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; { struct hfsfid *hfsfhp; struct vnode *nvp; int result; struct netcred *np; - DBG_FUNC_NAME("hfs_fhtovp"); - DBG_PRINT_FUNC_NAME(); *vpp = NULL; hfsfhp = (struct hfsfid *)fhp; @@ -1430,16 +1750,16 @@ struct ucred **credanonp; * error prone. Future, would be change the "wrap bit" to a unique * wrap number and use that for generation number. For now do this. */ - if ((hfsfhp->hfsfid_gen < VTOH(nvp)->h_meta->h_crtime)) { + if ((hfsfhp->hfsfid_gen < VTOC(nvp)->c_itime)) { vput(nvp); - return ESTALE; + return (ESTALE); }; *vpp = nvp; *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; - return 0; + return (0); } @@ -1447,251 +1767,383 @@ struct ucred **credanonp; * Vnode pointer to File handle */ /* ARGSUSED */ -static int hfs_vptofh(vp, fhp) -struct vnode *vp; -struct fid *fhp; +static int +hfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; { - struct hfsnode *hp; + struct cnode *cp; struct hfsfid *hfsfhp; - struct proc *p = current_proc(); - int result; - u_int32_t fileID; - DBG_FUNC_NAME("hfs_vptofh"); - DBG_PRINT_FUNC_NAME(); - hp = VTOH(vp); - hfsfhp = (struct hfsfid *)fhp; - - /* If a file handle is requested for a file on an HFS volume we must be sure - to create the thread record before returning the object id in the filehandle - to make sure the file can be retrieved by fileid if necessary: - */ - if ((vp->v_type == VREG) && ISHFS(VTOVCB(vp))) { - /* Create a thread record and return the FileID [which is the file's fileNumber] */ - /* lock catalog b-tree */ - if ((result = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p)) != 0) return result; - result = hfsCreateFileID(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &fileID); - (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p); - if (result) { - DBG_ERR(("hfs_vptofh: error %d on CreateFileIDRef.\n", result)); - return result; - }; - DBG_ASSERT(fileID == H_FILEID(hp)); - }; + if (ISHFS(VTOVCB(vp))) + return (EOPNOTSUPP); /* hfs standard is not exportable */ + cp = VTOC(vp); + hfsfhp = (struct hfsfid *)fhp; hfsfhp->hfsfid_len = sizeof(struct hfsfid); hfsfhp->hfsfid_pad = 0; - hfsfhp->hfsfid_cnid = H_FILEID(hp); - hfsfhp->hfsfid_gen = hp->h_meta->h_crtime; + hfsfhp->hfsfid_cnid = cp->c_cnid; + hfsfhp->hfsfid_gen = cp->c_itime; - return 0; + return (0); } /* * Initial HFS filesystems, done only once. */ -int +static int hfs_init(vfsp) -struct vfsconf *vfsp; + struct vfsconf *vfsp; { - int i; - static int done = 0; - OSErr err; + static int done = 0; - DBG_FUNC_NAME("hfs_init"); - DBG_PRINT_FUNC_NAME(); - - if (done) - return (0); - done = 1; - hfs_vhashinit(); - hfs_converterinit(); - - simple_lock_init (&gBufferPtrListLock); - - for (i = BUFFERPTRLISTSIZE - 1; i >= 0; --i) { - gBufferAddress[i] = NULL; - gBufferHeaderPtr[i] = NULL; - }; - gBufferListIndex = 0; + if (done) + return (0); + done = 1; + hfs_chashinit(); + hfs_converterinit(); +#if QUOTA + dqinit(); +#endif /* QUOTA */ /* * Allocate Catalog Iterator cache... */ - err = InitCatalogCache(); + (void) InitCatalogCache(); - return E_NONE; + return (0); } +// XXXdbg +#include + + /* - * fast filesystem related variables. + * HFS filesystem related variables. */ -static int hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) -int *name; -u_int namelen; -void *oldp; -size_t *oldlenp; -void *newp; -size_t newlen; -struct proc *p; +static int +hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; { - DBG_FUNC_NAME("hfs_sysctl"); - DBG_PRINT_FUNC_NAME(); + extern u_int32_t hfs_encodingbias; + + /* all sysctl names at this level are terminal */ + + if (name[0] == HFS_ENCODINGBIAS) + return (sysctl_int(oldp, oldlenp, newp, newlen, + &hfs_encodingbias)); + else if (name[0] == 0x082969) { + // make the file system journaled... + struct vnode *vp = p->p_fd->fd_cdir, *jvp; + struct hfsmount *hfsmp; + ExtendedVCB *vcb; + int retval; + struct cat_attr jnl_attr, jinfo_attr; + struct cat_fork jnl_fork, jinfo_fork; + void *jnl = NULL; + + /* Only root can enable journaling */ + if (current_proc()->p_ucred->cr_uid != 0) { + return (EPERM); + } + hfsmp = VTOHFS(vp); + if (hfsmp->hfs_fs_ronly) { + return EROFS; + } + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) { + printf("hfs: can't make a plain hfs volume journaled.\n"); + return EINVAL; + } - return (EOPNOTSUPP); -} + if (hfsmp->jnl) { + printf("hfs: volume @ mp 0x%x is already journaled!\n", vp->v_mount); + return EAGAIN; + } + vcb = HFSTOVCB(hfsmp); + if (BTHasContiguousNodes(VTOF(vcb->catalogRefNum)) == 0 || + BTHasContiguousNodes(VTOF(vcb->extentsRefNum)) == 0) { -/* This will return a vnode of either a directory or a data vnode based on an object id. If - * it is a file id, its data fork will be returned. - */ -int -hfs_vget(struct mount *mp, - void *ino, - struct vnode **vpp) -{ - struct hfsmount *hfsmp; - dev_t dev; - int retval = E_NONE; + printf("hfs: volume has a btree w/non-contiguous nodes. can not enable journaling.\n"); + return EINVAL; + } - DBG_VFS(("hfs_vget: ino = %ld\n", *(UInt32 *)ino)); + // make sure these both exist! + if ( GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, &jinfo_fork) == 0 + || GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, &jnl_fork) == 0) { - /* Check if unmount in progress */ - if (mp->mnt_kern_flag & MNTK_UNMOUNT) { - *vpp = NULL; - return (EPERM); - } + return EINVAL; + } - hfsmp = VFSTOHFS(mp); - dev = hfsmp->hfs_raw_dev; - - /* First check to see if it is in the cache */ - *vpp = hfs_vhashget(dev, *(UInt32 *)ino, kDefault); - - /* hide open files that have been deleted */ - if (*vpp != NULL) { - if ((VTOH(*vpp)->h_meta->h_metaflags & IN_NOEXISTS) || - (hfsmp->hfs_private_metadata_dir != 0) && - (H_DIRID(VTOH(*vpp)) == hfsmp->hfs_private_metadata_dir)) { - vput(*vpp); - retval = ENOENT; - goto Err_Exit; - } - } - - /* The vnode is not in the cache, so lets make it */ - if (*vpp == NULL) - { - hfsCatalogInfo catInfo; - struct proc *p = current_proc(); - UInt8 forkType; - - INIT_CATALOGDATA(&catInfo.nodeData, 0); - catInfo.hint = kNoHint; - /* Special-case the root's parent directory (DirID = 1) because - it doesn't actually exist in the catalog: */ - if ((*vpp == NULL) && (*(UInt32 *)ino == kRootParID)) { - bzero(&catInfo, sizeof(catInfo)); - catInfo.nodeData.cnd_type = kCatalogFolderNode; - catInfo.nodeData.cnm_nameptr = catInfo.nodeData.cnm_namespace; - catInfo.nodeData.cnm_namespace[0] = '/'; - catInfo.nodeData.cnm_length = 1; - catInfo.nodeData.cnd_nodeID = kRootParID; - catInfo.nodeData.cnm_parID = kRootParID; - catInfo.nodeData.cnd_valence = 1; - catInfo.nodeData.cnd_ownerID = 0; - catInfo.nodeData.cnd_groupID = 0; - catInfo.nodeData.cnd_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO); - } else { - - /* lock catalog b-tree */ - retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p); - if (retval != E_NONE) goto Lookup_Err_Exit; - - retval = hfs_getcatalog(VFSTOVCB(mp), *(UInt32 *)ino, NULL, -1, &catInfo); - - /* unlock catalog b-tree */ - (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); - - if (retval != E_NONE) goto Lookup_Err_Exit; - - /* hide open files that have been deleted */ - if ((hfsmp->hfs_private_metadata_dir != 0) && - (catInfo.nodeData.cnm_parID == hfsmp->hfs_private_metadata_dir)) { - retval = ENOENT; - goto Lookup_Err_Exit; - }; - }; - - forkType = (catInfo.nodeData.cnd_type == kCatalogFolderNode) ? kDirectory : kDataFork; - retval = hfs_vcreate(VFSTOVCB(mp), &catInfo, forkType, vpp); + hfs_sync(hfsmp->hfs_mp, MNT_WAIT, FSCRED, p); + bflushq(BQ_META); + + printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n", + (off_t)name[2], (off_t)name[3]); + + jvp = hfsmp->hfs_devvp; + jnl = journal_create(jvp, + (off_t)name[2] * (off_t)HFSTOVCB(hfsmp)->blockSize + + HFSTOVCB(hfsmp)->hfsPlusIOPosOffset, + (off_t)name[3], + hfsmp->hfs_devvp, + hfsmp->hfs_phys_block_size, + 0, + 0, + hfs_sync_metadata, hfsmp->hfs_mp); + + if (jnl == NULL) { + printf("hfs: FAILED to create the journal!\n"); + if (jvp && jvp != hfsmp->hfs_devvp) { + VOP_CLOSE(jvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, FSCRED, p); + } + jvp = NULL; + + return EINVAL; + } + + hfs_global_exclusive_lock_acquire(hfsmp); -Lookup_Err_Exit: - CLEAN_CATALOGDATA(&catInfo.nodeData); - }; + HFSTOVCB(hfsmp)->vcbJinfoBlock = name[1]; + HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeJournaledMask; + hfsmp->jvp = jvp; + hfsmp->jnl = jnl; + + // save this off for the hack-y check in hfs_remove() + hfsmp->jnl_start = (u_int32_t)name[2]; + hfsmp->hfs_jnlinfoblkid = jinfo_attr.ca_fileid; + hfsmp->hfs_jnlfileid = jnl_attr.ca_fileid; + + hfsmp->hfs_mp->mnt_flag |= MNT_JOURNALED; + + hfs_global_exclusive_lock_release(hfsmp); + hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); + + return 0; + } else if (name[0] == 0x031272) { + // clear the journaling bit + struct vnode *vp = p->p_fd->fd_cdir; + struct hfsmount *hfsmp; + void *jnl; + int retval; + + /* Only root can disable journaling */ + if (current_proc()->p_ucred->cr_uid != 0) { + return (EPERM); + } + hfsmp = VTOHFS(vp); + if (hfsmp->jnl == NULL) { + return EINVAL; + } + + printf("hfs: disabling journaling for mount @ 0x%x\n", vp->v_mount); + + jnl = hfsmp->jnl; + + hfs_global_exclusive_lock_acquire(hfsmp); - UBCINFOCHECK("hfs_vget", *vpp); + // Lights out for you buddy! + hfsmp->jnl = NULL; + journal_close(jnl); -Err_Exit: + if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) { + VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, FSCRED, p); + } + hfsmp->jnl = NULL; + hfsmp->jvp = NULL; + hfsmp->hfs_mp->mnt_flag &= ~MNT_JOURNALED; + hfsmp->jnl_start = 0; + hfsmp->hfs_jnlinfoblkid = 0; + hfsmp->hfs_jnlfileid = 0; + + HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeJournaledMask; + + hfs_global_exclusive_lock_release(hfsmp); + hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); + + return 0; + } - /* rember if a parent directory was looked up by CNID */ - if (retval == 0 && ((*vpp)->v_type == VDIR) - && lockstatus(&mp->mnt_lock) != LK_SHARED) - VTOH(*vpp)->h_nodeflags |= IN_BYCNID; + return (EOPNOTSUPP); +} - return (retval); +/* This will return a vnode of either a directory or a data vnode based on an object id. If + * it is a file id, its data fork will be returned. + */ +static int +hfs_vget(mp, ino, vpp) + struct mount *mp; + void *ino; + struct vnode **vpp; +{ + cnid_t cnid = *(cnid_t *)ino; + + /* Check for cnids that should't be exported. */ + if ((cnid < kHFSFirstUserCatalogNodeID) + && (cnid != kHFSRootFolderID && cnid != kHFSRootParentID)) + return (ENOENT); + /* Don't export HFS Private Data dir. */ + if (cnid == VFSTOHFS(mp)->hfs_privdir_desc.cd_cnid) + return (ENOENT); + + return (hfs_getcnode(VFSTOHFS(mp), cnid, NULL, 0, NULL, NULL, vpp)); } /* * Flush out all the files in a filesystem. */ int -hfs_flushfiles(struct mount *mp, int flags) +hfs_flushfiles(struct mount *mp, int flags, struct proc *p) { + register struct hfsmount *hfsmp; + int i; int error; +#if QUOTA + hfsmp = VFSTOHFS(mp); + + if (mp->mnt_flag & MNT_QUOTA) { + if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags)) + return (error); + for (i = 0; i < MAXQUOTAS; i++) { + if (hfsmp->hfs_qfiles[i].qf_vp == NULLVP) + continue; + hfs_quotaoff(p, mp, i); + } + /* + * Here we fall through to vflush again to ensure + * that we have gotten rid of all the system vnodes. + */ + } +#endif /* QUOTA */ + error = vflush(mp, NULLVP, (SKIPSYSTEM | SKIPSWAP | flags)); error = vflush(mp, NULLVP, (SKIPSYSTEM | flags)); return (error); } -short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor) +/* + * Update volume encoding bitmap (HFS Plus only) + */ +__private_extern__ +void +hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding) { - ExtendedVCB *vcb = HFSTOVCB(hfsmp); - FCB *fcb; - HFSMasterDirectoryBlock *mdb; - struct buf *bp; - int retval; - int size = kMDBSize; /* 512 */ - ByteCount namelen; +#define kIndexMacUkrainian 48 /* MacUkrainian encoding is 152 */ +#define kIndexMacFarsi 49 /* MacFarsi encoding is 140 */ + + UInt32 index; + + switch (encoding) { + case kTextEncodingMacUkrainian: + index = kIndexMacUkrainian; + break; + case kTextEncodingMacFarsi: + index = kIndexMacFarsi; + break; + default: + index = encoding; + break; + } + + if (index < 128) { + HFSTOVCB(hfsmp)->encodingsBitmap |= (1 << index); + HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00; + } +} + +/* + * Update volume stats + */ +__private_extern__ +int +hfs_volupdate(struct hfsmount *hfsmp, enum volop op, int inroot) +{ + ExtendedVCB *vcb; + + vcb = HFSTOVCB(hfsmp); + vcb->vcbFlags |= 0xFF00; + vcb->vcbLsMod = time.tv_sec; + + switch (op) { + case VOL_UPDATE: + break; + case VOL_MKDIR: + if (vcb->vcbDirCnt != 0xFFFFFFFF) + ++vcb->vcbDirCnt; + if (inroot && vcb->vcbNmRtDirs != 0xFFFF) + ++vcb->vcbNmRtDirs; + break; + case VOL_RMDIR: + if (vcb->vcbDirCnt != 0) + --vcb->vcbDirCnt; + if (inroot && vcb->vcbNmRtDirs != 0xFFFF) + --vcb->vcbNmRtDirs; + break; + case VOL_MKFILE: + if (vcb->vcbFilCnt != 0xFFFFFFFF) + ++vcb->vcbFilCnt; + if (inroot && vcb->vcbNmFls != 0xFFFF) + ++vcb->vcbNmFls; + break; + case VOL_RMFILE: + if (vcb->vcbFilCnt != 0) + --vcb->vcbFilCnt; + if (inroot && vcb->vcbNmFls != 0xFFFF) + --vcb->vcbNmFls; + break; + } + + if (hfsmp->jnl) { + hfs_flushvolumeheader(hfsmp, 0, 0); + } + + return (0); +} - if (vcb->vcbSigWord != kHFSSigWord) - return EINVAL; - DBG_ASSERT(hfsmp->hfs_devvp != NULL); +static int +hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush) +{ + ExtendedVCB *vcb = HFSTOVCB(hfsmp); + struct filefork *fp; + HFSMasterDirectoryBlock *mdb; + struct buf *bp = NULL; + int retval; + int sectorsize; + ByteCount namelen; - retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp); + sectorsize = hfsmp->hfs_phys_block_size; + retval = bread(hfsmp->hfs_devvp, HFS_PRI_SECTOR(sectorsize), sectorsize, NOCRED, &bp); if (retval) { - DBG_VFS((" hfs_flushMDB bread return error! (%d)\n", retval)); - if (bp) brelse(bp); + if (bp) + brelse(bp); return retval; } - DBG_ASSERT(bp != NULL); - DBG_ASSERT(bp->b_data != NULL); - DBG_ASSERT(bp->b_bcount == size); + DBG_ASSERT(bp != NULL); + DBG_ASSERT(bp->b_data != NULL); + DBG_ASSERT(bp->b_bcount == size); + + if (hfsmp->jnl) { + panic("hfs: standard hfs volumes should not be journaled!\n"); + } - mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size)); + mdb = (HFSMasterDirectoryBlock *)(bp->b_data + HFS_PRI_OFFSET(sectorsize)); - VCB_LOCK(vcb); - mdb->drCrDate = SWAP_BE32 (UTCToLocal(vcb->vcbCrDate)); - mdb->drLsMod = SWAP_BE32 (UTCToLocal(vcb->vcbLsMod)); - mdb->drAtrb = SWAP_BE16 (vcb->vcbAtrb); + mdb->drCrDate = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbCrDate))); + mdb->drLsMod = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbLsMod))); + mdb->drAtrb = SWAP_BE16 (vcb->vcbAtrb); mdb->drNmFls = SWAP_BE16 (vcb->vcbNmFls); mdb->drAllocPtr = SWAP_BE16 (vcb->nextAllocation); mdb->drClpSiz = SWAP_BE32 (vcb->vcbClpSiz); @@ -1704,7 +2156,7 @@ short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor) if (retval) retval = utf8_to_mac_roman(namelen, vcb->vcbVN, mdb->drVN); - mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(vcb->vcbVolBkUp)); + mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbVolBkUp))); mdb->drWrCnt = SWAP_BE32 (vcb->vcbWrCnt); mdb->drNmRtDirs = SWAP_BE16 (vcb->vcbNmRtDirs); mdb->drFilCnt = SWAP_BE32 (vcb->vcbFilCnt); @@ -1712,34 +2164,44 @@ short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor) bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo)); - fcb = VTOFCB(vcb->extentsRefNum); - /* HFSPlusToHFSExtents(fcb->fcbExtents, mdb->drXTExtRec); */ - mdb->drXTExtRec[0].startBlock = SWAP_BE16 (fcb->fcbExtents[0].startBlock); - mdb->drXTExtRec[0].blockCount = SWAP_BE16 (fcb->fcbExtents[0].blockCount); - mdb->drXTExtRec[1].startBlock = SWAP_BE16 (fcb->fcbExtents[1].startBlock); - mdb->drXTExtRec[1].blockCount = SWAP_BE16 (fcb->fcbExtents[1].blockCount); - mdb->drXTExtRec[2].startBlock = SWAP_BE16 (fcb->fcbExtents[2].startBlock); - mdb->drXTExtRec[2].blockCount = SWAP_BE16 (fcb->fcbExtents[2].blockCount); - - mdb->drXTFlSize = SWAP_BE32 (fcb->fcbPLen); - mdb->drXTClpSiz = SWAP_BE32 (fcb->fcbClmpSize); + fp = VTOF(vcb->extentsRefNum); + mdb->drXTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock); + mdb->drXTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount); + mdb->drXTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock); + mdb->drXTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount); + mdb->drXTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock); + mdb->drXTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount); + mdb->drXTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize); + mdb->drXTClpSiz = SWAP_BE32 (fp->ff_clumpsize); - fcb = VTOFCB(vcb->catalogRefNum); - /* HFSPlusToHFSExtents(fcb->fcbExtents, mdb->drCTExtRec); */ - mdb->drCTExtRec[0].startBlock = SWAP_BE16 (fcb->fcbExtents[0].startBlock); - mdb->drCTExtRec[0].blockCount = SWAP_BE16 (fcb->fcbExtents[0].blockCount); - mdb->drCTExtRec[1].startBlock = SWAP_BE16 (fcb->fcbExtents[1].startBlock); - mdb->drCTExtRec[1].blockCount = SWAP_BE16 (fcb->fcbExtents[1].blockCount); - mdb->drCTExtRec[2].startBlock = SWAP_BE16 (fcb->fcbExtents[2].startBlock); - mdb->drCTExtRec[2].blockCount = SWAP_BE16 (fcb->fcbExtents[2].blockCount); - - mdb->drCTFlSize = SWAP_BE32 (fcb->fcbPLen); - mdb->drCTClpSiz = SWAP_BE32 (fcb->fcbClmpSize); - VCB_UNLOCK(vcb); + fp = VTOF(vcb->catalogRefNum); + mdb->drCTExtRec[0].startBlock = SWAP_BE16 (fp->ff_extents[0].startBlock); + mdb->drCTExtRec[0].blockCount = SWAP_BE16 (fp->ff_extents[0].blockCount); + mdb->drCTExtRec[1].startBlock = SWAP_BE16 (fp->ff_extents[1].startBlock); + mdb->drCTExtRec[1].blockCount = SWAP_BE16 (fp->ff_extents[1].blockCount); + mdb->drCTExtRec[2].startBlock = SWAP_BE16 (fp->ff_extents[2].startBlock); + mdb->drCTExtRec[2].blockCount = SWAP_BE16 (fp->ff_extents[2].blockCount); + mdb->drCTFlSize = SWAP_BE32 (fp->ff_blocks * vcb->blockSize); + mdb->drCTClpSiz = SWAP_BE32 (fp->ff_clumpsize); + + /* If requested, flush out the alternate MDB */ + if (altflush) { + struct buf *alt_bp = NULL; + u_long altIDSector; + + altIDSector = HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count); + + if (meta_bread(hfsmp->hfs_devvp, altIDSector, sectorsize, NOCRED, &alt_bp) == 0) { + bcopy(mdb, alt_bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize); + + (void) VOP_BWRITE(alt_bp); + } else if (alt_bp) + brelse(alt_bp); + } - if (waitfor != MNT_WAIT) + if (waitfor != MNT_WAIT) bawrite(bp); - else + else retval = VOP_BWRITE(bp); MarkVCBClean( vcb ); @@ -1748,150 +2210,246 @@ short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor) } -short hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor) +__private_extern__ +int +hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) { - ExtendedVCB *vcb = HFSTOVCB(hfsmp); - FCB *fcb; - HFSPlusVolumeHeader *volumeHeader; - int retval; - int size = sizeof(HFSPlusVolumeHeader); - struct buf *bp; - int i; - - if (vcb->vcbSigWord != kHFSPlusSigWord) - return EINVAL; + ExtendedVCB *vcb = HFSTOVCB(hfsmp); + struct filefork *fp; + HFSPlusVolumeHeader *volumeHeader; + int retval; + struct buf *bp; + int i; + int sectorsize; + int priIDSector; + int critical = 0; - retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp); + if (vcb->vcbSigWord == kHFSSigWord) + return hfs_flushMDB(hfsmp, waitfor, altflush); + + if (altflush) + critical = 1; + sectorsize = hfsmp->hfs_phys_block_size; + priIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) + + HFS_PRI_SECTOR(sectorsize); + + // XXXdbg + hfs_global_shared_lock_acquire(hfsmp); + if (hfsmp->jnl) { + if (journal_start_transaction(hfsmp->jnl) != 0) { + hfs_global_shared_lock_release(hfsmp); + return EINVAL; + } + } + + retval = meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp); if (retval) { - DBG_VFS((" hfs_flushvolumeheader bread return error! (%d)\n", retval)); - if (bp) brelse(bp); - return retval; + if (bp) + brelse(bp); + + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); + } + hfs_global_shared_lock_release(hfsmp); + + return (retval); } - DBG_ASSERT(bp != NULL); - DBG_ASSERT(bp->b_data != NULL); - DBG_ASSERT(bp->b_bcount == size); + if (hfsmp->jnl) { + journal_modify_block_start(hfsmp->jnl, bp); + } - volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data + - IOBYTEOFFSETFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size)); + volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data + HFS_PRI_OFFSET(sectorsize)); /* * For embedded HFS+ volumes, update create date if it changed * (ie from a setattrlist call) */ - if ((vcb->hfsPlusIOPosOffset != 0) && (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate)) - { - struct buf *bp2; + if ((vcb->hfsPlusIOPosOffset != 0) && + (SWAP_BE32 (volumeHeader->createDate) != vcb->localCreateDate)) { + struct buf *bp2; HFSMasterDirectoryBlock *mdb; - retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, kMDBSize), - IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, kMDBSize), NOCRED, &bp2); - if (retval != E_NONE) { - if (bp2) brelse(bp2); + retval = meta_bread(hfsmp->hfs_devvp, HFS_PRI_SECTOR(sectorsize), + sectorsize, NOCRED, &bp2); + if (retval) { + if (bp2) + brelse(bp2); + retval = 0; } else { - mdb = (HFSMasterDirectoryBlock *)((char *)bp2->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, kMDBSize)); + mdb = (HFSMasterDirectoryBlock *)(bp2->b_data + + HFS_PRI_OFFSET(sectorsize)); if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate ) { + // XXXdbg + if (hfsmp->jnl) { + journal_modify_block_start(hfsmp->jnl, bp2); + } + mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate); /* pick up the new create date */ - (void) VOP_BWRITE(bp2); /* write out the changes */ + // XXXdbg + if (hfsmp->jnl) { + journal_modify_block_end(hfsmp->jnl, bp2); + } else { + (void) VOP_BWRITE(bp2); /* write out the changes */ + } } else { brelse(bp2); /* just release it */ } } - } + } + +// XXXdbg - only monkey around with the volume signature on non-root volumes +// +#if 0 + if (hfsmp->jnl && + hfsmp->hfs_fs_ronly == 0 && + (HFSTOVFS(hfsmp)->mnt_flag & MNT_ROOTFS) == 0) { + + int old_sig = volumeHeader->signature; + + if (vcb->vcbAtrb & kHFSVolumeUnmountedMask) { + volumeHeader->signature = kHFSPlusSigWord; + } else { + volumeHeader->signature = kHFSJSigWord; + } + + if (old_sig != volumeHeader->signature) { + altflush = 1; + } + } +#endif +// XXXdbg - VCB_LOCK(vcb); /* Note: only update the lower 16 bits worth of attributes */ - volumeHeader->attributes = SWAP_BE32 ((SWAP_BE32 (volumeHeader->attributes) & 0xFFFF0000) + (UInt16) vcb->vcbAtrb); - volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion); - volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate); /* volume create date is in local time */ - volumeHeader->modifyDate = SWAP_BE32 (vcb->vcbLsMod); - volumeHeader->backupDate = SWAP_BE32 (vcb->vcbVolBkUp); - volumeHeader->checkedDate = SWAP_BE32 (vcb->checkedDate); - volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt); - volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt); - volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks); - volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation); - volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz); - volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz); - volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID); - volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt); - volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap); - - bcopy( vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo) ); - - VCB_UNLOCK(vcb); - - fcb = VTOFCB(vcb->extentsRefNum); - /* bcopy( fcb->fcbExtents, volumeHeader->extentsFile.extents, sizeof(HFSPlusExtentRecord) ); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - volumeHeader->extentsFile.extents[i].startBlock = SWAP_BE32 (fcb->fcbExtents[i].startBlock); - volumeHeader->extentsFile.extents[i].blockCount = SWAP_BE32 (fcb->fcbExtents[i].blockCount); - } - - fcb->fcbFlags &= ~fcbModifiedMask; - volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fcb->fcbEOF); - volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fcb->fcbPLen / vcb->blockSize); - volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fcb->fcbClmpSize); - - fcb = VTOFCB(vcb->catalogRefNum); - /* bcopy( fcb->fcbExtents, volumeHeader->catalogFile.extents, sizeof(HFSPlusExtentRecord) ); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - volumeHeader->catalogFile.extents[i].startBlock = SWAP_BE32 (fcb->fcbExtents[i].startBlock); - volumeHeader->catalogFile.extents[i].blockCount = SWAP_BE32 (fcb->fcbExtents[i].blockCount); - } - - fcb->fcbFlags &= ~fcbModifiedMask; - volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fcb->fcbEOF); - volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fcb->fcbPLen / vcb->blockSize); - volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fcb->fcbClmpSize); - - fcb = VTOFCB(vcb->allocationsRefNum); - /* bcopy( fcb->fcbExtents, volumeHeader->allocationFile.extents, sizeof(HFSPlusExtentRecord) ); */ - for (i = 0; i < kHFSPlusExtentDensity; i++) { - volumeHeader->allocationFile.extents[i].startBlock = SWAP_BE32 (fcb->fcbExtents[i].startBlock); - volumeHeader->allocationFile.extents[i].blockCount = SWAP_BE32 (fcb->fcbExtents[i].blockCount); - } - - fcb->fcbFlags &= ~fcbModifiedMask; - volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fcb->fcbEOF); - volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fcb->fcbPLen / vcb->blockSize); - volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fcb->fcbClmpSize); - - if (waitfor != MNT_WAIT) - bawrite(bp); - else - retval = VOP_BWRITE(bp); - - MarkVCBClean( vcb ); + volumeHeader->attributes = SWAP_BE32 ((SWAP_BE32 (volumeHeader->attributes) & 0xFFFF0000) + (UInt16) vcb->vcbAtrb); + volumeHeader->journalInfoBlock = SWAP_BE32(vcb->vcbJinfoBlock); + if (hfsmp->jnl) { + volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSJMountVersion); + } else { + volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion); + } + volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate); /* volume create date is in local time */ + volumeHeader->modifyDate = SWAP_BE32 (to_hfs_time(vcb->vcbLsMod)); + volumeHeader->backupDate = SWAP_BE32 (to_hfs_time(vcb->vcbVolBkUp)); + volumeHeader->fileCount = SWAP_BE32 (vcb->vcbFilCnt); + volumeHeader->folderCount = SWAP_BE32 (vcb->vcbDirCnt); + volumeHeader->freeBlocks = SWAP_BE32 (vcb->freeBlocks); + volumeHeader->nextAllocation = SWAP_BE32 (vcb->nextAllocation); + volumeHeader->rsrcClumpSize = SWAP_BE32 (vcb->vcbClpSiz); + volumeHeader->dataClumpSize = SWAP_BE32 (vcb->vcbClpSiz); + volumeHeader->nextCatalogID = SWAP_BE32 (vcb->vcbNxtCNID); + volumeHeader->writeCount = SWAP_BE32 (vcb->vcbWrCnt); + volumeHeader->encodingsBitmap = SWAP_BE64 (vcb->encodingsBitmap); + + if (bcmp(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)) != 0) + critical = 1; + bcopy(vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo)); + + /* Sync Extents over-flow file meta data */ + fp = VTOF(vcb->extentsRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + volumeHeader->extentsFile.extents[i].startBlock = + SWAP_BE32 (fp->ff_extents[i].startBlock); + volumeHeader->extentsFile.extents[i].blockCount = + SWAP_BE32 (fp->ff_extents[i].blockCount); + } + FTOC(fp)->c_flag &= ~C_MODIFIED; + volumeHeader->extentsFile.logicalSize = SWAP_BE64 (fp->ff_size); + volumeHeader->extentsFile.totalBlocks = SWAP_BE32 (fp->ff_blocks); + volumeHeader->extentsFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize); + + /* Sync Catalog file meta data */ + fp = VTOF(vcb->catalogRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + volumeHeader->catalogFile.extents[i].startBlock = + SWAP_BE32 (fp->ff_extents[i].startBlock); + volumeHeader->catalogFile.extents[i].blockCount = + SWAP_BE32 (fp->ff_extents[i].blockCount); + } + FTOC(fp)->c_flag &= ~C_MODIFIED; + volumeHeader->catalogFile.logicalSize = SWAP_BE64 (fp->ff_size); + volumeHeader->catalogFile.totalBlocks = SWAP_BE32 (fp->ff_blocks); + volumeHeader->catalogFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize); + + /* Sync Allocation file meta data */ + fp = VTOF(vcb->allocationsRefNum); + for (i = 0; i < kHFSPlusExtentDensity; i++) { + volumeHeader->allocationFile.extents[i].startBlock = + SWAP_BE32 (fp->ff_extents[i].startBlock); + volumeHeader->allocationFile.extents[i].blockCount = + SWAP_BE32 (fp->ff_extents[i].blockCount); + } + FTOC(fp)->c_flag &= ~C_MODIFIED; + volumeHeader->allocationFile.logicalSize = SWAP_BE64 (fp->ff_size); + volumeHeader->allocationFile.totalBlocks = SWAP_BE32 (fp->ff_blocks); + volumeHeader->allocationFile.clumpSize = SWAP_BE32 (fp->ff_clumpsize); + + /* If requested, flush out the alternate volume header */ + if (altflush) { + struct buf *alt_bp = NULL; + u_long altIDSector; + + altIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) + + HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count); + + if (meta_bread(hfsmp->hfs_devvp, altIDSector, sectorsize, NOCRED, &alt_bp) == 0) { + if (hfsmp->jnl) { + journal_modify_block_start(hfsmp->jnl, alt_bp); + } + + bcopy(volumeHeader, alt_bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize); + if (hfsmp->jnl) { + journal_modify_block_end(hfsmp->jnl, alt_bp); + } else { + (void) VOP_BWRITE(alt_bp); + } + } else if (alt_bp) + brelse(alt_bp); + } + + // XXXdbg + if (hfsmp->jnl) { + journal_modify_block_end(hfsmp->jnl, bp); + journal_end_transaction(hfsmp->jnl); + } else { + if (waitfor != MNT_WAIT) + bawrite(bp); + else { + retval = VOP_BWRITE(bp); + /* When critical data changes, flush the device cache */ + if (critical && (retval == 0)) { + (void) VOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, + NULL, FWRITE, NOCRED, current_proc()); + } + } + } + hfs_global_shared_lock_release(hfsmp); + + vcb->vcbFlags &= 0x00FF; return (retval); } -/* - * Moved here to avoid having to define prototypes - */ - /* * hfs vfs operations. */ struct vfsops hfs_vfsops = { - hfs_mount, - hfs_start, - hfs_unmount, - hfs_root, - hfs_quotactl, - hfs_statfs, - hfs_sync, - hfs_vget, - hfs_fhtovp, - hfs_vptofh, - hfs_init, - hfs_sysctl + hfs_mount, + hfs_start, + hfs_unmount, + hfs_root, + hfs_quotactl, + hfs_statfs, + hfs_sync, + hfs_vget, + hfs_fhtovp, + hfs_vptofh, + hfs_init, + hfs_sysctl };