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 /* Access the given position and synchronise state with the file object. */ 112 113 static long _access(file_t *file, offset_t position) 114 { 115 long err; 116 117 if (file->can_mmap) 118 { 119 /* Where the position is outside the current region, re-map. */ 120 121 if ((position < file->start_pos) || (position >= file->end_pos)) 122 { 123 if (file_mmap(file, position, file_span(file))) 124 return -L4_EIO; 125 } 126 127 /* Otherwise, flush any written data in the current region and update the 128 file size details. */ 129 130 else 131 { 132 err = client_flush(file); 133 if (err) 134 return err; 135 } 136 } 137 else 138 { 139 /* Strict conditions for region navigation in pipes. */ 140 141 if ((position < file->start_pos) || (position > file->end_pos)) 142 { 143 return -L4_EIO; 144 } 145 146 /* The next region is only available at the end of the mapped memory. */ 147 148 else if (position == file->end_pos) 149 { 150 err = client_next_region(file); 151 if (err) 152 return err; 153 } 154 155 /* Within the current pipe region, synchronise with the pipe object. */ 156 157 else 158 { 159 err = client_current_region(file); 160 if (err) 161 return err; 162 } 163 } 164 165 /* Update the current data offset. */ 166 167 if (file->has_size) 168 file->data_current = position - file->start_pos; 169 else 170 file->data_current = 0; 171 172 return L4_EOK; 173 } 174 175 176 177 /* Flush data explicitly to the filesystem object. */ 178 179 long client_flush(file_t *file) 180 { 181 if (file == NULL) 182 return -L4_EINVAL; 183 184 /* Flush and retain most buffer settings. */ 185 186 return file_flush(file); 187 } 188 189 190 191 /* Map a memory region to a file. */ 192 193 void *client_mmap(file_t *file, offset_t position, offset_t length) 194 { 195 if ((file == NULL) || (file_mmap(file, position, length))) 196 return NULL; 197 198 return file->memory; 199 } 200 201 202 203 /* Obtain the current region of a pipe. */ 204 205 long client_current_region(file_t *file) 206 { 207 if (file == NULL) 208 return -L4_EINVAL; 209 210 return pipe_current(file); 211 } 212 213 214 215 /* Obtain the next region of a pipe. */ 216 217 long client_next_region(file_t *file) 218 { 219 if (file == NULL) 220 return -L4_EINVAL; 221 222 return pipe_next(file); 223 } 224 225 226 227 /* Ensure that memory is mapped for accessing the given file, using the 228 indicated count as a region size hint. */ 229 230 static void *_map_memory(file_t *file, offset_t count) 231 { 232 if (file->memory == NULL) 233 { 234 if (file->can_mmap) 235 return client_mmap(file, client_tell(file), count); 236 else if (pipe_current(file)) 237 return NULL; 238 } 239 240 return file->memory; 241 } 242 243 244 245 /* Read from the filesystem object into the buffer provided. */ 246 247 offset_t client_read(file_t *file, void *buf, offset_t count) 248 { 249 if (file == NULL) 250 return 0; 251 252 /* Map memory if none has been mapped so far. */ 253 254 if (_map_memory(file, count) == NULL) 255 return 0; 256 257 /* Amount available in the descriptor buffer already. */ 258 259 offset_t available = file_data_available(file); 260 offset_t to_transfer, total = 0; 261 262 while (count > 0) 263 { 264 /* If there is no data, try and obtain more data. */ 265 266 if (!available) 267 { 268 /* Flush any unwritten data, preparing to read from the file position at 269 the end of the data, and returning if no new data is available. */ 270 271 if (_access(file, file_data_end_position(file))) 272 break; 273 274 available = file_data_available(file); 275 276 if (!available) 277 break; 278 } 279 280 /* Transfer data into the supplied buffer. */ 281 282 to_transfer = available <= count ? available : count; 283 284 file_data_read(file, (char *) buf, to_transfer); 285 286 /* Update counters. */ 287 288 available -= to_transfer; 289 290 count -= to_transfer; 291 total += to_transfer; 292 293 buf = ((char *) buf + to_transfer); 294 } 295 296 return total; 297 } 298 299 300 301 /* Ensure that the buffer can provide the needed data. */ 302 303 offset_t client_seek(file_t *file, offset_t offset, int whence) 304 { 305 if (file == NULL) 306 return 0; 307 308 offset_t position, current = file_data_current_position(file), change; 309 310 switch (whence) 311 { 312 case SEEK_SET: 313 position = offset; 314 break; 315 316 case SEEK_CUR: 317 position = current + offset; 318 break; 319 320 case SEEK_END: 321 position = file->size + offset; 322 break; 323 324 default: 325 /* NOTE: Set errno to EINVAL. */ 326 return -1; 327 } 328 329 /* Retain the current position if unchanged. */ 330 331 if (position == current) 332 return position; 333 334 /* Move forward in the file. */ 335 336 if (position > current) 337 { 338 change = position - current; 339 340 /* Move towards the end of available data. 341 Request new data if not enough is available. */ 342 343 if (change <= file_data_available(file)) 344 { 345 file->data_current += change; 346 return position; 347 } 348 } 349 350 /* Move backward in the file. */ 351 352 else 353 { 354 change = current - position; 355 356 /* Move towards the start of available data. 357 Request new data if moving beyond the start of the data. */ 358 359 if (change <= file->data_current) 360 { 361 file->data_current -= change; 362 return position; 363 } 364 } 365 366 /* Handle unwritten data and reset the buffer for reading. */ 367 368 _access(file, position); 369 return position; 370 } 371 372 373 374 long client_tell(file_t *file) 375 { 376 if (file == NULL) 377 return -L4_EINVAL; 378 379 return file_data_current_position(file); 380 } 381 382 383 384 /* Write to the filesystem object from the buffer provided. */ 385 386 offset_t client_write(file_t *file, const void *buf, offset_t count) 387 { 388 if (file == NULL) 389 return 0; 390 391 /* Map memory if none has been mapped so far. */ 392 393 if (_map_memory(file, count) == NULL) 394 return 0; 395 396 /* Attempt to ensure that the file can accept the amount of data to be 397 written. This may not resize to the needed amount if a file has a fixed 398 size, but data will still be written to any available space. */ 399 400 offset_t needed_size = file_data_current_position(file) + count; 401 402 if (file->has_size) 403 { 404 if (file->size < needed_size) 405 { 406 file_resize(file, needed_size); 407 408 if (file->size < needed_size) 409 count = file->size - file_data_current_position(file); 410 } 411 } 412 413 /* Space remaining in the descriptor buffer. */ 414 415 offset_t space = file_data_space(file); 416 offset_t to_transfer, total = 0; 417 418 while (count > 0) 419 { 420 /* If no space is available, try and send data, reset the buffer. */ 421 422 if (!space) 423 { 424 /* Flush any unwritten data and continue writing from the current data 425 position. */ 426 427 if (_access(file, file_data_current_position(file))) 428 break; 429 430 space = file_data_space(file); 431 } 432 433 /* Transfer data into the supplied buffer. */ 434 435 to_transfer = space <= count ? space : count; 436 437 file_data_write(file, (char *) buf, to_transfer); 438 439 /* Update counters. */ 440 441 space -= to_transfer; 442 443 count -= to_transfer; 444 total += to_transfer; 445 446 buf = ((char *) buf + to_transfer); 447 } 448 449 return total; 450 } 451 452 // vim: tabstop=2 expandtab shiftwidth=2