github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap-confine/ns-support-test.c (about)

     1  /*
     2   * Copyright (C) 2016 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "ns-support.h"
    19  #include "ns-support.c"
    20  
    21  #include "../libsnap-confine-private/cleanup-funcs.h"
    22  #include "../libsnap-confine-private/test-utils.h"
    23  
    24  #include <errno.h>
    25  #include <linux/magic.h>	// for NSFS_MAGIC
    26  #include <sys/utsname.h>
    27  #include <sys/vfs.h>
    28  
    29  #include <glib.h>
    30  #include <glib/gstdio.h>
    31  
    32  // Set alternate namespace directory
    33  static void sc_set_ns_dir(const char *dir)
    34  {
    35  	sc_ns_dir = dir;
    36  }
    37  
    38  // A variant of unsetenv that is compatible with GDestroyNotify
    39  static void my_unsetenv(const char *k)
    40  {
    41  	unsetenv(k);
    42  }
    43  
    44  // Use temporary directory for namespace groups.
    45  //
    46  // The directory is automatically reset to the real value at the end of the
    47  // test.
    48  static const char *sc_test_use_fake_ns_dir(void)
    49  {
    50  	char *ns_dir = NULL;
    51  	if (g_test_subprocess()) {
    52  		// Check if the environment variable is set. If so then someone is already
    53  		// managing the temporary directory and we should not create a new one.
    54  		ns_dir = getenv("SNAP_CONFINE_NS_DIR");
    55  		g_assert_nonnull(ns_dir);
    56  	} else {
    57  		ns_dir = g_dir_make_tmp(NULL, NULL);
    58  		g_assert_nonnull(ns_dir);
    59  		g_test_queue_free(ns_dir);
    60  		g_assert_cmpint(setenv("SNAP_CONFINE_NS_DIR", ns_dir, 0), ==,
    61  				0);
    62  		g_test_queue_destroy((GDestroyNotify) my_unsetenv,
    63  				     "SNAP_CONFINE_NS_DIR");
    64  		g_test_queue_destroy((GDestroyNotify) rm_rf_tmp, ns_dir);
    65  	}
    66  	g_test_queue_destroy((GDestroyNotify) sc_set_ns_dir, SC_NS_DIR);
    67  	sc_set_ns_dir(ns_dir);
    68  	return ns_dir;
    69  }
    70  
    71  // Check that allocating a namespace group sets up internal data structures to
    72  // safe values.
    73  static void test_sc_alloc_mount_ns(void)
    74  {
    75  	struct sc_mount_ns *group = NULL;
    76  	group = sc_alloc_mount_ns();
    77  	g_test_queue_free(group);
    78  	g_assert_nonnull(group);
    79  	g_assert_cmpint(group->dir_fd, ==, -1);
    80  	g_assert_cmpint(group->pipe_master[0], ==, -1);
    81  	g_assert_cmpint(group->pipe_master[1], ==, -1);
    82  	g_assert_cmpint(group->pipe_helper[0], ==, -1);
    83  	g_assert_cmpint(group->pipe_helper[1], ==, -1);
    84  	g_assert_cmpint(group->child, ==, 0);
    85  	g_assert_null(group->name);
    86  }
    87  
    88  // Initialize a namespace group.
    89  //
    90  // The group is automatically destroyed at the end of the test.
    91  static struct sc_mount_ns *sc_test_open_mount_ns(const char *group_name)
    92  {
    93  	// Initialize a namespace group
    94  	struct sc_mount_ns *group = NULL;
    95  	if (group_name == NULL) {
    96  		group_name = "test-group";
    97  	}
    98  	group = sc_open_mount_ns(group_name);
    99  	g_test_queue_destroy((GDestroyNotify) sc_close_mount_ns, group);
   100  	// Check if the returned group data looks okay
   101  	g_assert_nonnull(group);
   102  	g_assert_cmpint(group->dir_fd, !=, -1);
   103  	g_assert_cmpint(group->pipe_master[0], ==, -1);
   104  	g_assert_cmpint(group->pipe_master[1], ==, -1);
   105  	g_assert_cmpint(group->pipe_helper[0], ==, -1);
   106  	g_assert_cmpint(group->pipe_helper[1], ==, -1);
   107  	g_assert_cmpint(group->child, ==, 0);
   108  	g_assert_cmpstr(group->name, ==, group_name);
   109  	return group;
   110  }
   111  
   112  // Check that initializing a namespace group creates the appropriate
   113  // filesystem structure.
   114  static void test_sc_open_mount_ns(void)
   115  {
   116  	const char *ns_dir = sc_test_use_fake_ns_dir();
   117  	sc_test_open_mount_ns(NULL);
   118  	// Check that the group directory exists
   119  	g_assert_true(g_file_test
   120  		      (ns_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
   121  }
   122  
   123  // Sanity check, ensure that the namespace filesystem identifier is what we
   124  // expect, aka NSFS_MAGIC.
   125  static void test_nsfs_fs_id(void)
   126  {
   127  	struct utsname uts;
   128  	if (uname(&uts) < 0) {
   129  		g_test_message("cannot use uname(2)");
   130  		g_test_fail();
   131  		return;
   132  	}
   133  	int major, minor;
   134  	if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
   135  		g_test_message("cannot use sscanf(2) to parse kernel release");
   136  		g_test_fail();
   137  		return;
   138  	}
   139  	if (major < 3 || (major == 3 && minor < 19)) {
   140  		g_test_skip("this test needs kernel 3.19+");
   141  		return;
   142  	}
   143  	struct statfs buf;
   144  	int err = statfs("/proc/self/ns/mnt", &buf);
   145  	g_assert_cmpint(err, ==, 0);
   146  	g_assert_cmpint(buf.f_type, ==, NSFS_MAGIC);
   147  }
   148  
   149  static void __attribute__((constructor)) init(void)
   150  {
   151  	g_test_add_func("/ns/sc_alloc_mount_ns", test_sc_alloc_mount_ns);
   152  	g_test_add_func("/ns/sc_open_mount_ns", test_sc_open_mount_ns);
   153  	g_test_add_func("/ns/nsfs_fs_id", test_nsfs_fs_id);
   154  }