From 761ee819568a52f1934ccf17bae20fe9816ebdec Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 17 Sep 2024 15:23:07 +0900 Subject: [PATCH] move readv/writev to the kernel currently, nuttx implements readv/writev on the top of read/write. while it might work for the simplest cases, it's broken by design. for example, it's impossible to make it work correctly for files which need to preserve data boundaries without allocating a single contiguous buffer. (udp socket, some character devices, etc) this change is a start of the migration to a better design. that is, implement read/write on the top of readv/writev. to avoid a single huge change, following things will NOT be done in this commit: * fix actual bugs caused by the original readv-based-on-read design. (cf. https://github.com/apache/nuttx/pull/12674) * adapt filesystems/drivers to actually benefit from the new interface. (except a few trivial examples) * eventually retire the old interface. * retire read/write syscalls. implement them in libc instead. * pread/pwrite/preadv/pwritev (except the introduction of struct uio, which is a preparation to back these variations with the new interface.) --- arch/arm/src/cxd56xx/cxd56_hostif.c | 4 +- drivers/bch/bchdev_driver.c | 4 +- drivers/i2c/i2c_driver.c | 4 +- drivers/i2c/i2c_slave_driver.c | 4 +- drivers/i3c/i3c_driver.c | 4 +- drivers/input/gt9xx.c | 4 +- drivers/lcd/ft80x.c | 4 +- drivers/lcd/pcf8574_lcd_backpack.c | 4 +- drivers/lcd/tda19988.c | 4 +- drivers/loop/loop.c | 27 ++- drivers/misc/dev_null.c | 42 ++-- drivers/misc/dev_zero.c | 57 +++-- drivers/pci/pci_uio_ivshmem.c | 4 +- drivers/pipes/fifo.c | 4 +- drivers/sensors/aht10.c | 4 +- drivers/sensors/hdc1008.c | 4 +- drivers/sensors/scd30.c | 4 +- drivers/sensors/scd41.c | 4 +- drivers/sensors/sgp30.c | 4 +- drivers/sensors/sht21.c | 4 +- drivers/sensors/sht3x.c | 4 +- drivers/sensors/sps30.c | 4 +- drivers/serial/pty.c | 4 +- drivers/serial/serial.c | 4 +- drivers/spi/spi_driver.c | 4 +- drivers/spi/spi_slave_driver.c | 4 +- drivers/timers/rtc.c | 4 +- drivers/video/v4l2_cap.c | 2 + drivers/video/v4l2_core.c | 2 + drivers/virtio/virtio-rng.c | 2 + drivers/virtio/virtio-rpmb.c | 2 + fs/binfs/fs_binfs.c | 2 + fs/cromfs/fs_cromfs.c | 2 + fs/fat/fs_fat32.c | 3 + fs/hostfs/hostfs.c | 2 + fs/littlefs/lfs_vfs.c | 2 + fs/mnemofs/mnemofs.c | 2 + fs/nfs/nfs_vfsops.c | 2 + fs/nxffs/nxffs_initialize.c | 2 + fs/procfs/fs_procfs.c | 2 + fs/romfs/fs_romfs.c | 2 + fs/rpmsgfs/rpmsgfs.c | 2 + fs/shm/shmfs.c | 2 + fs/smartfs/smartfs_smart.c | 2 + fs/spiffs/src/spiffs_vfs.c | 2 + fs/tmpfs/fs_tmpfs.c | 2 + fs/unionfs/fs_unionfs.c | 4 +- fs/userfs/fs_userfs.c | 2 + fs/v9fs/v9fs.c | 2 + fs/vfs/CMakeLists.txt | 1 + fs/vfs/Make.defs | 2 +- fs/vfs/fs_fstat.c | 4 +- fs/vfs/fs_open.c | 4 +- fs/vfs/fs_read.c | 280 ++++++++++++++++++++----- fs/vfs/fs_stat.c | 4 +- fs/vfs/fs_truncate.c | 2 +- fs/vfs/fs_uio.c | 66 ++++++ fs/vfs/fs_write.c | 309 +++++++++++++++++++++++----- fs/zipfs/zip_vfs.c | 2 + graphics/nxterm/nxterm_driver.c | 8 +- include/nuttx/fs/fs.h | 16 +- include/nuttx/fs/uio.h | 65 ++++++ include/sys/syscall_lookup.h | 2 + libs/libc/uio/CMakeLists.txt | 2 +- libs/libc/uio/Make.defs | 1 - libs/libc/uio/lib_readv.c | 125 ----------- libs/libc/uio/lib_writev.c | 124 ----------- syscall/syscall.csv | 2 + 68 files changed, 836 insertions(+), 452 deletions(-) create mode 100644 fs/vfs/fs_uio.c create mode 100644 include/nuttx/fs/uio.h delete mode 100644 libs/libc/uio/lib_readv.c delete mode 100644 libs/libc/uio/lib_writev.c diff --git a/arch/arm/src/cxd56xx/cxd56_hostif.c b/arch/arm/src/cxd56xx/cxd56_hostif.c index f37501fb52e..cb1756f60a6 100644 --- a/arch/arm/src/cxd56xx/cxd56_hostif.c +++ b/arch/arm/src/cxd56xx/cxd56_hostif.c @@ -147,7 +147,9 @@ static const struct file_operations g_hif_fops = hif_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - hif_poll /* poll */ + hif_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , hif_unlink /* unlink */ #endif diff --git a/drivers/bch/bchdev_driver.c b/drivers/bch/bchdev_driver.c index 6fbea4e8ec9..3c79652e609 100644 --- a/drivers/bch/bchdev_driver.c +++ b/drivers/bch/bchdev_driver.c @@ -80,7 +80,9 @@ const struct file_operations g_bch_fops = bch_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - bch_poll /* poll */ + bch_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , bch_unlink /* unlink */ #endif diff --git a/drivers/i2c/i2c_driver.c b/drivers/i2c/i2c_driver.c index b80981a49b8..58c5b1089b8 100644 --- a/drivers/i2c/i2c_driver.c +++ b/drivers/i2c/i2c_driver.c @@ -99,7 +99,9 @@ static const struct file_operations g_i2cdrvr_fops = i2cdrvr_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , i2cdrvr_unlink /* unlink */ #endif diff --git a/drivers/i2c/i2c_slave_driver.c b/drivers/i2c/i2c_slave_driver.c index f5531b7e7c6..c2fd05013ec 100644 --- a/drivers/i2c/i2c_slave_driver.c +++ b/drivers/i2c/i2c_slave_driver.c @@ -134,7 +134,9 @@ static const struct file_operations g_i2cslavefops = NULL, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - i2c_slave_poll /* poll */ + i2c_slave_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , i2c_slave_unlink /* unlink */ #endif diff --git a/drivers/i3c/i3c_driver.c b/drivers/i3c/i3c_driver.c index 05e013afd71..0bfc860982c 100644 --- a/drivers/i3c/i3c_driver.c +++ b/drivers/i3c/i3c_driver.c @@ -96,7 +96,9 @@ static const struct file_operations g_i3cdrvr_fops = i3cdrvr_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , i3cdrvr_unlink /* unlink */ #endif diff --git a/drivers/input/gt9xx.c b/drivers/input/gt9xx.c index 42423cd24f2..654cf0aab66 100644 --- a/drivers/input/gt9xx.c +++ b/drivers/input/gt9xx.c @@ -124,7 +124,9 @@ static const struct file_operations g_gt9xx_fileops = NULL, /* ioctl */ NULL, /* truncate */ NULL, /* mmap */ - gt9xx_poll /* poll */ + gt9xx_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL /* unlink */ #endif diff --git a/drivers/lcd/ft80x.c b/drivers/lcd/ft80x.c index b17e38cbff3..32f6913e884 100644 --- a/drivers/lcd/ft80x.c +++ b/drivers/lcd/ft80x.c @@ -136,7 +136,9 @@ static const struct file_operations g_ft80x_fops = ft80x_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , ft80x_unlink /* unlink */ #endif diff --git a/drivers/lcd/pcf8574_lcd_backpack.c b/drivers/lcd/pcf8574_lcd_backpack.c index b4ee2eaece7..62f31a18fab 100644 --- a/drivers/lcd/pcf8574_lcd_backpack.c +++ b/drivers/lcd/pcf8574_lcd_backpack.c @@ -119,7 +119,9 @@ static const struct file_operations g_pcf8574_lcd_fops = pcf8574_lcd_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - pcf8574_lcd_poll /* poll */ + pcf8574_lcd_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , pcf8574_lcd_unlink /* unlink */ #endif diff --git a/drivers/lcd/tda19988.c b/drivers/lcd/tda19988.c index 77d2c147cc7..dd3a023fe7d 100644 --- a/drivers/lcd/tda19988.c +++ b/drivers/lcd/tda19988.c @@ -173,7 +173,9 @@ static const struct file_operations g_tda19988_fops = tda19988_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - tda19988_poll /* poll */ + tda19988_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , tda19988_unlink /* unlink */ #endif diff --git a/drivers/loop/loop.c b/drivers/loop/loop.c index 5b8c068984b..5093f4df0c9 100644 --- a/drivers/loop/loop.c +++ b/drivers/loop/loop.c @@ -38,10 +38,10 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t loop_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static ssize_t loop_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); +static ssize_t loop_readv(FAR struct file *filep, + FAR const struct uio *uio); +static ssize_t loop_writev(FAR struct file *filep, + FAR const struct uio *uio); static int loop_ioctl(FAR struct file *filep, int cmd, unsigned long arg); @@ -53,10 +53,15 @@ static const struct file_operations g_loop_fops = { NULL, /* open */ NULL, /* close */ - loop_read, /* read */ - loop_write, /* write */ + NULL, /* read */ + NULL, /* write */ NULL, /* seek */ loop_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + NULL, /* poll */ + loop_readv, /* readv */ + loop_writev /* writev */ }; /**************************************************************************** @@ -67,8 +72,8 @@ static const struct file_operations g_loop_fops = * Name: loop_read ****************************************************************************/ -static ssize_t loop_read(FAR struct file *filep, FAR char *buffer, - size_t len) +static ssize_t loop_readv(FAR struct file *filep, + FAR const struct uio *uio) { return 0; /* Return EOF */ } @@ -77,10 +82,10 @@ static ssize_t loop_read(FAR struct file *filep, FAR char *buffer, * Name: loop_write ****************************************************************************/ -static ssize_t loop_write(FAR struct file *filep, FAR const char *buffer, - size_t len) +static ssize_t loop_writev(FAR struct file *filep, + FAR const struct uio *uio) { - return len; /* Say that everything was written */ + return uio_total_len(uio); /* Say that everything was written */ } /**************************************************************************** diff --git a/drivers/misc/dev_null.c b/drivers/misc/dev_null.c index d058d64d3e2..f622463beab 100644 --- a/drivers/misc/dev_null.c +++ b/drivers/misc/dev_null.c @@ -38,10 +38,10 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t devnull_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static ssize_t devnull_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); +static ssize_t devnull_readv(FAR struct file *filep, + FAR const struct uio *uio); +static ssize_t devnull_writev(FAR struct file *filep, + FAR const struct uio *uio); static int devnull_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); @@ -51,15 +51,17 @@ static int devnull_poll(FAR struct file *filep, FAR struct pollfd *fds, static const struct file_operations g_devnull_fops = { - NULL, /* open */ - NULL, /* close */ - devnull_read, /* read */ - devnull_write, /* write */ - NULL, /* seek */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* truncate */ - devnull_poll /* poll */ + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* writev */ + NULL, /* seek */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + devnull_poll, /* poll */ + devnull_readv, /* readv */ + devnull_writev /* writev */ }; /**************************************************************************** @@ -70,12 +72,11 @@ static const struct file_operations g_devnull_fops = * Name: devnull_read ****************************************************************************/ -static ssize_t devnull_read(FAR struct file *filep, FAR char *buffer, - size_t len) +static ssize_t devnull_readv(FAR struct file *filep, + FAR const struct uio *uio) { UNUSED(filep); - UNUSED(buffer); - UNUSED(len); + UNUSED(uio); return 0; /* Return EOF */ } @@ -84,13 +85,12 @@ static ssize_t devnull_read(FAR struct file *filep, FAR char *buffer, * Name: devnull_write ****************************************************************************/ -static ssize_t devnull_write(FAR struct file *filep, FAR const char *buffer, - size_t len) +static ssize_t devnull_writev(FAR struct file *filep, + FAR const struct uio *uio) { UNUSED(filep); - UNUSED(buffer); - return len; /* Say that everything was written */ + return uio_total_len(uio); /* Say that everything was written */ } /**************************************************************************** diff --git a/drivers/misc/dev_zero.c b/drivers/misc/dev_zero.c index 46d0f359838..efc0b23969f 100644 --- a/drivers/misc/dev_zero.c +++ b/drivers/misc/dev_zero.c @@ -38,10 +38,10 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t devzero_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static ssize_t devzero_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen); +static ssize_t devzero_readv(FAR struct file *filep, + FAR const struct uio *uio); +static ssize_t devzero_writev(FAR struct file *filep, + FAR const struct uio *uio); static int devzero_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); @@ -51,15 +51,17 @@ static int devzero_poll(FAR struct file *filep, FAR struct pollfd *fds, static const struct file_operations g_devzero_fops = { - NULL, /* open */ - NULL, /* close */ - devzero_read, /* read */ - devzero_write, /* write */ - NULL, /* seek */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* truncate */ - devzero_poll /* poll */ + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* seek */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + devzero_poll, /* poll */ + devzero_readv, /* readv */ + devzero_writev /* writev */ }; /**************************************************************************** @@ -70,26 +72,39 @@ static const struct file_operations g_devzero_fops = * Name: devzero_read ****************************************************************************/ -static ssize_t devzero_read(FAR struct file *filep, FAR char *buffer, - size_t len) +static ssize_t devzero_readv(FAR struct file *filep, + FAR const struct uio *uio) { + ssize_t total = uio_total_len(uio); + FAR const struct iovec *iov = uio->uio_iov; + int iovcnt = uio->uio_iovcnt; + int i; + UNUSED(filep); - memset(buffer, 0, len); - return len; + if (total < 0) + { + return total; + } + + for (i = 0; i < iovcnt; i++) + { + memset(iov[i].iov_base, 0, iov[i].iov_len); + } + + return total; } /**************************************************************************** * Name: devzero_write ****************************************************************************/ -static ssize_t devzero_write(FAR struct file *filep, FAR const char *buffer, - size_t len) +static ssize_t devzero_writev(FAR struct file *filep, + FAR const struct uio *uio) { UNUSED(filep); - UNUSED(buffer); - return len; + return uio_total_len(uio); } /**************************************************************************** diff --git a/drivers/pci/pci_uio_ivshmem.c b/drivers/pci/pci_uio_ivshmem.c index 54355f96d9a..fd1a87a5a78 100644 --- a/drivers/pci/pci_uio_ivshmem.c +++ b/drivers/pci/pci_uio_ivshmem.c @@ -120,7 +120,9 @@ static const struct file_operations g_uio_ivshmem_fops = NULL, /* ioctl */ uio_ivshmem_mmap, /* mmap */ NULL, /* truncate */ - uio_ivshmem_poll /* poll */ + uio_ivshmem_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL /* unlink */ #endif diff --git a/drivers/pipes/fifo.c b/drivers/pipes/fifo.c index 2ff13232e54..b8e296a6765 100644 --- a/drivers/pipes/fifo.c +++ b/drivers/pipes/fifo.c @@ -49,7 +49,9 @@ static const struct file_operations g_fifo_fops = pipecommon_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - pipecommon_poll /* poll */ + pipecommon_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , pipecommon_unlink /* unlink */ #endif diff --git a/drivers/sensors/aht10.c b/drivers/sensors/aht10.c index ccaa68c8c05..fde0e9a8447 100644 --- a/drivers/sensors/aht10.c +++ b/drivers/sensors/aht10.c @@ -118,7 +118,9 @@ static const struct file_operations g_aht10fops = aht10_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , aht10_unlink /* unlink */ #endif diff --git a/drivers/sensors/hdc1008.c b/drivers/sensors/hdc1008.c index 77c73d1eca9..920f30e8c05 100644 --- a/drivers/sensors/hdc1008.c +++ b/drivers/sensors/hdc1008.c @@ -163,7 +163,9 @@ static const struct file_operations g_hdc1008fops = hdc1008_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , hdc1008_unlink /* unlink */ #endif diff --git a/drivers/sensors/scd30.c b/drivers/sensors/scd30.c index 906bc1ac115..5cc5a8b5f57 100644 --- a/drivers/sensors/scd30.c +++ b/drivers/sensors/scd30.c @@ -184,7 +184,9 @@ static const struct file_operations g_scd30fops = scd30_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , scd30_unlink /* unlink */ #endif diff --git a/drivers/sensors/scd41.c b/drivers/sensors/scd41.c index 756c41a3cb6..08a03481c72 100644 --- a/drivers/sensors/scd41.c +++ b/drivers/sensors/scd41.c @@ -192,7 +192,9 @@ static const struct file_operations g_scd41fops = scd41_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , scd41_unlink /* unlink */ #endif diff --git a/drivers/sensors/sgp30.c b/drivers/sensors/sgp30.c index f82656c3a8e..b84697f1a56 100644 --- a/drivers/sensors/sgp30.c +++ b/drivers/sensors/sgp30.c @@ -161,7 +161,9 @@ static const struct file_operations g_sgp30fops = sgp30_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , sgp30_unlink /* unlink */ #endif diff --git a/drivers/sensors/sht21.c b/drivers/sensors/sht21.c index 487c90ed1d0..8502e9b9e18 100644 --- a/drivers/sensors/sht21.c +++ b/drivers/sensors/sht21.c @@ -133,7 +133,9 @@ static const struct file_operations g_sht21fops = sht21_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , sht21_unlink /* unlink */ #endif diff --git a/drivers/sensors/sht3x.c b/drivers/sensors/sht3x.c index 8fc52605f7d..5dbf4369943 100644 --- a/drivers/sensors/sht3x.c +++ b/drivers/sensors/sht3x.c @@ -172,7 +172,9 @@ static const struct file_operations g_sht3xfops = sht3x_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , sht3x_unlink /* unlink */ #endif diff --git a/drivers/sensors/sps30.c b/drivers/sensors/sps30.c index 7e2c6ba51ba..8d701a3ddcb 100644 --- a/drivers/sensors/sps30.c +++ b/drivers/sensors/sps30.c @@ -177,7 +177,9 @@ static const struct file_operations g_sps30fops = sps30_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , sps30_unlink /* unlink */ #endif diff --git a/drivers/serial/pty.c b/drivers/serial/pty.c index 92cbd06e5ac..0620979998d 100644 --- a/drivers/serial/pty.c +++ b/drivers/serial/pty.c @@ -136,7 +136,9 @@ static const struct file_operations g_pty_fops = pty_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - pty_poll /* poll */ + pty_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , pty_unlink /* unlink */ #endif diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index fc7dc47604b..2a7579f2124 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -145,7 +145,9 @@ static const struct file_operations g_serialops = uart_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - uart_poll /* poll */ + uart_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , uart_unlink /* unlink */ #endif diff --git a/drivers/spi/spi_driver.c b/drivers/spi/spi_driver.c index c5e49852f6f..0f812823023 100644 --- a/drivers/spi/spi_driver.c +++ b/drivers/spi/spi_driver.c @@ -99,7 +99,9 @@ static const struct file_operations g_spidrvr_fops = spidrvr_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , spidrvr_unlink /* unlink */ #endif diff --git a/drivers/spi/spi_slave_driver.c b/drivers/spi/spi_slave_driver.c index a5ef0a9f8ce..50fcec03a29 100644 --- a/drivers/spi/spi_slave_driver.c +++ b/drivers/spi/spi_slave_driver.c @@ -138,7 +138,9 @@ static const struct file_operations g_spislavefops = NULL, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - spi_slave_poll /* poll */ + spi_slave_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , spi_slave_unlink /* unlink */ #endif diff --git a/drivers/timers/rtc.c b/drivers/timers/rtc.c index 21db78a246f..ec3d87761c3 100644 --- a/drivers/timers/rtc.c +++ b/drivers/timers/rtc.c @@ -131,7 +131,9 @@ static const struct file_operations g_rtc_fops = rtc_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , rtc_unlink /* unlink */ #endif diff --git a/drivers/video/v4l2_cap.c b/drivers/video/v4l2_cap.c index 3c938c788e5..9f4beda84ab 100644 --- a/drivers/video/v4l2_cap.c +++ b/drivers/video/v4l2_cap.c @@ -351,6 +351,8 @@ static const struct file_operations g_capture_fops = capture_mmap, /* mmap */ NULL, /* truncate */ capture_poll, /* poll */ + NULL, /* readv */ + NULL, /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS capture_unlink, /* unlink */ #endif diff --git a/drivers/video/v4l2_core.c b/drivers/video/v4l2_core.c index 674308daaef..505383d6735 100644 --- a/drivers/video/v4l2_core.c +++ b/drivers/video/v4l2_core.c @@ -65,6 +65,8 @@ static const struct file_operations g_v4l2_fops = v4l2_mmap, /* mmap */ NULL, /* truncate */ v4l2_poll, /* poll */ + NULL, /* readv */ + NULL, /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS v4l2_unlink, /* unlink */ #endif diff --git a/drivers/virtio/virtio-rng.c b/drivers/virtio/virtio-rng.c index a3e4501e7be..0cacc86a420 100644 --- a/drivers/virtio/virtio-rng.c +++ b/drivers/virtio/virtio-rng.c @@ -91,6 +91,8 @@ static const struct file_operations g_virtio_rng_ops = NULL, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS NULL, /* unlink */ #endif diff --git a/drivers/virtio/virtio-rpmb.c b/drivers/virtio/virtio-rpmb.c index f855ad063cf..a7c2d5a0cfc 100644 --- a/drivers/virtio/virtio-rpmb.c +++ b/drivers/virtio/virtio-rpmb.c @@ -90,6 +90,8 @@ static const struct file_operations g_virtio_rpmb_ops = NULL, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS NULL, /* unlink */ #endif diff --git a/fs/binfs/fs_binfs.c b/fs/binfs/fs_binfs.c index 694ecbfbe61..0bcdef535f9 100644 --- a/fs/binfs/fs_binfs.c +++ b/fs/binfs/fs_binfs.c @@ -112,6 +112,8 @@ const struct mountpt_operations g_binfs_operations = NULL, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync */ binfs_dup, /* dup */ diff --git a/fs/cromfs/fs_cromfs.c b/fs/cromfs/fs_cromfs.c index 769639ebbe5..79cfafc01a4 100644 --- a/fs/cromfs/fs_cromfs.c +++ b/fs/cromfs/fs_cromfs.c @@ -192,6 +192,8 @@ const struct mountpt_operations g_cromfs_operations = NULL, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync */ cromfs_dup, /* dup */ diff --git a/fs/fat/fs_fat32.c b/fs/fat/fs_fat32.c index 40e913ac4c2..301429bbb40 100644 --- a/fs/fat/fs_fat32.c +++ b/fs/fat/fs_fat32.c @@ -132,6 +132,9 @@ const struct mountpt_operations g_fat_operations = NULL, /* mmap */ fat_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ + fat_sync, /* sync */ fat_dup, /* dup */ fat_fstat, /* fstat */ diff --git a/fs/hostfs/hostfs.c b/fs/hostfs/hostfs.c index 72f6cb2f9d4..1f34a034f80 100644 --- a/fs/hostfs/hostfs.c +++ b/fs/hostfs/hostfs.c @@ -148,6 +148,8 @@ const struct mountpt_operations g_hostfs_operations = NULL, /* mmap */ hostfs_ftruncate, /* ftruncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ hostfs_sync, /* sync */ hostfs_dup, /* dup */ diff --git a/fs/littlefs/lfs_vfs.c b/fs/littlefs/lfs_vfs.c index 249b945e7a9..167ed093477 100644 --- a/fs/littlefs/lfs_vfs.c +++ b/fs/littlefs/lfs_vfs.c @@ -170,6 +170,8 @@ const struct mountpt_operations g_littlefs_operations = NULL, /* mmap */ littlefs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ littlefs_sync, /* sync */ littlefs_dup, /* dup */ diff --git a/fs/mnemofs/mnemofs.c b/fs/mnemofs/mnemofs.c index 219abdabcae..d270242703f 100644 --- a/fs/mnemofs/mnemofs.c +++ b/fs/mnemofs/mnemofs.c @@ -188,6 +188,8 @@ const struct mountpt_operations g_mnemofs_operations = NULL, /* mmap */ mnemofs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ mnemofs_sync, /* sync */ mnemofs_dup, /* dup */ diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c index 3a32bc03d2d..fb69a303a7b 100644 --- a/fs/nfs/nfs_vfsops.c +++ b/fs/nfs/nfs_vfsops.c @@ -203,6 +203,8 @@ const struct mountpt_operations g_nfs_operations = NULL, /* mmap */ nfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ nfs_sync, /* sync */ nfs_dup, /* dup */ diff --git a/fs/nxffs/nxffs_initialize.c b/fs/nxffs/nxffs_initialize.c index 3e093c2f65b..22c94049100 100644 --- a/fs/nxffs/nxffs_initialize.c +++ b/fs/nxffs/nxffs_initialize.c @@ -62,6 +62,8 @@ const struct mountpt_operations g_nxffs_operations = NULL, /* truncate */ #endif NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync -- No buffered data */ nxffs_dup, /* dup */ diff --git a/fs/procfs/fs_procfs.c b/fs/procfs/fs_procfs.c index f70b8eeaf0e..d12c7acf6bd 100644 --- a/fs/procfs/fs_procfs.c +++ b/fs/procfs/fs_procfs.c @@ -287,6 +287,8 @@ const struct mountpt_operations g_procfs_operations = NULL, /* mmap */ NULL, /* truncate */ procfs_poll, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync */ procfs_dup, /* dup */ diff --git a/fs/romfs/fs_romfs.c b/fs/romfs/fs_romfs.c index 3bf433435fc..db0d03e5d8b 100644 --- a/fs/romfs/fs_romfs.c +++ b/fs/romfs/fs_romfs.c @@ -125,6 +125,8 @@ const struct mountpt_operations g_romfs_operations = romfs_mmap, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync */ romfs_dup, /* dup */ diff --git a/fs/rpmsgfs/rpmsgfs.c b/fs/rpmsgfs/rpmsgfs.c index edf9cf687b3..efea624917f 100644 --- a/fs/rpmsgfs/rpmsgfs.c +++ b/fs/rpmsgfs/rpmsgfs.c @@ -165,6 +165,8 @@ const struct mountpt_operations g_rpmsgfs_operations = NULL, /* mmap */ rpmsgfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ rpmsgfs_sync, /* sync */ rpmsgfs_dup, /* dup */ diff --git a/fs/shm/shmfs.c b/fs/shm/shmfs.c index d02beec7211..116a24caf28 100644 --- a/fs/shm/shmfs.c +++ b/fs/shm/shmfs.c @@ -74,6 +74,8 @@ const struct file_operations g_shmfs_operations = shmfs_mmap, /* mmap */ shmfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS shmfs_unlink /* unlink */ #endif diff --git a/fs/smartfs/smartfs_smart.c b/fs/smartfs/smartfs_smart.c index bc4e6477cdf..3aa84f627f6 100644 --- a/fs/smartfs/smartfs_smart.c +++ b/fs/smartfs/smartfs_smart.c @@ -145,6 +145,8 @@ const struct mountpt_operations g_smartfs_operations = NULL, /* mmap */ smartfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ smartfs_sync, /* sync */ smartfs_dup, /* dup */ diff --git a/fs/spiffs/src/spiffs_vfs.c b/fs/spiffs/src/spiffs_vfs.c index 2160a3b63be..324162af593 100644 --- a/fs/spiffs/src/spiffs_vfs.c +++ b/fs/spiffs/src/spiffs_vfs.c @@ -145,6 +145,8 @@ const struct mountpt_operations g_spiffs_operations = NULL, /* mmap */ spiffs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ spiffs_sync, /* sync */ spiffs_dup, /* dup */ diff --git a/fs/tmpfs/fs_tmpfs.c b/fs/tmpfs/fs_tmpfs.c index 152272fd945..decc697bcd0 100644 --- a/fs/tmpfs/fs_tmpfs.c +++ b/fs/tmpfs/fs_tmpfs.c @@ -187,6 +187,8 @@ const struct mountpt_operations g_tmpfs_operations = tmpfs_mmap, /* mmap */ tmpfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ tmpfs_sync, /* sync */ tmpfs_dup, /* dup */ diff --git a/fs/unionfs/fs_unionfs.c b/fs/unionfs/fs_unionfs.c index ff29050e462..1ee79d40cb9 100644 --- a/fs/unionfs/fs_unionfs.c +++ b/fs/unionfs/fs_unionfs.c @@ -230,7 +230,9 @@ const struct mountpt_operations g_unionfs_operations = unionfs_ioctl, /* ioctl */ NULL, /* mmap */ unionfs_truncate, /* truncate */ - NULL, /* pool */ + NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ unionfs_sync, /* sync */ unionfs_dup, /* dup */ diff --git a/fs/userfs/fs_userfs.c b/fs/userfs/fs_userfs.c index 893855cb5bf..fc2202bdef8 100644 --- a/fs/userfs/fs_userfs.c +++ b/fs/userfs/fs_userfs.c @@ -165,6 +165,8 @@ const struct mountpt_operations g_userfs_operations = NULL, /* mmap */ userfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ userfs_sync, /* sync */ userfs_dup, /* dup */ diff --git a/fs/v9fs/v9fs.c b/fs/v9fs/v9fs.c index 0d1c1f4d833..8823423aa09 100644 --- a/fs/v9fs/v9fs.c +++ b/fs/v9fs/v9fs.c @@ -126,6 +126,8 @@ const struct mountpt_operations g_v9fs_operations = NULL, /* mmap */ v9fs_vfs_truncate, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ v9fs_vfs_sync, /* sync */ v9fs_vfs_dup, /* dup */ diff --git a/fs/vfs/CMakeLists.txt b/fs/vfs/CMakeLists.txt index 36a790a3ee6..99af578b770 100644 --- a/fs/vfs/CMakeLists.txt +++ b/fs/vfs/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRCS fs_stat.c fs_sendfile.c fs_statfs.c + fs_uio.c fs_unlink.c fs_write.c fs_dir.c diff --git a/fs/vfs/Make.defs b/fs/vfs/Make.defs index 1e8c5b8d471..aaf1aa224f5 100644 --- a/fs/vfs/Make.defs +++ b/fs/vfs/Make.defs @@ -24,7 +24,7 @@ CSRCS += fs_chstat.c fs_close.c fs_dup.c fs_dup2.c fs_fcntl.c fs_epoll.c CSRCS += fs_fchstat.c fs_fstat.c fs_fstatfs.c fs_ioctl.c fs_lseek.c CSRCS += fs_mkdir.c fs_open.c fs_poll.c fs_pread.c fs_pwrite.c fs_read.c CSRCS += fs_rename.c fs_rmdir.c fs_select.c fs_sendfile.c fs_stat.c -CSRCS += fs_statfs.c fs_unlink.c fs_write.c fs_dir.c fs_fsync.c +CSRCS += fs_statfs.c fs_uio.c fs_unlink.c fs_write.c fs_dir.c fs_fsync.c CSRCS += fs_syncfs.c fs_truncate.c # Certain interfaces are not available if there is no mountpoint support diff --git a/fs/vfs/fs_fstat.c b/fs/vfs/fs_fstat.c index a3cb38df7b9..8e2099bedff 100644 --- a/fs/vfs/fs_fstat.c +++ b/fs/vfs/fs_fstat.c @@ -109,12 +109,12 @@ static int proxy_fstat(FAR struct file *filep, FAR struct inode *inode, { memset(buf, 0, sizeof(struct stat)); buf->st_mode = S_IFBLK; - if (inode->u.i_ops->read) + if (inode->u.i_ops->readv || inode->u.i_ops->read) { buf->st_mode |= S_IROTH | S_IRGRP | S_IRUSR; } - if (inode->u.i_ops->write) + if (inode->u.i_ops->writev || inode->u.i_ops->read) { buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR; } diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c index a8f1f469104..5dee0ee5021 100644 --- a/fs/vfs/fs_open.c +++ b/fs/vfs/fs_open.c @@ -78,8 +78,8 @@ static int inode_checkflags(FAR struct inode *inode, int oflags) return -ENXIO; } - if (((oflags & O_RDOK) != 0 && !ops->read && !ops->ioctl) || - ((oflags & O_WROK) != 0 && !ops->write && !ops->ioctl)) + if (((oflags & O_RDOK) != 0 && !ops->readv && !ops->read && !ops->ioctl) || + ((oflags & O_WROK) != 0 && !ops->writev && !ops->write && !ops->ioctl)) { return -EACCES; } diff --git a/fs/vfs/fs_read.c b/fs/vfs/fs_read.c index 08acce27dec..f6b40a0f6fd 100644 --- a/fs/vfs/fs_read.c +++ b/fs/vfs/fs_read.c @@ -36,10 +36,152 @@ #include "notify/notify.h" #include "inode/inode.h" +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_readv_compat + * + * Description: + * Emulate readv using file_operation::read. + * + ****************************************************************************/ + +static ssize_t file_readv_compat(FAR struct file *filep, + FAR const struct uio *uio) +{ + FAR const struct iovec *iov = uio->uio_iov; + int iovcnt = uio->uio_iovcnt; + FAR struct inode *inode = filep->f_inode; + ssize_t ntotal; + ssize_t nread; + size_t remaining; + FAR uint8_t *buffer; + int i; + + DEBUGASSERT(inode->u.i_ops->read != NULL); + + /* Process each entry in the struct iovec array */ + + for (i = 0, ntotal = 0; i < iovcnt; i++) + { + /* Ignore zero-length reads */ + + if (iov[i].iov_len > 0) + { + buffer = iov[i].iov_base; + remaining = iov[i].iov_len; + + /* Read repeatedly as necessary to fill buffer */ + + do + { + nread = inode->u.i_ops->read(filep, (void *)buffer, + remaining); + + /* Check for a read error */ + + if (nread < 0) + { + return ntotal ? ntotal : nread; + } + + /* Check for an end-of-file condition */ + + else if (nread == 0) + { + return ntotal; + } + + /* Update pointers and counts in order to handle partial + * buffer reads. + */ + + buffer += nread; + remaining -= nread; + ntotal += nread; + } + while (remaining > 0); + } + } + + return ntotal; +} + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: file_readv + * + * Description: + * file_readv() is an internal OS interface. It is functionally similar to + * the standard readv() interface except: + * + * - It does not modify the errno variable, + * - It is not a cancellation point, + * - It accepts a file structure instance instead of file descriptor. + * + * Input Parameters: + * filep - File structure instance + * uio - User buffer information + * + * Returned Value: + * The positive non-zero number of bytes read on success, 0 on if an + * end-of-file condition, or a negated errno value on any failure. + * + ****************************************************************************/ + +ssize_t file_readv(FAR struct file *filep, FAR const struct uio *uio) +{ + FAR struct inode *inode; + ssize_t ret = -EBADF; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + /* Was this file opened for read access? */ + + if ((filep->f_oflags & O_RDOK) == 0) + { + /* No.. File is not read-able */ + + ret = -EACCES; + } + + /* Is a driver or mountpoint registered? If so, does it support the read + * method? + * If yes, then let it perform the read. NOTE that for the case of the + * mountpoint, we depend on the read methods being identical in + * signature and position in the operations vtable. + */ + + else if (inode != NULL && inode->u.i_ops) + { + if (inode->u.i_ops->readv) + { + ret = inode->u.i_ops->readv(filep, uio); + } + else if (inode->u.i_ops->read) + { + ret = file_readv_compat(filep, uio); + } + } + + /* Return the number of bytes read (or possibly an error code) */ + +#ifdef CONFIG_FS_NOTIFY + if (ret > 0) + { + notify_read(filep); + } +#endif + + return ret; +} + /**************************************************************************** * Name: file_read * @@ -64,46 +206,59 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes) { - FAR struct inode *inode; - ssize_t ret = -EBADF; + struct iovec iov; + struct uio uio; - DEBUGASSERT(filep); - inode = filep->f_inode; + iov.iov_base = buf; + iov.iov_len = nbytes; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + return file_readv(filep, &uio); +} - /* Was this file opened for read access? */ +/**************************************************************************** + * Name: nx_readv + * + * Description: + * nx_readv() is an internal OS interface. It is functionally similar to + * the standard readv() interface except: + * + * - It does not modify the errno variable, and + * - It is not a cancellation point. + * + * Input Parameters: + * fd - File descriptor to read from + * iov - User-provided iovec to save the data + * iovcnt - The number of iovec + * + * Returned Value: + * The positive non-zero number of bytes read on success, 0 on if an + * end-of-file condition, or a negated errno value on any failure. + * + ****************************************************************************/ - if ((filep->f_oflags & O_RDOK) == 0) - { - /* No.. File is not read-able */ +ssize_t nx_readv(int fd, FAR const struct iovec *iov, int iovcnt) +{ + struct uio uio; + FAR struct file *filep; + ssize_t ret; - ret = -EACCES; - } - - /* Is a driver or mountpoint registered? If so, does it support the read - * method? + /* First, get the file structure. Note that on failure, + * fs_getfilep() will return the errno. */ - else if (inode != NULL && inode->u.i_ops && inode->u.i_ops->read) + ret = (ssize_t)fs_getfilep(fd, &filep); + if (ret < 0) { - /* Yes.. then let it perform the read. NOTE that for the case of the - * mountpoint, we depend on the read methods being identical in - * signature and position in the operations vtable. - */ - - ret = inode->u.i_ops->read(filep, - (FAR char *)buf, - (size_t)nbytes); + return ret; } - /* Return the number of bytes read (or possibly an error code) */ - -#ifdef CONFIG_FS_NOTIFY - if (ret > 0) - { - notify_read(filep); - } -#endif + /* Then let file_read do all of the work. */ + uio.uio_iov = iov; + uio.uio_iovcnt = iovcnt; + ret = file_readv(filep, &uio); + fs_putfilep(filep); return ret; } @@ -130,23 +285,48 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes) ssize_t nx_read(int fd, FAR void *buf, size_t nbytes) { - FAR struct file *filep; + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = nbytes; + return nx_readv(fd, &iov, 1); +} + +/**************************************************************************** + * Name: readv + * + * Description: + * The standard, POSIX read interface. + * + * Input Parameters: + * fd - File descriptor to read from + * iov - User-provided iovec to save the data + * iovcnt - The number of iovec + * + * Returned Value: + * The positive non-zero number of bytes read on success, 0 on if an + * end-of-file condition, or -1 on failure with errno set appropriately. + * + ****************************************************************************/ + +ssize_t readv(int fd, FAR const struct iovec *iov, int iovcnt) +{ ssize_t ret; - /* First, get the file structure. Note that on failure, - * fs_getfilep() will return the errno. - */ + /* readv() is a cancellation point */ - ret = (ssize_t)fs_getfilep(fd, &filep); + enter_cancellation_point(); + + /* Let nx_readv() do the real work */ + + ret = nx_readv(fd, iov, iovcnt); if (ret < 0) { - return ret; + set_errno(-ret); + ret = ERROR; } - /* Then let file_read do all of the work. */ - - ret = file_read(filep, buf, nbytes); - fs_putfilep(filep); + leave_cancellation_point(); return ret; } @@ -169,21 +349,9 @@ ssize_t nx_read(int fd, FAR void *buf, size_t nbytes) ssize_t read(int fd, FAR void *buf, size_t nbytes) { - ssize_t ret; + struct iovec iov; - /* read() is a cancellation point */ - - enter_cancellation_point(); - - /* Let nx_read() do the real work */ - - ret = nx_read(fd, buf, nbytes); - if (ret < 0) - { - set_errno(-ret); - ret = ERROR; - } - - leave_cancellation_point(); - return ret; + iov.iov_base = buf; + iov.iov_len = nbytes; + return readv(fd, &iov, 1); } diff --git a/fs/vfs/fs_stat.c b/fs/vfs/fs_stat.c index b647b30b717..81eaa74e1c9 100644 --- a/fs/vfs/fs_stat.c +++ b/fs/vfs/fs_stat.c @@ -379,12 +379,12 @@ int inode_stat(FAR struct inode *inode, FAR struct stat *buf, int resolve) * and write methods. */ - if (inode->u.i_ops->read) + if (inode->u.i_ops->readv || inode->u.i_ops->read) { buf->st_mode = S_IROTH | S_IRGRP | S_IRUSR; } - if (inode->u.i_ops->write) + if (inode->u.i_ops->writev || inode->u.i_ops->write) { buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR; } diff --git a/fs/vfs/fs_truncate.c b/fs/vfs/fs_truncate.c index d6dc7dfb6a4..681c47d61ce 100644 --- a/fs/vfs/fs_truncate.c +++ b/fs/vfs/fs_truncate.c @@ -82,7 +82,7 @@ int file_truncate(FAR struct file *filep, off_t length) * possible not the only indicator -- sufficient, but not necessary") */ - if (inode->u.i_ops->write == NULL) + if (inode->u.i_ops->writev == NULL && inode->u.i_ops->write == NULL) { fwarn("WARNING: File system is read-only\n"); return -EROFS; diff --git a/fs/vfs/fs_uio.c b/fs/vfs/fs_uio.c new file mode 100644 index 00000000000..455d0219610 --- /dev/null +++ b/fs/vfs/fs_uio.c @@ -0,0 +1,66 @@ +/**************************************************************************** + * fs/vfs/fs_uio.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: uio_total_len + * + * Description: + * Return the total length of data in bytes. + * Or -EOVERFLOW. + * + ****************************************************************************/ + +ssize_t uio_total_len(FAR const struct uio *uio) +{ + const struct iovec *iov = uio->uio_iov; + int iovcnt = uio->uio_iovcnt; + size_t len = 0; + int i; + + for (i = 0; i < iovcnt; i++) + { + if (SSIZE_MAX - len < iov[i].iov_len) + { + return -EOVERFLOW; + } + + len += iov[i].iov_len; + } + + return len; +} diff --git a/fs/vfs/fs_write.c b/fs/vfs/fs_write.c index 6849763a676..41ce33cf0f3 100644 --- a/fs/vfs/fs_write.c +++ b/fs/vfs/fs_write.c @@ -36,10 +36,139 @@ #include "notify/notify.h" #include "inode/inode.h" +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_writev_compat + * + * Description: + * Emulate writev using file_operation::write. + * + ****************************************************************************/ + +static ssize_t file_writev_compat(FAR struct file *filep, + FAR const struct uio *uio) +{ + FAR const struct iovec *iov = uio->uio_iov; + int iovcnt = uio->uio_iovcnt; + FAR struct inode *inode = filep->f_inode; + ssize_t ntotal; + ssize_t nwritten; + size_t remaining; + FAR uint8_t *buffer; + int i; + + DEBUGASSERT(inode->u.i_ops->write != NULL); + + /* Process each entry in the struct iovec array */ + + for (i = 0, ntotal = 0; i < iovcnt; i++) + { + /* Ignore zero-length writes */ + + if (iov[i].iov_len > 0) + { + buffer = iov[i].iov_base; + remaining = iov[i].iov_len; + + /* Write repeatedly as necessary to write the entire buffer */ + + do + { + nwritten = inode->u.i_ops->write(filep, (void *)buffer, + remaining); + + /* Check for a write error */ + + if (nwritten < 0) + { + return ntotal ? ntotal : nwritten; + } + + /* Update pointers and counts in order to handle partial + * buffer writes. + */ + + buffer += nwritten; + remaining -= nwritten; + ntotal += nwritten; + } + while (remaining > 0); + } + } + + return ntotal; +} + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: file_writev + * + * Description: + * Equivalent to the standard writev() function except that is accepts a + * struct file instance instead of a file descriptor. It is functionally + * equivalent to writev() except that in addition to the differences in + * input parameters: + * + * - It does not modify the errno variable, + * - It is not a cancellation point, and + * + * Input Parameters: + * filep - Instance of struct file to use with the write + * uio - User buffer information + * + * Returned Value: + * On success, the number of bytes written are returned (zero indicates + * nothing was written). On any failure, a negated errno value is returned + * (see comments withwrite() for a description of the appropriate errno + * values). + * + ****************************************************************************/ + +ssize_t file_writev(FAR struct file *filep, FAR const struct uio *uio) +{ + FAR struct inode *inode; + ssize_t ret = -EBADF; + + /* Was this file opened for write access? */ + + if ((filep->f_oflags & O_WROK) == 0) + { + return -EACCES; + } + + /* Is a driver registered? Does it support the write method? + * If yes, then let the driver perform the write. + */ + + inode = filep->f_inode; + if (inode != NULL && inode->u.i_ops) + { + if (inode->u.i_ops->writev) + { + ret = inode->u.i_ops->writev(filep, uio); + } + else if (inode->u.i_ops->write) + { + ret = file_writev_compat(filep, uio); + } + } + +#ifdef CONFIG_FS_NOTIFY + if (ret > 0) + { + notify_write(filep); + } +#endif + + return ret; +} + /**************************************************************************** * Name: file_write * @@ -68,34 +197,64 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf, size_t nbytes) { - FAR struct inode *inode; + struct iovec iov; + struct uio uio; + + iov.iov_base = (FAR void *)buf; + iov.iov_len = nbytes; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + return file_writev(filep, &uio); +} + +/**************************************************************************** + * Name: nx_writev + * + * Description: + * nx_writev() writes up to nytes bytes to the file referenced by the file + * descriptor fd from the buffer starting at buf. nx_writev() is an + * internal OS function. It is functionally equivalent to writev() except + * that: + * + * - It does not modify the errno variable, and + * - It is not a cancellation point. + * + * Input Parameters: + * fd - file descriptor to write to + * iov - Data to write + * iovcnt - The number of vectors + * + * Returned Value: + * On success, the number of bytes written are returned (zero indicates + * nothing was written). On any failure, a negated errno value is returned + * (see comments withwrite() for a description of the appropriate errno + * values). + * + ****************************************************************************/ + +ssize_t nx_writev(int fd, FAR const struct iovec *iov, int iovcnt) +{ + struct uio uio; + FAR struct file *filep; ssize_t ret; - /* Was this file opened for write access? */ + /* First, get the file structure. + * Note that fs_getfilep() will return the errno on failure. + */ - if ((filep->f_oflags & O_WROK) == 0) + ret = (ssize_t)fs_getfilep(fd, &filep); + if (ret >= 0) { - return -EACCES; + /* Perform the write operation using the file descriptor as an + * index. Note that file_write() will return the errno on failure. + */ + + uio.uio_iov = iov; + uio.uio_iovcnt = iovcnt; + ret = file_writev(filep, &uio); + fs_putfilep(filep); } - /* Is a driver registered? Does it support the write method? */ - - inode = filep->f_inode; - if (!inode || !inode->u.i_ops || !inode->u.i_ops->write) - { - return -EBADF; - } - - /* Yes, then let the driver perform the write */ - - ret = inode->u.i_ops->write(filep, buf, nbytes); -#ifdef CONFIG_FS_NOTIFY - if (ret > 0) - { - notify_write(filep); - } -#endif - return ret; } @@ -126,29 +285,79 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf, ssize_t nx_write(int fd, FAR const void *buf, size_t nbytes) { - FAR struct file *filep; + struct iovec iov; + + iov.iov_base = (void *)buf; + iov.iov_len = nbytes; + return nx_writev(fd, &iov, 1); +} + +/**************************************************************************** + * Name: writev + * + * Description: + * writev() writes up to nytes bytes to the file referenced by the file + * descriptor fd from the buffer starting at buf. + * + * Input Parameters: + * fd - file descriptor to write to + * iov - Data to write + * iovcnt - The number of vectors + * + * Returned Value: + * On success, the number of bytes written are returned (zero indicates + * nothing was written). On error, -1 is returned, and errno is set appro- + * priately: + * + * EAGAIN + * Non-blocking I/O has been selected using O_NONBLOCK and the write + * would block. + * EBADF + * fd is not a valid file descriptor or is not open for writing. + * EFAULT + * buf is outside your accessible address space. + * EFBIG + * An attempt was made to write a file that exceeds the implementation + * defined maximum file size or the process's file size limit, or + * to write at a position past the maximum allowed offset. + * EINTR + * The call was interrupted by a signal before any data was written. + * EINVAL + * fd is attached to an object which is unsuitable for writing; or + * the file was opened with the O_DIRECT flag, and either the address + * specified in buf, the value specified in count, or the current + * file offset is not suitably aligned. + * EIO + * A low-level I/O error occurred while modifying the inode. + * ENOSPC + * The device containing the file referred to by fd has no room for + * the data. + * EPIPE + * fd is connected to a pipe or socket whose reading end is closed. + * When this happens the writing process will also receive a SIGPIPE + * signal. (Thus, the write return value is seen only if the program + * catches, blocks or ignores this signal.) + * + ****************************************************************************/ + +ssize_t writev(int fd, FAR const struct iovec *iov, int iovcnt) +{ ssize_t ret; - if (buf == NULL) + /* write() is a cancellation point */ + + enter_cancellation_point(); + + /* Let nx_write() do all of the work */ + + ret = nx_writev(fd, iov, iovcnt); + if (ret < 0) { - return -EINVAL; - } - - /* First, get the file structure. - * Note that fs_getfilep() will return the errno on failure. - */ - - ret = (ssize_t)fs_getfilep(fd, &filep); - if (ret >= 0) - { - /* Perform the write operation using the file descriptor as an - * index. Note that file_write() will return the errno on failure. - */ - - ret = file_write(filep, buf, nbytes); - fs_putfilep(filep); + set_errno(-ret); + ret = ERROR; } + leave_cancellation_point(); return ret; } @@ -202,21 +411,9 @@ ssize_t nx_write(int fd, FAR const void *buf, size_t nbytes) ssize_t write(int fd, FAR const void *buf, size_t nbytes) { - ssize_t ret; + struct iovec iov; - /* write() is a cancellation point */ - - enter_cancellation_point(); - - /* Let nx_write() do all of the work */ - - ret = nx_write(fd, buf, nbytes); - if (ret < 0) - { - set_errno(-ret); - ret = ERROR; - } - - leave_cancellation_point(); - return ret; + iov.iov_base = (void *)buf; + iov.iov_len = nbytes; + return writev(fd, &iov, 1); } diff --git a/fs/zipfs/zip_vfs.c b/fs/zipfs/zip_vfs.c index 50897820e47..d59d6e94a71 100644 --- a/fs/zipfs/zip_vfs.c +++ b/fs/zipfs/zip_vfs.c @@ -137,6 +137,8 @@ const struct mountpt_operations g_zipfs_operations = NULL, /* mmap */ NULL, /* truncate */ NULL, /* poll */ + NULL, /* readv */ + NULL, /* writev */ NULL, /* sync */ zipfs_dup, /* dup */ diff --git a/graphics/nxterm/nxterm_driver.c b/graphics/nxterm/nxterm_driver.c index 4f350139371..24c1bc2f8b0 100644 --- a/graphics/nxterm/nxterm_driver.c +++ b/graphics/nxterm/nxterm_driver.c @@ -70,7 +70,9 @@ const struct file_operations g_nxterm_drvrops = nxterm_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - nxterm_poll /* poll */ + nxterm_poll, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , nxterm_unlink /* unlink */ #endif @@ -88,7 +90,9 @@ const struct file_operations g_nxterm_drvrops = nxterm_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + NULL, /* poll */ + NULL, /* readv */ + NULL /* writev */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , nxterm_unlink /* unlink */ #endif diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index cc9591fd054..f338d4e5850 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -28,9 +28,13 @@ ****************************************************************************/ #include -#include +#include +#include + +#include #include + #include #include #include @@ -183,6 +187,7 @@ struct statfs; struct pollfd; struct mtd_dev_s; struct tcb_s; +struct uio; /* The internal representation of type DIR is just a container for an inode * reference, and the path of directory. @@ -233,6 +238,8 @@ struct file_operations CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds, bool setup); + CODE ssize_t (*readv)(FAR struct file *filep, FAR const struct uio *uio); + CODE ssize_t (*writev)(FAR struct file *filep, FAR const struct uio *uio); /* The two structures need not be common after this point */ @@ -329,6 +336,9 @@ struct mountpt_operations CODE int (*truncate)(FAR struct file *filep, off_t length); CODE int (*poll)(FAR struct file *filep, FAR struct pollfd *fds, bool setup); + CODE ssize_t (*readv)(FAR struct file *filep, FAR const struct uio *uio); + CODE ssize_t (*writev)(FAR struct file *filep, FAR const struct uio *uio); + /* The two structures need not be common after this point. The following * are extended methods needed to deal with the unique needs of mounted * file systems. @@ -1416,6 +1426,7 @@ int close_mtddriver(FAR struct inode *pinode); ****************************************************************************/ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes); +ssize_t file_readv(FAR struct file *filep, FAR const struct uio *uio); /**************************************************************************** * Name: nx_read @@ -1439,6 +1450,7 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes); ****************************************************************************/ ssize_t nx_read(int fd, FAR void *buf, size_t nbytes); +ssize_t nx_readv(int fd, FAR const struct iovec *iov, int iovcnt); /**************************************************************************** * Name: file_write @@ -1468,6 +1480,7 @@ ssize_t nx_read(int fd, FAR void *buf, size_t nbytes); ssize_t file_write(FAR struct file *filep, FAR const void *buf, size_t nbytes); +ssize_t file_writev(FAR struct file *filep, FAR const struct uio *uio); /**************************************************************************** * Name: nx_write @@ -1495,6 +1508,7 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf, ****************************************************************************/ ssize_t nx_write(int fd, FAR const void *buf, size_t nbytes); +ssize_t nx_writev(int fd, FAR const struct iovec *iov, int iovcnt); /**************************************************************************** * Name: file_pread diff --git a/include/nuttx/fs/uio.h b/include/nuttx/fs/uio.h new file mode 100644 index 00000000000..e864745a18c --- /dev/null +++ b/include/nuttx/fs/uio.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * include/nuttx/fs/uio.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_FS_UIO_H +#define __INCLUDE_NUTTX_FS_UIO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Type Definitions + ****************************************************************************/ + +/* The structure to describe an user I/O operation. + * + * At this point, this is a bare minimum for readv/writev. + * In the future, we might extend this for other things like + * the file offset for pread/pwrite. + * + * This structure was inspired by BSDs. + * (Thus it doesn't have the NuttX-style "_s" suffix.) + */ + +struct uio +{ + FAR const struct iovec *uio_iov; + int uio_iovcnt; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: uio_total_len + * + * Description: + * Return the total length of data in bytes. + * Or -EOVERFLOW. + * + ****************************************************************************/ + +ssize_t uio_total_len(FAR const struct uio *uio); + +#endif /* __INCLUDE_NUTTX_FS_UIO_H */ diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h index 8e27edae007..5bef5e5d1ca 100644 --- a/include/sys/syscall_lookup.h +++ b/include/sys/syscall_lookup.h @@ -200,6 +200,8 @@ SYSCALL_LOOKUP(close, 1) SYSCALL_LOOKUP(ioctl, 3) SYSCALL_LOOKUP(read, 3) SYSCALL_LOOKUP(write, 3) +SYSCALL_LOOKUP(readv, 3) +SYSCALL_LOOKUP(writev, 3) SYSCALL_LOOKUP(pread, 4) SYSCALL_LOOKUP(pwrite, 4) #ifdef CONFIG_FS_AIO diff --git a/libs/libc/uio/CMakeLists.txt b/libs/libc/uio/CMakeLists.txt index 1074488fec1..9dbcbf0dce0 100644 --- a/libs/libc/uio/CMakeLists.txt +++ b/libs/libc/uio/CMakeLists.txt @@ -20,4 +20,4 @@ # # ############################################################################## -target_sources(c PRIVATE lib_readv.c lib_writev.c lib_preadv.c lib_pwritev.c) +target_sources(c PRIVATE lib_preadv.c lib_pwritev.c) diff --git a/libs/libc/uio/Make.defs b/libs/libc/uio/Make.defs index 04620764ddf..59bfb7d6640 100644 --- a/libs/libc/uio/Make.defs +++ b/libs/libc/uio/Make.defs @@ -22,7 +22,6 @@ # Add the uio.h C files to the build -CSRCS += lib_readv.c lib_writev.c CSRCS += lib_preadv.c lib_pwritev.c # Add the uio.h directory to the build diff --git a/libs/libc/uio/lib_readv.c b/libs/libc/uio/lib_readv.c deleted file mode 100644 index c16d0a673bd..00000000000 --- a/libs/libc/uio/lib_readv.c +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** - * libs/libc/uio/lib_readv.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include -#include - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: readv() - * - * Description: - * The readv() function is equivalent to read(), except as described below. - * The readv() function places the input data into the 'iovcnt' buffers - * specified by the members of the 'iov' array: iov[0], iov[1], ..., - * iov['iovcnt'-1]. The 'iovcnt' argument is valid if greater than 0 and - * less than or equal to IOV_MAX as defined in limits.h. - * - * Each iovec entry specifies the base address and length of an area in - * memory where data should be placed. The readv() function will always - * fill an area completely before proceeding to the next. - * - * TODO: pon successful completion, readv() will mark for update the - * st_atime field of the file. - * - * Input Parameters: - * filedes - The open file descriptor for the file to be read - * iov - Array of read buffer descriptors - * iovcnt - Number of elements in iov[] - * - * Returned Value: - * Upon successful completion, readv() will return a non-negative integer - * indicating the number of bytes actually read. Otherwise, the functions - * will return -1 and set errno to indicate the error. See read() for the - * list of returned errno values. In addition, the readv() function will - * fail if: - * - * EINVAL. - * The sum of the iov_len values in the iov array overflowed an ssize_t - * or The 'iovcnt' argument was less than or equal to 0, or greater than - * IOV_MAX (Not implemented). - * - ****************************************************************************/ - -ssize_t readv(int fildes, FAR const struct iovec *iov, int iovcnt) -{ - ssize_t ntotal; - ssize_t nread; - size_t remaining; - FAR uint8_t *buffer; - int i; - - /* Process each entry in the struct iovec array */ - - for (i = 0, ntotal = 0; i < iovcnt; i++) - { - /* Ignore zero-length reads */ - - if (iov[i].iov_len > 0) - { - buffer = iov[i].iov_base; - remaining = iov[i].iov_len; - - /* Read repeatedly as necessary to fill buffer */ - - do - { - /* NOTE: read() is a cancellation point */ - - nread = read(fildes, buffer, remaining); - - /* Check for a read error */ - - if (nread < 0) - { - return nread; - } - - /* Check for an end-of-file condition */ - - else if (nread == 0) - { - return ntotal; - } - - /* Update pointers and counts in order to handle partial - * buffer reads. - */ - - buffer += nread; - remaining -= nread; - ntotal += nread; - } - while (remaining > 0); - } - } - - return ntotal; -} diff --git a/libs/libc/uio/lib_writev.c b/libs/libc/uio/lib_writev.c deleted file mode 100644 index 79623e64b64..00000000000 --- a/libs/libc/uio/lib_writev.c +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** - * libs/libc/uio/lib_writev.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include -#include -#include - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: writev() - * - * Description: - * The writev() function is equivalent to write(), except as described - * below. The writev() function will gather output data from the 'iovcnt' - * buffers specified by the members of the 'iov' array: - * iov[0], iov[1], ..., iov[iovcnt-1]. - * The 'iovcnt' argument is valid if greater than 0 and less than or equal - * to IOV_MAX, as defined in limits.h. - * - * Each iovec entry specifies the base address and length of an area in - * memory from which data should be written. The writev() function always - * writes a complete area before proceeding to the next. - * - * If 'filedes' refers to a regular file and all of the iov_len members in - * the array pointed to by iov are 0, writev() will return 0 and have no - * other effect. For other file types, the behavior is unspecified. - * - * TODO: If the sum of the iov_len values is greater than SSIZE_MAX, the - * operation will fail and no data will be transferred. - * - * Input Parameters: - * filedes - The open file descriptor for the file to be write - * iov - Array of write buffer descriptors - * iovcnt - Number of elements in iov[] - * - * Returned Value: - * Upon successful completion, writev() shall return the number of bytes - * actually written. Otherwise, it shall return a value of -1, the file- - * pointer shall remain unchanged, and errno shall be set to indicate an - * error. See write for the list of returned errno values. In addition, - * the writev() function will fail if: - * - * EINVAL. - * The sum of the iov_len values in the iov array overflowed an ssize_t - * or The 'iovcnt' argument was less than or equal to 0, or greater than - * IOV_MAX (Not implemented). - * - ****************************************************************************/ - -ssize_t writev(int fildes, FAR const struct iovec *iov, int iovcnt) -{ - ssize_t ntotal; - ssize_t nwritten; - size_t remaining; - FAR uint8_t *buffer; - int i; - - /* Process each entry in the struct iovec array */ - - for (i = 0, ntotal = 0; i < iovcnt; i++) - { - /* Ignore zero-length writes */ - - if (iov[i].iov_len > 0) - { - buffer = iov[i].iov_base; - remaining = iov[i].iov_len; - - /* Write repeatedly as necessary to write the entire buffer */ - - do - { - /* NOTE: write() is a cancellation point */ - - nwritten = write(fildes, buffer, remaining); - - /* Check for a write error */ - - if (nwritten < 0) - { - return ntotal ? ntotal : ERROR; - } - - /* Update pointers and counts in order to handle partial - * buffer writes. - */ - - buffer += nwritten; - remaining -= nwritten; - ntotal += nwritten; - } - while (remaining > 0); - } - } - - return ntotal; -} diff --git a/syscall/syscall.csv b/syscall/syscall.csv index 1c3baae5315..f883a50c879 100644 --- a/syscall/syscall.csv +++ b/syscall/syscall.csv @@ -128,6 +128,7 @@ "putenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int","FAR const char *" "pwrite","unistd.h","","ssize_t","int","FAR const void *","size_t","off_t" "read","unistd.h","","ssize_t","int","FAR void *","size_t" +"readv","sys/uio.h","","ssize_t","int","FAR const struct iovec *","int" "readlink","unistd.h","defined(CONFIG_PSEUDOFS_SOFTLINKS)","ssize_t","FAR const char *","FAR char *","size_t" "recv","sys/socket.h","defined(CONFIG_NET)","ssize_t","int","FAR void *","size_t","int" "recvfrom","sys/socket.h","defined(CONFIG_NET)","ssize_t","int","FAR void*","size_t","int","FAR struct sockaddr*","FAR socklen_t*" @@ -208,3 +209,4 @@ "waitid","sys/wait.h","defined(CONFIG_SCHED_WAITPID) && defined(CONFIG_SCHED_HAVE_PARENT)","int","idtype_t","id_t"," FAR siginfo_t *","int" "waitpid","sys/wait.h","defined(CONFIG_SCHED_WAITPID)","pid_t","pid_t","FAR int *","int" "write","unistd.h","","ssize_t","int","FAR const void *","size_t" +"writev","sys/uio.h","","ssize_t","int","FAR const struct iovec *","int"