• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/fstools/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 <getopt.h>
 17 #include <stdio.h>
 18 #include <unistd.h>
 19 #include <syslog.h>
 20 #include <libgen.h>
 21 #include <glob.h>
 22 #include <dirent.h>
 23 #include <stdarg.h>
 24 #include <string.h>
 25 #include <errno.h>
 26 #include <fcntl.h>
 27 #include <limits.h>
 28 
 29 #include <sys/stat.h>
 30 #include <sys/types.h>
 31 #include <sys/swap.h>
 32 #include <sys/mount.h>
 33 #include <sys/wait.h>
 34 #include <sys/sysmacros.h>
 35 
 36 #include <uci.h>
 37 #include <uci_blob.h>
 38 
 39 #include <libubox/avl-cmp.h>
 40 #include <libubox/blobmsg_json.h>
 41 #include <libubox/list.h>
 42 #include <libubox/ulog.h>
 43 #include <libubox/utils.h>
 44 #include <libubox/vlist.h>
 45 #include <libubus.h>
 46 
 47 #include "probe.h"
 48 
 49 #define AUTOFS_MOUNT_PATH       "/tmp/run/blockd/"
 50 
 51 #ifdef UBIFS_EXTROOT
 52 #include "libubi/libubi.h"
 53 #endif
 54 
 55 enum {
 56         TYPE_MOUNT,
 57         TYPE_SWAP,
 58 };
 59 
 60 enum {
 61         TYPE_DEV,
 62         TYPE_HOTPLUG,
 63         TYPE_AUTOFS,
 64 };
 65 
 66 struct mount {
 67         struct vlist_node node;
 68         int type;
 69 
 70         char *target;
 71         char *path;
 72         char *options;
 73         uint32_t flags;
 74         char *uuid;
 75         char *label;
 76         char *device;
 77         int extroot;
 78         int autofs;
 79         int overlay;
 80         int disabled_fsck;
 81         int check_fs;
 82         unsigned int prio;
 83 };
 84 
 85 static struct vlist_tree mounts;
 86 static struct blob_buf b;
 87 static LIST_HEAD(devices);
 88 static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs;
 89 static unsigned int delay_root;
 90 
 91 enum {
 92         CFG_ANON_MOUNT,
 93         CFG_ANON_SWAP,
 94         CFG_AUTO_MOUNT,
 95         CFG_AUTO_SWAP,
 96         CFG_DELAY_ROOT,
 97         CFG_CHECK_FS,
 98         __CFG_MAX
 99 };
