feat(unistd): file I/O syscall wrappers
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "unistd_impl.h"
|
||||
|
||||
/*
|
||||
* access/faccessat over SYS_faccessat (the *at form is the modern kernel
|
||||
* ABI; SYS_faccessat2 is deliberately not used — the classic syscall
|
||||
* covers the POSIX amode set). access() passes AT_FDCWD and no flags.
|
||||
*/
|
||||
int
|
||||
access(const char *path, int amode)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_faccessat, VLIBC_UNISTD_AT_FDCWD, (long)path, amode, 0));
|
||||
}
|
||||
|
||||
int
|
||||
faccessat(int fd, const char *path, int amode, int flag)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_faccessat, fd, (long)path, amode, flag));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* close: unbuffered pass-through. There is nothing to flush — vlibc I/O is
|
||||
* unbuffered at this layer, and the stdio layer owns its own buffers.
|
||||
*/
|
||||
int
|
||||
close(int fildes)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_close, fildes));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* dup/dup2: descriptor duplication. The copies share the file description
|
||||
* (position, status flags, locks) with the original. On x86_64 SYS_dup2 is
|
||||
* a two-argument syscall.
|
||||
*/
|
||||
int
|
||||
dup(int fildes)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_dup, fildes));
|
||||
}
|
||||
|
||||
int
|
||||
dup2(int fildes, int fildes2)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_dup2, fildes, fildes2));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* dup3: dup2 with descriptor flags (O_CLOEXEC) applied atomically.
|
||||
* Linux-specific; the kernel rejects fildes == fildes2 with EINVAL.
|
||||
*/
|
||||
int
|
||||
dup3(int fildes, int fildes2, int flags)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_dup3, fildes, fildes2, flags));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* fsync/fdatasync: flush a descriptor's dirty data (and for fsync, the
|
||||
* metadata) to stable storage.
|
||||
*/
|
||||
int
|
||||
fsync(int fildes)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_fsync, fildes));
|
||||
}
|
||||
|
||||
int
|
||||
fdatasync(int fildes)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_fdatasync, fildes));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* lseek returns a full-width off_t: on x86_64 the offset is a 64-bit long
|
||||
* that can legitimately exceed INT_MAX, so the value must not be routed
|
||||
* through syscall_ret() (which narrows to int). The error translation is
|
||||
* inlined here with the same semantics: errno set from -r and (off_t)-1 on
|
||||
* error, errno untouched on success.
|
||||
*/
|
||||
off_t
|
||||
lseek(int fildes, off_t offset, int whence)
|
||||
{
|
||||
long r = __syscall3(SYS_lseek, fildes, offset, whence);
|
||||
|
||||
if (r < 0 && r > -4096)
|
||||
{
|
||||
errno = (int)-r;
|
||||
return (off_t)-1;
|
||||
}
|
||||
return (off_t)r;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* lseek64: glibc LFS alias. On x86_64 the LFS and non-LFS off_t are
|
||||
* identical (both 64-bit) and SYS_lseek is the single ABI, so this is a
|
||||
* plain delegation. Provided for source compatibility only.
|
||||
*/
|
||||
off_t
|
||||
lseek64(int fildes, off_t offset, int whence)
|
||||
{
|
||||
return lseek(fildes, offset, whence);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "unistd_impl.h"
|
||||
|
||||
/*
|
||||
* open/openat over SYS_openat (the *at syscall family is the modern kernel
|
||||
* ABI; SYS_open exists only for compatibility and is not used). The
|
||||
* optional fourth mode argument is read from the varargs only when oflag
|
||||
* contains O_CREAT or O_TMPFILE — otherwise the caller supplied no mode
|
||||
* and 0 is passed, which the kernel ignores. mode_t promotes to unsigned
|
||||
* int in the varargs list.
|
||||
*/
|
||||
int
|
||||
open(const char *path, int oflag, ...)
|
||||
{
|
||||
unsigned mode = 0;
|
||||
|
||||
if ((oflag & VLIBC_UNISTD_O_CREAT) != 0 || (oflag & VLIBC_UNISTD_O_TMPFILE) != 0)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, oflag);
|
||||
mode = va_arg(ap, unsigned int);
|
||||
va_end(ap);
|
||||
}
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_openat, VLIBC_UNISTD_AT_FDCWD, (long)path, oflag, (long)mode));
|
||||
}
|
||||
|
||||
int
|
||||
openat(int fd, const char *path, int oflag, ...)
|
||||
{
|
||||
unsigned mode = 0;
|
||||
|
||||
if ((oflag & VLIBC_UNISTD_O_CREAT) != 0 || (oflag & VLIBC_UNISTD_O_TMPFILE) != 0)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, oflag);
|
||||
mode = va_arg(ap, unsigned int);
|
||||
va_end(ap);
|
||||
}
|
||||
return syscall_ret(__syscall4(SYS_openat, fd, (long)path, oflag, (long)mode));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* pipe: plain POSIX pipe via SYS_pipe. Deliberately does NOT set
|
||||
* O_CLOEXEC — pipe2 is the flag-taking variant (level 2).
|
||||
*/
|
||||
int
|
||||
pipe(int fildes[2])
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_pipe, (long)fildes));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* pipe2: pipe with descriptor flags (O_CLOEXEC) applied atomically.
|
||||
* Linux-specific.
|
||||
*/
|
||||
int
|
||||
pipe2(int fildes[2], int flags)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_pipe2, (long)fildes, flags));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* pread/pwrite: positional I/O over SYS_pread64/SYS_pwrite64. On x86_64
|
||||
* the offset is a single 64-bit register argument (off_t == long), so
|
||||
* there is no lo/hi split. Positional I/O never touches the file position
|
||||
* — the kernel handles that.
|
||||
*/
|
||||
ssize_t
|
||||
pread(int fildes, void *buf, size_t nbyte, off_t offset)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_pread64, fildes, (long)buf, (long)nbyte, offset));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_pwrite64, fildes, (long)buf, (long)nbyte, offset));
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* read/write: unbuffered pass-through to the kernel. The raw result goes
|
||||
* through syscall_ret(), which returns the byte count on success and -1
|
||||
* with errno set on error; errno is untouched on success. No buffering, no
|
||||
* argument inspection.
|
||||
*/
|
||||
ssize_t
|
||||
read(int fildes, void *buf, size_t nbyte)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_read, fildes, (long)buf, (long)nbyte));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
write(int fildes, const void *buf, size_t nbyte)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_write, fildes, (long)buf, (long)nbyte));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sync: flush all filesystem caches to stable storage. The kernel's SYS_sync
|
||||
* always returns 0 and the POSIX interface is void, so the raw result is
|
||||
* deliberately not inspected (there is no failure to report).
|
||||
*/
|
||||
void
|
||||
sync(void)
|
||||
{
|
||||
__syscall0(SYS_sync);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* ftruncate: truncate an open descriptor; POSIX base form.
|
||||
*/
|
||||
int
|
||||
ftruncate(int fildes, off_t length)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_ftruncate, fildes, length));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* truncate: truncate by path name; XSI, so it is gated at level 2
|
||||
* (ftruncate is the POSIX base form).
|
||||
*/
|
||||
int
|
||||
truncate(const char *path, off_t length)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_truncate, (long)path, length));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef VLIBC_UNISTD_INTERNAL_H
|
||||
#define VLIBC_UNISTD_INTERNAL_H
|
||||
|
||||
/*
|
||||
* vlibc — internal constants for the unistd wrappers (temporary).
|
||||
*
|
||||
* include/fcntl.h is owned by todo 21; until it lands, the open/openat and
|
||||
* access wrappers need the O_* flag bits and AT_FDCWD locally. These names
|
||||
* carry the VLIBC_UNISTD_ prefix so they cannot collide with the real
|
||||
* constants todo 21 will publish. The values are kernel UAPI facts
|
||||
* (asm-generic/fcntl.h), transcribed, not invented:
|
||||
*
|
||||
* O_RDONLY 0x0, O_WRONLY 0x1, O_RDWR 0x2, O_CREAT 0x40, O_EXCL 0x80,
|
||||
* O_TRUNC 0x200, O_APPEND 0x400, O_CLOEXEC 0x80000,
|
||||
* O_TMPFILE 0x410000 (__O_TMPFILE 0x400000 | O_DIRECTORY 0x10000),
|
||||
* AT_FDCWD -100.
|
||||
*/
|
||||
|
||||
/* fcntl open-flag bits (kernel UAPI; see the header comment above). */
|
||||
#define VLIBC_UNISTD_O_RDONLY 0x0
|
||||
#define VLIBC_UNISTD_O_WRONLY 0x1
|
||||
#define VLIBC_UNISTD_O_RDWR 0x2
|
||||
#define VLIBC_UNISTD_O_CREAT 0x40
|
||||
#define VLIBC_UNISTD_O_EXCL 0x80
|
||||
#define VLIBC_UNISTD_O_TRUNC 0x200
|
||||
#define VLIBC_UNISTD_O_APPEND 0x400
|
||||
#define VLIBC_UNISTD_O_CLOEXEC 0x80000
|
||||
#define VLIBC_UNISTD_O_TMPFILE 0x410000
|
||||
|
||||
/* *at syscall base directory (kernel UAPI). */
|
||||
#define VLIBC_UNISTD_AT_FDCWD (-100)
|
||||
|
||||
#endif /* VLIBC_UNISTD_INTERNAL_H */
|
||||
Reference in New Issue
Block a user