From 4171e1dd9fb5cb6dcfd646c3992863e9df5eca13 Mon Sep 17 00:00:00 2001
From: Brian Carrier <carrier@sleuthkit.org>
Date: Mon, 12 Jan 2009 05:01:47 +0000
Subject: [PATCH] HFS fixes from Rob

---
 CHANGES.txt         |  2 +
 tsk3/fs/hfs.c       | 90 ++++++++++++++++++++++++++++++++-------------
 tsk3/fs/hfs_dent.c  |  2 +-
 tsk3/fs/ifind_lib.c | 26 +++++++++++++
 tsk3/fs/tsk_hfs.h   | 13 ++++---
 5 files changed, 102 insertions(+), 31 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index af19365a5..7bacfc226 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -24,6 +24,8 @@ to meta type).  (Bug: 2389901). Reported by Barry Grundy.
 
 1/11/09: Update: Support for HFS Wrappers was added.  Patch by Rob Joyce.
 
+1/11/09: Update: Lots of bug fixes in HFS code from Rob Joyce.
+
 
 ---------------- VERSION 3.0.0 -------------- 
 0/00/00: Update: Many, many, many API changes.
diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c
index fe18b22a8..e2e66ea02 100644
--- a/tsk3/fs/hfs.c
+++ b/tsk3/fs/hfs.c
@@ -2750,8 +2750,10 @@ print_inode_file(FILE * hFile, TSK_FS_INFO * fs, TSK_INUM_T inum)
     if (inum == HFS_ROOT_INUM)
         tsk_fprintf(hFile, "/");
     else {
-        if (print_parent_path(hFile, fs, inum))
+        if (print_parent_path(hFile, fs, inum)) {
+            tsk_fprintf(hFile, "unknown]");
             return 1;
+        }
     }
     tsk_fprintf(hFile, "]");
     return 0;
@@ -2819,6 +2821,12 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
         tsk_fprintf(hFile, " (Mac OS X, Journaled)\n");
     else if (tsk_getu32(fs->endian, sb->last_mnt_ver) == FSK_MOUNT_VERSION)
         tsk_fprintf(hFile, " (failed journal replay)\n");
+    else if (tsk_getu32(fs->endian,
+            sb->last_mnt_ver) == FSCK_MOUNT_VERSION)
+        tsk_fprintf(hFile, " (fsck_hfs)\n");
+    else if (tsk_getu32(fs->endian,
+            sb->last_mnt_ver) == OS89_MOUNT_VERSION)
+        tsk_fprintf(hFile, " (Mac OS 8.1 - 9.2.2)\n");
     else
         tsk_fprintf(hFile, "\n");
 
@@ -2847,8 +2855,8 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
 
     /* State of the file system */
     if ((tsk_getu32(fs->endian, hfs->fs->attr) & HFS_BIT_VOLUME_UNMOUNTED)
-        || ((tsk_getu32(fs->endian,
-                    hfs->fs->attr) & HFS_BIT_VOLUME_INCONSISTENT) == 0))
+        && (!(tsk_getu32(fs->endian,
+                    hfs->fs->attr) & HFS_BIT_VOLUME_INCONSISTENT)))
         tsk_fprintf(hFile, "Volume Unmounted Properly\n");
     else
         tsk_fprintf(hFile, "Volume Unmounted Improperly\n");
@@ -2856,6 +2864,10 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
     if (tsk_getu32(fs->endian, hfs->fs->attr) & HFS_BIT_VOLUME_BADBLOCKS)
         tsk_fprintf(hFile, "Volume has bad blocks\n");
 
+    if (tsk_getu32(fs->endian,
+            hfs->fs->attr) & HFS_BIT_VOLUME_SOFTWARE_LOCK)
+        tsk_fprintf(hFile, "Software write protect enabled\n");
+
     tsk_fprintf(hFile, "Write count: %" PRIu32 "\n",
         tsk_getu32(fs->endian, sb->write_cnt));
 
