Added initial support for bzip2 compression in template files
[jigit.git] / dump-jte.c
1 #include <errno.h>
2 #include <math.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <zlib.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <sys/mman.h>
12
13 /* Possible commands to run. Or them together... */
14 #define CMD_LIST        0x0001
15 #define CMD_BUILD_IMAGE 0x0002
16
17 typedef unsigned long long UINT64;
18 typedef unsigned long      UINT32;
19
20 #define BUF_SIZE 65536
21
22 /* Different types used in the extent_type below */
23 #define JTET_HEADER     0
24 #define JTET_FOOTER     1
25 #define JTET_FILE_MATCH 2
26 #define JTET_NOMATCH    3
27
28 #define JTE_ID_STRING     "JTE"
29 #define JTE_HEADER_STRING "MKJ IMAGE START"
30 #define JTE_FOOTER_STRING "*MKJ IMAGE END*"
31 #define JTE_VER_MAJOR     0x0001
32 #define JTE_VER_MINOR     0x0000
33
34 struct jt_extent_data
35 {
36     unsigned char id[4];                       /* "JTE" plus NULL terminator */
37     unsigned char extent_type;                 /* The type of this extent in the jigdo template file */
38     unsigned char extent_length[8];            /* The length in bytes of this extent, including all
39                                                   the metadata. 64-bit, big endian */
40     unsigned char start_sector[4];             /* The start sector of this extent within the output image;
41                                                   32-bit BE. Header and footer use 0xFFFFFFFF */
42     union
43     {
44         struct
45         {
46             unsigned char header_string[16];   /* Recognition string. Should contain "MKJ IMAGE START",
47                                                   including NULL terminator */
48             unsigned char version[4];          /* Version number, encoded MMmm */
49             unsigned char sector_size[4];      /* Sector size used in this image.
50                                                   _Always_ expected to be 2KB. Stored as 32-bit BE */
51             unsigned char pad[16];
52         } header;
53         struct
54         {
55             unsigned char footer_string[16];   /* Recognition string. Should contain "*MKJ IMAGE END*",
56                                                   including NULL terminator */
57             unsigned char image_size[8];       /* Size of image, in bytes. 64-bit BE. */
58             unsigned char md5[16];             /* MD5SUM of the entire image */
59         } footer;
60         struct
61         {
62             unsigned char file_length[8];      /* The actual length of the file stored in this extent.
63                                                   Will be <= extent_length; also 64-bit BE */
64             unsigned char filename_length[4];  /* The length of the following filename entry */
65             unsigned char md5[16];             /* MD5SUM of the _file_ data in this lump, without padding */
66             unsigned char pad[12];
67         } file_match;
68         struct
69         {
70             unsigned char unmatched_length[8]; /* The length of the data in this extent. Will be == 
71                                                   extent_length - sizeof(struct jt_extent_data) ; also 64-bit BE */
72             unsigned char md5[16];             /* MD5SUM of this lump of unmatched data */
73             unsigned char pad[16];
74         } nomatch;
75     } data;
76 };
77
78 int verbose = 0;
79 int cmd = 0;
80 UINT32 sector_size = 0;
81 int jte_fd = -1;
82 int out_fd = -1;
83
84 static char *print_md5(unsigned char *buf)
85 {
86     static char outbuf[33];
87
88     bzero(outbuf, sizeof(outbuf));
89     sprintf(outbuf, "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
90             buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
91             buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
92     return outbuf;
93 }
94
95 static int my_sendfile(int src_fd, int dst_fd, off_t length)
96 {
97     char buf[BUF_SIZE];
98     off_t bytes = 0;
99     off_t bytes_read = 0;
100     off_t bytes_written = 0;
101     off_t size = 0;
102
103     while (bytes < length)
104     {
105         size = length - bytes;
106         if (size > BUF_SIZE)
107             size = BUF_SIZE;
108         
109         bytes_read = read(src_fd, buf, size);
110         if (size != bytes_read)
111         {
112             printf("my_sendfile: FAILED to read %llu bytes from input file; error %d\n", size, errno);
113             return errno;
114         }
115         bytes_written = write(dst_fd, buf, bytes_read);
116         if (bytes_read != bytes_written)
117         {
118             printf("my_sendfile: FAILED to write %llu bytes to output file; error %d\n", bytes_read, errno);
119             return errno;
120         }
121         bytes += bytes_read;
122     }
123     
124     /* Now pad if necessary */
125     if (length % sector_size)
126     {
127         bzero (buf, BUF_SIZE);
128         off_t pad_size = sector_size - (length % sector_size);
129         
130         bytes = write(dst_fd, buf, pad_size);
131         if (-1 == bytes)
132         {
133             printf("my_sendfile: FAILED to pad with %llu bytes; error %d\n", pad_size, errno);
134             return errno;
135         }
136     }
137     
138     return 0;
139 }
140
141 UINT64 handle_jtet_header(UINT64 extent_length,
142                           UINT32 start_sector, unsigned char *buf)
143 {
144     struct jt_extent_data *extent = (struct jt_extent_data *)buf;
145     
146     sector_size = extent->data.header.sector_size[0] << 24;
147     sector_size |= extent->data.header.sector_size[1] << 16;
148     sector_size |= extent->data.header.sector_size[2] << 8;
149     sector_size |= extent->data.header.sector_size[3];
150     
151     if (cmd & CMD_LIST)
152     {
153         printf("  Header string: %s\n", extent->data.header.header_string);
154         printf("  JTE version: %d.%d\n",
155                (extent->data.header.version[0] << 8) | extent->data.header.version[1],
156                (extent->data.header.version[2] << 8) | extent->data.header.version[3]);
157         printf("  Sector size: %ld bytes\n", sector_size);
158     }
159     return extent_length;
160 }
161
162 UINT64 handle_jtet_footer(UINT64 extent_length,
163                           UINT32 start_sector, unsigned char *buf)
164 {
165     struct jt_extent_data *extent = (struct jt_extent_data *)buf;
166     UINT64 image_size = (UINT64)extent->data.footer.image_size[0] << 56;
167     image_size |= (UINT64)extent->data.footer.image_size[1] << 48;
168     image_size |= (UINT64)extent->data.footer.image_size[2] << 40;
169     image_size |= (UINT64)extent->data.footer.image_size[3] << 32;
170     image_size |= (UINT64)extent->data.footer.image_size[4] << 24;
171     image_size |= (UINT64)extent->data.footer.image_size[5] << 16;
172     image_size |= (UINT64)extent->data.footer.image_size[6] << 8;
173     image_size |= (UINT64)extent->data.footer.image_size[7];
174
175     if (cmd & CMD_LIST)
176     {
177         printf("  Footer string: %s\n", extent->data.footer.footer_string);
178         printf("  ISO image size: %llu\n", image_size);
179         printf("  ISO image MD5sum: %s\n", print_md5(extent->data.footer.md5));
180     }
181     return 0;
182 }
183
184 UINT64 handle_jtet_file_match(UINT64 extent_length,
185                               UINT32 start_sector, unsigned char *buf)
186 {
187     int error = 0;
188     int chunk_fd = -1;
189     struct jt_extent_data *extent = (struct jt_extent_data *)buf;
190     char *filename = &buf[57];
191     UINT64 length = 0;
192     int filename_length = 0;
193
194     length |= (UINT64)extent->data.file_match.file_length[0] << 56;
195     length |= (UINT64)extent->data.file_match.file_length[1] << 48;
196     length |= (UINT64)extent->data.file_match.file_length[2] << 40;
197     length |= (UINT64)extent->data.file_match.file_length[3] << 32;
198     length |= (UINT64)extent->data.file_match.file_length[4] << 24;
199     length |= (UINT64)extent->data.file_match.file_length[5] << 16;
200     length |= (UINT64)extent->data.file_match.file_length[6] << 8;
201     length |= (UINT64)extent->data.file_match.file_length[7];
202
203     filename_length |= extent->data.file_match.filename_length[0] << 24;
204     filename_length |= extent->data.file_match.filename_length[1] << 16;
205     filename_length |= extent->data.file_match.filename_length[2] << 8;
206     filename_length |= extent->data.file_match.filename_length[3];
207
208     if (cmd & CMD_LIST)
209     {
210         printf("  File length: %llu\n", length);
211         printf("  Filename len: %d\n", filename_length);
212         printf("  Filename: %s\n", filename);
213         printf("  File MD5sum: %s\n", print_md5(extent->data.file_match.md5));
214     }
215     
216     if (cmd & CMD_BUILD_IMAGE)
217     {
218         chunk_fd = open(filename, O_RDONLY|O_LARGEFILE);
219         if (-1 == chunk_fd)
220         {
221             printf("FAILED to open filename %s, error %d. Aborting\n", filename, errno);
222             return 0;
223         }
224         else
225         {
226             if (verbose)
227                 printf("Writing %7llu bytes of %s to output file\n", length, filename);
228             error = my_sendfile(chunk_fd, out_fd, length);
229             close(chunk_fd);
230             if (error)
231             {
232                 printf("FAILED to copy contents of %s into output file; error %d. Aborting\n", filename, error);
233                 return 0;
234             }
235         }
236     }
237     
238     return extent_length;
239 }
240
241 UINT64 handle_jtet_no_match(UINT64 extent_length,
242                             UINT32 start_sector, unsigned char *buf)
243 {
244     int error = 0;
245     UINT64 length = 0;
246     struct jt_extent_data *extent = (struct jt_extent_data *)buf;
247
248     length |= (UINT64)extent->data.nomatch.unmatched_length[0] << 56;
249     length |= (UINT64)extent->data.nomatch.unmatched_length[1] << 48;
250     length |= (UINT64)extent->data.nomatch.unmatched_length[2] << 40;
251     length |= (UINT64)extent->data.nomatch.unmatched_length[3] << 32;
252     length |= (UINT64)extent->data.nomatch.unmatched_length[4] << 24;
253     length |= (UINT64)extent->data.nomatch.unmatched_length[5] << 16;
254     length |= (UINT64)extent->data.nomatch.unmatched_length[6] << 8;
255     length |= (UINT64)extent->data.nomatch.unmatched_length[7];
256
257     if (cmd & CMD_LIST)
258     {
259         printf("  Unmatched data, length %llu\n", length);
260         printf("  Chunk MD5sum: %s\n", print_md5(extent->data.nomatch.md5));
261     }
262     
263     if (cmd & CMD_BUILD_IMAGE)
264     {
265         /* Seek past the header of this block */
266         lseek(jte_fd, sizeof(struct jt_extent_data), SEEK_CUR);
267         if (verbose)
268             printf("Writing %7llu bytes of unmatched image data to output file\n", length);
269         error = my_sendfile(jte_fd, out_fd, length);
270         if (error)
271         {
272             printf("FAILED to copy unmatched data chunk into output file; error %d. Aborting\n", error);
273             return 0;
274         }
275     }
276     
277     return extent_length;
278 }
279
280 UINT64 parse_jte_block(UINT64 offset, unsigned char *buf, size_t buf_size)
281 {
282     int    extent_type = 0;
283     UINT64 extent_length = 0;
284     UINT32 start_sector = 0;
285     struct jt_extent_data *extent = (struct jt_extent_data *)buf;
286     
287     if (strncmp(extent->id, JTE_ID_STRING, 4))
288     {
289         printf("Error! Didn't find expected JTE block at offset %lld\n", offset);
290         return 0;
291     }
292     if (cmd & CMD_LIST)
293         printf("\nJTE block found at offset %lld\n", offset);
294
295     extent_type = extent->extent_type;
296
297     extent_length |= (UINT64)extent->extent_length[0] << 56;
298     extent_length |= (UINT64)extent->extent_length[1] << 48;
299     extent_length |= (UINT64)extent->extent_length[2] << 40;
300     extent_length |= (UINT64)extent->extent_length[3] << 32;
301     extent_length |= (UINT64)extent->extent_length[4] << 24;
302     extent_length |= (UINT64)extent->extent_length[5] << 16;
303     extent_length |= (UINT64)extent->extent_length[6] << 8;
304     extent_length |= (UINT64)extent->extent_length[7];
305
306     start_sector |= extent->start_sector[0] << 24;
307     start_sector |= extent->start_sector[1] << 16;
308     start_sector |= extent->start_sector[2] << 8;
309     start_sector |= extent->start_sector[3];
310     
311     if (cmd & CMD_LIST)
312         printf("  extent type %d, length %llu, start_sector %ld (out offset %llu)\n",
313                extent_type, extent_length, start_sector, (off_t)start_sector * sector_size);
314
315     switch(extent_type)
316     {
317         case JTET_HEADER:
318             return handle_jtet_header(extent_length, start_sector, buf);
319         case JTET_FOOTER:
320             return handle_jtet_footer(extent_length, start_sector, buf);
321         case JTET_FILE_MATCH:
322             return handle_jtet_file_match(extent_length, start_sector, buf);
323         case JTET_NOMATCH:
324             return handle_jtet_no_match(extent_length, start_sector, buf);
325         default:
326             printf("Awooga! Invalid extent type!\n");
327             return 0;
328     }
329 }
330
331 int main(int argc, char **argv)
332 {
333     char *filename = NULL;
334     char *outfile = NULL;
335     unsigned char *buf = NULL;
336     UINT64 offset = 0;
337     UINT64 bytes = 0;
338     int done = 0;
339     int i = 0;
340     
341     for (i = 1; i < argc; i++)
342     {
343         if (!strcmp(argv[i], "-l"))
344             cmd |= CMD_LIST;
345         else if (!strcmp(argv[i], "-b"))
346             cmd |= CMD_BUILD_IMAGE;
347         else if (!strcmp(argv[i], "-v"))
348             verbose = 1;
349         else if (!strcmp(argv[i], "-o") && i < argc - 1)
350             outfile = argv[++i];
351         else if (!strcmp(argv[i], "-f") && i < argc - 1)
352             filename = argv[++i];
353     }
354
355     if (0 == cmd)
356         cmd = CMD_LIST;
357
358     if (NULL == filename)
359     {
360         printf("No filename specified! Try again...\n");
361         return EINVAL;
362     }
363
364     if ( (cmd & CMD_BUILD_IMAGE) && !outfile)
365     {
366         printf("No output filename given; aborting...\n");
367         return EINVAL;
368     }
369     
370     jte_fd = open(filename, O_RDONLY|O_LARGEFILE);
371     if (-1 == jte_fd)
372     {
373         printf("Failed to open input file %s, error %d!. Try again...\n", filename, errno);
374         return errno;
375     }
376
377     if (outfile)
378     {
379         out_fd = open(outfile, O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, 0644);
380         if (-1 == out_fd)
381         {
382             printf("Failed to open output file %s, error %d!\n", outfile, errno);
383             return errno;
384         }
385     }
386
387     buf = malloc(BUF_SIZE);
388     if (!buf)
389     {
390         printf("Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
391         return ENOMEM;
392     }
393
394     while (!done)
395     {
396         UINT64 start_offset = -1;
397         lseek(jte_fd, offset, SEEK_SET);
398         bytes = read(jte_fd, buf, BUF_SIZE);
399         if (0 >= bytes)
400         {
401             printf("Failed to read! error %d\n", errno);
402             done = 1;
403             break;
404         }
405         lseek(jte_fd, offset, SEEK_SET);
406         start_offset = parse_jte_block(offset, buf, bytes);
407         if (!start_offset)
408             break; /* We're finished! */
409         offset += start_offset;
410     }        
411     
412     close(jte_fd);
413     if (-1 != out_fd)
414         close(out_fd);
415
416     return 0;
417 }