diff mbox series

[2/2] selinux-testsuite: Add fs*(2) API filesystem tests

Message ID 20200120111113.23349-3-richard_c_haines@btinternet.com (mailing list archive)
State Superseded
Headers show
Series selinux-testsuite: Add fs*(2) API filesystem tests | expand

Commit Message

Richard Haines Jan. 20, 2020, 11:11 a.m. UTC
Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
fspick(2) and fsmount(2) api's introduced in kernel 5.2.

Also tests move_mount(2) using open_tree(2).

These tests use common code from tests/filesystem.

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 tests/Makefile                   |   6 +
 tests/fs_filesystem/.gitignore   |   3 +
 tests/fs_filesystem/Makefile     |  16 +
 tests/fs_filesystem/fs_common.c  | 110 ++++
 tests/fs_filesystem/fs_common.h  |  30 ++
 tests/fs_filesystem/fsmount.c    |  89 ++++
 tests/fs_filesystem/fspick.c     |  68 +++
 tests/fs_filesystem/move_mount.c |  76 +++
 tests/fs_filesystem/test         | 833 +++++++++++++++++++++++++++++++
 9 files changed, 1231 insertions(+)
 create mode 100644 tests/fs_filesystem/.gitignore
 create mode 100644 tests/fs_filesystem/Makefile
 create mode 100644 tests/fs_filesystem/fs_common.c
 create mode 100644 tests/fs_filesystem/fs_common.h
 create mode 100644 tests/fs_filesystem/fsmount.c
 create mode 100644 tests/fs_filesystem/fspick.c
 create mode 100644 tests/fs_filesystem/move_mount.c
 create mode 100755 tests/fs_filesystem/test

Comments

Stephen Smalley Jan. 21, 2020, 6:56 p.m. UTC | #1
On 1/20/20 6:11 AM, Richard Haines wrote:
> Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
> fspick(2) and fsmount(2) api's introduced in kernel 5.2.
> 
> Also tests move_mount(2) using open_tree(2).
> 
> These tests use common code from tests/filesystem.
> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> ---
<snip>
> diff --git a/tests/fs_filesystem/test b/tests/fs_filesystem/test
> new file mode 100755
> index 0000000..bda7fcf
> --- /dev/null
> +++ b/tests/fs_filesystem/test
> @@ -0,0 +1,833 @@
> +#!/usr/bin/perl
> +use Test::More;
<snip>
> +    # Set to '1' for testing 'hooks.c selinux_move_mount() FILE__MOUNTON'
> +    # Once kernel patch is mainline, add kernel version test.
> +    $test_move_mount = 0;
> +    if ($test_move_mount) {
> +        $test_count += 3;
> +    }

Wondering what we want the test above for test_move_mount to ultimately 
be.  It could be the first kernel version to include my fix.  That would 
avoid testsuite failures on kernels 5.2 through 5.5 assuming the fix is 
queued for 5.6.  On the other hand, it can be argued that the testsuite 
should fail on those kernels and the fix might end up going to stable 
and being back-ported to stable updates to those kernels, in which case 
testing those kernel versions may ultimately be useful.  Paul, what's 
your preference here?
Stephen Smalley Jan. 24, 2020, 3:13 p.m. UTC | #2
On 1/20/20 6:11 AM, Richard Haines wrote:
> Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
> fspick(2) and fsmount(2) api's introduced in kernel 5.2.
> 
> Also tests move_mount(2) using open_tree(2).
> 
> These tests use common code from tests/filesystem.
> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>

Only question I have is whether we want to enable the move_mount test 
unconditionally and let it break on kernels with the regression.  Otherwise,

Acked-by: Stephen Smalley <sds@tycho.nsa.gov>

> ---
>   tests/Makefile                   |   6 +
>   tests/fs_filesystem/.gitignore   |   3 +
>   tests/fs_filesystem/Makefile     |  16 +
>   tests/fs_filesystem/fs_common.c  | 110 ++++
>   tests/fs_filesystem/fs_common.h  |  30 ++
>   tests/fs_filesystem/fsmount.c    |  89 ++++
>   tests/fs_filesystem/fspick.c     |  68 +++
>   tests/fs_filesystem/move_mount.c |  76 +++
>   tests/fs_filesystem/test         | 833 +++++++++++++++++++++++++++++++
>   9 files changed, 1231 insertions(+)
>   create mode 100644 tests/fs_filesystem/.gitignore
>   create mode 100644 tests/fs_filesystem/Makefile
>   create mode 100644 tests/fs_filesystem/fs_common.c
>   create mode 100644 tests/fs_filesystem/fs_common.h
>   create mode 100644 tests/fs_filesystem/fsmount.c
>   create mode 100644 tests/fs_filesystem/fspick.c
>   create mode 100644 tests/fs_filesystem/move_mount.c
>   create mode 100755 tests/fs_filesystem/test

[...]
Paul Moore Jan. 24, 2020, 7:17 p.m. UTC | #3
On Fri, Jan 24, 2020 at 10:12 AM Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On 1/20/20 6:11 AM, Richard Haines wrote:
> > Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
> > fspick(2) and fsmount(2) api's introduced in kernel 5.2.
> >
> > Also tests move_mount(2) using open_tree(2).
> >
> > These tests use common code from tests/filesystem.
> >
> > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
>
> Only question I have is whether we want to enable the move_mount test
> unconditionally and let it break on kernels with the regression.  Otherwise,

Historically we haven't broken the test suite for older kernels, but I
will admit that testing older kernels is no longer a priority for me.

> Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
>
> > ---
> >   tests/Makefile                   |   6 +
> >   tests/fs_filesystem/.gitignore   |   3 +
> >   tests/fs_filesystem/Makefile     |  16 +
> >   tests/fs_filesystem/fs_common.c  | 110 ++++
> >   tests/fs_filesystem/fs_common.h  |  30 ++
> >   tests/fs_filesystem/fsmount.c    |  89 ++++
> >   tests/fs_filesystem/fspick.c     |  68 +++
> >   tests/fs_filesystem/move_mount.c |  76 +++
> >   tests/fs_filesystem/test         | 833 +++++++++++++++++++++++++++++++
> >   9 files changed, 1231 insertions(+)
> >   create mode 100644 tests/fs_filesystem/.gitignore
> >   create mode 100644 tests/fs_filesystem/Makefile
> >   create mode 100644 tests/fs_filesystem/fs_common.c
> >   create mode 100644 tests/fs_filesystem/fs_common.h
> >   create mode 100644 tests/fs_filesystem/fsmount.c
> >   create mode 100644 tests/fs_filesystem/fspick.c
> >   create mode 100644 tests/fs_filesystem/move_mount.c
> >   create mode 100755 tests/fs_filesystem/test
Stephen Smalley Jan. 24, 2020, 7:25 p.m. UTC | #4
On 1/24/20 2:17 PM, Paul Moore wrote:
> On Fri, Jan 24, 2020 at 10:12 AM Stephen Smalley <sds@tycho.nsa.gov> wrote:
>> On 1/20/20 6:11 AM, Richard Haines wrote:
>>> Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
>>> fspick(2) and fsmount(2) api's introduced in kernel 5.2.
>>>
>>> Also tests move_mount(2) using open_tree(2).
>>>
>>> These tests use common code from tests/filesystem.
>>>
>>> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
>>
>> Only question I have is whether we want to enable the move_mount test
>> unconditionally and let it break on kernels with the regression.  Otherwise,
> 
> Historically we haven't broken the test suite for older kernels, but I
> will admit that testing older kernels is no longer a priority for me.

If it were a new permission, I'd agree that we definitely want it 
conditional on the appropriate kernel version.  Since it is a fix for a 
regression in checking an existing permission, it wasn't as clear to me.
Won't matter for RHEL because the fs_filesystem tests are only enabled 
for >= 5.2 since the new system calls weren't added until then, so none 
of these tests will run on RHEL regardless.  It would only show up on 
Fedora kernels from 5.2 through 5.5 that don't have the fix back-ported.
Ondrej Mosnacek Jan. 24, 2020, 7:41 p.m. UTC | #5
On Fri, Jan 24, 2020 at 8:17 PM Paul Moore <paul@paul-moore.com> wrote:
> On Fri, Jan 24, 2020 at 10:12 AM Stephen Smalley <sds@tycho.nsa.gov> wrote:
> > On 1/20/20 6:11 AM, Richard Haines wrote:
> > > Test filesystem permissions using the fsopen(2), fsconfig(2), fsmount(2),
> > > fspick(2) and fsmount(2) api's introduced in kernel 5.2.
> > >
> > > Also tests move_mount(2) using open_tree(2).
> > >
> > > These tests use common code from tests/filesystem.
> > >
> > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> >
> > Only question I have is whether we want to enable the move_mount test
> > unconditionally and let it break on kernels with the regression.  Otherwise,
>
> Historically we haven't broken the test suite for older kernels, but I
> will admit that testing older kernels is no longer a priority for me.

I'd say if we expect the bug to be fixed in the stable branches of all
affected (& currently supported) kernel versions, we should just let
the test run unconditionally. I see little value in trying to match
the exact set of broken kernel versions. So my vote is to be strict
and if that turns out to cause too much trouble, we can always add
some check (and be more careful next time).

>
> > Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
> >
> > > ---
> > >   tests/Makefile                   |   6 +
> > >   tests/fs_filesystem/.gitignore   |   3 +
> > >   tests/fs_filesystem/Makefile     |  16 +
> > >   tests/fs_filesystem/fs_common.c  | 110 ++++
> > >   tests/fs_filesystem/fs_common.h  |  30 ++
> > >   tests/fs_filesystem/fsmount.c    |  89 ++++
> > >   tests/fs_filesystem/fspick.c     |  68 +++
> > >   tests/fs_filesystem/move_mount.c |  76 +++
> > >   tests/fs_filesystem/test         | 833 +++++++++++++++++++++++++++++++
> > >   9 files changed, 1231 insertions(+)
> > >   create mode 100644 tests/fs_filesystem/.gitignore
> > >   create mode 100644 tests/fs_filesystem/Makefile
> > >   create mode 100644 tests/fs_filesystem/fs_common.c
> > >   create mode 100644 tests/fs_filesystem/fs_common.h
> > >   create mode 100644 tests/fs_filesystem/fsmount.c
> > >   create mode 100644 tests/fs_filesystem/fspick.c
> > >   create mode 100644 tests/fs_filesystem/move_mount.c
> > >   create mode 100755 tests/fs_filesystem/test
>
> --
> paul moore
> www.paul-moore.com
>
diff mbox series

Patch

diff --git a/tests/Makefile b/tests/Makefile
index a1478f1..46a1641 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -98,6 +98,12 @@  export CFLAGS += -DHAVE_FS_WATCH_PERM
 endif
 endif
 
+ifeq ($(shell grep -q filesystem $(POLDEV)/include/support/all_perms.spt && echo true),true)
+ifneq ($(shell ./kvercmp $$(uname -r) 5.2),-1)
+SUBDIRS += fs_filesystem
+endif
+endif
+
 ifeq ($(DISTRO),RHEL4)
     SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
 endif
