Home | History | Annotate | Download | only in core
      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