3051720f65d8df6fc58848ff2422eae5d3c88d17
[project/fstools.git] / libfstools / ubi.c
1 /*
2  * Copyright (C) 2014 Daniel Golle <daniel@makrotopia.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 <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <dirent.h>
18
19 #include "libfstools.h"
20 #include "volume.h"
21
22 /* could use libubi-tiny instead, but already had the code directly reading
23  * from sysfs */
24 char *ubi_dir_name = "/sys/devices/virtual/ubi";
25
26 struct ubi_priv {
27         int             ubi_num;
28         int             ubi_volidx;
29 };
30
31 static struct driver ubi_driver;
32
33 static unsigned int
34 read_uint_from_file(char *dirname, char *filename, unsigned int *i)
35 {
36         FILE *f;
37         char fname[128];
38         int ret = -1;
39
40         snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
41
42         f = fopen(fname, "r");
43         if (!f)
44                 return ret;
45
46         if (fscanf(f, "%u", i) == 1)
47                 ret = 0;
48
49         fclose(f);
50         return ret;
51 }
52
53 static char
54 *read_string_from_file(char *dirname, char *filename)
55 {
56         FILE *f;
57         char fname[128];
58         char buf[128];
59         int i;
60         char *str;
61
62         snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
63
64         f = fopen(fname, "r");
65         if (!f)
66                 return NULL;
67
68         if (fgets(buf, 128, f) == NULL)
69                 return NULL;
70
71         i = strlen(buf);
72         do
73                 buf[i--]='\0';
74         while (buf[i] == '\n');
75
76         str = strdup(buf);
77         fclose(f);
78         return str;
79 }
80
81 static unsigned int
82 test_open(char *filename)
83 {
84         FILE *f;
85
86         f = fopen(filename, "r");
87         if (!f)
88                 return 0;
89
90         fclose(f);
91         return 1;
92 }
93
94 static int ubi_volume_init(struct volume *v)
95 {
96         char voldir[40], voldev[32], *volname;
97         struct ubi_priv *p;
98         unsigned int volsize;
99
100         p = (struct ubi_priv*)v->priv;
101
102         snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
103                 ubi_dir_name, p->ubi_num, p->ubi_num, p->ubi_volidx);
104
105         snprintf(voldev, sizeof(voldev), "/dev/ubi%u_%u",
106                 p->ubi_num, p->ubi_volidx);
107
108         volname = read_string_from_file(voldir, "name");
109         if (!volname)
110                 return -1;
111
112         if (read_uint_from_file(voldir, "data_bytes", &volsize))
113                 return -1;
114
115         v->name = volname;
116         v->type = UBIVOLUME;
117         v->size = volsize;
118         v->blk = strdup(voldev);
119
120         return 0;
121 }
122
123
124 static int ubi_volume_match(struct volume *v, char *name, int ubi_num, int volidx)
125 {
126         char voldir[40], volblkdev[40], *volname;
127         struct ubi_priv *p;
128
129         snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
130                 ubi_dir_name, ubi_num, ubi_num, volidx);
131
132         snprintf(volblkdev, sizeof(volblkdev), "/dev/ubiblock%u_%u",
133                 ubi_num, volidx);
134
135         /* skip if ubiblock device exists */
136         if (test_open(volblkdev))
137                 return -1;
138
139         volname = read_string_from_file(voldir, "name");
140
141         if (strncmp(name, volname, strlen(volname) + 1))
142                 return -1;
143
144         p = calloc(1, sizeof(struct ubi_priv));
145         if (!p)
146                 return -1;
147
148         v->priv = p;
149         v->drv = &ubi_driver;
150         p->ubi_num = ubi_num;
151         p->ubi_volidx = volidx;
152
153         return ubi_volume_init(v);
154 }
155
156 static int ubi_part_match(struct volume *v, char *name, unsigned int ubi_num)
157 {
158         unsigned int i, volumes_count;
159         char devdir[80];
160
161         snprintf(devdir, sizeof(devdir), "%s/ubi%u",
162                 ubi_dir_name, ubi_num);
163
164         if (read_uint_from_file(devdir, "volumes_count", &volumes_count))
165                 return -1;
166
167         for (i=0;i<volumes_count;i++) {
168                 if (!ubi_volume_match(v, name, ubi_num, i)) {
169                         return 0;
170                 }
171         }
172
173         return -1;
174 }
175
176 static int ubi_volume_find(struct volume *v, char *name)
177 {
178         DIR *ubi_dir;
179         struct dirent *ubi_dirent;
180         unsigned int ubi_num;
181         int ret = -1;
182
183         if (find_filesystem("ubifs"))
184                 return ret;
185
186         ubi_dir = opendir(ubi_dir_name);
187         /* check for os ubi support */
188         if (!ubi_dir)
189                 return ret;
190
191         /* probe ubi devices and volumes */
192         while ((ubi_dirent = readdir(ubi_dir)) != NULL) {
193                 if (ubi_dirent->d_name[0] == '.')
194                         continue;
195
196                 sscanf(ubi_dirent->d_name, "ubi%u", &ubi_num);
197                 if (!ubi_part_match(v, name, ubi_num)) {
198                         ret = 0;
199                         break;
200                 };
201         }
202         closedir(ubi_dir);
203         return ret;
204 }
205
206 static int ubi_volume_identify(struct volume *v)
207 {
208         return FS_UBIFS;
209 }
210
211 static struct driver ubi_driver = {
212         .name = "ubi",
213         .find = ubi_volume_find,
214         .init = ubi_volume_init,
215         .identify = ubi_volume_identify,
216 };
217
218 DRIVER(ubi_driver);