block: add support for generic mount options
[project/ubox.git] / block.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 #define _GNU_SOURCE
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <syslog.h>
19 #include <libgen.h>
20 #include <glob.h>
21
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/swap.h>
25 #include <sys/mount.h>
26 #include <sys/wait.h>
27
28 #include <uci.h>
29 #include <uci_blob.h>
30
31 #include <libubox/list.h>
32 #include <libubox/vlist.h>
33 #include <libubox/blobmsg_json.h>
34 #include <libubox/avl-cmp.h>
35
36 #include "libblkid-tiny/libblkid-tiny.h"
37
38 enum {
39         TYPE_MOUNT,
40         TYPE_SWAP,
41 };
42
43 struct mount {
44         struct vlist_node node;
45         int type;
46
47         char *target;
48         char *path;
49         char *options;
50         uint32_t flags;
51         char *uuid;
52         char *label;
53         char *device;
54         int extroot;
55         int overlay;
56         int disabled_fsck;
57         unsigned int prio;
58 };
59
60 static struct vlist_tree mounts;
61 static struct blob_buf b;
62 static LIST_HEAD(devices);
63 static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs;
64 static unsigned int delay_root;
65
66 enum {
67         CFG_ANON_MOUNT,
68         CFG_ANON_SWAP,
69         CFG_AUTO_MOUNT,
70         CFG_AUTO_SWAP,
71         CFG_DELAY_ROOT,
72         CFG_CHECK_FS,
73         __CFG_MAX
74 };
75
76 static const struct blobmsg_policy config_policy[__CFG_MAX] = {
77         [CFG_ANON_SWAP] = { .name = "anon_swap", .type = BLOBMSG_TYPE_INT32 },
78         [CFG_ANON_MOUNT] = { .name = "anon_mount", .type = BLOBMSG_TYPE_INT32 },
79         [CFG_AUTO_SWAP] = { .name = "auto_swap", .type = BLOBMSG_TYPE_INT32 },
80         [CFG_AUTO_MOUNT] = { .name = "auto_mount", .type = BLOBMSG_TYPE_INT32 },
81         [CFG_DELAY_ROOT] = { .name = "delay_root", .type = BLOBMSG_TYPE_INT32 },
82         [CFG_CHECK_FS] = { .name = "check_fs", .type = BLOBMSG_TYPE_INT32 },
83 };
84
85 enum {
86         MOUNT_UUID,
87         MOUNT_LABEL,
88         MOUNT_ENABLE,
89         MOUNT_TARGET,
90         MOUNT_DEVICE,
91         MOUNT_OPTIONS,
92         __MOUNT_MAX
93 };
94
95 static const struct uci_blob_param_list config_attr_list = {
96         .n_params = __CFG_MAX,
97         .params = config_policy,
98 };
99
100 static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
101         [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
102         [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
103         [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
104         [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
105         [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING },
106         [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
107 };
108
109 static const struct uci_blob_param_list mount_attr_list = {
110         .n_params = __MOUNT_MAX,
111         .params = mount_policy,
112 };
113
114 enum {
115         SWAP_ENABLE,
116         SWAP_UUID,
117         SWAP_DEVICE,
118         SWAP_PRIO,
119         __SWAP_MAX
120 };
121
122 static const struct blobmsg_policy swap_policy[__SWAP_MAX] = {
123         [SWAP_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
124         [SWAP_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
125         [SWAP_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
126         [SWAP_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
127 };
128
129 static const struct uci_blob_param_list swap_attr_list = {
130         .n_params = __SWAP_MAX,
131         .params = swap_policy,
132 };
133
134 struct mount_flag {
135         const char *name;
136         int32_t flag;
137 };
138
139 #ifndef MS_DIRSYNC
140 #       define MS_DIRSYNC               (1 << 7)
141 #endif
142
143 #ifndef MS_RELATIME
144 #       define MS_RELATIME              (1 << 21)
145 #endif
146
147 #ifndef MS_STRICTATIME
148 #       define MS_STRICTATIME   (1 << 24)
149 #endif
150
151 static const struct mount_flag mount_flags[] = {
152         { "sync",                       MS_SYNCHRONOUS  },
153         { "async",                      ~MS_SYNCHRONOUS },
154         { "dirsync",            MS_DIRSYNC              },
155         { "mand",                       MS_MANDLOCK             },
156         { "nomand",                     ~MS_MANDLOCK    },
157         { "atime",                      ~MS_NOATIME             },
158         { "noatime",            MS_NOATIME              },
159         { "dev",                        ~MS_NODEV               },
160         { "nodev",                      MS_NODEV                },
161         { "diratime",           ~MS_NODIRATIME  },
162         { "nodiratime",         MS_NODIRATIME   },
163         { "exec",                       ~MS_NOEXEC              },
164         { "noexec",                     MS_NOEXEC               },
165         { "suid",                       ~MS_NOSUID              },
166         { "nosuid",                     MS_NOSUID               },
167         { "rw",                         ~MS_RDONLY              },
168         { "ro",                         MS_RDONLY               },
169         { "relatime",           MS_RELATIME             },
170         { "norelatime",         ~MS_RELATIME    },
171         { "strictatime",        MS_STRICTATIME  },
172 };
173
174 static char *blobmsg_get_strdup(struct blob_attr *attr)
175 {
176         if (!attr)
177                 return NULL;
178
179         return strdup(blobmsg_get_string(attr));
180 }
181
182 static char *blobmsg_get_basename(struct blob_attr *attr)
183 {
184         if (!attr)
185                 return NULL;
186
187         return strdup(basename(blobmsg_get_string(attr)));
188 }
189
190 static void parse_mount_options(struct mount *m, char *optstr)
191 {
192         int i;
193         bool is_flag;
194         char *p, *opts, *last;
195
196         m->flags = 0;
197         m->options = NULL;
198
199         if (!optstr || !*optstr)
200                 return;
201
202         m->options = opts = calloc(1, strlen(optstr) + 1);
203
204         if (!m->options)
205                 return;
206
207         p = last = optstr;
208
209         do {
210                 p = strchr(p, ',');
211
212                 if (p)
213                         *p++ = 0;
214
215                 for (i = 0, is_flag = false; i < ARRAY_SIZE(mount_flags); i++) {
216                         if (!strcmp(last, mount_flags[i].name)) {
217                                 if (mount_flags[i].flag < 0)
218                                         m->flags &= (uint32_t)mount_flags[i].flag;
219                                 else
220                                         m->flags |= (uint32_t)mount_flags[i].flag;
221                                 is_flag = true;
222                                 break;
223                         }
224                 }
225
226                 if (!is_flag)
227                         opts += sprintf(opts, "%s%s", (opts > m->options) ? "," : "", last);
228
229                 last = p;
230
231         } while (p);
232
233         free(optstr);
234 }
235
236 static int mount_add(struct uci_section *s)
237 {
238         struct blob_attr *tb[__MOUNT_MAX] = { 0 };
239         struct mount *m;
240
241         blob_buf_init(&b, 0);
242         uci_to_blob(&b, s, &mount_attr_list);
243         blobmsg_parse(mount_policy, __MOUNT_MAX, tb, blob_data(b.head), blob_len(b.head));
244
245         if (!tb[MOUNT_LABEL] && !tb[MOUNT_UUID] && !tb[MOUNT_DEVICE])
246                 return -1;
247
248         if (tb[MOUNT_ENABLE] && !blobmsg_get_u32(tb[MOUNT_ENABLE]))
249                 return -1;
250
251         m = malloc(sizeof(struct mount));
252         m->type = TYPE_MOUNT;
253         m->uuid = blobmsg_get_strdup(tb[MOUNT_UUID]);
254         m->label = blobmsg_get_strdup(tb[MOUNT_LABEL]);
255         m->target = blobmsg_get_strdup(tb[MOUNT_TARGET]);
256         m->device = blobmsg_get_basename(tb[MOUNT_DEVICE]);
257
258         parse_mount_options(m, blobmsg_get_strdup(tb[MOUNT_OPTIONS]));
259
260         m->overlay = m->extroot = 0;
261         if (m->target && !strcmp(m->target, "/"))
262                 m->extroot = 1;
263         if (m->target && !strcmp(m->target, "/overlay"))
264                 m->extroot = m->overlay = 1;
265
266         if (m->uuid)
267                 vlist_add(&mounts, &m->node, m->uuid);
268         else if (m->label)
269                 vlist_add(&mounts, &m->node, m->label);
270         else if (m->device)
271                 vlist_add(&mounts, &m->node, m->device);
272
273         return 0;
274 }
275
276 static int swap_add(struct uci_section *s)
277 {
278         struct blob_attr *tb[__SWAP_MAX] = { 0 };
279         struct mount *m;
280
281         blob_buf_init(&b, 0);
282         uci_to_blob(&b, s, &swap_attr_list);
283         blobmsg_parse(swap_policy, __SWAP_MAX, tb, blob_data(b.head), blob_len(b.head));
284
285         if (!tb[SWAP_UUID] && !tb[SWAP_DEVICE])
286                 return -1;
287
288         m = malloc(sizeof(struct mount));
289         memset(m, 0, sizeof(struct mount));
290         m->type = TYPE_SWAP;
291         m->uuid = blobmsg_get_strdup(tb[SWAP_UUID]);
292         m->device = blobmsg_get_basename(tb[SWAP_DEVICE]);
293         if (tb[SWAP_PRIO])
294                 m->prio = blobmsg_get_u32(tb[SWAP_PRIO]);
295         if (m->prio)
296                 m->prio = ((m->prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER;
297
298         if ((!tb[SWAP_ENABLE]) || blobmsg_get_u32(tb[SWAP_ENABLE]))
299                 vlist_add(&mounts, &m->node, (m->uuid) ? (m->uuid) : (m->device));
300
301         return 0;
302 }
303
304 static int global_add(struct uci_section *s)
305 {
306         struct blob_attr *tb[__CFG_MAX] = { 0 };
307
308         blob_buf_init(&b, 0);
309         uci_to_blob(&b, s, &config_attr_list);
310         blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(b.head), blob_len(b.head));
311
312         if ((tb[CFG_ANON_MOUNT]) && blobmsg_get_u32(tb[CFG_ANON_MOUNT]))
313                 anon_mount = 1;
314         if ((tb[CFG_ANON_SWAP]) && blobmsg_get_u32(tb[CFG_ANON_SWAP]))
315                 anon_swap = 1;
316
317         if ((tb[CFG_AUTO_MOUNT]) && blobmsg_get_u32(tb[CFG_AUTO_MOUNT]))
318                 auto_mount = 1;
319         if ((tb[CFG_AUTO_SWAP]) && blobmsg_get_u32(tb[CFG_AUTO_SWAP]))
320                 auto_swap = 1;
321
322         if (tb[CFG_DELAY_ROOT])
323                 delay_root = blobmsg_get_u32(tb[CFG_DELAY_ROOT]);
324
325         if ((tb[CFG_CHECK_FS]) && blobmsg_get_u32(tb[CFG_CHECK_FS]))
326                 check_fs = 1;
327
328         return 0;
329 }
330
331 static struct mount* find_swap(const char *uuid, const char *device)
332 {
333         struct mount *m;
334
335         vlist_for_each_element(&mounts, m, node) {
336                 if (m->type != TYPE_SWAP)
337                         continue;
338                 if (uuid && m->uuid && !strcmp(m->uuid, uuid))
339                         return m;
340                 if (device && m->device && !strcmp(m->device, device))
341                         return m;
342         }
343
344         return NULL;
345 }
346
347 static struct mount* find_block(const char *uuid, const char *label, const char *device,
348                                 const char *target)
349 {
350         struct mount *m;
351
352         vlist_for_each_element(&mounts, m, node) {
353                 if (m->type != TYPE_MOUNT)
354                         continue;
355                 if (m->uuid && uuid && !strcmp(m->uuid, uuid))
356                         return m;
357                 if (m->label && label && !strcmp(m->label, label))
358                         return m;
359                 if (m->target && target && !strcmp(m->target, target))
360                         return m;
361                 if (m->device && device && !strcmp(m->device, device))
362                         return m;
363         }
364
365         return NULL;
366 }
367
368 static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new,
369                           struct vlist_node *node_old)
370 {
371 }
372
373 static int config_load(char *cfg)
374 {
375         struct uci_context *ctx;
376         struct uci_package *pkg;
377         struct uci_element *e;
378
379         vlist_init(&mounts, avl_strcmp, mounts_update);
380
381         ctx = uci_alloc_context();
382         if (cfg) {
383                 char path[32];
384                 snprintf(path, 32, "%s/etc/config", cfg);
385                 uci_set_confdir(ctx, path);
386         }
387
388         if (uci_load(ctx, "fstab", &pkg))
389                 return -1;
390
391         vlist_update(&mounts);
392         uci_foreach_element(&pkg->sections, e) {
393                 struct uci_section *s = uci_to_section(e);
394
395                 if (!strcmp(s->type, "mount"))
396                         mount_add(s);
397                 if (!strcmp(s->type, "swap"))
398                         swap_add(s);
399                 if (!strcmp(s->type, "global"))
400                         global_add(s);
401         }
402         vlist_flush(&mounts);
403
404         return 0;
405 }
406
407 static int _cache_load(const char *path)
408 {
409         int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
410         int j;
411         glob_t gl;
412
413         if (glob(path, gl_flags, NULL, &gl) < 0)
414                 return -1;
415
416         for (j = 0; j < gl.gl_pathc; j++) {
417                 struct blkid_struct_probe *pr = malloc(sizeof(struct blkid_struct_probe));
418                 memset(pr, 0, sizeof(struct blkid_struct_probe));
419                 probe_block(gl.gl_pathv[j], pr);
420                 if (pr->err || !pr->id)
421                         free(pr);
422                 else
423                         list_add_tail(&pr->list, &devices);
424         }
425
426         globfree(&gl);
427
428         return 0;
429 }
430
431 static void cache_load(int mtd)
432 {
433         if (mtd)
434                 _cache_load("/dev/mtdblock*");
435         _cache_load("/dev/mmcblk*");
436         _cache_load("/dev/sd*");
437         _cache_load("/dev/sdc*");
438         _cache_load("/dev/hd*");
439         _cache_load("/dev/md*");
440         _cache_load("/dev/mapper/*");
441 }
442
443 static int print_block_info(struct blkid_struct_probe *pr)
444 {
445         printf("%s:", pr->dev);
446         if (pr->uuid[0])
447                 printf(" UUID=\"%s\"", pr->uuid);
448
449         if (pr->label[0])
450                 printf(" LABEL=\"%s\"", pr->label);
451
452         if (pr->name[0])
453                 printf(" NAME=\"%s\"", pr->name);
454
455         if (pr->version[0])
456                 printf(" VERSION=\"%s\"", pr->version);
457
458         printf(" TYPE=\"%s\"\n", pr->id->name);
459
460         return 0;
461 }
462
463 static int print_block_uci(struct blkid_struct_probe *pr)
464 {
465         if (!strcmp(pr->id->name, "swap")) {
466                 printf("config 'swap'\n");
467         } else {
468                 printf("config 'mount'\n");
469                 printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
470         }
471         if (pr->uuid[0])
472                 printf("\toption\tuuid\t'%s'\n", pr->uuid);
473         else
474                 printf("\toption\tdevice\t'%s'\n", pr->dev);
475         printf("\toption\tenabled\t'0'\n\n");
476
477         return 0;
478 }
479
480 static struct blkid_struct_probe* find_block_info(char *uuid, char *label, char *path)
481 {
482         struct blkid_struct_probe *pr = NULL;
483
484         if (uuid)
485                 list_for_each_entry(pr, &devices, list)
486                         if (!strcmp(pr->uuid, uuid))
487                                 return pr;
488
489         if (label)
490                 list_for_each_entry(pr, &devices, list)
491                         if (strcmp(pr->label, label))
492                                 return pr;
493
494         if (path)
495                 list_for_each_entry(pr, &devices, list)
496                         if (!strcmp(pr->dev, path))
497                                 return pr;
498
499         return NULL;
500 }
501
502 static char* find_mount_point(char *block)
503 {
504         FILE *fp = fopen("/proc/mounts", "r");
505         static char line[256];
506         int len = strlen(block);
507         char *point = NULL;
508
509         if(!fp)
510                 return NULL;
511
512         while (fgets(line, sizeof(line), fp)) {
513                 if (!strncmp(line, block, len)) {
514                         char *p = &line[len + 1];
515                         char *t = strstr(p, " ");
516
517                         if (!t)
518                                 return NULL;
519                         *t = '\0';
520                         point = p;
521                         break;
522                 }
523         }
524
525         fclose(fp);
526
527         return point;
528 }
529
530 static void mkdir_p(char *dir)
531 {
532         char *l = strrchr(dir, '/');
533
534         if (l) {
535                 *l = '\0';
536                 mkdir_p(dir);
537                 *l = '/';
538                 mkdir(dir, 0755);
539         }
540 }
541
542 static void check_filesystem(struct blkid_struct_probe *pr)
543 {
544         pid_t pid;
545         struct stat statbuf;
546         char *e2fsck = "/usr/sbin/e2fsck";
547
548         if (strncmp(pr->id->name, "ext", 3)) {
549                 fprintf(stderr, "check_filesystem: %s is not supported\n", pr->id->name);
550                 return;
551         }
552
553         if (stat(e2fsck, &statbuf) < 0) {
554                 fprintf(stderr, "check_filesystem: %s not found\n", e2fsck);
555                 return;
556         }
557
558         pid = fork();
559         if (!pid) {
560                 execl(e2fsck, e2fsck, "-p", pr->dev, NULL);
561                 exit(-1);
562         } else if (pid > 0) {
563                 int status;
564
565                 waitpid(pid, &status, 0);
566                 if (WEXITSTATUS(status))
567                         fprintf(stderr, "check_filesystem: %s returned %d\n", e2fsck, WEXITSTATUS(status));
568         }
569 }
570
571 static int mount_device(struct blkid_struct_probe *pr, int hotplug)
572 {
573         struct mount *m;
574         char *device;
575
576         if (!pr)
577                 return -1;
578
579         device = basename(pr->dev);
580
581         if (!strcmp(pr->id->name, "swap")) {
582                 if (hotplug && !auto_swap)
583                         return -1;
584                 m = find_swap(pr->uuid, device);
585                 if (m || anon_swap)
586                         swapon(pr->dev, (m) ? (m->prio) : (0));
587
588                 return 0;
589         }
590
591         if (hotplug && !auto_mount)
592                 return -1;
593
594         if (find_mount_point(pr->dev)) {
595                 fprintf(stderr, "%s is already mounted\n", pr->dev);
596                 return -1;
597         }
598
599         m = find_block(pr->uuid, pr->label, device, NULL);
600         if (m && m->extroot)
601                 return -1;
602
603         if (m) {
604                 char *target = m->target;
605                 char _target[32];
606                 int err = 0;
607
608                 if (!target) {
609                         snprintf(_target, sizeof(_target), "/mnt/%s", device);
610                         target = _target;
611                 }
612                 mkdir_p(target);
613
614                 if (check_fs)
615                         check_filesystem(pr);
616
617                 err = mount(pr->dev, target, pr->id->name, m->flags,
618                             (m->options) ? (m->options) : (""));
619                 if (err)
620                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
621                                         pr->dev, pr->id->name, target, err, strerror(err));
622                 return err;
623         }
624
625         if (anon_mount) {
626                 char target[] = "/mnt/mmcblk123";
627                 int err = 0;
628
629                 snprintf(target, sizeof(target), "/mnt/%s", device);
630                 mkdir_p(target);
631
632                 if (check_fs)
633                         check_filesystem(pr);
634
635                 err = mount(pr->dev, target, pr->id->name, 0, "");
636                 if (err)
637                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
638                                         pr->dev, pr->id->name, target, err, strerror(err));
639                 return err;
640         }
641
642         return 0;
643 }
644
645 static int umount_device(struct blkid_struct_probe *pr)
646 {
647         struct mount *m;
648         char *device = basename(pr->dev);
649         char *mp;
650         int err;
651
652         if (!pr)
653                 return -1;
654
655         if (!strcmp(pr->id->name, "swap"))
656                 return -1;
657
658         mp = find_mount_point(pr->dev);
659         if (!mp)
660                 return -1;
661
662         m = find_block(pr->uuid, pr->label, device, NULL);
663         if (m && m->extroot)
664                 return -1;
665
666         err = umount2(mp, MNT_DETACH);
667         if (err)
668                 fprintf(stderr, "unmounting %s (%s)  failed (%d) - %s\n",
669                         pr->dev, mp, err, strerror(err));
670         else
671                 fprintf(stderr, "unmounted %s (%s)\n",
672                         pr->dev, mp);
673
674         return err;
675 }
676
677 static int main_hotplug(int argc, char **argv)
678 {
679         char path[32];
680         char *action, *device, *mount_point;
681
682         action = getenv("ACTION");
683         device = getenv("DEVNAME");
684
685         if (!action || !device)
686                 return -1;
687         snprintf(path, sizeof(path), "/dev/%s", device);
688
689         if (!strcmp(action, "remove")) {
690                 int err = 0;
691                 mount_point = find_mount_point(path);
692                 if (mount_point)
693                         err = umount2(mount_point, MNT_DETACH);
694
695                 if (err)
696                         fprintf(stderr, "umount of %s failed (%d) - %s\n",
697                                         mount_point, err, strerror(err));
698
699                 return 0;
700         } else if (strcmp(action, "add")) {
701                 fprintf(stderr, "Unkown action %s\n", action);
702
703                 return -1;
704         }
705
706         if (config_load(NULL))
707                 return -1;
708         cache_load(0);
709
710         return mount_device(find_block_info(NULL, NULL, path), 1);
711 }
712
713 static int find_block_mtd(char *name, char *part, int plen)
714 {
715         FILE *fp = fopen("/proc/mtd", "r");
716         static char line[256];
717         char *index = NULL;
718
719         if(!fp)
720                 return -1;
721
722         while (!index && fgets(line, sizeof(line), fp)) {
723                 if (strstr(line, name)) {
724                         char *eol = strstr(line, ":");
725
726                         if (!eol)
727                                 continue;
728
729                         *eol = '\0';
730                         index = &line[3];
731                 }
732         }
733
734         fclose(fp);
735
736         if (!index)
737                 return -1;
738
739         snprintf(part, plen, "/dev/mtdblock%s", index);
740
741         return 0;
742 }
743
744 static int check_extroot(char *path)
745 {
746         struct blkid_struct_probe *pr = NULL;
747         char fs[32];
748
749         if (find_block_mtd("rootfs", fs, sizeof(fs)))
750                 return -1;
751
752         list_for_each_entry(pr, &devices, list) {
753                 if (!strcmp(pr->dev, fs)) {
754                         struct stat s;
755                         FILE *fp = NULL;
756                         char tag[64];
757                         char uuid[64] = { 0 };
758
759                         snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path);
760                         if (stat(tag, &s)) {
761                                 fp = fopen(tag, "w+");
762                                 if (!fp) {
763                                         fprintf(stderr, "extroot: failed to write uuid tag file\n");
764                                         /* return 0 to continue boot regardless of error */
765                                         return 0;
766                                 }
767                                 fputs(pr->uuid, fp);
768                                 fclose(fp);
769                                 return 0;
770                         }
771
772                         fp = fopen(tag, "r");
773                         if (!fp) {
774                                 fprintf(stderr, "extroot: failed to open uuid tag file\n");
775                                 return -1;
776                         }
777
778                         fgets(uuid, sizeof(uuid), fp);
779                         fclose(fp);
780                         if (!strcmp(uuid, pr->uuid))
781                                 return 0;
782                         fprintf(stderr, "extroot: uuid tag does not match rom uuid\n");
783                 }
784         }
785         return -1;
786 }
787
788 static int mount_extroot(char *cfg)
789 {
790         char overlay[] = "/tmp/extroot/overlay";
791         char mnt[] = "/tmp/extroot/mnt";
792         char *path = mnt;
793         struct blkid_struct_probe *pr;
794         struct mount *m;
795         int err = -1;
796
797         if (config_load(cfg))
798                 return -2;
799
800         m = find_block(NULL, NULL, NULL, "/");
801         if (!m)
802                 m = find_block(NULL, NULL, NULL, "/overlay");
803
804         if (!m || !m->extroot)
805                 return -1;
806
807         pr = find_block_info(m->uuid, m->label, NULL);
808
809         if (!pr && delay_root){
810                 fprintf(stderr, "extroot: is not ready yet, retrying in %u seconds\n", delay_root);
811                 sleep(delay_root);
812                 mkblkdev();
813                 cache_load(0);
814                 pr = find_block_info(m->uuid, m->label, NULL);
815         }
816         if (pr) {
817                 if (strncmp(pr->id->name, "ext", 3)) {
818                         fprintf(stderr, "extroot: %s is not supported, try ext4\n", pr->id->name);
819                         return -1;
820                 }
821                 if (m->overlay)
822                         path = overlay;
823                 mkdir_p(path);
824
825                 if (check_fs)
826                         check_filesystem(pr);
827
828                 err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : (""));
829
830                 if (err) {
831                         fprintf(stderr, "mounting %s (%s) as %s failed (%d) - %s\n",
832                                         pr->dev, pr->id->name, path, err, strerror(err));
833                 } else if (m->overlay) {
834                         err = check_extroot(path);
835                         if (err)
836                                 umount(path);
837                 }
838         }
839
840         return err;
841 }
842
843 static int main_extroot(int argc, char **argv)
844 {
845         struct blkid_struct_probe *pr;
846         char fs[32] = { 0 };
847         char fs_data[32] = { 0 };
848         int err = -1;
849
850         if (!getenv("PREINIT"))
851                 return -1;
852
853         if (argc != 2) {
854                 fprintf(stderr, "Usage: block extroot mountpoint\n");
855                 return -1;
856         }
857
858         mkblkdev();
859         cache_load(1);
860
861         find_block_mtd("rootfs", fs, sizeof(fs));
862         if (!fs[0])
863                 return -2;
864
865         pr = find_block_info(NULL, NULL, fs);
866         if (!pr)
867                 return -3;
868
869         find_block_mtd("rootfs_data", fs_data, sizeof(fs_data));
870         if (fs_data[0]) {
871                 pr = find_block_info(NULL, NULL, fs_data);
872                 if (pr && !strcmp(pr->id->name, "jffs2")) {
873                         char cfg[] = "/tmp/jffs_cfg";
874
875                         mkdir_p(cfg);
876                         if (!mount(fs_data, cfg, "jffs2", MS_NOATIME, NULL)) {
877                                 err = mount_extroot(cfg);
878                                 umount2(cfg, MNT_DETACH);
879                         }
880                         if (err < 0)
881                                 rmdir("/tmp/overlay");
882                         rmdir(cfg);
883                         return err;
884                 }
885         }
886
887         return mount_extroot(NULL);
888 }
889
890 static int main_mount(int argc, char **argv)
891 {
892         struct blkid_struct_probe *pr;
893
894         if (config_load(NULL))
895                 return -1;
896
897         cache_load(1);
898         list_for_each_entry(pr, &devices, list)
899                 mount_device(pr, 0);
900
901         return 0;
902 }
903
904 static int main_umount(int argc, char **argv)
905 {
906         struct blkid_struct_probe *pr;
907
908         if (config_load(NULL))
909                 return -1;
910
911         cache_load(0);
912         list_for_each_entry(pr, &devices, list)
913                 umount_device(pr);
914
915         return 0;
916 }
917
918 static int main_detect(int argc, char **argv)
919 {
920         struct blkid_struct_probe *pr;
921
922         cache_load(0);
923         printf("config 'global'\n");
924         printf("\toption\tanon_swap\t'0'\n");
925         printf("\toption\tanon_mount\t'0'\n");
926         printf("\toption\tauto_swap\t'1'\n");
927         printf("\toption\tauto_mount\t'1'\n");
928         printf("\toption\tdelay_root\t'5'\n");
929         printf("\toption\tcheck_fs\t'0'\n\n");
930         list_for_each_entry(pr, &devices, list)
931                 print_block_uci(pr);
932
933         return 0;
934 }
935
936 static int main_info(int argc, char **argv)
937 {
938         int i;
939         struct blkid_struct_probe *pr;
940
941         cache_load(1);
942         if (argc == 2) {
943                 list_for_each_entry(pr, &devices, list)
944                         print_block_info(pr);
945
946                 return 0;
947         };
948
949         for (i = 2; i < argc; i++) {
950                 struct stat s;
951
952                 if (stat(argv[i], &s)) {
953                         fprintf(stderr, "failed to stat %s\n", argv[i]);
954                         continue;
955                 }
956                 if (!S_ISBLK(s.st_mode)) {
957                         fprintf(stderr, "%s is not a block device\n", argv[i]);
958                         continue;
959                 }
960                 pr = find_block_info(NULL, NULL, argv[i]);
961                 if (pr)
962                         print_block_info(pr);
963         }
964
965         return 0;
966 }
967
968 static int main_swapon(int argc, char **argv)
969 {
970         if (argc != 2) {
971                 fprintf(stderr, "Usage: swapon <-s> <-a> [DEVICE]\n\n\tStart swapping on [DEVICE]\n -a\tStart swapping on all swap devices\n -s\tShow summary\n");
972                 return -1;
973         }
974
975         if (!strcmp(argv[1], "-s")) {
976                 FILE *fp = fopen("/proc/swaps", "r");
977                 char *lineptr = NULL;
978                 size_t s;
979
980                 if (!fp) {
981                         fprintf(stderr, "failed to open /proc/swaps\n");
982                         return -1;
983                 }
984                 while (getline(&lineptr, &s, fp) > 0)
985                         printf(lineptr);
986                 if (lineptr)
987                         free(lineptr);
988                 fclose(fp);
989         } else if (!strcmp(argv[1], "-a")) {
990                 struct blkid_struct_probe *pr;
991
992                 cache_load(0);
993                 list_for_each_entry(pr, &devices, list) {
994                         if (strcmp(pr->id->name, "swap"))
995                                 continue;
996                         if (swapon(pr->dev, 0))
997                                 fprintf(stderr, "failed to swapon %s\n", pr->dev);
998                 }
999         } else {
1000                 struct stat s;
1001                 int err;
1002
1003                 if (stat(argv[1], &s) || (!S_ISBLK(s.st_mode) && !S_ISREG(s.st_mode))) {
1004                         fprintf(stderr, "%s is not a block device or file\n", argv[1]);
1005                         return -1;
1006                 }
1007                 err = swapon(argv[1], 0);
1008                 if (err) {
1009                         fprintf(stderr, "failed to swapon %s (%d)\n", argv[1], err);
1010                         return err;
1011                 }
1012         }
1013
1014         return 0;
1015 }
1016
1017 static int main_swapoff(int argc, char **argv)
1018 {
1019         if (argc != 2) {
1020                 fprintf(stderr, "Usage: swapoff [-a] [DEVICE]\n\n\tStop swapping on DEVICE\n -a\tStop swapping on all swap devices\n");
1021                 return -1;
1022         }
1023
1024         if (!strcmp(argv[1], "-a")) {
1025                 FILE *fp = fopen("/proc/swaps", "r");
1026                 char line[256];
1027
1028                 if (!fp) {
1029                         fprintf(stderr, "failed to open /proc/swaps\n");
1030                         return -1;
1031                 }
1032                 fgets(line, sizeof(line), fp);
1033                 while (fgets(line, sizeof(line), fp)) {
1034                         char *end = strchr(line, ' ');
1035                         int err;
1036
1037                         if (!end)
1038                                 continue;
1039                         *end = '\0';
1040                         err = swapoff(line);
1041                         if (err)
1042                                 fprintf(stderr, "failed to swapoff %s (%d)\n", line, err);
1043                 }
1044                 fclose(fp);
1045         } else {
1046                 struct stat s;
1047                 int err;
1048
1049                 if (stat(argv[1], &s) || (!S_ISBLK(s.st_mode) && !S_ISREG(s.st_mode))) {
1050                         fprintf(stderr, "%s is not a block device or file\n", argv[1]);
1051                         return -1;
1052                 }
1053                 err = swapoff(argv[1]);
1054                 if (err) {
1055                         fprintf(stderr, "fsiled to swapoff %s (%d)\n", argv[1], err);
1056                         return err;
1057                 }
1058         }
1059
1060         return 0;
1061 }
1062
1063 int main(int argc, char **argv)
1064 {
1065         char *base = basename(*argv);
1066
1067         umask(0);
1068
1069         if (!strcmp(base, "swapon"))
1070                 return main_swapon(argc, argv);
1071
1072         if (!strcmp(base, "swapoff"))
1073                 return main_swapoff(argc, argv);
1074
1075         if ((argc > 1) && !strcmp(base, "block")) {
1076                 if (!strcmp(argv[1], "info"))
1077                         return main_info(argc, argv);
1078
1079                 if (!strcmp(argv[1], "detect"))
1080                         return main_detect(argc, argv);
1081
1082                 if (!strcmp(argv[1], "hotplug"))
1083                         return main_hotplug(argc, argv);
1084
1085                 if (!strcmp(argv[1], "extroot"))
1086                         return main_extroot(argc, argv);
1087
1088                 if (!strcmp(argv[1], "mount"))
1089                         return main_mount(argc, argv);
1090
1091                 if (!strcmp(argv[1], "umount"))
1092                         return main_umount(argc, argv);
1093         }
1094
1095         fprintf(stderr, "Usage: block <info|mount|umount|detect>\n");
1096
1097         return -1;
1098 }