* [PATCH v4 1/3] tools/nolibc: fcntl: Add fallocate()
2026-05-07 9:03 [PATCH v4 0/3] nolibc: Add fallocate() Daniel Palmer
@ 2026-05-07 9:03 ` Daniel Palmer
2026-05-07 9:03 ` [PATCH v4 2/3] tools/nolibc: Add statfs() Daniel Palmer
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Daniel Palmer @ 2026-05-07 9:03 UTC (permalink / raw)
To: w, linux; +Cc: david.laight.linux, linux-kernel, Daniel Palmer
Add fallocate().
Some special care is needed to put the offset and size
into the syscall parameters for 32bit machines, x32,
and mipsn32.
For x32 we can just check if the kernel long size is the
same as off_t and use the same path as x86_64.
For mipsn32 we override the generic version and provide
one that does the right thing.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/arch-mips.h | 11 +++++++++++
tools/include/nolibc/fcntl.h | 31 +++++++++++++++++++++++++++++++
tools/include/nolibc/sys.h | 8 ++++++++
3 files changed, 50 insertions(+)
diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h
index 1400653c76c1..e4e42f2bcaf4 100644
--- a/tools/include/nolibc/arch-mips.h
+++ b/tools/include/nolibc/arch-mips.h
@@ -6,6 +6,7 @@
#ifndef _NOLIBC_ARCH_MIPS_H
#define _NOLIBC_ARCH_MIPS_H
+#include <linux/unistd.h>
#include "compiler.h"
#include "crt.h"
@@ -256,6 +257,16 @@
_arg4 ? -_num : _num; \
})
+/* The generic version of this will split offset and size for _ABIN32,
+ * override it and do the right thing here.
+ */
+static __attribute__((unused))
+int _sys_fallocate(int fd, int mode, off_t offset, off_t size)
+{
+ return __nolibc_syscall4(__NR_fallocate, fd, mode, offset, size);
+}
+#define _sys_fallocate _sys_fallocate
+
#endif /* _ABIO32 */
#ifndef NOLIBC_NO_RUNTIME
diff --git a/tools/include/nolibc/fcntl.h b/tools/include/nolibc/fcntl.h
index 014910a8e928..cea5fec66aec 100644
--- a/tools/include/nolibc/fcntl.h
+++ b/tools/include/nolibc/fcntl.h
@@ -14,6 +14,9 @@
#include "types.h"
#include "sys.h"
+/* For fallocate() modes */
+#include <linux/falloc.h>
+
/*
* int openat(int dirfd, const char *path, int flags[, mode_t mode]);
*/
@@ -80,4 +83,32 @@ int creat(const char *path, mode_t mode)
return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode);
}
+/*
+ * int fallocate(int fd, int mode, off_t offset, off_t size);
+ */
+
+#if !defined(_sys_fallocate)
+static __attribute__((unused))
+int _sys_fallocate(int fd, int mode, off_t offset, off_t size)
+{
+ /*
+ * For 32 bit machines __kernel_long_t will be 4, off_t will be 8
+ * and we need to split offset and size, for 64 machines we can use
+ * the values as-is.
+ */
+ if (sizeof(__kernel_long_t) != 8)
+ return __nolibc_syscall6(__NR_fallocate, fd, mode,
+ __NOLIBC_LLARGPART(offset, 0), __NOLIBC_LLARGPART(offset, 1),
+ __NOLIBC_LLARGPART(size, 0), __NOLIBC_LLARGPART(size, 1));
+ else
+ return __nolibc_syscall4(__NR_fallocate, fd, mode, offset, size);
+}
+#endif
+
+static __attribute__((unused))
+int fallocate(int fd, int mode, off_t offset, off_t size)
+{
+ return __sysret(_sys_fallocate(fd, mode, offset, size));
+}
+
#endif /* _NOLIBC_FCNTL_H */
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 33f9c970ae57..b7136a3a7d6a 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -29,6 +29,14 @@
#include "stdarg.h"
#include "types.h"
+/*
+ * Helper for 32bit machines where a 64bit syscall arg needs to be split into
+ * two 32bit parts while making sure the order of the low/high parts are correct
+ * for the endian:
+ * __NOLIBC_LLARGPART(x, 0), __NOLIBC_LLARGPART(x, 1)
+ */
+#define __NOLIBC_LLARGPART(_arg, _part) \
+ (((union { long long ll; long l[2]; }) { .ll = _arg }).l[_part])
/* Syscall return helper: takes the syscall value in argument and checks for an
* error in it. This may only be used with signed returns (int or long), but
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v4 2/3] tools/nolibc: Add statfs()
2026-05-07 9:03 [PATCH v4 0/3] nolibc: Add fallocate() Daniel Palmer
2026-05-07 9:03 ` [PATCH v4 1/3] tools/nolibc: fcntl: " Daniel Palmer
@ 2026-05-07 9:03 ` Daniel Palmer
2026-05-07 9:03 ` [PATCH v4 3/3] selftests/nolibc: Add a very basic test for fallocate() Daniel Palmer
2026-05-11 6:29 ` [PATCH v4 0/3] nolibc: Add fallocate() Willy Tarreau
3 siblings, 0 replies; 5+ messages in thread
From: Daniel Palmer @ 2026-05-07 9:03 UTC (permalink / raw)
To: w, linux; +Cc: david.laight.linux, linux-kernel, Daniel Palmer
Add statfs().
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/Makefile | 1 +
tools/include/nolibc/nolibc.h | 1 +
tools/include/nolibc/sys/statfs.h | 50 +++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)
create mode 100644 tools/include/nolibc/sys/statfs.h
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile
index 00fd2e566d75..e6281f58e6e2 100644
--- a/tools/include/nolibc/Makefile
+++ b/tools/include/nolibc/Makefile
@@ -60,6 +60,7 @@ all_files := \
sys/resource.h \
sys/select.h \
sys/stat.h \
+ sys/statfs.h \
sys/syscall.h \
sys/sysmacros.h \
sys/time.h \
diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h
index faa94f247281..425cf87befdc 100644
--- a/tools/include/nolibc/nolibc.h
+++ b/tools/include/nolibc/nolibc.h
@@ -107,6 +107,7 @@
#include "sys/resource.h"
#include "sys/select.h"
#include "sys/stat.h"
+#include "sys/statfs.h"
#include "sys/syscall.h"
#include "sys/sysmacros.h"
#include "sys/time.h"
diff --git a/tools/include/nolibc/sys/statfs.h b/tools/include/nolibc/sys/statfs.h
new file mode 100644
index 000000000000..26976ac2e94a
--- /dev/null
+++ b/tools/include/nolibc/sys/statfs.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * statfs for NOLIBC
+ * Copyright (C) 2026 Daniel Palmer <daniel@thingy.jp>
+ */
+
+/* make sure to include all global symbols */
+#include "../nolibc.h"
+
+#ifndef _NOLIBC_SYS_STATFS_H
+#define _NOLIBC_SYS_STATFS_H
+
+#include "../sys.h"
+
+/* Some preprocessor hackery to get struct statfs to
+ * always be the 64bit one.
+ */
+#define statfs __nolibc_kernel_statfs
+#define statfs64 __nolibc_kernel_statfs64
+#include <asm/statfs.h>
+#undef statfs
+#undef statfs64
+
+#ifdef __NR_statfs64
+#define statfs __nolibc_kernel_statfs64
+#else
+#define statfs __nolibc_kernel_statfs
+#endif
+
+/*
+ * statfs(const char *path, struct statfs *buf);
+ */
+
+static __attribute__((unused))
+int _sys_statfs(const char *path, struct statfs *buf)
+{
+#ifdef __NR_statfs64
+ return __nolibc_syscall3(__NR_statfs64, path, sizeof(*buf), buf);
+#else
+ return __nolibc_syscall2(__NR_statfs, path, buf);
+#endif
+}
+
+static __attribute__((unused))
+int statfs(const char *path, struct statfs *buf)
+{
+ return __sysret(_sys_statfs(path, buf));
+}
+
+#endif /* _NOLIBC_SYS_STATFS_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v4 3/3] selftests/nolibc: Add a very basic test for fallocate()
2026-05-07 9:03 [PATCH v4 0/3] nolibc: Add fallocate() Daniel Palmer
2026-05-07 9:03 ` [PATCH v4 1/3] tools/nolibc: fcntl: " Daniel Palmer
2026-05-07 9:03 ` [PATCH v4 2/3] tools/nolibc: Add statfs() Daniel Palmer
@ 2026-05-07 9:03 ` Daniel Palmer
2026-05-11 6:29 ` [PATCH v4 0/3] nolibc: Add fallocate() Willy Tarreau
3 siblings, 0 replies; 5+ messages in thread
From: Daniel Palmer @ 2026-05-07 9:03 UTC (permalink / raw)
To: w, linux; +Cc: david.laight.linux, linux-kernel, Daniel Palmer
1: Create a tmp file, fallocate() to make it a bit bigger, check the
size is what was expected.
2: Try to fallocate() (1 << 20), this should work.
3: Try to fallocate() (1 << 52), this should cause ENOSPC or EFBIG.
2 and 3 are basically to make sure if the offset or size are split
into a pair of registers for the syscall that we are passing them
the correct way around.
This test requires /tmp to be a tmpfs and not ramfs or something
else so before running the tests check what /tmp is and if its
not tmpfs skip the test.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/testing/selftests/nolibc/nolibc-test.c | 70 ++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 1db6e8d55c16..2a3b855214bc 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -48,6 +48,9 @@
#include <endian.h>
#include <alloca.h>
+/* For TMPFS_MAGIC */
+#include <linux/magic.h>
+
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#include "nolibc-test-linkage.h"
@@ -903,6 +906,66 @@ int test_getpagesize(void)
return !c;
}
+int test_fallocate(void)
+{
+ struct stat st;
+ int fd, r;
+
+ /* Create a new tmp file */
+ fd = open("/tmp", O_TMPFILE | O_RDWR, 0644);
+ if (fd == -1)
+ return -1;
+
+ /* Expand it to 42 bytes */
+ r = fallocate(fd, 0, 0, 42);
+ if (r)
+ goto close_tmpfile;
+
+ /* Get the new stat */
+ r = fstat(fd, &st);
+ if (r)
+ goto close_tmpfile;
+
+ /* It should be 42 bytes long */
+ if (st.st_size != 42) {
+ r = -1;
+ goto close_tmpfile;
+ }
+
+ /* Now try to allocate 1MiB. This puts a single bit
+ * into one of the registers if the size is split into
+ * two registers. This shouldn't fail if the bit is in
+ * the correct register.
+ */
+ r = fallocate(fd, 0, 0, (1ll << 20));
+ if (r)
+ goto close_tmpfile;
+
+ /* Check a massive size that puts a single bit into
+ * the other register if splitting.
+ * This should return an error and errno = ENOSPC or
+ * EFBIG indicating the value was passed correctly but it
+ * was rejected.
+ */
+ r = fallocate(fd, 0, 0, (1ll << (20 + 32)));
+ if (r != -1) {
+ r = -1;
+ goto close_tmpfile;
+ }
+ if (errno != ENOSPC && errno != EFBIG) {
+ r = -1;
+ goto close_tmpfile;
+ }
+
+ /* Test passed */
+ r = 0;
+
+close_tmpfile:
+ close(fd);
+
+ return r;
+}
+
int test_file_stream(void)
{
FILE *f;
@@ -1455,6 +1518,8 @@ int run_syscall(int min, int max)
void *p1, *p2;
int has_gettid = 1;
int has_brk;
+ int tmp_is_tmpfs = 0;
+ struct statfs tmp_statfs_buf;
/* <proc> indicates whether or not /proc is mounted */
proc = stat("/proc", &stat_buf) == 0;
@@ -1470,6 +1535,10 @@ int run_syscall(int min, int max)
/* on musl setting brk()/sbrk() always fails */
has_brk = brk(0) == 0;
+ /* Check if /tmp is tmpfs */
+ if (statfs("/tmp", &tmp_statfs_buf) == 0 && tmp_statfs_buf.f_type == TMPFS_MAGIC)
+ tmp_is_tmpfs = 1;
+
for (test = min; test >= 0 && test <= max; test++) {
int llen = 0; /* line length */
@@ -1512,6 +1581,7 @@ int run_syscall(int min, int max)
CASE_TEST(dup3_0); tmp = dup3(0, 100, 0); EXPECT_SYSNE(1, tmp, -1); close(tmp); break;
CASE_TEST(dup3_m1); tmp = dup3(-1, 100, 0); EXPECT_SYSER(1, tmp, -1, EBADF); if (tmp != -1) close(tmp); break;
CASE_TEST(execve_root); EXPECT_SYSER(1, execve("/", (char*[]){ [0] = "/", [1] = NULL }, NULL), -1, EACCES); break;
+ CASE_TEST(fallocate); EXPECT_SYSZR(tmp_is_tmpfs, test_fallocate()); break;
CASE_TEST(fchdir_stdin); EXPECT_SYSER(1, fchdir(STDIN_FILENO), -1, ENOTDIR); break;
CASE_TEST(fchdir_badfd); EXPECT_SYSER(1, fchdir(-1), -1, EBADF); break;
CASE_TEST(file_stream); EXPECT_SYSZR(1, test_file_stream()); break;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v4 0/3] nolibc: Add fallocate()
2026-05-07 9:03 [PATCH v4 0/3] nolibc: Add fallocate() Daniel Palmer
` (2 preceding siblings ...)
2026-05-07 9:03 ` [PATCH v4 3/3] selftests/nolibc: Add a very basic test for fallocate() Daniel Palmer
@ 2026-05-11 6:29 ` Willy Tarreau
3 siblings, 0 replies; 5+ messages in thread
From: Willy Tarreau @ 2026-05-11 6:29 UTC (permalink / raw)
To: Daniel Palmer; +Cc: linux, david.laight.linux, linux-kernel
Hi Daniel,
On Thu, May 07, 2026 at 06:03:50PM +0900, Daniel Palmer wrote:
> While poking around with my "static PIE for nommu" series I found
> I needed fallocate(). Implementing it turned out a bit more
> interesting than I thought it would be due to how the offset and
> size need to be passed on 32bit machines.
>
> v4:
> - Added statfs()...
> - Reworked the test a bit to use statfs() to work out if /tmp
> is a tmpfs or not and skip the test if it isn't. This skips
> the test on sparc32 where CONFIG_TMPFS=n.
> - Adding the needed bits to the sparc32 config to allow tmpfs
> to be enabled allows it to run and pass the test. I will check
> the changes and send a patch.
> - Cleaned up the if() around deciding how to pass the parameters
> for fallocate() based on the comments from David and Thomas.
> - Test passed on all of the supported targets.
(...)
Thanks, the whole series looks good to me:
Acked-by: Willy Tarreau <w@1wt.eu>
Let's wait for Thomas who usually spots finer issues than me ;-)
Thanks,
Willy
^ permalink raw reply [flat|nested] 5+ messages in thread