/*
 * 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 <http://www.gnu.org/licenses/>.
 *
 */

#include "mount-opt.h"
#include "mount-opt.c"

#include <errno.h>
#include <sys/mount.h>

#include <glib.h>

static void test_sc_mount_opt2str(void)
{
	char buf[1000] = { 0 };
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, 0), ==, "");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_RDONLY), ==, "ro");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NOSUID), ==,
			"nosuid");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NODEV), ==,
			"nodev");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NOEXEC), ==,
			"noexec");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_SYNCHRONOUS), ==,
			"sync");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_REMOUNT), ==,
			"remount");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_MANDLOCK), ==,
			"mand");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_DIRSYNC), ==,
			"dirsync");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NOATIME), ==,
			"noatime");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NODIRATIME), ==,
			"nodiratime");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_BIND), ==, "bind");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_REC | MS_BIND), ==,
			"rbind");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_MOVE), ==, "move");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_SILENT), ==,
			"silent");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_POSIXACL), ==,
			"acl");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_UNBINDABLE), ==,
			"unbindable");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_PRIVATE), ==,
			"private");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_REC | MS_PRIVATE),
			==, "rprivate");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_SLAVE), ==,
			"slave");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_REC | MS_SLAVE),
			==, "rslave");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_SHARED), ==,
			"shared");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_REC | MS_SHARED),
			==, "rshared");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_RELATIME), ==,
			"relatime");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_KERNMOUNT), ==,
			"kernmount");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_I_VERSION), ==,
			"iversion");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_STRICTATIME), ==,
			"strictatime");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_LAZYTIME), ==,
			"lazytime");
	// MS_NOSEC is not defined in userspace
	// MS_BORN is not defined in userspace
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_ACTIVE), ==,
			"active");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, MS_NOUSER), ==,
			"nouser");
	g_assert_cmpstr(sc_mount_opt2str(buf, sizeof buf, 0x300), ==, "0x300");
	// random compositions do work
	g_assert_cmpstr(sc_mount_opt2str
			(buf, sizeof buf, MS_RDONLY | MS_NOEXEC | MS_BIND), ==,
			"ro,noexec,bind");
}

static void test_sc_mount_cmd(void)
{
	char cmd[10000] = { 0 };

	// Typical mount
	sc_mount_cmd(cmd, sizeof cmd, "/dev/sda3", "/mnt", "ext4", MS_RDONLY,
		     NULL);
	g_assert_cmpstr(cmd, ==, "mount -t ext4 -o ro /dev/sda3 /mnt");

	// Bind mount
	sc_mount_cmd(cmd, sizeof cmd, "/source", "/target", NULL, MS_BIND,
		     NULL);
	g_assert_cmpstr(cmd, ==, "mount --bind /source /target");

	// + recursive
	sc_mount_cmd(cmd, sizeof cmd, "/source", "/target", NULL,
		     MS_BIND | MS_REC, NULL);
	g_assert_cmpstr(cmd, ==, "mount --rbind /source /target");

	// Shared subtree mount
	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL, MS_SHARED, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-shared /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL, MS_SLAVE, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-slave /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL, MS_PRIVATE, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-private /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL, MS_UNBINDABLE,
		     NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-unbindable /place");

	// + recursive
	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL,
		     MS_SHARED | MS_REC, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-rshared /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL, MS_SLAVE | MS_REC,
		     NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-rslave /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL,
		     MS_PRIVATE | MS_REC, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-rprivate /place");

	sc_mount_cmd(cmd, sizeof cmd, "/place", "none", NULL,
		     MS_UNBINDABLE | MS_REC, NULL);
	g_assert_cmpstr(cmd, ==, "mount --make-runbindable /place");

	// Move
	sc_mount_cmd(cmd, sizeof cmd, "/from", "/to", NULL, MS_MOVE, NULL);
	g_assert_cmpstr(cmd, ==, "mount --move /from /to");

	// Monster (invalid but let's format it)
	char from[PATH_MAX] = { 0 };
	char to[PATH_MAX] = { 0 };
	for (int i = 1; i < PATH_MAX - 1; ++i) {
		from[i] = 'a';
		to[i] = 'b';
	}
	from[0] = '/';
	to[0] = '/';
	from[PATH_MAX - 1] = 0;
	to[PATH_MAX - 1] = 0;
	int opts = MS_BIND | MS_MOVE | MS_SHARED | MS_SLAVE | MS_PRIVATE |
	    MS_UNBINDABLE | MS_REC | MS_RDONLY | MS_NOSUID | MS_NODEV |
	    MS_NOEXEC | MS_SYNCHRONOUS | MS_REMOUNT | MS_MANDLOCK | MS_DIRSYNC |
	    MS_NOATIME | MS_NODIRATIME | MS_BIND | MS_SILENT | MS_POSIXACL |
	    MS_RELATIME | MS_KERNMOUNT | MS_I_VERSION | MS_STRICTATIME |
	    MS_LAZYTIME;
	const char *fstype = "fstype";
	sc_mount_cmd(cmd, sizeof cmd, from, to, fstype, opts, NULL);
	const char *expected =
	    "mount -t fstype "
	    "--rbind --move --make-rshared --make-rslave --make-rprivate --make-runbindable "
	    "-o ro,nosuid,nodev,noexec,sync,remount,mand,dirsync,noatime,nodiratime,silent,"
	    "acl,relatime,kernmount,iversion,strictatime,lazytime "
	    "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
	    "/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
	g_assert_cmpstr(cmd, ==, expected);
}

