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))
701 sprintf(block_path, "/sbin/block");
703 if (stat(block_path, &s))
707 sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", prefix, prefix);
710 LOG("starting block executable %s\n", block_path);
714 mkdir("/tmp/extroot", 0755);
715 execl(block_path, block_path, "extroot", NULL);
717 } else if (pid > 0) {
720 waitpid(pid, &status, 0);
721 if (!WEXITSTATUS(status)) {
722 if (find_mount("/tmp/extroot/mnt")) {
723 mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT | MS_RDONLY, 0);
725 mkdir("/tmp/extroot/mnt/proc", 0755);
726 mkdir("/tmp/extroot/mnt/dev", 0755);
727 mkdir("/tmp/extroot/mnt/sys", 0755);
728 mkdir("/tmp/extroot/mnt/tmp", 0755);
729 mkdir("/tmp/extroot/mnt/rom", 0755);
731 if (mount_move("/tmp/extroot", "", "/mnt")) {
732 ERROR("moving pivotroot failed - continue normal boot\n");
733 umount("/tmp/extroot/mnt");
734 } else if (pivot("/mnt", "/rom")) {
735 ERROR("switching to pivotroot failed - continue normal boot\n");
738 umount("/tmp/overlay");
739 rmdir("/tmp/overlay");
740 rmdir("/tmp/extroot/mnt");
741 rmdir("/tmp/extroot");
744 } else if (find_mount("/tmp/extroot/overlay")) {
745 if (mount_move("/tmp/extroot", "", "/overlay")) {
746 ERROR("moving extroot failed - continue normal boot\n");
747 umount("/tmp/extroot/overlay");
748 } else if (fopivot("/overlay", "/rom")) {
749 ERROR("switching to extroot failed - continue normal boot\n");
752 umount("/tmp/overlay");
753 rmdir("/tmp/overlay");
754 rmdir("/tmp/extroot/overlay");
755 rmdir("/tmp/extroot");
759 ERROR("block executable did not set up an overlay\n");
762 ERROR("block executable failed with code %d\n", WEXITSTATUS(status));
768 int main(int argc, char **argv)
773 argv0 = basename(*argv);
775 if (!strcmp(basename(*argv), "jffs2mark"))
776 return main_jffs2mark(argc, argv);
778 if (!strcmp(basename(*argv), "jffs2reset"))
779 return main_jffs2reset(argc, argv);
781 if (!strcmp(basename(*argv), "switch2jffs"))
782 return main_switch2jffs(argc, argv);
784 if (!getenv("PREINIT"))
787 if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd))) {
789 } else if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
790 if (!find_mtd_char("rootfs", mtd, sizeof(mtd)))
792 LOG("mounting /dev/root\n");
793 mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
796 fprintf(stderr, "mount_root: switched to extroot\n");
800 switch (jffs2_ready(mtd)) {
806 find_mtd_block("rootfs_data", mtd, sizeof(mtd));
807 mp = find_mount_point(mtd, NULL);
809 LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
815 if (!extroot("/tmp/overlay")) {
816 fprintf(stderr, "mount_root: switched to extroot\n");
820 DEBUG(1, "switching to jffs2\n");
821 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
822 ERROR("switching to jffs2 failed - fallback to ramoverlay\n");