• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <lzma.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/mman.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 
18 #include "bmpblk_util.h"
19 #include "eficompress.h"
20 #include "vboot_api.h"
21 
22 // Returns pointer to buffer containing entire file, sets length.
read_entire_file(const char * filename,size_t * length)23 static void *read_entire_file(const char *filename, size_t *length) {
24   int fd;
25   struct stat sbuf;
26   void *ptr;
27 
28   *length = 0;                          // just in case
29 
30   if (0 != stat(filename, &sbuf)) {
31     fprintf(stderr, "Unable to stat %s: %s\n", filename, strerror(errno));
32     return 0;
33   }
34 
35   if (!sbuf.st_size) {
36     fprintf(stderr, "File %s is empty\n", filename);
37     return 0;
38   }
39 
40   fd = open(filename, O_RDONLY);
41   if (fd < 0) {
42     fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
43     return 0;
44   }
45 
46   ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
47   if (MAP_FAILED == ptr) {
48     fprintf(stderr, "Unable to mmap %s: %s\n", filename, strerror(errno));
49     close(fd);
50     return 0;
51   }
52 
53   *length = sbuf.st_size;
54 
55   close(fd);
56 
57   return ptr;
58 }
59 
60 
61 // Reclaims buffer from read_entire_file().
discard_file(void * ptr,size_t length)62 static void discard_file(void *ptr, size_t length) {
63   munmap(ptr, length);
64 }
65 
66 //////////////////////////////////////////////////////////////////////////////
67 
require_dir(const char * dirname)68 static int require_dir(const char *dirname) {
69   struct stat sbuf;
70 
71   if (0 == stat(dirname, &sbuf)) {
72     // Something's there. Is it a directory?
73     if (S_ISDIR(sbuf.st_mode)) {
74       return 0;
75     }
76     fprintf(stderr, "%s already exists and is not a directory\n", dirname);
77     return 1;
78   }
79 
80   // dirname doesn't exist. Try to create it.
81   if (ENOENT == errno) {
82     if (0 != mkdir(dirname, 0777)) {
83       fprintf(stderr, "Unable to create directory %s: %s\n",
84               dirname, strerror(errno));
85       return 1;
86     }
87     return 0;
88   }
89 
90   fprintf(stderr, "Unable to stat %s: %s\n", dirname, strerror(errno));
91   return 1;
92 }
93 
94 
95 
do_efi_decompress(ImageInfo * img)96 static void *do_efi_decompress(ImageInfo *img) {
97   void *ibuf;
98   void *sbuf;
99   void *obuf;
100   uint32_t isize;
101   uint32_t ssize;
102   uint32_t osize;
103   EFI_STATUS r;
104 
105   ibuf = (void*)(img + 1);
106   isize = img->compressed_size;
107 
108   r = EfiGetInfo(ibuf, isize, &osize, &ssize);
109   if (EFI_SUCCESS != r) {
110     fprintf(stderr, "EfiGetInfo() failed with code %d\n",
111             r);
112     return 0;
113   }
114 
115   sbuf = malloc(ssize);
116   if (!sbuf) {
117     fprintf(stderr, "Can't allocate %d bytes: %s\n",
118             ssize,
119             strerror(errno));
120     return 0;
121   }
122 
123   obuf = malloc(osize);
124   if (!obuf) {
125     fprintf(stderr, "Can't allocate %d bytes: %s\n",
126             osize,
127             strerror(errno));
128     free(sbuf);
129     return 0;
130   }
131 
132   r = EfiDecompress(ibuf, isize, obuf, osize, sbuf, ssize);
133   if (r != EFI_SUCCESS) {
134     fprintf(stderr, "EfiDecompress failed with code %d\n", r);
135     free(obuf);
136     free(sbuf);
137     return 0;
138   }
139 
140   free(sbuf);
141   return obuf;
142 }
143 
144 
145 
do_lzma_decompress(ImageInfo * img)146 static void *do_lzma_decompress(ImageInfo *img) {
147   void *ibuf;
148   void *obuf;
149   uint32_t isize;
150   uint32_t osize;
151   lzma_stream stream = LZMA_STREAM_INIT;
152   lzma_ret result;
153 
154   ibuf = (void*)(img + 1);
155   isize = img->compressed_size;
156   osize = img->original_size;
157   obuf = malloc(osize);
158   if (!obuf) {
159     fprintf(stderr, "Can't allocate %d bytes: %s\n",
160             osize,
161             strerror(errno));
162     return 0;
163   }
164 
165   result = lzma_auto_decoder(&stream, -1, 0);
166   if (result != LZMA_OK) {
167     fprintf(stderr, "Unable to initialize auto decoder (error: %d)!\n",
168             result);
169     free(obuf);
170     return 0;
171   }
172 
173   stream.next_in = ibuf;
174   stream.avail_in = isize;
175   stream.next_out = obuf;
176   stream.avail_out = osize;
177   result = lzma_code(&stream, LZMA_FINISH);
178   if (result != LZMA_STREAM_END) {
179     fprintf(stderr, "Unalbe to decode data (error: %d)!\n", result);
180     free(obuf);
181     return 0;
182   }
183   lzma_end(&stream);
184   return obuf;
185 }
186 
187 
188 
189 // Show what's inside. If todir is NULL, just print. Otherwise unpack.
dump_bmpblock(const char * infile,int show_as_yaml,const char * todir,int overwrite)190 int dump_bmpblock(const char *infile, int show_as_yaml,
191                   const char *todir, int overwrite) {
192   void *ptr, *data_ptr;
193   size_t length = 0;
194   BmpBlockHeader *hdr;
195   ImageInfo *img;
196   ScreenLayout *scr;
197   int loc_num;
198   int screen_num;
199   int i;
200   int offset;
201   int free_data;
202   char image_name[80];
203   char full_path_name[PATH_MAX];
204   int yfd, bfd;
205   FILE *yfp = stdout;
206   FILE *bfp = stdout;
207 
208   ptr = (void *)read_entire_file(infile, &length);
209   if (!ptr)
210     return 1;
211 
212   if (length < sizeof(BmpBlockHeader)) {
213     fprintf(stderr, "File %s is too small to be a BMPBLOCK\n", infile);
214     discard_file(ptr, length);
215     return 1;
216   }
217 
218   if (0 != memcmp(ptr, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
219     fprintf(stderr, "File %s is not a BMPBLOCK\n", infile);
220     discard_file(ptr, length);
221     return 1;
222   }
223 
224   if (todir) {
225     // Unpacking everything. Create the output directory if needed.
226     if (0 != require_dir(todir)) {
227       discard_file(ptr, length);
228       return 1;
229     }
230 
231     // Open yaml output.
232     show_as_yaml = 1;
233 
234     sprintf(full_path_name, "%s/%s", todir, "config.yaml");
235     yfd = open(full_path_name,
236                O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
237                0666);
238     if (yfd < 0) {
239       fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
240               strerror(errno));
241       discard_file(ptr, length);
242       return 1;
243     }
244 
245     yfp = fdopen(yfd, "wb");
246     if (!yfp) {
247       fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
248               strerror(errno));
249       close(yfd);
250       discard_file(ptr, length);
251       return 1;
252     }
253   }
254 
255   hdr = (BmpBlockHeader *)ptr;
256 
257   if (!show_as_yaml) {
258     printf("%s:\n", infile);
259     printf("  version %d.%d\n", hdr->major_version, hdr->minor_version);
260     printf("  %d screens\n", hdr->number_of_screenlayouts);
261     printf("  %d localizations\n", hdr->number_of_localizations);
262     printf("  %d discrete images\n", hdr->number_of_imageinfos);
263     discard_file(ptr, length);
264     return 0;
265   }
266 
267   // Write out yaml
268   fprintf(yfp, "bmpblock: %d.%d\n", hdr->major_version, hdr->minor_version);
269   offset = sizeof(BmpBlockHeader) +
270     (sizeof(ScreenLayout) *
271      hdr->number_of_localizations *
272      hdr->number_of_screenlayouts);
273   // FIXME(chromium-os:12134): The bmbblock structure allows each image to be
274   // compressed differently, but we haven't provided a way for the yaml file to
275   // specify that. Additionally, we allow the yaml file to specify a default
276   // compression scheme for all images, but only if that line appears in the
277   // yaml file before any images. Accordingly, we'll just check the first image
278   // to see if it has any compression, and if it does, we'll write that out as
279   // the default. When this bug is fixed, we should just write each image's
280   // compression setting separately.
281   img = (ImageInfo *)(ptr + offset);
282   if (img->compression)
283     fprintf(yfp, "compression: %d\n", img->compression);
284   fprintf(yfp, "images:\n");
285   for(i=0; i<hdr->number_of_imageinfos; i++) {
286     img = (ImageInfo *)(ptr + offset);
287     if (img->compressed_size) {
288       sprintf(image_name, "img_%08x.bmp", offset);
289       if (img->tag == TAG_HWID) {
290         fprintf(yfp, "  %s: %s  # %dx%d  %d/%d  tag=%d fmt=%d\n",
291                 RENDER_HWID, image_name,
292                 img->width, img->height,
293                 img->compressed_size, img->original_size,
294                 img->tag, img->format);
295       } else if (img->tag == TAG_HWID_RTOL) {
296         fprintf(yfp, "  %s: %s  # %dx%d  %d/%d  tag=%d fmt=%d\n",
297                 RENDER_HWID_RTOL, image_name,
298                 img->width, img->height,
299                 img->compressed_size, img->original_size,
300                 img->tag, img->format);
301       } else {
302         fprintf(yfp, "  img_%08x: %s  # %dx%d  %d/%d  tag=%d fmt=%d\n",
303                 offset, image_name,
304                 img->width, img->height,
305                 img->compressed_size, img->original_size,
306                 img->tag, img->format);
307       }
308       if (todir) {
309         sprintf(full_path_name, "%s/%s", todir, image_name);
310         bfd = open(full_path_name,
311                    O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
312                    0666);
313         if (bfd < 0) {
314           fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
315                   strerror(errno));
316           fclose(yfp);
317           discard_file(ptr, length);
318           return 1;
319         }
320         bfp = fdopen(bfd, "wb");
321         if (!bfp) {
322           fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
323                   strerror(errno));
324           close(bfd);
325           fclose(yfp);
326           discard_file(ptr, length);
327           return 1;
328         }
329         switch(img->compression) {
330         case COMPRESS_NONE:
331           data_ptr = ptr + offset + sizeof(ImageInfo);
332           free_data = 0;
333           break;
334         case COMPRESS_EFIv1:
335           data_ptr = do_efi_decompress(img);
336           if (!data_ptr) {
337             fclose(bfp);
338             fclose(yfp);
339             discard_file(ptr, length);
340             return 1;
341           }
342           free_data = 1;
343           break;
344         case COMPRESS_LZMA1:
345           data_ptr = do_lzma_decompress(img);
346           if (!data_ptr) {
347             fclose(bfp);
348             fclose(yfp);
349             discard_file(ptr, length);
350             return 1;
351           }
352           free_data = 1;
353           break;
354         default:
355           fprintf(stderr, "Unsupported compression method encountered.\n");
356           fclose(bfp);
357           fclose(yfp);
358           discard_file(ptr, length);
359           return 1;
360         }
361         if (1 != fwrite(data_ptr, img->original_size, 1, bfp)) {
362           fprintf(stderr, "Unable to write %s: %s\n", full_path_name,
363                   strerror(errno));
364           fclose(bfp);
365           fclose(yfp);
366           discard_file(ptr, length);
367           return 1;
368         }
369         fclose(bfp);
370         if (free_data)
371           free(data_ptr);
372       }
373     }
374     offset += sizeof(ImageInfo);
375     offset += img->compressed_size;
376     // 4-byte aligned
377     if ((offset & 3) > 0)
378       offset = (offset & ~3) + 4;
379   }
380   fprintf(yfp, "screens:\n");
381   for(loc_num = 0;
382       loc_num < hdr->number_of_localizations;
383       loc_num++) {
384     for(screen_num = 0;
385         screen_num < hdr->number_of_screenlayouts;
386         screen_num++) {
387       fprintf(yfp, "  scr_%d_%d:\n", loc_num, screen_num);
388       i = loc_num * hdr->number_of_screenlayouts + screen_num;
389       offset = sizeof(BmpBlockHeader) + i * sizeof(ScreenLayout);
390       scr = (ScreenLayout *)(ptr + offset);
391       for(i=0; i<MAX_IMAGE_IN_LAYOUT; i++) {
392         if (scr->images[i].image_info_offset) {
393           ImageInfo *iptr =
394             (ImageInfo *)(ptr + scr->images[i].image_info_offset);
395           if (iptr->tag == TAG_HWID) {
396             fprintf(yfp, "    - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
397                     scr->images[i].x, scr->images[i].y,
398                     RENDER_HWID, iptr->tag, iptr->format, iptr->compression,
399                     iptr->compressed_size, iptr->original_size);
400           } else if (iptr->tag == TAG_HWID_RTOL) {
401             fprintf(yfp, "    - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
402                     scr->images[i].x, scr->images[i].y,
403                     RENDER_HWID_RTOL, iptr->tag,
404                     iptr->format, iptr->compression,
405                     iptr->compressed_size, iptr->original_size);
406           } else {
407             fprintf(yfp, "    - [%d, %d, img_%08x]"
408                     " # tag=%d fmt=%d c=%d %d/%d\n",
409                     scr->images[i].x, scr->images[i].y,
410                     scr->images[i].image_info_offset,
411                     iptr->tag, iptr->format, iptr->compression,
412                     iptr->compressed_size, iptr->original_size);
413           }
414         }
415       }
416     }
417   }
418   fprintf(yfp, "localizations:\n");
419   for(loc_num = 0;
420       loc_num < hdr->number_of_localizations;
421       loc_num++) {
422     fprintf(yfp, "  - [");
423     for(screen_num = 0;
424         screen_num < hdr->number_of_screenlayouts;
425         screen_num++) {
426       fprintf(yfp, " scr_%d_%d", loc_num, screen_num);
427       if (screen_num != hdr->number_of_screenlayouts - 1)
428         fprintf(yfp, ",");
429     }
430     fprintf(yfp, " ]\n");
431   }
432 
433   if (hdr->locale_string_offset) {
434     char *loc_ptr = (char *)ptr + hdr->locale_string_offset;
435     char c;
436     fprintf(yfp, "locale_index:\n");
437     while ((c = *loc_ptr) != '\0') {
438       fprintf(yfp, "  - ");
439       do {
440         fputc(c, yfp);
441         loc_ptr++;
442       } while((c = *loc_ptr) != '\0');
443       loc_ptr++;
444       fputc('\n', yfp);
445     }
446   }
447 
448   if (todir)
449     fclose(yfp);
450 
451   discard_file(ptr, length);
452 
453   return 0;
454 }
455 
456