1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Test the statx() system call. |
3 | * |
4 | * Note that the output of this program is intended to look like the output of |
5 | * /bin/stat where possible. |
6 | * |
7 | * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. |
8 | * Written by David Howells (dhowells@redhat.com) |
9 | */ |
10 | |
11 | #define _GNU_SOURCE |
12 | #define _ATFILE_SOURCE |
13 | #include <stdio.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | #include <unistd.h> |
17 | #include <ctype.h> |
18 | #include <errno.h> |
19 | #include <time.h> |
20 | #include <sys/syscall.h> |
21 | #include <sys/types.h> |
22 | #include <linux/stat.h> |
23 | #include <linux/fcntl.h> |
24 | #define statx foo |
25 | #define statx_timestamp foo_timestamp |
26 | struct statx; |
27 | struct statx_timestamp; |
28 | #include <sys/stat.h> |
29 | #undef statx |
30 | #undef statx_timestamp |
31 | |
32 | #define AT_STATX_SYNC_TYPE 0x6000 |
33 | #define AT_STATX_SYNC_AS_STAT 0x0000 |
34 | #define AT_STATX_FORCE_SYNC 0x2000 |
35 | #define AT_STATX_DONT_SYNC 0x4000 |
36 | |
37 | #ifndef __NR_statx |
38 | #define __NR_statx -1 |
39 | #endif |
40 | |
41 | static __attribute__((unused)) |
42 | ssize_t statx(int dfd, const char *filename, unsigned flags, |
43 | unsigned int mask, struct statx *buffer) |
44 | { |
45 | return syscall(__NR_statx, dfd, filename, flags, mask, buffer); |
46 | } |
47 | |
48 | static void print_time(const char *field, struct statx_timestamp *ts) |
49 | { |
50 | struct tm tm; |
51 | time_t tim; |
52 | char buffer[100]; |
53 | int len; |
54 | |
55 | tim = ts->tv_sec; |
56 | if (!localtime_r(timer: &tim, tp: &tm)) { |
57 | perror(s: "localtime_r" ); |
58 | exit(status: 1); |
59 | } |
60 | len = strftime(s: buffer, maxsize: 100, format: "%F %T" , tp: &tm); |
61 | if (len == 0) { |
62 | perror(s: "strftime" ); |
63 | exit(status: 1); |
64 | } |
65 | printf(format: "%s" , field); |
66 | fwrite(ptr: buffer, size: 1, n: len, stdout); |
67 | printf(format: ".%09u" , ts->tv_nsec); |
68 | len = strftime(s: buffer, maxsize: 100, format: "%z" , tp: &tm); |
69 | if (len == 0) { |
70 | perror(s: "strftime2" ); |
71 | exit(status: 1); |
72 | } |
73 | fwrite(ptr: buffer, size: 1, n: len, stdout); |
74 | printf(format: "\n" ); |
75 | } |
76 | |
77 | static void dump_statx(struct statx *stx) |
78 | { |
79 | char buffer[256], ft = '?'; |
80 | |
81 | printf(format: "results=%x\n" , stx->stx_mask); |
82 | |
83 | printf(format: " " ); |
84 | if (stx->stx_mask & STATX_SIZE) |
85 | printf(format: " Size: %-15llu" , (unsigned long long)stx->stx_size); |
86 | if (stx->stx_mask & STATX_BLOCKS) |
87 | printf(format: " Blocks: %-10llu" , (unsigned long long)stx->stx_blocks); |
88 | printf(format: " IO Block: %-6llu" , (unsigned long long)stx->stx_blksize); |
89 | if (stx->stx_mask & STATX_TYPE) { |
90 | switch (stx->stx_mode & S_IFMT) { |
91 | case S_IFIFO: printf(format: " FIFO\n" ); ft = 'p'; break; |
92 | case S_IFCHR: printf(format: " character special file\n" ); ft = 'c'; break; |
93 | case S_IFDIR: printf(format: " directory\n" ); ft = 'd'; break; |
94 | case S_IFBLK: printf(format: " block special file\n" ); ft = 'b'; break; |
95 | case S_IFREG: printf(format: " regular file\n" ); ft = '-'; break; |
96 | case S_IFLNK: printf(format: " symbolic link\n" ); ft = 'l'; break; |
97 | case S_IFSOCK: printf(format: " socket\n" ); ft = 's'; break; |
98 | default: |
99 | printf(format: " unknown type (%o)\n" , stx->stx_mode & S_IFMT); |
100 | break; |
101 | } |
102 | } else { |
103 | printf(format: " no type\n" ); |
104 | } |
105 | |
106 | sprintf(s: buffer, format: "%02x:%02x" , stx->stx_dev_major, stx->stx_dev_minor); |
107 | printf(format: "Device: %-15s" , buffer); |
108 | if (stx->stx_mask & STATX_INO) |
109 | printf(format: " Inode: %-11llu" , (unsigned long long) stx->stx_ino); |
110 | if (stx->stx_mask & STATX_NLINK) |
111 | printf(format: " Links: %-5u" , stx->stx_nlink); |
112 | if (stx->stx_mask & STATX_TYPE) { |
113 | switch (stx->stx_mode & S_IFMT) { |
114 | case S_IFBLK: |
115 | case S_IFCHR: |
116 | printf(format: " Device type: %u,%u" , |
117 | stx->stx_rdev_major, stx->stx_rdev_minor); |
118 | break; |
119 | } |
120 | } |
121 | printf(format: "\n" ); |
122 | |
123 | if (stx->stx_mask & STATX_MODE) |
124 | printf(format: "Access: (%04o/%c%c%c%c%c%c%c%c%c%c) " , |
125 | stx->stx_mode & 07777, |
126 | ft, |
127 | stx->stx_mode & S_IRUSR ? 'r' : '-', |
128 | stx->stx_mode & S_IWUSR ? 'w' : '-', |
129 | stx->stx_mode & S_IXUSR ? 'x' : '-', |
130 | stx->stx_mode & S_IRGRP ? 'r' : '-', |
131 | stx->stx_mode & S_IWGRP ? 'w' : '-', |
132 | stx->stx_mode & S_IXGRP ? 'x' : '-', |
133 | stx->stx_mode & S_IROTH ? 'r' : '-', |
134 | stx->stx_mode & S_IWOTH ? 'w' : '-', |
135 | stx->stx_mode & S_IXOTH ? 'x' : '-'); |
136 | if (stx->stx_mask & STATX_UID) |
137 | printf(format: "Uid: %5d " , stx->stx_uid); |
138 | if (stx->stx_mask & STATX_GID) |
139 | printf(format: "Gid: %5d\n" , stx->stx_gid); |
140 | |
141 | if (stx->stx_mask & STATX_ATIME) |
142 | print_time(field: "Access: " , ts: &stx->stx_atime); |
143 | if (stx->stx_mask & STATX_MTIME) |
144 | print_time(field: "Modify: " , ts: &stx->stx_mtime); |
145 | if (stx->stx_mask & STATX_CTIME) |
146 | print_time(field: "Change: " , ts: &stx->stx_ctime); |
147 | if (stx->stx_mask & STATX_BTIME) |
148 | print_time(field: " Birth: " , ts: &stx->stx_btime); |
149 | |
150 | if (stx->stx_attributes_mask) { |
151 | unsigned char bits, mbits; |
152 | int loop, byte; |
153 | |
154 | static char attr_representation[64 + 1] = |
155 | /* STATX_ATTR_ flags: */ |
156 | "????????" /* 63-56 */ |
157 | "????????" /* 55-48 */ |
158 | "????????" /* 47-40 */ |
159 | "????????" /* 39-32 */ |
160 | "????????" /* 31-24 0x00000000-ff000000 */ |
161 | "????????" /* 23-16 0x00000000-00ff0000 */ |
162 | "???me???" /* 15- 8 0x00000000-0000ff00 */ |
163 | "?dai?c??" /* 7- 0 0x00000000-000000ff */ |
164 | ; |
165 | |
166 | printf(format: "Attributes: %016llx (" , |
167 | (unsigned long long)stx->stx_attributes); |
168 | for (byte = 64 - 8; byte >= 0; byte -= 8) { |
169 | bits = stx->stx_attributes >> byte; |
170 | mbits = stx->stx_attributes_mask >> byte; |
171 | for (loop = 7; loop >= 0; loop--) { |
172 | int bit = byte + loop; |
173 | |
174 | if (!(mbits & 0x80)) |
175 | putchar(c: '.'); /* Not supported */ |
176 | else if (bits & 0x80) |
177 | putchar(c: attr_representation[63 - bit]); |
178 | else |
179 | putchar(c: '-'); /* Not set */ |
180 | bits <<= 1; |
181 | mbits <<= 1; |
182 | } |
183 | if (byte) |
184 | putchar(c: ' '); |
185 | } |
186 | printf(format: ")\n" ); |
187 | } |
188 | } |
189 | |
190 | static void dump_hex(unsigned long long *data, int from, int to) |
191 | { |
192 | unsigned offset, print_offset = 1, col = 0; |
193 | |
194 | from /= 8; |
195 | to = (to + 7) / 8; |
196 | |
197 | for (offset = from; offset < to; offset++) { |
198 | if (print_offset) { |
199 | printf(format: "%04x: " , offset * 8); |
200 | print_offset = 0; |
201 | } |
202 | printf(format: "%016llx" , data[offset]); |
203 | col++; |
204 | if ((col & 3) == 0) { |
205 | printf(format: "\n" ); |
206 | print_offset = 1; |
207 | } else { |
208 | printf(format: " " ); |
209 | } |
210 | } |
211 | |
212 | if (!print_offset) |
213 | printf(format: "\n" ); |
214 | } |
215 | |
216 | int main(int argc, char **argv) |
217 | { |
218 | struct statx stx; |
219 | int ret, raw = 0, atflag = AT_SYMLINK_NOFOLLOW; |
220 | |
221 | unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; |
222 | |
223 | for (argv++; *argv; argv++) { |
224 | if (strcmp(s1: *argv, s2: "-F" ) == 0) { |
225 | atflag &= ~AT_STATX_SYNC_TYPE; |
226 | atflag |= AT_STATX_FORCE_SYNC; |
227 | continue; |
228 | } |
229 | if (strcmp(s1: *argv, s2: "-D" ) == 0) { |
230 | atflag &= ~AT_STATX_SYNC_TYPE; |
231 | atflag |= AT_STATX_DONT_SYNC; |
232 | continue; |
233 | } |
234 | if (strcmp(s1: *argv, s2: "-L" ) == 0) { |
235 | atflag &= ~AT_SYMLINK_NOFOLLOW; |
236 | continue; |
237 | } |
238 | if (strcmp(s1: *argv, s2: "-O" ) == 0) { |
239 | mask &= ~STATX_BASIC_STATS; |
240 | continue; |
241 | } |
242 | if (strcmp(s1: *argv, s2: "-A" ) == 0) { |
243 | atflag |= AT_NO_AUTOMOUNT; |
244 | continue; |
245 | } |
246 | if (strcmp(s1: *argv, s2: "-R" ) == 0) { |
247 | raw = 1; |
248 | continue; |
249 | } |
250 | |
251 | memset(s: &stx, c: 0xbf, n: sizeof(stx)); |
252 | ret = statx(AT_FDCWD, filename: *argv, flags: atflag, mask, buffer: &stx); |
253 | printf(format: "statx(%s) = %d\n" , *argv, ret); |
254 | if (ret < 0) { |
255 | perror(s: *argv); |
256 | exit(status: 1); |
257 | } |
258 | |
259 | if (raw) |
260 | dump_hex(data: (unsigned long long *)&stx, from: 0, to: sizeof(stx)); |
261 | |
262 | dump_statx(stx: &stx); |
263 | } |
264 | return 0; |
265 | } |
266 | |