1/*
2 * Copyright (c) 2015 Fujitsu Ltd.
3 * Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License
14 * alone with this program.
15 */
16
17/*
18 * DESCRIPTION
19 *  Test for feature UMOUNT_NOFOLLOW of umount2().
20 *  "Don't dereference target if it is a symbolic link,
21 *   and fails with the error EINVAL."
22 */
23
24#include <errno.h>
25#include <sys/mount.h>
26
27#include "test.h"
28#include "safe_macros.h"
29#include "lapi/mount.h"
30
31#include "umount2.h"
32
33#define DIR_MODE	(S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
34#define MNTPOINT	"mntpoint"
35#define SYMLINK	"symlink"
36
37static void setup(void);
38static void test_umount2(int i);
39static void verify_failure(int i);
40static void verify_success(int i);
41static void cleanup(void);
42
43static const char *device;
44static const char *fs_type;
45
46static int mount_flag;
47
48static struct test_case_t {
49	const char *mntpoint;
50	int exp_errno;
51	const char *desc;
52} test_cases[] = {
53	{SYMLINK, EINVAL,
54		"umount2('symlink', UMOUNT_NOFOLLOW) expected EINVAL"},
55	{MNTPOINT, 0,
56		"umount2('mntpoint', UMOUNT_NOFOLLOW) expected success"},
57};
58
59char *TCID = "umount2_03";
60int TST_TOTAL = ARRAY_SIZE(test_cases);
61
62int main(int ac, char **av)
63{
64	int lc;
65	int tc;
66
67	tst_parse_opts(ac, av, NULL, NULL);
68
69	setup();
70
71	for (lc = 0; TEST_LOOPING(lc); lc++) {
72		tst_count = 0;
73
74		for (tc = 0; tc < TST_TOTAL; tc++)
75			test_umount2(tc);
76	}
77
78	cleanup();
79	tst_exit();
80}
81
82static void setup(void)
83{
84	tst_require_root();
85
86	if ((tst_kvercmp(2, 6, 34)) < 0) {
87		tst_brkm(TCONF, NULL, "This test can only run on kernels "
88			 "that are 2.6.34 or higher");
89	}
90
91	tst_sig(NOFORK, DEF_HANDLER, NULL);
92
93	tst_tmpdir();
94
95	fs_type = tst_dev_fs_type();
96	device = tst_acquire_device(cleanup);
97
98	if (!device)
99		tst_brkm(TCONF, cleanup, "Failed to obtain block device");
100
101	tst_mkfs(cleanup, device, fs_type, NULL, NULL);
102
103	SAFE_MKDIR(cleanup, MNTPOINT, DIR_MODE);
104
105	SAFE_SYMLINK(cleanup, MNTPOINT, SYMLINK);
106
107	TEST_PAUSE;
108}
109
110static void test_umount2(int i)
111{
112	SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, 0, NULL);
113	mount_flag = 1;
114
115	TEST(umount2_retry(test_cases[i].mntpoint, UMOUNT_NOFOLLOW));
116
117	if (test_cases[i].exp_errno != 0)
118		verify_failure(i);
119	else
120		verify_success(i);
121
122	if (mount_flag) {
123		if (tst_umount(MNTPOINT))
124			tst_brkm(TBROK, cleanup, "umount() failed");
125		mount_flag = 0;
126	}
127}
128
129static void verify_failure(int i)
130{
131	if (TEST_RETURN == 0) {
132		tst_resm(TFAIL, "%s passed unexpectedly", test_cases[i].desc);
133		mount_flag = 0;
134		return;
135	}
136
137	if (TEST_ERRNO != test_cases[i].exp_errno) {
138		tst_resm(TFAIL | TTERRNO, "%s failed unexpectedly",
139			 test_cases[i].desc);
140		return;
141	}
142
143	tst_resm(TPASS | TTERRNO, "umount2(2) failed as expected");
144}
145
146static void verify_success(int i)
147{
148	if (TEST_RETURN != 0) {
149		tst_resm(TFAIL | TTERRNO, "%s failed unexpectedly",
150			 test_cases[i].desc);
151		return;
152	}
153
154	tst_resm(TPASS, "umount2(2) succeeded as expected");
155	mount_flag = 0;
156}
157
158static void cleanup(void)
159{
160	if (mount_flag && tst_umount(MNTPOINT))
161		tst_resm(TWARN | TERRNO, "Failed to unmount");
162
163	if (device)
164		tst_release_device(device);
165
166	tst_rmdir();
167}
168