]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib/nolibc/stdio: Add `libc` style minimal file operations functions 1043/head
authorEduard Mihailescu <mihailescu.eduard@gmail.com>
Fri, 11 Aug 2023 11:19:47 +0000 (14:19 +0300)
committerEduard Mihailescu <mihailescu.eduard@gmail.com>
Sun, 22 Oct 2023 13:11:51 +0000 (16:11 +0300)
If `vfscore` is enabled, then add a small set of minimally implemented
file handling functions like `fopen`, `fclose`, `fwrite`.
These are useful at link time for `gcc` objects such
as the ones from `gcov`.

Signed-off-by: Eduard-Florin Mihailescu <mihailescu.eduard@gmail.com>
lib/nolibc/exportsyms.uk
lib/nolibc/include/nolibc-internal/shareddefs.h
lib/nolibc/include/stdio.h
lib/nolibc/stdio.c

index ae8b5146e680aae5bd4c224f0bd320e7d2d181b7..508e6b21b8f5440b8a51eedfe66c23e06505fff0 100644 (file)
@@ -44,6 +44,17 @@ putchar
 fputs
 puts
 
+# file
+clearerr
+feof
+ferror
+fseek
+fopen
+fclose
+fdopen
+fread
+fwrite
+
 # stdlib
 abort
 exit
