]> git.saurik.com Git - apple/hfs.git/blobdiff - hfs_util/hfsutil_jnl.c
hfs-165.tar.gz
[apple/hfs.git] / hfs_util / hfsutil_jnl.c
index c4b6b6edd0234ca26b821c8ed90222003d7dc083..f83536dfdbc2c4ec7917caaf5bd7a0cb5e642108 100644 (file)
@@ -4,7 +4,7 @@
  * @APPLE_LICENSE_HEADER_START@
  * 
  * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
+ * are subject to the Apple Public Source License Version 1.2 (the
  * "License").  You may not use this file except in compliance with the
  * License.  Please obtain a copy of the License at
  * http://www.apple.com/publicsource and read it before using this file.
 #include <sys/vmmeter.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
+#include <sys/ioctl.h>
 
-#include <dev/disk.h>
+#include <sys/disk.h>
 #include <sys/loadable_fs.h>
 #include <hfs/hfs_format.h>
+#include <hfs/hfs_mount.h>    /* for hfs sysctl values */
 
 #include <errno.h>
 #include <fcntl.h>
 
 #include <architecture/byte_order.h>
 
-//
-// Secret HFS sysctl's to instruct it to turn
-// journaling on/off for a volume.
-//
-#define HFS_BECOME_JOURNALED   0x082969
-#define HFS_BECOME_UNJOURNALED 0x031272
-
+// just in case these aren't in <hfs/hfs_mount.h> yet
+#ifndef HFS_ENABLE_JOURNALING
+#define HFS_ENABLE_JOURNALING   0x082969
+#endif
+#ifndef HFS_DISABLE_JOURNALING
+#define HFS_DISABLE_JOURNALING 0x031272
+#endif
+#ifndef HFS_GET_JOURNAL_INFO
+#define HFS_GET_JOURNAL_INFO    0x6a6e6c69
+#endif
 
 /* getattrlist buffers start with an extra length field */
 struct ExtentsAttrBuf {
@@ -101,12 +106,12 @@ int hide_file(const char * file)
 
     result = getattrlist(file, &alist, &finderInfoBuf, sizeof(finderInfoBuf), 0);
     if (result) {
-               return (errno);
-       }
+       return (errno);
+    }
        
     if (finderInfoBuf.finderInfo.fdFlags & kIsInvisible) {
-               printf("hide: %s is alreadly invisible\n", file);
-               return (0);
+       printf("hide: %s is alreadly invisible\n", file);
+       return (0);
     }
 
     finderInfoBuf.finderInfo.fdFlags |= kIsInvisible;
@@ -116,28 +121,209 @@ int hide_file(const char * file)
     return (result == -1 ? errno : result);
 }
 
