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