file_readv_compat/file_writev_compat: Fix partial success handling

The problem has been inherited from the original libc readv/writev
implementation. However, now it's exposed in more situations because
this implemenation is used to back read/write as well.

I expect this fixes the regressions observed on the Espressif CI.
https://github.com/apache/nuttx/pull/13498#issuecomment-2448031197

Note that, even with this fix, these "compat" readv/writev
implementations are still inheritedly broken. (E.g. consider that
a data boundary happens to match one of iovec boundaries) However,
this fix is enough for read/write, where iovcnt is always 1.
This commit is contained in:
YAMAMOTO Takashi 2024-11-01 13:17:49 +09:00 committed by Xiang Xiao
parent 0fad2ee73f
commit 8241a10ebc
2 changed files with 56 additions and 61 deletions

View file

@ -68,42 +68,36 @@ static ssize_t file_readv_compat(FAR struct file *filep,
{
/* Ignore zero-length reads */
if (iov[i].iov_len > 0)
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);
continue;
}
buffer = iov[i].iov_base;
remaining = iov[i].iov_len;
nread = inode->u.i_ops->read(filep, (void *)buffer, remaining);
/* Check for a read error */
if (nread < 0)
{
return ntotal ? ntotal : nread;
}
ntotal += nread;
/* Check for a parital success condition, including an end-of-file */
if (nread < remaining)
{
return ntotal;
}
/* Update the pointer */
buffer += nread;
remaining -= nread;
}
return ntotal;

View file

@ -68,35 +68,36 @@ static ssize_t file_writev_compat(FAR struct file *filep,
{
/* Ignore zero-length writes */
if (iov[i].iov_len > 0)
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);
continue;
}
buffer = iov[i].iov_base;
remaining = iov[i].iov_len;
nwritten = inode->u.i_ops->write(filep, (void *)buffer, remaining);
/* Check for a write error */
if (nwritten < 0)
{
return ntotal ? ntotal : nwritten;
}
ntotal += nwritten;
/* Check for a parital success condition */
if (nwritten < remaining)
{
return ntotal;
}
/* Update the pointer */
buffer += nwritten;
remaining -= nwritten;
}
return ntotal;