-int
-get_start_block(const char *file)
+off_t
+get_start_block(const char *file, uint32_t fs_block_size)
 {
-    struct attrlist alist = {0};
-    ExtentsAttrBuf extentsbuf = {0};
+    off_t cur_pos, phys_start, len;
+    int fd, err;
+    struct log2phys l2p;
+    struct stat st;
 
-    alist.bitmapcount = ATTR_BIT_MAP_COUNT;
-    alist.fileattr = ATTR_FILE_DATAEXTENTS;
+    fd = open(file, O_RDONLY);
+    if (fd < 0) {
+       return -1;
+    }
+
+    if (fstat(fd, &st) < 0) {
+       fprintf(stderr, "can't stat %s (%s)\n", file, strerror(errno));
+       close(fd);
+       return -1;
+    }
+
+    fs_block_size = st.st_blksize; // XXXdbg quick hack for now
+
+    phys_start = len = 0;
+    for(cur_pos=0; cur_pos < st.st_size; cur_pos += fs_block_size) {
+       memset(&l2p, 0, sizeof(l2p));
+       lseek(fd, cur_pos, SEEK_SET);
+       err = fcntl(fd, F_LOG2PHYS, &l2p);
+
+       if (phys_start == 0) {
+           phys_start = l2p.l2p_devoffset;
+           len = fs_block_size;
+       } else if (l2p.l2p_devoffset != (phys_start + len)) {
+           // printf("    %lld : %lld - %lld\n", cur_pos, phys_start / fs_block_size, len / fs_block_size);
+           fprintf(stderr, "%s : is not contiguous!\n", file);
+           close(fd);
+           return -1;
+           // phys_start = l2p.l2p_devoffset;
+           // len = fs_block_size;
+       } else {
+           len += fs_block_size;
+       }
+    }
+
+    close(fd);
+
+    //printf("%s start offset %lld; byte len %lld (blksize %d)\n",
+    // file, phys_start, len, fs_block_size);
+
+    if ((phys_start / (unsigned)fs_block_size) & 0xffffffff00000000LL) {
+       fprintf(stderr, "%s : starting block is > 32bits!\n", file);
+       return -1;
+    }
+       
+    return phys_start;
+}
+
+
+//
+// Get the embedded offset (if any) for an hfs+ volume.
+// This is pretty skanky that we have to do this but
+// that's life...
+//
+#include <sys/disk.h>
+#include <hfs/hfs_format.h>
+
+#include <machine/endian.h>
+
+#define HFS_PRI_SECTOR(blksize)          (1024 / (blksize))
+#define HFS_PRI_OFFSET(blksize)          ((blksize) > 1024 ? 1024 : 0)
+
+#define SWAP_BE16(x) ntohs(x)
+#define SWAP_BE32(x) ntohl(x)
+
+
+off_t
+get_embedded_offset(char *devname)
+{
+    int fd = -1;
+    off_t ret = 0;
+    char *buff = NULL, rawdev[256];
+    u_int64_t blkcnt;
+    u_int32_t blksize;
+    HFSMasterDirectoryBlock *mdbp;
+    off_t embeddedOffset;
+    struct statfs sfs;
+    struct stat   st;
+       
+  restart:
+    if (stat(devname, &st) != 0) {
+       fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    if (S_ISCHR(st.st_mode) == 0) {
+       // hmmm, it's not the character special raw device so we
+       // should try to figure out the real device.
+       if (statfs(devname, &sfs) != 0) {
+           fprintf(stderr, "Can't find out any info about the fs for path %s (%s)\n",
+               devname, strerror(errno));
+           ret = -1;
+           goto out;
+       }
+
+       // copy the "/dev/"
+       strncpy(rawdev, sfs.f_mntfromname, 5);
+       rawdev[5] = 'r';
+       strcpy(&rawdev[6], &sfs.f_mntfromname[5]);
+       devname = &rawdev[0];
+       goto restart;
+    }
+
+    fd = open(devname, O_RDONLY);
+    if (fd < 0) {
+       fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    /* Get the real physical block size. */
+    if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
+       fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno));
+       blksize = 512;
+       ret = -1;
+       goto out;
+    }
+
+    /* Get the number of physical blocks. */
+    if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
+       struct stat st;
+       fprintf(stderr, "failed to get block count. trying stat().\n");
+       if (fstat(fd, &st) != 0) {
+           ret = -1;
+           goto out;
+       }
+
+       blkcnt = st.st_size / blksize;
+    }
+
+    /*
+     * At this point:
+     *   blksize has our prefered physical block size
+     *   blkcnt has the total number of physical blocks
+     */
+
+    buff = (char *)malloc(blksize);
+       
+    if (pread(fd, buff, blksize, HFS_PRI_SECTOR(blksize)*blksize) != blksize) {
+       fprintf(stderr, "failed to read volume header @ offset %d (%s)\n",
+           HFS_PRI_SECTOR(blksize), strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    mdbp = (HFSMasterDirectoryBlock *)buff;
+    if (   (SWAP_BE16(mdbp->drSigWord) != kHFSSigWord) 
+        && (SWAP_BE16(mdbp->drSigWord) != kHFSPlusSigWord)
+        && (SWAP_BE16(mdbp->drSigWord) != kHFSXSigWord)) {
+       ret = -1;
+       goto out;
+    }
 
-    if (getattrlist(file, &alist, &extentsbuf, sizeof(extentsbuf), 0)) {
-               fprintf(stderr, "could not get attrlist for %s (%s)", file, strerror(errno));
-               return -1;
+    if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
+       ret = -1;
+       goto out;
+    } else if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
+       /* Get the embedded Volume Header */
+       embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
+       embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) *
+                          (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
+
+       /*
+        * If the embedded volume doesn't start on a block
+        * boundary, then switch the device to a 512-byte
+        * block size so everything will line up on a block
+        * boundary.
+        */
+       if ((embeddedOffset % blksize) != 0) {
+           fprintf(stderr, "HFS Mount: embedded volume offset not"
+               " a multiple of physical block size (%d);"
+               " switching to 512\n", blksize);
+               
+           blkcnt  *= (blksize / 512);
+           blksize  = 512;
+       }
+
+    } else { /* pure HFS+ */ 
+       embeddedOffset = 0;
     }
 
-    if (extentsbuf.extents[1].startBlock != 0) {
-               fprintf(stderr, "Journal File not contiguous!\n");
-               return -1;
+    ret = embeddedOffset;
+
+  out:
+    if (buff) {
+       free(buff);
     }
+    if (fd >= 0) 
+       close(fd);
 
-    return extentsbuf.extents[0].startBlock;
+    return ret;
 }
 
+
+
 static const char *journal_fname = ".journal";
 static const char *jib_fname = ".journal_info_block";
 
@@ -148,66 +334,90 @@ DoMakeJournaled(char *volname, int jsize)
     char            *buf;
     int              ret;
     fstore_t         fst;
-    int              jstart_block, jinfo_block, sysctl_info[8];
+    int32_t          jstart_block, jinfo_block;
+    int              sysctl_info[8];
     JournalInfoBlock jib;
-       struct statfs    sfs;
-       static char      tmpname[MAXPATHLEN];
+    struct statfs    sfs;
+    static char      tmpname[MAXPATHLEN];
+    off_t            start_block, embedded_offset;
 
-       if (jsize != 0) {
-               journal_size = jsize;
-       }
+    if (statfs(volname, &sfs) != 0) {
+       fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
+       return 10;
+    }
 
-       if (statfs(volname, &sfs) != 0) {
-               fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
-               return 10;
-       }
+    // Make sure that we're HFS+.  First we check the fstypename.
+    // If that's ok then we try to create a symlink (which won't
+    // work on plain hfs volumes but will work on hfs+ volumes).
+    //
+    sprintf(tmpname, "%s/is_vol_hfs_plus", volname);
+    if (strcmp(sfs.f_fstypename, "hfs") != 0 ||
+       ((ret = symlink(tmpname, tmpname)) != 0 && errno == ENOTSUP)) {
+       fprintf(stderr, "%s is not an HFS+ volume.  Journaling only works on HFS+ volumes.\n",
+               volname);
+       return 10;
+    }
+    unlink(tmpname);
 
-       // Make sure that we're HFS+.  First we check the fstypename.
-       // If that's ok then we try to create a symlink (which won't
-       // work on plain hfs volumes but will work on hfs+ volumes).
-       //
-       sprintf(tmpname, "%s/is_vol_hfs_plus", volname);
-       if (strcmp(sfs.f_fstypename, "hfs") != 0 ||
-               ((ret = symlink(tmpname, tmpname)) != 0 && errno == ENOTSUP)) {
-               fprintf(stderr, "%s is not an HFS+ volume.  Journaling only works on HFS+ volumes.\n",
-                               volname);
-               return 10;
-       }
-       unlink(tmpname);
+    if (sfs.f_flags & MNT_JOURNALED) {
+       fprintf(stderr, "Volume %s is already journaled.\n", volname);
+       return 1;
+    }
 
-       if (sfs.f_flags & MNT_JOURNALED) {
-               fprintf(stderr, "Volume %s is already journaled.\n", volname);
-               return 1;
+    if (jsize != 0) {
+       journal_size = jsize;
+    } else {
+       int scale;
+
+       //
+       // we want at least 8 megs of journal for each 100 gigs of
+       // disk space.  We cap the size at 512 megs though.
+       //
+       scale = ((long long)sfs.f_bsize * (long long)((unsigned)sfs.f_blocks)) / (100*1024*1024*1024ULL);
+       journal_size *= (scale + 1);
+       if (journal_size > 512 * 1024 * 1024) {
+           journal_size = 512 * 1024 * 1024;
        }
+    }
 
     if (chdir(volname) != 0) {
-               fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
-                               volname, strerror(errno));
-               return 10;
-       }
+       fprintf(stderr, "Can't locate volume %s to make it journaled (%s).\n",
+               volname, strerror(errno));
+       return 10;
+    }
+
+
+    embedded_offset = get_embedded_offset(volname);
+    if (embedded_offset < 0) {
+       fprintf(stderr, "Can't calculate the embedded offset (if any) for %s.\n", volname);
+       fprintf(stderr, "Journal creation failure.\n");
+       return 15;
+    }
+    // printf("Embedded offset == 0x%llx\n", embedded_offset);
 
     fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
     if (fd < 0) {
-               fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
-                               volname, strerror(errno));
-               return 5;
+       fprintf(stderr, "Can't create journal file on volume %s (%s)\n",
+               volname, strerror(errno));
+       return 5;
     }
 
