diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt --- a/libc/src/__support/File/CMakeLists.txt +++ b/libc/src/__support/File/CMakeLists.txt @@ -14,7 +14,6 @@ libc.include.errno libc.src.__support.CPP.span libc.src.__support.threads.mutex - libc.src.errno.errno ) add_object_library( diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -16,6 +16,27 @@ namespace __llvm_libc { +template struct FileResult { + T return_val; + int error_val; + +public: + constexpr FileResult(T val) : return_val(val), error_val(0) {} + constexpr FileResult(T val, int error) : return_val(val), error_val(error) {} + + constexpr bool has_error() { return error_val != 0; } + constexpr int get_error() { return error_val; } + constexpr T get_value() { return return_val; } + + constexpr operator T() { return return_val; } + + // Only used for testing. + constexpr bool operator==(const FileResult &other) { + return (return_val == other.get_value()) && + (error_val == other.get_error()); + } +}; + // This a generic base class to encapsulate a platform independent file data // structure. Platform specific specializations should create a subclass as // suitable for their platform. @@ -26,13 +47,13 @@ using LockFunc = void(File *); using UnlockFunc = void(File *); - using WriteFunc = size_t(File *, const void *, size_t); - using ReadFunc = size_t(File *, void *, size_t); + using WriteFunc = FileResult(File *, const void *, size_t); + using ReadFunc = FileResult(File *, void *, size_t); // The SeekFunc is expected to return the current offset of the external // file position indicator. - using SeekFunc = long(File *, long, int); - using CloseFunc = int(File *); - using FlushFunc = int(File *); + using SeekFunc = FileResult(File *, long, int); + using CloseFunc = FileResult(File *); + using FlushFunc = FileResult(File *); using ModeFlags = uint32_t; @@ -174,35 +195,35 @@ } // Buffered write of |len| bytes from |data| without the file lock. - size_t write_unlocked(const void *data, size_t len); + FileResult write_unlocked(const void *data, size_t len); // Buffered write of |len| bytes from |data| under the file lock. - size_t write(const void *data, size_t len) { + FileResult write(const void *data, size_t len) { FileLock l(this); return write_unlocked(data, len); } // Buffered read of |len| bytes into |data| without the file lock. - size_t read_unlocked(void *data, size_t len); + FileResult read_unlocked(void *data, size_t len); // Buffered read of |len| bytes into |data| under the file lock. - size_t read(void *data, size_t len) { + FileResult read(void *data, size_t len) { FileLock l(this); return read_unlocked(data, len); } - int seek(long offset, int whence); + FileResult seek(long offset, int whence); - long tell(); + FileResult tell(); // If buffer has data written to it, flush it out. Does nothing if the // buffer is currently being used as a read buffer. - int flush() { + FileResult flush() { FileLock lock(this); return flush_unlocked(); } - int flush_unlocked(); + FileResult flush_unlocked(); // Returns EOF on error and keeps the file unchanged. int ungetc_unlocked(int c); @@ -225,7 +246,7 @@ int set_buffer(void *buffer, size_t size, int buffer_mode); // Closes the file stream and frees up all resources owned by it. - int close(); + FileResult close(); void lock() { mutex.lock(); } void unlock() { mutex.unlock(); } @@ -256,9 +277,9 @@ static ModeFlags mode_flags(const char *mode); private: - size_t write_unlocked_lbf(const uint8_t *data, size_t len); - size_t write_unlocked_fbf(const uint8_t *data, size_t len); - size_t write_unlocked_nbf(const uint8_t *data, size_t len); + FileResult write_unlocked_lbf(const uint8_t *data, size_t len); + FileResult write_unlocked_fbf(const uint8_t *data, size_t len); + FileResult write_unlocked_nbf(const uint8_t *data, size_t len); constexpr void adjust_buf() { if (read_allowed() && (buf == nullptr || bufsize == 0)) { @@ -285,7 +306,7 @@ // The implementaiton of this function is provided by the platfrom_file // library. -File *openfile(const char *path, const char *mode); +FileResult openfile(const char *path, const char *mode); // The platform_file library should implement it if it relevant for that // platform. diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp --- a/libc/src/__support/File/file.cpp +++ b/libc/src/__support/File/file.cpp @@ -10,17 +10,16 @@ #include "src/__support/CPP/span.h" -#include +#include // For error macros #include #include namespace __llvm_libc { -size_t File::write_unlocked(const void *data, size_t len) { +FileResult File::write_unlocked(const void *data, size_t len) { if (!write_allowed()) { - errno = EBADF; err = true; - return 0; + return {0, EBADF}; } prev_op = FileOp::WRITE; @@ -37,26 +36,29 @@ } } -size_t File::write_unlocked_nbf(const uint8_t *data, size_t len) { +FileResult File::write_unlocked_nbf(const uint8_t *data, size_t len) { if (pos > 0) { // If the buffer is not empty // Flush the buffer const size_t write_size = pos; - size_t bytes_written = platform_write(this, buf, write_size); + auto buf_result = platform_write(this, buf, write_size); + size_t bytes_written = buf_result.get_value(); pos = 0; // Buffer is now empty so reset pos to the beginning. // If less bytes were written than expected, then an error occurred. if (bytes_written < write_size) { err = true; - return 0; // No bytes from data were written, so return 0. + // No bytes from data were written, so return 0. + return {0, buf_result.get_error()}; } } - size_t written = platform_write(this, data, len); + auto result = platform_write(this, data, len); + size_t written = result.get_value(); if (written < len) err = true; - return written; + return result; } -size_t File::write_unlocked_fbf(const uint8_t *data, size_t len) { +FileResult File::write_unlocked_fbf(const uint8_t *data, size_t len) { const size_t init_pos = pos; const size_t bufspace = bufsize - pos; @@ -96,13 +98,17 @@ // We need to flush the buffer now, since there is still data and the buffer // is full. const size_t write_size = pos; - size_t bytes_written = platform_write(this, buf, write_size); + + auto buf_result = platform_write(this, buf, write_size); + size_t bytes_written = buf_result.get_value(); + pos = 0; // Buffer is now empty so reset pos to the beginning. // If less bytes were written than expected, then an error occurred. Return // the number of bytes that have been written from |data|. - if (bytes_written < write_size) { + if (bytes_written < write_size || buf_result.has_error()) { err = true; - return bytes_written <= init_pos ? 0 : bytes_written - init_pos; + return {bytes_written <= init_pos ? 0 : bytes_written - init_pos, + buf_result.get_error()}; } // The second piece is handled basically the same as the first, although we @@ -114,21 +120,22 @@ bufref[i] = remainder[i]; pos = remainder.size(); } else { - size_t bytes_written = - platform_write(this, remainder.data(), remainder.size()); + + auto result = platform_write(this, remainder.data(), remainder.size()); + size_t bytes_written = buf_result.get_value(); // If less bytes were written than expected, then an error occurred. Return // the number of bytes that have been written from |data|. - if (bytes_written < remainder.size()) { + if (bytes_written < remainder.size() || result.has_error()) { err = true; - return primary.size() + bytes_written; + return {primary.size() + bytes_written, result.get_error()}; } } return len; } -size_t File::write_unlocked_lbf(const uint8_t *data, size_t len) { +FileResult File::write_unlocked_lbf(const uint8_t *data, size_t len) { constexpr uint8_t NEWLINE_CHAR = '\n'; size_t last_newline = len; for (size_t i = len; i >= 1; --i) { @@ -175,11 +182,10 @@ return len; } -size_t File::read_unlocked(void *data, size_t len) { +FileResult File::read_unlocked(void *data, size_t len) { if (!read_allowed()) { - errno = EBADF; err = true; - return 0; + return {0, EBADF}; } prev_op = FileOp::READ; @@ -210,31 +216,33 @@ size_t to_fetch = len - available_data; if (to_fetch > bufsize) { - size_t fetched_size = platform_read(this, dataref.data(), to_fetch); - if (fetched_size < to_fetch) { - if (errno == 0) + auto result = platform_read(this, dataref.data(), to_fetch); + size_t fetched_size = result.get_value(); + if (fetched_size < to_fetch || result.has_error()) { + if (!result.has_error()) eof = true; else err = true; - return available_data + fetched_size; + return {available_data + fetched_size, result.has_error()}; } return len; } // Fetch and buffer another buffer worth of data. - size_t fetched_size = platform_read(this, buf, bufsize); + auto result = platform_read(this, buf, bufsize); + size_t fetched_size = result.get_value(); read_limit += fetched_size; size_t transfer_size = fetched_size >= to_fetch ? to_fetch : fetched_size; for (size_t i = 0; i < transfer_size; ++i) dataref[i] = bufref[i]; pos += transfer_size; - if (fetched_size < to_fetch) { - if (errno == 0) + if (fetched_size < to_fetch || result.has_error()) { + if (!result.has_error()) eof = true; else err = true; } - return transfer_size + available_data; + return {transfer_size + available_data, result.get_error()}; } int File::ungetc_unlocked(int c) { @@ -275,13 +283,15 @@ return c; } -int File::seek(long offset, int whence) { +FileResult File::seek(long offset, int whence) { FileLock lock(this); if (prev_op == FileOp::WRITE && pos > 0) { - size_t transferred_size = platform_write(this, buf, pos); - if (transferred_size < pos) { + + auto buf_result = platform_write(this, buf, pos); + size_t transferred_size = buf_result.get_value(); + if (transferred_size < pos || buf_result.has_error()) { err = true; - return -1; + return {-1, buf_result.get_error()}; } } else if (prev_op == FileOp::READ && whence == SEEK_CUR) { // More data could have been read out from the platform file than was @@ -294,22 +304,21 @@ // Reset the eof flag as a seek might move the file positon to some place // readable. eof = false; - long platform_pos = platform_seek(this, offset, whence); + auto result = platform_seek(this, offset, whence); + long platform_pos = result.get_value(); if (platform_pos >= 0) - return 0; + return {0, result.get_error()}; else - return -1; + return {-1, result.get_error()}; } -long File::tell() { +FileResult File::tell() { FileLock lock(this); - long platform_offset; - if (eof) - platform_offset = platform_seek(this, 0, SEEK_END); - else - platform_offset = platform_seek(this, 0, SEEK_CUR); - if (platform_offset < 0) - return -1; + auto seek_target = eof ? SEEK_END : SEEK_CUR; + auto result = platform_seek(this, 0, seek_target); + long platform_offset = result.get_value(); + if (platform_offset < 0 || result.has_error()) + return {-1, result.get_error()}; if (prev_op == FileOp::READ) return platform_offset - (read_limit - pos); else if (prev_op == FileOp::WRITE) @@ -318,12 +327,13 @@ return platform_offset; } -int File::flush_unlocked() { +FileResult File::flush_unlocked() { if (prev_op == FileOp::WRITE && pos > 0) { - size_t transferred_size = platform_write(this, buf, pos); - if (transferred_size < pos) { + auto buf_result = platform_write(this, buf, pos); + size_t transferred_size = buf_result.get_value(); + if (transferred_size < pos || buf_result.has_error()) { err = true; - return -1; + return {-1, buf_result.get_error()}; } pos = 0; return platform_flush(this); @@ -332,18 +342,20 @@ return 0; } -int File::close() { +FileResult File::close() { { FileLock lock(this); if (prev_op == FileOp::WRITE && pos > 0) { - size_t transferred_size = platform_write(this, buf, pos); - if (transferred_size < pos) { + auto buf_result = platform_write(this, buf, pos); + size_t transferred_size = buf_result.get_value(); + if (transferred_size < pos || buf_result.has_error()) { err = true; - return -1; + return {-1, buf_result.get_error()}; } } - if (platform_close(this) != 0) - return -1; + auto result = platform_close(this); + if (result.get_value() != 0 || result.has_error()) + return {-1, result.get_error()}; if (own_buf) free(buf); } diff --git a/libc/src/__support/File/linux_file.cpp b/libc/src/__support/File/linux_file.cpp --- a/libc/src/__support/File/linux_file.cpp +++ b/libc/src/__support/File/linux_file.cpp @@ -10,7 +10,7 @@ #include "src/__support/OSUtil/syscall.h" // For internal syscall function. -#include +#include // For error macros #include // For mode_t and other flags to the open syscall #include #include // For malloc @@ -20,11 +20,11 @@ namespace { -size_t write_func(File *, const void *, size_t); -size_t read_func(File *, void *, size_t); -long seek_func(File *, long, int); -int close_func(File *); -int flush_func(File *); +FileResult write_func(File *, const void *, size_t); +FileResult read_func(File *, void *, size_t); +FileResult seek_func(File *, long, int); +FileResult close_func(File *); +FileResult flush_func(File *); } // anonymous namespace @@ -51,75 +51,69 @@ namespace { -size_t write_func(File *f, const void *data, size_t size) { +FileResult write_func(File *f, const void *data, size_t size) { auto *lf = reinterpret_cast(f); - long ret = __llvm_libc::syscall_impl(SYS_write, lf->get_fd(), data, size); + int ret = __llvm_libc::syscall_impl(SYS_write, lf->get_fd(), data, size); if (ret < 0) { - errno = -ret; - return 0; + return {0, -ret}; } return ret; } -size_t read_func(File *f, void *buf, size_t size) { +FileResult read_func(File *f, void *buf, size_t size) { auto *lf = reinterpret_cast(f); - long ret = __llvm_libc::syscall_impl(SYS_read, lf->get_fd(), buf, size); + int ret = __llvm_libc::syscall_impl(SYS_read, lf->get_fd(), buf, size); if (ret < 0) { - errno = -ret; - return 0; + return {0, -ret}; } return ret; } -long seek_func(File *f, long offset, int whence) { +FileResult seek_func(File *f, long offset, int whence) { auto *lf = reinterpret_cast(f); long result; #ifdef SYS_lseek - long ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence); + int ret = __llvm_libc::syscall_impl(SYS_lseek, lf->get_fd(), offset, whence); result = ret; #elif defined(SYS__llseek) long result; - long ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32, - offset, &result, whence); + int ret = __llvm_libc::syscall_impl(SYS__llseek, lf->get_fd(), offset >> 32, + offset, &result, whence); #else #error "lseek and _llseek syscalls not available to perform a seek operation." #endif if (ret < 0) { - errno = -ret; - return -1; + return {-1, -ret}; } return result; } -int close_func(File *f) { +FileResult close_func(File *f) { auto *lf = reinterpret_cast(f); - long ret = __llvm_libc::syscall_impl(SYS_close, lf->get_fd()); + int ret = __llvm_libc::syscall_impl(SYS_close, lf->get_fd()); if (ret < 0) { - errno = -ret; - return -1; + return {-1, -ret}; } return 0; } -int flush_func(File *f) { +FileResult flush_func(File *f) { auto *lf = reinterpret_cast(f); - long ret = __llvm_libc::syscall_impl(SYS_fsync, lf->get_fd()); + int ret = __llvm_libc::syscall_impl(SYS_fsync, lf->get_fd()); if (ret < 0) { - errno = -ret; - return -1; + return {-1, -ret}; } return 0; } } // anonymous namespace -File *openfile(const char *path, const char *mode) { +FileResult openfile(const char *path, const char *mode) { using ModeFlags = File::ModeFlags; auto modeflags = File::mode_flags(mode); if (modeflags == 0) { - errno = EINVAL; - return nullptr; + return {nullptr, EINVAL}; } long open_flags = 0; if (modeflags & ModeFlags(File::OpenMode::APPEND)) { @@ -155,8 +149,7 @@ #endif if (fd < 0) { - errno = -fd; - return nullptr; + return {nullptr, -fd}; } void *buffer = malloc(File::DEFAULT_BUFFER_SIZE); diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -25,6 +25,7 @@ fclose.h DEPENDS libc.include.stdio + libc.include.errno libc.src.__support.File.file libc.src.__support.File.platform_file ) @@ -108,6 +109,7 @@ HDRS fgetc.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -120,6 +122,7 @@ HDRS fgetc_unlocked.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -132,6 +135,7 @@ HDRS getc.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -144,6 +148,7 @@ HDRS getc_unlocked.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -156,6 +161,7 @@ HDRS fgets.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -168,6 +174,7 @@ HDRS fflush.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -204,6 +211,7 @@ HDRS fread_unlocked.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -216,6 +224,7 @@ HDRS fread.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -228,6 +237,7 @@ HDRS fwrite_unlocked.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -240,6 +250,7 @@ HDRS fwrite.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -252,6 +263,7 @@ HDRS fputc.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -264,6 +276,7 @@ HDRS putc.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -276,6 +289,7 @@ HDRS putchar.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -288,6 +302,7 @@ HDRS fputs.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -301,6 +316,7 @@ HDRS puts.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -313,6 +329,7 @@ HDRS fseek.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file @@ -494,6 +511,7 @@ HDRS ftell.h DEPENDS + libc.include.errno libc.include.stdio libc.src.__support.File.file libc.src.__support.File.platform_file diff --git a/libc/src/stdio/fclose.cpp b/libc/src/stdio/fclose.cpp --- a/libc/src/stdio/fclose.cpp +++ b/libc/src/stdio/fclose.cpp @@ -9,12 +9,16 @@ #include "src/stdio/fclose.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fclose, (::FILE * stream)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->close(); + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->close(); + if (result.has_error()) + errno = result.get_error(); + return result.get_value(); } } // namespace __llvm_libc diff --git a/libc/src/stdio/fflush.cpp b/libc/src/stdio/fflush.cpp --- a/libc/src/stdio/fflush.cpp +++ b/libc/src/stdio/fflush.cpp @@ -9,12 +9,16 @@ #include "src/stdio/fflush.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fflush, (::FILE * stream)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->flush(); + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->flush(); + if (result.has_error()) + errno = result.get_error(); + return result.get_value(); } } // namespace __llvm_libc diff --git a/libc/src/stdio/fgetc.cpp b/libc/src/stdio/fgetc.cpp --- a/libc/src/stdio/fgetc.cpp +++ b/libc/src/stdio/fgetc.cpp @@ -9,13 +9,18 @@ #include "src/stdio/fgetc.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fgetc, (::FILE * stream)) { unsigned char c; - size_t r = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1); + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1); + size_t r = result.get_value(); + if (result.has_error()) + errno = result.get_error(); + if (r != 1) return EOF; return c; diff --git a/libc/src/stdio/fgetc_unlocked.cpp b/libc/src/stdio/fgetc_unlocked.cpp --- a/libc/src/stdio/fgetc_unlocked.cpp +++ b/libc/src/stdio/fgetc_unlocked.cpp @@ -9,14 +9,18 @@ #include "src/stdio/fgetc_unlocked.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fgetc_unlocked, (::FILE * stream)) { unsigned char c; - size_t r = + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(&c, 1); + size_t r = result.get_value(); + if (result.has_error()) + errno = result.get_error(); if (r != 1) return EOF; return c; diff --git a/libc/src/stdio/fgets.cpp b/libc/src/stdio/fgets.cpp --- a/libc/src/stdio/fgets.cpp +++ b/libc/src/stdio/fgets.cpp @@ -9,6 +9,7 @@ #include "src/stdio/fgets.h" #include "src/__support/File/file.h" +#include #include #include @@ -28,7 +29,11 @@ int i = 0; for (; i < (count - 1) && c != '\n'; ++i) { - size_t r = stream->read_unlocked(&c, 1); + auto result = stream->read_unlocked(&c, 1); + size_t r = result.get_value(); + if (result.has_error()) + errno = result.get_error(); + if (r != 1) break; str[i] = c; diff --git a/libc/src/stdio/fopen.cpp b/libc/src/stdio/fopen.cpp --- a/libc/src/stdio/fopen.cpp +++ b/libc/src/stdio/fopen.cpp @@ -9,13 +9,17 @@ #include "src/stdio/fopen.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(::FILE *, fopen, (const char *__restrict name, const char *__restrict mode)) { - return reinterpret_cast<::FILE *>(__llvm_libc::openfile(name, mode)); + auto result = __llvm_libc::openfile(name, mode); + if (result.has_error()) + errno = result.get_error(); + return reinterpret_cast<::FILE *>(result.get_value()); } } // namespace __llvm_libc diff --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp --- a/libc/src/stdio/fopencookie.cpp +++ b/libc/src/stdio/fopencookie.cpp @@ -23,7 +23,7 @@ cookie_io_functions_t ops; }; -size_t write_func(File *f, const void *data, size_t size) { +FileResult write_func(File *f, const void *data, size_t size) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.write == nullptr) return 0; @@ -31,7 +31,7 @@ reinterpret_cast(data), size); } -size_t read_func(File *f, void *data, size_t size) { +FileResult read_func(File *f, void *data, size_t size) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.read == nullptr) return 0; @@ -39,11 +39,10 @@ reinterpret_cast(data), size); } -long seek_func(File *f, long offset, int whence) { +FileResult seek_func(File *f, long offset, int whence) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.seek == nullptr) { - errno = EINVAL; - return -1; + return {-1, EINVAL}; } off64_t offset64 = offset; int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence); @@ -53,14 +52,14 @@ return -1; } -int close_func(File *f) { +FileResult close_func(File *f) { auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.close == nullptr) return 0; return cookie_file->ops.close(cookie_file->cookie); } -int flush_func(File *) { return 0; } +FileResult flush_func(File *) { return 0; } } // anonymous namespace diff --git a/libc/src/stdio/fputc.cpp b/libc/src/stdio/fputc.cpp --- a/libc/src/stdio/fputc.cpp +++ b/libc/src/stdio/fputc.cpp @@ -9,13 +9,19 @@ #include "src/stdio/fputc.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) { unsigned char uc = static_cast(c); - size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1); + + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1); + if (result.has_error()) + errno = result.get_error(); + size_t written = result.get_value(); + if (1 != written) { // The stream should be in an error state in this case. return EOF; diff --git a/libc/src/stdio/fputs.cpp b/libc/src/stdio/fputs.cpp --- a/libc/src/stdio/fputs.cpp +++ b/libc/src/stdio/fputs.cpp @@ -10,6 +10,7 @@ #include "src/__support/CPP/string_view.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { @@ -18,8 +19,12 @@ (const char *__restrict str, ::FILE *__restrict stream)) { cpp::string_view str_view(str); - size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write( + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write( str, str_view.size()); + if (result.has_error()) + errno = result.get_error(); + size_t written = result.get_value(); + if (str_view.size() != written) { // The stream should be in an error state in this case. return EOF; diff --git a/libc/src/stdio/fread.cpp b/libc/src/stdio/fread.cpp --- a/libc/src/stdio/fread.cpp +++ b/libc/src/stdio/fread.cpp @@ -9,6 +9,7 @@ #include "src/stdio/fread.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { @@ -18,9 +19,11 @@ ::FILE *stream)) { if (size == 0 || nmemb == 0) return 0; - return reinterpret_cast<__llvm_libc::File *>(stream)->read(buffer, - size * nmemb) / - size; + auto result = + reinterpret_cast<__llvm_libc::File *>(stream)->read(buffer, size * nmemb); + if (result.has_error()) + errno = result.get_error(); + return result.get_value() / size; } } // namespace __llvm_libc diff --git a/libc/src/stdio/fread_unlocked.cpp b/libc/src/stdio/fread_unlocked.cpp --- a/libc/src/stdio/fread_unlocked.cpp +++ b/libc/src/stdio/fread_unlocked.cpp @@ -9,6 +9,7 @@ #include "src/stdio/fread_unlocked.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { @@ -16,8 +17,13 @@ LLVM_LIBC_FUNCTION(size_t, fread_unlocked, (void *__restrict buffer, size_t size, size_t nmemb, ::FILE *stream)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked( + if (size == 0 || nmemb == 0) + return 0; + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked( buffer, size * nmemb); + if (result.has_error()) + errno = result.get_error(); + return result.get_value() / size; } } // namespace __llvm_libc diff --git a/libc/src/stdio/fseek.cpp b/libc/src/stdio/fseek.cpp --- a/libc/src/stdio/fseek.cpp +++ b/libc/src/stdio/fseek.cpp @@ -9,12 +9,17 @@ #include "src/stdio/fseek.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fseek, (::FILE * stream, long offset, int whence)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->seek(offset, whence); + auto result = + reinterpret_cast<__llvm_libc::File *>(stream)->seek(offset, whence); + if (result.has_error()) + errno = result.get_error(); + return result.get_value(); } } // namespace __llvm_libc diff --git a/libc/src/stdio/ftell.cpp b/libc/src/stdio/ftell.cpp --- a/libc/src/stdio/ftell.cpp +++ b/libc/src/stdio/ftell.cpp @@ -9,12 +9,16 @@ #include "src/stdio/ftell.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(long, ftell, (::FILE * stream)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->tell(); + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->tell(); + if (result.has_error()) + errno = result.get_error(); + return result.get_value(); } } // namespace __llvm_libc diff --git a/libc/src/stdio/fwrite.cpp b/libc/src/stdio/fwrite.cpp --- a/libc/src/stdio/fwrite.cpp +++ b/libc/src/stdio/fwrite.cpp @@ -9,6 +9,7 @@ #include "src/stdio/fwrite.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { @@ -18,9 +19,12 @@ ::FILE *stream)) { if (size == 0 || nmemb == 0) return 0; - return reinterpret_cast<__llvm_libc::File *>(stream)->write(buffer, - size * nmemb) / - size; + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write( + buffer, size * nmemb); + if (result.has_error()) + errno = result.get_error(); + + return result.get_value() / size; } } // namespace __llvm_libc diff --git a/libc/src/stdio/fwrite_unlocked.cpp b/libc/src/stdio/fwrite_unlocked.cpp --- a/libc/src/stdio/fwrite_unlocked.cpp +++ b/libc/src/stdio/fwrite_unlocked.cpp @@ -9,6 +9,7 @@ #include "src/stdio/fwrite_unlocked.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { @@ -16,8 +17,15 @@ LLVM_LIBC_FUNCTION(size_t, fwrite_unlocked, (const void *__restrict buffer, size_t size, size_t nmemb, ::FILE *stream)) { - return reinterpret_cast<__llvm_libc::File *>(stream)->write_unlocked( + + if (size == 0 || nmemb == 0) + return 0; + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write_unlocked( buffer, size * nmemb); + if (result.has_error()) + errno = result.get_error(); + + return result.get_value() / size; } } // namespace __llvm_libc diff --git a/libc/src/stdio/getc.cpp b/libc/src/stdio/getc.cpp --- a/libc/src/stdio/getc.cpp +++ b/libc/src/stdio/getc.cpp @@ -9,13 +9,18 @@ #include "src/stdio/getc.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, getc, (::FILE * stream)) { unsigned char c; - size_t r = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1); + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read(&c, 1); + size_t r = result.get_value(); + if (result.has_error()) + errno = result.get_error(); + if (r != 1) return EOF; return c; diff --git a/libc/src/stdio/getc_unlocked.cpp b/libc/src/stdio/getc_unlocked.cpp --- a/libc/src/stdio/getc_unlocked.cpp +++ b/libc/src/stdio/getc_unlocked.cpp @@ -9,14 +9,19 @@ #include "src/stdio/getc_unlocked.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, getc_unlocked, (::FILE * stream)) { unsigned char c; - size_t r = + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(&c, 1); + size_t r = result.get_value(); + if (result.has_error()) + errno = result.get_error(); + if (r != 1) return EOF; return c; diff --git a/libc/src/stdio/printf_core/file_writer.cpp b/libc/src/stdio/printf_core/file_writer.cpp --- a/libc/src/stdio/printf_core/file_writer.cpp +++ b/libc/src/stdio/printf_core/file_writer.cpp @@ -16,8 +16,9 @@ namespace printf_core { int FileWriter::write(const char *__restrict to_write, size_t len) { - int written = file->write_unlocked(to_write, len); - if (written != static_cast(len)) + auto result = file->write_unlocked(to_write, len); + int written = result.get_value(); + if (written != static_cast(len) || result.has_error()) written = FILE_WRITE_ERROR; if (file->error_unlocked()) written = FILE_STATUS_ERROR; diff --git a/libc/src/stdio/putc.cpp b/libc/src/stdio/putc.cpp --- a/libc/src/stdio/putc.cpp +++ b/libc/src/stdio/putc.cpp @@ -9,13 +9,19 @@ #include "src/stdio/putc.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) { unsigned char uc = static_cast(c); - size_t written = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1); + + auto result = reinterpret_cast<__llvm_libc::File *>(stream)->write(&uc, 1); + if (result.has_error()) + errno = result.get_error(); + size_t written = result.get_value(); + if (1 != written) { // The stream should be in an error state in this case. return EOF; diff --git a/libc/src/stdio/putchar.cpp b/libc/src/stdio/putchar.cpp --- a/libc/src/stdio/putchar.cpp +++ b/libc/src/stdio/putchar.cpp @@ -9,13 +9,19 @@ #include "src/stdio/putchar.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, putchar, (int c)) { unsigned char uc = static_cast(c); - size_t written = __llvm_libc::stdout->write(&uc, 1); + + auto result = __llvm_libc::stdout->write(&uc, 1); + if (result.has_error()) + errno = result.get_error(); + size_t written = result.get_value(); + if (1 != written) { // The stream should be in an error state in this case. return EOF; diff --git a/libc/src/stdio/puts.cpp b/libc/src/stdio/puts.cpp --- a/libc/src/stdio/puts.cpp +++ b/libc/src/stdio/puts.cpp @@ -10,18 +10,25 @@ #include "src/__support/CPP/string_view.h" #include "src/__support/File/file.h" +#include #include namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) { cpp::string_view str_view(str); - size_t written = __llvm_libc::stdout->write(str, str_view.size()); + auto result = __llvm_libc::stdout->write(str, str_view.size()); + if (result.has_error()) + errno = result.get_error(); + size_t written = result.get_value(); if (str_view.size() != written) { // The stream should be in an error state in this case. return EOF; } - written = __llvm_libc::stdout->write("\n", 1); + result = __llvm_libc::stdout->write("\n", 1); + if (result.has_error()) + errno = result.get_error(); + written = result.get_value(); if (1 != written) { // The stream should be in an error state in this case. return EOF; diff --git a/libc/src/stdio/scanf_core/file_reader.cpp b/libc/src/stdio/scanf_core/file_reader.cpp --- a/libc/src/stdio/scanf_core/file_reader.cpp +++ b/libc/src/stdio/scanf_core/file_reader.cpp @@ -15,7 +15,8 @@ char FileReader::get_char() { char tiny_buff = 0; - if (file->read_unlocked(&tiny_buff, 1) != 1) + auto result = file->read_unlocked(&tiny_buff, 1); + if (result.get_value() != 1 || result.has_error()) return 0; return tiny_buff; } diff --git a/libc/test/src/__support/File/file_test.cpp b/libc/test/src/__support/File/file_test.cpp --- a/libc/test/src/__support/File/file_test.cpp +++ b/libc/test/src/__support/File/file_test.cpp @@ -10,12 +10,12 @@ #include "utils/UnitTest/MemoryMatcher.h" #include "utils/UnitTest/Test.h" -#include #include #include using ModeFlags = __llvm_libc::File::ModeFlags; using MemoryView = __llvm_libc::memory::testing::MemoryView; +using __llvm_libc::FileResult; class StringFile : public __llvm_libc::File { static constexpr size_t SIZE = 512; @@ -24,11 +24,14 @@ size_t eof_marker; bool write_append; - static size_t str_read(__llvm_libc::File *f, void *data, size_t len); - static size_t str_write(__llvm_libc::File *f, const void *data, size_t len); - static long str_seek(__llvm_libc::File *f, long offset, int whence); - static int str_close(__llvm_libc::File *f) { return 0; } - static int str_flush(__llvm_libc::File *f) { return 0; } + static FileResult str_read(__llvm_libc::File *f, void *data, + size_t len); + static FileResult str_write(__llvm_libc::File *f, const void *data, + size_t len); + static FileResult str_seek(__llvm_libc::File *f, long offset, + int whence); + static FileResult str_close(__llvm_libc::File *f) { return 0; } + static FileResult str_flush(__llvm_libc::File *f) { return 0; } public: explicit StringFile(char *buffer, size_t buflen, int bufmode, bool owned, @@ -67,7 +70,8 @@ } }; -size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) { +FileResult StringFile::str_read(__llvm_libc::File *f, void *data, + size_t len) { StringFile *sf = static_cast(f); if (sf->pos >= sf->eof_marker) return 0; @@ -78,8 +82,8 @@ return i; } -size_t StringFile::str_write(__llvm_libc::File *f, const void *data, - size_t len) { +FileResult StringFile::str_write(__llvm_libc::File *f, const void *data, + size_t len) { StringFile *sf = static_cast(f); if (sf->write_append) sf->pos = sf->eof_marker; @@ -94,7 +98,8 @@ return i; } -long StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) { +FileResult StringFile::str_seek(__llvm_libc::File *f, long offset, + int whence) { StringFile *sf = static_cast(f); if (whence == SEEK_SET) sf->pos = offset; @@ -119,33 +124,35 @@ StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w"); - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream - ASSERT_EQ(f->flush(), 0); + ASSERT_EQ(f->flush().get_value(), 0); EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available EXPECT_STREQ(f->get_str(), data); f->reset(); ASSERT_EQ(f->get_pos(), size_t(0)); - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream // The second write should trigger a buffer flush. - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_GE(f->get_pos(), size_t(0)); - ASSERT_EQ(f->flush(), 0); + ASSERT_EQ(f->flush().get_value(), 0); EXPECT_EQ(f->get_pos(), 2 * sizeof(data)); MemoryView src1("hello, file\0hello, file", sizeof(data) * 2), dst1(f->get_str(), sizeof(data) * 2); EXPECT_MEM_EQ(src1, dst1); char read_data[sizeof(data)]; - // This is not a readable file. - EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0)); - EXPECT_TRUE(f->error()); - EXPECT_NE(errno, 0); - errno = 0; + { + // This is not a readable file. + auto result = f->read(read_data, sizeof(data)); + EXPECT_EQ(result.get_value(), size_t(0)); + EXPECT_TRUE(f->error()); + EXPECT_TRUE(result.has_error()); + } - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, WriteLineBuffered) { @@ -162,8 +169,8 @@ StringFile *f_full = new_string_file(file_buffer_full, FILE_BUFFER_SIZE, _IOFBF, false, "w"); - ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data))); - ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data)).get_value()); + ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data)).get_value()); EXPECT_EQ(f_line->get_pos(), size_t(6)); // buffer after the newline EXPECT_EQ(f_full->get_pos(), size_t(0)); // buffer all of data @@ -175,12 +182,12 @@ const char data2[] = "longer for an \n overflow"; - ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2))); + ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2)).get_value()); // The line buffer's initial contents should be " file\0" // Writing data2 should write up until the newline, even though that doesn't // all fit in the buffer. - ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2))); + ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2)).get_value()); // The full buffer's initial contents should be "hello\n file\0" // Writing data2 should cause a flush of the buffer, as well as the remainder // to be written directly since it doesn't fit in the buffer. @@ -196,16 +203,16 @@ dst_full_final(f_full->get_str(), 37); EXPECT_MEM_EQ(src3, dst_full_final); - ASSERT_EQ(f_line->flush(), 0); - ASSERT_EQ(f_full->flush(), 0); + ASSERT_EQ(f_line->flush().get_value(), 0); + ASSERT_EQ(f_full->flush().get_value(), 0); EXPECT_EQ(f_line->get_pos(), sizeof(data) + sizeof(data2)); MemoryView dst_line_final(f_line->get_str(), 37); EXPECT_MEM_EQ(src3, dst_line_final); EXPECT_MEM_EQ(src3, dst_full_final); - ASSERT_EQ(f_line->close(), 0); - ASSERT_EQ(f_full->close(), 0); + ASSERT_EQ(f_line->close().get_value(), 0); + ASSERT_EQ(f_full->close().get_value(), 0); } TEST(LlvmLibcFileTest, WriteUnbuffered) { @@ -215,12 +222,12 @@ StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, _IONBF, false, "w"); - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_EQ(f->get_pos(), sizeof(data)); // no buffering means this is written immediately. EXPECT_STREQ(f->get_str(), data); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, ReadOnly) { @@ -233,7 +240,7 @@ constexpr size_t READ_SIZE = sizeof(initial_content) / 2; char read_data[READ_SIZE]; - ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); EXPECT_FALSE(f->iseof()); // Reading less than file buffer worth will still read one // full buffer worth of data. @@ -246,14 +253,14 @@ // Reading another buffer worth should read out everything in // the file. - ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); EXPECT_FALSE(f->iseof()); MemoryView src2(initial_content + READ_SIZE, READ_SIZE), dst2(read_data, READ_SIZE); EXPECT_MEM_EQ(src2, dst2); // Another read should trigger an EOF. - ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); EXPECT_TRUE(f->iseof()); // Reset the pos to the beginning of the file which should allow @@ -261,17 +268,19 @@ for (size_t i = 0; i < READ_SIZE; ++i) read_data[i] = 0; f->seek(0, SEEK_SET); - ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE); EXPECT_MEM_EQ(src3, dst3); - // This is not a writable file. - EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0)); - EXPECT_TRUE(f->error()); - EXPECT_NE(errno, 0); - errno = 0; + { + // This is not a writable file. + auto result = f->write(initial_content, sizeof(initial_content)); + EXPECT_EQ(result.get_value(), size_t(0)); + EXPECT_TRUE(f->error()); + EXPECT_TRUE(result.has_error()); + } - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, ReadSeekCurAndRead) { @@ -285,15 +294,15 @@ constexpr size_t READ_SIZE = 5; char data[READ_SIZE]; data[READ_SIZE - 1] = '\0'; - ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(f->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "1234"); - ASSERT_EQ(f->seek(5, SEEK_CUR), 0); - ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(f->seek(5, SEEK_CUR).get_value(), 0); + ASSERT_EQ(f->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "0987"); - ASSERT_EQ(f->seek(-5, SEEK_CUR), 0); - ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(f->seek(-5, SEEK_CUR).get_value(), 0); + ASSERT_EQ(f->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "9098"); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, AppendOnly) { @@ -307,20 +316,24 @@ constexpr size_t READ_SIZE = 5; char read_data[READ_SIZE]; - // This is not a readable file. - ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0)); - EXPECT_TRUE(f->error()); - EXPECT_NE(errno, 0); - errno = 0; + + { + // This is not a readable file. + auto result = f->read(read_data, READ_SIZE); + EXPECT_EQ(result.get_value(), size_t(0)); + EXPECT_TRUE(f->error()); + EXPECT_TRUE(result.has_error()); + } // Write should succeed but will be buffered in the file stream. - ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data)); + ASSERT_EQ(f->write(write_data, sizeof(write_data)).get_value(), + sizeof(write_data)); EXPECT_EQ(f->get_pos(), size_t(0)); // Flushing will write to the file. - EXPECT_EQ(f->flush(), int(0)); + EXPECT_EQ(f->flush().get_value(), int(0)); EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content)); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, WriteUpdate) { @@ -330,17 +343,17 @@ StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w+"); - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream - ASSERT_EQ(f->seek(0, SEEK_SET), 0); + ASSERT_EQ(f->seek(0, SEEK_SET).get_value(), 0); // Seek flushes the stream buffer so we can read the previously written data. char read_data[sizeof(data)]; - ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data)); + ASSERT_EQ(f->read(read_data, sizeof(data)).get_value(), sizeof(data)); EXPECT_STREQ(read_data, data); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, ReadUpdate) { @@ -353,7 +366,7 @@ constexpr size_t READ_SIZE = sizeof(initial_content) / 2; char read_data[READ_SIZE]; - ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); EXPECT_FALSE(f->iseof()); // Reading less than file buffer worth will still read one // full buffer worth of data. @@ -364,16 +377,17 @@ MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE); EXPECT_MEM_EQ(src1, dst1); - ASSERT_EQ(f->seek(0, SEEK_SET), 0); + ASSERT_EQ(f->seek(0, SEEK_SET).get_value(), 0); const char write_data[] = "hello, file"; - ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data))); + ASSERT_EQ(sizeof(write_data), + f->write(write_data, sizeof(write_data)).get_value()); EXPECT_STREQ(file_buffer, write_data); - ASSERT_EQ(f->flush(), 0); + ASSERT_EQ(f->flush().get_value(), 0); MemoryView dst2(f->get_str(), sizeof(write_data)), src2(write_data, sizeof(write_data)); EXPECT_MEM_EQ(src2, dst2); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, AppendUpdate) { @@ -385,17 +399,17 @@ new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a+"); f->reset_and_fill(initial_content, sizeof(initial_content)); - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream - ASSERT_EQ(f->flush(), 0); + ASSERT_EQ(f->flush().get_value(), 0); // The flush should write |data| to the endof the file. EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content)); - ASSERT_EQ(f->seek(0, SEEK_SET), 0); + ASSERT_EQ(f->seek(0, SEEK_SET).get_value(), 0); // Seeking to the beginning of the file should not affect the place // where write happens. - ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); - ASSERT_EQ(f->flush(), 0); + ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).get_value()); + ASSERT_EQ(f->flush().get_value(), 0); EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content)); MemoryView src1(initial_content, sizeof(initial_content)), dst1(f->get_str(), sizeof(initial_content)); @@ -408,14 +422,14 @@ EXPECT_MEM_EQ(src3, dst3); // Reads can happen from any point. - ASSERT_EQ(f->seek(0, SEEK_SET), 0); + ASSERT_EQ(f->seek(0, SEEK_SET).get_value(), 0); constexpr size_t READ_SIZE = 10; char read_data[READ_SIZE]; - ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); + ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).get_value()); MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE); EXPECT_MEM_EQ(src4, dst4); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, SmallBuffer) { @@ -426,13 +440,13 @@ StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w"); - ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE)); + ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE).get_value()); // Since data much larger than the buffer is being written, all of it should // be available in the file without a flush operation. EXPECT_EQ(f->get_pos(), sizeof(WRITE_DATA)); ASSERT_STREQ(f->get_str(), WRITE_DATA); - ASSERT_EQ(f->close(), 0); + ASSERT_EQ(f->close().get_value(), 0); } TEST(LlvmLibcFileTest, ZeroLengthBuffer) { @@ -442,9 +456,9 @@ StringFile *f_lbf = new_string_file(nullptr, 0, _IOLBF, true, "w"); StringFile *f_nbf = new_string_file(nullptr, 0, _IONBF, true, "w"); - ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE)); - ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE)); - ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE)); + ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).get_value()); + ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).get_value()); + ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).get_value()); // Since there is no buffer space, all of the written data should // be available in the file without a flush operation. EXPECT_EQ(f_fbf->get_pos(), sizeof(WRITE_DATA)); @@ -454,9 +468,9 @@ ASSERT_STREQ(f_lbf->get_str(), WRITE_DATA); ASSERT_STREQ(f_nbf->get_str(), WRITE_DATA); - ASSERT_EQ(f_fbf->close(), 0); - ASSERT_EQ(f_lbf->close(), 0); - ASSERT_EQ(f_nbf->close(), 0); + ASSERT_EQ(f_fbf->close().get_value(), 0); + ASSERT_EQ(f_lbf->close().get_value(), 0); + ASSERT_EQ(f_nbf->close().get_value(), 0); } TEST(LlvmLibcFileTest, WriteNothing) { @@ -473,15 +487,15 @@ StringFile *f_nbf = new_string_file(file_buffer_nbf, FILE_BUFFER_SIZE, _IONBF, false, "w"); - ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE)); - ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE)); - ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE)); + ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).get_value()); + ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).get_value()); + ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).get_value()); ASSERT_FALSE(f_fbf->error_unlocked()); ASSERT_FALSE(f_lbf->error_unlocked()); ASSERT_FALSE(f_nbf->error_unlocked()); - ASSERT_EQ(f_fbf->close(), 0); - ASSERT_EQ(f_lbf->close(), 0); - ASSERT_EQ(f_nbf->close(), 0); + ASSERT_EQ(f_fbf->close().get_value(), 0); + ASSERT_EQ(f_lbf->close().get_value(), 0); + ASSERT_EQ(f_nbf->close().get_value(), 0); } diff --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp --- a/libc/test/src/__support/File/platform_file_test.cpp +++ b/libc/test/src/__support/File/platform_file_test.cpp @@ -19,96 +19,98 @@ constexpr char FILENAME[] = "testdata/create_write_close_and_readback.test"; File *file = __llvm_libc::openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->write(TEXT, TEXT_SIZE).get_value(), TEXT_SIZE); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); char data[sizeof(TEXT)]; - ASSERT_EQ(file->read(data, TEXT_SIZE), TEXT_SIZE); + ASSERT_EQ(file->read(data, TEXT_SIZE).get_value(), TEXT_SIZE); data[TEXT_SIZE] = '\0'; ASSERT_STREQ(data, TEXT); // Reading more data should trigger EOF. - ASSERT_EQ(file->read(data, TEXT_SIZE), size_t(0)); + ASSERT_EQ(file->read(data, TEXT_SIZE).get_value(), size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, CreateWriteSeekAndReadBack) { constexpr char FILENAME[] = "testdata/create_write_seek_and_readback.test"; File *file = __llvm_libc::openfile(FILENAME, "w+"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE); + ASSERT_EQ(file->write(TEXT, TEXT_SIZE).get_value(), TEXT_SIZE); - ASSERT_EQ(file->seek(0, SEEK_SET), 0); + ASSERT_EQ(file->seek(0, SEEK_SET).get_value(), 0); char data[sizeof(TEXT)]; - ASSERT_EQ(file->read(data, TEXT_SIZE), TEXT_SIZE); + ASSERT_EQ(file->read(data, TEXT_SIZE).get_value(), TEXT_SIZE); data[TEXT_SIZE] = '\0'; ASSERT_STREQ(data, TEXT); // Reading more data should trigger EOF. - ASSERT_EQ(file->read(data, TEXT_SIZE), size_t(0)); + ASSERT_EQ(file->read(data, TEXT_SIZE).get_value(), size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, CreateAppendCloseAndReadBack) { constexpr char FILENAME[] = "testdata/create_append_close_and_readback.test"; File *file = __llvm_libc::openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->write(TEXT, TEXT_SIZE).get_value(), TEXT_SIZE); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "a"); ASSERT_FALSE(file == nullptr); constexpr char APPEND_TEXT[] = " Append Text"; constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1; - ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).get_value(), + APPEND_TEXT_SIZE); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); constexpr size_t READ_SIZE = TEXT_SIZE + APPEND_TEXT_SIZE; char data[READ_SIZE + 1]; - ASSERT_EQ(file->read(data, READ_SIZE), READ_SIZE); + ASSERT_EQ(file->read(data, READ_SIZE).get_value(), READ_SIZE); data[READ_SIZE] = '\0'; ASSERT_STREQ(data, "Hello, File Append Text"); // Reading more data should trigger EOF. - ASSERT_EQ(file->read(data, READ_SIZE), size_t(0)); + ASSERT_EQ(file->read(data, READ_SIZE).get_value(), size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, CreateAppendSeekAndReadBack) { constexpr char FILENAME[] = "testdata/create_append_seek_and_readback.test"; File *file = __llvm_libc::openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->write(TEXT, TEXT_SIZE), TEXT_SIZE); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->write(TEXT, TEXT_SIZE).get_value(), TEXT_SIZE); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "a+"); ASSERT_FALSE(file == nullptr); constexpr char APPEND_TEXT[] = " Append Text"; constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1; - ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE); + ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).get_value(), + APPEND_TEXT_SIZE); - ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END), 0); + ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END).get_value(), 0); char data[APPEND_TEXT_SIZE + 1]; - ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE), APPEND_TEXT_SIZE); + ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).get_value(), APPEND_TEXT_SIZE); data[APPEND_TEXT_SIZE] = '\0'; ASSERT_STREQ(data, APPEND_TEXT); // Reading more data should trigger EOF. - ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE), size_t(0)); + ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).get_value(), size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, LargeFile) { @@ -124,24 +126,24 @@ constexpr int REPEAT = 5; for (int i = 0; i < REPEAT; ++i) { - ASSERT_EQ(file->write(write_data, DATA_SIZE), DATA_SIZE); + ASSERT_EQ(file->write(write_data, DATA_SIZE).get_value(), DATA_SIZE); } - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); constexpr size_t READ_SIZE = DATA_SIZE * REPEAT; char data[READ_SIZE] = {0}; - ASSERT_EQ(file->read(data, READ_SIZE), READ_SIZE); + ASSERT_EQ(file->read(data, READ_SIZE).get_value(), READ_SIZE); for (size_t i = 0; i < READ_SIZE; ++i) ASSERT_EQ(data[i], BYTE); // Reading more data should trigger EOF. - ASSERT_EQ(file->read(data, 1), size_t(0)); + ASSERT_EQ(file->read(data, 1).get_value(), size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) { @@ -149,8 +151,9 @@ File *file = __llvm_libc::openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); constexpr char CONTENT[] = "1234567890987654321"; - ASSERT_EQ(sizeof(CONTENT) - 1, file->write(CONTENT, sizeof(CONTENT) - 1)); - ASSERT_EQ(0, file->close()); + ASSERT_EQ(sizeof(CONTENT) - 1, + file->write(CONTENT, sizeof(CONTENT) - 1).get_value()); + ASSERT_EQ(0, file->close().get_value()); file = __llvm_libc::openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); @@ -158,16 +161,16 @@ constexpr size_t READ_SIZE = 5; char data[READ_SIZE]; data[READ_SIZE - 1] = '\0'; - ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(file->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "1234"); - ASSERT_EQ(file->seek(5, SEEK_CUR), 0); - ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(file->seek(5, SEEK_CUR).get_value(), 0); + ASSERT_EQ(file->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "0987"); - ASSERT_EQ(file->seek(-5, SEEK_CUR), 0); - ASSERT_EQ(file->read(data, READ_SIZE - 1), READ_SIZE - 1); + ASSERT_EQ(file->seek(-5, SEEK_CUR).get_value(), 0); + ASSERT_EQ(file->read(data, READ_SIZE - 1).get_value(), READ_SIZE - 1); ASSERT_STREQ(data, "9098"); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, IncorrectOperation) { @@ -176,24 +179,24 @@ File *file = __llvm_libc::openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->read(data, 1), size_t(0)); // Cannot read + ASSERT_EQ(file->read(data, 1).get_value(), size_t(0)); // Cannot read ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->write(data, 1), size_t(0)); // Cannot write + ASSERT_EQ(file->write(data, 1).get_value(), size_t(0)); // Cannot write ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); file = __llvm_libc::openfile(FILENAME, "a"); ASSERT_FALSE(file == nullptr); - ASSERT_EQ(file->read(data, 1), size_t(0)); // Cannot read + ASSERT_EQ(file->read(data, 1).get_value(), size_t(0)); // Cannot read ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(file->close(), 0); + ASSERT_EQ(file->close().get_value(), 0); } TEST(LlvmLibcPlatformFileTest, StdOutStdErrSmokeTest) { diff --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp --- a/libc/test/src/stdio/fileop_test.cpp +++ b/libc/test/src/stdio/fileop_test.cpp @@ -78,6 +78,11 @@ __llvm_libc::clearerr(file); ASSERT_EQ(__llvm_libc::ferror(file), 0); + errno = 0; + ASSERT_EQ(__llvm_libc::fwrite("nothing", 1, 1, file), size_t(0)); + ASSERT_NE(errno, 0); + errno = 0; + ASSERT_EQ(__llvm_libc::fclose(file), 0); // Now try puts. @@ -90,6 +95,12 @@ __llvm_libc::clearerr(file); ASSERT_EQ(__llvm_libc::ferror(file), 0); + // This is not a readable file. + errno = 0; + ASSERT_EQ(__llvm_libc::fread(data, 1, 1, file), size_t(0)); + ASSERT_NE(errno, 0); + errno = 0; + ASSERT_EQ(0, __llvm_libc::fclose(file)); file = __llvm_libc::fopen(FILENAME, "r"); @@ -100,6 +111,21 @@ read_data[sizeof(CONTENT) - 1] = '\0'; ASSERT_STREQ(read_data, CONTENT); ASSERT_EQ(__llvm_libc::fclose(file), 0); + + // Check that the other functions correctly set errno. + + errno = 0; + ASSERT_NE(__llvm_libc::fseek(file, 0, SEEK_SET), 0); + EXPECT_NE(errno, 0); + + errno = 0; + ASSERT_NE(__llvm_libc::fclose(file), 0); + EXPECT_NE(errno, 0); + + errno = 0; + ASSERT_EQ(__llvm_libc::fopen("INVALID FILE NAME", "r"), + static_cast(nullptr)); + EXPECT_NE(errno, 0); } TEST(LlvmLibcFILETest, FFlush) { @@ -132,6 +158,7 @@ constexpr size_t WRITE_NMEMB = sizeof(WRITE_DATA) / sizeof(MyStruct); constexpr char FILENAME[] = "testdata/fread_fwrite.test"; + errno = 0; FILE *file = __llvm_libc::fopen(FILENAME, "w"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(size_t(0), __llvm_libc::fwrite(WRITE_DATA, 0, 1, file)); diff --git a/libc/test/src/stdio/ftell_test.cpp b/libc/test/src/stdio/ftell_test.cpp --- a/libc/test/src/stdio/ftell_test.cpp +++ b/libc/test/src/stdio/ftell_test.cpp @@ -16,6 +16,7 @@ #include "src/stdio/setvbuf.h" #include "utils/UnitTest/Test.h" +#include #include class LlvmLibcFTellTest : public __llvm_libc::testing::Test { @@ -53,6 +54,10 @@ ASSERT_EQ(size_t(__llvm_libc::ftell(file)), READ_SIZE); ASSERT_EQ(0, __llvm_libc::fclose(file)); + + errno = 0; + ASSERT_EQ(__llvm_libc::ftell(file), long(-1)); + ASSERT_NE(errno, 0); } };