de78539122aa334ecdc8285313307459c83faa87
[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_point(char *block, char *fs)
101 {
102 FILE *fp = fopen("/proc/mounts", "r");
103 static char line[256];
104 int len = strlen(block);
105 char *point = NULL;
106
107 if(!fp)
108 return NULL;
109
110 while (fgets(line, sizeof(line), fp)) {
111 if (!strncmp(line, block, len)) {
112 char *p = &line[len + 1];
113 char *t = strstr(p, " ");
114
115 if (!t)
116 return NULL;
117
118 *t = '\0';
119 t++;
120
121 if (fs && strncmp(t, fs, strlen(fs))) {
122 ERROR("block is mounted with wrong fs\n");
123 return NULL;
124 }
125 point = p;
126 break;
127 }
128 }
129
130 fclose(fp);
131
132 return point;
133 }
134
135 static char* find_mtd_index(char *name)
136 {
137 FILE *fp = fopen("/proc/mtd", "r");
138 static char line[256];
139 char *index = NULL;
140
141 if(!fp)
142 return index;
143
144 while (!index && fgets(line, sizeof(line), fp)) {
145 if (strstr(line, name)) {
146 char *eol = strstr(line, ":");
147
148 if (!eol)
149 continue;
150
151 *eol = '\0';
152 index = &line[3];
153 DEBUG(1, "found %s -> index:%s\n", name, index);
154 }
155 }
156
157 fclose(fp);
158
159 return index;
160 }
161
162 static int find_mtd_block(char *name, char *part, int plen)
163 {
164 char *index = find_mtd_index(name);
165
166 if (!index)
167 return -1;
168
169 snprintf(part, plen, "/dev/mtdblock%s", index);
170 DEBUG(1, "found %s -> %s\n", name, part);
171
172 return 0;
173 }
174
175 static int find_mtd_char(char *name, char *part, int plen)
176 {
177 char *index = find_mtd_index(name);
178
179 if (!index)
180 return -1;
181
182 snprintf(part, plen, "/dev/mtd%s", index);
183 DEBUG(1, "found %s -> %s\n", name, part);
184
185 return 0;
186 }
187
188 static int mtd_unlock(char *mtd)
189 {
190 struct erase_info_user mtdlock;
191 struct mtd_info_user mtdinfo;
192 int fd = open(mtd, O_RDWR | O_SYNC);
193 int ret = -1;
194
195 DEBUG(1, "%s\n", mtd);
196
197 if (!fd) {
198 ERROR("failed to open %s: %s\n", mtd, strerror(errno));
199 return -1;
200 }
201
202 ret = ioctl(fd, MEMGETINFO, &mtdinfo);
203 if (ret) {
204 ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
205 goto err_out;
206 }
207
208 mtdlock.start = 0;
209 mtdlock.length = mtdinfo.size;
210 ioctl(fd, MEMUNLOCK, &mtdlock);
211
212 err_out:
213 close(fd);
214
215 return ret;
216 }
217
218 static int mtd_mount_jffs2(void)
219 {
220 char rootfs_data[32];
221
222
223 if (mkdir("/tmp/overlay", 0755)) {
224 ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno));
225 return -1;
226 }
227
228 if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) {
229 ERROR("rootfs_data does not exist\n");
230 return -1;
231 }
232
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));
235 return -1;
236 }
237
238 find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data));
239
240 return mtd_unlock(rootfs_data);
241 }
242
243 static int jffs2_ready(char *mtd)
244 {
245 FILE *fp = fopen(mtd, "r");
246 __u32 deadc0de;
247 __u16 jffs2;
248 size_t sz;
249
250 if (!fp) {
251 ERROR("reading %s failed\n", mtd);
252 exit(-1);
253 }
254
255 sz = fread(&deadc0de, sizeof(deadc0de), 1, fp);
256 fclose(fp);
257
258 if (sz != 1) {
259 ERROR("reading %s failed: %s\n", mtd, strerror(errno));
260 exit(-1);
261 }
262
263 deadc0de = __be32_to_cpu(deadc0de);
264 jffs2 = __be16_to_cpu(deadc0de >> 16);
265
266 if (jffs2 == 0x1985) {
267 LOG("jffs2 is ready\n");
268 return FS_JFFS2;
269 }
270
271 if (deadc0de == 0xdeadc0de) {
272 LOG("jffs2 is not ready - marker found\n");
273 return FS_DEADCODE;
274 }
275
276 ERROR("No jffs2 marker was found\n");
277
278 return FS_NONE;
279 }
280
281 static int check_fs_exists(char *fs)
282 {
283 FILE *fp = fopen("/proc/filesystems", "r");
284 static char line[256];
285 int ret = -1;
286
287 DEBUG(2, "%s\n", fs);
288
289 if (!fp) {
290 ERROR("opening /proc/filesystems failed: %s\n", strerror(errno));
291 goto out;
292 }
293
294 while (ret && fgets(line, sizeof(line), fp))
295 if (strstr(line, fs))
296 ret = 0;
297
298 fclose(fp);
299
300 out:
301 return ret;
302 }
303
304 static int mount_move(char *oldroot, char *newroot, char *dir)
305 {
306 #ifndef MS_MOVE
307 #define MS_MOVE (1 << 13)
308 #endif
309 struct stat s;
310 char olddir[64];
311 char newdir[64];
312 int ret;
313
314 DEBUG(2, "%s %s\n", oldroot, dir);
315
316 snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
317 snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
318
319 if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
320 return -1;
321
322 if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
323 return -1;
324
325 ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
326
327 if (ret)
328 DEBUG(1, "failed %s %s: %s\n", olddir, newdir, strerror(errno));
329
330 return ret;
331 }
332
333 static int pivot(char *new, char *old)
334 {
335 char pivotdir[64];
336 int ret;
337
338 DEBUG(2, "%s %s\n", new, old);
339
340 if (mount_move("", new, "/proc"))
341 return -1;
342
343 snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
344
345 ret = pivot_root(new, pivotdir);
346
347 if (ret < 0) {
348 ERROR("pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno));
349 return -1;
350 }
351
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");
357
358 return 0;
359 }
360
361
362 static int fopivot(char *rw_root, char *ro_root)
363 {
364 char overlay[64], lowerdir[64];
365
366 DEBUG(2, "%s %s\n", rw_root, ro_root);
367
368 if (check_fs_exists("overlay")) {
369 ERROR("BUG: no suitable fs found\n");
370 return -1;
371 }
372
373 snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
374 snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root);
375
376 if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
377 ERROR("mount failed: %s\n", strerror(errno));
378 return -1;
379 }
380
381 return pivot("/mnt", ro_root);
382 }
383
384 static int ramoverlay(void)
385 {
386 DEBUG(2, "\n");
387
388 mkdir("/tmp/root", 0755);
389 mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
390
391 return fopivot("/tmp/root", "/rom");
392 }
393
394 static int switch2jffs(void)
395 {
396 char mtd[32];
397
398 if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
399 ERROR("no rootfs_data was found\n");
400 return -1;
401 }
402
403 if (mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL)) {
404 ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd, strerror(errno));
405 return -1;
406 }
407
408 if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
409 ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno));
410 return -1;
411 }
412
413 system("cp -a /tmp/root/* /rom/overlay");
414
415 if (pivot("/rom", "/mnt")) {
416 ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno));
417 return -1;
418 }
419
420 if (mount_move("/mnt", "/tmp/root", "")) {
421 ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
422 return -1;
423 }
424
425 return fopivot("/overlay", "/rom");
426 }
427
428 static int handle_whiteout(const char *dir)
429 {
430 struct stat s;
431 char link[256];
432 ssize_t sz;
433 struct dirent **namelist;
434 int n;
435
436 n = scandir(dir, &namelist, NULL, NULL);
437
438 if (n < 1)
439 return -1;
440
441 while (n--) {
442 char file[256];
443
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);
447 if (sz > 0) {
448 char *orig;
449
450 link[sz] = '\0';
451 orig = strstr(&file[1], "/");
452 if (orig && !strcmp(link, "(overlay-whiteout)")) {
453 DEBUG(1, "unlinking %s\n", orig);
454 unlink(orig);
455 }
456 }
457 }
458 free(namelist[n]);
459 }
460 free(namelist);
461
462 return 0;
463 }
464
465 static int main_switch2jffs(int argc, char **argv)
466 {
467 char mtd[32];
468 char *mp;
469 int ret = -1;
470
471 if (find_overlay_mount("overlayfs:/tmp/root"))
472 return -1;
473
474 if (check_fs_exists("overlay")) {
475 ERROR("overlayfs not found\n");
476 return ret;
477 }
478
479 find_mtd_block("rootfs_data", mtd, sizeof(mtd));
480 mp = find_mount_point(mtd, NULL);
481 if (mp) {
482 LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
483 return -1;
484 }
485
486 if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
487 ERROR("no rootfs_data was found\n");
488 return ret;
489 }
490
491 switch (jffs2_ready(mtd)) {
492 case FS_NONE:
493 ERROR("no jffs2 marker found\n");
494 /* fall through */
495
496 case FS_DEADCODE:
497 ret = switch2jffs();
498 if (!ret) {
499 DEBUG(1, "doing fo cleanup\n");
500 umount2("/tmp/root", MNT_DETACH);
501 foreachdir("/overlay/", handle_whiteout);
502 }
503 break;
504
505 case FS_JFFS2:
506 ret = mtd_mount_jffs2();
507 if (ret)
508 break;
509 if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
510 ERROR("switching to jffs2 failed\n");
511 ret = -1;
512 }
513 break;
514 }
515
516 return ret;
517 }
518
519 static int extroot(void)
520 {
521 struct stat s;
522 pid_t pid;
523
524 if (stat("/sbin/block", &s))
525 return -1;
526
527 pid = fork();
528 if (!pid) {
529 mkdir("/tmp/extroot", 0755);
530 execl("/sbin/block", "/sbin/block", "extroot", NULL);
531 exit(-1);
532 } else if (pid > 0) {
533 int status;
534
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");
542 umount("overlay");
543 } else {
544 return 0;
545 }
546 }
547 }
548 return -1;
549 }
550
551 int main(int argc, char **argv)
552 {
553 char *mp;
554 char mtd[32];
555
556 argv0 = basename(*argv);
557
558 if (!strcmp(basename(*argv), "switch2jffs"))
559 return main_switch2jffs(argc, argv);
560
561 if (!getenv("PREINIT"))
562 return -1;
563
564 if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
565 if (!find_mtd_char("rootfs", mtd, sizeof(mtd)))
566 mtd_unlock(mtd);
567 LOG("mounting /dev/root\n");
568 mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
569 } else {
570 if (!extroot()) {
571 fprintf(stderr, "mount_root: switched to extroot\n");
572 return 0;
573 }
574
575 switch (jffs2_ready(mtd)) {
576 case FS_NONE:
577 case FS_DEADCODE:
578 return ramoverlay();
579
580 case FS_JFFS2:
581 find_mtd_block("rootfs_data", mtd, sizeof(mtd));
582 mp = find_mount_point(mtd, NULL);
583 if (mp) {
584 LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
585 return -1;
586 }
587
588 mtd_mount_jffs2();
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");
592 return ramoverlay();
593 }
594 }
595 }
596
597 return 0;
598 }