/*
- * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/mount.h>
+#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/stat.h>
#include <sys/lock.h>
#include <sys/quota.h>
#include <sys/disk.h>
+#include <sys/paths.h>
+#include <sys/utfconv.h>
+
+// XXXdbg
+#include <vfs/vfs_journal.h>
#include <miscfs/specfs/specdev.h>
#include <hfs/hfs_mount.h>
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()
{
}
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);
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);
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 */
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.
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) {
goto error_exit;
}
-
/* Set the mount flag to indicate that we support volfs */
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);
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) {
/* 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);
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;
return (error);
}
-
/* update cnode's catalog descriptor */
(void) replace_desc(cp, &desc);
}
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);
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));
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);
}
+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;
+
+ *rvp = NULL;
+
+ MALLOC(rawbuf, char *, MAXPATHLEN, M_HFSMNT, M_WAITOK);
+ if (rawbuf == NULL) {
+ retval = ENOMEM;
+ goto error_exit;
+ }
+
+ 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';
+
+ dp = &rawbuf[namelen-1];
+ while(dp >= rawbuf && *dp != '/') {
+ dp--;
+ }
+
+ 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';
+
+ 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;
+ }
+
+ *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
*/
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;
return (retval);
}
+ mdb_offset = HFS_PRI_SECTOR(blksize);
if ((retval = meta_bread(devvp, HFS_PRI_SECTOR(blksize), blksize, cred, &bp))) {
goto error_exit;
}
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
*/
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;
/* 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 */
} else /* Mount an HFS Plus disk */ {
HFSPlusVolumeHeader *vhp;
off_t embeddedOffset;
+ int jnl_disable = 0;
/* Get the embedded Volume Header */
if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
hfsmp->hfs_phys_block_count = disksize / blksize;
- retval = meta_bread(devvp, (embeddedOffset / blksize) +
- HFS_PRI_SECTOR(blksize), blksize, cred, &bp);
+ 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);
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 {
+ // 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
+
(void) hfs_getconverter(0, &hfsmp->hfs_get_unicode, &hfsmp->hfs_get_hfsname);
- retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p);
+ 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.
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);
+ retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embeddedOffset, disksize, p, args);
}
if (retval)
(void) hfs_relconverter(0);
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);
}
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;
int retval = E_NONE;
int flags;
int force;
+ int started_tr = 0, grabbed_lock = 0;
flags = 0;
force = 0;
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) {
+ journal_start_transaction(hfsmp->jnl);
+ started_tr = 1;
+ }
+
retval = VOP_FSYNC(HFSTOVCB(hfsmp)->catalogRefNum, NOCRED, MNT_WAIT, p);
if (retval && !force)
- return (retval);
-
+ goto err_exit;
+
retval = VOP_FSYNC(HFSTOVCB(hfsmp)->extentsRefNum, NOCRED, MNT_WAIT, p);
if (retval && !force)
- return (retval);
+ 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 (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)
- return (retval);
+ 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;
}
-
- retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
+#else
+ HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask;
+#endif
+ retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT, 1);
if (retval) {
HFSTOVCB(hfsmp)->vcbAtrb &= ~kHFSVolumeUnmountedMask;
if (!force)
- return (retval); /* could not flush everything */
+ 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
*/
(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_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE,
+ NOCRED, p);
+ vrele(hfsmp->jvp);
+ 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);
FREE(hfsmp, M_HFSMNT);
mp->mnt_data = (qaddr_t)0;
return (0);
+
+ err_exit:
+ if (hfsmp->jnl && started_tr) {
+ journal_end_transaction(hfsmp->jnl);
+ }
+ if (grabbed_lock) {
+ hfs_global_shared_lock_release(hfsmp);
+ }
+ return retval;
}
}
+
+
/*
* Get file system statistics.
*/
}
+//
+// 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;
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
+ // 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
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.
+ if (vp->v_tag != VT_HFS || cp == NULL || vp->v_flag & (VXLOCK|VORECLAIM)) {
+ 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(&mntvnode_slock);
error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
if (error) {
- if (error == ENOENT)
+ if (error == ENOENT) {
+ /*
+ * If vnode is being reclaimed, yield so
+ * that it can be removed from our list.
+ */
+ if (UBCISVALID(vp))
+ (void) tsleep((caddr_t)&lbolt, PINOD, "hfs_sync", 0);
goto loop;
+ }
simple_lock(&mntvnode_slock);
continue;
}
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);
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) &&
#if QUOTA
hfs_qsync(mp);
#endif /* QUOTA */
+
+ hfs_hotfilesync(hfsmp, p);
/*
* 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 (error)
+ allerror = error;
}
+ if (hfsmp->jnl) {
+ journal_flush(hfsmp->jnl);
+ }
+
+ err_exit:
return (allerror);
}
* Get the export permission structure for this <mp, client> tuple.
*/
np = vfs_export_lookup(mp, &VFSTOHFS(mp)->hfs_export, nam);
- if (np == NULL) {
+ if (nam && (np == NULL)) {
return EACCES;
};
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);
}
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);
dqinit();
#endif /* QUOTA */
+ BTReserveSetup();
+
/*
* Allocate Catalog Iterator cache...
*/
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 <sys/filedesc.h>
+
/*
* HFS filesystem related variables.
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 (namelen != 1)
- return (ENOTDIR); /* overloaded */
- if (name[0] == HFS_ENCODINGBIAS)
- return (sysctl_int(oldp, oldlenp, newp, newlen,
- &hfs_encodingbias));
+ 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;
+ 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_flags & HFS_READ_ONLY) {
+ return EROFS;
+ }
+ if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
+ printf("hfs: can't make a plain hfs volume journaled.\n");
+ return EINVAL;
+ }
+
+ 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) {
+
+ printf("hfs: volume has a btree w/non-contiguous nodes. can not enable journaling.\n");
+ return EINVAL;
+ }
+
+ // 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) {
+
+ return EINVAL;
+ }
+
+ 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)((unsigned)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_flags & HFS_READ_ONLY ? FREAD : FREAD|FWRITE, FSCRED, p);
+ }
+ jvp = NULL;
+
+ return EINVAL;
+ }
+
+ hfs_global_exclusive_lock_acquire(hfsmp);
+
+ 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->jnl_size = (off_t)((unsigned)name[3]);
+ 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] == HFS_DISABLE_JOURNALING) {
+ // clear the journaling bit
+ struct vnode *vp = p->p_fd->fd_cdir;
+ void *jnl;
+ int retval;
+
+ /* Only root can disable journaling */
+ if (current_proc()->p_ucred->cr_uid != 0) {
+ return (EPERM);
+ }
+
+ hfsmp = VTOHFS(vp);
+
+ printf("hfs: disabling journaling for mount @ 0x%x\n", vp->v_mount);
+
+ jnl = hfsmp->jnl;
+
+ hfs_global_exclusive_lock_acquire(hfsmp);
+
+ // Lights out for you buddy!
+ hfsmp->jnl = NULL;
+ journal_close(jnl);
+
+ if (hfsmp->jvp && hfsmp->jvp != hfsmp->hfs_devvp) {
+ VOP_CLOSE(hfsmp->jvp, hfsmp->hfs_flags & HFS_READ_ONLY ? 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;
+ } 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);
}
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) {
- if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags))
+
+ /* 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 (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);
}
break;
}
- if (index < 128) {
- HFSTOVCB(hfsmp)->encodingsBitmap |= (1 << index);
+ if (index < 64) {
+ HFSTOVCB(hfsmp)->encodingsBitmap |= (u_int64_t)(1ULL << index);
HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00;
}
}
--vcb->vcbNmFls;
break;
}
+
+ if (hfsmp->jnl) {
+ hfs_flushvolumeheader(hfsmp, 0, 0);
+ }
+
return (0);
}
ByteCount namelen;
sectorsize = hfsmp->hfs_phys_block_size;
-
retval = bread(hfsmp->hfs_devvp, HFS_PRI_SECTOR(sectorsize), sectorsize, NOCRED, &bp);
if (retval) {
if (bp)
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 *)(bp->b_data + HFS_PRI_OFFSET(sectorsize));
mdb->drCrDate = SWAP_BE32 (UTCToLocal(to_hfs_time(vcb->vcbCrDate)));
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)
bawrite(bp);
- else
+ else
retval = VOP_BWRITE(bp);
MarkVCBClean( vcb );
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)
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);
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) {
if (bp)
brelse(bp);
+
+ if (hfsmp->jnl) {
+ journal_end_transaction(hfsmp->jnl);
+ }
+ hfs_global_shared_lock_release(hfsmp);
+
+ printf("HFS: err %d reading VH blk (%s)\n", retval, vcb->vcbVN);
return (retval);
}
+ if (hfsmp->jnl) {
+ journal_modify_block_start(hfsmp->jnl, bp);
+ }
+
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)
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
{
/* 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->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));
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);
- (void) VOP_BWRITE(alt_bp);
+
+ if (hfsmp->jnl) {
+ journal_modify_block_end(hfsmp->jnl, alt_bp);
+ } else {
+ (void) VOP_BWRITE(alt_bp);
+ }
} else if (alt_bp)
brelse(alt_bp);
}
- if (waitfor != MNT_WAIT)
- bawrite(bp);
- else {
- retval = VOP_BWRITE(bp);
- /* When critical data changes, flush the device cache */
- if (critical && (retval == 0)) {
+ // 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());
+ NULL, FWRITE, NOCRED, current_proc());
+ }
}
}
+ hfs_global_shared_lock_release(hfsmp);
vcb->vcbFlags &= 0x00FF;
return (retval);
}
+/*
+ * 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.
*/