[libc-commits] [libc] 9beb8d1 - [libc] move errno out of file internals

Michael Jones via libc-commits libc-commits at lists.llvm.org
Mon Dec 12 13:11:51 PST 2022


Author: Michael Jones
Date: 2022-12-12T13:11:45-08:00
New Revision: 9beb8d1109c00575c9a30df5f931804cd81ad891

URL: https://github.com/llvm/llvm-project/commit/9beb8d1109c00575c9a30df5f931804cd81ad891
DIFF: https://github.com/llvm/llvm-project/commit/9beb8d1109c00575c9a30df5f931804cd81ad891.diff

LOG: [libc] move errno out of file internals

Now errno is only set by the terminal entrypoints, and not the internal
implementations. This patch is part of the larger effort to not set
errno in libc internal code: https://github.com/llvm/llvm-project/issues/59278

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D139576

Added: 
    libc/src/__support/CPP/expected.h
    libc/src/__support/error_or.h

Modified: 
    libc/src/__support/CMakeLists.txt
    libc/src/__support/CPP/CMakeLists.txt
    libc/src/__support/File/CMakeLists.txt
    libc/src/__support/File/file.cpp
    libc/src/__support/File/file.h
    libc/src/__support/File/linux_file.cpp
    libc/src/__support/threads/linux/CMakeLists.txt
    libc/src/__support/threads/linux/thread.cpp
    libc/src/pthread/pthread_setname_np.cpp
    libc/src/stdio/CMakeLists.txt
    libc/src/stdio/fclose.cpp
    libc/src/stdio/fflush.cpp
    libc/src/stdio/fgetc.cpp
    libc/src/stdio/fgetc_unlocked.cpp
    libc/src/stdio/fgets.cpp
    libc/src/stdio/fopen.cpp
    libc/src/stdio/fopencookie.cpp
    libc/src/stdio/fputc.cpp
    libc/src/stdio/fputs.cpp
    libc/src/stdio/fread.cpp
    libc/src/stdio/fread_unlocked.cpp
    libc/src/stdio/fseek.cpp
    libc/src/stdio/ftell.cpp
    libc/src/stdio/fwrite.cpp
    libc/src/stdio/fwrite_unlocked.cpp
    libc/src/stdio/getc.cpp
    libc/src/stdio/getc_unlocked.cpp
    libc/src/stdio/printf_core/file_writer.cpp
    libc/src/stdio/putc.cpp
    libc/src/stdio/putchar.cpp
    libc/src/stdio/puts.cpp
    libc/src/stdio/scanf_core/file_reader.cpp
    libc/test/src/__support/File/file_test.cpp
    libc/test/src/__support/File/platform_file_test.cpp
    libc/test/src/stdio/fileop_test.cpp
    libc/test/src/stdio/ftell_test.cpp

Removed: 
    libc/src/__support/CPP/error.h


################################################################################
diff  --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index e72437b833087..ff9a20d1590f1 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -30,6 +30,14 @@ add_header_library(
     sanitizer.h
 )
 
+add_header_library(
+  error_or
+  HDRS
+    error_or.h
+  DEPENDS
+    libc.src.__support.CPP.expected
+)
+
 add_header_library(
   ctype_utils
   HDRS

diff  --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index cb24b46e69695..c6e3733d3186e 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -94,7 +94,7 @@ add_header_library(
 )
 
 add_header_library(
-  error
+  expected
   HDRS
-    error.h
+    expected.h
 )

diff  --git a/libc/src/__support/CPP/error.h b/libc/src/__support/CPP/error.h
deleted file mode 100644
index 1e7a501dc7df4..0000000000000
--- a/libc/src/__support/CPP/error.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//===-- A simple classes to manage error return vals ----------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_ERROR_H
-#define LLVM_LIBC_SRC_SUPPORT_CPP_ERROR_H
-
-namespace __llvm_libc {
-namespace cpp {
-// Many C functions return an error val and/or the actual result of the
-// evaluation/operation performed by the function. This file defines a simple
-// convenience data structure to encapsulate the error and the actual val in
-// a single place.
-
-struct Error {
-  int error_code;
-};
-
-// This class is implemented in a simple fashion as the intention is it add
-// more generality as required. Currently, it only supports simple copyable
-// types for T.
-template <typename T> class ErrorOr {
-  bool is_error;
-
-  union {
-    T val;
-    Error error;
-  };
-
-public:
-  ErrorOr(const T &value) : is_error(false), val(value) {}
-
-  ErrorOr(const Error &error) : is_error(true), error(error) {}
-
-  operator bool() { return !is_error; }
-
-  operator T &() { return val; }
-
-  T &value() { return val; }
-
-  int error_code() { return is_error ? error.error_code : 0; }
-};
-
-} // namespace cpp
-} // namespace __llvm_libc
-
-#endif // LLVM_LIBC_SRC_SUPPORT_CPP_ERROR_H

diff  --git a/libc/src/__support/CPP/expected.h b/libc/src/__support/CPP/expected.h
new file mode 100644
index 0000000000000..aab80c2d983d6
--- /dev/null
+++ b/libc/src/__support/CPP/expected.h
@@ -0,0 +1,52 @@
+//===-- Holds an expected or unexpected value -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SUPPORT_CPP_EXPECTED_H
+#define LLVM_LIBC_SUPPORT_CPP_EXPECTED_H
+
+namespace __llvm_libc::cpp {
+
+// This is used to hold an unexpected value so that a 
diff erent constructor is
+// selected.
+template <class T> class unexpected {
+  T value;
+
+public:
+  constexpr explicit unexpected(T value) : value(value) {}
+  constexpr T error() { return value; }
+};
+
+template <class T, class E> class expected {
+  union {
+    T exp;
+    E unexp;
+  };
+  bool is_expected;
+
+public:
+  constexpr expected(T exp) : exp(exp), is_expected(true) {}
+  constexpr expected(unexpected<E> unexp)
+      : unexp(unexp.error()), is_expected(false) {}
+
+  constexpr bool has_value() { return is_expected; }
+
+  constexpr T value() { return exp; }
+  constexpr E error() { return unexp; }
+
+  constexpr operator T() { return exp; }
+  constexpr operator bool() { return is_expected; }
+
+  constexpr T &operator*() { return exp; }
+  constexpr const T &operator*() const { return exp; }
+  constexpr T *operator->() { return &exp; }
+  constexpr const T *operator->() const { return &exp; }
+};
+
+} // namespace __llvm_libc::cpp
+
+#endif // LLVM_LIBC_SUPPORT_CPP_EXPECTED_H

diff  --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index d92346d463afa..dd99e0df21105 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -14,7 +14,7 @@ add_object_library(
     libc.include.errno
     libc.src.__support.CPP.span
     libc.src.__support.threads.mutex
-    libc.src.errno.errno
+    libc.src.__support.error_or
 )
 
 add_object_library(
@@ -41,6 +41,7 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}_file.cpp)
       libc.include.sys_syscall
       libc.src.__support.OSUtil.osutil
       libc.src.errno.errno
