Cope with interface changes in jigdb.h
[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 "endian.h"
25 #include "jigdb.h"
26 #include "jte.h"
27 #include "md5.h"
28
29 FILE *G_logfile = NULL;
30 FILE *G_outfile = NULL;
31 FILE *G_missing_file = NULL;
32 long long G_start_offset = 0;
33 long long G_end_offset = 0;
34 int G_quick = 0;
35 int G_verbose = 0;
36 UINT64 G_out_size = 0;
37 char *G_missing_filename = NULL;
38
39 typedef enum state_
40 {
41     STARTING,
42     IN_DATA,
43     IN_DESC,
44     DUMP_DESC,
45     DONE,
46     ERROR
47 } e_state;
48
49 match_list_t *G_match_list_head = NULL;
50 match_list_t *G_match_list_tail = NULL;
51
52 md5_list_t *G_md5_list_head = NULL;
53 md5_list_t *G_md5_list_tail = NULL;
54
55 extern void file_missing(char *missing, char *filename)
56 {
57     if (!G_missing_file)
58     {
59         G_missing_file = fopen(missing, "wb");
60         if (!G_missing_file)
61         {
62             fprintf(G_logfile, "file_missing: Unable to open missing log %s; error %d\n", missing, errno);
63             exit(1);
64         }
65     }
66     fprintf(G_missing_file, "%s\n", filename);
67 }
68
69 extern void display_progress(FILE *file, char *text)
70 {
71     INT64 written = ftello(file);
72     if (G_out_size > 0)
73         fprintf(G_logfile, "\r %5.2f%%  %-60.60s",
74                100.0 * written / G_out_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         fprintf(G_logfile, "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     fprintf(G_logfile, "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                     fprintf(stderr, "Unable to open output file %s\n", optarg);
202                     return errno;
203                 }
204                 break;
205             case 'j':
206                 if (jigdo_filename)
207                 {
208                     fprintf(G_logfile, "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                     fprintf(G_logfile, "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                     fprintf(G_logfile, "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                     fprintf(G_logfile, "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                 fprintf(G_logfile, "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                 fprintf(G_logfile, "Unknown option!\n");
273                 return EINVAL;
274         }
275     }
276
277     if (0 == G_end_offset)
278         G_end_offset = LLONG_MAX;
279
280     if ((NULL == jigdo_filename) &&
281         (NULL == md5_filename) && 
282         (NULL == db_filename) && 
283         !sizeonly)
284     {
285         fprintf(G_logfile, "No jigdo file, DB file or MD5 file specified!\n");
286         usage(argv[0]);
287         return EINVAL;
288     }
289     
290     if (NULL == template_filename)
291     {
292         fprintf(G_logfile, "No template file specified!\n");
293         usage(argv[0]);
294         return EINVAL;
295     }    
296
297     if (md5_filename)
298     {
299         /* Build up a list of the files we've been fed */
300         error = parse_md5_file(md5_filename);
301         if (error)
302         {
303             fprintf(G_logfile, "Unable to parse the MD5 file %s\n", md5_filename);
304             return error;
305         }
306     }
307
308     if (jigdo_filename)
309     {
310         /* Build up a list of file mappings */
311         error = parse_jigdo_file(jigdo_filename);
312         if (error)
313         {
314             fprintf(G_logfile, "Unable to parse the jigdo file %s\n", jigdo_filename);
315             return error;
316         }
317     }
318
319     if (!output_name)
320         output_name = "to stdout";
321
322     if (db_filename)
323     {
324         dbp = db_open(db_filename);
325         if (!dbp)
326         {
327             fprintf(G_logfile, "Failed to open DB file %s, error %d\n", db_filename, errno);
328             return errno;
329         }
330         /* If we have a DB, then we should cache the template
331          * information in it too. Check and see if there is
332          * information about this template file in the database
333          * already. */
334     }
335
336     /* See if we know about this template file */
337     error = db_lookup_template_by_path(dbp, template_filename, &template);
338     if (error)
339     {
340         /* Not found. Parse it and put the details in the database */
341         error = add_new_template_file(dbp, template_filename);
342         if (error)
343         {
344             fprintf(G_logfile, "Unable to add template file %s to database, error %d\n",
345                     template_filename, error);
346             return error;
347         }
348         error = db_lookup_template_by_path(dbp, template_filename, &template);
349         if (error)
350         {
351             fprintf(G_logfile, "Unable to re-read newly-added template file %s, error %d!\n",
352                     template_filename, error);
353             return error;
354         }
355     }
356
357     jdp = jd_open(dbp, template_filename);
358     if (!jdp)
359     {
360         error = errno;
361         fprintf(G_logfile, "Unable to open JD interface for template file %s (error %d)\n",
362                 template_filename, error);
363         return error;
364     }
365
366     if (!G_quick)
367         mk_MD5Init(&image_context);
368
369     /* Now the main loop - iterate in read/write/md5sum */
370     current_offset = G_start_offset;
371     while (!error && (current_offset < G_end_offset))
372     {
373         error = jd_read(jdp, current_offset, sizeof(data_buf), data_buf, &bytes_read);
374         if (error)
375         {
376             fprintf(G_logfile, "Failed to read %d bytes at offset %lld, error %d\n",
377                     sizeof(data_buf), current_offset, error);
378             break;
379         }
380         if (0 == bytes_read)
381             break;
382         
383         bytes_written = 0;
384         while (bytes_written < bytes_read)
385         {
386             size_t this_write = fwrite(data_buf, 1, bytes_read, G_outfile);
387             if (-1 == this_write)
388             {
389                 fprintf(G_logfile, "Failed to write %lld bytes at offset %lld, error %d\n",
390                         bytes_read, current_offset, error);
391                 break;
392             }
393             bytes_written += this_write;
394         }
395         
396         if (!G_quick)
397             mk_MD5Update(&image_context, data_buf, bytes_read);
398
399         if (G_verbose)
400         {
401             char *last_file = NULL;                
402             error = jd_last_filename(jdp, &last_file);
403             if (!error && (G_end_offset > G_start_offset))
404                 fprintf(G_logfile, "\r %5.2f%%  %-60.60s",
405                         100.0 * (current_offset - G_start_offset) / (G_end_offset - G_start_offset), last_file);
406         }
407     }
408     if (error)
409     {
410         fprintf(G_logfile, "Failed to create image, error %d\n", error);
411         return error;
412     }
413
414     if (G_verbose)
415     {
416         fprintf(G_logfile, "\n");
417         if (!G_quick)
418         {
419             int i = 0;
420             mk_MD5Final (image_md5, &image_context);
421             fprintf(G_logfile, "Output image MD5 is    ");
422             for (i = 0; i < 16; i++)
423                 fprintf(G_logfile, "%2.2x", image_md5[i]);
424             fprintf(G_logfile, "\n");
425         }
426         fprintf(G_logfile, "Output image length written is %lld bytes\n", G_end_offset - G_start_offset);
427     }
428
429 #if 0
430     /* Read the template file and actually build the image to <outfile> */
431     error = parse_template_file(template_filename, sizeonly, G_missing_filename,
432                                 G_outfile, output_name, dbp);
433     if (error)
434     {
435         fprintf(G_logfile, "Unable to recreate image from template file %s\n", template_filename);
436         if (G_missing_filename)
437             fprintf(G_logfile, "%s contains the list of missing files\n", G_missing_filename);
438         return error;
439     }        
440 #endif
441
442     fclose(G_logfile);
443     return 0;
444 }
445