X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/43866e378188c25dd1e2208016ab3cbeb086ae6c..55e303ae13a4cf49d70f2294092726f2fffb9ef2:/bsd/hfs/hfs_vfsops.c?ds=sidebyside diff --git a/bsd/hfs/hfs_vfsops.c b/bsd/hfs/hfs_vfsops.c index 513f49ca6..d05facee6 100644 --- a/bsd/hfs/hfs_vfsops.c +++ b/bsd/hfs/hfs_vfsops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -74,11 +74,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include // XXXdbg #include @@ -118,11 +121,14 @@ static int hfs_mountfs __P((struct vnode *devvp, struct mount *mp, struct proc * struct hfs_mount_args *args)); static int hfs_statfs __P((struct mount *mp, register struct statfs *sbp, struct proc *p)); +static int hfs_flushfiles __P((struct mount *, int, struct proc *)); +static int hfs_extendfs __P((struct mount *, u_int64_t, struct proc *)); /* * Called by vfs_mountroot when mounting HFS Plus as root. */ +__private_extern__ int hfs_mountroot() { @@ -146,9 +152,13 @@ hfs_mountroot() } if ((error = hfs_mountfs(rootvp, mp, p, NULL))) { mp->mnt_vfc->vfc_refcount--; + + if (mp->mnt_kern_flag & MNTK_IO_XINFO) + FREE(mp->mnt_xinfo_ptr, M_TEMP); vfs_unbusy(mp, p); + vrele(rootvp); /* release the reference from bdevvp() */ - _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT); + FREE_ZONE(mp, sizeof (struct mount), M_MOUNT); return (error); } simple_lock(&mountlist_slock); @@ -208,7 +218,8 @@ hfs_mount(mp, path, data, ndp, p) if (mp->mnt_flag & MNT_UPDATE) { hfsmp = VFSTOHFS(mp); - if ((hfsmp->hfs_fs_ronly == 0) && (mp->mnt_flag & MNT_RDONLY)) { + if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) && + (mp->mnt_flag & MNT_RDONLY)) { /* use VFS_SYNC to push out System (btree) files */ retval = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p); @@ -221,7 +232,7 @@ hfs_mount(mp, path, data, ndp, p) if ((retval = hfs_flushfiles(mp, flags, p))) goto error_exit; - hfsmp->hfs_fs_ronly = 1; + hfsmp->hfs_flags |= HFS_READ_ONLY; retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); /* also get the volume bitmap blocks */ @@ -229,16 +240,30 @@ hfs_mount(mp, path, data, ndp, p) retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p); if (retval) { - hfsmp->hfs_fs_ronly = 0; + hfsmp->hfs_flags &= ~HFS_READ_ONLY; goto error_exit; } + + if (hfsmp->jnl) { + hfs_global_exclusive_lock_acquire(hfsmp); + + journal_close(hfsmp->jnl); + hfsmp->jnl = NULL; + + // Note: we explicitly don't want to shutdown + // access to the jvp because we may need + // it later if we go back to being read-write. + + hfs_global_exclusive_lock_release(hfsmp); + } } 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 ((hfsmp->hfs_flags & HFS_READ_ONLY) && + (mp->mnt_kern_flag & MNTK_WANTRDWR)) { /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. @@ -257,16 +282,61 @@ hfs_mount(mp, path, data, ndp, p) if (retval != E_NONE) goto error_exit; - /* only change hfs_fs_ronly after a successfull write */ - hfsmp->hfs_fs_ronly = 0; + // If the journal was shut-down previously because we were + // asked to be read-only, let's start it back up again now + + if ( (HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) + && hfsmp->jnl == NULL + && hfsmp->jvp != NULL) { + int flags; + + if (hfsmp->hfs_flags & HFS_NEED_JNL_RESET) { + flags = JOURNAL_RESET; + } else { + flags = 0; + } + + hfs_global_exclusive_lock_acquire(hfsmp); + + hfsmp->jnl = journal_open(hfsmp->jvp, + (hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset, + hfsmp->jnl_size, + hfsmp->hfs_devvp, + hfsmp->hfs_phys_block_size, + flags, + 0, + hfs_sync_metadata, hfsmp->hfs_mp); + + hfs_global_exclusive_lock_release(hfsmp); + + if (hfsmp->jnl == NULL) { + retval = EINVAL; + goto error_exit; + } else { + hfsmp->hfs_flags &= ~HFS_NEED_JNL_RESET; + } + + } + + /* Only clear HFS_READ_ONLY after a successfull write */ + hfsmp->hfs_flags &= ~HFS_READ_ONLY; } - if ((hfsmp->hfs_fs_ronly == 0) && + if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) && (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)) { /* setup private/hidden directory for unlinked files */ - hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(HFSTOVCB(hfsmp)); + FindMetaDataDirectory(HFSTOVCB(hfsmp)); if (hfsmp->jnl) hfs_remove_orphans(hfsmp); + + /* + * Allow hot file clustering if conditions allow. + */ + if ((hfsmp->hfs_flags & HFS_METADATA_ZONE) && + (mp->mnt_flag & MNT_RDONLY) && + (mp->mnt_kern_flag & MNTK_WANTRDWR)) { + (void) hfs_recording_init(hfsmp, p); + } } if (args.fspec == 0) { @@ -374,15 +444,22 @@ hfs_changefs(mp, args, p) 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))); + permswitch = (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) && + ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) == 0)) || + (((hfsmp->hfs_flags & HFS_UNKNOWN_PERMS) == 0) && + (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS))); + /* The root filesystem must operate with actual permissions: */ if (permswitch && (mp->mnt_flag & MNT_ROOTFS) && (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS)) { mp->mnt_flag &= ~MNT_UNKNOWNPERMISSIONS; /* Just say "No". */ return EINVAL; - }; - hfsmp->hfs_unknownpermissions = ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) != 0); - namefix = permfix = 0; + } + if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) + hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS; + else + hfsmp->hfs_flags &= ~HFS_UNKNOWN_PERMS; + + namefix = permfix = 0; /* Change the timezone (Note: this affects all hfs volumes and hfs+ volume create dates) */ if (args->hfs_timezone.tz_minuteswest != VNOVAL) { @@ -413,7 +490,7 @@ hfs_changefs(mp, args, p) /* Change the hfs encoding value (hfs only) */ if ((HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) && - (hfsmp->hfs_encoding != (u_long)VNOVAL) && + (args->hfs_encoding != (u_long)VNOVAL) && (hfsmp->hfs_encoding != args->hfs_encoding)) { retval = hfs_getconverter(args->hfs_encoding, &get_unicode_func, &get_hfsname_func); @@ -482,6 +559,7 @@ loop: continue; } + /* Get the real uid/gid and perm mask from disk. */ if (permswitch || permfix) { cp->c_uid = cnattr.ca_uid; cp->c_gid = cnattr.ca_gid; @@ -614,7 +692,6 @@ loop: return (error); } - /* update cnode's catalog descriptor */ (void) replace_desc(cp, &desc); } @@ -640,8 +717,10 @@ loop: vhp = (HFSPlusVolumeHeader *) (bp->b_data + HFS_PRI_OFFSET(sectorsize)); /* Do a quick sanity check */ - if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord || - SWAP_BE16(vhp->version) != kHFSPlusVersion || + if ((SWAP_BE16(vhp->signature) != kHFSPlusSigWord && + SWAP_BE16(vhp->signature) != kHFSXSigWord) || + (SWAP_BE16(vhp->version) != kHFSPlusVersion && + SWAP_BE16(vhp->version) != kHFSXVersion) || SWAP_BE32(vhp->blockSize) != vcb->blockSize) { brelse(bp); return (EIO); @@ -723,8 +802,11 @@ loop: cat_releasedesc(&cndesc); /* Re-establish private/hidden directory for unlinked files */ - hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb); + FindMetaDataDirectory(vcb); + /* In case any volume information changed to trigger a notification */ + hfs_generate_volume_notifications(hfsmp); + return (0); } @@ -924,8 +1006,6 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, 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 @@ -937,9 +1017,11 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, 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); + hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA; + if (ronly) + hfsmp->hfs_flags |= HFS_READ_ONLY; + if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) + hfsmp->hfs_flags |= HFS_UNKNOWN_PERMS; for (i = 0; i < MAXQUOTAS; i++) hfsmp->hfs_qfiles[i].qf_vp = NULLVP; @@ -974,9 +1056,9 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, /* 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; + hfsmp->hfs_flags |= HFS_WRITEABLE_MEDIA; else - hfsmp->hfs_media_writeable = 0; + hfsmp->hfs_flags &= ~HFS_WRITEABLE_MEDIA; } /* Mount a standard HFS disk */ @@ -1104,8 +1186,43 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, 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; + // if the journal failed to open, then set the lastMountedVersion + // to be "FSK!" which fsck_hfs will see and force the fsck instead + // of just bailing out because the volume is journaled. + if (ronly != 0 || devvp == rootvp) { + HFSPlusVolumeHeader *vhp; + + hfsmp->hfs_flags |= HFS_NEED_JNL_RESET; + + if (mdb_offset == 0) { + mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize); + } + + bp = NULL; + retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp); + if (retval == 0) { + vhp = (HFSPlusVolumeHeader *)(bp->b_data + HFS_PRI_OFFSET(blksize)); + + if (SWAP_BE16(vhp->signature) == kHFSPlusSigWord || SWAP_BE16(vhp->signature) == kHFSXSigWord) { + vhp->lastMountedVersion = SWAP_BE32('FSK!'); + bwrite(bp); + } else { + brelse(bp); + } + bp = NULL; + } else if (bp) { + brelse(bp); + } + } + + // if this isn't the root device just bail out. + // if it is the root device we just continue on + // in the hopes that fsck_hfs will be able to + // fix any damage that exists on the volume. + if (devvp != rootvp) { + retval = EINVAL; + goto error_exit; + } } } // XXXdbg @@ -1134,6 +1251,15 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, hfsmp->hfs_phys_block_count *= hfsmp->hfs_phys_block_size / blksize; hfsmp->hfs_phys_block_size = blksize; + if (hfsmp->jnl) { + // close and re-open this with the new block size + journal_close(hfsmp->jnl); + hfsmp->jnl = NULL; + if (hfs_early_journal_init(hfsmp, vhp, args, embeddedOffset, mdb_offset, mdbp, cred) == 0) { + mp->mnt_flag |= MNT_JOURNALED; + } + } + /* Try again with a smaller block size... */ retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args); } @@ -1150,6 +1276,45 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p, mp->mnt_maxsymlinklen = 0; devvp->v_specflags |= SI_MOUNTEDON; + if (args) { + /* + * Set the free space warning levels for a non-root volume: + * + * Set the lower freespace limit (the level that will trigger a warning) + * to 5% of the volume size or 250MB, whichever is less, and the desired + * level (which will cancel the alert request) to 1/2 above that limit. + * Start looking for free space to drop below this level and generate a + * warning immediately if needed: + */ + hfsmp->hfs_freespace_notify_warninglimit = + MIN(HFS_LOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize, + (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKTRIGGERFRACTION); + hfsmp->hfs_freespace_notify_desiredlevel = + MIN(HFS_LOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize, + (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKSHUTOFFFRACTION); + } else { + /* + * Set the free space warning levels for the root volume: + * + * Set the lower freespace limit (the level that will trigger a warning) + * to 1% of the volume size or 50MB, whichever is less, and the desired + * level (which will cancel the alert request) to 2% or 75MB, whichever is less. + */ + hfsmp->hfs_freespace_notify_warninglimit = + MIN(HFS_ROOTLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize, + (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKTRIGGERFRACTION); + hfsmp->hfs_freespace_notify_desiredlevel = + MIN(HFS_ROOTLOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize, + (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKSHUTOFFFRACTION); + }; + + /* + * Start looking for free space to drop below this level and generate a + * warning immediately if needed: + */ + hfsmp->hfs_notification_conditions = 0; + hfs_generate_volume_notifications(hfsmp); + if (ronly == 0) { (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); } @@ -1214,13 +1379,16 @@ hfs_unmount(mp, mntflags, p) if ((retval = hfs_flushfiles(mp, flags, p)) && !force) return (retval); + if (hfsmp->hfs_flags & HFS_METADATA_ZONE) + (void) hfs_recording_suspend(hfsmp, p); + /* * Flush out the b-trees, volume bitmap and Volume Header */ - if (hfsmp->hfs_fs_ronly == 0) { + if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) { hfs_global_shared_lock_acquire(hfsmp); grabbed_lock = 1; - if (hfsmp->jnl) { + if (hfsmp->jnl) { journal_start_transaction(hfsmp->jnl); started_tr = 1; } @@ -1242,18 +1410,27 @@ hfs_unmount(mp, mntflags, p) } } + if (hfsmp->hfc_filevp && (hfsmp->hfc_filevp->v_flag & VSYSTEM)) { + retval = VOP_FSYNC(hfsmp->hfc_filevp, NOCRED, MNT_WAIT, p); + if (retval && !force) + goto err_exit; + } + if (retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p)) { if (!force) goto err_exit; } - + +#if 0 /* See if this volume is damaged, is so do not unmount cleanly */ if (HFSTOVCB(hfsmp)->vcbFlags & kHFS_DamagedVolume) { HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask; } else { HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask; } - +#else + HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask; +#endif retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); if (retval) { HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask; @@ -1280,26 +1457,45 @@ hfs_unmount(mp, mntflags, p) */ (void) hfsUnmount(hfsmp, p); + /* + * Last chance to dump unreferenced system files. + */ + (void) vflush(mp, NULLVP, FORCECLOSE); + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) (void) hfs_relconverter(hfsmp->hfs_encoding); // XXXdbg if (hfsmp->jnl) { journal_close(hfsmp->jnl); + hfsmp->jnl = NULL; } if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) { - retval = VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, + retval = VOP_CLOSE(hfsmp->jvp, + hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(hfsmp->jvp); - hfsmp->jvp = NULL; + hfsmp->jvp = NULL; } // XXXdbg +#ifdef HFS_SPARSE_DEV + /* Drop our reference on the backing fs (if any). */ + if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) { + struct vnode * tmpvp; + + hfsmp->hfs_flags &= ~HFS_HAS_SPARSE_DEVICE; + tmpvp = hfsmp->hfs_backingfs_rootvp; + hfsmp->hfs_backingfs_rootvp = NULLVP; + vrele(tmpvp); + } +#endif /* HFS_SPARSE_DEV */ + hfsmp->hfs_devvp->v_specflags &= ~SI_MOUNTEDON; retval = VOP_CLOSE(hfsmp->hfs_devvp, - hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, - NOCRED, p); + hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, + NOCRED, p); if (retval && !force) return(retval); @@ -1551,9 +1747,8 @@ hfs_sync(mp, waitfor, cred, p) return (0); hfsmp = VFSTOHFS(mp); - if (hfsmp->hfs_fs_ronly != 0) { - panic("update: rofs mod"); - }; + if (hfsmp->hfs_flags & HFS_READ_ONLY) + return (EROFS); #if 0 // XXXdbg first go through and flush out any modified @@ -1590,12 +1785,7 @@ loop: // 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) { + if (vp->v_tag != VT_HFS || cp == NULL || vp->v_flag & (VXLOCK|VORECLAIM)) { simple_unlock(&vp->v_interlock); continue; } @@ -1619,9 +1809,15 @@ loop: } didhold = ubc_hold(vp); + + // mark the cnode so that fsync won't flush + // the journal since we're going to do that... + cp->c_flag |= C_FROMSYNC; if ((error = VOP_FSYNC(vp, cred, waitfor, p))) { allerror = error; }; + cp->c_flag &= ~C_FROMSYNC; + VOP_UNLOCK(vp, 0, p); if (didhold) ubc_rele(vp); @@ -1675,6 +1871,8 @@ loop: #if QUOTA hfs_qsync(mp); #endif /* QUOTA */ + + hfs_hotfilesync(hfsmp, p); /* * Write back modified superblock. */ @@ -1731,7 +1929,7 @@ hfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) * Get the export permission structure for this tuple. */ np = vfs_export_lookup(mp, &VFSTOHFS(mp)->hfs_export, nam); - if (np == NULL) { + if (nam && (np == NULL)) { return EACCES; }; @@ -1755,9 +1953,23 @@ hfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) return (ESTALE); }; + if (VNAME(nvp) == NULL) { + struct cnode *cp = VTOC(nvp); + + if (nvp == cp->c_rsrc_vp) { + // the +1/-2 thing is to skip the leading "/" on the rsrc fork spec + // and to not count the trailing null byte at the end of the string. + VNAME(nvp) = add_name(_PATH_RSRCFORKSPEC+1, sizeof(_PATH_RSRCFORKSPEC)-2, 0, 0); + } else { + VNAME(nvp) = add_name(cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen, 0, 0); + } + } + *vpp = nvp; - *exflagsp = np->netc_exflags; - *credanonp = &np->netc_anon; + if (np) { + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + } return (0); } @@ -1782,7 +1994,7 @@ hfs_vptofh(vp, fhp) hfsfhp = (struct hfsfid *)fhp; hfsfhp->hfsfid_len = sizeof(struct hfsfid); hfsfhp->hfsfid_pad = 0; - hfsfhp->hfsfid_cnid = cp->c_cnid; + hfsfhp->hfsfid_cnid = cp->c_fileid; hfsfhp->hfsfid_gen = cp->c_itime; return (0); @@ -1807,6 +2019,8 @@ hfs_init(vfsp) dqinit(); #endif /* QUOTA */ + BTReserveSetup(); + /* * Allocate Catalog Iterator cache... */ @@ -1815,6 +2029,31 @@ hfs_init(vfsp) return (0); } +static int +hfs_getmountpoint(vp, hfsmpp) + struct vnode *vp; + struct hfsmount **hfsmpp; +{ + struct hfsmount * hfsmp; + + if (vp == NULL) + return (EINVAL); + + if ((vp->v_flag & VROOT) == 0) + return (EINVAL); + + if (strcmp(vp->v_mount->mnt_stat.f_fstypename, "hfs") != 0) + return (EINVAL); + + hfsmp = VTOHFS(vp); + + if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) + return (EINVAL); + + *hfsmpp = hfsmp; + + return (0); +} // XXXdbg #include @@ -1833,17 +2072,68 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) size_t newlen; struct proc *p; { - extern u_int32_t hfs_encodingbias; + extern u_int32_t hfs_getencodingbias(void); + extern void hfs_setencodingbias(u_int32_t); + + int error; + struct sysctl_req *req; + struct vfsidctl vc; + struct mount *mp; + struct hfsmount *hfsmp; + struct vfsquery vq; /* 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) { + if (name[0] == HFS_ENCODINGBIAS) { + u_int32_t bias; + + bias = hfs_getencodingbias(); + error = sysctl_int(oldp, oldlenp, newp, newlen, &bias); + if (error == 0 && newp) + hfs_setencodingbias(bias); + return (error); + + } else if (name[0] == HFS_EXTEND_FS) { + u_int64_t newsize; + + if (newp == NULL) + return (EINVAL); + if ((error = hfs_getmountpoint(p->p_fd->fd_cdir, &hfsmp))) + return (error); + error = sysctl_quad(oldp, oldlenp, newp, newlen, &newsize); + if (error) + return (error); + + error = hfs_extendfs(HFSTOVFS(hfsmp), newsize, p); + return (error); + + } else if (name[0] == HFS_ENCODINGHINT) { + size_t bufsize; + size_t bytes; + u_int32_t hint; + u_int16_t *unicode_name; + char *filename; + + bufsize = MAX(newlen * 3, MAXPATHLEN); + MALLOC(filename, char *, newlen, M_TEMP, M_WAITOK); + MALLOC(unicode_name, u_int16_t *, bufsize, M_TEMP, M_WAITOK); + + error = copyin(newp, (caddr_t)filename, newlen); + if (error == 0) { + error = utf8_decodestr(filename, newlen - 1, unicode_name, + &bytes, bufsize, 0, UTF_DECOMPOSED); + if (error == 0) { + hint = hfs_pickencoding(unicode_name, bytes / 2); + error = sysctl_int(oldp, oldlenp, NULL, NULL, &hint); + } + } + FREE(unicode_name, M_TEMP); + FREE(filename, M_TEMP); + return (error); + + } else if (name[0] == HFS_ENABLE_JOURNALING) { // 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; @@ -1851,11 +2141,12 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) void *jnl = NULL; /* Only root can enable journaling */ - if (current_proc()->p_ucred->cr_uid != 0) { + if (current_proc()->p_ucred->cr_uid != 0) { return (EPERM); } + hfsmp = VTOHFS(vp); - if (hfsmp->hfs_fs_ronly) { + if (hfsmp->hfs_flags & HFS_READ_ONLY) { return EROFS; } if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) { @@ -1893,7 +2184,7 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) jnl = journal_create(jvp, (off_t)name[2] * (off_t)HFSTOVCB(hfsmp)->blockSize + HFSTOVCB(hfsmp)->hfsPlusIOPosOffset, - (off_t)name[3], + (off_t)((unsigned)name[3]), hfsmp->hfs_devvp, hfsmp->hfs_phys_block_size, 0, @@ -1903,7 +2194,7 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) 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); + VOP_CLOSE(jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, FSCRED, p); } jvp = NULL; @@ -1919,6 +2210,7 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) // save this off for the hack-y check in hfs_remove() hfsmp->jnl_start = (u_int32_t)name[2]; + hfsmp->jnl_size = (off_t)((unsigned)name[3]); hfsmp->hfs_jnlinfoblkid = jinfo_attr.ca_fileid; hfsmp->hfs_jnlfileid = jnl_attr.ca_fileid; @@ -1928,21 +2220,18 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); return 0; - } else if (name[0] == 0x031272) { + } else if (name[0] == HFS_DISABLE_JOURNALING) { // 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) { + 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); @@ -1955,7 +2244,7 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) journal_close(jnl); if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) { - VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, FSCRED, p); + VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, FSCRED, p); } hfsmp->jnl = NULL; hfsmp->jvp = NULL; @@ -1970,7 +2259,45 @@ hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1); return 0; - } + } else if (name[0] == HFS_GET_JOURNAL_INFO) { + struct vnode *vp = p->p_fd->fd_cdir; + off_t jnl_start, jnl_size; + + hfsmp = VTOHFS(vp); + if (hfsmp->jnl == NULL) { + jnl_start = 0; + jnl_size = 0; + } else { + jnl_start = (off_t)(hfsmp->jnl_start * HFSTOVCB(hfsmp)->blockSize) + (off_t)HFSTOVCB(hfsmp)->hfsPlusIOPosOffset; + jnl_size = (off_t)hfsmp->jnl_size; + } + + if ((error = copyout((caddr_t)&jnl_start, (void *)name[1], sizeof(off_t))) != 0) { + return error; + } + if ((error = copyout((caddr_t)&jnl_size, (void *)name[2], sizeof(off_t))) != 0) { + return error; + } + + return 0; + } else if (name[0] == HFS_SET_PKG_EXTENSIONS) { + + return set_package_extensions_table((void *)name[1], name[2], name[3]); + + } else if (name[0] == VFS_CTL_QUERY) { + req = oldp; /* we're new style vfs sysctl. */ + + error = SYSCTL_IN(req, &vc, sizeof(vc)); + if (error) return (error); + + mp = vfs_getvfs(&vc.vc_fsid); + if (mp == NULL) return (ENOENT); + + hfsmp = VFSTOHFS(mp); + bzero(&vq, sizeof(vq)); + vq.vq_flags = hfsmp->hfs_notification_conditions; + return SYSCTL_OUT(req, &vq, sizeof(vq));; + }; return (EOPNOTSUPP); } @@ -1998,37 +2325,150 @@ hfs_vget(mp, ino, vpp) return (hfs_getcnode(VFSTOHFS(mp), cnid, NULL, 0, NULL, NULL, vpp)); } +/* + * Check to see if a given vnode is only referenced for events: + * [ entered with vp->v_interlock locked ] + */ +static int +hfs_evtonly(struct vnode *vp) +{ + int ubc_refcount; + + ubc_refcount = UBCINFOEXISTS(vp) ? 1 : 0; + return (vp->v_usecount == (ubc_refcount + EVTONLYREFS(vp))); +} + +/* + * Check to see if all non-system vnodes for a given mountpoint are events-only + */ +static int +hfs_flush_evtonly(struct mount *mp, int flags, int dispose, struct proc *p) +{ + struct vnode *vp, *nvp; + int busy = 0; + + simple_lock(&mntvnode_slock); +loop: + for (vp = LIST_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) { + if (vp->v_mount != mp) goto loop; + nvp = vp->v_mntvnodes.le_next; + + simple_lock(&vp->v_interlock); + /* + * Skip over a vnodes marked VSYSTEM or VNOFLUSH. + */ + if ((flags & SKIPSYSTEM) && ((vp->v_flag & VSYSTEM) || (vp->v_flag & VNOFLUSH))) { + simple_unlock(&vp->v_interlock); + continue; + }; + /* + * Skip over a vnodes marked VSWAP. + */ + if ((flags & SKIPSWAP) && (vp->v_flag & VSWAP)) { + simple_unlock(&vp->v_interlock); + continue; + } + if (hfs_evtonly(vp)) { + if (dispose) { + /* "dispose" implies "forcibly", a la "FORCECLOSE": */ + simple_unlock(&mntvnode_slock); + vgonel(vp, p); + simple_lock(&mntvnode_slock); + } else { + simple_unlock(&vp->v_interlock); + }; + continue; + }; + + simple_unlock(&vp->v_interlock); + ++busy; + /* If asked to dispose, keep trying. If only checking, the answer is now known. */ + if (dispose) { + continue; + } else { + break; + }; + } + simple_unlock(&mntvnode_slock); + + return (busy == 0); +} + /* * Flush out all the files in a filesystem. */ -int +static int hfs_flushfiles(struct mount *mp, int flags, struct proc *p) { - register struct hfsmount *hfsmp; + struct hfsmount *hfsmp; + struct vnode *skipvp = NULLVP; + struct vnode *rsrcvp; + int quotafilecnt; int i; int error; -#if QUOTA hfsmp = VFSTOHFS(mp); +#if QUOTA + /* + * The open quota files have an indirect reference on + * the root directory vnode. We must account for this + * extra reference when doing the intial vflush. + */ + quotafilecnt = 0; + if (mp->mnt_flag & MNT_QUOTA) { + + /* Find out how many quota files we have open. */ + for (i = 0; i < MAXQUOTAS; i++) { + if (hfsmp->hfs_qfiles[i].qf_vp != NULLVP) + ++quotafilecnt; + } + + /* Obtain the root vnode so we can skip over it. */ + if (hfs_chashget(hfsmp->hfs_raw_dev, kRootDirID, 0, + &skipvp, &rsrcvp) == NULL) { + skipvp = NULLVP; + } + } +#endif /* QUOTA */ + + error = vflush(mp, skipvp, SKIPSYSTEM | SKIPSWAP | flags); + /* + * If the vflush() call failed solely because there are + * some event-only vnodes in the list, then forcibly get + * rid of those vnodes before the final vflush() pass. + */ + if ((error == EBUSY) && hfs_flush_evtonly(mp, SKIPSYSTEM | SKIPSWAP, 0, p)) { + (void) hfs_flush_evtonly(mp, SKIPSYSTEM | SKIPSWAP, 1, p); + }; + error = vflush(mp, skipvp, SKIPSYSTEM | flags); + +#if QUOTA if (mp->mnt_flag & MNT_QUOTA) { - if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags)) + if (skipvp) { + /* + * See if there are additional references on the + * root vp besides the ones obtained from the open + * quota files and the hfs_chashget call above. + */ + if ((error == 0) && + (skipvp->v_usecount > (1 + quotafilecnt))) { + error = EBUSY; /* root directory is still open */ + } + vput(skipvp); + } + if (error && (flags & FORCECLOSE) == 0) 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. - */ + error = vflush(mp, NULLVP, SKIPSYSTEM | flags); } #endif /* QUOTA */ - error = vflush(mp, NULLVP, (SKIPSYSTEM | SKIPSWAP | flags)); - error = vflush(mp, NULLVP, (SKIPSYSTEM | flags)); - return (error); } @@ -2056,8 +2496,8 @@ hfs_setencodingbits(struct hfsmount *hfsmp, u_int32_t encoding) break; } - if (index < 128) { - HFSTOVCB(hfsmp)->encodingsBitmap |= (1 << index); + if (index < 64) { + HFSTOVCB(hfsmp)->encodingsBitmap |= (u_int64_t)(1ULL << index); HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00; } } @@ -2209,7 +2649,14 @@ hfs_flushMDB(struct hfsmount *hfsmp, int waitfor, int altflush) return (retval); } - +/* + * Flush any dirty in-memory mount data to the on-disk + * volume header. + * + * Note: the on-disk volume signature is intentionally + * not flushed since the on-disk "H+" and "HX" signatures + * are always stored in-memory as "H+". + */ __private_extern__ int hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) @@ -2223,7 +2670,12 @@ hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) int sectorsize; int priIDSector; int critical = 0; + u_int16_t signature; + u_int16_t version; + if (hfsmp->hfs_flags & HFS_READ_ONLY) { + return(0); + } if (vcb->vcbSigWord == kHFSSigWord) return hfs_flushMDB(hfsmp, waitfor, altflush); @@ -2252,6 +2704,7 @@ hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) } hfs_global_shared_lock_release(hfsmp); + printf("HFS: err %d reading VH blk (%s)\n", retval, vcb->vcbVN); return (retval); } @@ -2261,6 +2714,24 @@ hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data + HFS_PRI_OFFSET(sectorsize)); + /* + * Sanity check what we just read. + */ + signature = SWAP_BE16 (volumeHeader->signature); + version = SWAP_BE16 (volumeHeader->version); + if ((signature != kHFSPlusSigWord && signature != kHFSXSigWord) || + (version < kHFSPlusVersion) || (version > 100) || + (SWAP_BE32 (volumeHeader->blockSize) != vcb->blockSize)) { +#if 1 + panic("HFS: corrupt VH on %s, sig 0x%04x, ver %d, blksize %d", + vcb->vcbVN, signature, version, + SWAP_BE32 (volumeHeader->blockSize)); +#endif + printf("HFS: corrupt VH blk (%s)\n", vcb->vcbVN); + brelse(bp); + return (EIO); + } + /* * For embedded HFS+ volumes, update create date if it changed * (ie from a setattrlist call) @@ -2303,28 +2774,6 @@ hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) } } -// 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 - /* Note: only update the lower 16 bits worth of attributes */ volumeHeader->attributes = SWAP_BE32 ((SWAP_BE32 (volumeHeader->attributes) & 0xFFFF0000) + (UInt16) vcb->vcbAtrb); volumeHeader->journalInfoBlock = SWAP_BE32(vcb->vcbJinfoBlock); @@ -2436,6 +2885,251 @@ hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor, int altflush) } +/* + * Extend a file system. + */ +static int +hfs_extendfs(struct mount *mp, u_int64_t newsize, struct proc *p) +{ + struct vnode *vp; + struct vnode *devvp; + struct buf *bp; + struct hfsmount *hfsmp; + struct filefork *fp = NULL; + ExtendedVCB *vcb; + struct cat_fork forkdata; + u_int64_t oldsize; + u_int64_t newblkcnt; + u_int32_t addblks; + u_int64_t sectorcnt; + u_int32_t sectorsize; + daddr_t prev_alt_sector; + daddr_t bitmapblks; + int error; + + hfsmp = VFSTOHFS(mp); + devvp = hfsmp->hfs_devvp; + vcb = HFSTOVCB(hfsmp); + + /* + * - HFS Plus file systems only. + * - Journaling must be enabled. + * - No embedded volumes. + */ + if ((vcb->vcbSigWord == kHFSSigWord) || + (hfsmp->jnl == NULL) || + (vcb->hfsPlusIOPosOffset != 0)) { + return (EPERM); + } + /* + * If extending file system by non-root, then verify + * ownership and check permissions. + */ + if (p->p_ucred->cr_uid != 0) { + error = hfs_root(mp, &vp); + if (error) + return (error); + error = hfs_owner_rights(hfsmp, VTOC(vp)->c_uid, p->p_ucred, p, 0); + if (error == 0) { + error = hfs_write_access(vp, p->p_ucred, p, false); + } + vput(vp); + if (error) + return (error); + + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p); + VOP_UNLOCK(devvp, 0, p); + if (error) + return (error); + } + if (VOP_IOCTL(devvp, DKIOCGETBLOCKSIZE, (caddr_t)§orsize, 0, FSCRED, p)) { + return (ENXIO); + } + if (sectorsize != hfsmp->hfs_phys_block_size) { + return (ENXIO); + } + if (VOP_IOCTL(devvp, DKIOCGETBLOCKCOUNT, (caddr_t)§orcnt, 0, FSCRED, p)) { + return (ENXIO); + } + if ((sectorsize * sectorcnt) < newsize) { + printf("hfs_extendfs: not enough space on device\n"); + return (ENOSPC); + } + oldsize = (u_int64_t)hfsmp->hfs_phys_block_count * + (u_int64_t)hfsmp->hfs_phys_block_size; + + /* + * Validate new size. + */ + if ((newsize <= oldsize) || (newsize % vcb->blockSize)) { + printf("hfs_extendfs: invalid size\n"); + return (EINVAL); + } + newblkcnt = newsize / vcb->blockSize; + if (newblkcnt > (u_int64_t)0xFFFFFFFF) + return (EOVERFLOW); + + addblks = newblkcnt - vcb->totalBlocks; + + printf("hfs_extendfs: growing %s by %d blocks\n", vcb->vcbVN, addblks); + /* + * Enclose changes inside a transaction. + */ + hfs_global_shared_lock_acquire(hfsmp); + if (journal_start_transaction(hfsmp->jnl) != 0) { + hfs_global_shared_lock_release(hfsmp); + return (EINVAL); + } + + /* + * Remember the location of existing alternate VH. + */ + prev_alt_sector = (vcb->hfsPlusIOPosOffset / sectorsize) + + HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count); + + vp = vcb->allocationsRefNum; + error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (error) { + goto out2; + } + fp = VTOF(vp); + bcopy(&fp->ff_data, &forkdata, sizeof(forkdata)); + + /* + * Calculate additional space required (if any) by allocation bitmap. + */ + bitmapblks = roundup(newblkcnt / 8, vcb->vcbVBMIOSize) / vcb->blockSize; + if (bitmapblks > fp->ff_blocks) + bitmapblks -= fp->ff_blocks; + else + bitmapblks = 0; + + if (bitmapblks > 0) { + daddr_t blkno; + daddr_t blkcnt; + + /* + * Add a new extent to the allocation bitmap file. + */ + error = AddFileExtent(vcb, fp, vcb->totalBlocks, bitmapblks); + if (error) { + printf("hfs_extendfs: error %d adding extents\n", error); + goto out; + } + blkcnt = bitmapblks; + blkno = fp->ff_blocks; + fp->ff_blocks += bitmapblks; + fp->ff_size += (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize; + VTOC(vp)->c_blocks = fp->ff_blocks; + /* + * Zero out the new bitmap blocks. + */ + { + + bp = NULL; + while (blkcnt > 0) { + error = meta_bread(vp, blkno, vcb->blockSize, NOCRED, &bp); + if (error) { + if (bp) { + brelse(bp); + } + break; + } + bzero((char *)bp->b_data, vcb->blockSize); + bp->b_flags |= B_AGE; + error = bwrite(bp); + if (error) + break; + --blkcnt; + ++blkno; + } + } + if (error) { + printf("hfs_extendfs: error %d clearing blocks\n", error); + goto out; + } + /* + * Mark the new bitmap space as allocated. + */ + error = BlockMarkAllocated(vcb, vcb->totalBlocks, bitmapblks); + if (error) { + printf("hfs_extendfs: error %d setting bitmap\n", error); + goto out; + } + } + /* + * Mark the new alternate VH as allocated. + */ + if (vcb->blockSize == 512) + error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 2, 2); + else + error = BlockMarkAllocated(vcb, vcb->totalBlocks + addblks - 1, 1); + if (error) { + printf("hfs_extendfs: error %d setting bitmap (VH)\n", error); + goto out; + } + /* + * Mark the old alternate VH as free. + */ + if (vcb->blockSize == 512) + (void) BlockMarkFree(vcb, vcb->totalBlocks - 2, 2); + else + (void) BlockMarkFree(vcb, vcb->totalBlocks - 1, 1); + + /* + * Adjust file system variables for new space. + */ + vcb->totalBlocks += addblks; + vcb->freeBlocks += addblks - bitmapblks; + hfsmp->hfs_phys_block_count = newsize / sectorsize; + + MarkVCBDirty(vcb); + error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH); + if (error) { + printf("hfs_extendfs: couldn't flush volume headers (%d)", error); + /* + * Restore to old state. + */ + fp->ff_size -= (u_int64_t)bitmapblks * (u_int64_t)vcb->blockSize; + vcb->totalBlocks -= addblks; + vcb->freeBlocks -= addblks - bitmapblks; + hfsmp->hfs_phys_block_count = oldsize / sectorsize; + MarkVCBDirty(vcb); + if (vcb->blockSize == 512) + (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 2, 2); + else + (void) BlockMarkAllocated(vcb, vcb->totalBlocks - 1, 1); + goto out; + } + /* + * Invalidate the old alternate volume header. + */ + bp = NULL; + if (meta_bread(hfsmp->hfs_devvp, prev_alt_sector, sectorsize, + NOCRED, &bp) == 0) { + journal_modify_block_start(hfsmp->jnl, bp); + bzero(bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize); + journal_modify_block_end(hfsmp->jnl, bp); + } else if (bp) { + brelse(bp); + } +out: + if (error && fp) { + /* Restore allocation fork. */ + bcopy(&forkdata, &fp->ff_data, sizeof(forkdata)); + VTOC(vp)->c_blocks = fp->ff_blocks; + + } + VOP_UNLOCK(vp, 0, p); +out2: + journal_end_transaction(hfsmp->jnl); + hfs_global_shared_lock_release(hfsmp); + + return (error); +} + + /* * hfs vfs operations. */