blob: 7b5147f098b8a9b5460b4a0cfc2fa45683382d69 [file] [log] [blame]
Lorenz Brun5999e922021-01-27 18:53:54 +01001From 5837a1c690d51c270c8f04029530f2ce6856739a Mon Sep 17 00:00:00 2001
Lorenz Brun1d801752020-04-02 09:24:51 +02002From: David Howells <dhowells@redhat.com>
3Date: Fri, 6 Mar 2020 14:59:51 +0000
Lorenz Brun5999e922021-01-27 18:53:54 +01004Subject: [PATCH 2/3] fsinfo: Add fsinfo() syscall to query filesystem
Lorenz Brun1d801752020-04-02 09:24:51 +02005 information
6
7Add a system call to allow filesystem information to be queried. A request
8value can be given to indicate the desired attribute. Support is provided
9for enumerating multi-value attributes.
10
11===============
12NEW SYSTEM CALL
13===============
14
15The new system call looks like:
16
17 int ret = fsinfo(int dfd,
18 const char *pathname,
19 const struct fsinfo_params *params,
20 size_t params_size,
21 void *result_buffer,
22 size_t result_buf_size);
23
24The params parameter optionally points to a block of parameters:
25
26 struct fsinfo_params {
27 __u64 resolve_flags;
28 __u32 at_flags;
29 __u32 flags;
30 __u32 request;
31 __u32 Nth;
32 __u32 Mth;
33 };
34
35If params is NULL, the default is that params->request is
36FSINFO_ATTR_STATFS and all the other fields are 0. params_size indicates
37the size of the parameter struct. If the parameter block is short compared
38to what the kernel expects, the missing length will be set to 0; if the
39parameter block is longer, an error will be given if the excess is not all
40zeros.
41
42The object to be queried is specified as follows - part param->flags
43indicates the type of reference:
44
45 (1) FSINFO_FLAGS_QUERY_PATH - dfd, pathname and at_flags indicate a
46 filesystem object to query.
47
48 There is no separate system call providing an analogue of lstat() -
49 AT_SYMLINK_NOFOLLOW should be set in at_flags instead.
50 AT_NO_AUTOMOUNT can also be used to an allow automount point to be
51 queried without triggering it.
52
53 RESOLVE_* flags can also be set in resolve_flags to further restrict
54 the patchwalk.
55
56 (2) FSINFO_FLAGS_QUERY_FD - dfd indicates a file descriptor pointing to
57 the filesystem object to query. pathname should be NULL.
58
59 (3) FSINFO_FLAGS_QUERY_MOUNT - pathname indicates the numeric ID of the
60 mountpoint to query as a string. dfd is used to constrain which
61 mounts can be accessed. If dfd is AT_FDCWD, the mount must be within
62 the subtree rooted at chroot, otherwise the mount must be within the
63 subtree rooted at the directory specified by dfd.
64
65 (4) In the future FSINFO_FLAGS_QUERY_FSCONTEXT will be added - dfd will
66 indicate a context handle fd obtained from fsopen() or fspick(),
67 allowing that to be queried before the target superblock is attached
68 to the filesystem or even created.
69
70params->request indicates the attribute/attributes to be queried. This can
71be one of:
72
73 FSINFO_ATTR_STATFS - statfs-style info
74 FSINFO_ATTR_IDS - Filesystem IDs
75 FSINFO_ATTR_LIMITS - Filesystem limits
76 FSINFO_ATTR_SUPPORTS - Support for statx, ioctl, etc.
77 FSINFO_ATTR_TIMESTAMP_INFO - Inode timestamp info
78 FSINFO_ATTR_VOLUME_ID - Volume ID (string)
79 FSINFO_ATTR_VOLUME_UUID - Volume UUID
80 FSINFO_ATTR_VOLUME_NAME - Volume name (string)
81 FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO - Information about attr Nth
82 FSINFO_ATTR_FSINFO_ATTRIBUTES - List of supported attrs
83
84Some attributes (such as the servers backing a network filesystem) can have
85multiple values. These can be enumerated by setting params->Nth and
86params->Mth to 0, 1, ... until ENODATA is returned.
87
88result_buffer and result_buf_size point to the reply buffer. The buffer is
89filled up to the specified size, even if this means truncating the reply.
90The size of the full reply is returned, irrespective of the amount data
91that was copied. In future versions, this will allow extra fields to be
92tacked on to the end of the reply, but anyone not expecting them will only
93get the subset they're expecting. If either buffer of result_buf_size are
940, no copy will take place and the data size will be returned.
95
Lorenz Brun5999e922021-01-27 18:53:54 +010096Ported to 5.10 by Lorenz Brun <lorenz@nexantic.com>
Lorenz Brun1d801752020-04-02 09:24:51 +020097
98Signed-off-by: David Howells <dhowells@redhat.com>
99cc: linux-api@vger.kernel.org
100---
101 arch/alpha/kernel/syscalls/syscall.tbl | 1 +
102 arch/arm/tools/syscall.tbl | 1 +
103 arch/arm64/include/asm/unistd.h | 2 +-
104 arch/arm64/include/asm/unistd32.h | 2 +
105 arch/ia64/kernel/syscalls/syscall.tbl | 1 +
106 arch/m68k/kernel/syscalls/syscall.tbl | 1 +
107 arch/microblaze/kernel/syscalls/syscall.tbl | 1 +
108 arch/mips/kernel/syscalls/syscall_n32.tbl | 1 +
109 arch/mips/kernel/syscalls/syscall_n64.tbl | 1 +
110 arch/mips/kernel/syscalls/syscall_o32.tbl | 1 +
111 arch/parisc/kernel/syscalls/syscall.tbl | 1 +
112 arch/powerpc/kernel/syscalls/syscall.tbl | 1 +
113 arch/s390/kernel/syscalls/syscall.tbl | 1 +
114 arch/sh/kernel/syscalls/syscall.tbl | 1 +
115 arch/sparc/kernel/syscalls/syscall.tbl | 1 +
116 arch/x86/entry/syscalls/syscall_32.tbl | 1 +
117 arch/x86/entry/syscalls/syscall_64.tbl | 1 +
118 arch/xtensa/kernel/syscalls/syscall.tbl | 1 +
119 fs/Kconfig | 7 +
120 fs/Makefile | 1 +
Lorenz Brun5999e922021-01-27 18:53:54 +0100121 fs/fsinfo.c | 596 ++++++++++++++++++
Lorenz Brun1d801752020-04-02 09:24:51 +0200122 include/linux/fs.h | 4 +
Lorenz Brun5999e922021-01-27 18:53:54 +0100123 include/linux/fsinfo.h | 74 +++
Lorenz Brun1d801752020-04-02 09:24:51 +0200124 include/linux/syscalls.h | 4 +
125 include/uapi/asm-generic/unistd.h | 4 +-
Lorenz Brun5999e922021-01-27 18:53:54 +0100126 include/uapi/linux/fsinfo.h | 189 ++++++
Lorenz Brun1d801752020-04-02 09:24:51 +0200127 kernel/sys_ni.c | 1 +
Lorenz Brun5999e922021-01-27 18:53:54 +0100128 samples/vfs/Makefile | 2 +-
129 samples/vfs/test-fsinfo.c | 646 ++++++++++++++++++++
130 29 files changed, 1545 insertions(+), 3 deletions(-)
Lorenz Brun1d801752020-04-02 09:24:51 +0200131 create mode 100644 fs/fsinfo.c
132 create mode 100644 include/linux/fsinfo.h
133 create mode 100644 include/uapi/linux/fsinfo.h
134 create mode 100644 samples/vfs/test-fsinfo.c
135
136diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100137index ee7b01bb7346c..c955e31d57289 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200138--- a/arch/alpha/kernel/syscalls/syscall.tbl
139+++ b/arch/alpha/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100140@@ -480,3 +480,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200141 548 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100142 549 common faccessat2 sys_faccessat2
143 550 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200144+551 common fsinfo sys_fsinfo
145diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100146index d056a548358ea..308dfe68d892c 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200147--- a/arch/arm/tools/syscall.tbl
148+++ b/arch/arm/tools/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100149@@ -454,3 +454,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200150 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100151 439 common faccessat2 sys_faccessat2
152 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200153+441 common fsinfo sys_fsinfo
154diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h
Lorenz Brun5999e922021-01-27 18:53:54 +0100155index b3b2019f8d16b..86a9d7b3eabe9 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200156--- a/arch/arm64/include/asm/unistd.h
157+++ b/arch/arm64/include/asm/unistd.h
158@@ -38,7 +38,7 @@
159 #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5)
160 #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800)
161
Lorenz Brun5999e922021-01-27 18:53:54 +0100162-#define __NR_compat_syscalls 441
Lorenz Brun1d801752020-04-02 09:24:51 +0200163+#define __NR_compat_syscalls 442
164 #endif
165
166 #define __ARCH_WANT_SYS_CLONE
167diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h
Lorenz Brun5999e922021-01-27 18:53:54 +0100168index 107f08e03b9fd..20650e0edacb2 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200169--- a/arch/arm64/include/asm/unistd32.h
170+++ b/arch/arm64/include/asm/unistd32.h
Lorenz Brun5999e922021-01-27 18:53:54 +0100171@@ -889,6 +889,8 @@ __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
172 __SYSCALL(__NR_faccessat2, sys_faccessat2)
173 #define __NR_process_madvise 440
174 __SYSCALL(__NR_process_madvise, sys_process_madvise)
Lorenz Brun1d801752020-04-02 09:24:51 +0200175+#define __NR_fsinfo 441
176+__SYSCALL(__NR_fsinfo, sys_fsinfo)
177
178 /*
179 * Please add new compat syscalls above this comment and update
180diff --git a/arch/ia64/kernel/syscalls/syscall.tbl b/arch/ia64/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100181index b96ed8b8a5089..548428f7b76c7 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200182--- a/arch/ia64/kernel/syscalls/syscall.tbl
183+++ b/arch/ia64/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100184@@ -361,3 +361,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200185 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100186 439 common faccessat2 sys_faccessat2
187 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200188+441 common fsinfo sys_fsinfo
189diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100190index 625fb6d328424..3dd9c1f0815ce 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200191--- a/arch/m68k/kernel/syscalls/syscall.tbl
192+++ b/arch/m68k/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100193@@ -440,3 +440,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200194 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100195 439 common faccessat2 sys_faccessat2
196 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200197+441 common fsinfo sys_fsinfo
198diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100199index aae729c95cf99..420a6d0b01004 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200200--- a/arch/microblaze/kernel/syscalls/syscall.tbl
201+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100202@@ -446,3 +446,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200203 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100204 439 common faccessat2 sys_faccessat2
205 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200206+441 common fsinfo sys_fsinfo
207diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100208index 32817c954435d..01420fe120bab 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200209--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
210+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100211@@ -379,3 +379,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200212 438 n32 pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100213 439 n32 faccessat2 sys_faccessat2
214 440 n32 process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200215+441 n32 fsinfo sys_fsinfo
216diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100217index 9e4ea3c31b1ce..6c319c38779e9 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200218--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
219+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100220@@ -355,3 +355,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200221 438 n64 pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100222 439 n64 faccessat2 sys_faccessat2
223 440 n64 process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200224+441 n64 fsinfo sys_fsinfo
225diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100226index 29f5f28cf5cea..46d4aa7ecb306 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200227--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
228+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100229@@ -428,3 +428,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200230 438 o32 pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100231 439 o32 faccessat2 sys_faccessat2
232 440 o32 process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200233+441 o32 fsinfo sys_fsinfo
234diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100235index f375ea528e59c..008282637f6af 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200236--- a/arch/parisc/kernel/syscalls/syscall.tbl
237+++ b/arch/parisc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100238@@ -438,3 +438,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200239 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100240 439 common faccessat2 sys_faccessat2
241 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200242+441 common fsinfo sys_fsinfo
243diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100244index 1275daec7fec3..85da23afb3535 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200245--- a/arch/powerpc/kernel/syscalls/syscall.tbl
246+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100247@@ -530,3 +530,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200248 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100249 439 common faccessat2 sys_faccessat2
250 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200251+441 common fsinfo sys_fsinfo
252diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100253index 28c1680004834..83fdaebbf0b08 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200254--- a/arch/s390/kernel/syscalls/syscall.tbl
255+++ b/arch/s390/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100256@@ -443,3 +443,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200257 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100258 439 common faccessat2 sys_faccessat2 sys_faccessat2
259 440 common process_madvise sys_process_madvise sys_process_madvise
260+441 common fsinfo sys_fsinfo sys_fsinfo
Lorenz Brun1d801752020-04-02 09:24:51 +0200261diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100262index 783738448ff55..e1f52ffa86bb8 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200263--- a/arch/sh/kernel/syscalls/syscall.tbl
264+++ b/arch/sh/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100265@@ -443,3 +443,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200266 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100267 439 common faccessat2 sys_faccessat2
268 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200269+441 common fsinfo sys_fsinfo
270diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100271index 78160260991be..1f42dc9965c23 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200272--- a/arch/sparc/kernel/syscalls/syscall.tbl
273+++ b/arch/sparc/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100274@@ -486,3 +486,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200275 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100276 439 common faccessat2 sys_faccessat2
277 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200278+441 common fsinfo sys_fsinfo
279diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100280index 0d0667a9fbd70..70da9bad3f79d 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200281--- a/arch/x86/entry/syscalls/syscall_32.tbl
282+++ b/arch/x86/entry/syscalls/syscall_32.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100283@@ -445,3 +445,4 @@
284 438 i386 pidfd_getfd sys_pidfd_getfd
285 439 i386 faccessat2 sys_faccessat2
286 440 i386 process_madvise sys_process_madvise
287+441 i386 fsinfo sys_fsinfo
Lorenz Brun1d801752020-04-02 09:24:51 +0200288diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100289index 379819244b91d..94a51b78e85ed 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200290--- a/arch/x86/entry/syscalls/syscall_64.tbl
291+++ b/arch/x86/entry/syscalls/syscall_64.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100292@@ -362,6 +362,7 @@
293 438 common pidfd_getfd sys_pidfd_getfd
294 439 common faccessat2 sys_faccessat2
295 440 common process_madvise sys_process_madvise
296+441 common fsinfo sys_fsinfo
Lorenz Brun1d801752020-04-02 09:24:51 +0200297
298 #
Lorenz Brun5999e922021-01-27 18:53:54 +0100299 # Due to a historical design error, certain syscalls are numbered differently
Lorenz Brun1d801752020-04-02 09:24:51 +0200300diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100301index b070f272995d6..2723c2f43c59a 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200302--- a/arch/xtensa/kernel/syscalls/syscall.tbl
303+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
Lorenz Brun5999e922021-01-27 18:53:54 +0100304@@ -411,3 +411,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200305 438 common pidfd_getfd sys_pidfd_getfd
Lorenz Brun5999e922021-01-27 18:53:54 +0100306 439 common faccessat2 sys_faccessat2
307 440 common process_madvise sys_process_madvise
Lorenz Brun1d801752020-04-02 09:24:51 +0200308+441 common fsinfo sys_fsinfo
309diff --git a/fs/Kconfig b/fs/Kconfig
Lorenz Brun5999e922021-01-27 18:53:54 +0100310index aa4c122823018..b84156272983b 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200311--- a/fs/Kconfig
312+++ b/fs/Kconfig
313@@ -15,6 +15,13 @@ config VALIDATE_FS_PARSER
314 Enable this to perform validation of the parameter description for a
315 filesystem when it is registered.
316
317+config FSINFO
318+ bool "Enable the fsinfo() system call"
319+ help
320+ Enable the file system information querying system call to allow
321+ comprehensive information to be retrieved about a filesystem,
322+ superblock or mount object.
323+
324 if BLOCK
325
326 config FS_IOMAP
327diff --git a/fs/Makefile b/fs/Makefile
Lorenz Brun5999e922021-01-27 18:53:54 +0100328index 999d1a23f036c..02da949de3b42 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200329--- a/fs/Makefile
330+++ b/fs/Makefile
331@@ -54,6 +54,7 @@ obj-$(CONFIG_COREDUMP) += coredump.o
332 obj-$(CONFIG_SYSCTL) += drop_caches.o
333
334 obj-$(CONFIG_FHANDLE) += fhandle.o
335+obj-$(CONFIG_FSINFO) += fsinfo.o
336 obj-y += iomap/
337
338 obj-y += quota/
339diff --git a/fs/fsinfo.c b/fs/fsinfo.c
340new file mode 100644
Lorenz Brun5999e922021-01-27 18:53:54 +0100341index 0000000000000..7d9c73e9cbdef
Lorenz Brun1d801752020-04-02 09:24:51 +0200342--- /dev/null
343+++ b/fs/fsinfo.c
Lorenz Brun5999e922021-01-27 18:53:54 +0100344@@ -0,0 +1,596 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200345+// SPDX-License-Identifier: GPL-2.0
346+/* Filesystem information query.
347+ *
348+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
349+ * Written by David Howells (dhowells@redhat.com)
350+ */
351+#include <linux/syscalls.h>
352+#include <linux/fs.h>
353+#include <linux/file.h>
354+#include <linux/mount.h>
355+#include <linux/namei.h>
356+#include <linux/statfs.h>
357+#include <linux/security.h>
358+#include <linux/uaccess.h>
359+#include <linux/fsinfo.h>
360+#include <uapi/linux/mount.h>
361+#include "internal.h"
362+
363+/**
Lorenz Brun5999e922021-01-27 18:53:54 +0100364+ * fsinfo_opaque - Store opaque blob as an fsinfo attribute value.
365+ * @s: The blob to store (may be NULL)
366+ * @ctx: The parameter context
367+ * @len: The length of the blob
368+ */
369+int fsinfo_opaque(const void *s, struct fsinfo_context *ctx, unsigned int len)
370+{
371+ void *p = ctx->buffer;
372+ int ret = 0;
373+
374+ if (s) {
375+ if (!ctx->want_size_only)
376+ memcpy(p, s, len);
377+ ret = len;
378+ }
379+
380+ return ret;
381+}
382+EXPORT_SYMBOL(fsinfo_opaque);
383+
384+/**
Lorenz Brun1d801752020-04-02 09:24:51 +0200385+ * fsinfo_string - Store a NUL-terminated string as an fsinfo attribute value.
386+ * @s: The string to store (may be NULL)
387+ * @ctx: The parameter context
388+ */
389+int fsinfo_string(const char *s, struct fsinfo_context *ctx)
390+{
Lorenz Brun5999e922021-01-27 18:53:54 +0100391+ if (!s)
392+ return 1;
393+ return fsinfo_opaque(s, ctx, min_t(size_t, strlen(s) + 1, ctx->buf_size));
Lorenz Brun1d801752020-04-02 09:24:51 +0200394+}
395+EXPORT_SYMBOL(fsinfo_string);
396+
397+/*
398+ * Get basic filesystem stats from statfs.
399+ */
400+static int fsinfo_generic_statfs(struct path *path, struct fsinfo_context *ctx)
401+{
402+ struct fsinfo_statfs *p = ctx->buffer;
403+ struct kstatfs buf;
404+ int ret;
405+
406+ ret = vfs_statfs(path, &buf);
407+ if (ret < 0)
408+ return ret;
409+
410+ p->f_blocks.lo = buf.f_blocks;
411+ p->f_bfree.lo = buf.f_bfree;
412+ p->f_bavail.lo = buf.f_bavail;
413+ p->f_files.lo = buf.f_files;
414+ p->f_ffree.lo = buf.f_ffree;
415+ p->f_favail.lo = buf.f_ffree;
416+ p->f_bsize = buf.f_bsize;
417+ p->f_frsize = buf.f_frsize;
418+ return sizeof(*p);
419+}
420+
421+static int fsinfo_generic_ids(struct path *path, struct fsinfo_context *ctx)
422+{
423+ struct fsinfo_ids *p = ctx->buffer;
424+ struct super_block *sb;
425+ struct kstatfs buf;
426+ int ret;
427+
428+ ret = vfs_statfs(path, &buf);
429+ if (ret < 0 && ret != -ENOSYS)
430+ return ret;
431+ if (ret == 0)
432+ memcpy(&p->f_fsid, &buf.f_fsid, sizeof(p->f_fsid));
433+
434+ sb = path->dentry->d_sb;
435+ p->f_fstype = sb->s_magic;
436+ p->f_dev_major = MAJOR(sb->s_dev);
437+ p->f_dev_minor = MINOR(sb->s_dev);
438+ p->f_sb_id = sb->s_unique_id;
439+ strlcpy(p->f_fs_name, sb->s_type->name, sizeof(p->f_fs_name));
440+ return sizeof(*p);
441+}
442+
443+int fsinfo_generic_limits(struct path *path, struct fsinfo_context *ctx)
444+{
445+ struct fsinfo_limits *p = ctx->buffer;
446+ struct super_block *sb = path->dentry->d_sb;
447+
448+ p->max_file_size.hi = 0;
449+ p->max_file_size.lo = sb->s_maxbytes;
450+ p->max_ino.hi = 0;
451+ p->max_ino.lo = UINT_MAX;
452+ p->max_hard_links = sb->s_max_links;
453+ p->max_uid = UINT_MAX;
454+ p->max_gid = UINT_MAX;
455+ p->max_projid = UINT_MAX;
456+ p->max_filename_len = NAME_MAX;
457+ p->max_symlink_len = PATH_MAX;
458+ p->max_xattr_name_len = XATTR_NAME_MAX;
459+ p->max_xattr_body_len = XATTR_SIZE_MAX;
460+ p->max_dev_major = 0xffffff;
461+ p->max_dev_minor = 0xff;
462+ return sizeof(*p);
463+}
464+EXPORT_SYMBOL(fsinfo_generic_limits);
465+
466+int fsinfo_generic_supports(struct path *path, struct fsinfo_context *ctx)
467+{
468+ struct fsinfo_supports *p = ctx->buffer;
469+ struct super_block *sb = path->dentry->d_sb;
470+
471+ p->stx_mask = STATX_BASIC_STATS;
472+ if (sb->s_d_op && sb->s_d_op->d_automount)
473+ p->stx_attributes |= STATX_ATTR_AUTOMOUNT;
474+ return sizeof(*p);
475+}
476+EXPORT_SYMBOL(fsinfo_generic_supports);
477+
478+static const struct fsinfo_timestamp_info fsinfo_default_timestamp_info = {
479+ .atime = {
480+ .minimum = S64_MIN,
481+ .maximum = S64_MAX,
482+ .gran_mantissa = 1,
483+ .gran_exponent = 0,
484+ },
485+ .mtime = {
486+ .minimum = S64_MIN,
487+ .maximum = S64_MAX,
488+ .gran_mantissa = 1,
489+ .gran_exponent = 0,
490+ },
491+ .ctime = {
492+ .minimum = S64_MIN,
493+ .maximum = S64_MAX,
494+ .gran_mantissa = 1,
495+ .gran_exponent = 0,
496+ },
497+ .btime = {
498+ .minimum = S64_MIN,
499+ .maximum = S64_MAX,
500+ .gran_mantissa = 1,
501+ .gran_exponent = 0,
502+ },
503+};
504+
505+int fsinfo_generic_timestamp_info(struct path *path, struct fsinfo_context *ctx)
506+{
507+ struct fsinfo_timestamp_info *p = ctx->buffer;
508+ struct super_block *sb = path->dentry->d_sb;
509+ s8 exponent;
510+
511+ *p = fsinfo_default_timestamp_info;
512+
513+ if (sb->s_time_gran < 1000000000) {
514+ if (sb->s_time_gran < 1000)
515+ exponent = -9;
516+ else if (sb->s_time_gran < 1000000)
517+ exponent = -6;
518+ else
519+ exponent = -3;
520+
521+ p->atime.gran_exponent = exponent;
522+ p->mtime.gran_exponent = exponent;
523+ p->ctime.gran_exponent = exponent;
524+ p->btime.gran_exponent = exponent;
525+ }
526+
527+ return sizeof(*p);
528+}
529+EXPORT_SYMBOL(fsinfo_generic_timestamp_info);
530+
531+static int fsinfo_generic_volume_uuid(struct path *path, struct fsinfo_context *ctx)
532+{
533+ struct fsinfo_volume_uuid *p = ctx->buffer;
534+ struct super_block *sb = path->dentry->d_sb;
535+
536+ memcpy(p, &sb->s_uuid, sizeof(*p));
537+ return sizeof(*p);
538+}
539+
540+static int fsinfo_generic_volume_id(struct path *path, struct fsinfo_context *ctx)
541+{
542+ return fsinfo_string(path->dentry->d_sb->s_id, ctx);
543+}
544+
545+static const struct fsinfo_attribute fsinfo_common_attributes[] = {
546+ FSINFO_VSTRUCT (FSINFO_ATTR_STATFS, fsinfo_generic_statfs),
547+ FSINFO_VSTRUCT (FSINFO_ATTR_IDS, fsinfo_generic_ids),
548+ FSINFO_VSTRUCT (FSINFO_ATTR_LIMITS, fsinfo_generic_limits),
549+ FSINFO_VSTRUCT (FSINFO_ATTR_SUPPORTS, fsinfo_generic_supports),
550+ FSINFO_VSTRUCT (FSINFO_ATTR_TIMESTAMP_INFO, fsinfo_generic_timestamp_info),
551+ FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, fsinfo_generic_volume_id),
552+ FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
553+
554+ FSINFO_LIST (FSINFO_ATTR_FSINFO_ATTRIBUTES, (void *)123UL),
555+ FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, (void *)123UL),
556+ {}
557+};
558+
559+/*
560+ * Determine an attribute's minimum buffer size and, if the buffer is large
561+ * enough, get the attribute value.
562+ */
563+static int fsinfo_get_this_attribute(struct path *path,
564+ struct fsinfo_context *ctx,
565+ const struct fsinfo_attribute *attr)
566+{
567+ int buf_size;
568+
569+ if (ctx->Nth != 0 && !(attr->flags & (FSINFO_FLAGS_N | FSINFO_FLAGS_NM)))
570+ return -ENODATA;
571+ if (ctx->Mth != 0 && !(attr->flags & FSINFO_FLAGS_NM))
572+ return -ENODATA;
573+
574+ switch (attr->type) {
575+ case FSINFO_TYPE_VSTRUCT:
576+ ctx->clear_tail = true;
577+ buf_size = attr->size;
578+ break;
579+ case FSINFO_TYPE_STRING:
580+ case FSINFO_TYPE_OPAQUE:
581+ case FSINFO_TYPE_LIST:
582+ buf_size = 4096;
583+ break;
584+ default:
585+ return -ENOPKG;
586+ }
587+
588+ if (ctx->buf_size < buf_size)
589+ return buf_size;
590+
591+ return attr->get(path, ctx);
592+}
593+
594+static void fsinfo_attributes_insert(struct fsinfo_context *ctx,
595+ const struct fsinfo_attribute *attr)
596+{
597+ __u32 *p = ctx->buffer;
598+ unsigned int i;
599+
600+ if (ctx->usage >= ctx->buf_size ||
601+ ctx->buf_size - ctx->usage < sizeof(__u32)) {
602+ ctx->usage += sizeof(__u32);
603+ return;
604+ }
605+
606+ for (i = 0; i < ctx->usage / sizeof(__u32); i++)
607+ if (p[i] == attr->attr_id)
608+ return;
609+
610+ p[i] = attr->attr_id;
611+ ctx->usage += sizeof(__u32);
612+}
613+
614+static int fsinfo_list_attributes(struct path *path,
615+ struct fsinfo_context *ctx,
616+ const struct fsinfo_attribute *attributes)
617+{
618+ const struct fsinfo_attribute *a;
619+
620+ for (a = attributes; a->get; a++)
621+ fsinfo_attributes_insert(ctx, a);
622+ return -EOPNOTSUPP; /* We want to go through all the lists */
623+}
624+
625+static int fsinfo_get_attribute_info(struct path *path,
626+ struct fsinfo_context *ctx,
627+ const struct fsinfo_attribute *attributes)
628+{
629+ const struct fsinfo_attribute *a;
630+ struct fsinfo_attribute_info *p = ctx->buffer;
631+
632+ if (!ctx->buf_size)
633+ return sizeof(*p);
634+
635+ for (a = attributes; a->get; a++) {
636+ if (a->attr_id == ctx->Nth) {
637+ p->attr_id = a->attr_id;
638+ p->type = a->type;
639+ p->flags = a->flags;
640+ p->size = a->size;
641+ p->size = a->size;
642+ return sizeof(*p);
643+ }
644+ }
645+ return -EOPNOTSUPP; /* We want to go through all the lists */
646+}
647+
648+/**
649+ * fsinfo_get_attribute - Look up and handle an attribute
650+ * @path: The object to query
651+ * @params: Parameters to define a request and place to store result
652+ * @attributes: List of attributes to search.
653+ *
654+ * Look through a list of attributes for one that matches the requested
655+ * attribute then call the handler for it.
656+ */
657+int fsinfo_get_attribute(struct path *path, struct fsinfo_context *ctx,
658+ const struct fsinfo_attribute *attributes)
659+{
660+ const struct fsinfo_attribute *a;
661+
662+ switch (ctx->requested_attr) {
663+ case FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO:
664+ return fsinfo_get_attribute_info(path, ctx, attributes);
665+ case FSINFO_ATTR_FSINFO_ATTRIBUTES:
666+ return fsinfo_list_attributes(path, ctx, attributes);
667+ default:
668+ for (a = attributes; a->get; a++)
669+ if (a->attr_id == ctx->requested_attr)
670+ return fsinfo_get_this_attribute(path, ctx, a);
671+ return -EOPNOTSUPP;
672+ }
673+}
674+EXPORT_SYMBOL(fsinfo_get_attribute);
675+
676+/**
677+ * generic_fsinfo - Handle an fsinfo attribute generically
678+ * @path: The object to query
679+ * @params: Parameters to define a request and place to store result
680+ */
681+static int fsinfo_call(struct path *path, struct fsinfo_context *ctx)
682+{
683+ int ret;
684+
685+ if (path->dentry->d_sb->s_op->fsinfo) {
686+ ret = path->dentry->d_sb->s_op->fsinfo(path, ctx);
687+ if (ret != -EOPNOTSUPP)
688+ return ret;
689+ }
690+ ret = fsinfo_get_attribute(path, ctx, fsinfo_common_attributes);
691+ if (ret != -EOPNOTSUPP)
692+ return ret;
693+
694+ switch (ctx->requested_attr) {
695+ case FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO:
696+ return -ENODATA;
697+ case FSINFO_ATTR_FSINFO_ATTRIBUTES:
698+ return ctx->usage;
699+ default:
700+ return -EOPNOTSUPP;
701+ }
702+}
703+
704+/**
705+ * vfs_fsinfo - Retrieve filesystem information
706+ * @path: The object to query
707+ * @params: Parameters to define a request and place to store result
708+ *
709+ * Get an attribute on a filesystem or an object within a filesystem. The
710+ * filesystem attribute to be queried is indicated by @ctx->requested_attr, and
711+ * if it's a multi-valued attribute, the particular value is selected by
712+ * @ctx->Nth and then @ctx->Mth.
713+ *
714+ * For common attributes, a value may be fabricated if it is not supported by
715+ * the filesystem.
716+ *
717+ * On success, the size of the attribute's value is returned (0 is a valid
718+ * size). A buffer will have been allocated and will be pointed to by
719+ * @ctx->buffer. The caller must free this with kvfree().
720+ *
721+ * Errors can also be returned: -ENOMEM if a buffer cannot be allocated, -EPERM
722+ * or -EACCES if permission is denied by the LSM, -EOPNOTSUPP if an attribute
723+ * doesn't exist for the specified object or -ENODATA if the attribute exists,
724+ * but the Nth,Mth value does not exist. -EMSGSIZE indicates that the value is
725+ * unmanageable internally and -ENOPKG indicates other internal failure.
726+ *
727+ * Errors such as -EIO may also come from attempts to access media or servers
728+ * to obtain the requested information if it's not immediately to hand.
729+ *
730+ * [*] Note that the caller may set @ctx->want_size_only if it only wants the
731+ * size of the value and not the data. If this is set, a buffer may not be
732+ * allocated under some circumstances. This is intended for size query by
733+ * userspace.
734+ *
735+ * [*] Note that @ctx->clear_tail will be returned set if the data should be
736+ * padded out with zeros when writing it to userspace.
737+ */
738+static int vfs_fsinfo(struct path *path, struct fsinfo_context *ctx)
739+{
740+ struct dentry *dentry = path->dentry;
741+ int ret;
742+
743+ ret = security_sb_statfs(dentry);
744+ if (ret)
745+ return ret;
746+
747+ /* Call the handler to find out the buffer size required. */
748+ ctx->buf_size = 0;
749+ ret = fsinfo_call(path, ctx);
750+ if (ret < 0 || ctx->want_size_only)
751+ return ret;
752+ ctx->buf_size = ret;
753+
754+ do {
755+ /* Allocate a buffer of the requested size. */
756+ if (ctx->buf_size > INT_MAX)
757+ return -EMSGSIZE;
758+ ctx->buffer = kvzalloc(ctx->buf_size, GFP_KERNEL);
759+ if (!ctx->buffer)
760+ return -ENOMEM;
761+
762+ ctx->usage = 0;
763+ ctx->skip = 0;
764+ ret = fsinfo_call(path, ctx);
765+ if (IS_ERR_VALUE((long)ret))
766+ return ret;
767+ if ((unsigned int)ret <= ctx->buf_size)
768+ return ret; /* It fitted */
769+
770+ /* We need to resize the buffer */
771+ ctx->buf_size = roundup(ret, PAGE_SIZE);
772+ kvfree(ctx->buffer);
773+ ctx->buffer = NULL;
774+ } while (!signal_pending(current));
775+
776+ return -ERESTARTSYS;
777+}
778+
779+static int vfs_fsinfo_path(int dfd, const char __user *pathname,
780+ const struct fsinfo_params *up,
781+ struct fsinfo_context *ctx)
782+{
783+ struct path path;
784+ unsigned lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
785+ int ret = -EINVAL;
786+
787+ if (up->resolve_flags & ~VALID_RESOLVE_FLAGS)
788+ return -EINVAL;
789+ if (up->at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
790+ AT_EMPTY_PATH))
791+ return -EINVAL;
792+
793+ if (up->resolve_flags & RESOLVE_NO_XDEV)
794+ lookup_flags |= LOOKUP_NO_XDEV;
795+ if (up->resolve_flags & RESOLVE_NO_MAGICLINKS)
796+ lookup_flags |= LOOKUP_NO_MAGICLINKS;
797+ if (up->resolve_flags & RESOLVE_NO_SYMLINKS)
798+ lookup_flags |= LOOKUP_NO_SYMLINKS;
799+ if (up->resolve_flags & RESOLVE_BENEATH)
800+ lookup_flags |= LOOKUP_BENEATH;
801+ if (up->resolve_flags & RESOLVE_IN_ROOT)
802+ lookup_flags |= LOOKUP_IN_ROOT;
803+ if (up->at_flags & AT_SYMLINK_NOFOLLOW)
804+ lookup_flags &= ~LOOKUP_FOLLOW;
805+ if (up->at_flags & AT_NO_AUTOMOUNT)
806+ lookup_flags &= ~LOOKUP_AUTOMOUNT;
807+ if (up->at_flags & AT_EMPTY_PATH)
808+ lookup_flags |= LOOKUP_EMPTY;
809+
810+retry:
811+ ret = user_path_at(dfd, pathname, lookup_flags, &path);
812+ if (ret)
813+ goto out;
814+
815+ ret = vfs_fsinfo(&path, ctx);
816+ path_put(&path);
817+ if (retry_estale(ret, lookup_flags)) {
818+ lookup_flags |= LOOKUP_REVAL;
819+ goto retry;
820+ }
821+out:
822+ return ret;
823+}
824+
825+static int vfs_fsinfo_fd(unsigned int fd, struct fsinfo_context *ctx)
826+{
827+ struct fd f = fdget_raw(fd);
828+ int ret = -EBADF;
829+
830+ if (f.file) {
831+ ret = vfs_fsinfo(&f.file->f_path, ctx);
832+ fdput(f);
833+ }
834+ return ret;
835+}
836+
837+/**
838+ * sys_fsinfo - System call to get filesystem information
839+ * @dfd: Base directory to pathwalk from or fd referring to filesystem.
840+ * @pathname: Filesystem to query or NULL.
841+ * @params: Parameters to define request (NULL: FSINFO_ATTR_STATFS).
842+ * @params_size: Size of parameter buffer.
843+ * @result_buffer: Result buffer.
844+ * @result_buf_size: Size of result buffer.
845+ *
846+ * Get information on a filesystem. The filesystem attribute to be queried is
847+ * indicated by @_params->request, and some of the attributes can have multiple
848+ * values, indexed by @_params->Nth and @_params->Mth. If @_params is NULL,
849+ * then the 0th fsinfo_attr_statfs attribute is queried. If an attribute does
850+ * not exist, EOPNOTSUPP is returned; if the Nth,Mth value does not exist,
851+ * ENODATA is returned.
852+ *
853+ * On success, the size of the attribute's value is returned. If
854+ * @result_buf_size is 0 or @result_buffer is NULL, only the size is returned.
855+ * If the size of the value is larger than @result_buf_size, it will be
856+ * truncated by the copy. If the size of the value is smaller than
857+ * @result_buf_size then the excess buffer space will be cleared. The full
858+ * size of the value will be returned, irrespective of how much data is
859+ * actually placed in the buffer.
860+ */
861+SYSCALL_DEFINE6(fsinfo,
862+ int, dfd,
863+ const char __user *, pathname,
864+ const struct fsinfo_params __user *, params,
865+ size_t, params_size,
866+ void __user *, result_buffer,
867+ size_t, result_buf_size)
868+{
869+ struct fsinfo_context ctx;
870+ struct fsinfo_params user_params;
871+ unsigned int result_size;
872+ void *r;
873+ int ret;
874+
875+ if ((!params && params_size) ||
876+ ( params && !params_size) ||
877+ (!result_buffer && result_buf_size) ||
878+ ( result_buffer && !result_buf_size))
879+ return -EINVAL;
880+ if (result_buf_size > UINT_MAX)
881+ return -EOVERFLOW;
882+
883+ memset(&ctx, 0, sizeof(ctx));
884+ ctx.requested_attr = FSINFO_ATTR_STATFS;
885+ ctx.flags = FSINFO_FLAGS_QUERY_PATH;
886+ ctx.want_size_only = (result_buf_size == 0);
887+
888+ if (params) {
889+ ret = copy_struct_from_user(&user_params, sizeof(user_params),
890+ params, params_size);
891+ if (ret < 0)
892+ return ret;
893+ if (user_params.flags & ~FSINFO_FLAGS_QUERY_MASK)
894+ return -EINVAL;
895+ ctx.flags = user_params.flags;
896+ ctx.requested_attr = user_params.request;
897+ ctx.Nth = user_params.Nth;
898+ ctx.Mth = user_params.Mth;
899+ }
900+
901+ switch (ctx.flags & FSINFO_FLAGS_QUERY_MASK) {
902+ case FSINFO_FLAGS_QUERY_PATH:
903+ ret = vfs_fsinfo_path(dfd, pathname, &user_params, &ctx);
904+ break;
905+ case FSINFO_FLAGS_QUERY_FD:
906+ if (pathname)
907+ return -EINVAL;
908+ ret = vfs_fsinfo_fd(dfd, &ctx);
909+ break;
910+ default:
911+ return -EINVAL;
912+ }
913+
914+ if (ret < 0)
915+ goto error;
916+
917+ r = ctx.buffer + ctx.skip;
918+ result_size = min_t(size_t, ret, result_buf_size);
919+ if (result_size > 0 &&
920+ copy_to_user(result_buffer, r, result_size) != 0) {
921+ ret = -EFAULT;
922+ goto error;
923+ }
924+
925+ /* Clear any part of the buffer that we won't fill if we're putting a
926+ * struct in there. Strings, opaque objects and arrays are expected to
927+ * be variable length.
928+ */
929+ if (ctx.clear_tail &&
930+ result_buf_size > result_size &&
931+ clear_user(result_buffer + result_size,
932+ result_buf_size - result_size) != 0) {
933+ ret = -EFAULT;
934+ goto error;
935+ }
936+
937+error:
938+ kvfree(ctx.buffer);
939+ return ret;
940+}
941diff --git a/include/linux/fs.h b/include/linux/fs.h
Lorenz Brun5999e922021-01-27 18:53:54 +0100942index e1325dec4fa86..2d5a2f709f322 100644
Lorenz Brun1d801752020-04-02 09:24:51 +0200943--- a/include/linux/fs.h
944+++ b/include/linux/fs.h
945@@ -68,6 +68,7 @@ struct fsverity_info;
946 struct fsverity_operations;
947 struct fs_context;
948 struct fs_parameter_spec;
949+struct fsinfo_context;
950
951 extern void __init inode_init(void);
952 extern void __init inode_init_early(void);
Lorenz Brun5999e922021-01-27 18:53:54 +0100953@@ -1951,6 +1952,9 @@ struct super_operations {
Lorenz Brun1d801752020-04-02 09:24:51 +0200954 int (*thaw_super) (struct super_block *);
955 int (*unfreeze_fs) (struct super_block *);
956 int (*statfs) (struct dentry *, struct kstatfs *);
957+#ifdef CONFIG_FSINFO
958+ int (*fsinfo)(struct path *, struct fsinfo_context *);
959+#endif
960 int (*remount_fs) (struct super_block *, int *, char *);
961 void (*umount_begin) (struct super_block *);
962
963diff --git a/include/linux/fsinfo.h b/include/linux/fsinfo.h
964new file mode 100644
Lorenz Brun5999e922021-01-27 18:53:54 +0100965index 0000000000000..a811d69b02ff0
Lorenz Brun1d801752020-04-02 09:24:51 +0200966--- /dev/null
967+++ b/include/linux/fsinfo.h
Lorenz Brun5999e922021-01-27 18:53:54 +0100968@@ -0,0 +1,74 @@
Lorenz Brun1d801752020-04-02 09:24:51 +0200969+// SPDX-License-Identifier: GPL-2.0
970+/* Filesystem information query
971+ *
972+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
973+ * Written by David Howells (dhowells@redhat.com)
974+ */
975+
976+#ifndef _LINUX_FSINFO_H
977+#define _LINUX_FSINFO_H
978+
979+#ifdef CONFIG_FSINFO
980+
981+#include <uapi/linux/fsinfo.h>
982+
983+struct path;
984+
985+#define FSINFO_NORMAL_ATTR_MAX_SIZE 4096
986+
987+struct fsinfo_context {
988+ __u32 flags; /* [in] FSINFO_FLAGS_* */
989+ __u32 requested_attr; /* [in] What is being asking for */
990+ __u32 Nth; /* [in] Instance of it (some may have multiple) */
991+ __u32 Mth; /* [in] Subinstance */
992+ bool want_size_only; /* [in] Just want to know the size, not the data */
993+ bool clear_tail; /* [out] T if tail of buffer should be cleared */
994+ unsigned int skip; /* [out] Number of bytes to skip in buffer */
995+ unsigned int usage; /* [tmp] Amount of buffer used (if large) */
996+ unsigned int buf_size; /* [tmp] Size of ->buffer[] */
997+ void *buffer; /* [out] The reply buffer */
998+};
999+
1000+/*
1001+ * A filesystem information attribute definition.
1002+ */
1003+struct fsinfo_attribute {
1004+ unsigned int attr_id; /* The ID of the attribute */
1005+ enum fsinfo_value_type type:8; /* The type of the attribute's value(s) */
1006+ unsigned int flags:8;
1007+ unsigned int size:16; /* - Value size (FSINFO_STRUCT/LIST) */
1008+ int (*get)(struct path *path, struct fsinfo_context *params);
1009+};
1010+
1011+#define __FSINFO(A, T, S, G, F) \
1012+ { .attr_id = A, .type = T, .flags = F, .size = S, .get = G }
1013+
1014+#define _FSINFO(A, T, S, G) __FSINFO(A, T, S, G, 0)
1015+#define _FSINFO_N(A, T, S, G) __FSINFO(A, T, S, G, FSINFO_FLAGS_N)
1016+#define _FSINFO_NM(A, T, S, G) __FSINFO(A, T, S, G, FSINFO_FLAGS_NM)
1017+
1018+#define _FSINFO_VSTRUCT(A,S,G) _FSINFO (A, FSINFO_TYPE_VSTRUCT, sizeof(S), G)
1019+#define _FSINFO_VSTRUCT_N(A,S,G) _FSINFO_N (A, FSINFO_TYPE_VSTRUCT, sizeof(S), G)
1020+#define _FSINFO_VSTRUCT_NM(A,S,G) _FSINFO_NM(A, FSINFO_TYPE_VSTRUCT, sizeof(S), G)
1021+
1022+#define FSINFO_VSTRUCT(A,G) _FSINFO_VSTRUCT (A, A##__STRUCT, G)
1023+#define FSINFO_VSTRUCT_N(A,G) _FSINFO_VSTRUCT_N (A, A##__STRUCT, G)
1024+#define FSINFO_VSTRUCT_NM(A,G) _FSINFO_VSTRUCT_NM(A, A##__STRUCT, G)
1025+#define FSINFO_STRING(A,G) _FSINFO (A, FSINFO_TYPE_STRING, 0, G)
1026+#define FSINFO_STRING_N(A,G) _FSINFO_N (A, FSINFO_TYPE_STRING, 0, G)
1027+#define FSINFO_STRING_NM(A,G) _FSINFO_NM(A, FSINFO_TYPE_STRING, 0, G)
1028+#define FSINFO_OPAQUE(A,G) _FSINFO (A, FSINFO_TYPE_OPAQUE, 0, G)
1029+#define FSINFO_LIST(A,G) _FSINFO (A, FSINFO_TYPE_LIST, sizeof(A##__STRUCT), G)
1030+#define FSINFO_LIST_N(A,G) _FSINFO_N (A, FSINFO_TYPE_LIST, sizeof(A##__STRUCT), G)
1031+
Lorenz Brun5999e922021-01-27 18:53:54 +01001032+extern int fsinfo_opaque(const void *, struct fsinfo_context *, unsigned int);
Lorenz Brun1d801752020-04-02 09:24:51 +02001033+extern int fsinfo_string(const char *, struct fsinfo_context *);
1034+extern int fsinfo_generic_timestamp_info(struct path *, struct fsinfo_context *);
1035+extern int fsinfo_generic_supports(struct path *, struct fsinfo_context *);
1036+extern int fsinfo_generic_limits(struct path *, struct fsinfo_context *);
1037+extern int fsinfo_get_attribute(struct path *, struct fsinfo_context *,
1038+ const struct fsinfo_attribute *);
1039+
1040+#endif /* CONFIG_FSINFO */
1041+
1042+#endif /* _LINUX_FSINFO_H */
1043diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
Lorenz Brun5999e922021-01-27 18:53:54 +01001044index 37bea07c12f21..e315f719899a1 100644
Lorenz Brun1d801752020-04-02 09:24:51 +02001045--- a/include/linux/syscalls.h
1046+++ b/include/linux/syscalls.h
1047@@ -47,6 +47,7 @@ struct stat64;
1048 struct statfs;
1049 struct statfs64;
1050 struct statx;
1051+struct fsinfo_params;
Lorenz Brun1d801752020-04-02 09:24:51 +02001052 struct sysinfo;
1053 struct timespec;
Lorenz Brun5999e922021-01-27 18:53:54 +01001054 struct __kernel_old_timeval;
1055@@ -1008,6 +1009,9 @@ asmlinkage long sys_pidfd_send_signal(int pidfd, int sig,
Lorenz Brun1d801752020-04-02 09:24:51 +02001056 siginfo_t __user *info,
1057 unsigned int flags);
1058 asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags);
1059+asmlinkage long sys_fsinfo(int dfd, const char __user *pathname,
Lorenz Brun5999e922021-01-27 18:53:54 +01001060+ const struct fsinfo_params __user *params, size_t params_size,
Lorenz Brun1d801752020-04-02 09:24:51 +02001061+ void __user *result_buffer, size_t result_buf_size);
1062
1063 /*
1064 * Architecture-specific system calls
1065diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
Lorenz Brun5999e922021-01-27 18:53:54 +01001066index 2056318988f77..3a2e44f770f8e 100644
Lorenz Brun1d801752020-04-02 09:24:51 +02001067--- a/include/uapi/asm-generic/unistd.h
1068+++ b/include/uapi/asm-generic/unistd.h
Lorenz Brun5999e922021-01-27 18:53:54 +01001069@@ -859,9 +859,11 @@ __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
1070 __SYSCALL(__NR_faccessat2, sys_faccessat2)
1071 #define __NR_process_madvise 440
1072 __SYSCALL(__NR_process_madvise, sys_process_madvise)
Lorenz Brun1d801752020-04-02 09:24:51 +02001073+#define __NR_fsinfo 441
1074+__SYSCALL(__NR_fsinfo, sys_fsinfo)
1075
1076 #undef __NR_syscalls
Lorenz Brun5999e922021-01-27 18:53:54 +01001077-#define __NR_syscalls 441
1078+#define __NR_syscalls 443
Lorenz Brun1d801752020-04-02 09:24:51 +02001079
1080 /*
1081 * 32 bit systems traditionally used different
1082diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
1083new file mode 100644
Lorenz Brun5999e922021-01-27 18:53:54 +01001084index 0000000000000..65892239ba86c
Lorenz Brun1d801752020-04-02 09:24:51 +02001085--- /dev/null
1086+++ b/include/uapi/linux/fsinfo.h
Lorenz Brun5999e922021-01-27 18:53:54 +01001087@@ -0,0 +1,189 @@
Lorenz Brun1d801752020-04-02 09:24:51 +02001088+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
1089+/* fsinfo() definitions.
1090+ *
1091+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
1092+ * Written by David Howells (dhowells@redhat.com)
1093+ */
1094+#ifndef _UAPI_LINUX_FSINFO_H
1095+#define _UAPI_LINUX_FSINFO_H
1096+
1097+#include <linux/types.h>
1098+#include <linux/socket.h>
1099+#include <linux/openat2.h>
1100+
1101+/*
1102+ * The filesystem attributes that can be requested. Note that some attributes
1103+ * may have multiple instances which can be switched in the parameter block.
1104+ */
1105+#define FSINFO_ATTR_STATFS 0x00 /* statfs()-style state */
1106+#define FSINFO_ATTR_IDS 0x01 /* Filesystem IDs */
1107+#define FSINFO_ATTR_LIMITS 0x02 /* Filesystem limits */
1108+#define FSINFO_ATTR_SUPPORTS 0x03 /* What's supported in statx, iocflags, ... */
1109+#define FSINFO_ATTR_TIMESTAMP_INFO 0x04 /* Inode timestamp info */
1110+#define FSINFO_ATTR_VOLUME_ID 0x05 /* Volume ID (string) */
1111+#define FSINFO_ATTR_VOLUME_UUID 0x06 /* Volume UUID (LE uuid) */
1112+#define FSINFO_ATTR_VOLUME_NAME 0x07 /* Volume name (string) */
1113+
1114+#define FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO 0x100 /* Information about attr N (for path) */
1115+#define FSINFO_ATTR_FSINFO_ATTRIBUTES 0x101 /* List of supported attrs (for path) */
1116+
1117+/*
1118+ * Optional fsinfo() parameter structure.
1119+ *
1120+ * If this is not given, it is assumed that fsinfo_attr_statfs instance 0,0 is
1121+ * desired.
1122+ */
1123+struct fsinfo_params {
1124+ __u64 resolve_flags; /* RESOLVE_* flags */
1125+ __u32 at_flags; /* AT_* flags */
1126+ __u32 flags; /* Flags controlling fsinfo() specifically */
1127+#define FSINFO_FLAGS_QUERY_MASK 0x0007 /* What object should fsinfo() query? */
1128+#define FSINFO_FLAGS_QUERY_PATH 0x0000 /* - path, specified by dirfd,pathname,AT_EMPTY_PATH */
1129+#define FSINFO_FLAGS_QUERY_FD 0x0001 /* - fd specified by dirfd */
1130+ __u32 request; /* ID of requested attribute */
1131+ __u32 Nth; /* Instance of it (some may have multiple) */
1132+ __u32 Mth; /* Subinstance of Nth instance */
1133+};
1134+
1135+enum fsinfo_value_type {
1136+ FSINFO_TYPE_VSTRUCT = 0, /* Version-lengthed struct (up to 4096 bytes) */
1137+ FSINFO_TYPE_STRING = 1, /* NUL-term var-length string (up to 4095 chars) */
1138+ FSINFO_TYPE_OPAQUE = 2, /* Opaque blob (unlimited size) */
1139+ FSINFO_TYPE_LIST = 3, /* List of ints/structs (unlimited size) */
1140+};
1141+
1142+/*
1143+ * Information struct for fsinfo(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO).
1144+ *
1145+ * This gives information about the attributes supported by fsinfo for the
1146+ * given path.
1147+ */
1148+struct fsinfo_attribute_info {
1149+ unsigned int attr_id; /* The ID of the attribute */
1150+ enum fsinfo_value_type type; /* The type of the attribute's value(s) */
1151+ unsigned int flags;
1152+#define FSINFO_FLAGS_N 0x01 /* - Attr has a set of values */
1153+#define FSINFO_FLAGS_NM 0x02 /* - Attr has a set of sets of values */
1154+ unsigned int size; /* - Value size (FSINFO_STRUCT/FSINFO_LIST) */
1155+};
1156+
1157+#define FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO__STRUCT struct fsinfo_attribute_info
1158+#define FSINFO_ATTR_FSINFO_ATTRIBUTES__STRUCT __u32
1159+
1160+struct fsinfo_u128 {
1161+#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN)
1162+ __u64 hi;
1163+ __u64 lo;
1164+#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN)
1165+ __u64 lo;
1166+ __u64 hi;
1167+#endif
1168+};
1169+
1170+/*
1171+ * Information struct for fsinfo(FSINFO_ATTR_STATFS).
1172+ * - This gives extended filesystem information.
1173+ */
1174+struct fsinfo_statfs {
1175+ struct fsinfo_u128 f_blocks; /* Total number of blocks in fs */
1176+ struct fsinfo_u128 f_bfree; /* Total number of free blocks */
1177+ struct fsinfo_u128 f_bavail; /* Number of free blocks available to ordinary user */
1178+ struct fsinfo_u128 f_files; /* Total number of file nodes in fs */
1179+ struct fsinfo_u128 f_ffree; /* Number of free file nodes */
1180+ struct fsinfo_u128 f_favail; /* Number of file nodes available to ordinary user */
1181+ __u64 f_bsize; /* Optimal block size */
1182+ __u64 f_frsize; /* Fragment size */
1183+};
1184+
1185+#define FSINFO_ATTR_STATFS__STRUCT struct fsinfo_statfs
1186+
1187+/*
1188+ * Information struct for fsinfo(FSINFO_ATTR_IDS).
1189+ *
1190+ * List of basic identifiers as is normally found in statfs().
1191+ */
1192+struct fsinfo_ids {
1193+ char f_fs_name[15 + 1]; /* Filesystem name */
1194+ __u64 f_fsid; /* Short 64-bit Filesystem ID (as statfs) */
1195+ __u64 f_sb_id; /* Internal superblock ID for sbnotify()/mntnotify() */
1196+ __u32 f_fstype; /* Filesystem type from linux/magic.h [uncond] */
1197+ __u32 f_dev_major; /* As st_dev_* from struct statx [uncond] */
1198+ __u32 f_dev_minor;
1199+ __u32 __padding[1];
1200+};
1201+
1202+#define FSINFO_ATTR_IDS__STRUCT struct fsinfo_ids
1203+
1204+/*
1205+ * Information struct for fsinfo(FSINFO_ATTR_LIMITS).
1206+ *
1207+ * List of supported filesystem limits.
1208+ */
1209+struct fsinfo_limits {
1210+ struct fsinfo_u128 max_file_size; /* Maximum file size */
1211+ struct fsinfo_u128 max_ino; /* Maximum inode number */
1212+ __u64 max_uid; /* Maximum UID supported */
1213+ __u64 max_gid; /* Maximum GID supported */
1214+ __u64 max_projid; /* Maximum project ID supported */
1215+ __u64 max_hard_links; /* Maximum number of hard links on a file */
1216+ __u64 max_xattr_body_len; /* Maximum xattr content length */
1217+ __u32 max_xattr_name_len; /* Maximum xattr name length */
1218+ __u32 max_filename_len; /* Maximum filename length */
1219+ __u32 max_symlink_len; /* Maximum symlink content length */
1220+ __u32 max_dev_major; /* Maximum device major representable */
1221+ __u32 max_dev_minor; /* Maximum device minor representable */
1222+ __u32 __padding[1];
1223+};
1224+
1225+#define FSINFO_ATTR_LIMITS__STRUCT struct fsinfo_limits
1226+
1227+/*
1228+ * Information struct for fsinfo(FSINFO_ATTR_SUPPORTS).
1229+ *
1230+ * What's supported in various masks, such as statx() attribute and mask bits
1231+ * and IOC flags.
1232+ */
1233+struct fsinfo_supports {
1234+ __u64 stx_attributes; /* What statx::stx_attributes are supported */
1235+ __u32 stx_mask; /* What statx::stx_mask bits are supported */
1236+ __u32 fs_ioc_getflags; /* What FS_IOC_GETFLAGS may return */
1237+ __u32 fs_ioc_setflags_set; /* What FS_IOC_SETFLAGS may set */
1238+ __u32 fs_ioc_setflags_clear; /* What FS_IOC_SETFLAGS may clear */
Lorenz Brun5999e922021-01-27 18:53:54 +01001239+ __u32 fs_ioc_fsgetxattr_xflags; /* What FS_IOC_FSGETXATTR[A] may return in fsx_xflags */
1240+ __u32 fs_ioc_fssetxattr_xflags_set; /* What FS_IOC_FSSETXATTR may set in fsx_xflags */
1241+ __u32 fs_ioc_fssetxattr_xflags_clear; /* What FS_IOC_FSSETXATTR may set in fsx_xflags */
Lorenz Brun1d801752020-04-02 09:24:51 +02001242+ __u32 win_file_attrs; /* What DOS/Windows FILE_* attributes are supported */
Lorenz Brun1d801752020-04-02 09:24:51 +02001243+};
1244+
1245+#define FSINFO_ATTR_SUPPORTS__STRUCT struct fsinfo_supports
1246+
1247+struct fsinfo_timestamp_one {
1248+ __s64 minimum; /* Minimum timestamp value in seconds */
1249+ __s64 maximum; /* Maximum timestamp value in seconds */
1250+ __u16 gran_mantissa; /* Granularity(secs) = mant * 10^exp */
1251+ __s8 gran_exponent;
1252+ __u8 __padding[5];
1253+};
1254+
1255+/*
1256+ * Information struct for fsinfo(FSINFO_ATTR_TIMESTAMP_INFO).
1257+ */
1258+struct fsinfo_timestamp_info {
1259+ struct fsinfo_timestamp_one atime; /* Access time */
1260+ struct fsinfo_timestamp_one mtime; /* Modification time */
1261+ struct fsinfo_timestamp_one ctime; /* Change time */
1262+ struct fsinfo_timestamp_one btime; /* Birth/creation time */
1263+};
1264+
1265+#define FSINFO_ATTR_TIMESTAMP_INFO__STRUCT struct fsinfo_timestamp_info
1266+
1267+/*
1268+ * Information struct for fsinfo(FSINFO_ATTR_VOLUME_UUID).
1269+ */
1270+struct fsinfo_volume_uuid {
1271+ __u8 uuid[16];
1272+};
1273+
1274+#define FSINFO_ATTR_VOLUME_UUID__STRUCT struct fsinfo_volume_uuid
1275+
1276+#endif /* _UAPI_LINUX_FSINFO_H */
1277diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
Lorenz Brun5999e922021-01-27 18:53:54 +01001278index f27ac94d5fa72..3cf167e82606f 100644
Lorenz Brun1d801752020-04-02 09:24:51 +02001279--- a/kernel/sys_ni.c
1280+++ b/kernel/sys_ni.c
1281@@ -51,6 +51,7 @@ COND_SYSCALL_COMPAT(io_pgetevents);
1282 COND_SYSCALL(io_uring_setup);
1283 COND_SYSCALL(io_uring_enter);
1284 COND_SYSCALL(io_uring_register);
1285+COND_SYSCALL(fsinfo);
1286
1287 /* fs/xattr.c */
1288
1289diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile
Lorenz Brun5999e922021-01-27 18:53:54 +01001290index 6377a678134ac..8efb67c45ceaa 100644
Lorenz Brun1d801752020-04-02 09:24:51 +02001291--- a/samples/vfs/Makefile
1292+++ b/samples/vfs/Makefile
Lorenz Brun5999e922021-01-27 18:53:54 +01001293@@ -1,4 +1,4 @@
Lorenz Brun1d801752020-04-02 09:24:51 +02001294 # SPDX-License-Identifier: GPL-2.0-only
Lorenz Brun5999e922021-01-27 18:53:54 +01001295-userprogs-always-y += test-fsmount test-statx
1296+userprogs-always-y += test-fsinfo test-fsmount test-statx
Lorenz Brun1d801752020-04-02 09:24:51 +02001297
Lorenz Brun5999e922021-01-27 18:53:54 +01001298 userccflags += -I usr/include
Lorenz Brun1d801752020-04-02 09:24:51 +02001299diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
1300new file mode 100644
Lorenz Brun5999e922021-01-27 18:53:54 +01001301index 0000000000000..934b25399ffed
Lorenz Brun1d801752020-04-02 09:24:51 +02001302--- /dev/null
1303+++ b/samples/vfs/test-fsinfo.c
Lorenz Brun5999e922021-01-27 18:53:54 +01001304@@ -0,0 +1,646 @@
Lorenz Brun1d801752020-04-02 09:24:51 +02001305+// SPDX-License-Identifier: GPL-2.0-or-later
1306+/* Test the fsinfo() system call
1307+ *
1308+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
1309+ * Written by David Howells (dhowells@redhat.com)
1310+ */
1311+
1312+#define _GNU_SOURCE
1313+#define _ATFILE_SOURCE
1314+#include <stdbool.h>
1315+#include <stdio.h>
1316+#include <stdlib.h>
1317+#include <stdint.h>
1318+#include <string.h>
1319+#include <unistd.h>
1320+#include <ctype.h>
1321+#include <errno.h>
1322+#include <time.h>
1323+#include <math.h>
1324+#include <fcntl.h>
1325+#include <sys/syscall.h>
1326+#include <linux/fsinfo.h>
1327+#include <linux/socket.h>
1328+#include <sys/stat.h>
1329+#include <arpa/inet.h>
1330+
1331+#ifndef __NR_fsinfo
1332+#define __NR_fsinfo -1
1333+#endif
1334+
1335+static bool debug = 0;
1336+static bool list_last;
1337+
1338+static __attribute__((unused))
1339+ssize_t fsinfo(int dfd, const char *filename,
1340+ struct fsinfo_params *params, size_t params_size,
1341+ void *result_buffer, size_t result_buf_size)
1342+{
1343+ return syscall(__NR_fsinfo, dfd, filename,
1344+ params, params_size,
1345+ result_buffer, result_buf_size);
1346+}
1347+
1348+struct fsinfo_attribute {
1349+ unsigned int attr_id;
1350+ enum fsinfo_value_type type;
1351+ unsigned int size;
1352+ const char *name;
1353+ void (*dump)(void *reply, unsigned int size);
1354+};
1355+
1356+static const struct fsinfo_attribute fsinfo_attributes[];
1357+
1358+static ssize_t get_fsinfo(const char *, const char *, struct fsinfo_params *, void **);
1359+
Lorenz Brun5999e922021-01-27 18:53:54 +01001360+static void dump_hex(FILE *f, unsigned char *data, int from, int to)
Lorenz Brun1d801752020-04-02 09:24:51 +02001361+{
Lorenz Brun5999e922021-01-27 18:53:54 +01001362+ unsigned offset, col = 0;
1363+ bool print_offset = true;
Lorenz Brun1d801752020-04-02 09:24:51 +02001364+
1365+ for (offset = from; offset < to; offset++) {
1366+ if (print_offset) {
Lorenz Brun5999e922021-01-27 18:53:54 +01001367+ fprintf(f, "%04x: ", offset);
Lorenz Brun1d801752020-04-02 09:24:51 +02001368+ print_offset = 0;
1369+ }
Lorenz Brun5999e922021-01-27 18:53:54 +01001370+ fprintf(f, "%02x", data[offset]);
Lorenz Brun1d801752020-04-02 09:24:51 +02001371+ col++;
1372+ if ((col & 3) == 0) {
Lorenz Brun5999e922021-01-27 18:53:54 +01001373+ if ((col & 15) == 0) {
1374+ fprintf(f, "\n");
1375+ print_offset = 1;
1376+ } else {
1377+ fprintf(f, " ");
1378+ }
Lorenz Brun1d801752020-04-02 09:24:51 +02001379+ }
1380+ }
1381+
1382+ if (!print_offset)
Lorenz Brun5999e922021-01-27 18:53:54 +01001383+ fprintf(f, "\n");
Lorenz Brun1d801752020-04-02 09:24:51 +02001384+}
1385+
1386+static void dump_attribute_info(void *reply, unsigned int size)
1387+{
1388+ struct fsinfo_attribute_info *attr_info = reply;
1389+ const struct fsinfo_attribute *attr;
1390+ char type[32], val_size[32];
1391+
1392+ switch (attr_info->type) {
1393+ case FSINFO_TYPE_VSTRUCT: strcpy(type, "V-STRUCT"); break;
1394+ case FSINFO_TYPE_STRING: strcpy(type, "STRING"); break;
1395+ case FSINFO_TYPE_OPAQUE: strcpy(type, "OPAQUE"); break;
1396+ case FSINFO_TYPE_LIST: strcpy(type, "LIST"); break;
1397+ default:
1398+ sprintf(type, "type-%x", attr_info->type);
1399+ break;
1400+ }
1401+
1402+ if (attr_info->flags & FSINFO_FLAGS_N)
1403+ strcat(type, " x N");
1404+ else if (attr_info->flags & FSINFO_FLAGS_NM)
1405+ strcat(type, " x NM");
1406+
1407+ for (attr = fsinfo_attributes; attr->name; attr++)
1408+ if (attr->attr_id == attr_info->attr_id)
1409+ break;
1410+
1411+ if (attr_info->size)
1412+ sprintf(val_size, "%u", attr_info->size);
1413+ else
1414+ strcpy(val_size, "-");
1415+
1416+ printf("%8x %-12s %08x %5s %s\n",
1417+ attr_info->attr_id,
1418+ type,
1419+ attr_info->flags,
1420+ val_size,
1421+ attr->name ? attr->name : "");
1422+}
1423+
1424+static void dump_fsinfo_generic_statfs(void *reply, unsigned int size)
1425+{
1426+ struct fsinfo_statfs *f = reply;
1427+
1428+ printf("\n");
1429+ printf("\tblocks : n=%llu fr=%llu av=%llu\n",
1430+ (unsigned long long)f->f_blocks.lo,
1431+ (unsigned long long)f->f_bfree.lo,
1432+ (unsigned long long)f->f_bavail.lo);
1433+
1434+ printf("\tfiles : n=%llu fr=%llu av=%llu\n",
1435+ (unsigned long long)f->f_files.lo,
1436+ (unsigned long long)f->f_ffree.lo,
1437+ (unsigned long long)f->f_favail.lo);
Lorenz Brun5999e922021-01-27 18:53:54 +01001438+ printf("\tbsize : %llu\n",
1439+ (unsigned long long)f->f_bsize);
1440+ printf("\tfrsize : %llu\n",
1441+ (unsigned long long)f->f_frsize);
Lorenz Brun1d801752020-04-02 09:24:51 +02001442+}
1443+
1444+static void dump_fsinfo_generic_ids(void *reply, unsigned int size)
1445+{
1446+ struct fsinfo_ids *f = reply;
1447+
1448+ printf("\n");
1449+ printf("\tdev : %02x:%02x\n", f->f_dev_major, f->f_dev_minor);
1450+ printf("\tfs : type=%x name=%s\n", f->f_fstype, f->f_fs_name);
1451+ printf("\tfsid : %llx\n", (unsigned long long)f->f_fsid);
1452+ printf("\tsbid : %llx\n", (unsigned long long)f->f_sb_id);
1453+}
1454+
1455+static void dump_fsinfo_generic_limits(void *reply, unsigned int size)
1456+{
1457+ struct fsinfo_limits *f = reply;
1458+
1459+ printf("\n");
1460+ printf("\tmax file size: %llx%016llx\n",
1461+ (unsigned long long)f->max_file_size.hi,
1462+ (unsigned long long)f->max_file_size.lo);
1463+ printf("\tmax ino : %llx%016llx\n",
1464+ (unsigned long long)f->max_ino.hi,
1465+ (unsigned long long)f->max_ino.lo);
1466+ printf("\tmax ids : u=%llx g=%llx p=%llx\n",
1467+ (unsigned long long)f->max_uid,
1468+ (unsigned long long)f->max_gid,
1469+ (unsigned long long)f->max_projid);
1470+ printf("\tmax dev : maj=%x min=%x\n",
1471+ f->max_dev_major, f->max_dev_minor);
1472+ printf("\tmax links : %llx\n",
1473+ (unsigned long long)f->max_hard_links);
1474+ printf("\tmax xattr : n=%x b=%llx\n",
1475+ f->max_xattr_name_len,
1476+ (unsigned long long)f->max_xattr_body_len);
1477+ printf("\tmax len : file=%x sym=%x\n",
1478+ f->max_filename_len, f->max_symlink_len);
1479+}
1480+
1481+static void dump_fsinfo_generic_supports(void *reply, unsigned int size)
1482+{
1483+ struct fsinfo_supports *f = reply;
1484+
1485+ printf("\n");
1486+ printf("\tstx_attr : %llx\n", (unsigned long long)f->stx_attributes);
1487+ printf("\tstx_mask : %x\n", f->stx_mask);
1488+ printf("\tfs_ioc_*flags: get=%x set=%x clr=%x\n",
1489+ f->fs_ioc_getflags, f->fs_ioc_setflags_set, f->fs_ioc_setflags_clear);
Lorenz Brun5999e922021-01-27 18:53:54 +01001490+ printf("\tfs_ioc_*xattr: fsx_xflags: get=%x set=%x clr=%x\n",
1491+ f->fs_ioc_fsgetxattr_xflags,
1492+ f->fs_ioc_fssetxattr_xflags_set,
1493+ f->fs_ioc_fssetxattr_xflags_clear);
Lorenz Brun1d801752020-04-02 09:24:51 +02001494+ printf("\twin_fattrs : %x\n", f->win_file_attrs);
1495+}
1496+
1497+static void print_time(struct fsinfo_timestamp_one *t, char stamp)
1498+{
Lorenz Brun5999e922021-01-27 18:53:54 +01001499+ printf("\t%ctime : gran=%uE%d range=%llx-%llx\n",
Lorenz Brun1d801752020-04-02 09:24:51 +02001500+ stamp,
Lorenz Brun5999e922021-01-27 18:53:54 +01001501+ t->gran_mantissa, t->gran_exponent,
1502+ (long long)t->minimum, (long long)t->maximum);
Lorenz Brun1d801752020-04-02 09:24:51 +02001503+}
1504+
1505+static void dump_fsinfo_generic_timestamp_info(void *reply, unsigned int size)
1506+{
1507+ struct fsinfo_timestamp_info *f = reply;
1508+
1509+ printf("\n");
1510+ print_time(&f->atime, 'a');
1511+ print_time(&f->mtime, 'm');
1512+ print_time(&f->ctime, 'c');
1513+ print_time(&f->btime, 'b');
1514+}
1515+
1516+static void dump_fsinfo_generic_volume_uuid(void *reply, unsigned int size)
1517+{
1518+ struct fsinfo_volume_uuid *f = reply;
1519+
1520+ printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x"
1521+ "-%02x%02x%02x%02x%02x%02x\n",
1522+ f->uuid[ 0], f->uuid[ 1],
1523+ f->uuid[ 2], f->uuid[ 3],
1524+ f->uuid[ 4], f->uuid[ 5],
1525+ f->uuid[ 6], f->uuid[ 7],
1526+ f->uuid[ 8], f->uuid[ 9],
1527+ f->uuid[10], f->uuid[11],
1528+ f->uuid[12], f->uuid[13],
1529+ f->uuid[14], f->uuid[15]);
1530+}
1531+
1532+static void dump_string(void *reply, unsigned int size)
1533+{
1534+ char *s = reply, *p;
1535+ bool nl = false, last_nl = false;
1536+
1537+ p = s;
1538+ if (size >= 4096) {
1539+ size = 4096;
1540+ p[4092] = '.';
1541+ p[4093] = '.';
1542+ p[4094] = '.';
1543+ p[4095] = 0;
1544+ } else {
1545+ p[size] = 0;
1546+ }
1547+
1548+ for (p = s; *p; p++) {
1549+ if (*p == '\n') {
1550+ last_nl = nl = true;
1551+ continue;
1552+ }
1553+ last_nl = false;
1554+ if (!isprint(*p) && *p != '\t')
1555+ *p = '?';
1556+ }
1557+
1558+ if (nl)
1559+ putchar('\n');
1560+ printf("%s", s);
1561+ if (!last_nl)
1562+ putchar('\n');
1563+}
1564+
1565+#define dump_fsinfo_meta_attribute_info (void *)0x123
1566+#define dump_fsinfo_meta_attributes (void *)0x123
1567+
1568+/*
1569+ *
1570+ */
1571+#define __FSINFO(A, T, S, G, F, N) \
1572+ { .attr_id = A, .type = T, .size = S, .name = N, .dump = dump_##G }
1573+
1574+#define _FSINFO(A,T,S,G,N) __FSINFO(A, T, S, G, 0, N)
1575+#define _FSINFO_N(A,T,S,G,N) __FSINFO(A, T, S, G, FSINFO_FLAGS_N, N)
1576+#define _FSINFO_NM(A,T,S,G,N) __FSINFO(A, T, S, G, FSINFO_FLAGS_NM, N)
1577+
1578+#define _FSINFO_VSTRUCT(A,S,G,N) _FSINFO (A, FSINFO_TYPE_VSTRUCT, sizeof(S), G, N)
1579+#define _FSINFO_VSTRUCT_N(A,S,G,N) _FSINFO_N (A, FSINFO_TYPE_VSTRUCT, sizeof(S), G, N)
1580+#define _FSINFO_VSTRUCT_NM(A,S,G,N) _FSINFO_NM(A, FSINFO_TYPE_VSTRUCT, sizeof(S), G, N)
1581+
1582+#define FSINFO_VSTRUCT(A,G) _FSINFO_VSTRUCT (A, A##__STRUCT, G, #A)
1583+#define FSINFO_VSTRUCT_N(A,G) _FSINFO_VSTRUCT_N (A, A##__STRUCT, G, #A)
1584+#define FSINFO_VSTRUCT_NM(A,G) _FSINFO_VSTRUCT_NM(A, A##__STRUCT, G, #A)
1585+#define FSINFO_STRING(A,G) _FSINFO (A, FSINFO_TYPE_STRING, 0, G, #A)
1586+#define FSINFO_STRING_N(A,G) _FSINFO_N (A, FSINFO_TYPE_STRING, 0, G, #A)
1587+#define FSINFO_STRING_NM(A,G) _FSINFO_NM(A, FSINFO_TYPE_STRING, 0, G, #A)
1588+#define FSINFO_OPAQUE(A,G) _FSINFO (A, FSINFO_TYPE_OPAQUE, 0, G, #A)
1589+#define FSINFO_LIST(A,G) _FSINFO (A, FSINFO_TYPE_LIST, sizeof(A##__STRUCT), G, #A)
1590+#define FSINFO_LIST_N(A,G) _FSINFO_N (A, FSINFO_TYPE_LIST, sizeof(A##__STRUCT), G, #A)
1591+
1592+static const struct fsinfo_attribute fsinfo_attributes[] = {
1593+ FSINFO_VSTRUCT (FSINFO_ATTR_STATFS, fsinfo_generic_statfs),
1594+ FSINFO_VSTRUCT (FSINFO_ATTR_IDS, fsinfo_generic_ids),
1595+ FSINFO_VSTRUCT (FSINFO_ATTR_LIMITS, fsinfo_generic_limits),
1596+ FSINFO_VSTRUCT (FSINFO_ATTR_SUPPORTS, fsinfo_generic_supports),
1597+ FSINFO_VSTRUCT (FSINFO_ATTR_TIMESTAMP_INFO, fsinfo_generic_timestamp_info),
1598+ FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, string),
1599+ FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
1600+ FSINFO_STRING (FSINFO_ATTR_VOLUME_NAME, string),
1601+ FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, fsinfo_meta_attribute_info),
1602+ FSINFO_LIST (FSINFO_ATTR_FSINFO_ATTRIBUTES, fsinfo_meta_attributes),
1603+ {}
1604+};
1605+
Lorenz Brun5999e922021-01-27 18:53:54 +01001606+static __attribute__((noreturn))
1607+void bad_value(const char *what,
1608+ struct fsinfo_params *params,
1609+ const struct fsinfo_attribute *attr,
1610+ const struct fsinfo_attribute_info *attr_info,
1611+ void *reply, unsigned int size)
1612+{
1613+ printf("\n");
1614+ fprintf(stderr, "%s %s{%u}{%u} t=%x f=%x s=%x\n",
1615+ what, attr->name, params->Nth, params->Mth,
1616+ attr_info->type, attr_info->flags, attr_info->size);
1617+ fprintf(stderr, "size=%u\n", size);
1618+ dump_hex(stderr, reply, 0, size);
1619+ exit(1);
1620+}
1621+
Lorenz Brun1d801752020-04-02 09:24:51 +02001622+static void dump_value(unsigned int attr_id,
1623+ const struct fsinfo_attribute *attr,
1624+ const struct fsinfo_attribute_info *attr_info,
1625+ void *reply, unsigned int size)
1626+{
1627+ if (!attr || !attr->dump) {
1628+ printf("<no dumper>\n");
1629+ return;
1630+ }
1631+
1632+ if (attr->type == FSINFO_TYPE_VSTRUCT && size < attr->size) {
1633+ printf("<short data %u/%u>\n", size, attr->size);
1634+ return;
1635+ }
1636+
1637+ attr->dump(reply, size);
1638+}
1639+
1640+static void dump_list(unsigned int attr_id,
1641+ const struct fsinfo_attribute *attr,
1642+ const struct fsinfo_attribute_info *attr_info,
1643+ void *reply, unsigned int size)
1644+{
1645+ size_t elem_size = attr_info->size;
1646+ unsigned int ix = 0;
1647+
1648+ printf("\n");
1649+ if (!attr || !attr->dump) {
1650+ printf("<no dumper>\n");
1651+ return;
1652+ }
1653+
1654+ if (attr->type == FSINFO_TYPE_VSTRUCT && size < attr->size) {
1655+ printf("<short data %u/%u>\n", size, attr->size);
1656+ return;
1657+ }
1658+
1659+ list_last = false;
1660+ while (size >= elem_size) {
1661+ printf("\t[%02x] ", ix);
1662+ if (size == elem_size)
1663+ list_last = true;
1664+ attr->dump(reply, size);
1665+ reply += elem_size;
1666+ size -= elem_size;
1667+ ix++;
1668+ }
1669+}
1670+
1671+/*
1672+ * Call fsinfo, expanding the buffer as necessary.
1673+ */
1674+static ssize_t get_fsinfo(const char *file, const char *name,
1675+ struct fsinfo_params *params, void **_r)
1676+{
1677+ ssize_t ret;
1678+ size_t buf_size = 4096;
1679+ void *r;
1680+
1681+ for (;;) {
1682+ r = malloc(buf_size);
1683+ if (!r) {
1684+ perror("malloc");
1685+ exit(1);
1686+ }
1687+ memset(r, 0xbd, buf_size);
1688+
1689+ errno = 0;
1690+ ret = fsinfo(AT_FDCWD, file, params, sizeof(*params), r, buf_size - 1);
1691+ if (ret == -1)
1692+ goto error;
1693+
1694+ if (ret <= buf_size - 1)
1695+ break;
1696+ buf_size = (ret + 4096 - 1) & ~(4096 - 1);
1697+ }
1698+
1699+ if (debug)
1700+ printf("fsinfo(%s,%s,%u,%u) = %zd\n",
1701+ file, name, params->Nth, params->Mth, ret);
1702+
1703+ ((char *)r)[ret] = 0;
1704+ *_r = r;
1705+ return ret;
1706+
1707+error:
1708+ *_r = NULL;
1709+ free(r);
1710+ if (debug)
1711+ printf("fsinfo(%s,%s,%u,%u) = %m\n",
1712+ file, name, params->Nth, params->Mth);
1713+ return ret;
1714+}
1715+
1716+/*
1717+ * Try one subinstance of an attribute.
1718+ */
1719+static int try_one(const char *file, struct fsinfo_params *params,
1720+ const struct fsinfo_attribute_info *attr_info, bool raw)
1721+{
1722+ const struct fsinfo_attribute *attr;
1723+ const char *name;
1724+ size_t size = 4096;
1725+ char namebuf[32];
1726+ void *r;
1727+
1728+ for (attr = fsinfo_attributes; attr->name; attr++) {
1729+ if (attr->attr_id == params->request) {
1730+ name = attr->name;
1731+ if (strncmp(name, "fsinfo_generic_", 15) == 0)
1732+ name += 15;
1733+ goto found;
1734+ }
1735+ }
1736+
1737+ sprintf(namebuf, "<unknown-%x>", params->request);
1738+ name = namebuf;
1739+ attr = NULL;
1740+
1741+found:
1742+ size = get_fsinfo(file, name, params, &r);
1743+
1744+ if (size == -1) {
1745+ if (errno == ENODATA) {
1746+ if (!(attr_info->flags & (FSINFO_FLAGS_N | FSINFO_FLAGS_NM)) &&
Lorenz Brun5999e922021-01-27 18:53:54 +01001747+ params->Nth == 0 && params->Mth == 0)
1748+ bad_value("Unexpected ENODATA",
1749+ params, attr, attr_info, r, size);
Lorenz Brun1d801752020-04-02 09:24:51 +02001750+ free(r);
1751+ return (params->Mth == 0) ? 2 : 1;
1752+ }
1753+ if (errno == EOPNOTSUPP) {
Lorenz Brun5999e922021-01-27 18:53:54 +01001754+ if (params->Nth > 0 || params->Mth > 0)
1755+ bad_value("Should return ENODATA",
1756+ params, attr, attr_info, r, size);
Lorenz Brun1d801752020-04-02 09:24:51 +02001757+ //printf("\e[33m%s\e[m: <not supported>\n",
1758+ // fsinfo_attr_names[attr]);
1759+ free(r);
1760+ return 2;
1761+ }
1762+ perror(file);
1763+ exit(1);
1764+ }
1765+
1766+ if (raw) {
1767+ if (size > 4096)
1768+ size = 4096;
Lorenz Brun5999e922021-01-27 18:53:54 +01001769+ dump_hex(stdout, r, 0, size);
Lorenz Brun1d801752020-04-02 09:24:51 +02001770+ free(r);
1771+ return 0;
1772+ }
1773+
1774+ switch (attr_info->flags & (FSINFO_FLAGS_N | FSINFO_FLAGS_NM)) {
1775+ case 0:
1776+ printf("\e[33m%s\e[m: ", name);
1777+ break;
1778+ case FSINFO_FLAGS_N:
1779+ printf("\e[33m%s{%u}\e[m: ", name, params->Nth);
1780+ break;
1781+ case FSINFO_FLAGS_NM:
1782+ printf("\e[33m%s{%u,%u}\e[m: ", name, params->Nth, params->Mth);
1783+ break;
1784+ }
1785+
1786+ switch (attr_info->type) {
Lorenz Brun1d801752020-04-02 09:24:51 +02001787+ case FSINFO_TYPE_STRING:
Lorenz Brun5999e922021-01-27 18:53:54 +01001788+ if (size == 0 || ((char *)r)[size - 1] != 0)
1789+ bad_value("Unterminated string",
1790+ params, attr, attr_info, r, size);
1791+ case FSINFO_TYPE_VSTRUCT:
1792+ case FSINFO_TYPE_OPAQUE:
Lorenz Brun1d801752020-04-02 09:24:51 +02001793+ dump_value(params->request, attr, attr_info, r, size);
1794+ free(r);
1795+ return 0;
1796+
1797+ case FSINFO_TYPE_LIST:
1798+ dump_list(params->request, attr, attr_info, r, size);
1799+ free(r);
1800+ return 0;
1801+
Lorenz Brun1d801752020-04-02 09:24:51 +02001802+ default:
Lorenz Brun5999e922021-01-27 18:53:54 +01001803+ bad_value("Fishy type", params, attr, attr_info, r, size);
Lorenz Brun1d801752020-04-02 09:24:51 +02001804+ }
1805+}
1806+
1807+static int cmp_u32(const void *a, const void *b)
1808+{
1809+ return *(const int *)a - *(const int *)b;
1810+}
1811+
1812+/*
1813+ *
1814+ */
1815+int main(int argc, char **argv)
1816+{
1817+ struct fsinfo_attribute_info attr_info;
1818+ struct fsinfo_params params = {
1819+ .at_flags = AT_SYMLINK_NOFOLLOW,
1820+ .flags = FSINFO_FLAGS_QUERY_PATH,
1821+ };
1822+ unsigned int *attrs, ret, nr, i;
1823+ bool meta = false;
1824+ int raw = 0, opt, Nth, Mth;
1825+
1826+ while ((opt = getopt(argc, argv, "Madlr"))) {
1827+ switch (opt) {
1828+ case 'M':
1829+ meta = true;
1830+ continue;
1831+ case 'a':
1832+ params.at_flags |= AT_NO_AUTOMOUNT;
1833+ params.flags = FSINFO_FLAGS_QUERY_PATH;
1834+ continue;
1835+ case 'd':
1836+ debug = true;
1837+ continue;
1838+ case 'l':
1839+ params.at_flags &= ~AT_SYMLINK_NOFOLLOW;
1840+ params.flags = FSINFO_FLAGS_QUERY_PATH;
1841+ continue;
1842+ case 'r':
1843+ raw = 1;
1844+ continue;
1845+ }
1846+ break;
1847+ }
1848+
1849+ argc -= optind;
1850+ argv += optind;
1851+
1852+ if (argc != 1) {
1853+ printf("Format: test-fsinfo [-Madlr] <path>\n");
1854+ exit(2);
1855+ }
1856+
1857+ /* Retrieve a list of supported attribute IDs */
1858+ params.request = FSINFO_ATTR_FSINFO_ATTRIBUTES;
1859+ params.Nth = 0;
1860+ params.Mth = 0;
1861+ ret = get_fsinfo(argv[0], "attributes", &params, (void **)&attrs);
1862+ if (ret == -1) {
1863+ fprintf(stderr, "Unable to get attribute list: %m\n");
1864+ exit(1);
1865+ }
1866+
1867+ if (ret % sizeof(attrs[0])) {
1868+ fprintf(stderr, "Bad length of attribute list (0x%x)\n", ret);
1869+ exit(2);
1870+ }
1871+
1872+ nr = ret / sizeof(attrs[0]);
1873+ qsort(attrs, nr, sizeof(attrs[0]), cmp_u32);
1874+
1875+ if (meta) {
1876+ printf("ATTR ID TYPE FLAGS SIZE NAME\n");
1877+ printf("======== ============ ======== ===== =========\n");
1878+ for (i = 0; i < nr; i++) {
1879+ params.request = FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO;
1880+ params.Nth = attrs[i];
1881+ params.Mth = 0;
1882+ ret = fsinfo(AT_FDCWD, argv[0],
1883+ &params, sizeof(params),
1884+ &attr_info, sizeof(attr_info));
1885+ if (ret == -1) {
1886+ fprintf(stderr, "Can't get info for attribute %x: %m\n", attrs[i]);
1887+ exit(1);
1888+ }
1889+
1890+ dump_attribute_info(&attr_info, ret);
1891+ }
1892+ exit(0);
1893+ }
1894+
1895+ for (i = 0; i < nr; i++) {
1896+ params.request = FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO;
1897+ params.Nth = attrs[i];
1898+ params.Mth = 0;
1899+ ret = fsinfo(AT_FDCWD, argv[0],
1900+ &params, sizeof(params),
1901+ &attr_info, sizeof(attr_info));
1902+ if (ret == -1) {
1903+ fprintf(stderr, "Can't get info for attribute %x: %m\n", attrs[i]);
1904+ exit(1);
1905+ }
1906+
1907+ if (attrs[i] == FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO ||
1908+ attrs[i] == FSINFO_ATTR_FSINFO_ATTRIBUTES)
1909+ continue;
1910+
1911+ if (attrs[i] != attr_info.attr_id) {
1912+ fprintf(stderr, "ID for %03x returned %03x\n",
1913+ attrs[i], attr_info.attr_id);
1914+ break;
1915+ }
1916+ Nth = 0;
1917+ do {
1918+ Mth = 0;
1919+ do {
1920+ params.request = attrs[i];
1921+ params.Nth = Nth;
1922+ params.Mth = Mth;
1923+
1924+ switch (try_one(argv[0], &params, &attr_info, raw)) {
1925+ case 0:
1926+ continue;
1927+ case 1:
1928+ goto done_M;
1929+ case 2:
1930+ goto done_N;
1931+ }
1932+ } while (++Mth < 100);
1933+
1934+ done_M:
1935+ if (Mth >= 100) {
1936+ fprintf(stderr, "Fishy: Mth %x[%u][%u]\n", attrs[i], Nth, Mth);
1937+ break;
1938+ }
1939+
1940+ } while (++Nth < 100);
1941+
1942+ done_N:
1943+ if (Nth >= 100) {
1944+ fprintf(stderr, "Fishy: Nth %x[%u]\n", attrs[i], Nth);
1945+ break;
1946+ }
1947+ }
1948+
1949+ return 0;
1950+}
1951--
Lorenz Brun5999e922021-01-27 18:53:54 +010019522.25.1
Lorenz Brun1d801752020-04-02 09:24:51 +02001953