1 /* 2 * Filesystem client functions. 3 * 4 * Copyright (C) 2018, 2019, 2020, 2021 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/env.h> 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 27 #include "client.h" 28 29 30 31 /* Default size of pipe regions. */ 32 33 const offset_t DEFAULT_PIPE_SIZE = 4096; 34 35 36 37 /* Close a filesystem object. */ 38 39 void client_close(file_t *file) 40 { 41 if (file == NULL) 42 return; 43 44 file_close(file); 45 free(file); 46 } 47 48 49 50 /* Open a filesystem object. */ 51 52 file_t *client_open(const char *name, flags_t flags) 53 { 54 return client_open_device("server", name, flags); 55 } 56 57 /* Open a filesystem object via a named capability. */ 58 59 file_t *client_open_device(const char *cap, const char *name, flags_t flags) 60 { 61 file_t *file = (file_t *) malloc(sizeof(file_t)); 62 63 if (file == NULL) 64 return NULL; 65 66 l4_cap_idx_t server = l4re_env_get_cap(cap); 67 68 if (file_open(file, name, flags, server)) 69 { 70 free(file); 71 return NULL; 72 } 73 74 return file; 75 } 76 77 78 79 /* Open a pipe object. */ 80 81 long client_pipe(file_t **reader, file_t **writer) 82 { 83 *reader = (file_t *) malloc(sizeof(file_t)); 84 85 if (*reader == NULL) 86 return -L4_ENOMEM; 87 88 *writer = (file_t *) malloc(sizeof(file_t)); 89 90 if (*writer == NULL) 91 { 92 free(*reader); 93 return -L4_ENOMEM; 94 } 95 96 l4_cap_idx_t server = l4re_env_get_cap("pipes"); 97 98 long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server); 99 100 if (err) 101 { 102 free(*reader); 103 free(*writer); 104 } 105 106 return err; 107 } 108 109 110 111 /* Flush data conditionally to the filesystem object. */ 112 113 static long _flush(file_t *file, offset_t position) 114 { 115 long err; 116 117 /* Where the position is outside the current region, re-map. */ 118 119 if ((position < file->start_pos) || (position >= file->end_pos)) 120 { 121 if (file->can_mmap) 122 { 123 if (file_mmap(file, position, file_span(file))) 124 return -L4_EIO; 125 } 126 127 /* Strict conditions for region navigation in pipes. */ 128 129 else 130 { 131 if ((position != file->end_pos) || (client_next_region(file) == NULL)) 132 return -L4_EIO; 133 } 134 } 135 136 /* Otherwise, flush any written data in the current region and update the 137 file size details. */ 138 139 else 140 { 141 if (file->can_mmap) 142 { 143 err = client_flush(file); 144 if (err) 145 return err; 146 } 147 else 148 { 149 if (client_current_region(file) == NULL) 150 return -L4_EIO; 151 } 152 } 153 154 /* Update the current data offset. */ 155 156 if (file->has_size) 157 file->data_current = position - file->start_pos; 158 else 159 file->data_current = 0; 160 161 return L4_EOK; 162 } 163 164 165 166 /* Flush data explicitly to the filesystem object. */ 167 168 long client_flush(file_t *file) 169 { 170 if (file == NULL) 171 return -L4_EINVAL; 172 173 /* Flush and retain most buffer settings. */ 174 175 return file_flush(file); 176 } 177 178 179 180 /* Map a memory region to a file. */ 181 182 void *client_mmap(file_t *file, offset_t position, offset_t length) 183 { 184 if ((file == NULL) || (file_mmap(file, position, length))) 185 return NULL; 186 187 return file->memory; 188 } 189 190 191 192 /* Obtain the current region of a pipe. */ 193 194 void *client_current_region(file_t *file) 195 { 196 if ((file == NULL) || (pipe_current(file))) 197 return NULL; 198 199 return file->memory; 200 } 201 202 203 204 /* Obtain the next region of a pipe. */ 205 206 void *client_next_region(file_t *file) 207 { 208 if ((file == NULL) || (pipe_next(file))) 209 return NULL; 210 211 return file->memory; 212 } 213 214 215 216 /* Ensure that memory is mapped for accessing the given file, using the 217 indicated count as a region size hint. */ 218 219 static void *_map_memory(file_t *file, offset_t count) 220 { 221 if (file->memory == NULL) 222 { 223 if (file->can_mmap) 224 return client_mmap(file, client_tell(file), count); 225 else if (pipe_current(file)) 226 return NULL; 227 } 228 229 return file->memory; 230 } 231 232 233 234 /* Read from the filesystem object into the buffer provided. */ 235 236 offset_t client_read(file_t *file, void *buf, offset_t count) 237 { 238 if (file == NULL) 239 return 0; 240 241 /* Map memory if none has been mapped so far. */ 242 243 if (_map_memory(file, count) == NULL) 244 return 0; 245 246 /* Amount available in the descriptor buffer already. */ 247 248 offset_t available = file_data_available(file); 249 offset_t to_transfer, total = 0; 250 251 while (count > 0) 252 { 253 /* If there is no data, try and obtain more data. */ 254 255 if (!available) 256 { 257 /* Flush any unwritten data, preparing to read from the file position at 258 the end of the data, and returning if no new data is available. */ 259 260 if (_flush(file, file_data_end_position(file))) 261 break; 262 263 available = file_data_available(file); 264 265 if (!available) 266 break; 267 } 268 269 /* Transfer data into the supplied buffer. */ 270 271 to_transfer = available <= count ? available : count; 272 273 file_data_read(file, (char *) buf, to_transfer); 274 275 /* Update counters. */ 276 277 available -= to_transfer; 278 279 count -= to_transfer; 280 total += to_transfer; 281 282 buf = ((char *) buf + to_transfer); 283 } 284 285 return total; 286 } 287 288 289 290 /* Ensure that the buffer can provide the needed data. */ 291 292 offset_t client_seek(file_t *file, offset_t offset, int whence) 293 { 294 if (file == NULL) 295 return 0; 296 297 offset_t position, current = file_data_current_position(file), change; 298 299 switch (whence) 300 { 301 case SEEK_SET: 302 position = offset; 303 break; 304 305 case SEEK_CUR: 306 position = current + offset; 307 break; 308 309 case SEEK_END: 310 position = file->size + offset; 311 break; 312 313 default: 314 /* NOTE: Set errno to EINVAL. */ 315 return -1; 316 } 317 318 /* Retain the current position if unchanged. */ 319 320 if (position == current) 321 return position; 322 323 /* Move forward in the file. */ 324 325 if (position > current) 326 { 327 change = position - current; 328 329 /* Move towards the end of available data. 330 Request new data if not enough is available. */ 331 332 if (change <= file_data_available(file)) 333 { 334 file->data_current += change; 335 return position; 336 } 337 } 338 339 /* Move backward in the file. */ 340 341 else 342 { 343 change = current - position; 344 345 /* Move towards the start of available data. 346 Request new data if moving beyond the start of the data. */ 347 348 if (change <= file->data_current) 349 { 350 file->data_current -= change; 351 return position; 352 } 353 } 354 355 /* Handle unwritten data and reset the buffer for reading. */ 356 357 _flush(file, position); 358 return position; 359 } 360 361 362 363 long client_tell(file_t *file) 364 { 365 if (file == NULL) 366 return -L4_EINVAL; 367 368 return file_data_current_position(file); 369 } 370 371 372 373 /* Write to the filesystem object from the buffer provided. */ 374 375 offset_t client_write(file_t *file, const void *buf, offset_t count) 376 { 377 if (file == NULL) 378 return 0; 379 380 /* Map memory if none has been mapped so far. */ 381 382 if (_map_memory(file, count) == NULL) 383 return 0; 384 385 /* Attempt to ensure that the file can accept the amount of data to be 386 written. This may not resize to the needed amount if a file has a fixed 387 size, but data will still be written to any available space. */ 388 389 offset_t needed_size = file_data_current_position(file) + count; 390 391 if (file->has_size) 392 { 393 if (file->size < needed_size) 394 { 395 file_resize(file, needed_size); 396 397 if (file->size < needed_size) 398 count = file->size - file_data_current_position(file); 399 } 400 } 401 402 /* Space remaining in the descriptor buffer. */ 403 404 offset_t space = file_data_space(file); 405 offset_t to_transfer, total = 0; 406 407 while (count > 0) 408 { 409 /* If no space is available, try and send data, reset the buffer. */ 410 411 if (!space) 412 { 413 /* Flush any unwritten data and continue writing from the current data 414 position. */ 415 416 if (_flush(file, file_data_current_position(file))) 417 break; 418 419 space = file_data_space(file); 420 } 421 422 /* Transfer data into the supplied buffer. */ 423 424 to_transfer = space <= count ? space : count; 425 426 file_data_write(file, (char *) buf, to_transfer); 427 428 /* Update counters. */ 429 430 space -= to_transfer; 431 432 count -= to_transfer; 433 total += to_transfer; 434 435 buf = ((char *) buf + to_transfer); 436 } 437 438 return total; 439 } 440 441 // vim: tabstop=2 expandtab shiftwidth=2