• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/fstools/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 }
356 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt