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