1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libfsclient/lib/src/file.cc Thu Apr 15 23:15:17 2021 +0200
1.3 @@ -0,0 +1,452 @@
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 +/* Update the extent of the file in a region using the region start and end
1.43 + positions and the file size. */
1.44 +
1.45 +static void _update_extent(file_t *file)
1.46 +{
1.47 + /* Handle files ending after or within the region. */
1.48 +
1.49 + if (file->size > file->start_pos)
1.50 + {
1.51 + if (file->size > file->end_pos)
1.52 + file->data_end = file->end_pos - file->start_pos;
1.53 + else
1.54 + file->data_end = file->size - file->start_pos;
1.55 + }
1.56 +
1.57 + /* Handle files ending before the region. */
1.58 +
1.59 + else
1.60 + file->data_end = 0;
1.61 +}
1.62 +
1.63 +
1.64 +
1.65 +/* Initialise the given file structure. */
1.66 +
1.67 +void file_init(file_t *file)
1.68 +{
1.69 + file->memory = NULL;
1.70 + file->ref = L4_INVALID_CAP;
1.71 + file->start_pos = 0;
1.72 + file->end_pos = 0;
1.73 + file->data_end = 0;
1.74 + file->data_current = 0;
1.75 + file->can_mmap = 1;
1.76 +}
1.77 +
1.78 +
1.79 +
1.80 +/* Release resources for the given file. */
1.81 +
1.82 +void file_close(file_t *file)
1.83 +{
1.84 + if (l4_is_valid_cap(file->ref))
1.85 + ipc_cap_free_um(file->ref);
1.86 +
1.87 + if (file->memory != NULL)
1.88 + ipc_detach_dataspace(file->memory);
1.89 +
1.90 + file_init(file);
1.91 +}
1.92 +
1.93 +/* Open a file using the given structure, indicating the filename and
1.94 + filesystem server. The file_mmap function should be used to obtain access to
1.95 + memory providing file data. This is a convenience function invoking
1.96 + file_context and file_context_open. */
1.97 +
1.98 +long file_open(file_t *file, const char *filename, flags_t flags, l4_cap_idx_t server)
1.99 +{
1.100 + file_t context;
1.101 + long err;
1.102 +
1.103 + err = file_context(&context, server);
1.104 + if (err)
1.105 + return err;
1.106 +
1.107 + if (!file_string_set(&context, filename, 0, NULL))
1.108 + return -L4_ENOMEM;
1.109 +
1.110 + err = file_context_open(file, flags, &context);
1.111 +
1.112 + /* Close the context, although a separate mechanism could permit contexts to
1.113 + open several files. */
1.114 +
1.115 + file_close(&context);
1.116 + return err;
1.117 +}
1.118 +
1.119 +
1.120 +
1.121 +/* Initialise a file structure for a context obtained from the given server
1.122 + attaching memory to communicate filename information. */
1.123 +
1.124 +long file_context(file_t *file, l4_cap_idx_t server)
1.125 +{
1.126 + if (l4_is_invalid_cap(server))
1.127 + return -L4_EINVAL;
1.128 +
1.129 + client_Opener opener(server);
1.130 + offset_t size;
1.131 + flags_t flags;
1.132 + long err;
1.133 +
1.134 + file_init(file);
1.135 +
1.136 + err = opener.context(&file->ref);
1.137 + if (err)
1.138 + return err;
1.139 +
1.140 + client_Dataspace context_ds(file->ref);
1.141 +
1.142 + err = context_ds.info(&size, &flags);
1.143 + if (err)
1.144 + return err;
1.145 +
1.146 + file->start_pos = 0;
1.147 + file->end_pos = size;
1.148 +
1.149 + return ipc_attach_dataspace(file->ref, size, (void **) &file->memory);
1.150 +}
1.151 +
1.152 +/* Open a file using the given structure and context. */
1.153 +
1.154 +long file_context_open(file_t *file, flags_t flags, file_t *context)
1.155 +{
1.156 + client_OpenerContext openercontext(context->ref);
1.157 + file_init(file);
1.158 + return openercontext.open(flags, &file->size, &file->ref);
1.159 +}
1.160 +
1.161 +
1.162 +
1.163 +/* Flush populated data and obtain an updated file size and populated data
1.164 + details. */
1.165 +
1.166 +long file_flush(file_t *file)
1.167 +{
1.168 + client_File _file(file->ref);
1.169 + long err = _file.flush(file->data_current, &file->size);
1.170 +
1.171 + if (err)
1.172 + return err;
1.173 +
1.174 + _update_extent(file);
1.175 +
1.176 + return L4_EOK;
1.177 +}
1.178 +
1.179 +/* Map a region of the given file to a memory region, obtaining an updated file
1.180 + size and populated data details. Unmap any previously mapped region. */
1.181 +
1.182 +long file_mmap(file_t *file, offset_t position, offset_t length)
1.183 +{
1.184 + char *memory = file->memory;
1.185 + client_MappedFile mapped_file(file->ref);
1.186 + long err = mapped_file.mmap(position, length, &file->start_pos,
1.187 + &file->end_pos, &file->size);
1.188 +
1.189 + if (err)
1.190 + return err;
1.191 +
1.192 + _update_extent(file);
1.193 +
1.194 + err = ipc_attach_dataspace(file->ref, file_span(file), (void **) &file->memory);
1.195 + if (err)
1.196 + return err;
1.197 +
1.198 + if (memory != NULL)
1.199 + ipc_detach_dataspace(memory);
1.200 +
1.201 + return L4_EOK;
1.202 +}
1.203 +
1.204 +/* Resize a file, obtaining updated file size and populated data details. */
1.205 +
1.206 +long file_resize(file_t *file, offset_t size)
1.207 +{
1.208 + client_File _file(file->ref);
1.209 + offset_t file_size = size;
1.210 + long err = _file.resize(&file_size);
1.211 +
1.212 + if (err)
1.213 + return err;
1.214 +
1.215 + file->size = file_size;
1.216 + _update_extent(file);
1.217 + return L4_EOK;
1.218 +}
1.219 +
1.220 +
1.221 +
1.222 +/* Return the amount of data in the mapped region for the given file. */
1.223 +
1.224 +offset_t file_populated_span(file_t *file)
1.225 +{
1.226 + offset_t size = file_span(file);
1.227 + return (file->data_end < size) ? file->data_end : size;
1.228 +}
1.229 +
1.230 +/* Return the size of the mapped region for the given file. */
1.231 +
1.232 +offset_t file_span(file_t *file)
1.233 +{
1.234 + return file->end_pos - file->start_pos;
1.235 +}
1.236 +
1.237 +
1.238 +
1.239 +/* Get a pointer to any terminated string at the given offset or NULL if the
1.240 + data from offset is not terminated. */
1.241 +
1.242 +char *file_string_get(file_t *file, offset_t offset)
1.243 +{
1.244 + offset_t limit = file_span(file) - offset;
1.245 +
1.246 + if (strnlen(file->memory + offset, limit) < limit)
1.247 + return file->memory + offset;
1.248 + else
1.249 + return NULL;
1.250 +}
1.251 +
1.252 +/* Copy a string to the mapped region at the given offset, returning 1 (true)
1.253 + where all characters were copied, 0 (false) otherwise. The precise number of
1.254 + characters copied, excluding the zero terminator is provided via the written
1.255 + parameter if it is not specified as NULL. */
1.256 +
1.257 +int file_string_set(file_t *file, const char *data, offset_t offset,
1.258 + offset_t *written)
1.259 +{
1.260 + offset_t i, pos, limit = file_span(file);
1.261 +
1.262 + /* Do not attempt to copy data with an invalid offset. */
1.263 +
1.264 + if (offset >= limit)
1.265 + {
1.266 + if (written != NULL)
1.267 + *written = 0;
1.268 + return 0;
1.269 + }
1.270 +
1.271 + /* Copy the data to the given offset, stopping at the end of the region. */
1.272 +
1.273 + for (i = 0, pos = offset; pos < limit; i++, pos++)
1.274 + {
1.275 + file->memory[pos] = data[i];
1.276 +
1.277 + /* Terminator written, can return immediately. */
1.278 +
1.279 + if (!data[i])
1.280 + {
1.281 + if (written != NULL)
1.282 + *written = pos - offset;
1.283 + return 1;
1.284 + }
1.285 + }
1.286 +
1.287 + /* Terminate the incomplete string at the end of the region. */
1.288 +
1.289 + file->memory[limit - 1] = '\0';
1.290 + if (written != NULL)
1.291 + *written = limit - 1 - offset;
1.292 + return 0;
1.293 +}
1.294 +
1.295 +
1.296 +
1.297 +/* Return the number of remaining populated bytes in the region. */
1.298 +
1.299 +offset_t file_data_available(file_t *file)
1.300 +{
1.301 + return file_populated_span(file) - file->data_current;
1.302 +}
1.303 +
1.304 +/* Return the current data offset in the region. */
1.305 +
1.306 +char *file_data_current(file_t *file)
1.307 +{
1.308 + return file->memory + file->data_current;
1.309 +}
1.310 +
1.311 +/* Return the current access position in the file. */
1.312 +
1.313 +offset_t file_data_current_position(file_t *file)
1.314 +{
1.315 + return file->start_pos + file->data_current;
1.316 +}
1.317 +
1.318 +/* Return the position of the end of the populated bytes in the region. */
1.319 +
1.320 +offset_t file_data_end_position(file_t *file)
1.321 +{
1.322 + return file->start_pos + file->data_end;
1.323 +}
1.324 +
1.325 +/* Return the amount of remaining space in the region. */
1.326 +
1.327 +offset_t file_data_space(file_t *file)
1.328 +{
1.329 + return file_span(file) - file->data_current;
1.330 +}
1.331 +
1.332 +
1.333 +
1.334 +/* Copy data to the given buffer from the current data position, updating the
1.335 + position. */
1.336 +
1.337 +void file_data_read(file_t *file, char *buf, size_t to_transfer)
1.338 +{
1.339 + memcpy(buf, file_data_current(file), to_transfer);
1.340 +
1.341 + /* Update position details. */
1.342 +
1.343 + file->data_current += to_transfer;
1.344 +}
1.345 +
1.346 +/* Copy data from the given buffer to the current data position, updating the
1.347 + position and the extent of populated data if this was exceeded. */
1.348 +
1.349 +void file_data_write(file_t *file, char *buf, size_t to_transfer)
1.350 +{
1.351 + memcpy(file_data_current(file), buf, to_transfer);
1.352 +
1.353 + /* Update position details. */
1.354 +
1.355 + file->data_current += to_transfer;
1.356 +
1.357 + if (file->data_current > file->data_end)
1.358 + file->data_end = file->data_current;
1.359 +}
1.360 +
1.361 +
1.362 +
1.363 +/* Open two pipe endpoints using the given pipe server. */
1.364 +
1.365 +long pipe_open(offset_t size, file_t *reader, file_t *writer, l4_cap_idx_t server)
1.366 +{
1.367 + if (l4_is_invalid_cap(server))
1.368 + return -L4_EINVAL;
1.369 +
1.370 + client_PipeOpener opener(server);
1.371 +
1.372 + file_init(reader);
1.373 + file_init(writer);
1.374 +
1.375 + /* Pipes can usually only be accessed via region navigation. */
1.376 +
1.377 + reader->can_mmap = 0;
1.378 + writer->can_mmap = 0;
1.379 +
1.380 + long err = opener.pipe(size, &reader->ref, &writer->ref);
1.381 + if (err)
1.382 + return err;
1.383 +
1.384 + err = pipe_next(writer) || pipe_next(reader);
1.385 +
1.386 + if (err)
1.387 + {
1.388 + file_close(reader);
1.389 + file_close(writer);
1.390 + }
1.391 +
1.392 + return err;
1.393 +}
1.394 +
1.395 +/* Access the current region for a pipe endpoint. */
1.396 +
1.397 +long pipe_current(file_t *pipe)
1.398 +{
1.399 + client_Pipe _pipe(pipe->ref);
1.400 + long err = _pipe.current_region(&pipe->data_end, &pipe->size);
1.401 + char *memory = pipe->memory;
1.402 +
1.403 + if (err)
1.404 + return err;
1.405 +
1.406 + pipe->end_pos = pipe->size;
1.407 +
1.408 + err = ipc_attach_dataspace(pipe->ref, file_span(pipe), (void **) &pipe->memory);
1.409 + if (err)
1.410 + return err;
1.411 +
1.412 + if (memory != NULL)
1.413 + ipc_detach_dataspace(memory);
1.414 +
1.415 + return L4_EOK;
1.416 +}
1.417 +
1.418 +/* Access the next region for a pipe endpoint, updating the eventual size of
1.419 + the current region. */
1.420 +
1.421 +long pipe_next(file_t *pipe)
1.422 +{
1.423 + client_Pipe _pipe(pipe->ref);
1.424 + long err = _pipe.next_region(&pipe->data_end, &pipe->size);
1.425 + char *memory = pipe->memory;
1.426 +
1.427 + if (err)
1.428 + return err;
1.429 +
1.430 + pipe->end_pos = pipe->size;
1.431 +
1.432 + err = ipc_attach_dataspace(pipe->ref, file_span(pipe), (void **) &pipe->memory);
1.433 + if (err)
1.434 + return err;
1.435 +
1.436 + if (memory != NULL)
1.437 + ipc_detach_dataspace(memory);
1.438 +
1.439 + return L4_EOK;
1.440 +}
1.441 +
1.442 +/* Set the size of the written region. */
1.443 +
1.444 +long pipe_written(file_t *pipe, offset_t size)
1.445 +{
1.446 + if (size <= pipe->size)
1.447 + {
1.448 + pipe->data_end = size;
1.449 + return L4_EOK;
1.450 + }
1.451 + else
1.452 + return -L4_EINVAL;
1.453 +}
1.454 +
1.455 +// vim: tabstop=2 expandtab shiftwidth=2