1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/client/file.cc Fri Apr 02 01:27:50 2021 +0200
1.3 @@ -0,0 +1,320 @@
1.4 +/*
1.5 + * File access convenience functions.
1.6 + *
1.7 + * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <ipc/cap_alloc.h>
1.26 +#include <ipc/mem_ipc.h>
1.27 +
1.28 +#include <string.h>
1.29 +
1.30 +#include "dataspace_client.h"
1.31 +#include "file_client.h"
1.32 +#include "opener_client.h"
1.33 +#include "opener_context_client.h"
1.34 +#include "pipe_client.h"
1.35 +#include "pipe_opener_client.h"
1.36 +#include "mapped_file_client.h"
1.37 +
1.38 +#include "file.h"
1.39 +
1.40 +
1.41 +
1.42 +/* Release resources for the given file. */
1.43 +
1.44 +void file_close(file_t *file)
1.45 +{
1.46 + if (l4_is_valid_cap(file->ref))
1.47 + ipc_cap_free_um(file->ref);
1.48 +
1.49 + if (file->memory != NULL)
1.50 + ipc_detach_dataspace(file->memory);
1.51 +
1.52 + file_init(file);
1.53 +}
1.54 +
1.55 +/* Initialise a file structure for a context obtained from the given server. */
1.56 +
1.57 +long file_context(file_t *file, l4_cap_idx_t server)
1.58 +{
1.59 + client_Opener opener(server);
1.60 + unsigned long size, flags;
1.61 + long err;
1.62 +
1.63 + file_init(file);
1.64 +
1.65 + err = opener.context(&file->ref);
1.66 + if (err)
1.67 + return err;
1.68 +
1.69 + client_Dataspace context_ds(file->ref);
1.70 +
1.71 + err = context_ds.info(&size, &flags);
1.72 + if (err)
1.73 + return err;
1.74 +
1.75 + file->start_pos = 0;
1.76 + file->end_pos = size;
1.77 +
1.78 + return ipc_attach_dataspace(file->ref, size, (void **) &file->memory);
1.79 +}
1.80 +
1.81 +/* Open a file using the given structure and context. */
1.82 +
1.83 +long file_context_open(file_t *file, file_t *context)
1.84 +{
1.85 + client_OpenerContext openercontext(context->ref);
1.86 + file_init(file);
1.87 + return openercontext.open(L4_FPAGE_RW, &file->size, &file->ref);
1.88 +}
1.89 +
1.90 +/* Initialise the given file structure. */
1.91 +
1.92 +void file_init(file_t *file)
1.93 +{
1.94 + file->memory = NULL;
1.95 + file->ref = L4_INVALID_CAP;
1.96 + file->start_pos = 0;
1.97 + file->end_pos = 0;
1.98 + file->data_end = 0;
1.99 +}
1.100 +
1.101 +/* Open a file using the given structure, indicating the filename and
1.102 + filesystem server. This is a convenience function invoking file_context and
1.103 + file_context_open. */
1.104 +
1.105 +long file_open(file_t *file, const char *filename, l4_cap_idx_t server)
1.106 +{
1.107 + file_t context;
1.108 + long err;
1.109 +
1.110 + err = file_context(&context, server);
1.111 + if (err)
1.112 + return err;
1.113 +
1.114 + if (!file_string_set(&context, filename, 0, NULL))
1.115 + return -L4_ENOMEM;
1.116 +
1.117 + err = file_context_open(file, &context);
1.118 + file_close(&context);
1.119 + return err;
1.120 +}
1.121 +
1.122 +
1.123 +
1.124 +/* Map a region of the given file to a memory region. */
1.125 +
1.126 +long file_mmap(file_t *file, offset_t position, offset_t length)
1.127 +{
1.128 + client_MappedFile mapped_file(file->ref);
1.129 + long err = mapped_file.mmap(position, length, &file->start_pos, &file->end_pos, &file->data_end);
1.130 +
1.131 + if (err)
1.132 + return err;
1.133 +
1.134 + return ipc_attach_dataspace(file->ref, file_span(file), (void **) &file->memory);
1.135 +}
1.136 +
1.137 +/* Resize a file. */
1.138 +
1.139 +long file_resize(file_t *file, offset_t size)
1.140 +{
1.141 + client_File _file(file->ref);
1.142 + offset_t file_size = size;
1.143 + long err = _file.resize(&file_size);
1.144 +
1.145 + if (!err)
1.146 + {
1.147 + /* Determine the extent of the file in this region. */
1.148 +
1.149 + if (file_size > file->end_pos)
1.150 + file->data_end = file_span(file);
1.151 + else
1.152 + file->data_end = file_size - file->start_pos;
1.153 +
1.154 + /* Update the file size locally. */
1.155 +
1.156 + file->size = file_size;
1.157 + }
1.158 +
1.159 + return err;
1.160 +}
1.161 +
1.162 +
1.163 +
1.164 +/* Return the amount of data in the mapped region for the given file. */
1.165 +
1.166 +offset_t file_populated_span(file_t *file)
1.167 +{
1.168 + offset_t size = file_span(file);
1.169 + return (file->data_end < size) ? file->data_end : size;
1.170 +}
1.171 +
1.172 +/* Return the size of the mapped region for the given file. */
1.173 +
1.174 +offset_t file_span(file_t *file)
1.175 +{
1.176 + return file->end_pos - file->start_pos;
1.177 +}
1.178 +
1.179 +
1.180 +
1.181 +/* Get a pointer to any terminated string at the given offset or NULL if the
1.182 + data from offset is not terminated. */
1.183 +
1.184 +char *file_string_get(file_t *file, offset_t offset)
1.185 +{
1.186 + offset_t limit = file_span(file) - offset;
1.187 +
1.188 + if (strnlen(file->memory + offset, limit) < limit)
1.189 + return file->memory + offset;
1.190 + else
1.191 + return NULL;
1.192 +}
1.193 +
1.194 +/* Copy a string to the mapped region at the given offset, returning 1 (true)
1.195 + where all characters were copied, 0 (false) otherwise. The precise number of
1.196 + characters copied, excluding the zero terminator is provided via the written
1.197 + parameter if it is not specified as NULL. */
1.198 +
1.199 +int file_string_set(file_t *file, const char *data, offset_t offset,
1.200 + offset_t *written)
1.201 +{
1.202 + offset_t i, pos, limit = file_span(file);
1.203 +
1.204 + /* Do not attempt to copy data with an invalid offset. */
1.205 +
1.206 + if (offset >= limit)
1.207 + {
1.208 + if (written != NULL)
1.209 + *written = 0;
1.210 + return 0;
1.211 + }
1.212 +
1.213 + /* Copy the data to the given offset, stopping at the end of the region. */
1.214 +
1.215 + for (i = 0, pos = offset; pos < limit; i++, pos++)
1.216 + {
1.217 + file->memory[pos] = data[i];
1.218 +
1.219 + /* Terminator written, can return immediately. */
1.220 +
1.221 + if (!data[i])
1.222 + {
1.223 + if (written != NULL)
1.224 + *written = pos - offset;
1.225 + return 1;
1.226 + }
1.227 + }
1.228 +
1.229 + /* Terminate the incomplete string at the end of the region. */
1.230 +
1.231 + file->memory[limit - 1] = '\0';
1.232 + if (written != NULL)
1.233 + *written = limit - 1 - offset;
1.234 + return 0;
1.235 +}
1.236 +
1.237 +
1.238 +
1.239 +/* Open two pipe endpoints using the given pipe server. */
1.240 +
1.241 +long pipe_open(offset_t size, file_t *reader, file_t *writer, l4_cap_idx_t server)
1.242 +{
1.243 + client_PipeOpener opener(server);
1.244 +
1.245 + file_init(reader);
1.246 + file_init(writer);
1.247 +
1.248 + long err = opener.pipe(size, &reader->ref, &writer->ref);
1.249 + if (err)
1.250 + return err;
1.251 +
1.252 + err = pipe_next(writer) || pipe_next(reader);
1.253 +
1.254 + if (err)
1.255 + {
1.256 + file_close(reader);
1.257 + file_close(writer);
1.258 + }
1.259 +
1.260 + return err;
1.261 +}
1.262 +
1.263 +/* Access the current region for a pipe endpoint. */
1.264 +
1.265 +long pipe_current(file_t *pipe)
1.266 +{
1.267 + client_Pipe _pipe(pipe->ref);
1.268 + long err = _pipe.current_region(&pipe->data_end, &pipe->size);
1.269 + char *memory = pipe->memory;
1.270 +
1.271 + if (err)
1.272 + return err;
1.273 +
1.274 + pipe->end_pos = pipe->size;
1.275 +
1.276 + err = ipc_attach_dataspace(pipe->ref, file_span(pipe), (void **) &pipe->memory);
1.277 + if (err)
1.278 + return err;
1.279 +
1.280 + if (memory != NULL)
1.281 + ipc_detach_dataspace(memory);
1.282 +
1.283 + return L4_EOK;
1.284 +}
1.285 +
1.286 +/* Access the next region for a pipe endpoint, updating the eventual size of
1.287 + the current region. */
1.288 +
1.289 +long pipe_next(file_t *pipe)
1.290 +{
1.291 + client_Pipe _pipe(pipe->ref);
1.292 + long err = _pipe.next_region(&pipe->data_end, &pipe->size);
1.293 + char *memory = pipe->memory;
1.294 +
1.295 + if (err)
1.296 + return err;
1.297 +
1.298 + pipe->end_pos = pipe->size;
1.299 +
1.300 + err = ipc_attach_dataspace(pipe->ref, file_span(pipe), (void **) &pipe->memory);
1.301 + if (err)
1.302 + return err;
1.303 +
1.304 + if (memory != NULL)
1.305 + ipc_detach_dataspace(memory);
1.306 +
1.307 + return L4_EOK;
1.308 +}
1.309 +
1.310 +/* Set the size of the written region. */
1.311 +
1.312 +long pipe_written(file_t *pipe, offset_t size)
1.313 +{
1.314 + if (size <= pipe->size)
1.315 + {
1.316 + pipe->data_end = size;
1.317 + return L4_EOK;
1.318 + }
1.319 + else
1.320 + return -L4_EINVAL;
1.321 +}
1.322 +
1.323 +// vim: tabstop=2 expandtab shiftwidth=2