BLI_fileops: Add 'BLI_read' wrapper to ensure the requested data is read #113474
@ -290,6 +290,14 @@ void *BLI_gzopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT
|
||||
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* A version of `read` with the following differences:
|
||||
* - continues reading until failure or the requested size is met.
|
||||
* - Reads `size_t` bytes instead of `int` on WIN32.
|
||||
* \return the number of bytes read.
|
||||
*/
|
||||
ssize_t BLI_read(int fd, void *buf, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Returns true if the file with the specified name can be written.
|
||||
* This implementation uses access(2), which makes the check according
|
||||
|
@ -92,6 +92,51 @@ static char *windows_operation_string(FileExternalOperation operation)
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t BLI_read(int fd, void *buf, size_t nbytes)
|
||||
{
|
||||
/* Define our own read as `read` is not guaranteed to read the number of bytes requested.
|
||||
* This happens rarely but was observed with larger than 2GB files on Linux, see: #113473.
|
||||
*
|
||||
* Even though this is a loop, the most common code-path will exit with "Success" case.
|
||||
* In the case where read more data than the file contains, it will loop twice,
|
||||
* exiting on EOF with the second iteration. */
|
||||
ssize_t nbytes_read_total = 0;
|
||||
while (true) {
|
||||
ssize_t nbytes_read = read(fd,
|
||||
buf,
|
||||
#ifdef WIN32
|
||||
ideasman42 marked this conversation as resolved
Outdated
|
||||
/* Read must not exceed INT_MAX on WIN32, clamp. */
|
||||
MIN2(nbytes, INT_MAX)
|
||||
#else
|
||||
nbytes
|
||||
#endif
|
||||
);
|
||||
if (nbytes_read == nbytes) {
|
||||
/* Success (common case). */
|
||||
return nbytes_read_total + nbytes_read;
|
||||
}
|
||||
if (nbytes_read == 0) {
|
||||
/* EOF (common case for the second iteration when reading more data than `fd` contains). */
|
||||
return nbytes_read_total;
|
||||
}
|
||||
if (nbytes_read < 0) {
|
||||
/* Error. */
|
||||
return nbytes_read;
|
||||
}
|
||||
if (UNLIKELY(nbytes_read > nbytes)) {
|
||||
/* Badly behaving C-API - this should never happen.
|
||||
* Possibly an invalid internal state/corruption, only check to prevent an eternal loop. */
|
||||
BLI_assert_unreachable();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If this is reached, fewer bytes were read than were requested. */
|
||||
buf = (void *)(((char *)buf) + nbytes_read);
|
||||
nbytes_read_total += nbytes_read;
|
||||
nbytes -= nbytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
bool BLI_file_external_operation_supported(const char *filepath, FileExternalOperation operation)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
@ -124,7 +124,7 @@ static bool buffer_from_filepath(const char *filepath, void **r_mem, size_t *r_s
|
||||
else if (r_mem && UNLIKELY(!(mem = static_cast<uchar *>(MEM_mallocN(size, __func__))))) {
|
||||
CLOG_WARN(&LOG, "error allocating buffer for '%s'", filepath);
|
||||
}
|
||||
else if (r_mem && UNLIKELY((size_read = read(file, mem, size)) != size)) {
|
||||
else if (r_mem && UNLIKELY((size_read = BLI_read(file, mem, size)) != size)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"error '%s' while reading '%s' (expected %" PRIu64 ", was %" PRId64 ")",
|
||||
strerror(errno),
|
||||
|
Loading…
Reference in New Issue
Block a user
ifndef
->ifdef