• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetMtdSize(const char * mtd_device,uint64_t * size)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  
ForkExecV(const char * cwd,const char * const argv[])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  
ForkExecL(const char * cwd,const char * cmd,...)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  
read_write(int source_fd,uint64_t size,const char * src_name,int idx)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  
split_gpt(const char * dir_name,const char * file_name)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  
remove_file_or_dir(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)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  
RemoveDir(const char * dir)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.
ReadNorFlash(char * temp_dir_template)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.
WriteNorFlash(const char * dir)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