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 #include <ipc/irq.h> 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 28 #include "client.h" 29 30 31 32 /* Default size of pipe regions. */ 33 34 const offset_t DEFAULT_PIPE_SIZE = 4096; 35 36 37 38 /* Access the given position and synchronise state with the file object. */ 39 40 static long _access(file_t *file, offset_t position) 41 { 42 long err; 43 44 if (file->can_mmap) 45 { 46 /* Where the position is outside the current region, re-map. */ 47 48 if ((position < file->start_pos) || (position >= file->end_pos)) 49 { 50 if (file_mmap(file, position, file_span(file))) 51 return -L4_EIO; 52 } 53 54 /* Otherwise, flush any written data in the current region and update the 55 file size details. */ 56 57 else 58 { 59 err = client_flush(file); 60 if (err) 61 return err; 62 } 63 } 64 else 65 { 66 /* Strict conditions for region navigation in pipes. */ 67 68 if ((position < file->start_pos) || (position > file->end_pos)) 69 { 70 return -L4_EIO; 71 } 72 73 /* The next region is only available at the end of the mapped memory. */ 74 75 else if (position == file->end_pos) 76 { 77 err = client_next_region(file); 78 if (err) 79 return err; 80 81 file->data_current = 0; 82 return L4_EOK; 83 } 84 85 /* Within the current pipe region, synchronise with the pipe object. */ 86 87 else 88 { 89 err = client_current_region(file); 90 if (err) 91 return err; 92 } 93 } 94 95 /* Update the current data offset. */ 96 97 file->data_current = position - file->start_pos; 98 99 return L4_EOK; 100 } 101 102 103 104 /* Return whether an access could occur, blocking if necessary. */ 105 106 static int _access_blocking(file_t *file, offset_t position) 107 { 108 long err; 109 110 while ((err = _access(file, position))) 111 { 112 /* Exit if blocking is not configured or suitable. */ 113 114 if ((err != -L4_EBUSY) || !file->can_block) 115 return 0; 116 117 /* Handle an inability to access by blocking, exiting if waiting failed. */ 118 119 if (client_wait_file(file)) 120 return 0; 121 } 122 123 return 1; 124 } 125 126 127 128 /* Ensure that memory is mapped for accessing the given file, using the 129 indicated count as a region size hint. */ 130 131 static void *_map_memory(file_t *file, offset_t count) 132 { 133 if (file->memory == NULL) 134 { 135 if (file->can_mmap) 136 return client_mmap(file, client_tell(file), count); 137 else if (pipe_current(file)) 138 return NULL; 139 } 140 141 return file->memory; 142 } 143 144 145 146 /* Close a filesystem object. */ 147 148 void client_close(file_t *file) 149 { 150 if (file == NULL) 151 return; 152 153 file_close(file); 154 free(file); 155 } 156 157 158 159 /* Open a filesystem object. */ 160 161 file_t *client_open(const char *name, flags_t flags) 162 { 163 return client_open_device("server", name, flags); 164 } 165 166 /* Open a filesystem object via a named capability. */ 167 168 file_t *client_open_device(const char *cap, const char *name, flags_t flags) 169 { 170 file_t *file = (file_t *) malloc(sizeof(file_t)); 171 172 if (file == NULL) 173 return NULL; 174 175 l4_cap_idx_t server = l4re_env_get_cap(cap); 176 177 if (file_open(file, name, flags, server)) 178 { 179 free(file); 180 return NULL; 181 } 182 183 return file; 184 } 185 186 187 188 /* Open a pipe object. */ 189 190 long client_pipe(file_t **reader, file_t **writer) 191 { 192 *reader = (file_t *) malloc(sizeof(file_t)); 193 194 if (*reader == NULL) 195 return -L4_ENOMEM; 196 197 *writer = (file_t *) malloc(sizeof(file_t)); 198 199 if (*writer == NULL) 200 { 201 free(*reader); 202 return -L4_ENOMEM; 203 } 204 205 l4_cap_idx_t server = l4re_env_get_cap("pipes"); 206 207 long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server); 208 209 if (err) 210 { 211 free(*reader); 212 free(*writer); 213 } 214 215 return err; 216 } 217 218 219 220 /* Flush data explicitly to the filesystem object. */ 221 222 long client_flush(file_t *file) 223 { 224 if (file == NULL) 225 return -L4_EINVAL; 226 227 /* Flush and retain most buffer settings. */ 228 229 return file_flush(file); 230 } 231 232 233 234 /* Map a memory region to a file. */ 235 236 void *client_mmap(file_t *file, offset_t position, offset_t length) 237 { 238 if ((file == NULL) || (file_mmap(file, position, length))) 239 return NULL; 240 241 return file->memory; 242 } 243 244 245 246 /* Obtain the current region of a pipe. */ 247 248 long client_current_region(file_t *file) 249 { 250 if (file == NULL) 251 return -L4_EINVAL; 252 253 return pipe_current(file); 254 } 255 256 257 258 /* Obtain the next region of a pipe. */ 259 260 long client_next_region(file_t *file) 261 { 262 if (file == NULL) 263 return -L4_EINVAL; 264 265 return pipe_next(file); 266 } 267 268 269 270 /* Read from the filesystem object into the buffer provided. */ 271 272 offset_t client_read(file_t *file, void *buf, offset_t count) 273 { 274 if (file == NULL) 275 return 0; 276 277 /* Map memory if none has been mapped so far. */ 278 279 if (_map_memory(file, count) == NULL) 280 return 0; 281 282 /* Amount available in the descriptor buffer already. */ 283 284 offset_t available = file_data_available(file); 285 offset_t to_transfer, total = 0; 286 287 while (count > 0) 288 { 289 /* If there is no data, try and obtain more data. */ 290 291 if (!available) 292 { 293 /* Flush any unwritten data, preparing to read from the file position at 294 the end of the data, and returning if no new data is available. */ 295 296 if (!_access_blocking(file, file_data_end_position(file))) 297 break; 298 299 available = file_data_available(file); 300 301 if (!available) 302 break; 303 } 304 305 /* Transfer data into the supplied buffer. */ 306 307 to_transfer = available <= count ? available : count; 308 309 file_data_read(file, (char *) buf, to_transfer); 310 311 /* Update counters. */ 312 313 available -= to_transfer; 314 315 count -= to_transfer; 316 total += to_transfer; 317 318 buf = ((char *) buf + to_transfer); 319 } 320 321 return total; 322 } 323 324 325 326 /* Ensure that the buffer can provide the needed data. */ 327 328 offset_t client_seek(file_t *file, offset_t offset, int whence) 329 { 330 if (file == NULL) 331 return 0; 332 333 offset_t position, current = file_data_current_position(file), change; 334 335 switch (whence) 336 { 337 case SEEK_SET: 338 position = offset; 339 break; 340 341 case SEEK_CUR: 342 position = current + offset; 343 break; 344 345 case SEEK_END: 346 position = file->size + offset; 347 break; 348 349 default: 350 /* NOTE: Set errno to EINVAL. */ 351 return current; 352 } 353 354 /* Retain the current position if unchanged. */ 355 356 if (position == current) 357 return position; 358 359 /* Move forward in the file. */ 360 361 if (position > current) 362 { 363 change = position - current; 364 365 /* Move towards the end of available data. 366 Request new data if not enough is available. */ 367 368 if (change <= file_data_available(file)) 369 { 370 file->data_current += change; 371 return position; 372 } 373 } 374 375 /* Move backward in the file. */ 376 377 else 378 { 379 change = current - position; 380 381 /* Move towards the start of available data. 382 Request new data if moving beyond the start of the data. */ 383 384 if (change <= file->data_current) 385 { 386 file->data_current -= change; 387 return position; 388 } 389 } 390 391 /* Handle unwritten data and reset the buffer for reading. */ 392 393 if (_access(file, position)) 394 return current; 395 396 return position; 397 } 398 399 400 401 /* Set or unset blocking access for a file. */ 402 403 long client_set_blocking(file_t *file, notify_flags_t flags) 404 { 405 long err; 406 407 if (file->can_block == flags) 408 return L4_EOK; 409 410 // NOTE: Set appropriate flags. 411 412 if (flags) 413 err = client_subscribe(file, flags); 414 else 415 err = client_unsubscribe(file); 416 417 if (err) 418 return err; 419 420 file->can_block = flags; 421 return L4_EOK; 422 } 423 424 425 426 /* Subscribe from events concerning a file. */ 427 428 long client_subscribe(file_t *file, notify_flags_t flags) 429 { 430 if (file == NULL) 431 return -L4_EINVAL; 432 433 return file_notify_subscribe(file, flags); 434 } 435 436 437 438 /* Return the current position in the file. */ 439 440 long client_tell(file_t *file) 441 { 442 if (file == NULL) 443 return -L4_EINVAL; 444 445 return file_data_current_position(file); 446 } 447 448 449 450 /* Unsubscribe from events concerning a file. */ 451 452 long client_unsubscribe(file_t *file) 453 { 454 if (file == NULL) 455 return -L4_EINVAL; 456 457 return file_notify_unsubscribe(file); 458 } 459 460 461 462 /* Wait for events involving a specific file. */ 463 464 long client_wait_file(file_t *file) 465 { 466 if (file == NULL) 467 return -L4_EINVAL; 468 469 return file_notify_wait_file(file); 470 } 471 472 /* Wait for events concerning files, referencing a file object if an event is 473 delivered. */ 474 475 long client_wait_files(file_t **file) 476 { 477 return file_notify_wait_files(file); 478 } 479 480 481 482 /* Write to the filesystem object from the buffer provided. */ 483 484 offset_t client_write(file_t *file, const void *buf, offset_t count) 485 { 486 if (file == NULL) 487 return 0; 488 489 /* Map memory if none has been mapped so far. */ 490 491 if (_map_memory(file, count) == NULL) 492 return 0; 493 494 /* Attempt to ensure that the file can accept the amount of data to be 495 written. This may not resize to the needed amount if a file has a fixed 496 size, but data will still be written to any available space. */ 497 498 offset_t needed_size = file_data_current_position(file) + count; 499 500 if (file->has_size) 501 { 502 if (file->size < needed_size) 503 { 504 file_resize(file, needed_size); 505 506 if (file->size < needed_size) 507 count = file->size - file_data_current_position(file); 508 } 509 } 510 511 /* Space remaining in the descriptor buffer. */ 512 513 offset_t space = file_data_space(file); 514 offset_t to_transfer, total = 0; 515 516 while (count > 0) 517 { 518 /* If no space is available, try and send data, reset the buffer. */ 519 520 if (!space) 521 { 522 /* Flush any unwritten data and continue writing from the current data 523 position. */ 524 525 if (!_access_blocking(file, file_data_current_position(file))) 526 break; 527 528 space = file_data_space(file); 529 } 530 531 /* Transfer data into the supplied buffer. */ 532 533 to_transfer = space <= count ? space : count; 534 535 file_data_write(file, (char *) buf, to_transfer); 536 537 /* Update counters. */ 538 539 space -= to_transfer; 540 541 count -= to_transfer; 542 total += to_transfer; 543 544 buf = ((char *) buf + to_transfer); 545 } 546 547 return total; 548 } 549 550 // vim: tabstop=2 expandtab shiftwidth=2