[libc-commits] [libc] df4814d - [libc] Add a linux file implementation.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Mon Mar 21 00:07:39 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-03-21T07:07:22Z
New Revision: df4814d45d6b691fea9e51c032832730f7a75c71

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

LOG: [libc] Add a linux file implementation.

Reviewed By: lntue

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

Added: 
    libc/src/__support/File/linux_file.cpp
    libc/test/src/__support/File/platform_file_test.cpp
    libc/test/src/__support/File/testdata/CMakeLists.txt

Modified: 
    libc/src/__support/File/CMakeLists.txt
    libc/test/src/__support/File/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index e80eaa27a9109..6a202649cbbad 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -15,3 +15,19 @@ add_object_library(
     libc.include.errno
     libc.src.errno.errno
 )
+
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}_file.cpp)
+  add_object_library(
+    platform_file
+    SRCS
+      ${LIBC_TARGET_OS}_file.cpp
+    DEPENDS
+      .file
+      libc.include.errno
+      libc.include.fcntl
+      libc.include.sys_syscall
+      libc.src.__support.OSUtil.osutil
+      libc.src.errno.errno
+  )
+endif()
+

diff  --git a/libc/src/__support/File/linux_file.cpp b/libc/src/__support/File/linux_file.cpp
new file mode 100644
index 0000000000000..b1a58d94e7207
--- /dev/null
+++ b/libc/src/__support/File/linux_file.cpp
@@ -0,0 +1,168 @@
+//===--- Linux specialization of the File data structure ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "file.h"
+
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+
+#include <errno.h>
+#include <fcntl.h>       // For mode_t and other flags to the open syscall
+#include <stdlib.h>      // For malloc
+#include <sys/syscall.h> // For syscall numbers
+
+namespace __llvm_libc {
+
+namespace {
+
+size_t write_func(File *, const void *, size_t);
+size_t read_func(File *, void *, size_t);
+int seek_func(File *, long, int);
+int close_func(File *);
+int flush_func(File *);
+
+} // anonymous namespace
+
+class LinuxFile : public File {
+  int fd;
+
+public:
+  constexpr LinuxFile(int file_descriptor, void *buffer, size_t buffer_size,
+                      int buffer_mode, bool owned, File::ModeFlags modeflags)
+      : File(&write_func, &read_func, &seek_func, &close_func, flush_func,
+             buffer, buffer_size, buffer_mode, owned, modeflags),
+        fd(file_descriptor) {}
+
+  static void init(LinuxFile *f, int file_descriptor, void *buffer,
+                   size_t buffer_size, int buffer_mode, bool owned,
+                   File::ModeFlags modeflags) {
+    File::init(f, &write_func, &read_func, &seek_func, &close_func, &flush_func,
+               buffer, buffer_size, buffer_mode, owned, modeflags);
+    f->fd = file_descriptor;
+  }
+
+  int get_fd() const { return fd; }
+};
+
+namespace {
+
+size_t write_func(File *f, const void *data, size_t size) {
+  auto *lf = reinterpret_cast<LinuxFile *>(f);
+  long ret = __llvm_libc::syscall(SYS_write, lf->get_fd(), data, size);
+  if (ret < 0) {
+    errno = -ret;
+    return 0;
+  }
+  return ret;
+}
+
+size_t read_func(File *f, void *buf, size_t size) {
+  auto *lf = reinterpret_cast<LinuxFile *>(f);
+  long ret = __llvm_libc::syscall(SYS_read, lf->get_fd(), buf, size);
+  if (ret < 0) {
+    errno = -ret;
+    return 0;
+  }
+  return ret;
+}
+
+int seek_func(File *f, long offset, int whence) {
+  auto *lf = reinterpret_cast<LinuxFile *>(f);
+#ifdef SYS_lseek
+  long ret = __llvm_libc::syscall(SYS_lseek, lf->get_fd(), offset, whence);
+#elif defined(SYS__llseek)
+  long result;
+  long ret = __llvm_libc::syscall(SYS__lseek, 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 0;
+}
+
+int close_func(File *f) {
+  auto *lf = reinterpret_cast<LinuxFile *>(f);
+  long ret = __llvm_libc::syscall(SYS_close, lf->get_fd());
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+int flush_func(File *f) {
+  auto *lf = reinterpret_cast<LinuxFile *>(f);
+  long ret = __llvm_libc::syscall(SYS_fsync, lf->get_fd());
+  if (ret < 0) {
+    errno = -ret;
+    return -1;
+  }
+  return 0;
+}
+
+} // anonymous namespace
+
+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;
+  }
+  long open_flags = 0;
+  if (modeflags & ModeFlags(File::OpenMode::APPEND)) {
+    open_flags = O_CREAT | O_APPEND;
+    if (modeflags & ModeFlags(File::OpenMode::PLUS))
+      open_flags |= O_RDWR;
+    else
+      open_flags |= O_WRONLY;
+  } else if (modeflags & ModeFlags(File::OpenMode::WRITE)) {
+    open_flags = O_CREAT | O_TRUNC;
+    if (modeflags & ModeFlags(File::OpenMode::PLUS))
+      open_flags |= O_RDWR;
+    else
+      open_flags |= O_WRONLY;
+  } else {
+    if (modeflags & ModeFlags(File::OpenMode::PLUS))
+      open_flags |= O_RDWR;
+    else
+      open_flags |= O_RDONLY;
+  }
+
+  // File created will have 0666 permissions.
+  constexpr long OPEN_MODE =
+      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+#ifdef SYS_open
+  int fd = __llvm_libc::syscall(SYS_open, path, open_flags, OPEN_MODE);
+#elif defined(SYS_openat)
+  int fd =
+      __llvm_libc::syscall(SYS_openat, AT_FDCWD, path, open_flags, OPEN_MODE);
+#else
+#error "SYS_open and SYS_openat syscalls not available to perform a file open."
+#endif
+
+  if (fd < 0) {
+    errno = -fd;
+    return nullptr;
+  }
+
+  void *buffer = malloc(File::DEFAULT_BUFFER_SIZE);
+  auto *file = reinterpret_cast<LinuxFile *>(malloc(sizeof(LinuxFile)));
+  LinuxFile::init(
+      file, fd, buffer, File::DEFAULT_BUFFER_SIZE,
+      0, // TODO: Set the correct buffer mode when buffer mode is available.
+      true, modeflags);
+  return file;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/test/src/__support/File/CMakeLists.txt b/libc/test/src/__support/File/CMakeLists.txt
index 2757acc5b7040..e75da1bd744c3 100644
--- a/libc/test/src/__support/File/CMakeLists.txt
+++ b/libc/test/src/__support/File/CMakeLists.txt
@@ -15,3 +15,19 @@ add_libc_unittest(
 target_link_libraries(
   libc.test.src.__support.File.file_test PRIVATE LibcMemoryHelpers
 )
+
+if (TARGET libc.src.__support.File.platform_file)
+  add_libc_unittest(
+    platform_file_test
+    SUITE
+      libc_support_unittests
+    SRCS
+      platform_file_test.cpp
+    DEPENDS
+      libc.src.__support.File.file
+      libc.src.__support.File.platform_file
+      libc.include.stdio
+  )
+endif()
+
+add_subdirectory(testdata)

diff  --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp
new file mode 100644
index 0000000000000..55909806fb16d
--- /dev/null
+++ b/libc/test/src/__support/File/platform_file_test.cpp
@@ -0,0 +1,171 @@
+//===-- Unittests for target platform file implementation -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/File/file.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdio.h> // For SEEK_* macros
+
+using File = __llvm_libc::File;
+constexpr char TEXT[] = "Hello, File";
+constexpr size_t TEXT_SIZE = sizeof(TEXT) - 1; // Ignore the null terminator
+
+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->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);
+  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_TRUE(file->iseof());
+
+  ASSERT_EQ(file->close(), 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->seek(0, SEEK_SET), 0);
+
+  char data[sizeof(TEXT)];
+  ASSERT_EQ(file->read(data, TEXT_SIZE), 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_TRUE(file->iseof());
+
+  ASSERT_EQ(file->close(), 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);
+
+  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);
+
+  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);
+  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_TRUE(file->iseof());
+
+  ASSERT_EQ(file->close(), 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);
+
+  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->seek(-APPEND_TEXT_SIZE, SEEK_END), 0);
+  char data[APPEND_TEXT_SIZE + 1];
+  ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE), 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_TRUE(file->iseof());
+
+  ASSERT_EQ(file->close(), 0);
+}
+
+TEST(LlvmLibcPlatformFileTest, LargeFile) {
+  constexpr size_t DATA_SIZE = File::DEFAULT_BUFFER_SIZE >> 2;
+  constexpr char BYTE = 123;
+  char write_data[DATA_SIZE];
+  for (size_t i = 0; i < DATA_SIZE; ++i)
+    write_data[i] = BYTE;
+
+  constexpr char FILENAME[] = "testdata/large_file.test";
+  File *file = __llvm_libc::openfile(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  constexpr int REPEAT = 5;
+  for (int i = 0; i < REPEAT; ++i) {
+    ASSERT_EQ(file->write(write_data, DATA_SIZE), DATA_SIZE);
+  }
+  ASSERT_EQ(file->close(), 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);
+
+  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_TRUE(file->iseof());
+
+  ASSERT_EQ(file->close(), 0);
+}
+
+TEST(LlvmLibcPlatformFileTest, IncorrectOperation) {
+  constexpr char FILENAME[] = "testdata/incorrect_operation.test";
+  char data[1] = {123};
+
+  File *file = __llvm_libc::openfile(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+  ASSERT_EQ(file->read(data, 1), 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_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_FALSE(file->iseof());
+  ASSERT_TRUE(file->error());
+  ASSERT_EQ(file->close(), 0);
+}

diff  --git a/libc/test/src/__support/File/testdata/CMakeLists.txt b/libc/test/src/__support/File/testdata/CMakeLists.txt
new file mode 100644
index 0000000000000..e69de29bb2d1d


        


More information about the libc-commits mailing list