split fs-state back into seperate tools
[project/fstools.git] / libfstools / 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 "libfstools.h"
34 #include "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_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
177 {
178 uint32_t md5[4] = { 0 };
179 struct file_header hdr;
180 struct stat s;
181 char buffer[256];
182 int in = 0, len, offset;
183 int ret = -1;
184
185 if (stat(file, &s) || md5sum(file, md5)) {
186 fprintf(stderr, "stat failed on %s\n", file);
187 goto out;
188 }
189
190 if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
191 fprintf(stderr, "upgrade is too big for the flash\n");
192 goto out;
193 }
194 volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
195 volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
196
197 hdr.length = s.st_size;
198 hdr.magic = OWRT;
199 hdr.type = type;
200 hdr.seq = seq;
201 memcpy(hdr.md5, md5, sizeof(md5));
202 hdr_to_be32(&hdr);
203
204 if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
205 fprintf(stderr, "failed to write header\n");
206 goto out;
207 }
208
209 in = open(file, O_RDONLY);
210 if (in < 1) {
211 fprintf(stderr, "failed to open %s\n", file);
212 goto out;
213 }
214
215 offset = (block * v->block_size) + sizeof(struct file_header);
216
217 while ((len = read(in, buffer, sizeof(buffer))) > 0) {
218 if (volume_write(v, buffer, offset, len) < 0)
219 goto out;
220 offset += len;
221 }
222
223 ret = 0;
224
225 out:
226 if (in > 0)
227 close(in);
228
229 return ret;
230 }
231
232 static int
233 snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
234 {
235 struct file_header hdr;
236 char buffer[256];
237 int out, offset = 0;
238
239 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
240 fprintf(stderr, "failed to read header\n");
241 return -1;
242 }
243 be32_to_hdr(&hdr);
244
245 if (hdr.magic != OWRT)
246 return -1;
247
248 if (hdr.type != type)
249 return -1;
250
251 if (valid_file_size(hdr.length))
252 return -1;
253
254 out = open(file, O_WRONLY | O_CREAT, 0700);
255 if (!out) {
256 fprintf(stderr, "failed to open %s\n", file);
257 return -1;
258 }
259
260 while (hdr.length > 0) {
261 int len = sizeof(buffer);
262
263 if (hdr.length < len)
264 len = hdr.length;
265
266 if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
267 return -1;
268
269 offset += len;
270 hdr.length -= len;
271 }
272
273 close(out);
274
275 if (verify_file_hash(file, hdr.md5)) {
276 fprintf(stderr, "md5 verification failed\n");
277 unlink(file);
278 return 0;
279 }
280
281 block += pad_file_size(v, hdr.length) / v->block_size;
282
283 return block;
284 }
285
286 static int
287 sentinel_write(struct volume *v, uint32_t _seq)
288 {
289 int ret, block;
290 struct stat s;
291 uint32_t seq;
292
293 if (stat("/tmp/config.tar.gz", &s)) {
294 fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
295 return -1;
296 }
297
298 snapshot_next_free(v, &seq);
299 if (_seq)
300 seq = _seq;
301 block = v->size / v->block_size;
302 block -= pad_file_size(v, s.st_size) / v->block_size;
303 if (block < 0)
304 block = 0;
305
306 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
307 if (ret)
308 fprintf(stderr, "failed to write sentinel\n");
309 else
310 fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
311 return ret;
312 }
313
314 static int
315 volatile_write(struct volume *v, uint32_t _seq)
316 {
317 int block, ret;
318 uint32_t seq;
319
320 block = snapshot_next_free(v, &seq);
321 if (_seq)
322 seq = _seq;
323 if (block < 0)
324 block = 0;
325
326 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
327 if (ret)
328 fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
329 else
330 fprintf(stderr, "wrote /tmp/config.tar.gz\n");
331 return ret;
332 }
333
334 static int
335 snapshot_sync(void)
336 {
337 struct volume *v = volume_find("rootfs_data");
338 struct file_header sentinel, conf;
339 int next, block = 0;
340 uint32_t seq;
341
342 if (!v)
343 return -1;
344
345 next = snapshot_next_free(v, &seq);
346 block = config_find(v, &conf, &sentinel);
347 if (is_config(&conf) && conf.seq != seq) {
348 conf.magic = 0;
349 volume_erase(v, next * v->block_size, 2 * v->block_size);
350 }
351
352 if (is_config(&sentinel) && (sentinel.seq != seq)) {
353 sentinel.magic = 0;
354 volume_erase(v, block * v->block_size, v->block_size);
355 }
356
357 if (!is_config(&conf) && !is_config(&sentinel)) {
358 // fprintf(stderr, "no config found\n");
359 } else if (((is_config(&conf) && is_config(&sentinel)) &&
360 (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
361 (is_config(&conf) && !is_config(&sentinel))) {
362 uint32_t seq;
363 int next = snapshot_next_free(v, &seq);
364 int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
365 if (ret > 0) {
366 if (sentinel_write(v, conf.seq))
367 fprintf(stderr, "failed to write sentinel data");
368 }
369 } else if (!is_config(&conf) && is_config(&sentinel) && next) {
370 int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
371 if (ret > 0)
372 if (volatile_write(v, sentinel.seq))
373 fprintf(stderr, "failed to write sentinel data");
374 } else
375 fprintf(stderr, "config in sync\n");
376
377 unlink("/tmp/config.tar.gz");
378
379 return 0;
380 }
381
382 static int
383 _ramoverlay(char *rom, char *overlay)
384 {
385 mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
386 return fopivot(overlay, rom);
387 }
388
389 int
390 mount_snapshot(void)
391 {
392 snapshot_sync();
393 setenv("SNAPSHOT", "magic", 1);
394 _ramoverlay("/rom", "/overlay");
395 system("/sbin/snapshot unpack");
396 foreachdir("/overlay/", handle_whiteout);
397 mkdir("/volatile", 0700);
398 _ramoverlay("/rom", "/volatile");
399 mount_move("/rom/volatile", "/volatile", "");
400 mount_move("/rom/rom", "/rom", "");
401 system("/sbin/snapshot config_unpack");
402 foreachdir("/volatile/", handle_whiteout);
403 unsetenv("SNAPSHOT");
404 return -1;
405 }