bfd: loop through read()/write() when the action is incomplete

The callers of bread() and bwrite() assume the operation reads/writes
the complete length of the passed buffer.
We must loop when invoking the read()/write() APIs.

Fixes #1504

Signed-off-by: Nicolas Viennot <Nicolas.Viennot@twosigma.com>
This commit is contained in:
Nicolas Viennot 2021-06-18 21:38:07 +00:00 committed by Andrei Vagin
parent 24bc083653
commit 031a8d7905
3 changed files with 71 additions and 7 deletions

View file

@ -145,7 +145,7 @@ static int brefill(struct bfd *f)
memmove(b->mem, b->data, b->sz);
b->data = b->mem;
ret = read(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
ret = read_all(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
if (ret < 0) {
pr_perror("Error reading file");
return -1;
@ -241,7 +241,7 @@ static int bflush(struct bfd *bfd)
if (!b->sz)
return 0;
ret = write(bfd->fd, b->data, b->sz);
ret = write_all(bfd->fd, b->data, b->sz);
if (ret != b->sz)
return -1;
@ -261,7 +261,7 @@ static int __bwrite(struct bfd *bfd, const void *buf, int size)
}
if (size > BUFSIZE)
return write(bfd->fd, buf, size);
return write_all(bfd->fd, buf, size);
memcpy(b->data + b->sz, buf, size);
b->sz += size;
@ -271,7 +271,7 @@ static int __bwrite(struct bfd *bfd, const void *buf, int size)
int bwrite(struct bfd *bfd, const void *buf, int size)
{
if (!bfd_buffered(bfd))
return write(bfd->fd, buf, size);
return write_all(bfd->fd, buf, size);
return __bwrite(bfd, buf, size);
}
@ -280,8 +280,13 @@ int bwritev(struct bfd *bfd, const struct iovec *iov, int cnt)
{
int i, written = 0;
if (!bfd_buffered(bfd))
if (!bfd_buffered(bfd)) {
/*
* FIXME writev() should be called again if writev() writes
* less bytes than requested.
*/
return writev(bfd->fd, iov, cnt);
}
for (i = 0; i < cnt; i++) {
int ret;
@ -304,7 +309,7 @@ int bread(struct bfd *bfd, void *buf, int size)
int more = 1, filled = 0;
if (!bfd_buffered(bfd))
return read(bfd->fd, buf, size);
return read_all(bfd->fd, buf, size);
while (more > 0) {
int chunk;

View file

@ -385,4 +385,7 @@ extern int mount_detached_fs(const char *fsname);
extern char *get_legacy_iptables_bin(bool ipv6);
extern ssize_t read_all(int fd, void *buf, size_t size);
extern ssize_t write_all(int fd, const void *buf, size_t size);
#endif /* __CR_UTIL_H__ */

View file

@ -1583,4 +1583,60 @@ char *get_legacy_iptables_bin(bool ipv6)
iptables_present[ipv6] = 1;
return iptables_bin[ipv6];
}
}
/*
* read_all() behaves like read() without the possibility of partial reads.
* Use only with blocking I/O.
*/
ssize_t read_all(int fd, void *buf, size_t size)
{
ssize_t n = 0;
while (size > 0) {
ssize_t ret = read(fd, buf, size);
if (ret == -1) {
if (errno == EINTR)
continue;
/*
* The caller should use standard read() for
* non-blocking I/O.
*/
if (errno == EAGAIN || errno == EWOULDBLOCK)
errno = EINVAL;
return ret;
}
if (ret == 0)
break;
n += ret;
buf = (char *)buf + ret;
size -= ret;
}
return n;
}
/*
* write_all() behaves like write() without the possibility of partial writes.
* Use only with blocking I/O.
*/
ssize_t write_all(int fd, const void *buf, size_t size)
{
ssize_t n = 0;
while (size > 0) {
ssize_t ret = write(fd, buf, size);
if (ret == -1) {
if (errno == EINTR)
continue;
/*
* The caller should use standard write() for
* non-blocking I/O.
*/
if (errno == EAGAIN || errno == EWOULDBLOCK)
errno = EINVAL;
return ret;
}
n += ret;
buf = (char *)buf + ret;
size -= ret;
}
return n;
}