1/* 2 * confmtd read/write utility functions 3 * 4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: $ 19 */ 20 21#include <stdio.h> 22#include <stdlib.h> 23#include <string.h> 24#include <unistd.h> 25#include <limits.h> 26#include <sys/stat.h> 27#include <fcntl.h> 28#include <errno.h> 29#include <error.h> 30#include <sys/ioctl.h> 31#include <dirent.h> 32 33#ifdef LINUX26 34#include <mtd/mtd-user.h> 35#else /* LINUX26 */ 36#include <linux/mtd/mtd.h> 37#endif /* LINUX26 */ 38 39#include <confmtd_utils.h> 40 41static unsigned short 42confmtd_checksum(const char *data, int datalen) 43{ 44 unsigned short checksum = 0; 45 unsigned short *ptr = (unsigned short *)data; 46 int len = datalen; 47 48 while (len > 0) { 49 if (len == 1) 50 checksum += (*ptr & 0xff00); 51 else 52 checksum += *ptr; 53 ptr++; 54 len -= 2; 55 } 56 return checksum; 57} 58 59/* 60 * Open an MTD device 61 * @param mtd path to or partition name of MTD device 62 * @param flags open() flags 63 * @return return value of open() 64 */ 65int 66confmtd_open(const char *mtd, int flags) 67{ 68 FILE *fp; 69 char dev[PATH_MAX]; 70 int i; 71 72 if ((fp = fopen("/proc/mtd", "r"))) { 73 while (fgets(dev, sizeof(dev), fp)) { 74 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { 75#ifdef LINUX26 76 snprintf(dev, sizeof(dev), "/dev/mtd%d", i); 77#else 78 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i); 79#endif 80 fclose(fp); 81 return open(dev, flags); 82 } 83 } 84 fclose(fp); 85 } 86 87 return open(mtd, flags); 88} 89 90/* 91 * Write a file to an MTD device 92 * @param path file to write or a URL 93 * @param mtd path to or partition name of MTD device 94 * @return 0 on success and errno on failure 95 */ 96int 97confmtd_backup() 98{ 99 char *cmd = "tar cf - confmtd -C /tmp | gzip -c > "CONFMTD_TGZ_TMP_FILE; 100 const char *cp_file = "cp "CONFMTD_TGZ_TMP_FILE" "NAND_DIR; 101 mtd_info_t mtd_info; 102 erase_info_t erase_info; 103 int mtd_fd = -1; 104 char *buf = NULL; 105 char *tmp_buf = NULL; 106 struct stat tmp_stat; 107 int ret = -1; 108 confmtd_hdr_t mtd_hdr; 109 DIR *dir; 110 int fd = -1; 111 112 /* backup confmtd directiries to raw partition */ 113 unlink(CONFMTD_TGZ_TMP_FILE); 114 115 if ((dir = opendir(NAND_DIR))) { 116 closedir(dir); 117 system(cmd); 118 system(cp_file); 119 120 unlink(CONFMTD_TGZ_TMP_FILE); 121 122 return 0; 123 } 124 125 if (!(dir = opendir(RAMFS_CONFMTD_DIR))) { 126 fprintf(stderr, "Cannot find %s\n", RAMFS_CONFMTD_DIR); 127 return -1; 128 } else { 129 closedir(dir); 130 } 131 132 /* Open MTD device and get sector size */ 133 if ((mtd_fd = confmtd_open("confmtd", O_RDWR)) < 0 || 134 ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) { 135 perror("confmtd"); 136 goto fail; 137 } 138 139 /* create as tar file */ 140 errno = 0; 141 142 system(cmd); 143 144 if ((fd = open(CONFMTD_TGZ_TMP_FILE, O_RDONLY)) < 0) { 145 fprintf(stderr, "Open %s fail\n", CONFMTD_TGZ_TMP_FILE); 146 goto fail; 147 } 148 149 if (fstat(fd, &tmp_stat)) { 150 perror("tgz"); 151 goto fail; 152 } 153 154 if ((tmp_stat.st_size + sizeof(confmtd_hdr_t)) > mtd_info.size || tmp_stat.st_size == 0) { 155 perror("size"); 156 goto fail; 157 } 158 159 /* Allocate temporary buffer */ 160 if ((tmp_buf = malloc(tmp_stat.st_size)) == NULL) { 161 perror("malloc"); 162 goto fail; 163 } 164 165 if ((buf = malloc(tmp_stat.st_size + sizeof(confmtd_hdr_t))) == NULL) { 166 perror("malloc"); 167 goto fail; 168 } 169 170 if (read(fd, tmp_buf, tmp_stat.st_size) != tmp_stat.st_size) { 171 fprintf(stderr, "read %s: size mismatch\n", CONFMTD_TGZ_TMP_FILE); 172 goto fail; 173 } 174 175 erase_info.start = 0; 176 erase_info.length = mtd_info.size; 177 178 /* create mtd header content */ 179 memset(&mtd_hdr, 0, sizeof(mtd_hdr)); 180 snprintf(&mtd_hdr.magic, sizeof(mtd_hdr.magic), "%s", CONFMTD_MAGIC); 181 mtd_hdr.len = tmp_stat.st_size; 182 mtd_hdr.checksum = confmtd_checksum(tmp_buf, tmp_stat.st_size); 183 184 memcpy(buf, &mtd_hdr, sizeof(confmtd_hdr_t)); 185 memcpy((buf + sizeof(confmtd_hdr_t)), tmp_buf, tmp_stat.st_size); 186 187 /* Do it */ 188 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info); 189 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 || 190 write(mtd_fd, buf, (sizeof(confmtd_hdr_t) + tmp_stat.st_size)) != 191 (sizeof(confmtd_hdr_t) + tmp_stat.st_size)) { 192 perror("write"); 193 goto fail; 194 } 195 196 printf("update confmtd partition OK\n"); 197 ret = 0; 198 199fail: 200 if (tmp_buf) 201 free(tmp_buf); 202 203 if (buf) 204 free(buf); 205 206 if (mtd_fd >= 0) 207 close(mtd_fd); 208 209 if (fd >= 0) 210 close(fd); 211 212 unlink(CONFMTD_TGZ_TMP_FILE); 213 214 return ret; 215} 216 217/* 218 * Write a file to an MTD device 219 * @param path file to write or a URL 220 * @param mtd path to or partition name of MTD device 221 * @return 0 on success and errno on failure 222 */ 223int 224confmtd_restore() 225{ 226 char *cmd = "gunzip -c "CONFMTD_TGZ_TMP_FILE" | tar xf - -C /tmp"; 227 const char *cp_file = "cp "NAND_FILE" "CONFMTD_TGZ_TMP_FILE; 228 mtd_info_t mtd_info; 229 int mtd_fd = -1; 230 FILE *fp = NULL; 231 char *buf = NULL; 232 int ret = -1; 233 confmtd_hdr_t mtd_hdr = {0}; 234 struct stat tmp_stat; 235 DIR *dir; 236 237 /* create confmtd directory */ 238 if (mkdir(RAMFS_CONFMTD_DIR, 0777) < 0 && errno != EEXIST) { 239 fprintf(stderr, "%s not created\n", RAMFS_CONFMTD_DIR); 240 return ret; 241 } 242 243 if ((dir = opendir(NAND_DIR))) { 244 closedir(dir); 245 if (stat(NAND_FILE, &tmp_stat)) { 246 perror(NAND_FILE); 247 return ret; 248 } 249 system(cp_file); 250 system(cmd); 251 252 unlink(CONFMTD_TGZ_TMP_FILE); 253 254 return 0; 255 } 256 257 /* Open MTD device and get sector size */ 258 if ((mtd_fd = confmtd_open("confmtd", O_RDWR)) < 0 || 259 ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) { 260 printf("mtd\n"); 261 goto fail; 262 } 263 264 read(mtd_fd, &mtd_hdr, sizeof(confmtd_hdr_t)); 265 266 if (strcmp(&mtd_hdr.magic, CONFMTD_MAGIC) != 0) { 267 printf("magic incorrect\n"); 268 goto fail; 269 } 270 271 if (mtd_hdr.len > mtd_info.size) { 272 printf("size too long\n"); 273 goto fail; 274 } 275 276 /* Allocate temporary buffer */ 277 if ((buf = malloc(mtd_hdr.len)) == NULL) { 278 printf("buffer\n"); 279 goto fail; 280 } 281 read(mtd_fd, buf, mtd_hdr.len); 282 283 if (confmtd_checksum(buf, mtd_hdr.len) != mtd_hdr.checksum) { 284 printf("checksum\n"); 285 goto fail; 286 } 287 288 /* write mtd data to tar file */ 289 if ((fp = fopen(CONFMTD_TGZ_TMP_FILE, "w")) == NULL) { 290 printf("%s: can't open file\n", CONFMTD_TGZ_TMP_FILE); 291 goto fail; 292 } 293 fwrite(buf, 1, mtd_hdr.len, fp); 294 fclose(fp); 295 296 /* untar confmtd directory */ 297 system(cmd); 298 299 unlink(CONFMTD_TGZ_TMP_FILE); 300 301 ret = 0; 302fail: 303 if (buf) 304 free(buf); 305 306 if (mtd_fd >= 0) 307 close(mtd_fd); 308 309 return ret; 310} 311