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
);