summaryrefslogtreecommitdiffstats
path: root/cpukit/libfs/src/imfs/imfs_load_tar.c
blob: 26794f9242a841cafdc3281197ae600246a1bffb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
 * $Id$
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

/**************************************************************************
 * This file implements the "mount" procedure for tar-based IMFS
 * extensions.  The TAR is not actually mounted under the IMFS.
 * Directories from the TAR file are created as usual in the IMFS.
 * File entries are created as IMFS_LINEAR_FILE nodes with their nods
 * pointing to addresses in the TAR image.
 *************************************************************************/

#include <rtems.h>
#include <rtems/libio_.h>
#include <string.h>
#include <chain.h>
#include <imfs.h>


/**************************************************************************
 * TAR file format:
 *
 *   Offset   Length   Contents
 *     0    100 bytes  File name ('\0' terminated, 99 maxmum length)
 *   100      8 bytes  File mode (in octal ascii)
 *   108      8 bytes  User ID (in octal ascii)
 *   116      8 bytes  Group ID (in octal ascii)
 *   124     12 bytes  File size (s) (in octal ascii)
 *   136     12 bytes  Modify time (in octal ascii)
 *   148      8 bytes  Header checksum (in octal ascii)
 *   156      1 bytes  Link flag
 *   157    100 bytes  Linkname ('\0' terminated, 99 maxmum length)
 *   257      8 bytes  Magic ("ustar  \0")
 *   265     32 bytes  User name ('\0' terminated, 31 maxmum length)
 *   297     32 bytes  Group name ('\0' terminated, 31 maxmum length)
 *   329      8 bytes  Major device ID (in octal ascii)
 *   337      8 bytes  Minor device ID (in octal ascii)
 *   345    167 bytes  Padding
 *   512   (s+p)bytes  File contents (s+p) := (((s) + 511) & ~511),
 *                     round up to 512 bytes
 * 
 *   Checksum:
 *   int i, sum;
 *   char* header = tar_header_pointer;
 *   sum = 0;
 *   for(i = 0; i < 512; i++)
 *       sum += 0xFF & header[i];
 *************************************************************************/

#define LF_OLDNORMAL  '\0'     /* Normal disk file, Unix compatible */
#define LF_NORMAL     '0'      /* Normal disk file                  */
#define LF_LINK       '1'      /* Link to previously dumped file    */
#define LF_SYMLINK    '2'      /* Symbolic link                     */
#define LF_CHR        '3'      /* Character special file            */
#define LF_BLK        '4'      /* Block special file                */
#define LF_DIR        '5'      /* Directory                         */
#define LF_FIFO       '6'      /* FIFO special file                 */
#define LF_CONFIG     '7'      /* Contiguous file                   */

#define MAX_NAME_FIELD_SIZE      99

#define MIN(a,b)   ((a)>(b)?(b):(a))

static unsigned long octal2ulong(char *octascii, int len);
static int compute_tar_header_checksum(char *bufr);

/**************************************************************************
 * rtems_tarfs_load
 *
 * Here we create the mountpoint directory and load the tarfs at
 * that node.  Once the IMFS has been mounted, we work through the
 * tar image and perform as follows:
 *  - For directories, simply call mkdir().  The IMFS creates nodes as
 *    needed.
 *  - For files, we make our own calls to IMFS eval_for_make and 
 *    create_node.
 *************************************************************************/
