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., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <errno.h>
00024 #include <gpxe/list.h>
00025 #include <gpxe/xfer.h>
00026 #include <gpxe/open.h>
00027 #include <gpxe/process.h>
00028 #include <gpxe/posix_io.h>
00029 
00030 /** @file
00031  *
00032  * POSIX-like I/O
00033  *
00034  * These functions provide traditional blocking I/O semantics.  They
00035  * are designed to be used by the PXE TFTP API.  Because they block,
00036  * they may not be used by most other portions of the gPXE codebase.
00037  */
00038 
00039 /** An open file */
00040 struct posix_file {
00041         /** Reference count for this object */
00042         struct refcnt refcnt;
00043         /** List of open files */
00044         struct list_head list;
00045         /** File descriptor */
00046         int fd;
00047         /** Overall status
00048          *
00049          * Set to -EINPROGRESS while data transfer is in progress.
00050          */
00051         int rc;
00052         /** Data transfer interface */
00053         struct xfer_interface xfer;
00054         /** Current seek position */
00055         size_t pos;
00056         /** File size */
00057         size_t filesize;
00058         /** Received data queue */
00059         struct list_head data;
00060 };
00061 
00062 /** List of open files */
00063 static LIST_HEAD ( posix_files );
00064 
00065 /**
00066  * Free open file
00067  *
00068  * @v refcnt            Reference counter
00069  */
00070 static void posix_file_free ( struct refcnt *refcnt ) {
00071         struct posix_file *file =
00072                 container_of ( refcnt, struct posix_file, refcnt );
00073         struct io_buffer *iobuf;
00074         struct io_buffer *tmp;
00075 
00076         list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
00077                 list_del ( &iobuf->list );
00078                 free_iob ( iobuf );
00079         }
00080         free ( file );
00081 }
00082 
00083 /**
00084  * Terminate file data transfer
00085  *
00086  * @v file              POSIX file
00087  * @v rc                Reason for termination
00088  */
00089 static void posix_file_finished ( struct posix_file *file, int rc ) {
00090         xfer_nullify ( &file->xfer );
00091         xfer_close ( &file->xfer, rc );
00092         file->rc = rc;
00093 }
00094 
00095 /**
00096  * Handle close() event
00097  *
00098  * @v xfer              POSIX file data transfer interface
00099  * @v rc                Reason for close
00100  */
00101 static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) {
00102         struct posix_file *file =
00103                 container_of ( xfer, struct posix_file, xfer );
00104 
00105         posix_file_finished ( file, rc );
00106 }
00107 
00108 /**
00109  * Handle deliver_iob() event
00110  *
00111  * @v xfer              POSIX file data transfer interface
00112  * @v iobuf             I/O buffer
00113  * @v meta              Data transfer metadata
00114  * @ret rc              Return status code
00115  */
00116 static int
00117 posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
00118                               struct io_buffer *iobuf,
00119                               struct xfer_metadata *meta ) {
00120         struct posix_file *file =
00121                 container_of ( xfer, struct posix_file, xfer );
00122 
00123         /* Keep track of file position solely for the filesize */
00124         if ( meta->whence != SEEK_CUR )
00125                 file->pos = 0;
00126         file->pos += meta->offset;
00127         if ( file->filesize < file->pos )
00128                 file->filesize = file->pos;
00129 
00130         if ( iob_len ( iobuf ) ) {
00131                 list_add_tail ( &iobuf->list, &file->data );
00132         } else {
00133                 free_iob ( iobuf );
00134         }
00135 
00136         return 0;
00137 }
00138 
00139 /** POSIX file data transfer interface operations */
00140 static struct xfer_interface_operations posix_file_xfer_operations = {
00141         .close          = posix_file_xfer_close,
00142         .vredirect      = xfer_vreopen,
00143         .window         = unlimited_xfer_window,
00144         .alloc_iob      = default_xfer_alloc_iob,
00145         .deliver_iob    = posix_file_xfer_deliver_iob,
00146         .deliver_raw    = xfer_deliver_as_iob,
00147 };
00148 
00149 /**
00150  * Identify file by file descriptor
00151  *
00152  * @v fd                File descriptor
00153  * @ret file            Corresponding file, or NULL
00154  */
00155 static struct posix_file * posix_fd_to_file ( int fd ) {
00156         struct posix_file *file;
00157 
00158         list_for_each_entry ( file, &posix_files, list ) {
00159                 if ( file->fd == fd )
00160                         return file;
00161         }
00162         return NULL;
00163 }
00164 
00165 /**
00166  * Find an available file descriptor
00167  *
00168  * @ret fd              File descriptor, or negative error number
00169  */
00170 static int posix_find_free_fd ( void ) {
00171         int fd;
00172 
00173         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
00174                 if ( ! posix_fd_to_file ( fd ) )
00175                         return fd;
00176         }
00177         DBG ( "POSIX could not find free file descriptor\n" );
00178         return -ENFILE;
00179 }
00180 
00181 /**
00182  * Open file
00183  *
00184  * @v uri_string        URI string
00185  * @ret fd              File descriptor, or negative error number
00186  */
00187 int open ( const char *uri_string ) {
00188         struct posix_file *file;
00189         int fd;
00190         int rc;
00191 
00192         /* Find a free file descriptor to use */
00193         fd = posix_find_free_fd();
00194         if ( fd < 0 )
00195                 return fd;
00196 
00197         /* Allocate and initialise structure */
00198         file = zalloc ( sizeof ( *file ) );
00199         if ( ! file )
00200                 return -ENOMEM;
00201         file->refcnt.free = posix_file_free;
00202         file->fd = fd;
00203         file->rc = -EINPROGRESS;
00204         xfer_init ( &file->xfer, &posix_file_xfer_operations,
00205                     &file->refcnt );
00206         INIT_LIST_HEAD ( &file->data );
00207 
00208         /* Open URI on data transfer interface */
00209         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
00210                 goto err;
00211 
00212         /* Wait for open to succeed or fail */
00213         while ( list_empty ( &file->data ) ) {
00214                 step();
00215                 if ( file->rc == 0 )
00216                         break;
00217                 if ( file->rc != -EINPROGRESS ) {
00218                         rc = file->rc;
00219                         goto err;
00220                 }
00221         }
00222 
00223         /* Add to list of open files.  List takes reference ownership. */
00224         list_add ( &file->list, &posix_files );
00225         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
00226         return fd;
00227 
00228  err:
00229         posix_file_finished ( file, rc );
00230         ref_put ( &file->refcnt );
00231         return rc;
00232 }
00233 
00234 /**
00235  * Check file descriptors for readiness
00236  *
00237  * @v readfds           File descriptors to check
00238  * @v wait              Wait until data is ready
00239  * @ret nready          Number of ready file descriptors
00240  */
00241 int select ( fd_set *readfds, int wait ) {
00242         struct posix_file *file;
00243         int fd;
00244 
00245         do {
00246                 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
00247                         if ( ! FD_ISSET ( fd, readfds ) )
00248                                 continue;
00249                         file = posix_fd_to_file ( fd );
00250                         if ( ! file )
00251                                 return -EBADF;
00252                         if ( ( list_empty ( &file->data ) ) &&
00253                              ( file->rc == -EINPROGRESS ) )
00254                                 continue;
00255                         /* Data is available or status has changed */
00256                         FD_ZERO ( readfds );
00257                         FD_SET ( fd, readfds );
00258                         return 1;
00259                 }
00260                 step();
00261         } while ( wait );
00262 
00263         return 0;
00264 }
00265 
00266 /**
00267  * Read data from file
00268  *
00269  * @v buffer            Data buffer
00270  * @v offset            Starting offset within data buffer
00271  * @v len               Maximum length to read
00272  * @ret len             Actual length read, or negative error number
00273  *
00274  * This call is non-blocking; if no data is available to read then
00275  * -EWOULDBLOCK will be returned.
00276  */
00277 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
00278         struct posix_file *file;
00279         struct io_buffer *iobuf;
00280         size_t len;
00281 
00282         /* Identify file */
00283         file = posix_fd_to_file ( fd );
00284         if ( ! file )
00285                 return -EBADF;
00286 
00287         /* Try to fetch more data if none available */
00288         if ( list_empty ( &file->data ) )
00289                 step();
00290 
00291         /* Dequeue at most one received I/O buffer into user buffer */
00292         list_for_each_entry ( iobuf, &file->data, list ) {
00293                 len = iob_len ( iobuf );
00294                 if ( len > max_len )
00295                         len = max_len;
00296                 copy_to_user ( buffer, offset, iobuf->data, len );
00297                 iob_pull ( iobuf, len );
00298                 if ( ! iob_len ( iobuf ) ) {
00299                         list_del ( &iobuf->list );
00300                         free_iob ( iobuf );
00301                 }
00302                 file->pos += len;
00303                 assert ( len != 0 );
00304                 return len;
00305         }
00306 
00307         /* If file has completed, return (after returning all data) */
00308         if ( file->rc != -EINPROGRESS ) {
00309                 assert ( list_empty ( &file->data ) );
00310                 return file->rc;
00311         }
00312 
00313         /* No data ready and file still in progress; return -WOULDBLOCK */
00314         return -EWOULDBLOCK;
00315 }
00316 
00317 /**
00318  * Determine file size
00319  *
00320  * @v fd                File descriptor
00321  * @ret size            File size, or negative error number
00322  */
00323 ssize_t fsize ( int fd ) {
00324         struct posix_file *file;
00325 
00326         /* Identify file */
00327         file = posix_fd_to_file ( fd );
00328         if ( ! file )
00329                 return -EBADF;
00330 
00331         return file->filesize;
00332 }
00333 
00334 /**
00335  * Close file
00336  *
00337  * @v fd                File descriptor
00338  * @ret rc              Return status code
00339  */
00340 int close ( int fd ) {
00341         struct posix_file *file;
00342 
00343         /* Identify file */
00344         file = posix_fd_to_file ( fd );
00345         if ( ! file )
00346                 return -EBADF;
00347 
00348         /* Terminate data transfer */
00349         posix_file_finished ( file, 0 );
00350 
00351         /* Remove from list of open files and drop reference */
00352         list_del ( &file->list );
00353         ref_put ( &file->refcnt );
00354         return 0;
00355 }

Generated on Tue Apr 6 20:00:51 2010 for gPXE by  doxygen 1.5.7.1