source: rtems/cpukit/libmisc/untar/untar.c @ 5dff7425

4.115
Last change on this file since 5dff7425 was 11925eef, checked in by Sebastian Huber <sebastian.huber@…>, on 11/21/14 at 07:49:57

Delete or rename MIN/MAX macros and defines

Include <sys/param.h> if necessary to get the MIN()/MAX() macros.

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