2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
28 #include <sys/types.h>
29 #include <sys/mount.h>
31 #include <asm/byteorder.h>
33 #include <mtd/mtd-user.h>
35 #define DEBUG(level, fmt, ...) do { \
37 fprintf(stderr, "%s %s(%d): " fmt, argv0, __func__, __LINE__, ## __VA_ARGS__); \
40 #define LOG(fmt, ...) do { \
41 syslog(LOG_INFO, fmt, ## __VA_ARGS__); \
42 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
45 #define ERROR(fmt, ...) do { \
46 syslog(LOG_ERR, fmt, ## __VA_ARGS__); \
47 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
56 static const char *argv0
;
58 /* this is a raw syscall - man 2 pivot_root */
59 extern int pivot_root(const char *new_root
, const char *put_old
);
63 static void foreachdir(const char *dir
, int (*cb
)(const char*))
69 if (dir
[strlen(dir
) - 1] == '/')
70 snprintf(globdir
, 256, "%s*", dir
);
72 snprintf(globdir
, 256, "%s/*", dir
);
74 if (!glob(globdir
, GLOB_NOESCAPE
| GLOB_MARK
| GLOB_ONLYDIR
, NULL
, &gl
))
75 for (j
= 0; j
< gl
.gl_pathc
; j
++)
76 foreachdir(gl
.gl_pathv
[j
], cb
);
81 static char* find_mount_point(char *block
, char *fs
)
83 FILE *fp
= fopen("/proc/mounts", "r");
84 static char line
[256];
85 int len
= strlen(block
);
91 while (fgets(line
, sizeof(line
), fp
)) {
92 if (!strncmp(line
, block
, len
)) {
93 char *p
= &line
[len
+ 1];
94 char *t
= strstr(p
, " ");
102 if (fs
&& strncmp(t
, fs
, strlen(fs
))) {
103 ERROR("block is mounted with wrong fs\n");
116 static char* find_mtd_index(char *name
)
118 FILE *fp
= fopen("/proc/mtd", "r");
119 static char line
[256];
125 while (!index
&& fgets(line
, sizeof(line
), fp
)) {
126 if (strstr(line
, name
)) {
127 char *eol
= strstr(line
, ":");
134 DEBUG(1, "found %s -> index:%s\n", name
, index
);
143 static int find_mtd_block(char *name
, char *part
, int plen
)
145 char *index
= find_mtd_index(name
);
150 snprintf(part
, plen
, "/dev/mtdblock%s", index
);
151 DEBUG(1, "found %s -> %s\n", name
, part
);
156 static int find_mtd_char(char *name
, char *part
, int plen
)
158 char *index
= find_mtd_index(name
);
163 snprintf(part
, plen
, "/dev/mtd%s", index
);
164 DEBUG(1, "found %s -> %s\n", name
, part
);
169 static int mtd_unlock(char *mtd
)
171 struct erase_info_user mtdlock
;
172 struct mtd_info_user mtdinfo
;
173 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
176 DEBUG(1, "%s\n", mtd
);
179 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
183 ret
= ioctl(fd
, MEMGETINFO
, &mtdinfo
);
185 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
190 mtdlock
.length
= mtdinfo
.size
;
191 ioctl(fd
, MEMUNLOCK
, &mtdlock
);
199 static int mtd_erase(const char *mtd
)
201 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
202 struct mtd_info_user i
;
203 struct erase_info_user e
;
207 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
211 ret
= ioctl(fd
, MEMGETINFO
, &i
);
213 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
217 e
.length
= i
.erasesize
;
218 for (e
.start
= 0; e
.start
< i
.size
; e
.start
+= i
.erasesize
) {
219 ioctl(fd
, MEMUNLOCK
, &e
);
220 if(ioctl(fd
, MEMERASE
, &e
))
221 ERROR("Failed to erase block on %s at 0x%x\n", mtd
, e
.start
);
228 static int do_mount_jffs2(void)
230 char rootfs_data
[32];
233 if (mkdir("/tmp/overlay", 0755)) {
234 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno
));
238 if (find_mtd_block("rootfs_data", rootfs_data
, sizeof(rootfs_data
))) {
239 ERROR("rootfs_data does not exist\n");
243 if (mount(rootfs_data
, "/tmp/overlay", "jffs2", MS_NOATIME
, NULL
)) {
244 ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data
, strerror(errno
));
248 find_mtd_char("rootfs_data", rootfs_data
, sizeof(rootfs_data
));
250 return mtd_unlock(rootfs_data
);
253 static int jffs2_ready(char *mtd
)
255 FILE *fp
= fopen(mtd
, "r");
261 ERROR("reading %s failed\n", mtd
);
265 sz
= fread(&deadc0de
, sizeof(deadc0de
), 1, fp
);
269 ERROR("reading %s failed: %s\n", mtd
, strerror(errno
));
273 deadc0de
= __be32_to_cpu(deadc0de
);
274 jffs2
= __be16_to_cpu(deadc0de
>> 16);
276 if (jffs2
== 0x1985) {
277 LOG("jffs2 is ready\n");
281 if (deadc0de
== 0xdeadc0de) {
282 LOG("jffs2 is not ready - marker found\n");
286 ERROR("No jffs2 marker was found\n");
291 static int check_fs_exists(char *fs
)
293 FILE *fp
= fopen("/proc/filesystems", "r");
294 static char line
[256];
297 DEBUG(2, "%s\n", fs
);
300 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno
));
304 while (ret
&& fgets(line
, sizeof(line
), fp
))
305 if (strstr(line
, fs
))
314 static int mount_move(char *oldroot
, char *newroot
, char *dir
)
317 #define MS_MOVE (1 << 13)
324 DEBUG(2, "%s %s\n", oldroot
, dir
);
326 snprintf(olddir
, sizeof(olddir
), "%s%s", oldroot
, dir
);
327 snprintf(newdir
, sizeof(newdir
), "%s%s", newroot
, dir
);
329 if (stat(olddir
, &s
) || !S_ISDIR(s
.st_mode
))
332 if (stat(newdir
, &s
) || !S_ISDIR(s
.st_mode
))
335 ret
= mount(olddir
, newdir
, NULL
, MS_NOATIME
| MS_MOVE
, NULL
);
338 DEBUG(1, "failed %s %s: %s\n", olddir
, newdir
, strerror(errno
));
343 static int pivot(char *new, char *old
)
348 DEBUG(2, "%s %s\n", new, old
);
350 if (mount_move("", new, "/proc"))
353 snprintf(pivotdir
, sizeof(pivotdir
), "%s%s", new, old
);
355 ret
= pivot_root(new, pivotdir
);
358 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir
, strerror(errno
));
362 mount_move(old
, "", "/dev");
363 mount_move(old
, "", "/tmp");
364 mount_move(old
, "", "/sys");
365 mount_move(old
, "", "/overlay");
371 static int fopivot(char *rw_root
, char *ro_root
)
373 char overlay
[64], lowerdir
[64];
375 DEBUG(2, "%s %s\n", rw_root
, ro_root
);
377 if (check_fs_exists("overlay")) {
378 ERROR("BUG: no suitable fs found\n");
382 snprintf(overlay
, sizeof(overlay
), "overlayfs:%s", rw_root
);
383 snprintf(lowerdir
, sizeof(lowerdir
), "lowerdir=/,upperdir=%s", rw_root
);
385 if (mount(overlay
, "/mnt", "overlayfs", MS_NOATIME
, lowerdir
)) {
386 ERROR("mount failed: %s\n", strerror(errno
));
390 return pivot("/mnt", ro_root
);
393 static int ramoverlay(void)
397 mkdir("/tmp/root", 0755);
398 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME
, "mode=0755");
400 return fopivot("/tmp/root", "/rom");
403 static int switch2jffs(void)
407 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
408 ERROR("no rootfs_data was found\n");
412 if (mount(mtd
, "/rom/overlay", "jffs2", MS_NOATIME
, NULL
)) {
413 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd
, strerror(errno
));
417 if (mount("none", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0)) {
418 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno
));
422 system("cp -a /tmp/root/* /rom/overlay");
424 if (pivot("/rom", "/mnt")) {
425 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno
));
429 if (mount_move("/mnt", "/tmp/root", "")) {
430 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno
));
434 return fopivot("/overlay", "/rom");
437 static int handle_whiteout(const char *dir
)
442 struct dirent
**namelist
;
445 n
= scandir(dir
, &namelist
, NULL
, NULL
);
453 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
454 if (!lstat(file
, &s
) && S_ISLNK(s
.st_mode
)) {
455 sz
= readlink(file
, link
, sizeof(link
) - 1);
460 orig
= strstr(&file
[1], "/");
461 if (orig
&& !strcmp(link
, "(overlay-whiteout)")) {
462 DEBUG(1, "unlinking %s\n", orig
);
474 static int main_switch2jffs(int argc
, char **argv
)
480 if (check_fs_exists("overlay")) {
481 ERROR("overlayfs not found\n");
485 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
486 mp
= find_mount_point(mtd
, NULL
);
488 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
492 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
493 ERROR("no rootfs_data was found\n");
497 switch (jffs2_ready(mtd
)) {
499 ERROR("no jffs2 marker found\n");
505 DEBUG(1, "doing fo cleanup\n");
506 umount2("/tmp/root", MNT_DETACH
);
507 foreachdir("/overlay/", handle_whiteout
);
512 ret
= do_mount_jffs2();
515 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
516 ERROR("switching to jffs2 failed\n");
525 static int ask_user(int argc
, char **argv
)
527 if ((argc
< 2) || strcmp(argv
[1], "-y")) {
528 LOG("This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
529 if (getchar() != 'y')
536 static int handle_rmdir(const char *dir
)
539 struct dirent
**namelist
;
542 n
= scandir(dir
, &namelist
, NULL
, NULL
);
550 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
551 if (!lstat(file
, &s
) && !S_ISDIR(s
.st_mode
)) {
552 DEBUG(1, "unlinking %s\n", file
);
559 DEBUG(1, "rmdir %s\n", dir
);
565 static int main_jffs2reset(int argc
, char **argv
)
570 if (ask_user(argc
, argv
))
573 if (check_fs_exists("overlay")) {
574 ERROR("overlayfs not found\n");
578 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
579 ERROR("no rootfs_data was found\n");
583 mp
= find_mount_point(mtd
, "jffs2");
585 LOG("%s is mounted as %s, only ereasing files\n", mtd
, mp
);
586 foreachdir(mp
, handle_rmdir
);
587 mount(mp
, "/", NULL
, MS_REMOUNT
, 0);
589 LOG("%s is not mounted, erasing it\n", mtd
);
590 find_mtd_char("rootfs_data", mtd
, sizeof(mtd
));
597 static int main_jffs2mark(int argc
, char **argv
)
600 __u32 deadc0de
= __cpu_to_be32(0xdeadc0de);
604 if (ask_user(argc
, argv
))
607 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
608 ERROR("no rootfs_data was found\n");
612 fp
= fopen(mtd
, "w");
613 LOG("%s - marking with deadc0de\n", mtd
);
615 ERROR("opening %s failed\n", mtd
);
619 sz
= fwrite(&deadc0de
, sizeof(deadc0de
), 1, fp
);
623 ERROR("writing %s failed: %s\n", mtd
, strerror(errno
));
630 int main(int argc
, char **argv
)
635 argv0
= basename(*argv
);
637 if (!strcmp(basename(*argv
), "switch2jffs"))
638 return main_switch2jffs(argc
, argv
);
640 if (!strcmp(basename(*argv
), "jffs2mark"))
641 return main_jffs2mark(argc
, argv
);
643 if (!strcmp(basename(*argv
), "jffs2reset"))
644 return main_jffs2reset(argc
, argv
);
646 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
647 if (!find_mtd_char("rootfs", mtd
, sizeof(mtd
)))
649 LOG("mounting /dev/root\n");
650 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0);
652 switch (jffs2_ready(mtd
)) {
658 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
659 mp
= find_mount_point(mtd
, NULL
);
661 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
666 DEBUG(1, "switching to jffs2\n");
667 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
668 ERROR("switching to jffs2 failed - fallback to ramoverlay\n");