@@ -2876,39 +2888,34 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
 
     inode = tsk_getu32(fs->endian, &(sb->finder_info[0]));
     tsk_fprintf(hFile, "Bootable Folder ID: %" PRIuINUM, inode);
-    if (inode > HFS_ROOT_INUM)
-        if (print_inode_file(hFile, fs, inode))
-            return 1;
+    if (inode > 0)
+        print_inode_file(hFile, fs, inode);
     tsk_fprintf(hFile, "\n");
 
     inode = tsk_getu32(fs->endian, &(sb->finder_info[4]));
     tsk_fprintf(hFile, "Startup App ID: %" PRIuINUM, inode);
-    if (inode > HFS_ROOT_INUM)
-        if (print_inode_file(hFile, fs, inode))
-            return 1;
+    if (inode > 0)
+        print_inode_file(hFile, fs, inode);
     tsk_fprintf(hFile, "\n");
 
     inode = tsk_getu32(fs->endian, &(sb->finder_info[8]));
     tsk_fprintf(hFile, "Startup Open Folder ID: %" PRIuINUM, inode);
-    if (inode > HFS_ROOT_INUM)
-        if (print_inode_file(hFile, fs, inode))
-            return 1;
+    if (inode > 0)
+        print_inode_file(hFile, fs, inode);
     tsk_fprintf(hFile, "\n");
 
     inode = tsk_getu32(fs->endian, &(sb->finder_info[12]));
     tsk_fprintf(hFile, "Mac OS 8/9 Blessed System Folder ID: %" PRIuINUM,
         inode);
-    if (inode > HFS_ROOT_INUM)
-        if (print_inode_file(hFile, fs, inode))
-            return 1;
+    if (inode > 0)
+        print_inode_file(hFile, fs, inode);
     tsk_fprintf(hFile, "\n");
 
     inode = tsk_getu32(fs->endian, &(sb->finder_info[20]));
     tsk_fprintf(hFile, "Mac OS X Blessed System Folder ID: %" PRIuINUM,
         inode);
-    if (inode > HFS_ROOT_INUM)
-        if (print_inode_file(hFile, fs, inode))
-            return 1;
+    if (inode > 0)
+        print_inode_file(hFile, fs, inode);
     tsk_fprintf(hFile, "\n");
 
     tsk_fprintf(hFile, "Volume Identifier: %08" PRIx32 "%08" PRIx32 "\n",
@@ -2958,7 +2965,18 @@ print_addr_act(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr,
     return TSK_WALK_CONT;
 }
 
-uint8_t
+/**
+ * Print details on a specific file to a file handle. 
+ *
+ * @param fs File system file is located in
+ * @param hFile File name to print text to
+ * @param inum Address of file in file system
+ * @param numblock The number of blocks in file to force print (can go beyond file size)
+ * @param sec_skew Clock skew in seconds to also print times in
+ * 
+ * @returns 1 on error and 0 on success
+ */
+static uint8_t
 hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     TSK_DADDR_T numblock, int32_t sec_skew)
 {
@@ -2973,11 +2991,11 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
             "hfs_istat: inum: %" PRIuINUM " numblock: %" PRIu32 "\n",
             inum, numblock);
 
-
-
-
-    if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL)
+    if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
+        strncat(tsk_errstr2, " - istat",
+            TSK_ERRSTR_L - strlen(tsk_errstr2));
         return 1;
+    }
 
     tsk_fprintf(hFile, "\nINODE INFORMATION\n");
     tsk_fprintf(hFile, "Entry:\t%" PRIuINUM "\n", inum);
@@ -2993,6 +3011,29 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
     tsk_fs_make_ls(fs_file->meta, hfs_mode);
     tsk_fprintf(hFile, "Mode:\t%s\n", hfs_mode);
 
