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