diff --git a/tests/fs_filesystem/.gitignore b/tests/fs_filesystem/.gitignore
new file mode 100644
index 0000000..449f625
--- /dev/null
+++ b/tests/fs_filesystem/.gitignore
@@ -0,0 +1,3 @@ 
+fsmount
+fspick
+move_mount
diff --git a/tests/fs_filesystem/Makefile b/tests/fs_filesystem/Makefile
new file mode 100644
index 0000000..f7ae89d
--- /dev/null
+++ b/tests/fs_filesystem/Makefile
@@ -0,0 +1,16 @@ 
+TARGETS = fsmount fspick move_mount
+
+DEPS = fs_common.c fs_common.h
+
+LDLIBS += -lselinux
+
+USE_FS = ../filesystem
+
+all: $(TARGETS)
+	@set -e; for i in $(USE_FS); do $(MAKE) -C $$i all ; done
+
+clean:
+	rm -f $(TARGETS)
+	@set -e; for i in $(USE_FS); do $(MAKE) -C $$i clean ; done
+
+$(TARGETS): $(DEPS)
diff --git a/tests/fs_filesystem/fs_common.c b/tests/fs_filesystem/fs_common.c
new file mode 100644
index 0000000..79200ac
--- /dev/null
+++ b/tests/fs_filesystem/fs_common.c
@@ -0,0 +1,110 @@ 
+#include "fs_common.h"
+
+int fsopen(const char *fs_name, unsigned int flags)
+{
+	return syscall(__NR_fsopen, fs_name, flags);
+}
+
+int fsmount(int fsfd, unsigned int flags, unsigned int ms_flags)
+{
+	return syscall(__NR_fsmount, fsfd, flags, ms_flags);
+}
+
+int fsconfig(int fsfd, unsigned int cmd, const char *key,
+	     const void *val, int aux)
+{
+	return syscall(__NR_fsconfig, fsfd, cmd, key, val, aux);
+}
+
+int fspick(int dirfd, const char *pathname, unsigned int flags)
+{
+	return syscall(__NR_fspick, dirfd, pathname, flags);
+}
+
+int move_mount(int from_dfd, const char *from_pathname, int to_dfd,
+	       const char *to_pathname, unsigned int flags)
+{
+	return syscall(__NR_move_mount, from_dfd, from_pathname,
+		       to_dfd, to_pathname, flags);
+}
+
+int open_tree(int dirfd, const char *pathname, unsigned int flags)
+{
+	return syscall(__NR_open_tree, dirfd, pathname, flags);
+}
+
+int fsconfig_opts(int fd, char *src, char *tgt, char *opts, bool verbose)
+{
+	int result, i, save_errno, start_count, max_entries = 0;
+	int cmd[MAX_OPS];
+	char *key[MAX_OPS], *value[MAX_OPS];
+	char *src_str = "source";
+
+	/* If src then fsmount(2), else its going to be fspick(2) */
+	if (src) {
+		cmd[0] = FSCONFIG_SET_STRING;
+		key[0] = src_str;
+		value[0] = src;
+		start_count = 1;
+	} else {
+		start_count = 0;
+	}
+
+	for (i = start_count; i < MAX_OPS; i++) {
+		value[i] = strsep(&opts, ",");
+		if (!value[i]) {
+			max_entries = i + 1;
+			break;
+		}
+		cmd[i] = FSCONFIG_SET_STRING;
+	}
+
+	for (i = start_count; value[i] != NULL; i++) {
+		key[i] = strsep(&value[i], "=");
+		if (!value[i])
+			cmd[i] = FSCONFIG_SET_FLAG;
+	}
+
+	if (src) {
+		cmd[i] = FSCONFIG_CMD_CREATE;
+		key[i] = NULL;
+		value[i] = NULL;
+	} else {
+		cmd[i] = FSCONFIG_CMD_RECONFIGURE;
+		key[i] = NULL;
+		value[i] = NULL;
+	}
+
+	for (i = 0; i != max_entries; i++) {
+		if (verbose) {
+			switch (cmd[i]) {
+			case FSCONFIG_CMD_CREATE:
+				printf("fsconfig(FSCONFIG_CMD_CREATE, %s, %s, 0)\n",
+				       key[i], value[i]);
+				break;
+			case FSCONFIG_CMD_RECONFIGURE:
+				printf("fsconfig(FSCONFIG_CMD_RECONFIGURE, %s, %s, 0)\n",
+				       key[i], value[i]);
+				break;
+			case FSCONFIG_SET_FLAG:
+				printf("fsconfig(FSCONFIG_SET_FLAG, %s, %s, 0)\n",
+				       key[i], value[i]);
+				break;
+			case FSCONFIG_SET_STRING:
+				printf("fsconfig(FSCONFIG_SET_STRING, %s, %s, 0)\n",
+				       key[i], value[i]);
+				break;
+			}
+		}
+
+		result = fsconfig(fd, cmd[i], key[i], value[i], 0);
+		save_errno = errno;
+		if (result < 0) {
+			fprintf(stderr, "Failed fsconfig(2): %s\n",
+				strerror(save_errno));
+			return save_errno;
+		}
+	}
+
+	return 0;
+}
diff --git a/tests/fs_filesystem/fs_common.h b/tests/fs_filesystem/fs_common.h
new file mode 100644
index 0000000..e2fd08f
--- /dev/null
+++ b/tests/fs_filesystem/fs_common.h
@@ -0,0 +1,30 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/prctl.h>
+#include <linux/mount.h>
+#include <linux/unistd.h>
+#include <selinux/selinux.h>
+
+#ifndef AT_EMPTY_PATH
+#define AT_EMPTY_PATH		0x1000
+#endif
+#ifndef AT_RECURSIVE
+#define AT_RECURSIVE		0x8000
+#endif
+
+int fsopen(const char *fs_name, unsigned int flags);
+int fsmount(int fsfd, unsigned int flags, unsigned int ms_flags);
+int fsconfig(int fsfd, unsigned int cmd, const char *key,
+	     const void *val, int aux);
+int move_mount(int from_dfd, const char *from_pathname, int to_dfd,
+	       const char *to_pathname, unsigned int flags);
+int open_tree(int dirfd, const char *pathname, unsigned int flags);
+int fspick(int dirfd, const char *pathname, unsigned int flags);
+
+#define MAX_OPS 40
+int fsconfig_opts(int fd, char *src, char *tgt, char *opts, bool verbose);
diff --git a/tests/fs_filesystem/fsmount.c b/tests/fs_filesystem/fsmount.c
new file mode 100644
index 0000000..320d82a
--- /dev/null
+++ b/tests/fs_filesystem/fsmount.c
@@ -0,0 +1,89 @@ 
+#include "fs_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s -s src -t tgt -f fs_type [-o options] [-v]\n"
+		"Where:\n\t"
+		"-s  Source path\n\t"
+		"-t  Target path\n\t"
+		"-f  Filesystem type\n\t"
+		"-o  Options list (comma separated list)\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, fsfd, mfd;
+	char *context, *src = NULL, *tgt = NULL, *fs_type = NULL, *opts = NULL;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "s:t:f:o:v")) != -1) {
+		switch (opt) {
+		case 's':
+			src = optarg;
+			break;
+		case 't':
+			tgt = optarg;
+			break;
+		case 'f':
+			fs_type = optarg;
+			break;
+		case 'o':
+			opts = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!src || !tgt || !fs_type)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	if (verbose)
+		printf("Mounting\n\tsrc: %s\n\ttgt: %s\n\tfs_type: %s\n\topts: %s\n",
+		       src, tgt, fs_type, opts);
+
+	fsfd = fsopen(fs_type, 0);
+	if (fsfd < 0) {
+		fprintf(stderr, "Failed fsopen(2): %s\n", strerror(errno));
+		return -1;
+	}
+
+	/* config_opts() will return 0 or errno from fsconfig(2) */
+	result = fsconfig_opts(fsfd, src, NULL, opts, verbose);
+	if (result) {
+		fprintf(stderr, "Failed config_opts\n");
+		return result;
+	}
+
+	mfd = fsmount(fsfd, 0, 0);
+	if (mfd < 0) {
+		fprintf(stderr, "Failed fsmount(2): %s\n", strerror(errno));
+		return -1;
+	}
+	close(fsfd);
+
+	result = move_mount(mfd, "", AT_FDCWD, tgt, MOVE_MOUNT_F_EMPTY_PATH);
+	if (result < 0) {
+		fprintf(stderr, "Failed move_mount(2): %s\n", strerror(errno));
+		return -1;
+	}
+	close(mfd);
+
+	return 0;
+}
diff --git a/tests/fs_filesystem/fspick.c b/tests/fs_filesystem/fspick.c
new file mode 100644
index 0000000..b1e221d
--- /dev/null
+++ b/tests/fs_filesystem/fspick.c
@@ -0,0 +1,68 @@ 
+#include "fs_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s -t tgt -o options [-v]\n"
+		"Where:\n\t"
+		"-t  Target path\n\t"
+		"-f  Filesystem type\n\t"
+		"-o  Options list (comma separated list)\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, pfd;
+	char *context, *tgt = NULL, *opts = NULL;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "t:o:v")) != -1) {
+		switch (opt) {
+		case 't':
+			tgt = optarg;
+			break;
+		case 'o':
+			opts = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!tgt || !opts)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	if (verbose)
+		printf("Mounting\n\ttgt: %s\n\topts: %s\n", tgt, opts);
+
+	pfd = fspick(AT_FDCWD, tgt, 0);
+	if (pfd < 0) {
+		fprintf(stderr, "Failed fspick(2): %s\n", strerror(errno));
+		return -1;
+	}
+
+	/* config_opts() will return 0 or errno from fsconfig(2) */
+	result = fsconfig_opts(pfd, NULL, tgt, opts, verbose);
+	if (result) {
+		fprintf(stderr, "Failed config_opts\n");
+		return result;
+	}
+
+	close(pfd);
+	return 0;
+}
diff --git a/tests/fs_filesystem/move_mount.c b/tests/fs_filesystem/move_mount.c
new file mode 100644
index 0000000..1b17df9
--- /dev/null
+++ b/tests/fs_filesystem/move_mount.c
@@ -0,0 +1,76 @@ 
+#include "fs_common.h"
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s -s src -t tgt [-v]\n"
+		"Where:\n\t"
+		"-s  Source path\n\t"
+		"-t  Target path\n\t"
+		"-v  Print information.\n", progname);
+	exit(-1);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt, result, fd1, fd2, save_errno;
+	char *context, *src = NULL, *tgt = NULL;
+	bool verbose = false;
+
+	while ((opt = getopt(argc, argv, "s:t:v")) != -1) {
+		switch (opt) {
+		case 's':
+			src = optarg;
+			break;
+		case 't':
+			tgt = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (!src || !tgt)
+		print_usage(argv[0]);
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			return -1;
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	if (verbose)
+		printf("Mounting\n\tsrc: %s\n\ttgt: %s\n", src, tgt);
+
+	fd1 = open_tree(AT_FDCWD, src, 0);
+	if (fd1 < 0) {
+		fprintf(stderr, "Failed fd1 open_tree(2): %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	fd2 = open_tree(fd1, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | AT_RECURSIVE);
+	if (fd2 < 0) {
+		fprintf(stderr, "Failed fd2 open_tree(2): %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	result = move_mount(fd2, "", AT_FDCWD, tgt, MOVE_MOUNT_F_EMPTY_PATH);
+	save_errno = errno;
+	if (result < 0) {
+		fprintf(stderr, "Failed move_mount(2): %s\n", strerror(errno));
+		return save_errno;
+	}
+
+	close(fd1);
+	close(fd2);
+	return 0;
+}
diff --git a/tests/fs_filesystem/test b/tests/fs_filesystem/test
new file mode 100755
index 0000000..bda7fcf
--- /dev/null
+++ b/tests/fs_filesystem/test
@@ -0,0 +1,833 @@ 
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    # Load common subroutines from filesystem/Filesystem.pm
+    use File::Basename qw(dirname);
+    use Cwd qw(abs_path);
+    use lib dirname( abs_path $0) . '/../filesystem';
+    use Filesystem
+      qw(check_config udisks2_stop udisks2_restart get_loop_dev attach_dev make_fs mk_mntpoint_1 mk_mntpoint_2 cleanup cleanup1 reaper);
+
+    # Some code in tests/filesystem is reused
+    $filesystem_dir = "$basedir/../filesystem";
+    $test_count     = 63;
+
+    # Options: -v = Verbose, -d disable udisks(8) daemon
+    $v              = " ";
+    $disable_udisks = 0;
+    $udisks2_status = 0;
+    foreach $arg (@ARGV) {
+        if ( $arg eq "-v" ) {
+            $v = $arg;
+        }
+        elsif ( $arg eq "-d" ) {
+            $disable_udisks = 1;
+        }
+    }
+
+    # Check if watch and/or named type_transition rules configured
+    ( $addit, $test_watch, $test_name_trans ) =
+      check_config( $basedir, "$filesystem_dir/fanotify_fs" );
+
+    # Set to '1' for testing 'hooks.c selinux_move_mount() FILE__MOUNTON'
+    # Once kernel patch is mainline, add kernel version test.
+    $test_move_mount = 0;
+    if ($test_move_mount) {
+        $test_count += 3;
+    }
+
+    plan tests => ( $test_count += $addit );
+}
+
+# Set initial filesystem type
+$fs_type = "ext4";
+
+# Keep a list of devices used for removal at end of test.
+$device_count = 0;
+my @device_list;
+
+if ($disable_udisks) {
+    $udisks2_status = udisks2_stop();
+}
+
+cleanup($basedir);
+
+############### Test Basic Mount/Unmount ##########################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$mount_opts1 =
+"quota,usrquota,grpquota,defcontext=system_u:object_r:test_filesystem_file_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$mount_opts1\n";
+$result = system(
+"runcon -t test_filesystem_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $mount_opts1 $v"
+);
+ok( $result eq 0 );
+
+# On RHEL-6, there is a type transition to quota_t when running quotacheck
+# as unconfined_t. Using "runcon `id -Z` quotacheck ..." resolves this.
+print "Running quotacheck(8) to init user/group quota files\n";
+$result = system("runcon `id -Z` quotacheck -ugF vfsv0 $basedir/mntpoint/mp1");
+ok( $result eq 0 );
+
+print "Toggle User & Group quotas on/off\n";
+$result = system(
+"runcon -t test_filesystem_t $filesystem_dir/quotas_test -s $dev -t $basedir/mntpoint/mp1/aquota.user $v"
+);
+ok( $result eq 0 );
+$result = system(
+"runcon -t test_filesystem_t $filesystem_dir/quotas_test -s $dev -t $basedir/mntpoint/mp1/aquota.group $v"
+);
+ok( $result eq 0 );
+
+print "Get statfs(2)\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/statfs_test -t $basedir/mntpoint $v"
+  );
+ok( $result eq 0 );
+
+print
+"Creating 'trans_test_file' and checking context changed via type_transition rule\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/create_file -f $basedir/mntpoint/mp1/trans_test_file -e test_filesystem_filetranscon_t $v"
+  );
+ok( $result eq 0 );
+
+print "Creating 'test_file' and changing its context via setfilecon(3)\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/create_file_change_context -t test_filesystem_filecon_t -f $basedir/mntpoint/mp1/test_file $v"
+  );
+ok( $result eq 0 );
+
+if ($test_name_trans) {
+    print
+"Creating 'name_trans_test_file1' and checking context changed via name-based type_transition rule\n";
+    $result = system(
+"runcon -t test_filesystem_t $filesystem_dir/create_file -f $basedir/mntpoint/mp1/name_trans_test_file1 -e test_filesystem_filenametranscon1_t $v"
+    );
+    ok( $result eq 0 );
+
+    print
+"Creating 'name_trans_test_file2' and checking context changed via name-based type_transition rule\n";
+    $result = system(
+"runcon -t test_filesystem_t $filesystem_dir/create_file -f $basedir/mntpoint/mp1/name_trans_test_file2 -e test_filesystem_filenametranscon2_t $v"
+    );
+    ok( $result eq 0 );
+}
+
+if ($test_watch) {
+    print "fanotify(7) test\n";
+    $result = system(
+"runcon -t test_filesystem_t $filesystem_dir/fanotify_fs $v -t $basedir/mntpoint/mp1"
+    );
+    ok( $result eq 0 );
+}
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Test Move Mount ##########################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$mount_opts2 =
+"quota,usrquota,grpquota,rootcontext=system_u:object_r:test_filesystem_file_t:s0";
+
+print "Mount on $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_t  $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $mount_opts2 $v"
+);
+ok( $result eq 0 );
+
+mk_mntpoint_2("$basedir/mntpoint");
+
+print "Now move_mount(2) filesystem to $basedir/mntpoint/mp2\n";
+$result = system(
+"runcon -t test_filesystem_t $basedir/move_mount -s $basedir/mntpoint/mp1 -t $basedir/mntpoint/mp2 $v"
+);
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp2\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/umount -t $basedir/mntpoint/mp2 $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Test Change Mount Options ##########################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$mount_opts2 =
+"quota,usrquota,grpquota,rootcontext=system_u:object_r:test_filesystem_file_t:s0";
+
+print "Mount on $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_t  $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $mount_opts2 $v"
+);
+ok( $result eq 0 );
+
+$change_opts = "rootcontext=system_u:object_r:test_filesystem_file_t:s0";
+print "Now change options with fspick(2)\n";
+$result = system(
+"runcon -t test_filesystem_t $basedir/fspick -t $basedir/mntpoint/mp1 -o $change_opts $v"
+);
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { relabelfrom } ##########################
+# hooks.c may_context_mount_sb_relabel() FILESYSTEM__RELABELFROM
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_relabelfrom =
+  "defcontext=system_u:object_r:test_filesystem_sb_relabel_no_relabelfrom_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelfrom\n";
+$result = system(
+"runcon -t test_filesystem_sb_relabel_no_relabelfrom_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelfrom $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { relabelto } ##########################
+# hooks.c may_context_mount_sb_relabel() FILESYSTEM__RELABELTO
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_relabelto =
+  "fscontext=system_u:object_r:test_filesystem_sb_relabel_no_relabelto_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelto\n";
+$result = system(
+"runcon -t test_filesystem_sb_relabel_no_relabelto_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelto $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { relabelfrom } ##########################
+# hooks.c may_context_mount_inode_relabel() FILESYSTEM__RELABELFROM
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_relabelfrom =
+  "rootcontext=system_u:object_r:test_filesystem_no_inode_no_relabelfrom_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_relabelfrom\n";
+$result = system(
+"runcon -t test_filesystem_no_inode_no_relabelfrom_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_relabelfrom $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { associate } ##########################
+# hooks.c may_context_mount_inode_relabel() FILESYSTEM__ASSOCIATE
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+
+# This defcontext will trigger denial.
+$opts_no_associate =
+"defcontext=system_u:object_r:test_filesystem_inode_relabel_no_associate_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate\n";
+$result = system(
+"runcon -t test_filesystem_inode_relabel_no_associate_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { associate } ##########################
+# hooks.c may_create() FILESYSTEM__ASSOCIATE
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+
+# Use this fscontext= to get sensible audit log entry of:
+#  "allow unlabeled_t test_filesystem_may_create_no_associate_t:filesystem associate;"
+$opts_no_associate_file =
+  "fscontext=system_u:object_r:test_filesystem_may_create_no_associate_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate_file\n";
+$result = system(
+"runcon -t test_filesystem_may_create_no_associate_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate_file $v"
+);
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_may_create_no_associate_t $filesystem_dir/create_file_change_context -t unconfined_t -f $basedir/mntpoint/mp1/test_file $v 2>&1"
+  );
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_may_create_no_associate_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { quotamod } ##########################
+# hooks.c selinux_quotactl() FILESYSTEM__QUOTAMOD
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_quotamod =
+"quota,usrquota,grpquota,fscontext=system_u:object_r:test_filesystem_no_quotamod_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_quotamod\n";
+$result = system(
+"runcon -t test_filesystem_no_quotamod_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_quotamod $v 2>&1"
+);
+ok( $result eq 0 );
+
+# No need to run quotacheck(8) as never gets far enough to read quota file
+print "Toggle User & Group quotas on/off\n";
+$result = system(
+"runcon -t test_filesystem_no_quotamod_t $filesystem_dir/quotas_test -s $dev -t $basedir/mntpoint/mp1/aquota.user $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_no_quotamod_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { quotaget } ##########################
+# hooks.c selinux_quotactl() FILESYSTEM__QUOTAGET
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_quotaget =
+"quota,usrquota,grpquota,context=system_u:object_r:test_filesystem_no_quotaget_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_quotaget\n";
+$result = system(
+"runcon -t test_filesystem_no_quotaget_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_quotaget $v"
+);
+ok( $result eq 0 );
+
+print "Running quotacheck(8) to init user/group quota files\n";
+$result = system("runcon `id -Z` quotacheck -ugF vfsv0 $basedir/mntpoint/mp1");
+ok( $result eq 0 );
+
+print "Toggle User & Group quotas on/off\n";
+$result = system(
+"runcon -t test_filesystem_no_quotaget_t $filesystem_dir/quotas_test -s $dev -t $basedir/mntpoint/mp1/aquota.user $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_no_quotaget_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny file { quotaon } ##########################
+# hooks.c selinux_quota_on() FILE__QUOTAON
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_quotaon =
+  "quota,usrquota,grpquota,context=system_u:object_r:test_file_no_quotaon_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_quotaon\n";
+$result = system(
+"runcon -t test_file_no_quotaon_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_quotaon $v"
+);
+ok( $result eq 0 );
+
+print "Running quotacheck(8) to init user/group quota files\n";
+$result = system("runcon `id -Z` quotacheck -ugF vfsv0 $basedir/mntpoint/mp1");
+ok( $result eq 0 );
+
+print "Toggle User quotas on/off\n";
+$result = system(
+"runcon -t test_file_no_quotaon_t $filesystem_dir/quotas_test -s $dev -t $basedir/mntpoint/mp1/aquota.user $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_file_no_quotaon_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { mount } ##########################
+# hooks.c selinux_sb_kern_mount() FILESYSTEM__MOUNT
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_mount = "rootcontext=system_u:object_r:test_filesystem_no_mount_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_mount\n";
+$result = system(
+"runcon -t test_filesystem_no_mount_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_mount $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { getattr } ##########################
+# hooks.c selinux_sb_statfs() FILESYSTEM__GETATTR
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_getattr =
+  "rootcontext=system_u:object_r:test_filesystem_no_getattr_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_getattr\n";
+$result = system(
+"runcon -t test_filesystem_no_getattr_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_getattr $v"
+);
+ok( $result eq 0 );
+
+$result = system(
+"runcon -t test_filesystem_no_getattr_t $filesystem_dir/statfs_test -t $basedir/mntpoint $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_no_getattr_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+################ Deny file { mounton } ##########################
+# hooks.c selinux_move_mount() FILE__MOUNTON
+if ($test_move_mount) {
+    mk_mntpoint_1("$basedir/mntpoint");
+    ( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+    make_fs( $fs_type, $dev, $basedir );
+    $opts_no_remount =
+      "rootcontext=system_u:object_r:test_move_mount_no_mounton_t:s0";
+
+    print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+    print "Using mount options:\n\t$opts_no_remount\n";
+    $result = system(
+"runcon -t test_move_mount_no_mounton_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_remount $v"
+    );
+    ok( $result eq 0 );
+
+    mk_mntpoint_2("$basedir/mntpoint");
+
+    print "Now move filesystem, should fail\n";
+    $result = system(
+"runcon -t test_move_mount_no_mounton_t $basedir/move_mount -s $basedir/mntpoint/mp2 -t $basedir/mntpoint/mp1 $v 2>&1"
+    );
+    ok( $result >> 8 eq 13 );
+
+    print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+    $result = system(
+"runcon -t test_move_mount_no_mounton_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+    );
+    ok( $result eq 0 );
+
+    print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+    cleanup1( $basedir, $dev );
+}
+
+############### Deny filesystem { unmount } ##########################
+# hooks.c selinux_umount() FILESYSTEM__UNMOUNT
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_unmount =
+  "rootcontext=system_u:object_r:test_filesystem_no_unmount_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_unmount\n";
+$result = system(
+"runcon -t test_filesystem_no_unmount_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_unmount $v"
+);
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_no_unmount_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v 2>&1"
+);
+ok( $result >> 8 eq 13 );
+
+# Make sure it does get unmounted
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { associate }  ##########################
+# hooks.c selinux_inode_setxattr() FILESYSTEM__ASSOCIATE
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$opts_no_associate_xattr =
+"defcontext=system_u:object_r:test_filesystem_inode_setxattr_no_associate_t:s0,fscontext=system_u:object_r:test_filesystem_inode_setxattr_no_associate_t:s0";
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$opts_no_associate_xattr\n";
+$result = system(
+"runcon -t test_filesystem_inode_setxattr_no_associate_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_associate_xattr $v"
+);
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_inode_setxattr_no_associate_t $filesystem_dir/create_file_change_context -t unconfined_t -f $basedir/mntpoint/mp1/test_file $v 2>&1"
+  );
+ok( $result >> 8 eq 13 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_inode_setxattr_no_associate_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+############### Deny filesystem { watch }  ##########################
+# hooks.c selinux_path_notify() FILESYSTEM__WATCH
+if ($test_watch) {
+    mk_mntpoint_1("$basedir/mntpoint");
+    ( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+    make_fs( $fs_type, $dev, $basedir );
+    $opts_no_watch = "context=system_u:object_r:test_filesystem_no_watch_t:s0";
+
+    print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+    print "Using mount options:\n\t$opts_no_watch\n";
+    $result = system(
+"runcon -t test_filesystem_no_watch_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $opts_no_watch $v"
+    );
+    ok( $result eq 0 );
+
+    print "test_fanotify\n";
+    $result = system(
+"runcon -t test_filesystem_no_watch_t $filesystem_dir/fanotify_fs $v -t $basedir/mntpoint/mp1 2>&1"
+    );
+    ok( $result >> 8 eq 13 );
+
+    print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+    $result = system(
+"runcon -t test_filesystem_no_watch_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+    );
+    ok( $result eq 0 );
+
+    print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+    cleanup1( $basedir, $dev );
+}
+
+##########################################################################
+# context     - Useful when mounting filesystems that do not support extended
+#               attributes.
+#   Tested by - Creating a filesystem that has xattrs set to a different value,
+#               then mount with context= and confirm that the files have that
+#               context as well as any newly created files (even if fscreate
+#               was set to something else), and that setfilecon/setxattr() on
+#               files within the mount fails with errno EOPNOTSUPP.
+##########################################################################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+
+# Mount with xttrs to create a file with specific context.
+$context1_opts =
+  "defcontext=system_u:object_r:test_filesystem_context_file_t:s0";
+
+print "Testing 'context=' mount option\n";
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$context1_opts\n";
+$result = system(
+"runcon -t test_filesystem_context_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $context1_opts $v"
+);
+ok( $result eq 0 );
+
+# Create file with 'test_filesystem_filecon_t' context
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/create_file_change_context -t test_filesystem_filecon_t -f $basedir/mntpoint/mp1/test_file $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_context_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+# Need to free the loop device, then get new one and attach
+system("losetup -d $dev 2>/dev/null");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+attach_dev( $dev, $basedir );
+
+# Mount again with no xttr support
+$context2_opts = "context=system_u:object_r:test_filesystem_context_file_t:s0";
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$context2_opts\n";
+$result = system(
+"runcon -t test_filesystem_context_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $context2_opts $v"
+);
+ok( $result eq 0 );
+
+# Now check the context on file is system_u:object_r:test_filesystem_context_file_t:s0
+print "Check test file context $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/check_file_context -f $basedir/mntpoint/mp1/test_file -e system_u:object_r:test_filesystem_context_file_t:s0 $v"
+  );
+ok( $result eq 0 );
+
+# Then create a file with 'test_filesystem_filecon_t' context, this should fail with EOPNOTSUPP
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/create_file_change_context -t test_filesystem_filecon_t -f $basedir/mntpoint/mp1/test_file $v 2>/dev/null"
+  );
+ok( $result >> 8 eq 95 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+##########################################################################
+# rootcontext - Explicitly label the root inode of the filesystem being
+#               mounted before that filesystem or inode becomes visible
+#               to userspace.
+#   Tested by - Set mountpoint to unlabeled_t and then check that the
+#               context of the root directory matches rootcontext= after
+#               the mount operation.
+##########################################################################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$root_opts = "rootcontext=system_u:object_r:test_filesystem_context_file_t:s0";
+
+print "Testing 'rootcontext=' mount option\n";
+
+# Reset mountpoint to 'unlabeled_t' so it is different to any other possible test values.
+print "Resetting MP to unlabeled_t $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/check_mount_context -r -m $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$root_opts\n";
+$result = system(
+"runcon -t test_filesystem_context_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $root_opts $v"
+);
+ok( $result eq 0 );
+
+# Now check the mountpoint is the 'rootcontext=' value
+print "Check MP context $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/check_mount_context -m $basedir/mntpoint/mp1 -e system_u:object_r:test_filesystem_context_file_t:s0 $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_context_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+##########################################################################
+# defcontext  - Set default security context for unlabeled files.
+#               This overrides the value set for unlabeled files in policy
+#               and requires a filesystem that supports xattr labeling.
+#   Tested by - Create filesystem that has files w/o xattrs and then confirm
+#               that they are mapped to the specified defcontext upon mount,
+#               where defcontext differs from the policy default.
+##########################################################################
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+make_fs( $fs_type, $dev, $basedir );
+$test_opts = "context=system_u:object_r:test_filesystem_context_file_t:s0";
+
+print "Testing 'defcontext=' mount option\n";
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$test_opts\n";
+$result = system(
+"runcon -t test_filesystem_context_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $test_opts $v"
+);
+ok( $result eq 0 );
+
+# Create file, its context will be system_u:object_r:test_filesystem_context_file_t:s0 from $test_opts
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -u system_u -t test_filesystem_fscontext_t $filesystem_dir/create_file -f $basedir/mntpoint/mp1/test_file -e test_filesystem_context_file_t $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result = system(
+"runcon -t test_filesystem_context_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+);
+ok( $result eq 0 );
+
+# Need to free the loop device, then get new dev one and attach
+system("losetup -d $dev 2>/dev/null");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+attach_dev( $dev, $basedir );
+
+# Mount again with defcontext=
+$defcontext_opts = "defcontext=system_u:object_r:test_filesystem_filecon_t:s0";
+print "Mount $fs_type filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$defcontext_opts\n";
+$result = system(
+"runcon -t test_filesystem_context_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $defcontext_opts $v"
+);
+ok( $result eq 0 );
+
+# Now check the file context is now system_u:object_r:test_filesystem_filecon_t:s0
+print "Check test file context $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/check_file_context -f $basedir/mntpoint/mp1/test_file -e system_u:object_r:test_filesystem_filecon_t:s0 $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_context_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+##########################################################################
+# fscontext   - Sets the overarching filesystem label to a specific security
+#               context. This filesystem label is separate from the individual
+#               labels on the files.
+#   Tested by - Mount a tmpfs (fs_use_trans) filesystem with fscontext= and
+#               then create a file within it, checking its context.
+##########################################################################
+$fs_type = "tmpfs";
+mk_mntpoint_1("$basedir/mntpoint");
+( $dev, $device_count ) = get_loop_dev( \@device_list, $device_count );
+$fscontext_opts =
+"fscontext=system_u:object_r:test_filesystem_fscontext_fs_t:s0,size=10M,mode=0770";
+
+print "Testing 'fscontext=' mount option\n";
+print "Mount tmpfs filesystem on $basedir/mntpoint/mp1\n";
+print "Using mount options:\n\t$fscontext_opts\n";
+$result = system(
+"runcon -t test_filesystem_fscontext_t $basedir/fsmount -s $dev -t $basedir/mntpoint/mp1 -f $fs_type -o $fscontext_opts $v"
+);
+ok( $result eq 0 );
+
+print "Creating test file $basedir/mntpoint/mp1/test_file\n";
+$result =
+  system(
+"runcon -t test_filesystem_fscontext_t $filesystem_dir/create_file_change_context -t test_filesystem_filecon_t -f $basedir/mntpoint/mp1/test_file $v"
+  );
+ok( $result eq 0 );
+
+print "Unmount filesystem from $basedir/mntpoint/mp1\n";
+$result =
+  system(
+"runcon -t test_filesystem_fscontext_t $filesystem_dir/umount -t $basedir/mntpoint/mp1 $v"
+  );
+ok( $result eq 0 );
+
+print "Removing: $dev $basedir/mntpoint $basedir/fstest\n";
+cleanup1( $basedir, $dev );
+
+reaper( \@device_list, $filesystem_dir, $v );
+
+if ($disable_udisks) {
+    udisks2_restart($udisks2_status);
+}
+
+exit;