Updated email mdc's email address
[gpxe.git] / src / core / posix_io.c
blob3b5660e4f9b0361fde7045980728039f64454a57
1 /*
2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <gpxe/list.h>
23 #include <gpxe/xfer.h>
24 #include <gpxe/open.h>
25 #include <gpxe/process.h>
26 #include <gpxe/posix_io.h>
28 /** @file
30 * POSIX-like I/O
32 * These functions provide traditional blocking I/O semantics. They
33 * are designed to be used by the PXE TFTP API. Because they block,
34 * they may not be used by most other portions of the gPXE codebase.
37 /** An open file */
38 struct posix_file {
39 /** Reference count for this object */
40 struct refcnt refcnt;
41 /** List of open files */
42 struct list_head list;
43 /** File descriptor */
44 int fd;
45 /** Overall status
47 * Set to -EINPROGRESS while data transfer is in progress.
49 int rc;
50 /** Data transfer interface */
51 struct xfer_interface xfer;
52 /** Current seek position */
53 size_t pos;
54 /** File size */
55 size_t filesize;
56 /** Received data queue */
57 struct list_head data;
60 /** List of open files */
61 static LIST_HEAD ( posix_files );
63 /** Minimum file descriptor that will ever be allocated */
64 #define POSIX_FD_MIN ( 1 )
66 /** Maximum file descriptor that will ever be allocated */
67 #define POSIX_FD_MAX ( 255 )
69 /**
70 * Free open file
72 * @v refcnt Reference counter
74 static void posix_file_free ( struct refcnt *refcnt ) {
75 struct posix_file *file =
76 container_of ( refcnt, struct posix_file, refcnt );
77 struct io_buffer *iobuf;
78 struct io_buffer *tmp;
80 list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
81 list_del ( &iobuf->list );
82 free_iob ( iobuf );
84 free ( file );
87 /**
88 * Terminate file data transfer
90 * @v file POSIX file
91 * @v rc Reason for termination
93 static void posix_file_finished ( struct posix_file *file, int rc ) {
94 xfer_nullify ( &file->xfer );
95 xfer_close ( &file->xfer, rc );
96 file->rc = rc;
99 /**
100 * Handle close() event
102 * @v xfer POSIX file data transfer interface
103 * @v rc Reason for close
105 static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) {
106 struct posix_file *file =
107 container_of ( xfer, struct posix_file, xfer );
109 posix_file_finished ( file, rc );
113 * Handle seek() event
115 * @v xfer POSIX file data transfer interface
116 * @v pos New position
117 * @ret rc Return status code
119 static int posix_file_xfer_seek ( struct xfer_interface *xfer, off_t offset,
120 int whence ) {
121 struct posix_file *file =
122 container_of ( xfer, struct posix_file, xfer );
124 switch ( whence ) {
125 case SEEK_SET:
126 file->pos = offset;
127 break;
128 case SEEK_CUR:
129 file->pos += offset;
130 break;
133 if ( file->filesize < file->pos )
134 file->filesize = file->pos;
136 return 0;
140 * Handle deliver_iob() event
142 * @v xfer POSIX file data transfer interface
143 * @v iobuf I/O buffer
144 * @ret rc Return status code
146 static int posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
147 struct io_buffer *iobuf ) {
148 struct posix_file *file =
149 container_of ( xfer, struct posix_file, xfer );
151 list_add_tail ( &iobuf->list, &file->data );
152 return 0;
155 /** POSIX file data transfer interface operations */
156 static struct xfer_interface_operations posix_file_xfer_operations = {
157 .close = posix_file_xfer_close,
158 .vredirect = xfer_vopen,
159 .request = ignore_xfer_request,
160 .seek = posix_file_xfer_seek,
161 .alloc_iob = default_xfer_alloc_iob,
162 .deliver_iob = posix_file_xfer_deliver_iob,
163 .deliver_raw = xfer_deliver_as_iob,
167 * Identify file by file descriptor
169 * @v fd File descriptor
170 * @ret file Corresponding file, or NULL
172 static struct posix_file * posix_fd_to_file ( int fd ) {
173 struct posix_file *file;
175 list_for_each_entry ( file, &posix_files, list ) {
176 if ( file->fd == fd )
177 return file;
179 return NULL;
183 * Find an available file descriptor
185 * @ret fd File descriptor, or negative error number
187 static int posix_find_free_fd ( void ) {
188 int fd;
190 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
191 if ( ! posix_fd_to_file ( fd ) )
192 return fd;
194 DBG ( "POSIX could not find free file descriptor\n" );
195 return -ENFILE;
199 * Open file
201 * @v uri_string URI string
202 * @ret fd File descriptor, or negative error number
204 int open ( const char *uri_string ) {
205 struct posix_file *file;
206 int fd;
207 int rc;
209 /* Find a free file descriptor to use */
210 fd = posix_find_free_fd();
211 if ( fd < 0 )
212 return fd;
214 /* Allocate and initialise structure */
215 file = malloc ( sizeof ( *file ) );
216 if ( ! file )
217 return -ENOMEM;
218 memset ( file, 0, sizeof ( *file ) );
219 file->refcnt.free = posix_file_free;
220 file->fd = fd;
221 file->rc = -EINPROGRESS;
222 xfer_init ( &file->xfer, &posix_file_xfer_operations,
223 &file->refcnt );
224 INIT_LIST_HEAD ( &file->data );
226 /* Open URI on data transfer interface */
227 if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 )
228 goto err;
230 /* Wait for open to succeed or fail */
231 while ( list_empty ( &file->data ) ) {
232 step();
233 if ( file->rc == 0 )
234 break;
235 if ( file->rc != -EINPROGRESS ) {
236 rc = file->rc;
237 goto err;
241 /* Add to list of open files. List takes reference ownership. */
242 list_add ( &file->list, &posix_files );
243 DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
244 return fd;
246 err:
247 posix_file_finished ( file, rc );
248 ref_put ( &file->refcnt );
249 return rc;
253 * Read data from file
255 * @v buffer Data buffer
256 * @v offset Starting offset within data buffer
257 * @v len Maximum length to read
258 * @ret len Actual length read, or negative error number
260 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
261 struct posix_file *file;
262 struct io_buffer *iobuf;
263 size_t frag_len;
264 ssize_t len = 0;
266 /* Identify file */
267 file = posix_fd_to_file ( fd );
268 if ( ! file )
269 return -EBADF;
271 while ( 1 ) {
272 /* Try to fetch more data if none available */
273 if ( list_empty ( &file->data ) )
274 step();
275 /* Dequeue at most one received I/O buffer into user buffer */
276 list_for_each_entry ( iobuf, &file->data, list ) {
277 frag_len = iob_len ( iobuf );
278 if ( frag_len > max_len )
279 frag_len = max_len;
280 copy_to_user ( buffer, offset, iobuf->data,
281 frag_len );
282 iob_pull ( iobuf, frag_len );
283 if ( ! iob_len ( iobuf ) ) {
284 list_del ( &iobuf-> list );
285 free_iob ( iobuf );
287 file->pos += frag_len;
288 len += frag_len;
289 offset += frag_len;
290 max_len -= frag_len;
291 break;
293 /* If buffer is full, return */
294 if ( ! max_len )
295 return len;
296 /* If file has completed, return */
297 if ( file->rc != -EINPROGRESS )
298 return ( file->rc ? file->rc : len );
303 * Determine file size
305 * @v fd File descriptor
306 * @ret size File size, or negative error number
308 ssize_t fsize ( int fd ) {
309 struct posix_file *file;
311 /* Identify file */
312 file = posix_fd_to_file ( fd );
313 if ( ! file )
314 return -EBADF;
316 return file->filesize;
320 * Close file
322 * @v fd File descriptor
323 * @ret rc Return status code
325 int close ( int fd ) {
326 struct posix_file *file;
328 /* Identify file */
329 file = posix_fd_to_file ( fd );
330 if ( ! file )
331 return -EBADF;
333 /* Terminate data transfer */
334 posix_file_finished ( file, 0 );
336 /* Remove from list of open files and drop reference */
337 list_del ( &file->list );
338 ref_put ( &file->refcnt );
339 return 0;