]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_vfsutils.c
xnu-344.12.2.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
index c45f8a898cb04cbdb99732df5e63635e65ade57f..386acae02507bf36778e8f948c27a8a8648bf439 100644 (file)
@@ -55,6 +55,7 @@ extern uid_t console_user;
 
 
 static void ReleaseMetaFileVNode(struct vnode *vp);
+static int  hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
 
 u_int32_t GetLogicalBlockSize(struct vnode *vp);
 
@@ -246,7 +247,7 @@ CmdDone:
 //*******************************************************************************
 
 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
-       off_t embeddedOffset, u_int64_t disksize, struct proc *p)
+       off_t embeddedOffset, u_int64_t disksize, struct proc *p, void *args)
 {
        register ExtendedVCB *vcb;
        struct cat_desc cndesc;
@@ -254,9 +255,15 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
        UInt32 blockSize;
        OSErr retval;
 
-       if (SWAP_BE16(vhp->signature) != kHFSPlusSigWord ||
-           SWAP_BE16(vhp->version) != kHFSPlusVersion)
-               return (EINVAL);
+       // XXXdbg - added the kHFSJSigWord case
+       if ((SWAP_BE16(vhp->signature) != kHFSPlusSigWord &&
+                SWAP_BE16(vhp->signature) != kHFSJSigWord) ||
+           SWAP_BE16(vhp->version) != kHFSPlusVersion) {
+               // XXXdbg
+               printf("hfs: mount: sig 0x%x and version 0x%x are not HFS or HFS+.\n",
+                          vhp->signature, vhp->version);
+               return (EINVAL);
+       }
 
        /* Block size must be at least 512 and a power of 2 */
        blockSize = SWAP_BE32(vhp->blockSize);
@@ -264,7 +271,7 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
                return (EINVAL);
    
        /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
-       if (hfsmp->hfs_fs_ronly == 0 && (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
+       if (hfsmp->hfs_fs_ronly == 0 && hfsmp->jnl == NULL && (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
                return (EINVAL);
 
        /* Make sure we can live with the physical block size. */
@@ -280,6 +287,13 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
        vcb = HFSTOVCB(hfsmp);
 
        vcb->vcbSigWord = SWAP_BE16(vhp->signature);
+
+       // XXXdbg - remap this in case we've mounted a dirty journaled volume
+       if (vcb->vcbSigWord == kHFSJSigWord) {
+               vcb->vcbSigWord = kHFSPlusSigWord;
+       }
+
+       vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
        vcb->vcbLsMod   = to_bsd_time(SWAP_BE32(vhp->modifyDate));
        vcb->vcbAtrb    = (UInt16)SWAP_BE32(vhp->attributes);
        vcb->vcbClpSiz  = SWAP_BE32(vhp->rsrcClumpSize);
@@ -413,6 +427,9 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
 
        /* mark the volume dirty (clear clean unmount bit) */
        vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
+       if (hfsmp->jnl && hfsmp->hfs_fs_ronly == 0) {
+               hfs_flushvolumeheader(hfsmp, TRUE, TRUE);
+       }
 
        /*
         * all done with metadata files so we can unlock now...
@@ -423,12 +440,46 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
 
        /* setup private/hidden directory for unlinked files */
        hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
+       if (hfsmp->jnl && (hfsmp->hfs_fs_ronly == 0))
+               hfs_remove_orphans(hfsmp);
 
        if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )     // if the disk is not write protected
        {
                MarkVCBDirty( vcb );    // mark VCB dirty so it will be written
        }
 
+
+       //
+       // Check if we need to do late journal initialization.  This only
+       // happens if a previous version of MacOS X (or 9) touched the disk.
+       // In that case hfs_late_journal_init() will go re-locate the journal 
+       // and journal_info_block files and validate that they're still kosher.
+       //
+       if (   (vcb->vcbAtrb & kHFSVolumeJournaledMask)
+               && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
+               && (hfsmp->jnl == NULL)) {
+
+               retval = hfs_late_journal_init(hfsmp, vhp, args);
+               if (retval != 0) {
+                       hfsmp->jnl = NULL;
+                       goto ErrorExit;
+               } else if (hfsmp->jnl) {
+                       hfsmp->hfs_mp->mnt_flag |= MNT_JOURNALED;
+               }
+       } else if (hfsmp->jnl) {
+               struct cat_attr jinfo_attr, jnl_attr;
+               
+               // if we're here we need to fill in the fileid's for the
+               // journal and journal_info_block.
+               hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
+               hfsmp->hfs_jnlfileid    = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
+               if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
+                       printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
+                       printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
+               }
+       }
+
+
        return (0);
 
 ErrorExit:
@@ -759,13 +810,28 @@ FindMetaDataDirectory(ExtendedVCB *vcb)
        fndrinfo->frLocation.h = SWAP_BE16 (22460);
        fndrinfo->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);            
 
+       // XXXdbg
+       hfs_global_shared_lock_acquire(hfsmp);
+       if (hfsmp->jnl) {
+           if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
+                       hfs_global_shared_lock_release(hfsmp);
+                       return (0);
+           }
+       }
+
        error = cat_create(hfsmp, &hfsmp->hfs_privdir_desc,
                        &hfsmp->hfs_privdir_attr, &out_desc);
 
        /* Unlock catalog b-tree */
        (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
-       if (error)
-               return (0);
+       if (error) {
+           if (hfsmp->jnl) {
+                       journal_end_transaction(hfsmp->jnl);
+           }
+               hfs_global_shared_lock_release(hfsmp);
+
+           return (0);
+       }
 
        hfsmp->hfs_privdir_desc.cd_hint = out_desc.cd_hint;
        hfsmp->hfs_privdir_desc.cd_cnid = out_desc.cd_cnid;
@@ -783,11 +849,209 @@ FindMetaDataDirectory(ExtendedVCB *vcb)
                vput(dvp);
        }
        hfs_volupdate(hfsmp, VOL_MKDIR, 1);
+       if (hfsmp->jnl) {
+           journal_end_transaction(hfsmp->jnl);
+       } 
+       hfs_global_shared_lock_release(hfsmp);
+
        cat_releasedesc(&out_desc);
 
        return (out_desc.cd_cnid);
 }
 