static void test_sc_umount_cmd(void)
{
	char cmd[1000] = { 0 };

	// Typical umount
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", 0);
	g_assert_cmpstr(cmd, ==, "umount /mnt/foo");

	// Force
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", MNT_FORCE);
	g_assert_cmpstr(cmd, ==, "umount --force /mnt/foo");

	// Detach
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", MNT_DETACH);
	g_assert_cmpstr(cmd, ==, "umount --lazy /mnt/foo");

	// Expire
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", MNT_EXPIRE);
	g_assert_cmpstr(cmd, ==, "umount --expire /mnt/foo");

	// O_NOFOLLOW variant for umount
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo", UMOUNT_NOFOLLOW);
	g_assert_cmpstr(cmd, ==, "umount --no-follow /mnt/foo");

	// Everything at once
	sc_umount_cmd(cmd, sizeof cmd, "/mnt/foo",
		      MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW);
	g_assert_cmpstr(cmd, ==,
			"umount --force --lazy --expire --no-follow /mnt/foo");
}

static bool broken_mount(struct sc_fault_state *state, void *ptr)
{
	errno = EACCES;
	return true;
}

static void test_sc_do_mount(gconstpointer snap_debug)
{
	if (g_test_subprocess()) {
		sc_break("mount", broken_mount);
		if (GPOINTER_TO_INT(snap_debug) == 1) {
			g_assert_true(g_setenv
				      ("SNAP_CONFINE_DEBUG", "1", true));
		}
		sc_do_mount("/foo", "/bar", "ext4", MS_RDONLY, NULL);

		g_test_message("expected sc_do_mount not to return");
		sc_reset_faults();
		g_test_fail();
		return;
	}
	g_test_trap_subprocess(NULL, 0, 0);
	g_test_trap_assert_failed();
	if (GPOINTER_TO_INT(snap_debug) == 0) {
		g_test_trap_assert_stderr
		    ("cannot perform operation: mount -t ext4 -o ro /foo /bar: Permission denied\n");
	} else {
		/* with snap_debug the debug output hides the actual mount commands *but*
		 * they are still shown if there was an error
		 */
		g_test_trap_assert_stderr
		    ("DEBUG: performing operation: (disabled) use debug build to see details\n"
		     "cannot perform operation: mount -t ext4 -o ro /foo /bar: Permission denied\n");
	}
}

static bool broken_umount(struct sc_fault_state *state, void *ptr)
{
	errno = EACCES;
	return true;
}

