diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c
index fa7fe6416290c9ccb13a85b4774a6d43068aaa48..326faeed22051e3d60f929420c1c33463d85877b 100644
--- a/tsk3/fs/hfs.c
+++ b/tsk3/fs/hfs.c
@@ -1266,7 +1266,10 @@ hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat, unsigned char * is_error) {
 
 	*is_error = 0;  // default, not an error
 
-	// TODO:  TEST entry for non-null and good data here
+	if(cat == NULL) {
+		error_detected(TSK_ERR_FS_ARG, "hfs_follow_hard_link: Pointer to Catalog entry (2nd arg) is null");
+		return 0;
+	}
 
 	TSK_FS_INFO * fs = (TSK_FS_INFO *) hfs;
 	TSK_INUM_T cnid = tsk_getu32(fs->endian, cat->std.cnid);
@@ -1300,7 +1303,6 @@ hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat, unsigned char * is_error) {
 				(hfs->has_meta_dir_crtime && crtime == hfs->metadir_crtime) ||
 				(hfs->has_root_crtime && crtime == hfs->root_crtime)) {
 			// OK, this is a hard link to a file.
-			//printf("hardlink file case ");
 			fflush(stdout);
 			uint32_t linkNum = tsk_getu32(fs->endian, cat->std.perm.special.inum);
 			char fNameBuf[50];
@@ -1308,12 +1310,10 @@ hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat, unsigned char * is_error) {
 			snprintf(fNameBuf, 50, "/" UTF8_NULL_REPLACE  UTF8_NULL_REPLACE UTF8_NULL_REPLACE UTF8_NULL_REPLACE
 					"HFS+ Private Data/iNode%" PRIu32, linkNum);
 			TSK_INUM_T target_cnid;   //  This is the real CNID of the file.
-			TSK_FS_FILE * result_file;
 
 			int8_t result = tsk_fs_path2inum(fs, fNameBuf, &target_cnid, NULL);
 			if(result == 0) {
 				// Succeeded in finding that target_cnid in the Catalog file
-				//printf(" %" PRIuINUM " --> %" PRIuINUM "\n", cnid, target_cnid);
 				return target_cnid;
 			} else {
 				// This should be a hard link, BUT...
@@ -1345,31 +1345,23 @@ hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat, unsigned char * is_error) {
 			return cnid;
 		}
 
-		//printf("crtime = %u  meta cr time = %u  dir cr time = %u  root cr time = %u\n",
-		//		crtime, hfs->meta_crtime, hfs->metadir_crtime, hfs->root_crtime);
-
 		// Now we need to check the creation time against the three FS creation times
 		if((hfs->has_meta_crtime && crtime == hfs->meta_crtime) ||
 				(hfs->has_meta_dir_crtime && crtime == hfs->metadir_crtime) ||
 				(hfs->has_root_crtime && crtime == hfs->root_crtime)) {
 			// OK, this is a hard link to a directory.
-			//printf("hardlink directory case ");
 			fflush(stdout);
 			uint32_t linkNum = tsk_getu32(fs->endian, cat->std.perm.special.inum);
 			char fNameBuf[50];
 			memset(fNameBuf, 0, 50);
 			snprintf(fNameBuf, 50, "/.HFS+ Private Directory Data%c/dir_%" PRIu32, (char) 0xD, linkNum);
 			TSK_INUM_T target_cnid;   //  This is the real CNID of the file.
-			TSK_FS_FILE * result_file;
 
-			//printf("Calling path2inum, with %s\n", fNameBuf);
-			//fflush(stdout);
+
 			int8_t result = tsk_fs_path2inum(fs, fNameBuf, &target_cnid, NULL);
-			//printf("path2inum result = %d\n", result);
-			//fflush(stdout);
+
 			if(result == 0) {
 				// Succeeded in finding that target_cnid in the Catalog file
-				//printf(" %" PRIuINUM " --> %" PRIuINUM "\n", cnid, target_cnid);
 				return target_cnid;
 			} else {
 				// This should be a hard link to a directory, BUT...
@@ -1393,7 +1385,6 @@ hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat, unsigned char * is_error) {
 			}
 		}
 	}
-	//printf("not a hard link case\n");
 	// It cannot be a hard link (file or directory)
 	return cnid;
 }
