* [PATCH 0/2] nolibc: Add sendfile()
@ 2026-08-28 10:32 Daniel Palmer
2026-08-28 10:32 ` [PATCH 1/2] tools/nolibc: " Daniel Palmer
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Daniel Palmer @ 2026-08-28 10:32 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
This is another thing from my backlog[0] and seemed simple enough so
sending now. Also, this feels right for nolibc as it lets you move lots
of data around without needing to allocate memory or even transfer it
into userspace. I've used it to implement cp and dd in my 64KB userspace
for linux on the sega megadrive.
There is some 32bit weirdness again, I think I caught that and added
some static asserts to make sure it doesn't silently break.
Maybe the test should actually test the offset?
0 - fallocate(), static pie, sockets,...
Daniel Palmer (2):
tools/nolibc: Add sendfile()
selftests/nolibc: Add basic test for sendfile()
tools/include/nolibc/Makefile | 1 +
tools/include/nolibc/nolibc.h | 1 +
tools/include/nolibc/sys/sendfile.h | 41 ++++++++++++
tools/testing/selftests/nolibc/nolibc-test.c | 70 ++++++++++++++++++++
4 files changed, 113 insertions(+)
create mode 100644 tools/include/nolibc/sys/sendfile.h
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] tools/nolibc: Add sendfile()
2026-08-28 10:32 [PATCH 0/2] nolibc: Add sendfile() Daniel Palmer
@ 2026-08-28 10:32 ` Daniel Palmer
2026-08-28 10:32 ` [PATCH 2/2] selftests/nolibc: Add basic test for sendfile() Daniel Palmer
2026-08-28 16:52 ` [PATCH 0/2] nolibc: Add sendfile() Thomas Weißschuh
2 siblings, 0 replies; 4+ messages in thread
From: Daniel Palmer @ 2026-08-28 10:32 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
This is useful because it allows copying data up to 2GB from one
fd to another without a buffer in userspace and with a single syscall.
For 32bit kernels we use the sendfile64 syscall to allow for 64bit
offsets to work.
Signed-off-by: Daniel Palmer <daniel@thingy.jp>
---
tools/include/nolibc/Makefile | 1 +
tools/include/nolibc/nolibc.h | 1 +
tools/include/nolibc/sys/sendfile.h | 41 +++++++++++++++++++++++++++++
3 files changed, 43 insertions(+)
create mode 100644 tools/include/nolibc/sys/sendfile.h
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile
index 6d213d372bf8..668883f13cd6 100644
--- a/tools/include/nolibc/Makefile
+++ b/tools/include/nolibc/Makefile
@@ -59,6 +59,7 @@ all_files := \
sys/reboot.h \
sys/resource.h \
sys/select.h \
+ sys/sendfile.h \
sys/stat.h \
sys/syscall.h \
sys/sysmacros.h \
diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h
index faa94f247281..352a207bbedc 100644
--- a/tools/include/nolibc/nolibc.h
+++ b/tools/include/nolibc/nolibc.h
@@ -106,6 +106,7 @@
#include "sys/reboot.h"
#include "sys/resource.h"
#include "sys/select.h"
+#include "sys/sendfile.h"
#include "sys/stat.h"
#include "sys/syscall.h"
#include "sys/sysmacros.h"
diff --git a/tools/include/nolibc/sys/sendfile.h b/tools/include/nolibc/sys/sendfile.h
new file mode 100644
index 000000000000..7136c0eee547
--- /dev/null
+++ b/tools/include/nolibc/sys/sendfile.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * sendfile for NOLIBC
+ * Copyright (C) 2026 Daniel Palmer <daniel@thingy.jp>
+ */
+
+/* make sure to include all global symbols */
+#include "../nolibc.h"
+
+#ifndef _NOLIBC_SYS_SENDFILE_H
+#define _NOLIBC_SYS_SENDFILE_H
+
+#include "../sys.h"
+#include <linux/unistd.h>
+
+/*
+ * ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
+ */
+
+static __attribute__((unused))
+ssize_t _sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
+{
+ __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_loff_t));
+
+#ifdef __NR_sendfile64
+ /* 32bit kernels use sendfile64 so offset is treated as a __kernel_loff_t */
+ return __nolibc_syscall4(__NR_sendfile64, out_fd, in_fd, offset, count);
+#else
+ __nolibc_static_assert(sizeof(*offset) == sizeof(__kernel_off_t));
+
+ return __nolibc_syscall4(__NR_sendfile, out_fd, in_fd, offset, count);
+#endif
+}
+
+static __attribute__((unused))
+ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
+{
+ return __sysret(_sys_sendfile(out_fd, in_fd, offset, count));
+}
+
+#endif /* _NOLIBC_SYS_SENDFILE_H */
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/2] selftests/nolibc: Add basic test for sendfile()
2026-08-28 10:32 [PATCH 0/2] nolibc: Add sendfile() Daniel Palmer
2026-08-28 10:32 ` [PATCH 1/2] tools/nolibc: " Daniel Palmer
@ 2026-08-28 10:32 ` Daniel Palmer
2026-08-28 16:52 ` [PATCH 0/2] nolibc: Add sendfile() Thomas Weißschuh
2 siblings, 0 replies; 4+ messages in thread
From: Daniel Palmer @ 2026-08-28 10:32 UTC (permalink / raw)
To: w, linux; +Cc: linux-kernel, Daniel Palmer
Basic smoke test for sendfile().
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 ed860b0a15a1..48e1183a00bb 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1563,6 +1563,75 @@ int test_large_file(void)
return ret;
}
+int test_sendfile(void)
+{
+ char data_in[] = "This is some data";
+ const size_t data_sz = sizeof(data_in);
+ char data_out[data_sz];
+ int in_fd, out_fd;
+ int ret = 0;
+
+ /* Create two tmp files */
+ in_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644);
+ if (in_fd == -1)
+ return __LINE__;
+
+ out_fd = open("/tmp", O_TMPFILE | O_RDWR, 0644);
+ if (out_fd == -1) {
+ ret = __LINE__;
+ goto close_in;
+ }
+
+ /* Populate the "in" file */
+ ret = write(in_fd, data_in, data_sz);
+ if (ret != data_sz) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Rewind the "in" file */
+ if (lseek(in_fd, 0, SEEK_SET)) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Use sendfile() to copy "in" to "out" */
+ ret = sendfile(out_fd, in_fd, NULL, data_sz);
+ if (ret != data_sz) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Rewind the "out" file */
+ if (lseek(out_fd, 0, SEEK_SET)) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Read back the transferred data */
+ ret = read(out_fd, data_out, data_sz);
+ if (ret != data_sz) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Check we have the same data in both files */
+ if (memcmp(data_out, data_in, data_sz)) {
+ ret = __LINE__;
+ goto close_out;
+ }
+
+ /* Test passed */
+ ret = 0;
+
+close_out:
+ close(out_fd);
+close_in:
+ close(in_fd);
+
+ return ret;
+}
+
/* Run syscall tests between IDs <min> and <max>.
* Return 0 on success, non-zero on failure.
*/
@@ -1684,6 +1753,7 @@ int run_syscall(int min, int max)
CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break;
CASE_TEST(select_stdout); EXPECT_SYSNE(1, ({ fd_set fds; FD_ZERO(&fds); FD_SET(1, &fds); select(2, NULL, &fds, NULL, NULL); }), -1); break;
CASE_TEST(select_fault); EXPECT_SYSER(1, select(1, (void *)1, NULL, NULL, 0), -1, EFAULT); break;
+ CASE_TEST(sendfile); EXPECT_SYSZR(1, test_sendfile()); break;
CASE_TEST(stat_blah); EXPECT_SYSER(1, stat("/proc/self/blah", &stat_buf), -1, ENOENT); break;
CASE_TEST(stat_fault); EXPECT_SYSER(1, stat(NULL, &stat_buf), -1, EFAULT); break;
CASE_TEST(stat_rdev); EXPECT_SYSZR(1, ({ int ret = stat("/dev/null", &stat_buf); ret ?: stat_buf.st_rdev != makedev(1, 3); })); break;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] nolibc: Add sendfile()
2026-08-28 10:32 [PATCH 0/2] nolibc: Add sendfile() Daniel Palmer
2026-08-28 10:32 ` [PATCH 1/2] tools/nolibc: " Daniel Palmer
2026-08-28 10:32 ` [PATCH 2/2] selftests/nolibc: Add basic test for sendfile() Daniel Palmer
@ 2026-08-28 16:52 ` Thomas Weißschuh
2 siblings, 0 replies; 4+ messages in thread
From: Thomas Weißschuh @ 2026-08-28 16:52 UTC (permalink / raw)
To: Daniel Palmer; +Cc: w, linux-kernel
Aug 28, 2026 12:32:21 Daniel Palmer <daniel@thingy.jp>:
> This is another thing from my backlog[0] and seemed simple enough so
> sending now. Also, this feels right for nolibc as it lets you move lots
> of data around without needing to allocate memory or even transfer it
> into userspace. I've used it to implement cp and dd in my 64KB userspace
> for linux on the sega megadrive.
>
> There is some 32bit weirdness again, I think I caught that and added
> some static asserts to make sure it doesn't silently break.
>
> Maybe the test should actually test the offset?
>
> 0 - fallocate(), static pie, sockets,...
>
> Daniel Palmer (2):
> tools/nolibc: Add sendfile()
> selftests/nolibc: Add basic test for sendfile()
Looks good, thanks!
I have applied the patches with some minor changes
and will push them out after -rc1.
> tools/include/nolibc/Makefile | 1 +
> tools/include/nolibc/nolibc.h | 1 +
> tools/include/nolibc/sys/sendfile.h | 41 ++++++++++++
> tools/testing/selftests/nolibc/nolibc-test.c | 70 ++++++++++++++++++++
> 4 files changed, 113 insertions(+)
> create mode 100644 tools/include/nolibc/sys/sendfile.h
>
> --
> 2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-28 16:53 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-28 10:32 [PATCH 0/2] nolibc: Add sendfile() Daniel Palmer
2026-08-28 10:32 ` [PATCH 1/2] tools/nolibc: " Daniel Palmer
2026-08-28 10:32 ` [PATCH 2/2] selftests/nolibc: Add basic test for sendfile() Daniel Palmer
2026-08-28 16:52 ` [PATCH 0/2] nolibc: Add sendfile() Thomas Weißschuh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®