+      libc.src.__support.error_or
   )
 endif()
 

diff  --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index edb2467929f2d..1b6d2cb859892 100644
--- 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 <errno.h>
+#include <errno.h> // For error macros
 #include <stdio.h>
 #include <stdlib.h>
 
 namespace __llvm_libc {
 
-size_t File::write_unlocked(const void *data, size_t len) {
+FileIOResult 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,27 @@ size_t File::write_unlocked(const void *data, size_t len) {
   }
 }
 
-size_t File::write_unlocked_nbf(const uint8_t *data, size_t len) {
+FileIOResult 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 write_result = platform_write(this, buf, write_size);
     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) {
+    if (write_result < 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, write_result.error};
     }
   }
 
-  size_t written = platform_write(this, data, len);
-  if (written < len)
+  auto write_result = platform_write(this, data, len);
+  if (write_result < len)
     err = true;
-  return written;
+  return write_result;
 }
 
-size_t File::write_unlocked_fbf(const uint8_t *data, size_t len) {
+FileIOResult 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 +96,17 @@ size_t File::write_unlocked_fbf(const uint8_t *data, size_t len) {
   // 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.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 (buf_result.has_error() || bytes_written < write_size) {
     err = true;
-    return bytes_written <= init_pos ? 0 : bytes_written - init_pos;
+    return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
+            buf_result.error};
   }
 
   // The second piece is handled basically the same as the first, although we
@@ -114,21 +118,22 @@ size_t File::write_unlocked_fbf(const uint8_t *data, size_t len) {
       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.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 (result.has_error() || bytes_written < remainder.size()) {
       err = true;
-      return primary.size() + bytes_written;
+      return {primary.size() + bytes_written, result.error};
     }
   }
 
   return len;
 }
 
