From 0d2168f6c46712841dbf5710b8a9c3d0dd70de27 Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Fri, 20 Feb 2026 09:24:18 -0300 Subject: [PATCH 1/6] sys/compat/linux/arch/amd64/syscalls.master: Added sycall 40 (sendfile) wrapper --- sys/compat/linux/arch/amd64/syscalls.master | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/compat/linux/arch/amd64/syscalls.master b/sys/compat/linux/arch/amd64/syscalls.master index 2279eb36d8182..be492840c28a7 100644 --- a/sys/compat/linux/arch/amd64/syscalls.master +++ b/sys/compat/linux/arch/amd64/syscalls.master @@ -145,7 +145,8 @@ struct itimerval50 *itv, \ struct itimerval50 *oitv); } 39 STD { pid_t|sys||getpid(void); } -40 UNIMPL sendfile +40 STD { ssize_t|linux_sys||sendfile(int out_fd, int in_fd, \ + off_t *offset, size_t count); } 41 STD { int|linux_sys||socket(int domain, \ int type, int protocol); } 42 STD { int|linux_sys||connect(int s, \ From 085b14c104cb8c6a23071dbb9b6180fd7a51a7ff Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Fri, 20 Feb 2026 09:31:13 -0300 Subject: [PATCH 2/6] sys/compat/linux/arch/amd64/: Ran makesyscalls.sh to update syscalls files --- sys/compat/linux/arch/amd64/linux_syscall.h | 7 +++- .../linux/arch/amd64/linux_syscallargs.h | 14 ++++++- sys/compat/linux/arch/amd64/linux_syscalls.c | 10 ++--- sys/compat/linux/arch/amd64/linux_sysent.c | 12 +++--- .../linux/arch/amd64/linux_systrace_args.c | 38 ++++++++++++++++++- 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/sys/compat/linux/arch/amd64/linux_syscall.h b/sys/compat/linux/arch/amd64/linux_syscall.h index 215a2ff4bafeb..4b014c6fa5271 100644 --- a/sys/compat/linux/arch/amd64/linux_syscall.h +++ b/sys/compat/linux/arch/amd64/linux_syscall.h @@ -1,9 +1,9 @@ -/* $NetBSD: linux_syscall.h,v 1.87 2025/11/10 15:41:57 christos Exp $ */ +/* $NetBSD$ */ /* * System call numbers. * - * DO NOT EDIT-- this file is automatically generated. + * DO NOT EDIT-- this file is generated by makesyscalls.sh * created from NetBSD: syscalls.master,v 1.78 2025/11/10 15:41:38 christos Exp */ @@ -135,6 +135,9 @@ /* syscall: "getpid" ret: "pid_t" args: */ #define LINUX_SYS_getpid 39 +/* syscall: "sendfile" ret: "ssize_t" args: "int" "int" "off_t *" "size_t" */ +#define LINUX_SYS_sendfile 40 + /* syscall: "socket" ret: "int" args: "int" "int" "int" */ #define LINUX_SYS_socket 41 diff --git a/sys/compat/linux/arch/amd64/linux_syscallargs.h b/sys/compat/linux/arch/amd64/linux_syscallargs.h index 6f67a4e56ed9e..d56b391276448 100644 --- a/sys/compat/linux/arch/amd64/linux_syscallargs.h +++ b/sys/compat/linux/arch/amd64/linux_syscallargs.h @@ -1,9 +1,9 @@ -/* $NetBSD: linux_syscallargs.h,v 1.87 2025/11/10 15:41:57 christos Exp $ */ +/* $NetBSD$ */ /* * System call argument lists. * - * DO NOT EDIT-- this file is automatically generated. + * DO NOT EDIT-- this file is generated by makesyscalls.sh * created from NetBSD: syscalls.master,v 1.78 2025/11/10 15:41:38 christos Exp */ @@ -186,6 +186,14 @@ check_syscall_args(linux_sys_alarm) struct compat_50_sys_setitimer_args; +struct linux_sys_sendfile_args { + syscallarg(int) out_fd; + syscallarg(int) in_fd; + syscallarg(off_t *) offset; + syscallarg(size_t) count; +}; +check_syscall_args(linux_sys_sendfile) + struct linux_sys_socket_args { syscallarg(int) domain; syscallarg(int) type; @@ -1425,6 +1433,8 @@ int compat_50_sys_setitimer(struct lwp *, const struct compat_50_sys_setitimer_a int sys_getpid(struct lwp *, const void *, register_t *); +int linux_sys_sendfile(struct lwp *, const struct linux_sys_sendfile_args *, register_t *); + int linux_sys_socket(struct lwp *, const struct linux_sys_socket_args *, register_t *); int linux_sys_connect(struct lwp *, const struct linux_sys_connect_args *, register_t *); diff --git a/sys/compat/linux/arch/amd64/linux_syscalls.c b/sys/compat/linux/arch/amd64/linux_syscalls.c index 7b435991531a6..295ad8b2ec462 100644 --- a/sys/compat/linux/arch/amd64/linux_syscalls.c +++ b/sys/compat/linux/arch/amd64/linux_syscalls.c @@ -1,14 +1,14 @@ -/* $NetBSD: linux_syscalls.c,v 1.87 2025/11/10 15:41:57 christos Exp $ */ +/* $NetBSD$ */ /* * System call names. * - * DO NOT EDIT-- this file is automatically generated. + * DO NOT EDIT-- this file is generated by makesyscalls.sh * created from NetBSD: syscalls.master,v 1.78 2025/11/10 15:41:38 christos Exp */ #include -__KERNEL_RCSID(0, "$NetBSD: linux_syscalls.c,v 1.87 2025/11/10 15:41:57 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD$"); #if defined(_KERNEL_OPT) #if defined(_KERNEL_OPT) @@ -89,7 +89,7 @@ const char *const linux_syscallnames[] = { /* 37 */ "alarm", /* 38 */ "setitimer", /* 39 */ "getpid", - /* 40 */ "#40 (unimplemented sendfile)", + /* 40 */ "sendfile", /* 41 */ "socket", /* 42 */ "connect", /* 43 */ "oaccept", @@ -633,7 +633,7 @@ const char *const altlinux_syscallnames[] = { /* 37 */ NULL, /* alarm */ /* 38 */ NULL, /* setitimer */ /* 39 */ NULL, /* getpid */ - /* 40 */ NULL, /* unimplemented sendfile */ + /* 40 */ NULL, /* sendfile */ /* 41 */ NULL, /* socket */ /* 42 */ NULL, /* connect */ /* 43 */ "accept", diff --git a/sys/compat/linux/arch/amd64/linux_sysent.c b/sys/compat/linux/arch/amd64/linux_sysent.c index 8d91d859ebad6..5cc1b65a645a3 100644 --- a/sys/compat/linux/arch/amd64/linux_sysent.c +++ b/sys/compat/linux/arch/amd64/linux_sysent.c @@ -1,14 +1,14 @@ -/* $NetBSD: linux_sysent.c,v 1.87 2025/11/10 15:41:57 christos Exp $ */ +/* $NetBSD$ */ /* * System call switch table. * - * DO NOT EDIT-- this file is automatically generated. + * DO NOT EDIT-- this file is generated by makesyscalls.sh * created from NetBSD: syscalls.master,v 1.78 2025/11/10 15:41:38 christos Exp */ #include -__KERNEL_RCSID(0, "$NetBSD: linux_sysent.c,v 1.87 2025/11/10 15:41:57 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD$"); #if defined(_KERNEL_OPT) #include "opt_sysv.h" @@ -241,8 +241,10 @@ struct sysent linux_sysent[] = { .sy_call = (sy_call_t *)sys_getpid }, /* 39 = getpid */ { - .sy_call = linux_sys_nosys, - }, /* 40 = filler */ + ns(struct linux_sys_sendfile_args), + .sy_flags = SYCALL_ARG_PTR, + .sy_call = (sy_call_t *)linux_sys_sendfile + }, /* 40 = sendfile */ { ns(struct linux_sys_socket_args), .sy_call = (sy_call_t *)linux_sys_socket diff --git a/sys/compat/linux/arch/amd64/linux_systrace_args.c b/sys/compat/linux/arch/amd64/linux_systrace_args.c index 4effe6f8f11fa..9ab95cc240d06 100644 --- a/sys/compat/linux/arch/amd64/linux_systrace_args.c +++ b/sys/compat/linux/arch/amd64/linux_systrace_args.c @@ -1,9 +1,9 @@ -/* $NetBSD: linux_systrace_args.c,v 1.31 2025/11/10 15:41:57 christos Exp $ */ +/* $NetBSD$ */ /* * System call argument to DTrace register array conversion. * - * DO NOT EDIT-- this file is automatically generated. + * DO NOT EDIT-- this file is generated by makesyscalls.sh * This file is part of the DTrace syscall provider. */ @@ -351,6 +351,16 @@ systrace_args(register_t sysnum, const void *params, uintptr_t *uarg, size_t *n_ *n_args = 0; break; } + /* linux_sys_sendfile */ + case 40: { + const struct linux_sys_sendfile_args *p = params; + iarg[0] = SCARG(p, out_fd); /* int */ + iarg[1] = SCARG(p, in_fd); /* int */ + uarg[2] = (intptr_t) SCARG(p, offset); /* off_t * */ + uarg[3] = SCARG(p, count); /* size_t */ + *n_args = 4; + break; + } /* linux_sys_socket */ case 41: { const struct linux_sys_socket_args *p = params; @@ -2780,6 +2790,25 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) /* sys_getpid */ case 39: break; + /* linux_sys_sendfile */ + case 40: + switch(ndx) { + case 0: + p = "int"; + break; + case 1: + p = "int"; + break; + case 2: + p = "off_t *"; + break; + case 3: + p = "size_t"; + break; + default: + break; + }; + break; /* linux_sys_socket */ case 41: switch(ndx) { @@ -6113,6 +6142,11 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; /* sys_getpid */ case 39: + /* linux_sys_sendfile */ + case 40: + if (ndx == 0 || ndx == 1) + p = "ssize_t"; + break; /* linux_sys_socket */ case 41: if (ndx == 0 || ndx == 1) From f453a1fe12547b3f51a07a2f92813669fdd9d2d2 Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Fri, 20 Feb 2026 09:41:54 -0300 Subject: [PATCH 3/6] sys/compat/linux/common/linux_file: Added template for sendfile --- sys/compat/linux/common/linux_file.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sys/compat/linux/common/linux_file.c b/sys/compat/linux/common/linux_file.c index 4c3ead76b6eb0..13d0d5a66df71 100644 --- a/sys/compat/linux/common/linux_file.c +++ b/sys/compat/linux/common/linux_file.c @@ -275,6 +275,25 @@ linux_sys_openat(struct lwp *l, const struct linux_sys_openat_args *uap, return 0; } +/* + * sendfile(2). + * + * + */ +int +linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, + register_t *retval) +{ + /* + * syscallarg(int) out_fd; + * syscallarg(int) in_fd; + * syscallarg(off_t *) offset; + * syscallarg(size_t) count; + */ + + return ENOSYS; +} + /* * Most actions in the fcntl() call are straightforward; simply * pass control to the NetBSD system call. A few commands need From edfcfb8765adcda2058dc9b458623a90d075b9a0 Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Sun, 22 Feb 2026 23:00:37 -0300 Subject: [PATCH 4/6] compat_linux: add linux_sys_sendfile(2) Implements Linux-compatible sendfile semantics and passes LTP coverage. XXX: ENOBUFS from fo_write() may be reported with non-zero progress for SOCK_DGRAM. Needs more work. --- sys/compat/linux/common/linux_file.c | 239 ++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 1 deletion(-) diff --git a/sys/compat/linux/common/linux_file.c b/sys/compat/linux/common/linux_file.c index 13d0d5a66df71..b0e111f85706d 100644 --- a/sys/compat/linux/common/linux_file.c +++ b/sys/compat/linux/common/linux_file.c @@ -291,7 +291,244 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, * syscallarg(size_t) count; */ - return ENOSYS; + printf("Iniciando syscall\n"); + printf("===============================================================\n"); + + /* Args from the syscall */ + int in_fd = SCARG(uap, in_fd); + int out_fd = SCARG(uap, out_fd); + off_t *user_offset = SCARG(uap, offset); + size_t count = SCARG(uap, count); + + file_t *in_fp = NULL; + file_t *out_fp = NULL; + + off_t in_offset = 0; + bool has_user_offset = (user_offset != NULL); + size_t bytes_left; + size_t total_bytes_copied = 0; + const size_t MAX_BYTES_TO_TRANSFER = 2147479552; + const off_t OFF_MAX = __type_max(off_t); + + /* Structures for actual copy */ + char *buffer = NULL; + struct uio auio; + struct iovec aiov; + + int error = 0; + + /* The count must not be more than what the man page specifies */ + if (count > MAX_BYTES_TO_TRANSFER) + count = MAX_BYTES_TO_TRANSFER; + + if (has_user_offset) { + error = copyin(user_offset, &in_offset, sizeof(in_offset)); + if (error) + goto out; + } + + if (count > OFF_MAX - in_offset) { + error = EOVERFLOW; + goto out; + } + + in_fp = fd_getfile(in_fd); + out_fp = fd_getfile(out_fd); + + if ((in_fp == NULL) || (out_fp == NULL)) { + error = EBADF; + goto out; + } + + /* + * in_fp normally can only be a regular file, + * but if out_fd is a pipe in_fd can also be a + * pipe or a socket (in which case the sendfile would + * desugars to a splice) + */ + switch (in_fp->f_type) { + + case DTYPE_VNODE: + struct vnode *in_vn = in_fp->f_vnode; + + if (in_vn->v_type != VREG) { + error = EINVAL; + goto out; + } + break; + + /* case DTYPE_PIPE: Don't work on linux either */ + case DTYPE_SOCKET: + if (has_user_offset) { + error = EINVAL; + goto out; + } + if (out_fp->f_type != DTYPE_PIPE) { + error = EINVAL; + goto out; + } + break; + default: + error = EINVAL; + goto out; + } + + /* out_fp may be a regular file, a pipe or a tcp socket */ + switch (out_fp->f_type) { + + case DTYPE_VNODE: + struct vnode *out_vn = out_fp->f_vnode; + + if (out_vn->v_type != VREG) { + error = EINVAL; + goto out; + } + break; + + case DTYPE_SOCKET: + // struct socket *so = out_fp->f_socket; + + // if (so->so_type != SOCK_STREAM) { + // error = EINVAL; + // goto out; + // } + break; + + case DTYPE_PIPE: + break; + + default: + error = EINVAL; + goto out; + } + + if ((in_fp->f_flag & FREAD) == 0) { + error = EBADF; + goto out; + } + + if (((out_fp->f_flag & FWRITE) == 0) || + ((out_fp->f_flag & FAPPEND) != 0)) { + error = EBADF; + goto out; + } + + buffer = kmem_alloc(LINUX_COPY_FILE_RANGE_MAX_CHUNK, KM_SLEEP); + + bytes_left = count; + + printf("Entrando no loop\n"); + while (bytes_left > 0) { + printf("IN offset loop start: %jd\n", (intmax_t)in_fp->f_offset); + printf("OUT offset loop start: %jd\n", (intmax_t)out_fp->f_offset); + + size_t to_copy = MIN(bytes_left, LINUX_COPY_FILE_RANGE_MAX_CHUNK); + size_t bytes_read = 0; + size_t bytes_written = 0; + + /* Set up iovec and uio for reading */ + aiov.iov_base = buffer; + aiov.iov_len = to_copy; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = to_copy; + // auio.uio_offset = has_user_offset ? in_offset : in_fp->f_offset; + auio.uio_rw = UIO_READ; + // auio.uio_vmspace = l->l_proc->p_vmspace; + UIO_SETUP_SYSSPACE(&auio); + + /* Read the in_fp */ + error = (*in_fp->f_ops->fo_read)(in_fp, + has_user_offset ? &in_offset : &in_fp->f_offset, &auio, + in_fp->f_cred, 0); + + if (error) + break; /* Error when reading */ + + bytes_read = to_copy - auio.uio_resid; + + printf("Bytes Read: %jd\n", (intmax_t)bytes_read); + if (bytes_read == 0) { + printf("EOF"); + /* EOF reached */ + break; + } + + /* Set up iovec and uio for writing */ + aiov.iov_base = buffer; + aiov.iov_len = bytes_read; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_resid = bytes_read; + // auio.uio_offset = out_fp->f_offset; + auio.uio_rw = UIO_WRITE; + // auio.uio_vmspace = l->l_proc->p_vmspace; + UIO_SETUP_SYSSPACE(&auio); + + /* Write to out_fp */ + error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, &auio, + out_fp->f_cred, 0); + + error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, &auio, out_fp->f_cred, 0); + + // PRINT DE DEBUG CRÍTICO: + printf("DEBUG: error = %d, resid_inicial = %zu, resid_final = %zu\n", + error, bytes_read, auio.uio_resid); + + bytes_written = bytes_read - auio.uio_resid; + printf("Bytes Written: %jd\n", (intmax_t)bytes_written); + + if (error == ENOBUFS) { + bytes_written = 0; /* Assumes that nothing was written */ + error = EAGAIN; /* The syscall expects this error */ + } + + if (has_user_offset) { + in_offset += bytes_written; + } + else { + in_fp->f_offset += bytes_written; + } + out_fp->f_offset += bytes_written; + + total_bytes_copied += bytes_written; + bytes_left -= bytes_written; + + /* Exit only after updating the values */ + if (error) + break; /* Error when writing */ + + printf("IN offset loop end: %jd\n", (intmax_t)in_fp->f_offset); + printf("OUT offset loop end: %jd\n", (intmax_t)out_fp->f_offset); + } + + printf("saindo do loop\n"); + + if (total_bytes_copied > 0) { + error = 0; + + } + + if (has_user_offset) { + int copy_err = copyout(&in_offset, user_offset, sizeof(in_offset)); + /* Overrides error only if there's something wrong with the copyout */ + if (copy_err) + error = copy_err; + } + + *retval = total_bytes_copied; + + goto out; + +out: + printf("Estou no goto!\n"); + if (buffer) + kmem_free(buffer, LINUX_COPY_FILE_RANGE_MAX_CHUNK); + if (in_fp) + fd_putfile(in_fd); + if (out_fp) + fd_putfile(out_fd); + return error; } /* From ce058de885b1389ae075909c178a95a9b0d239ff Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Mon, 23 Feb 2026 13:54:05 -0300 Subject: [PATCH 5/6] compat_linux: update linux_sys_sendfile(2) Fixed error handling for the fo_write(). The ENOBUFS with non-zero progress is now correctly treated. Removed debug printfs and improved comments. --- sys/compat/linux/common/linux_file.c | 63 +++++++--------------------- 1 file changed, 16 insertions(+), 47 deletions(-) diff --git a/sys/compat/linux/common/linux_file.c b/sys/compat/linux/common/linux_file.c index b0e111f85706d..241ca0d553514 100644 --- a/sys/compat/linux/common/linux_file.c +++ b/sys/compat/linux/common/linux_file.c @@ -276,9 +276,7 @@ linux_sys_openat(struct lwp *l, const struct linux_sys_openat_args *uap, } /* - * sendfile(2). - * - * + * sendfile(2). Unlike the linux implementation, this one is not zero-copy */ int linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, @@ -290,9 +288,6 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, * syscallarg(off_t *) offset; * syscallarg(size_t) count; */ - - printf("Iniciando syscall\n"); - printf("===============================================================\n"); /* Args from the syscall */ int in_fd = SCARG(uap, in_fd); @@ -341,10 +336,9 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, } /* - * in_fp normally can only be a regular file, - * but if out_fd is a pipe in_fd can also be a - * pipe or a socket (in which case the sendfile would - * desugars to a splice) + * Normally, in_fd can only be a regular file, however, if out_fd + * is a pipe the linux sendfile desugars to a splice, allowing + * in_fd to be a socket, but NOT a pipe (even though splice accepts). */ switch (in_fp->f_type) { @@ -357,8 +351,8 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, } break; - /* case DTYPE_PIPE: Don't work on linux either */ case DTYPE_SOCKET: + /* If in_fd is a socket, user_offset must be NULL */ if (has_user_offset) { error = EINVAL; goto out; @@ -373,7 +367,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, goto out; } - /* out_fp may be a regular file, a pipe or a tcp socket */ + /* out_fp may be a regular file, a pipe or a socket */ switch (out_fp->f_type) { case DTYPE_VNODE: @@ -386,12 +380,6 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, break; case DTYPE_SOCKET: - // struct socket *so = out_fp->f_socket; - - // if (so->so_type != SOCK_STREAM) { - // error = EINVAL; - // goto out; - // } break; case DTYPE_PIPE: @@ -417,10 +405,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, bytes_left = count; - printf("Entrando no loop\n"); while (bytes_left > 0) { - printf("IN offset loop start: %jd\n", (intmax_t)in_fp->f_offset); - printf("OUT offset loop start: %jd\n", (intmax_t)out_fp->f_offset); size_t to_copy = MIN(bytes_left, LINUX_COPY_FILE_RANGE_MAX_CHUNK); size_t bytes_read = 0; @@ -432,9 +417,8 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = to_copy; - // auio.uio_offset = has_user_offset ? in_offset : in_fp->f_offset; + auio.uio_offset = has_user_offset ? in_offset : in_fp->f_offset; auio.uio_rw = UIO_READ; - // auio.uio_vmspace = l->l_proc->p_vmspace; UIO_SETUP_SYSSPACE(&auio); /* Read the in_fp */ @@ -447,9 +431,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, bytes_read = to_copy - auio.uio_resid; - printf("Bytes Read: %jd\n", (intmax_t)bytes_read); if (bytes_read == 0) { - printf("EOF"); /* EOF reached */ break; } @@ -460,27 +442,25 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = bytes_read; - // auio.uio_offset = out_fp->f_offset; + auio.uio_offset = out_fp->f_offset; auio.uio_rw = UIO_WRITE; - // auio.uio_vmspace = l->l_proc->p_vmspace; UIO_SETUP_SYSSPACE(&auio); /* Write to out_fp */ error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, &auio, out_fp->f_cred, 0); - error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, &auio, out_fp->f_cred, 0); - - // PRINT DE DEBUG CRÍTICO: - printf("DEBUG: error = %d, resid_inicial = %zu, resid_final = %zu\n", - error, bytes_read, auio.uio_resid); + error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, + &auio, out_fp->f_cred, 0); bytes_written = bytes_read - auio.uio_resid; - printf("Bytes Written: %jd\n", (intmax_t)bytes_written); - if (error == ENOBUFS) { - bytes_written = 0; /* Assumes that nothing was written */ - error = EAGAIN; /* The syscall expects this error */ + /* Exit only after updating the values */ + if (error) { + if (error == ENOBUFS) { + error = EAGAIN; /* What the syscall expects */ + } + break; /* Error when writing */ } if (has_user_offset) { @@ -493,20 +473,10 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, total_bytes_copied += bytes_written; bytes_left -= bytes_written; - - /* Exit only after updating the values */ - if (error) - break; /* Error when writing */ - - printf("IN offset loop end: %jd\n", (intmax_t)in_fp->f_offset); - printf("OUT offset loop end: %jd\n", (intmax_t)out_fp->f_offset); } - printf("saindo do loop\n"); - if (total_bytes_copied > 0) { error = 0; - } if (has_user_offset) { @@ -521,7 +491,6 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, goto out; out: - printf("Estou no goto!\n"); if (buffer) kmem_free(buffer, LINUX_COPY_FILE_RANGE_MAX_CHUNK); if (in_fp) From d4b88436e29cf7fc32f3478b365047c2be2690d8 Mon Sep 17 00:00:00 2001 From: henriquebritoM Date: Mon, 23 Feb 2026 19:48:56 -0300 Subject: [PATCH 6/6] compat_linux: update linux_sys_sendfile(2) Fixed double writting, increased buffer size and improved offset logic. Performance now is about the same as read() + write(). --- sys/compat/linux/common/linux_file.c | 41 +++++++++++----------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/sys/compat/linux/common/linux_file.c b/sys/compat/linux/common/linux_file.c index 241ca0d553514..e60098dc2de79 100644 --- a/sys/compat/linux/common/linux_file.c +++ b/sys/compat/linux/common/linux_file.c @@ -275,9 +275,7 @@ linux_sys_openat(struct lwp *l, const struct linux_sys_openat_args *uap, return 0; } -/* - * sendfile(2). Unlike the linux implementation, this one is not zero-copy - */ +/* sendfile(2) */ int linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, register_t *retval) @@ -299,6 +297,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, file_t *out_fp = NULL; off_t in_offset = 0; + off_t in_offset_before_reads; bool has_user_offset = (user_offset != NULL); size_t bytes_left; size_t total_bytes_copied = 0; @@ -401,31 +400,32 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, goto out; } - buffer = kmem_alloc(LINUX_COPY_FILE_RANGE_MAX_CHUNK, KM_SLEEP); + buffer = kmem_alloc(MAXBSIZE, KM_SLEEP); bytes_left = count; + in_offset_before_reads = in_fp->f_offset; + if (has_user_offset) + in_fp->f_offset = in_offset; while (bytes_left > 0) { - size_t to_copy = MIN(bytes_left, LINUX_COPY_FILE_RANGE_MAX_CHUNK); + size_t to_copy = MIN(bytes_left, MAXBSIZE); size_t bytes_read = 0; size_t bytes_written = 0; /* Set up iovec and uio for reading */ aiov.iov_base = buffer; aiov.iov_len = to_copy; + auio.uio_resid = to_copy; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; - auio.uio_resid = to_copy; - auio.uio_offset = has_user_offset ? in_offset : in_fp->f_offset; auio.uio_rw = UIO_READ; UIO_SETUP_SYSSPACE(&auio); /* Read the in_fp */ - error = (*in_fp->f_ops->fo_read)(in_fp, - has_user_offset ? &in_offset : &in_fp->f_offset, &auio, + error = (*in_fp->f_ops->fo_read)(in_fp, &in_fp->f_offset, &auio, in_fp->f_cred, 0); - + if (error) break; /* Error when reading */ @@ -439,23 +439,18 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, /* Set up iovec and uio for writing */ aiov.iov_base = buffer; aiov.iov_len = bytes_read; + auio.uio_resid = bytes_read; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; - auio.uio_resid = bytes_read; - auio.uio_offset = out_fp->f_offset; auio.uio_rw = UIO_WRITE; - UIO_SETUP_SYSSPACE(&auio); + UIO_SETUP_SYSSPACE(&auio); /* Write to out_fp */ error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, &auio, out_fp->f_cred, 0); - error = (*out_fp->f_ops->fo_write)(out_fp, &out_fp->f_offset, - &auio, out_fp->f_cred, 0); - bytes_written = bytes_read - auio.uio_resid; - /* Exit only after updating the values */ if (error) { if (error == ENOBUFS) { error = EAGAIN; /* What the syscall expects */ @@ -463,12 +458,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, break; /* Error when writing */ } - if (has_user_offset) { - in_offset += bytes_written; - } - else { - in_fp->f_offset += bytes_written; - } + in_fp->f_offset += bytes_written; out_fp->f_offset += bytes_written; total_bytes_copied += bytes_written; @@ -480,7 +470,8 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, } if (has_user_offset) { - int copy_err = copyout(&in_offset, user_offset, sizeof(in_offset)); + int copy_err = copyout(&in_fp->f_offset, user_offset, sizeof(in_offset)); + in_fp->f_offset = in_offset_before_reads; /* returns to original */ /* Overrides error only if there's something wrong with the copyout */ if (copy_err) error = copy_err; @@ -492,7 +483,7 @@ linux_sys_sendfile(struct lwp *l, const struct linux_sys_sendfile_args *uap, out: if (buffer) - kmem_free(buffer, LINUX_COPY_FILE_RANGE_MAX_CHUNK); + kmem_free(buffer, MAXBSIZE); if (in_fp) fd_putfile(in_fd); if (out_fp)