#include <uci.h>
#include <uci_blob.h>
-#include <libubox/ulog.h>
+#include <libubox/avl-cmp.h>
+#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
+#include <libubox/ulog.h>
+#include <libubox/utils.h>
#include <libubox/vlist.h>
-#include <libubox/blobmsg_json.h>
-#include <libubox/avl-cmp.h>
#include <libubus.h>
#include "probe.h"
}
-static int print_block_uci(struct probe_info *pr)
-{
- if (!strcmp(pr->type, "swap")) {
- printf("config 'swap'\n");
- } else {
- printf("config 'mount'\n");
- printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
- }
- if (pr->uuid)
- printf("\toption\tuuid\t'%s'\n", pr->uuid);
- else
- printf("\toption\tdevice\t'%s'\n", pr->dev);
- printf("\toption\tenabled\t'0'\n\n");
-
- return 0;
-}
-
static struct probe_info* find_block_info(char *uuid, char *label, char *path)
{
struct probe_info *pr = NULL;
{
FILE *fp = fopen("/proc/self/mountinfo", "r");
static char line[256];
- int len = strlen(block);
char *point = NULL, *pos, *tmp, *cpoint, *devname;
struct stat s;
int rstat;
*pos = '\0';
devname = tmp;
- if (!strncmp(block, devname, len)) {
+ if (!strcmp(block, devname)) {
point = strdup(cpoint);
break;
}
return point;
}
+static int print_block_uci(struct probe_info *pr)
+{
+ if (!strcmp(pr->type, "swap")) {
+ printf("config 'swap'\n");
+ } else {
+ char *mp = find_mount_point(pr->dev);
+
+ printf("config 'mount'\n");
+ if (mp) {
+ printf("\toption\ttarget\t'%s'\n", mp);
+ free(mp);
+ } else {
+ printf("\toption\ttarget\t'/mnt/%s'\n", basename(pr->dev));
+ }
+ }
+ if (pr->uuid)
+ printf("\toption\tuuid\t'%s'\n", pr->uuid);
+ else
+ printf("\toption\tdevice\t'%s'\n", pr->dev);
+ printf("\toption\tenabled\t'0'\n\n");
+
+ return 0;
+}
+
static int print_block_info(struct probe_info *pr)
{
static char *mp;
return 0;
}
-static void mkdir_p(char *dir)
-{
- char *l = strrchr(dir, '/');
-
- if (l) {
- *l = '\0';
- mkdir_p(dir);
- *l = '/';
- mkdir(dir, 0755);
- }
-}
-
static void check_filesystem(struct probe_info *pr)
{
pid_t pid;
struct stat statbuf;
const char *e2fsck = "/usr/sbin/e2fsck";
const char *f2fsck = "/usr/sbin/fsck.f2fs";
- const char *dosfsck = "/usr/sbin/dosfsck";
+ const char *fatfsck = "/usr/sbin/fsck.fat";
const char *btrfsck = "/usr/bin/btrfsck";
const char *ntfsck = "/usr/bin/ntfsfix";
const char *ckfs;
return;
if (!strncmp(pr->type, "vfat", 4)) {
- ckfs = dosfsck;
+ ckfs = fatfsck;
} else if (!strncmp(pr->type, "f2fs", 4)) {
ckfs = f2fsck;
} else if (!strncmp(pr->type, "ext", 3)) {
return err;
}
-static void blockd_notify(char *device, struct mount *m, struct probe_info *pr)
+static int blockd_notify(const char *method, char *device, struct mount *m,
+ struct probe_info *pr)
{
struct ubus_context *ctx = ubus_connect(NULL);
uint32_t id;
+ int err;
if (!ctx)
- return;
+ return -ENXIO;
if (!ubus_lookup_id(ctx, "block", &id)) {
struct blob_buf buf = { 0 };
blobmsg_add_u32(&buf, "remove", 1);
}
- ubus_invoke(ctx, id, "hotplug", buf.head, NULL, NULL, 3000);
+ err = ubus_invoke(ctx, id, method, buf.head, NULL, NULL, 3000);
+ } else {
+ err = -ENOENT;
}
ubus_free(ctx);
+
+ return err;
}
static int mount_device(struct probe_info *pr, int type)
{
struct mount *m;
+ struct stat st;
+ char *_target = NULL;
+ char *target;
char *device;
char *mp;
+ int err;
if (!pr)
return -1;
return 0;
}
+ m = find_block(pr->uuid, pr->label, device, NULL);
+ if (m && m->extroot)
+ return -1;
+
mp = find_mount_point(pr->dev);
- if (mp && (type != TYPE_HOTPLUG)) {
- ULOG_ERR("%s is already mounted on %s\n", pr->dev, mp);
+ if (mp) {
+ if (m && m->type == TYPE_MOUNT && m->target && strcmp(m->target, mp)) {
+ ULOG_ERR("%s is already mounted on %s\n", pr->dev, mp);
+ err = -1;
+ } else
+ err = 0;
free(mp);
- return -1;
+ return err;
}
- m = find_block(pr->uuid, pr->label, device, NULL);
- if (m && m->extroot)
- return -1;
+ if (type == TYPE_HOTPLUG)
+ blockd_notify("hotplug", device, m, pr);
+ /* Check if device should be mounted & set the target directory */
if (m) {
switch (type) {
case TYPE_HOTPLUG:
- blockd_notify(device, m, pr);
if (m->autofs)
return 0;
if (!auto_mount)
return -1;
break;
}
- } else if (type == TYPE_HOTPLUG) {
- blockd_notify(device, NULL, pr);
- }
-
- if (m) {
- char *target = m->target;
- char _target[32];
- int err = 0;
if (m->autofs) {
- snprintf(_target, sizeof(_target), "/tmp/run/blockd/%s", device);
+ if (asprintf(&_target, "/tmp/run/blockd/%s", device) == -1)
+ exit(ENOMEM);
+
target = _target;
- }
- if (!target) {
- snprintf(_target, sizeof(_target), "/mnt/%s", device);
+ } else if (m->target) {
+ target = m->target;
+ } else {
+ if (asprintf(&_target, "/mnt/%s", device) == -1)
+ exit(ENOMEM);
+
target = _target;
}
- mkdir_p(target);
+ } else if (anon_mount) {
+ if (asprintf(&_target, "/mnt/%s", device) == -1)
+ exit(ENOMEM);
- if (check_fs)
- check_filesystem(pr);
-
- err = handle_mount(pr->dev, target, pr->type, m);
- if (err)
- ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
- pr->dev, pr->type, target, errno);
- else
- handle_swapfiles(true);
- return err;
+ target = _target;
+ } else {
+ /* No reason to mount this device */
+ return 0;
}
- if (anon_mount) {
- char target[32];
- int err = 0;
+ /* Mount the device */
- snprintf(target, sizeof(target), "/mnt/%s", device);
- mkdir_p(target);
+ if (check_fs)
+ check_filesystem(pr);
- if (check_fs)
- check_filesystem(pr);
+ mkdir_p(target, 0755);
+ if (!lstat(target, &st) && S_ISLNK(st.st_mode))
+ unlink(target);
+
+ err = handle_mount(pr->dev, target, pr->type, m);
+ if (err) {
+ ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
+ pr->dev, pr->type, target, errno);
+
+ if (_target)
+ free(_target);
- err = handle_mount(pr->dev, target, pr->type, NULL);
- if (err)
- ULOG_ERR("mounting %s (%s) as %s failed (%d) - %m\n",
- pr->dev, pr->type, target, errno);
- else
- handle_swapfiles(true);
return err;
}
+ if (_target)
+ free(_target);
+
+ handle_swapfiles(true);
+
+ if (type != TYPE_AUTOFS)
+ blockd_notify("mount", device, NULL, NULL);
+
return 0;
}
-static int umount_device(struct probe_info *pr)
+static int umount_device(char *path, int type, bool all)
{
- struct mount *m;
- char *device = basename(pr->dev);
char *mp;
int err;
- if (!pr)
- return -1;
-
- if (!strcmp(pr->type, "swap"))
- return -1;
-
- mp = find_mount_point(pr->dev);
+ mp = find_mount_point(path);
if (!mp)
return -1;
+ if (!strcmp(mp, "/") && !all)
+ return 0;
- m = find_block(pr->uuid, pr->label, device, NULL);
- if (m && m->extroot)
- return -1;
+ if (type != TYPE_AUTOFS)
+ blockd_notify("umount", basename(path), NULL, NULL);
err = umount2(mp, MNT_DETACH);
- if (err)
- ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n",
- pr->dev, mp, errno);
- else
- ULOG_INFO("unmounted %s (%s)\n",
- pr->dev, mp);
+ if (err) {
+ ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n", path, mp,
+ errno);
+ } else {
+ ULOG_INFO("unmounted %s (%s)\n", path, mp);
+ rmdir(mp);
+ }
free(mp);
return err;
static int mount_action(char *action, char *device, int type)
{
- char path[32];
- char *mount_point;
+ char *path = NULL;
+ struct probe_info *pr;
if (!action || !device)
return -1;
- snprintf(path, sizeof(path), "/dev/%s", device);
- if (!strcmp(action, "remove")) {
- int err = 0;
+ if (config_load(NULL))
+ return -1;
- if (type == TYPE_HOTPLUG)
- blockd_notify(device, NULL, NULL);
+ cache_load(1);
- mount_point = find_mount_point(path);
- if (mount_point)
- err = umount2(mount_point, MNT_DETACH);
+ list_for_each_entry(pr, &devices, list)
+ if (!strcmp(basename(pr->dev), device))
+ path = pr->dev;
+
+ if (!path)
+ return -1;
+
+ if (!strcmp(action, "remove")) {
+ if (type == TYPE_HOTPLUG)
+ blockd_notify("hotplug", device, NULL, NULL);
- if (err)
- ULOG_ERR("umount of %s failed (%d) - %m\n",
- mount_point, errno);
+ umount_device(path, type, true);
- free(mount_point);
return 0;
} else if (strcmp(action, "add")) {
ULOG_ERR("Unkown action %s\n", action);
return -1;
}
- if (config_load(NULL))
- return -1;
- cache_load(0);
-
return mount_device(find_block_info(NULL, NULL, path), type);
}
static int main_hotplug(int argc, char **argv)
{
- return mount_action(getenv("ACTION"), getenv("DEVNAME"), TYPE_HOTPLUG);
+ char *devname = getenv("DEVNAME");
+
+ /* resolve device mapper name for dm-* if possible */
+ if (devname && strlen(devname) >= 2 && !strncmp(devname, "dm-", 3)) {
+ char *dmdevnamep;
+ char dmname[256];
+
+ if (asprintf(&dmdevnamep, "/sys/%s/dm/name", getenv("DEVPATH")) == -1)
+ exit(ENOMEM);
+
+ FILE *fp = fopen(dmdevnamep, "r");
+ free(dmdevnamep);
+
+ if (fp) {
+ if (fgets(dmname, sizeof(dmname), fp))
+ devname = dmname;
+
+ fclose(fp);
+ }
+ }
+
+ return mount_action(getenv("ACTION"), devname, TYPE_HOTPLUG);
}
static int main_autofs(int argc, char **argv)
{
+ int err = 0;
+
if (argc < 3)
return -1;
if (config_load(NULL))
return -1;
- cache_load(0);
+ cache_load(1);
list_for_each_entry(pr, &devices, list) {
- struct mount *m = find_block(pr->uuid, pr->label, NULL, NULL);
+ struct mount *m;
+ char *mp;
- if (m && m->autofs)
- mount_device(pr, TYPE_HOTPLUG);
- else
- blockd_notify(pr->dev, m, pr);
+ if (!strcmp(pr->type, "swap"))
+ continue;
+
+ m = find_block(pr->uuid, pr->label, NULL, NULL);
+ if (m && m->extroot)
+ continue;
+
+ blockd_notify("hotplug", pr->dev, m, pr);
+ if ((!m || !m->autofs) && (mp = find_mount_point(pr->dev))) {
+ blockd_notify("mount", pr->dev, NULL, NULL);
+ free(mp);
+ }
}
- return 0;
+ } else {
+ if (argc < 4)
+ return -EINVAL;
+
+ err = mount_action(argv[2], argv[3], TYPE_AUTOFS);
+ }
+
+ if (err) {
+ ULOG_ERR("autofs: \"%s\" action has failed: %d\n", argv[2], err);
}
- return mount_action(argv[2], argv[3], TYPE_AUTOFS);
+
+ return err;
}
static int find_block_mtd(char *name, char *part, int plen)
return err;
}
-
-#else
+#endif
static int find_root_dev(char *buf, int len)
{
return -1;
}
-#endif
-
static int test_fs_support(const char *name)
{
char line[128], *p;
return rv;
}
+/**
+ * Check if mounted partition is a valid extroot
+ *
+ * @path target mount point
+ *
+ * Valid extroot partition has to contain /etc/.extroot-uuid with UUID of root
+ * device. This function reads UUID and verifies it OR writes UUID to
+ * .extroot-uuid if it doesn't exist yet (first extroot usage).
+ */
static int check_extroot(char *path)
{
struct probe_info *pr = NULL;
+ struct probe_info *tmp;
+ struct stat s;
+ char uuid[64] = { 0 };
char devpath[32];
+ char tag[64];
+ FILE *fp;
+ int err;
+ err = find_block_mtd("\"rootfs\"", devpath, sizeof(devpath));
#ifdef UBIFS_EXTROOT
- if (find_block_mtd("\"rootfs\"", devpath, sizeof(devpath))) {
- int err = -1;
+ if (err) {
libubi_t libubi;
libubi = libubi_open();
err = find_block_ubi_RO(libubi, "rootfs", devpath, sizeof(devpath));
libubi_close(libubi);
- if (err)
- return -1;
}
-#else
- if (find_block_mtd("\"rootfs\"", devpath, sizeof(devpath))) {
- if (find_root_dev(devpath, sizeof(devpath))) {
- ULOG_ERR("extroot: unable to determine root device\n");
- return -1;
+#endif
+ if (err) {
+ err = find_root_dev(devpath, sizeof(devpath));
+ }
+ if (err) {
+ ULOG_ERR("extroot: unable to determine root device\n");
+ return -1;
+ }
+
+ /* Find root device probe_info so we know its UUID */
+ list_for_each_entry(tmp, &devices, list) {
+ if (!strcmp(tmp->dev, devpath)) {
+ pr = tmp;
+ break;
}
}
-#endif
+ if (!pr) {
+ ULOG_ERR("extroot: unable to lookup root device %s\n", devpath);
+ return -1;
+ }
- list_for_each_entry(pr, &devices, list) {
- if (!strcmp(pr->dev, devpath)) {
- struct stat s;
- FILE *fp = NULL;
- char tag[64];
- char uuid[64] = { 0 };
-
- snprintf(tag, sizeof(tag), "%s/etc", path);
- if (stat(tag, &s))
- mkdir_p(tag);
-
- snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path);
- if (stat(tag, &s)) {
- fp = fopen(tag, "w+");
- if (!fp) {
- ULOG_ERR("extroot: failed to write UUID to %s: %d (%m)\n",
- tag, errno);
- /* return 0 to continue boot regardless of error */
- return 0;
- }
- fputs(pr->uuid, fp);
- fclose(fp);
- return 0;
- }
+ snprintf(tag, sizeof(tag), "%s/etc", path);
+ if (stat(tag, &s))
+ mkdir_p(tag, 0755);
- fp = fopen(tag, "r");
- if (!fp) {
- ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n",
- tag, errno);
- return -1;
- }
+ snprintf(tag, sizeof(tag), "%s/etc/.extroot-uuid", path);
+ if (stat(tag, &s)) {
+ fp = fopen(tag, "w+");
+ if (!fp) {
+ ULOG_ERR("extroot: failed to write UUID to %s: %d (%m)\n",
+ tag, errno);
+ /* return 0 to continue boot regardless of error */
+ return 0;
+ }
+ fputs(pr->uuid, fp);
+ fclose(fp);
+ return 0;
+ }
- if (!fgets(uuid, sizeof(uuid), fp))
- ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n",
- tag, errno);
- fclose(fp);
+ fp = fopen(tag, "r");
+ if (!fp) {
+ ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n", tag,
+ errno);
+ return -1;
+ }
- if (*uuid && !strcasecmp(uuid, pr->uuid))
- return 0;
+ if (!fgets(uuid, sizeof(uuid), fp))
+ ULOG_ERR("extroot: failed to read UUID from %s: %d (%m)\n", tag,
+ errno);
+ fclose(fp);
- ULOG_ERR("extroot: UUID mismatch (root: %s, %s: %s)\n",
- pr->uuid, basename(path), uuid);
- return -1;
- }
- }
+ if (*uuid && !strcasecmp(uuid, pr->uuid))
+ return 0;
- ULOG_ERR("extroot: unable to lookup root device %s\n", devpath);
+ ULOG_ERR("extroot: UUID mismatch (root: %s, %s: %s)\n", pr->uuid,
+ basename(path), uuid);
return -1;
}
ULOG_INFO("extroot: device not present, retrying in %u seconds\n", delay_root);
sleep(delay_root);
make_devs();
- cache_load(0);
+ cache_load(1);
pr = find_block_info(m->uuid, m->label, m->device);
}
if (pr) {
if (m->overlay)
path = overlay;
- mkdir_p(path);
+ mkdir_p(path, 0755);
if (check_fs)
check_filesystem(pr);
return err;
}
+/**
+ * Look for extroot config and mount it if present
+ *
+ * Look for /etc/config/fstab on all supported partitions and use it for
+ * mounting extroot if specified.
+ */
static int main_extroot(int argc, char **argv)
{
struct probe_info *pr;
* Mount MTD part and try extroot (using
* /etc/config/fstab from that partition)
*/
- mkdir_p(cfg);
+ mkdir_p(cfg, 0755);
if (!mount(blkdev_path, cfg, "jffs2", MS_NOATIME, NULL)) {
err = mount_extroot(cfg);
umount2(cfg, MNT_DETACH);
char cfg[] = "/tmp/ubifs_cfg";
/* Mount volume and try extroot (using fstab from that vol) */
- mkdir_p(cfg);
+ mkdir_p(cfg, 0755);
if (!mount(blkdev_path, cfg, "ubifs", MS_NOATIME, NULL)) {
err = mount_extroot(cfg);
umount2(cfg, MNT_DETACH);
}
#endif
+ /* As a last resort look for /etc/config/fstab on "rootfs" partition */
return mount_extroot(NULL);
}
static int main_umount(int argc, char **argv)
{
struct probe_info *pr;
+ bool all = false;
if (config_load(NULL))
return -1;
handle_swapfiles(false);
- cache_load(0);
- list_for_each_entry(pr, &devices, list)
- umount_device(pr);
+ cache_load(1);
+
+ if (argc == 3)
+ all = !strcmp(argv[2], "-a");
+
+ list_for_each_entry(pr, &devices, list) {
+ struct mount *m;
+
+ if (!strcmp(pr->type, "swap"))
+ continue;
+
+ m = find_block(pr->uuid, pr->label, basename(pr->dev), NULL);
+ if (m && m->extroot)
+ continue;
+
+ umount_device(pr->dev, TYPE_DEV, all);
+ }
return 0;
}
default:
return swapon_usage();
}
-
}
if (optind != (argc - 1))