+__private_extern__
+u_long
+GetFileInfo(ExtendedVCB *vcb, u_int32_t dirid, char *name,
+                       struct cat_attr *fattr, struct cat_fork *forkinfo)
+{
+       struct hfsmount * hfsmp;
+       struct vnode * dvp = NULL;
+       struct cnode * dcp = NULL;
+       struct FndrDirInfo * fndrinfo;
+       struct cat_desc jdesc;
+       struct timeval tv;
+       int error;
+       
+       if (vcb->vcbSigWord != kHFSPlusSigWord)
+               return (0);
+
+       hfsmp = VCBTOHFS(vcb);
+
+       memset(&jdesc, 0, sizeof(struct cat_desc));
+       jdesc.cd_parentcnid = kRootDirID;
+       jdesc.cd_nameptr = name;
+       jdesc.cd_namelen = strlen(name);
+
+       /* Lock catalog b-tree */
+       error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());    
+       if (error)
+               return (0);
+
+       error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo);
+
+       (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
+
+       if (error == 0) {
+               return (fattr->ca_fileid);
+       } else if (hfsmp->hfs_fs_ronly) {
+               return (0);
+       }
+}
+
+
+/*
+ * On Journaled HFS, there can be orphaned files.  These
+ * are files that were unlinked while busy. If the volume
+ * was not cleanly unmounted then some of these files may
+ * have persisted and need to be removed.
+ */
+__private_extern__
+void
+hfs_remove_orphans(struct hfsmount * hfsmp)
+{
+       struct BTreeIterator * iterator = NULL;
+       struct FSBufferDescriptor btdata;
+       struct HFSPlusCatalogFile filerec;
+       struct HFSPlusCatalogKey * keyp;
+       FCB *fcb;
+       ExtendedVCB *vcb;
+       char filename[32];
+       char tempname[32];
+       size_t namelen;
+       int catlock = 0;
+       int result, started_tr = 0;
+       
+       if (hfsmp->hfs_orphans_cleaned)
+               return;
+
+       vcb = HFSTOVCB(hfsmp);
+       fcb = VTOF(vcb->catalogRefNum);
+
+       btdata.bufferAddress = &filerec;
+       btdata.itemSize = sizeof(filerec);
+       btdata.itemCount = 1;
+
+       MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
+       keyp = (HFSPlusCatalogKey*)&iterator->key;
+       keyp->parentID = hfsmp->hfs_private_metadata_dir;
+
+       // XXXdbg
+       hfs_global_shared_lock_acquire(hfsmp);
+       if (hfsmp->jnl) {
+           if (journal_start_transaction(hfsmp->jnl) != 0) {
+                       hfs_global_shared_lock_release(hfsmp);
+                       return;
+           }
+               started_tr = 1;
+       }
+
+       /* Lock catalog b-tree */
+       result = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, current_proc());   
+       if (result)
+               goto exit;
+       catlock = 1;
+
+       /*
+        * Position the iterator at the folder thread record.
+        * (i.e. one record before first child)
+        */
+       result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
+       if (result)
+               goto exit;
+
+       /* Visit all the children in the HFS+ private directory. */
+       for (;;) {
+               result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
+               if (result)
+                       break;
+               if (keyp->parentID != hfsmp->hfs_private_metadata_dir)
+                       break;
+               if (filerec.recordType != kHFSPlusFileRecord)
+                       continue;
+               
+               (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
+                                     filename, &namelen, sizeof(filename), 0, 0);
+               
+               (void) sprintf(tempname, "%s%d", HFS_DELETE_PREFIX, filerec.fileID);
+               
+               /*
+                * Delete all files named "tempxxx", where
+                * xxx is the file's cnid in decimal.
+                *
+                * Delete all files named "iNodexxx", that
+                * have a link count of zero.
+                */
+               if (bcmp(tempname, filename, namelen) == 0) {
+                       struct filefork fork = {0};
+                       struct cnode cnode = {0};
+
+                       // XXXdebug
+                       //printf("hfs_remove_orphans: removing %s\n", filename);
+
+                       /* Build a fake cnode */
+                       cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_metadata_dir;
+                       cnode.c_desc.cd_nameptr = filename;
+                       cnode.c_desc.cd_namelen = namelen;
+                       cnode.c_desc.cd_cnid = filerec.fileID;
+                       cnode.c_attr.ca_fileid = filerec.fileID;
+                       cnode.c_blocks = filerec.dataFork.totalBlocks +
+                                        filerec.resourceFork.totalBlocks;
+
+                       /* Position iterator at previous entry */
+                       if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
+                           NULL, NULL) != 0)
+                               break;
+                       
+                       /* Truncate the file to zero (both forks) */
+                       if (filerec.dataFork.totalBlocks > 0) {
+                               fork.ff_cp = &cnode;
+                               cnode.c_datafork = ⋔
+                               bcopy(&filerec.dataFork, &fork.ff_data, sizeof(struct cat_fork));
+                               if (TruncateFileC(vcb, (FCB*)&fork, 0, false) != 0) {
+                                       printf("error truncting data fork!\n");
+                                       break;
+                               }
+                       }
+                       if (filerec.resourceFork.totalBlocks > 0) {
+                               fork.ff_cp = &cnode;
+                               cnode.c_datafork = NULL;
+                               cnode.c_rsrcfork = ⋔
+                               bcopy(&filerec.resourceFork, &fork.ff_data, sizeof(struct cat_fork));
+                               if (TruncateFileC(vcb, (FCB*)&fork, 0, false) != 0) {
+                                       printf("error truncting rsrc fork!\n");
+                                       break;
+                               }
+                       }
+
+                       /* Remove the file record from the Catalog */   
+                       if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
+                               printf("error deleting cat rec!\n");
+                               break;
+                       }
+                       
+                       /* Update parent and volume counts */   
+                       hfsmp->hfs_privdir_attr.ca_entries--;
+                       (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
+                                        &hfsmp->hfs_privdir_attr, NULL, NULL);
+                       hfs_volupdate(hfsmp, VOL_RMFILE, 0);
+               }
+       }
+       
+exit:
+       /* Unlock catalog b-tree */
+       if (catlock)
+               (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, current_proc());
+
+       if (started_tr) {
+               journal_end_transaction(hfsmp->jnl);
+       }
+       hfs_global_shared_lock_release(hfsmp);
+
+       FREE(iterator, M_TEMP);
+       hfsmp->hfs_orphans_cleaned = 1;
+}
+
 
 /*
  * This will return the correct logical block size for a given vnode.
@@ -860,12 +1124,14 @@ short MacToVFSError(OSErr err)
 
        switch (err) {
        case dskFulErr:                 /*    -34 */
-       case btNoSpaceAvail:            /* -32733 */
+               return ENOSPC;
+       case btNoSpaceAvail:    /* -32733 */
+               return EFBIG;
        case fxOvFlErr:                 /* -32750 */
-               return ENOSPC;          /*    +28 */
+               return EOVERFLOW;
        
        case btBadNode:                 /* -32731 */
-               return EIO;             /*    +5 */
+               return EBADF;
        
        case memFullErr:                /*  -108 */
                return ENOMEM;          /*   +12 */
@@ -885,7 +1151,7 @@ short MacToVFSError(OSErr err)
                return EISDIR;          /*     21 */
        
        case fxRangeErr:                /* -32751 */
-               return EIO;             /*      5 */
+               return ERANGE;
        
        case bdNamErr:                  /*   -37 */
                return ENAMETOOLONG;    /*    63 */
@@ -995,4 +1261,299 @@ hfs_relnamehints(struct cnode *dcp)
 }
 
 
+__private_extern__
+int
+hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
+                                          void *_args, int embeddedOffset, int mdb_offset,
+                                          HFSMasterDirectoryBlock *mdbp, struct ucred *cred)
+{
+       JournalInfoBlock *jibp;
+       struct buf       *jinfo_bp, *bp;
+       int               sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+       int               retval, blksize = hfsmp->hfs_phys_block_size;
+       struct vnode     *devvp;
+       struct hfs_mount_args *args = _args;
+
+       devvp = hfsmp->hfs_devvp;
+
+       if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+               arg_flags  = args->journal_flags;
+               arg_tbufsz = args->journal_tbuffer_size;
+       }
+
+       sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
+                               
+       retval = meta_bread(devvp,
+                                               embeddedOffset/blksize + 
+                                               (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
+                                               SWAP_BE32(vhp->blockSize), cred, &jinfo_bp);
+       if (retval)
+               return retval;
+
+       jibp = (JournalInfoBlock *)jinfo_bp->b_data;
+       jibp->flags  = SWAP_BE32(jibp->flags);
+       jibp->offset = SWAP_BE64(jibp->offset);
+       jibp->size   = SWAP_BE64(jibp->size);
+
+       if (jibp->flags & kJIJournalInFSMask) {
+               hfsmp->jvp = hfsmp->hfs_devvp;
+       } else {
+               printf("hfs: journal not stored in fs! don't know what to do.\n");
+               brelse(jinfo_bp);
+               return EINVAL;
+       }
+
+       // save this off for the hack-y check in hfs_remove()
+       hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
+
+       if (jibp->flags & kJIJournalNeedInitMask) {
+               printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+                          jibp->offset + (off_t)embeddedOffset, jibp->size);
+               hfsmp->jnl = journal_create(hfsmp->jvp,
+                                                                       jibp->offset + (off_t)embeddedOffset,
+                                                                       jibp->size,
+                                                                       devvp,
+                                                                       blksize,
+                                                                       arg_flags,
+                                                                       arg_tbufsz,
+                                                                       hfs_sync_metadata, hfsmp->hfs_mp);
+
+               // no need to start a transaction here... if this were to fail
+               // we'd just re-init it on the next mount.
+               jibp->flags &= ~kJIJournalNeedInitMask;
+               jibp->flags  = SWAP_BE32(jibp->flags);
+               bwrite(jinfo_bp);
+               jinfo_bp = NULL;
+               jibp     = NULL;
+       } else { 
+               //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+               //         jibp->offset + (off_t)embeddedOffset,
+               //         jibp->size, SWAP_BE32(vhp->blockSize));
+                               
+               hfsmp->jnl = journal_open(hfsmp->jvp,
+                                                                 jibp->offset + (off_t)embeddedOffset,
+                                                                 jibp->size,
+                                                                 devvp,
+                                                                 blksize,
+                                                                 arg_flags,
+                                                                 arg_tbufsz,
+                                                                 hfs_sync_metadata, hfsmp->hfs_mp);
+
+               brelse(jinfo_bp);
+               jinfo_bp = NULL;
+               jibp     = NULL;
+
+               if (hfsmp->jnl && mdbp) {
+                       // reload the mdb because it could have changed
+                       // if the journal had to be replayed.
+                       retval = meta_bread(devvp, mdb_offset, blksize, cred, &bp);
+                       if (retval) {
+                               brelse(bp);
+                               printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
+                                          retval);
+                               return retval;
+                       }
+                       bcopy(bp->b_data + HFS_PRI_OFFSET(blksize), mdbp, 512);
+                       brelse(bp);
+                       bp = NULL;
+               }
+       }
+
+
+       //printf("journal @ 0x%x\n", hfsmp->jnl);
+       
+       // if we expected the journal to be there and we couldn't
+       // create it or open it then we have to bail out.
+       if (hfsmp->jnl == NULL) {
+               hfsmp->jnl_start = 0;
+               
+               printf("hfs: failed to open/create the journal (retval %d).\n", retval);
+               return EINVAL;
+       }
 
