libc: io: implement file io buffering and internal locking for concurrency
This commit is contained in:
@@ -15,10 +15,13 @@ int fclose(struct __opaque_file *stream)
|
|||||||
|
|
||||||
close(stream->f_fd);
|
close(stream->f_fd);
|
||||||
|
|
||||||
if (stream->f_buf) {
|
if (stream->f_buf.buf_ptr) {
|
||||||
free(stream->f_buf);
|
free(stream->f_buf.buf_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(stream->f_flags & FILE_STATIC)) {
|
||||||
|
free(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(stream);
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
int ferr(struct __opaque_file *stream)
|
int ferror(struct __opaque_file *stream)
|
||||||
{
|
{
|
||||||
return (stream->f_flags & FILE_ERR) != 0;
|
return (stream->f_flags & FILE_ERR) != 0;
|
||||||
}
|
}
|
||||||
+12
-16
@@ -3,29 +3,25 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int fgetc(struct __opaque_file *stream)
|
int __fgetc(struct __opaque_file *stream)
|
||||||
{
|
{
|
||||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t available = __libc_file_available(stream);
|
char c = 0;
|
||||||
if (available == 0) {
|
int ret = __libc_file_read(stream, &c, 1);
|
||||||
int err = __libc_file_refill(stream);
|
if (ret < 1) {
|
||||||
if (err != SUCCESS) {
|
|
||||||
__set_errno(err);
|
|
||||||
stream->f_flags |= FILE_ERR;
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
available = __libc_file_available(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (available == 0) {
|
|
||||||
stream->f_flags |= FILE_EOF;
|
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
char c = stream->f_buf[stream->f_buf_readptr++];
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fgetc(struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
|
int ret = __fgetc(stream);
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ char *fgets(
|
|||||||
int count,
|
int count,
|
||||||
struct __opaque_file *restrict stream)
|
struct __opaque_file *restrict stream)
|
||||||
{
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||||
|
__libc_file_unlock(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,13 +30,16 @@ char *fgets(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ferr(stream)) {
|
if (ferror(stream)) {
|
||||||
|
__libc_file_unlock(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feof(stream) && i == 0) {
|
if (feof(stream) && i == 0) {
|
||||||
|
__libc_file_unlock(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__libc_file_unlock(stream);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|||||||
+299
-46
@@ -1,76 +1,329 @@
|
|||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <mango/futex.h>
|
||||||
|
#include <mango/log.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define DEFAULT_BUFFER_SIZE 4096
|
#define OP_READ 1
|
||||||
|
#define OP_WRITE 2
|
||||||
|
|
||||||
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
|
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
|
||||||
|
|
||||||
int __libc_file_refill(struct __opaque_file *f)
|
static long clear_buf(struct __opaque_file *f)
|
||||||
{
|
{
|
||||||
f->f_buf_datalen = 0;
|
__libc_ringbuf_clear(&f->f_buf);
|
||||||
f->f_buf_readptr = 0;
|
lseek(f->f_fd, f->f_seek, SEEK_SET);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (f->f_fd < 0) {
|
static long refill_buf(struct __opaque_file *f)
|
||||||
return EBADF;
|
{
|
||||||
|
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 (f->f_flags & FILE_ERR) {
|
if (nr_read > 0) {
|
||||||
return EIO;
|
return nr_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f->f_flags & FILE_EOF) {
|
if (r < 0) {
|
||||||
return SUCCESS;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!f->f_buf) {
|
return nr_read;
|
||||||
f->f_buf_max = DEFAULT_BUFFER_SIZE;
|
}
|
||||||
f->f_buf = malloc(f->f_buf_max);
|
|
||||||
if (!f->f_buf) {
|
static long flush_buf(struct __opaque_file *f)
|
||||||
f->f_buf_max = 0;
|
{
|
||||||
return ENOMEM;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long r = read(f->f_fd, f->f_buf, f->f_buf_max);
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return -r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->f_buf_datalen = r;
|
return nr_read;
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int __libc_file_read(struct __opaque_file *f, void *out, size_t count)
|
static long read_nobuf(struct __opaque_file *f, void *out, size_t count)
|
||||||
{
|
{
|
||||||
if (f->f_fd < 0) {
|
kern_tracef("read_nobuf");
|
||||||
return EBADF;
|
return read(f->f_fd, out, count);
|
||||||
}
|
|
||||||
|
|
||||||
if (f->f_flags & FILE_ERR) {
|
|
||||||
return EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f->f_flags & FILE_EOF) {
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t available = f->f_buf_datalen - f->f_buf_readptr;
|
|
||||||
if (count > available) {
|
|
||||||
count = available;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(out, f->f_buf + f->f_buf_readptr, count);
|
|
||||||
|
|
||||||
f->f_buf_readptr += count;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t __libc_file_available(struct __opaque_file *f)
|
long __libc_file_read(struct __opaque_file *f, void *out, size_t count)
|
||||||
{
|
{
|
||||||
return f->f_buf_datalen - f->f_buf_readptr;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,38 @@
|
|||||||
#ifndef _LIBC_IO_FILE_H_
|
#ifndef _LIBC_IO_FILE_H_
|
||||||
#define _LIBC_IO_FILE_H_
|
#define _LIBC_IO_FILE_H_
|
||||||
|
|
||||||
|
#include "ringbuf.h"
|
||||||
|
|
||||||
|
#include <mango/types.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
enum file_flags {
|
enum file_flags {
|
||||||
FILE_EOF = 0x01u,
|
FILE_EOF = 0x01u,
|
||||||
FILE_ERR = 0x02u,
|
FILE_ERR = 0x02u,
|
||||||
FILE_BIN = 0x04u,
|
FILE_BIN = 0x04u,
|
||||||
|
FILE_STATIC = 0x08u,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct __opaque_file {
|
struct __opaque_file {
|
||||||
enum file_flags f_flags;
|
enum file_flags f_flags;
|
||||||
|
kern_futex_t f_lock;
|
||||||
|
int f_prev;
|
||||||
|
int f_buffer_mode;
|
||||||
int f_fd;
|
int f_fd;
|
||||||
char f_unget;
|
char f_unget;
|
||||||
char *f_buf;
|
/* the current seek position of the buffered data (i.e. the offset of
|
||||||
size_t f_buf_datalen, f_buf_max;
|
* the data that would be returned by reading f_buf). */
|
||||||
size_t f_buf_readptr;
|
size_t f_seek;
|
||||||
|
struct ringbuf f_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int __libc_file_refill(struct __opaque_file *f);
|
extern void __libc_file_lock(struct __opaque_file *f);
|
||||||
extern int __libc_file_read(struct __opaque_file *f, void *out, size_t count);
|
extern void __libc_file_unlock(struct __opaque_file *f);
|
||||||
extern size_t __libc_file_available(struct __opaque_file *f);
|
|
||||||
|
extern long __libc_file_read(struct __opaque_file *f, void *p, size_t count);
|
||||||
|
extern long __libc_file_write(
|
||||||
|
struct __opaque_file *f,
|
||||||
|
const void *p,
|
||||||
|
size_t count);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -143,6 +143,12 @@ struct __opaque_file *fopen(const char *path, const char *mode)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fflags & FILE_BIN) {
|
||||||
|
out->f_buffer_mode = _IOFBF;
|
||||||
|
} else {
|
||||||
|
out->f_buffer_mode = _IOLBF;
|
||||||
|
}
|
||||||
|
|
||||||
out->f_fd = fd;
|
out->f_fd = fd;
|
||||||
out->f_flags = fflags;
|
out->f_flags = fflags;
|
||||||
__set_errno(SUCCESS);
|
__set_errno(SUCCESS);
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
int fprintf(struct __opaque_file *stream, const char *format, ...)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int __fputc(int c, struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
char cv = c;
|
||||||
|
int ret = __libc_file_write(stream, &cv, 1);
|
||||||
|
if (ret < 1) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fputc(int c, struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
|
int ret = __fputc(c, stream);
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int __fputc(int c, struct __opaque_file *stream);
|
||||||
|
|
||||||
|
int fputs(const char *restrict str, struct __opaque_file *restrict stream)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
|
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
int err = 0;
|
||||||
|
for (i = 0; str[i]; i++) {
|
||||||
|
err = __fputc(str[i], stream);
|
||||||
|
if (err < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
size_t fread(void *buf, size_t size, size_t count, struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
|
size_t total = size * count;
|
||||||
|
if (total == 0) {
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long ret = __libc_file_read(stream, buf, total);
|
||||||
|
if (ret == 0) {
|
||||||
|
stream->f_flags |= FILE_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
stream->f_flags |= FILE_ERR;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int fseek(struct __opaque_file *stream, long offset, int origin)
|
||||||
|
{
|
||||||
|
return lseek(stream->f_fd, offset, origin);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
long ftell(struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
return lseek(stream->f_fd, 0, SEEK_CUR);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
size_t fwrite(
|
||||||
|
const void *buf,
|
||||||
|
size_t size,
|
||||||
|
size_t count,
|
||||||
|
struct __opaque_file *stream)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stream);
|
||||||
|
size_t total = size * count;
|
||||||
|
if (total == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long ret = __libc_file_write(stream, buf, total);
|
||||||
|
if (ret == 0) {
|
||||||
|
stream->f_flags |= FILE_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
stream->f_flags |= FILE_ERR;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__libc_file_unlock(stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
int ret = vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <mango/log.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define DEFAULT_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
static int ringbuf_init(struct ringbuf *buf)
|
||||||
|
{
|
||||||
|
if (buf->buf_ptr) {
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf->buf_max) {
|
||||||
|
buf->buf_max = DEFAULT_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->buf_ptr = malloc(buf->buf_max);
|
||||||
|
if (!buf->buf_ptr) {
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libc_ringbuf_get_read_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
const char **out_bufp,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
int r = ringbuf_init(buf);
|
||||||
|
if (r != SUCCESS) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t contiguous_capacity = 0;
|
||||||
|
if (buf->buf_readp > buf->buf_writep) {
|
||||||
|
contiguous_capacity = buf->buf_max - buf->buf_readp;
|
||||||
|
} else {
|
||||||
|
contiguous_capacity = buf->buf_writep - buf->buf_readp;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_tracef(
|
||||||
|
"OPEN READ: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu",
|
||||||
|
buf->buf_readp,
|
||||||
|
buf->buf_writep,
|
||||||
|
buf->buf_max,
|
||||||
|
buf->buf_ptr + buf->buf_readp,
|
||||||
|
contiguous_capacity);
|
||||||
|
*out_len = contiguous_capacity;
|
||||||
|
|
||||||
|
if (contiguous_capacity == 0) {
|
||||||
|
*out_bufp = NULL;
|
||||||
|
} else {
|
||||||
|
*out_bufp = buf->buf_ptr + buf->buf_readp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __libc_ringbuf_put_read_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
const char **bufp,
|
||||||
|
size_t *len)
|
||||||
|
{
|
||||||
|
buf->buf_readp += *len;
|
||||||
|
|
||||||
|
if (buf->buf_readp == buf->buf_writep) {
|
||||||
|
buf->buf_readp = buf->buf_writep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_tracef("CLOSE READ: %p r=%zu", *bufp, *len);
|
||||||
|
|
||||||
|
*bufp = NULL;
|
||||||
|
*len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libc_ringbuf_get_write_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
char **out_bufp,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
int r = ringbuf_init(buf);
|
||||||
|
if (r != SUCCESS) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t contiguous_capacity = 0;
|
||||||
|
if (buf->buf_writep >= buf->buf_readp) {
|
||||||
|
contiguous_capacity = buf->buf_max - buf->buf_writep - 1;
|
||||||
|
|
||||||
|
if (buf->buf_readp > 0) {
|
||||||
|
contiguous_capacity++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
contiguous_capacity = buf->buf_readp - buf->buf_writep - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_tracef(
|
||||||
|
"OPEN WRITE: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu",
|
||||||
|
buf->buf_readp,
|
||||||
|
buf->buf_writep,
|
||||||
|
buf->buf_max,
|
||||||
|
buf->buf_ptr + buf->buf_readp,
|
||||||
|
contiguous_capacity);
|
||||||
|
|
||||||
|
*out_len = contiguous_capacity;
|
||||||
|
|
||||||
|
if (contiguous_capacity == 0) {
|
||||||
|
*out_bufp = NULL;
|
||||||
|
} else {
|
||||||
|
*out_bufp = buf->buf_ptr + buf->buf_writep;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __libc_ringbuf_put_write_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
char **bufp,
|
||||||
|
size_t *len)
|
||||||
|
{
|
||||||
|
buf->buf_writep += *len;
|
||||||
|
|
||||||
|
if (buf->buf_readp == buf->buf_writep) {
|
||||||
|
buf->buf_readp = buf->buf_writep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kern_tracef(
|
||||||
|
"CLOSE WRITE: %p r=%zu (%c)",
|
||||||
|
*bufp,
|
||||||
|
*len,
|
||||||
|
*(char *)(*bufp));
|
||||||
|
|
||||||
|
*bufp = NULL;
|
||||||
|
*len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count)
|
||||||
|
{
|
||||||
|
size_t nr_read = 0;
|
||||||
|
long r = 0;
|
||||||
|
|
||||||
|
while (nr_read < count) {
|
||||||
|
const char *src = NULL;
|
||||||
|
size_t available = 0;
|
||||||
|
r = __libc_ringbuf_get_read_buffer(buf, &src, &available);
|
||||||
|
if (r < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dest = p + nr_read;
|
||||||
|
size_t to_copy = count - nr_read;
|
||||||
|
|
||||||
|
if (available == 0) {
|
||||||
|
__libc_ringbuf_put_read_buffer(buf, &src, &available);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_copy > available) {
|
||||||
|
to_copy = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest, src, to_copy);
|
||||||
|
|
||||||
|
nr_read += available;
|
||||||
|
__libc_ringbuf_put_read_buffer(buf, &src, &to_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nr_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
long __libc_ringbuf_write(struct ringbuf *buf, const void *p, size_t count)
|
||||||
|
{
|
||||||
|
size_t nr_written = 0;
|
||||||
|
long r = 0;
|
||||||
|
|
||||||
|
while (nr_written < count) {
|
||||||
|
char *dest = NULL;
|
||||||
|
size_t available = 0;
|
||||||
|
r = __libc_ringbuf_get_write_buffer(buf, &dest, &available);
|
||||||
|
if (r < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *src = p + nr_written;
|
||||||
|
size_t to_copy = count - nr_written;
|
||||||
|
|
||||||
|
if (available == 0) {
|
||||||
|
__libc_ringbuf_put_write_buffer(buf, &dest, &available);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_copy > available) {
|
||||||
|
to_copy = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest, src, to_copy);
|
||||||
|
|
||||||
|
nr_written += available;
|
||||||
|
__libc_ringbuf_put_write_buffer(buf, &dest, &to_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nr_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libc_ringbuf_clear(struct ringbuf *buf)
|
||||||
|
{
|
||||||
|
buf->buf_readp = buf->buf_writep = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libc_ringbuf_resize(struct ringbuf *buf, size_t max)
|
||||||
|
{
|
||||||
|
if (!(buf->buf_flags & RINGBUF_STATIC)) {
|
||||||
|
free(buf->buf_ptr);
|
||||||
|
buf->buf_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->buf_readp = buf->buf_writep = 0;
|
||||||
|
buf->buf_flags &= ~RINGBUF_STATIC;
|
||||||
|
buf->buf_max = max;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __libc_ringbuf_resize_static(struct ringbuf *buf, void *p, size_t max)
|
||||||
|
{
|
||||||
|
if (!(buf->buf_flags & RINGBUF_STATIC)) {
|
||||||
|
free(buf->buf_ptr);
|
||||||
|
buf->buf_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->buf_readp = buf->buf_writep = 0;
|
||||||
|
buf->buf_flags |= RINGBUF_STATIC;
|
||||||
|
buf->buf_ptr = p;
|
||||||
|
buf->buf_max = max;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t __libc_ringbuf_available(const struct ringbuf *buf)
|
||||||
|
{
|
||||||
|
if (buf->buf_readp < buf->buf_writep) {
|
||||||
|
return buf->buf_writep - buf->buf_readp;
|
||||||
|
} else if (buf->buf_readp > buf->buf_writep) {
|
||||||
|
return buf->buf_max - buf->buf_readp + buf->buf_writep;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t __libc_ringbuf_capacity(const struct ringbuf *buf)
|
||||||
|
{
|
||||||
|
if (buf->buf_readp > buf->buf_writep) {
|
||||||
|
return buf->buf_readp - buf->buf_writep - 1;
|
||||||
|
} else {
|
||||||
|
return buf->buf_max - buf->buf_writep + buf->buf_readp - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef _LIBC_IO_RINGBUF_H_
|
||||||
|
#define _LIBC_IO_RINGBUF_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
enum ringbuf_flags {
|
||||||
|
RINGBUF_NONE = 0,
|
||||||
|
RINGBUF_STATIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ringbuf {
|
||||||
|
enum ringbuf_flags buf_flags;
|
||||||
|
char *buf_ptr;
|
||||||
|
size_t buf_max;
|
||||||
|
|
||||||
|
size_t buf_readp, buf_writep;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int __libc_ringbuf_get_read_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
const char **out_bufp,
|
||||||
|
size_t *out_len);
|
||||||
|
extern void __libc_ringbuf_put_read_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
const char **bufp,
|
||||||
|
size_t *len);
|
||||||
|
|
||||||
|
extern int __libc_ringbuf_get_write_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
char **out_bufp,
|
||||||
|
size_t *out_len);
|
||||||
|
extern void __libc_ringbuf_put_write_buffer(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
char **bufp,
|
||||||
|
size_t *len);
|
||||||
|
|
||||||
|
extern long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count);
|
||||||
|
extern long __libc_ringbuf_write(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
const void *p,
|
||||||
|
size_t count);
|
||||||
|
|
||||||
|
extern int __libc_ringbuf_clear(struct ringbuf *buf);
|
||||||
|
extern int __libc_ringbuf_resize(struct ringbuf *buf, size_t max);
|
||||||
|
extern int __libc_ringbuf_resize_static(
|
||||||
|
struct ringbuf *buf,
|
||||||
|
void *p,
|
||||||
|
size_t max);
|
||||||
|
|
||||||
|
extern size_t __libc_ringbuf_available(const struct ringbuf *buf);
|
||||||
|
extern size_t __libc_ringbuf_capacity(const struct ringbuf *buf);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int setvbuf(
|
||||||
|
struct __opaque_file *restrict stream,
|
||||||
|
char *restrict buf,
|
||||||
|
int mode,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case _IOFBF:
|
||||||
|
case _IOLBF:
|
||||||
|
case _IONBF:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return __set_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->f_prev != 0) {
|
||||||
|
return __set_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = SUCCESS;
|
||||||
|
if (buf) {
|
||||||
|
err = __libc_ringbuf_resize_static(&stream->f_buf, buf, size);
|
||||||
|
} else {
|
||||||
|
err = __libc_ringbuf_resize(&stream->f_buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != SUCCESS) {
|
||||||
|
return __set_errno(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->f_buffer_mode = mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static struct __opaque_file __stderr = {
|
||||||
|
.f_fd = 2,
|
||||||
|
.f_flags = FILE_STATIC,
|
||||||
|
.f_buffer_mode = _IONBF,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct __opaque_file *__libc_file_stderr(void)
|
||||||
|
{
|
||||||
|
return &__stderr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static struct __opaque_file __stdin = {
|
||||||
|
.f_fd = 0,
|
||||||
|
.f_flags = FILE_STATIC,
|
||||||
|
.f_buffer_mode = _IOLBF,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct __opaque_file *__libc_file_stdin(void)
|
||||||
|
{
|
||||||
|
return &__stdin;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static struct __opaque_file __stdout = {
|
||||||
|
.f_fd = 1,
|
||||||
|
.f_flags = FILE_STATIC,
|
||||||
|
.f_buffer_mode = _IOLBF,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct __opaque_file *__libc_file_stdout(void)
|
||||||
|
{
|
||||||
|
return &__stdout;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int __libc_fctprintf(
|
||||||
|
void (*out)(char character, void *arg),
|
||||||
|
void *arg,
|
||||||
|
const char *format,
|
||||||
|
va_list va);
|
||||||
|
extern int __fputc(int c, struct __opaque_file *stream);
|
||||||
|
|
||||||
|
static inline void _out_file(char character, void *arg)
|
||||||
|
{
|
||||||
|
struct __opaque_file *fp = arg;
|
||||||
|
__fputc(character, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vprintf(const char *format, va_list arg)
|
||||||
|
{
|
||||||
|
__libc_file_lock(stdout);
|
||||||
|
|
||||||
|
int ret = __libc_fctprintf(_out_file, stdout, format, arg);
|
||||||
|
|
||||||
|
__libc_file_unlock(stdout);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <mango/handle.h>
|
||||||
|
#include <mango/msg.h>
|
||||||
|
#include <rosetta/fs.h>
|
||||||
|
#include <sys/remote.h>
|
||||||
|
|
||||||
|
int write(int fd, const void *buf, size_t count)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
size_t nr_written;
|
||||||
|
kern_status_t status = fs_write(fd, buf, count, &err, &nr_written);
|
||||||
|
if (status != KERN_OK) {
|
||||||
|
return __set_errno(__errno_from_kern_status(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != SUCCESS) {
|
||||||
|
return __set_errno(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nr_written;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user