-       // make sure that it has no r/w/x privs (only could happen if
-       // the file already existed since open() doesn't reset the mode
-       // bits).
-       //
-       fchmod(fd, 0);
+    // make sure that it has no r/w/x privs (only could happen if
+    // the file already existed since open() doesn't reset the mode
+    // bits).
+    //
+    fchmod(fd, 0);
 
     block_size = sfs.f_bsize;
     if ((journal_size % block_size) != 0) {
-               fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
-                               journal_size/1024, volname, block_size);
-               close(fd);
-               unlink(journal_fname);
-               return 5;
+       fprintf(stderr, "Journal size %dk is not a multiple of volume %s block size (%d).\n",
+               journal_size/1024, volname, block_size);
+       close(fd);
+       unlink(journal_fname);
+       return 5;
     }
 
+  retry:
     memset(&fst, 0, sizeof(fst));
     fst.fst_flags   = F_ALLOCATECONTIG|F_ALLOCATEALL;
     fst.fst_length  = journal_size;
@@ -215,65 +425,98 @@ DoMakeJournaled(char *volname, int jsize)
     
     ret = fcntl(fd, F_PREALLOCATE, &fst);
     if (ret < 0) {
-               fprintf(stderr, "Pre-allocating the journal file failed on volume %s (%s)\n",
-                               volname, strerror(errno));
-               fprintf(stderr, "Try using a smaller (%d k) journal size\n", journal_size/2/1024);
-               close(fd);
-               unlink(journal_fname);
-               return 10;
+       if (journal_size >= 2*1024*1024) {
+           fprintf(stderr, "Not enough contiguous space for a %d k journal.  Retrying.\n",
+                   journal_size/1024);
+           journal_size /= 2;
+           ftruncate(fd, 0);     // make sure the file is zero bytes long.
+           goto retry;
+       } else {
+           fprintf(stderr, "Disk too fragmented to enable journaling.\n");
+           fprintf(stderr, "Please run a defragmenter on %s.\n", volname);
+           close(fd);
+           unlink(journal_fname);
+           return 10;
+       }
     }
 
     printf("Allocated %lldK for journal file.\n", fst.fst_bytesalloc/1024LL);
     buf = (char *)calloc(block_size, 1);
     if (buf) {
-               for(i=0; i < journal_size/block_size; i++) {
-                       ret = write(fd, buf, block_size);
-                       if (ret != block_size) {
-                               break;
-                       }
-               }
+       for(i=0; i < journal_size/block_size; i++) {
+           ret = write(fd, buf, block_size);
+           if (ret != block_size) {
+               break;
+           }
+       }
                
-               if (i*block_size != journal_size) {
-                       fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n",
-                                       journal_size/1024, volname, strerror(errno));
-               }
+       if (i*block_size != journal_size) {
+           fprintf(stderr, "Failed to write %dk to journal on volume %s (%s)\n",
+                   journal_size/1024, volname, strerror(errno));
+       }
     } else {
-               printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
-                          volname, strerror(errno));
+       printf("Could not allocate memory to write to the journal on volume %s (%s)\n",
+              volname, strerror(errno));
     }
 
     fsync(fd);
     close(fd);
     hide_file(journal_fname);
 
