Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stdio/file: fix numerous issues in the fread/fwrite functions #376

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 60 additions & 28 deletions stdio/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,16 +290,25 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream)
}


static int full_read(int fd, void *ptr, size_t size)
static int full_read(int fd, void *ptr, size_t size, int *eof)
{
int err;
int total = 0;

*eof = 0;

while (size) {
if ((err = __safe_read(fd, ptr, size)) < 0) {
return -1;
err = __safe_read_nb(fd, ptr, size);
if (err < 0) {
if (errno == EAGAIN) {
return total;
}
else {
return -1;
}
}
else if (!err) {
*eof = 1;
return total;
}
ptr += err;
Expand Down Expand Up @@ -328,21 +337,34 @@ static int full_write(int fd, const void *ptr, size_t size)

size_t fread_unlocked(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
int err;
int err, eof;
size_t readsz = nmemb * size;
size_t bytes;

if (!readsz) {
if (readsz == 0) {
return 0;
}

if (stream->buffer == NULL) {
/* unbuffered read */
if ((err = full_read(stream->fd, ptr, readsz)) < 0) {
err = full_read(stream->fd, ptr, readsz, &eof);
if (err >= 0) {
if (err < readsz) {
if (eof == 1) {
stream->flags |= F_EOF;
}
else {
/* received EAGAIN */
stream->flags |= F_ERROR;
}
}

return err / size;
}
else {
stream->flags |= F_ERROR;
return 0;
}

return err / size;
}

/* flush write buffer if writing */
Expand All @@ -363,43 +385,49 @@ size_t fread_unlocked(void *ptr, size_t size, size_t nmemb, FILE *stream)

/* refill read buffer */
if (stream->bufpos == stream->bufeof) {
if ((err = __safe_read(stream->fd, stream->buffer, stream->bufsz)) == -1) {
err = __safe_read_nb(stream->fd, stream->buffer, stream->bufsz);
if (err < 0) {
stream->flags |= F_ERROR;
return 0;
}

stream->bufpos = 0;
stream->bufeof = err;

if (!err) {
else if (err == 0) {
stream->flags |= F_EOF;
return 0;
}
else {
stream->bufpos = 0;
stream->bufeof = err;
}
}

if ((bytes = stream->bufeof - stream->bufpos)) {
bytes = min(bytes, readsz);
memcpy(ptr, stream->buffer + stream->bufpos, bytes);
bytes = min(stream->bufeof - stream->bufpos, readsz);
memcpy(ptr, stream->buffer + stream->bufpos, bytes);

ptr += bytes;
stream->bufpos += bytes;
readsz -= bytes;
ptr += bytes;
stream->bufpos += bytes;
readsz -= bytes;

if (!readsz) {
return nmemb;
}
if (readsz == 0) {
return nmemb;
}

/* read remainder directly into ptr */
if ((err = full_read(stream->fd, ptr, readsz)) != -1) {
/* read the remaining bytes directly into the user buffer */
err = full_read(stream->fd, ptr, readsz, &eof);
if (err >= 0) {
bytes += err;

if (err < readsz) {
stream->flags |= F_EOF;
if (eof == 1) {
stream->flags |= F_EOF;
}
else {
/* received EAGAIN */
stream->flags |= F_ERROR;
}
}
}
else {
stream->flags |= F_ERROR;
return 0;
}

return bytes / size;
Expand Down Expand Up @@ -437,7 +465,11 @@ size_t fwrite_unlocked(const void *ptr, size_t size, size_t nmemb, FILE *stream)

/* discard reading buffer */
if (!(stream->flags & F_WRITING)) {
fflush_unlocked(stream);
if (stream->bufpos != stream->bufeof) {
if (fflush_unlocked(stream) < 0) {
return 0;
}
}
stream->flags |= F_WRITING;
stream->bufpos = 0;
}
Expand Down
4 changes: 4 additions & 0 deletions unistd/file-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ ssize_t __safe_write(int fd, const void *buff, size_t size);
ssize_t __safe_read(int fd, void *buf, size_t size);


/* non-blocking variant - returns on EAGAIN */
ssize_t __safe_read_nb(int fd, void *buf, size_t size);


int __safe_open(const char *path, int oflag, mode_t mode);


Expand Down
18 changes: 13 additions & 5 deletions unistd/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,26 @@ ssize_t __safe_read(int fd, void *buf, size_t size)
continue;
}
else {
break;
return -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fixed error handling in blocking safe read to always return -1 on error.

How is this different? The else branch is taken when rlen < 0, so if (rlen < 0) below is still executed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right - disregard this comment.

}
}

if (rlen < 0) {
return -1;
}

return size - todo;
}


ssize_t __safe_read_nb(int fd, void *buf, size_t size)
{
ssize_t rlen;

do {
rlen = read(fd, buf, size);
} while (rlen < 0 && errno == EINTR);

return rlen;
}


int __safe_open(const char *path, int oflag, mode_t mode)
{
int err;
Expand Down
Loading