validate: fix return type of dt_parse() call
[project/ubox.git] / mount_root.c
1 /*
2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4 *
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
8 *
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.
13 */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <getopt.h>
18 #include <syslog.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <libgen.h>
24 #include <glob.h>
25 #include <dirent.h>
26
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/mount.h>
30 #include <sys/wait.h>
31
32 #include <asm/byteorder.h>
33
34 #include <mtd/mtd-user.h>
35
36 #define DEBUG(level, fmt, ...) do { \
37 if (debug >= level) \
38 fprintf(stderr, "%s %s(%d): " fmt, argv0, __func__, __LINE__, ## __VA_ARGS__); \
39 } while (0)
40
41 #define LOG(fmt, ...) do { \
42 syslog(LOG_INFO, fmt, ## __VA_ARGS__); \
43 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
44 } while (0)
45
46 #define ERROR(fmt, ...) do { \
47 syslog(LOG_ERR, fmt, ## __VA_ARGS__); \
48 fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
49 } while (0)
50
51 enum {
52 FS_NONE,
53 FS_JFFS2,
54 FS_DEADCODE,
55 };
56
57 static const char *argv0;
58
59 /* this is a raw syscall - man 2 pivot_root */
60 extern int pivot_root(const char *new_root, const char *put_old);
61
62 static int debug = 0;
63
64 static void foreachdir(const char *dir, int (*cb)(const char*))
65 {
66 char globdir[256];
67 glob_t gl;
68 int j;
69
70 if (dir[strlen(dir) - 1] == '/')
71 snprintf(globdir, 256, "%s*", dir);
72 else
73 snprintf(globdir, 256, "%s/*", dir);
74
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);
78
79 cb(dir);
80 }
81
82 static int find_overlay_mount(char *overlay)
83 {
84 FILE *fp = fopen("/proc/mounts", "r");
85 static char line[256];
86 int ret = -1;
87
88 if(!fp)
89 return ret;
90
91 while (ret && fgets(line, sizeof(line), fp))
92 if (!strncmp(line, overlay, strlen(overlay)))
93 ret = 0;
94
95 fclose(fp);
96
97 return ret;
98 }
99
100 static char* find_mount(char *mp)
101 {
102 FILE *fp = fopen("/proc/mounts", "r");
103 static char line[256];
104 char *point = NULL;
105
106 if(!fp)
107 return NULL;
108
109 while (fgets(line, sizeof(line), fp)) {
110 char *s, *t = strstr(line, " ");
111
112 if (!t) {
113 fclose(fp);
114 return NULL;
115 }
116 t++;
117 s = strstr(t, " ");
118 if (!s) {
119 fclose(fp);
120 return NULL;
121 }
122 *s = '\0';
123
124 if (!strcmp(t, mp)) {
125 fclose(fp);
126 return t;
127 }
128 }
129
130 fclose(fp);
131
132 return point;
133 }
134
135 static char* find_mount_point(char *block, char *fs)
136 {
137 FILE *fp = fopen("/proc/mounts", "r");
138 static char line[256];
139 int len = strlen(block);
140 char *point = NULL;
141
142 if(!fp)
143 return NULL;
144
145 while (fgets(line, sizeof(line), fp)) {
146 if (!strncmp(line, block, len)) {
147 char *p = &line[len + 1];
148 char *t = strstr(p, " ");
149
150 if (!t) {
151 fclose(fp);
152 return NULL;
153 }
154
155 *t = '\0';
156 t++;
157
158 if (fs && strncmp(t, fs, strlen(fs))) {
159 fclose(fp);
160 ERROR("block is mounted with wrong fs\n");
161 return NULL;
162 }
163 point = p;
164 break;
165 }
166 }
167
168 fclose(fp);
169
170 return point;
171 }
172
173 static char* find_mtd_index(char *name)
174 {
175 FILE *fp = fopen("/proc/mtd", "r");
176 static char line[256];
177 char *index = NULL;
178
179 if(!fp)
180 return index;
181
182 while (!index && fgets(line, sizeof(line), fp)) {
183 if (strstr(line, name)) {
184 char *eol = strstr(line, ":");
185
186 if (!eol)
187 continue;
188
189 *eol = '\0';
190 index = &line[3];
191 DEBUG(1, "found %s -> index:%s\n", name, index);
192 }
193 }
194
195 fclose(fp);
196
197 return index;
198 }
199
200 static int find_mtd_block(char *name, char *part, int plen)
201 {
202 char *index = find_mtd_index(name);
203
204 if (!index)
205 return -1;
206
207 snprintf(part, plen, "/dev/mtdblock%s", index);
208 DEBUG(1, "found %s -> %s\n", name, part);
209
210 return 0;
211 }
212
213 static int find_mtd_char(char *name, char *part, int plen)
214 {
215 char *index = find_mtd_index(name);
216
217 if (!index)
218 return -1;
219
220 snprintf(part, plen, "/dev/mtd%s", index);
221 DEBUG(1, "found %s -> %s\n", name, part);
222
223 return 0;
224 }
225
226 static int mtd_unlock(char *mtd)
227 {
228 struct erase_info_user mtdlock;
229 struct mtd_info_user mtdinfo;
230 int fd = open(mtd, O_RDWR | O_SYNC);
231 int ret = -1;
232
233 DEBUG(1, "%s\n", mtd);
234
235 if (!fd) {
236 ERROR("failed to open %s: %s\n", mtd, strerror(errno));
237 return -1;
238 }
239
240 ret = ioctl(fd, MEMGETINFO, &mtdinfo);
241 if (ret) {
242 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
243 goto err_out;
244 }
245
246 mtdlock.start = 0;
247 mtdlock.length = mtdinfo.size;
248 ioctl(fd, MEMUNLOCK, &mtdlock);
249
250 err_out:
251 close(fd);
252
253 return ret;
254 }
255
256 static int mtd_mount_jffs2(void)
257 {
258 char rootfs_data[32];
259
260 if (mkdir("/tmp/overlay", 0755)) {
261 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno));
262 return -1;
263 }
264
265 if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) {
266 ERROR("rootfs_data does not exist\n");
267 return -1;
268 }
269
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));
272 return -1;
273 }
274
275 find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data));
276
277 return mtd_unlock(rootfs_data);
278 }
279
280 static int jffs2_ready(char *mtd)
281 {
282 FILE *fp = fopen(mtd, "r");
283 __u32 deadc0de;
284 __u16 jffs2;
285 size_t sz;
286
287 if (!fp) {
288 ERROR("reading %s failed\n", mtd);
289 exit(-1);
290 }
291
292 sz = fread(&deadc0de, sizeof(deadc0de), 1, fp);
293 fclose(fp);
294
295 if (sz != 1) {
296 ERROR("reading %s failed: %s\n", mtd, strerror(errno));
297 exit(-1);
298 }
299
300 deadc0de = __be32_to_cpu(deadc0de);
301 jffs2 = __be16_to_cpu(deadc0de >> 16);
302
303 if (jffs2 == 0x1985) {
304 LOG("jffs2 is ready\n");
305 return FS_JFFS2;
306 }
307
308 if (deadc0de == 0xdeadc0de) {
309 LOG("jffs2 is not ready - marker found\n");
310 return FS_DEADCODE;
311 }
312
313 ERROR("No jffs2 marker was found\n");
314
315 return FS_NONE;
316 }
317
318 static int check_fs_exists(char *fs)
319 {
320 FILE *fp = fopen("/proc/filesystems", "r");
321 static char line[256];
322 int ret = -1;
323
324 DEBUG(2, "%s\n", fs);
325
326 if (!fp) {
327 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno));
328 goto out;
329 }
330
331 while (ret && fgets(line, sizeof(line), fp))
332 if (strstr(line, fs))
333 ret = 0;
334
335 fclose(fp);
336
337 out:
338 return ret;
339 }
340
341 static int mount_move(char *oldroot, char *newroot, char *dir)
342 {
343 #ifndef MS_MOVE
344 #define MS_MOVE (1 << 13)
345 #endif
346 struct stat s;
347 char olddir[64];
348 char newdir[64];
349 int ret;
350
351 DEBUG(2, "%s %s %s\n", oldroot, newroot, dir);
352
353 snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
354 snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
355
356 if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
357 return -1;
358
359 if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
360 return -1;
361
362 ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
363
364 if (ret)
365 DEBUG(1, "failed %s %s: %s\n", olddir, newdir, strerror(errno));
366
367 return ret;
368 }
369
370 static int pivot(char *new, char *old)
371 {
372 char pivotdir[64];
373 int ret;
374
375 DEBUG(2, "%s %s\n", new, old);
376
377 if (mount_move("", new, "/proc"))
378 return -1;
379
380 snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
381
382 ret = pivot_root(new, pivotdir);
383
384 if (ret < 0) {
385 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno));
386 return -1;
387 }
388
389 mount_move(old, "", "/dev");
390 mount_move(old, "", "/tmp");
391 mount_move(old, "", "/sys");
392 mount_move(old, "", "/overlay");
393
394 return 0;
395 }
396
397 static int fopivot(char *rw_root, char *ro_root)
398 {
399 char overlay[64], lowerdir[64];
400
401 DEBUG(2, "%s %s\n", rw_root, ro_root);
402
403 if (check_fs_exists("overlay")) {
404 ERROR("BUG: no suitable fs found\n");
405 return -1;
406 }
407
408 snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
409 snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root);
410
411 if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
412 ERROR("mount failed: %s\n", strerror(errno));
413 return -1;
414 }
415
416 return pivot("/mnt", ro_root);
417 }
418
419 static int ramoverlay(void)
420 {
421 DEBUG(2, "\n");
422
423 mkdir("/tmp/root", 0755);
424 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
425
426 return fopivot("/tmp/root", "/rom");
427 }
428
429 static int switch2jffs(void)
430 {
431 char mtd[32];
432
433 if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd)))
434 return 0;
435
436 if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
437 ERROR("no rootfs_data was found\n");
438 return -1;
439 }
440
441 if (mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL)) {
442 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd, strerror(errno));
443 return -1;
444 }
445
446 if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
447 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno));
448 return -1;
449 }
450
451 system("cp -a /tmp/root/* /rom/overlay");
452
453 if (pivot("/rom", "/mnt")) {
454 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno));
455 return -1;
456 }
457
458 if (mount_move("/mnt", "/tmp/root", "")) {
459 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
460 return -1;
461 }
462
463 return fopivot("/overlay", "/rom");
464 }
465
466 static int handle_whiteout(const char *dir)
467 {
468 struct stat s;
469 char link[256];
470 ssize_t sz;
471 struct dirent **namelist;
472 int n;
473
474 n = scandir(dir, &namelist, NULL, NULL);
475
476 if (n < 1)
477 return -1;
478
479 while (n--) {
480 char file[256];
481
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);
485 if (sz > 0) {
486 char *orig;
487
488 link[sz] = '\0';
489 orig = strstr(&file[1], "/");
490 if (orig && !strcmp(link, "(overlay-whiteout)")) {
491 DEBUG(1, "unlinking %s\n", orig);
492 unlink(orig);
493 }
494 }
495 }
496 free(namelist[n]);
497 }
498 free(namelist);
499
500 return 0;
501 }
502
503 static int mtd_erase(const char *mtd)
504 {
505 int fd = open(mtd, O_RDWR | O_SYNC);
506 struct mtd_info_user i;
507 struct erase_info_user e;
508 int ret;
509
510 if (!fd) {
511 ERROR("failed to open %s: %s\n", mtd, strerror(errno));
512 return -1;
513 }
514
515 ret = ioctl(fd, MEMGETINFO, &i);
516 if (ret) {
517 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
518 return -1;
519 }
520
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);
526 }
527
528 close(fd);
529 return 0;
530 }
531
532 static int ask_user(int argc, char **argv)
533 {
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')
537 return -1;
538 }
539 return 0;
540
541 }
542
543 static int handle_rmdir(const char *dir)
544 {
545 struct stat s;
546 struct dirent **namelist;
547 int n;
548
549 n = scandir(dir, &namelist, NULL, NULL);
550
551 if (n < 1)
552 return -1;
553
554 while (n--) {
555 char file[256];
556
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);
560 unlink(file);
561 }
562 free(namelist[n]);
563 }
564 free(namelist);
565
566 DEBUG(1, "rmdir %s\n", dir);
567 rmdir(dir);
568
569 return 0;
570 }
571
572 static int main_jffs2reset(int argc, char **argv)
573 {
574 char mtd[32];
575 char *mp;
576
577 if (ask_user(argc, argv))
578 return -1;
579
580 if (check_fs_exists("overlay")) {
581 ERROR("overlayfs not found\n");
582 return -1;
583 }
584
585 if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
586 ERROR("no rootfs_data was found\n");
587 return -1;
588 }
589
590 mp = find_mount_point(mtd, "jffs2");
591 if (mp) {
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);
595 } else {
596 LOG("%s is not mounted, erasing it\n", mtd);
597 find_mtd_char("rootfs_data", mtd, sizeof(mtd));
598 mtd_erase(mtd);
599 }
600
601 return 0;
602 }
603
604 static int main_jffs2mark(int argc, char **argv)
605 {
606 FILE *fp;
607 __u32 deadc0de = __cpu_to_be32(0xdeadc0de);
608 char mtd[32];
609 size_t sz;
610
611 if (ask_user(argc, argv))
612 return -1;
613
614 if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
615 ERROR("no rootfs_data was found\n");
616 return -1;
617 }
618
619 fp = fopen(mtd, "w");
620 LOG("%s - marking with deadc0de\n", mtd);
621 if (!fp) {
622 ERROR("opening %s failed\n", mtd);
623 return -1;
624 }
625
626 sz = fwrite(&deadc0de, sizeof(deadc0de), 1, fp);
627 fclose(fp);
628
629 if (sz != 1) {
630 ERROR("writing %s failed: %s\n", mtd, strerror(errno));
631 return -1;
632 }
633
634 return 0;
635 }
636 static int main_switch2jffs(int argc, char **argv)
637 {
638 char mtd[32];
639 char *mp;
640 int ret = -1;
641
642 if (find_overlay_mount("overlayfs:/tmp/root"))
643 return -1;
644
645 if (check_fs_exists("overlay")) {
646 ERROR("overlayfs not found\n");
647 return ret;
648 }
649
650 find_mtd_block("rootfs_data", mtd, sizeof(mtd));
651 mp = find_mount_point(mtd, NULL);
652 if (mp) {
653 LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
654 return -1;
655 }
656
657 if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
658 ERROR("no rootfs_data was found\n");
659 return ret;
660 }
661
662 switch (jffs2_ready(mtd)) {
663 case FS_NONE:
664 ERROR("no jffs2 marker found\n");
665 /* fall through */
666
667 case FS_DEADCODE:
668 ret = switch2jffs();
669 if (!ret) {
670 DEBUG(1, "doing fo cleanup\n");
671 umount2("/tmp/root", MNT_DETACH);
672 foreachdir("/overlay/", handle_whiteout);
673 }
674 break;
675
676 case FS_JFFS2:
677 ret = mtd_mount_jffs2();
678 if (ret)
679 break;
680 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
681 ERROR("switching to jffs2 failed\n");
682 ret = -1;
683 }
684 break;
685 }
686
687 return ret;
688 }
689
690 static int extroot(const char *prefix)
691 {
692 char block_path[32];
693 char kmod_loader[64];
694 struct stat s;
695 pid_t pid;
696
697 sprintf(block_path, "%s/sbin/block", prefix);
698
699 if (stat(block_path, &s))
700 return -1;
701
702 sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", prefix, prefix);
703 system(kmod_loader);
704
705 pid = fork();
706 if (!pid) {
707 mkdir("/tmp/extroot", 0755);
708 execl(block_path, block_path, "extroot", NULL);
709 exit(-1);
710 } else if (pid > 0) {
711 int status;
712
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);
717
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);
723
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");
729 umount("/mnt");
730 } else {
731 umount("/tmp/overlay");
732 rmdir("/tmp/overlay");
733 rmdir("/tmp/extroot/mnt");
734 rmdir("/tmp/extroot");
735 return 0;
736 }
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");
743 umount("/overlay");
744 } else {
745 umount("/tmp/overlay");
746 rmdir("/tmp/overlay");
747 rmdir("/tmp/extroot/overlay");
748 rmdir("/tmp/extroot");
749 return 0;
750 }
751 }
752 }
753 }
754 return -1;
755 }
756
757 int main(int argc, char **argv)
758 {
759 char *mp;
760 char mtd[32];
761
762 argv0 = basename(*argv);
763
764 if (!strcmp(basename(*argv), "jffs2mark"))
765 return main_jffs2mark(argc, argv);
766
767 if (!strcmp(basename(*argv), "jffs2reset"))
768 return main_jffs2reset(argc, argv);
769
770 if (!strcmp(basename(*argv), "switch2jffs"))
771 return main_switch2jffs(argc, argv);
772
773 if (!getenv("PREINIT"))
774 return -1;
775
776 if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd))) {
777 ramoverlay();
778 } else if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
779 if (!find_mtd_char("rootfs", mtd, sizeof(mtd)))
780 mtd_unlock(mtd);
781 LOG("mounting /dev/root\n");
782 mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
783 } else {
784 if (!extroot("")) {
785 fprintf(stderr, "mount_root: switched to extroot\n");
786 return 0;
787 }
788
789 switch (jffs2_ready(mtd)) {
790 case FS_NONE:
791 case FS_DEADCODE:
792 return ramoverlay();
793
794 case FS_JFFS2:
795 find_mtd_block("rootfs_data", mtd, sizeof(mtd));
796 mp = find_mount_point(mtd, NULL);
797 if (mp) {
798 LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
799 return -1;
800 }
801
802 mtd_mount_jffs2();
803
804 if (!extroot("/tmp/overlay")) {
805 fprintf(stderr, "mount_root: switched to extroot\n");
806 return 0;
807 }
808
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");
812 return ramoverlay();
813 }
814 }
815 }
816
817 return 0;
818 }