-size_t File::write_unlocked_lbf(const uint8_t *data, size_t len) {
+FileIOResult 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 +180,10 @@ size_t File::write_unlocked_lbf(const uint8_t *data, size_t len) {
   return len;
 }
 
-size_t File::read_unlocked(void *data, size_t len) {
+FileIOResult 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 +214,33 @@ size_t File::read_unlocked(void *data, size_t len) {
 
   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.value;
+    if (result.has_error() || fetched_size < to_fetch) {
+      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.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 (result.has_error() || fetched_size < to_fetch) {
+    if (!result.has_error())
       eof = true;
     else
       err = true;
   }
-  return transfer_size + available_data;
+  return {transfer_size + available_data, result.error};
 }
 
 int File::ungetc_unlocked(int c) {
@@ -275,13 +281,14 @@ int File::ungetc_unlocked(int c) {
   return c;
 }
 
-int File::seek(long offset, int whence) {
+ErrorOr<int> 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);
+    if (buf_result.has_error() || buf_result.value < pos) {
       err = true;
-      return -1;
+      return Error(buf_result.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 +301,20 @@ int File::seek(long offset, int whence) {
   // 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);
-  if (platform_pos >= 0)
-    return 0;
+  auto result = platform_seek(this, offset, whence);
+  if (!result.has_value())
+    return Error(result.error());
   else
-    return -1;
+    return 0;
 }
 
-long File::tell() {
+ErrorOr<long> 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);
+  if (!result.has_value() || result.value() < 0)
+    return Error(result.error());
+  long platform_offset = result.value();
   if (prev_op == FileOp::READ)
     return platform_offset - (read_limit - pos);
   else if (prev_op == FileOp::WRITE)
@@ -320,10 +325,10 @@ long File::tell() {
 
 int 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);
+    if (buf_result.has_error() || buf_result.value < pos) {
       err = true;
-      return -1;
+      return buf_result.error;
     }
     pos = 0;
     return platform_flush(this);
@@ -336,14 +341,15 @@ int 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);
+      if (buf_result.has_error() || buf_result.value < pos) {
         err = true;
-        return -1;
+        return buf_result.error;
       }
     }
-    if (platform_close(this) != 0)
-      return -1;
+    int result = platform_close(this);
+    if (result != 0)
+      return result;
     if (own_buf)
       free(buf);
   }

diff  --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index d182ea4c81b4d..7ba6c27f2d4e1 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
 #define LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H
 
+#include "src/__support/error_or.h"
 #include "src/__support/threads/mutex.h"
 
 #include <stddef.h>
@@ -16,6 +17,18 @@
 
 namespace __llvm_libc {
 
+struct FileIOResult {
+  size_t value;
+  int error;
+
+  constexpr FileIOResult(size_t val) : value(val), error(0) {}
+  constexpr FileIOResult(size_t val, int error) : value(val), error(error) {}
+
+  constexpr bool has_error() { return error != 0; }
+
+  constexpr operator size_t() { return value; }
+};
+
 // 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,11 +39,11 @@ class File {
   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 = FileIOResult(File *, const void *, size_t);
+  using ReadFunc = FileIOResult(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 SeekFunc = ErrorOr<long>(File *, long, int);
   using CloseFunc = int(File *);
   using FlushFunc = int(File *);
 
@@ -174,26 +187,26 @@ class File {
   }
 
   // Buffered write of |len| bytes from |data| without the file lock.
-  size_t write_unlocked(const void *data, size_t len);
+  FileIOResult 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) {
+  FileIOResult 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);
+  FileIOResult 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) {
+  FileIOResult read(void *data, size_t len) {
     FileLock l(this);
     return read_unlocked(data, len);
   }
 
-  int seek(long offset, int whence);
+  ErrorOr<int> seek(long offset, int whence);
 
-  long tell();
+  ErrorOr<long> tell();
 
   // If buffer has data written to it, flush it out. Does nothing if the
   // buffer is currently being used as a read buffer.
@@ -256,9 +269,9 @@ class File {
   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);
+  FileIOResult write_unlocked_lbf(const uint8_t *data, size_t len);
+  FileIOResult write_unlocked_fbf(const uint8_t *data, size_t len);
+  FileIOResult write_unlocked_nbf(const uint8_t *data, size_t len);
 
   constexpr void adjust_buf() {
     if (read_allowed() && (buf == nullptr || bufsize == 0)) {
@@ -285,7 +298,7 @@ class File {
 
 // The implementaiton of this function is provided by the platfrom_file
 // library.
-File *openfile(const char *path, const char *mode);
+ErrorOr<File *> 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/linux_file.cpp b/libc/src/__support/File/linux_file.cpp
index 09a880743baf4..b7ff089905ff8 100644
--- 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 <errno.h>
+#include <errno.h> // For error macros
 #include <fcntl.h> // For mode_t and other flags to the open syscall
 #include <stdio.h>
 #include <stdlib.h>      // For malloc
@@ -20,9 +20,9 @@ namespace __llvm_libc {
 
 namespace {
 
-size_t write_func(File *, const void *, size_t);
-size_t read_func(File *, void *, size_t);
-long seek_func(File *, long, int);
+FileIOResult write_func(File *, const void *, size_t);
+FileIOResult read_func(File *, void *, size_t);
+ErrorOr<long> seek_func(File *, long, int);
 int close_func(File *);
 int flush_func(File *);
 
@@ -51,75 +51,70 @@ class LinuxFile : public File {
 
 namespace {
 
-size_t write_func(File *f, const void *data, size_t size) {
+FileIOResult write_func(File *f, const void *data, size_t size) {
   auto *lf = reinterpret_cast<LinuxFile *>(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) {
+FileIOResult read_func(File *f, void *buf, size_t size) {
   auto *lf = reinterpret_cast<LinuxFile *>(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) {
+ErrorOr<long> seek_func(File *f, long offset, int whence) {
   auto *lf = reinterpret_cast<LinuxFile *>(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;
-  }
+  if (ret < 0)
+    return Error(-ret);
+
   return result;
 }
 
 int close_func(File *f) {
   auto *lf = reinterpret_cast<LinuxFile *>(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 -ret;
   }
   return 0;
 }
 
 int flush_func(File *f) {
   auto *lf = reinterpret_cast<LinuxFile *>(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 -ret;
   }
   return 0;
 }
 
 } // anonymous namespace
 
-File *openfile(const char *path, const char *mode) {
+ErrorOr<File *> 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};
+    return Error(EINVAL);
   }
   long open_flags = 0;
   if (modeflags & ModeFlags(File::OpenMode::APPEND)) {
@@ -155,8 +150,8 @@ File *openfile(const char *path, const char *mode) {
 #endif
 
   if (fd < 0) {
-    errno = -fd;
-    return nullptr;
+    return Error(-fd);
+    // return {nullptr, -fd};
   }
 
   void *buffer = malloc(File::DEFAULT_BUFFER_SIZE);

diff  --git a/libc/src/__support/error_or.h b/libc/src/__support/error_or.h
new file mode 100644
index 0000000000000..7d1f82aeb2e01
--- /dev/null
+++ b/libc/src/__support/error_or.h
@@ -0,0 +1,39 @@
+//===-- A data structure for returning an error or a value ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H
+#define LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H
+
+#include "src/__support/CPP/expected.h"
+
+namespace __llvm_libc {
+
+template <class T> using ErrorOr = cpp::expected<T, int>;
+
+using Error = cpp::unexpected<int>;
+
+// template <typename T> struct ErrorOr {
+//   union {
+//     T value;
+//     int error;
+//   };
+//   bool is_error;
+
+//   constexpr ErrorOr(T value) : value(value), is_error(false) {}
+//   constexpr ErrorOr(int error, bool is_error)
+//       : error(error), is_error(is_error) {}
+
+//   constexpr bool has_error() { return is_error; }
+
+//   constexpr operator bool() { return is_error; }
+//   constexpr operator T() { return value; }
+// };
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SUPPORT_ERROR_OR_RESULT_H

diff  --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index d26b130d580e0..28593b0318dcb 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -29,7 +29,7 @@ add_object_library(
     libc.config.linux.app_h
     libc.include.sys_syscall
     libc.src.__support.CPP.atomic
-    libc.src.__support.CPP.error
+    libc.src.__support.error_or
     libc.src.__support.CPP.stringstream
     libc.src.__support.CPP.string_view
     libc.src.__support.threads.thread_common

diff  --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index 15f38c0a0eba2..e0fc69034a5f6 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -8,11 +8,11 @@
 
 #include "src/__support/threads/thread.h"
 #include "config/linux/app.h"
-#include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/atomic.h"
-#include "src/__support/CPP/error.h"
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/stringstream.h"
-#include "src/__support/OSUtil/syscall.h"           // For syscall functions.
+#include "src/__support/OSUtil/syscall.h" // For syscall functions.
+#include "src/__support/error_or.h"
 #include "src/__support/threads/linux/futex_word.h" // For FutexWordType
 
 #ifdef LLVM_LIBC_ARCH_AARCH64
@@ -54,7 +54,7 @@ static constexpr unsigned CLONE_SYSCALL_FLAGS =
                            // wake the joining thread.
     | CLONE_SETTLS;        // Setup the thread pointer of the new thread.
 
-static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
+static inline ErrorOr<void *> alloc_stack(size_t size) {
   long mmap_result =
       __llvm_libc::syscall_impl(MMAP_SYSCALL_NUMBER,
                                 0, // No special address
@@ -65,7 +65,7 @@ static inline cpp::ErrorOr<void *> alloc_stack(size_t size) {
                                 0   // No offset
       );
   if (mmap_result < 0 && (uintptr_t(mmap_result) >= UINTPTR_MAX - size))
-    return cpp::Error{int(-mmap_result)};
+    return Error{int(-mmap_result)};
   return reinterpret_cast<void *>(mmap_result);
 }
 
@@ -113,8 +113,7 @@ __attribute__((always_inline)) inline uintptr_t get_start_args_addr() {
 #endif
 }
 
-__attribute__((noinline))
-static void start_thread() {
+__attribute__((noinline)) static void start_thread() {
   auto *start_args = reinterpret_cast<StartArgs *>(get_start_args_addr());
   auto *attrib = start_args->thread_attrib;
   self.attrib = attrib;
@@ -141,7 +140,7 @@ int Thread::run(ThreadStyle style, ThreadRunner runner, void *arg, void *stack,
       size = DEFAULT_STACK_SIZE;
     auto alloc = alloc_stack(size);
     if (!alloc)
-      return alloc.error_code();
+      return alloc.error();
     else
       stack = alloc.value();
     owned_stack = true;
@@ -374,7 +373,7 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
   // These callbacks could be the ones registered by the language runtimes,
   // for example, the destructors of thread local objects. They can also
   // be destructors of the TSS objects set using API like pthread_setspecific.
-  // NOTE: We cannot call the atexit callbacks as part of the 
+  // NOTE: We cannot call the atexit callbacks as part of the
   // cleanup_thread_resources function as that function can be called from a
   // 
diff erent thread. The destructors of thread local and TSS objects should
   // be called by the thread which owns them.

diff  --git a/libc/src/pthread/pthread_setname_np.cpp b/libc/src/pthread/pthread_setname_np.cpp
index 8eed595ef5cca..425c25bd3a5f7 100644
--- a/libc/src/pthread/pthread_setname_np.cpp
+++ b/libc/src/pthread/pthread_setname_np.cpp
@@ -9,7 +9,6 @@
 #include "pthread_setname_np.h"
 
 #include "src/__support/CPP/string_view.h"
-#include "src/__support/CPP/error.h"
 #include "src/__support/common.h"
 #include "src/__support/threads/thread.h"
 

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 1a8308e50c2a8..d10f527627ba4 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -25,6 +25,7 @@ add_entrypoint_object(
     fclose.h
   DEPENDS
     libc.include.stdio
+    libc.include.errno
     libc.src.__support.File.file
     libc.src.__support.File.platform_file
 )
@@ -108,6 +109,7 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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 @@ add_entrypoint_object(
   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
index 48803e33acb3d..c287442dc0010 100644
--- a/libc/src/stdio/fclose.cpp
+++ b/libc/src/stdio/fclose.cpp
@@ -9,12 +9,18 @@
 #include "src/stdio/fclose.h"
 #include "src/__support/File/file.h"
 
+#include <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(int, fclose, (::FILE * stream)) {
-  return reinterpret_cast<__llvm_libc::File *>(stream)->close();
+  int result = reinterpret_cast<__llvm_libc::File *>(stream)->close();
+  if (result != 0) {
+    errno = result;
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fflush.cpp b/libc/src/stdio/fflush.cpp
index 2dc78e3e72e25..75cf4d3860eab 100644
--- a/libc/src/stdio/fflush.cpp
+++ b/libc/src/stdio/fflush.cpp
@@ -9,12 +9,18 @@
 #include "src/stdio/fflush.h"
 #include "src/__support/File/file.h"
 
+#include <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(int, fflush, (::FILE * stream)) {
-  return reinterpret_cast<__llvm_libc::File *>(stream)->flush();
+  int result = reinterpret_cast<__llvm_libc::File *>(stream)->flush();
+  if (result != 0) {
+    errno = result;
+    return EOF;
+  }
+  return 0;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fgetc.cpp b/libc/src/stdio/fgetc.cpp
index b94cec541e85b..33b27a2449ed7 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 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.value;
+  if (result.has_error())
+    errno = result.error;
+
   if (r != 1)
     return EOF;
   return c;

diff  --git a/libc/src/stdio/fgetc_unlocked.cpp b/libc/src/stdio/fgetc_unlocked.cpp
index d61493b5bba10..abec1d968429f 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 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.value;
+  if (result.has_error())
+    errno = result.error;
   if (r != 1)
     return EOF;
   return c;

diff  --git a/libc/src/stdio/fgets.cpp b/libc/src/stdio/fgets.cpp
index ddce420450d23..bb7c794d86149 100644
--- 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 <errno.h>
 #include <stddef.h>
 #include <stdio.h>
 
@@ -28,7 +29,11 @@ LLVM_LIBC_FUNCTION(char *, fgets,
   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.value;
+    if (result.has_error())
+      errno = result.error;
+
     if (r != 1)
       break;
     str[i] = c;

diff  --git a/libc/src/stdio/fopen.cpp b/libc/src/stdio/fopen.cpp
index f34b4ed96999a..f39b65eddf68f 100644
--- a/libc/src/stdio/fopen.cpp
+++ b/libc/src/stdio/fopen.cpp
@@ -9,13 +9,19 @@
 #include "src/stdio/fopen.h"
 #include "src/__support/File/file.h"
 
+#include <errno.h>
 #include <stdio.h>
 
 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_value()) {
+    errno = result.error();
+    return nullptr;
+  }
+  return reinterpret_cast<::FILE *>(result.value());
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp
index 85f6de7595ce9..bd9846020f746 100644
--- a/libc/src/stdio/fopencookie.cpp
+++ b/libc/src/stdio/fopencookie.cpp
@@ -23,7 +23,7 @@ class CookieFile : public __llvm_libc::File {
   cookie_io_functions_t ops;
 };
 
-size_t write_func(File *f, const void *data, size_t size) {
+FileIOResult write_func(File *f, const void *data, size_t size) {
   auto cookie_file = reinterpret_cast<CookieFile *>(f);
   if (cookie_file->ops.write == nullptr)
     return 0;
@@ -31,7 +31,7 @@ size_t write_func(File *f, const void *data, size_t size) {
                                 reinterpret_cast<const char *>(data), size);
 }
 
-size_t read_func(File *f, void *data, size_t size) {
+FileIOResult read_func(File *f, void *data, size_t size) {
   auto cookie_file = reinterpret_cast<CookieFile *>(f);
   if (cookie_file->ops.read == nullptr)
     return 0;
@@ -39,11 +39,10 @@ size_t read_func(File *f, void *data, size_t size) {
                                reinterpret_cast<char *>(data), size);
 }
 
-long seek_func(File *f, long offset, int whence) {
+ErrorOr<long> seek_func(File *f, long offset, int whence) {
   auto cookie_file = reinterpret_cast<CookieFile *>(f);
   if (cookie_file->ops.seek == nullptr) {
-    errno = EINVAL;
-    return -1;
+    return Error(EINVAL);
   }
   off64_t offset64 = offset;
   int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);

diff  --git a/libc/src/stdio/fputc.cpp b/libc/src/stdio/fputc.cpp
index 1120ab2e57102..8287e4e8770a0 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(int, fputc, (int c, ::FILE *stream)) {
   unsigned char uc = static_cast<unsigned char>(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.error;
+  size_t written = result.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
index aee15c1a2ac14..0b1a3e18786fe 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
@@ -18,8 +19,12 @@ LLVM_LIBC_FUNCTION(int, fputs,
                    (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.error;
+  size_t written = result.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
index 779816ecfb220..103d88c21a0a0 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
@@ -18,9 +19,11 @@ LLVM_LIBC_FUNCTION(size_t, fread,
                     ::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.error;
+  return result.value / size;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fread_unlocked.cpp b/libc/src/stdio/fread_unlocked.cpp
index 162cc4e9a4122..18e716048eb05 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
@@ -16,8 +17,13 @@ namespace __llvm_libc {
 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.error;
+  return result.value / size;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fseek.cpp b/libc/src/stdio/fseek.cpp
index 296acffdd9413..4a29c99509d19 100644
--- a/libc/src/stdio/fseek.cpp
+++ b/libc/src/stdio/fseek.cpp
@@ -9,12 +9,19 @@
 #include "src/stdio/fseek.h"
 #include "src/__support/File/file.h"
 
+#include <errno.h>
 #include <stdio.h>
 
 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_value()) {
+    errno = result.error();
+    return -1;
+  }
+  return 0;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/ftell.cpp b/libc/src/stdio/ftell.cpp
index 40783ac58fca4..2d11a42a7466f 100644
--- a/libc/src/stdio/ftell.cpp
+++ b/libc/src/stdio/ftell.cpp
@@ -9,12 +9,18 @@
 #include "src/stdio/ftell.h"
 #include "src/__support/File/file.h"
 
+#include <errno.h>
 #include <stdio.h>
 
 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_value()) {
+    errno = result.error();
+    return -1;
+  }
+  return result.value();
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fwrite.cpp b/libc/src/stdio/fwrite.cpp
index ffa555839bc71..43f02017ca50d 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
@@ -18,9 +19,12 @@ LLVM_LIBC_FUNCTION(size_t, fwrite,
                     ::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.error;
+
+  return result.value / size;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/fwrite_unlocked.cpp b/libc/src/stdio/fwrite_unlocked.cpp
index 085b77516d9eb..47fe075ca6267 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
@@ -16,8 +17,15 @@ namespace __llvm_libc {
 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.error;
+
+  return result.value / size;
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdio/getc.cpp b/libc/src/stdio/getc.cpp
index 406e83f2b3627..a27f79fe99dcb 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 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.value;
+  if (result.has_error())
+    errno = result.error;
+
   if (r != 1)
     return EOF;
   return c;

diff  --git a/libc/src/stdio/getc_unlocked.cpp b/libc/src/stdio/getc_unlocked.cpp
index 48adba5ff4118..ecb31848ae53d 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 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.value;
+  if (result.has_error())
+    errno = result.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
index 7e0dd1b678e7b..0e07e1c1eb8a7 100644
--- a/libc/src/stdio/printf_core/file_writer.cpp
+++ b/libc/src/stdio/printf_core/file_writer.cpp
@@ -16,8 +16,9 @@ namespace __llvm_libc {
 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<int>(len))
+  auto result = file->write_unlocked(to_write, len);
+  int written = result.value;
+  if (written != static_cast<int>(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
index f3f9221986e45..3016060c9efba 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(int, putc, (int c, ::FILE *stream)) {
   unsigned char uc = static_cast<unsigned char>(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.error;
+  size_t written = result.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
index 919a42b801a60..2cf5d64d94aeb 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(int, putchar, (int c)) {
   unsigned char uc = static_cast<unsigned char>(c);
-  size_t written = __llvm_libc::stdout->write(&uc, 1);
+
+  auto result = __llvm_libc::stdout->write(&uc, 1);
+  if (result.has_error())
+    errno = result.error;
+  size_t written = result.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
index 93132da8bed32..57e4919500f5a 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 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.error;
+  size_t written = result.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.error;
+  written = result.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
index f39c3b9ab8412..51e22299e3dd2 100644
--- a/libc/src/stdio/scanf_core/file_reader.cpp
+++ b/libc/src/stdio/scanf_core/file_reader.cpp
@@ -15,7 +15,8 @@ namespace scanf_core {
 
 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.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
index bdf8de3639eb6..2931fea9e57c7 100644
--- a/libc/test/src/__support/File/file_test.cpp
+++ b/libc/test/src/__support/File/file_test.cpp
@@ -7,15 +7,17 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/__support/File/file.h"
+#include "src/__support/error_or.h"
 #include "utils/UnitTest/MemoryMatcher.h"
 #include "utils/UnitTest/Test.h"
 
-#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 using ModeFlags = __llvm_libc::File::ModeFlags;
 using MemoryView = __llvm_libc::memory::testing::MemoryView;
+using __llvm_libc::ErrorOr;
+using __llvm_libc::FileIOResult;
 
 class StringFile : public __llvm_libc::File {
   static constexpr size_t SIZE = 512;
@@ -24,9 +26,10 @@ class StringFile : public __llvm_libc::File {
   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 FileIOResult str_read(__llvm_libc::File *f, void *data, size_t len);
+  static FileIOResult str_write(__llvm_libc::File *f, const void *data,
+                                size_t len);
+  static ErrorOr<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; }
 
@@ -67,7 +70,8 @@ class StringFile : public __llvm_libc::File {
   }
 };
 
-size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) {
+FileIOResult StringFile::str_read(__llvm_libc::File *f, void *data,
+                                  size_t len) {
   StringFile *sf = static_cast<StringFile *>(f);
   if (sf->pos >= sf->eof_marker)
     return 0;
@@ -78,8 +82,8 @@ size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) {
   return i;
 }
 
-size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
-                             size_t len) {
+FileIOResult StringFile::str_write(__llvm_libc::File *f, const void *data,
+                                   size_t len) {
   StringFile *sf = static_cast<StringFile *>(f);
   if (sf->write_append)
     sf->pos = sf->eof_marker;
@@ -94,7 +98,8 @@ size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
   return i;
 }
 
-long StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
+ErrorOr<long> StringFile::str_seek(__llvm_libc::File *f, long offset,
+                                   int whence) {
   StringFile *sf = static_cast<StringFile *>(f);
   if (whence == SEEK_SET)
     sf->pos = offset;
@@ -119,7 +124,7 @@ TEST(LlvmLibcFileTest, WriteOnly) {
   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)).value);
   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
   ASSERT_EQ(f->flush(), 0);
   EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available
@@ -127,10 +132,10 @@ TEST(LlvmLibcFileTest, WriteOnly) {
 
   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)).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)).value);
   EXPECT_GE(f->get_pos(), size_t(0));
   ASSERT_EQ(f->flush(), 0);
   EXPECT_EQ(f->get_pos(), 2 * sizeof(data));
@@ -139,11 +144,13 @@ TEST(LlvmLibcFileTest, WriteOnly) {
   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.value, size_t(0));
+    EXPECT_TRUE(f->error());
+    EXPECT_TRUE(result.has_error());
+  }
 
   ASSERT_EQ(f->close(), 0);
 }
@@ -162,8 +169,8 @@ TEST(LlvmLibcFileTest, WriteLineBuffered) {
   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)).value);
+  ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data)).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 @@ TEST(LlvmLibcFileTest, WriteLineBuffered) {
 
   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)).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)).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.
@@ -215,7 +222,7 @@ TEST(LlvmLibcFileTest, WriteUnbuffered) {
   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)).value);
   EXPECT_EQ(f->get_pos(),
             sizeof(data)); // no buffering means this is written immediately.
   EXPECT_STREQ(f->get_str(), data);
@@ -233,7 +240,7 @@ TEST(LlvmLibcFileTest, ReadOnly) {
 
   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).value);
   EXPECT_FALSE(f->iseof());
   // Reading less than file buffer worth will still read one
   // full buffer worth of data.
@@ -246,14 +253,14 @@ TEST(LlvmLibcFileTest, ReadOnly) {
 
   // 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).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).value);
   EXPECT_TRUE(f->iseof());
 
   // Reset the pos to the beginning of the file which should allow
@@ -261,15 +268,17 @@ TEST(LlvmLibcFileTest, ReadOnly) {
   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).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.value, size_t(0));
+    EXPECT_TRUE(f->error());
+    EXPECT_TRUE(result.has_error());
+  }
 
   ASSERT_EQ(f->close(), 0);
 }
@@ -285,13 +294,13 @@ TEST(LlvmLibcFileTest, ReadSeekCurAndRead) {
   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).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).value(), 0);
+  ASSERT_EQ(f->read(data, READ_SIZE - 1).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).value(), 0);
+  ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
   ASSERT_STREQ(data, "9098");
   ASSERT_EQ(f->close(), 0);
 }
@@ -307,14 +316,17 @@ TEST(LlvmLibcFileTest, AppendOnly) {
 
   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.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)).value, sizeof(write_data));
   EXPECT_EQ(f->get_pos(), size_t(0));
   // Flushing will write to the file.
   EXPECT_EQ(f->flush(), int(0));
@@ -330,14 +342,14 @@ TEST(LlvmLibcFileTest, WriteUpdate) {
   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)).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).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)).value, sizeof(data));
   EXPECT_STREQ(read_data, data);
 
   ASSERT_EQ(f->close(), 0);
@@ -353,7 +365,7 @@ TEST(LlvmLibcFileTest, ReadUpdate) {
 
   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).value);
   EXPECT_FALSE(f->iseof());
   // Reading less than file buffer worth will still read one
   // full buffer worth of data.
@@ -364,9 +376,9 @@ TEST(LlvmLibcFileTest, ReadUpdate) {
   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).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)).value);
   EXPECT_STREQ(file_buffer, write_data);
   ASSERT_EQ(f->flush(), 0);
   MemoryView dst2(f->get_str(), sizeof(write_data)),
@@ -385,16 +397,16 @@ TEST(LlvmLibcFileTest, AppendUpdate) {
       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)).value);
   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
   ASSERT_EQ(f->flush(), 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).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(sizeof(data), f->write(data, sizeof(data)).value);
   ASSERT_EQ(f->flush(), 0);
   EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content));
   MemoryView src1(initial_content, sizeof(initial_content)),
@@ -408,10 +420,10 @@ TEST(LlvmLibcFileTest, AppendUpdate) {
   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).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).value);
   MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE);
   EXPECT_MEM_EQ(src4, dst4);
 
