summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/untar/untar.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libmisc/untar/untar.c')
-rw-r--r--cpukit/libmisc/untar/untar.c135
1 files changed, 92 insertions, 43 deletions
diff --git a/cpukit/libmisc/untar/untar.c b/cpukit/libmisc/untar/untar.c
index a2f09fb99f..fff737db68 100644
--- a/cpukit/libmisc/untar/untar.c
+++ b/cpukit/libmisc/untar/untar.c
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
/**
* @file
*
@@ -11,9 +13,26 @@
*
* 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
- * http://www.rtems.org/license/LICENSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
@@ -65,6 +84,14 @@
#define MAX_NAME_FIELD_SIZE 99
+#define TAR_BLOCK_SIZE 512
+#define TAR_BLOCK_SIZE_MASK (TAR_BLOCK_SIZE - 1)
+
+/*
+ * Number of blocks read or written
+ */
+#define TAR_WORK_BLOCKS 16
+
static int _rtems_tar_header_checksum(const char *bufr);
/*
@@ -126,30 +153,25 @@ Make_Path(const rtems_printer *printer, char *path)
*p = '\0';
if (p[1] == '\0') {
- /* Speculatively unlink the last component so that it can be re-created */
- unlink(path);
return 0;
}
if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
- if (errno == EEXIST || errno == EISDIR) {
+ if (errno == EEXIST) {
+ /* If it exists already: Check whether it is a directory */
struct stat sb;
-
- if (stat(path, &sb) != 0) {
+ if (lstat(path, &sb) != 0) {
+ Print_Error(printer, "lstat", path);
+ return -1;
+ } else if (!S_ISDIR(sb.st_mode)) {
+ rtems_printf(printer,
+ "untar: mkdir: %s: exists but is not a directory\n",
+ path);
return -1;
}
-
- if (!S_ISDIR(sb.st_mode)) {
- if (unlink(path) != 0) {
- Print_Error(printer, "unlink", path);
- return -1;
- }
-
- if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
- Print_Error(printer, "mkdir (unlink)", path);
- return -1;
- }
- }
+ } else {
+ Print_Error(printer, "mkdir", path);
+ return -1;
}
}
@@ -206,6 +228,12 @@ Untar_ProcessHeader(
if (Make_Path(ctx->printer, ctx->file_path) != 0) {
retval = UNTAR_FAIL;
+ } else {
+ /*
+ * Speculatively unlink. This should unlink everything but non-empty
+ * directories or write protected stuff.
+ */
+ unlink(ctx->file_path);
}
if (ctx->linkflag == SYMTYPE) {
@@ -220,13 +248,28 @@ Untar_ProcessHeader(
} else if (ctx->linkflag == REGTYPE) {
rtems_printf(ctx->printer, "untar: file: %s (s:%lu,m:%04lo)\n",
ctx->file_path, ctx->file_size, ctx->mode);
- ctx->nblocks = (((ctx->file_size) + 511) & ~511) / 512;
+ ctx->nblocks =
+ (((ctx->file_size) + TAR_BLOCK_SIZE_MASK) & ~TAR_BLOCK_SIZE_MASK) / TAR_BLOCK_SIZE;
} else if (ctx->linkflag == DIRTYPE) {
rtems_printf(ctx->printer, "untar: dir: %s\n", ctx->file_path);
r = mkdir(ctx->file_path, ctx->mode);
if (r != 0) {
- Print_Error(ctx->printer, "mkdir", ctx->file_path);
- retval = UNTAR_FAIL;
+ if (errno == EEXIST) {
+ /* If it exists already: Check whether it is a directory */
+ struct stat sb;
+ if (lstat(ctx->file_path, &sb) != 0) {
+ Print_Error(ctx->printer, "lstat", ctx->file_path);
+ retval = UNTAR_FAIL;
+ } else if (!S_ISDIR(sb.st_mode)) {
+ rtems_printf(ctx->printer,
+ "untar: mkdir: %s: exists but is not a directory\n",
+ ctx->file_path);
+ retval = UNTAR_FAIL;
+ }
+ } else {
+ Print_Error(ctx->printer, "mkdir", ctx->file_path);
+ retval = UNTAR_FAIL;
+ }
}
}
@@ -277,14 +320,14 @@ Untar_FromMemory_Print(
ptr = 0;
while (true) {
- if (ptr + 512 > size) {
+ if (ptr + TAR_BLOCK_SIZE > size) {
retval = UNTAR_SUCCESSFUL;
break;
}
/* Read the header */
bufr = &tar_ptr[ptr];
- ptr += 512;
+ ptr += TAR_BLOCK_SIZE;
retval = Untar_ProcessHeader(&ctx, bufr);
@@ -295,27 +338,26 @@ Untar_FromMemory_Print(
if ((fd = open(ctx.file_path,
O_TRUNC | O_CREAT | O_WRONLY, ctx.mode)) == -1) {
Print_Error(printer, "open", ctx.file_path);
- ptr += 512 * ctx.nblocks;
+ ptr += TAR_BLOCK_SIZE * ctx.nblocks;
} else {
- unsigned long sizeToGo = ctx.file_size;
- ssize_t len;
- ssize_t i;
- ssize_t n;
-
/*
* 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 < ctx.nblocks; i++) {
- len = ((sizeToGo < 512L) ? (sizeToGo) : (512L));
- n = write(fd, &tar_ptr[ptr], len);
+ ssize_t file_size = ctx.file_size;
+ size_t blocks = ctx.nblocks;
+ while (blocks > 0) {
+ size_t blks = blocks > TAR_WORK_BLOCKS ? TAR_WORK_BLOCKS : blocks;
+ ssize_t len = MIN(blks * TAR_BLOCK_SIZE, file_size);
+ ssize_t n = write(fd, &tar_ptr[ptr], len);
if (n != len) {
Print_Error(printer, "write", ctx.file_path);
retval = UNTAR_FAIL;
break;
}
- ptr += 512;
- sizeToGo -= n;
+ ptr += blks * TAR_BLOCK_SIZE;
+ file_size -= n;
+ blocks -= blks;
}
close(fd);
}
@@ -385,7 +427,6 @@ Untar_FromFile_Print(
char *bufr;
ssize_t n;
int retval;
- unsigned long i;
char buf[UNTAR_FILE_NAME_SIZE];
Untar_HeaderContext ctx;
@@ -395,7 +436,7 @@ Untar_FromFile_Print(
return UNTAR_FAIL;
}
- bufr = (char *)malloc(512);
+ bufr = (char *)malloc(TAR_WORK_BLOCKS * TAR_BLOCK_SIZE);
if (bufr == NULL) {
close(fd);
return(UNTAR_FAIL);
@@ -408,7 +449,7 @@ Untar_FromFile_Print(
while (1) {
/* Read the header */
/* If the header read fails, we just consider it the end of the tarfile. */
- if ((n = read(fd, bufr, 512)) != 512) {
+ if ((n = read(fd, bufr, TAR_BLOCK_SIZE)) != TAR_BLOCK_SIZE) {
break;
}
@@ -426,12 +467,20 @@ Untar_FromFile_Print(
*/
if ((out_fd = creat(ctx.file_path, ctx.mode)) == -1) {
- (void) lseek(fd, SEEK_CUR, 512UL * ctx.nblocks);
+ /* Couldn't create that file. Abort. */
+ retval = UNTAR_FAIL;
+ break;
} else {
- for (i = 0; i < ctx.nblocks; i++) {
- n = read(fd, bufr, 512);
- n = MIN(n, ctx.file_size - (i * 512UL));
+ ssize_t file_size = ctx.file_size;
+ size_t blocks = ctx.nblocks;
+ while (blocks > 0) {
+ size_t blks = blocks > TAR_WORK_BLOCKS ? TAR_WORK_BLOCKS : blocks;
+ size_t bytes = blks * TAR_BLOCK_SIZE;
+ n = read(fd, bufr, bytes);
+ n = MIN(n, file_size);
(void) write(out_fd, bufr, n);
+ file_size -= n;
+ blocks -= blks;
}
close(out_fd);
}