libfstools: fit: improve fit_volume_find string handling
[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 #include "snapshot.h"
36
37 int
38 verify_file_hash(char *file, uint32_t *hash)
39 {
40 uint32_t md5[4];
41
42 if (md5sum(file, md5) <= 0) {
43 ULOG_ERR("failed to generate md5 sum\n");
44 return -1;
45 }
46
47 if (memcmp(md5, hash, sizeof(md5))) {
48 ULOG_ERR("failed to verify hash of %s.\n", file);
49 return -1;
50 }
51
52 return 0;
53 }
54
55 int
56 snapshot_next_free(struct volume *v, uint32_t *seq)
57 {
58 struct file_header hdr = { 0 };
59 int block = 0;
60
61 *seq = rand();
62
63 do {
64 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
65 ULOG_ERR("scanning for next free block failed\n");
66 return 0;
67 }
68
69 be32_to_hdr(&hdr);
70
71 if (hdr.magic != OWRT)
72 break;
73
74 if (hdr.type == DATA && !valid_file_size(hdr.length)) {
75 if (*seq + 1 != hdr.seq && block)
76 return block;
77 *seq = hdr.seq;
78 block += pad_file_size(v, hdr.length) / v->block_size;
79 }
80 } while (hdr.type == DATA);
81
82 return block;
83 }
84
85 int
86 config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
87 {
88 uint32_t seq;
89 int i, next = snapshot_next_free(v, &seq);
90
91 conf->magic = sentinel->magic = 0;
92
93 if (!volume_read(v, conf, next, sizeof(*conf)))
94 be32_to_hdr(conf);
95
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");
99 return -1;
100 }
101 be32_to_hdr(sentinel);
102
103 if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
104 if (next == i)
105 return -1;
106 return i;
107 }
108 }
109
110 return -1;
111 }
112
113 int
114 snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
115 {
116 uint32_t md5[4] = { 0 };
117 struct file_header hdr;
118 struct stat s;
119 char buffer[256];
120 int in = 0, len, offset;
121 int ret = -1;
122
123 if (stat(file, &s) || md5sum(file, md5) != s.st_size) {
124 ULOG_ERR("stat failed on %s\n", file);
125 goto out;
126 }
127
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");
130 goto out;
131 }
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);
134
135 hdr.length = s.st_size;
136 hdr.magic = OWRT;
137 hdr.type = type;
138 hdr.seq = seq;
139 memcpy(hdr.md5, md5, sizeof(md5));
140 hdr_to_be32(&hdr);
141
142 if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
143 ULOG_ERR("failed to write header\n");
144 goto out;
145 }
146
147 in = open(file, O_RDONLY);
148 if (in < 0) {
149 ULOG_ERR("failed to open %s\n", file);
150 goto out;
151 }
152
153 offset = (block * v->block_size) + sizeof(struct file_header);
154
155 while ((len = read(in, buffer, sizeof(buffer))) > 0) {
156 if (volume_write(v, buffer, offset, len) < 0)
157 goto out;
158 offset += len;
159 }
160
161 ret = 0;
162
163 out:
164 if (in >= 0)
165 close(in);
166
167 return ret;
168 }
169
170 int
171 snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
172 {
173 struct file_header hdr;
174 char buffer[256];
175 int out, offset = 0;
176
177 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
178 ULOG_ERR("failed to read header\n");
179 return -1;
180 }
181 be32_to_hdr(&hdr);
182
183 if (hdr.magic != OWRT)
184 return -1;
185
186 if (hdr.type != type)
187 return -1;
188
189 if (valid_file_size(hdr.length))
190 return -1;
191
192 out = open(file, O_WRONLY | O_CREAT, 0700);
193 if (out < 0) {
194 ULOG_ERR("failed to open %s\n", file);
195 return -1;
196 }
197
198 offset = block * v->block_size + sizeof(hdr);
199
200 while (hdr.length > 0) {
201 int len = sizeof(buffer);
202
203 if (hdr.length < len)
204 len = hdr.length;
205
206 if (volume_read(v, buffer, offset, len)) {
207 close(out);
208 return -1;
209 }
210 if (write(out, buffer, len) != len) {
211 close(out);
212 return -1;
213 }
214 offset += len;
215 hdr.length -= len;
216 }
217
218 close(out);
219
220 if (verify_file_hash(file, hdr.md5)) {
221 ULOG_ERR("md5 verification failed\n");
222 unlink(file);
223 return 0;
224 }
225
226 block += pad_file_size(v, hdr.length) / v->block_size;
227
228 return block;
229 }
230
231 int
232 sentinel_write(struct volume *v, uint32_t _seq)
233 {
234 int ret, block;
235 struct stat s;
236 uint32_t seq;
237
238 if (stat("/tmp/config.tar.gz", &s)) {
239 ULOG_ERR("failed to stat /tmp/config.tar.gz\n");
240 return -1;
241 }
242
243 snapshot_next_free(v, &seq);
244 if (_seq)
245 seq = _seq;
246 block = v->size / v->block_size;
247 block -= pad_file_size(v, s.st_size) / v->block_size;
248 if (block < 0)
249 block = 0;
250
251 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
252 if (ret)
253 ULOG_ERR("failed to write sentinel\n");
254 else
255 ULOG_INFO("wrote /tmp/config.tar.gz sentinel\n");
256 return ret;
257 }
258
259 int
260 volatile_write(struct volume *v, uint32_t _seq)
261 {
262 int block, ret;
263 uint32_t seq;
264
265 block = snapshot_next_free(v, &seq);
266 if (_seq)
267 seq = _seq;
268 if (block < 0)
269 block = 0;
270
271 ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
272 if (ret)
273 ULOG_ERR("failed to write /tmp/config.tar.gz\n");
274 else
275 ULOG_INFO("wrote /tmp/config.tar.gz\n");
276 return ret;
277 }
278
279 static int
280 snapshot_sync(struct volume *v)
281 {
282 struct file_header sentinel, conf;
283 int next, block = 0;
284 uint32_t seq;
285
286 next = snapshot_next_free(v, &seq);
287 block = config_find(v, &conf, &sentinel);
288 if (is_config(&conf) && conf.seq != seq) {
289 conf.magic = 0;
290 volume_erase(v, next * v->block_size, 2 * v->block_size);
291 }
292
293 if (is_config(&sentinel) && (sentinel.seq != seq)) {
294 sentinel.magic = 0;
295 volume_erase(v, block * v->block_size, v->block_size);
296 }
297
298 if (!is_config(&conf) && !is_config(&sentinel)) {
299 // ULOG_ERR("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))) {
303 uint32_t seq;
304 int next = snapshot_next_free(v, &seq);
305 int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
306 if (ret > 0) {
307 if (sentinel_write(v, conf.seq))
308 ULOG_ERR("failed to write sentinel data");
309 }
310 } else if (!is_config(&conf) && is_config(&sentinel) && next) {
311 int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
312 if (ret > 0)
313 if (volatile_write(v, sentinel.seq))
314 ULOG_ERR("failed to write sentinel data");
315 } else
316 ULOG_INFO("config in sync\n");
317
318 unlink("/tmp/config.tar.gz");
319
320 return 0;
321 }
322
323 static int
324 _ramoverlay(char *rom, char *overlay)
325 {
326 mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
327 return fopivot(overlay, rom);
328 }
329
330 int
331 mount_snapshot(struct volume *v)
332 {
333 snapshot_sync(v);
334 setenv("SNAPSHOT", "magic", 1);
335 _ramoverlay("/rom", "/overlay");
336 if (system("/sbin/snapshot unpack") == -1) {
337 perror("system");
338 return -1;
339 }
340 foreachdir("/overlay/", handle_whiteout);
341 if (mkdir("/volatile", 0700) == -1 && errno != EEXIST) {
342 perror("mkdir");
343 return -1;
344 }
345 _ramoverlay("/rom", "/volatile");
346 mount_move("/rom/volatile", "/volatile", "");
347 mount_move("/rom/rom", "/rom", "");
348 if (system("/sbin/snapshot config_unpack")) {
349 perror("system");
350 return -1;
351 }
352 foreachdir("/volatile/", handle_whiteout);
353 unsetenv("SNAPSHOT");
354 return -1;
355 }