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