-    jstart_block = get_start_block(journal_fname);
+    start_block = get_start_block(journal_fname, block_size);
+    if (start_block == (off_t)-1) {
+       fprintf(stderr, "Failed to get start block for %s (%s)\n",
+               journal_fname, strerror(errno));
+       unlink(journal_fname);
+       return 20;
+    }
+    jstart_block = (start_block / block_size) - (embedded_offset / block_size);
 
-    memset(&jib, 0, sizeof(jib));
+    memset(&jib, 'Z', sizeof(jib));
     jib.flags  = kJIJournalInFSMask;
-    jib.offset = (off_t)jstart_block * (off_t)block_size;
-    jib.size   = (off_t)journal_size;
+    jib.offset = (off_t)((unsigned)jstart_block) * (off_t)((unsigned)block_size);
+    jib.size   = (off_t)((unsigned)journal_size);
 
     fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000);
     if (fd < 0) {
-               fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
-                               volname, strerror(errno));
-               unlink(journal_fname);
-               return 5;
+       fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n",
+               volname, strerror(errno));
+       unlink(journal_fname);
+       return 5;
     }
     
+    // swap the data before we copy it
+    jib.flags  = OSSwapBigToHostInt32(jib.flags);
+    jib.offset = OSSwapBigToHostInt64(jib.offset);
+    jib.size   = OSSwapBigToHostInt64(jib.size);
+    
     memcpy(buf, &jib, sizeof(jib));
