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