unsigned int prio;
};
-static struct vlist_tree mounts;
+struct device {
+ struct vlist_node node;
+
+ struct probe_info *pr;
+ struct mount *m;
+};
+
+static void vlist_nop_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ struct vlist_node *node_old);
+static int devices_cmp(const void *k1, const void *k2, void *ptr);
+
static struct blob_buf b;
-static LIST_HEAD(devices);
+static VLIST_TREE(mounts, avl_strcmp, vlist_nop_update, false, false);
+static VLIST_TREE(devices, devices_cmp, vlist_nop_update, false, false);
static int anon_mount, anon_swap, auto_mount, auto_swap, check_fs;
static unsigned int delay_root;
return NULL;
}
-static void mounts_update(struct vlist_tree *tree, struct vlist_node *node_new,
+static void vlist_nop_update(struct vlist_tree *tree, struct vlist_node *node_new,
struct vlist_node *node_old)
{
+ // NOTE: free on delete skipped
}
static struct uci_package * config_try_load(struct uci_context *ctx, char *path)
struct uci_element *e;
char path[64];
- vlist_init(&mounts, avl_strcmp, mounts_update);
-
if (cfg) {
snprintf(path, sizeof(path), "%s/upper/etc/config/fstab", cfg);
pkg = config_try_load(ctx, path);
static struct probe_info* _probe_path(char *path)
{
- struct probe_info *pr;
+ struct device *dev;
char tmppath[64];
/* skip ubi device if ubiblock device is present */
if (path[5] == 'u' && path[6] == 'b' && path[7] == 'i' &&
path[8] >= '0' && path[8] <= '9' ) {
snprintf(tmppath, sizeof(tmppath), "/dev/ubiblock%s", path + 8);
- list_for_each_entry(pr, &devices, list)
- if (!strcasecmp(pr->dev, tmppath))
+ vlist_for_each_element(&devices, dev, node)
+ if (!strcasecmp(dev->pr->dev, tmppath))
return NULL;
}
int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
int j;
glob_t gl;
+ struct device *dev;
if (glob(path, gl_flags, NULL, &gl) < 0)
return -1;
for (j = 0; j < gl.gl_pathc; j++) {
struct probe_info *pr = _probe_path(gl.gl_pathv[j]);
- if (pr)
- list_add_tail(&pr->list, &devices);
+ if (pr) {
+ dev = malloc(sizeof(struct device));
+ dev->pr = pr;
+ dev->m = find_block(pr->uuid, pr->label,
+ basename(pr->dev), NULL);
+ vlist_add(&devices, &dev->node, dev);
+ }
}
globfree(&gl);
return 0;
}
+static int devices_cmp(const void *k1, const void *k2, void *ptr)
+{
+ struct device *dev1 = (struct device *)k1;
+ struct device *dev2 = (struct device *)k2;
+
+ if (dev1->m) {
+ if (!dev2->m)
+ return -1;
+ if (dev1->m->type == TYPE_MOUNT && dev2->m->type == TYPE_MOUNT &&
+ dev1->m->target && dev2->m->target) {
+ int len1 = strlen(dev1->m->target);
+ int len2 = strlen(dev2->m->target);
+ if (len1 != len2)
+ return len1 - len2;
+ }
+ } else if (dev2->m) {
+ return 1;
+ }
+ return strcmp(dev1->pr->dev, dev2->pr->dev);
+}
+
static void cache_load(int mtd)
{
+ vlist_update(&devices);
if (mtd) {
_cache_load("/dev/mtdblock*");
_cache_load("/dev/ubiblock*");
_cache_load("/dev/vd*");
_cache_load("/dev/xvd*");
_cache_load("/dev/mapper/*");
+ vlist_flush(&devices);
}
-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)
+static struct device* find_block_device(char *uuid, char *label, char *path)
{
- struct probe_info *pr = NULL;
+ struct device *dev;
if (uuid)
- list_for_each_entry(pr, &devices, list)
- if (pr->uuid && !strcasecmp(pr->uuid, uuid))
- return pr;
+ vlist_for_each_element(&devices, dev, node)
+ if (dev->pr->uuid && !strcasecmp(dev->pr->uuid, uuid))
+ return dev;
if (label)
- list_for_each_entry(pr, &devices, list)
- if (pr->label && !strcmp(pr->label, label))
- return pr;
+ vlist_for_each_element(&devices, dev, node)
+ if (dev->pr->label && !strcmp(dev->pr->label, label))
+ return dev;
if (path)
- list_for_each_entry(pr, &devices, list)
- if (pr->dev && !strcmp(basename(pr->dev), basename(path)))
- return pr;
+ vlist_for_each_element(&devices, dev, node)
+ if (dev->pr->dev && !strcmp(basename(dev->pr->dev), basename(path)))
+ return dev;
return 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;
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 int hotplug_call_mount(const char *action, const char *device)
+{
+ pid_t pid;
+ int err = 0;
+
+ pid = fork();
+ if (!pid) {
+ char * const argv[] = { "hotplug-call", "mount", NULL };
+
+ setenv("ACTION", action, 1);
+ setenv("DEVICE", device, 1);
+
+ execv("/sbin/hotplug-call", argv);
+ exit(-1);
+ } else if (pid > 0) {
+ int status;
+
+ pid = waitpid(pid, &status, 0);
+ if (pid <= 0 || !WIFEXITED(status) || WEXITSTATUS(status)) {
+ err = -ENOEXEC;
+ ULOG_ERR("hotplug-call call failed\n");
+ }
+ } else {
+ err = -errno;
+ }
+
+ return err;
+}
+
static int handle_mount(const char *source, const char *target,
const char *fstype, struct mount *m)
{
return err;
}
-static void blockd_notify(char *device, struct mount *m, struct probe_info *pr)
+static int blockd_notify(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, "hotplug", buf.head, NULL, NULL, 3000);
+ } else {
+ err = -ENOENT;
}
ubus_free(ctx);
+
+ return err;
}
-static int mount_device(struct probe_info *pr, int type)
+static int mount_device(struct device *dev, int type)
{
struct mount *m;
+ struct probe_info *pr;
char _target[32];
char *target;
char *device;
char *mp;
int err;
- if (!pr)
+ if (!dev)
return -1;
+ pr = dev->pr;
device = basename(pr->dev);
if (!strcmp(pr->type, "swap")) {
return 0;
}
+ m = dev->m;
+ 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 && 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(device, m, pr);
handle_swapfiles(true);
+ if (type != TYPE_AUTOFS)
+ hotplug_call_mount("add", device);
+
return 0;
}
-static int umount_device(char *path)
+static int umount_device(char *path, int type, bool all)
{
char *mp;
int err;
mp = find_mount_point(path);
if (!mp)
return -1;
+ if (!strcmp(mp, "/") && !all)
+ return 0;
+
+ if (type != TYPE_AUTOFS)
+ hotplug_call_mount("remove", basename(path));
err = umount2(mp, MNT_DETACH);
- if (err)
+ if (err) {
ULOG_ERR("unmounting %s (%s) failed (%d) - %m\n", path, mp,
errno);
- else
+ } 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)
{
+ struct device *the_dev, *dev;
char path[32];
if (!action || !device)
return -1;
- snprintf(path, sizeof(path), "/dev/%s", device);
+
+ if (config_load(NULL))
+ return -1;
+ cache_load(0);
+
+ the_dev = find_block_device(NULL, NULL, device);
if (!strcmp(action, "remove")) {
if (type == TYPE_HOTPLUG)
blockd_notify(device, NULL, NULL);
- umount_device(path);
-
+ if (!the_dev || !the_dev->m || the_dev->m->type != TYPE_MOUNT) {
+ snprintf(path, sizeof(path), "/dev/%s", device);
+ umount_device(path, type, true);
+ } else
+ vlist_for_element_to_last_reverse(&devices, the_dev, dev, node)
+ if (dev->m && dev->m->type == TYPE_MOUNT)
+ umount_device(dev->pr->dev, type, true);
return 0;
- } else if (strcmp(action, "add")) {
- ULOG_ERR("Unkown action %s\n", action);
-
- return -1;
+ } else if (!strcmp(action, "add")) {
+ if (!the_dev)
+ return -1;
+ if (the_dev->m && the_dev->m->type == TYPE_MOUNT) {
+ vlist_for_first_to_element(&devices, the_dev, dev, node) {
+ if (dev->m && dev->m->type == TYPE_MOUNT) {
+ int err = mount_device(dev, type);
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+ } else
+ return mount_device(the_dev, type);
}
-
- if (config_load(NULL))
- return -1;
- cache_load(0);
-
- return mount_device(find_block_info(NULL, NULL, path), type);
+ ULOG_ERR("Unkown action %s\n", action);
+ return -1;
}
static int main_hotplug(int argc, char **argv)
static int main_autofs(int argc, char **argv)
{
+ int err = 0;
+
if (argc < 3)
return -1;
if (!strcmp(argv[2], "start")) {
+ struct device *dev;
struct probe_info *pr;
if (config_load(NULL))
return -1;
cache_load(0);
- list_for_each_entry(pr, &devices, list) {
- struct mount *m = find_block(pr->uuid, pr->label, NULL, NULL);
+ vlist_for_each_element(&devices, dev, node) {
+ struct mount *m;
- if (m && m->autofs)
- mount_device(pr, TYPE_HOTPLUG);
- else
- blockd_notify(pr->dev, m, pr);
+ pr = dev->pr;
+ if (!strcmp(pr->type, "swap"))
+ continue;
+
+ m = dev->m;
+ if (m && m->extroot)
+ continue;
+
+ blockd_notify(pr->dev, m, pr);
}
- return 0;
+ } else if (!strcmp(argv[2], "available")) {
+ err = hotplug_call_mount("add", argv[3]);
+ } else if (!strcmp(argv[2], "unavailable")) {
+ err = hotplug_call_mount("remove", argv[3]);
+ } 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)
static int check_extroot(char *path)
{
- struct probe_info *pr = NULL;
+ struct device *dev;
+ struct probe_info *pr;
char devpath[32];
#ifdef UBIFS_EXTROOT
}
#endif
- list_for_each_entry(pr, &devices, list) {
+ vlist_for_each_element(&devices, dev, node) {
+ pr = dev->pr;
if (!strcmp(pr->dev, devpath)) {
struct stat s;
FILE *fp = NULL;
char overlay[] = "/tmp/extroot/overlay";
char mnt[] = "/tmp/extroot/mnt";
char *path = mnt;
+ struct device *dev;
struct probe_info *pr;
struct mount *m;
int err = -1;
}
/* Find block device pointed by the mount config */
- pr = find_block_info(m->uuid, m->label, m->device);
+ dev = find_block_device(m->uuid, m->label, m->device);
- if (!pr && delay_root){
+ if (!dev && delay_root){
ULOG_INFO("extroot: device not present, retrying in %u seconds\n", delay_root);
sleep(delay_root);
make_devs();
- cache_load(0);
- pr = find_block_info(m->uuid, m->label, m->device);
+ cache_load(1);
+ dev = find_block_device(m->uuid, m->label, m->device);
}
- if (pr) {
+ if (dev) {
+ pr = dev->pr;
if (strncmp(pr->type, "ext", 3) &&
strncmp(pr->type, "f2fs", 4) &&
strncmp(pr->type, "btrfs", 5) &&
static int main_extroot(int argc, char **argv)
{
- struct probe_info *pr;
char blkdev_path[32] = { 0 };
int err = -1;
#ifdef UBIFS_EXTROOT
/* Start with looking for MTD partition */
find_block_mtd("\"rootfs_data\"", blkdev_path, sizeof(blkdev_path));
if (blkdev_path[0]) {
- pr = find_block_info(NULL, NULL, blkdev_path);
- if (pr && !strcmp(pr->type, "jffs2")) {
+ struct device *dev = find_block_device(NULL, NULL, blkdev_path);
+ if (dev && !strcmp(dev->pr->type, "jffs2")) {
char cfg[] = "/tmp/jffs_cfg";
/*
static int main_mount(int argc, char **argv)
{
- struct probe_info *pr;
+ struct device *dev;
if (config_load(NULL))
return -1;
cache_load(1);
- list_for_each_entry(pr, &devices, list)
- mount_device(pr, TYPE_DEV);
+ vlist_for_each_element(&devices, dev, node)
+ mount_device(dev, TYPE_DEV);
handle_swapfiles(true);
static int main_umount(int argc, char **argv)
{
+ struct device *dev;
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) {
+
+ if (argc == 3)
+ all = !strcmp(argv[2], "-a");
+
+ vlist_for_each_element_reverse(&devices, dev, node) {
struct mount *m;
+ pr = dev->pr;
if (!strcmp(pr->type, "swap"))
continue;
- m = find_block(pr->uuid, pr->label, basename(pr->dev), NULL);
+ m = dev->m;
if (m && m->extroot)
continue;
- umount_device(pr->dev);
+ umount_device(pr->dev, TYPE_DEV, all);
}
return 0;
static int main_detect(int argc, char **argv)
{
- struct probe_info *pr;
+ struct device *dev;
cache_load(0);
printf("config 'global'\n");
printf("\toption\tauto_mount\t'1'\n");
printf("\toption\tdelay_root\t'5'\n");
printf("\toption\tcheck_fs\t'0'\n\n");
- list_for_each_entry(pr, &devices, list)
- print_block_uci(pr);
+ vlist_for_each_element(&devices, dev, node)
+ print_block_uci(dev->pr);
return 0;
}
static int main_info(int argc, char **argv)
{
int i;
- struct probe_info *pr;
+ struct device *dev;
cache_load(1);
if (argc == 2) {
- list_for_each_entry(pr, &devices, list)
- print_block_info(pr);
+ vlist_for_each_element(&devices, dev, node)
+ print_block_info(dev->pr);
return 0;
};
ULOG_ERR("%s is not a block device\n", argv[i]);
continue;
}
- pr = find_block_info(NULL, NULL, argv[i]);
- if (pr)
- print_block_info(pr);
+ dev = find_block_device(NULL, NULL, argv[i]);
+ if (dev)
+ print_block_info(dev->pr);
}
return 0;
FILE *fp;
char *lineptr;
size_t s;
+ struct device *dev;
struct probe_info *pr;
int flags = 0;
int pri;
return 0;
case 'a':
cache_load(0);
- list_for_each_entry(pr, &devices, list) {
+ vlist_for_each_element(&devices, dev, node) {
+ pr = dev->pr;
if (strcmp(pr->type, "swap"))
continue;
if (swapon(pr->dev, 0))
default:
return swapon_usage();
}
-
}
if (optind != (argc - 1))