#include #include #include #include #include <9p.h> #include #include "common.h" #include "debug.h" #include "utils.h" #include "string.h" #include "collection.h" #include "function.h" #include "array.h" #include "set.h" #include "file.h" #include "filepath.h" #include "holemanager.h" #include "fs.h" #include "rule.h" #include "filehandle.h" enum { DEBUG_DIRECTORYHANDLE = false }; typedef void (*filehandlefree)(FileHandle *); typedef struct FileHandleOperations { /** closes the current handle after which the handle is unusable */ void (*close)(FileHandle *handle); void (*read)(FileHandle *handle, Req *request, HoleManager *holemanager); void (*write)(FileHandle *handle, Req *request); filehandlefree free; } FileHandleOperations; struct FileHandle { FileHandleOperations *ops; }; void filehandle_free(FileHandle *self) { if(self == nil) { return; } filehandle_close(self); assert_valid(self->ops->free); self->ops->free(self); } void filehandle_close(FileHandle *self) { assert_valid(self); assert_valid(self->ops); assert_valid(self->ops->close); self->ops->close(self); } void filehandle_read(FileHandle *self, Req *request, HoleManager *holemanager) { assert_valid(self); assert_valid(self->ops); assert_valid(self->ops->read); self->ops->read(self, request, holemanager); } void filehandle_write(FileHandle *self, Req *request) { assert_valid(self); assert_valid(self->ops); assert_valid(self->ops->write); self->ops->write(self, request); } typedef struct SingleFileHandle { FileHandle; int fd; } SingleFileHandle; static void singlefilehandle_close(FileHandle *handle); static void singlefilehandle_read( FileHandle *handle, Req *request, HoleManager *holemanager); static void singlefilehandle_write(FileHandle *handle, Req *request); static FileHandleOperations singlefileops = { .close = singlefilehandle_close, .read = singlefilehandle_read, .write = singlefilehandle_write, .free = (filehandlefree)free }; SingleFileHandle *singlefilehandle_open(int fd) { SingleFileHandle *result; assert(fd_isopen(fd)); result = (SingleFileHandle *)emalloc_fs(sizeof(*result)); result->ops = &singlefileops; result->fd = fd; return result; } static void singlefilehandle_close(FileHandle *handle) { SingleFileHandle *self = (SingleFileHandle *)handle; assert(fd_isopen(self->fd)); file_close(self->fd); self->fd = INVALID_FD; } static void singlefilehandle_read( FileHandle *handle, Req *request, HoleManager *) { long readcount; static char buffer[FS_READ_BUFFER_MAX]; SingleFileHandle *self = (SingleFileHandle *)handle; assert(fd_isopen(self->fd)); request->ofcall.count = (request->ifcall.count > sizeof(buffer)) ? request->ifcall.count : sizeof(buffer); readcount = pread(self->fd, buffer, request->ofcall.count, request->ifcall.offset); return_9p_error_if(readcount < 0, last_error()); request->ofcall.count = readcount; request->ofcall.data = buffer; return_9p_success(); } static void singlefilehandle_write(FileHandle *handle, Req *request) { long writecount; SingleFileHandle *self = (SingleFileHandle *)handle; assert(fd_isopen(self->fd)); writecount = pwrite(self->fd, request->ifcall.data, request->ifcall.count, request->ifcall.offset); return_9p_error_if(writecount < 0, last_error()); request->ofcall.count = writecount; return_9p_success(); } typedef struct RuleFDPair { Rule *rule; int fd; } RuleFDPair; static RuleFDPair *rulefdpair_new(Rule *rule, int fd) { RuleFDPair *result; assert_valid(rule); assert(fd_isopen(fd)); result = (RuleFDPair *)emallocz_fs(sizeof(*result)); result->rule = rule; result->fd = fd; return result; } static void rulefdpair_free(RuleFDPair *self) { if(self == nil) { return; } assert_valid(self->rule); assert(fd_isopen(self->fd)); file_close(self->fd); free(self); } struct DirectoryHandle { FileHandle; char *path; Array *rulefdpairs; Array *dirs; }; static void directoryhandle_close(FileHandle *handle); static void directoryhandle_read( FileHandle *handle, Req *request, HoleManager *holemanager); static void directoryhandle_write(FileHandle *handle, Req *request); static void directoryhandle_free(FileHandle *handle); static FileHandleOperations directoryops = { .close = directoryhandle_close, .read = directoryhandle_read, .free = directoryhandle_free }; DirectoryHandle *directoryhandle_new(char *path) { DirectoryHandle *result; result = (DirectoryHandle *)emallocz_fs(sizeof(*result)); result->ops = &directoryops; result->path = estrdup_fs(path); result->rulefdpairs = array_new(); result->dirs = array_new(); return result; } static void directoryhandle_free(FileHandle *handle) { DirectoryHandle *self = (DirectoryHandle *)handle; free(self->path); assert(array_size(self->rulefdpairs) == 0); array_free(self->rulefdpairs); assert(array_size(self->dirs) == 0); array_free(self->dirs); free(self); } static void directoryhandle_close(FileHandle *handle) { DirectoryHandle *self = (DirectoryHandle *)handle; array_clear_with(self->rulefdpairs, (functionunary)rulefdpair_free); array_clear_with(self->dirs, free); } static void directoryhandle_dir_add(DirectoryHandle *self, Dir *d) { array_add(self->dirs, dir_clone(d)); } typedef struct DirectoryHandleReadData { DirectoryHandle *self; Set *names; HoleManager *holemanager; } DirectoryHandleReadData; static void directoryhandle_dir_addall( DirectoryHandleReadData *data, RuleFDPair *pair) { int i; int total; Dir *all; String *filepath; total = dirreadall(pair->fd, &all); if(total < 0) { return; } filepath = s_copy(data->self->path); for(i = 0; i < total; ++i) { filepath_append(&filepath, all[i].name); if( !set_includes(data->names, all[i].name) && !holemanager_includes(data->holemanager, s_to_c(filepath)) && rule_exists(pair->rule, s_to_c(filepath))) { directoryhandle_dir_add(data->self, &(all[i])); set_add(data->names, estrdup_fs(all[i].name)); } filepath_remove_last(filepath); } free(all); s_free(filepath); } static collection_do_ret directoryhandle_read_each(void *p, void *arg) { RuleFDPair *pair = (RuleFDPair *)p; DirectoryHandleReadData *data = (DirectoryHandleReadData *)arg; assert_valid(pair); assert_valid(data); directoryhandle_dir_addall(data, pair); return COLLECTION_DO_CONTINUE; } enum { DIRGEN_ERROR = -1, DIRGEN_SUCCESS = 0 }; static int directoryhandle_dirgen(int n, Dir *result, void *aux) { DirectoryHandle *self = (DirectoryHandle *)aux; assert_valid(self); assert_valid(result); if(n < 0 || n >= array_size(self->dirs)) { return DIRGEN_ERROR; } dir_copy((Dir *)(array_at(self->dirs, n)), result); return DIRGEN_SUCCESS; } static void directoryhandle_reread_if_offset_zero( DirectoryHandle *self, Req *request, HoleManager *holemanager) { DirectoryHandleReadData data; if(request->ifcall.offset != 0) { return; } data.self = self; data.names = set_new(string_isequal, string_hash); data.holemanager = holemanager; array_unary_do(self->dirs, free); array_do(self->rulefdpairs, directoryhandle_read_each, &data); set_free_with(data.names, free); } static void directoryhandle_read( FileHandle *handle, Req *request, HoleManager *holemanager) { DirectoryHandle *self = (DirectoryHandle *)handle; directoryhandle_reread_if_offset_zero(self, request, holemanager); dirread9p(request, directoryhandle_dirgen, self); return_9p_success(); } static void directoryhandle_write(FileHandle *, Req *) { /* this should never happen */ FATAL(DEBUG_DIRECTORYHANDLE, "directoryhandle_write should never be called"); assert_valid(false); } uint directoryhandle_count(DirectoryHandle *self) { assert_valid(self); return array_size(self->rulefdpairs); } void directoryhandle_add(DirectoryHandle *self, Rule *rule, int fd) { assert_valid(self); assert_valid(rule); assert(fd_isopen(fd)); array_add(self->rulefdpairs, rulefdpair_new(rule, fd)); }