source: rtems/cpukit/libmisc/untar/untar.c @ 9394aa5e

5
Last change on this file since 9394aa5e was 9394aa5e, checked in by Pavel Pisa <pisa@…>, on 11/23/15 at 09:09:06

untar: do not exit with error when created directory already exists.

The problem exists for both RTEMS untar implementations and their
variants: Untar_FromMemory(), Untar_FromFile() and rtems_tarfs_load().

If filesystem object already exists at extracted directory path
then if it is directory, creation is ignored. Attempt
to delete/unlink object and make directory is tried for other cases.

This simple approach problem reported in ticket fixes #2413.
Behavior follows GNU tar and BSD tar practice for directories
but much more work is required to achieve full semantics
of the full featured tar implementation still.

  • Property mode set to 100644
File size: 9.3 KB
Line 
1/**
2 * @file
3
4 * @brief Untar an Image
5 * @ingroup libmisc_untar_img Untar Image
6
7 * FIXME:
8 *   1. Symbolic links are not created.
9 *   2. Untar_FromMemory uses FILE *fp.
10 *   3. How to determine end of archive?
11
12 */
13
14/*
15 *  Written by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
16
17 *  The license and distribution terms for this file may be
18 *  found in the file LICENSE in this distribution or at
19 *  http://www.rtems.org/license/LICENSE.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include <sys/param.h>
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <rtems/untar.h>
35#include <rtems/bspIo.h>
36
37
38/*
39 * TAR file format:
40
41 *   Offset   Length   Contents
42 *     0    100 bytes  File name ('\0' terminated, 99 maxmum length)
43 *   100      8 bytes  File mode (in octal ascii)
44 *   108      8 bytes  User ID (in octal ascii)
45 *   116      8 bytes  Group ID (in octal ascii)
46 *   124     12 bytes  File size (s) (in octal ascii)
47 *   136     12 bytes  Modify time (in octal ascii)
48 *   148      8 bytes  Header checksum (in octal ascii)
49 *   156      1 bytes  Link flag
50 *   157    100 bytes  Linkname ('\0' terminated, 99 maxmum length)
51 *   257      8 bytes  Magic PAX ("ustar\0" + 2 bytes padding)
52 *   257      8 bytes  Magic GNU tar ("ustar  \0")
53 *   265     32 bytes  User name ('\0' terminated, 31 maxmum length)
54 *   297     32 bytes  Group name ('\0' terminated, 31 maxmum length)
55 *   329      8 bytes  Major device ID (in octal ascii)
56 *   337      8 bytes  Minor device ID (in octal ascii)
57 *   345    155 bytes  Prefix
58 *   512   (s+p)bytes  File contents (s+p) := (((s) + 511) & ~511),
59 *                     round up to 512 bytes
60 *
61 *   Checksum:
62 *   int i, sum;
63 *   char* header = tar_header_pointer;
64 *   sum = 0;
65 *   for(i = 0; i < 512; i++)
66 *       sum += 0xFF & header[i];
67 */
68
69#define MAX_NAME_FIELD_SIZE      99
70
71/*
72 * This converts octal ASCII number representations into an
73 * unsigned long.  Only support 32-bit numbers for now.
74 */
75unsigned long
76_rtems_octal2ulong(
77  const char *octascii,
78  size_t len
79)
80{
81  size_t        i;
82  unsigned long num;
83
84  num = 0;
85  for (i=0; i < len; i++) {
86    if ((octascii[i] < '0') || (octascii[i] > '9')) {
87      continue;
88    }
89    num  = num * 8 + ((unsigned long)(octascii[i] - '0'));
90  }
91  return(num);
92}
93
94/*
95 * Function: Untar_FromMemory
96 *
97 * Description:
98 *
99 *    This is a simple subroutine used to rip links, directories, and
100 *    files out of a block of memory.
101 *
102 *
103 * Inputs:
104 *
105 *    void *  tar_buf    - Pointer to TAR buffer.
106 *    size_t  size       - Length of TAR buffer.
107 *
108 *
109 * Output:
110 *
111 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
112 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
113 *          UNTAR_INVALID_HEADER    for an invalid header.
114 *
115 */
116int
117Untar_FromMemory(
118  void   *tar_buf,
119  size_t  size
120)
121{
122  FILE           *fp;
123  const char     *tar_ptr = (const char *)tar_buf;
124  const char     *bufr;
125  size_t         n;
126  char           fname[100];
127  char           linkname[100];
128  int            sum;
129  int            hdr_chksum;
130  int            retval;
131  unsigned long  ptr;
132  unsigned long  i;
133  unsigned long  nblocks;
134  unsigned long  file_size;
135  unsigned char  linkflag;
136
137  ptr = 0;
138  while (1) {
139    if (ptr + 512 > size) {
140      retval = UNTAR_SUCCESSFUL;
141      break;
142    }
143
144    /* Read the header */
145    bufr = &tar_ptr[ptr];
146    ptr += 512;
147    if (strncmp(&bufr[257], "ustar", 5)) {
148      retval = UNTAR_SUCCESSFUL;
149      break;
150    }
151
152    strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
153    fname[MAX_NAME_FIELD_SIZE] = '\0';
154
155    linkflag   = bufr[156];
156    file_size  = _rtems_octal2ulong(&bufr[124], 12);
157
158    /*
159     * Compute the TAR checksum and check with the value in
160     * the archive.  The checksum is computed over the entire
161     * header, but the checksum field is substituted with blanks.
162     */
163    hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
164    sum = _rtems_tar_header_checksum(bufr);
165
166    if (sum != hdr_chksum) {
167      retval = UNTAR_INVALID_CHECKSUM;
168      break;
169    }
170
171    /*
172     * We've decoded the header, now figure out what it contains and
173     * do something with it.
174     */
175    if (linkflag == SYMTYPE) {
176      strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
177      linkname[MAX_NAME_FIELD_SIZE] = '\0';
178      symlink(linkname, fname);
179    } else if (linkflag == REGTYPE) {
180      nblocks = (((file_size) + 511) & ~511) / 512;
181      if ((fp = fopen(fname, "w")) == NULL) {
182        printk("Untar: failed to create file %s\n", fname);
183        ptr += 512 * nblocks;
184      } else {
185        unsigned long sizeToGo = file_size;
186        size_t len;
187
188        /*
189         * Read out the data.  There are nblocks of data where nblocks
190         * is the file_size rounded to the nearest 512-byte boundary.
191         */
192        for (i=0; i<nblocks; i++) {
193          len = ((sizeToGo < 512L)?(sizeToGo):(512L));
194          n = fwrite(&tar_ptr[ptr], 1, len, fp);
195          if (n != len) {
196            printk("untar: Error during write\n");
197            retval  = UNTAR_FAIL;
198            break;
199          }
200          ptr += 512;
201          sizeToGo -= n;
202        }
203        fclose(fp);
204      }
205    } else if (linkflag == DIRTYPE) {
206      if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
207        if (errno == EEXIST) {
208          struct stat stat_buf;
209          if ( stat(fname, &stat_buf) == 0 ) {
210            if (  S_ISDIR(stat_buf.st_mode) ) {
211              continue;
212            } else {
213              if ( unlink(fname) != -1 ) {
214                if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
215                  continue;
216              }
217            }
218          }
219        }
220        printk("Untar: failed to create directory %s\n", fname);
221        retval = UNTAR_FAIL;
222        break;
223      }
224    }
225  }
226
227  return(retval);
228}
229
230/*
231 * Function: Untar_FromFile
232 *
233 * Description:
234 *
235 *    This is a simple subroutine used to rip links, directories, and
236 *    files out of a TAR file.
237 *
238 * Inputs:
239 *
240 *    const char *tar_name   - TAR filename.
241 *
242 * Output:
243 *
244 *    int - UNTAR_SUCCESSFUL (0)    on successful completion.
245 *          UNTAR_INVALID_CHECKSUM  for an invalid header checksum.
246 *          UNTAR_INVALID_HEADER    for an invalid header.
247 */
248int
249Untar_FromFile(
250  const char *tar_name
251)
252{
253  int            fd;
254  char           *bufr;
255  ssize_t        n;
256  char           fname[100];
257  char           linkname[100];
258  int            sum;
259  int            hdr_chksum;
260  int            retval;
261  unsigned long  i;
262  unsigned long  nblocks;
263  unsigned long  size;
264  unsigned char  linkflag;
265
266  retval = UNTAR_SUCCESSFUL;
267
268  if ((fd = open(tar_name, O_RDONLY)) < 0) {
269    return UNTAR_FAIL;
270  }
271
272  bufr = (char *)malloc(512);
273  if (bufr == NULL) {
274    close(fd);
275    return(UNTAR_FAIL);
276  }
277
278  while (1) {
279    /* Read the header */
280    /* If the header read fails, we just consider it the end of the tarfile. */
281    if ((n = read(fd, bufr, 512)) != 512) {
282      break;
283    }
284
285    if (strncmp(&bufr[257], "ustar", 5)) {
286      break;
287    }
288
289    strncpy(fname, bufr, MAX_NAME_FIELD_SIZE);
290    fname[MAX_NAME_FIELD_SIZE] = '\0';
291
292    linkflag   = bufr[156];
293    size       = _rtems_octal2ulong(&bufr[124], 12);
294
295    /*
296     * Compute the TAR checksum and check with the value in
297     * the archive.  The checksum is computed over the entire
298     * header, but the checksum field is substituted with blanks.
299     */
300    hdr_chksum = _rtems_octal2ulong(&bufr[148], 8);
301    sum = _rtems_tar_header_checksum(bufr);
302
303    if (sum != hdr_chksum) {
304      retval = UNTAR_INVALID_CHECKSUM;
305      break;
306    }
307
308    /*
309     * We've decoded the header, now figure out what it contains and
310     * do something with it.
311     */
312    if (linkflag == SYMTYPE) {
313      strncpy(linkname, &bufr[157], MAX_NAME_FIELD_SIZE);
314      linkname[MAX_NAME_FIELD_SIZE] = '\0';
315      symlink(linkname,fname);
316    } else if (linkflag == REGTYPE) {
317      int out_fd;
318
319      /*
320       * Read out the data.  There are nblocks of data where nblocks
321       * is the size rounded to the nearest 512-byte boundary.
322       */
323      nblocks = (((size) + 511) & ~511) / 512;
324
325      if ((out_fd = creat(fname, 0644)) == -1) {
326        (void) lseek(fd, SEEK_CUR, 512 * nblocks);
327      } else {
328        for (i=0; i<nblocks; i++) {
329          n = read(fd, bufr, 512);
330          n = MIN(n, size - i*512);
331          (void) write(out_fd, bufr, n);
332        }
333        close(out_fd);
334      }
335    } else if (linkflag == DIRTYPE) {
336      if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) != 0 ) {
337        if (errno == EEXIST) {
338          struct stat stat_buf;
339          if ( stat(fname, &stat_buf) == 0 ) {
340            if (  S_ISDIR(stat_buf.st_mode) ) {
341              continue;
342            } else {
343              if ( unlink(fname) != -1 ) {
344                if ( mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0 )
345                  continue;
346              }
347            }
348          }
349        }
350      }
351    }
352  }
353  free(bufr);
354  close(fd);
355
356  return(retval);
357}
358
359/*
360 * Compute the TAR checksum and check with the value in
361 * the archive.  The checksum is computed over the entire
362 * header, but the checksum field is substituted with blanks.
363 */
364int
365_rtems_tar_header_checksum(
366  const char *bufr
367)
368{
369  int  i, sum;
370
371  sum = 0;
372  for (i=0; i<512; i++) {
373    if ((i >= 148) && (i < 156))
374      sum += 0xff & ' ';
375    else
376     sum += 0xff & bufr[i];
377  }
378  return(sum);
379}
Note: See TracBrowser for help on using the repository browser.