1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2015-2022 FUJITSU LIMITED. All rights reserved
4 * Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com>
5 */
6
7/*\
8 * [Description]
9 *
10 * Test for feature MNT_EXPIRE of umount2():
11 *
12 * - EINVAL when flag is specified with either MNT_FORCE or MNT_DETACH
13 * - EAGAIN when initial call to umount2(2) with MNT_EXPIRE
14 * - EAGAIN when umount2(2) with MNT_EXPIRE after access(2)
15 * - succeed when second call to umount2(2) with MNT_EXPIRE
16 *
17 * Test for feature UMOUNT_NOFOLLOW of umount2():
18 *
19 * - EINVAL when target is a symbolic link
20 * - succeed when target is a mount point
21 */
22
23#include "lapi/mount.h"
24#include "tst_test.h"
25
26#define MNTPOINT        "mntpoint"
27#define SYMLINK	"symlink"
28
29#define FLAG_DESC(x, y) .flag = x, .exp_errno = 0, \
30	.desc = "umount2("y") with "#x" expected success"
31
32#define FLAG_EXP_ERRNO_DESC(x, y, z) .flag = x, .exp_errno = y, \
33	.desc = "umount2("z") with "#x" expected "#y
34
35static int mount_flag;
36
37static struct tcase {
38	int flag;
39	int exp_errno;
40	const char *desc;
41	const char *mntpoint;
42	int do_access;
43} tcases[] = {
44	{FLAG_EXP_ERRNO_DESC(MNT_EXPIRE | MNT_FORCE, EINVAL, ""), MNTPOINT, 0},
45	{FLAG_EXP_ERRNO_DESC(MNT_EXPIRE | MNT_DETACH, EINVAL, ""), MNTPOINT, 0},
46	{FLAG_EXP_ERRNO_DESC(MNT_EXPIRE, EAGAIN, "initial call"), MNTPOINT, 0},
47	{FLAG_EXP_ERRNO_DESC(MNT_EXPIRE, EAGAIN, "after access"), MNTPOINT, 1},
48	{FLAG_DESC(MNT_EXPIRE, "second call"), MNTPOINT, 0},
49	{FLAG_EXP_ERRNO_DESC(UMOUNT_NOFOLLOW, EINVAL, "symlink"), SYMLINK, 0},
50	{FLAG_DESC(UMOUNT_NOFOLLOW, "mntpoint"), MNTPOINT, 0},
51};
52
53static int umount2_retry(const char *target, int flags)
54{
55	int i, ret;
56
57	for (i = 0; i < 50; i++) {
58		ret = umount2(target, flags);
59		if (ret == 0 || errno != EBUSY)
60			return ret;
61
62		tst_res(TINFO, "umount('%s', %i) failed with EBUSY, try %2i...",
63			target, flags, i);
64
65		usleep(100000);
66	}
67
68	tst_res(TWARN, "Failed to umount('%s', %i) after 50 retries",
69		target, flags);
70
71	errno = EBUSY;
72	return -1;
73}
74
75static void test_umount2(unsigned int n)
76{
77	struct tcase *tc = &tcases[n];
78
79	if (!mount_flag) {
80		SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, NULL);
81		mount_flag = 1;
82	}
83
84	tst_res(TINFO, "Testing %s", tc->desc);
85
86	if (tc->do_access)
87		SAFE_ACCESS(MNTPOINT, F_OK);
88
89	if (tc->exp_errno)
90		TST_EXP_FAIL(umount2_retry(tc->mntpoint, tc->flag), tc->exp_errno,
91			"umount2_retry(%s, %d)", tc->mntpoint, tc->flag);
92	else
93		TST_EXP_PASS(umount2_retry(tc->mntpoint, tc->flag),
94			"umount2_retry(%s, %d)", tc->mntpoint, tc->flag);
95
96	if (!!tc->exp_errno ^ !!TST_PASS)
97		mount_flag = 0;
98}
99
100static void setup(void)
101{
102	SAFE_SYMLINK(MNTPOINT, SYMLINK);
103}
104
105static void cleanup(void)
106{
107	if (mount_flag)
108		SAFE_UMOUNT(MNTPOINT);
109}
110
111static struct tst_test test = {
112	.tcnt = ARRAY_SIZE(tcases),
113	.cleanup = cleanup,
114	.setup = setup,
115	.needs_root = 1,
116	.format_device = 1,
117	.mntpoint = MNTPOINT,
118	.test = test_umount2,
119};
120