[lib][stdio] wrap file io APIs with stdio APIs

Implement stdio file io APIs with LK lib/fs APIs

Signed-off-by: Chieh-Min Wang <cmwang@google.com>
This commit is contained in:
Chieh-Min Wang
2022-08-04 13:07:32 +08:00
committed by Travis Geiselbrecht
parent d39f2db58d
commit b651168fa1
3 changed files with 252 additions and 19 deletions

View File

@@ -6,6 +6,7 @@
* https://opensource.org/licenses/MIT
*/
#include <lib/fs.h>
#include <lk/err.h>
#include <string.h>
#include <string.h>
@@ -57,8 +58,56 @@ static bool test_path_normalize(void) {
END_TEST;
}
#define TEST_MNT "/test"
#define TEST_FILE TEST_MNT "/stdio_file_tests.txt"
static inline void test_stdio_fs_teardown(void *ptr) {
fs_remove_file(TEST_FILE);
fs_unmount(TEST_MNT);
}
static bool test_stdio_fs(void) {
__attribute__((cleanup(test_stdio_fs_teardown))) BEGIN_TEST;
// Setup
const char *content = "Hello World\n";
const size_t content_len = strlen(content);
fs_mount(TEST_MNT, "memfs", NULL);
// Tests
FILE *stream = fopen(TEST_FILE, "w");
ASSERT_NE(NULL, stream, "failed to open/create file " TEST_MNT);
char buf[1024];
// test stdout fprintf, fputs
EXPECT_EQ(content_len, fprintf(stdout, "%s", content), "");
EXPECT_EQ(content_len, fputs(content, stdout), "");
// test fwrite
EXPECT_EQ(content_len, fwrite(content, 1, content_len, stream), "");
// test fread
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
EXPECT_EQ(content_len, fread(buf, 1, content_len, stream), "");
EXPECT_BYTES_EQ((const uint8_t *)content, (const uint8_t *)buf, content_len,
"fread content mismatched");
// testing fputs
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
EXPECT_EQ(content_len, fputs(content, stream), "");
// testing fgets
ASSERT_EQ(NO_ERROR, fseek(stream, 0, SEEK_SET), "fseek failed");
ASSERT_NE(NULL, fgets(buf, content_len + 1, stream), "");
EXPECT_BYTES_EQ((const uint8_t *)content, (const uint8_t *)buf, content_len,
"fgets content mismatched");
END_TEST;
}
BEGIN_TEST_CASE(fs_tests);
RUN_TEST(test_path_normalize);
RUN_TEST(test_stdio_fs);
END_TEST_CASE(fs_tests);
#endif // WITH_LIB_UNITTEST

View File

@@ -11,11 +11,29 @@
#include <printf.h>
#include <sys/types.h>
#include <lib/io.h>
#if defined(WITH_LIB_FS)
#include <lib/fs.h>
#endif // WITH_LIB_FS
__BEGIN_CDECLS
#if defined(WITH_LIB_FS)
struct fs_handle {
filehandle *handle;
off_t offset;
bool readonly;
};
#endif // WITH_LIB_FS
typedef struct FILE {
#if defined(WITH_LIB_FS)
union {
io_handle_t *io;
struct fs_handle fs_handle;
};
bool use_fs;
#else
io_handle_t *io;
#endif // WITH_LIB_FS
} FILE;
extern FILE __stdio_FILEs[];
@@ -24,6 +42,8 @@ extern FILE __stdio_FILEs[];
#define stdout (&__stdio_FILEs[1])
#define stderr (&__stdio_FILEs[2])
#define EOF (-1)
FILE *fopen(const char *filename, const char *mode);
int fclose(FILE *stream);
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
@@ -45,9 +65,12 @@ int putchar(int c);
int fputs(const char *s, FILE *fp);
int puts(const char *str);
int getc(FILE *fp);
int fgetc(FILE *fp);
#define getc(fp) fgetc(fp)
int getchar(void);
char *fgets(char *s, int size, FILE *stream);
#if !DISABLE_DEBUG_OUTPUT
int printf(const char *fmt, ...) __PRINTFLIKE(1, 2);
int vprintf(const char *fmt, va_list ap);

View File