@@ -426,7 +438,7 @@ TEST(LlvmLibcFileTest, SmallBuffer) {
   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).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));
@@ -442,9 +454,9 @@ TEST(LlvmLibcFileTest, ZeroLengthBuffer) {
   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).value);
+  ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);
+  ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).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));
@@ -473,9 +485,9 @@ TEST(LlvmLibcFileTest, WriteNothing) {
   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).value);
+  ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);
+  ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).value);
 
   ASSERT_FALSE(f_fbf->error_unlocked());
   ASSERT_FALSE(f_lbf->error_unlocked());

diff  --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp
index fe04b6f643e5a..51f7f9aeea03d 100644
--- a/libc/test/src/__support/File/platform_file_test.cpp
+++ b/libc/test/src/__support/File/platform_file_test.cpp
@@ -19,18 +19,18 @@ TEST(LlvmLibcPlatformFileTest, CreateWriteCloseAndReadBack) {
   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->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
   ASSERT_EQ(file->close(), 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).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).value, size_t(0));
   ASSERT_TRUE(file->iseof());
 
   ASSERT_EQ(file->close(), 0);
@@ -40,17 +40,17 @@ 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).value, TEXT_SIZE);
 
-  ASSERT_EQ(file->seek(0, SEEK_SET), 0);
+  ASSERT_EQ(file->seek(0, SEEK_SET).value(), 0);
 
   char data[sizeof(TEXT)];