100 
101 static const struct blobmsg_policy config_policy[__CFG_MAX] = {
102         [CFG_ANON_SWAP] = { .name = "anon_swap", .type = BLOBMSG_TYPE_INT32 },
103         [CFG_ANON_MOUNT] = { .name = "anon_mount", .type = BLOBMSG_TYPE_INT32 },
104         [CFG_AUTO_SWAP] = { .name = "auto_swap", .type = BLOBMSG_TYPE_INT32 },
105         [CFG_AUTO_MOUNT] = { .name = "auto_mount", .type = BLOBMSG_TYPE_INT32 },
106         [CFG_DELAY_ROOT] = { .name = "delay_root", .type = BLOBMSG_TYPE_INT32 },
107         [CFG_CHECK_FS] = { .name = "check_fs", .type = BLOBMSG_TYPE_INT32 },
108 };
109 
110 enum {
111         MOUNT_UUID,
112         MOUNT_LABEL,
113         MOUNT_ENABLE,
114         MOUNT_TARGET,
115         MOUNT_DEVICE,
116         MOUNT_OPTIONS,
117         MOUNT_AUTOFS,
118         __MOUNT_MAX
119 };
120 
121 static const struct uci_blob_param_list config_attr_list = {
122         .n_params = __CFG_MAX,
123         .params = config_policy,
124 };
125 
126 static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
127         [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
128         [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
129         [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
130         [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
131         [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING },
132         [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
133         [MOUNT_AUTOFS] = { .name = "autofs", .type = BLOBMSG_TYPE_INT32 },
134 };
135 
136 static const struct uci_blob_param_list mount_attr_list = {
137         .n_params = __MOUNT_MAX,
138         .params = mount_policy,
139 };
140 
141 enum {
142         SWAP_ENABLE,
143         SWAP_UUID,
144         SWAP_LABEL,
145         SWAP_DEVICE,
146         SWAP_PRIO,
147         __SWAP_MAX
148 };
149 
150 static const struct blobmsg_policy swap_policy[__SWAP_MAX] = {
151         [SWAP_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
152         [SWAP_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
153         [SWAP_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
154         [SWAP_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
155         [SWAP_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
156 };
157 
158 static const struct uci_blob_param_list swap_attr_list = {
159         .n_params = __SWAP_MAX,
160         .params = swap_policy,
161 };
162 
163 struct mount_flag {
164         const char *name;
165         int32_t flag;
166 };
167 
168 static const struct mount_flag mount_flags[] = {
169         { "sync",               MS_SYNCHRONOUS  },
170         { "async",              ~MS_SYNCHRONOUS },
171         { "dirsync",            MS_DIRSYNC      },
172         { "mand",               MS_MANDLOCK     },
173         { "nomand",             ~MS_MANDLOCK    },
174         { "atime",              ~MS_NOATIME     },
175         { "noatime",            MS_NOATIME      },
176         { "dev",                ~MS_NODEV       },
177         { "nodev",              MS_NODEV        },
178         { "diratime",           ~MS_NODIRATIME  },
179         { "nodiratime",         MS_NODIRATIME   },
180         { "exec",               ~MS_NOEXEC      },
181         { "noexec",             MS_NOEXEC       },
182         { "suid",               ~MS_NOSUID      },
183         { "nosuid",             MS_NOSUID       },
184         { "rw",                 ~MS_RDONLY      },
185         { "ro",                 MS_RDONLY       },
186         { "relatime",           MS_RELATIME     },
187         { "norelatime",         ~MS_RELATIME    },
188         { "strictatime",        MS_STRICTATIME  },
189         { "acl",                MS_POSIXACL     },
190         { "noacl",              ~MS_POSIXACL    },
191         { "nouser_xattr",       MS_NOUSER       },
192         { "user_xattr",         ~MS_NOUSER      },
193 };
194 
195 static char *blobmsg_get_strdup(struct blob_attr *attr)
196 {
197         if (!attr)
198                 return NULL;
199 
200         return strdup(blobmsg_get_string(attr));
201 }
202 
203 static char *blobmsg_get_basename(struct blob_attr *attr)
204 {
205         if (!attr)
206                 return NULL;
207 
208         return strdup(basename(blobmsg_get_string(attr)));
209 }
210 
211 static void parse_mount_options(struct mount *m, char *optstr)
212 {
213         int i;
214         bool is_flag;
215         char *p, *opts, *last;
216 
217         m->flags = 0;
218         m->options = NULL;
219         m->check_fs = 0;
220 
221         if (!optstr || !*optstr)
222                 return;
223 
224         m->options = opts = calloc(1, strlen(optstr) + 1);
225 
226         if (!m->options)
227                 return;
228 
229         p = last = optstr;
230 
231         do {
232                 p = strchr(p, ',');
233 
234                 if (p)
235                         *p++ = 0;
236 
237                 for (i = 0, is_flag = false; i < ARRAY_SIZE(mount_flags); i++) {
238                         if (!strcmp(last, mount_flags[i].name)) {
239                                 if (mount_flags[i].flag < 0)
240                                         m->flags &= (uint32_t)mount_flags[i].flag;
241                                 else
242                                         m->flags |= (uint32_t)mount_flags[i].flag;
243                                 is_flag = true;
244                                 break;
245                         }
246                 }
247 
248                 if (!is_flag && !strcmp(last, "check_fs")) {
249                         m->check_fs = 1;
250                         is_flag = true;
251                 }
252 
253                 if (!is_flag)
254                         opts += sprintf(opts, "%s%s", (opts > m->options) ? "," : "", last);
255 
256                 last = p;
257 
258         } while (p);
259 
260         free(optstr);
261 }
262 
263 static int mount_add(struct uci_section *s)
264 {
265         struct blob_attr *tb[__MOUNT_MAX] = { 0 };
266         struct mount *m;
267 
268         blob_buf_init(&b, 0);
269         uci_to_blob(&b, s, &mount_attr_list);
270         blobmsg_parse(mount_policy, __MOUNT_MAX, tb, blob_data(b.head), blob_len(b.head));
271 
272         if (!tb[MOUNT_LABEL] && !tb[MOUNT_UUID] && !tb[MOUNT_DEVICE])
273                 return -1;
274 
275         if (tb[MOUNT_ENABLE] && !blobmsg_get_u32(tb[MOUNT_ENABLE]))
276                 return -1;
277 
278         m = malloc(sizeof(struct mount));
279         m->type = TYPE_MOUNT;
280         m->uuid = blobmsg_get_strdup(tb[MOUNT_UUID]);
281         m->label = blobmsg_get_strdup(tb[MOUNT_LABEL]);
282         m->target = blobmsg_get_strdup(tb[MOUNT_TARGET]);
283         m->device = blobmsg_get_basename(tb[MOUNT_DEVICE]);
284         if (tb[MOUNT_AUTOFS])
285                 m->autofs = blobmsg_get_u32(tb[MOUNT_AUTOFS]);
286         else
287                 m->autofs = 0;
288         parse_mount_options(m, blobmsg_get_strdup(tb[MOUNT_OPTIONS]));
289 
290         m->overlay = m->extroot = 0;
291         if (m->target && !strcmp(m->target, "/"))
292                 m->extroot = 1;
293         if (m->target && !strcmp(m->target, "/overlay"))
294                 m->extroot = m->overlay = 1;
295 
296         if (m->target && *m->target != '/') {
297                 ULOG_WARN("ignoring mount section %s due to invalid target '%s'\n",
298                           s->e.name, m->target);
299                 free(m);
300                 return -1;
301         }
302 
303         if (m->uuid)
304                 vlist_add(&mounts, &m->node, m->uuid);
305         else if (m->label)
306                 vlist_add(&mounts, &m->node, m->label);
307         else if (m->device)
308                 vlist_add(&mounts, &m->node, m->device);
309 
310         return 0;
311 }
312 
313 static int swap_add(struct uci_section *s)
314 {
315         struct blob_attr *tb[__SWAP_MAX] = { 0 };
316         struct mount *m;
317 
318         blob_buf_init(&b, 0);
319         uci_to_blob(&b, s, &swap_attr_list);
320         blobmsg_parse(swap_policy, __SWAP_MAX, tb, blob_data(b.head), blob_len(b.head));
321 
322         if (!tb[SWAP_UUID] && !tb[SWAP_LABEL] && !tb[SWAP_DEVICE])
323                 return -1;
324 
325         m = malloc(sizeof(struct mount));
326         memset(m, 0, sizeof(struct mount));
327         m->type = TYPE_SWAP;
328         m->uuid = blobmsg_get_strdup(tb[SWAP_UUID]);
329         m->label = blobmsg_get_strdup(tb[SWAP_LABEL]);
330         m->device = blobmsg_get_basename(tb[SWAP_DEVICE]);
331         if (tb[SWAP_PRIO])
332                 m->prio = blobmsg_get_u32(tb[SWAP_PRIO]);
333         if (m->prio)
334                 m->prio = ((m->prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER;
335 
336         if ((!tb[SWAP_ENABLE]) || blobmsg_get_u32(tb[SWAP_ENABLE])) {
337                 /* store complete swap path */
338                 if (tb[SWAP_DEVICE])
339                         m->target = blobmsg_get_strdup(tb[SWAP_DEVICE]);
340 
341                 if (m->uuid)
342                         vlist_add(&mounts, &m->node, m->uuid);
343                 else if (m->label)
344                         vlist_add(&mounts, &m->node, m->label);
345                 else if (m->device)
346                         vlist_add(&mounts, &m->node, m->device);
347         }
348 
349         return 0;
350 }
351 
352 static int global_add(struct uci_section *s)
353 {
354         struct blob_attr *tb[__CFG_MAX] = { 0 };
355 
356         blob_buf_init(&b, 0);
357         uci_to_blob(&b, s, &config_attr_list);
358         blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(b.head), blob_len(b.head));
359 
360         if ((tb[CFG_ANON_MOUNT]) && blobmsg_get_u32(tb[CFG_ANON_MOUNT]))
361                 anon_mount = 1;
362         if ((tb[CFG_ANON_SWAP]) && blobmsg_get_u32(tb[CFG_ANON_SWAP]))
363                 anon_swap = 1;
364 
365         if ((tb[CFG_AUTO_MOUNT]) && blobmsg_get_u32(tb[CFG_AUTO_MOUNT]))
366                 auto_mount = 1;
367         if ((tb[CFG_AUTO_SWAP]) && blobmsg_get_u32(tb[CFG_AUTO_SWAP]))
368                 auto_swap = 1;
369 
370         if (tb[CFG_DELAY_ROOT])
371                 delay_root = blobmsg_get_u32(tb[CFG_DELAY_ROOT]);
372 
373         if ((tb[CFG_CHECK_FS]) && blobmsg_get_u32(tb[CFG_CHECK_FS]))
374                 check_fs = 1;
375 
376         return 0;
377 }
378 
379 static struct mount* find_swap(const char *uuid, const char *label, const char *device)
380 {
381         struct mount *m;
382 
383         vlist_for_each_element(&mounts, m, node) {
384                 if (m->type != TYPE_SWAP)
385                         continue;
386                 if (uuid && m->uuid && !strcasecmp(m->uuid, uuid))
387                         return m;
388                 if (label && m->label && !strcmp(m->label, label))
389                         return m;
390                 if (device && m->device && !strcmp(m->device, device))
391                         return m;
392         }
393 
394         return NULL;
395 }
396 
397 static struct mount* find_block(const char *uuid, const char *label, const char *device,
398                                 const char *target)
399 {
400         struct mount *m;
401 
402         vlist_for_each_element(&mounts, m, node) {
403                 if (m->type != TYPE_MOUNT)
404                         continue;
405                 if (m->uuid && uuid && !strcasecmp(m->uuid, uuid))
406                         return m;
407                 if (m->label && label && !strcmp(m->label, label))
408                         return m;
409                 if (m->target && target && !strcmp(m->target, target))
410                         return m;
411                 if (m->device && device && !strcmp(m->device, device))
412                         return m;
413         }
414 
415         return NULL;
416 }
417 
418 static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new,
419                           struct vlist_node *node_old)
420 {
421 }
422 
423 static struct uci_package * config_try_load(struct uci_context *ctx, char *path)
424 {
425         char *file = basename(path);
426         char *dir = dirname(path);
427         char *err;
428         struct uci_package *pkg;
429 
430         uci_set_confdir(ctx, dir);
431         ULOG_INFO("attempting to load %s/%s\n", dir, file);
432 
433         if (uci_load(ctx, file, &pkg)) {
434                 uci_get_errorstr(ctx, &err, file);
435                 ULOG_ERR("unable to load configuration (%s)\n", err);
436 
437                 free(err);
438                 return NULL;
439         }
440 
441         return pkg;
442 }
443 
444 static int config_load(char *cfg)
445 {
446         struct uci_context *ctx = uci_alloc_context();
447         struct uci_package *pkg = NULL;
448         struct uci_element *e;
449         char path[64];
450 
451         vlist_init(&mounts, avl_strcmp, mounts_update);
452 
453         if (cfg) {
454                 snprintf(path, sizeof(path), "%s/upper/etc/config/fstab", cfg);
455                 pkg = config_try_load(ctx, path);
456 
457                 if (!pkg) {
458                         snprintf(path, sizeof(path), "%s/etc/config/fstab", cfg);
459                         pkg = config_try_load(ctx, path);
460                 }
461         }
462 
463         if (!pkg) {
464                 snprintf(path, sizeof(path), "/etc/config/fstab");
465                 pkg = config_try_load(ctx, path);
466         }
467 
468         if (!pkg) {
469                 ULOG_ERR("no usable configuration\n");
470                 return -1;
471         }
472 
473         vlist_update(&mounts);
474         uci_foreach_element(&pkg->sections, e) {
475                 struct uci_section *s = uci_to_section(e);
476 
477                 if (!strcmp(s->type, "mount"))
478                         mount_add(s);
479                 if (!strcmp(s->type, "swap"))
480                         swap_add(s);
481                 if (!strcmp(s->type, "global"))
482                         global_add(s);
483         }
484         vlist_flush(&mounts);
485 
486         return 0;
487 }
488 
489 static bool mtdblock_is_nand(char *mtdnum)
490 {
491         char tmppath[64];
492         char buf[16];
493         FILE *fp;
494 
495         snprintf(tmppath, sizeof(tmppath) - 1, "/sys/class/mtd/mtd%s/type", mtdnum);
496         fp = fopen(tmppath, "r");
497         if (!fp)
498                 return false;
499 
500         if (!fgets(buf, sizeof(buf), fp)) {
501                 fclose(fp);
502                 return false;
503         }
504         fclose(fp);
505         buf[sizeof(buf) - 1] = '\0'; /* make sure buf is 0-terminated */
506         buf[strlen(buf) - 1] = '\0'; /* strip final char (newline) */
507 
508         if (strcmp(buf, "nand"))
509                 return false;
510 
511         /*
512          * --- CUT HERE ---
513          * Keep probing rootfs and rootfs_data in the meantime to not break
514          * devices using JFFS2 on NAND but only trigger the kernel warnings.
515          * Remove this once all devices using JFFS2 and squashfs directly on
516          * NAND have been converted to UBI.
517          */
518         snprintf(tmppath, sizeof(tmppath) - 1, "/sys/class/mtd/mtd%s/name", mtdnum);
519         fp = fopen(tmppath, "r");
520         if (!fp)
521                 return false;
522 
523         if (!fgets(buf, sizeof(buf), fp)) {
524                 fclose(fp);
525                 return false;
526         }
527         fclose(fp);
528         buf[sizeof(buf) - 1] = '\0'; /* make sure buf is 0-terminated */
529         buf[strlen(buf) - 1] = '\0'; /* strip final char (newline) */
530 
531         /* only return true if name differs from 'rootfs' and 'rootfs_data' */
532         if (strcmp(buf, "rootfs") && strcmp(buf, "rootfs_data"))
533                 return true;
534 
535         /* --- CUT HERE --- */
536         return false;
537 }
538 
539 static struct probe_info* _probe_path(char *path)
540 {
541         struct probe_info *pr, *epr;
542         char tmppath[64];
543 
544         if (!strncmp(path, "/dev/mtdblock", 13) && mtdblock_is_nand(path + 13))
545                 return NULL;
546 
547         pr = probe_path(path);
548         if (!pr)
549                 return NULL;
550 
551         if (path[5] == 'u' && path[6] == 'b' && path[7] == 'i' &&
552             path[8] >= '' && path[8] <= '9' ) {
553                 /* skip ubi device if not UBIFS (as it requires ubiblock) */
554                 if (strcmp("ubifs", pr->type))
555                         return NULL;
556 
557                 /* skip ubi device if ubiblock device is present */
558                 snprintf(tmppath, sizeof(tmppath), "/dev/ubiblock%s", path + 8);
559                 list_for_each_entry(epr, &devices, list)
560                         if (!strcmp(epr->dev, tmppath))
561                                 return NULL;
562         }
563 
564         return pr;
565 }
566 
567 static int _cache_load(const char *path)
568 {
569         int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
570         int j;
571         glob_t gl;
572 
573         if (glob(path, gl_flags, NULL, &gl) < 0)
574                 return -1;
575 
576         for (j = 0; j < gl.gl_pathc; j++) {
577                 struct probe_info *pr = _probe_path(gl.gl_pathv[j]);
578                 if (pr)
579                         list_add_tail(&pr->list, &devices);
580         }
581 
582         globfree(&gl);
583 
584         return 0;
585 }
586 
587 static void cache_load(int mtd)
588 {
589         if (mtd) {
590                 _cache_load("/dev/mtdblock*");
591                 _cache_load("/dev/ubiblock*");
592                 _cache_load("/dev/ubi[0-9]*");
593         }
594         _cache_load("/dev/loop*");
595         _cache_load("/dev/mmcblk*");
596         _cache_load("/dev/sd*");
597         _cache_load("/dev/hd*");
598         _cache_load("/dev/md*");
599         _cache_load("/dev/nvme*");
600         _cache_load("/dev/vd*");
601         _cache_load("/dev/xvd*");
602         _cache_load("/dev/dm-*");
603         _cache_load("/dev/fit*");
604 }
605 
606 
607 static struct probe_info* find_block_info(char *uuid, char *label, char *path)
608 {
609         struct probe_info *pr = NULL;
610 
611         if (uuid)
612                 list_for_each_entry(pr, &devices, list)
613                         if (pr->uuid && !strcasecmp(pr->uuid, uuid))
614                                 return pr;
615 
616         if (label)
617                 list_for_each_entry(pr, &devices, list)
618                         if (pr->label && !strcmp(pr->label, label))
619                                 return pr;
620 
621         if (path)
622                 list_for_each_entry(pr, &devices, list)
623                         if (pr->dev && !strcmp(basename(pr->dev), basename(path)))
624                                 return pr;
625 
626         return NULL;
627 }
628 
629 static char* find_mount_point(char *block)
630 {
631         FILE *fp = fopen("/proc/self/mountinfo", "r");
632         static char line[256];
633         char *point = NULL, *pos, *tmp, *cpoint, *devname;
634         struct stat s;
635         int rstat;
636         unsigned int minor, major;
637 
638         if (!fp)
639                 return NULL;
640 
641         rstat = stat(block, &s);
642 
643         while (fgets(line, sizeof(line), fp)) {
644                 pos = strchr(line, ' ');
645                 if (!pos)
646                         continue;
647 
648                 pos = strchr(pos + 1, ' ');
649                 if (!pos)
650                         continue;
651 
652                 tmp = ++pos;
653                 pos = strchr(pos, ':');
654                 if (!pos)
655                         continue;
656 
657                 *pos = '\0';
658                 major = atoi(tmp);
659                 tmp = ++pos;
660                 pos = strchr(pos, ' ');
661                 if (!pos)
662                         continue;
663 
664                 *pos = '\0';
665                 minor = atoi(tmp);
666                 pos = strchr(pos + 1, ' ');
667                 if (!pos)
668                         continue;
669                 tmp = ++pos;
670 
671                 pos = strchr(pos, ' ');
672                 if (!pos)
673                         continue;
674                 *pos = '\0';
675                 cpoint = tmp;
676 
677                 pos = strchr(pos + 1, ' ');
678                 if (!pos)
679                         continue;
680 
681                 pos = strchr(pos + 1, ' ');
682                 if (!pos)
683                         continue;
684 
685                 pos = strchr(pos + 1, ' ');
686                 if (!pos)
687                         continue;
688 
689                 tmp = ++pos;
690                 pos = strchr(pos, ' ');
691                 if (!pos)
692                         continue;
693 
694                 *pos = '\0';
695                 devname = tmp;
696                 if (!strcmp(block, devname)) {
697                         point = strdup(cpoint);
698                         break;
699                 }
700 
701                 if (rstat)
702                         continue;
703 
704                 if (!S_ISBLK(s.st_mode))
705                         continue;
706 
707                 if (major == major(s.st_rdev) &&
708                     minor == minor(s.st_rdev)) {
709                         point = strdup(cpoint);
710                         break;
711                 }
712         }
713 
714         fclose(fp);
715 
716         return point;
717 }
718 
719 static int print_block_uci(struct probe_info *pr)
720 {
721         if (!strcmp(pr->type, "swap")) {
722                 printf("config 'swap'\n");
723         } else {
724                 char *mp = find_mount_point(pr->dev);
725 
726                 printf("config 'mount'\n");
727                 if (mp) {
728                         printf("\toption\ttarget\t'%s'\n", mp);
729                         free(mp);
730                 } else {
731                         printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
732                 }
733         }
734         if (pr->uuid)
735                 printf("\toption\tuuid\t'%s'\n", pr->uuid);
736         else
737                 printf("\toption\tdevice\t'%s'\n", pr->dev);
738         printf("\toption\tenabled\t''\n\n");
739 
740         return 0;
741 }
742 
743 static int print_block_info(struct probe_info *pr)
744 {
745         static char *mp;
746 
747         mp = find_mount_point(pr->dev);
748         printf("%s:", pr->dev);
749         if (pr->uuid)
750                 printf(" UUID=\"%s\"", pr->uuid);
751 
752         if (pr->label)
753                 printf(" LABEL=\"%s\"", pr->label);
754 
755         if (pr->version)
756                 printf(" VERSION=\"%s\"", pr->version);
757 
758         if (mp) {
759                 printf(" MOUNT=\"%s\"", mp);
760                 free(mp);
761         }
762 
763         printf(" TYPE=\"%s\"\n", pr->type);
764 
765         return 0;
766 }
767 
768 static void check_filesystem(struct probe_info *pr)
769 {
770         pid_t pid;
771         struct stat statbuf;
772         const char *e2fsck = "/usr/sbin/e2fsck";
773         const char *f2fsck = "/usr/sbin/fsck.f2fs";
774         const char *fatfsck = "/usr/sbin/fsck.fat";
775         const char *btrfsck = "/usr/bin/btrfsck";
776         const char *ntfsck = "/usr/bin/ntfsfix";
777         const char *ckfs;
778 
779         /* UBIFS does not need stuff like fsck */
780         if (!strncmp(pr->type, "ubifs", 5))
781                 return;
782 
783         if (!strncmp(pr->type, "vfat", 4)) {
784                 ckfs = fatfsck;
785         } else if (!strncmp(pr->type, "f2fs", 4)) {
786                 ckfs = f2fsck;
787         } else if (!strncmp(pr->type, "ext", 3)) {
788                 ckfs = e2fsck;
789         } else if (!strncmp(pr->type, "btrfs", 5)) {
790                 ckfs = btrfsck;
791         } else if (!strncmp(pr->type, "ntfs", 4)) {
792                 ckfs = ntfsck;
793         } else {
794                 ULOG_ERR("check_filesystem: %s is not supported\n", pr->type);
795                 return;
796         }
797 
798         if (stat(ckfs, &statbuf) < 0) {
799                 ULOG_ERR("check_filesystem: %s not found\n", ckfs);
800                 return;
801         }
802 
803         pid = fork();
804         if (!pid) {
805                 if(!strncmp(pr->type, "f2fs", 4)) {
806                         execl(ckfs, ckfs, "-a", pr->dev, NULL);
807                         exit(EXIT_FAILURE);
808                 } else if(!strncmp(pr->type, "btrfs", 5)) {
809                         execl(ckfs, ckfs, "--repair", pr->dev, NULL);
810                         exit(EXIT_FAILURE);
811                 } else if(!strncmp(pr->type, "ntfs", 4)) {
812                         execl(ckfs, ckfs, "-b", pr->dev, NULL);
813                         exit(EXIT_FAILURE);
814                 } else {
815                         execl(ckfs, ckfs, "-p", pr->dev, NULL);
816                         exit(EXIT_FAILURE);
817                 }
818         } else if (pid > 0) {
819                 int status;
820 
821                 waitpid(pid, &status, 0);
822                 if (WIFEXITED(status) && WEXITSTATUS(status))
823                         ULOG_ERR("check_filesystem: %s returned %d\n", ckfs, WEXITSTATUS(status));
824                 if (WIFSIGNALED(status))
825                         ULOG_ERR("check_filesystem: %s terminated by %s\n", ckfs, strsignal(WTERMSIG(status)));
826         }
827 }
828 
829 static void handle_swapfiles(bool on)
830 {
831         struct stat s;
832         struct mount *m;
833         struct probe_info *pr;
834 
835         vlist_for_each_element(&mounts, m, node)
836         {
837                 if (m->type != TYPE_SWAP || !m->target)
838                         continue;
839 
840                 if (stat(m->target, &s) || !S_ISREG(s.st_mode))
841                         continue;
842 
843                 pr = _probe_path(m->target);
844 
845                 if (!pr)
846                         continue;
847 
848                 if (!strcmp(pr->type, "swap")) {
849                         if (on)
850                                 swapon(pr->dev, m->prio);
851                         else
852                                 swapoff(pr->dev);
853                 }
854 
855                 free(pr);
856         }
857 }
858 
859 static void to_devnull(int fd)
860 {
861         int devnull = open("/dev/null", fd ? O_WRONLY : O_RDONLY);
862 
863         if (devnull >= 0)
864                 dup2(devnull, fd);
865 
866         if (devnull > STDERR_FILENO)
867                 close(devnull);
868 }
869 
870 static int exec_mount(const char *source, const char *target,
871                       const char *fstype, const char *options)
872 {
873         pid_t pid;
874         struct stat s;
875         FILE *mount_fd;
876         int err, status, pfds[2];
877         char errmsg[128], cmd[sizeof("/sbin/mount.XXXXXXXXXXXXXXXX\0")];
878 
879         snprintf(cmd, sizeof(cmd), "/sbin/mount.%s", fstype);
880 
881         if (stat(cmd, &s) < 0 || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR)) {
882                 ULOG_ERR("No \"mount.%s\" utility available\n", fstype);
883                 return -1;
884         }
885 
886         if (pipe(pfds) < 0)
887                 return -1;
888 
889         fcntl(pfds[0], F_SETFD, fcntl(pfds[0], F_GETFD) | FD_CLOEXEC);
890         fcntl(pfds[1], F_SETFD, fcntl(pfds[1], F_GETFD) | FD_CLOEXEC);
891 
892         pid = vfork();
893 
894         switch (pid) {
895         case -1:
896                 close(pfds[0]);
897                 close(pfds[1]);
898 
899                 return -1;
900 
901         case 0:
902                 to_devnull(STDIN_FILENO);
903                 to_devnull(STDOUT_FILENO);
904 
905                 dup2(pfds[1], STDERR_FILENO);
906                 close(pfds[0]);
907                 close(pfds[1]);
908 
909                 if (options && *options)
910                         execl(cmd, cmd, "-o", options, source, target, NULL);
911                 else
912                         execl(cmd, cmd, source, target, NULL);
913 
914                 return -1;
915 
916         default:
917                 close(pfds[1]);
918 
919                 mount_fd = fdopen(pfds[0], "r");
920 
921                 while (fgets(errmsg, sizeof(errmsg), mount_fd))
922                         ULOG_ERR("mount.%s: %s", fstype, errmsg);
923 
924                 fclose(mount_fd);
925 
926                 err = waitpid(pid, &status, 0);
927 
928                 if (err != -1) {
929                         if (status != 0) {
930                                 ULOG_ERR("mount.%s: failed with status %d\n", fstype, status);
931                                 errno = EINVAL;
932                                 err = -1;
933                         } else {
934                                 errno = 0;
935                                 err = 0;
936                         }
937                 }
938 
939                 break;
940         }
941 
942         return err;
943 }
944 
945 static const char * const ntfs_fs[] = { "ntfs3", "ntfs-3g", "antfs", "ntfs" };
946 
947 static int handle_mount(const char *source, const char *target,
948                         const char *fstype, struct mount *m)
949 {
950         size_t mount_opts_len;
951         char *mount_opts = NULL, *ptr;
952         const char * const *filesystems;
953         int err = -EINVAL;
954         size_t count;
955         int i;
956 
957         if (!strcmp(fstype, "ntfs")) {
958                 filesystems = ntfs_fs;
959                 count = ARRAY_SIZE(ntfs_fs);
960         } else {
961                 filesystems = &fstype;
962                 count = 1;
963         }
964 
965         for (i = 0; i < count; i++) {
966                 const char *fs = filesystems[i];
967 
968                 err = mount(source, target, fs, m ? m->flags : 0,
969                             (m && m->options) ? m->options : "");
970                 if (!err || errno != ENODEV)
971                         break;
972         }
973 
974         /* Requested file system type is not available in kernel,
975            attempt to call mount helper. */
976         if (err == -1 && errno == ENODEV) {
977                 if (m) {
978                         /* Convert mount flags back into string representation,
979                            first calculate needed length of string buffer... */
980                         mount_opts_len = 1 + (m->options ? strlen(m->options) : 0);
981 
982                         for (i = 0; i < ARRAY_SIZE(mount_flags); i++)
983                                 if ((mount_flags[i].flag > 0) &&
984                                     (mount_flags[i].flag < INT_MAX) &&
985                                     (m->flags & (uint32_t)mount_flags[i].flag))
986                                         mount_opts_len += strlen(mount_flags[i].name) + 1;
987 
988                         /* ... then now allocate and fill it ... */
989                         ptr = mount_opts = calloc(1, mount_opts_len);
990 
991                         if (!ptr) {
992                                 errno = ENOMEM;
993                                 return -1;
994                         }
995 
996                         if (m->options)
997                                 ptr += sprintf(ptr, "%s,", m->options);
998 
999                         for (i = 0; i < ARRAY_SIZE(mount_flags); i++)
1000                                 if ((mount_flags[i].flag > 0) &&
1001                                     (mount_flags[i].flag < INT_MAX) &&
1002                                     (m->flags & (uint32_t)mount_flags[i].flag))
1003                                         ptr += sprintf(ptr, "%s,", mount_flags[i].name);
1004 
1005                         mount_opts[mount_opts_len - 1] = 0;
1006                 }
1007 
1008                 /* ... and now finally invoke the external mount program */
1009                 for (i = 0; i < count; i++) {
1010                         const char *fs = filesystems[i];
1011 
1012                         err = exec_mount(source, target, fs, mount_opts);
1013                         if (!err)
1014                                 break;
1015                 }
1016         }
1017 
1018         free(mount_opts);
1019 
1020         return err;
1021 }
1022 
1023 static int blockd_notify(const char *method, char *device, struct mount *m,
1024                          struct probe_info *pr)
1025 {
1026         struct ubus_context *ctx = ubus_connect(NULL);
1027         uint32_t id;
1028         int err;
1029 
1030         if (!ctx)
1031                 return -ENXIO;
1032 
1033         if (!ubus_lookup_id(ctx, "block", &id)) {
1034                 struct blob_buf buf = { 0 };
1035                 char *d = strrchr(device, '/');
1036 
1037                 if (d)
1038                         d++;
1039                 else
1040                         d = device;
1041 
1042                 blob_buf_init(&buf, 0);
1043 
1044                 if (m) {
1045 
1046                         blobmsg_add_string(&buf, "device", d);
1047                         if (m->uuid)
1048                                 blobmsg_add_string(&buf, "uuid", m->uuid);
1049                         if (m->label)
1050                                 blobmsg_add_string(&buf, "label", m->label);
1051                         if (m->target)
1052                                 blobmsg_add_string(&buf, "target", m->target);
1053                         if (m->options)
1054                                 blobmsg_add_string(&buf, "options", m->options);
1055                         if (m->autofs)
1056                                 blobmsg_add_u32(&buf, "autofs", m->autofs);
1057                         if (pr->type)
1058                                 blobmsg_add_string(&buf, "type", pr->type);
1059                         if (pr->version)
1060                                 blobmsg_add_string(&buf, "version", pr->version);
1061                 } else if (pr) {
1062                         blobmsg_add_string(&buf, "device", d);
1063                         if (pr->uuid)
1064                                 blobmsg_add_string(&buf, "uuid", pr->uuid);
1065                         if (pr->label)
1066                                 blobmsg_add_string(&buf, "label", pr->label);
1067                         if (pr->type)
1068                                 blobmsg_add_string(&buf, "type", pr->type);
1069                         if (pr->version)
1070                                 blobmsg_add_string(&buf, "version", pr->version);
1071                         blobmsg_add_u32(&buf, "anon", 1);
1072                 } else {
1073                         blobmsg_add_string(&buf, "device", d);
1074                         blobmsg_add_u32(&buf, "remove", 1);
1075                 }
1076 
1077                 err = ubus_invoke(ctx, id, method, buf.head, NULL, NULL, 3000);
1078         } else {
1079                 err = -ENOENT;
1080         }
1081 
1082         ubus_free(ctx);
1083 
1084         return err;
1085 }
1086 
1087 /* The autofs trigger only fires for devices blockd registered for autofs, so a
1088  * missing fstab section just means the device was injected at runtime over ubus
1089  * (e.g. by uvol). Mount it via the regular autofs path; blockd's symlink already
1090  * exposes it at the registered target. */
1091 static struct mount* autofs_runtime_block(const char *device)
1092 {
1093         static struct mount m;
1094 
1095         memset(&m, 0, sizeof(m));
1096         m.type = TYPE_MOUNT;
1097         m.autofs = 1;
1098         m.device = (char *)device;
1099 
1100         return &m;
1101 }
1102 
1103 static int mount_device(struct probe_info *pr, const char *options, int type)
1104 {
1105         struct mount *m;
1106         struct stat st;
1107         char *_target = NULL;
1108         char *target;
1109         char *device;
1110         char *mp;
1111         int err;
1112 
1113         if (!pr)
1114                 return -1;
1115 
1116         device = basename(pr->dev);
1117 
1118         if (!strcmp(pr->type, "swap")) {
1119                 if ((type == TYPE_HOTPLUG) && !auto_swap)
1120                         return -1;
1121                 m = find_swap(pr->uuid, pr->label, device);
1122                 if (m || anon_swap)
1123                         swapon(pr->dev, (m) ? (m->prio) : (0));
1124 
1125                 return 0;
1126         }
1127 
1128         m = find_block(pr->uuid, pr->label, device, NULL);
1129         if (!m && type == TYPE_AUTOFS) {
1130                 m = autofs_runtime_block(device);
1131                 /* runtime volumes have no fstab entry; honour the options the
1132                  * registrant declared over blockd (e.g. ro for a ro volume) */
1133                 if (m && options)
1134                         parse_mount_options(m, strdup(options));
1135         }
1136         if (m && m->extroot)
1137                 return -1;
1138 
1139         mp = find_mount_point(pr->dev);
1140         if (mp) {
1141                 if (m && m->type == TYPE_MOUNT && m->target && strcmp(m->target, mp)) {
1142                         ULOG_ERR("%s is already mounted on %s\n", pr->dev, mp);
1143                         err = -1;
1144                 } else
1145                         err = 0;
1146                 free(mp);
1147                 return err;
1148         }
1149 
1150         if (type == TYPE_HOTPLUG)
1151                 blockd_notify("hotplug", device, m, pr);
1152 
1153         /* Check if device should be mounted & set the target directory */
1154         if (m) {
1155                 switch (type) {
1156                 case TYPE_HOTPLUG:
1157                         if (m->autofs)
1158                                 return 0;
1159                         if (!auto_mount)
1160                                 return -1;
1161                         break;
1162                 case TYPE_AUTOFS:
1163                         if (!m->autofs)
1164                                 return -1;
1165                         break;
1166                 case TYPE_DEV:
1167                         if (m->autofs)
1168                                 return -1;
1169                         break;
1170                 }
1171 
1172                 if (m->autofs) {
1173                         if (asprintf(&_target, "/tmp/run/blockd/%s", device) == -1)
1174                                 exit(ENOMEM);
1175 
1176                         target = _target;
1177                 } else if (m->target) {
1178                         target = m->target;
1179                 } else {
1180                         if (asprintf(&_target, "/mnt/%s", device) == -1)
1181                                 exit(ENOMEM);
1182 
1183                         target = _target;
1184                 }
1185         } else if (anon_mount) {
1186                 if (asprintf(&_target, "/mnt/%s", device) == -1)
1187                         exit(ENOMEM);
1188 
1189                 target = _target;
1190         } else {
1191                 /* No reason to mount this device */
1192                 return 0;
1193         }
1194 
1195         /* Mount the device */
1196 
1197         if (check_fs || (m && m->check_fs))
1198                 check_filesystem(pr);
1199 
1200         mkdir_p(target, 0755);
1201         if (!lstat(target, &st) && S_ISLNK(st.st_mode))
1202                 unlink(target);
1203 
1204         err = handle_mount(pr->dev, target, pr->type, m);
1205         if (err) {
1206                 ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
1207                                 pr->dev, pr->type, target, errno);
1208 
1209                 if (_target)
1210                         free(_target);
1211 
1212                 return err;
1213         }
1214 
1215         if (_target)
1216                 free(_target);
1217 
1218         handle_swapfiles(true);
1219 
1220         if (type != TYPE_AUTOFS)
1221                 blockd_notify("mount", device, NULL, NULL);
1222 
1223         return 0;
1224 }
1225 
1226 static int umount_device(char *path, int type, bool all)
1227 {
1228         char *mp, *devpath;
1229         int err;
1230 
1231         if (strlen(path) > 5 && !strncmp("/dev/", path, 5)) {
1232                 mp = find_mount_point(path);
1233         } else {
1234                 devpath = malloc(strlen(path) + 6);
1235                 strcpy(devpath, "/dev/");
1236                 strcat(devpath, path);
1237                 mp = find_mount_point(devpath);
1238                 free(devpath);
1239         }
1240 
1241         if (!mp)
1242                 return -1;
1243         if (!strcmp(mp, "/") && !all) {
1244                 free(mp);
1245                 return 0;
1246         }
1247         if (type != TYPE_AUTOFS)
1248                 blockd_notify("umount", basename(path), NULL, NULL);
1249 
1250         err = umount2(mp, MNT_DETACH);
1251         if (err) {
1252                 ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n", path, mp,
1253                          errno);
1254         } else {
1255                 ULOG_INFO("unmounted %s (%s)\n", path, mp);
1256                 rmdir(mp);
1257         }
1258 
1259         free(mp);
1260         return err;
1261 }
1262 
1263 static int mount_action(char *action, char *device, const char *options, int type)
1264 {
1265         char *path = NULL;
1266         struct probe_info *pr;
1267 
1268         if (!action || !device)
1269                 return -1;
1270 
1271         if (!strcmp(action, "remove")) {
1272                 if (type == TYPE_HOTPLUG)
1273                         blockd_notify("hotplug", device, NULL, NULL);
1274 
1275                 umount_device(device, type, true);
1276 
1277                 return 0;
1278         } else if (strcmp(action, "add")) {
1279                 ULOG_ERR("Unkown action %s\n", action);
1280 
1281                 return -1;
1282         }
1283 
1284         if (config_load(NULL))
1285                 return -1;
1286 
1287         cache_load(1);
1288 
1289         list_for_each_entry(pr, &devices, list)
1290                 if (!strcmp(basename(pr->dev), device))
1291                         path = pr->dev;
1292 
1293         if (!path)
1294                 return -1;
1295 
1296         return mount_device(find_block_info(NULL, NULL, path), options, type);
1297 }
1298 
1299 static int main_hotplug(int argc, char **argv)
1300 {
1301         return mount_action(getenv("ACTION"), getenv("DEVNAME"), NULL, TYPE_HOTPLUG);
1302 }
1303 
1304 static int main_autofs(int argc, char **argv)
1305 {
1306         int err = 0;
1307 
1308         if (argc < 3)
1309                 return -1;
1310 
1311         if (!strcmp(argv[2], "start")) {
1312                 struct probe_info *pr;
1313 
1314                 if (config_load(NULL))
1315                         return -1;
1316 
1317                 cache_load(1);
1318                 list_for_each_entry(pr, &devices, list) {
1319                         struct mount *m;
1320                         char *mp;
1321 
1322                         if (!strcmp(pr->type, "swap"))
1323                                 continue;
1324 
1325                         m = find_block(pr->uuid, pr->label, NULL, NULL);
1326                         if (m && m->extroot)
1327                                 continue;
1328 
1329                         blockd_notify("hotplug", pr->dev, m, pr);
1330                         if ((!m || !m->autofs) && (mp = find_mount_point(pr->dev))) {
1331                                 blockd_notify("mount", pr->dev, NULL, NULL);
1332                                 free(mp);
1333                         }
1334                 }
1335         } else {
1336                 if (argc < 4)
1337                         return -EINVAL;
1338 
1339                 err = mount_action(argv[2], argv[3], (argc > 4) ? argv[4] : NULL, TYPE_AUTOFS);
1340         }
1341 
1342         if (err) {
1343                 ULOG_ERR("autofs: \"%s\" action has failed: %d\n", argv[2], err);
1344         }
1345 
1346         return err;
1347 }
1348 
1349 static int find_block_mtd(char *name, char *part, int plen)
1350 {
1351         FILE *fp = fopen("/proc/mtd", "r");
1352         static char line[256];
1353         char *index = NULL;
1354 
1355         if(!fp)
1356                 return -1;
1357 
1358         while (!index && fgets(line, sizeof(line), fp)) {
1359                 if (strstr(line, name)) {
1360                         char *eol = strstr(line, ":");
1361 
1362                         if (!eol)
1363                                 continue;
1364 
1365                         *eol = '\0';
1366                         index = &line[3];
1367                 }
1368         }
1369 
1370         fclose(fp);
1371 
1372         if (!index)
1373                 return -1;
1374 
1375         snprintf(part, plen, "/dev/mtdblock%s", index);
1376 
1377         return 0;
1378 }
1379 
1380 #ifdef UBIFS_EXTROOT
1381 static int find_ubi_vol(libubi_t libubi, char *name, int *dev_num, int *vol_id)
1382 {
1383         int dev = 0;
1384 
1385         while (ubi_dev_present(libubi, dev))
1386         {
1387                 struct ubi_dev_info dev_info;
1388                 struct ubi_vol_info vol_info;
1389 
1390                 if (ubi_get_dev_info1(libubi, dev++, &dev_info))
1391                         continue;
1392                 if (ubi_get_vol_info1_nm(libubi, dev_info.dev_num, name, &vol_info))
1393                         continue;
1394 
1395                 *dev_num = dev_info.dev_num;
1396                 *vol_id = vol_info.vol_id;
1397 
1398                 return 0;
1399         }
1400 
1401         return -1;
1402 }
1403 
1404 static int find_block_ubi(libubi_t libubi, char *name, char *part, int plen)
1405 {
1406         int dev_num;
1407         int vol_id;
1408         int err = -1;
1409 
1410         err = find_ubi_vol(libubi, name, &dev_num, &vol_id);
1411         if (!err)
1412                 snprintf(part, plen, "/dev/ubi%d_%d", dev_num, vol_id);
1413 
1414         return err;
1415 }
1416 
1417 static int find_block_ubi_RO(libubi_t libubi, char *name, char *part, int plen)
1418 {
1419         int dev_num;
1420         int vol_id;
1421         int err = -1;
1422 
1423         err = find_ubi_vol(libubi, name, &dev_num, &vol_id);
1424         if (!err)
1425                 snprintf(part, plen, "/dev/ubiblock%d_%d", dev_num, vol_id);
1426 
1427         return err;
1428 }
1429 #endif
1430 
1431 static int find_dev(const char *path, char *buf, int len)
1432 {
1433         DIR *d;
1434         dev_t root;
1435         struct stat s;
1436         struct dirent *e;
1437 
1438         if (stat(path, &s))
1439                 return -1;
1440 
1441         if (!(d = opendir("/dev")))
1442                 return -1;
1443 
1444         root = s.st_dev;
1445 
1446         while ((e = readdir(d)) != NULL) {
1447                 snprintf(buf, len, "/dev/%s", e->d_name);
1448 
1449                 if (stat(buf, &s) || s.st_rdev != root)
1450                         continue;
1451 
1452                 closedir(d);
1453                 return 0;
1454         }
1455 
1456         closedir(d);
1457         return -1;
1458 }
1459 
1460 static int find_root_dev(char *buf, int len)
1461 {
1462         int err = find_dev("/", buf, len);
1463         if (err)
1464             err = find_dev("/rom", buf, len);
1465 
1466         return err;
1467 }
1468 
1469 static int test_fs_support(const char *name)
1470 {
1471         char line[128], *p;
1472         int rv = -1;
1473         FILE *f;
1474 
1475         if ((f = fopen("/proc/filesystems", "r")) != NULL) {
1476                 while (fgets(line, sizeof(line), f)) {
1477                         p = strtok(line, "\t\n");
1478 
1479                         if (p && !strcmp(p, "nodev"))
1480                                 p = strtok(NULL, "\t\n");
1481 
1482                         if (p && !strcmp(p, name)) {
1483                                 rv = 0;
1484                                 break;
1485                         }
1486                 }
1487                 fclose(f);
1488         }
1489 
1490         return rv;
1491 }
1492 
1493 /**
1494  * Check if mounted partition is a valid extroot
1495  *
1496  * @path target mount point
1497  *
1498  * Valid extroot partition has to contain /etc/.extroot-uuid with UUID of root
1499  * device. This function reads UUID and verifies it OR writes UUID to
1500  * .extroot-uuid if it doesn't exist yet (first extroot usage).
1501  */
1502 static int check_extroot(char *path)
1503 {
1504         struct probe_info *pr = NULL;
1505         struct probe_info *tmp;
1506         struct stat s;
1507         char uuid[64] = { 0 };
1508         char devpath[32];
1509         char tag[64];
1510         FILE *fp;
1511         int err;
1512 
1513         snprintf(tag, sizeof(tag), "%s/etc/.extroot-default", path);
1514         if (stat(tag, &s))
1515                 return 0;
1516 
1517         err = find_root_dev(devpath, sizeof(devpath));
1518         if (err)
1519                 err = find_block_mtd("\"rootfs\"", devpath, sizeof(devpath));
1520 #ifdef UBIFS_EXTROOT
1521         if (err) {
1522                 libubi_t libubi;
1523 
1524                 libubi = libubi_open();
1525                 err = find_block_ubi_RO(libubi, "rootfs", devpath, sizeof(devpath));
1526                 libubi_close(libubi);
1527         }
1528 #endif
1529         if (err) {
1530                 ULOG_ERR("extroot: unable to determine root device\n");
1531                 return -1;
1532         }
1533 
1534         /* Find root device probe_info so we know its UUID */
1535         list_for_each_entry(tmp, &devices, list) {
1536                 if (!strcmp(tmp->dev, devpath)) {
1537                         pr = tmp;
1538                         break;
1539                 }
1540         }
1541         if (!pr) {
1542                 ULOG_ERR("extroot: unable to lookup root device %s\n", devpath);
1543                 return -1;
1544         }
1545 
1546         snprintf(tag, sizeof(tag), "%s/etc", path);
1547         if (stat(tag, &s))
1548                 mkdir_p(tag, 0755);
1549 
1550         snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path);
1551         if (stat(tag, &s)) {
1552                 fp = fopen(tag, "w+");
1553                 if (!fp) {
1554                         ULOG_ERR("extroot: failed to write UUID to %s: %d (%m)\n",
1555                                  tag, errno);
1556                         /* return 0 to continue boot regardless of error */
1557                         return 0;
1558                 }
1559                 fputs(pr->uuid, fp);
1560                 fclose(fp);
1561                 return 0;
1562         }
1563 
1564         fp = fopen(tag, "r");
1565         if (!fp) {
1566                 ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n", tag,
1567                          errno);
1568                 return -1;
1569         }
1570 
1571         if (!fgets(uuid, sizeof(uuid), fp))
1572                 ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n", tag,
1573                          errno);
1574         fclose(fp);
1575 
1576         if (*uuid && !strcasecmp(uuid, pr->uuid))
1577                 return 0;
1578 
1579         ULOG_ERR("extroot: UUID mismatch (root: %s, %s: %s)\n", pr->uuid,
1580                  basename(path), uuid);
1581         return -1;
1582 }
1583 
1584 /*
1585  * Read info about extroot from UCI (using prefix) and mount it.
1586  */
1587 static int mount_extroot(char *cfg)
1588 {
1589         char overlay[] = "/tmp/extroot/overlay";
1590         char mnt[] = "/tmp/extroot/mnt";
1591         char *path = mnt;
1592         struct probe_info *pr;
1593         struct mount *m;
1594         int err = -1;
1595 
1596         /* Load @cfg/etc/config/fstab */
1597         if (config_load(cfg))
1598                 return -2;
1599 
1600         /* See if there is extroot-specific mount config */
1601         m = find_block(NULL, NULL, NULL, "/");
1602         if (!m)
1603                 m = find_block(NULL, NULL, NULL, "/overlay");
1604 
1605         if (!m || !m->extroot)
1606         {
1607                 ULOG_INFO("extroot: not configured\n");
1608                 return -1;
1609         }
1610 
1611         /* Find block device pointed by the mount config */
1612         pr = find_block_info(m->uuid, m->label, m->device);
1613 
1614         if (!pr && delay_root){
1615                 ULOG_INFO("extroot: device not present, retrying in %u seconds\n", delay_root);
1616                 sleep(delay_root);
1617                 make_devs();
1618                 cache_load(1);
1619                 pr = find_block_info(m->uuid, m->label, m->device);
1620         }
1621         if (pr) {
1622                 if (strncmp(pr->type, "ext", 3) &&
1623                     strncmp(pr->type, "f2fs", 4) &&
1624                     strncmp(pr->type, "btrfs", 5) &&
1625                     strncmp(pr->type, "ntfs", 4) &&
1626                     strncmp(pr->type, "ubifs", 5)) {
1627                         ULOG_ERR("extroot: unsupported filesystem %s, try ext4, f2fs, btrfs, ntfs or ubifs\n", pr->type);
1628                         return -1;
1629                 }
1630 
1631                 if (test_fs_support(pr->type)) {
1632                         ULOG_ERR("extroot: filesystem %s not supported by kernel\n", pr->type);
1633                         return -1;
1634                 }
1635 
1636                 if (m->overlay)
1637                         path = overlay;
1638                 mkdir_p(path, 0755);
1639 
1640                 if (check_fs)
1641                         check_filesystem(pr);
1642 
1643                 err = mount(pr->dev, path, pr->type, m->flags,
1644                             (m->options) ? (m->options) : (""));
1645 
1646                 if (err) {
1647                         ULOG_ERR("extroot: mounting %s (%s) on %s failed: %d (%m)\n",
1648                                  pr->dev, pr->type, path, errno);
1649                 } else if (m->overlay) {
1650                         err = check_extroot(path);
1651                         if (err)
1652                                 umount(path);
1653                 }
1654         } else {
1655                 ULOG_ERR("extroot: cannot find device %s%s\n",
1656                          (m->uuid ? "with UUID " : (m->label ? "with label " : "")),
1657                          (m->uuid ? m->uuid : (m->label ? m->label : m->device)));
1658         }
1659 
1660         return err;
1661 }
1662 
1663 /**
1664  * Look for extroot config and mount it if present
1665  *
1666  * Look for /etc/config/fstab on all supported partitions and use it for
1667  * mounting extroot if specified.
1668  */
1669 static int main_extroot(int argc, char **argv)
1670 {
1671         struct probe_info *pr;
1672         char blkdev_path[32] = { 0 };
1673         int err = -1;
1674 #ifdef UBIFS_EXTROOT
1675         libubi_t libubi;
1676 #endif
1677 
1678         if (!getenv("PREINIT"))
1679                 return -1;
1680 
1681         if (argc != 2) {
1682                 ULOG_ERR("Usage: block extroot\n");
1683                 return -1;
1684         }
1685 
1686         make_devs();
1687         cache_load(1);
1688 
1689         /* enable LOG_INFO messages */
1690         ulog_threshold(LOG_INFO);
1691 
1692         /* try the currently mounted overlay if exists */
1693         err = mount_extroot("/tmp/overlay");
1694         if (!err)
1695             return err;
1696 
1697         /*
1698          * Look for "rootfs_data". We will want to mount it and check for
1699          * extroot configuration.
1700          */
1701 
1702         /* Start with looking for MTD partition */
1703         find_block_mtd("\"rootfs_data\"", blkdev_path, sizeof(blkdev_path));
1704         if (blkdev_path[0]) {
1705                 pr = find_block_info(NULL, NULL, blkdev_path);
1706                 if (pr && !strcmp(pr->type, "jffs2")) {
1707                         char cfg[] = "/tmp/jffs_cfg";
1708 
1709                         /*
1710                          * Mount MTD part and try extroot (using
1711                          * /etc/config/fstab from that partition)
1712                          */
1713                         mkdir_p(cfg, 0755);
1714                         if (!mount(blkdev_path, cfg, "jffs2", MS_NOATIME, NULL)) {
1715                                 err = mount_extroot(cfg);
1716                                 umount2(cfg, MNT_DETACH);
1717                         }
1718                         if (err < 0)
1719                                 rmdir("/tmp/overlay");
1720                         rmdir(cfg);
1721                         return err;
1722                 }
1723         }
1724 
1725 #ifdef UBIFS_EXTROOT
1726         /* ... but it also could be an UBI volume */
1727         memset(blkdev_path, 0, sizeof(blkdev_path));
1728         libubi = libubi_open();
1729         find_block_ubi(libubi, "rootfs_data", blkdev_path, sizeof(blkdev_path));
1730         libubi_close(libubi);
1731         if (blkdev_path[0]) {
1732                 char cfg[] = "/tmp/ubifs_cfg";
1733 
1734                 /* Mount volume and try extroot (using fstab from that vol) */
1735                 mkdir_p(cfg, 0755);
1736                 if (!mount(blkdev_path, cfg, "ubifs", MS_NOATIME, NULL)) {
1737                         err = mount_extroot(cfg);
1738                         umount2(cfg, MNT_DETACH);
1739                 }
1740                 if (err < 0)
1741                         rmdir("/tmp/overlay");
1742                 rmdir(cfg);
1743                 return err;
1744        }
1745 #endif
1746 
1747         /* As a last resort look for /etc/config/fstab on "rootfs" partition */
1748         return mount_extroot(NULL);
1749 }
1750 
1751 static int main_mount(int argc, char **argv)
1752 {
1753         struct probe_info *pr;
1754 
1755         if (config_load(NULL))
1756                 return -1;
1757 
1758         cache_load(1);
1759         list_for_each_entry(pr, &devices, list)
1760                 mount_device(pr, NULL, TYPE_DEV);
1761 
1762         handle_swapfiles(true);
1763 
1764         return 0;
1765 }
1766 
1767 static int main_umount(int argc, char **argv)
1768 {
1769         struct probe_info *pr;
1770         bool all = false;
1771 
1772         if (config_load(NULL))
1773                 return -1;
1774 
1775         handle_swapfiles(false);
1776 
1777         cache_load(1);
1778 
1779         if (argc == 3)
1780                 all = !strcmp(argv[2], "-a");
1781 
1782         list_for_each_entry(pr, &devices, list) {
1783                 struct mount *m;
1784 
1785                 if (!strcmp(pr->type, "swap"))
1786                         continue;
1787 
1788                 m = find_block(pr->uuid, pr->label, basename(pr->dev), NULL);
1789                 if (m && m->extroot)
1790                         continue;
1791 
1792                 umount_device(pr->dev, TYPE_DEV, all);
1793         }
1794 
1795         return 0;
1796 }
1797 
1798 static int main_detect(int argc, char **argv)
1799 {
1800         struct probe_info *pr;
1801 
1802         cache_load(0);
1803         printf("config 'global'\n");
1804         printf("\toption\tanon_swap\t''\n");
1805         printf("\toption\tanon_mount\t''\n");
1806         printf("\toption\tauto_swap\t'1'\n");
1807         printf("\toption\tauto_mount\t'1'\n");
1808         printf("\toption\tdelay_root\t'5'\n");
1809         printf("\toption\tcheck_fs\t''\n\n");
1810         list_for_each_entry(pr, &devices, list)
1811                 print_block_uci(pr);
1812 
1813         return 0;
1814 }
1815 
1816 static int main_info(int argc, char **argv)
1817 {
1818         int i;
1819         struct probe_info *pr;
1820 
1821         cache_load(1);
1822         if (argc == 2) {
1823                 list_for_each_entry(pr, &devices, list)
1824                         print_block_info(pr);
1825 
1826                 return 0;
1827         };
1828 
1829         for (i = 2; i < argc; i++) {
1830                 struct stat s;
1831 
1832                 if (stat(argv[i], &s)) {
1833                         ULOG_ERR("failed to stat %s\n", argv[i]);
1834                         continue;
1835                 }
1836                 if (!S_ISBLK(s.st_mode) && !(S_ISCHR(s.st_mode) && major(s.st_rdev) == 250)) {
1837                         ULOG_ERR("%s is not a block device\n", argv[i]);
1838                         continue;
1839                 }
1840                 pr = find_block_info(NULL, NULL, argv[i]);
1841                 if (pr)
1842                         print_block_info(pr);
1843         }
1844 
1845         return 0;
1846 }
1847 
1848 static int swapon_usage(void)
1849 {
1850         fprintf(stderr, "Usage: swapon [-s] [-a] [[-p pri] DEVICE]\n\n"
1851                 "\tStart swapping on [DEVICE]\n"
1852                 " -a\tStart swapping on all swap devices\n"
1853                 " -p pri\tSet priority of swap device\n"
1854                 " -s\tShow summary\n");
1855         return -1;
1856 }
1857 
1858 static int main_swapon(int argc, char **argv)
1859 {
1860         int ch;
1861         FILE *fp;
1862         char *lineptr;
1863         size_t s;
1864         struct probe_info *pr;
1865         int flags = 0;
1866         int pri;
1867         struct stat st;
1868         int err;
1869 
1870         while ((ch = getopt(argc, argv, "ap:s")) != -1) {
1871                 switch(ch) {
1872                 case 's':
1873                         fp = fopen("/proc/swaps", "r");
1874                         lineptr = NULL;
1875 
1876                         if (!fp) {
1877                                 ULOG_ERR("failed to open /proc/swaps\n");
1878                                 return -1;
1879                         }
1880                         while (getline(&lineptr, &s, fp) > 0)
1881                                 printf("%s", lineptr);
1882                         if (lineptr)
1883                                 free(lineptr);
1884                         fclose(fp);
1885                         return 0;
1886                 case 'a':
1887                         cache_load(0);
1888                         list_for_each_entry(pr, &devices, list) {
1889                                 if (strcmp(pr->type, "swap"))
1890                                         continue;
1891                                 if (swapon(pr->dev, 0))
1892                                         ULOG_ERR("failed to swapon %s\n", pr->dev);
1893                         }
1894                         return 0;
1895                 case 'p':
1896                         pri = atoi(optarg);
1897                         if (pri >= 0)
1898                                 flags = ((pri << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER;
1899                         break;
1900                 default:
1901                         return swapon_usage();
1902                 }
1903         }
1904 
1905         if (optind != (argc - 1))
1906                 return swapon_usage();
1907 
1908         if (stat(argv[optind], &st) || (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))) {
1909                 ULOG_ERR("%s is not a block device or file\n", argv[optind]);
1910                 return -1;
1911         }
1912         err = swapon(argv[optind], flags);
1913         if (err) {
1914                 ULOG_ERR("failed to swapon %s (%d)\n", argv[optind], err);
1915                 return err;
1916         }
1917 
1918         return 0;
1919 }
1920 
1921 static int main_swapoff(int argc, char **argv)
1922 {
1923         if (argc != 2) {
1924                 ULOG_ERR("Usage: swapoff [-a] [DEVICE]\n\n"
1925                         "\tStop swapping on DEVICE\n"
1926                         " -a\tStop swapping on all swap devices\n");
1927                 return -1;
1928         }
1929 
1930         if (!strcmp(argv[1], "-a")) {
1931                 FILE *fp = fopen("/proc/swaps", "r");
1932                 char line[256];
1933 
1934                 if (!fp) {
1935                         ULOG_ERR("failed to open /proc/swaps\n");
1936                         return -1;
1937                 }
1938                 if (fgets(line, sizeof(line), fp))
1939                         while (fgets(line, sizeof(line), fp)) {
1940                                 char *end = strchr(line, ' ');
1941                                 int err;
1942 
1943                                 if (!end)
1944                                         continue;
1945                                 *end = '\0';
1946                                 err = swapoff(line);
1947                                 if (err)
1948                                         ULOG_ERR("failed to swapoff %s (%d)\n", line, err);
1949                         }
1950                 fclose(fp);
1951         } else {
1952                 struct stat s;
1953                 int err;
1954 
1955                 if (stat(argv[1], &s) || (!S_ISBLK(s.st_mode) && !S_ISREG(s.st_mode))) {
1956                         ULOG_ERR("%s is not a block device or file\n", argv[1]);
1957                         return -1;
1958                 }
1959                 err = swapoff(argv[1]);
1960                 if (err) {
1961                         ULOG_ERR("failed to swapoff %s (%d)\n", argv[1], err);
1962                         return err;
1963                 }
1964         }
1965 
1966         return 0;
1967 }
1968 
1969 int main(int argc, char **argv)
1970 {
1971         char *base = basename(*argv);
1972 
1973         umask(0);
1974 
1975         ulog_open(-1, -1, "block");
1976         ulog_threshold(LOG_NOTICE);
1977 
1978         if (!strcmp(base, "swapon"))
1979                 return main_swapon(argc, argv);
1980 
1981         if (!strcmp(base, "swapoff"))
1982                 return main_swapoff(argc, argv);
1983 
1984         if ((argc > 1) && !strcmp(base, "block")) {
1985                 if (!strcmp(argv[1], "info"))
1986                         return main_info(argc, argv);
1987 
1988                 if (!strcmp(argv[1], "detect"))
1989                         return main_detect(argc, argv);
1990 
1991                 if (!strcmp(argv[1], "hotplug"))
1992                         return main_hotplug(argc, argv);
1993 
1994                 if (!strcmp(argv[1], "autofs"))
1995                         return main_autofs(argc, argv);
1996 
1997                 if (!strcmp(argv[1], "extroot"))
1998                         return main_extroot(argc, argv);
1999 
2000                 if (!strcmp(argv[1], "mount"))
2001                         return main_mount(argc, argv);
2002 
2003                 if (!strcmp(argv[1], "umount"))
2004                         return main_umount(argc, argv);
2005 
2006                 if (!strcmp(argv[1], "remount")) {
2007                         int ret = main_umount(argc, argv);
2008 
2009                         if (!ret)
2010                                 ret = main_mount(argc, argv);
2011                         return ret;
2012                 }
2013         }
2014 
2015         ULOG_ERR("Usage: block <info|mount|umount|detect>\n");
2016 
2017         return -1;
2018 }
2019 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt