Index: cpukit/libmisc/untar/untar.c
===================================================================
--- cpukit/libmisc/untar/untar.c	(revision 560db81027c507476cbc552e16e6ca7440eb2b2b)
+++ cpukit/libmisc/untar/untar.c	(revision d84e346b26017f021c1a7d5c8ad078c7264240ab)
@@ -14,5 +14,7 @@
 /*
  *  Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
-
+ *
+ *  Copyright 2016 Chris Johns <chrisj@rtems.org>
+ *
  *  The license and distribution terms for this file may be
  *  found in the file LICENSE in this distribution or at
@@ -24,4 +26,5 @@
 #endif
 
+#include <stdbool.h>
 #include <sys/param.h>
 #include <stdio.h>
@@ -65,5 +68,5 @@
  *   for(i = 0; i < 512; i++)
  *       sum += 0xFF & header[i];
- */ 
+ */
 
 #define MAX_NAME_FIELD_SIZE      99
@@ -72,5 +75,7 @@
  * This converts octal ASCII number representations into an
  * unsigned long.  Only support 32-bit numbers for now.
- */ 
+ *
+ * warning: this code is referenced in the IMFS.
+ */
 unsigned long
 _rtems_octal2ulong(
@@ -93,4 +98,242 @@
 
 /*
+ * Common error message formatter.
+ */
+static void
+Print_Error(const rtems_printer *printer, const char* message, const char* path)
+{
+  rtems_printf(printer, "untar: %s: %s: (%d) %s\n",
+               message, path, errno, strerror(errno));
+}
+
+/*
+ * Get the type of node on in the file system if present.
+ */
+static int
+Stat_Node(const char* path)
+{
+  struct stat sb;
+  if (stat(path, &sb) < 0)
+    return -1;
+  if (S_ISDIR(sb.st_mode))
+    return DIRTYPE;
+  return REGTYPE;
+}
+
+/*
+ * Make the directory path for a file if it does not exist.
+ */
+static int
+Make_Path(const rtems_printer *printer, const char* filename, bool end_is_dir)
+{
+  char* copy = strdup(filename);
+  char* path = copy;
+
+  /*
+   * Skip leading path separators.
+   */
+  while (*path == '/')
+    ++path;
+
+  /*
+   * Any path left?
+   */
+  if (*path != '\0') {
+    bool  path_end = false;
+    char* end = path;
+    int   r;
+
+    /*
+     * Split the path into directory components. Check the node and if a file
+     * and not the end of the path remove it and create a directory. If a
+     * directory and not the end of the path decend into the directory.
+     */
+    while (!path_end) {
+      while (*end != '\0' && *end != '/')
+        ++end;
+
+      /*
+       * Are we at the end of the path?
+       */
+      if (*end == '\0')
+        path_end = true;
+
+      /*
+       * Split the path.
+       */
+      *end = '\0';
+
+      /*
+       * Get the node's status, exists, error, directory or regular? Regular
+       * means not a directory.
+       */
+      r = Stat_Node(path);
+
+      /*
+       * If there are errors other than not existing we are finished.
+       */
+      if (r < 0 && errno != ENOENT) {
+        Print_Error(printer, "stat", path);
+        return -1;
+      }
+
+      /*
+       * If a file remove and create a directory if not the end.
+       */
+      if (r == REGTYPE) {
+        r = unlink(path);
+        if (r < 0) {
+          Print_Error(printer, "unlink", path);
+          free(copy);
+          return -1;
+        }
+        if (!path_end) {
+          r = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
+          if (r < 0) {
+            Print_Error(printer, "mkdir", path);
+            free(copy);
+            return -1;
+          }
+        }
+      }
+      else if (r < 0) {
+        /*
+         * Node does not exist which means the rest of the path will not exist.
+         */
+        while (!path_end) {
+          r = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
+          if (r < 0) {
+            Print_Error(printer, "mkdir", path);
+            free(copy);
+            return -1;
+          }
+          if (!path_end) {
+            *end = '/';
+            ++end;
+          }
+          while (*end != '\0' && *end != '/')
+            ++end;
+          if (*end == '\0')
+            path_end = true;
+        }
+      }
+      else if (path_end && r == DIRTYPE && !end_is_dir) {
+        /*
+         * We only handle a directory if at the end of the path and the end is
+         * a file. If we cannot remove the directory because it is not empty we
+         * raise an error. Otherwise this is a directory and we do nothing
+         * which lets us decend into it.
+         */
+        r = rmdir(path);
+        if (r < 0) {
+          Print_Error(printer, "rmdir", path);
+          free(copy);
+          return -1;
+        }
+      }
+
+      /*
+       * If not the end of the path put back the directory separator.
+       */
+      if (!path_end) {
+        *end = '/';
+        ++end;
+      }
+    }
+  }
+
+  free(copy);
+
+  return 0;
+}
+
+static int
+Untar_ProcessHeader(
+  const char          *bufr,
+  char                *fname,
+  unsigned long       *file_size,
+  unsigned long       *nblocks,
+  unsigned char       *linkflag,
+  const rtems_printer *printer
+)
+{
+  char           linkname[100];
+  int            sum;
+  int            hdr_chksum;
+  int            retval = UNTAR_SUCCESSFUL;
+
+  fname[0] = '\0';
+  *file_size = 0;
+  *nblocks = 0;
+  *linkflag = -1;
+
+  if (strncmp(&bufr[257], "ustar", 5)) {
+    return UNTAR_SUCCESSFUL;
+  }
+
+  /*
+   * Compute the TAR checksum and check with the value in the archive.  The
+   * checksum is computed over the entire header, but the checksum field is
+   * substituted with blanks.
+   */
+  hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
+  sum        = _rtems_tar_header_checksum(bufr);
+
+  if (sum != hdr_chksum) {
+    rtems_printf(printer, "untar: file header checksum error\n");
+    return UNTAR_INVALID_CHECKSUM;
+  }
+
+  strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
+  fname[MAX_NAME_FIELD_SIZE] = '\0';
+
+  *linkflag   = bufr[156];
+  *file_size = _rtems_octal2ulong(&bufr[124], 12);
+
+  /*
+   * We've decoded the header, now figure out what it contains and do something
+   * with it.
+   */
+  if (*linkflag == SYMTYPE) {
+    strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
+    linkname[MAX_NAME_FIELD_SIZE] = '\0';
+    rtems_printf(printer, "untar: symlink: %s -> %s\n", linkname, fname);
+    symlink(linkname, fname);
+  } else if (*linkflag == REGTYPE) {
+    rtems_printf(printer, "untar: file: %s (%i)\n", fname, (int) *file_size);
+    *nblocks = (((*file_size) + 511) & ~511) / 512;
+    if (Make_Path(printer, fname, false) < 0) {
+      retval  = UNTAR_FAIL;
+    }
+  } else if (*linkflag == DIRTYPE) {
+    int r;
+    rtems_printf(printer, "untar: dir: %s\n", fname);
+    if (Make_Path(printer, fname, true) < 0) {
+      retval  = UNTAR_FAIL;
+    }
+    r = mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO);
+    if (r < 0) {
+      if (errno == EEXIST) {
+        struct stat stat_buf;
+        if (stat(fname, &stat_buf) == 0) {
+          if (!S_ISDIR(stat_buf.st_mode)) {
+            r = unlink(fname);
+            if (r == 0) {
+              r = mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO);
+            }
+          }
+        }
+      }
+      if (r < 0) {
+        Print_Error(printer, "mkdir", fname);
+        retval = UNTAR_FAIL;
+      }
+    }
+  }
+
+  return retval;
+}
+
+/*
  * Function: Untar_FromMemory
  *
@@ -115,7 +358,8 @@
  */
 int
