330 lines
5.2 KiB
C
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;
|
|
}
|