[rtems commit] libmisc/untar: Support directory create and overwrites. Share the common code.
Chris Johns
chrisj at rtems.org
Sat Jun 4 00:11:25 UTC 2016
Module: rtems
Branch: master
Commit: d84e346b26017f021c1a7d5c8ad078c7264240ab
Changeset: http://git.rtems.org/rtems/commit/?id=d84e346b26017f021c1a7d5c8ad078c7264240ab
Author: Chris Johns <chrisj at rtems.org>
Date: Thu May 19 09:18:21 2016 +1000
libmisc/untar: Support directory create and overwrites. Share the common code.
Support creating directories for files with a path depth greater than 1. Some
tar files can have files with a path depth greater than 1 and no directory
entry in the tar file to create a directory.
Support overwriting existing files and directories failing in a similar
way to tar on common hosts. If a file is replaced with a file delete the
file and create a new file. If a directory replaces a file remove the file
and create the directory. If a file replaces a directory remove the directory,
and if the directory is not empty and cannot be removed report an error. If a
directory alreday exists do nothing leaving the contents untouched.
The untar code now shares the common header parsing and initial processing
with the actual writes still separate. No changes to the IMFS have been made.
Updates #2415.
Closes #2207.
---
cpukit/libmisc/untar/untar.c | 463 ++++++++++++++++++++++++++++-----------
cpukit/libmisc/untar/untar.h | 7 +-
testsuites/libtests/tar01/init.c | 9 +-
3 files changed, 349 insertions(+), 130 deletions(-)
diff --git a/cpukit/libmisc/untar/untar.c b/cpukit/libmisc/untar/untar.c
index e4b0aeb..f6f4f0c 100644
--- a/cpukit/libmisc/untar/untar.c
+++ b/cpukit/libmisc/untar/untar.c
@@ -13,7 +13,9 @@
/*
* Written by: Jake Janovetz <janovetz at tempest.ece.uiuc.edu>
-
+ *
+ * Copyright 2016 Chris Johns <chrisj at rtems.org>
+ *
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
@@ -23,6 +25,7 @@
#include "config.h"
#endif
+#include <stdbool.h>
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
@@ -64,14 +67,16 @@
* sum = 0;
* for(i = 0; i < 512; i++)
* sum += 0xFF & header[i];
- */
+ */
#define MAX_NAME_FIELD_SIZE 99
/*
* 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(
const char *octascii,
@@ -92,6 +97,244 @@ _rtems_octal2ulong(
}
/*
+ * 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
*
* Description:
@@ -114,28 +357,26 @@ _rtems_octal2ulong(
*
*/
int
-Untar_FromMemory(
- void *tar_buf,
- size_t size
+Untar_FromMemory_Print(
+ void *tar_buf,
+ size_t size,
+ const rtems_printer *printer
)
{
FILE *fp;
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;
break;
@@ -144,56 +385,31 @@ Untar_FromMemory(
/* Read the header */
bufr = &tar_ptr[ptr];
ptr += 512;
- if (strncmp(&bufr[257], "ustar", 5)) {
- 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);
+ retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
- /*
- * 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;
+ if (retval != UNTAR_SUCCESSFUL)
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;
}
@@ -202,29 +418,42 @@ Untar_FromMemory(
}
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);
+ return retval;
+}
+
+/*
+ * Function: Untar_FromMemory
+ *
+ * Description:
+ *
+ * This is a simple subroutine used to rip links, directories, and
+ * files out of a block of memory.
+ *
+ *
+ * Inputs:
+ *
+ * void * tar_buf - Pointer to TAR buffer.
+ * size_t size - Length of TAR buffer.
+ *
+ *
+ * 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_FromMemory(
+ void *tar_buf,
+ size_t size
+)
+{
+ return Untar_FromMemory_Print(tar_buf, size, false);
}
/*
@@ -246,21 +475,19 @@ Untar_FromMemory(
* UNTAR_INVALID_HEADER for an invalid header.
*/
int
-Untar_FromFile(
- const char *tar_name
+Untar_FromFile_Print(
+ const char *tar_name,
+ const rtems_printer *printer
)
{
int fd;
char *bufr;
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;
retval = UNTAR_SUCCESSFUL;
@@ -282,78 +509,62 @@ Untar_FromFile(
break;
}
- if (strncmp(&bufr[257], "ustar", 5)) {
- 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);
+ retval = Untar_ProcessHeader(bufr, fname, &file_size, &nblocks, &linkflag, printer);
- if (sum != hdr_chksum) {
- retval = UNTAR_INVALID_CHECKSUM;
+ if (retval != UNTAR_SUCCESSFUL)
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;
/*
* Read out the data. There are nblocks of data where nblocks
* 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);
}
/*
diff --git a/cpukit/libmisc/untar/untar.h b/cpukit/libmisc/untar/untar.h
index c1f71dc..d67c29e 100644
--- a/cpukit/libmisc/untar/untar.h
+++ b/cpukit/libmisc/untar/untar.h
@@ -2,7 +2,7 @@
* @file
*
* @brief Untar an Image
- *
+ *
* This file defines the interface to methods which can untar an image.
*/
@@ -17,9 +17,12 @@
#ifndef _RTEMS_UNTAR_H
#define _RTEMS_UNTAR_H
+#include <stdbool.h>
#include <stddef.h>
#include <tar.h>
+#include <rtems/print.h>
+
/**
* @defgroup libmisc_untar_img Untar Image
*
@@ -37,7 +40,9 @@ extern "C" {
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);
/**************************************************************************
* This converts octal ASCII number representations into an
diff --git a/testsuites/libtests/tar01/init.c b/testsuites/libtests/tar01/init.c
index 8ff107e..54760e9 100644
--- a/testsuites/libtests/tar01/init.c
+++ b/testsuites/libtests/tar01/init.c
@@ -44,9 +44,12 @@ void test_cat(
void test_untar_from_memory(void)
{
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));
exit(1);
@@ -56,7 +59,7 @@ void test_untar_from_memory(void)
/******************/
printf( "========= /home/test_file =========\n" );
test_cat( "/home/test_file", 0, 0 );
-
+
/******************/
printf( "========= /symlink =========\n" );
test_cat( "/symlink", 0, 0 );
@@ -97,7 +100,7 @@ void test_untar_from_file(void)
/******************/
printf( "========= /dest/home/test_file =========\n" );
test_cat( "/dest/home/test_file", 0, 0 );
-
+
/******************/
printf( "========= /dest/symlink =========\n" );
test_cat( "/dest/symlink", 0, 0 );
More information about the vc
mailing list