#include "file.h" #include #include #include #include #include #include #include #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; }