@@ -2177,9 +2168,10 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const HFS_ENTRY * a_hfs_entry,
     TSK_FS_FILE * a_fs_file)
 {
 
-	// Note, a_hfs_entry->cat is really of type hfs_file.  But, the first
-	// member of that struct is a hfs_file_folder.  So, this cast is appropriate.
-	const hfs_file_folder * a_entry = &(a_hfs_entry->cat);
+	// Note, a_hfs_entry->cat is really of type hfs_file.  But, hfs_file_folder is a union
+	// of that type with hfs_folder.  Both of hfs_file and hfs_folder have the same first member.
+	// So, this cast is appropriate.
+	const hfs_file_folder * a_entry = (hfs_file_folder *) &(a_hfs_entry->cat);
 
 	TSK_FS_META * a_fs_meta = a_fs_file->meta;
 
@@ -2416,11 +2408,11 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
         return 1;
 
     /* Copy the structure in hfs to generic fs_inode */
-    if (hfs_dinode_copy(hfs, (const hfs_file_folder *) &entry,
-            a_fs_file)) {
+    if (hfs_dinode_copy(hfs,  &entry,   a_fs_file)) {
         return 1;
     }
 
+
     return 0;
 }
 
@@ -2437,7 +2429,7 @@ hfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
     int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
 {
     TSK_FS_INFO *fs;
-    HFS_INFO *hfs;
+    //HFS_INFO *hfs;
 
     if (tsk_verbose)
         tsk_fprintf(stderr,
@@ -2466,11 +2458,11 @@ hfs_attr_walk_special(const TSK_FS_ATTR * fs_attr,
     }
 
     fs = fs_attr->fs_file->fs_info;
-    hfs = (HFS_INFO *) fs;
+    //hfs = (HFS_INFO *) fs;
     TSK_ENDIAN_ENUM endian = fs->endian;
 
     /* This MUST be a compressed attribute     */
-    if (!fs_attr->flags & TSK_FS_ATTR_COMP) {
+    if (!(fs_attr->flags & TSK_FS_ATTR_COMP)) {
         error_detected(TSK_ERR_FS_FWALK,
             "hfs_attr_walk_special: called with non-special attribute: %x",
             fs_attr->flags);
@@ -2686,7 +2678,7 @@ hfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
         tsk_fprintf(stderr,
             "hfs_file_read_special: called because this file is compressed, with data in the resource fork\n");
     TSK_FS_INFO *fs = NULL;
-    HFS_INFO *hfs = NULL;
+    //HFS_INFO *hfs = NULL;
 
     // Reading zero bytes?  OK at any offset, I say!
     if (a_len == 0)
@@ -2701,11 +2693,11 @@ hfs_file_read_special(const TSK_FS_ATTR * a_fs_attr,
     }
 
     fs = a_fs_attr->fs_file->fs_info;
-    hfs = (HFS_INFO *) fs;
+    //hfs = (HFS_INFO *) fs;
     TSK_ENDIAN_ENUM endian = fs->endian;
 
     // This should be a compressed file.  If not, that's an error!
-    if (!a_fs_attr->flags & TSK_FS_ATTR_COMP) {
+    if (!(a_fs_attr->flags & TSK_FS_ATTR_COMP)) {
         error_detected(TSK_ERR_FS_ARG,
             "hfs_file_read_special: called with non-special attribute: %x",
             a_fs_attr->flags);
@@ -3464,8 +3456,8 @@ hfs_load_extended_attrs(TSK_FS_FILE * fs_file,
                         tsk_getu64(TSK_LIT_ENDIAN,
                         cmph->uncompressed_size);
                     *uncompressedSize = uncSize;
-                    unsigned char reallyCompressed;
-                    uint64_t cmpSize = 0;
+                    //unsigned char reallyCompressed;
+                    //uint64_t cmpSize = 0;
                     if (cmpType == 3) {
                         // Data is inline.  We will load the uncompressed data as a resident attribute.
                         if (tsk_verbose)
@@ -3495,8 +3487,8 @@ hfs_load_extended_attrs(TSK_FS_FILE * fs_file,
                                     tsk_fprintf(stderr,
                                         "hfs_load_extended_attrs: Leading byte, 0x0F, indicates that the data is not really compressed.\n"
                                         "hfs_load_extended_attrs:  Loading the default DATA attribute.");
-                                reallyCompressed = FALSE;
-                                cmpSize = attributeLength - 17; // subtr. size of header + 1 indicator byte
+                                //reallyCompressed = FALSE;
+                                //cmpSize = attributeLength - 17; // subtr. size of header + 1 indicator byte
 
                                 // Load the remainder of the attribute as 128-0
                                 // set the details in the fs_attr structure.  Note, we are loading this
@@ -3516,12 +3508,12 @@ hfs_load_extended_attrs(TSK_FS_FILE * fs_file,
 
                             }
                             else {      // Leading byte is not 0x0F
-                                reallyCompressed = TRUE;
+                                //reallyCompressed = TRUE;
 #ifdef HAVE_LIBZ
                                 if (tsk_verbose)
                                     tsk_fprintf(stderr,
                                         "hfs_load_extended_attrs: Uncompressing (inflating) data.");
-                                cmpSize = attributeLength - 16; // subt size of header
+                                // cmpSize = attributeLength - 16; // subt size of header
                                 // Uncompress the remainder of the attribute, and load as 128-0
                                 char *uncBuf = (char *) tsk_malloc(uncSize + 100);        // add some extra space
                                 if (uncBuf == NULL) {
@@ -3602,7 +3594,7 @@ hfs_load_extended_attrs(TSK_FS_FILE * fs_file,
                     }
                     else if (cmpType == 4) {
                         // Data is compressed in the resource fork
-                        reallyCompressed = TRUE;
+                        //reallyCompressed = TRUE;
                         *compDataInRSRC = TRUE; // The compressed data is in the RSRC fork
                         if (tsk_verbose)
                             tsk_fprintf(stderr,
@@ -4062,100 +4054,6 @@ hfs_load_attrs(TSK_FS_FILE * fs_file)
         return 1;
     }
 
-    /* Some notes on how to implement hard links.
-     *
-     @@@ We need to detect hard links and load up the indirect node info
-     instead of the current node info.
-
-     //Detect Hard links
-     else if ((tsk_getu32(fs->endian,
-     entry.cat.std.u_info.file_type) == HFS_HARDLINK_FILE_TYPE)
-     && (tsk_getu32(fs->endian,
-     entry.cat.std.u_info.file_cr) ==
-     HFS_HARDLINK_FILE_CREATOR)) {
-
-     //  Get the indirect node value
-     tsk_getu32(fs->endian, entry.cat.std.perm.special.inum)
-
-     // Find the indirect node
-     "/____HFS+ Private Data/iNodeXXXX"
-     // Load its runs and look in extents for others (based on its CNID)
-     *
-     * PROPOSED:
-     if ((tsk_getu32(fs->endian,
-     entry.cat.std.u_info.file_type) == HFS_HARDLINK_FILE_TYPE)
-     && (tsk_getu32(fs->endian,
-     entry.cat.std.u_info.file_cr) ==
-     HFS_HARDLINK_FILE_CREATOR)) {
-
-     //  Get the indirect node value
-     uint32_t iinum = tsk_getu32(fs->endian, entry.cat.std.perm.special.inum);
-
-     Now, need to find the  file.  iinum is actually part of the
-     filename.  We find *that* file and then use its cnid (or inum).
-
-     #include "tsk_fs.h"  <- need this
-
-     fs_file = tsk_fs_open_file_meta(fs, NULL, @@@iinum);
-
-     // Now repeat the logic from above. This fs_file must be well formed
-     if ((fs_file == NULL) || (fs_file->meta == NULL)
-     || (fs_file->fs_info == NULL)) {
-     tsk_errno = TSK_ERR_FS_ARG;
-     tsk_error_set_errstr(
-     "hfs_load_attrs: fs_file or meta is NULL");
-     return 1;
-     }
-
-     // All is well.  No need to recompute fs and hfs -- they should be the same
-
-     }  // OK, now continue with normal processing, but use the new fs_file.
-
-     *************** END of proposed hardlink code ***********************************/
-//  I am saving the following Hardlink code -- trying a different approach.
-
-//    printf("BB Meta = %llu\n", (long long unsigned int)fs_file->meta);
-//    fflush(stdout);
-//
-//    TSK_FS_FILE *link_file;
-//    unsigned char is_error;
-//    link_file = hfs_is_hard_link(fs_file, NULL, &is_error);
-//    if(is_error) {
-//    	tsk_fprintf(stderr, "WARNING: Error occurred in trying to test for a"
-//    			" hard link.  Will treat file as a non-link.\n");
-//    	tsk_error_print(stderr);
-//    	tsk_error_reset();
-//    } else if(link_file != NULL){
-//    	if(link_file->meta == NULL) {
-//    		tsk_fprintf(stderr, "hfs_load_attrs: WARNING, hard link target exists"
-//    				" but does not have metadata.\n");
-//    	} else {
-//    		if(tsk_verbose)
-//    			tsk_fprintf(stderr, "hfs_load_attrs: File is a hard link, real inum (CNID) is %"
-//    					PRIuINUM "\n", link_file->meta->addr);
-//    		tsk_fs_meta_close(fs_file->meta);
-//    		fs_file->meta = link_file->meta;
-//
-//    		// Now, we want to "close" the link_file, but that would also free the metadata,
-//    		// which we want to save. So, we do it by hand.
-//    		if ( (link_file->tag == TSK_FS_FILE_TAG)) {
-//
-//    			link_file->tag = 0;
-//    			if (link_file->name) {
-//    				tsk_fs_name_free(link_file->name);
-//    				link_file->name = NULL;
-//    			}
-//
-//    			free(link_file);
-//    		}
-//
-//    	}
-//    }
-//
-//    printf("CC Meta = %llu\n", (long long unsigned int) fs_file->meta);
-//    fflush(stdout);
-
-
     // Initialize the attribute counter
     reset_attribute_counter();
 
@@ -6003,9 +5901,12 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
 
     // Now the metadata directory
     TSK_INUM_T inum;
-    // TODO:  I may need to construct this string more carefully, so that the "null replacement"
-    // is given by a parameter.  Vanilla TSK uses '^' but other systems such as Mac Marshal
-    // use 0xfffd (UTF16).  This is claimed to translate to 0xEF 0xBF 0xBD.
+    // The metadata directory is a sub-directory of the root.  Its name begins with four nulls, followed
+    // by "HFS+ Private Data".  The file system parsing code replaces nulls in filenames with UTF8_NULL_REPLACE.
+    // In the released version of TSK, this replacement is the character '^'.
+    // NOTE: There is a standard Unicode replacement which is 0xfffd in UTF16 and 0xEF 0xBF 0xBD in UTF8.
+    // Systems that require the standard definition can redefine UTF8_NULL_REPLACE and UTF16_NULL_REPLACE
+    // in tsk_hfs.h
     int8_t result = tsk_fs_path2inum(fs, "/" UTF8_NULL_REPLACE UTF8_NULL_REPLACE UTF8_NULL_REPLACE UTF8_NULL_REPLACE
     		"HFS+ Private Data", &inum, NULL);
     if(result == 0) {
@@ -6024,6 +5925,10 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     // Now, the directory metadata directory!
     char dirn[31];
     memset(dirn, 0, 31);
+
+    // The "directory" metadata directory, where hardlinked directories actually live, is a subdirectory
+    // of the root.  The beginning of the name of this directory is ".HFS+ Private Directory Data" which
+    // is followed by a carriage return (ASCII 13).
     strncpy(dirn, "/.HFS+ Private Directory Data", 29);
     dirn[29] = (char) 13;
 
diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h
index a0de7e2e6ebad5039a554b4c1ef84ee2d030df29..88ecb2cadafc4cf3158fb9aaf37a43d3185839cf 100644
--- a/tsk3/fs/tsk_hfs.h
+++ b/tsk3/fs/tsk_hfs.h
@@ -148,10 +148,16 @@
 
 #define UTF16_NULL 0x0000
 #define UTF16_NULL_REPLACE 0x005e
+
+// This is the standard Unicode replacement character in UTF16
+//#define UTF16_NULL_REPLACE 0xfffd
+
 #define UTF16_SLASH 0x002f
 #define UTF16_COLON 0x003a
 #define UTF16_LEAST_PRINTABLE 0x0020
 #define UTF8_NULL_REPLACE "^"
+
+// This is the standard Unicode replacement character in UTF8
 //#define UTF8_NULL_REPLACE "\xef\xbf\xbd"