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
, " ");
124 if (!strcmp(t
, mp
)) {
135 static char* find_mount_point(char *block
, char *fs
)
137 FILE *fp
= fopen("/proc/mounts", "r");
138 static char line
[256];
139 int len
= strlen(block
);
145 while (fgets(line
, sizeof(line
), fp
)) {
146 if (!strncmp(line
, block
, len
)) {
147 char *p
= &line
[len
+ 1];
148 char *t
= strstr(p
, " ");
158 if (fs
&& strncmp(t
, fs
, strlen(fs
))) {
160 ERROR("block is mounted with wrong fs\n");
173 static char* find_mtd_index(char *name
)
175 FILE *fp
= fopen("/proc/mtd", "r");
176 static char line
[256];
182 while (!index
&& fgets(line
, sizeof(line
), fp
)) {
183 if (strstr(line
, name
)) {
184 char *eol
= strstr(line
, ":");
191 DEBUG(1, "found %s -> index:%s\n", name
, index
);
200 static int find_mtd_block(char *name
, char *part
, int plen
)
202 char *index
= find_mtd_index(name
);
207 snprintf(part
, plen
, "/dev/mtdblock%s", index
);
208 DEBUG(1, "found %s -> %s\n", name
, part
);
213 static int find_mtd_char(char *name
, char *part
, int plen
)
215 char *index
= find_mtd_index(name
);
220 snprintf(part
, plen
, "/dev/mtd%s", index
);
221 DEBUG(1, "found %s -> %s\n", name
, part
);
226 static int mtd_unlock(char *mtd
)
228 struct erase_info_user mtdlock
;
229 struct mtd_info_user mtdinfo
;
230 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
233 DEBUG(1, "%s\n", mtd
);
236 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
240 ret
= ioctl(fd
, MEMGETINFO
, &mtdinfo
);
242 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
247 mtdlock
.length
= mtdinfo
.size
;
248 ioctl(fd
, MEMUNLOCK
, &mtdlock
);
256 static int mtd_mount_jffs2(void)
258 char rootfs_data
[32];
260 if (mkdir("/tmp/overlay", 0755)) {
261 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno
));
265 if (find_mtd_block("rootfs_data", rootfs_data
, sizeof(rootfs_data
))) {
266 ERROR("rootfs_data does not exist\n");
270 if (mount(rootfs_data
, "/tmp/overlay", "jffs2", MS_NOATIME
, NULL
)) {
271 ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data
, strerror(errno
));
275 find_mtd_char("rootfs_data", rootfs_data
, sizeof(rootfs_data
));
277 return mtd_unlock(rootfs_data
);
280 static int jffs2_ready(char *mtd
)
282 FILE *fp
= fopen(mtd
, "r");
288 ERROR("reading %s failed\n", mtd
);
292 sz
= fread(&deadc0de
, sizeof(deadc0de
), 1, fp
);
296 ERROR("reading %s failed: %s\n", mtd
, strerror(errno
));
300 deadc0de
= __be32_to_cpu(deadc0de
);
301 jffs2
= __be16_to_cpu(deadc0de
>> 16);
303 if (jffs2
== 0x1985) {
304 LOG("jffs2 is ready\n");
308 if (deadc0de
== 0xdeadc0de) {
309 LOG("jffs2 is not ready - marker found\n");
313 ERROR("No jffs2 marker was found\n");
318 static int check_fs_exists(char *fs
)
320 FILE *fp
= fopen("/proc/filesystems", "r");
321 static char line
[256];
324 DEBUG(2, "%s\n", fs
);
327 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno
));
331 while (ret
&& fgets(line
, sizeof(line
), fp
))
332 if (strstr(line
, fs
))
341 static int mount_move(char *oldroot
, char *newroot
, char *dir
)
344 #define MS_MOVE (1 << 13)
351 DEBUG(2, "%s %s %s\n", oldroot
, newroot
, dir
);
353 snprintf(olddir
, sizeof(olddir
), "%s%s", oldroot
, dir
);
354 snprintf(newdir
, sizeof(newdir
), "%s%s", newroot
, dir
);
356 if (stat(olddir
, &s
) || !S_ISDIR(s
.st_mode
))
359 if (stat(newdir
, &s
) || !S_ISDIR(s
.st_mode
))
362 ret
= mount(olddir
, newdir
, NULL
, MS_NOATIME
| MS_MOVE
, NULL
);
365 DEBUG(1, "failed %s %s: %s\n", olddir
, newdir
, strerror(errno
));
370 static int pivot(char *new, char *old
)
375 DEBUG(2, "%s %s\n", new, old
);
377 if (mount_move("", new, "/proc"))
380 snprintf(pivotdir
, sizeof(pivotdir
), "%s%s", new, old
);
382 ret
= pivot_root(new, pivotdir
);
385 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir
, strerror(errno
));
389 mount_move(old
, "", "/dev");
390 mount_move(old
, "", "/tmp");
391 mount_move(old
, "", "/sys");
392 mount_move(old
, "", "/overlay");
397 static int fopivot(char *rw_root
, char *ro_root
)
399 char overlay
[64], lowerdir
[64];
401 DEBUG(2, "%s %s\n", rw_root
, ro_root
);
403 if (check_fs_exists("overlay")) {
404 ERROR("BUG: no suitable fs found\n");
408 snprintf(overlay
, sizeof(overlay
), "overlayfs:%s", rw_root
);
409 snprintf(lowerdir
, sizeof(lowerdir
), "lowerdir=/,upperdir=%s", rw_root
);
411 if (mount(overlay
, "/mnt", "overlayfs", MS_NOATIME
, lowerdir
)) {
412 ERROR("mount failed: %s\n", strerror(errno
));
416 return pivot("/mnt", ro_root
);
419 static int ramoverlay(void)
423 mkdir("/tmp/root", 0755);
424 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME
, "mode=0755");
426 return fopivot("/tmp/root", "/rom");
429 static int switch2jffs(void)
433 if (!find_mtd_block("rootfs_patches", mtd
, sizeof(mtd
)))
436 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
437 ERROR("no rootfs_data was found\n");
441 if (mount(mtd
, "/rom/overlay", "jffs2", MS_NOATIME
, NULL
)) {
442 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd
, strerror(errno
));
446 if (mount("none", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0)) {
447 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno
));
451 system("cp -a /tmp/root/* /rom/overlay");
453 if (pivot("/rom", "/mnt")) {
454 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno
));
458 if (mount_move("/mnt", "/tmp/root", "")) {
459 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno
));
463 return fopivot("/overlay", "/rom");
466 static int handle_whiteout(const char *dir
)
471 struct dirent
**namelist
;
474 n
= scandir(dir
, &namelist
, NULL
, NULL
);
482 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
483 if (!lstat(file
, &s
) && S_ISLNK(s
.st_mode
)) {
484 sz
= readlink(file
, link
, sizeof(link
) - 1);
489 orig
= strstr(&file
[1], "/");
490 if (orig
&& !strcmp(link
, "(overlay-whiteout)")) {
491 DEBUG(1, "unlinking %s\n", orig
);
503 static int mtd_erase(const char *mtd
)
505 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
506 struct mtd_info_user i
;
507 struct erase_info_user e
;
511 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
515 ret
= ioctl(fd
, MEMGETINFO
, &i
);
517 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
521 e
.length
= i
.erasesize
;
522 for (e
.start
= 0; e
.start
< i
.size
; e
.start
+= i
.erasesize
) {
523 ioctl(fd
, MEMUNLOCK
, &e
);
524 if(ioctl(fd
, MEMERASE
, &e
))
525 ERROR("Failed to erase block on %s at 0x%x\n", mtd
, e
.start
);
532 static int ask_user(int argc
, char **argv
)
534 if ((argc
< 2) || strcmp(argv
[1], "-y")) {
535 LOG("This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
536 if (getchar() != 'y')
543 static int handle_rmdir(const char *dir
)
546 struct dirent
**namelist
;
549 n
= scandir(dir
, &namelist
, NULL
, NULL
);
557 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
558 if (!lstat(file
, &s
) && !S_ISDIR(s
.st_mode
)) {
559 DEBUG(1, "unlinking %s\n", file
);
566 DEBUG(1, "rmdir %s\n", dir
);
572 static int main_jffs2reset(int argc
, char **argv
)
577 if (ask_user(argc
, argv
))
580 if (check_fs_exists("overlay")) {
581 ERROR("overlayfs not found\n");
585 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
586 ERROR("no rootfs_data was found\n");
590 mp
= find_mount_point(mtd
, "jffs2");
592 LOG("%s is mounted as %s, only erasing files\n", mtd
, mp
);
593 foreachdir(mp
, handle_rmdir
);
594 mount(mp
, "/", NULL
, MS_REMOUNT
, 0);
596 LOG("%s is not mounted, erasing it\n", mtd
);
597 find_mtd_char("rootfs_data", mtd
, sizeof(mtd
));
604 static int main_jffs2mark(int argc
, char **argv
)
607 __u32 deadc0de
= __cpu_to_be32(0xdeadc0de);
611 if (ask_user(argc
, argv
))
614 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
615 ERROR("no rootfs_data was found\n");
619 fp
= fopen(mtd
, "w");
620 LOG("%s - marking with deadc0de\n", mtd
);
622 ERROR("opening %s failed\n", mtd
);
626 sz
= fwrite(&deadc0de
, sizeof(deadc0de
), 1, fp
);
630 ERROR("writing %s failed: %s\n", mtd
, strerror(errno
));
636 static int main_switch2jffs(int argc
, char **argv
)
642 if (find_overlay_mount("overlayfs:/tmp/root"))
645 if (check_fs_exists("overlay")) {
646 ERROR("overlayfs not found\n");
650 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
651 mp
= find_mount_point(mtd
, NULL
);
653 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
657 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
658 ERROR("no rootfs_data was found\n");
662 switch (jffs2_ready(mtd
)) {
664 ERROR("no jffs2 marker found\n");
670 DEBUG(1, "doing fo cleanup\n");
671 umount2("/tmp/root", MNT_DETACH
);
672 foreachdir("/overlay/", handle_whiteout
);
677 ret
= mtd_mount_jffs2();
680 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
681 ERROR("switching to jffs2 failed\n");
690 static int extroot(const char *prefix
)
693 char kmod_loader
[64];
697 sprintf(block_path
, "%s/sbin/block", prefix
);
699 if (stat(block_path
, &s
))
702 sprintf(kmod_loader
, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", prefix
, prefix
);
707 mkdir("/tmp/extroot", 0755);
708 execl(block_path
, block_path
, "extroot", NULL
);
710 } else if (pid
> 0) {
713 waitpid(pid
, &status
, 0);
714 if (!WEXITSTATUS(status
)) {
715 if (find_mount("/tmp/extroot/mnt")) {
716 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
| MS_RDONLY
, 0);
718 mkdir("/tmp/extroot/mnt/proc", 0755);
719 mkdir("/tmp/extroot/mnt/dev", 0755);
720 mkdir("/tmp/extroot/mnt/sys", 0755);
721 mkdir("/tmp/extroot/mnt/tmp", 0755);
722 mkdir("/tmp/extroot/mnt/rom", 0755);
724 if (mount_move("/tmp/extroot", "", "/mnt")) {
725 ERROR("moving pivotroot failed - continue normal boot\n");
726 umount("/tmp/extroot/mnt");
727 } else if (pivot("/mnt", "/rom")) {
728 ERROR("switching to pivotroot failed - continue normal boot\n");
731 umount("/tmp/overlay");
732 rmdir("/tmp/overlay");
733 rmdir("/tmp/extroot/mnt");
734 rmdir("/tmp/extroot");
737 } else if (find_mount("/tmp/extroot/overlay")) {
738 if (mount_move("/tmp/extroot", "", "/overlay")) {
739 ERROR("moving extroot failed - continue normal boot\n");
740 umount("/tmp/extroot/overlay");
741 } else if (fopivot("/overlay", "/rom")) {
742 ERROR("switching to extroot failed - continue normal boot\n");
745 umount("/tmp/overlay");
746 rmdir("/tmp/overlay");
747 rmdir("/tmp/extroot/overlay");
748 rmdir("/tmp/extroot");
757 int main(int argc
, char **argv
)
762 argv0
= basename(*argv
);
764 if (!strcmp(basename(*argv
), "jffs2mark"))
765 return main_jffs2mark(argc
, argv
);
767 if (!strcmp(basename(*argv
), "jffs2reset"))
768 return main_jffs2reset(argc
, argv
);
770 if (!strcmp(basename(*argv
), "switch2jffs"))
771 return main_switch2jffs(argc
, argv
);
773 if (!getenv("PREINIT"))
776 if (!find_mtd_block("rootfs_patches", mtd
, sizeof(mtd
))) {
778 } else if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
779 if (!find_mtd_char("rootfs", mtd
, sizeof(mtd
)))
781 LOG("mounting /dev/root\n");
782 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0);
785 fprintf(stderr
, "mount_root: switched to extroot\n");
789 switch (jffs2_ready(mtd
)) {
795 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
796 mp
= find_mount_point(mtd
, NULL
);
798 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
804 if (!extroot("/tmp/overlay")) {
805 fprintf(stderr
, "mount_root: switched to extroot\n");
809 DEBUG(1, "switching to jffs2\n");
810 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
811 ERROR("switching to jffs2 failed - fallback to ramoverlay\n");