2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
16 #include <sys/types.h>
17 #include <sys/ioctl.h>
18 #include <sys/mount.h>
19 #include <mtd/mtd-user.h>
29 #include <libubox/list.h>
30 #include <libubox/blob.h>
31 #include <libubox/md5.h>
33 #include "../fs-state.h"
34 #include "../lib/mtd.h"
37 #define OWRT 0x4f575254
38 #define DATA 0x44415441
39 #define CONF 0x434f4e46
50 is_config(struct file_header *h)
52 return ((h->magic == OWRT) && (h->type == CONF));
56 valid_file_size(int fs)
58 if ((fs > 8 * 1024 * 1204) || (fs <= 0))
65 hdr_to_be32(struct file_header *hdr)
67 uint32_t *h = (uint32_t *) hdr;
70 for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
71 h[i] = cpu_to_be32(h[i]);
75 be32_to_hdr(struct file_header *hdr)
77 uint32_t *h = (uint32_t *) hdr;
80 for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
81 h[i] = be32_to_cpu(h[i]);
85 pad_file_size(int size)
89 size += sizeof(struct file_header);
90 mod = size % erasesize;
100 verify_file_hash(char *file, uint32_t *hash)
104 if (md5sum(file, md5)) {
105 fprintf(stderr, "failed to generate md5 sum\n");
109 if (memcmp(md5, hash, sizeof(md5))) {
110 fprintf(stderr, "failed to verify hash of %s.\n", file);
118 snapshot_next_free(int fd, uint32_t *seq)
120 struct file_header hdr = { 0 };
126 if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
127 fprintf(stderr, "scanning for next free block failed\n");
133 if (hdr.magic != OWRT)
136 if (hdr.type == DATA && !valid_file_size(hdr.length)) {
137 if (*seq + 1 != hdr.seq && block)
140 block += pad_file_size(hdr.length) / erasesize;
142 } while (hdr.type == DATA);
148 config_find(int fd, struct file_header *conf, struct file_header *sentinel)
151 int i, next = snapshot_next_free(fd, &seq);
153 conf->magic = sentinel->magic = 0;
155 if (!mtd_read_buffer(fd, conf, next, sizeof(*conf)))
158 for (i = (mtdsize / erasesize) - 1; i > 0; i--) {
159 if (mtd_read_buffer(fd, sentinel, i * erasesize, sizeof(*sentinel))) {
160 fprintf(stderr, "failed to read header\n");
163 be32_to_hdr(sentinel);
165 if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
178 int fd = mtd_load("rootfs_data");
179 struct file_header hdr = { 0 }, conf;
185 fprintf(stderr, "sectors:\t%d, erasesize:\t%dK\n", mtdsize / erasesize, erasesize / 1024);
187 if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
188 fprintf(stderr, "scanning for next free block failed\n");
195 if (hdr.magic != OWRT)
198 if (hdr.type == DATA)
199 fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq);
200 else if (hdr.type == CONF)
201 fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq);
203 if (hdr.type == DATA && !valid_file_size(hdr.length))
204 block += pad_file_size(hdr.length) / erasesize;
205 } while (hdr.type == DATA);
206 block = config_find(fd, &conf, &hdr);
208 fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(hdr.length) / erasesize, hdr.seq);
214 snapshot_write_file(int fd, int block, char *file, uint32_t seq, uint32_t type)
216 uint32_t md5[4] = { 0 };
217 struct file_header hdr;
220 int in = 0, len, offset;
223 if (stat(file, &s) || md5sum(file, md5)) {
224 fprintf(stderr, "stat failed on %s\n", file);
228 if ((block * erasesize) + pad_file_size(s.st_size) > mtdsize) {
229 fprintf(stderr, "upgrade is too big for the flash\n");
232 mtd_erase(fd, block, (pad_file_size(s.st_size) / erasesize));
233 mtd_erase(fd, block + (pad_file_size(s.st_size) / erasesize), 1);
235 hdr.length = s.st_size;
239 memcpy(hdr.md5, md5, sizeof(md5));
242 if (mtd_write_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
243 fprintf(stderr, "failed to write header\n");
247 in = open(file, O_RDONLY);
249 fprintf(stderr, "failed to open %s\n", file);
253 offset = (block * erasesize) + sizeof(struct file_header);
255 while ((len = read(in, buffer, sizeof(buffer))) > 0) {
256 if (mtd_write_buffer(fd, buffer, offset, len) < 0)
271 snapshot_read_file(int fd, int block, char *file, uint32_t type)
273 struct file_header hdr;
277 if (mtd_read_buffer(fd, &hdr, block * erasesize, sizeof(struct file_header))) {
278 fprintf(stderr, "failed to read header\n");
283 if (hdr.magic != OWRT)
286 if (hdr.type != type)
289 if (valid_file_size(hdr.length))
292 out = open(file, O_WRONLY | O_CREAT, 0700);
294 fprintf(stderr, "failed to open %s\n", file);
298 while (hdr.length > 0) {
299 int len = sizeof(buffer);
301 if (hdr.length < len)
304 if ((read(fd, buffer, len) != len) || (write(out, buffer, len) != len)) {
313 if (verify_file_hash(file, hdr.md5)) {
314 fprintf(stderr, "md5 verification failed\n");
319 block += pad_file_size(hdr.length) / erasesize;
325 sentinel_write(int fd, uint32_t _seq)
331 if (stat("/tmp/config.tar.gz", &s)) {
332 fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
336 snapshot_next_free(fd, &seq);
339 block = mtdsize / erasesize;
340 block -= pad_file_size(s.st_size) / erasesize;
344 ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF);
346 fprintf(stderr, "failed to write sentinel\n");
348 fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
353 volatile_write(int fd, uint32_t _seq)
358 block = snapshot_next_free(fd, &seq);
364 ret = snapshot_write_file(fd, block, "/tmp/config.tar.gz", seq, CONF);
366 fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
368 fprintf(stderr, "wrote /tmp/config.tar.gz\n");
373 config_write(int argc, char **argv)
377 fd = mtd_load("rootfs_data");
379 fprintf(stderr, "failed to open rootfs_config\n");
383 ret = volatile_write(fd, 0);
385 ret = sentinel_write(fd, 0);
393 config_read(int argc, char **argv)
395 struct file_header conf, sentinel;
396 int fd, next, block, ret = 0;
399 fd = mtd_load("rootfs_data");
401 fprintf(stderr, "failed to open rootfs_data\n");
405 block = config_find(fd, &conf, &sentinel);
406 next = snapshot_next_free(fd, &seq);
407 if (is_config(&conf) && conf.seq == seq)
409 else if (!is_config(&sentinel) || sentinel.seq != seq)
412 unlink("/tmp/config.tar.gz");
413 ret = snapshot_read_file(fd, block, "/tmp/config.tar.gz", CONF);
416 fprintf(stderr, "failed to read /tmp/config.tar.gz\n");
422 snapshot_write(int argc, char **argv)
427 mtd = mtd_load("rootfs_data");
429 fprintf(stderr, "failed to open rootfs_data\n");
433 block = snapshot_next_free(mtd, &seq);
437 ret = snapshot_write_file(mtd, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
439 fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n");
441 fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n");
449 snapshot_mark(int argc, char **argv)
452 __be32 owrt = cpu_to_be32(OWRT);
456 fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
457 if (getchar() != 'y')
460 if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
461 fprintf(stderr, "no rootfs_data was found\n");
465 fp = fopen(mtd, "w");
466 fprintf(stderr, "%s - marking with 0x4f575254\n", mtd);
468 fprintf(stderr, "opening %s failed\n", mtd);
472 sz = fwrite(&owrt, sizeof(owrt), 1, fp);
476 fprintf(stderr, "writing %s failed: %s\n", mtd, strerror(errno));
484 snapshot_read(int argc, char **argv)
487 int block = 0, fd, ret = 0;
489 fd = mtd_load("rootfs_data");
491 fprintf(stderr, "failed to open rootfs_data\n");
496 block = atoi(argv[1]);
497 if (block >= (mtdsize / erasesize)) {
498 fprintf(stderr, "invalid block %d > %d\n", block, mtdsize / erasesize);
501 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
503 ret = snapshot_read_file(fd, block, file, DATA);
508 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
509 block = snapshot_read_file(fd, block, file, DATA);
520 int fd = mtd_load("rootfs_data");
521 struct file_header sentinel, conf;
528 next = snapshot_next_free(fd, &seq);
529 block = config_find(fd, &conf, &sentinel);
530 if (is_config(&conf) && conf.seq != seq) {
532 mtd_erase(fd, next, 2);
535 if (is_config(&sentinel) && (sentinel.seq != seq)) {
537 mtd_erase(fd, block, 1);
540 if (!is_config(&conf) && !is_config(&sentinel)) {
541 // fprintf(stderr, "no config found\n");
542 } else if (((is_config(&conf) && is_config(&sentinel)) &&
543 (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
544 (is_config(&conf) && !is_config(&sentinel))) {
546 int next = snapshot_next_free(fd, &seq);
547 int ret = snapshot_read_file(fd, next, "/tmp/config.tar.gz", CONF);
549 if (sentinel_write(fd, conf.seq))
550 fprintf(stderr, "failed to write sentinel data");
552 } else if (!is_config(&conf) && is_config(&sentinel) && next) {
553 int ret = snapshot_read_file(fd, block, "/tmp/config.tar.gz", CONF);
555 if (volatile_write(fd, sentinel.seq))
556 fprintf(stderr, "failed to write sentinel data");
558 fprintf(stderr, "config in sync\n");
560 unlink("/tmp/config.tar.gz");
567 _ramoverlay(char *rom, char *overlay)
569 mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
570 return fopivot(overlay, rom);
577 setenv("SNAPSHOT", "magic", 1);
578 _ramoverlay("/rom", "/overlay");
579 system("/sbin/snapshot unpack");
580 foreachdir("/overlay/", handle_whiteout);
581 mkdir("/volatile", 0700);
582 _ramoverlay("/rom", "/volatile");
583 mount_move("/rom/volatile", "/volatile", "");
584 mount_move("/rom/rom", "/rom", "");
585 system("/sbin/snapshot config_unpack");
586 foreachdir("/volatile/", handle_whiteout);
587 unsetenv("SNAPSHOT");
591 static struct backend_handler snapshot_handlers[] = {
593 .name = "config_read",
596 .name = "config_write",
600 .cli = snapshot_read,
603 .cli = snapshot_write,
606 .cli = snapshot_mark,
609 static struct backend snapshot_backend = {
611 .num_handlers = ARRAY_SIZE(snapshot_handlers),
612 .handlers = snapshot_handlers,
613 .mount = snapshot_mount,
614 .info = snapshot_info,
616 BACKEND(snapshot_backend);