• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, flag = 0;
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, "%"SCNu64" %*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 			if (buf->stx_mnt_id == mnt_id) {
73 				flag = 1;
74 				break;
75 			}
76 			tst_res(TINFO, "%s doesn't contain %"PRIu64" %d:%d",
77 				line, (uint64_t)buf->stx_mnt_id, buf->stx_dev_major, buf->stx_dev_minor);
78 		}
79 	}
80 
81 	SAFE_FCLOSE(file);
82 
83 	if (flag)
84 		tst_res(TPASS,
85 			"statx.stx_mnt_id equals to mount_id(%"PRIu64") in /proc/self/mountinfo",
86 			mnt_id);
87 	else
88 		tst_res(TFAIL,
89 			"statx.stx_mnt_id(%"PRIu64") doesn't exist in /proc/self/mountinfo",
90 			(uint64_t)buf->stx_mnt_id);
91 
92 	pid = getpid();
93 	snprintf(line, PATH_MAX, "/proc/%d/fdinfo/%d", pid, file_fd);
94 	TST_ASSERT_FILE_INT(line, "mnt_id:", buf->stx_mnt_id);
95 }
96 #endif
97 
test_normal_file(void)98 static void test_normal_file(void)
99 {
100 	struct statx buff;
101 
102 	TEST(statx(AT_FDCWD, TESTFILE, 0, 0, &buff));
103 	if (TST_RET == 0)
104 		tst_res(TPASS,
105 			"statx(AT_FDCWD, %s, 0, 0, &buff)", TESTFILE);
106 	else
107 		tst_brk(TFAIL | TTERRNO,
108 			"statx(AT_FDCWD, %s, 0, 0, &buff)", TESTFILE);
109 
110 	if (geteuid() == buff.stx_uid)
111 		tst_res(TPASS, "stx_uid(%u) is correct", buff.stx_uid);
112 	else
113 		tst_res(TFAIL, "stx_uid(%u) is different from euid(%u)",
114 			buff.stx_uid, geteuid());
115 
116 	if (getegid() == buff.stx_gid)
117 		tst_res(TPASS, "stx_gid(%u) is correct", buff.stx_gid);
118 	else
119 		tst_res(TFAIL, "stx_gid(%u) is different from egid(%u)",
120 			buff.stx_gid, getegid());
121 
122 	if (buff.stx_size == SIZE)
123 		tst_res(TPASS,
124 			"stx_size(%"PRIu64") is correct", (uint64_t)buff.stx_size);
125 	else
126 		tst_res(TFAIL,
127 			"stx_size(%"PRIu64") is different from expected(%u)",
128 			(uint64_t)buff.stx_size, SIZE);
129 
130 	if ((buff.stx_mode & ~(S_IFMT)) == MODE)
131 		tst_res(TPASS, "stx_mode(%u) is correct", buff.stx_mode);
132 	else
133 		tst_res(TFAIL, "stx_mode(%u) is different from expected(%u)",
134 			buff.stx_mode, MODE);
135 
136 	if (buff.stx_blocks <= buff.stx_blksize/512 * 2)
137 		tst_res(TPASS, "stx_blocks(%"PRIu64") is valid",
138 			(uint64_t)buff.stx_blocks);
139 	else
140 		tst_res(TFAIL, "stx_blocks(%"PRIu64") is invalid",
141 			(uint64_t)buff.stx_blocks);
142 
143 	if (buff.stx_nlink == 1)
144 		tst_res(TPASS, "stx_nlink(1) is correct");
145 	else
146 		tst_res(TFAIL, "stx_nlink(%u) is different from expected(1)",
147 			buff.stx_nlink);
148 
149 #ifdef HAVE_STRUCT_STATX_STX_MNT_ID
150 	test_mnt_id(&buff);
151 #else
152 	tst_res(TCONF, "stx_mnt_id is not defined in struct statx");
153 #endif
154 }
155 
test_device_file(void)156 static void test_device_file(void)
157 {
158 	struct statx buff;
159 
160 	TEST(statx(AT_FDCWD, DEVICEFILE, 0, 0, &buff));
161 	if (TST_RET == 0)
162 		tst_res(TPASS,
163 			"statx(AT_FDCWD, %s, 0, 0, &buff)", DEVICEFILE);
164 	else
165 		tst_brk(TFAIL | TTERRNO,
166 			"statx(AT_FDCWD, %s, 0, 0, &buff)", DEVICEFILE);
167 
168 	if (buff.stx_rdev_major == MAJOR)
169 		tst_res(TPASS, "stx_rdev_major(%u) is correct",
170 			buff.stx_rdev_major);
171 	else
172 		tst_res(TFAIL,
173 			"stx_rdev_major(%u) is different from expected(%u)",
174 			buff.stx_rdev_major, MAJOR);
175 
176 	if (buff.stx_rdev_minor == MINOR)
177 		tst_res(TPASS, "stx_rdev_minor(%u) is correct",
178 			buff.stx_rdev_minor);
179 	else
180 		tst_res(TFAIL,
181 			"stx_rdev_minor(%u) is different from expected(%u)",
182 			buff.stx_rdev_minor, MINOR);
183 }
184 
185 
186 static struct tcase {
187 	void (*tfunc)(void);
188 } tcases[] = {
189 	{&test_normal_file},
190 	{&test_device_file}
191 };
192 
run(unsigned int i)193 static void run(unsigned int i)
194 {
195 	tcases[i].tfunc();
196 }
197 
setup(void)198 static void setup(void)
199 {
200 	char data_buff[SIZE];
201 
202 	umask(0);
203 
204 	memset(data_buff, '@', sizeof(data_buff));
205 
206 	file_fd =  SAFE_OPEN(TESTFILE, O_RDWR|O_CREAT, MODE);
207 	SAFE_WRITE(SAFE_WRITE_ALL, file_fd, data_buff, sizeof(data_buff));
208 
209 	SAFE_MKNOD(DEVICEFILE, S_IFBLK | 0777, makedev(MAJOR, MINOR));
210 }
211 
cleanup(void)212 static void cleanup(void)
213 {
214 	if (file_fd > -1)
215 		SAFE_CLOSE(file_fd);
216 }
217 
218 static struct tst_test test = {
219 	.test = run,
220 	.tcnt = ARRAY_SIZE(tcases),
221 	.setup = setup,
222 	.cleanup = cleanup,
223 	.min_kver = "4.11",
224 	.needs_devfs = 1,
225 	.mntpoint = MNTPOINT,
226 	.needs_root = 1,
227 };
228