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

4.115
Last change on this file since d9bc968d was d9bc968d, checked in by Joel Sherrill <joel.sherrill@…>, on 11/07/11 at 21:42:22

2011-11-07 Ralf Corsepius <ralf.corsepius@…>

PR 1952/cpukit

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