block: fix ubi?_? glob pattern
[project/fstools.git] / 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 #include <inttypes.h>
29
30 #include <libubox/list.h>
31 #include <libubox/blob.h>
32 #include <libubox/md5.h>
33 #include <libubox/ulog.h>
34
35 #include "libfstools/libfstools.h"
36 #include "libfstools/volume.h"
37 #include "libfstools/snapshot.h"
38
39 static int
40 config_write(int argc, char **argv)
41 {
42         struct volume *v = volume_find("rootfs_data");
43         int ret;
44
45         if (!v)
46                 return -1;
47
48         ret = volatile_write(v, 0);
49         if (!ret)
50                 ret = sentinel_write(v, 0);
51
52         return ret;
53 }
54
55 static int
56 config_read(int argc, char **argv)
57 {
58         struct volume *v = volume_find("rootfs_data");
59         struct file_header conf, sentinel;
60         int next, block, ret = 0;
61         uint32_t seq;
62
63         if (!v)
64                 return -1;
65
66         block = config_find(v, &conf, &sentinel);
67         next = snapshot_next_free(v, &seq);
68         if (is_config(&conf) && conf.seq == seq)
69                 block = next;
70         else if (!is_config(&sentinel) || sentinel.seq != seq)
71                 return -1;
72
73         unlink("/tmp/config.tar.gz");
74         ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
75
76         if (ret < 1)
77                 ULOG_ERR("failed to read /tmp/config.tar.gz\n");
78
79         return ret;
80 }
81
82 static int
83 snapshot_write(int argc, char **argv)
84 {
85         struct volume *v = volume_find("rootfs_data");
86         int block, ret;
87         uint32_t seq;
88
89         if (!v)
90                 return -1;
91
92         block = snapshot_next_free(v, &seq);
93         if (block < 0)
94                 block = 0;
95
96         ret = snapshot_write_file(v, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
97         if (ret)
98                 ULOG_ERR("failed to write /tmp/snapshot.tar.gz\n");
99         else
100                 ULOG_INFO("wrote /tmp/snapshot.tar.gz\n");
101
102         return ret;
103 }
104
105 static int
106 snapshot_mark(int argc, char **argv)
107 {
108         __be32 owrt = cpu_to_be32(OWRT);
109         struct volume *v;
110         size_t sz;
111         int fd;
112
113         ULOG_WARN("This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
114         if (getchar() != 'y')
115                 return -1;
116
117         v = volume_find("rootfs_data");
118         if (!v) {
119                 ULOG_ERR("MTD partition 'rootfs_data' not found\n");
120                 return -1;
121         }
122
123         fd = open(v->blk, O_WRONLY);
124         ULOG_INFO("%s - marking with 0x%08x\n", v->blk, owrt);
125         if (fd < 0) {
126                 ULOG_ERR("opening %s failed\n", v->blk);
127                 return -1;
128         }
129
130         sz = write(fd, &owrt, sizeof(owrt));
131         close(fd);
132
133         if (sz != 1) {
134                 ULOG_ERR("writing %s failed: %s\n", v->blk, strerror(errno));
135                 return -1;
136         }
137
138         return 0;
139 }
140
141 static int
142 snapshot_read(int argc, char **argv)
143 {
144         struct volume *v = volume_find("rootfs_data");;
145         int block = 0, ret = 0;
146         char file[64];
147
148         if (!v)
149                 return -1;
150
151         if (argc > 2) {
152                 block = atoi(argv[2]);
153                 if (block >= (v->size / v->block_size)) {
154                         ULOG_ERR("invalid block %d > %" PRIu64 "\n",
155                                  block, (uint64_t) v->size / v->block_size);
156                         goto out;
157                 }
158                 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
159
160                 ret = snapshot_read_file(v, block, file, DATA);
161                 goto out;
162         }
163
164         do {
165                 snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
166                 block = snapshot_read_file(v, block, file, DATA);
167         } while (block > 0);
168
169 out:
170         return ret;
171 }
172
173 static int
174 snapshot_info(void)
175 {
176         struct volume *v = volume_find("rootfs_data");
177         struct file_header hdr = { 0 }, conf;
178         int block = 0;
179
180         if (!v)
181                 return -1;
182
183         ULOG_INFO("sectors:\t%" PRIu64 ", block_size:\t%dK\n",
184                   (uint64_t) v->size / v->block_size, v->block_size / 1024);
185         do {
186                 if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
187                         ULOG_ERR("scanning for next free block failed\n");
188                         return 0;
189                 }
190
191                 be32_to_hdr(&hdr);
192
193                 if (hdr.magic != OWRT)
194                         break;
195
196                 if (hdr.type == DATA)
197                         ULOG_INFO("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);
198                 else if (hdr.type == CONF)
199                         ULOG_INFO("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);
200
201                 if (hdr.type == DATA && !valid_file_size(hdr.length))
202                         block += pad_file_size(v, hdr.length) / v->block_size;
203         } while (hdr.type == DATA);
204         block = config_find(v, &conf, &hdr);
205         if (block > 0)
206                 ULOG_INFO("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);
207
208         return 0;
209 }
210
211 int main(int argc, char **argv)
212 {
213         if (argc < 2)
214                 return -1;
215
216         if (!strcmp(argv[1], "config_read"))
217                 return config_read(argc, argv);
218         if (!strcmp(argv[1], "config_write"))
219                 return config_write(argc, argv);
220         if (!strcmp(argv[1], "read"))
221                 return snapshot_read(argc, argv);
222         if (!strcmp(argv[1], "write"))
223                 return snapshot_write(argc, argv);
224         if (!strcmp(argv[1], "mark"))
225                 return snapshot_mark(argc, argv);
226         if (!strcmp(argv[1], "info"))
227                 return snapshot_info();
228         return -1;
229 }