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>
32 #include <asm/byteorder.h>
34 #include <mtd/mtd-user.h>
36 #define DEBUG(level, fmt, ...) do { \
38 fprintf(stderr, "%s %s(%d): " fmt, argv0, __func__, __LINE__, ## __VA_ARGS__); \
41 #define LOG(fmt, ...) do { \
42 syslog(LOG_INFO, fmt, ## __VA_ARGS__); \
43 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
46 #define ERROR(fmt, ...) do { \
47 syslog(LOG_ERR, fmt, ## __VA_ARGS__); \
48 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
57 static const char *argv0
;
59 /* this is a raw syscall - man 2 pivot_root */
60 extern int pivot_root(const char *new_root
, const char *put_old
);
64 static void foreachdir(const char *dir
, int (*cb
)(const char*))
70 if (dir
[strlen(dir
) - 1] == '/')
71 snprintf(globdir
, 256, "%s*", dir
);
73 snprintf(globdir
, 256, "%s/*", dir
);
75 if (!glob(globdir
, GLOB_NOESCAPE
| GLOB_MARK
| GLOB_ONLYDIR
, NULL
, &gl
))
76 for (j
= 0; j
< gl
.gl_pathc
; j
++)
77 foreachdir(gl
.gl_pathv
[j
], cb
);
82 static int find_overlay_mount(char *overlay
)
84 FILE *fp
= fopen("/proc/mounts", "r");
85 static char line
[256];
91 while (ret
&& fgets(line
, sizeof(line
), fp
))
92 if (!strncmp(line
, overlay
, strlen(overlay
)))
100 static char* find_mount(char *mp
)
102 FILE *fp
= fopen("/proc/mounts", "r");
103 static char line
[256];
109 while (fgets(line
, sizeof(line
), fp
)) {
110 char *s
, *t
= strstr(line
, " ");
120 if (!strcmp(t
, mp
)) {
131 static char* find_mount_point(char *block
, char *fs
)
133 FILE *fp
= fopen("/proc/mounts", "r");
134 static char line
[256];
135 int len
= strlen(block
);
141 while (fgets(line
, sizeof(line
), fp
)) {
142 if (!strncmp(line
, block
, len
)) {
143 char *p
= &line
[len
+ 1];
144 char *t
= strstr(p
, " ");
152 if (fs
&& strncmp(t
, fs
, strlen(fs
))) {
153 ERROR("block is mounted with wrong fs\n");
166 static char* find_mtd_index(char *name
)
168 FILE *fp
= fopen("/proc/mtd", "r");
169 static char line
[256];
175 while (!index
&& fgets(line
, sizeof(line
), fp
)) {
176 if (strstr(line
, name
)) {
177 char *eol
= strstr(line
, ":");
184 DEBUG(1, "found %s -> index:%s\n", name
, index
);
193 static int find_mtd_block(char *name
, char *part
, int plen
)
195 char *index
= find_mtd_index(name
);
200 snprintf(part
, plen
, "/dev/mtdblock%s", index
);
201 DEBUG(1, "found %s -> %s\n", name
, part
);
206 static int find_mtd_char(char *name
, char *part
, int plen
)
208 char *index
= find_mtd_index(name
);
213 snprintf(part
, plen
, "/dev/mtd%s", index
);
214 DEBUG(1, "found %s -> %s\n", name
, part
);
219 static int mtd_unlock(char *mtd
)
221 struct erase_info_user mtdlock
;
222 struct mtd_info_user mtdinfo
;
223 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
226 DEBUG(1, "%s\n", mtd
);
229 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
233 ret
= ioctl(fd
, MEMGETINFO
, &mtdinfo
);
235 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
240 mtdlock
.length
= mtdinfo
.size
;
241 ioctl(fd
, MEMUNLOCK
, &mtdlock
);
249 static int mtd_mount_jffs2(void)
251 char rootfs_data
[32];
253 if (mkdir("/tmp/overlay", 0755)) {
254 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno
));
258 if (find_mtd_block("rootfs_data", rootfs_data
, sizeof(rootfs_data
))) {
259 ERROR("rootfs_data does not exist\n");
263 if (mount(rootfs_data
, "/tmp/overlay", "jffs2", MS_NOATIME
, NULL
)) {
264 ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data
, strerror(errno
));
268 find_mtd_char("rootfs_data", rootfs_data
, sizeof(rootfs_data
));
270 return mtd_unlock(rootfs_data
);
273 static int jffs2_ready(char *mtd
)
275 FILE *fp
= fopen(mtd
, "r");
281 ERROR("reading %s failed\n", mtd
);
285 sz
= fread(&deadc0de
, sizeof(deadc0de
), 1, fp
);
289 ERROR("reading %s failed: %s\n", mtd
, strerror(errno
));
293 deadc0de
= __be32_to_cpu(deadc0de
);
294 jffs2
= __be16_to_cpu(deadc0de
>> 16);
296 if (jffs2
== 0x1985) {
297 LOG("jffs2 is ready\n");
301 if (deadc0de
== 0xdeadc0de) {
302 LOG("jffs2 is not ready - marker found\n");
306 ERROR("No jffs2 marker was found\n");
311 static int check_fs_exists(char *fs
)
313 FILE *fp
= fopen("/proc/filesystems", "r");
314 static char line
[256];
317 DEBUG(2, "%s\n", fs
);
320 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno
));
324 while (ret
&& fgets(line
, sizeof(line
), fp
))
325 if (strstr(line
, fs
))
334 static int mount_move(char *oldroot
, char *newroot
, char *dir
)
337 #define MS_MOVE (1 << 13)
344 DEBUG(2, "%s %s %s\n", oldroot
, newroot
, dir
);
346 snprintf(olddir
, sizeof(olddir
), "%s%s", oldroot
, dir
);
347 snprintf(newdir
, sizeof(newdir
), "%s%s", newroot
, dir
);
349 if (stat(olddir
, &s
) || !S_ISDIR(s
.st_mode
))
352 if (stat(newdir
, &s
) || !S_ISDIR(s
.st_mode
))
355 ret
= mount(olddir
, newdir
, NULL
, MS_NOATIME
| MS_MOVE
, NULL
);
358 DEBUG(1, "failed %s %s: %s\n", olddir
, newdir
, strerror(errno
));
363 static int pivot(char *new, char *old
)
368 DEBUG(2, "%s %s\n", new, old
);
370 if (mount_move("", new, "/proc"))
373 snprintf(pivotdir
, sizeof(pivotdir
), "%s%s", new, old
);
375 ret
= pivot_root(new, pivotdir
);
378 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir
, strerror(errno
));
382 mount_move(old
, "", "/dev");
383 mount_move(old
, "", "/tmp");
384 mount_move(old
, "", "/sys");
385 mount_move(old
, "", "/overlay");
390 static int fopivot(char *rw_root
, char *ro_root
)
392 char overlay
[64], lowerdir
[64];
394 DEBUG(2, "%s %s\n", rw_root
, ro_root
);
396 if (check_fs_exists("overlay")) {
397 ERROR("BUG: no suitable fs found\n");
401 snprintf(overlay
, sizeof(overlay
), "overlayfs:%s", rw_root
);
402 snprintf(lowerdir
, sizeof(lowerdir
), "lowerdir=/,upperdir=%s", rw_root
);
404 if (mount(overlay
, "/mnt", "overlayfs", MS_NOATIME
, lowerdir
)) {
405 ERROR("mount failed: %s\n", strerror(errno
));
409 return pivot("/mnt", ro_root
);
412 static int ramoverlay(void)
416 mkdir("/tmp/root", 0755);
417 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME
, "mode=0755");
419 return fopivot("/tmp/root", "/rom");
422 static int switch2jffs(void)
426 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
427 ERROR("no rootfs_data was found\n");
431 if (mount(mtd
, "/rom/overlay", "jffs2", MS_NOATIME
, NULL
)) {
432 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd
, strerror(errno
));
436 if (mount("none", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0)) {
437 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno
));
441 system("cp -a /tmp/root/* /rom/overlay");
443 if (pivot("/rom", "/mnt")) {
444 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno
));
448 if (mount_move("/mnt", "/tmp/root", "")) {
449 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno
));
453 return fopivot("/overlay", "/rom");
456 static int handle_whiteout(const char *dir
)
461 struct dirent
**namelist
;
464 n
= scandir(dir
, &namelist
, NULL
, NULL
);
472 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
473 if (!lstat(file
, &s
) && S_ISLNK(s
.st_mode
)) {
474 sz
= readlink(file
, link
, sizeof(link
) - 1);
479 orig
= strstr(&file
[1], "/");
480 if (orig
&& !strcmp(link
, "(overlay-whiteout)")) {
481 DEBUG(1, "unlinking %s\n", orig
);
493 static int mtd_erase(const char *mtd
)
495 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
496 struct mtd_info_user i
;
497 struct erase_info_user e
;
501 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
505 ret
= ioctl(fd
, MEMGETINFO
, &i
);
507 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
511 e
.length
= i
.erasesize
;
512 for (e
.start
= 0; e
.start
< i
.size
; e
.start
+= i
.erasesize
) {
513 ioctl(fd
, MEMUNLOCK
, &e
);
514 if(ioctl(fd
, MEMERASE
, &e
))
515 ERROR("Failed to erase block on %s at 0x%x\n", mtd
, e
.start
);
522 static int ask_user(int argc
, char **argv
)
524 if ((argc
< 2) || strcmp(argv
[1], "-y")) {
525 LOG("This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
526 if (getchar() != 'y')
533 static int handle_rmdir(const char *dir
)
536 struct dirent
**namelist
;
539 n
= scandir(dir
, &namelist
, NULL
, NULL
);
547 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
548 if (!lstat(file
, &s
) && !S_ISDIR(s
.st_mode
)) {
549 DEBUG(1, "unlinking %s\n", file
);
556 DEBUG(1, "rmdir %s\n", dir
);
562 static int main_jffs2reset(int argc
, char **argv
)
567 if (ask_user(argc
, argv
))
570 if (check_fs_exists("overlay")) {
571 ERROR("overlayfs not found\n");
575 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
576 ERROR("no rootfs_data was found\n");
580 mp
= find_mount_point(mtd
, "jffs2");
582 LOG("%s is mounted as %s, only ereasing files\n", mtd
, mp
);
583 foreachdir(mp
, handle_rmdir
);
584 mount(mp
, "/", NULL
, MS_REMOUNT
, 0);
586 LOG("%s is not mounted, erasing it\n", mtd
);
587 find_mtd_char("rootfs_data", mtd
, sizeof(mtd
));
594 static int main_jffs2mark(int argc
, char **argv
)
597 __u32 deadc0de
= __cpu_to_be32(0xdeadc0de);
601 if (ask_user(argc
, argv
))
604 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
605 ERROR("no rootfs_data was found\n");
609 fp
= fopen(mtd
, "w");
610 LOG("%s - marking with deadc0de\n", mtd
);
612 ERROR("opening %s failed\n", mtd
);
616 sz
= fwrite(&deadc0de
, sizeof(deadc0de
), 1, fp
);
620 ERROR("writing %s failed: %s\n", mtd
, strerror(errno
));
626 static int main_switch2jffs(int argc
, char **argv
)
632 if (find_overlay_mount("overlayfs:/tmp/root"))
635 if (check_fs_exists("overlay")) {
636 ERROR("overlayfs not found\n");
640 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
641 mp
= find_mount_point(mtd
, NULL
);
643 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
647 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
648 ERROR("no rootfs_data was found\n");
652 switch (jffs2_ready(mtd
)) {
654 ERROR("no jffs2 marker found\n");
660 DEBUG(1, "doing fo cleanup\n");
661 umount2("/tmp/root", MNT_DETACH
);
662 foreachdir("/overlay/", handle_whiteout
);
667 ret
= mtd_mount_jffs2();
670 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
671 ERROR("switching to jffs2 failed\n");
680 static int extroot(const char *prefix
)
683 char kmod_loader
[64];
687 sprintf(block_path
, "%s/sbin/block", prefix
);
689 if (stat(block_path
, &s
))
692 sprintf(kmod_loader
, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", prefix
, prefix
);
697 mkdir("/tmp/extroot", 0755);
698 execl(block_path
, block_path
, "extroot", NULL
);
700 } else if (pid
> 0) {
703 waitpid(pid
, &status
, 0);
704 if (!WEXITSTATUS(status
)) {
705 if (find_mount("/tmp/extroot/mnt")) {
706 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
| MS_RDONLY
, 0);
708 mkdir("/tmp/extroot/mnt/proc", 0755);
709 mkdir("/tmp/extroot/mnt/dev", 0755);
710 mkdir("/tmp/extroot/mnt/sys", 0755);
711 mkdir("/tmp/extroot/mnt/tmp", 0755);
712 mkdir("/tmp/extroot/mnt/rom", 0755);
714 if (mount_move("/tmp/extroot", "", "/mnt")) {
715 ERROR("moving pivotroot failed - continue normal boot\n");
716 umount("/tmp/extroot/mnt");
717 } else if (pivot("/mnt", "/rom")) {
718 ERROR("switching to pivotroot failed - continue normal boot\n");
721 umount("/tmp/overlay");
722 rmdir("/tmp/overlay");
723 rmdir("/tmp/extroot/mnt");
724 rmdir("/tmp/extroot");
727 } else if (find_mount("/tmp/extroot/overlay")) {
728 if (mount_move("/tmp/extroot", "", "/overlay")) {
729 ERROR("moving extroot failed - continue normal boot\n");
730 umount("/tmp/extroot/overlay");
731 } else if (fopivot("/overlay", "/rom")) {
732 ERROR("switching to extroot failed - continue normal boot\n");
735 umount("/tmp/overlay");
736 rmdir("/tmp/overlay");
737 rmdir("/tmp/extroot/overlay");
738 rmdir("/tmp/extroot");
747 int main(int argc
, char **argv
)
752 argv0
= basename(*argv
);
754 if (!strcmp(basename(*argv
), "jffs2mark"))
755 return main_jffs2mark(argc
, argv
);
757 if (!strcmp(basename(*argv
), "jffs2reset"))
758 return main_jffs2reset(argc
, argv
);
760 if (!strcmp(basename(*argv
), "switch2jffs"))
761 return main_switch2jffs(argc
, argv
);
763 if (!getenv("PREINIT"))
766 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
767 if (!find_mtd_char("rootfs", mtd
, sizeof(mtd
)))
769 LOG("mounting /dev/root\n");
770 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0);
773 fprintf(stderr
, "mount_root: switched to extroot\n");
777 switch (jffs2_ready(mtd
)) {
783 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
784 mp
= find_mount_point(mtd
, NULL
);
786 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
792 if (!extroot("/tmp/overlay")) {
793 fprintf(stderr
, "mount_root: switched to extroot\n");
797 DEBUG(1, "switching to jffs2\n");
798 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
799 ERROR("switching to jffs2 failed - fallback to ramoverlay\n");