Home | History | Annotate | Download | only in cgpt
      1 /* Copyright 2015 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  * This utility wraps around "cgpt" execution to work with NAND. If the target
      6  * device is an MTD device, this utility will read the GPT structures from
      7  * FMAP, invokes "cgpt" on that, and writes the result back to NOR flash. */
      8 
      9 #include <err.h>
     10 #include <errno.h>
     11 #include <fcntl.h>
     12 #include <inttypes.h>
     13 #include <limits.h>
     14 #include <linux/major.h>
     15 #include <stdbool.h>
     16 #include <stdlib.h>
     17 #include <stdint.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 
     24 #include "cgpt.h"
     25 #include "cgpt_nor.h"
     26 #include "cryptolib.h"
     27 
     28 // Check if cmdline |argv| has "-D". "-D" signifies that GPT structs are stored
     29 // off device, and hence we should not wrap around cgpt.
     30 static bool has_dash_D(int argc, const char *const argv[]) {
     31   int i;
     32   // We go from 2, because the second arg is a cgpt command such as "create".
     33   for (i = 2; i < argc; ++i) {
     34     if (strcmp("-D", argv[i]) == 0) {
     35       return true;
     36     }
     37   }
     38   return false;
     39 }
     40 
     41 // Check if |device_path| is an MTD device based on its major number being 90.
     42 static bool is_mtd(const char *device_path) {
     43   struct stat stat;
     44   if (lstat(device_path, &stat) != 0) {
     45     return false;
     46   }
     47 
     48   if (major(stat.st_rdev) != MTD_CHAR_MAJOR) {
     49     return false;
     50   }
     51 
     52   return true;
     53 }
     54 
     55 // Return the element in |argv| that is an MTD device.
     56 static const char *find_mtd_device(int argc, const char *const argv[]) {
     57   int i;
     58   for (i = 2; i < argc; ++i) {
     59     if (is_mtd(argv[i])) {
     60       return argv[i];
     61     }
     62   }
     63   return NULL;
     64 }
     65 
     66 static int wrap_cgpt(int argc,
     67                      const char *const argv[],
     68                      const char *mtd_device) {
     69   uint8_t *original_hash = NULL;
     70   uint8_t *modified_hash = NULL;
     71   int ret = 0;
     72 
     73   // Create a temp dir to work in.
     74   ret++;
     75   char temp_dir[] = "/tmp/cgpt_wrapper.XXXXXX";
     76   if (ReadNorFlash(temp_dir) != 0) {
     77     return ret;
     78   }
     79   char rw_gpt_path[PATH_MAX];
     80   if (snprintf(rw_gpt_path, sizeof(rw_gpt_path), "%s/rw_gpt", temp_dir) < 0) {
     81     goto cleanup;
     82   }
     83   original_hash = DigestFile(rw_gpt_path, SHA1_DIGEST_ALGORITHM);
     84 
     85   // Obtain the MTD size.
     86   ret++;
     87   uint64_t drive_size = 0;
     88   if (GetMtdSize(mtd_device, &drive_size) != 0) {
     89     Error("Cannot get the size of %s.\n", mtd_device);
     90     goto cleanup;
     91   }
     92 
     93   // Launch cgpt on "rw_gpt" with -D size.
     94   ret++;
     95   const char** my_argv = calloc(argc + 2 + 1, sizeof(char *));
     96   if (my_argv == NULL) {
     97     errno = ENOMEM;
     98     goto cleanup;
     99   }
    100   memcpy(my_argv, argv, sizeof(char *) * argc);
    101   char *real_cgpt;
    102   if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
    103     free(my_argv);
    104     goto cleanup;
    105   }
    106   my_argv[0] = real_cgpt;
    107 
    108   int i;
    109   for (i = 2; i < argc; ++i) {
    110     if (strcmp(my_argv[i], mtd_device) == 0) {
    111       my_argv[i] = rw_gpt_path;
    112     }
    113   }
    114   my_argv[argc] = "-D";
    115   char size[32];
    116   snprintf(size, sizeof(size), "%" PRIu64, drive_size);
    117   my_argv[argc + 1] = size;
    118   i = ForkExecV(NULL, my_argv);
    119   free(real_cgpt);
    120   free(my_argv);
    121   if (i != 0) {
    122     Error("Cannot exec cgpt to modify rw_gpt.\n");
    123     goto cleanup;
    124   }
    125 
    126   // Write back "rw_gpt" to NOR flash in two chunks.
    127   ret++;
    128   modified_hash = DigestFile(rw_gpt_path, SHA1_DIGEST_ALGORITHM);
    129   if (original_hash != NULL && modified_hash != NULL) {
    130     if (memcmp(original_hash, modified_hash, SHA1_DIGEST_SIZE) != 0) {
    131       ret = WriteNorFlash(temp_dir);
    132     } else {
    133       ret = 0;
    134     }
    135   }
    136 
    137 cleanup:
    138   free(original_hash);
    139   free(modified_hash);
    140   RemoveDir(temp_dir);
    141   return ret;
    142 }
    143 
    144 int main(int argc, const char *argv[]) {
    145   char resolved_cgpt[PATH_MAX];
    146   pid_t pid = getpid();
    147   char exe_link[40];
    148 
    149   if (argc < 1) {
    150     return -1;
    151   }
    152 
    153   snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
    154   memset(resolved_cgpt, 0, sizeof(resolved_cgpt));
    155   if (readlink(exe_link, resolved_cgpt, sizeof(resolved_cgpt) - 1) == -1) {
    156     perror("readlink");
    157     return -1;
    158   }
    159 
    160   argv[0] = resolved_cgpt;
    161 
    162   if (argc > 2 && !has_dash_D(argc, argv)) {
    163     const char *mtd_device = find_mtd_device(argc, argv);
    164     if (mtd_device) {
    165       return wrap_cgpt(argc, argv, mtd_device);
    166     }
    167   }
    168 
    169   // Forward to cgpt as-is. Real cgpt has been renamed cgpt.bin.
    170   char *real_cgpt;
    171   if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
    172     return -1;
    173   }
    174   argv[0] = real_cgpt;
    175   if (execv(argv[0], (char * const *)argv) == -1) {
    176     err(-2, "execv(%s) failed", real_cgpt);
    177   }
    178   return -2;
    179 }
    180