1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libfsclient/lib/src/client.cc Thu Apr 15 23:15:17 2021 +0200
1.3 @@ -0,0 +1,390 @@
1.4 +/*
1.5 + * Filesystem client functions.
1.6 + *
1.7 + * Copyright (C) 2018, 2019, 2020, 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 <l4/re/env.h>
1.26 +
1.27 +#include <stdio.h>
1.28 +#include <stdlib.h>
1.29 +
1.30 +#include "client.h"
1.31 +
1.32 +
1.33 +
1.34 +/* Default size of pipe regions. */
1.35 +
1.36 +const offset_t DEFAULT_PIPE_SIZE = 4096;
1.37 +
1.38 +
1.39 +
1.40 +/* Close a filesystem object. */
1.41 +
1.42 +void client_close(file_t *file)
1.43 +{
1.44 + if (file == NULL)
1.45 + return;
1.46 +
1.47 + file_close(file);
1.48 + free(file);
1.49 +}
1.50 +
1.51 +
1.52 +
1.53 +/* Open a filesystem object. */
1.54 +
1.55 +file_t *client_open(const char *name, flags_t flags)
1.56 +{
1.57 + file_t *file = (file_t *) malloc(sizeof(file_t));
1.58 +
1.59 + if (file == NULL)
1.60 + return NULL;
1.61 +
1.62 + l4_cap_idx_t server = l4re_env_get_cap("server");
1.63 +
1.64 + if (file_open(file, name, flags, server))
1.65 + {
1.66 + free(file);
1.67 + return NULL;
1.68 + }
1.69 +
1.70 + return file;
1.71 +}
1.72 +
1.73 +
1.74 +
1.75 +/* Open a pipe object. */
1.76 +
1.77 +long client_pipe(file_t **reader, file_t **writer)
1.78 +{
1.79 + *reader = (file_t *) malloc(sizeof(file_t));
1.80 +
1.81 + if (*reader == NULL)
1.82 + return -L4_ENOMEM;
1.83 +
1.84 + *writer = (file_t *) malloc(sizeof(file_t));
1.85 +
1.86 + if (*writer == NULL)
1.87 + {
1.88 + free(*reader);
1.89 + return -L4_ENOMEM;
1.90 + }
1.91 +
1.92 + l4_cap_idx_t server = l4re_env_get_cap("pipes");
1.93 +
1.94 + long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server);
1.95 +
1.96 + if (err)
1.97 + {
1.98 + free(*reader);
1.99 + free(*writer);
1.100 + }
1.101 +
1.102 + return err;
1.103 +}
1.104 +
1.105 +
1.106 +
1.107 +/* Flush data conditionally to the filesystem object. */
1.108 +
1.109 +static long _flush(file_t *file, offset_t position)
1.110 +{
1.111 + long err;
1.112 +
1.113 + /* Where the position is outside the current region, re-map. */
1.114 +
1.115 + if ((position < file->start_pos) || (position >= file->end_pos))
1.116 + {
1.117 + if (file->can_mmap)
1.118 + {
1.119 + if (file_mmap(file, position, file_span(file)))
1.120 + return -L4_EIO;
1.121 + }
1.122 +
1.123 + /* Strict conditions for region navigation in pipes. */
1.124 +
1.125 + else if ((position != file->end_pos) ||
1.126 + (client_next_region(file) == NULL))
1.127 + return -L4_EIO;
1.128 + }
1.129 +
1.130 + /* Otherwise, flush any written data in the current region and update the
1.131 + file size details. */
1.132 +
1.133 + else
1.134 + {
1.135 + err = client_flush(file);
1.136 +
1.137 + if (err)
1.138 + return err;
1.139 + }
1.140 +
1.141 + /* Update the current data offset. */
1.142 +
1.143 + file->data_current = position - file->start_pos;
1.144 + return L4_EOK;
1.145 +}
1.146 +
1.147 +
1.148 +
1.149 +/* Flush data explicitly to the filesystem object. */
1.150 +
1.151 +long client_flush(file_t *file)
1.152 +{
1.153 + if (file == NULL)
1.154 + return -L4_EINVAL;
1.155 +
1.156 + /* Flush and retain most buffer settings. */
1.157 +
1.158 + return file_flush(file);
1.159 +}
1.160 +
1.161 +
1.162 +
1.163 +/* Map a memory region to a file. */
1.164 +
1.165 +void *client_mmap(file_t *file, offset_t position, offset_t length)
1.166 +{
1.167 + if ((file == NULL) || (file_mmap(file, position, length)))
1.168 + return NULL;
1.169 +
1.170 + return file->memory;
1.171 +}
1.172 +
1.173 +
1.174 +
1.175 +/* Obtain the current region of a pipe. */
1.176 +
1.177 +void *client_current_region(file_t *file)
1.178 +{
1.179 + if ((file == NULL) || (pipe_current(file)))
1.180 + return NULL;
1.181 +
1.182 + return file->memory;
1.183 +}
1.184 +
1.185 +
1.186 +
1.187 +/* Obtain the next region of a pipe. */
1.188 +
1.189 +void *client_next_region(file_t *file)
1.190 +{
1.191 + if ((file == NULL) || (pipe_next(file)))
1.192 + return NULL;
1.193 +
1.194 + return file->memory;
1.195 +}
1.196 +
1.197 +
1.198 +
1.199 +/* Read from the filesystem object into the buffer provided. */
1.200 +
1.201 +offset_t client_read(file_t *file, void *buf, offset_t count)
1.202 +{
1.203 + if (file == NULL)
1.204 + return 0;
1.205 +
1.206 + /* Amount available in the descriptor buffer already. */
1.207 +
1.208 + offset_t available = file_data_available(file);
1.209 + offset_t to_transfer, total = 0;
1.210 +
1.211 + while (count > 0)
1.212 + {
1.213 + /* If there is no data, try and obtain more data. */
1.214 +
1.215 + if (!available)
1.216 + {
1.217 + /* Flush any unwritten data, preparing to read from the file position at
1.218 + the end of the data, and returning if no new data is available. */
1.219 +
1.220 + if (_flush(file, file_data_end_position(file)))
1.221 + break;
1.222 +
1.223 + available = file_data_available(file);
1.224 +
1.225 + if (!available)
1.226 + break;
1.227 + }
1.228 +
1.229 + /* Transfer data into the supplied buffer. */
1.230 +
1.231 + to_transfer = available <= count ? available : count;
1.232 +
1.233 + file_data_read(file, (char *) buf, to_transfer);
1.234 +
1.235 + /* Update counters. */
1.236 +
1.237 + available -= to_transfer;
1.238 +
1.239 + count -= to_transfer;
1.240 + total += to_transfer;
1.241 +
1.242 + buf = ((char *) buf + to_transfer);
1.243 + }
1.244 +
1.245 + return total;
1.246 +}
1.247 +
1.248 +
1.249 +
1.250 +/* Ensure that the buffer can provide the needed data. */
1.251 +
1.252 +offset_t client_seek(file_t *file, offset_t offset, int whence)
1.253 +{
1.254 + if (file == NULL)
1.255 + return 0;
1.256 +
1.257 + offset_t position, current = file_data_current_position(file), change;
1.258 +
1.259 + switch (whence)
1.260 + {
1.261 + case SEEK_SET:
1.262 + position = offset;
1.263 + break;
1.264 +
1.265 + case SEEK_CUR:
1.266 + position = current + offset;
1.267 + break;
1.268 +
1.269 + case SEEK_END:
1.270 + position = file->size + offset;
1.271 + break;
1.272 +
1.273 + default:
1.274 + /* NOTE: Set errno to EINVAL. */
1.275 + return -1;
1.276 + }
1.277 +
1.278 + /* Retain the current position if unchanged. */
1.279 +
1.280 + if (position == current)
1.281 + return position;
1.282 +
1.283 + /* Move forward in the file. */
1.284 +
1.285 + if (position > current)
1.286 + {
1.287 + change = position - current;
1.288 +
1.289 + /* Move towards the end of available data.
1.290 + Request new data if not enough is available. */
1.291 +
1.292 + if (change <= file_data_available(file))
1.293 + {
1.294 + file->data_current += change;
1.295 + return position;
1.296 + }
1.297 + }
1.298 +
1.299 + /* Move backward in the file. */
1.300 +
1.301 + else
1.302 + {
1.303 + change = current - position;
1.304 +
1.305 + /* Move towards the start of available data.
1.306 + Request new data if moving beyond the start of the data. */
1.307 +
1.308 + if (change <= file->data_current)
1.309 + {
1.310 + file->data_current -= change;
1.311 + return position;
1.312 + }
1.313 + }
1.314 +
1.315 + /* Handle unwritten data and reset the buffer for reading. */
1.316 +
1.317 + _flush(file, position);
1.318 + return position;
1.319 +}
1.320 +
1.321 +
1.322 +
1.323 +long client_tell(file_t *file)
1.324 +{
1.325 + if (file == NULL)
1.326 + return -L4_EINVAL;
1.327 +
1.328 + return file_data_current_position(file);
1.329 +}
1.330 +
1.331 +
1.332 +
1.333 +/* Write to the filesystem object from the buffer provided. */
1.334 +
1.335 +offset_t client_write(file_t *file, const void *buf, offset_t count)
1.336 +{
1.337 + if (file == NULL)
1.338 + return 0;
1.339 +
1.340 + /* Attempt to ensure that the file can accept the amount of data to be
1.341 + written. This may not resize to the needed amount if a file has a fixed
1.342 + size, but data will still be written to any available space. */
1.343 +
1.344 + offset_t needed_size = file_data_current_position(file) + count;
1.345 +
1.346 + if (file->size < needed_size)
1.347 + {
1.348 + file_resize(file, needed_size);
1.349 +
1.350 + if (file->size < needed_size)
1.351 + count = file->size - file_data_current_position(file);
1.352 + }
1.353 +
1.354 + /* Space remaining in the descriptor buffer. */
1.355 +
1.356 + offset_t space = file_data_space(file);
1.357 + offset_t to_transfer, total = 0;
1.358 +
1.359 + while (count > 0)
1.360 + {
1.361 + /* If no space is available, try and send data, reset the buffer. */
1.362 +
1.363 + if (!space)
1.364 + {
1.365 + /* Flush any unwritten data and continue writing from the current data
1.366 + position. */
1.367 +
1.368 + if (_flush(file, file_data_current_position(file)))
1.369 + break;
1.370 +
1.371 + space = file_data_space(file);
1.372 + }
1.373 +
1.374 + /* Transfer data into the supplied buffer. */
1.375 +
1.376 + to_transfer = space <= count ? space : count;
1.377 +
1.378 + file_data_write(file, (char *) buf, to_transfer);
1.379 +
1.380 + /* Update counters. */
1.381 +
1.382 + space -= to_transfer;
1.383 +
1.384 + count -= to_transfer;
1.385 + total += to_transfer;
1.386 +
1.387 + buf = ((char *) buf + to_transfer);
1.388 + }
1.389 +
1.390 + return total;
1.391 +}
1.392 +
1.393 +// vim: tabstop=2 expandtab shiftwidth=2