initial import of fs-tools package
[project/fstools.git] / lib / 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/stat.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/ioctl.h>
18 #include <asm/byteorder.h>
19 #include <mtd/mtd-user.h>
20
21 #include <errno.h>
22 #include <glob.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <libgen.h>
27 #include <unistd.h>
28 #include <string.h>
29
30 #include "../fs-state.h"
31 #include "mtd.h"
32
33 #define PATH_MAX        256
34
35 int mtdsize = 0;
36 int erasesize = 0;
37
38 int
39 mtd_open(const char *mtd, int block)
40 {
41         FILE *fp;
42         char dev[PATH_MAX];
43         int i, ret, flags = O_RDWR | O_SYNC;
44
45         if ((fp = fopen("/proc/mtd", "r"))) {
46                 while (fgets(dev, sizeof(dev), fp)) {
47                         if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
48                                 snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
49                                 ret = open(dev, flags);
50                                 if (ret < 0) {
51                                         snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
52                                         ret = open(dev, flags);
53                                 }
54                                 fclose(fp);
55                                 return ret;
56                         }
57                 }
58                 fclose(fp);
59         }
60
61         return open(mtd, flags);
62 }
63
64 int
65 mtd_load(const char *mtd)
66 {
67         struct mtd_info_user mtdInfo;
68         struct erase_info_user mtdLockInfo;
69         int fd;
70
71         fd = mtd_open(mtd, 0);
72         if (fd < 0) {
73                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
74                 return -1;
75         }
76
77         if (ioctl(fd, MEMGETINFO, &mtdInfo)) {
78                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
79                 close(fd);
80                 return -1;
81         }
82
83         mtdsize = mtdInfo.size;
84         erasesize = mtdInfo.erasesize;
85
86         mtdLockInfo.start = 0;
87         mtdLockInfo.length = mtdsize;
88         ioctl(fd, MEMUNLOCK, &mtdLockInfo);
89
90         return fd;
91 }
92
93 void
94 mtd_erase(int fd, int first_block, int num_blocks)
95 {
96         struct erase_info_user eiu;
97
98         eiu.length = erasesize;
99         for (eiu.start = first_block * erasesize;
100                         eiu.start < mtdsize && eiu.start < (first_block + num_blocks) * erasesize;
101                         eiu.start += erasesize) {
102                 fprintf(stderr, "erasing %x %x\n", eiu.start, erasesize);
103                 ioctl(fd, MEMUNLOCK, &eiu);
104                 if (ioctl(fd, MEMERASE, &eiu))
105                         fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
106         }
107 }
108
109 int
110 mtd_unlock(int fd)
111 {
112         struct mtd_info_user mtdinfo;
113         int ret = ioctl(fd, MEMGETINFO, &mtdinfo);
114
115         if (ret) {
116                 fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", fd, strerror(errno));
117         } else {
118                 struct erase_info_user mtdlock;
119
120                 mtdlock.start = 0;
121                 mtdlock.length = mtdinfo.size;
122                 ioctl(fd, MEMUNLOCK, &mtdlock);
123         }
124
125         return ret;
126 }
127
128 int
129 mtd_read_buffer(int fd, void *buf, int offset, int length)
130 {
131         if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
132                 fprintf(stderr, "lseek/read failed\n");
133                 return -1;
134         }
135
136         if (read(fd, buf, length) == -1) {
137                 fprintf(stderr, "read failed\n");
138                 return -1;
139         }
140
141         return 0;
142 }
143
144 int
145 mtd_write_buffer(int fd, void *buf, int offset, int length)
146 {
147         if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
148                 fprintf(stderr, "lseek/write failed at offset %d\n", offset);
149                 perror("lseek");
150                 return -1;
151         }
152
153         if (write(fd, buf, length) == -1) {
154                 fprintf(stderr, "write failed\n");
155                 return -1;
156         }
157
158         return 0;
159 }
160
161 int
162 mtd_identify(char *mtd)
163 {
164         int fd = mtd_load(mtd);
165         __u32 deadc0de;
166         __u16 jffs2;
167         size_t sz;
168
169         if (!fd) {
170                 fprintf(stderr, "reading %s failed\n", mtd);
171                 return -1;
172         }
173
174         sz = read(fd, &deadc0de, sizeof(deadc0de));
175         close(fd);
176
177         if (sz != sizeof(deadc0de)) {
178                 fprintf(stderr, "reading %s failed: %s\n", mtd, strerror(errno));
179                 return -1;
180         }
181
182         if (deadc0de == 0x4f575254)
183                 return FS_SNAPSHOT;
184
185         deadc0de = __be32_to_cpu(deadc0de);
186         jffs2 = __be16_to_cpu(deadc0de >> 16);
187
188         if (jffs2 == 0x1985) {
189                 fprintf(stderr, "jffs2 is ready\n");
190                 return FS_JFFS2;
191         }
192
193         if (deadc0de == 0xdeadc0de) {
194                 fprintf(stderr, "jffs2 is not ready - marker found\n");
195                 return FS_DEADCODE;
196         }
197
198         fprintf(stderr, "No jffs2 marker was found\n");
199
200         return FS_NONE;
201 }