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 
      6 #include <err.h>
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <ftw.h>
     10 #include <inttypes.h>
     11 #include <linux/major.h>
     12 #include <stdbool.h>
     13 #include <stdarg.h>
     14 #include <stdlib.h>
     15 #include <stdint.h>
     16 #include <stdio.h>
     17 #include <string.h>
     18 #include <sys/stat.h>
     19 #include <sys/types.h>
     20 #include <sys/wait.h>
     21 #include <unistd.h>
     22 
     23 #include "cgpt.h"
     24 #include "cgpt_nor.h"
     25 
     26 static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
     27 
     28 // Obtain the MTD size from its sysfs node.
     29 int GetMtdSize(const char *mtd_device, uint64_t *size) {
     30   mtd_device = strrchr(mtd_device, '/');
     31   if (mtd_device == NULL) {
     32     errno = EINVAL;
     33     return 1;
     34   }
     35   char *sysfs_name;
     36   if (asprintf(&sysfs_name, "/sys/class/mtd%s/size", mtd_device) == -1) {
     37     return 1;
     38   }
     39   FILE *fp = fopen(sysfs_name, "r");
     40   free(sysfs_name);
     41   if (fp == NULL) {
     42     return 1;
     43   }
     44   int ret = (fscanf(fp, "%" PRIu64 "\n", size) != 1);
     45   fclose(fp);
     46   return ret;
     47 }
     48 
     49 int ForkExecV(const char *cwd, const char *const argv[]) {
     50   pid_t pid = fork();
     51   if (pid == -1) {
     52     return -1;
     53   }
     54   int status = -1;
     55   if (pid == 0) {
     56     if (cwd && chdir(cwd) != 0) {
     57       return -1;
     58     }
     59     execv(argv[0], (char *const *)argv);
     60     // If this is reached, execv fails.
     61     err(-1, "Cannot exec %s in %s.", argv[0], cwd);
     62   } else {
     63     if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status))
     64       return WEXITSTATUS(status);
     65   }
     66   return status;
     67 }
     68 
     69 int ForkExecL(const char *cwd, const char *cmd, ...) {
     70   int argc;
     71   va_list ap;
     72   va_start(ap, cmd);
     73   for (argc = 1; va_arg(ap, char *) != NULL; ++argc);
     74   va_end(ap);
     75 
     76   va_start(ap, cmd);
     77   const char **argv = calloc(argc + 1, sizeof(char *));
     78   if (argv == NULL) {
     79     errno = ENOMEM;
     80     return -1;
     81   }
     82   argv[0] = cmd;
     83   int i;
     84   for (i = 1; i < argc; ++i) {
     85     argv[i] = va_arg(ap, char *);
     86   }
     87   va_end(ap);
     88 
     89   int ret = ForkExecV(cwd, argv);
     90   free(argv);
     91   return ret;
     92 }
     93 
     94 static int read_write(int source_fd,
     95                       uint64_t size,
     96                       const char *src_name,
     97                       int idx) {
     98   int ret = 1;
     99   const int bufsize = 4096;
    100   char *buf = malloc(bufsize);
    101   if (buf == NULL) {
    102     goto clean_exit;
    103   }
    104 
    105   ret++;
    106   char *dest;
    107   if (asprintf(&dest, "%s_%d", src_name, idx) == -1) {
    108     goto free_buf;
    109   }
    110 
    111   ret++;
    112   int dest_fd = open(dest, O_WRONLY | O_CLOEXEC | O_CREAT, 0600);
    113   if (dest_fd < 0) {
    114     goto free_dest;
    115   }
    116 
    117   ret++;
    118   uint64_t copied = 0;
    119   ssize_t nr_read;
    120   ssize_t nr_write;
    121   while (copied < size) {
    122     size_t to_read = size - copied;
    123     if (to_read > bufsize) {
    124       to_read = bufsize;
    125     }
    126     nr_read = read(source_fd, buf, to_read);
    127     if (nr_read < 0) {
    128       goto close_dest_fd;
    129     }
    130     nr_write = 0;
    131     while (nr_write < nr_read) {
    132       ssize_t s = write(dest_fd, buf + nr_write, nr_read - nr_write);
    133       if (s < 0) {
    134         goto close_dest_fd;
    135       }
    136       nr_write += s;
    137     }
    138     copied += nr_read;
    139   }
    140 
    141   ret = 0;
    142 
    143 close_dest_fd:
    144   close(dest_fd);
    145 free_dest:
    146   free(dest);
    147 free_buf:
    148   free(buf);
    149 clean_exit:
    150   return ret;
    151 }
    152 
    153 static int split_gpt(const char *dir_name, const char *file_name) {
    154   int ret = 1;
    155   char *source;
    156   if (asprintf(&source, "%s/%s", dir_name, file_name) == -1) {
    157     goto clean_exit;
    158   }
    159 
    160   ret++;
    161   int fd = open(source, O_RDONLY | O_CLOEXEC);
    162   if (fd < 0) {
    163     goto free_source;
    164   }
    165 
    166   ret++;
    167   struct stat stat;
    168   if (fstat(fd, &stat) != 0 || (stat.st_size & 1) != 0) {
    169     goto close_fd;
    170   }
    171   uint64_t half_size = stat.st_size / 2;
    172 
    173   ret++;
    174   if (read_write(fd, half_size, source, 1) != 0 ||
    175       read_write(fd, half_size, source, 2) != 0) {
    176     goto close_fd;
    177   }
    178 
    179   ret = 0;
    180 close_fd:
    181   close(fd);
    182 free_source:
    183   free(source);
    184 clean_exit:
    185   return ret;
    186 }
    187 
    188 static int remove_file_or_dir(const char *fpath, const struct stat *sb,
    189                               int typeflag, struct FTW *ftwbuf) {
    190   return remove(fpath);
    191 }
    192 
    193 int RemoveDir(const char *dir) {
    194   return nftw(dir, remove_file_or_dir, 20, FTW_DEPTH | FTW_PHYS);
    195 }
    196 
    197 // Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
    198 // |temp_dir_template| is passed to mkdtemp() so it must satisfy all
    199 // requirements by mkdtemp.
    200 int ReadNorFlash(char *temp_dir_template) {
    201   int ret = 0;
    202 
    203   // Create a temp dir to work in.
    204   ret++;
    205   if (mkdtemp(temp_dir_template) == NULL) {
    206     Error("Cannot create a temporary directory.\n");
    207     return ret;
    208   }
    209 
    210   // Read RW_GPT section from NOR flash to "rw_gpt".
    211   ret++;
    212   int fd_flags = fcntl(1, F_GETFD);
    213   // Close stdout on exec so that flashrom does not muck up cgpt's output.
    214   fcntl(1, F_SETFD, FD_CLOEXEC);
    215   if (ForkExecL(temp_dir_template, FLASHROM_PATH, "-i", "RW_GPT:rw_gpt", "-r",
    216                 NULL) != 0) {
    217     Error("Cannot exec flashrom to read from RW_GPT section.\n");
    218     RemoveDir(temp_dir_template);
    219   } else {
    220     ret = 0;
    221   }
    222 
    223   fcntl(1, F_SETFD, fd_flags);
    224   return ret;
    225 }
    226 
    227 // Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
    228 int WriteNorFlash(const char *dir) {
    229   int ret = 0;
    230   ret++;
    231   if (split_gpt(dir, "rw_gpt") != 0) {
    232     Error("Cannot split rw_gpt in two.\n");
    233     return ret;
    234   }
    235   ret++;
    236   int nr_fails = 0;
    237   int fd_flags = fcntl(1, F_GETFD);
    238   // Close stdout on exec so that flashrom does not muck up cgpt's output.
    239   fcntl(1, F_SETFD, FD_CLOEXEC);
    240   if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_PRIMARY:rw_gpt_1",
    241                 "-w", "--fast-verify", NULL) != 0) {
    242     Warning("Cannot write the 1st half of rw_gpt back with flashrom.\n");
    243     nr_fails++;
    244   }
    245   if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_SECONDARY:rw_gpt_2",
    246                 "-w", "--fast-verify", NULL) != 0) {
    247     Warning("Cannot write the 2nd half of rw_gpt back with flashrom.\n");
    248     nr_fails++;
    249   }
    250   fcntl(1, F_SETFD, fd_flags);
    251   switch (nr_fails) {
    252     case 0: ret = 0; break;
    253     case 1: Warning("It might still be okay.\n"); break;
    254     case 2: Error("Cannot write both parts back with flashrom.\n"); break;
    255   }
    256   return ret;
    257 }
    258