-Untar_FromMemory(
-  void   *tar_buf,
-  size_t  size
+Untar_FromMemory_Print(
+  void                *tar_buf,
+  size_t               size,
+  const rtems_printer *printer
 )
 {
@@ -123,18 +367,15 @@
   const char     *tar_ptr = (const char *)tar_buf;
   const char     *bufr;
-  size_t         n;
   char           fname[100];
-  char           linkname[100];
-  int            sum;
-  int            hdr_chksum;
-  int            retval;
+  int            retval = UNTAR_SUCCESSFUL;
   unsigned long  ptr;
-  unsigned long  i;
   unsigned long  nblocks;
   unsigned long  file_size;
   unsigned char  linkflag;
 
+  rtems_printf(printer, "untar: memory at %p (%zu)\n", tar_buf, size);
+
   ptr = 0;
-  while (1) {
+  while (true) {
     if (ptr + 512 > size) {
       retval = UNTAR_SUCCESSFUL;
@@ -145,54 +386,29 @@
     bufr = &tar_ptr[ptr];
     ptr += 512;
-    if (strncmp(&bufr[257], "ustar", 5)) {
-      retval = UNTAR_SUCCESSFUL;
+
+    retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
+
+    if (retval != UNTAR_SUCCESSFUL)
       break;
-    }
-
-    strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
-    fname[MAX_NAME_FIELD_SIZE] = '\0';
-
-    linkflag   = bufr[156];
-    file_size  = _rtems_octal2ulong(&bufr[124], 12);
-
-    /*
-     * Compute the TAR checksum and check with the value in
-     * the archive.  The checksum is computed over the entire
-     * header, but the checksum field is substituted with blanks.
-     */
-    hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
-    sum = _rtems_tar_header_checksum(bufr);
-
-    if (sum != hdr_chksum) {
-      retval = UNTAR_INVALID_CHECKSUM;
-      break;
-    }
-
-    /*
-     * We've decoded the header, now figure out what it contains and
-     * do something with it.
-     */
-    if (linkflag == SYMTYPE) {
-      strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
-      linkname[MAX_NAME_FIELD_SIZE] = '\0';
-      symlink(linkname, fname);
-    } else if (linkflag == REGTYPE) {
-      nblocks = (((file_size) + 511) & ~511) / 512;
+
+    if (linkflag == REGTYPE) {
       if ((fp = fopen(fname, "w")) == NULL) {
-        printk("Untar: failed to create file %s\n", fname);
+        Print_Error(printer, "open", fname);
         ptr += 512 * nblocks;
       } else {
         unsigned long sizeToGo = file_size;
-        size_t len;
+        size_t        len;
+        size_t        i;
+        size_t        n;
 
         /*
-         * Read out the data.  There are nblocks of data where nblocks
-         * is the file_size rounded to the nearest 512-byte boundary.
+         * Read out the data.  There are nblocks of data where nblocks is the
+         * file_size rounded to the nearest 512-byte boundary.
          */
-        for (i=0; i<nblocks; i++) {
-          len = ((sizeToGo < 512L)?(sizeToGo):(512L));
+        for (i = 0; i < nblocks; i++) {
+          len = ((sizeToGo < 512L) ? (sizeToGo) : (512L));
           n = fwrite(&tar_ptr[ptr], 1, len, fp);
           if (n != len) {
-            printk("untar: Error during write\n");
+            Print_Error(printer, "write", fname);
             retval  = UNTAR_FAIL;
             break;
@@ -203,40 +419,25 @@
         fclose(fp);
       }
-    } else if (linkflag == DIRTYPE) {
-      if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
-        if (errno == EEXIST) {
-          struct stat stat_buf;
-          if ( stat(fname, &stat_buf) == 0 ) {
-            if (  S_ISDIR(stat_buf.st_mode) ) {
-              continue;
-            } else {
-              if ( unlink(fname) != -1 ) {
-                if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
-                  continue;
-              }
-            }
-          }
-        }
-        printk("Untar: failed to create directory %s\n", fname);
-        retval = UNTAR_FAIL;
-        break;
-      }
-    }
-  }
-
-  return(retval);
-}
-
-/*
- * Function: Untar_FromFile
+
+    }
+  }
+
+  return retval;
+}
+
+/*
+ * Function: Untar_FromMemory
  *
  * Description:
  *
  *    This is a simple subroutine used to rip links, directories, and
- *    files out of a TAR file.
+ *    files out of a block of memory.
+ *
  *
  * Inputs:
  *
- *    const char *tar_name   - TAR filename.
+ *    void *  tar_buf    - Pointer to TAR buffer.
+ *    size_t  size       - Length of TAR buffer.
+ *
  *
  * Output:
@@ -245,8 +446,37 @@
  *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
  *          UNTAR_INVALID_HEADER    for an invalid header.
+ *
  */
 int
-Untar_FromFile(
-  const char *tar_name
+Untar_FromMemory(
+  void   *tar_buf,
+  size_t  size
+)
+{
+  return Untar_FromMemory_Print(tar_buf, size, false);
+}
+
+/*
+ * Function: Untar_FromFile
+ *
+ * Description:
+ *
+ *    This is a simple subroutine used to rip links, directories, and
+ *    files out of a TAR file.
+ *
+ * Inputs:
+ *
+ *    const char *tar_name   - TAR filename.
+ *
+ * Output:
+ *
+ *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
+ *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
+ *          UNTAR_INVALID_HEADER    for an invalid header.
+ */
+int
+Untar_FromFile_Print(
+  const char          *tar_name,
+  const rtems_printer *printer
 )
 {
@@ -255,11 +485,8 @@
   ssize_t        n;
   char           fname[100];
-  char           linkname[100];
-  int            sum;
-  int            hdr_chksum;
   int            retval;
   unsigned long  i;
   unsigned long  nblocks;
-  unsigned long  size;
+  unsigned long  file_size;
   unsigned char  linkflag;
 
@@ -283,36 +510,10 @@
     }
 
-    if (strncmp(&bufr[257], "ustar", 5)) {
+    retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
+
+    if (retval != UNTAR_SUCCESSFUL)
       break;
-    }
-
-    strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
-    fname[MAX_NAME_FIELD_SIZE] = '\0';
-
-    linkflag   = bufr[156];
-    size       = _rtems_octal2ulong(&bufr[124], 12);
-
-    /*
-     * Compute the TAR checksum and check with the value in
-     * the archive.  The checksum is computed over the entire
-     * header, but the checksum field is substituted with blanks.
-     */
-    hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
-    sum = _rtems_tar_header_checksum(bufr);
-
-    if (sum != hdr_chksum) {
-      retval = UNTAR_INVALID_CHECKSUM;
-      break;
-    }
-
-    /*
-     * We've decoded the header, now figure out what it contains and
-     * do something with it.
-     */
-    if (linkflag == SYMTYPE) {
-      strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
-      linkname[MAX_NAME_FIELD_SIZE] = '\0';
-      symlink(linkname,fname);
-    } else if (linkflag == REGTYPE) {
+
+    if (linkflag == REGTYPE) {
       int out_fd;
 
@@ -321,38 +522,48 @@
        * is the size rounded to the nearest 512-byte boundary.
        */
-      nblocks = (((size) + 511) & ~511) / 512;
 
       if ((out_fd = creat(fname, 0644)) == -1) {
-        (void) lseek(fd, SEEK_CUR, 512 * nblocks);
+        (void) lseek(fd, SEEK_CUR, 512UL * nblocks);
       } else {
-        for (i=0; i<nblocks; i++) {
+        for (i = 0; i < nblocks; i++) {
           n = read(fd, bufr, 512);
-          n = MIN(n, size - i*512);
+          n = MIN(n, file_size - (i * 512UL));
           (void) write(out_fd, bufr, n);
         }
         close(out_fd);
       }
-    } else if (linkflag == DIRTYPE) {
-      if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
-        if (errno == EEXIST) {
-          struct stat stat_buf;
-          if ( stat(fname, &stat_buf) == 0 ) {
-            if (  S_ISDIR(stat_buf.st_mode) ) {
-              continue;
-            } else {
-              if ( unlink(fname) != -1 ) {
-                if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
-                  continue;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
+    }
+  }
+
   free(bufr);
   close(fd);
 
-  return(retval);
+  return retval;
+}
+
+/*
+ * Function: Untar_FromFile
+ *
+ * Description:
+ *
+ *    This is a simple subroutine used to rip links, directories, and
+ *    files out of a TAR file.
+ *
+ * Inputs:
+ *
+ *    const char *tar_name   - TAR filename.
+ *
+ * Output:
+ *
+ *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
+ *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
+ *          UNTAR_INVALID_HEADER    for an invalid header.
+ */
+int
+Untar_FromFile(
+  const char *tar_name
+)
+{
+  return Untar_FromFile_Print(tar_name, NULL);
 }
 
Index: cpukit/libmisc/untar/untar.h
===================================================================
--- cpukit/libmisc/untar/untar.h	(revision 560db81027c507476cbc552e16e6ca7440eb2b2b)
+++ cpukit/libmisc/untar/untar.h	(revision d84e346b26017f021c1a7d5c8ad078c7264240ab)
@@ -3,5 +3,5 @@
  *
  * @brief Untar an Image
- * 
+ *
  * This file defines the interface to methods which can untar an image.
  */
@@ -18,6 +18,9 @@
 #define _RTEMS_UNTAR_H
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <tar.h>
+
+#include <rtems/print.h>
 
 /**
@@ -38,5 +41,7 @@
 
 int Untar_FromMemory(void *tar_buf, size_t size);
+int Untar_FromMemory_Print(void *tar_buf, size_t size, const rtems_printer* printer);
 int Untar_FromFile(const char *tar_name);
+int Untar_FromFile_Print(const char *tar_name, const rtems_printer* printer);
 
 /**************************************************************************
Index: testsuites/libtests/tar01/init.c
===================================================================
--- testsuites/libtests/tar01/init.c	(revision 560db81027c507476cbc552e16e6ca7440eb2b2b)
+++ testsuites/libtests/tar01/init.c	(revision d84e346b26017f021c1a7d5c8ad078c7264240ab)
@@ -45,7 +45,10 @@
 {
   rtems_status_code sc;
+  rtems_printer     printer;
+
+  rtems_print_printer_printf(&printer);
 
   printf("Untaring from memory - ");
-  sc = Untar_FromMemory((void *)TARFILE_START, TARFILE_SIZE);
+  sc = Untar_FromMemory_Print((void *)TARFILE_START, TARFILE_SIZE, &printer);
   if (sc != RTEMS_SUCCESSFUL) {
     printf ("error: untar failed: %s\n", rtems_status_text (sc));
@@ -57,5 +60,5 @@
   printf( "========= /home/test_file =========\n" );
   test_cat( "/home/test_file", 0, 0 );
-  
+
   /******************/
   printf( "========= /symlink =========\n" );
@@ -98,5 +101,5 @@
   printf( "========= /dest/home/test_file =========\n" );
   test_cat( "/dest/home/test_file", 0, 0 );
-  
+
   /******************/
   printf( "========= /dest/symlink =========\n" );
