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