Files
rosetta/lib/libc/io/stdio/file.c
T

330 lines
5.2 KiB
C

#include "file.h"
#include <errno.h>
#include <mango/futex.h>
#include <mango/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define OP_READ 1
#define OP_WRITE 2
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
static long clear_buf(struct __opaque_file *f)
{
__libc_ringbuf_clear(&f->f_buf);
lseek(f->f_fd, f->f_seek, SEEK_SET);
return 0;
}
static long refill_buf(struct __opaque_file *f)
{
kern_tracef("refill");
char *buf = NULL;
size_t available = 0;
long r = 0;
size_t nr_read = 0;
while (1) {
__libc_ringbuf_get_write_buffer(&f->f_buf, &buf, &available);
if (available == 0) {
__libc_ringbuf_put_write_buffer(
&f->f_buf,
&buf,
&available);
break;
}
kern_tracef("read(%d, %p, %zu)", f->f_fd, buf, available);
r = read(f->f_fd, buf, available);
if (r <= 0) {
break;
}
available = r;
nr_read += r;
__libc_ringbuf_put_write_buffer(&f->f_buf, &buf, &available);
}
if (nr_read > 0) {
return nr_read;
}
if (r < 0) {
return -errno;
}
return nr_read;
}
static long flush_buf(struct __opaque_file *f)
{
const char *buf = NULL;
size_t available = 0;
long r = 0;
size_t nr_written = 0;
while (1) {
__libc_ringbuf_get_read_buffer(&f->f_buf, &buf, &available);
if (available == 0) {
__libc_ringbuf_put_read_buffer(
&f->f_buf,
&buf,
&available);
break;
}
r = write(f->f_fd, buf, available);
kern_tracef(
"write(%d, %p, %zu) = %ld",
f->f_fd,
buf,
available,
r);
if (r < 0) {
break;
}
available = r;
nr_written += r;
__libc_ringbuf_put_read_buffer(&f->f_buf, &buf, &available);
}
if (nr_written > 0) {
return nr_written;
}
if (r < 0) {
return -errno;
}
return 0;
}
void __libc_file_lock(struct __opaque_file *f)
{
kern_futex_t expected = 0;
while (1) {
kern_tracef("lock=%u (%p)", f->f_lock, f);
expected = 0;
if (__atomic_compare_exchange_n(
&f->f_lock,
&expected,
1,
0,
__ATOMIC_ACQUIRE,
__ATOMIC_ACQUIRE)) {
kern_tracef("locked=%u (%p)", f->f_lock, f);
return;
}
kern_tracef("wait=%u (%p)", expected, f);
futex_wait(&f->f_lock, 1, FUTEX_PRIVATE);
}
}
void __libc_file_unlock(struct __opaque_file *f)
{
f->f_lock = 0;
futex_wake(&f->f_lock, 1, FUTEX_PRIVATE);
kern_tracef("unlocked=%u (%p)", f->f_lock, f);
}
static long read_buf(struct __opaque_file *f, void *out, size_t count)
{
kern_tracef("read_buf(%p, %zu)", out, count);
char *dest = out;
size_t nr_read = 0;
long r = 0;
while (nr_read < count) {
r = __libc_ringbuf_read(
&f->f_buf,
dest + nr_read,
count - nr_read);
if (r < 0) {
break;
}
nr_read += r;
if (r == 0) {
r = refill_buf(f);
if (r <= 0) {
break;
}
}
}
if (r < 0) {
return r;
}
return nr_read;
}
static long read_nobuf(struct __opaque_file *f, void *out, size_t count)
{
kern_tracef("read_nobuf");
return read(f->f_fd, out, count);
}
long __libc_file_read(struct __opaque_file *f, void *out, size_t count)
{
long ret = 0;
if (f->f_prev != OP_READ) {
ret = flush_buf(f);
clear_buf(f);
}
if (ret != 0) {
return __set_errno(ret);
}
switch (f->f_buffer_mode) {
case _IOFBF:
case _IOLBF:
ret = read_buf(f, out, count);
break;
case _IONBF:
ret = read_nobuf(f, out, count);
break;
default:
ret = -EINVAL;
break;
}
f->f_prev = OP_READ;
if (ret < 0) {
f->f_flags |= FILE_ERR;
return __set_errno(-ret);
}
if (ret == 0) {
f->f_flags |= FILE_EOF;
}
f->f_seek += ret;
return ret;
}
static long write_fullbuf(struct __opaque_file *f, const void *p, size_t count)
{
const char *src = p;
size_t nr_written = 0;
long r = 0;
while (nr_written < count) {
r = __libc_ringbuf_write(
&f->f_buf,
src + nr_written,
count - nr_written);
if (r < 0) {
break;
}
nr_written += r;
if (r == 0) {
r = flush_buf(f);
if (r <= 0) {
break;
}
}
}
if (r < 0) {
return r;
}
return nr_written;
}
static long write_linebuf(
struct __opaque_file *f,
const void *out,
size_t count)
{
const char *s = out;
size_t nr_written = 0;
long r = 0;
while (nr_written < count) {
char c = s[nr_written];
r = __libc_ringbuf_write(&f->f_buf, &c, 1);
if (r < 0) {
break;
}
nr_written += r;
if (r == 0 || c == '\n') {
r = flush_buf(f);
if (r < 0) {
break;
}
}
}
if (r < 0) {
return r;
}
return nr_written;
}
static long write_nobuf(struct __opaque_file *f, const void *out, size_t count)
{
return write(f->f_fd, out, count);
}
long __libc_file_write(struct __opaque_file *f, const void *buf, size_t count)
{
if (f->f_lock != 1) {
kern_tracef("file is not locked!!!");
}
long ret = 0;
if (f->f_prev != OP_WRITE) {
ret = clear_buf(f);
}
if (ret != 0) {
return __set_errno(ret);
}
switch (f->f_buffer_mode) {
case _IOFBF:
ret = write_fullbuf(f, buf, count);
break;
case _IOLBF:
ret = write_linebuf(f, buf, count);
break;
case _IONBF:
ret = write_nobuf(f, buf, count);
break;
default:
ret = -EINVAL;
break;
}
f->f_prev = OP_WRITE;
if (ret < 0) {
f->f_flags |= FILE_ERR;
return __set_errno(-ret);
}
if (ret == 0) {
f->f_flags |= FILE_EOF;
}
f->f_seek += ret;
return ret;
}