#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ubc.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <dev/disk.h>
#include <sys/lock.h>
#include <miscfs/specfs/specdev.h>
#include <hfs/hfs_mount.h>
#include "hfs.h"
#include "hfs_dbg.h"
#include "hfs_endian.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
Ptr gBufferAddress[BUFFERPTRLISTSIZE];
struct buf *gBufferHeaderPtr[BUFFERPTRLISTSIZE];
int gBufferListIndex;
simple_lock_data_t gBufferPtrListLock;
struct FSVarsRec *gFSMVars;
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 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_statfs();
int
hfs_mountroot()
{
extern struct vnode *rootvp;
struct mount *mp;
struct proc *p = current_proc();
struct hfsmount *hfsmp;
int error;
if ((error = bdevvp(rootdev, &rootvp))) {
printf("hfs_mountroot: can't setup bdevvp");
return (error);
}
if ((error = vfs_rootmountalloc("hfs", "root_device", &mp)))
return (error);
if ((error = hfs_mountfs(rootvp, mp, p, NULL))) {
mp->mnt_vfc->vfc_refcount--;
vfs_unbusy(mp, p);
_FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
return (error);
}
simple_lock(&mountlist_slock);
CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
simple_unlock(&mountlist_slock);
hfsmp = VFSTOHFS(mp);
hfsmp->hfs_dir_mask = (S_IRWXU|S_IRWXG|S_IRWXO);
hfsmp->hfs_file_mask = (S_IRWXU|S_IRWXG|S_IRWXO);
(void)hfs_statfs(mp, &mp->mnt_stat, p);
vfs_unbusy(mp, p);
inittodr(to_bsd_time(HFSTOVCB(hfsmp)->vcbLsMod));
return (0);
}
int
hfs_mount (mp, path, data, ndp, p)
register struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
struct hfsmount *hfsmp = NULL;
struct vnode *devvp;
struct hfs_mount_args args;
size_t size;
int retval = E_NONE;
int flags;
mode_t accessmode;
int loadconv = 0;
if ((retval = copyin(data, (caddr_t)&args, sizeof(args))))
goto error_exit;
if (mp->mnt_flag & MNT_UPDATE) {
hfsmp = VFSTOHFS(mp);
if ((hfsmp->hfs_fs_ronly == 0) && (mp->mnt_flag & MNT_RDONLY)) {
retval = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p);
if (retval && ((mp->mnt_flag & MNT_FORCE) == 0))
goto error_exit;
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
if ((retval = hfs_flushfiles(mp, flags)))
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);
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;
}
}
if ((mp->mnt_flag & MNT_RELOAD) &&
(retval = hfs_reload(mp, ndp->ni_cnd.cn_cred, p)))
goto error_exit;
if (hfsmp->hfs_fs_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
if (p->p_ucred->cr_uid != 0) {
devvp = hfsmp->hfs_devvp;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
if ((retval = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p))) {
VOP_UNLOCK(devvp, 0, p);
goto error_exit;
}
VOP_UNLOCK(devvp, 0, p);
}
if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT);
else
retval = hfs_flushMDB(hfsmp, MNT_WAIT);
if (retval != E_NONE)
goto error_exit;
hfsmp->hfs_fs_ronly = 0;
hfsmp->hfs_fs_clean = 0;
}
if ((hfsmp->hfs_fs_ronly == 0) &&
(HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)) {
hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(HFSTOVCB(hfsmp));
}
if (args.fspec == 0) {
return vfs_export(mp, &hfsmp->hfs_export, &args.export);
}
}
NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
retval = namei(ndp);
if (retval != E_NONE) {
DBG_ERR(("hfs_mount: CAN'T GET DEVICE: %s, %x\n", args.fspec, ndp->ni_vp->v_rdev));
goto error_exit;
}
devvp = ndp->ni_vp;
if (devvp->v_type != VBLK) {
vrele(devvp);
retval = ENOTBLK;
goto error_exit;
}
if (major(devvp->v_rdev) >= nblkdev) {
vrele(devvp);
retval = ENXIO;
goto error_exit;
}
if (p->p_ucred->cr_uid != 0) {
accessmode = VREAD;
if ((mp->mnt_flag & MNT_RDONLY) == 0)
accessmode |= VWRITE;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
if ((retval = VOP_ACCESS(devvp, accessmode, p->p_ucred, p))) {
vput(devvp);
goto error_exit;
}
VOP_UNLOCK(devvp, 0, p);
}
if ((mp->mnt_flag & MNT_UPDATE) == 0) {
retval = hfs_mountfs(devvp, mp, p, &args);
if (retval != E_NONE)
vrele(devvp);
} else {
if (devvp != hfsmp->hfs_devvp)
retval = EINVAL;
else
retval = hfs_changefs(mp, &args, p);
vrele(devvp);
}
if (retval != E_NONE) {
goto error_exit;
}
mp->mnt_flag |= MNT_DOVOLFS;
if (VFSTOVCB(mp)->vcbSigWord == kHFSSigWord) {
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);
(void)hfs_statfs(mp, &mp->mnt_stat, p);
return (E_NONE);
error_exit:
return (retval);
}
int
hfs_changefs(mp, args, p)
struct mount *mp;
struct hfs_mount_args *args;
struct proc *p;
{
int retval;
int namefix, permfix, permswitch;
struct hfsmount *hfsmp;
struct hfsnode *hp;
mode_t hfs_file_mask;
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;
hfsmp = VFSTOHFS(mp);
vcb = HFSTOVCB(hfsmp);
permswitch = (((hfsmp->hfs_unknownpermissions != 0) && ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) == 0)) ||
((hfsmp->hfs_unknownpermissions == 0) && ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0)));
hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0);
namefix = permfix = 0;
if (args->hfs_timezone.tz_minuteswest != VNOVAL) {
gTimeZone = args->hfs_timezone;
}
if ((args->hfs_uid != (uid_t)VNOVAL) && (hfsmp->hfs_uid != args->hfs_uid)) {
hfsmp->hfs_uid = args->hfs_uid;
++permfix;
}
if ((args->hfs_gid != (gid_t)VNOVAL) && (hfsmp->hfs_gid != args->hfs_gid)) {
hfsmp->hfs_gid = args->hfs_gid;
++permfix;
}
if (args->hfs_mask != (mode_t)VNOVAL) {
if (hfsmp->hfs_dir_mask != (args->hfs_mask & ALLPERMS)) {
hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
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 == 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;
hfsmp->hfs_get_unicode = get_unicode_func;
++namefix;
}
if (!(namefix || permfix || permswitch)) goto exit;
simple_lock(&mntvnode_slock);
loop:
for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
if (vp->v_mount != mp)
goto loop;
simple_lock(&vp->v_interlock);
nvp = vp->v_mntvnodes.le_next;
if (vp->v_flag & VSYSTEM) {
simple_unlock(&vp->v_interlock);
continue;
}
simple_unlock(&mntvnode_slock);
retval = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
if (retval) {
simple_lock(&mntvnode_slock);
if (retval == ENOENT)
goto loop;
continue;
}
hp = VTOH(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);
if (retval) {
if (namefix)
cache_purge(vp);
vput(vp);
simple_lock(&mntvnode_slock);
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) {
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 {
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;
hp->h_meta->h_mode = ACCESSPERMS;
if ((hp->h_meta->h_mode & IFMT) == IFDIR) {
hp->h_meta->h_mode &= VTOHFS(vp)->hfs_dir_mask;
} else {
hp->h_meta->h_mode &= VTOHFS(vp)->hfs_file_mask;
}
};
};
if (namefix) {
cache_purge(vp);
hfs_name_CatToMeta(&catInfo.nodeData, hp->h_meta);
if (catInfo.nodeData.cnd_nodeID == kHFSRootFolderID)
strncpy(vcb->vcbVN, H_NAME(hp), NAME_MAX);
}
CLEAN_CATALOGDATA(&catInfo.nodeData);
vput(vp);
simple_lock(&mntvnode_slock);
}
simple_unlock(&mntvnode_slock);
exit:
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:
return (retval);
}
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 buf *bp;
int size, error, i;
struct hfsmount *hfsmp;
struct HFSPlusVolumeHeader *vhp;
ExtendedVCB *vcb;
FCB *fcb;
if ((mountp->mnt_flag & MNT_RDONLY) == 0)
return (EINVAL);
hfsmp = VFSTOHFS(mountp);
vcb = HFSTOVCB(hfsmp);
if (vcb->vcbSigWord == kHFSSigWord)
return (EINVAL);
devvp = hfsmp->hfs_devvp;
if (vinvalbuf(devvp, 0, cred, p, 0, 0))
panic("hfs_reload: dirty1");
InvalidateCatalogCache(vcb);
size = kMDBSize;
error = bread( hfsmp->hfs_devvp,
IOBLKNOFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size),
IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size),
NOCRED,
&bp);
if (error) {
if (bp != NULL)
brelse(bp);
return (error);
}
vhp = (HFSPlusVolumeHeader *) ((char *)bp->b_data +
IOBYTEOFFSETFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size));
if ((ValidVolumeHeader(vhp) != 0) || (vcb->blockSize != SWAP_BE32 (vhp->blockSize))) {
brelse(bp);
return (EIO);
}
vcb->vcbLsMod = SWAP_BE32 (vhp->modifyDate);
vcb->vcbAtrb = (UInt16) SWAP_BE32 (vhp->attributes);
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->nextAllocation = SWAP_BE32 (vhp->nextAllocation);
vcb->totalBlocks = SWAP_BE32 (vhp->totalBlocks);
vcb->freeBlocks = SWAP_BE32 (vhp->freeBlocks);
vcb->checkedDate = SWAP_BE32 (vhp->checkedDate);
vcb->encodingsBitmap = SWAP_BE64 (vhp->encodingsBitmap);
bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));
vcb->localCreateDate = SWAP_BE32 (vhp->createDate);
fcb = VTOFCB((struct vnode *)vcb->extentsRefNum);
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);
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);
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);
brelse(bp);
vhp = NULL;
fcb = VTOFCB((struct vnode *)vcb->extentsRefNum);
if (error = MacToVFSError( BTReloadData(fcb) ))
return (error);
fcb = VTOFCB((struct vnode *)vcb->catalogRefNum);
if (error = MacToVFSError( BTReloadData(fcb) ))
return (error);
if ((error = MacToVFSError( GetVolumeNameFromCatalog(vcb) )))
return (error);
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;
if (vrecycle(vp, &mntvnode_slock, p))
goto loop;
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");
hp = VTOH(vp);
if ((vp->v_flag & VSYSTEM) == 0) {
hfsCatalogInfo catInfo;
catInfo.hint = kNoHint;
INIT_CATALOGDATA(&catInfo.nodeData, 0);
if ((error = hfs_getcatalog(vcb, H_FILEID(hp), NULL, -1, &catInfo))) {
vput(vp);
CLEAN_CATALOGDATA(&catInfo.nodeData);
return (error);
}
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);
CLEAN_CATALOGDATA(&catInfo.nodeData);
}
vput(vp);
simple_lock(&mntvnode_slock);
}
simple_unlock(&mntvnode_slock);
return (0);
}
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;
if ((retval = vfs_mountedon(devvp)))
return (retval);
if ((vcount(devvp) > 1) && (devvp != rootvp))
return (EBUSY);
if ((retval = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0)))
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;
retval = VOP_IOCTL(devvp, DKIOCSETBLOCKSIZE, &blksize, FWRITE, cred, p);
if (retval) return retval;
devvp->v_specsize = blksize;
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));
MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK);
bzero(hfsmp, sizeof(struct hfsmount));
simple_lock_init(&hfsmp->hfs_renamelock);
DBG_VFS(("hfs_mountfs: Initializing hfsmount structure at 0x%lX...\n", (u_long)hfsmp));
mp->mnt_data = (qaddr_t)hfsmp;
hfsmp->hfs_mp = mp;
hfsmp->hfs_vcb.vcb_hfsmp = hfsmp;
hfsmp->hfs_raw_dev = devvp->v_rdev;
hfsmp->hfs_devvp = devvp;
hfsmp->hfs_phys_block_size = blksize;
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;
hfsmp->hfs_gid = (args->hfs_gid == (gid_t)VNOVAL) ? UNKNOWNGID : args->hfs_gid;
if (hfsmp->hfs_gid == 0xfffffffd) hfsmp->hfs_gid = UNKNOWNGID;
if (args->hfs_mask != (mode_t)VNOVAL) {
hfsmp->hfs_dir_mask = args->hfs_mask & ALLPERMS;
if (args->flags & HFSFSMNT_NOXONFILES) {
hfsmp->hfs_file_mask = (args->hfs_mask & DEFFILEMODE);
} else {
hfsmp->hfs_file_mask = args->hfs_mask & ALLPERMS;
}
} else {
hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS;
hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE;
};
} else {
if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
hfsmp->hfs_uid = UNKNOWNUID;
hfsmp->hfs_gid = UNKNOWNGID;
hfsmp->hfs_dir_mask = UNKNOWNPERMISSIONS & ALLPERMS;
hfsmp->hfs_file_mask = UNKNOWNPERMISSIONS & DEFFILEMODE;
};
};
retval = VOP_IOCTL(devvp, DKIOCNUMBLKS, (caddr_t)&diskBlks, 0, cred, p);
if (retval) return retval;
if (SWAP_BE16 (mdbp->drSigWord) == kHFSPlusSigWord) {
(void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
retval = hfs_MountHFSPlusVolume(hfsmp, (HFSPlusVolumeHeader*) bp->b_data, 0, diskBlks, p);
} 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));
diskBlks = SWAP_BE16 (mdbp->drEmbedExtent.blockCount) * (SWAP_BE32 (mdbp->drAlBlkSiz)/kHFSBlockSize);
brelse(bp);
bp = NULL;
mdbp = NULL;
retval = bread( devvp,
IOBLKNOFORBLK(kMasterDirectoryBlock+embBlkOffset, blksize),
IOBYTECCNTFORBLK(kMasterDirectoryBlock+embBlkOffset, kMDBSize, blksize),
cred,
&bp);
if (retval) {
goto error_exit;
};
vhp = (HFSPlusVolumeHeader*) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, blksize));
(void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embBlkOffset, diskBlks, p);
} else if (devvp != rootvp) {
if (args) {
hfsmp->hfs_encoding = args->hfs_encoding;
HFSTOVCB(hfsmp)->volumeNameEncodingHint = args->hfs_encoding;
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_MountHFSVolume( hfsmp, mdbp, diskBlks, p);
if (retval)
(void) hfs_relconverter(hfsmp->hfs_encoding);
} else {
retval = EINVAL;
}
if ( retval ) {
goto error_exit;
}
brelse(bp);
bp = NULL;
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;
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;
}
std_exit:
return (retval);
}
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);
}
int
hfs_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct hfsmount *hfsmp = VFSTOHFS(mp);
int retval = E_NONE;
int flags;
flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if ((retval = hfs_flushfiles(mp, flags)))
return (retval);
if (hfsmp->hfs_fs_ronly == 0) {
retval = VOP_FSYNC(HFSTOVCB(hfsmp)->catalogRefNum, NOCRED, MNT_WAIT, p);
if (retval && ((mntflags & MNT_FORCE) == 0))
return (retval);
retval = VOP_FSYNC(HFSTOVCB(hfsmp)->extentsRefNum, NOCRED, MNT_WAIT, p);
if (retval && ((mntflags & MNT_FORCE) == 0))
return (retval);
if (retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p)) {
if ((mntflags & MNT_FORCE) == 0)
return (retval);
}
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;
}
if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT);
else
retval = hfs_flushMDB(hfsmp, MNT_WAIT);
if (retval) {
hfsmp->hfs_fs_clean = 0;
HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
if ((mntflags & MNT_FORCE) == 0)
return (retval);
}
}
(void) hfsUnmount(hfsmp, p);
if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord)
(void) hfs_relconverter(hfsmp->hfs_encoding);
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);
FREE(hfsmp, M_HFSMNT);
mp->mnt_data = (qaddr_t)0;
return (retval);
}
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();
if ((retval = VFS_VGET(mp, &rootObjID, &nvp)))
return (retval);
*vpp = nvp;
return (0);
}
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();
return (EOPNOTSUPP);
}
static int
hfs_statfs(mp, sbp, p)
struct mount *mp;
register struct statfs *sbp;
struct proc *p;
{
ExtendedVCB *vcb = VFSTOVCB(mp);
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_files = vcb->totalBlocks - 2;
sbp->f_ffree = MIN(freeCNIDs, vcb->freeBlocks);
sbp->f_type = 0;
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy((caddr_t)mp->mnt_stat.f_mntonname,
(caddr_t)&sbp->f_mntonname[0], MNAMELEN);
bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
(caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
}
return (0);
}
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;
int error, allerror = 0;
DBG_FUNC_NAME("hfs_sync");
DBG_PRINT_FUNC_NAME();
if (mp->mnt_flag & MNT_UPDATE)
return (0);
hfsmp = VFSTOHFS(mp);
if (hfsmp->hfs_fs_ronly != 0) {
panic("update: rofs mod");
};
loop:;
simple_lock(&mntvnode_slock);
for (vp = mp->mnt_vnodelist.lh_first;
vp != NULL;
vp = nvp) {
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_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 ((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);
vput(vp);
simple_lock(&mntvnode_slock);
};
vcb = HFSTOVCB(hfsmp);
{
struct vnode *btvp;
btvp = vcb->extentsRefNum;
if ((btvp==0) || (btvp->v_type == VNON) || (btvp->v_mount != mp))
goto skipBtree;
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);
goto skipBtree;
}
simple_unlock(&mntvnode_slock);
error = vget(btvp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
if (error) {
simple_lock(&mntvnode_slock);
goto skipBtree;
}
if ((error = VOP_FSYNC(btvp, cred, waitfor, p)))
allerror = error;
VOP_UNLOCK(btvp, 0, p);
vrele(btvp);
simple_lock(&mntvnode_slock);
};
skipBtree:;
simple_unlock(&mntvnode_slock);
if ((error = VOP_FSYNC(hfsmp->hfs_devvp, cred, waitfor, p)))
allerror = error;
if (IsVCBDirty(vcb)) {
if (vcb->vcbSigWord == kHFSPlusSigWord)
error = hfs_flushvolumeheader(hfsmp, waitfor);
else
error = hfs_flushMDB(hfsmp, waitfor);
if (error)
allerror = error;
};
return (allerror);
}
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;
{
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;
np = vfs_export_lookup(mp, &VFSTOHFS(mp)->hfs_export, nam);
if (np == NULL) {
return EACCES;
};
result = VFS_VGET(mp, &hfsfhp->hfsfid_cnid, &nvp);
if (result) return result;
if (nvp == NULL) return ESTALE;
if ((hfsfhp->hfsfid_gen != VTOH(nvp)->h_meta->h_crtime)) {
vput(nvp);
return ESTALE;
};
*vpp = nvp;
*exflagsp = np->netc_exflags;
*credanonp = &np->netc_anon;
return 0;
}
static int hfs_vptofh(vp, fhp)
struct vnode *vp;
struct fid *fhp;
{
struct hfsnode *hp;
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 ((vp->v_type == VREG) && ISHFS(VTOVCB(vp))) {
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));
};
hfsfhp->hfsfid_len = sizeof(struct hfsfid);
hfsfhp->hfsfid_pad = 0;
hfsfhp->hfsfid_cnid = H_FILEID(hp);
hfsfhp->hfsfid_gen = hp->h_meta->h_crtime;
return 0;
}
int
hfs_init(vfsp)
struct vfsconf *vfsp;
{
int i;
static int done = 0;
OSErr err;
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;
MALLOC(gFSMVars, FSVarsRec *, sizeof(FSVarsRec), M_TEMP, M_WAITOK);
bzero(gFSMVars, sizeof(FSVarsRec));
err = InitCatalogCache();
#if HFS_DIAGNOSTIC
if (err) panic("hfs_init: Error returned from InitCatalogCache() call.");
#endif
return E_NONE;
}
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();
return (EOPNOTSUPP);
}
int
hfs_vget(struct mount *mp,
void *ino,
struct vnode **vpp)
{
struct hfsmount *hfsmp;
dev_t dev;
int retval = E_NONE;
DBG_VFS(("hfs_vget: ino = %ld\n", *(UInt32 *)ino));
if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
*vpp = NULL;
return (EPERM);
}
hfsmp = VFSTOHFS(mp);
dev = hfsmp->hfs_raw_dev;
*vpp = hfs_vhashget(dev, *(UInt32 *)ino, kDefault);
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;
}
}
if (*vpp == NULL)
{
hfsCatalogInfo catInfo;
struct proc *p = current_proc();
UInt8 forkType;
INIT_CATALOGDATA(&catInfo.nodeData, 0);
catInfo.hint = kNoHint;
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 {
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);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (retval != E_NONE) goto Lookup_Err_Exit;
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);
Lookup_Err_Exit:
CLEAN_CATALOGDATA(&catInfo.nodeData);
};
UBCINFOCHECK("hfs_vget", *vpp);
Err_Exit:
if (retval != E_NONE) {
DBG_VFS(("hfs_vget: Error returned of %d\n", retval));
}
else {
DBG_VFS(("hfs_vget: vp = 0x%x\n", (u_int)*vpp));
}
return (retval);
}
int
hfs_flushfiles(struct mount *mp, int flags)
{
int error;
error = vflush(mp, NULLVP, (SKIPSYSTEM | SKIPSWAP | flags));
error = vflush(mp, NULLVP, (SKIPSYSTEM | flags));
return (error);
}
short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor)
{
ExtendedVCB *vcb = HFSTOVCB(hfsmp);
FCB *fcb;
HFSMasterDirectoryBlock *mdb;
struct buf *bp;
int retval;
int size = kMDBSize;
ByteCount namelen;
if (vcb->vcbSigWord != kHFSSigWord)
return EINVAL;
DBG_ASSERT(hfsmp->hfs_devvp != NULL);
retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size),
IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp);
if (retval) {
DBG_VFS((" hfs_flushMDB bread return error! (%d)\n", retval));
if (bp) brelse(bp);
return retval;
}
DBG_ASSERT(bp != NULL);
DBG_ASSERT(bp->b_data != NULL);
DBG_ASSERT(bp->b_bcount == size);
mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size));
VCB_LOCK(vcb);
mdb->drCrDate = SWAP_BE32 (UTCToLocal(vcb->vcbCrDate));
mdb->drLsMod = SWAP_BE32 (UTCToLocal(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);
mdb->drNxtCNID = SWAP_BE32 (vcb->vcbNxtCNID);
mdb->drFreeBks = SWAP_BE16 (vcb->freeBlocks);
namelen = strlen(vcb->vcbVN);
retval = utf8_to_hfs(vcb, namelen, vcb->vcbVN, mdb->drVN);
if (retval)
retval = utf8_to_mac_roman(namelen, vcb->vcbVN, mdb->drVN);
mdb->drVolBkUp = SWAP_BE32 (UTCToLocal(vcb->vcbVolBkUp));
mdb->drVSeqNum = SWAP_BE16 (vcb->vcbVSeqNum);
mdb->drWrCnt = SWAP_BE32 (vcb->vcbWrCnt);
mdb->drNmRtDirs = SWAP_BE16 (vcb->vcbNmRtDirs);
mdb->drFilCnt = SWAP_BE32 (vcb->vcbFilCnt);
mdb->drDirCnt = SWAP_BE32 (vcb->vcbDirCnt);
bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo));
fcb = VTOFCB(vcb->extentsRefNum);
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);
fcb = VTOFCB(vcb->catalogRefNum);
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);
if (waitfor != MNT_WAIT)
bawrite(bp);
else
retval = VOP_BWRITE(bp);
MarkVCBClean( vcb );
return (retval);
}
short hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor)
{
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;
retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size),
IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp);
if (retval) {
DBG_VFS((" hfs_flushvolumeheader bread return error! (%d)\n", retval));
if (bp) brelse(bp);
return retval;
}
DBG_ASSERT(bp != NULL);
DBG_ASSERT(bp->b_data != NULL);
DBG_ASSERT(bp->b_bcount == size);
volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data +
IOBYTEOFFSETFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size));
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);
} else {
mdb = (HFSMasterDirectoryBlock *)((char *)bp2->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, kMDBSize));
if ( SWAP_BE32 (mdb->drCrDate) != vcb->localCreateDate )
{
mdb->drCrDate = SWAP_BE32 (vcb->localCreateDate);
(void) VOP_BWRITE(bp2);
}
else
{
brelse(bp2);
}
}
}
VCB_LOCK(vcb);
volumeHeader->attributes = SWAP_BE32 ((SWAP_BE32 (volumeHeader->attributes) & 0xFFFF0000) + (UInt16) vcb->vcbAtrb);
volumeHeader->lastMountedVersion = SWAP_BE32 (kHFSPlusMountVersion);
volumeHeader->createDate = SWAP_BE32 (vcb->localCreateDate);
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);
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);
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);
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 );
return (retval);
}
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
};