| Message ID | 20260518-misc-2026q2-v2-7-6c16fe448301@bsdimp.com |
|---|---|
| State | New |
| Headers | show |
| Series | bsd-user: Upstream most of the remaining system calls | expand |
On 5/18/2026 2:27 PM, Warner Losh wrote: > Add FreeBSD-specific file operation shims: pipe2, chflagsat, > close_range, copy_file_range, and __specialfd. Also add the > safe_copy_file_range and safe_ppoll syscall wrappers and the > target_specialfd_eventfd type definitions. > > Signed-off-by: Stacey Son <sson@FreeBSD.org> > Signed-off-by: Warner Losh <imp@bsdimp.com> > Assisted-by: Claude Opus 4.6 (1M context) > --- > bsd-user/freebsd/os-file.h | 129 ++++++++++++++++++++++++++++++++++++++++++ > bsd-user/freebsd/os-syscall.c | 7 +++ > 2 files changed, 136 insertions(+) > > diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h > new file mode 100644 > index 0000000000..c1da52789a > --- /dev/null > +++ b/bsd-user/freebsd/os-file.h > @@ -0,0 +1,129 @@ > +/* > + * FreeBSD file related system call shims and definitions > + * > + * Copyright (c) 2014 Stacey D. Son > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > +#ifndef FREEBSD_OS_FILE_H > +#define FREEBSD_OS_FILE_H > + > +#include <sys/specialfd.h> > + > +/* > + * Asynchronous I/O. > + */ > + > +/* pipe2(2) */ > +static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int flags) > +{ > + int host_pipe[2]; > + int host_ret = pipe2(host_pipe, flags); > + /* XXXss - flags should be translated from target to host. */ > + > + if (host_ret == -1) { > + return get_errno(host_ret); > + } > + > + /* > + * pipe2() returns it's second FD by copying it back to userspace and not in > + * a second register like pipe(2): set_second_rval(env, host_pipe[1]); > + * > + * Copy the FD's back to userspace: > + */ > + if (put_user_s32(host_pipe[0], pipedes) || > + put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) { > + close(host_pipe[0]); > + close(host_pipe[1]); > + return -TARGET_EFAULT; > + } > + return 0; > +} > + > +/* chflagsat(2) */ > +static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path, > + abi_ulong flags, int atflags) > +{ > + abi_long ret; > + void *p; > + > + LOCK_PATH(p, path); > + ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)? */ > + UNLOCK_PATH(p, path); > + > + return ret; > +} > + > +/* close_range(2) */ > +static inline abi_long do_freebsd_close_range(unsigned int lowfd, > + unsigned int highfd, int flags) > +{ > + > + return get_errno(close_range(lowfd, highfd, flags)); > +} > + > +ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int); > + > +/* copy_file_range(2) */ > +static inline abi_long do_freebsd_copy_file_range(int infd, > + abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len, > + unsigned int flags) > +{ > + off_t inoff, outoff, *inp, *outp; > + abi_long ret; > + > + inp = outp = NULL; > + if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) { > + return -TARGET_EFAULT; > + } else if (inofftp != 0) { > + inoff = tswap64(*(off_t *)g2h_untagged(inofftp)); > + inp = &inoff; > + } > + if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp, sizeof(off_t))) { > + return -TARGET_EFAULT; > + } else if (outofftp != 0) { > + outoff = tswap64(*(off_t *)g2h_untagged(outofftp)); > + outp = &outoff; > + } > + Very small nit, but this pattern is a bit weird to read: Would be better with: if (inofftp != 0) { if (!access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) { return -TARGET_EFAULT; } inoff = tswap64(*(off_t *)g2h_untagged(inofftp)); inp = &inoff; } > + ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len, > + flags)); > + > + if (!is_error(ret)) { > + if (inofftp != 0) { > + *(off_t *)g2h_untagged(inofftp) = tswap64(inoff); > + } > + if (outofftp != 0) { > + *(off_t *)g2h_untagged(outofftp) = tswap64(outoff); > + } > + } > + return ret; > +} > + > +static inline abi_long do_freebsd___specialfd(int type, abi_ulong req, > + size_t len) > +{ > + abi_long ret; > + > + ret = -TARGET_EINVAL; > + switch (type) { > + case TARGET_SPECIALFD_EVENT: { > + struct specialfd_eventfd evfd; > + struct target_specialfd_eventfd *target_eventfd; > + > + if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) { > + return -TARGET_EFAULT; > + } > + > + evfd.initval = tswap32(target_eventfd->initval); > + evfd.flags = tswap32(target_eventfd->flags); > + ret = get_errno(syscall(SYS___specialfd, type, &evfd, sizeof(evfd))); > + unlock_user_struct(target_eventfd, req, 0); > + break; > + } > + } > + > + return ret; > +} > + > +#endif /* FREEBSD_OS_FILE_H */ > diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c > index fc6273a780..8a25be4ac7 100644 > --- a/bsd-user/freebsd/os-syscall.c > +++ b/bsd-user/freebsd/os-syscall.c > @@ -45,6 +45,7 @@ > #include "os-stat.h" > #include "os-proc.h" > #include "os-signal.h" > +#include "os-file.h" > #include "os-misc.h" > > /* I/O */ > @@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt); > safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt, > off_t, offset); > > +safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds, > + const struct timespec *, restrict_timeout, const sigset_t *, > + restrict_newsigmask); > +safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd, > + off_t *, outoffp, size_t, len, unsigned int, flags); > + > /* used in os-proc */ > safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options, > struct rusage *, rusage); > Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
On Fri, May 22, 2026 at 4:54 PM Pierrick Bouvier < pierrick.bouvier@oss.qualcomm.com> wrote: > On 5/18/2026 2:27 PM, Warner Losh wrote: > > Add FreeBSD-specific file operation shims: pipe2, chflagsat, > > close_range, copy_file_range, and __specialfd. Also add the > > safe_copy_file_range and safe_ppoll syscall wrappers and the > > target_specialfd_eventfd type definitions. > > > > Signed-off-by: Stacey Son <sson@FreeBSD.org> > > Signed-off-by: Warner Losh <imp@bsdimp.com> > > Assisted-by: Claude Opus 4.6 (1M context) > > --- > > bsd-user/freebsd/os-file.h | 129 > ++++++++++++++++++++++++++++++++++++++++++ > > bsd-user/freebsd/os-syscall.c | 7 +++ > > 2 files changed, 136 insertions(+) > > > > diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h > > new file mode 100644 > > index 0000000000..c1da52789a > > --- /dev/null > > +++ b/bsd-user/freebsd/os-file.h > > @@ -0,0 +1,129 @@ > > +/* > > + * FreeBSD file related system call shims and definitions > > + * > > + * Copyright (c) 2014 Stacey D. Son > > + * > > + * SPDX-License-Identifier: GPL-2.0-or-later > > + */ > > +#ifndef FREEBSD_OS_FILE_H > > +#define FREEBSD_OS_FILE_H > > + > > +#include <sys/specialfd.h> > > + > > +/* > > + * Asynchronous I/O. > > + */ > > + > > +/* pipe2(2) */ > > +static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int > flags) > > +{ > > + int host_pipe[2]; > > + int host_ret = pipe2(host_pipe, flags); > > + /* XXXss - flags should be translated from target to host. */ > > + > > + if (host_ret == -1) { > > + return get_errno(host_ret); > > + } > > + > > + /* > > + * pipe2() returns it's second FD by copying it back to userspace > and not in > > + * a second register like pipe(2): set_second_rval(env, > host_pipe[1]); > > + * > > + * Copy the FD's back to userspace: > > + */ > > + if (put_user_s32(host_pipe[0], pipedes) || > > + put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) { > > + close(host_pipe[0]); > > + close(host_pipe[1]); > > + return -TARGET_EFAULT; > > + } > > + return 0; > > +} > > + > > +/* chflagsat(2) */ > > +static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path, > > + abi_ulong flags, int atflags) > > +{ > > + abi_long ret; > > + void *p; > > + > > + LOCK_PATH(p, path); > > + ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)? > */ > > + UNLOCK_PATH(p, path); > > + > > + return ret; > > +} > > + > > +/* close_range(2) */ > > +static inline abi_long do_freebsd_close_range(unsigned int lowfd, > > + unsigned int highfd, int flags) > > +{ > > + > > + return get_errno(close_range(lowfd, highfd, flags)); > > +} > > + > > +ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t, > unsigned int); > > + > > +/* copy_file_range(2) */ > > +static inline abi_long do_freebsd_copy_file_range(int infd, > > + abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len, > > + unsigned int flags) > > +{ > > + off_t inoff, outoff, *inp, *outp; > > + abi_long ret; > > + > > + inp = outp = NULL; > > + if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp, > sizeof(off_t))) { > > + return -TARGET_EFAULT; > > + } else if (inofftp != 0) { > > + inoff = tswap64(*(off_t *)g2h_untagged(inofftp)); > > + inp = &inoff; > > + } > > + if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp, > sizeof(off_t))) { > > + return -TARGET_EFAULT; > > + } else if (outofftp != 0) { > > + outoff = tswap64(*(off_t *)g2h_untagged(outofftp)); > > + outp = &outoff; > > + } > > + > > Very small nit, but this pattern is a bit weird to read: > > Would be better with: > > if (inofftp != 0) { > if (!access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) { > return -TARGET_EFAULT; > } > inoff = tswap64(*(off_t *)g2h_untagged(inofftp)); > inp = &inoff; > } > Yes. That's a better pattern. I've redone this and outoffp and had claude double check. You can check in the next rev :) > > + ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len, > > + flags)); > > + > > + if (!is_error(ret)) { > > + if (inofftp != 0) { > > + *(off_t *)g2h_untagged(inofftp) = tswap64(inoff); > > + } > > + if (outofftp != 0) { > > + *(off_t *)g2h_untagged(outofftp) = tswap64(outoff); > > + } > > + } > > + return ret; > > +} > > + > > +static inline abi_long do_freebsd___specialfd(int type, abi_ulong req, > > + size_t len) > > +{ > > + abi_long ret; > > + > > + ret = -TARGET_EINVAL; > > + switch (type) { > > + case TARGET_SPECIALFD_EVENT: { > > + struct specialfd_eventfd evfd; > > + struct target_specialfd_eventfd *target_eventfd; > > + > > + if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) { > > + return -TARGET_EFAULT; > > + } > > + > > + evfd.initval = tswap32(target_eventfd->initval); > > + evfd.flags = tswap32(target_eventfd->flags); > > + ret = get_errno(syscall(SYS___specialfd, type, &evfd, > sizeof(evfd))); > > + unlock_user_struct(target_eventfd, req, 0); > > + break; > > + } > > + } > > + > > + return ret; > > +} > > + > > +#endif /* FREEBSD_OS_FILE_H */ > > diff --git a/bsd-user/freebsd/os-syscall.c > b/bsd-user/freebsd/os-syscall.c > > index fc6273a780..8a25be4ac7 100644 > > --- a/bsd-user/freebsd/os-syscall.c > > +++ b/bsd-user/freebsd/os-syscall.c > > @@ -45,6 +45,7 @@ > > #include "os-stat.h" > > #include "os-proc.h" > > #include "os-signal.h" > > +#include "os-file.h" > > #include "os-misc.h" > > > > /* I/O */ > > @@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct > iovec *, iov, int, iovcnt); > > safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, > int, iovcnt, > > off_t, offset); > > > > +safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds, > > + const struct timespec *, restrict_timeout, const sigset_t *, > > + restrict_newsigmask); > > +safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, > int, outfd, > > + off_t *, outoffp, size_t, len, unsigned int, flags); > > + > > /* used in os-proc */ > > safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options, > > struct rusage *, rusage); > > > > Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> > Thanks! Warner
diff --git a/bsd-user/freebsd/os-file.h b/bsd-user/freebsd/os-file.h new file mode 100644 index 0000000000..c1da52789a --- /dev/null +++ b/bsd-user/freebsd/os-file.h @@ -0,0 +1,129 @@ +/* + * FreeBSD file related system call shims and definitions + * + * Copyright (c) 2014 Stacey D. Son + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef FREEBSD_OS_FILE_H +#define FREEBSD_OS_FILE_H + +#include <sys/specialfd.h> + +/* + * Asynchronous I/O. + */ + +/* pipe2(2) */ +static abi_long do_bsd_pipe2(CPUArchState *env, abi_ulong pipedes, int flags) +{ + int host_pipe[2]; + int host_ret = pipe2(host_pipe, flags); + /* XXXss - flags should be translated from target to host. */ + + if (host_ret == -1) { + return get_errno(host_ret); + } + + /* + * pipe2() returns it's second FD by copying it back to userspace and not in + * a second register like pipe(2): set_second_rval(env, host_pipe[1]); + * + * Copy the FD's back to userspace: + */ + if (put_user_s32(host_pipe[0], pipedes) || + put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) { + close(host_pipe[0]); + close(host_pipe[1]); + return -TARGET_EFAULT; + } + return 0; +} + +/* chflagsat(2) */ +static inline abi_long do_bsd_chflagsat(int fd, abi_ulong path, + abi_ulong flags, int atflags) +{ + abi_long ret; + void *p; + + LOCK_PATH(p, path); + ret = get_errno(chflagsat(fd, p, flags, atflags)); /* XXX path(p)? */ + UNLOCK_PATH(p, path); + + return ret; +} + +/* close_range(2) */ +static inline abi_long do_freebsd_close_range(unsigned int lowfd, + unsigned int highfd, int flags) +{ + + return get_errno(close_range(lowfd, highfd, flags)); +} + +ssize_t safe_copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int); + +/* copy_file_range(2) */ +static inline abi_long do_freebsd_copy_file_range(int infd, + abi_ulong inofftp, int outfd, abi_ulong outofftp, size_t len, + unsigned int flags) +{ + off_t inoff, outoff, *inp, *outp; + abi_long ret; + + inp = outp = NULL; + if (inofftp != 0 && !access_ok(VERIFY_WRITE, inofftp, sizeof(off_t))) { + return -TARGET_EFAULT; + } else if (inofftp != 0) { + inoff = tswap64(*(off_t *)g2h_untagged(inofftp)); + inp = &inoff; + } + if (outofftp != 0 && !access_ok(VERIFY_WRITE, outofftp, sizeof(off_t))) { + return -TARGET_EFAULT; + } else if (outofftp != 0) { + outoff = tswap64(*(off_t *)g2h_untagged(outofftp)); + outp = &outoff; + } + + ret = get_errno(safe_copy_file_range(infd, inp, outfd, outp, len, + flags)); + + if (!is_error(ret)) { + if (inofftp != 0) { + *(off_t *)g2h_untagged(inofftp) = tswap64(inoff); + } + if (outofftp != 0) { + *(off_t *)g2h_untagged(outofftp) = tswap64(outoff); + } + } + return ret; +} + +static inline abi_long do_freebsd___specialfd(int type, abi_ulong req, + size_t len) +{ + abi_long ret; + + ret = -TARGET_EINVAL; + switch (type) { + case TARGET_SPECIALFD_EVENT: { + struct specialfd_eventfd evfd; + struct target_specialfd_eventfd *target_eventfd; + + if (!lock_user_struct(VERIFY_READ, target_eventfd, req, 1)) { + return -TARGET_EFAULT; + } + + evfd.initval = tswap32(target_eventfd->initval); + evfd.flags = tswap32(target_eventfd->flags); + ret = get_errno(syscall(SYS___specialfd, type, &evfd, sizeof(evfd))); + unlock_user_struct(target_eventfd, req, 0); + break; + } + } + + return ret; +} + +#endif /* FREEBSD_OS_FILE_H */ diff --git a/bsd-user/freebsd/os-syscall.c b/bsd-user/freebsd/os-syscall.c index fc6273a780..8a25be4ac7 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -45,6 +45,7 @@ #include "os-stat.h" #include "os-proc.h" #include "os-signal.h" +#include "os-file.h" #include "os-misc.h" /* I/O */ @@ -66,6 +67,12 @@ safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt); safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt, off_t, offset); +safe_syscall4(int, ppoll, struct pollfd *, fds, nfds_t, nfds, + const struct timespec *, restrict_timeout, const sigset_t *, + restrict_newsigmask); +safe_syscall6(ssize_t, copy_file_range, int, infd, off_t *, inoffp, int, outfd, + off_t *, outoffp, size_t, len, unsigned int, flags); + /* used in os-proc */ safe_syscall4(pid_t, wait4, pid_t, wpid, int *, status, int, options, struct rusage *, rusage);