iPXE
posix_io.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <errno.h>
00029 #include <ipxe/list.h>
00030 #include <ipxe/iobuf.h>
00031 #include <ipxe/xfer.h>
00032 #include <ipxe/open.h>
00033 #include <ipxe/process.h>
00034 #include <ipxe/posix_io.h>
00035 
00036 /** @file
00037  *
00038  * POSIX-like I/O
00039  *
00040  * These functions provide traditional blocking I/O semantics.  They
00041  * are designed to be used by the PXE TFTP API.  Because they block,
00042  * they may not be used by most other portions of the iPXE codebase.
00043  */
00044 
00045 /** An open file */
00046 struct posix_file {
00047         /** Reference count for this object */
00048         struct refcnt refcnt;
00049         /** List of open files */
00050         struct list_head list;
00051         /** File descriptor */
00052         int fd;
00053         /** Overall status
00054          *
00055          * Set to -EINPROGRESS while data transfer is in progress.
00056          */
00057         int rc;
00058         /** Data transfer interface */
00059         struct interface xfer;
00060         /** Current seek position */
00061         size_t pos;
00062         /** File size */
00063         size_t filesize;
00064         /** Received data queue */
00065         struct list_head data;
00066 };
00067 
00068 /** List of open files */
00069 static LIST_HEAD ( posix_files );
00070 
00071 /**
00072  * Free open file
00073  *
00074  * @v refcnt            Reference counter
00075  */
00076 static void posix_file_free ( struct refcnt *refcnt ) {
00077         struct posix_file *file =
00078                 container_of ( refcnt, struct posix_file, refcnt );
00079         struct io_buffer *iobuf;
00080         struct io_buffer *tmp;
00081 
00082         list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
00083                 list_del ( &iobuf->list );
00084                 free_iob ( iobuf );
00085         }
00086         free ( file );
00087 }
00088 
00089 /**
00090  * Terminate file data transfer
00091  *
00092  * @v file              POSIX file
00093  * @v rc                Reason for termination
00094  */
00095 static void posix_file_finished ( struct posix_file *file, int rc ) {
00096         intf_shutdown ( &file->xfer, rc );
00097         file->rc = rc;
00098 }
00099 
00100 /**
00101  * Handle deliver_iob() event
00102  *
00103  * @v file              POSIX file
00104  * @v iobuf             I/O buffer
00105  * @v meta              Data transfer metadata
00106  * @ret rc              Return status code
00107  */
00108 static int posix_file_xfer_deliver ( struct posix_file *file,
00109                                      struct io_buffer *iobuf,
00110                                      struct xfer_metadata *meta ) {
00111 
00112         /* Keep track of file position solely for the filesize */
00113         if ( meta->flags & XFER_FL_ABS_OFFSET )
00114                 file->pos = 0;
00115         file->pos += meta->offset;
00116         if ( file->filesize < file->pos )
00117                 file->filesize = file->pos;
00118 
00119         if ( iob_len ( iobuf ) ) {
00120                 list_add_tail ( &iobuf->list, &file->data );
00121         } else {
00122                 free_iob ( iobuf );
00123         }
00124 
00125         return 0;
00126 }
00127 
00128 /** POSIX file data transfer interface operations */
00129 static struct interface_operation posix_file_xfer_operations[] = {
00130         INTF_OP ( xfer_deliver, struct posix_file *, posix_file_xfer_deliver ),
00131         INTF_OP ( intf_close, struct posix_file *, posix_file_finished ),
00132 };
00133 
00134 /** POSIX file data transfer interface descriptor */
00135 static struct interface_descriptor posix_file_xfer_desc =
00136         INTF_DESC ( struct posix_file, xfer, posix_file_xfer_operations );
00137 
00138 /**
00139  * Identify file by file descriptor
00140  *
00141  * @v fd                File descriptor
00142  * @ret file            Corresponding file, or NULL
00143  */
00144 static struct posix_file * posix_fd_to_file ( int fd ) {
00145         struct posix_file *file;
00146 
00147         list_for_each_entry ( file, &posix_files, list ) {
00148                 if ( file->fd == fd )
00149                         return file;
00150         }
00151         return NULL;
00152 }
00153 
00154 /**
00155  * Find an available file descriptor
00156  *
00157  * @ret fd              File descriptor, or negative error number
00158  */
00159 static int posix_find_free_fd ( void ) {
00160         int fd;
00161 
00162         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
00163                 if ( ! posix_fd_to_file ( fd ) )
00164                         return fd;
00165         }
00166         DBG ( "POSIX could not find free file descriptor\n" );
00167         return -ENFILE;
00168 }
00169 
00170 /**
00171  * Open file
00172  *
00173  * @v uri_string        URI string
00174  * @ret fd              File descriptor, or negative error number
00175  */
00176 int open ( const char *uri_string ) {
00177         struct posix_file *file;
00178         int fd;
00179         int rc;
00180 
00181         /* Find a free file descriptor to use */
00182         fd = posix_find_free_fd();
00183         if ( fd < 0 )
00184                 return fd;
00185 
00186         /* Allocate and initialise structure */
00187         file = zalloc ( sizeof ( *file ) );
00188         if ( ! file )
00189                 return -ENOMEM;
00190         ref_init ( &file->refcnt, posix_file_free );
00191         file->fd = fd;
00192         file->rc = -EINPROGRESS;
00193         intf_init ( &file->xfer, &posix_file_xfer_desc, &file->refcnt );
00194         INIT_LIST_HEAD ( &file->data );
00195 
00196         /* Open URI on data transfer interface */
00197         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
00198                 goto err;
00199 
00200         /* Wait for open to succeed or fail */
00201         while ( list_empty ( &file->data ) ) {
00202                 step();
00203                 if ( file->rc == 0 )
00204                         break;
00205                 if ( file->rc != -EINPROGRESS ) {
00206                         rc = file->rc;
00207                         goto err;
00208                 }
00209         }
00210 
00211         /* Add to list of open files.  List takes reference ownership. */
00212         list_add ( &file->list, &posix_files );
00213         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
00214         return fd;
00215 
00216  err:
00217         posix_file_finished ( file, rc );
00218         ref_put ( &file->refcnt );
00219         return rc;
00220 }
00221 
00222 /**
00223  * Check file descriptors for readiness
00224  *
00225  * @v readfds           File descriptors to check
00226  * @v wait              Wait until data is ready
00227  * @ret nready          Number of ready file descriptors
00228  */
00229 int select ( fd_set *readfds, int wait ) {
00230         struct posix_file *file;
00231         int fd;
00232 
00233         do {
00234                 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
00235                         if ( ! FD_ISSET ( fd, readfds ) )
00236                                 continue;
00237                         file = posix_fd_to_file ( fd );
00238                         if ( ! file )
00239                                 return -EBADF;
00240                         if ( ( list_empty ( &file->data ) ) &&
00241                              ( file->rc == -EINPROGRESS ) )
00242                                 continue;
00243                         /* Data is available or status has changed */
00244                         FD_ZERO ( readfds );
00245                         FD_SET ( fd, readfds );
00246                         return 1;
00247                 }
00248                 step();
00249         } while ( wait );
00250 
00251         return 0;
00252 }
00253 
00254 /**
00255  * Read data from file
00256  *
00257  * @v buffer            Data buffer
00258  * @v offset            Starting offset within data buffer
00259  * @v len               Maximum length to read
00260  * @ret len             Actual length read, or negative error number
00261  *
00262  * This call is non-blocking; if no data is available to read then
00263  * -EWOULDBLOCK will be returned.
00264  */
00265 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
00266         struct posix_file *file;
00267         struct io_buffer *iobuf;
00268         size_t len;
00269 
00270         /* Identify file */
00271         file = posix_fd_to_file ( fd );
00272         if ( ! file )
00273                 return -EBADF;
00274 
00275         /* Try to fetch more data if none available */
00276         if ( list_empty ( &file->data ) )
00277                 step();
00278 
00279         /* Dequeue at most one received I/O buffer into user buffer */
00280         list_for_each_entry ( iobuf, &file->data, list ) {
00281                 len = iob_len ( iobuf );
00282                 if ( len > max_len )
00283                         len = max_len;
00284                 copy_to_user ( buffer, offset, iobuf->data, len );
00285                 iob_pull ( iobuf, len );
00286                 if ( ! iob_len ( iobuf ) ) {
00287                         list_del ( &iobuf->list );
00288                         free_iob ( iobuf );
00289                 }
00290                 file->pos += len;
00291                 assert ( len != 0 );
00292                 return len;
00293         }
00294 
00295         /* If file has completed, return (after returning all data) */
00296         if ( file->rc != -EINPROGRESS ) {
00297                 assert ( list_empty ( &file->data ) );
00298                 return file->rc;
00299         }
00300 
00301         /* No data ready and file still in progress; return -WOULDBLOCK */
00302         return -EWOULDBLOCK;
00303 }
00304 
00305 /**
00306  * Determine file size
00307  *
00308  * @v fd                File descriptor
00309  * @ret size            File size, or negative error number
00310  */
00311 ssize_t fsize ( int fd ) {
00312         struct posix_file *file;
00313 
00314         /* Identify file */
00315         file = posix_fd_to_file ( fd );
00316         if ( ! file )
00317                 return -EBADF;
00318 
00319         return file->filesize;
00320 }
00321 
00322 /**
00323  * Close file
00324  *
00325  * @v fd                File descriptor
00326  * @ret rc              Return status code
00327  */
00328 int close ( int fd ) {
00329         struct posix_file *file;
00330 
00331         /* Identify file */
00332         file = posix_fd_to_file ( fd );
00333         if ( ! file )
00334                 return -EBADF;
00335 
00336         /* Terminate data transfer */
00337         posix_file_finished ( file, 0 );
00338 
00339         /* Remove from list of open files and drop reference */
00340         list_del ( &file->list );
00341         ref_put ( &file->refcnt );
00342         return 0;
00343 }