Fix fread() with 2gb+ sizes

This commit is contained in:
Justine Tunney 2024-05-24 19:28:23 -07:00
parent 5f61d273e4
commit ed93fc3dd7
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
8 changed files with 254 additions and 119 deletions

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
@ -25,6 +26,41 @@
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
static ssize_t readvall(int fd, struct iovec *iov, int iovlen) {
int olde;
ssize_t rc;
size_t got, toto;
toto = 0;
olde = errno;
do {
if ((rc = readv(fd, iov, iovlen)) == -1) {
if (toto && errno == EINTR) {
errno = olde;
continue;
}
return -1;
}
got = rc;
toto += got;
for (;;) {
if (!iov->iov_len) {
--iovlen;
++iov;
} else if (got >= iov->iov_len) {
got -= iov->iov_len;
--iovlen;
++iov;
} else {
iov->iov_base += got;
iov->iov_len -= got;
break;
}
}
} while (got && iovlen);
return toto;
}
/**
* Reads data from stream.
@ -36,11 +72,10 @@
size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
char *p;
ssize_t rc;
size_t n, m;
struct iovec iov[2];
if (!stride) {
return 0;
}
size_t n, m, got, need;
// check state and parameters
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
f->state = errno = EBADF;
return 0;
@ -53,52 +88,74 @@ size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
f->state = errno = EOVERFLOW;
return 0;
}
if (!n)
return 0;
// try to fulfill request from buffer if possible
p = buf;
m = f->end - f->beg;
if (MIN(n, m)) {
memcpy(p, f->buf + f->beg, MIN(n, m));
}
if (n < m) {
f->beg += n;
return count;
}
if (n == m) {
f->beg = f->end = 0;
if (n <= m) {
memcpy(p, f->buf + f->beg, n);
if ((f->beg += n) == f->end) {
f->beg = 0;
f->end = 0;
}
return count;
}
// handle end-of-file condition in fileless mode
if (f->fd == -1) {
f->beg = 0;
f->end = 0;
f->state = -1;
m /= stride;
m *= stride;
if (m)
memcpy(p, f->buf + f->beg, m);
if ((f->beg += m) == f->end) {
f->state = EOF;
f->beg = 0;
f->end = 0;
}
return m / stride;
}
// `n` is number of bytes requested by caller
// `m` is how much of `n` came from existing buffer
// `iov[0]` reads remainder of the caller request
// `iov[1]` reads ahead extra content into buffer
if (m)
memcpy(p, f->buf + f->beg, m);
iov[0].iov_base = p + m;
iov[0].iov_len = n - m;
iov[0].iov_len = need = n - m;
if (f->bufmode != _IONBF && n < f->size) {
iov[1].iov_base = f->buf;
if (f->size > PUSHBACK) {
if (f->size > PUSHBACK)
iov[1].iov_len = f->size - PUSHBACK;
} else {
else
iov[1].iov_len = f->size;
}
} else {
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
}
if ((rc = readv(f->fd, iov, 2)) == -1) {
if ((rc = readvall(f->fd, iov, 2)) == -1) {
f->state = errno;
return 0;
}
n = rc;
f->beg = 0;
f->end = 0;
if (n > iov[0].iov_len) {
f->end += n - iov[0].iov_len;
return count;
} else {
n = (m + n) / stride;
if (n < count)
f->state = -1;
return n;
got = rc;
// handle partial fulfillment
if (got < need) {
got += m;
if (got % stride) {
f->state = eio();
return 0;
}
f->beg = 0;
f->end = 0;
f->state = EOF;
return got / stride;
}
// handle overfulfillment
f->beg = 0;
f->end = got - need;
return count;
}

View file

@ -27,6 +27,40 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
static ssize_t writevall(int fd, struct iovec *iov, int iovlen) {
int olde;
ssize_t rc;
size_t got, toto;
toto = 0;
olde = errno;
do {
if ((rc = writev(fd, iov, iovlen)) == -1) {
if (toto && errno == EINTR) {
errno = olde;
continue;
}
return -1;
}
got = rc;
toto += got;
for (;;) {
if (!iov->iov_len) {
--iovlen;
++iov;
} else if (got >= iov->iov_len) {
got -= iov->iov_len;
--iovlen;
++iov;
} else {
iov->iov_base += got;
iov->iov_len -= got;
break;
}
}
} while (got && iovlen);
return toto;
}
/**
* Writes data to stream.
*
@ -104,7 +138,7 @@ size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
iov[1].iov_base = (void *)data;
iov[1].iov_len = n;
n += f->beg;
if (__robust_writev(f->fd, iov, 2) == -1) {
if ((rc = writevall(f->fd, iov, 2)) == -1) {
f->state = errno;
return 0;
}

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/iovec.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/dce.h"
#include "libc/fmt/internal.h"
#include "libc/limits.h"
@ -42,7 +42,7 @@ static int vdprintf_putc(const char *s, struct VdprintfState *t, size_t n) {
iov[0].iov_len = t->n;
iov[1].iov_base = (void *)s;
iov[1].iov_len = n;
if (__robust_writev(t->fd, iov, 2) == -1) {
if (writev(t->fd, iov, 2) == -1) {
return -1;
}
t->t += t->n;
@ -68,7 +68,7 @@ int vdprintf(int fd, const char *fmt, va_list va) {
if (t.n) {
iov[0].iov_base = t.b;
iov[0].iov_len = t.n;
if (__robust_writev(t.fd, iov, 1) == -1) {
if (writev(t.fd, iov, 1) == -1) {
return -1;
}
t.t += t.n;