1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2015-2022 FUJITSU LIMITED. All rights reserved
4 * Author: Guangwen Feng <[email protected]>
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
35 static int mount_flag;
36
37 static 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
umount2_retry(const char * target,int flags)53 static 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
test_umount2(unsigned int n)75 static 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
setup(void)100 static void setup(void)
101 {
102 SAFE_SYMLINK(MNTPOINT, SYMLINK);
103 }
104
cleanup(void)105 static void cleanup(void)
106 {
107 if (mount_flag)
108 SAFE_UMOUNT(MNTPOINT);
109 }
110
111 static 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