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