-  ASSERT_EQ(file->read(data, TEXT_SIZE), TEXT_SIZE);
+  ASSERT_EQ(file->read(data, TEXT_SIZE).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).value, size_t(0));
   ASSERT_TRUE(file->iseof());
 
   ASSERT_EQ(file->close(), 0);
@@ -60,26 +60,26 @@ 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->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
   ASSERT_EQ(file->close(), 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).value, APPEND_TEXT_SIZE);
   ASSERT_EQ(file->close(), 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).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).value, size_t(0));
   ASSERT_TRUE(file->iseof());
 
   ASSERT_EQ(file->close(), 0);
@@ -89,23 +89,23 @@ 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->write(TEXT, TEXT_SIZE).value, TEXT_SIZE);
   ASSERT_EQ(file->close(), 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).value, APPEND_TEXT_SIZE);
 
-  ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END), 0);
+  ASSERT_EQ(file->seek(-APPEND_TEXT_SIZE, SEEK_END).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).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).value, size_t(0));
   ASSERT_TRUE(file->iseof());
 
   ASSERT_EQ(file->close(), 0);
@@ -124,7 +124,7 @@ TEST(LlvmLibcPlatformFileTest, LargeFile) {
 
   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).value, DATA_SIZE);
   }
   ASSERT_EQ(file->close(), 0);
 
