xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/statx.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1 /*
2  * SPDX-License-Identifier: MIT
3  *
4  * Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <sys/syscall.h>
31 #include <unistd.h>
32 
33 /*
34  * statx() may be available in the kernel, but not in the libc, so we build
35  * our own wrapper if we can't link one.
36  */
37 
38 #ifndef __NR_statx
39 #if defined(__x86_64__)
40 #define	__NR_statx (332)
41 #elif defined(__i386__)
42 #define	__NR_statx (383)
43 #elif defined(__s390__)
44 #define	__NR_statx (379)
45 #elif defined(__arm__)
46 #define	__NR_statx (397)
47 #elif defined(__aarch64__)
48 #define	__NR_statx (291)
49 #elif defined(__powerpc__)
50 #define	__NR_statx (383)
51 #else
52 #error "no definition of __NR_statx for this platform"
53 #endif
54 #endif /* __NR_statx */
55 
56 
57 int
58 statx(int, const char *, int, unsigned int, void *)
59     __attribute__((weak));
60 
61 static inline int
_statx(int fd,const char * path,int flags,unsigned int mask,void * stx)62 _statx(int fd, const char *path, int flags, unsigned int mask, void *stx)
63 {
64 	if (statx)
65 		return (statx(fd, path, flags, mask, stx));
66 	else
67 		return (syscall(__NR_statx, fd, path, flags, mask, stx));
68 }
69 
70 #ifndef STATX_TYPE
71 #define	STATX_TYPE	(1<<0)
72 #endif
73 #ifndef STATX_MODE
74 #define	STATX_MODE	(1<<1)
75 #endif
76 #ifndef STATX_NLINK
77 #define	STATX_NLINK	(1<<2)
78 #endif
79 #ifndef STATX_UID
80 #define	STATX_UID	(1<<3)
81 #endif
82 #ifndef STATX_GID
83 #define	STATX_GID	(1<<4)
84 #endif
85 #ifndef STATX_ATIME
86 #define	STATX_ATIME	(1<<5)
87 #endif
88 #ifndef STATX_MTIME
89 #define	STATX_MTIME	(1<<6)
90 #endif
91 #ifndef STATX_CTIME
92 #define	STATX_CTIME	(1<<7)
93 #endif
94 #ifndef STATX_INO
95 #define	STATX_INO	(1<<8)
96 #endif
97 #ifndef STATX_SIZE
98 #define	STATX_SIZE	(1<<9)
99 #endif
100 #ifndef STATX_BLOCKS
101 #define	STATX_BLOCKS	(1<<10)
102 #endif
103 #ifndef STATX_BTIME
104 #define	STATX_BTIME	(1<<11)
105 #endif
106 #ifndef STATX_MNT_ID
107 #define	STATX_MNT_ID	(1<<12)
108 #endif
109 #ifndef STATX_DIOALIGN
110 #define	STATX_DIOALIGN	(1<<13)
111 #endif
112 
113 typedef struct {
114 	int64_t		tv_sec;
115 	uint32_t	tv_nsec;
116 	int32_t		_pad;
117 } stx_timestamp_t;
118 _Static_assert(sizeof (stx_timestamp_t) == 0x10,
119 	"stx_timestamp_t not 16 bytes");
120 
121 typedef struct {
122 	uint32_t	stx_mask;
123 	uint32_t	stx_blksize;
124 	uint64_t	stx_attributes;
125 	uint32_t	stx_nlink;
126 	uint32_t	stx_uid;
127 	uint32_t	stx_gid;
128 	uint16_t	stx_mode;
129 	uint16_t	_pad1;
130 	uint64_t	stx_ino;
131 	uint64_t	stx_size;
132 	uint64_t	stx_blocks;
133 	uint64_t	stx_attributes_mask;
134 	stx_timestamp_t	stx_atime;
135 	stx_timestamp_t	stx_btime;
136 	stx_timestamp_t	stx_ctime;
137 	stx_timestamp_t	stx_mtime;
138 	uint32_t	stx_rdev_major;
139 	uint32_t	stx_rdev_minor;
140 	uint32_t	stx_dev_major;
141 	uint32_t	stx_dev_minor;
142 	uint64_t	stx_mnt_id;
143 	uint32_t	stx_dio_mem_align;
144 	uint32_t	stx_dio_offset_align;
145 	uint64_t	_pad2[12];
146 } stx_t;
147 _Static_assert(sizeof (stx_t) == 0x100, "stx_t not 256 bytes");
148 
149 typedef struct {
150 	const char *name;
151 	unsigned int mask;
152 } stx_field_t;
153 
154 stx_field_t fields[] = {
155 	{ "type",	STATX_TYPE },
156 	{ "mode",	STATX_MODE },
157 	{ "nlink",	STATX_NLINK },
158 	{ "uid",	STATX_UID },
159 	{ "gid",	STATX_GID },
160 	{ "atime",	STATX_ATIME },
161 	{ "mtime",	STATX_MTIME },
162 	{ "ctime",	STATX_CTIME },
163 	{ "ino",	STATX_INO },
164 	{ "size",	STATX_SIZE },
165 	{ "blocks",	STATX_BLOCKS },
166 	{ "btime",	STATX_BTIME },
167 	{ "mnt_id",	STATX_MNT_ID },
168 	{ "dioalign",	STATX_DIOALIGN },
169 	{ NULL },
170 };
171 
172 static int
usage(void)173 usage(void)
174 {
175 	printf(
176 	    "usage: statx <field[,field,field]> <file>\n"
177 	    "available fields:\n");
178 
179 	int w = 0;
180 	for (stx_field_t *f = fields; f->name != NULL; f++) {
181 		if (w > 0 && (w + strlen(f->name) + 1) > 60) {
182 			fputc('\n', stdout);
183 			w = 0;
184 		}
185 		if (w == 0)
186 			fputc(' ', stdout);
187 		w += printf(" %s", f->name);
188 	}
189 	if (w > 0)
190 		fputc('\n', stdout);
191 	return (1);
192 }
193 
194 int
main(int argc,char ** argv)195 main(int argc, char **argv)
196 {
197 	if (argc < 3)
198 		return (usage());
199 
200 	unsigned int mask = 0;
201 
202 	char *name;
203 	while ((name = strsep(&argv[1], ",")) != NULL) {
204 		stx_field_t *f;
205 		for (f = fields; f->name != NULL; f++) {
206 			if (strcmp(name, f->name) == 0) {
207 				mask |= f->mask;
208 				break;
209 			}
210 		}
211 		if (f->name == NULL) {
212 			fprintf(stderr, "unknown field name: %s\n", name);
213 			return (usage());
214 		}
215 	}
216 
217 	int fd = open(argv[2], O_PATH);
218 	if (fd < 0) {
219 		fprintf(stderr, "open: %s: %s\n", argv[2], strerror(errno));
220 		return (1);
221 	}
222 
223 	stx_t stx = {};
224 
225 	if (_statx(fd, "",
226 	    AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, &stx) < 0) {
227 		fprintf(stderr, "statx: %s: %s\n", argv[2], strerror(errno));
228 		close(fd);
229 		return (1);
230 	}
231 
232 	int rc = 0;
233 
234 	for (stx_field_t *f = fields; f->name != NULL; f++) {
235 		if (!(mask & f->mask))
236 			continue;
237 		if (!(stx.stx_mask & f->mask)) {
238 			printf("statx: kernel did not return field: %s\n",
239 			    f->name);
240 			rc = 2;
241 			continue;
242 		}
243 	}
244 
245 	if (rc > 0)
246 		return (rc);
247 
248 	for (stx_field_t *f = fields; f->name != NULL; f++) {
249 		if (!(mask & f->mask))
250 			continue;
251 
252 		switch (f->mask) {
253 		case STATX_TYPE:
254 			printf("type: %u\n", stx.stx_mode & S_IFMT);
255 			break;
256 		case STATX_MODE:
257 			printf("mode: %u\n", stx.stx_mode & ~S_IFMT);
258 			break;
259 		case STATX_NLINK:
260 			printf("nlink: %u\n", stx.stx_nlink);
261 			break;
262 		case STATX_UID:
263 			printf("uid: %u\n", stx.stx_uid);
264 			break;
265 		case STATX_GID:
266 			printf("gid: %u\n", stx.stx_gid);
267 			break;
268 		case STATX_ATIME:
269 			printf("atime: %ld.%u\n",
270 			    stx.stx_atime.tv_sec, stx.stx_atime.tv_nsec);
271 			break;
272 		case STATX_MTIME:
273 			printf("mtime: %ld.%u\n",
274 			    stx.stx_mtime.tv_sec, stx.stx_mtime.tv_nsec);
275 			break;
276 		case STATX_CTIME:
277 			printf("ctime: %ld.%u\n",
278 			    stx.stx_ctime.tv_sec, stx.stx_ctime.tv_nsec);
279 			break;
280 		case STATX_INO:
281 			printf("ino: %lu\n", stx.stx_ino);
282 			break;
283 		case STATX_SIZE:
284 			printf("size: %lu\n", stx.stx_size);
285 			break;
286 		case STATX_BLOCKS:
287 			printf("blocks: %lu\n", stx.stx_blocks);
288 			break;
289 		case STATX_BTIME:
290 			printf("btime: %ld.%u\n",
291 			    stx.stx_btime.tv_sec, stx.stx_btime.tv_nsec);
292 			break;
293 		case STATX_MNT_ID:
294 			printf("mnt_id: %lu\n", stx.stx_mnt_id);
295 			break;
296 		case STATX_DIOALIGN:
297 			printf("dioalign: %u %u\n",
298 			    stx.stx_dio_mem_align, stx.stx_dio_offset_align);
299 			break;
300 		}
301 	}
302 
303 	return (rc);
304 }
305