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 "libfstools.h"
38 verify_file_hash(char *file, uint32_t *hash)
42 if (md5sum(file, md5)) {
43 ULOG_ERR("failed to generate md5 sum\n");
47 if (memcmp(md5, hash, sizeof(md5))) {
48 ULOG_ERR("failed to verify hash of %s.\n", file);
56 snapshot_next_free(struct volume *v, uint32_t *seq)
58 struct file_header hdr = { 0 };
64 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
65 ULOG_ERR("scanning for next free block failed\n");
71 if (hdr.magic != OWRT)
74 if (hdr.type == DATA && !valid_file_size(hdr.length)) {
75 if (*seq + 1 != hdr.seq && block)
78 block += pad_file_size(v, hdr.length) / v->block_size;
80 } while (hdr.type == DATA);
86 config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
89 int i, next = snapshot_next_free(v, &seq);
91 conf->magic = sentinel->magic = 0;
93 if (!volume_read(v, conf, next, sizeof(*conf)))
96 for (i = (v->size / v->block_size) - 1; i > 0; i--) {
97 if (volume_read(v, sentinel, i * v->block_size, sizeof(*sentinel))) {
98 ULOG_ERR("failed to read header\n");
101 be32_to_hdr(sentinel);
103 if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
114 snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
116 uint32_t md5[4] = { 0 };
117 struct file_header hdr;
120 int in = 0, len, offset;
123 if (stat(file, &s) || md5sum(file, md5)) {
124 ULOG_ERR("stat failed on %s\n", file);
128 if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
129 ULOG_ERR("upgrade is too big for the flash\n");
132 volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
133 volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
135 hdr.length = s.st_size;
139 memcpy(hdr.md5, md5, sizeof(md5));
142 if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
143 ULOG_ERR("failed to write header\n");
147 in = open(file, O_RDONLY);
149 ULOG_ERR("failed to open %s\n", file);
153 offset = (block * v->block_size) + sizeof(struct file_header);
155 while ((len = read(in, buffer, sizeof(buffer))) > 0) {
156 if (volume_write(v, buffer, offset, len) < 0)
171 snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
173 struct file_header hdr;
177 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
178 ULOG_ERR("failed to read header\n");
183 if (hdr.magic != OWRT)
186 if (hdr.type != type)
189 if (valid_file_size(hdr.length))
192 out = open(file, O_WRONLY | O_CREAT, 0700);
194 ULOG_ERR("failed to open %s\n", file);
198 offset = block * v->block_size + sizeof(hdr);
200 while (hdr.length > 0) {
201 int len = sizeof(buffer);
203 if (hdr.length < len)
206 if (volume_read(v, buffer, offset, len))
208 if (write(out, buffer, len) != len)
216 if (verify_file_hash(file, hdr.md5)) {
217 ULOG_ERR("md5 verification failed\n");
222 block += pad_file_size(v, hdr.length) / v->block_size;
228 sentinel_write(struct volume *v, uint32_t _seq)
234 if (stat("/tmp/config.tar.gz", &s)) {
235 ULOG_ERR("failed to stat /tmp/config.tar.gz\n");
239 snapshot_next_free(v, &seq);
242 block = v->size / v->block_size;
243 block -= pad_file_size(v, s.st_size) / v->block_size;
247 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
249 ULOG_ERR("failed to write sentinel\n");
251 ULOG_INFO("wrote /tmp/config.tar.gz sentinel\n");
256 volatile_write(struct volume *v, uint32_t _seq)
261 block = snapshot_next_free(v, &seq);
267 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
269 ULOG_ERR("failed to write /tmp/config.tar.gz\n");
271 ULOG_INFO("wrote /tmp/config.tar.gz\n");
276 snapshot_sync(struct volume *v)
278 struct file_header sentinel, conf;
282 next = snapshot_next_free(v, &seq);
283 block = config_find(v, &conf, &sentinel);
284 if (is_config(&conf) && conf.seq != seq) {
286 volume_erase(v, next * v->block_size, 2 * v->block_size);
289 if (is_config(&sentinel) && (sentinel.seq != seq)) {
291 volume_erase(v, block * v->block_size, v->block_size);
294 if (!is_config(&conf) && !is_config(&sentinel)) {
295 // ULOG_ERR("no config found\n");
296 } else if (((is_config(&conf) && is_config(&sentinel)) &&
297 (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
298 (is_config(&conf) && !is_config(&sentinel))) {
300 int next = snapshot_next_free(v, &seq);
301 int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
303 if (sentinel_write(v, conf.seq))
304 ULOG_ERR("failed to write sentinel data");
306 } else if (!is_config(&conf) && is_config(&sentinel) && next) {
307 int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
309 if (volatile_write(v, sentinel.seq))
310 ULOG_ERR("failed to write sentinel data");
312 ULOG_INFO("config in sync\n");
314 unlink("/tmp/config.tar.gz");
320 _ramoverlay(char *rom, char *overlay)
322 mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
323 return fopivot(overlay, rom);
327 mount_snapshot(struct volume *v)
330 setenv("SNAPSHOT", "magic", 1);
331 _ramoverlay("/rom", "/overlay");
332 if (system("/sbin/snapshot unpack") == -1) {
336 foreachdir("/overlay/", handle_whiteout);
337 mkdir("/volatile", 0700);
338 _ramoverlay("/rom", "/volatile");
339 mount_move("/rom/volatile", "/volatile", "");
340 mount_move("/rom/rom", "/rom", "");
341 if (system("/sbin/snapshot config_unpack")) {
345 foreachdir("/volatile/", handle_whiteout);
346 unsetenv("SNAPSHOT");