1 /* Copyright 2015 The ChromiumOS Authors
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 #if !defined(__FreeBSD__)
12 #include <linux/major.h>
13 #endif
14 #include <stdbool.h>
15 #include <stdarg.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 <sys/wait.h>
23 #include <unistd.h>
24
25 #include "cgpt.h"
26 #include "cgpt_nor.h"
27 #include "subprocess.h"
28
29 static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
30
31 // Obtain the MTD size from its sysfs node.
GetMtdSize(const char * mtd_device,uint64_t * size)32 int GetMtdSize(const char *mtd_device, uint64_t *size) {
33 mtd_device = strrchr(mtd_device, '/');
34 if (mtd_device == NULL) {
35 errno = EINVAL;
36 return 1;
37 }
38 char *sysfs_name;
39 if (asprintf(&sysfs_name, "/sys/class/mtd%s/size", mtd_device) == -1) {
40 return 1;
41 }
42 FILE *fp = fopen(sysfs_name, "r");
43 free(sysfs_name);
44 if (fp == NULL) {
45 return 1;
46 }
47 int ret = (fscanf(fp, "%" PRIu64 "\n", size) != 1);
48 fclose(fp);
49 return ret;
50 }
51
52 // TODO(b:184812319): Remove these functions and use subprocess_run everywhere.
ForkExecV(const char * cwd,const char * const argv[])53 int ForkExecV(const char *cwd, const char *const argv[]) {
54 pid_t pid = fork();
55 if (pid == -1) {
56 return -1;
57 }
58 int status = -1;
59 if (pid == 0) {
60 if (cwd && chdir(cwd) != 0) {
61 return -1;
62 }
63 execv(argv[0], (char *const *)argv);
64 // If this is reached, execv fails.
65 err(-1, "Cannot exec %s in %s.", argv[0], cwd);
66 } else {
67 if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status))
68 return WEXITSTATUS(status);
69 }
70 return status;
71 }
72
ForkExecL(const char * cwd,const char * cmd,...)73 static int ForkExecL(const char *cwd, const char *cmd, ...) {
74 int argc;
75 va_list ap;
76 va_start(ap, cmd);
77 for (argc = 1; va_arg(ap, char *) != NULL; ++argc);
78 va_end(ap);
79
80 va_start(ap, cmd);
81 const char **argv = calloc(argc + 1, sizeof(char *));
82 if (argv == NULL) {
83 errno = ENOMEM;
84 va_end(ap);
85 return -1;
86 }
87 argv[0] = cmd;
88 int i;
89 for (i = 1; i < argc; ++i) {
90 argv[i] = va_arg(ap, char *);
91 }
92 va_end(ap);
93
94 int ret = ForkExecV(cwd, argv);
95 free(argv);
96 return ret;
97 }
98
read_write(int source_fd,uint64_t size,const char * src_name,int idx)99 static int read_write(int source_fd,
100 uint64_t size,
101 const char *src_name,
102 int idx) {
103 int ret = 1;
104 const int bufsize = 4096;
105 char *buf = malloc(bufsize);
106 if (buf == NULL) {
107 goto clean_exit;
108 }
109
110 ret++;
111 char *dest;
112 if (asprintf(&dest, "%s_%d", src_name, idx) == -1) {
113 goto free_buf;
114 }
115
116 ret++;
117 int dest_fd = open(dest, O_WRONLY | O_CLOEXEC | O_CREAT, 0600);
118 if (dest_fd < 0) {
119 goto free_dest;
120 }
121
122 ret++;
123 uint64_t copied = 0;
124 ssize_t nr_read;
125 ssize_t nr_write;
126 while (copied < size) {
127 size_t to_read = size - copied;
128 if (to_read > bufsize) {
129 to_read = bufsize;
130 }
131 nr_read = read(source_fd, buf, to_read);
132 if (nr_read < 0) {
133 goto close_dest_fd;
134 }
135 nr_write = 0;
136 while (nr_write < nr_read) {
137 ssize_t s = write(dest_fd, buf + nr_write, nr_read - nr_write);
138 if (s < 0) {
139 goto close_dest_fd;
140 }
141 nr_write += s;
142 }
143 copied += nr_read;
144 }
145
146 ret = 0;
147
148 close_dest_fd:
149 close(dest_fd);
150 free_dest:
151 free(dest);
152 free_buf:
153 free(buf);
154 clean_exit:
155 return ret;
156 }
157
split_gpt(const char * dir_name,const char * file_name)158 static int split_gpt(const char *dir_name, const char *file_name) {
159 int ret = 1;
160 char *source;
161 if (asprintf(&source, "%s/%s", dir_name, file_name) == -1) {
162 goto clean_exit;
163 }
164
165 ret++;
166 int fd = open(source, O_RDONLY | O_CLOEXEC);
167 if (fd < 0) {
168 goto free_source;
169 }
170
171 ret++;
172 struct stat stat;
173 if (fstat(fd, &stat) != 0 || (stat.st_size & 1) != 0) {
174 goto close_fd;
175 }
176 uint64_t half_size = stat.st_size / 2;
177
178 ret++;
179 if (read_write(fd, half_size, source, 1) != 0 ||
180 read_write(fd, half_size, source, 2) != 0) {
181 goto close_fd;
182 }
183
184 ret = 0;
185 close_fd:
186 close(fd);
187 free_source:
188 free(source);
189 clean_exit:
190 return ret;
191 }
192
remove_file_or_dir(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)193 static int remove_file_or_dir(const char *fpath, const struct stat *sb,
194 int typeflag, struct FTW *ftwbuf) {
195 return remove(fpath);
196 }
197
RemoveDir(const char * dir)198 int RemoveDir(const char *dir) {
199 return nftw(dir, remove_file_or_dir, 20, FTW_DEPTH | FTW_PHYS);
200 }
201
202 #define FLASHROM_RW_GPT_PRI "RW_GPT_PRIMARY:rw_gpt_1",
203 #define FLASHROM_RW_GPT_SEC "RW_GPT_SECONDARY:rw_gpt_2"
204 #define FLASHROM_RW_GPT "RW_GPT:rw_gpt"
205
206 // Read RW_GPT from NOR flash to "rw_gpt" in a dir.
207 // TODO(b:184812319): Replace this function with flashrom_read.
ReadNorFlash(const char * dir)208 int ReadNorFlash(const char *dir) {
209 int ret = 0;
210
211 // Read RW_GPT section from NOR flash to "rw_gpt".
212 ret++;
213
214 char *cwd = getcwd(NULL, 0);
215 if (!cwd) {
216 Error("Cannot get current directory.\n");
217 return ret;
218 }
219 if (chdir(dir) < 0) {
220 Error("Cannot change directory.\n");
221 goto out_free;
222 }
223 const char *const argv[] = {FLASHROM_PATH, "-i", FLASHROM_RW_GPT, "-r"};
224 // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
225 // output.
226 if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
227 Error("Cannot exec flashrom to read from RW_GPT section.\n");
228 } else {
229 ret = 0;
230 }
231 if (chdir(cwd) < 0) {
232 Error("Cannot change directory back to original.\n");
233 goto out_free;
234 }
235
236 out_free:
237 free(cwd);
238 return ret;
239 }
240
FlashromWriteRegion(const char * region)241 static int FlashromWriteRegion(const char *region)
242 {
243 const char *const argv[] = {FLASHROM_PATH, "-i", region, "-w", "--noverify-all"};
244 // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
245 // output.
246 if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
247 Warning("Cannot write '%s' back with flashrom.\n", region);
248 return 1;
249 }
250 return 0;
251 }
252
253 // Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
254 // TODO(b:184812319): Replace this function with flashrom_write.
WriteNorFlash(const char * dir)255 int WriteNorFlash(const char *dir) {
256 int ret = 0;
257
258 ret++;
259 if (split_gpt(dir, "rw_gpt") != 0) {
260 Error("Cannot split rw_gpt in two.\n");
261 return ret;
262 }
263 ret++;
264 int nr_fails = 0;
265
266 char *cwd = getcwd(NULL, 0);
267 if (!cwd) {
268 Error("Cannot get current directory.\n");
269 return ret;
270 }
271 if (chdir(dir) < 0) {
272 Error("Cannot change directory.\n");
273 goto out_free;
274 }
275 if (FlashromWriteRegion(FLASHROM_RW_GPT_PRI))
276 nr_fails++;
277 if (FlashromWriteRegion(FLASHROM_RW_GPT_SEC))
278 nr_fails++;
279
280 if (chdir(cwd) < 0) {
281 Error("Cannot change directory back to original.\n");
282 goto out_free;
283 }
284 switch (nr_fails) {
285 case 0: ret = 0; break;
286 case 1: Warning("It might still be okay.\n"); break;
287 case 2: Error("Cannot write both parts back with flashrom.\n"); break;
288 }
289
290 out_free:
291 free(cwd);
292 return ret;
293 }
294