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_point(char *block
, char *fs
)
102 FILE *fp
= fopen("/proc/mounts", "r");
103 static char line
[256];
104 int len
= strlen(block
);
110 while (fgets(line
, sizeof(line
), fp
)) {
111 if (!strncmp(line
, block
, len
)) {
112 char *p
= &line
[len
+ 1];
113 char *t
= strstr(p
, " ");
121 if (fs
&& strncmp(t
, fs
, strlen(fs
))) {
122 ERROR("block is mounted with wrong fs\n");
135 static char* find_mtd_index(char *name
)
137 FILE *fp
= fopen("/proc/mtd", "r");
138 static char line
[256];
144 while (!index
&& fgets(line
, sizeof(line
), fp
)) {
145 if (strstr(line
, name
)) {
146 char *eol
= strstr(line
, ":");
153 DEBUG(1, "found %s -> index:%s\n", name
, index
);
162 static int find_mtd_block(char *name
, char *part
, int plen
)
164 char *index
= find_mtd_index(name
);
169 snprintf(part
, plen
, "/dev/mtdblock%s", index
);
170 DEBUG(1, "found %s -> %s\n", name
, part
);
175 static int find_mtd_char(char *name
, char *part
, int plen
)
177 char *index
= find_mtd_index(name
);
182 snprintf(part
, plen
, "/dev/mtd%s", index
);
183 DEBUG(1, "found %s -> %s\n", name
, part
);
188 static int mtd_unlock(char *mtd
)
190 struct erase_info_user mtdlock
;
191 struct mtd_info_user mtdinfo
;
192 int fd
= open(mtd
, O_RDWR
| O_SYNC
);
195 DEBUG(1, "%s\n", mtd
);
198 ERROR("failed to open %s: %s\n", mtd
, strerror(errno
));
202 ret
= ioctl(fd
, MEMGETINFO
, &mtdinfo
);
204 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd
, strerror(errno
));
209 mtdlock
.length
= mtdinfo
.size
;
210 ioctl(fd
, MEMUNLOCK
, &mtdlock
);
218 static int mtd_mount_jffs2(void)
220 char rootfs_data
[32];
223 if (mkdir("/tmp/overlay", 0755)) {
224 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno
));
228 if (find_mtd_block("rootfs_data", rootfs_data
, sizeof(rootfs_data
))) {
229 ERROR("rootfs_data does not exist\n");
233 if (mount(rootfs_data
, "/tmp/overlay", "jffs2", MS_NOATIME
, NULL
)) {
234 ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data
, strerror(errno
));
238 find_mtd_char("rootfs_data", rootfs_data
, sizeof(rootfs_data
));
240 return mtd_unlock(rootfs_data
);
243 static int jffs2_ready(char *mtd
)
245 FILE *fp
= fopen(mtd
, "r");
251 ERROR("reading %s failed\n", mtd
);
255 sz
= fread(&deadc0de
, sizeof(deadc0de
), 1, fp
);
259 ERROR("reading %s failed: %s\n", mtd
, strerror(errno
));
263 deadc0de
= __be32_to_cpu(deadc0de
);
264 jffs2
= __be16_to_cpu(deadc0de
>> 16);
266 if (jffs2
== 0x1985) {
267 LOG("jffs2 is ready\n");
271 if (deadc0de
== 0xdeadc0de) {
272 LOG("jffs2 is not ready - marker found\n");
276 ERROR("No jffs2 marker was found\n");
281 static int check_fs_exists(char *fs
)
283 FILE *fp
= fopen("/proc/filesystems", "r");
284 static char line
[256];
287 DEBUG(2, "%s\n", fs
);
290 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno
));
294 while (ret
&& fgets(line
, sizeof(line
), fp
))
295 if (strstr(line
, fs
))
304 static int mount_move(char *oldroot
, char *newroot
, char *dir
)
307 #define MS_MOVE (1 << 13)
314 DEBUG(2, "%s %s\n", oldroot
, dir
);
316 snprintf(olddir
, sizeof(olddir
), "%s%s", oldroot
, dir
);
317 snprintf(newdir
, sizeof(newdir
), "%s%s", newroot
, dir
);
319 if (stat(olddir
, &s
) || !S_ISDIR(s
.st_mode
))
322 if (stat(newdir
, &s
) || !S_ISDIR(s
.st_mode
))
325 ret
= mount(olddir
, newdir
, NULL
, MS_NOATIME
| MS_MOVE
, NULL
);
328 DEBUG(1, "failed %s %s: %s\n", olddir
, newdir
, strerror(errno
));
333 static int pivot(char *new, char *old
)
338 DEBUG(2, "%s %s\n", new, old
);
340 if (mount_move("", new, "/proc"))
343 snprintf(pivotdir
, sizeof(pivotdir
), "%s%s", new, old
);
345 ret
= pivot_root(new, pivotdir
);
348 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir
, strerror(errno
));
352 mount_move(old
, "", "/dev");
353 mount_move(old
, "", "/tmp");
354 mount_move(old
, "", "/sys");
355 mount_move(old
, "", "/overlay");
356 mount_move(old
, "", "/extroot");
362 static int fopivot(char *rw_root
, char *ro_root
)
364 char overlay
[64], lowerdir
[64];
366 DEBUG(2, "%s %s\n", rw_root
, ro_root
);
368 if (check_fs_exists("overlay")) {
369 ERROR("BUG: no suitable fs found\n");
373 snprintf(overlay
, sizeof(overlay
), "overlayfs:%s", rw_root
);
374 snprintf(lowerdir
, sizeof(lowerdir
), "lowerdir=/,upperdir=%s", rw_root
);
376 if (mount(overlay
, "/mnt", "overlayfs", MS_NOATIME
, lowerdir
)) {
377 ERROR("mount failed: %s\n", strerror(errno
));
381 return pivot("/mnt", ro_root
);
384 static int ramoverlay(void)
388 mkdir("/tmp/root", 0755);
389 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME
, "mode=0755");
391 return fopivot("/tmp/root", "/rom");
394 static int switch2jffs(void)
398 if (find_mtd_block("rootfs_data", mtd
, sizeof(mtd
))) {
399 ERROR("no rootfs_data was found\n");
403 if (mount(mtd
, "/rom/overlay", "jffs2", MS_NOATIME
, NULL
)) {
404 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd
, strerror(errno
));
408 if (mount("none", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0)) {
409 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno
));
413 system("cp -a /tmp/root/* /rom/overlay");
415 if (pivot("/rom", "/mnt")) {
416 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno
));
420 if (mount_move("/mnt", "/tmp/root", "")) {
421 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno
));
425 return fopivot("/overlay", "/rom");
428 static int handle_whiteout(const char *dir
)
433 struct dirent
**namelist
;
436 n
= scandir(dir
, &namelist
, NULL
, NULL
);
444 snprintf(file
, sizeof(file
), "%s%s", dir
, namelist
[n
]->d_name
);
445 if (!lstat(file
, &s
) && S_ISLNK(s
.st_mode
)) {
446 sz
= readlink(file
, link
, sizeof(link
) - 1);
451 orig
= strstr(&file
[1], "/");
452 if (orig
&& !strcmp(link
, "(overlay-whiteout)")) {
453 DEBUG(1, "unlinking %s\n", orig
);
465 static int main_switch2jffs(int argc
, char **argv
)
471 if (find_overlay_mount("overlayfs:/tmp/root"))
474 if (check_fs_exists("overlay")) {
475 ERROR("overlayfs not found\n");
479 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
480 mp
= find_mount_point(mtd
, NULL
);
482 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
486 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
487 ERROR("no rootfs_data was found\n");
491 switch (jffs2_ready(mtd
)) {
493 ERROR("no jffs2 marker found\n");
499 DEBUG(1, "doing fo cleanup\n");
500 umount2("/tmp/root", MNT_DETACH
);
501 foreachdir("/overlay/", handle_whiteout
);
506 ret
= mtd_mount_jffs2();
509 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
510 ERROR("switching to jffs2 failed\n");
519 static int extroot(void)
524 if (stat("/sbin/block", &s
))
529 mkdir("/tmp/extroot", 0755);
530 execl("/sbin/block", "/sbin/block", "extroot", NULL
);
532 } else if (pid
> 0) {
535 waitpid(pid
, &status
, 0);
536 if (!WEXITSTATUS(status
)) {
537 if (mount_move("/tmp", "", "/overlay")) {
538 ERROR("moving extroot failed - continue normal boot\n");
539 umount("/tmp/overlay");
540 } else if (fopivot("/overlay", "/rom")) {
541 ERROR("switching to extroot failed - continue normal boot\n");
551 int main(int argc
, char **argv
)
556 argv0
= basename(*argv
);
558 if (!strcmp(basename(*argv
), "switch2jffs"))
559 return main_switch2jffs(argc
, argv
);
561 if (!getenv("PREINIT"))
564 if (find_mtd_char("rootfs_data", mtd
, sizeof(mtd
))) {
565 if (!find_mtd_char("rootfs", mtd
, sizeof(mtd
)))
567 LOG("mounting /dev/root\n");
568 mount("/dev/root", "/", NULL
, MS_NOATIME
| MS_REMOUNT
, 0);
571 fprintf(stderr
, "mount_root: switched to extroot\n");
575 switch (jffs2_ready(mtd
)) {
581 find_mtd_block("rootfs_data", mtd
, sizeof(mtd
));
582 mp
= find_mount_point(mtd
, NULL
);
584 LOG("rootfs_data:%s is already mounted as %s\n", mtd
, mp
);
589 DEBUG(1, "switching to jffs2\n");
590 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
591 ERROR("switching to jffs2 failed - fallback to ramoverlay\n");