Use the base path as the root of the overlaid filesystem
[jigit.git] / mkimage.c
1 /*
2  * mkimage
3  *
4  * Tool to create an ISO image from jigdo files
5  *
6  * Copyright (c) 2004 Steve McIntyre <steve@einval.com>
7  *
8  * GPL v2 - see COPYING
9  */
10
11 #define BZ2_SUPPORT
12
13 #include <errno.h>
14 #include <math.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <limits.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/mman.h>
24 #include <stdarg.h>
25 #include "endian.h"
26 #include "jigdb.h"
27 #include "jte.h"
28 #include "md5.h"
29
30 FILE *G_logfile = NULL;
31 FILE *G_outfile = NULL;
32 FILE *G_missing_file = NULL;
33 long long G_start_offset = 0;
34 long long G_end_offset = 0;
35 int G_quick = 0;
36 int G_verbose = 0;
37 UINT64 G_out_size = 0;
38 char *G_missing_filename = NULL;
39
40 typedef enum state_
41 {
42     STARTING,
43     IN_DATA,
44     IN_DESC,
45     DUMP_DESC,
46     DONE,
47     ERROR
48 } e_state;
49
50 match_list_t *G_match_list_head = NULL;
51 match_list_t *G_match_list_tail = NULL;
52
53 md5_list_t *G_md5_list_head = NULL;
54 md5_list_t *G_md5_list_tail = NULL;
55
56 extern void file_missing(char *missing, char *filename)
57 {
58     if (!G_missing_file)
59     {
60         G_missing_file = fopen(missing, "wb");
61         if (!G_missing_file)
62         {
63             jd_log(0, "file_missing: Unable to open missing log %s; error %d\n", missing, errno);
64             exit(1);
65         }
66     }
67     fprintf(G_missing_file, "%s\n", filename);
68 }
69
70 void display_progress(int verbose_level, INT64 image_size, INT64 current_offset, char *text)
71 {
72     if ((verbose_level <= G_verbose) && (image_size > 0))
73         jd_log(1, "\r %5.2f%%  %-60.60s",
74                100.0 * current_offset / image_size, text);
75 }
76
77 static int add_match_entry(char *match)
78 {
79     match_list_t *entry = NULL;
80     char *mirror_path = NULL;
81     char *ptr = match;
82
83     /* Split "Foo=/mirror/foo" into its components */
84     while (*ptr)
85     {
86         if ('=' == *ptr)
87         {
88             *ptr = 0;
89             ptr++;
90             mirror_path = ptr;
91             break;
92         }
93         ptr++;
94     }
95
96     if (!mirror_path)
97     {
98         jd_log(0, "Could not parse malformed match entry \"%s\"\n", match);
99         return EINVAL;
100     }        
101     
102     entry = calloc(1, sizeof(*entry));
103     if (!entry)
104         return ENOMEM;
105
106     jd_log(1, "Adding match entry %s:%s\n", match, mirror_path);
107
108     entry->match = match;
109     entry->mirror_path = mirror_path;
110     
111     if (!G_match_list_head)
112     {
113         G_match_list_head = entry;
114         G_match_list_tail = entry;
115     }
116     else
117     {
118         G_match_list_tail->next = entry;
119         G_match_list_tail = entry;
120     }
121     
122     return 0;
123 }
124
125 static void usage(char *progname)
126 {
127     printf("%s [OPTIONS]\n\n", progname);
128     printf(" Options:\n");
129         printf(" -M <missing name>   Rather than try to build the image, just check that\n");
130         printf("                     all the needed files are available. If any are missing,\n");
131         printf("                     list them in this file.\n");
132     printf(" -d <DB name>        Specify an input MD5 database file, as created by jigsum\n");
133     printf(" -e <bytenum>        End byte number; will end at EOF if not specified\n");    
134     printf(" -f <MD5 name>       Specify an input MD5 file. MD5s must be in jigdo's\n");
135         printf("                     pseudo-base64 format\n");
136     printf(" -j <jigdo name>     Specify the input jigdo file\n");
137     printf(" -l <logfile>        Specify a logfile to append to.\n");
138     printf("                     If not specified, will log to stderr\n");
139     printf(" -m <item=path>      Map <item> to <path> to find the files in the mirror\n");
140     printf(" -o <outfile>        Specify a file to write the ISO image to.\n");
141     printf("                     If not specified, will write to stdout\n");
142     printf(" -q                  Quick mode. Don't check MD5sums. Dangerous!\n");
143     printf(" -s <bytenum>        Start byte number; will start at 0 if not specified\n");
144     printf(" -t <template name>  Specify the input template file\n");
145         printf(" -v                  Make the output logging more verbose\n");
146     printf(" -z                  Don't attempt to rebuild the image; simply print its\n");
147     printf("                     size in bytes\n");
148 }
149
150 int main(int argc, char **argv)
151 {
152     char *template_filename = NULL;
153     char *jigdo_filename = NULL;
154     char *md5_filename = NULL;
155     char *output_name = NULL;
156     char *db_filename = NULL;
157     int c = -1;
158     int error = 0;
159     int sizeonly = 0;
160     JIGDB *dbp = NULL;
161     db_template_entry_t template;
162     JD *jdp = NULL;
163     INT64 current_offset = 0;
164     unsigned char data_buf[BUF_SIZE];
165     INT64 bytes_read = 0;
166     INT64 bytes_written = 0;
167     struct mk_MD5Context image_context;
168     unsigned char image_md5[16];
169
170     G_logfile = stderr;
171     G_outfile = stdout;
172
173     while(1)
174     {
175         c = getopt(argc, argv, ":?M:d:e:f:h:j:l:m:o:qs:t:vz");
176         if (-1 == c)
177             break;
178         
179         switch(c)
180         {
181             case 'v':
182                 G_verbose++;
183                 break;
184             case 'q':
185                 G_quick = 1;
186                 break;
187             case 'l':
188                 G_logfile = fopen(optarg, "ab");
189                 if (!G_logfile)
190                 {
191                     fprintf(stderr, "Unable to open log file %s\n", optarg);
192                     return errno;
193                 }
194                 setlinebuf(G_logfile);
195                 break;
196             case 'o':
197                 output_name = optarg;
198                 G_outfile = fopen(output_name, "wb");
199                 if (!G_outfile)
200                 {
201                     jd_log(0, "Unable to open output file %s\n", optarg);
202                     return errno;
203                 }
204                 break;
205             case 'j':
206                 if (jigdo_filename)
207                 {
208                     jd_log(0, "Can only specify one jigdo file!\n");
209                     return EINVAL;
210                 }
211                 /* else */
212                 jigdo_filename = optarg;
213                 break;
214             case 't':
215                 if (template_filename)
216                 {
217                     jd_log(0, "Can only specify one template file!\n");
218                     return EINVAL;
219                 }
220                 /* else */
221                 template_filename = optarg;
222                 break;
223             case 'f':
224                 if (md5_filename)
225                 {
226                     jd_log(0, "Can only specify one MD5 file!\n");
227                     return EINVAL;
228                 }
229                 /* else */
230                 md5_filename = optarg;
231                 break;                
232             case 'd':
233                 if (db_filename)
234                 {
235                     jd_log(0, "Can only specify one db file!\n");
236                     return EINVAL;
237                 }
238                 /* else */
239                 db_filename = optarg;
240                 break;                
241             case 'm':
242                 error = add_match_entry(strdup(optarg));
243                 if (error)
244                     return error;
245                 break;
246             case 'M':
247                 G_missing_filename = optarg;
248                 break;
249             case ':':
250                 jd_log(0, "Missing argument!\n");
251                 return EINVAL;
252                 break;
253             case 'h':
254             case '?':
255                 usage(argv[0]);
256                 return 0;
257                 break;
258             case 's':
259                 G_start_offset = strtoull(optarg, NULL, 10);
260                 if (G_start_offset != 0)
261                     G_quick = 1;
262                 break;
263             case 'e':
264                 G_end_offset = strtoull(optarg, NULL, 10);
265                 if (G_end_offset != 0)
266                     G_quick = 1;
267                 break;
268             case 'z':
269                 sizeonly = 1;
270                 break;
271             default:
272                 jd_log(0, "Unknown option!\n");
273                 return EINVAL;
274         }
275     }
276
277     if ((NULL == jigdo_filename) &&
278         (NULL == md5_filename) && 
279         (NULL == db_filename) && 
280         !sizeonly)
281     {
282         jd_log(0, "No jigdo file, DB file or MD5 file specified!\n");
283         usage(argv[0]);
284         return EINVAL;
285     }
286     
287     if (NULL == template_filename)
288     {
289         jd_log(0, "No template file specified!\n");
290         usage(argv[0]);
291         return EINVAL;
292     }    
293
294     if (md5_filename)
295     {
296         /* Build up a list of the files we've been fed */
297         error = parse_md5_file(md5_filename, &G_md5_list_head);
298         if (error)
299         {
300             jd_log(0, "Unable to parse the MD5 file %s\n", md5_filename);
301             return error;
302         }
303     }
304
305     if (jigdo_filename)
306     {
307         /* Build up a list of file mappings */
308         error = parse_jigdo_file(jigdo_filename, &G_md5_list_head, G_match_list_head, G_missing_filename);
309         if (error)
310         {
311             jd_log(0, "Unable to parse the jigdo file %s\n", jigdo_filename);
312             return error;
313         }
314     }
315
316     if (!output_name)
317         output_name = "to stdout";
318
319     if (db_filename)
320     {
321         dbp = db_open(db_filename);
322         if (!dbp)
323         {
324             jd_log(0, "Failed to open DB file %s, error %d\n", db_filename, errno);
325             return errno;
326         }
327         /* If we have a DB, then we should cache the template
328          * information in it too. Check and see if there is
329          * information about this template file in the database
330          * already. */
331     }
332
333     /* See if we know about this template file */
334     error = db_lookup_template_by_path(dbp, template_filename, &template);
335     if (error)
336     {
337         /* Not found. Parse it and put the details in the database */
338         error = add_new_template_file(dbp, template_filename);
339         if (error)
340         {
341             jd_log(0, "Unable to add template file %s to database, error %d\n",
342                    template_filename, error);
343             return error;
344         }
345         error = db_lookup_template_by_path(dbp, template_filename, &template);
346         if (error)
347         {
348             jd_log(0, "Unable to re-read newly-added template file %s, error %d!\n",
349                    template_filename, error);
350             return error;
351         }
352     }
353
354     /* Allocate enough space to cache details about 1 compressed lump,
355      * that's all we need here */
356     error = jd_init(1);
357     if (error)
358     {
359         jd_log(0, "Unable to init JD cache interface, error %d\n", error);
360         return error;
361     }
362
363     jdp = jd_open(dbp, template_filename);
364     if (!jdp)
365     {
366         error = errno;
367         jd_log(0, "Unable to open JD interface for template file %s (error %d)\n",
368                template_filename, error);
369         return error;
370     }
371
372     if (!G_quick)
373         mk_MD5Init(&image_context);
374
375     error = jd_size(jdp, &G_out_size);
376     if (error)
377     {
378         jd_log(0, "Unable to read image size from the template information. Error %d\n", error);
379         return error;
380     }
381     
382     if (0 == G_end_offset)
383         G_end_offset = G_out_size;
384
385     /* Now the main loop - iterate in read/write/md5sum */
386     current_offset = G_start_offset;
387     while (!error && (current_offset < G_end_offset))
388     {
389         error = jd_read(jdp, current_offset, sizeof(data_buf), data_buf, &bytes_read);
390         if (error)
391         {
392             jd_log(0, "Failed to read %d bytes at offset %lld, error %d\n",
393                    sizeof(data_buf), current_offset, error);
394             break;
395         }
396         if (0 == bytes_read)
397             break;
398         
399         bytes_written = 0;
400         while (bytes_written < bytes_read)
401         {
402             size_t this_write = fwrite(data_buf, 1, bytes_read, G_outfile);
403             if (-1 == this_write)
404             {
405                 jd_log(0, "Failed to write %lld bytes at offset %lld, error %d\n",
406                        bytes_read, current_offset, error);
407                 break;
408             }
409             bytes_written += this_write;
410         }
411         
412         if (!G_quick)
413             mk_MD5Update(&image_context, data_buf, bytes_read);
414
415         current_offset += bytes_read;
416
417         if (G_verbose)
418         {
419             char *last_file = NULL;                
420             error = jd_last_filename(jdp, &last_file);
421             if (!error && (G_end_offset > G_start_offset))
422                 jd_log(1, "\r %5.2f%%  %-60.60s",
423                        100.0 * (current_offset - G_start_offset) / (G_end_offset - G_start_offset), last_file);
424         }
425     }
426     if (error)
427     {
428         jd_log(0, "Failed to create image, error %d\n", error);
429         return error;
430     }
431
432     if (G_verbose)
433     {
434         jd_log(1, "\n");
435         if (!G_quick)
436         {
437             mk_MD5Final (image_md5, &image_context);
438             char *out_md5 = hex_dump(image_md5, 16);
439             jd_log(1, "Output image MD5 is    %s\n", out_md5);
440             free(out_md5);
441         }
442         jd_log(1, "Output image length written is %lld bytes\n", G_end_offset - G_start_offset);
443     }
444
445 #if 0
446     /* Read the template file and actually build the image to <outfile> */
447     error = parse_template_file(template_filename, sizeonly, G_missing_filename,
448                                 G_outfile, output_name, dbp);
449     if (error)
450     {
451         jd_log(0, "Unable to recreate image from template file %s\n", template_filename);
452         if (G_missing_filename)
453             jd_log(0, "%s contains the list of missing files\n", G_missing_filename);
454         return error;
455     }        
456 #endif
457
458     fclose(G_logfile);
459     return 0;
460 }
461
462 int jd_log(int level, char *fmt, ...)
463 {
464     int error = 0;    
465     va_list ap;
466
467     if (level <= G_verbose)
468     {
469         va_start(ap, fmt);
470         error = vfprintf(G_logfile, fmt, ap);
471         va_end(ap);
472     }
473     return error;
474 }