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 fprintf(stderr
, "failed to generate md5 sum\n");
47 if (memcmp(md5
, hash
, sizeof(md5
))) {
48 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "stat failed on %s\n", file
);
128 if ((block
* v
->block_size
) + pad_file_size(v
, s
.st_size
) > v
->size
) {
129 fprintf(stderr
, "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 fprintf(stderr
, "failed to write header\n");
147 in
= open(file
, O_RDONLY
);
149 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "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 fprintf(stderr
, "failed to write sentinel\n");
251 fprintf(stderr
, "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 fprintf(stderr
, "failed to write /tmp/config.tar.gz\n");
271 fprintf(stderr
, "wrote /tmp/config.tar.gz\n");
278 struct volume
*v
= volume_find("rootfs_data");
279 struct file_header sentinel
, conf
;
286 next
= snapshot_next_free(v
, &seq
);
287 block
= config_find(v
, &conf
, &sentinel
);
288 if (is_config(&conf
) && conf
.seq
!= seq
) {
290 volume_erase(v
, next
* v
->block_size
, 2 * v
->block_size
);
293 if (is_config(&sentinel
) && (sentinel
.seq
!= seq
)) {
295 volume_erase(v
, block
* v
->block_size
, v
->block_size
);
298 if (!is_config(&conf
) && !is_config(&sentinel
)) {
299 // fprintf(stderr, "no config found\n");
300 } else if (((is_config(&conf
) && is_config(&sentinel
)) &&
301 (memcmp(conf
.md5
, sentinel
.md5
, sizeof(conf
.md5
)) || (conf
.seq
!= sentinel
.seq
))) ||
302 (is_config(&conf
) && !is_config(&sentinel
))) {
304 int next
= snapshot_next_free(v
, &seq
);
305 int ret
= snapshot_read_file(v
, next
, "/tmp/config.tar.gz", CONF
);
307 if (sentinel_write(v
, conf
.seq
))
308 fprintf(stderr
, "failed to write sentinel data");
310 } else if (!is_config(&conf
) && is_config(&sentinel
) && next
) {
311 int ret
= snapshot_read_file(v
, block
, "/tmp/config.tar.gz", CONF
);
313 if (volatile_write(v
, sentinel
.seq
))
314 fprintf(stderr
, "failed to write sentinel data");
316 fprintf(stderr
, "config in sync\n");
318 unlink("/tmp/config.tar.gz");
324 _ramoverlay(char *rom
, char *overlay
)
326 mount("tmpfs", overlay
, "tmpfs", MS_NOATIME
, "mode=0755");
327 return fopivot(overlay
, rom
);
334 setenv("SNAPSHOT", "magic", 1);
335 _ramoverlay("/rom", "/overlay");
336 system("/sbin/snapshot unpack");
337 foreachdir("/overlay/", handle_whiteout
);
338 mkdir("/volatile", 0700);
339 _ramoverlay("/rom", "/volatile");
340 mount_move("/rom/volatile", "/volatile", "");
341 mount_move("/rom/rom", "/rom", "");
342 system("/sbin/snapshot config_unpack");
343 foreachdir("/volatile/", handle_whiteout
);
344 unsetenv("SNAPSHOT");