source: trunk/package/mtd/src/mtd.c @ 17522

Last change on this file since 17522 was 17522, checked in by nbd, 8 years ago

fix jffs2 eof marker search in mtd (patch by Daniel Dickinson)

  • Property svn:eol-style set to native
File size: 11.1 KB
Line 
1/*
2 * mtd - simple memory technology device manipulation tool
3 *
4 * Copyright (C) 2005 Waldemar Brodkorb <wbx@dass-it.de>,
5 *                        Felix Fietkau <nbd@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 *
22 * The code is based on the linux-mtd examples.
23 */
24
25#include <limits.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <stdint.h>
30#include <signal.h>
31#include <sys/ioctl.h>
32#include <sys/syscall.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <error.h>
36#include <time.h>
37#include <string.h>
38#include <sys/ioctl.h>
39#include <sys/types.h>
40#include <sys/param.h>
41#include <sys/mount.h>
42#include <sys/stat.h>
43#include <sys/reboot.h>
44#include <linux/reboot.h>
45#include "mtd-api.h"
46#include "mtd.h"
47
48#define MAX_ARGS 8
49#define JFFS2_DEFAULT_DIR       "" /* directory name without /, empty means root dir */
50
51struct trx_header {
52        uint32_t magic;         /* "HDR0" */
53        uint32_t len;           /* Length of file including header */
54        uint32_t crc32;         /* 32-bit CRC from flag_version to end of file */
55        uint32_t flag_version;  /* 0:15 flags, 16:31 version */
56        uint32_t offsets[3];    /* Offsets of partitions from start of header */
57};
58
59static char *buf = NULL;
60static char *imagefile = NULL;
61static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
62static int buflen = 0;
63int quiet;
64int mtdsize = 0;
65int erasesize = 0;
66
67int mtd_open(const char *mtd, bool block)
68{
69        FILE *fp;
70        char dev[PATH_MAX];
71        int i;
72        int ret;
73        int flags = O_RDWR | O_SYNC;
74
75        if ((fp = fopen("/proc/mtd", "r"))) {
76                while (fgets(dev, sizeof(dev), fp)) {
77                        if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
78                                snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
79                                if ((ret=open(dev, flags))<0) {
80                                        snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
81                                        ret=open(dev, flags);
82                                }
83                                fclose(fp);
84                                return ret;
85                        }
86                }
87                fclose(fp);
88        }
89
90        return open(mtd, flags);
91}
92
93int mtd_check_open(const char *mtd)
94{
95        struct mtd_info_user mtdInfo;
96        int fd;
97
98        fd = mtd_open(mtd, false);
99        if(fd < 0) {
100                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
101                return 0;
102        }
103
104        if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
105                fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
106                close(fd);
107                return 0;
108        }
109        mtdsize = mtdInfo.size;
110        erasesize = mtdInfo.erasesize;
111
112        return fd;
113}
114
115int mtd_erase_block(int fd, int offset)
116{
117        struct erase_info_user mtdEraseInfo;
118
119        mtdEraseInfo.start = offset;
120        mtdEraseInfo.length = erasesize;
121        ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
122        if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) {
123                fprintf(stderr, "Erasing mtd failed.\n");
124                exit(1);
125        }
126        return 0;
127}
128
129int mtd_write_buffer(int fd, const char *buf, int offset, int length)
130{
131        lseek(fd, offset, SEEK_SET);
132        write(fd, buf, length);
133        return 0;
134}
135
136
137static int
138image_check(int imagefd, const char *mtd)
139{
140        int ret = 1;
141#ifdef target_brcm
142        ret = trx_check(imagefd, mtd, buf, &buflen);
143#endif
144        return ret;
145}
146
147static int mtd_check(const char *mtd)
148{
149        int fd;
150
151        fd = mtd_check_open(mtd);
152        if (!fd)
153                return 0;
154
155        if (!buf)
156                buf = malloc(erasesize);
157
158        close(fd);
159        return 1;
160}
161
162static int
163mtd_unlock(const char *mtd)
164{
165        int fd;
166        struct erase_info_user mtdLockInfo;
167
168        fd = mtd_check_open(mtd);
169        if(fd <= 0) {
170                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
171                exit(1);
172        }
173
174        if (quiet < 2) 
175                fprintf(stderr, "Unlocking %s ...\n", mtd);
176
177        mtdLockInfo.start = 0;
178        mtdLockInfo.length = mtdsize;
179        if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
180                close(fd);
181                return 0;
182        }
183               
184        close(fd);
185        return 0;
186}
187
188static int
189mtd_erase(const char *mtd)
190{
191        int fd;
192        struct erase_info_user mtdEraseInfo;
193
194        if (quiet < 2)
195                fprintf(stderr, "Erasing %s ...\n", mtd);
196
197        fd = mtd_check_open(mtd);
198        if(fd <= 0) {
199                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
200                exit(1);
201        }
202
203        mtdEraseInfo.length = erasesize;
204
205        for (mtdEraseInfo.start = 0;
206                 mtdEraseInfo.start < mtdsize;
207                 mtdEraseInfo.start += erasesize) {
208               
209                ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
210                if(ioctl(fd, MEMERASE, &mtdEraseInfo))
211                        fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
212        }               
213
214        close(fd);
215        return 0;
216
217}
218
219static int
220mtd_refresh(const char *mtd)
221{
222        int fd;
223
224        if (quiet < 2)
225                fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
226
227        fd = mtd_check_open(mtd);
228        if(fd <= 0) {
229                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
230                exit(1);
231        }
232
233        if (ioctl(fd, MTDREFRESH, NULL)) {
234                fprintf(stderr, "Failed to refresh the MTD device\n");
235                close(fd);
236                exit(1);
237        }
238        close(fd);
239
240        if (quiet < 2)
241                fprintf(stderr, "\n");
242
243        return 0;
244}
245
246static int
247mtd_write(int imagefd, const char *mtd)
248{
249        int fd, result;
250        ssize_t r, w, e;
251
252        fd = mtd_check_open(mtd);
253        if(fd < 0) {
254                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
255                exit(1);
256        }
257               
258        if (quiet < 2)
259                fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
260
261        r = w = e = 0;
262        if (!quiet)
263                fprintf(stderr, " [ ]");
264
265        for (;;) {
266                /* buffer may contain data already (from trx check) */
267                do {
268                        r = read(imagefd, buf + buflen, erasesize - buflen);
269                        if (r < 0) {
270                                if ((errno == EINTR) || (errno == EAGAIN))
271                                        continue;
272                                else {
273                                        perror("read");
274                                        break;
275                                }
276                        }
277
278                        if (r == 0)
279                                break;
280
281                        buflen += r;
282                } while (buflen < erasesize);
283
284                if (buflen == 0)
285                        break;
286
287                if (jffs2file) {
288                        if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
289                                if (!quiet)
290                                        fprintf(stderr, "\b\b\b   ");
291                                if (quiet < 2)
292                                        fprintf(stderr, "\nAppending jffs2 data to from %s to %s...", jffs2file, mtd);
293                                /* got an EOF marker - this is the place to add some jffs2 data */
294                                mtd_replace_jffs2(mtd, fd, e, jffs2file);
295                                goto done;
296                        }
297                        /* no EOF marker, make sure we figure out the last inode number
298                         * before appending some data */
299                        mtd_parse_jffs2data(buf, jffs2dir);
300                }
301
302                /* need to erase the next block before writing data to it */
303                while (w + buflen > e) {
304                        if (!quiet)
305                                fprintf(stderr, "\b\b\b[e]");
306
307                        mtd_erase_block(fd, e);
308
309                        /* erase the chunk */
310                        e += erasesize;
311                }
312               
313                if (!quiet)
314                        fprintf(stderr, "\b\b\b[w]");
315               
316                if ((result = write(fd, buf, buflen)) < buflen) {
317                        if (result < 0) {
318                                fprintf(stderr, "Error writing image.\n");
319                                exit(1);
320                        } else {
321                                fprintf(stderr, "Insufficient space.\n");
322                                exit(1);
323                        }
324                }
325                w += buflen;
326
327                buflen = 0;
328        }
329        if (!quiet)
330                fprintf(stderr, "\b\b\b\b");
331
332done:
333        if (quiet < 2)
334                fprintf(stderr, "\n");
335
336        close(fd);
337        return 0;
338}
339
340static void usage(void)
341{
342        fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
343        "The device is in the format of mtdX (eg: mtd4) or its label.\n"
344        "mtd recognizes these commands:\n"
345        "        unlock                  unlock the device\n"
346        "        refresh                 refresh mtd partition\n"
347        "        erase                   erase all data on device\n"
348        "        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
349        "        jffs2write <file>       append <file> to the jffs2 partition on the device\n"
350        "Following options are available:\n"
351        "        -q                      quiet mode (once: no [w] on writing,\n"
352        "                                           twice: no status messages)\n"
353        "        -r                      reboot after successful command\n"
354        "        -f                      force write without trx checks\n"
355        "        -e <device>             erase <device> before executing the command\n"
356        "        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
357        "        -j <name>               integrate <file> into jffs2 data when writing an image\n"
358        "\n"
359        "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
360        "         mtd -r write linux.trx linux\n\n");
361        exit(1);
362}
363
364static void do_reboot(void)
365{
366        fprintf(stderr, "Rebooting ...\n");
367        fflush(stderr);
368
369        /* try regular reboot method first */
370        system("/sbin/reboot");
371        sleep(2);
372
373        /* if we're still alive at this point, force the kernel to reboot */
374        syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
375}
376
377int main (int argc, char **argv)
378{
379        int ch, i, boot, imagefd = 0, force, unlocked;
380        char *erase[MAX_ARGS], *device = NULL;
381        enum {
382                CMD_ERASE,
383                CMD_WRITE,
384                CMD_UNLOCK,
385                CMD_REFRESH,
386                CMD_JFFS2WRITE
387        } cmd = -1;
388       
389        erase[0] = NULL;
390        boot = 0;
391        force = 0;
392        buflen = 0;
393        quiet = 0;
394
395        while ((ch = getopt(argc, argv, "frqe:d:j:")) != -1)
396                switch (ch) {
397                        case 'f':
398                                force = 1;
399                                break;
400                        case 'r':
401                                boot = 1;
402                                break;
403                        case 'j':
404                                jffs2file = optarg;
405                                break;
406                        case 'q':
407                                quiet++;
408                                break;
409                        case 'e':
410                                i = 0;
411                                while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
412                                        i++;
413                                       
414                                erase[i++] = optarg;
415                                erase[i] = NULL;
416                                break;
417                        case 'd':
418                                jffs2dir = optarg;
419                                break;
420                        case '?':
421                        default:
422                                usage();
423                }
424        argc -= optind;
425        argv += optind;
426       
427        if (argc < 2)
428                usage();
429
430        if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
431                cmd = CMD_UNLOCK;
432                device = argv[1];
433        } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
434                cmd = CMD_REFRESH;
435                device = argv[1];
436        } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
437                cmd = CMD_ERASE;
438                device = argv[1];
439        } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
440                cmd = CMD_WRITE;
441                device = argv[2];
442       
443                if (strcmp(argv[1], "-") == 0) {
444                        imagefile = "<stdin>";
445                        imagefd = 0;
446                } else {
447                        imagefile = argv[1];
448                        if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
449                                fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
450                                exit(1);
451                        }
452                }
453       
454                if (!mtd_check(device)) {
455                        fprintf(stderr, "Can't open device for writing!\n");
456                        exit(1);
457                }
458                /* check trx file before erasing or writing anything */
459                if (!image_check(imagefd, device) && !force) {
460                        fprintf(stderr, "Image check failed.\n");
461                        exit(1);
462                }
463        } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
464                cmd = CMD_JFFS2WRITE;
465                device = argv[2];
466       
467                imagefile = argv[1];
468                if (!mtd_check(device)) {
469                        fprintf(stderr, "Can't open device for writing!\n");
470                        exit(1);
471                }
472        } else {
473                usage();
474        }
475
476        sync();
477       
478        i = 0;
479        unlocked = 0;
480        while (erase[i] != NULL) {
481                mtd_unlock(erase[i]);
482                mtd_erase(erase[i]);
483                if (strcmp(erase[i], device) == 0)
484                        unlocked = 1;
485                i++;
486        }
487       
488               
489        switch (cmd) {
490                case CMD_UNLOCK:
491                        if (!unlocked)
492                                mtd_unlock(device);
493                        break;
494                case CMD_ERASE:
495                        if (!unlocked)
496                                mtd_unlock(device);
497                        mtd_erase(device);
498                        break;
499                case CMD_WRITE:
500                        if (!unlocked)
501                                mtd_unlock(device);
502                        mtd_write(imagefd, device);
503                        break;
504                case CMD_JFFS2WRITE:
505                        if (!unlocked)
506                                mtd_unlock(device);
507                        mtd_write_jffs2(device, imagefile, jffs2dir);
508                        break;
509                case CMD_REFRESH:
510                        mtd_refresh(device);
511                        break;
512        }
513
514        sync();
515       
516        if (boot)
517                do_reboot();
518
519        return 0;
520}
Note: See TracBrowser for help on using the repository browser.