source: rtems/cpukit/libmisc/untar/untar.c

Last change on this file was 4551f5f, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 26, 2019 at 7:07:55 AM

untar: Properly make parent path

Close #3823.

  • Property mode set to 100644
File size: 13.9 KB
Line 
1/**
2 * @file
3 *
4 * @brief Untar an Image
5 *
6 * @ingroup libmisc_untar_img Untar Image
7 */
8
9/*
10 *  Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
11 *
12 *  Copyright 2016 Chris Johns <chrisj@rtems.org>
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <stdbool.h>
24#include <sys/param.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <errno.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <rtems/untar.h>
33#include <rtems/bspIo.h>
34
35/*
36 * TAR file format:
37
38 *   Offset   Length   Contents
39 *     0    100 bytes  File name ('\0' terminated, 99 maxmum length)
40 *   100      8 bytes  File mode (in octal ascii)
41 *   108      8 bytes  User ID (in octal ascii)
42 *   116      8 bytes  Group ID (in octal ascii)
43 *   124     12 bytes  File size (s) (in octal ascii)
44 *   136     12 bytes  Modify time (in octal ascii)
45 *   148      8 bytes  Header checksum (in octal ascii)
46 *   156      1 bytes  Link flag
47 *   157    100 bytes  Linkname ('\0' terminated, 99 maxmum length)
48 *   257      8 bytes  Magic PAX ("ustar\0" + 2 bytes padding)
49 *   257      8 bytes  Magic GNU tar ("ustar  \0")
50 *   265     32 bytes  User name ('\0' terminated, 31 maxmum length)
51 *   297     32 bytes  Group name ('\0' terminated, 31 maxmum length)
52 *   329      8 bytes  Major device ID (in octal ascii)
53 *   337      8 bytes  Minor device ID (in octal ascii)
54 *   345    155 bytes  Prefix
55 *   512   (s+p)bytes  File contents (s+p) := (((s) + 511) & ~511),
56 *                     round up to 512 bytes
57 *
58 *   Checksum:
59 *   int i, sum;
60 *   char* header = tar_header_pointer;
61 *   sum = 0;
62 *   for(i = 0; i < 512; i++)
63 *       sum += 0xFF & header[i];
64 */
65
66#define MAX_NAME_FIELD_SIZE      99
67
68static int _rtems_tar_header_checksum(const char *bufr);
69
70/*
71 * This converts octal ASCII number representations into an
72 * unsigned long.  Only support 32-bit numbers for now.
73 */
74static unsigned long
75_rtems_octal2ulong(
76  const char *octascii,
77  size_t len
78)
79{
80  size_t        i;
81  unsigned long num;
82
83  num = 0;
84  for (i=0; i < len; i++) {
85    if ((octascii[i] < '0') || (octascii[i] > '9')) {
86      continue;
87    }
88    num  = num * 8 + ((unsigned long)(octascii[i] - '0'));
89  }
90  return(num);
91}
92
93/*
94 * Common error message formatter.
95 */
96static void
97Print_Error(const rtems_printer *printer, const char* message, const char* path)
98{
99  rtems_printf(printer, "untar: %s: %s: (%d) %s\n",
100               message, path, errno, strerror(errno));
101}
102
103/*
104 * Make the directory path for a file if it does not exist.
105 */
106static int
107Make_Path(const rtems_printer *printer, char *path)
108{
109  char *p;
110
111  /*
112   * Skip leading path separators.
113   */
114  while (*path == '/') {
115    ++path;
116  }
117
118  p = path;
119
120  for (; ; ++p) {
121    if (p[0] == '\0') {
122      return 0;
123    } else if (p[0] != '/') {
124      continue;
125    }
126
127    *p = '\0';
128    if (p[1] == '\0') {
129      /* Speculatively unlink the last component so that it can be re-created */
130      unlink(path);
131      return 0;
132    }
133
134    if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
135      if (errno == EEXIST || errno == EISDIR) {
136        struct stat sb;
137
138        if (stat(path, &sb) != 0) {
139          return -1;
140        }
141
142        if (!S_ISDIR(sb.st_mode)) {
143          if (unlink(path) != 0) {
144            Print_Error(printer, "unlink", path);
145            return -1;
146          }
147
148          if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
149            Print_Error(printer, "mkdir (unlink)", path);
150            return -1;
151          }
152        }
153      }
154    }
155
156    *p = '/';
157  }
158
159  return 0;
160}
161
162int
163Untar_ProcessHeader(
164  Untar_HeaderContext *ctx,
165  const char          *bufr
166)
167{
168  int sum;
169  int hdr_chksum;
170  int retval = UNTAR_SUCCESSFUL;
171  int r;
172
173  ctx->file_name[0] = '\0';
174  ctx->file_size = 0;
175  ctx->nblocks = 0;
176  ctx->linkflag = -1;
177
178  if (strncmp(&bufr[257], "ustar", 5)) {
179    return UNTAR_SUCCESSFUL;
180  }
181
182  /*
183   * Compute the TAR checksum and check with the value in the archive.  The
184   * checksum is computed over the entire header, but the checksum field is
185   * substituted with blanks.
186   */
187  hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
188  sum        = _rtems_tar_header_checksum(bufr);
189
190  if (sum != hdr_chksum) {
191    rtems_printf(ctx->printer, "untar: file header checksum error\n");
192    return UNTAR_INVALID_CHECKSUM;
193  }
194
195  strlcpy(ctx->file_name, bufr, UNTAR_FILE_NAME_SIZE);
196
197  ctx->mode = strtoul(&bufr[100], NULL, 8);
198
199  ctx->linkflag   = bufr[156];
200  ctx->file_size = _rtems_octal2ulong(&bufr[124], 12);
201
202  /*
203   * We've decoded the header, now figure out what it contains and do something
204   * with it.
205   */
206
207  if (Make_Path(ctx->printer, ctx->file_path) != 0) {
208    retval = UNTAR_FAIL;
209  }
210
211  if (ctx->linkflag == SYMTYPE) {
212    strlcpy(ctx->link_name, &bufr[157], sizeof(ctx->link_name));
213    rtems_printf(ctx->printer, "untar: symlink: %s -> %s\n",
214                 ctx->link_name, ctx->file_path);
215    r = symlink(ctx->link_name, ctx->file_path);
216    if (r != 0) {
217      Print_Error(ctx->printer, "symlink", ctx->file_path);
218      retval = UNTAR_FAIL;
219    }
220  } else if (ctx->linkflag == REGTYPE) {
221    rtems_printf(ctx->printer, "untar: file: %s (s:%lu,m:%04lo)\n",
222                 ctx->file_path, ctx->file_size, ctx->mode);
223    ctx->nblocks = (((ctx->file_size) + 511) & ~511) / 512;
224  } else if (ctx->linkflag == DIRTYPE) {
225    rtems_printf(ctx->printer, "untar: dir: %s\n", ctx->file_path);
226    r = mkdir(ctx->file_path, ctx->mode);
227    if (r != 0) {
228      Print_Error(ctx->printer, "mkdir", ctx->file_path);
229      retval = UNTAR_FAIL;
230    }
231  }
232
233  return retval;
234}
235
236/*
237 * Function: Untar_FromMemory
238 *
239 * Description:
240 *
241 *    This is a simple subroutine used to rip links, directories, and
242 *    files out of a block of memory.
243 *
244 *
245 * Inputs:
246 *
247 *    void *  tar_buf    - Pointer to TAR buffer.
248 *    size_t  size       - Length of TAR buffer.
249 *
250 *
251 * Output:
252 *
253 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
254 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
255 *          UNTAR_INVALID_HEADER    for an invalid header.
256 *
257 */
258int
259Untar_FromMemory_Print(
260  void                *tar_buf,
261  size_t               size,
262  const rtems_printer *printer
263)
264{
265  int                  fd;
266  const char          *tar_ptr = (const char *)tar_buf;
267  const char          *bufr;
268  char                 buf[UNTAR_FILE_NAME_SIZE];
269  Untar_HeaderContext  ctx;
270  int                  retval = UNTAR_SUCCESSFUL;
271  unsigned long        ptr;
272
273  ctx.file_path = buf;
274  ctx.file_name = buf;
275  ctx.printer = printer;
276  rtems_printf(printer, "untar: memory at %p (%zu)\n", tar_buf, size);
277
278  ptr = 0;
279  while (true) {
280    if (ptr + 512 > size) {
281      retval = UNTAR_SUCCESSFUL;
282      break;
283    }
284
285    /* Read the header */
286    bufr = &tar_ptr[ptr];
287    ptr += 512;
288
289    retval = Untar_ProcessHeader(&ctx, bufr);
290
291    if (retval != UNTAR_SUCCESSFUL)
292      break;
293
294    if (ctx.linkflag == REGTYPE) {
295      if ((fd = open(ctx.file_path,
296                     O_TRUNC | O_CREAT | O_WRONLY, ctx.mode)) == -1) {
297        Print_Error(printer, "open", ctx.file_path);
298        ptr += 512 * ctx.nblocks;
299      } else {
300        unsigned long sizeToGo = ctx.file_size;
301        ssize_t       len;
302        ssize_t       i;
303        ssize_t       n;
304
305        /*
306         * Read out the data.  There are nblocks of data where nblocks is the
307         * file_size rounded to the nearest 512-byte boundary.
308         */
309        for (i = 0; i < ctx.nblocks; i++) {
310          len = ((sizeToGo < 512L) ? (sizeToGo) : (512L));
311          n = write(fd, &tar_ptr[ptr], len);
312          if (n != len) {
313            Print_Error(printer, "write", ctx.file_path);
314            retval  = UNTAR_FAIL;
315            break;
316          }
317          ptr += 512;
318          sizeToGo -= n;
319        }
320        close(fd);
321      }
322
323    }
324  }
325
326  return retval;
327}
328
329/*
330 * Function: Untar_FromMemory
331 *
332 * Description:
333 *
334 *    This is a simple subroutine used to rip links, directories, and
335 *    files out of a block of memory.
336 *
337 *
338 * Inputs:
339 *
340 *    void *  tar_buf    - Pointer to TAR buffer.
341 *    size_t  size       - Length of TAR buffer.
342 *
343 *
344 * Output:
345 *
346 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
347 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
348 *          UNTAR_INVALID_HEADER    for an invalid header.
349 *
350 */
351int
352Untar_FromMemory(
353  void   *tar_buf,
354  size_t  size
355)
356{
357  return Untar_FromMemory_Print(tar_buf, size, false);
358}
359
360/*
361 * Function: Untar_FromFile
362 *
363 * Description:
364 *
365 *    This is a simple subroutine used to rip links, directories, and
366 *    files out of a TAR file.
367 *
368 * Inputs:
369 *
370 *    const char *tar_name   - TAR filename.
371 *
372 * Output:
373 *
374 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
375 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
376 *          UNTAR_INVALID_HEADER    for an invalid header.
377 */
378int
379Untar_FromFile_Print(
380  const char          *tar_name,
381  const rtems_printer *printer
382)
383{
384  int                  fd;
385  char                *bufr;
386  ssize_t              n;
387  int                  retval;
388  unsigned long        i;
389  char                 buf[UNTAR_FILE_NAME_SIZE];
390  Untar_HeaderContext  ctx;
391
392  retval = UNTAR_SUCCESSFUL;
393
394  if ((fd = open(tar_name, O_RDONLY)) < 0) {
395    return UNTAR_FAIL;
396  }
397
398  bufr = (char *)malloc(512);
399  if (bufr == NULL) {
400    close(fd);
401    return(UNTAR_FAIL);
402  }
403
404  ctx.file_path = buf;
405  ctx.file_name = buf;
406  ctx.printer = printer;
407
408  while (1) {
409    /* Read the header */
410    /* If the header read fails, we just consider it the end of the tarfile. */
411    if ((n = read(fd, bufr, 512)) != 512) {
412      break;
413    }
414
415    retval = Untar_ProcessHeader(&ctx, bufr);
416
417    if (retval != UNTAR_SUCCESSFUL)
418      break;
419
420    if (ctx.linkflag == REGTYPE) {
421      int out_fd;
422
423      /*
424       * Read out the data.  There are nblocks of data where nblocks
425       * is the size rounded to the nearest 512-byte boundary.
426       */
427
428      if ((out_fd = creat(ctx.file_path, ctx.mode)) == -1) {
429        (void) lseek(fd, SEEK_CUR, 512UL * ctx.nblocks);
430      } else {
431        for (i = 0; i < ctx.nblocks; i++) {
432          n = read(fd, bufr, 512);
433          n = MIN(n, ctx.file_size - (i * 512UL));
434          (void) write(out_fd, bufr, n);
435        }
436        close(out_fd);
437      }
438    }
439  }
440
441  free(bufr);
442  close(fd);
443
444  return retval;
445}
446
447
448void Untar_ChunkContext_Init(Untar_ChunkContext *context)
449{
450  context->base.file_path = context->buf;
451  context->base.file_name = context->buf;
452  context->state = UNTAR_CHUNK_HEADER;
453  context->done_bytes = 0;
454  context->out_fd = -1;
455}
456
457int Untar_FromChunk_Print(
458  Untar_ChunkContext *context,
459  void *chunk,
460  size_t chunk_size,
461  const rtems_printer* printer
462)
463{
464  char *buf;
465  size_t done;
466  size_t todo;
467  size_t remaining;
468  size_t consume;
469  int retval;
470
471  buf = chunk;
472  done = 0;
473  todo = chunk_size;
474
475  context->base.printer = printer;
476
477  while (todo > 0) {
478    switch (context->state) {
479      case UNTAR_CHUNK_HEADER:
480        remaining = 512 - context->done_bytes;
481        consume = MIN(remaining, todo);
482        memcpy(&context->header[context->done_bytes], &buf[done], consume);
483        context->done_bytes += consume;
484
485        if (context->done_bytes == 512) {
486          retval = Untar_ProcessHeader(
487            &context->base,
488            &context->header[0]
489          );
490
491          if (retval != UNTAR_SUCCESSFUL) {
492            context->state = UNTAR_CHUNK_ERROR;
493            return retval;
494          }
495
496          if (context->base.linkflag == REGTYPE) {
497            context->out_fd = creat(context->base.file_path,
498                                    context->base.mode);
499
500            if (context->out_fd >= 0) {
501              context->state = UNTAR_CHUNK_WRITE;
502              context->done_bytes = 0;
503            } else {
504              context->state = UNTAR_CHUNK_SKIP;
505              context->base.file_size = 512 * context->base.nblocks;
506              context->done_bytes = 0;
507            }
508          } else {
509              context->done_bytes = 0;
510          }
511        }
512
513        break;
514      case UNTAR_CHUNK_SKIP:
515        remaining = context->base.file_size - context->done_bytes;
516        consume = MIN(remaining, todo);
517        context->done_bytes += consume;
518
519        if (context->done_bytes == context->base.file_size) {
520          context->state = UNTAR_CHUNK_HEADER;
521          context->done_bytes = 0;
522        }
523
524        break;
525      case UNTAR_CHUNK_WRITE:
526        remaining = context->base.file_size - context->done_bytes;
527        consume = MIN(remaining, todo);
528        write(context->out_fd, &buf[done], consume);
529        context->done_bytes += consume;
530
531        if (context->done_bytes == context->base.file_size) {
532          close(context->out_fd);
533          context->out_fd = -1;
534          context->state = UNTAR_CHUNK_SKIP;
535          context->base.file_size = 512 * context->base.nblocks
536            - context->base.file_size;
537          context->done_bytes = 0;
538        }
539
540        break;
541      default:
542        return UNTAR_FAIL;
543    }
544
545    done += consume;
546    todo -= consume;
547  }
548
549  return UNTAR_SUCCESSFUL;
550}
551
552/*
553 * Function: Untar_FromFile
554 *
555 * Description:
556 *
557 *    This is a simple subroutine used to rip links, directories, and
558 *    files out of a TAR file.
559 *
560 * Inputs:
561 *
562 *    const char *tar_name   - TAR filename.
563 *
564 * Output:
565 *
566 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
567 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
568 *          UNTAR_INVALID_HEADER    for an invalid header.
569 */
570int
571Untar_FromFile(
572  const char *tar_name
573)
574{
575  return Untar_FromFile_Print(tar_name, NULL);
576}
577
578/*
579 * Compute the TAR checksum and check with the value in
580 * the archive.  The checksum is computed over the entire
581 * header, but the checksum field is substituted with blanks.
582 */
583static int
584_rtems_tar_header_checksum(
585  const char *bufr
586)
587{
588  int  i, sum;
589
590  sum = 0;
591  for (i=0; i<512; i++) {
592    if ((i >= 148) && (i < 156))
593      sum += 0xff & ' ';
594    else
595     sum += 0xff & bufr[i];
596  }
597  return(sum);
598}
Note: See TracBrowser for help on using the repository browser.