/*
- * 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>
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) {
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);
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);
}
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 */
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
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);
}
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 ((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;
}
}
}
+ 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;
*/
(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);
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
// 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;
}
}
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);
#if QUOTA
hfs_qsync(mp);
#endif /* QUOTA */
+
+ hfs_hotfilesync(hfsmp, p);
/*
* Write back modified superblock.
*/
* 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>
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;
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) {
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,
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;
// 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;
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);
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;
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) {
+
+ /* 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);
}
break;
}
- if (index < 128) {
- HFSTOVCB(hfsmp)->encodingsBitmap |= (1 << index);
+ if (index < 64) {
+ HFSTOVCB(hfsmp)->encodingsBitmap |= (u_int64_t)(1ULL << index);
HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00;
}
}
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);
}
hfs_global_shared_lock_release(hfsmp);
+ printf("HFS: err %d reading VH blk (%s)\n", retval, vcb->vcbVN);
return (retval);
}
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)
}
}
-// 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);
}
+/*
+ * 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.
*/