diff --git a/lib/libfs/context.c b/lib/libfs/context.c index 6fb9a0c..dc0d740 100644 --- a/lib/libfs/context.c +++ b/lib/libfs/context.c @@ -1,6 +1,7 @@ #include "btree.h" #include "file.h" #include "interface.h" +#include "mapping.h" #include #include @@ -8,9 +9,16 @@ #include #include #include +#include #include +#include +#include +#include +#include #include +#define TEMP_OBJECT_SIZE 0x10000 + BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file); BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file); @@ -18,12 +26,21 @@ struct fs_context { struct fs_superblock *ctx_sb; struct fs_allocator *ctx_alloc; struct btree ctx_filelist; + kern_handle_t ctx_vm_controller; + kern_handle_t ctx_channel; + kern_handle_t ctx_temp_object; + void *ctx_temp_object_buf; struct fs_vtable ctx_vtable; }; struct fs_context *fs_context_create(struct fs_allocator *alloc) { + kern_handle_t self, address_space; + task_self(&self); + task_get_address_space(self, &address_space); + kern_handle_close(self); + struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx); if (!ctx) { return NULL; @@ -31,10 +48,48 @@ struct fs_context *fs_context_create(struct fs_allocator *alloc) memset(ctx, 0x0, sizeof *ctx); + kern_status_t status = vm_controller_create(&ctx->ctx_vm_controller); + if (status != KERN_OK) { + fs_free(alloc, ctx); + return NULL; + } + + status = vm_object_create( + NULL, + 0, + TEMP_OBJECT_SIZE, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, + &ctx->ctx_temp_object); + if (status != KERN_OK) { + kern_handle_close(ctx->ctx_vm_controller); + fs_free(alloc, ctx); + return NULL; + } + + virt_addr_t temp_buffer; + status = address_space_map( + address_space, + MAP_ADDRESS_ANY, + ctx->ctx_temp_object, + 0, + TEMP_OBJECT_SIZE, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, + &temp_buffer); + if (status != KERN_OK) { + kern_handle_close(ctx->ctx_temp_object); + kern_handle_close(ctx->ctx_vm_controller); + fs_free(alloc, ctx); + return NULL; + } + + ctx->ctx_temp_object_buf = (void *)temp_buffer; ctx->ctx_alloc = alloc; ctx->ctx_vtable.open = fs_msg_open; + ctx->ctx_vtable.close = fs_msg_close; ctx->ctx_vtable.read = fs_msg_read; + ctx->ctx_vtable.write = fs_msg_write; + ctx->ctx_vtable.map = fs_msg_map; return ctx; } @@ -73,6 +128,112 @@ enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx) return FS_ERR_NOT_IMPLEMENTED; } +void fs_context_set_channel(struct fs_context *ctx, kern_handle_t channel) +{ + ctx->ctx_channel = channel; +} + +kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx) +{ + return ctx->ctx_vm_controller; +} + +static enum fs_status handle_msg(struct fs_context *ctx) +{ + xpc_msg_t msg; + kern_status_t status = xpc_msg_recv_nowait(ctx->ctx_channel, &msg); + if (status == KERN_NO_ENTRY) { + return FS_SUCCESS; + } + + if (status != KERN_OK) { + kern_logf("message recv error %d", status); + return FS_ERR_INTERNAL_FAILURE; + } + + switch (msg.msg_header.hdr_interface) { + case INTERFACE_FS: + status = fs_context_dispatch_msg(ctx, &msg); + break; + default: + kern_logf( + "unknown message protocol %u", + msg.msg_header.hdr_interface); + xpc_msg_reply_error(&msg, KERN_UNSUPPORTED); + break; + } + + return FS_SUCCESS; +} + +static enum fs_status handle_page_request(struct fs_context *ctx) +{ + equeue_packet_page_request_t packet; + vm_controller_recv(ctx->ctx_vm_controller, &packet); + struct file_mapping *mapping = (struct file_mapping *)packet.req_vmo; + kern_logf( + "received page request [%zx-%zx] for file %s", + packet.req_offset, + packet.req_offset + packet.req_length, + mapping->m_file->f_dent->d_name); + + size_t length = packet.req_length; + if (length > TEMP_OBJECT_SIZE) { + length = TEMP_OBJECT_SIZE; + } + + char *dst = ctx->ctx_temp_object_buf; + xpc_buffer_t buf = XPC_LOCAL_BUFFER_OUT(dst, TEMP_OBJECT_SIZE); + enum fs_status status = fs_file_read_at( + mapping->m_file, + &buf, + mapping->m_file_offset + packet.req_offset, + length); + if (status != FS_SUCCESS) { + kern_logf("map-read failed with code %d", status); + return status; + } + + vm_controller_supply_pages( + ctx->ctx_vm_controller, + mapping->m_vmo, + packet.req_offset, + ctx->ctx_temp_object, + 0, + packet.req_length); + return FS_SUCCESS; +} + +enum fs_status fs_context_handle_request(struct fs_context *ctx) +{ + kern_wait_item_t waiters[] = { + { + .w_handle = ctx->ctx_channel, + .w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED, + }, + { + .w_handle = ctx->ctx_vm_controller, + .w_waitfor = VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED, + }, + }; + const size_t nr_waiters = sizeof waiters / sizeof waiters[0]; + + kern_status_t kstatus = kern_object_wait(waiters, nr_waiters); + if (kstatus != KERN_OK) { + return FS_ERR_INTERNAL_FAILURE; + } + + if (waiters[0].w_observed & CHANNEL_SIGNAL_MSG_RECEIVED) { + return handle_msg(ctx); + } + + if (waiters[1].w_observed & VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED) { + return handle_page_request(ctx); + } + + return FS_SUCCESS; +} + struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id) { struct fs_file *f = get_file(&ctx->ctx_filelist, id); diff --git a/lib/libfs/file.c b/lib/libfs/file.c index f8018fc..5857974 100644 --- a/lib/libfs/file.c +++ b/lib/libfs/file.c @@ -25,9 +25,22 @@ enum fs_status fs_file_read( return status; } -enum fs_status fs_file_write( +enum fs_status fs_file_read_at( struct fs_file *f, struct xpc_buffer *buf, + off_t offset, + size_t count) +{ + if (!f->f_ops || !f->f_ops->f_read) { + return FS_ERR_NOT_IMPLEMENTED; + } + + return f->f_ops->f_read(f, buf, count, &offset); +} + +enum fs_status fs_file_write( + struct fs_file *f, + const struct xpc_buffer *buf, size_t count) { if (!f->f_ops || !f->f_ops->f_write) { diff --git a/lib/libfs/file.h b/lib/libfs/file.h index d566aff..a6c425e 100644 --- a/lib/libfs/file.h +++ b/lib/libfs/file.h @@ -2,6 +2,7 @@ #define _FS_FILE_H_ #include "btree.h" +#include "queue.h" #include #include @@ -18,6 +19,8 @@ struct fs_file { off_t f_seek; struct fs_inode *f_inode; struct fs_dentry *f_dent; + + struct queue f_mappings; }; #endif diff --git a/lib/libfs/include/fs/context.h b/lib/libfs/include/fs/context.h index 498b220..d9e760a 100644 --- a/lib/libfs/include/fs/context.h +++ b/lib/libfs/include/fs/context.h @@ -30,6 +30,12 @@ extern enum fs_status fs_context_mount_filesystem( enum fs_mount_flags flags); extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx); +extern void fs_context_set_channel( + struct fs_context *ctx, + kern_handle_t channel); +extern kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx); +extern enum fs_status fs_context_handle_request(struct fs_context *ctx); + extern struct fs_file *fs_context_open_file( struct fs_context *ctx, unsigned long id); diff --git a/lib/libfs/include/fs/file.h b/lib/libfs/include/fs/file.h index af36791..4d9d335 100644 --- a/lib/libfs/include/fs/file.h +++ b/lib/libfs/include/fs/file.h @@ -27,9 +27,14 @@ extern enum fs_status fs_file_read( struct fs_file *f, struct xpc_buffer *buf, size_t count); -extern enum fs_status fs_file_write( +extern enum fs_status fs_file_read_at( struct fs_file *f, struct xpc_buffer *buf, + off_t offset, + size_t count); +extern enum fs_status fs_file_write( + struct fs_file *f, + const struct xpc_buffer *buf, size_t count); #endif diff --git a/lib/libfs/interface.h b/lib/libfs/interface.h index f57e9ee..924550d 100644 --- a/lib/libfs/interface.h +++ b/lib/libfs/interface.h @@ -17,9 +17,8 @@ extern kern_status_t fs_msg_open( int *out_err, void *arg); extern kern_status_t fs_msg_close( - const struct msg_endpoint *sender, - const char *path, - int flags, + xpc_context_t *ctx, + const xpc_endpoint_t *sender, int *out_err, void *arg); @@ -31,5 +30,21 @@ extern kern_status_t fs_msg_read( size_t *out_nr_read, xpc_buffer_t *out_data, void *arg); +extern kern_status_t fs_msg_write( + xpc_context_t *ctx, + const xpc_endpoint_t *sender, + const xpc_buffer_t *data, + int *out_err, + size_t *out_nr_written, + void *arg); + +extern kern_status_t fs_msg_map( + xpc_context_t *ctx, + const xpc_endpoint_t *sender, + int prot, + int flags, + int *out_err, + kern_handle_t *out_vmo, + void *arg); #endif diff --git a/lib/libfs/interface/close.c b/lib/libfs/interface/close.c new file mode 100644 index 0000000..7fecdb7 --- /dev/null +++ b/lib/libfs/interface/close.c @@ -0,0 +1,14 @@ +#include "../interface.h" + +#include +#include + +extern kern_status_t fs_msg_close( + xpc_context_t *ctx, + const xpc_endpoint_t *sender, + int *out_err, + void *arg) +{ + *out_err = ENOSYS; + return KERN_OK; +} diff --git a/lib/libfs/interface/map.c b/lib/libfs/interface/map.c new file mode 100644 index 0000000..c855f57 --- /dev/null +++ b/lib/libfs/interface/map.c @@ -0,0 +1,74 @@ +#include "../file.h" +#include "../mapping.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern kern_status_t fs_msg_map( + xpc_context_t *xpc, + const xpc_endpoint_t *sender, + int prot, + int flags, + int *out_err, + kern_handle_t *out_vmo, + void *arg) +{ + struct fs_context *ctx = arg; + struct fs_file *f = fs_context_get_file(ctx, sender->e_port); + if (!f) { + *out_err = EBADF; + return KERN_OK; + } + + struct file_mapping *mapping = fs_context_alloc(ctx, sizeof *mapping); + if (!mapping) { + *out_err = ENOMEM; + return KERN_OK; + } + + kern_logf("mapping file %s", f->f_dent->d_name); + vm_prot_t vm_prot = VM_PROT_USER; + if (prot & PROT_READ) { + vm_prot |= VM_PROT_READ; + } + + if (prot & PROT_WRITE) { + vm_prot |= VM_PROT_WRITE; + } + + if (prot & PROT_EXEC) { + vm_prot |= VM_PROT_EXEC; + } + + kern_handle_t vmo = KERN_HANDLE_INVALID; + kern_status_t status = vm_controller_create_object( + fs_context_get_vm_controller(ctx), + f->f_dent->d_name, + strlen(f->f_dent->d_name), + (equeue_key_t)mapping, + f->f_inode->i_size, + vm_prot, + &vmo); + + if (status != KERN_OK) { + fs_context_free(ctx, mapping); + *out_err = __errno_from_kern_status(status); + return KERN_OK; + } + + mapping->m_file = f; + mapping->m_file_offset = 0; + kern_handle_duplicate(vmo, &mapping->m_vmo); + queue_push_back(&f->f_mappings, &mapping->m_entry); + + *out_err = SUCCESS; + *out_vmo = vmo; + return KERN_OK; +} diff --git a/lib/libfs/interface/write.c b/lib/libfs/interface/write.c new file mode 100644 index 0000000..4573025 --- /dev/null +++ b/lib/libfs/interface/write.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +extern kern_status_t fs_msg_write( + xpc_context_t *xpc, + const xpc_endpoint_t *sender, + const xpc_buffer_t *data, + int *out_err, + size_t *out_nr_written, + void *arg) +{ + struct fs_context *ctx = arg; + struct fs_file *f = fs_context_get_file(ctx, sender->e_port); + if (!f) { + *out_err = EBADF; + return KERN_OK; + } + + size_t start = fs_file_get_cursor(f); + enum fs_status status = fs_file_write(f, data, data->buf_len); + size_t end = fs_file_get_cursor(f); + + *out_err = fs_status_to_errno(status); + *out_nr_written = end - start; + + return KERN_OK; +} diff --git a/lib/libfs/mapping.h b/lib/libfs/mapping.h new file mode 100644 index 0000000..30f3c0d --- /dev/null +++ b/lib/libfs/mapping.h @@ -0,0 +1,17 @@ +#ifndef MAPPING_H_ +#define MAPPING_H_ + +#include "queue.h" + +#include + +struct fs_file; + +struct file_mapping { + struct fs_file *m_file; + kern_handle_t m_vmo; + off_t m_file_offset; + struct queue_entry m_entry; +}; + +#endif diff --git a/lib/libfs/queue.c b/lib/libfs/queue.c new file mode 100644 index 0000000..5c6060e --- /dev/null +++ b/lib/libfs/queue.c @@ -0,0 +1,138 @@ +#include "queue.h" + +size_t queue_length(struct queue *q) +{ + size_t i = 0; + struct queue_entry *x = q->q_first; + while (x) { + i++; + x = x->qe_next; + } + + return i; +} + +void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before) +{ + struct queue_entry *x = before->qe_prev; + if (x) { + x->qe_next = entry; + } else { + q->q_first = entry; + } + + entry->qe_prev = x; + + before->qe_prev = entry; + entry->qe_next = before; +} + +void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after) +{ + struct queue_entry *x = after->qe_next; + if (x) { + x->qe_prev = entry; + } else { + q->q_last = entry; + } + + entry->qe_prev = x; + + after->qe_next = entry; + entry->qe_prev = after; +} + +void queue_push_front(struct queue *q, struct queue_entry *entry) +{ + if (q->q_first) { + q->q_first->qe_prev = entry; + } + + entry->qe_next = q->q_first; + entry->qe_prev = NULL; + + q->q_first = entry; + + if (!q->q_last) { + q->q_last = entry; + } +} + +void queue_push_back(struct queue *q, struct queue_entry *entry) +{ + if (q->q_last) { + q->q_last->qe_next = entry; + } + + entry->qe_prev = q->q_last; + entry->qe_next = NULL; + + q->q_last = entry; + + if (!q->q_first) { + q->q_first = entry; + } +} + +struct queue_entry *queue_pop_front(struct queue *q) +{ + struct queue_entry *x = q->q_first; + if (x) { + queue_delete(q, x); + } + + return x; +} + +struct queue_entry *queue_pop_back(struct queue *q) +{ + struct queue_entry *x = q->q_last; + if (x) { + queue_delete(q, x); + } + + return x; +} + +void queue_delete(struct queue *q, struct queue_entry *entry) +{ + if (!entry) { + return; + } + + if (entry == q->q_first) { + q->q_first = q->q_first->qe_next; + } + + if (entry == q->q_last) { + q->q_last = q->q_last->qe_prev; + } + + if (entry->qe_next) { + entry->qe_next->qe_prev = entry->qe_prev; + } + + if (entry->qe_prev) { + entry->qe_prev->qe_next = entry->qe_next; + } + + entry->qe_next = entry->qe_prev = NULL; +} + +void queue_delete_all(struct queue *q) +{ + struct queue_entry *x = q->q_first; + while (x) { + struct queue_entry *next = x->qe_next; + x->qe_next = x->qe_prev = NULL; + x = next; + } + + q->q_first = q->q_last = NULL; +} diff --git a/lib/libfs/queue.h b/lib/libfs/queue.h new file mode 100644 index 0000000..787fb99 --- /dev/null +++ b/lib/libfs/queue.h @@ -0,0 +1,100 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QUEUE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL}) +#define QUEUE_ENTRY_INIT \ + ((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL}) + +#define queue_foreach(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_first(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_next(&((iter_name)->node_member)))) + +#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_last(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_prev(&((iter_name)->node_member)))) + +struct queue_entry { + struct queue_entry *qe_next; + struct queue_entry *qe_prev; +}; + +struct queue { + struct queue_entry *q_first; + struct queue_entry *q_last; +}; + +static inline void queue_init(struct queue *q) +{ + memset(q, 0x00, sizeof *q); +} +static inline bool queue_empty(struct queue *q) +{ + return q->q_first == NULL; +} + +static inline struct queue_entry *queue_first(struct queue *q) +{ + return q->q_first; +} +static inline struct queue_entry *queue_last(struct queue *q) +{ + return q->q_last; +} +static inline struct queue_entry *queue_next(struct queue_entry *entry) +{ + return entry->qe_next; +} +static inline struct queue_entry *queue_prev(struct queue_entry *entry) +{ + return entry->qe_prev; +} + +extern size_t queue_length(struct queue *q); + +extern void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before); +extern void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after); + +extern void queue_push_front(struct queue *q, struct queue_entry *entry); +extern void queue_push_back(struct queue *q, struct queue_entry *entry); + +extern struct queue_entry *queue_pop_front(struct queue *q); +extern struct queue_entry *queue_pop_back(struct queue *q); + +extern void queue_delete(struct queue *q, struct queue_entry *entry); +extern void queue_delete_all(struct queue *q); + +#ifdef __cplusplus +} +#endif + +#endif