Home | History | Annotate | Download | only in utility
      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.
     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().
     62 static void discard_file(void *ptr, size_t length) {
     63   munmap(ptr, length);
     64 }
     65 
     66 //////////////////////////////////////////////////////////////////////////////
     67 
     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 
     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 
    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.
    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