static void test_sc_do_umount(gconstpointer snap_debug)
{
	if (g_test_subprocess()) {
		sc_break("umount", broken_umount);
		if (GPOINTER_TO_INT(snap_debug) == 1) {
			g_assert_true(g_setenv
				      ("SNAP_CONFINE_DEBUG", "1", true));
		}
		sc_do_umount("/foo", MNT_DETACH);

		g_test_message("expected sc_do_umount not to return");
		sc_reset_faults();
		g_test_fail();
		return;
	}
	g_test_trap_subprocess(NULL, 0, 0);
	g_test_trap_assert_failed();
	if (GPOINTER_TO_INT(snap_debug) == 0) {
		g_test_trap_assert_stderr
		    ("cannot perform operation: umount --lazy /foo: Permission denied\n");
	} else {
		/* with snap_debug the debug output hides the actual mount commands *but*
		 * they are still shown if there was an error
		 */
		g_test_trap_assert_stderr
		    ("DEBUG: performing operation: (disabled) use debug build to see details\n"
		     "cannot perform operation: umount --lazy /foo: Permission denied\n");
	}
}

static bool missing_mount(struct sc_fault_state *state, void *ptr)
{
	errno = ENOENT;
	return true;
}

static void test_sc_do_optional_mount_missing(void)
{
	sc_break("mount", missing_mount);
	bool ok = sc_do_optional_mount("/foo", "/bar", "ext4", MS_RDONLY, NULL);
	g_assert_false(ok);
	sc_reset_faults();
}

static void test_sc_do_optional_mount_failure(gconstpointer snap_debug)
{
	if (g_test_subprocess()) {
		sc_break("mount", broken_mount);
		if (GPOINTER_TO_INT(snap_debug) == 1) {
			g_assert_true(g_setenv
				      ("SNAP_CONFINE_DEBUG", "1", true));
		}
		(void)sc_do_optional_mount("/foo", "/bar", "ext4", MS_RDONLY,
					   NULL);

		g_test_message("expected sc_do_mount not to return");
		sc_reset_faults();
		g_test_fail();
		return;
	}
	g_test_trap_subprocess(NULL, 0, 0);
	g_test_trap_assert_failed();
	if (GPOINTER_TO_INT(snap_debug) == 0) {
		g_test_trap_assert_stderr
		    ("cannot perform operation: mount -t ext4 -o ro /foo /bar: Permission denied\n");
	} else {
		/* with snap_debug the debug output hides the actual mount commands *but*
		 * they are still shown if there was an error
		 */
		g_test_trap_assert_stderr
		    ("DEBUG: performing operation: (disabled) use debug build to see details\n"
		     "cannot perform operation: mount -t ext4 -o ro /foo /bar: Permission denied\n");
	}
}

static void __attribute__((constructor)) init(void)
{
	g_test_add_func("/mount/sc_mount_opt2str", test_sc_mount_opt2str);
	g_test_add_func("/mount/sc_mount_cmd", test_sc_mount_cmd);
	g_test_add_func("/mount/sc_umount_cmd", test_sc_umount_cmd);
	g_test_add_data_func("/mount/sc_do_mount", GINT_TO_POINTER(0),
			     test_sc_do_mount);
	g_test_add_data_func("/mount/sc_do_umount", GINT_TO_POINTER(0),
			     test_sc_do_umount);
	g_test_add_data_func("/mount/sc_do_mount_with_debug",
			     GINT_TO_POINTER(1), test_sc_do_mount);
	g_test_add_data_func("/mount/sc_do_umount_with_debug",
			     GINT_TO_POINTER(1), test_sc_do_umount);
	g_test_add_func("/mount/sc_do_optional_mount_missing",
			test_sc_do_optional_mount_missing);
	g_test_add_data_func("/mount/sc_do_optional_mount_failure",
			     GINT_TO_POINTER(0),
			     test_sc_do_optional_mount_failure);
	g_test_add_data_func("/mount/sc_do_optional_mount_failure_with_debug",
			     GINT_TO_POINTER(1),
			     test_sc_do_optional_mount_failure);
}