@@ -6,15 +6,24 @@
* https://opensource.org/licenses/MIT
*/
#include <lk/debug.h>
#include <lk/err.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <platform/debug.h>
#if defined(WITH_LIB_FS)
#define STDIO_FIELDS .use_fs = false,
#else
#define STDIO_FIELDS
#endif // WITH_LIB_FS
#define DEFINE_STDIO_DESC(id) \
[(id)] = { \
.io = &console_io, \
STDIO_FIELDS \
}
FILE __stdio_FILEs[3] = {
@@ -24,9 +33,126 @@ FILE __stdio_FILEs[3] = {
};
#undef DEFINE_STDIO_DESC
FILE *fopen(const char *filename, const char *mode) {
#if defined(WITH_LIB_FS)
FILE *stream = (FILE *) malloc(sizeof(FILE));
if (stream == NULL) {
return NULL;
}
stream->use_fs = true;
stream->fs_handle.offset = 0;
stream->fs_handle.readonly = (!strchr(mode, 'w') && !strchr(mode, 'a'));
status_t ret = fs_open_file(filename, &(stream->fs_handle.handle));
if (ret == ERR_NOT_FOUND && !stream->fs_handle.readonly) {
ret = fs_create_file(filename, &(stream->fs_handle.handle), 0);
}
if (ret != NO_ERROR) {
free(stream);
return NULL;
}
if (strchr(mode, 'a')) {
struct file_stat stat;
if (NO_ERROR == fs_stat_file(stream->fs_handle.handle, &stat)) {
stream->fs_handle.offset = stat.size;
}
}
return stream;
#endif // WITH_LIB_FS
return NULL;
}
int fclose(FILE *stream) {
#if defined(WITH_LIB_FS)
if (stream && !stream->use_fs) {
fs_close_file(stream->fs_handle.handle);
free(stream);
}
#endif // WITH_LIB_FS
return 0;
}
size_t fread(void *ptr, size_t size, size_t count, FILE *stream) {
#if defined(WITH_LIB_FS)
if (stream->use_fs) {
size_t rsize = fs_read_file(stream->fs_handle.handle, ptr,
stream->fs_handle.offset, size * count);
stream->fs_handle.offset += rsize;
return rsize / size;
}
#endif // WITH_LIB_FS
return io_read(stream->io, ptr, size * count) / size;
}
int fflush(FILE *stream) {
return 0;
}
int feof(FILE *stream) {
#if defined(WITH_LIB_FS)
if (!stream->use_fs) {
return 0;
}
struct file_stat stat;
if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
return 1;
}
return (uint64_t)stream->fs_handle.offset >= stat.size;
#endif // WITH_LIB_FS
return 0;
}
static inline off_t clamp(off_t val, off_t min, off_t max) {
const off_t t = (val < min) ? min : val;
return (t > max) ? max : t;
}
int fseek(FILE *stream, long offset, int whence) {
#if defined(WITH_LIB_FS)
if (!stream->use_fs) {
return 0;
}
struct file_stat stat;
if (NO_ERROR != fs_stat_file(stream->fs_handle.handle, &stat)) {
return -1;
}
switch (whence) {
case SEEK_SET:
stream->fs_handle.offset = clamp(offset, 0, stat.size);
break;
case SEEK_CUR:
stream->fs_handle.offset = clamp(stream->fs_handle.offset + offset, 0, stat.size);
break;
case SEEK_END:
stream->fs_handle.offset = clamp(stat.size - offset, 0, stat.size);
break;
default:
return -1;
}
#endif // WITH_LIB_FS
return 0;
}
long ftell(FILE *stream) {
#if defined(WITH_LIB_FS)
if (!stream->use_fs) {
return 0;
}
return stream->fs_handle.offset;
#endif // WITH_LIB_FS
return 0;
}
int fputc(int _c, FILE *fp) {
unsigned char c = _c;
return io_write(fp->io, (char *)&c, 1);
return fwrite(&c, /*size=*/1, /*count=*/1, fp);
}
int putchar(int c) {
@@ -42,40 +168,75 @@ int puts(const char *str) {
int fputs(const char *s, FILE *fp) {
size_t len = strlen(s);
return io_write(fp->io, s, len);
return fwrite(s, /*size=*/1, /*count=*/len, fp);
}
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp) {
size_t bytes_written;
if (size == 0 || count == 0)
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) {
if (stream == stdin) {
return 0;
// fast path for size == 1
if (likely(size == 1)) {
return io_write(fp->io, ptr, count);
}
bytes_written = io_write(fp->io, ptr, size * count);
return bytes_written / size;
if (stream == stdout || stream == stderr) {
size_t bytes_written;
if (size == 0 || count == 0)
return 0;
// fast path for size == 1
if (likely(size == 1)) {
return io_write(stream->io, ptr, count);
}
bytes_written = io_write(stream->io, ptr, size * count);
return bytes_written / size;
}
#if defined(WITH_LIB_FS)
if (stream->fs_handle.readonly) {
return 0;
}
size_t wsize = fs_write_file(stream->fs_handle.handle, ptr,
stream->fs_handle.offset, size * count);
stream->fs_handle.offset += wsize;
return wsize / size;
#endif // WITH_LIB_FS
return 0;
}
int getc(FILE *fp) {
int fgetc(FILE *fp) {
char c;
ssize_t ret = io_read(fp->io, &c, sizeof(c));
ssize_t ret = fread(&c, /*size=*/1, /*count=*/1, fp);
return (ret > 0) ? c : ret;
return (ret > 0) ? c : EOF;
}
int getchar(void) {
return getc(stdin);
}
char *fgets(char *s, int size, FILE *stream) {
int c = -1;
char *cs = s;
if (size < 1) {
return NULL;
}
while (--size > 0 && (c = fgetc(stream)) != EOF) {
if ((*cs++ = c) == '\n') {
break;
}
}
*cs = '\0';
return (c == EOF && cs == s) ? NULL : s;
}
int _fprintf_output_func(const char *str, size_t len, void *state) {
FILE *fp = (FILE *)state;
return io_write(fp->io, str, len);
return fwrite(str, /*size=*/1, /*count=*/len, fp);
}
int vfprintf(FILE *fp, const char *fmt, va_list ap) {