1 /* 2 * Filesystem client functions. 3 * 4 * Copyright (C) 2018, 2019, 2020, 2021, 2022, 5 * 2023 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/re/env.h> 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include <systypes/fcntl.h> 30 31 #include "client.h" 32 33 34 35 /* Default size of pipe regions. */ 36 37 const offset_t DEFAULT_PIPE_SIZE = 4096; 38 39 /* Size of the core member region of a directory entry structure. */ 40 41 const offset_t DIRENT_CORE_SIZE = (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name)); 42 43 44 45 /* Merging of region flags from protection and access flags. */ 46 47 static l4re_rm_flags_t _combine_region_flags(l4re_rm_flags_t region_flags, 48 flags_t flags) 49 { 50 return region_flags & (file_region_flags(flags) | L4RE_RM_F_X); 51 } 52 53 /* Conversion of protection and access flags to region flags. */ 54 55 l4re_rm_flags_t client_region_flags(prot_t prot, flags_t flags) 56 { 57 l4re_rm_flags_t rm_flags = 0; 58 59 if (prot & PROT_READ) 60 rm_flags |= L4RE_RM_F_R; 61 if (prot & PROT_WRITE) 62 rm_flags |= L4RE_RM_F_W; 63 if (prot & PROT_EXEC) 64 rm_flags |= L4RE_RM_F_X; 65 66 return _combine_region_flags(rm_flags, flags); 67 } 68 69 70 71 /* Access the given position and synchronise state with the file object. Pipe 72 objects may return busy conditions indicating that the desired access cannot 73 yet be fulfilled. */ 74 75 static long _access(file_t *file, offset_t position) 76 { 77 long err; 78 79 if (file->object_flags & OBJECT_SUPPORTS_MMAP) 80 { 81 /* Where the position is outside the current region, re-map. */ 82 83 if ((position < file->start_pos) || (position >= file->end_pos)) 84 { 85 if (file_mmap(file, position, file_span(file), 0, 0, 86 file_region_flags(file->flags))) 87 return -L4_EIO; 88 } 89 90 /* Otherwise, flush any written data in the current region and update the 91 file size details. */ 92 93 else 94 { 95 err = client_flush(file); 96 if (err) 97 return err; 98 } 99 100 /* Update the current data offset. */ 101 102 file->data_current = position - file->start_pos; 103 104 return L4_EOK; 105 } 106 else 107 { 108 /* Handle the initial condition with no current region. */ 109 110 if (file->memory == NULL) 111 { 112 err = client_current_region(file); 113 if (err) 114 return err; 115 } 116 117 /* Strict conditions for region navigation in pipes. */ 118 119 if ((position < file->start_pos) || (position > file->end_pos)) 120 { 121 return -L4_EIO; 122 } 123 124 /* The next region is only available at the end of the mapped memory. */ 125 126 else if (position == file->end_pos) 127 { 128 err = client_next_region(file); 129 if (err) 130 return err; 131 132 file->data_current = 0; 133 return L4_EOK; 134 } 135 136 /* Within the current pipe region, synchronise with the pipe object. */ 137 138 else 139 { 140 return client_current_region(file); 141 } 142 } 143 } 144 145 146 147 /* Return whether an operation on file should block for more content or more 148 space. A file must be configured for blocking, not be closed, and must either 149 be lacking content (if reading) or space (if writing). */ 150 151 static int _operation_blocking(file_t *file, int reading) 152 { 153 return (file->can_block && !(file->notifiable.notifications & NOTIFY_PEER_CLOSED) && ( 154 (reading && !file_data_available(file)) || 155 (!reading && !file_data_space(file)))); 156 } 157 158 159 160 /* Return whether an access could occur, blocking if necessary. */ 161 162 static int _access_blocking(file_t *file, offset_t position, int reading) 163 { 164 long err; 165 166 /* Attempt to access the position, handling an error condition or a blocking 167 condition. */ 168 169 while ((err = _access(file, position)) || _operation_blocking(file, reading)) 170 { 171 position = file->data_current; 172 173 /* Exit if blocking is not configured or suitable. */ 174 175 if ((err && (err != -L4_EBUSY)) || !file->can_block) 176 return 0; 177 178 /* Handle an inability to access by blocking, exiting if waiting failed. */ 179 180 if (client_wait_file(file, client_notifier_task())) 181 return 0; 182 } 183 184 return 1; 185 } 186 187 188 189 /* Ensure that memory is mapped for accessing the given file, using the 190 indicated count as a region size hint. */ 191 192 static void *_map_memory(file_t *file, offset_t count) 193 { 194 if (file->memory == NULL) 195 { 196 if (file->object_flags & OBJECT_SUPPORTS_MMAP) 197 return client_mmap(file, client_tell(file), count, 0, 0, 198 file_region_flags(file->flags)); 199 else if (pipe_current(file)) 200 return NULL; 201 } 202 203 return file->memory; 204 } 205 206 207 208 /* Open a file opening object. */ 209 210 l4_cap_idx_t client_open_for_user(user_t user) 211 { 212 l4_cap_idx_t server = l4re_env_get_cap("server"); 213 214 return client_open_for_user_using(user, server); 215 } 216 217 /* Open a file opening object via a named capability. */ 218 219 l4_cap_idx_t client_open_for_user_using(user_t user, l4_cap_idx_t server) 220 { 221 if (l4_is_invalid_cap(server)) 222 return L4_INVALID_CAP; 223 224 l4_cap_idx_t opener; 225 long err = file_open_for_user(user, server, &opener); 226 227 if (err) 228 return L4_INVALID_CAP; 229 230 return opener; 231 } 232 233 234 235 /* Close a filesystem object. */ 236 237 void client_close(file_t *file) 238 { 239 if (file == NULL) 240 return; 241 242 file_flush(file); 243 file_close(file); 244 free(file); 245 } 246 247 248 249 /* Open a filesystem object. */ 250 251 file_t *client_open(const char *name, flags_t flags) 252 { 253 l4_cap_idx_t server = l4re_env_get_cap("server"); 254 255 return client_open_using(name, flags, server); 256 } 257 258 /* Open a filesystem object via a named capability. */ 259 260 file_t *client_open_using(const char *name, flags_t flags, l4_cap_idx_t server) 261 { 262 if (l4_is_invalid_cap(server)) 263 return NULL; 264 265 file_t *file = (file_t *) malloc(sizeof(file_t)); 266 267 if (file == NULL) 268 return NULL; 269 270 /* Return any allocated structure even if an error occurs. */ 271 272 file->error = file_open(file, name, flags, server); 273 return file; 274 } 275 276 277 278 /* Open a directory listing stream via the given named directory. */ 279 280 file_t *client_opendir(const char *name) 281 { 282 l4_cap_idx_t server = l4re_env_get_cap("server"); 283 284 return client_opendir_using(name, server); 285 } 286 287 /* Open a directory listing stream via the given named directory and a named 288 capability. */ 289 290 file_t *client_opendir_using(const char *name, l4_cap_idx_t server) 291 { 292 file_t *file = client_open_using(name, O_DIRECTORY, server); 293 294 if (file == NULL) 295 return NULL; 296 297 /* Return the directory structure itself for error handling. */ 298 299 if (!client_opened(file)) 300 return file; 301 302 file_t *reader = client_opendir_at(file); 303 304 /* Release the directory and return the reader even if an error occurs. */ 305 306 client_close(file); 307 return reader; 308 } 309 310 311 312 /* Open a directory listing stream via the given directory. */ 313 314 file_t *client_opendir_at(file_t *file) 315 { 316 file_t *reader = (file_t *) malloc(sizeof(file_t)); 317 318 if (reader == NULL) 319 return NULL; 320 321 /* Return any allocated structure even if an error occurs. */ 322 323 reader->error = directory_opendir(file, reader); 324 325 /* Set blocking read mode to be able to conveniently read directory entries 326 from the stream. If this fails, the error is set on the structure, but the 327 stream will be open. */ 328 329 if (!reader->error) 330 reader->error = client_set_blocking(reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED); 331 332 return reader; 333 } 334 335 336 337 /* Open another instance of an opened filesystem object. */ 338 339 file_t *client_reopen(file_t *file, flags_t flags) 340 { 341 if (file == NULL) 342 return NULL; 343 344 file_t *new_file = (file_t *) malloc(sizeof(file_t)); 345 346 if (new_file == NULL) 347 return NULL; 348 349 /* Return any allocated structure even if an error occurs. */ 350 351 new_file->error = file_reopen(file, new_file, flags); 352 return new_file; 353 } 354 355 356 357 /* Open a pipe object, returning any error condition. */ 358 359 long client_pipe(file_t **reader, file_t **writer, flags_t flags) 360 { 361 l4_cap_idx_t server = l4re_env_get_cap("pipes"); 362 363 return client_pipe_using(reader, writer, flags, server); 364 } 365 366 long client_pipe_using(file_t **reader, file_t **writer, flags_t flags, l4_cap_idx_t server) 367 { 368 *reader = NULL; 369 *writer = NULL; 370 371 if (l4_is_invalid_cap(server)) 372 return -L4_EINVAL; 373 374 *reader = (file_t *) malloc(sizeof(file_t)); 375 376 if (*reader == NULL) 377 return -L4_ENOMEM; 378 379 *writer = (file_t *) malloc(sizeof(file_t)); 380 381 if (*writer == NULL) 382 { 383 free(*reader); 384 return -L4_ENOMEM; 385 } 386 387 long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server); 388 389 /* Set blocking if successful and non-blocking is not indicated. */ 390 391 if (!err && !(flags & O_NONBLOCK)) 392 { 393 err = client_set_blocking(*reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED); 394 if (!err) 395 err = client_set_blocking(*writer, NOTIFY_SPACE_AVAILABLE | NOTIFY_PEER_CLOSED); 396 } 397 398 if (err) 399 { 400 free(*reader); 401 free(*writer); 402 } 403 404 return err; 405 } 406 407 408 409 /* Determine whether a file has been successfully opened. */ 410 411 int client_opened(file_t *file) 412 { 413 return (file != NULL) && !file->error; 414 } 415 416 417 418 /* Make a directory in the filesystem. */ 419 420 long client_mkdir(const char *path, mode_t mode) 421 { 422 l4_cap_idx_t server = l4re_env_get_cap("server"); 423 424 return client_mkdir_using(path, mode, server); 425 } 426 427 /* Make a directory in the filesystem via a named capability. */ 428 429 long client_mkdir_using(const char *path, mode_t mode, l4_cap_idx_t server) 430 { 431 return file_mkdir(path, mode, server); 432 } 433 434 435 436 /* Remove a file from the filesystem. */ 437 438 long client_remove(const char *path) 439 { 440 l4_cap_idx_t server = l4re_env_get_cap("server"); 441 442 return client_remove_using(path, server); 443 } 444 445 /* Remove a file from the filesystem via a named capability. */ 446 447 long client_remove_using(const char *path, l4_cap_idx_t server) 448 { 449 return file_remove(path, server); 450 } 451 452 453 454 /* Rename a file in the filesystem. */ 455 456 long client_rename(const char *source, const char *target) 457 { 458 l4_cap_idx_t server = l4re_env_get_cap("server"); 459 460 return client_rename_using(source, target, server); 461 } 462 463 /* Rename a file in the filesystem via a named capability. */ 464 465 long client_rename_using(const char *source, const char *target, l4_cap_idx_t server) 466 { 467 return file_rename(source, target, server); 468 } 469 470 471 472 /* Obtain filesystem object statistics. */ 473 474 long client_stat(const char *path, struct stat *st) 475 { 476 l4_cap_idx_t server = l4re_env_get_cap("server"); 477 478 return client_stat_using(path, st, server); 479 } 480 481 /* Obtain object statistics from the filesystem via a named capability. */ 482 483 long client_stat_using(const char *path, struct stat *st, l4_cap_idx_t server) 484 { 485 return file_stat(path, st, server); 486 } 487 488 489 490 /* Obtain the current region of a pipe. */ 491 492 long client_current_region(file_t *file) 493 { 494 if (!client_opened(file)) 495 return -L4_EINVAL; 496 497 return pipe_current(file); 498 } 499 500 501 502 /* Flush data explicitly to the filesystem object. */ 503 504 long client_flush(file_t *file) 505 { 506 if (!client_opened(file)) 507 return -L4_EINVAL; 508 509 /* Flush and retain most buffer settings. */ 510 511 return file_flush(file); 512 } 513 514 515 516 /* Map a memory region to a file. */ 517 518 void *client_mmap(file_t *file, offset_t position, offset_t length, 519 offset_t start_visible, offset_t end_visible, 520 l4re_rm_flags_t region_flags) 521 { 522 if (!client_opened(file) || file_mmap(file, position, length, start_visible, 523 end_visible, region_flags)) 524 return NULL; 525 526 return file->memory; 527 } 528 529 530 531 /* Obtain the next region of a pipe. */ 532 533 long client_next_region(file_t *file) 534 { 535 if (!client_opened(file)) 536 return -L4_EINVAL; 537 538 return pipe_next(file); 539 } 540 541 542 543 /* Close a notifier object. */ 544 545 void client_notifier_close(file_notifier_t *notifier) 546 { 547 file_notify_close(notifier); 548 } 549 550 /* Obtain a local notifier object. */ 551 552 file_notifier_t *client_notifier_local() 553 { 554 return file_notify_local(); 555 } 556 557 /* Obtain a task-wide notifier object. */ 558 559 file_notifier_t *client_notifier_task() 560 { 561 return file_notify_task(); 562 } 563 564 565 566 /* Read a directory entry. This must be freed by the caller after use. */ 567 568 struct dirent *client_readdir(file_t *file) 569 { 570 char buffer[DIRENT_CORE_SIZE]; 571 offset_t nread = client_read(file, buffer, DIRENT_CORE_SIZE); 572 573 /* Stop if no new structure can be successfully read. */ 574 575 if (nread != DIRENT_CORE_SIZE) 576 return NULL; 577 578 struct dirent *dirent = (struct dirent *) buffer; 579 offset_t remaining = dirent->d_reclen - DIRENT_CORE_SIZE; 580 581 /* Allocate a buffer for the complete structure. */ 582 583 char *entry = (char *) calloc(DIRENT_CORE_SIZE + remaining, sizeof(char)); 584 585 if (entry == NULL) 586 return NULL; 587 588 /* Copy the start of the entry into a new buffer. */ 589 590 memcpy(entry, buffer, DIRENT_CORE_SIZE); 591 592 /* Append to the entry buffer. */ 593 594 char *current = entry + DIRENT_CORE_SIZE; 595 596 nread = client_read(file, current, remaining); 597 598 /* Stop if no complete structure can be successfully read. */ 599 600 if (nread != remaining) 601 { 602 free(entry); 603 return NULL; 604 } 605 606 return (struct dirent *) entry; 607 } 608 609 610 611 /* Read from the filesystem object into the buffer provided. */ 612 613 offset_t client_read(file_t *file, void *buf, offset_t count) 614 { 615 if (!client_opened(file)) 616 return 0; 617 618 /* Map memory if none has been mapped so far. */ 619 620 if (_map_memory(file, count) == NULL) 621 return 0; 622 623 /* Amount available in the descriptor buffer already. */ 624 625 offset_t available = file_data_available(file); 626 offset_t to_transfer, total = 0; 627 628 while (count > 0) 629 { 630 /* If there is no data, try and obtain more data. */ 631 632 if (!available) 633 { 634 /* Flush any unwritten data, preparing to read from the file position at 635 the end of the data, and returning if no new data is available. */ 636 637 if (!_access_blocking(file, file_data_end_position(file), 1)) 638 break; 639 640 available = file_data_available(file); 641 642 if (!available) 643 break; 644 } 645 646 /* Transfer data into the supplied buffer. */ 647 648 to_transfer = available <= count ? available : count; 649 650 file_data_read(file, (char *) buf, to_transfer); 651 652 /* Update counters. */ 653 654 available -= to_transfer; 655 656 count -= to_transfer; 657 total += to_transfer; 658 659 buf = ((char *) buf + to_transfer); 660 } 661 662 return total; 663 } 664 665 666 667 /* Ensure that the buffer can provide the needed data. */ 668 669 offset_t client_seek(file_t *file, offset_t offset, int whence) 670 { 671 if (!client_opened(file)) 672 return 0; 673 674 offset_t position, current = file_data_current_position(file), change; 675 676 switch (whence) 677 { 678 case SEEK_SET: 679 position = offset; 680 break; 681 682 case SEEK_CUR: 683 position = current + offset; 684 break; 685 686 case SEEK_END: 687 position = file->size + offset; 688 break; 689 690 default: 691 /* NOTE: Set errno to EINVAL. */ 692 return current; 693 } 694 695 /* Retain the current position if unchanged. */ 696 697 if (position == current) 698 return position; 699 700 /* Move forward in the file. */ 701 702 if (position > current) 703 { 704 change = position - current; 705 706 /* Move towards the end of available data. 707 Request new data if not enough is available. */ 708 709 if (change <= file_data_available(file)) 710 { 711 file->data_current += change; 712 return position; 713 } 714 } 715 716 /* Move backward in the file. */ 717 718 else 719 { 720 change = current - position; 721 722 /* Move towards the start of available data. 723 Request new data if moving beyond the start of the data. */ 724 725 if (change <= file->data_current) 726 { 727 file->data_current -= change; 728 return position; 729 } 730 } 731 732 /* Handle unwritten data and reset the buffer for reading. */ 733 734 if (_access(file, position)) 735 return current; 736 737 return position; 738 } 739 740 741 742 /* Set or unset blocking access for a file. */ 743 744 long client_set_blocking(file_t *file, notify_flags_t flags) 745 { 746 long err; 747 748 if (file->can_block == flags) 749 return L4_EOK; 750 751 /* Since blocking access is used with specific file notifications, the 752 per-task notifier is used. */ 753 754 file_notifier_t *notifier = client_notifier_task(); 755 756 if (flags) 757 err = client_subscribe(file, flags, notifier); 758 else 759 err = client_unsubscribe(file, notifier); 760 761 if (err) 762 return err; 763 764 file->can_block = flags; 765 return L4_EOK; 766 } 767 768 769 770 /* Subscribe from events concerning a file. */ 771 772 long client_subscribe(file_t *file, notify_flags_t flags, file_notifier_t *notifier) 773 { 774 if (!client_opened(file)) 775 return -L4_EINVAL; 776 777 return file_notify_subscribe(file, flags, notifier); 778 } 779 780 781 782 /* Return the current position in the file. */ 783 784 offset_t client_tell(file_t *file) 785 { 786 if (!client_opened(file)) 787 return -L4_EINVAL; 788 789 return file_data_current_position(file); 790 } 791 792 793 794 /* Unsubscribe from events concerning a file. */ 795 796 long client_unsubscribe(file_t *file, file_notifier_t *notifier) 797 { 798 if (!client_opened(file)) 799 return -L4_EINVAL; 800 801 return file_notify_unsubscribe(file, notifier); 802 } 803 804 805 806 /* Wait for events involving a specific file. */ 807 808 long client_wait_file(file_t *file, file_notifier_t *notifier) 809 { 810 if (!client_opened(file)) 811 return -L4_EINVAL; 812 813 return file_notify_wait_file(file, notifier); 814 } 815 816 /* Wait for events concerning files, referencing a file object if an event is 817 delivered. */ 818 819 long client_wait_files(file_t **file, file_notifier_t *notifier) 820 { 821 return file_notify_wait_files(file, notifier); 822 } 823 824 825 826 /* Write to the filesystem object from the buffer provided. */ 827 828 offset_t client_write(file_t *file, const void *buf, offset_t count) 829 { 830 if (!client_opened(file)) 831 return 0; 832 833 /* Map memory if none has been mapped so far. */ 834 835 if (_map_memory(file, count) == NULL) 836 return 0; 837 838 /* Attempt to ensure that the file can accept the amount of data to be 839 written. This may not resize to the needed amount if a file has a fixed 840 size, but data will still be written to any available space. */ 841 842 offset_t needed_size = file_data_current_position(file) + count; 843 844 if (file->object_flags & OBJECT_HAS_SIZE) 845 { 846 if (file->size < needed_size) 847 { 848 file_resize(file, needed_size); 849 850 if (file->size < needed_size) 851 count = file->size - file_data_current_position(file); 852 } 853 } 854 855 /* Space remaining in the descriptor buffer. */ 856 857 offset_t space = file_data_space(file); 858 offset_t to_transfer, total = 0; 859 860 while (count > 0) 861 { 862 /* If no space is available, try and send data, reset the buffer. */ 863 864 if (!space) 865 { 866 /* Flush any unwritten data and continue writing from the current data 867 position. */ 868 869 if (!_access_blocking(file, file_data_current_position(file), 0)) 870 break; 871 872 space = file_data_space(file); 873 } 874 875 /* Transfer data into the supplied buffer. */ 876 877 to_transfer = space <= count ? space : count; 878 879 file_data_write(file, (char *) buf, to_transfer); 880 881 /* Update counters. */ 882 883 space -= to_transfer; 884 885 count -= to_transfer; 886 total += to_transfer; 887 888 buf = ((char *) buf + to_transfer); 889 } 890 891 return total; 892 } 893 894 // vim: tabstop=2 expandtab shiftwidth=2