int
rtems_tarfs_load(char *mountpoint,
                 unsigned char *tar_image,
                 unsigned long tar_size)
{
   rtems_filesystem_location_info_t root_loc;
   rtems_filesystem_location_info_t loc;
   char            *hdr_ptr;
   char            filename[100];
   char            full_filename[256];
   int             hdr_chksum;
   unsigned char   linkflag;
   unsigned long   file_size;
   unsigned long   file_mode;
   int             offset;
   unsigned long   nblocks;
   IMFS_jnode_t    *node;
   int             status;


   status = rtems_filesystem_evaluate_path(mountpoint, 0, &root_loc, 0);
   if (status != 0)
      return(-1);

   if (root_loc.ops != &IMFS_ops)
      return(-1);

   /***********************************************************************
    * Create an IMFS node structure pointing to tar image memory.
    **********************************************************************/
   offset = 0;
   while (1)
   {
      if (offset + 512 > tar_size)
         break;

      /******************************************************************
       * Read a header.
       ******************************************************************/
      hdr_ptr = &tar_image[offset];
      offset += 512;
      if (strncmp(&hdr_ptr[257], "ustar  ", 7))
         break;

      strncpy(filename, hdr_ptr, MAX_NAME_FIELD_SIZE);
      filename[MAX_NAME_FIELD_SIZE] = '\0';

      linkflag   = hdr_ptr[156];
      file_mode  = octal2ulong(&hdr_ptr[100], 8);
      file_size  = octal2ulong(&hdr_ptr[124], 12);
      hdr_chksum = (int)octal2ulong(&hdr_ptr[148], 8);

      if (compute_tar_header_checksum(hdr_ptr) != hdr_chksum)
         break;

      /******************************************************************
       * Generate an IMFS node depending on the file type.
       * - For directories, just create directories as usual.  IMFS 
       *   will take care of the rest.
       * - For files, create a file node with special tarfs properties.
       *****************************************************************/
      if (linkflag == LF_DIR)
      {
         strcpy(full_filename, mountpoint);
         if (full_filename[strlen(full_filename)-1] != '/')
            strcat(full_filename, "/");
         strcat(full_filename, filename);
         mkdir(full_filename, S_IRWXU | S_IRWXG | S_IRWXO);
      }
      /******************************************************************
       * Create a LINEAR_FILE node if no user write permission.
       *****************************************************************/
      else if ((linkflag == LF_NORMAL) &&
               ((file_mode & 0200) == 0000))
      {
         const char  *name;

         loc = root_loc;
         if (IMFS_evaluate_for_make(filename, &loc, &name) == 0)
         {
            node = IMFS_create_node(&loc,
                                    IMFS_LINEAR_FILE, (char *)name,
                                    (S_IRUSR | S_IRGRP | S_IROTH) | S_IFREG,
                                    NULL);
            node->info.linearfile.size   = file_size;
            node->info.linearfile.direct = &tar_image[offset];
         }

         nblocks = (((file_size) + 511) & ~511) / 512;
         offset += 512 * nblocks;
      }
      /******************************************************************
       * Create a regular MEMORY_FILE if write permission exists.
       *****************************************************************/
      else if ((linkflag == LF_NORMAL) &&
               ((file_mode & 0200) == 0200))
      {
         int fd;
         int n, left, ptr;

         strcpy(full_filename, mountpoint);
         if (full_filename[strlen(full_filename)-1] != '/')
            strcat(full_filename, "/");
         strcat(full_filename, filename);

         fd = creat(full_filename, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP);
         if (fd != -1)
         {
            left = file_size;
            ptr  = offset;
            while ((n = write(fd, &tar_image[ptr], left)) > 0)
            {
               left -= n;
               ptr += n;
            }
            close(fd);
         }

         nblocks = (((file_size) + 511) & ~511) / 512;
         offset += 512 * nblocks;
      }
   }

   return(status);
}

/**************************************************************************
 * This converts octal ASCII number representations into an
 * unsigned long.  Only support 32-bit numbers for now.
 *************************************************************************/
static unsigned long
octal2ulong(char *octascii, int len)
{
   int           i;
   unsigned long num;
   unsigned long mult;

   num = 0;
   mult = 1;
   for (i=len-1; i>=0; i--)
   {
      if (octascii[i] < '0') 
         continue;
      if (octascii[i] > '9')
         continue;

      num  += mult*((unsigned long)(octascii[i] - '0'));
      mult *= 8;
   }
   return(num);
}


/************************************************************************
 * Compute the TAR checksum and check with the value in 
 * the archive.  The checksum is computed over the entire
 * header, but the checksum field is substituted with blanks.
 ************************************************************************/
static int
compute_tar_header_checksum(char *bufr)
{
   int  i, sum;


   sum = 0;
   for (i=0; i<512; i++)
   {
      if ((i >= 148) && (i < 156))
         sum += 0xff & ' ';
      else
         sum += 0xff & bufr[i];
   }
   return(sum);
}