#pragma once #include #include #include #include struct VfsBackend { /* amount of using references * VfsMount * VfsReq * Handle * once it reaches 0, it'll never increase */ size_t usehcnt; /* VfsMount */ /* amount of providing references * Proc * 0 - orphaned, will never increase */ // TODO move this into .user size_t provhcnt; VfsReq *queue; bool is_user; union { struct { Proc *handler; } user; struct { void (*accept)(VfsReq *); void (*cleanup)(VfsBackend *); void *data; } kern; }; }; /* describes an in-progress vfs call */ struct VfsReq { enum vfs_op type; struct { bool kern; // if false: use .buf ; if true: use .buf_kern union { char __user *buf; char *buf_kern; }; size_t len; } input; struct { char __user *buf; size_t len; } output; // TODO why doesn't this just have a reference to the handle? void __user *id; // handle.file.id long offset; int flags; Proc *caller; VfsBackend *backend; VfsReq *queue_next; VfsReq *postqueue_next; /* used by kernel backends */ /* only one of these queues is in use at a given moment, they could * be merged into a single field */ }; /** Assigns the vfs_request to the caller, and dispatches the call */ void vfsreq_dispatchcopy(VfsReq); void vfsreq_finish(VfsReq*, char __user *stored, long ret, int flags, Proc *handler); static inline void vfsreq_finish_short(VfsReq *req, long ret) { vfsreq_finish(req, (void __user *)ret, ret, 0, NULL); } /** Try to accept an enqueued request */ void vfsback_useraccept(VfsReq *); /** Decrements the "user" reference count. */ void vfsback_userdown(VfsBackend *); /** Decrements the "provider" reference count. */ void vfsback_provdown(VfsBackend *); struct ReqQueue { VfsReq *head; }; void postqueue_init(ReqQueue *q); void postqueue_join(ReqQueue *q, VfsReq *req); VfsReq *postqueue_pop(ReqQueue *q); /** If there are any pending read requests, and the ring buffer isn't empty, fulfill them * all with a single read. */ void postqueue_ringreadall(ReqQueue *q, ring_t *r);