+
+    // now put it back the way it was
+    jib.size   = OSSwapBigToHostInt64(jib.size);
+    jib.offset = OSSwapBigToHostInt64(jib.offset);
+    jib.flags  = OSSwapBigToHostInt32(jib.flags);
+
     if (write(fd, buf, block_size) != block_size) {
-               fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n",
-                               volname, strerror(errno));
-               unlink(journal_fname);
-               return 10;
+       fprintf(stderr, "Failed to write journal info block on volume %s (%s)!\n",
+               volname, strerror(errno));
+       unlink(journal_fname);
+       return 10;
     }
 
     fsync(fd);
     close(fd);
     hide_file(jib_fname);
 
-    jinfo_block = get_start_block(jib_fname);
+    start_block = get_start_block(jib_fname, block_size);
+    if (start_block == (off_t)-1) {
+       fprintf(stderr, "Failed to get start block for %s (%s)\n",
+               jib_fname, strerror(errno));
+       unlink(journal_fname);
+       unlink(jib_fname);
+       return 20;
+    }
+    jinfo_block = (start_block / block_size) - (embedded_offset / block_size);
 
 
     //
@@ -282,7 +525,7 @@ DoMakeJournaled(char *volname, int jsize)
     memset(sysctl_info, 0, sizeof(sysctl_info));
     sysctl_info[0] = CTL_VFS;
     sysctl_info[1] = sfs.f_fsid.val[1];
-    sysctl_info[2] = HFS_BECOME_JOURNALED;
+    sysctl_info[2] = HFS_ENABLE_JOURNALING;
     sysctl_info[3] = jinfo_block;
     sysctl_info[4] = jstart_block;
     sysctl_info[5] = journal_size;
