#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <String.h>
#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));
}
|