blob: bf0a1764af72359817dbca9797f9994da019f8f7 [file] [log] [blame]
Nam T. Nguyend1236e42015-01-08 11:43:27 -08001/* 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
26static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
27
28// Obtain the MTD size from its sysfs node.
29int 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
49int 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
69int 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
94static 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
143close_dest_fd:
144 close(dest_fd);
145free_dest:
146 free(dest);
147free_buf:
148 free(buf);
149clean_exit:
150 return ret;
151}
152
153static 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;
180close_fd:
181 close(fd);
182free_source:
183 free(source);
184clean_exit:
185 return ret;
186}
187
188static int remove_file_or_dir(const char *fpath, const struct stat *sb,
189 int typeflag, struct FTW *ftwbuf) {
190 return remove(fpath);
191}
192
193int 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.
200int 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++;
Nam T. Nguyen616153a2015-01-21 14:37:10 -0800212 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);
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800215 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);
Nam T. Nguyen616153a2015-01-21 14:37:10 -0800219 } else {
220 ret = 0;
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800221 }
222
Nam T. Nguyen616153a2015-01-21 14:37:10 -0800223 fcntl(1, F_SETFD, fd_flags);
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800224 return ret;
225}
226
227// Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
228int 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;
Nam T. Nguyen616153a2015-01-21 14:37:10 -0800237 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);
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800240 if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_PRIMARY:rw_gpt_1",
Nam T. Nguyen4e1a9562015-01-21 10:04:38 -0800241 "-w", "--fast-verify", NULL) != 0) {
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800242 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",
Nam T. Nguyen4e1a9562015-01-21 10:04:38 -0800246 "-w", "--fast-verify", NULL) != 0) {
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800247 Warning("Cannot write the 2nd half of rw_gpt back with flashrom.\n");
248 nr_fails++;
249 }
Nam T. Nguyen616153a2015-01-21 14:37:10 -0800250 fcntl(1, F_SETFD, fd_flags);
Nam T. Nguyend1236e42015-01-08 11:43:27 -0800251 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}