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