@@ -132,13 +132,13 @@ TEST(LlvmLibcPlatformFileTest, LargeFile) {
   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).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).value, size_t(0));
   ASSERT_TRUE(file->iseof());
 
   ASSERT_EQ(file->close(), 0);
@@ -149,7 +149,8 @@ TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) {
   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(sizeof(CONTENT) - 1,
+            file->write(CONTENT, sizeof(CONTENT) - 1).value);
   ASSERT_EQ(0, file->close());
 
   file = __llvm_libc::openfile(FILENAME, "r");
@@ -158,13 +159,13 @@ TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) {
   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).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).value(), 0);
+  ASSERT_EQ(file->read(data, READ_SIZE - 1).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).value(), 0);
+  ASSERT_EQ(file->read(data, READ_SIZE - 1).value, READ_SIZE - 1);
   ASSERT_STREQ(data, "9098");
 
   ASSERT_EQ(file->close(), 0);
@@ -176,21 +177,21 @@ TEST(LlvmLibcPlatformFileTest, IncorrectOperation) {
 
   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).value, size_t(0)); // Cannot read
   ASSERT_FALSE(file->iseof());
   ASSERT_TRUE(file->error());
   ASSERT_EQ(file->close(), 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).value, size_t(0)); // Cannot write
   ASSERT_FALSE(file->iseof());
   ASSERT_TRUE(file->error());
   ASSERT_EQ(file->close(), 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).value, size_t(0)); // Cannot read
   ASSERT_FALSE(file->iseof());
   ASSERT_TRUE(file->error());
   ASSERT_EQ(file->close(), 0);

diff  --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp
index 6f80163ae421a..49af4683d7979 100644
--- a/libc/test/src/stdio/fileop_test.cpp
+++ b/libc/test/src/stdio/fileop_test.cpp
@@ -78,6 +78,11 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
   __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 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
   __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 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
   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<FILE *>(nullptr));
+  EXPECT_NE(errno, 0);
 }
 
 TEST(LlvmLibcFILETest, FFlush) {
@@ -132,6 +158,7 @@ TEST(LlvmLibcFILETest, FOpenFWriteSizeGreaterThanOne) {
   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
index a788c759300ea..f83a74db10380 100644
--- 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 <errno.h>
 #include <stdio.h>
 
 class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
@@ -53,6 +54,10 @@ class LlvmLibcFTellTest : public __llvm_libc::testing::Test {
     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);
   }
 };
 


        


More information about the libc-commits mailing list