split snapshot tool properly
[project/fstools.git] / libfstools / mtd.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/mount.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <asm/byteorder.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <mtd/mtd-user.h>
23
24 #include "libfstools.h"
25
26 #include "volume.h"
27
28 #define PATH_MAX 256
29
30 struct mtd_priv {
31 int fd;
32 int idx;
33 char *chr;
34 };
35
36 static struct driver mtd_driver;
37
38 static int mtd_open(const char *mtd, int block)
39 {
40 FILE *fp;
41 char dev[PATH_MAX];
42 int i, ret, flags = O_RDWR | O_SYNC;
43
44 if ((fp = fopen("/proc/mtd", "r"))) {
45 while (fgets(dev, sizeof(dev), fp)) {
46 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
47 snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
48 ret = open(dev, flags);
49 if (ret < 0) {
50 snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
51 ret = open(dev, flags);
52 }
53 fclose(fp);
54 return ret;
55 }
56 }
57 fclose(fp);
58 }
59
60 return open(mtd, flags);
61 }
62
63 static void mtd_volume_close(struct volume *v)
64 {
65 struct mtd_priv *p = (struct mtd_priv*) v->priv;
66
67 if (!p->fd)
68 return;
69
70 close(p->fd);
71 p->fd = 0;
72 }
73
74 static int mtd_volume_load(struct volume *v)
75 {
76 struct mtd_priv *p = (struct mtd_priv*) v->priv;
77 struct mtd_info_user mtdInfo;
78 struct erase_info_user mtdLockInfo;
79
80 if (p->fd)
81 return 0;
82
83 if (!p->chr)
84 return -1;
85
86 p->fd = mtd_open(p->chr, 0);
87 if (p->fd < 0) {
88 p->fd = 0;
89 fprintf(stderr, "Could not open mtd device: %s\n", p->chr);
90 return -1;
91 }
92
93 if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
94 mtd_volume_close(v);
95 fprintf(stderr, "Could not get MTD device info from %s\n", p->chr);
96 return -1;
97 }
98
99 v->size = mtdInfo.size;
100 v->block_size = mtdInfo.erasesize;
101 switch (mtdInfo.type) {
102 case MTD_NORFLASH:
103 v->type = NORFLASH;
104 break;
105 case MTD_NANDFLASH:
106 v->type = NANDFLASH;
107 break;
108 case MTD_UBIVOLUME:
109 v->type = UBIVOLUME;
110 break;
111 default:
112 v->type = UNKNOWN_TYPE;
113 break;
114 }
115
116 mtdLockInfo.start = 0;
117 mtdLockInfo.length = v->size;
118 ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
119
120 return 0;
121 }
122
123 static char* mtd_find_index(char *name)
124 {
125 FILE *fp = fopen("/proc/mtd", "r");
126 static char line[256];
127 char *index = NULL;
128
129 if(!fp)
130 return index;
131
132 while (!index && fgets(line, sizeof(line), fp)) {
133 if (strstr(line, name)) {
134 char *eol = strstr(line, ":");
135
136 if (!eol)
137 continue;
138
139 *eol = '\0';
140 index = &line[3];
141 }
142 }
143
144 fclose(fp);
145
146 return index;
147 }
148
149 static int mtd_volume_find(struct volume *v, char *name)
150 {
151 char *idx = mtd_find_index(name);
152 struct mtd_priv *p;
153 char buffer[32];
154
155 if (!idx)
156 return -1;
157
158 p = calloc(1, sizeof(struct mtd_priv));
159 if (!p)
160 return -1;
161
162 v->priv = p;
163 v->name = strdup(name);
164 v->drv = &mtd_driver;
165 p->idx = atoi(idx);
166
167 snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
168 v->blk = strdup(buffer);
169
170 snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
171 p->chr = strdup(buffer);
172
173 if (mtd_volume_load(v)) {
174 fprintf(stderr, "reading %s failed\n", v->name);
175 return -1;
176 }
177
178 return 0;
179 }
180
181 static int mtd_volume_identify(struct volume *v)
182 {
183 struct mtd_priv *p = (struct mtd_priv*) v->priv;
184 __u32 deadc0de;
185 __u16 jffs2;
186 size_t sz;
187
188 if (mtd_volume_load(v)) {
189 fprintf(stderr, "reading %s failed\n", v->name);
190 return -1;
191 }
192
193 sz = read(p->fd, &deadc0de, sizeof(deadc0de));
194
195 if (sz != sizeof(deadc0de)) {
196 fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno));
197 return -1;
198 }
199
200 if (deadc0de == __be32_to_cpu(0x4f575254))
201 return FS_SNAPSHOT;
202
203 deadc0de = __be32_to_cpu(deadc0de);
204 if (deadc0de == 0xdeadc0de) {
205 fprintf(stderr, "jffs2 is not ready - marker found\n");
206 return FS_DEADCODE;
207 }
208
209 jffs2 = __be16_to_cpu(deadc0de >> 16);
210 if (jffs2 == 0x1985) {
211 fprintf(stderr, "jffs2 is ready\n");
212 return FS_JFFS2;
213 }
214
215 if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
216 fprintf(stderr, "jffs2 is ready\n");
217 return FS_JFFS2;
218 }
219
220 fprintf(stderr, "No jffs2 marker was found\n");
221
222 return FS_NONE;
223 }
224
225 static int mtd_volume_erase(struct volume *v, int offset, int len)
226 {
227 struct mtd_priv *p = (struct mtd_priv*) v->priv;
228 struct erase_info_user eiu;
229 int first_block, num_blocks;
230
231 if (mtd_volume_load(v))
232 return -1;
233
234 if (offset % v->block_size || len % v->block_size) {
235 fprintf(stderr, "mtd erase needs to be block aligned\n");
236 return -1;
237 }
238
239 first_block = offset / v->block_size;
240 num_blocks = len / v->block_size;
241 eiu.length = v->block_size;
242
243 for (eiu.start = first_block * v->block_size;
244 eiu.start < v->size && eiu.start < (first_block + num_blocks) * v->block_size;
245 eiu.start += v->block_size) {
246 fprintf(stderr, "erasing %x %x\n", eiu.start, v->block_size);
247 ioctl(p->fd, MEMUNLOCK, &eiu);
248 if (ioctl(p->fd, MEMERASE, &eiu))
249 fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
250 }
251
252 mtd_volume_close(v);
253
254 return 0;
255 }
256
257 static int mtd_volume_erase_all(struct volume *v)
258 {
259 mtd_volume_erase(v, 0, v->size);
260 mtd_volume_close(v);
261
262 return 0;
263 }
264
265 static int mtd_volume_init(struct volume *v)
266 {
267 struct mtd_priv *p = (struct mtd_priv*) v->priv;
268 struct mtd_info_user mtdinfo;
269 int ret;
270
271 if (mtd_volume_load(v))
272 return -1;
273
274 ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
275 if (ret) {
276 fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", p->fd, strerror(errno));
277 } else {
278 struct erase_info_user mtdlock;
279
280 mtdlock.start = 0;
281 mtdlock.length = mtdinfo.size;
282 ioctl(p->fd, MEMUNLOCK, &mtdlock);
283 }
284
285 return ret;
286 }
287
288 static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
289 {
290 struct mtd_priv *p = (struct mtd_priv*) v->priv;
291
292 if (mtd_volume_load(v))
293 return -1;
294
295 if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
296 fprintf(stderr, "lseek/read failed\n");
297 return -1;
298 }
299
300 if (read(p->fd, buf, length) == -1) {
301 fprintf(stderr, "read failed\n");
302 return -1;
303 }
304
305 return 0;
306 }
307
308 static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
309 {
310 struct mtd_priv *p = (struct mtd_priv*) v->priv;
311
312 if (mtd_volume_load(v))
313 return -1;
314
315 if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
316 fprintf(stderr, "lseek/write failed at offset %d\n", offset);
317 perror("lseek");
318 return -1;
319 }
320
321 if (write(p->fd, buf, length) == -1) {
322 fprintf(stderr, "write failed\n");
323 return -1;
324 }
325
326 return 0;
327 }
328
329 static struct driver mtd_driver = {
330 .name = "mtd",
331 .find = mtd_volume_find,
332 .init = mtd_volume_init,
333 .erase = mtd_volume_erase,
334 .erase_all = mtd_volume_erase_all,
335 .read = mtd_volume_read,
336 .write = mtd_volume_write,
337 .identify = mtd_volume_identify,
338 };
339 DRIVER(mtd_driver);