5873f5c060952c4b705c2ace3d316f2e7ee5fa3b
[project/fstools.git] / backend / snapshot.c
1 /*
2  * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
3  *
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
7  *
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.
12  */
13
14 #include <sys/stat.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/ioctl.h>
18 #include <sys/mount.h>
19 #include <mtd/mtd-user.h>
20
21 #include <glob.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <libgen.h>
26 #include <unistd.h>
27 #include <string.h>
28
29 #include <libubox/list.h>
30 #include <libubox/blob.h>
31 #include <libubox/md5.h>
32
33 #include "../fs-state.h"
34 #include "../driver/volume.h"
35
36 #define PATH_MAX        256
37 #define OWRT            0x4f575254
38 #define DATA            0x44415441
39 #define CONF            0x434f4e46
40
41 struct file_header {
42         uint32_t magic;
43         uint32_t type;
44         uint32_t seq;
45         uint32_t length;
46         uint32_t md5[4];
47 };
48
49 static inline int
50 is_config(struct file_header *h)
51 {
52         return ((h->magic == OWRT) && (h->type == CONF));
53 }
54
55 static inline int
56 valid_file_size(int fs)
57 {
58         if ((fs > 8 * 1024 * 1204) || (fs <= 0))
59                 return -1;
60
61         return 0;
62 }
63
64 static void
65 hdr_to_be32(struct file_header *hdr)
66 {
67         uint32_t *h = (uint32_t *) hdr;
68         int i;
69
70         for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
71                 h[i] = cpu_to_be32(h[i]);
72 }
73
74 static void
75 be32_to_hdr(struct file_header *hdr)
76 {
77         uint32_t *h = (uint32_t *) hdr;
78         int i;
79
80         for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
81                 h[i] = be32_to_cpu(h[i]);
82 }
83
84 static int
85 pad_file_size(struct volume *v, int size)
86 {
87         int mod;
88
89         size += sizeof(struct file_header);
90         mod = size % v->block_size;
91         if (mod) {
92                 size -= mod;
93                 size += v->block_size;
94         }
95
96         return size;
97 }
98
99 static int
100 verify_file_hash(char *file, uint32_t *hash)
101 {
102         uint32_t md5[4];
103
104         if (md5sum(file, md5)) {
105                 fprintf(stderr, "failed to generate md5 sum\n");
106                 return -1;
107         }
108
109         if (memcmp(md5, hash, sizeof(md5))) {
110                 fprintf(stderr, "failed to verify hash of %s.\n", file);
111                 return -1;
112         }
113
114         return 0;
115 }
116
117 static int
118 snapshot_next_free(struct volume *v, uint32_t *seq)
119 {
120         struct file_header hdr = { 0 };
121         int block = 0;
122
123         *seq = rand();
124
125         do {
126                 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
127                         fprintf(stderr, "scanning for next free block failed\n");
128                         return 0;
129                 }
130
131                 be32_to_hdr(&hdr);
132
133                 if (hdr.magic != OWRT)
134                         break;
135
136                 if (hdr.type == DATA && !valid_file_size(hdr.length)) {
137                         if (*seq + 1 != hdr.seq && block)
138                                 return block;
139                         *seq = hdr.seq;
140                         block += pad_file_size(v, hdr.length) / v->block_size;
141                 }
142         } while (hdr.type == DATA);
143
144         return block;
145 }
146
147 static int
148 config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
149 {
150         uint32_t seq;
151         int i, next = snapshot_next_free(v, &seq);
152
153         conf->magic = sentinel->magic = 0;
154
155         if (!volume_read(v, conf, next, sizeof(*conf)))
156                 be32_to_hdr(conf);
157
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");
161                         return -1;
162                 }
163                 be32_to_hdr(sentinel);
164
165                 if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
166                         if (next == i)
167                                 return -1;
168                         return i;
169                 }
170         }
171
172         return -1;
173 }
174
175 static int
176 snapshot_info(void)
177 {
178         struct volume *v = volume_find("rootfs_data");
179         struct file_header hdr = { 0 }, conf;
180         int block = 0;
181
182         if (!v)
183                 return -1;
184
185         fprintf(stderr, "sectors:\t%llu, block_size:\t%dK\n", v->size / v->block_size, v->block_size / 1024);
186         do {
187                 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
188                         fprintf(stderr, "scanning for next free block failed\n");
189                         return 0;
190                 }
191
192                 be32_to_hdr(&hdr);
193
194                 if (hdr.magic != OWRT)
195                         break;
196
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);
201
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);
206         if (block > 0)
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);
208
209         return 0;
210 }
211
212 static int
213 snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
214 {
215         uint32_t md5[4] = { 0 };
216         struct file_header hdr;
217         struct stat s;
218         char buffer[256];
219         int in = 0, len, offset;
220         int ret = -1;
221
222         if (stat(file, &s) || md5sum(file, md5)) {
223                 fprintf(stderr, "stat failed on %s\n", file);
224                 goto out;
225         }
226
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");
229                 goto out;
230         }
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);
233
234         hdr.length = s.st_size;
235         hdr.magic = OWRT;
236         hdr.type = type;
237         hdr.seq = seq;
238         memcpy(hdr.md5, md5, sizeof(md5));
239         hdr_to_be32(&hdr);
240
241         if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
242                 fprintf(stderr, "failed to write header\n");
243                 goto out;
244         }
245
246         in = open(file, O_RDONLY);
247         if (in < 1) {
248                 fprintf(stderr, "failed to open %s\n", file);
249                 goto out;
250         }
251
252         offset = (block * v->block_size) + sizeof(struct file_header);
253
254         while ((len = read(in, buffer, sizeof(buffer))) > 0) {
255                 if (volume_write(v, buffer, offset, len) < 0)
256                         goto out;
257                 offset += len;
258         }
259
260         ret = 0;
261
262 out:
263         if (in > 0)
264                 close(in);
265
266         return ret;
267 }
268
269 static int
270 snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
271 {
272         struct file_header hdr;
273         char buffer[256];
274         int out, offset = 0;
275
276         if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
277                 fprintf(stderr, "failed to read header\n");
278                 return -1;
279         }
280         be32_to_hdr(&hdr);
281
282         if (hdr.magic != OWRT)
283                 return -1;
284
285         if (hdr.type != type)
286                 return -1;
287
288         if (valid_file_size(hdr.length))
289                 return -1;
290
291         out = open(file, O_WRONLY | O_CREAT, 0700);
292         if (!out) {
293                 fprintf(stderr, "failed to open %s\n", file);
294                 return -1;
295         }
296
297         while (hdr.length > 0) {
298                 int len = sizeof(buffer);
299
300                 if (hdr.length < len)
301                         len = hdr.length;
302
303                 if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
304                         return -1;
305
306                 offset += len;
307                 hdr.length -= len;
308         }
309
310         close(out);
311
312         if (verify_file_hash(file, hdr.md5)) {
313                 fprintf(stderr, "md5 verification failed\n");
314                 unlink(file);
315                 return 0;
316         }
317
318         block += pad_file_size(v, hdr.length) / v->block_size;
319
320         return block;
321 }
322
323 static int
324 sentinel_write(struct volume *v, uint32_t _seq)
325 {
326         int ret, block;
327         struct stat s;
328         uint32_t seq;
329
330         if (stat("/tmp/config.tar.gz", &s)) {
331                 fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
332                 return -1;
333         }
334
335         snapshot_next_free(v, &seq);
336         if (_seq)
337                 seq = _seq;
338         block = v->size / v->block_size;
339         block -= pad_file_size(v, s.st_size) / v->block_size;
340         if (block < 0)
341                 block = 0;
342
343         ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
344         if (ret)
345                 fprintf(stderr, "failed to write sentinel\n");
346         else
347                 fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
348         return ret;
349 }
350
351 static int
352 volatile_write(struct volume *v, uint32_t _seq)
353 {
354         int block, ret;
355         uint32_t seq;
356
357         block = snapshot_next_free(v, &seq);
358         if (_seq)
359                 seq = _seq;
360         if (block < 0)
361                 block = 0;
362
363         ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
364         if (ret)
365                 fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
366         else
367                 fprintf(stderr, "wrote /tmp/config.tar.gz\n");
368         return ret;
369 }
370
371 static int
372 config_write(int argc, char **argv)
373 {
374         struct volume *v = volume_find("rootfs_data");
375         int ret;
376
377         if (!v)
378                 return -1;
379
380         ret = volatile_write(v, 0);
381         if (!ret)
382                 ret = sentinel_write(v, 0);
383
384         return ret;
385 }
386
387 static int
388 config_read(int argc, char **argv)
389 {
390         struct volume *v = volume_find("rootfs_data");
391         struct file_header conf, sentinel;
392         int next, block, ret = 0;
393         uint32_t seq;
394
395         if (!v)
396                 return -1;
397
398         block = config_find(v, &conf, &sentinel);
399         next = snapshot_next_free(v, &seq);
400         if (is_config(&conf) && conf.seq == seq)
401                 block = next;
402         else if (!is_config(&sentinel) || sentinel.seq != seq)
403                 return -1;
404
405         unlink("/tmp/config.tar.gz");
406         ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
407
408         if (ret < 1)
409                 fprintf(stderr, "failed to read /tmp/config.tar.gz\n");
410
411         return ret;
412 }
413
414 static int
415 snapshot_write(int argc, char **argv)
416 {
417         struct volume *v = volume_find("rootfs_data");
418         int block, ret;
419         uint32_t seq;
420
421         if (!v)
422                 return -1;
423
424         block = snapshot_next_free(v, &seq);
425         if (block < 0)
426                 block = 0;
427
428         ret = snapshot_write_file(v, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
429         if (ret)
430                 fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n");
431         else
432                 fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n");
433
434         return ret;
435 }
436
437 static int
438 snapshot_mark(int argc, char **argv)
439 {
440         __be32 owrt = cpu_to_be32(OWRT);
441         struct volume *v;
442         size_t sz;
443         int fd;
444
445         fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
446         if (getchar() != 'y')
447                 return -1;
448
449         v = volume_find("rootfs_data");
450         if (!v) {
451                 fprintf(stderr, "no rootfs_data was found\n");
452                 return -1;
453         }
454
455         fd = open(v->blk, O_WRONLY);
456         fprintf(stderr, "%s - marking with 0x%08x\n", v->blk, owrt);
457         if (fd < 0) {
458                 fprintf(stderr, "opening %s failed\n", v->blk);
459                 return -1;
460         }
461
462         sz = write(fd, &owrt, sizeof(owrt));
463         close(fd);
464
465         if (sz != 1) {
466                 fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
467                 return -1;
468         }
469
470         return 0;
471 }
472
473 static int
474 snapshot_read(int argc, char **argv)
475 {
476         struct volume *v = volume_find("rootfs_data");;
477         int block = 0, ret = 0;
478         char file[64];
479
480         if (!v)
481                 return -1;
482
483         if (argc > 1) {
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);
487                         goto out;
488                 }
489                 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
490
491                 ret = snapshot_read_file(v, block, file, DATA);
492                 goto out;
493         }
494
495         do {
496                 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
497                 block = snapshot_read_file(v, block, file, DATA);
498         } while (block > 0);
499
500 out:
501         return ret;
502 }
503
504 static int
505 snapshot_sync(void)
506 {
507         struct volume *v = volume_find("rootfs_data");
508         struct file_header sentinel, conf;
509         int next, block = 0;
510         uint32_t seq;
511
512         if (!v)
513                 return -1;
514
515         next = snapshot_next_free(v, &seq);
516         block = config_find(v, &conf, &sentinel);
517         if (is_config(&conf) && conf.seq != seq) {
518                 conf.magic = 0;
519                 volume_erase(v, next * v->block_size, 2 * v->block_size);
520         }
521
522         if (is_config(&sentinel) && (sentinel.seq != seq)) {
523                 sentinel.magic = 0;
524                 volume_erase(v, block * v->block_size, v->block_size);
525         }
526
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))) {
532                 uint32_t seq;
533                 int next = snapshot_next_free(v, &seq);
534                 int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
535                 if (ret > 0) {
536                         if (sentinel_write(v, conf.seq))
537                                 fprintf(stderr, "failed to write sentinel data");
538                 }
539         } else if (!is_config(&conf) && is_config(&sentinel) && next) {
540                 int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
541                 if (ret > 0)
542                         if (volatile_write(v, sentinel.seq))
543                                 fprintf(stderr, "failed to write sentinel data");
544         } else
545                 fprintf(stderr, "config in sync\n");
546
547         unlink("/tmp/config.tar.gz");
548
549         return 0;
550 }
551
552 static int
553 _ramoverlay(char *rom, char *overlay)
554 {
555         mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
556         return fopivot(overlay, rom);
557 }
558
559 static int
560 snapshot_mount(void)
561 {
562         snapshot_sync();
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");
574         return -1;
575 }
576
577 static struct backend_handler snapshot_handlers[] = {
578 {
579         .name = "config_read",
580         .cli = config_read,
581 }, {
582         .name = "config_write",
583         .cli = config_write,
584 }, {
585         .name = "read",
586         .cli = snapshot_read,
587 }, {
588         .name = "write",
589         .cli = snapshot_write,
590 }, {
591         .name = "mark",
592         .cli = snapshot_mark,
593 }};
594
595 static struct backend snapshot_backend = {
596         .name = "snapshot",
597         .num_handlers = ARRAY_SIZE(snapshot_handlers),
598         .handlers = snapshot_handlers,
599         .mount = snapshot_mount,
600         .info = snapshot_info,
601 };
602 BACKEND(snapshot_backend);