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 file_t *file = (file_t *) malloc(sizeof(file_t)); 55 56 if (file == NULL) 57 return NULL; 58 59 l4_cap_idx_t server = l4re_env_get_cap("server"); 60 61 if (file_open(file, name, flags, server)) 62 { 63 free(file); 64 return NULL; 65 } 66 67 return file; 68 } 69 70 71 72 /* Open a pipe object. */ 73 74 long client_pipe(file_t **reader, file_t **writer) 75 { 76 *reader = (file_t *) malloc(sizeof(file_t)); 77 78 if (*reader == NULL) 79 return -L4_ENOMEM; 80 81 *writer = (file_t *) malloc(sizeof(file_t)); 82 83 if (*writer == NULL) 84 { 85 free(*reader); 86 return -L4_ENOMEM; 87 } 88 89 l4_cap_idx_t server = l4re_env_get_cap("pipes"); 90 91 long err = pipe_open(DEFAULT_PIPE_SIZE, *reader, *writer, server); 92 93 if (err) 94 { 95 free(*reader); 96 free(*writer); 97 } 98 99 return err; 100 } 101 102 103 104 /* Flush data conditionally to the filesystem object. */ 105 106 static long _flush(file_t *file, offset_t position) 107 { 108 long err; 109 110 /* Where the position is outside the current region, re-map. */ 111 112 if ((position < file->start_pos) || (position >= file->end_pos)) 113 { 114 if (file->can_mmap) 115 { 116 if (file_mmap(file, position, file_span(file))) 117 return -L4_EIO; 118 } 119 120 /* Strict conditions for region navigation in pipes. */ 121 122 else if ((position != file->end_pos) || 123 (client_next_region(file) == NULL)) 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 134 if (err) 135 return err; 136 } 137 138 /* Update the current data offset. */ 139 140 file->data_current = position - file->start_pos; 141 return L4_EOK; 142 } 143 144 145 146 /* Flush data explicitly to the filesystem object. */ 147 148 long client_flush(file_t *file) 149 { 150 if (file == NULL) 151 return -L4_EINVAL; 152 153 /* Flush and retain most buffer settings. */ 154 155 return file_flush(file); 156 } 157 158 159 160 /* Map a memory region to a file. */ 161 162 void *client_mmap(file_t *file, offset_t position, offset_t length) 163 { 164 if ((file == NULL) || (file_mmap(file, position, length))) 165 return NULL; 166 167 return file->memory; 168 } 169 170 171 172 /* Obtain the current region of a pipe. */ 173 174 void *client_current_region(file_t *file) 175 { 176 if ((file == NULL) || (pipe_current(file))) 177 return NULL; 178 179 return file->memory; 180 } 181 182 183 184 /* Obtain the next region of a pipe. */ 185 186 void *client_next_region(file_t *file) 187 { 188 if ((file == NULL) || (pipe_next(file))) 189 return NULL; 190 191 return file->memory; 192 } 193 194 195 196 /* Read from the filesystem object into the buffer provided. */ 197 198 offset_t client_read(file_t *file, void *buf, offset_t count) 199 { 200 if (file == NULL) 201 return 0; 202 203 /* Map memory if none has been mapped so far. */ 204 205 if ((file->memory == NULL) && (client_mmap(file, client_tell(file), count) == NULL)) 206 return 0; 207 208 /* Amount available in the descriptor buffer already. */ 209 210 offset_t available = file_data_available(file); 211 offset_t to_transfer, total = 0; 212 213 while (count > 0) 214 { 215 /* If there is no data, try and obtain more data. */ 216 217 if (!available) 218 { 219 /* Flush any unwritten data, preparing to read from the file position at 220 the end of the data, and returning if no new data is available. */ 221 222 if (_flush(file, file_data_end_position(file))) 223 break; 224 225 available = file_data_available(file); 226 227 if (!available) 228 break; 229 } 230 231 /* Transfer data into the supplied buffer. */ 232 233 to_transfer = available <= count ? available : count; 234 235 file_data_read(file, (char *) buf, to_transfer); 236 237 /* Update counters. */ 238 239 available -= to_transfer; 240 241 count -= to_transfer; 242 total += to_transfer; 243 244 buf = ((char *) buf + to_transfer); 245 } 246 247 return total; 248 } 249 250 251 252 /* Ensure that the buffer can provide the needed data. */ 253 254 offset_t client_seek(file_t *file, offset_t offset, int whence) 255 { 256 if (file == NULL) 257 return 0; 258 259 offset_t position, current = file_data_current_position(file), change; 260 261 switch (whence) 262 { 263 case SEEK_SET: 264 position = offset; 265 break; 266 267 case SEEK_CUR: 268 position = current + offset; 269 break; 270 271 case SEEK_END: 272 position = file->size + offset; 273 break; 274 275 default: 276 /* NOTE: Set errno to EINVAL. */ 277 return -1; 278 } 279 280 /* Retain the current position if unchanged. */ 281 282 if (position == current) 283 return position; 284 285 /* Move forward in the file. */ 286 287 if (position > current) 288 { 289 change = position - current; 290 291 /* Move towards the end of available data. 292 Request new data if not enough is available. */ 293 294 if (change <= file_data_available(file)) 295 { 296 file->data_current += change; 297 return position; 298 } 299 } 300 301 /* Move backward in the file. */ 302 303 else 304 { 305 change = current - position; 306 307 /* Move towards the start of available data. 308 Request new data if moving beyond the start of the data. */ 309 310 if (change <= file->data_current) 311 { 312 file->data_current -= change; 313 return position; 314 } 315 } 316 317 /* Handle unwritten data and reset the buffer for reading. */ 318 319 _flush(file, position); 320 return position; 321 } 322 323 324 325 long client_tell(file_t *file) 326 { 327 if (file == NULL) 328 return -L4_EINVAL; 329 330 return file_data_current_position(file); 331 } 332 333 334 335 /* Write to the filesystem object from the buffer provided. */ 336 337 offset_t client_write(file_t *file, const void *buf, offset_t count) 338 { 339 if (file == NULL) 340 return 0; 341 342 /* Map memory if none has been mapped so far. */ 343 344 if ((file->memory == NULL) && (client_mmap(file, client_tell(file), count) == NULL)) 345 return 0; 346 347 /* Attempt to ensure that the file can accept the amount of data to be 348 written. This may not resize to the needed amount if a file has a fixed 349 size, but data will still be written to any available space. */ 350 351 offset_t needed_size = file_data_current_position(file) + count; 352 353 if (file->size < needed_size) 354 { 355 file_resize(file, needed_size); 356 357 if (file->size < needed_size) 358 count = file->size - file_data_current_position(file); 359 } 360 361 /* Space remaining in the descriptor buffer. */ 362 363 offset_t space = file_data_space(file); 364 offset_t to_transfer, total = 0; 365 366 while (count > 0) 367 { 368 /* If no space is available, try and send data, reset the buffer. */ 369 370 if (!space) 371 { 372 /* Flush any unwritten data and continue writing from the current data 373 position. */ 374 375 if (_flush(file, file_data_current_position(file))) 376 break; 377 378 space = file_data_space(file); 379 } 380 381 /* Transfer data into the supplied buffer. */ 382 383 to_transfer = space <= count ? space : count; 384 385 file_data_write(file, (char *) buf, to_transfer); 386 387 /* Update counters. */ 388 389 space -= to_transfer; 390 391 count -= to_transfer; 392 total += to_transfer; 393 394 buf = ((char *) buf + to_transfer); 395 } 396 397 return total; 398 } 399 400 // vim: tabstop=2 expandtab shiftwidth=2