+    if (sec_skew != 0) {
+        tsk_fprintf(hFile, "\nAdjusted times:\n");
+        fs_file->meta->mtime -= sec_skew;
+        fs_file->meta->atime -= sec_skew;
+        fs_file->meta->ctime -= sec_skew;
+        fs_file->meta->crtime -= sec_skew;
+        fs_file->meta->time2.hfs.bkup_time -= sec_skew;
+        tsk_fprintf(hFile, "Created:\t%s", ctime(&fs_file->meta->crtime));
+        tsk_fprintf(hFile, "Content Modified:\t%s",
+            ctime(&fs_file->meta->mtime));
+        tsk_fprintf(hFile, "Attributes Modified:\t%s",
+            ctime(&fs_file->meta->ctime));
+        tsk_fprintf(hFile, "Accessed:\t%s", ctime(&fs_file->meta->atime));
+        tsk_fprintf(hFile, "Backed Up:\t%s",
+            ctime(&fs_file->meta->time2.hfs.bkup_time));
+        fs_file->meta->mtime += sec_skew;
+        fs_file->meta->atime += sec_skew;
+        fs_file->meta->ctime += sec_skew;
+        fs_file->meta->crtime += sec_skew;
+        fs_file->meta->time2.hfs.bkup_time += sec_skew;
+        tsk_fprintf(hFile, "\nOriginal times:\n");
+    }
+
     tsk_fprintf(hFile, "Created:\t%s", ctime(&fs_file->meta->crtime));
     tsk_fprintf(hFile, "Content Modified:\t%s",
         ctime(&fs_file->meta->mtime));
@@ -3195,8 +3236,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
                 tsk_getu16(fs->endian, wrapper_sb->drAlBlSt);
             uint32_t drAlBlkSiz =
                 tsk_getu32(fs->endian, wrapper_sb->drAlBlkSiz);
