1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) Zilogic Systems Pvt. Ltd., 2018
4 * Email: code@zilogic.com
5 */
6
7 /*\
8 * [Description]
9 *
10 * This code tests the functionality of statx system call.
11 *
12 * The metadata for normal file are tested against expected values:
13 *
14 * - gid
15 * - uid
16 * - mode
17 * - blocks
18 * - size
19 * - nlink
20 * - mnt_id
21 *
22 * The metadata for device file are tested against expected values:
23 *
24 * - MAJOR number
25 * - MINOR number
26 */
27
28 #define _GNU_SOURCE
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <sys/sysmacros.h>
33 #include "tst_test.h"
34 #include "tst_safe_macros.h"
35 #include "lapi/stat.h"
36 #include "tst_safe_stdio.h"
37 #include <string.h>
38 #include <inttypes.h>
39
40 #define TESTFILE "test_file"
41 #define MNTPOINT "mntpoint/"
42 #define DEVICEFILE MNTPOINT"blk_dev"
43 #define MODE 0644
44
45 #define SIZE 256
46 #define MAJOR 8
47 #define MINOR 1
48
49 static int file_fd = -1;
50
51 #ifdef HAVE_STRUCT_STATX_STX_MNT_ID
test_mnt_id(struct statx * buf)52 static void test_mnt_id(struct statx *buf)
53 {
54 FILE *file;
55 char line[PATH_MAX];
56 int pid;
57 unsigned int line_mjr, line_mnr;
58 uint64_t mnt_id;
59
60 if (!(buf->stx_mask & STATX_MNT_ID)) {
61 tst_res(TCONF, "stx_mnt_id is not supported until linux 5.8");
62 return;
63 }
64
65 file = SAFE_FOPEN("/proc/self/mountinfo", "r");
66
67 while (fgets(line, sizeof(line), file)) {
68 if (sscanf(line, "%ld %*d %d:%d", &mnt_id, &line_mjr, &line_mnr) != 3)
69 continue;
70
71 if (line_mjr == buf->stx_dev_major && line_mnr == buf->stx_dev_minor)
72 break;
73 }
74
75 SAFE_FCLOSE(file);
76
77 if (buf->stx_mnt_id == mnt_id)
78 tst_res(TPASS,
79 "statx.stx_mnt_id equals to mount_id(%"PRIu64") in /proc/self/mountinfo",
80 mnt_id);
81 else
82 tst_res(TFAIL,
83 "statx.stx_mnt_id(%"PRIu64") is different from mount_id(%"PRIu64") in /proc/self/mountinfo",
84 (uint64_t)buf->stx_mnt_id, mnt_id);
85
86 pid = getpid();
87 snprintf(line, PATH_MAX, "/proc/%d/fdinfo/%d", pid, file_fd);
88 TST_ASSERT_FILE_INT(line, "mnt_id:", buf->stx_mnt_id);
89 }
90 #endif
91
test_normal_file(void)92 static void test_normal_file(void)
93 {
94 struct statx buff;
95
96 TEST(statx(AT_FDCWD, TESTFILE, 0, 0, &buff));
97 if (TST_RET == 0)
98 tst_res(TPASS,
99 "statx(AT_FDCWD, %s, 0, 0, &buff)", TESTFILE);
100 else
101 tst_brk(TFAIL | TTERRNO,
102 "statx(AT_FDCWD, %s, 0, 0, &buff)", TESTFILE);
103
104 if (geteuid() == buff.stx_uid)
105 tst_res(TPASS, "stx_uid(%u) is correct", buff.stx_uid);
106 else
107 tst_res(TFAIL, "stx_uid(%u) is different from euid(%u)",
108 buff.stx_uid, geteuid());
109
110 if (getegid() == buff.stx_gid)
111 tst_res(TPASS, "stx_gid(%u) is correct", buff.stx_gid);
112 else
113 tst_res(TFAIL, "stx_gid(%u) is different from egid(%u)",
114 buff.stx_gid, getegid());
115
116 if (buff.stx_size == SIZE)
117 tst_res(TPASS,
118 "stx_size(%"PRIu64") is correct", (uint64_t)buff.stx_size);
119 else
120 tst_res(TFAIL,
121 "stx_size(%"PRIu64") is different from expected(%u)",
122 (uint64_t)buff.stx_size, SIZE);
123
124 if ((buff.stx_mode & ~(S_IFMT)) == MODE)
125 tst_res(TPASS, "stx_mode(%u) is correct", buff.stx_mode);
126 else
127 tst_res(TFAIL, "stx_mode(%u) is different from expected(%u)",
128 buff.stx_mode, MODE);
129
130 if (buff.stx_blocks <= buff.stx_blksize/512 * 2)
131 tst_res(TPASS, "stx_blocks(%"PRIu64") is valid",
132 (uint64_t)buff.stx_blocks);
133 else
134 tst_res(TFAIL, "stx_blocks(%"PRIu64") is invalid",
135 (uint64_t)buff.stx_blocks);
136
137 if (buff.stx_nlink == 1)
138 tst_res(TPASS, "stx_nlink(1) is correct");
139 else
140 tst_res(TFAIL, "stx_nlink(%u) is different from expected(1)",
141 buff.stx_nlink);
142
143 #ifdef HAVE_STRUCT_STATX_STX_MNT_ID
144 test_mnt_id(&buff);
145 #else
146 tst_res(TCONF, "stx_mnt_id is not defined in struct statx");
147 #endif
148 }
149
test_device_file(void)150 static void test_device_file(void)
151 {
152 struct statx buff;
153
154 TEST(statx(AT_FDCWD, DEVICEFILE, 0, 0, &buff));
155 if (TST_RET == 0)
156 tst_res(TPASS,
157 "statx(AT_FDCWD, %s, 0, 0, &buff)", DEVICEFILE);
158 else
159 tst_brk(TFAIL | TTERRNO,
160 "statx(AT_FDCWD, %s, 0, 0, &buff)", DEVICEFILE);
161
162 if (buff.stx_rdev_major == MAJOR)
163 tst_res(TPASS, "stx_rdev_major(%u) is correct",
164 buff.stx_rdev_major);
165 else
166 tst_res(TFAIL,
167 "stx_rdev_major(%u) is different from expected(%u)",
168 buff.stx_rdev_major, MAJOR);
169
170 if (buff.stx_rdev_minor == MINOR)
171 tst_res(TPASS, "stx_rdev_minor(%u) is correct",
172 buff.stx_rdev_minor);
173 else
174 tst_res(TFAIL,
175 "stx_rdev_minor(%u) is different from expected(%u)",
176 buff.stx_rdev_minor, MINOR);
177 }
178
179
180 static struct tcase {
181 void (*tfunc)(void);
182 } tcases[] = {
183 {&test_normal_file},
184 {&test_device_file}
185 };
186
run(unsigned int i)187 static void run(unsigned int i)
188 {
189 tcases[i].tfunc();
190 }
191
setup(void)192 static void setup(void)
193 {
194 char data_buff[SIZE];
195
196 umask(0);
197
198 memset(data_buff, '@', sizeof(data_buff));
199
200 file_fd = SAFE_OPEN(TESTFILE, O_RDWR|O_CREAT, MODE);
201 SAFE_WRITE(1, file_fd, data_buff, sizeof(data_buff));
202
203 SAFE_MKNOD(DEVICEFILE, S_IFBLK | 0777, makedev(MAJOR, MINOR));
204 }
205
cleanup(void)206 static void cleanup(void)
207 {
208 if (file_fd > -1)
209 SAFE_CLOSE(file_fd);
210 }
211
212 static struct tst_test test = {
213 .test = run,
214 .tcnt = ARRAY_SIZE(tcases),
215 .setup = setup,
216 .cleanup = cleanup,
217 .min_kver = "4.11",
218 .needs_devfs = 1,
219 .mntpoint = MNTPOINT,
220 .needs_root = 1,
221 };
222