530ce6511dbbde7db4fe409383e342367dc85616
[people/mcb30/gpxe.git] / src / core / posix_io.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@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 #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>
27
28 /** @file
29  *
30  * POSIX-like I/O
31  *
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.
35  */
36
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
46          *
47          * Set to -EINPROGRESS while data transfer is in progress.
48          */
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;
58 };
59
60 /** List of open files */
61 static LIST_HEAD ( posix_files );
62
63 /**
64  * Free open file
65  *
66  * @v refcnt            Reference counter
67  */
68 static void posix_file_free ( struct refcnt *refcnt ) {
69         struct posix_file *file =
70                 container_of ( refcnt, struct posix_file, refcnt );
71         struct io_buffer *iobuf;
72         struct io_buffer *tmp;
73
74         list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
75                 list_del ( &iobuf->list );
76                 free_iob ( iobuf );
77         }
78         free ( file );
79 }
80
81 /**
82  * Terminate file data transfer
83  *
84  * @v file              POSIX file
85  * @v rc                Reason for termination
86  */
87 static void posix_file_finished ( struct posix_file *file, int rc ) {
88         xfer_nullify ( &file->xfer );
89         xfer_close ( &file->xfer, rc );
90         file->rc = rc;
91 }
92
93 /**
94  * Handle close() event
95  *
96  * @v xfer              POSIX file data transfer interface
97  * @v rc                Reason for close
98  */
99 static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) {
100         struct posix_file *file =
101                 container_of ( xfer, struct posix_file, xfer );
102
103         posix_file_finished ( file, rc );
104 }
105
106 /**
107  * Handle seek() event
108  *
109  * @v xfer              POSIX file data transfer interface
110  * @v pos               New position
111  * @ret rc              Return status code
112  */
113 static int posix_file_xfer_seek ( struct xfer_interface *xfer, off_t offset,
114                                   int whence ) {
115         struct posix_file *file =
116                 container_of ( xfer, struct posix_file, xfer );
117
118         switch ( whence ) {
119         case SEEK_SET:
120                 file->pos = offset;
121                 break;
122         case SEEK_CUR:
123                 file->pos += offset;
124                 break;
125         }
126
127         if ( file->filesize < file->pos )
128                 file->filesize = file->pos;
129
130         return 0;
131 }
132
133 /**
134  * Handle deliver_iob() event
135  *
136  * @v xfer              POSIX file data transfer interface
137  * @v iobuf             I/O buffer
138  * @v meta              Data transfer metadata, or NULL
139  * @ret rc              Return status code
140  */
141 static int
142 posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
143                               struct io_buffer *iobuf,
144                               struct xfer_metadata *meta __unused ) {
145         struct posix_file *file =
146                 container_of ( xfer, struct posix_file, xfer );
147
148         list_add_tail ( &iobuf->list, &file->data );
149         return 0;
150 }
151
152 /** POSIX file data transfer interface operations */
153 static struct xfer_interface_operations posix_file_xfer_operations = {
154         .close          = posix_file_xfer_close,
155         .vredirect      = xfer_vopen,
156         .seek           = posix_file_xfer_seek,
157         .window         = unlimited_xfer_window,
158         .alloc_iob      = default_xfer_alloc_iob,
159         .deliver_iob    = posix_file_xfer_deliver_iob,
160         .deliver_raw    = xfer_deliver_as_iob,
161 };
162
163 /**
164  * Identify file by file descriptor
165  *
166  * @v fd                File descriptor
167  * @ret file            Corresponding file, or NULL
168  */
169 static struct posix_file * posix_fd_to_file ( int fd ) {
170         struct posix_file *file;
171
172         list_for_each_entry ( file, &posix_files, list ) {
173                 if ( file->fd == fd )
174                         return file;
175         }
176         return NULL;
177 }
178
179 /**
180  * Find an available file descriptor
181  *
182  * @ret fd              File descriptor, or negative error number
183  */
184 static int posix_find_free_fd ( void ) {
185         int fd;
186
187         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
188                 if ( ! posix_fd_to_file ( fd ) )
189                         return fd;
190         }
191         DBG ( "POSIX could not find free file descriptor\n" );
192         return -ENFILE;
193 }
194
195 /**
196  * Open file
197  *
198  * @v uri_string        URI string
199  * @ret fd              File descriptor, or negative error number
200  */
201 int open ( const char *uri_string ) {
202         struct posix_file *file;
203         int fd;
204         int rc;
205
206         /* Find a free file descriptor to use */
207         fd = posix_find_free_fd();
208         if ( fd < 0 )
209                 return fd;
210
211         /* Allocate and initialise structure */
212         file = zalloc ( sizeof ( *file ) );
213         if ( ! file )
214                 return -ENOMEM;
215         file->refcnt.free = posix_file_free;
216         file->fd = fd;
217         file->rc = -EINPROGRESS;
218         xfer_init ( &file->xfer, &posix_file_xfer_operations,
219                     &file->refcnt );
220         INIT_LIST_HEAD ( &file->data );
221
222         /* Open URI on data transfer interface */
223         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
224                 goto err;
225
226         /* Wait for open to succeed or fail */
227         while ( list_empty ( &file->data ) ) {
228                 step();
229                 if ( file->rc == 0 )
230                         break;
231                 if ( file->rc != -EINPROGRESS ) {
232                         rc = file->rc;
233                         goto err;
234                 }
235         }
236
237         /* Add to list of open files.  List takes reference ownership. */
238         list_add ( &file->list, &posix_files );
239         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
240         return fd;
241
242  err:
243         posix_file_finished ( file, rc );
244         ref_put ( &file->refcnt );
245         return rc;
246 }
247
248 /**
249  * Check file descriptors for readiness
250  *
251  * @v readfds           File descriptors to check
252  * @v wait              Wait until data is ready
253  * @ret nready          Number of ready file descriptors
254  */
255 int select ( fd_set *readfds, int wait ) {
256         struct posix_file *file;
257         int fd;
258
259         do {
260                 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
261                         if ( ! FD_ISSET ( fd, readfds ) )
262                                 continue;
263                         file = posix_fd_to_file ( fd );
264                         if ( ! file )
265                                 return -EBADF;
266                         if ( ( list_empty ( &file->data ) ) &&
267                              ( file->rc != -EINPROGRESS ) )
268                                 continue;
269                         /* Data is available or status has changed */
270                         FD_ZERO ( readfds );
271                         FD_SET ( fd, readfds );
272                         return 1;
273                 }
274                 step();
275         } while ( wait );
276
277         return 0;
278 }
279
280 /**
281  * Read data from file
282  *
283  * @v buffer            Data buffer
284  * @v offset            Starting offset within data buffer
285  * @v len               Maximum length to read
286  * @ret len             Actual length read, or negative error number
287  *
288  * This call is non-blocking; if no data is available to read then
289  * -EWOULDBLOCK will be returned.
290  */
291 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
292         struct posix_file *file;
293         struct io_buffer *iobuf;
294         size_t len;
295
296         /* Identify file */
297         file = posix_fd_to_file ( fd );
298         if ( ! file )
299                 return -EBADF;
300
301         /* Try to fetch more data if none available */
302         if ( list_empty ( &file->data ) )
303                 step();
304
305         /* Dequeue at most one received I/O buffer into user buffer */
306         list_for_each_entry ( iobuf, &file->data, list ) {
307                 len = iob_len ( iobuf );
308                 if ( len > max_len )
309                         len = max_len;
310                 copy_to_user ( buffer, offset, iobuf->data, len );
311                 iob_pull ( iobuf, len );
312                 if ( ! iob_len ( iobuf ) ) {
313                         list_del ( &iobuf->list );
314                         free_iob ( iobuf );
315                 }
316                 file->pos += len;
317                 return len;
318         }
319
320         /* If file has completed, return (after returning all data) */
321         if ( file->rc != -EINPROGRESS )
322                 return file->rc;
323
324         /* No data ready and file still in progress; return -WOULDBLOCK */
325         return -EWOULDBLOCK;
326 }
327
328 /**
329  * Determine file size
330  *
331  * @v fd                File descriptor
332  * @ret size            File size, or negative error number
333  */
334 ssize_t fsize ( int fd ) {
335         struct posix_file *file;
336
337         /* Identify file */
338         file = posix_fd_to_file ( fd );
339         if ( ! file )
340                 return -EBADF;
341
342         return file->filesize;
343 }
344
345 /**
346  * Close file
347  *
348  * @v fd                File descriptor
349  * @ret rc              Return status code
350  */
351 int close ( int fd ) {
352         struct posix_file *file;
353
354         /* Identify file */
355         file = posix_fd_to_file ( fd );
356         if ( ! file )
357                 return -EBADF;
358
359         /* Terminate data transfer */
360         posix_file_finished ( file, 0 );
361
362         /* Remove from list of open files and drop reference */
363         list_del ( &file->list );
364         ref_put ( &file->refcnt );
365         return 0;
366 }