index 1c5542eee439d1ffa9a0fdda298ff080972bb873..53b0651fe0b25b0880b4436d8c6b0c83c12a56c0 100644 (file)
@@ -88,6 +88,11 @@ typedef struct {
 #define __DEFINED_max_align_t
 #endif
 
+#if (defined __NEED_FILE && !defined __DEFINED_FILE)
+typedef struct _nolibc_file FILE;
+#define __DEFINED_FILE
+#endif
+
 #if defined(__NEED_useconds_t) && !defined(__DEFINED_useconds_t)
 typedef unsigned useconds_t;
 #define __DEFINED_useconds_t
index 7681aedd4425c43f84efe0c87d54a329b792f671..9f23dcc8ae4e2164dc90c03115ffd40470caf154 100644 (file)
 
 #include <uk/essentials.h>
 
+
+#if CONFIG_LIBVFSCORE
+#include <vfscore/file.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -43,11 +48,9 @@ extern "C" {
 #define __NEED_NULL
 #define __NEED_size_t
 #define __NEED_ssize_t
+#define __NEED_FILE
 #include <nolibc-internal/shareddefs.h>
 
-struct _nolibc_fd;
-typedef struct _nolibc_fd FILE;
-
 extern FILE *stdin;
 extern FILE *stdout;
 extern FILE *stderr;
@@ -92,8 +95,17 @@ int fputs(const char *restrict s, FILE *restrict stream);
 int puts(const char *s);
 
 #if CONFIG_LIBVFSCORE
+void clearerr(FILE *stream);
 int rename(const char *oldpath, const char *newpath);
-#endif
+int feof(FILE *stream);
+int ferror(FILE *stream);
+int fseek(FILE *stream, long offset, int whence);
+int fclose(FILE *stream);
+FILE *fopen(const char *pathname, const char *mode);
+FILE *fdopen(int fd, const char *mode);
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+#endif /* CONFIG_LIBVFSCORE */
 
 #ifdef __STDIO_H_DEFINED_va_list
 #undef va_list
index 9600ca21706aa2c950dc919c1fff05ee3bfddd8b..ae77caae423a3d7e9b620b0baaafbb082b3b57e8 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <uk/essentials.h>
+#include <uk/arch/lcpu.h>
 #include <uk/plat/console.h>
 
 /* 64 bits + 0-Byte at end */
@@ -542,3 +548,230 @@ int puts(const char *s)
 {
        return fputs_internal(s, stdout, 1);
 }
+
+#if CONFIG_LIBVFSCORE
+struct _nolibc_file {
+       int fd;
+       int errno;
+       bool eof;
+       off_t offset;
+};
+
+void clearerr(FILE *stream)
+{
+       stream->eof = 0;
+       stream->errno = 0;
+}
+
+/* The following code is derived from musl libc */
+static int __fmodeflags(const char *mode, int *flags)
+{
+       if (!strchr("rwa", *mode)) {
+               errno = EINVAL;
+               return -1;
+       }
+       if (strchr(mode, '+'))
+               *flags = O_RDWR;
+       else if (*mode == 'r')
+               *flags = O_RDONLY;
+       else
+               *flags = O_WRONLY;
+       if (strchr(mode, 'x'))
+               *flags |= O_EXCL;
+       if (strchr(mode, 'e'))
+               *flags |= O_CLOEXEC;
+       if (*mode != 'r')
+               *flags |= O_CREAT;
+       if (*mode == 'w')
+               *flags |= O_TRUNC;
+       if (*mode == 'a')
+               *flags |= O_APPEND;
+
+       return 0;
+}
+
+FILE *fopen(const char *pathname, const char *mode)
+{
+       int flags;
+       int fd;
+       int ret;
+
+       ret = __fmodeflags(mode, &flags);
+
+       if (ret < 0)
+               return NULL;
+
+       fd = open(pathname, flags, 0666);
+
+       if (fd < 0)
+               return NULL;
+
+       return fdopen(fd, mode);
+}
+
+int feof(FILE *stream)
+{
+       return stream->eof;
+}
+
+int ferror(FILE *stream)
+{
+       return stream->errno;
+}
+
+int fclose(FILE *stream)
+{
+       int ret = close(stream->fd);
+
+       free(stream);
+       return ret;
+}
+
+FILE *fdopen(int fd, const char *mode __unused)
+{
+       FILE *f = (FILE *)malloc(sizeof(FILE));
+
+       if (!f)
+               return NULL;
+       f->fd = fd;
+       f->errno = 0;
+       f->eof = 0;
+       f->offset = 0;
+       return f;
+}
+
+int fseek(FILE *stream, long offset, int whence)
+{
+       off_t new_offset;
+
+       switch (whence) {
+       case SEEK_SET:
+       {
+               // Absolute offset from the beginning
+               if (unlikely(offset < 0)) {
+                       stream->errno = 1;
+                       return -1;
+               }
+               new_offset = offset;
+               break;
+       }
+
+       case SEEK_CUR:
+       {
+               // Offset from the current position
+               new_offset = stream->offset + offset;
+               if (unlikely(new_offset < 0)) {
+                       stream->errno = 1;
+                       return -1;
+               }
+               break;
+       }
+
+       case SEEK_END:
+       {
+               struct stat st;
+
+               if (unlikely(fstat(stream->fd, &st) != 0)) {
+                       stream->errno = 1;
+                       return -1;
+               }
+
+               new_offset = st.st_size + offset;
+               if (unlikely(new_offset < 0)) {
+                       stream->errno = 1;
+                       return -1;
+               }
+       }
+       break;
+
+       default:
+       {
+               // Invalid whence
+               stream->errno = 1;
+               return -1;
+       }
+       }
+
+       // Update the stream's offset
+       stream->offset = new_offset;
+       return 0;
+}
+
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       if (unlikely(!stream))
+               return 0;
+
+       if (unlikely(!ptr || !size || !nmemb)) {
+               stream->errno = EINVAL;
+               return 0;
+       }
+
+       if (unlikely(stream->fd < 0)) {
+               stream->errno = EBADF;
+               return 0;
+       }
+
+       if (unlikely(SIZE_MAX / size < nmemb)) {
+               stream->errno = EOVERFLOW;
+               return 0;
+       }
+
+       size_t total = 0;
+
+       while (total < size * nmemb) {
+               ssize_t ret = pread(stream->fd,
+                               ((char *) ptr) + total,
+                               size * nmemb - total, stream->offset);
+               if (ret > 0)
+                       stream->offset += ret;  // Update the offset
+
+               if (ret < 0)
+                       return total / size;
+               if (ret == 0) {
+                       stream->eof = 1;
+                       return total / size;
+               }
+               total += ret;
+       }
+
+       return total / size;
+}
+
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       if (unlikely(!stream))
+               return 0;
+
+       if (unlikely(!ptr || !size || !nmemb)) {
+               stream->errno = EINVAL;
+               return 0;
+       }
+
+       if (unlikely(stream->fd < 0)) {
+               stream->errno = EBADF;
+               return 0;
+       }
+
+       if (unlikely(SIZE_MAX / size < nmemb)) {
+               stream->errno = EOVERFLOW;
+               return 0;
+       }
+
+       size_t total = 0;
+
+       while (total < size * nmemb) {
+               ssize_t ret = pwrite(stream->fd,
+                               ((char *) ptr) + total,
+                               size * nmemb - total, stream->offset);
+               if (ret > 0)
+                       stream->offset += ret;  // Update the offset
+
+               if (ret < 0)
+                       return total / size;
+               total += ret;
+       }
+
+       return total / size;
+}
+#endif /* CONFIG_LIBVFSCORE */