+       return 0;
+}
+
+
+//
+// This function will go and re-locate the .journal_info_block and
+// the .journal files in case they moved (which can happen if you
+// run Norton SpeedDisk).  If we fail to find either file we just
+// disable journaling for this volume and return.  We turn off the
+// journaling bit in the vcb and assume it will get written to disk
+// later (if it doesn't on the next mount we'd do the same thing
+// again which is harmless).  If we disable journaling we don't
+// return an error so that the volume is still mountable.
+//
+// If the info we find for the .journal_info_block and .journal files
+// isn't what we had stored, we re-set our cached info and proceed
+// with opening the journal normally.
+//
+static int
+hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
+{
+       JournalInfoBlock *jibp;
+       struct buf       *jinfo_bp, *bp;
+       int               sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+       int               retval, need_flush = 0, write_jibp = 0;
+       struct vnode     *devvp;
+       struct cat_attr   jib_attr, jattr;
+       struct cat_fork   jib_fork, jfork;
+       ExtendedVCB      *vcb;
+       u_long            fid;
+       struct hfs_mount_args *args = _args;
+       
+       devvp = hfsmp->hfs_devvp;
+       vcb = HFSTOVCB(hfsmp);
+       
+       if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+               if (args->journal_disable) {
+                       return 0;
+               }
+
+               arg_flags  = args->journal_flags;
+               arg_tbufsz = args->journal_tbuffer_size;
+       }
+
+       fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
+       if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
+               printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
+                          jib_fork.cf_extents[0].startBlock);
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
+       }
+       hfsmp->hfs_jnlinfoblkid = fid;
+
+       // make sure the journal_info_block begins where we think it should.
+       if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
+               printf("hfs: The journal_info_block moved (was: %d; is: %d).  Fixing up\n",
+                          SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
+
+               vcb->vcbJinfoBlock    = jib_fork.cf_extents[0].startBlock;
+               vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
+       }
+
+
+       sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_phys_block_size;
+       retval = meta_bread(devvp,
+                                               vcb->hfsPlusIOPosOffset / hfsmp->hfs_phys_block_size + 
+                                               (SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock),
+                                               SWAP_BE32(vhp->blockSize), NOCRED, &jinfo_bp);
+       if (retval) {
+               printf("hfs: can't read journal info block. disabling journaling.\n");
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
+       }
+
+       jibp = (JournalInfoBlock *)jinfo_bp->b_data;
+       jibp->flags  = SWAP_BE32(jibp->flags);
+       jibp->offset = SWAP_BE64(jibp->offset);
+       jibp->size   = SWAP_BE64(jibp->size);
+
+       fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
+       if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
+               printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
+                          jfork.cf_extents[0].startBlock);
+               brelse(jinfo_bp);
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
+       }
+       hfsmp->hfs_jnlfileid = fid;
+
+       // make sure the journal file begins where we think it should.
+       if ((jibp->offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
+               printf("hfs: The journal file moved (was: %lld; is: %d).  Fixing up\n",
+                          (jibp->offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
+
+               jibp->offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
+               write_jibp   = 1;
+       }
+
+       // check the size of the journal file.
+       if (jibp->size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
+               printf("hfs: The journal file changed size! (was %lld; is %lld).  Fixing up.\n",
+                          jibp->size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
+               
+               jibp->size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
+               write_jibp = 1;
+       }
+       
+       if (jibp->flags & kJIJournalInFSMask) {
+               hfsmp->jvp = hfsmp->hfs_devvp;
+       } else {
+               printf("hfs: journal not stored in fs! don't know what to do.\n");
+               brelse(jinfo_bp);
+               return EINVAL;
+       }
+
+       // save this off for the hack-y check in hfs_remove()
+       hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
+
+       if (jibp->flags & kJIJournalNeedInitMask) {
+               printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+                          jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size);
+               hfsmp->jnl = journal_create(hfsmp->jvp,
+                                                                       jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
+                                                                       jibp->size,
+                                                                       devvp,
+                                                                       hfsmp->hfs_phys_block_size,
+                                                                       arg_flags,
+                                                                       arg_tbufsz,
+                                                                       hfs_sync_metadata, hfsmp->hfs_mp);
+
+               // no need to start a transaction here... if this were to fail
+               // we'd just re-init it on the next mount.
+               jibp->flags &= ~kJIJournalNeedInitMask;
+               write_jibp   = 1;
+
+       } else { 
+               //
+               // if we weren't the last person to mount this volume
+               // then we need to throw away the journal because it
+               // is likely that someone else mucked with the disk.
+               // if the journal is empty this is no big deal.  if the
+               // disk is dirty this prevents us from replaying the
+               // journal over top of changes that someone else made.
+               //
+               arg_flags |= JOURNAL_RESET;
+               
+               //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+               //         jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
+               //         jibp->size, SWAP_BE32(vhp->blockSize));
+                               
+               hfsmp->jnl = journal_open(hfsmp->jvp,
+                                                                 jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
+                                                                 jibp->size,
+                                                                 devvp,
+                                                                 hfsmp->hfs_phys_block_size,
+                                                                 arg_flags,
+                                                                 arg_tbufsz,
+                                                                 hfs_sync_metadata, hfsmp->hfs_mp);
+       }
+                       
+
+       if (write_jibp) {
+               jibp->flags  = SWAP_BE32(jibp->flags);
+               jibp->offset = SWAP_BE64(jibp->offset);
+               jibp->size   = SWAP_BE64(jibp->size);
+
+               bwrite(jinfo_bp);
+       } else {
+               brelse(jinfo_bp);
+       } 
+       jinfo_bp = NULL;
+       jibp     = NULL;
+
+       //printf("journal @ 0x%x\n", hfsmp->jnl);
+       
+       // if we expected the journal to be there and we couldn't
+       // create it or open it then we have to bail out.
+       if (hfsmp->jnl == NULL) {
+               hfsmp->jnl_start = 0;
+               
+               printf("hfs: failed to open/create the journal (retval %d).\n", retval);
+               return EINVAL;
+       }
+
+       return 0;
+}