From 3e6af6182a0989a06da887d1f81669062ae738de Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Mon, 17 Oct 2016 14:46:08 +0200 Subject: [PATCH 1/4] Add a tool for decoding mount flags This patch adds a new utility function that converts mount option flags to a string representation along with a trivial program exposing it. Signed-off-by: Zygmunt Krynicki --- .gitignore | 1 + src/Makefile.am | 8 ++- src/decode-mount-opts.c | 37 +++++++++++++ src/mount-opt.c | 112 ++++++++++++++++++++++++++++++++++++++++ src/mount-opt.h | 29 +++++++++++ 5 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/decode-mount-opts.c create mode 100644 src/mount-opt.c create mode 100644 src/mount-opt.h diff --git a/.gitignore b/.gitignore index d2030e3..c101b76 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ src/snap-confine src/snap-discard-ns src/snap-confine-unit-tests src/snap-confine.apparmor +src/decode-mount-opts *~ *.o diff --git a/src/Makefile.am b/src/Makefile.am index 1f649a0..74ce9a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,14 @@ libexec_PROGRAMS = snap-confine snap-discard-ns +noinst_PROGRAMS = decode-mount-opts if WITH_UNIT_TESTS -noinst_PROGRAMS = snap-confine-unit-tests +noinst_PROGRAMS += snap-confine-unit-tests endif +decode_mount_opts_SOURCES = \ + decode-mount-opts.c \ + mount-opt.c \ + mount-opt.h + snap_discard_ns_SOURCES = \ ns-support.c \ ns-support.h \ diff --git a/src/decode-mount-opts.c b/src/decode-mount-opts.c new file mode 100644 index 0000000..d077bb1 --- /dev/null +++ b/src/decode-mount-opts.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include "mount-opt.h" + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("usage: decode-mount-opts OPT\n"); + return 0; + } + char *end; + unsigned long mountflags = strtoul(argv[1], &end, 0); + if (*end != '\0') { + fprintf(stderr, "cannot parse given argument as a number\n"); + return 1; + } + printf("%#lx is %s\n", mountflags, sc_mount_opt2str(mountflags)); + return 0; +} diff --git a/src/mount-opt.c b/src/mount-opt.c new file mode 100644 index 0000000..5da176b --- /dev/null +++ b/src/mount-opt.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "mount-opt.h" + +#include +#include +#include + +const char *sc_mount_opt2str(unsigned long flags) +{ + static char buf[1000]; + unsigned long used = 0; + strcpy(buf, ""); +#define F(FLAG, TEXT) do if (flags & (FLAG)) { strcat(buf, #TEXT ","); flags ^= (FLAG); } while (0) + F(MS_RDONLY, ro); + F(MS_NOSUID, nosuid); + F(MS_NODEV, nodev); + F(MS_NOEXEC, noexec); + F(MS_SYNCHRONOUS, sync); + F(MS_REMOUNT, remount); + F(MS_MANDLOCK, mand); + F(MS_DIRSYNC, dirsync); + F(MS_NOATIME, noatime); + F(MS_NODIRATIME, nodiratime); + if (flags & MS_BIND) { + if (flags & MS_REC) { + strcat(buf, "rbind,"); + used |= MS_REC; + } else { + strcat(buf, "bind,"); + } + flags ^= MS_BIND; + } + F(MS_MOVE, move); + // The MS_REC flag handled separately by affected flags (MS_BIND, + // MS_PRIVATE, MS_SLAVE, MS_SHARED) + // XXX: kernel has MS_VERBOSE, glibc has MS_SILENT, both use the same constant + F(MS_SILENT, silent); + F(MS_POSIXACL, posixacl); + F(MS_UNBINDABLE, unbindable); + if (flags & MS_PRIVATE) { + if (flags & MS_REC) { + strcat(buf, "rprivate,"); + used |= MS_REC; + } else { + strcat(buf, "private,"); + } + flags ^= MS_PRIVATE; + } + if (flags & MS_SLAVE) { + if (flags & MS_REC) { + strcat(buf, "rslave,"); + used |= MS_REC; + } else { + strcat(buf, "slave,"); + } + flags ^= MS_SLAVE; + } + if (flags & MS_SHARED) { + if (flags & MS_REC) { + strcat(buf, "rshared,"); + used |= MS_REC; + } else { + strcat(buf, "shared,"); + } + flags ^= MS_SHARED; + } + flags ^= used; // this is just for MS_REC + F(MS_RELATIME, relatime); + F(MS_KERNMOUNT, kernmount); + F(MS_I_VERSION, iversion); + F(MS_STRICTATIME, strictatime); + F(MS_LAZYTIME, lazytime); +#ifndef MS_NOSEC +#define MS_NOSEC (1 << 28) +#endif + F(MS_NOSEC, nosec); +#ifndef MS_BORN +#define MS_BORN (1 << 29) +#endif + F(MS_BORN, born); + F(MS_ACTIVE, active); + F(MS_NOUSER, nouser); +#undef F + // Render any flags that are unaccounted for. + if (flags) { + char of[128]; + sprintf(of, "%#lx", flags); + strcat(buf, of); + } + // Chop the excess comma from the end. + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == ',') { + buf[len - 1] = 0; + } + return buf; +} diff --git a/src/mount-opt.h b/src/mount-opt.h new file mode 100644 index 0000000..494afdc --- /dev/null +++ b/src/mount-opt.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SNAP_CONFINE_MOUNT_OPT_H +#define SNAP_CONFINE_MOUNT_OPT_H + +/** + * Convert flags for mount(2) system call to a string representation. + * + * The function uses an internal static buffer that is overwritten on each + * request. + **/ +const char *sc_mount_opt2str(unsigned long flags); + +#endif // SNAP_CONFINE_MOUNT_OPT_H From e2a255ce0d722d485fbb3c3b9c644ab8e1d9d5e7 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 18 Oct 2016 05:45:41 +0200 Subject: [PATCH 2/4] Discard mount_flags_to_string Signed-off-by: Zygmunt Krynicki --- src/Makefile.am | 4 ++++ src/quirks.c | 48 ++---------------------------------------------- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 74ce9a3..1365402 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -58,6 +58,8 @@ snap_confine_SOURCES = \ user-support.h \ quirks.c \ quirks.h \ + mount-opt.c \ + mount-opt.h \ mountinfo.c \ mountinfo.h \ ns-support.c \ @@ -96,6 +98,8 @@ snap_confine_unit_tests_SOURCES = \ user-support.h \ quirks.c \ quirks.h \ + mount-opt.c \ + mount-opt.h \ unit-tests.c \ unit-tests.h \ utils-test.c \ diff --git a/src/quirks.c b/src/quirks.c index 6f1d861..3dd4b29 100644 --- a/src/quirks.c +++ b/src/quirks.c @@ -28,6 +28,7 @@ #include "utils.h" #include "cleanup-funcs.h" #include "classic.h" +#include "mount-opt.h" // XXX: for smaller patch, this should be in utils.h later #include "user-support.h" @@ -73,51 +74,6 @@ static void sc_quirk_setup_tmpfs(const char *dirname) }; } -const char *mount_flags_to_string(unsigned flags) -{ - static char buf[1000]; - strcpy(buf, ""); - if (flags & MS_BIND) { - if (flags & MS_REC) { - strcat(buf, "rbind,"); - } else { - strcat(buf, "bind,"); - } - } - if (flags & MS_PRIVATE) { - if (flags & MS_REC) { - strcat(buf, "rprivate,"); - } else { - strcat(buf, "private,"); - } - } - if (flags & MS_SLAVE) { - if (flags & MS_REC) { - strcat(buf, "rslave,"); - } else { - strcat(buf, "slave,"); - } - } - if (flags & MS_SHARED) { - if (flags & MS_REC) { - strcat(buf, "rshared,"); - } else { - strcat(buf, "shared,"); - } - } - if (flags & MS_MOVE) { - strcat(buf, "move,"); - } - if (flags & MS_UNBINDABLE) { - strcat(buf, "unbindable,"); - } - size_t len = strlen(buf); - if (len > 0 && buf[len - 1] == ',') { - buf[len - 1] = 0; - } - return buf; -} - /** * Create an empty directory and bind mount something there. * @@ -131,7 +87,7 @@ static void sc_quirk_mkdir_bind(const char *src_dir, const char *dest_dir, flags |= MS_BIND; debug("creating empty directory at %s", dest_dir); mkpath(dest_dir); - const char *flags_str = mount_flags_to_string(flags); + const char *flags_str = sc_mount_opt2str(flags); debug("performing operation: mount %s %s -o %s", src_dir, dest_dir, flags_str); if (mount(src_dir, dest_dir, NULL, flags, NULL) != 0) { From a3049482f960465ee0b36d2613c5b8d663c9ba9a Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 18 Oct 2016 06:01:40 +0200 Subject: [PATCH 3/4] Add unit tests for mount-opt.c Signed-off-by: Zygmunt Krynicki --- src/Makefile.am | 5 ++-- src/mount-opt-test.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ src/mount-opt.c | 2 +- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/mount-opt-test.c diff --git a/src/Makefile.am b/src/Makefile.am index 1365402..0ec044c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,8 +98,6 @@ snap_confine_unit_tests_SOURCES = \ user-support.h \ quirks.c \ quirks.h \ - mount-opt.c \ - mount-opt.h \ unit-tests.c \ unit-tests.h \ utils-test.c \ @@ -107,7 +105,8 @@ snap_confine_unit_tests_SOURCES = \ mount-support-test.c \ verify-executable-name-test.c \ mountinfo-test.c \ - ns-support-test.c + ns-support-test.c \ + mount-opt-test.c snap_confine_unit_tests_CFLAGS = $(snap_confine_CFLAGS) $(GLIB_CFLAGS) snap_confine_unit_tests_LDADD = $(snap_confine_LDADD) $(GLIB_LIBS) snap_confine_unit_tests_LDFLAGS = $(snap_confine_LDFLAGS) diff --git a/src/mount-opt-test.c b/src/mount-opt-test.c new file mode 100644 index 0000000..45ad596 --- /dev/null +++ b/src/mount-opt-test.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "mount-opt.h" +#include "mount-opt.c" + +#include +#include + +static void test_sc_mount_opt2str() +{ + g_assert_cmpstr(sc_mount_opt2str(0), ==, ""); + g_assert_cmpstr(sc_mount_opt2str(MS_RDONLY), ==, "ro"); + g_assert_cmpstr(sc_mount_opt2str(MS_NOSUID), ==, "nosuid"); + g_assert_cmpstr(sc_mount_opt2str(MS_NODEV), ==, "nodev"); + g_assert_cmpstr(sc_mount_opt2str(MS_NOEXEC), ==, "noexec"); + g_assert_cmpstr(sc_mount_opt2str(MS_SYNCHRONOUS), ==, "sync"); + g_assert_cmpstr(sc_mount_opt2str(MS_REMOUNT), ==, "remount"); + g_assert_cmpstr(sc_mount_opt2str(MS_MANDLOCK), ==, "mand"); + g_assert_cmpstr(sc_mount_opt2str(MS_DIRSYNC), ==, "dirsync"); + g_assert_cmpstr(sc_mount_opt2str(MS_NOATIME), ==, "noatime"); + g_assert_cmpstr(sc_mount_opt2str(MS_NODIRATIME), ==, "nodiratime"); + g_assert_cmpstr(sc_mount_opt2str(MS_BIND), ==, "bind"); + g_assert_cmpstr(sc_mount_opt2str(MS_REC | MS_BIND), ==, "rbind"); + g_assert_cmpstr(sc_mount_opt2str(MS_MOVE), ==, "move"); + g_assert_cmpstr(sc_mount_opt2str(MS_SILENT), ==, "silent"); + g_assert_cmpstr(sc_mount_opt2str(MS_POSIXACL), ==, "acl"); + g_assert_cmpstr(sc_mount_opt2str(MS_UNBINDABLE), ==, "unbindable"); + g_assert_cmpstr(sc_mount_opt2str(MS_PRIVATE), ==, "private"); + g_assert_cmpstr(sc_mount_opt2str(MS_REC | MS_PRIVATE), ==, "rprivate"); + g_assert_cmpstr(sc_mount_opt2str(MS_SLAVE), ==, "slave"); + g_assert_cmpstr(sc_mount_opt2str(MS_REC | MS_SLAVE), ==, "rslave"); + g_assert_cmpstr(sc_mount_opt2str(MS_SHARED), ==, "shared"); + g_assert_cmpstr(sc_mount_opt2str(MS_REC | MS_SHARED), ==, "rshared"); + g_assert_cmpstr(sc_mount_opt2str(MS_RELATIME), ==, "relatime"); + g_assert_cmpstr(sc_mount_opt2str(MS_KERNMOUNT), ==, "kernmount"); + g_assert_cmpstr(sc_mount_opt2str(MS_I_VERSION), ==, "iversion"); + g_assert_cmpstr(sc_mount_opt2str(MS_STRICTATIME), ==, "strictatime"); + g_assert_cmpstr(sc_mount_opt2str(MS_LAZYTIME), ==, "lazytime"); + // MS_NOSEC is not defined in userspace + // MS_BORN is not defined in userspace + g_assert_cmpstr(sc_mount_opt2str(MS_ACTIVE), ==, "active"); + g_assert_cmpstr(sc_mount_opt2str(MS_NOUSER), ==, "nouser"); + g_assert_cmpstr(sc_mount_opt2str(0x300), ==, "0x300"); +} + +static void __attribute__ ((constructor)) init() +{ + g_test_add_func("/mount/sc_mount_opt2str", test_sc_mount_opt2str); +} diff --git a/src/mount-opt.c b/src/mount-opt.c index 5da176b..519a18e 100644 --- a/src/mount-opt.c +++ b/src/mount-opt.c @@ -51,7 +51,7 @@ const char *sc_mount_opt2str(unsigned long flags) // MS_PRIVATE, MS_SLAVE, MS_SHARED) // XXX: kernel has MS_VERBOSE, glibc has MS_SILENT, both use the same constant F(MS_SILENT, silent); - F(MS_POSIXACL, posixacl); + F(MS_POSIXACL, acl); F(MS_UNBINDABLE, unbindable); if (flags & MS_PRIVATE) { if (flags & MS_REC) { From 0c0667d528404dcf989baf5d8116ddf0ae48ba80 Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki Date: Tue, 18 Oct 2016 07:22:58 +0200 Subject: [PATCH 4/4] Add one more test Signed-off-by: Zygmunt Krynicki --- src/mount-opt-test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mount-opt-test.c b/src/mount-opt-test.c index 45ad596..9fb8590 100644 --- a/src/mount-opt-test.c +++ b/src/mount-opt-test.c @@ -56,6 +56,8 @@ static void test_sc_mount_opt2str() g_assert_cmpstr(sc_mount_opt2str(MS_ACTIVE), ==, "active"); g_assert_cmpstr(sc_mount_opt2str(MS_NOUSER), ==, "nouser"); g_assert_cmpstr(sc_mount_opt2str(0x300), ==, "0x300"); + // random compositions do work + g_assert_cmpstr(sc_mount_opt2str(MS_RDONLY | MS_NOEXEC | MS_BIND), ==, "ro,noexec,bind"); } static void __attribute__ ((constructor)) init()