1 /* 2 * Copyright (C) 2007 Michael Brown <mbrown (at) fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 FILE_LICENCE ( GPL2_OR_LATER ); 20 21 #include <stdlib.h> 22 #include <string.h> 23 #include <errno.h> 24 #include <gpxe/list.h> 25 #include <gpxe/xfer.h> 26 #include <gpxe/open.h> 27 #include <gpxe/process.h> 28 #include <gpxe/posix_io.h> 29 30 /** @file 31 * 32 * POSIX-like I/O 33 * 34 * These functions provide traditional blocking I/O semantics. They 35 * are designed to be used by the PXE TFTP API. Because they block, 36 * they may not be used by most other portions of the gPXE codebase. 37 */ 38 39 /** An open file */ 40 struct posix_file { 41 /** Reference count for this object */ 42 struct refcnt refcnt; 43 /** List of open files */ 44 struct list_head list; 45 /** File descriptor */ 46 int fd; 47 /** Overall status 48 * 49 * Set to -EINPROGRESS while data transfer is in progress. 50 */ 51 int rc; 52 /** Data transfer interface */ 53 struct xfer_interface xfer; 54 /** Current seek position */ 55 size_t pos; 56 /** File size */ 57 size_t filesize; 58 /** Received data queue */ 59 struct list_head data; 60 }; 61 62 /** List of open files */ 63 static LIST_HEAD ( posix_files ); 64 65 /** 66 * Free open file 67 * 68 * @v refcnt Reference counter 69 */ 70 static void posix_file_free ( struct refcnt *refcnt ) { 71 struct posix_file *file = 72 container_of ( refcnt, struct posix_file, refcnt ); 73 struct io_buffer *iobuf; 74 struct io_buffer *tmp; 75 76 list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) { 77 list_del ( &iobuf->list ); 78 free_iob ( iobuf ); 79 } 80 free ( file ); 81 } 82 83 /** 84 * Terminate file data transfer 85 * 86 * @v file POSIX file 87 * @v rc Reason for termination 88 */ 89 static void posix_file_finished ( struct posix_file *file, int rc ) { 90 xfer_nullify ( &file->xfer ); 91 xfer_close ( &file->xfer, rc ); 92 file->rc = rc; 93 } 94 95 /** 96 * Handle close() event 97 * 98 * @v xfer POSIX file data transfer interface 99 * @v rc Reason for close 100 */ 101 static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) { 102 struct posix_file *file = 103 container_of ( xfer, struct posix_file, xfer ); 104 105 posix_file_finished ( file, rc ); 106 } 107 108 /** 109 * Handle deliver_iob() event 110 * 111 * @v xfer POSIX file data transfer interface 112 * @v iobuf I/O buffer 113 * @v meta Data transfer metadata 114 * @ret rc Return status code 115 */ 116 static int 117 posix_file_xfer_deliver_iob ( struct xfer_interface *xfer, 118 struct io_buffer *iobuf, 119 struct xfer_metadata *meta ) { 120 struct posix_file *file = 121 container_of ( xfer, struct posix_file, xfer ); 122 123 /* Keep track of file position solely for the filesize */ 124 if ( meta->whence != SEEK_CUR ) 125 file->pos = 0; 126 file->pos += meta->offset; 127 if ( file->filesize < file->pos ) 128 file->filesize = file->pos; 129 130 if ( iob_len ( iobuf ) ) { 131 list_add_tail ( &iobuf->list, &file->data ); 132 } else { 133 free_iob ( iobuf ); 134 } 135 136 return 0; 137 } 138 139 /** POSIX file data transfer interface operations */ 140 static struct xfer_interface_operations posix_file_xfer_operations = { 141 .close = posix_file_xfer_close, 142 .vredirect = xfer_vreopen, 143 .window = unlimited_xfer_window, 144 .alloc_iob = default_xfer_alloc_iob, 145 .deliver_iob = posix_file_xfer_deliver_iob, 146 .deliver_raw = xfer_deliver_as_iob, 147 }; 148 149 /** 150 * Identify file by file descriptor 151 * 152 * @v fd File descriptor 153 * @ret file Corresponding file, or NULL 154 */ 155 static struct posix_file * posix_fd_to_file ( int fd ) { 156 struct posix_file *file; 157 158 list_for_each_entry ( file, &posix_files, list ) { 159 if ( file->fd == fd ) 160 return file; 161 } 162 return NULL; 163 } 164 165 /** 166 * Find an available file descriptor 167 * 168 * @ret fd File descriptor, or negative error number 169 */ 170 static int posix_find_free_fd ( void ) { 171 int fd; 172 173 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { 174 if ( ! posix_fd_to_file ( fd ) ) 175 return fd; 176 } 177 DBG ( "POSIX could not find free file descriptor\n" ); 178 return -ENFILE; 179 } 180 181 /** 182 * Open file 183 * 184 * @v uri_string URI string 185 * @ret fd File descriptor, or negative error number 186 */ 187 int open ( const char *uri_string ) { 188 struct posix_file *file; 189 int fd; 190 int rc; 191 192 /* Find a free file descriptor to use */ 193 fd = posix_find_free_fd(); 194 if ( fd < 0 ) 195 return fd; 196 197 /* Allocate and initialise structure */ 198 file = zalloc ( sizeof ( *file ) ); 199 if ( ! file ) 200 return -ENOMEM; 201 file->refcnt.free = posix_file_free; 202 file->fd = fd; 203 file->rc = -EINPROGRESS; 204 xfer_init ( &file->xfer, &posix_file_xfer_operations, 205 &file->refcnt ); 206 INIT_LIST_HEAD ( &file->data ); 207 208 /* Open URI on data transfer interface */ 209 if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 ) 210 goto err; 211 212 /* Wait for open to succeed or fail */ 213 while ( list_empty ( &file->data ) ) { 214 step(); 215 if ( file->rc == 0 ) 216 break; 217 if ( file->rc != -EINPROGRESS ) { 218 rc = file->rc; 219 goto err; 220 } 221 } 222 223 /* Add to list of open files. List takes reference ownership. */ 224 list_add ( &file->list, &posix_files ); 225 DBG ( "POSIX opened %s as file %d\n", uri_string, fd ); 226 return fd; 227 228 err: 229 posix_file_finished ( file, rc ); 230 ref_put ( &file->refcnt ); 231 return rc; 232 } 233 234 /** 235 * Check file descriptors for readiness 236 * 237 * @v readfds File descriptors to check 238 * @v wait Wait until data is ready 239 * @ret nready Number of ready file descriptors 240 */ 241 int select ( fd_set *readfds, int wait ) { 242 struct posix_file *file; 243 int fd; 244 245 do { 246 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { 247 if ( ! FD_ISSET ( fd, readfds ) ) 248 continue; 249 file = posix_fd_to_file ( fd ); 250 if ( ! file ) 251 return -EBADF; 252 if ( ( list_empty ( &file->data ) ) && 253 ( file->rc == -EINPROGRESS ) ) 254 continue; 255 /* Data is available or status has changed */ 256 FD_ZERO ( readfds ); 257 FD_SET ( fd, readfds ); 258 return 1; 259 } 260 step(); 261 } while ( wait ); 262 263 return 0; 264 } 265 266 /** 267 * Read data from file 268 * 269 * @v buffer Data buffer 270 * @v offset Starting offset within data buffer 271 * @v len Maximum length to read 272 * @ret len Actual length read, or negative error number 273 * 274 * This call is non-blocking; if no data is available to read then 275 * -EWOULDBLOCK will be returned. 276 */ 277 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) { 278 struct posix_file *file; 279 struct io_buffer *iobuf; 280 size_t len; 281 282 /* Identify file */ 283 file = posix_fd_to_file ( fd ); 284 if ( ! file ) 285 return -EBADF; 286 287 /* Try to fetch more data if none available */ 288 if ( list_empty ( &file->data ) ) 289 step(); 290 291 /* Dequeue at most one received I/O buffer into user buffer */ 292 list_for_each_entry ( iobuf, &file->data, list ) { 293 len = iob_len ( iobuf ); 294 if ( len > max_len ) 295 len = max_len; 296 copy_to_user ( buffer, offset, iobuf->data, len ); 297 iob_pull ( iobuf, len ); 298 if ( ! iob_len ( iobuf ) ) { 299 list_del ( &iobuf->list ); 300 free_iob ( iobuf ); 301 } 302 file->pos += len; 303 assert ( len != 0 ); 304 return len; 305 } 306 307 /* If file has completed, return (after returning all data) */ 308 if ( file->rc != -EINPROGRESS ) { 309 assert ( list_empty ( &file->data ) ); 310 return file->rc; 311 } 312 313 /* No data ready and file still in progress; return -WOULDBLOCK */ 314 return -EWOULDBLOCK; 315 } 316 317 /** 318 * Determine file size 319 * 320 * @v fd File descriptor 321 * @ret size File size, or negative error number 322 */ 323 ssize_t fsize ( int fd ) { 324 struct posix_file *file; 325 326 /* Identify file */ 327 file = posix_fd_to_file ( fd ); 328 if ( ! file ) 329 return -EBADF; 330 331 return file->filesize; 332 } 333 334 /** 335 * Close file 336 * 337 * @v fd File descriptor 338 * @ret rc Return status code 339 */ 340 int close ( int fd ) { 341 struct posix_file *file; 342 343 /* Identify file */ 344 file = posix_fd_to_file ( fd ); 345 if ( ! file ) 346 return -EBADF; 347 348 /* Terminate data transfer */ 349 posix_file_finished ( file, 0 ); 350 351 /* Remove from list of open files and drop reference */ 352 list_del ( &file->list ); 353 ref_put ( &file->refcnt ); 354 return 0; 355 } 356