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 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++; 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. 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; 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