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