libfstools: ubi: rework reading volumes
[project/fstools.git] / ubi.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/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdint.h>
19 #include <getopt.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "libubi/libubi-tiny.h"
25
26 static int print_usage(void)
27 {
28         printf("ubi info\n");
29         printf("ubi detach kernel|rootfs\n");
30         printf("ubi kernel <image.kernel.ubi>\n");
31         printf("ubi rootfs <image.rootfs.ubi>\n");
32         printf("ubi overlay <image.rootfs-overlay.ubi>\n");
33
34         return -1;
35 }
36
37 static int mtd_find_index(char *name)
38 {
39         FILE *fp = fopen("/proc/mtd", "r");
40         char line[256];
41         char *index = NULL;
42
43         if (!fp)
44                 return -1;
45
46         while (!index && fgets(line, sizeof(line), fp)) {
47                 if (strstr(line, name)) {
48                         char *eol = strstr(line, ":");
49
50                         if (!eol)
51                                 continue;
52
53                         *eol = '\0';
54                         index = &line[3];
55                 }
56         }
57
58         fclose(fp);
59
60         if (!index)
61                 return -1;
62
63         return atoi(index);
64 }
65
66 static int mtd_find(char *name, char *ret)
67 {
68         int index = mtd_find_index(name);
69         if (index < 0)
70                 return -1;
71
72         sprintf(ret, "/dev/mtd%d", index);
73
74         return 0;
75 }
76
77 static int ubi_find(libubi_t libubi, char *name, char *ret)
78 {
79         int index = mtd_find_index(name);
80         int ubi = 0;
81
82         while (ubi_dev_present(libubi, ubi))
83         {
84                 struct ubi_dev_info info;
85
86                 if (ubi_get_dev_info1(libubi, ubi++, &info))
87                         continue;
88
89                 if (info.mtd_num != index)
90                         continue;
91
92                 sprintf(ret, "/dev/ubi%d", info.dev_num);
93
94                 return 0;
95         }
96
97         return -1;
98 }
99
100 static int volume_find(libubi_t libubi, char *name, char *ret)
101 {
102         int index = mtd_find_index(name);
103         struct ubi_vol_info vol;
104         int ubi = 0;
105
106         if (index < 0)
107                 return -1;
108
109         if (mtd_num2ubi_dev(libubi, index, &ubi)) {
110                 fprintf(stderr, "failed to get ubi node for %s\n", name);
111                 return -1;
112         }
113
114         if (ubi_get_vol_info1_nm(libubi, ubi, name, &vol)) {
115                 fprintf(stderr, "failed to get ubi volume info for %s\n", name);
116                 return -1;
117         }
118
119         sprintf(ret, "/dev/ubi%d_%d", ubi, vol.vol_id);
120
121         return 0;
122 }
123
124 static int main_detach(char *type)
125 {
126         libubi_t libubi;
127         char mtd[64];
128         int err;
129
130         if (!strcmp(type, "kernel"))
131                 err = mtd_find("kernel_ubi", mtd);
132         else if (!strcmp(type, "rootfs"))
133                 err = mtd_find("rootfs_ubi", mtd);
134         else
135                 return print_usage();
136
137         if (err) {
138                 fprintf(stderr, "failed to find mtd partition %s_ubi\n", type);
139                 return -1;
140         }
141
142         libubi = libubi_open();
143         if (!libubi) {
144                 fprintf(stderr, "cannot open libubi");
145                 return -1;
146         }
147
148         err = ubidetach(libubi, mtd);
149         if (err) {
150                 fprintf(stderr, "cannot detach \"%s\"", mtd);
151                 return -1;
152         }
153
154         return 0;
155 }
156
157 static int main_image(char *partition, char *image, char *overlay)
158 {
159         libubi_t libubi;
160         struct stat s;
161         int err;
162         char mtd[64];
163         char _part[64];
164         char node[64];
165         char volume[64];
166         char _data[64];
167         char *data = NULL;
168
169         if (stat(image, &s)) {
170                 fprintf(stderr, "image not found %s\n", image);
171                 return -1;
172         }
173
174         if (!strcmp(partition, "kernel"))
175                 err = mtd_find("kernel", _part);
176         else
177                 err = mtd_find("rootfs", _part);
178
179         if (overlay && !mtd_find(overlay, _data))
180                 data = _data;
181
182         libubi = libubi_open();
183         if (!libubi) {
184                 fprintf(stderr, "cannot open libubi");
185                 return -1;
186         }
187
188         if (!strcmp(partition, "kernel"))
189                 err = mtd_find("kernel_ubi", mtd);
190         else
191                 err = mtd_find("rootfs_ubi", mtd);
192         if (err) {
193                 fprintf(stderr, "failed to find mtd parent %s_ubi\n", partition);
194                 return -1;
195         }
196
197         if (!strcmp(partition, "kernel"))
198                 err = ubi_find(libubi, "kernel_ubi", node);
199         else
200                 err = ubi_find(libubi, "rootfs_ubi", node);
201         if (err) {
202                 fprintf(stderr, "failed to find ubi volume %s\n", partition);
203                 return -1;
204         }
205
206         err = ubidetach(libubi, mtd);
207         if (err) {
208                 fprintf(stderr, "cannot detach \"%s\"", mtd);
209                 return -1;
210         }
211
212         err = ubiattach(libubi, mtd);
213         if (err) {
214                 fprintf(stderr, "cannot attach \"%s\"", mtd);
215                 return -1;
216         }
217
218         if (data) {
219                 err = ubirmvol(libubi, node, overlay);
220                 if (err) {
221                         fprintf(stderr, "cannot remove \"%s\"", node);
222                         return -1;
223                 }
224         }
225
226         if (volume_find(libubi, partition, volume) < 0) {
227                 fprintf(stderr, "failed to find ubi volume %s\n", partition);
228                 return -1;
229         }
230
231         err = ubirsvol(libubi, node, partition, s.st_size);
232         if (err) {
233                 fprintf(stderr, "cannot resize \"%s\"", partition);
234                 return -1;
235         }
236
237         err = ubiupdatevol(libubi, volume, image);
238         if (err) {
239                 fprintf(stderr, "cannot update \"%s\"", volume);
240                 return -1;
241         }
242
243         if (overlay) {
244                 err = ubimkvol(libubi, node, overlay, 1);
245                 if (err) {
246                         fprintf(stderr, "cannot make \"%s\"", overlay);
247                         return -1;
248                 }
249         }
250
251         libubi_close(libubi);
252
253         return err;
254 }
255
256 static int main_info(void)
257 {
258         struct ubi_info info;
259         libubi_t libubi;
260         int i;
261
262         libubi = libubi_open();
263         if (!libubi) {
264                 fprintf(stderr, "cannot open libubi");
265                 return -1;
266         }
267
268         if (ubi_get_info(libubi, &info)) {
269                 fprintf(stderr, "failed to get info\n");
270                 return -1;
271         }
272
273         for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
274                 struct ubi_dev_info dinfo;
275                 char ubi[64];
276                 int j;
277
278                 sprintf(ubi, "/dev/ubi%d", i);
279                 if (ubi_get_dev_info(libubi, ubi, &dinfo))
280                         continue;
281                 printf("device - %s\n  size: %lldBytes\n  bad blocks: %d\n",
282                         &ubi[5], dinfo.total_bytes, dinfo.bad_count);
283                 for (j = dinfo.lowest_vol_id; j <= dinfo.highest_vol_id; j++) {
284                         struct ubi_vol_info vinfo;
285
286                         sprintf(ubi, "/dev/ubi%d_%d", i, j);
287                         if (ubi_get_vol_info(libubi, ubi, &vinfo))
288                                 continue;
289                         printf("  volume - %s\n", &ubi[5]);
290                         printf("\tname: %s\n", vinfo.name);
291                         printf("\tsize: %lld\n", vinfo.data_bytes);
292                 }
293         }
294
295         libubi_close(libubi);
296
297         return 0;
298 }
299
300 int main(int argc, char **argv)
301 {
302         if (argc > 1 && !strcmp(argv[1], "info"))
303                 return main_info();
304
305         if (argc < 3)
306                 return print_usage();
307
308         if (!strcmp(argv[1], "kernel")) {
309                 return main_image("kernel", argv[2], NULL);
310
311         } else if (!strcmp(argv[1], "rootfs")) {
312                 return main_image("rootfs", argv[2], NULL);
313
314         } else if (!strcmp(argv[1], "overlay")) {
315                 return main_image("rootfs", argv[2], "rootfs_data");
316
317         } else if (!strcmp(argv[1], "detach")) {
318                 return main_detach(argv[2]);
319         }
320
321         return -1;
322 }
323