@@ -294,66 +537,299 @@ DoMakeJournaled(char *volname, int jsize)
 
     ret = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
     if (ret != 0) {
-               fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
-                               volname, strerror(errno));
-               unlink(journal_fname);
-               unlink(jib_fname);
-               return 20;
+       fprintf(stderr, "Failed to make volume %s journaled (%s)\n",
+               volname, strerror(errno));
+       unlink(journal_fname);
+       unlink(jib_fname);
+       return 20;
     }
-
-       return 0;
+    
+    return 0;
 }
 
 
 int
 DoUnJournal(char *volname)
 {
-       int           result;
+    int           result;
     int           sysctl_info[8];
-       struct statfs sfs;
-       char          jbuf[MAXPATHLEN];
+    struct statfs sfs;
+    char          jbuf[MAXPATHLEN];
        
-       if (statfs(volname, &sfs) != 0) {
-               fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
-               return 10;
-       }
+    if (statfs(volname, &sfs) != 0) {
+       fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
+       return 10;
+    }
+
+    if ((sfs.f_flags & MNT_JOURNALED) == 0) {
+       fprintf(stderr, "Volume %s is not journaled.\n", volname);
+       return 1;
+    }
+
+    if (chdir(volname) != 0) {
+       fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
+               volname, strerror(errno));
+       return 10;
+    }
+       
+    memset(sysctl_info, 0, sizeof(sysctl_info));
+    sysctl_info[0] = CTL_VFS;
+    sysctl_info[1] = sfs.f_fsid.val[1];
+    sysctl_info[2] = HFS_DISABLE_JOURNALING;
+       
+    result = sysctl((void *)sysctl_info, 3, NULL, NULL, NULL, 0);
+    if (result != 0) {
+       fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
+               volname, strerror(errno));
+       return 20;
+    }
+
+    sprintf(jbuf, "%s/%s", volname, journal_fname);
+    if (unlink(jbuf) != 0) {
+       fprintf(stderr, "Failed to remove the journal %s (%s)\n",
+               jbuf, strerror(errno));
+    }
+
+    sprintf(jbuf, "%s/%s", volname, jib_fname);
+    if (unlink(jbuf) != 0) {
+       fprintf(stderr, "Failed to remove the journal info block %s (%s)\n",
+               jbuf, strerror(errno));
+    }
+
+    printf("Journaling disabled on %s\n", volname);
+           
+    return 0;
+}
+
+
+int
+DoGetJournalInfo(char *volname)
+{
+    int           result;
+    int           sysctl_info[8];
+    struct statfs sfs;
+    off_t         jstart, jsize;
+       
+    if (statfs(volname, &sfs) != 0) {
+       fprintf(stderr, "Can't stat volume %s (%s).\n", volname, strerror(errno));
+       return 10;
+    }
+
+    if ((sfs.f_flags & MNT_JOURNALED) == 0) {
+       fprintf(stderr, "Volume %s is not journaled.\n", volname);
+       return 1;
+    }
+
+    if (chdir(volname) != 0) {
+       fprintf(stderr, "Can't cd to volume %s to get journal info (%s).\n",
+               volname, strerror(errno));
+       return 10;
+    }
+       
+    memset(sysctl_info, 0, sizeof(sysctl_info));
+    sysctl_info[0] = CTL_VFS;
+    sysctl_info[1] = sfs.f_fsid.val[1];
+    sysctl_info[2] = HFS_GET_JOURNAL_INFO;
+    sysctl_info[3] = (int)&jstart;
+    sysctl_info[4] = (int)&jsize;
+       
+    result = sysctl((void *)sysctl_info, 5, NULL, NULL, NULL, 0);
+    if (result != 0) {
+       fprintf(stderr, "Failed to get journal info for volume %s (%s)\n",
+               volname, strerror(errno));
+       return 20;
+    }
+
+    if (jsize == 0) {
+       printf("%s : not journaled.\n", volname);
+    } else {
+       printf("%s : journal size %lld k at offset 0x%llx\n", volname, jsize/1024, jstart);
+    }
+
+    return 0;
+}
+
 
