diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:33:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:33:34 +0000 |
commit | 1272be04be0cb803eec87f602edb2e3e6f111aea (patch) | |
tree | bce17f6478cdd9f3c4ec3d751135dc42786d6a56 /libmount/src | |
parent | Releasing progress-linux version 2.39.3-11~progress7.99u1. (diff) | |
download | util-linux-1272be04be0cb803eec87f602edb2e3e6f111aea.tar.xz util-linux-1272be04be0cb803eec87f602edb2e3e6f111aea.zip |
Merging upstream version 2.40.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libmount/src')
-rw-r--r-- | libmount/src/Makemodule.am | 2 | ||||
-rw-r--r-- | libmount/src/cache.c | 14 | ||||
-rw-r--r-- | libmount/src/context.c | 54 | ||||
-rw-r--r-- | libmount/src/context_mount.c | 34 | ||||
-rw-r--r-- | libmount/src/context_umount.c | 2 | ||||
-rw-r--r-- | libmount/src/hook_loopdev.c | 20 | ||||
-rw-r--r-- | libmount/src/hook_mount.c | 84 | ||||
-rw-r--r-- | libmount/src/hook_veritydev.c | 39 | ||||
-rw-r--r-- | libmount/src/libmount.h.in | 4 | ||||
-rw-r--r-- | libmount/src/libmount.sym | 8 | ||||
-rw-r--r-- | libmount/src/lock.c | 57 | ||||
-rw-r--r-- | libmount/src/monitor.c | 79 | ||||
-rw-r--r-- | libmount/src/mountP.h | 48 | ||||
-rw-r--r-- | libmount/src/optlist.c | 66 | ||||
-rw-r--r-- | libmount/src/optstr.c | 130 | ||||
-rw-r--r-- | libmount/src/tab.c | 45 | ||||
-rw-r--r-- | libmount/src/tab_diff.c | 6 | ||||
-rw-r--r-- | libmount/src/tab_update.c | 318 | ||||
-rw-r--r-- | libmount/src/utils.c | 99 | ||||
-rw-r--r-- | libmount/src/version.c | 3 |
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; |