summaryrefslogtreecommitdiffstats
path: root/libmount/src
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src')
-rw-r--r--libmount/src/Makemodule.am2
-rw-r--r--libmount/src/cache.c14
-rw-r--r--libmount/src/context.c54
-rw-r--r--libmount/src/context_mount.c34
-rw-r--r--libmount/src/context_umount.c2
-rw-r--r--libmount/src/hook_loopdev.c20
-rw-r--r--libmount/src/hook_mount.c84
-rw-r--r--libmount/src/hook_veritydev.c39
-rw-r--r--libmount/src/libmount.h.in4
-rw-r--r--libmount/src/libmount.sym8
-rw-r--r--libmount/src/lock.c57
-rw-r--r--libmount/src/monitor.c79
-rw-r--r--libmount/src/mountP.h48
-rw-r--r--libmount/src/optlist.c66
-rw-r--r--libmount/src/optstr.c130
-rw-r--r--libmount/src/tab.c45
-rw-r--r--libmount/src/tab_diff.c6
-rw-r--r--libmount/src/tab_update.c318
-rw-r--r--libmount/src/utils.c99
-rw-r--r--libmount/src/version.c3
20 files changed, 850 insertions, 262 deletions
diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am
index 367bc46..f8cedbd 100644
--- a/libmount/src/Makemodule.am
+++ b/libmount/src/Makemodule.am
@@ -101,7 +101,7 @@ check_PROGRAMS += test_mount_context test_mount_context_mount
check_PROGRAMS += test_mount_monitor
endif
-libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS)
+libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS)
libmount_tests_ldflags = -static
libmount_tests_ldadd = libmount.la libblkid.la $(LDADD) $(REALTIME_LIBS)
diff --git a/libmount/src/cache.c b/libmount/src/cache.c
index 6286aeb..b795634 100644
--- a/libmount/src/cache.c
+++ b/libmount/src/cache.c
@@ -202,7 +202,7 @@ static int cache_add_entry(struct libmnt_cache *cache, char *key,
if (cache->nents == cache->nallocs) {
size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
- e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
+ e = reallocarray(cache->ents, sz, sizeof(struct mnt_cache_entry));
if (!e)
return -ENOMEM;
cache->ents = e;
@@ -748,7 +748,9 @@ char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
#ifdef TEST_PROGRAM
-static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
+static int test_resolve_path(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
@@ -771,7 +773,9 @@ static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
+static int test_resolve_spec(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
@@ -794,7 +798,9 @@ static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_read_tags(struct libmnt_test *ts __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
{
char line[BUFSIZ];
struct libmnt_cache *cache;
diff --git a/libmount/src/context.c b/libmount/src/context.c
index 0cd3201..952287a 100644
--- a/libmount/src/context.c
+++ b/libmount/src/context.c
@@ -107,7 +107,7 @@ void mnt_free_context(struct libmnt_context *cxt)
mnt_unref_optlist(cxt->optlist_saved);
mnt_unref_optlist(cxt->optlist);
- mnt_free_lock(cxt->lock);
+ mnt_unref_lock(cxt->lock);
mnt_free_update(cxt->update);
mnt_context_set_target_ns(cxt, NULL);
@@ -314,6 +314,8 @@ int mnt_context_reset_status(struct libmnt_context *cxt)
if (!cxt)
return -EINVAL;
+ reset_syscall_status(cxt);
+
cxt->syscall_status = 1; /* means not called yet */
cxt->helper_exec_status = 1;
cxt->helper_status = 0;
@@ -549,10 +551,10 @@ int mnt_context_enable_onlyonce(struct libmnt_context *cxt, int enable)
}
/**
- * mnt_context_is_lazy:
+ * mnt_context_is_onlyonce:
* @cxt: mount context
*
- * Returns: 1 if lazy umount is enabled or 0
+ * Returns: 1 if only-once mount is enabled or 0
*/
int mnt_context_is_onlyonce(struct libmnt_context *cxt)
{
@@ -2179,6 +2181,10 @@ int mnt_context_prepare_update(struct libmnt_context *cxt)
rc = mnt_update_set_fs(cxt->update, flags,
NULL, cxt->fs);
+ if (mnt_update_is_ready(cxt->update)) {
+ DBG(CXT, ul_debugobj(cxt, "update is ready"));
+ mnt_update_start(cxt->update);
+ }
return rc < 0 ? rc : 0;
}
@@ -2207,9 +2213,9 @@ int mnt_context_update_tabs(struct libmnt_context *cxt)
&& mnt_context_get_helper_status(cxt) == 0
&& mnt_context_utab_writable(cxt)) {
- if (mnt_update_already_done(cxt->update, cxt->lock)) {
+ if (mnt_update_already_done(cxt->update)) {
DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated"));
- goto end;
+ goto emit;
}
} else if (cxt->helper) {
DBG(CXT, ul_debugobj(cxt, "don't update: external helper"));
@@ -2225,8 +2231,13 @@ int mnt_context_update_tabs(struct libmnt_context *cxt)
}
rc = mnt_update_table(cxt->update, cxt->lock);
+emit:
+ if (rc == 0 && !mnt_context_within_helper(cxt))
+ mnt_update_emit_event(cxt->update);
end:
+ mnt_update_end(cxt->update);
+
if (!mnt_context_switch_ns(cxt, ns_old))
return -MNT_ERR_NAMESPACE;
return rc;
@@ -2737,7 +2748,6 @@ int mnt_context_get_excode(
return rc;
}
-
/**
* mnt_context_init_helper
* @cxt: mount context
@@ -2773,6 +2783,14 @@ int mnt_context_init_helper(struct libmnt_context *cxt, int action,
return rc;
}
+/*
+ * libmount used in /sbin/[u]mount.<type> helper
+ */
+int mnt_context_within_helper(struct libmnt_context *cxt)
+{
+ return cxt && (cxt->flags & MNT_FL_HELPER);
+}
+
/**
* mnt_context_helper_setopt:
* @cxt: context
@@ -2850,7 +2868,7 @@ static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid)
if (!cxt)
return -EINVAL;
- pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1);
+ pids = reallocarray(cxt->children, cxt->nchildren + 1, sizeof(pid_t));
if (!pids)
return -ENOMEM;
@@ -3166,7 +3184,8 @@ struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt)
#ifdef TEST_PROGRAM
-static int test_search_helper(struct libmnt_test *ts, int argc, char *argv[])
+static int test_search_helper(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
const char *type;
@@ -3200,7 +3219,8 @@ static void lock_fallback(void)
mnt_unlock_file(lock);
}
-static int test_mount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3250,7 +3270,8 @@ static int test_mount(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_umount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_umount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3305,7 +3326,8 @@ err:
return rc;
}
-static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_flags(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int idx = 1, rc = 0;
struct libmnt_context *cxt;
@@ -3343,7 +3365,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[])
+static int test_cxtsync(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_fs *fs;
@@ -3387,7 +3410,8 @@ static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mountall(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_iter *itr;
@@ -3405,10 +3429,8 @@ static int test_mountall(struct libmnt_test *ts, int argc, char *argv[])
mnt_context_set_options_pattern(cxt, argv[idx + 1]);
idx += 2;
}
- if (argv[idx] && !strcmp(argv[idx], "-t")) {
+ if (argv[idx] && !strcmp(argv[idx], "-t"))
mnt_context_set_fstype_pattern(cxt, argv[idx + 1]);
- idx += 2;
- }
}
while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index 41986e7..478a9fd 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -1571,6 +1571,12 @@ int mnt_context_get_mount_excode(
*/
syserr = mnt_context_get_syscall_errno(cxt);
+ if (buf && cxt->syscall_errmsg) {
+ snprintf(buf, bufsz, _("%s system call failed: %s"),
+ cxt->syscall_name ? : "mount",
+ cxt->syscall_errmsg);
+ return MNT_EX_FAIL;
+ }
switch(syserr) {
case EPERM:
@@ -1617,10 +1623,8 @@ int mnt_context_get_mount_excode(
return MNT_EX_SUCCESS;
if (buf)
snprintf(buf, bufsz, _("special device %s does not exist"), src);
- } else if (buf) {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ } else
+ goto generic_error;
break;
case ENOTDIR:
@@ -1633,10 +1637,8 @@ int mnt_context_get_mount_excode(
if (buf)
snprintf(buf, bufsz, _("special device %s does not exist "
"(a path prefix is not a directory)"), src);
- } else if (buf) {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ } else
+ goto generic_error;
break;
case EINVAL:
@@ -1717,10 +1719,8 @@ int mnt_context_get_mount_excode(
snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src);
else if (mflags & MS_BIND)
snprintf(buf, bufsz, _("bind %s failed"), src);
- else {
- errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
- }
+ else
+ goto generic_error;
break;
case ENOMEDIUM:
@@ -1740,9 +1740,11 @@ int mnt_context_get_mount_excode(
/* fallthrough */
default:
+ generic_error:
if (buf) {
errno = syserr;
- snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
+ snprintf(buf, bufsz, _("%s system call failed: %m"),
+ cxt->syscall_name ? : "mount");
}
break;
}
@@ -1752,7 +1754,8 @@ int mnt_context_get_mount_excode(
#ifdef TEST_PROGRAM
-static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
+static int test_perms(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_optlist *ls;
@@ -1795,7 +1798,8 @@ static int test_perms(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_fixopts(struct libmnt_test *ts, int argc, char *argv[])
+static int test_fixopts(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_context *cxt;
struct libmnt_optlist *ls;
diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c
index 26394d5..79b6237 100644
--- a/libmount/src/context_umount.c
+++ b/libmount/src/context_umount.c
@@ -270,7 +270,7 @@ static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tg
*/
if (mnt_context_is_restricted(cxt)
|| *tgt != '/'
- || (cxt->flags & MNT_FL_HELPER)
+ || mnt_context_within_helper(cxt)
|| mnt_context_is_force(cxt)
|| mnt_context_is_lazy(cxt)
|| mnt_context_is_nocanonicalize(cxt)
diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c
index 8c8f7f2..597b933 100644
--- a/libmount/src/hook_loopdev.c
+++ b/libmount/src/hook_loopdev.c
@@ -140,10 +140,6 @@ static int setup_loopdev(struct libmnt_context *cxt,
DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file));
- ol = mnt_context_get_optlist(cxt);
- if (!ol)
- return -ENOMEM;
-
if (mnt_optlist_is_rdonly(ol)) {
DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag"));
lo_flags |= LO_FLAGS_READ_ONLY;
@@ -356,15 +352,19 @@ success:
*/
mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux);
- /* we have to keep the device open until mount(1),
- * otherwise it will be auto-cleared by kernel
+ /*
+ * We have to keep the device open until mount(1), otherwise it
+ * will be auto-cleared by kernel. However we don't want to
+ * keep writeable fd as kernel wants to block all writers to
+ * the device being mounted (in the more hardened
+ * configurations). So grab read-only fd instead.
*/
- hd->loopdev_fd = loopcxt_get_fd(&lc);
+ hd->loopdev_fd = open(lc.device, O_RDONLY | O_CLOEXEC);
if (hd->loopdev_fd < 0) {
- DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD"));
+ DBG(LOOP,
+ ul_debugobj(cxt, "failed to reopen loopdev FD"));
rc = -errno;
- } else
- loopcxt_set_fd(&lc, -1, 0);
+ }
}
done:
loopcxt_deinit(&lc);
diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c
index dc3dfa7..f0cc381 100644
--- a/libmount/src/hook_mount.c
+++ b/libmount/src/hook_mount.c
@@ -65,6 +65,42 @@ static void close_sysapi_fds(struct libmnt_sysapi *api)
api->fd_tree = api->fd_fs = -1;
}
+static void save_fd_messages(struct libmnt_context *cxt, int fd)
+{
+ uint8_t buf[BUFSIZ];
+ int rc;
+
+ free(cxt->syscall_errmsg);
+ cxt->syscall_errmsg = NULL;
+
+ while ((rc = read(fd, buf, sizeof(buf))) != -1) {
+ if (rc > 0 && buf[rc - 1] == '\n')
+ buf[rc - 1] = '\0';
+ DBG(CXT, ul_debug("message from kernel: \"%*s\"", rc, buf));
+
+ if (rc < 3 || strncmp((char *) buf, "e ", 2) != 0)
+ continue;
+ if (cxt->syscall_errmsg)
+ strappend(&cxt->syscall_errmsg, "; ");
+
+ strappend(&cxt->syscall_errmsg, ((char *) buf) + 2);
+ }
+}
+
+static void hookset_set_syscall_status(struct libmnt_context *cxt,
+ const char *name, int x)
+{
+ struct libmnt_sysapi *api;
+
+ set_syscall_status(cxt, name, x);
+
+ if (!x) {
+ api = get_sysapi(cxt);
+ if (api && api->fd_fs >= 0)
+ save_fd_messages(cxt, api->fd_fs);
+ }
+}
+
/*
* This hookset uses 'struct libmnt_sysapi' (mountP.h) as hookset data.
*/
@@ -122,26 +158,33 @@ static inline int fsconfig_set_value(
const char *name, const char *value)
{
int rc;
- char *p = NULL;
+ char *s = NULL;
+ /* "\," is a way to use comma in values, let's remove \ escape */
if (value && strstr(value, "\\,")) {
- p = strdup(value);
- if (!p)
- return -EINVAL;
+ char *x, *p;
- strrem(p, '\\');
- value = p;
+ s = strdup(value);
+ if (!s)
+ return -EINVAL;
+ for (x = p = s; *x; p++, x++) {
+ if (*x == '\\' && *(x + 1) == ',')
+ x++;
+ *p = *x;
+ }
+ *p = '\0';
+ value = s;
}
DBG(HOOK, ul_debugobj(hs, " fsconfig(name=\"%s\" value=\"%s\")", name,
value ? : ""));
if (value) {
rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0);
- free(p);
+ free(s);
} else
rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
return rc;
}
@@ -181,6 +224,9 @@ static int configure_superblock(struct libmnt_context *cxt,
/* Ignore VFS flags, userspace and external options */
continue;
+ if (!value && mnt_opt_is_sepnodata(opt))
+ value = ""; /* force use the value as string */
+
rc = fsconfig_set_value(cxt, hs, fd, name, value);
if (rc != 0)
goto done;
@@ -206,7 +252,7 @@ static int open_fs_configuration_context(struct libmnt_context *cxt,
DBG(HOOK, ul_debug(" fsopen(%s)", type));
api->fd_fs = fsopen(type, FSOPEN_CLOEXEC);
- set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
+ hookset_set_syscall_status(cxt, "fsopen", api->fd_fs >= 0);
if (api->fd_fs < 0)
return -errno;
api->is_new_fs = 1;
@@ -245,7 +291,7 @@ static int open_mount_tree(struct libmnt_context *cxt, const char *path, unsigne
oflg & OPEN_TREE_CLONE ? " clone" : "",
oflg & AT_RECURSIVE ? " recursive" : ""));
fd = open_tree(AT_FDCWD, path, oflg);
- set_syscall_status(cxt, "open_tree", fd >= 0);
+ hookset_set_syscall_status(cxt, "open_tree", fd >= 0);
return fd;
}
@@ -285,19 +331,19 @@ static int hook_create_mount(struct libmnt_context *cxt,
DBG(HOOK, ul_debugobj(hs, "init FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_SET_STRING, "source", src, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
if (!rc)
rc = configure_superblock(cxt, hs, api->fd_fs, 0);
if (!rc) {
DBG(HOOK, ul_debugobj(hs, "create FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
}
if (!rc) {
api->fd_tree = fsmount(api->fd_fs, FSMOUNT_CLOEXEC, 0);
- set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
+ hookset_set_syscall_status(cxt, "fsmount", api->fd_tree >= 0);
if (api->fd_tree < 0)
rc = -errno;
}
@@ -346,7 +392,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt,
if (api->fd_fs < 0) {
api->fd_fs = fspick(api->fd_tree, "", FSPICK_EMPTY_PATH |
FSPICK_NO_AUTOMOUNT);
- set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
+ hookset_set_syscall_status(cxt, "fspick", api->fd_fs >= 0);
if (api->fd_fs < 0)
return -errno;
}
@@ -355,7 +401,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt,
if (!rc) {
DBG(HOOK, ul_debugobj(hs, "re-configurate FS"));
rc = fsconfig(api->fd_fs, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0);
- set_syscall_status(cxt, "fsconfig", rc == 0);
+ hookset_set_syscall_status(cxt, "fsconfig", rc == 0);
}
DBG(HOOK, ul_debugobj(hs, "reconf FS done [rc=%d]", rc));
@@ -393,7 +439,7 @@ static int set_vfsflags(struct libmnt_context *cxt,
errno = 0;
rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr));
- set_syscall_status(cxt, "mount_setattr", rc == 0);
+ hookset_set_syscall_status(cxt, "mount_setattr", rc == 0);
if (rc && errno == EINVAL)
return -MNT_ERR_APPLYFLAGS;
@@ -485,7 +531,7 @@ static int hook_set_propagation(struct libmnt_context *cxt,
(uint64_t) attr.propagation));
rc = mount_setattr(api->fd_tree, "", flgs, &attr, sizeof(attr));
- set_syscall_status(cxt, "mount_setattr", rc == 0);
+ hookset_set_syscall_status(cxt, "mount_setattr", rc == 0);
if (rc && errno == EINVAL)
return -MNT_ERR_APPLYFLAGS;
@@ -527,7 +573,7 @@ static int hook_attach_target(struct libmnt_context *cxt,
}
rc = move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH);
- set_syscall_status(cxt, "move_mount", rc == 0);
+ hookset_set_syscall_status(cxt, "move_mount", rc == 0);
return rc == 0 ? 0 : -errno;
}
@@ -613,7 +659,7 @@ static int init_sysapi(struct libmnt_context *cxt,
else if (!fsopen_is_supported()) {
errno = ENOSYS;
rc = -errno;
- set_syscall_status(cxt, "fsopen", rc == 0);
+ hookset_set_syscall_status(cxt, "fsopen", rc == 0);
}
if (rc < 0)
goto fail;
diff --git a/libmount/src/hook_veritydev.c b/libmount/src/hook_veritydev.c
index f91778a..6a9e644 100644
--- a/libmount/src/hook_veritydev.c
+++ b/libmount/src/hook_veritydev.c
@@ -349,25 +349,8 @@ static int setup_veritydev( struct libmnt_context *cxt,
backing_file = mnt_fs_get_srcpath(cxt->fs);
if (!backing_file)
return -EINVAL;
- else {
- /* To avoid clashes, prefix libmnt_ to all mapper devices */
- char *p, *path = strdup(backing_file);
- if (!path)
- return -ENOMEM;
-
- p = stripoff_last_component(path);
- if (p)
- mapper_device = calloc(strlen(p) + sizeof("libmnt_"), sizeof(char));
- if (mapper_device) {
- strcat(mapper_device, "libmnt_");
- strcat(mapper_device, p);
- }
- free(path);
- if (!mapper_device)
- return -ENOMEM;
- }
- DBG(HOOK, ul_debugobj(hs, "verity: setup for %s [%s]", backing_file, mapper_device));
+ DBG(HOOK, ul_debugobj(hs, "verity: setup for %s", backing_file));
/* verity.hashdevice= */
if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace)))
@@ -467,6 +450,13 @@ static int setup_veritydev( struct libmnt_context *cxt,
rc = -EINVAL;
}
+ /* To avoid clashes, use the roothash as the device name. This allows us to reuse already open devices, saving
+ * a lot of time and resources when there are duplicated mounts. If the roothash is the same, then the volumes
+ * are also guaranteed to be identical. This is what systemd also does, so we can deduplicate across the whole
+ * system. */
+ if (asprintf(&mapper_device, "%s-verity", root_hash) < 0)
+ rc = -ENOMEM;
+
if (!rc)
rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) );
if (rc)
@@ -506,7 +496,9 @@ static int setup_veritydev( struct libmnt_context *cxt,
* If the mapper device already exists, and if libcryptsetup supports it, get the root
* hash associated with the existing one and compare it with the parameter passed by
* the user. If they match, then we can be sure the user intended to mount the exact
- * same device, and simply reuse it and return success.
+ * same device, and simply reuse it and return success. Although we use the roothash
+ * as the device mapper name, and root privileges are required to open them, better be
+ * safe than sorry, so double check that the actual root hash used matches.
* The kernel does the refcounting for us.
* If libcryptsetup does not support getting the root hash out of an existing device,
* then return an error and tell the user that the device is already in use.
@@ -562,15 +554,10 @@ static int setup_veritydev( struct libmnt_context *cxt,
}
if (!rc) {
- hsd->devname = calloc(strlen(mapper_device)
- + sizeof(_PATH_DEV_MAPPER) + 2, sizeof(char));
- if (!hsd->devname)
+ if (asprintf(&hsd->devname, _PATH_DEV_MAPPER "/%s", mapper_device) == -1)
rc = -ENOMEM;
- else {
- strcat(hsd->devname, _PATH_DEV_MAPPER "/");
- strcat(hsd->devname, mapper_device);
+ else
rc = mnt_fs_set_source(cxt->fs, hsd->devname);
- }
}
done:
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
index 06c2704..d893c26 100644
--- a/libmount/src/libmount.h.in
+++ b/libmount/src/libmount.h.in
@@ -444,6 +444,8 @@ extern const struct libmnt_optmap *mnt_get_builtin_optmap(int id);
/* lock.c */
extern struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
__ul_attribute__((warn_unused_result));
+extern void mnt_ref_lock(struct libmnt_lock *ml);
+extern void mnt_unref_lock(struct libmnt_lock *ml);
extern void mnt_free_lock(struct libmnt_lock *ml);
extern void mnt_unlock_file(struct libmnt_lock *ml);
@@ -697,6 +699,8 @@ extern int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable);
extern int mnt_monitor_enable_userspace(struct libmnt_monitor *mn,
int enable, const char *filename);
+extern int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable);
+
extern int mnt_monitor_get_fd(struct libmnt_monitor *mn);
extern int mnt_monitor_close_fd(struct libmnt_monitor *mn);
extern int mnt_monitor_wait(struct libmnt_monitor *mn, int timeout);
diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym
index 715bb5c..2b6b12d 100644
--- a/libmount/src/libmount.sym
+++ b/libmount/src/libmount.sym
@@ -370,8 +370,14 @@ MOUNT_2_38 {
MOUNT_2_39 {
mnt_cache_set_sbprobe;
mnt_context_enable_onlyonce;
- mnt_context_is_lazy;
+ mnt_context_is_onlyonce;
mnt_context_enable_noautofs;
mnt_table_enable_noautofs;
mnt_table_is_noautofs;
} MOUNT_2_38;
+
+MOUNT_2_40 {
+ mnt_ref_lock;
+ mnt_unref_lock;
+ mnt_monitor_veil_kernel;
+} MOUNT_2_39;
diff --git a/libmount/src/lock.c b/libmount/src/lock.c
index 4835406..8aca8a7 100644
--- a/libmount/src/lock.c
+++ b/libmount/src/lock.c
@@ -36,6 +36,7 @@
* lock handler
*/
struct libmnt_lock {
+ int refcount; /* reference counter */
char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
int lockfile_fd; /* lock file descriptor */
@@ -73,6 +74,7 @@ struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id __attribute__((_
if (!ml)
goto err;
+ ml->refcount = 1;
ml->lockfile_fd = -1;
ml->lockfile = lo;
@@ -89,18 +91,57 @@ err:
* mnt_free_lock:
* @ml: struct libmnt_lock handler
*
- * Deallocates mnt_lock.
+ * Deallocates libmnt_lock. This function does not care about reference count. Don't
+ * use this function directly -- it's better to use mnt_unref_lock().
+ *
+ * The reference counting is supported since util-linux v2.40.
*/
void mnt_free_lock(struct libmnt_lock *ml)
{
if (!ml)
return;
- DBG(LOCKS, ul_debugobj(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
+
+ DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%d]",
+ ml->locked ? " !!! LOCKED !!!" : "",
+ ml->refcount));
free(ml->lockfile);
free(ml);
}
/**
+ * mnt_ref_lock:
+ * @ml: lock pointer
+ *
+ * Increments reference counter.
+ *
+ * Since: 2.40
+ */
+void mnt_ref_lock(struct libmnt_lock *ml)
+{
+ if (ml) {
+ ml->refcount++;
+ /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/
+ }
+}
+
+/**
+ * mnt_unref_lock:
+ * @ml: lock pointer
+ *
+ * De-increments reference counter, on zero the @ml is automatically
+ * deallocated by mnt_free_lock).
+ */
+void mnt_unref_lock(struct libmnt_lock *ml)
+{
+ if (ml) {
+ ml->refcount--;
+ /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/
+ if (ml->refcount <= 0)
+ mnt_free_lock(ml);
+ }
+}
+
+/**
* mnt_lock_block_signals:
* @ml: struct libmnt_lock handler
* @enable: TRUE/FALSE
@@ -146,7 +187,7 @@ static int lock_simplelock(struct libmnt_lock *ml)
const char *lfile;
int rc;
struct stat sb;
- const mode_t lock_mask = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+ const mode_t lock_mask = S_IRUSR|S_IWUSR;
assert(ml);
@@ -161,8 +202,7 @@ static int lock_simplelock(struct libmnt_lock *ml)
sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
}
- ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC,
- S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, lock_mask);
if (ml->lockfile_fd < 0) {
rc = -errno;
goto err;
@@ -287,7 +327,7 @@ static void clean_lock(void)
if (!lock)
return;
mnt_unlock_file(lock);
- mnt_free_lock(lock);
+ mnt_unref_lock(lock);
}
static void __attribute__((__noreturn__)) sig_handler(int sig)
@@ -295,7 +335,8 @@ static void __attribute__((__noreturn__)) sig_handler(int sig)
errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
}
-static int test_lock(struct libmnt_test *ts, int argc, char *argv[])
+static int test_lock(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
time_t synctime = 0;
unsigned int usecs;
@@ -367,7 +408,7 @@ static int test_lock(struct libmnt_test *ts, int argc, char *argv[])
increment_data(datafile, verbose, l);
mnt_unlock_file(lock);
- mnt_free_lock(lock);
+ mnt_unref_lock(lock);
lock = NULL;
/* The mount command usually finishes after a mtab update. We
diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c
index f99751e..941f5d5 100644
--- a/libmount/src/monitor.c
+++ b/libmount/src/monitor.c
@@ -64,6 +64,8 @@ struct libmnt_monitor {
int fd; /* public monitor file descriptor */
struct list_head ents;
+
+ unsigned int kernel_veiled: 1;
};
struct monitor_opers {
@@ -226,18 +228,16 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
assert(me->path);
/*
- * libmount uses rename(2) to atomically update utab, monitor
- * rename changes is too tricky. It seems better to monitor utab
- * lockfile close.
+ * libmount uses utab.event file to monitor and control utab updates
*/
- if (asprintf(&filename, "%s.lock", me->path) <= 0) {
- rc = -errno;
+ if (asprintf(&filename, "%s.event", me->path) <= 0) {
+ rc = -ENOMEM;
goto done;
}
- /* try lock file if already exists */
+ /* try event file if already exists */
errno = 0;
- wd = inotify_add_watch(me->fd, filename, IN_CLOSE_NOWRITE);
+ wd = inotify_add_watch(me->fd, filename, IN_CLOSE_WRITE);
if (wd >= 0) {
DBG(MONITOR, ul_debug(" added inotify watch for %s [fd=%d]", filename, wd));
rc = 0;
@@ -256,7 +256,7 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd)
if (!*filename)
break;
- /* try directory where is the lock file */
+ /* try directory where is the event file */
errno = 0;
wd = inotify_add_watch(me->fd, filename, IN_CREATE|IN_ISDIR);
if (wd >= 0) {
@@ -339,10 +339,10 @@ static int userspace_event_verify(struct libmnt_monitor *mn,
e = (const struct inotify_event *) p;
DBG(MONITOR, ul_debugobj(mn, " inotify event 0x%x [%s]\n", e->mask, e->len ? e->name : ""));
- if (e->mask & IN_CLOSE_NOWRITE)
+ if (e->mask & IN_CLOSE_WRITE)
status = 1;
else {
- /* event on lock file */
+ /* add watch for the event file */
userspace_add_watch(me, &status, &fd);
if (fd != e->wd) {
@@ -473,12 +473,28 @@ err:
return rc;
}
+static int kernel_event_verify(struct libmnt_monitor *mn,
+ struct monitor_entry *me)
+{
+ int status = 1;
+
+ if (!mn || !me || me->fd < 0)
+ return 0;
+
+ if (mn->kernel_veiled && access(MNT_PATH_UTAB ".act", F_OK) == 0) {
+ status = 0;
+ DBG(MONITOR, ul_debugobj(mn, "kernel event veiled"));
+ }
+ return status;
+}
+
/*
* kernel monitor operations
*/
static const struct monitor_opers kernel_opers = {
.op_get_fd = kernel_monitor_get_fd,
.op_close_fd = kernel_monitor_close_fd,
+ .op_event_verify = kernel_event_verify
};
/**
@@ -547,6 +563,28 @@ err:
return rc;
}
+/**
+ * mnt_monitor_veil_kernel:
+ * @mn: monitor instance
+ * @enable: 1 or 0
+ *
+ * Force monitor to ignore kernel events if the same mount/umount operation
+ * will generate an userspace event later. The kernel-only mount operation will
+ * be not affected.
+ *
+ * Return: 0 on success and <0 on error.
+ *
+ * Since: 2.40
+ */
+int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable)
+{
+ if (!mn)
+ return -EINVAL;
+
+ mn->kernel_veiled = enable ? 1 : 0;
+ return 0;
+}
+
/*
* Add/Remove monitor entry to/from monitor epoll.
*/
@@ -854,6 +892,8 @@ static struct libmnt_monitor *create_test_monitor(int argc, char *argv[])
warn("failed to initialize kernel monitor");
goto err;
}
+ } else if (strcmp(argv[i], "veil") == 0) {
+ mnt_monitor_veil_kernel(mn, 1);
}
}
if (i == 1) {
@@ -870,7 +910,8 @@ err:
/*
* create a monitor and add the monitor fd to epoll
*/
-static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int cleanup)
+static int __test_epoll(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[], int cleanup)
{
int fd, efd = -1, rc = -1;
struct epoll_event ev;
@@ -900,12 +941,14 @@ static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int clea
goto done;
}
- printf("waiting for changes...\n");
do {
const char *filename = NULL;
struct epoll_event events[1];
- int n = epoll_wait(efd, events, 1, -1);
+ int n;
+
+ printf("waiting for changes...\n");
+ n = epoll_wait(efd, events, 1, -1);
if (n < 0) {
rc = -errno;
warn("polling error");
@@ -947,7 +990,8 @@ static int test_epoll_cleanup(struct libmnt_test *ts, int argc, char *argv[])
/*
* create a monitor and wait for a change
*/
-static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
+static int test_wait(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *filename;
struct libmnt_monitor *mn = create_test_monitor(argc, argv);
@@ -962,6 +1006,7 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
while (mnt_monitor_next_change(mn, &filename, NULL) == 0)
printf(" %s: change detected\n", filename);
+ printf("waiting for changes...\n");
}
mnt_unref_monitor(mn);
return 0;
@@ -970,9 +1015,9 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[])
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
- { "--epoll", test_epoll, "<userspace kernel ...> monitor in epoll" },
- { "--epoll-clean", test_epoll_cleanup, "<userspace kernel ...> monitor in epoll and clean events" },
- { "--wait", test_wait, "<userspace kernel ...> monitor wait function" },
+ { "--epoll", test_epoll, "<userspace kernel veil ...> monitor in epoll" },
+ { "--epoll-clean", test_epoll_cleanup, "<userspace kernel veil ...> monitor in epoll and clean events" },
+ { "--wait", test_wait, "<userspace kernel veil ...> monitor wait function" },
{ NULL }
};
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 339e276..fcc40bf 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -432,6 +432,7 @@ struct libmnt_context
int syscall_status; /* 1: not called yet, 0: success, <0: -errno */
const char *syscall_name; /* failed syscall name */
+ char *syscall_errmsg; /* message from kernel */
struct libmnt_ns ns_orig; /* original namespace */
struct libmnt_ns ns_tgt; /* target namespace */
@@ -479,22 +480,27 @@ struct libmnt_context
/* Flags usable with MS_BIND|MS_REMOUNT */
#define MNT_BIND_SETTABLE (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOATIME|MS_NODIRATIME|MS_RELATIME|MS_RDONLY|MS_NOSYMFOLLOW)
-#define set_syscall_status(_cxt, _name, _x) __extension__ ({ \
- if (!(_x)) { \
- DBG(CXT, ul_debug("syscall '%s' [%m]", _name)); \
- (_cxt)->syscall_status = -errno; \
- (_cxt)->syscall_name = (_name); \
- } else { \
- DBG(CXT, ul_debug("syscall '%s' [success]", _name)); \
- (_cxt)->syscall_status = 0; \
- } \
- })
-
-#define reset_syscall_status(_cxt) __extension__ ({ \
- DBG(CXT, ul_debug("reset syscall status")); \
- (_cxt)->syscall_status = 0; \
- (_cxt)->syscall_name = NULL; \
- })
+static inline void set_syscall_status(struct libmnt_context *cxt, const char *name, int x)
+{
+ if (!x) {
+ DBG(CXT, ul_debug("syscall '%s' [%m]", name));
+ cxt->syscall_status = -errno;
+ cxt->syscall_name = name;
+ } else {
+ DBG(CXT, ul_debug("syscall '%s' [success]", name));
+ cxt->syscall_status = 0;
+ }
+}
+
+static inline void reset_syscall_status(struct libmnt_context *cxt)
+{
+ DBG(CXT, ul_debug("reset syscall status"));
+ cxt->syscall_status = 0;
+ cxt->syscall_name = NULL;
+
+ free(cxt->syscall_errmsg);
+ cxt->syscall_errmsg = NULL;
+}
/* optmap.c */
extern const struct libmnt_optmap *mnt_optmap_get_entry(
@@ -506,6 +512,7 @@ extern const struct libmnt_optmap *mnt_optmap_get_entry(
/* optstr.c */
extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end);
+extern int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing);
extern int mnt_buffer_append_option(struct ul_buffer *buf,
const char *name, size_t namesz,
@@ -601,6 +608,7 @@ extern int mnt_opt_set_value(struct libmnt_opt *opt, const char *str);
extern int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num);
extern int mnt_opt_set_quoted_value(struct libmnt_opt *opt, const char *str);
extern int mnt_opt_is_external(struct libmnt_opt *opt);
+extern int mnt_opt_is_sepnodata(struct libmnt_opt *opt);
/* fs.c */
extern int mnt_fs_follow_optlist(struct libmnt_fs *fs, struct libmnt_optlist *ol);
@@ -617,6 +625,8 @@ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o);
extern int mnt_context_utab_writable(struct libmnt_context *cxt);
extern const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt);
+extern int mnt_context_within_helper(struct libmnt_context *cxt);
+
extern int mnt_context_get_mountinfo(struct libmnt_context *cxt, struct libmnt_table **tb);
extern int mnt_context_get_mountinfo_for_target(struct libmnt_context *cxt,
struct libmnt_table **mountinfo, const char *tgt);
@@ -658,9 +668,11 @@ extern int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs
extern struct libmnt_optlist *mnt_context_get_optlist(struct libmnt_context *cxt);
/* tab_update.c */
+extern int mnt_update_emit_event(struct libmnt_update *upd);
extern int mnt_update_set_filename(struct libmnt_update *upd, const char *filename);
-extern int mnt_update_already_done(struct libmnt_update *upd,
- struct libmnt_lock *lc);
+extern int mnt_update_already_done(struct libmnt_update *upd);
+extern int mnt_update_start(struct libmnt_update *upd);
+extern int mnt_update_end(struct libmnt_update *upd);
#if __linux__
/* btrfs.c */
diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c
index 0702ada..476acfd 100644
--- a/libmount/src/optlist.c
+++ b/libmount/src/optlist.c
@@ -46,6 +46,7 @@ struct libmnt_opt {
unsigned int external : 1, /* visible for external helpers only */
recursive : 1, /* recursive flag */
+ sepnodata : 1, /* value separator, but without data ("name=") */
is_linux : 1, /* defined in ls->linux_map (VFS attr) */
quoted : 1; /* name="value" */
};
@@ -438,6 +439,10 @@ static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
opt->value = strndup(value, valsz);
if (!opt->value)
goto fail;
+
+ } else if (value) {
+ /* separator specified, but empty value ("name=") */
+ opt->sepnodata = 1;
}
if (namesz) {
opt->name = strndup(name, namesz);
@@ -957,7 +962,8 @@ int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr,
continue;
rc = mnt_buffer_append_option(&buf,
opt->name, strlen(opt->name),
- opt->value,
+ opt->value ? opt->value :
+ opt->sepnodata ? "" : NULL,
opt->value ? strlen(opt->value) : 0,
opt->quoted);
if (rc)
@@ -1043,6 +1049,7 @@ struct libmnt_optlist *mnt_copy_optlist(struct libmnt_optlist *ls)
no->src = opt->src;
no->external = opt->external;
no->quoted = opt->quoted;
+ no->sepnodata = opt->sepnodata;
}
}
@@ -1184,6 +1191,11 @@ int mnt_opt_is_external(struct libmnt_opt *opt)
return opt && opt->external ? 1 : 0;
}
+int mnt_opt_is_sepnodata(struct libmnt_opt *opt)
+{
+ return opt->sepnodata;
+}
+
#ifdef TEST_PROGRAM
@@ -1241,7 +1253,8 @@ static inline unsigned long str2flg(const char *str)
return (unsigned long) strtox64_or_err(str, "connt convert string to flags");
}
-static int test_append_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1257,7 +1270,8 @@ static int test_append_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_prepend_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1273,7 +1287,8 @@ static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1289,7 +1304,8 @@ static int test_set_str(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1305,7 +1321,8 @@ static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
int rc;
@@ -1321,7 +1338,8 @@ static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_get_str(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get_str(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
const struct libmnt_optmap *map;
@@ -1380,7 +1398,8 @@ done:
return rc;
}
-static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get_flg(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_optlist *ol;
unsigned long flags = 0;
@@ -1397,6 +1416,36 @@ static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
+static int test_split(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
+{
+ struct libmnt_optlist *ol;
+ int rc;
+ struct libmnt_iter itr;
+ struct libmnt_opt *opt;
+ const char *name, *value;
+
+ if (argc != 2)
+ return -EINVAL;
+ rc = mk_optlist(&ol, argv[1]);
+ if (rc)
+ goto done;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) {
+ name = mnt_opt_get_name(opt);
+ value = mnt_opt_get_value(opt);
+
+ printf("%s = %s\n", name, value ?: "(null)");
+ }
+
+done:
+ mnt_unref_optlist(ol);
+ return rc;
+}
+
+
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
@@ -1407,6 +1456,7 @@ int main(int argc, char *argv[])
{ "--set-flg", test_set_flg, "<list> <flg> linux|user set to the list" },
{ "--get-str", test_get_str, "<list> [linux|user] all options in string" },
{ "--get-flg", test_get_flg, "<list> linux|user all options by flags" },
+ { "--split", test_split, "<list> split options into key-value pairs"},
{ NULL }
};
diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c
index e86b962..7ebc295 100644
--- a/libmount/src/optstr.c
+++ b/libmount/src/optstr.c
@@ -43,13 +43,16 @@ struct libmnt_optloc {
* Locates the first option that matches @name. The @end is set to the
* char behind the option (it means ',' or \0).
*
+ * @ol is optional.
+ *
* Returns negative number on parse error, 1 when not found and 0 on success.
*/
-static int mnt_optstr_locate_option(char *optstr, const char *name,
+static int mnt_optstr_locate_option(char *optstr,
+ const char *name, size_t namesz,
struct libmnt_optloc *ol)
{
char *n;
- size_t namesz, nsz;
+ size_t nsz;
int rc;
if (!optstr)
@@ -57,27 +60,30 @@ static int mnt_optstr_locate_option(char *optstr, const char *name,
assert(name);
- namesz = strlen(name);
+ if (!namesz)
+ namesz = strlen(name);
if (!namesz)
return 1;
do {
rc = ul_optstr_next(&optstr, &n, &nsz,
- &ol->value, &ol->valsz);
+ ol ? &ol->value : NULL,
+ ol ? &ol->valsz : NULL);
if (rc)
break;
if (namesz == nsz && strncmp(n, name, nsz) == 0) {
- ol->begin = n;
- ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
- ol->namesz = nsz;
+ if (ol) {
+ ol->begin = n;
+ ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr;
+ ol->namesz = nsz;
+ }
return 0;
}
} while(1);
return rc;
}
-
/**
* mnt_optstr_next_option:
* @optstr: option string, returns the position of the next option
@@ -223,7 +229,7 @@ int mnt_optstr_get_option(const char *optstr, const char *name,
if (!optstr || !name)
return -EINVAL;
- rc = mnt_optstr_locate_option((char *) optstr, name, &ol);
+ rc = mnt_optstr_locate_option((char *) optstr, name, 0, &ol);
if (!rc) {
if (value)
*value = ol.value;
@@ -255,7 +261,7 @@ int mnt_optstr_deduplicate_option(char **optstr, const char *name)
do {
struct libmnt_optloc ol = MNT_INIT_OPTLOC;
- rc = mnt_optstr_locate_option(opt, name, &ol);
+ rc = mnt_optstr_locate_option(opt, name, 0, &ol);
if (!rc) {
if (begin) {
/* remove the previous instance */
@@ -368,7 +374,7 @@ int mnt_optstr_set_option(char **optstr, const char *name, const char *value)
return -EINVAL;
if (*optstr)
- rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
if (rc < 0)
return rc; /* parse error */
if (rc == 1)
@@ -411,7 +417,7 @@ int mnt_optstr_remove_option(char **optstr, const char *name)
if (!optstr || !name)
return -EINVAL;
- rc = mnt_optstr_locate_option(*optstr, name, &ol);
+ rc = mnt_optstr_locate_option(*optstr, name, 0, &ol);
if (rc != 0)
return rc;
@@ -571,6 +577,51 @@ int mnt_optstr_get_options(const char *optstr, char **subset,
return rc;
}
+/*
+ * @optstr: string with comma separated list of options
+ * @wanted: options expected in @optstr
+ * @missing: returns options from @wanted which missing in @optstr (optional)
+ *
+ * Retursn: <0 on error, 0 on missing options, 1 if nothing is missing
+ */
+int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing)
+{
+ char *name, *val, *str = (char *) wanted;
+ size_t namesz = 0, valsz = 0;
+ struct ul_buffer buf = UL_INIT_BUFFER;
+ int rc = 0;
+
+ if (!wanted)
+ return 1;
+ if (missing) {
+ /* caller wants data, prepare buffer */
+ ul_buffer_set_chunksize(&buf, strlen(wanted) + 3); /* to call realloc() only once */
+ *missing = NULL;
+ }
+
+ while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) {
+
+ rc = mnt_optstr_locate_option((char *) optstr, name, namesz, NULL);
+ if (rc == 1) { /* not found */
+ if (!missing)
+ return 0;
+ rc = mnt_buffer_append_option(&buf, name, namesz, val, valsz, 0);
+ }
+ if (rc < 0)
+ break;
+ rc = 0;
+ }
+
+ if (!rc && missing) {
+ if (ul_buffer_is_empty(&buf))
+ rc = 1;
+ else
+ *missing = ul_buffer_get_data(&buf, NULL, NULL);
+ } else
+ ul_buffer_free_data(&buf);
+
+ return rc;
+}
/**
* mnt_optstr_get_flags:
@@ -910,7 +961,8 @@ int mnt_match_options(const char *optstr, const char *pattern)
}
#ifdef TEST_PROGRAM
-static int test_append(struct libmnt_test *ts, int argc, char *argv[])
+static int test_append(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -933,7 +985,8 @@ static int test_append(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_prepend(struct libmnt_test *ts, int argc, char *argv[])
+static int test_prepend(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -956,7 +1009,8 @@ static int test_prepend(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_split(struct libmnt_test *ts, int argc, char *argv[])
+static int test_split(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr, *user = NULL, *fs = NULL, *vfs = NULL;
int rc;
@@ -982,7 +1036,8 @@ static int test_split(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
+static int test_flags(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
int rc;
@@ -1010,7 +1065,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_apply(struct libmnt_test *ts, int argc, char *argv[])
+static int test_apply(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
int rc, map;
@@ -1042,7 +1098,8 @@ static int test_apply(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_set(struct libmnt_test *ts, int argc, char *argv[])
+static int test_set(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *value = NULL, *name;
char *optstr;
@@ -1065,7 +1122,8 @@ static int test_set(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_get(struct libmnt_test *ts, int argc, char *argv[])
+static int test_get(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr;
const char *name;
@@ -1094,7 +1152,32 @@ static int test_get(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+static int test_missing(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
+{
+ const char *optstr;
+ const char *wanted;
+ char *missing = NULL;
+ int rc;
+
+ if (argc < 2)
+ return -EINVAL;
+ optstr = argv[1];
+ wanted = argv[2];
+
+ rc = mnt_optstr_get_missing(optstr, wanted, &missing);
+ if (rc == 0)
+ printf("missing: %s\n", missing);
+ else if (rc == 1) {
+ printf("nothing\n");
+ rc = 0;
+ } else
+ printf("parse error: %s\n", optstr);
+ return rc;
+}
+
+static int test_remove(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *name;
char *optstr;
@@ -1114,7 +1197,8 @@ static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_dedup(struct libmnt_test *ts, int argc, char *argv[])
+static int test_dedup(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *name;
char *optstr;
@@ -1134,7 +1218,8 @@ static int test_dedup(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_match(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char *optstr, *pattern;
@@ -1156,6 +1241,7 @@ int main(int argc, char *argv[])
{ "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" },
{ "--set", test_set, "<optstr> <name> [<value>] (un)set value" },
{ "--get", test_get, "<optstr> <name> search name in optstr" },
+ { "--missing",test_missing,"<optstr> <wanted> what from wanted is missing" },
{ "--remove", test_remove, "<optstr> <name> remove name in optstr" },
{ "--dedup", test_dedup, "<optstr> <name> deduplicate name in optstr" },
{ "--match", test_match, "<optstr> <pattern> compare optstr with pattern" },
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
index 9725664..526edce 100644
--- a/libmount/src/tab.c
+++ b/libmount/src/tab.c
@@ -1925,7 +1925,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
#ifdef TEST_PROGRAM
#include "pathnames.h"
-static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
+static int parser_errcb(struct libmnt_table *tb __attribute__((unused)),
+ const char *filename, int line)
{
fprintf(stderr, "%s:%d: parse error\n", filename, line);
@@ -1954,12 +1955,16 @@ err:
return NULL;
}
-static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
+static int test_copy_fs(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
int rc = -1;
+ if (argc != 2)
+ return -1;
+
tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
@@ -1984,7 +1989,8 @@ done:
return rc;
}
-static int test_parse(struct libmnt_test *ts, int argc, char *argv[])
+static int test_parse(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb = NULL;
struct libmnt_iter *itr = NULL;
@@ -2020,7 +2026,8 @@ done:
return rc;
}
-static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_idx(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs = NULL;
@@ -2065,7 +2072,8 @@ done:
return rc;
}
-static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
+static int test_find(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[], int dr)
{
struct libmnt_table *tb;
struct libmnt_fs *fs = NULL;
@@ -2107,23 +2115,29 @@ done:
return rc;
}
-static int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_bw(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
}
-static int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_fw(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
return test_find(ts, argc, argv, MNT_ITER_FORWARD);
}
-static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_pair(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
struct libmnt_cache *mpc = NULL;
int rc = -1;
+ if (argc != 4)
+ return -1;
+
tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
@@ -2144,13 +2158,17 @@ done:
return rc;
}
-static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+static int test_find_mountpoint(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
struct libmnt_fs *fs;
struct libmnt_cache *mpc = NULL;
int rc = -1;
+ if (argc != 2)
+ return -1;
+
tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
if (!tb)
return -1;
@@ -2171,13 +2189,17 @@ done:
return rc;
}
-static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
+static int test_is_mounted(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb = NULL, *fstab = NULL;
struct libmnt_fs *fs;
struct libmnt_iter *itr = NULL;
struct libmnt_cache *mpc = NULL;
+ if (argc != 2)
+ return -1;
+
tb = mnt_new_table_from_file("/proc/self/mountinfo");
if (!tb) {
fprintf(stderr, "failed to parse mountinfo\n");
@@ -2227,7 +2249,8 @@ static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)),
return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1;
}
-static int test_uniq(struct libmnt_test *ts, int argc, char *argv[])
+static int test_uniq(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb;
int rc = -1;
diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c
index d5fbc4d..2765caf 100644
--- a/libmount/src/tab_diff.c
+++ b/libmount/src/tab_diff.c
@@ -309,7 +309,8 @@ done:
#ifdef TEST_PROGRAM
-static int test_diff(struct libmnt_test *ts, int argc, char *argv[])
+static int test_diff(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_table *tb_old, *tb_new;
struct libmnt_tabdiff *diff;
@@ -317,6 +318,9 @@ static int test_diff(struct libmnt_test *ts, int argc, char *argv[])
struct libmnt_fs *old, *new;
int rc = -1, change;
+ if (argc != 3)
+ return -1;
+
tb_old = mnt_new_table_from_file(argv[1]);
tb_new = mnt_new_table_from_file(argv[2]);
diff = mnt_new_tabdiff();
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c
index f5e5d30..87512af 100644
--- a/libmount/src/tab_update.c
+++ b/libmount/src/tab_update.c
@@ -35,9 +35,15 @@ struct libmnt_update {
struct libmnt_fs *fs;
char *filename;
unsigned long mountflags;
- int ready;
+
+ int act_fd;
+ char *act_filename;
+
+ unsigned int ready : 1,
+ missing_options : 1;
struct libmnt_table *mountinfo;
+ struct libmnt_lock *lock;
};
static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
@@ -56,6 +62,7 @@ struct libmnt_update *mnt_new_update(void)
if (!upd)
return NULL;
+ upd->act_fd = -1;
DBG(UPDATE, ul_debugobj(upd, "allocate"));
return upd;
}
@@ -73,10 +80,14 @@ void mnt_free_update(struct libmnt_update *upd)
DBG(UPDATE, ul_debugobj(upd, "free"));
+ mnt_unref_lock(upd->lock);
mnt_unref_fs(upd->fs);
mnt_unref_table(upd->mountinfo);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
free(upd->target);
free(upd->filename);
+ free(upd->act_filename);
free(upd);
}
@@ -173,7 +184,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
mnt_unref_fs(upd->fs);
free(upd->target);
- upd->ready = FALSE;
+ upd->ready = 0;
upd->fs = NULL;
upd->target = NULL;
upd->mountflags = 0;
@@ -206,7 +217,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
}
DBG(UPDATE, ul_debugobj(upd, "ready"));
- upd->ready = TRUE;
+ upd->ready = 1;
return 0;
}
@@ -666,43 +677,42 @@ static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
return update_table(upd, tb);
}
-static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_add_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
if (tb)
rc = add_file_entry(tb, upd);
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_remove_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->target);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -714,23 +724,22 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc
rc = update_table(upd, tb);
}
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_modify_target(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
assert(upd);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -779,14 +788,13 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l
}
done:
- if (lc)
- mnt_unlock_file(lc);
-
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
+/* replaces option in the table entry by new options from @udp */
+static int update_modify_options(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
@@ -794,13 +802,13 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename));
fs = upd->fs;
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -819,11 +827,77 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
rc = add_file_entry(tb, upd); /* not found, add new */
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
+ mnt_unref_table(tb);
+ return rc;
+}
+
+/* add user options missing in the table entry by new options from @udp */
+static int update_add_options(struct libmnt_update *upd)
+{
+ struct libmnt_table *tb = NULL;
+ int rc = 0;
+ struct libmnt_fs *fs;
+
+ assert(upd);
+ assert(upd->fs);
+ assert(upd->lock);
+
+ if (!upd->fs->user_optstr)
+ return 0;
+ DBG(UPDATE, ul_debugobj(upd, "%s: add options", upd->filename));
+
+ fs = upd->fs;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
+ if (tb) {
+ struct libmnt_fs *cur = mnt_table_find_target(tb,
+ mnt_fs_get_target(fs),
+ MNT_ITER_BACKWARD);
+ if (cur) {
+ char *u = NULL;
+
+ rc = mnt_optstr_get_missing(cur->user_optstr, upd->fs->user_optstr, &u);
+ if (!rc && u) {
+ DBG(UPDATE, ul_debugobj(upd, " add missing: %s", u));
+ rc = mnt_optstr_append_option(&cur->user_optstr, u, NULL);
+ }
+ if (!rc && u)
+ rc = update_table(upd, tb);
+
+ if (rc == 1) /* nothing is missing */
+ rc = 0;
+ } else
+ rc = add_file_entry(tb, upd); /* not found, add new */
+ }
+
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
+
+}
+
+static int update_init_lock(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ assert(upd);
+
+ if (lc) {
+ mnt_unref_lock(upd->lock);
+ mnt_ref_lock(lc);
+ upd->lock = lc;
+ } else if (!upd->lock) {
+ upd->lock = mnt_new_lock(upd->filename, 0);
+ if (!upd->lock)
+ return -ENOMEM;
+ mnt_lock_block_signals(upd->lock, TRUE);
+ }
+
+ return 0;
}
/**
@@ -842,7 +916,6 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
*/
int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
{
- struct libmnt_lock *lc0 = lc;
int rc = -EINVAL;
if (!upd || !upd->filename)
@@ -854,33 +927,32 @@ int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
if (upd->fs) {
DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
}
- if (!lc) {
- lc = mnt_new_lock(upd->filename, 0);
- if (lc)
- mnt_lock_block_signals(lc, TRUE);
- }
+
+ rc = update_init_lock(upd, lc);
+ if (rc)
+ goto done;
if (!upd->fs && upd->target)
- rc = update_remove_entry(upd, lc); /* umount */
+ rc = update_remove_entry(upd); /* umount */
else if (upd->mountflags & MS_MOVE)
- rc = update_modify_target(upd, lc); /* move */
+ rc = update_modify_target(upd); /* move */
else if (upd->mountflags & MS_REMOUNT)
- rc = update_modify_options(upd, lc); /* remount */
+ rc = update_modify_options(upd); /* remount */
+ else if (upd->fs && upd->missing_options)
+ rc = update_add_options(upd); /* mount by externel helper */
else if (upd->fs)
- rc = update_add_entry(upd, lc); /* mount */
+ rc = update_add_entry(upd); /* mount */
- upd->ready = FALSE;
+ upd->ready = 1;
+done:
DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]",
upd->filename, rc));
- if (lc != lc0)
- mnt_free_lock(lc);
return rc;
}
-int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
+int mnt_update_already_done(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
- struct libmnt_lock *lc0 = lc;
int rc = 0;
if (!upd || !upd->filename || (!upd->fs && !upd->target))
@@ -888,37 +960,32 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename));
- if (!lc) {
- lc = mnt_new_lock(upd->filename, 0);
- if (lc)
- mnt_lock_block_signals(lc, TRUE);
- }
- if (lc) {
- rc = mnt_lock_file(lc);
- if (rc) {
- rc = -MNT_ERR_LOCK;
- goto done;
- }
- }
-
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
- if (lc)
- mnt_unlock_file(lc);
if (!tb)
goto done;
if (upd->fs) {
/* mount */
+ struct libmnt_fs *fs;
const char *tgt = mnt_fs_get_target(upd->fs);
const char *src = mnt_fs_get_bindsrc(upd->fs) ?
mnt_fs_get_bindsrc(upd->fs) :
mnt_fs_get_source(upd->fs);
- if (mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD)) {
+ fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
+ if (fs) {
DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s",
upd->filename, src, tgt));
+
+ /* Check if utab entry (probably writen by /sbin/mount.<type>
+ * helper) contains all options expected by this update */
+ if (mnt_optstr_get_missing(fs->user_optstr, upd->fs->user_optstr, NULL) == 0) {
+ upd->missing_options = 1;
+ DBG(UPDATE, ul_debugobj(upd, " missing options detected"));
+ }
+ } else
rc = 1;
- }
+
} else if (upd->target) {
/* umount */
if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) {
@@ -930,13 +997,133 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
mnt_unref_table(tb);
done:
- if (lc && lc != lc0)
- mnt_free_lock(lc);
DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]",
upd->filename, rc));
return rc;
}
+int mnt_update_emit_event(struct libmnt_update *upd)
+{
+ char *filename;
+ int fd;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (asprintf(&filename, "%s.event", upd->filename) <= 0)
+ return -ENOMEM;
+
+ DBG(UPDATE, ul_debugobj(upd, "emitting utab event"));
+
+ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ free(filename);
+ if (fd < 0)
+ return -errno;
+ close(fd);
+ return 0;
+}
+
+/*
+ * Let's use /run/mount/utab.act file to report to libmount monitor that
+ * libmount is working with utab. In this case, the monitor can ignore all
+ * events from kernel until entire mount (with all steps) is done.
+ *
+ * For example mount NFS with x-* options, means
+ * - create utab.act and mark it as used (by LOCK_SH)
+ * - exec /sbin/mount.nfs
+ * - call mount(2) (kernel event on /proc/self/mounts)
+ * - utab update (NFS stuff)
+ * - utab update (add x-* userspace options)
+ * - unlink utab.act (if not use anyone else)
+ * - release event by /run/mount/utab.event
+ *
+ * Note, this is used only when utab is in the game (x-* options).
+ */
+int mnt_update_start(struct libmnt_update *upd)
+{
+ int rc = 0;
+ mode_t oldmask;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (!upd->act_filename &&
+ asprintf(&upd->act_filename, "%s.act", upd->filename) <= 0)
+ return -ENOMEM;
+
+ /* Use exclusive lock to avoid some other proces will remove the the
+ * file before it's marked as used by LOCK_SH (below) */
+ rc = update_init_lock(upd, NULL);
+ if (rc)
+ return rc;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ DBG(UPDATE, ul_debugobj(upd, "creating act file"));
+
+ oldmask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+ upd->act_fd = open(upd->act_filename, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
+ umask(oldmask);
+
+ if (upd->act_fd < 0) {
+ rc = -errno;
+ goto fail;
+ }
+
+ /* mark the file as used */
+ rc = flock(upd->act_fd, LOCK_SH);
+ if (rc) {
+ rc = -errno;
+ goto fail;
+ }
+
+ mnt_unlock_file(upd->lock);
+ return 0;
+fail:
+ DBG(UPDATE, ul_debugobj(upd, "act file failed [rc=%d]", rc));
+ mnt_unlock_file(upd->lock);
+ unlink(upd->act_filename);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return rc;
+}
+
+int mnt_update_end(struct libmnt_update *upd)
+{
+ int rc;
+
+ if (!upd || upd->act_fd < 0)
+ return -EINVAL;
+
+ DBG(UPDATE, ul_debugobj(upd, "removing act file"));
+
+ /* make sure nobody else will use the file */
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ /* mark the file as unused */
+ flock(upd->act_fd, LOCK_UN);
+ errno = 0;
+
+ /* check if nobody else need the file (if yes, then the file is under LOCK_SH) )*/
+ if (flock(upd->act_fd, LOCK_EX | LOCK_NB) != 0) {
+ if (errno == EWOULDBLOCK)
+ DBG(UPDATE, ul_debugobj(upd, "act file used, no unlink"));
+ } else {
+ DBG(UPDATE, ul_debugobj(upd, "unlinking act file"));
+ unlink(upd->act_filename);
+ }
+
+ mnt_unlock_file(upd->lock);
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return 0;
+}
#ifdef TEST_PROGRAM
@@ -970,7 +1157,8 @@ done:
return rc;
}
-static int test_add(struct libmnt_test *ts, int argc, char *argv[])
+static int test_add(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -987,14 +1175,16 @@ static int test_add(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remove(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
if (argc < 2)
return -1;
return update(argv[1], NULL, 0);
}
-static int test_move(struct libmnt_test *ts, int argc, char *argv[])
+static int test_move(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1010,7 +1200,8 @@ static int test_move(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1025,7 +1216,8 @@ static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_replace(struct libmnt_test *ts, int argc, char *argv[])
+static int test_replace(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
struct libmnt_table *tb = mnt_new_table();
diff --git a/libmount/src/utils.c b/libmount/src/utils.c
index 3817b39..a2f8ea0 100644
--- a/libmount/src/utils.c
+++ b/libmount/src/utils.c
@@ -526,7 +526,7 @@ static int add_filesystem(char ***filesystems, char *name)
if (n == 0 || !((n + 1) % MYCHUNK)) {
size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
- char **x = realloc(*filesystems, items * sizeof(char *));
+ char **x = reallocarray(*filesystems, items, sizeof(char *));
if (!x)
goto err;
@@ -1032,7 +1032,7 @@ int mnt_open_uniq_filename(const char *filename, char **name)
rc = asprintf(&n, "%s.XXXXXX", filename);
if (rc <= 0)
- return -errno;
+ return -ENOMEM;
/* This is for very old glibc and for compatibility with Posix, which says
* nothing about mkstemp() mode. All sane glibc use secure mode (0600).
@@ -1134,7 +1134,7 @@ char *mnt_get_kernel_cmdline_option(const char *name)
int val = 0;
char *p, *res = NULL, *mem = NULL;
char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
- const char *path = _PATH_PROC_CMDLINE;
+ const char *path;
if (!name || !name[0])
return NULL;
@@ -1143,6 +1143,8 @@ char *mnt_get_kernel_cmdline_option(const char *name)
path = getenv("LIBMOUNT_KERNEL_CMDLINE");
if (!path)
path = _PATH_PROC_CMDLINE;
+#else
+ path = _PATH_PROC_CMDLINE;
#endif
f = fopen(path, "r" UL_CLOEXECSTR);
if (!f)
@@ -1287,8 +1289,12 @@ done:
}
#ifdef TEST_PROGRAM
-static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match_fstype(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *type = argv[1];
char *pattern = argv[2];
@@ -1296,8 +1302,12 @@ static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
+static int test_match_options(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1305,8 +1315,12 @@ static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
+static int test_startswith(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1314,8 +1328,12 @@ static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
+static int test_endswith(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 3)
+ return -1;
+
char *optstr = argv[1];
char *pattern = argv[2];
@@ -1323,8 +1341,12 @@ static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mountpoint(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *path = canonicalize_path(argv[1]),
*mnt = path ? mnt_get_mountpoint(path) : NULL;
@@ -1334,13 +1356,17 @@ static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
+static int test_filesystems(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
char **filesystems = NULL;
int rc;
- rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
- if (!rc) {
+ if (argc != 1 && argc != 2)
+ return -1;
+
+ rc = mnt_get_filesystems(&filesystems, argc == 2 ? argv[1] : NULL);
+ if (!rc && filesystems) {
char **p;
for (p = filesystems; *p; p++)
printf("%s\n", *p);
@@ -1349,8 +1375,12 @@ static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
+static int test_chdir(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
int rc;
char *path = canonicalize_path(argv[1]),
*last = NULL;
@@ -1368,8 +1398,12 @@ static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
+static int test_kernel_cmdline(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *name = argv[1];
char *res;
@@ -1387,7 +1421,8 @@ static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
}
-static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
+static int test_guess_root(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int rc;
char *real;
@@ -1413,10 +1448,14 @@ static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
return 0;
}
-static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
+static int test_mkdir(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
int rc;
+ if (argc != 2)
+ return -1;
+
rc = ul_mkdir_p(argv[1], S_IRWXU |
S_IRGRP | S_IXGRP |
S_IROTH | S_IXOTH);
@@ -1425,11 +1464,15 @@ static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
+static int test_statfs_type(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct statfs vfs;
int rc;
+ if (argc != 2)
+ return -1;
+
rc = statfs(argv[1], &vfs);
if (rc)
printf("%s: statfs failed: %m\n", argv[1]);
@@ -1440,8 +1483,12 @@ static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_uid(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
uid_t uid = (uid_t) -1;
int rc;
@@ -1455,8 +1502,12 @@ static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_gid(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
gid_t gid = (gid_t) -1;
int rc;
@@ -1470,8 +1521,12 @@ static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_parse_mode(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *str = argv[1];
mode_t mod = (mode_t) -1;
int rc;
@@ -1488,8 +1543,12 @@ static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int tests_stat(struct libmnt_test *ts, int argc, char *argv[])
+static int tests_stat(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
+ if (argc != 2)
+ return -1;
+
char *path = argv[1];
struct stat st;
int rc;
diff --git a/libmount/src/version.c b/libmount/src/version.c
index 894c20c..b1e3432 100644
--- a/libmount/src/version.c
+++ b/libmount/src/version.c
@@ -121,7 +121,8 @@ int mnt_get_library_features(const char ***features)
}
#ifdef TEST_PROGRAM
-static int test_version(struct libmnt_test *ts, int argc, char *argv[])
+static int test_version(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
const char *ver;
const char **features;