-       if ((sfs.f_flags & MNT_JOURNALED) == 0) {
-               fprintf(stderr, "Volume %s is not journaled.\n", volname);
-               return 1;
+int
+RawDisableJournaling(char *devname)
+{
+    int fd = -1, ret = 0;
+    char *buff = NULL, rawdev[256];
+    u_int64_t disksize;
+    u_int64_t blkcnt;
+    u_int32_t blksize;
+    daddr_t   mdb_offset;
+    HFSMasterDirectoryBlock *mdbp;
+    HFSPlusVolumeHeader *vhp;
+    off_t embeddedOffset, hdr_offset;
+    struct statfs sfs;
+    struct stat   st;
+       
+  restart:
+    if (stat(devname, &st) != 0) {
+       fprintf(stderr, "Could not access %s (%s)\n", devname, strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    if (S_ISCHR(st.st_mode) == 0) {
+       // hmmm, it's not the character special raw device so we
+       // should try to figure out the real device.
+       if (S_ISBLK(st.st_mode)) {
+           strcpy(rawdev, "/dev/r");
+           strcat(rawdev, devname + 5);
+       } else {
+           if (statfs(devname, &sfs) != 0) {
+               fprintf(stderr, "Can't find out any info about the fs for path %s (%s)\n",
+                   devname, strerror(errno));
+               ret = -1;
+               goto out;
+           }
+
+           // copy the "/dev/"
+           strncpy(rawdev, sfs.f_mntfromname, 5);
+           rawdev[5] = 'r';
+           strcpy(&rawdev[6], &sfs.f_mntfromname[5]);
        }
 
-       if (chdir(volname) != 0) {
-               fprintf(stderr, "Can't locate volume %s to turn off journaling (%s).\n",
-                               volname, strerror(errno));
-               return 10;
+       devname = &rawdev[0];
+       goto restart;
+    }
+
+    fd = open(devname, O_RDWR);
+    if (fd < 0) {
+       fprintf(stderr, "can't open: %s (%s)\n", devname, strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    /* Get the real physical block size. */
+    if (ioctl(fd, DKIOCGETBLOCKSIZE, (caddr_t)&blksize) != 0) {
+       fprintf(stderr, "can't get the device block size (%s). assuming 512\n", strerror(errno));
+       blksize = 512;
+       ret = -1;
+       goto out;
+    }
+
+    /* Get the number of physical blocks. */
+    if (ioctl(fd, DKIOCGETBLOCKCOUNT, (caddr_t)&blkcnt)) {
+       struct stat st;
+
+       if (fstat(fd, &st) != 0) {
+           ret = -1;
+           goto out;
        }
+
+       blkcnt = st.st_size / blksize;
+    }
+
+    /* Compute an accurate disk size */
+    disksize = blkcnt * (u_int64_t)blksize;
+
+    /*
+     * There are only 31 bits worth of block count in
+     * the buffer cache.  So for large volumes a 4K
+     * physical block size is needed.
+     */
+    if (blksize == 512 && blkcnt > (u_int64_t)0x000000007fffffff) {
+       blksize = 4096;
+    }
+
+    /*
+     * At this point:
+     *   blksize has our prefered physical block size
+     *   blkcnt has the total number of physical blocks
+     */
+
+    buff  = (char *)malloc(blksize);
        
-       memset(sysctl_info, 0, sizeof(sysctl_info));
-       sysctl_info[0] = CTL_VFS;
-       sysctl_info[1] = sfs.f_fsid.val[1];
-       sysctl_info[2] = HFS_BECOME_UNJOURNALED;
+    hdr_offset = HFS_PRI_SECTOR(blksize)*blksize;
+    if (pread(fd, buff, blksize, hdr_offset) != blksize) {
+       fprintf(stderr, "failed to read volume header @ offset %lld (%s)\n",
+           hdr_offset, strerror(errno));
+       ret = -1;
+       goto out;
+    }
+
+    // if we read in an empty bunch of junk at location zero, then
+    // retry at offset 0x400 which is where the header normally is.
+    if (*(int *)buff == 0 && hdr_offset == 0) {
+       hdr_offset = 0x400;
+       if (pread(fd, buff, blksize, hdr_offset) != blksize) {
+           fprintf(stderr, "failed to read volume header @ offset %lld (%s)\n",
+               hdr_offset, strerror(errno));
+           ret = -1;
+           goto out;
+       }
+    }
+
+
+
+    mdbp = (HFSMasterDirectoryBlock *)buff;
+    if ((SWAP_BE16(mdbp->drSigWord) == kHFSSigWord) && (SWAP_BE16(mdbp->drEmbedSigWord) != kHFSPlusSigWord)) {
+       // normal hfs can not ever be journaled
+       fprintf(stderr, "disable_journaling: volume is only regular HFS, not HFS+\n");
+       goto out;
+    } 
        
-       result = sysctl((void *)sysctl_info, 6, NULL, NULL, NULL, 0);
-       if (result != 0) {
-               fprintf(stderr, "Failed to make volume %s UN-journaled (%s)\n",
-                               volname, strerror(errno));
-               return 20;
+    /* Get the embedded Volume Header */
+    if (SWAP_BE16(mdbp->drEmbedSigWord) == kHFSPlusSigWord) {
+       embeddedOffset = SWAP_BE16(mdbp->drAlBlSt) * 512;
+       embeddedOffset += (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.startBlock) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
+
+       /*
+        * If the embedded volume doesn't start on a block
+        * boundary, then switch the device to a 512-byte
+        * block size so everything will line up on a block
+        * boundary.
+        */
+       if ((embeddedOffset % blksize) != 0) {
+           fprintf(stderr, "HFS Mount: embedded volume offset not"
+               " a multiple of physical block size (%d);"
+               " switching to 512\n", blksize);
+               
+           blkcnt  *= (blksize / 512);
+           blksize  = 512;
        }
 
-       sprintf(jbuf, "%s/%s", volname, journal_fname);
-       if (unlink(jbuf) != 0) {
-               fprintf(stderr, "Failed to remove the journal %s (%s)\n",
-                               jbuf, strerror(errno));
+       disksize = (u_int64_t)SWAP_BE16(mdbp->drEmbedExtent.blockCount) * (u_int64_t)SWAP_BE32(mdbp->drAlBlkSiz);
+
+       mdb_offset = (embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize);
+       hdr_offset = mdb_offset * blksize;
+       if (pread(fd, buff, blksize, hdr_offset) != blksize) {
+           fprintf(stderr, "failed to read the embedded vhp @ offset %d\n", mdb_offset * blksize);
+           ret = -1;
+           goto out;
        }
 
-       sprintf(jbuf, "%s/%s", volname, jib_fname);
-       if (unlink(jbuf) != 0) {
-               fprintf(stderr, "Failed to remove the journal info block %s (%s)\n",
-                               jbuf, strerror(errno));
+       vhp = (HFSPlusVolumeHeader*) buff;
+    } else /* pure HFS+ */ {
+       embeddedOffset = 0;
+       vhp = (HFSPlusVolumeHeader*) mdbp;
+    }
+
+
+    if ((SWAP_BE32(vhp->attributes) & kHFSVolumeJournaledMask) != 0) {
+       unsigned int tmp = SWAP_BE32(vhp->attributes);
+
+       tmp &= ~kHFSVolumeJournaledMask;
+       vhp->attributes = SWAP_BE32(tmp);
+       if ((tmp = pwrite(fd, buff, blksize, hdr_offset)) != blksize) {
+           fprintf(stderr, "Update of super-block on %s failed! (%d != %d, %s)\n",
+               devname, tmp, blksize, strerror(errno));
+       } else {
+           fprintf(stderr, "Turned off the journaling bit for %s\n", devname);
        }
+    } else {
+       fprintf(stderr, "disable_journaling: volume was not journaled.\n");
+    }
 
-       printf("Journaling disabled on %s\n", volname);
-           
-       return 0;
+       
+  out:
+    if (buff)
+       free(buff);
+    if (fd >= 0) 
+       close(fd);
+
+    return ret;
 }