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"
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(struct volume *v, int size)
89 size += sizeof(struct file_header);
90 mod = size % v->block_size;
93 size += v->block_size;
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(struct volume *v, uint32_t *seq)
120 struct file_header hdr = { 0 };
126 if (volume_read(v, &hdr, block * v->block_size, 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(v, hdr.length) / v->block_size;
142 } while (hdr.type == DATA);
148 config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
151 int i, next = snapshot_next_free(v, &seq);
153 conf->magic = sentinel->magic = 0;
155 if (!volume_read(v, conf, next, sizeof(*conf)))
158 for (i = (v->size / v->block_size) - 1; i > 0; i--) {
159 if (volume_read(v, sentinel, i * v->block_size, 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 struct volume *v = volume_find("rootfs_data");
179 struct file_header hdr = { 0 }, conf;
185 fprintf(stderr, "sectors:\t%llu, block_size:\t%dK\n", v->size / v->block_size, v->block_size / 1024);
187 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
188 fprintf(stderr, "scanning for next free block failed\n");
194 if (hdr.magic != OWRT)
197 if (hdr.type == DATA)
198 fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
199 else if (hdr.type == CONF)
200 fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
202 if (hdr.type == DATA && !valid_file_size(hdr.length))
203 block += pad_file_size(v, hdr.length) / v->block_size;
204 } while (hdr.type == DATA);
205 block = config_find(v, &conf, &hdr);
207 fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
213 snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
215 uint32_t md5[4] = { 0 };
216 struct file_header hdr;
219 int in = 0, len, offset;
222 if (stat(file, &s) || md5sum(file, md5)) {
223 fprintf(stderr, "stat failed on %s\n", file);
227 if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
228 fprintf(stderr, "upgrade is too big for the flash\n");
231 volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
232 volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
234 hdr.length = s.st_size;
238 memcpy(hdr.md5, md5, sizeof(md5));
241 if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
242 fprintf(stderr, "failed to write header\n");
246 in = open(file, O_RDONLY);
248 fprintf(stderr, "failed to open %s\n", file);
252 offset = (block * v->block_size) + sizeof(struct file_header);
254 while ((len = read(in, buffer, sizeof(buffer))) > 0) {
255 if (volume_write(v, buffer, offset, len) < 0)
270 snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
272 struct file_header hdr;
276 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
277 fprintf(stderr, "failed to read header\n");
282 if (hdr.magic != OWRT)
285 if (hdr.type != type)
288 if (valid_file_size(hdr.length))
291 out = open(file, O_WRONLY | O_CREAT, 0700);
293 fprintf(stderr, "failed to open %s\n", file);
297 while (hdr.length > 0) {
298 int len = sizeof(buffer);
300 if (hdr.length < len)
303 if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
312 if (verify_file_hash(file, hdr.md5)) {
313 fprintf(stderr, "md5 verification failed\n");
318 block += pad_file_size(v, hdr.length) / v->block_size;
324 sentinel_write(struct volume *v, uint32_t _seq)
330 if (stat("/tmp/config.tar.gz", &s)) {
331 fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
335 snapshot_next_free(v, &seq);
338 block = v->size / v->block_size;
339 block -= pad_file_size(v, s.st_size) / v->block_size;
343 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
345 fprintf(stderr, "failed to write sentinel\n");
347 fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
352 volatile_write(struct volume *v, uint32_t _seq)
357 block = snapshot_next_free(v, &seq);
363 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
365 fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
367 fprintf(stderr, "wrote /tmp/config.tar.gz\n");
372 config_write(int argc, char **argv)
374 struct volume *v = volume_find("rootfs_data");
380 ret = volatile_write(v, 0);
382 ret = sentinel_write(v, 0);
388 config_read(int argc, char **argv)
390 struct volume *v = volume_find("rootfs_data");
391 struct file_header conf, sentinel;
392 int next, block, ret = 0;
398 block = config_find(v, &conf, &sentinel);
399 next = snapshot_next_free(v, &seq);
400 if (is_config(&conf) && conf.seq == seq)
402 else if (!is_config(&sentinel) || sentinel.seq != seq)
405 unlink("/tmp/config.tar.gz");
406 ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
409 fprintf(stderr, "failed to read /tmp/config.tar.gz\n");
415 snapshot_write(int argc, char **argv)
417 struct volume *v = volume_find("rootfs_data");
424 block = snapshot_next_free(v, &seq);
428 ret = snapshot_write_file(v, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
430 fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n");
432 fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n");
438 snapshot_mark(int argc, char **argv)
440 __be32 owrt = cpu_to_be32(OWRT);
445 fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
446 if (getchar() != 'y')
449 v = volume_find("rootfs_data");
451 fprintf(stderr, "no rootfs_data was found\n");
455 fd = open(v->blk, O_WRONLY);
456 fprintf(stderr, "%s - marking with 0x%08x\n", v->blk, owrt);
458 fprintf(stderr, "opening %s failed\n", v->blk);
462 sz = write(fd, &owrt, sizeof(owrt));
466 fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
474 snapshot_read(int argc, char **argv)
476 struct volume *v = volume_find("rootfs_data");;
477 int block = 0, ret = 0;
484 block = atoi(argv[1]);
485 if (block >= (v->size / v->block_size)) {
486 fprintf(stderr, "invalid block %d > %llu\n", block, v->size / v->block_size);
489 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
491 ret = snapshot_read_file(v, block, file, DATA);
496 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
497 block = snapshot_read_file(v, block, file, DATA);
507 struct volume *v = volume_find("rootfs_data");
508 struct file_header sentinel, conf;
515 next = snapshot_next_free(v, &seq);
516 block = config_find(v, &conf, &sentinel);
517 if (is_config(&conf) && conf.seq != seq) {
519 volume_erase(v, next * v->block_size, 2 * v->block_size);
522 if (is_config(&sentinel) && (sentinel.seq != seq)) {
524 volume_erase(v, block * v->block_size, v->block_size);
527 if (!is_config(&conf) && !is_config(&sentinel)) {
528 // fprintf(stderr, "no config found\n");
529 } else if (((is_config(&conf) && is_config(&sentinel)) &&
530 (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
531 (is_config(&conf) && !is_config(&sentinel))) {
533 int next = snapshot_next_free(v, &seq);
534 int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
536 if (sentinel_write(v, conf.seq))
537 fprintf(stderr, "failed to write sentinel data");
539 } else if (!is_config(&conf) && is_config(&sentinel) && next) {
540 int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
542 if (volatile_write(v, sentinel.seq))
543 fprintf(stderr, "failed to write sentinel data");
545 fprintf(stderr, "config in sync\n");
547 unlink("/tmp/config.tar.gz");
553 _ramoverlay(char *rom, char *overlay)
555 mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
556 return fopivot(overlay, rom);
563 setenv("SNAPSHOT", "magic", 1);
564 _ramoverlay("/rom", "/overlay");
565 system("/sbin/snapshot unpack");
566 foreachdir("/overlay/", handle_whiteout);
567 mkdir("/volatile", 0700);
568 _ramoverlay("/rom", "/volatile");
569 mount_move("/rom/volatile", "/volatile", "");
570 mount_move("/rom/rom", "/rom", "");
571 system("/sbin/snapshot config_unpack");
572 foreachdir("/volatile/", handle_whiteout);
573 unsetenv("SNAPSHOT");
577 static struct backend_handler snapshot_handlers[] = {
579 .name = "config_read",
582 .name = "config_write",
586 .cli = snapshot_read,
589 .cli = snapshot_write,
592 .cli = snapshot_mark,
595 static struct backend snapshot_backend = {
597 .num_handlers = ARRAY_SIZE(snapshot_handlers),
598 .handlers = snapshot_handlers,
599 .mount = snapshot_mount,
600 .info = snapshot_info,
602 BACKEND(snapshot_backend);