-            uint16_t startBlock =
-                tsk_getu16(fs->endian,
+            uint16_t startBlock = tsk_getu16(fs->endian,
                 wrapper_sb->drEmbedExtent_startBlock);
             TSK_OFF_T hfsplus_offset =
                 (drAlBlSt * (TSK_OFF_T) 512) +
diff --git a/tsk3/fs/hfs_dent.c b/tsk3/fs/hfs_dent.c
index 9deea822c..ca8e2c3df 100644
--- a/tsk3/fs/hfs_dent.c
+++ b/tsk3/fs/hfs_dent.c
@@ -73,7 +73,7 @@
 #define UTF16_NULL 0x0000
 #define UTF16_NULL_REPLACE 0xfffd
 #define UTF16_SLASH 0x002f
-#define UTF16_COLON 0x001a
+#define UTF16_COLON 0x003a
 
 /* convert HFS+'s UTF16 to UTF8
  * replaces null characters with another character (0xfffd)
diff --git a/tsk3/fs/ifind_lib.c b/tsk3/fs/ifind_lib.c
index b7932921c..e20307e42 100644
--- a/tsk3/fs/ifind_lib.c
+++ b/tsk3/fs/ifind_lib.c
@@ -26,6 +26,7 @@
  */
 
 #include "tsk_fs_i.h"
+#include "tsk_hfs.h"
 
 
 /*******************************************************************************
@@ -240,6 +241,8 @@ tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path,
                 return -1;
             }
 
+            // @@@ It seems that we could abstract this and have a name comparison
+            // function for each file system that hides the case sensitive details...
             /* 
              * Check if this is the name that we are currently looking for,
              * as identified in 'cur_dir'
@@ -302,6 +305,29 @@ tsk_fs_path2inum(TSK_FS_INFO * a_fs, const char *a_path,
                 }
             }
 
+            /* HFS+ can be case-sensitive or case-insensitive */
+            else if (TSK_FS_TYPE_ISHFS(a_fs->ftype)) {
+                HFS_INFO *hfs = (HFS_INFO *)a_fs;
+                if ( hfs->is_case_sensitive ) {
+                    if (strcmp(fs_file->name->name, cur_dir) == 0) {
+                        found_name = 1;
+                    }
+                } else {
+                    if (strcasecmp(fs_file->name->name, cur_dir) == 0) {
+                        found_name = 1;
+                    }
+                }
+            }
+
+            /* Unknown how to compare names in this filesystem */
+            else {
+                tsk_errno = TSK_ERR_FS_GENFS;
+                snprintf(tsk_errstr, TSK_ERRSTR_L,
+                         "tsk_fs_path2inum: File System type not supported for file name comparison (%X)",
+                         a_fs->ftype);
+                return TSK_WALK_ERROR;
+            }
+			
             /* if found_name is 1, this entry was our target.  Update
              * data and move on to the next step, if needed. */
             if (found_name) {
diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h
index 5852b5475..bdf28db9a 100644
--- a/tsk3/fs/tsk_hfs.h
+++ b/tsk3/fs/tsk_hfs.h
@@ -80,16 +80,18 @@
  * Constants
  */
 
-#define HFS_MAGIC	0x4244  /* BD in big endian */
+#define HFS_MAGIC	    0x4244      /* BD in big endian */
 #define HFSPLUS_MAGIC	0x482b  /* H+ in big endian */
 #define HFSX_MAGIC      0x4858  /* HX in big endian */
 
 #define HFSPLUS_VERSION 0x0004  /* all HFS+ volumes are version 4 */
 #define HFSX_VERSION    0x0005  /* HFSX volumes start with version 5 */
 
-#define HFSPLUS_MOUNT_VERSION 0x31302e30        /* '10.0 for Mac OS X */
-#define HFSJ_MOUNT_VERSION    0x4846534a        /* 'HFSJ' for jounraled HFS+ on Mac OS X */
+#define HFSPLUS_MOUNT_VERSION 0x31302e30        /* '10.0' for Mac OS X */
+#define HFSJ_MOUNT_VERSION    0x4846534a        /* 'HFSJ' for journaled HFS+ on Mac OS X */
 #define FSK_MOUNT_VERSION     0x46534b21        /* 'FSK!' for failed journal replay */
+#define FSCK_MOUNT_VERSION    0x6673636b        /* 'fsck' for fsck_hfs */
+#define OS89_MOUNT_VERSION    0x382e3130        /* '8.10' for Mac OS 8.1-9.2.2 */
 
 #define HFS_SBOFF	1024
 #define HFS_FILE_CONTENT_LEN 160        // size of two hfs_fork data structures
@@ -116,11 +118,12 @@
  */
 #define NSEC_BTWN_1904_1970	(uint32_t) 2082844800U
 
-#define HFS_BIT_VOLUME_UNMOUNTED	(uint32_t)(1 << 8)      /* set if the volume was unmounted properly; as per TN 1150, modern Macintosh OSes always leave this bit set */
+#define HFS_BIT_VOLUME_UNMOUNTED	(uint32_t)(1 << 8)      /* set if the volume was unmounted properly; as per TN 1150, modern Macintosh OSes always leave this bit set for the boot volume */
 #define HFS_BIT_VOLUME_BADBLOCKS        (uint32_t)(1 << 9)      /* set if there are any bad blocks for this volume (in the Extents B-tree) */
 #define HFS_BIT_VOLUME_INCONSISTENT	(uint32_t)(1 << 11)     /* cleared if the volume was unmounted properly */
-#define HFS_BIT_VOLUME_JOURNALED	(uint32_t)(1 << 13)
 #define HFS_BIT_VOLUME_CNIDS_REUSED     (uint32_t)(1 << 12)     /* set if CNIDs have wrapped around past the maximum value and are being reused; in this case, there are CNIDs on the disk larger than the nextCatalogId field */
+#define HFS_BIT_VOLUME_JOURNALED	(uint32_t)(1 << 13)
+#define HFS_BIT_VOLUME_SOFTWARE_LOCK	(uint32_t)(1 << 14)     /* set if volume should be write-protected in software */
 
 /* constants for BTree header record attributes */
 #define HFS_BT_BIGKEYS 0x00000002       /* kBTBigKeysMask : key length field is 16 bits */
-- 
GitLab