Free I/O buffers when we are finished with them!
[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 /** Minimum file descriptor that will ever be allocated */
64 #define POSIX_FD_MIN ( 1 )
65
66 /** Maximum file descriptor that will ever be allocated */
67 #define POSIX_FD_MAX ( 255 )
68
69 /**
70  * Free open file
71  *
72  * @v refcnt            Reference counter
73  */
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;
79
80         list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
81                 list_del ( &iobuf->list );
82                 free_iob ( iobuf );
83         }
84         free ( file );
85 }
86
87 /**
88  * Terminate file data transfer
89  *
90  * @v file              POSIX file
91  * @v rc                Reason for termination
92  */
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;
97 }
98
99 /**
100  * Handle close() event
101  *
102  * @v xfer              POSIX file data transfer interface
103  * @v rc                Reason for close
104  */
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 );
108
109         posix_file_finished ( file, rc );
110 }
111
112 /**
113  * Handle seek() event
114  *
115  * @v xfer              POSIX file data transfer interface
116  * @v pos               New position
117  * @ret rc              Return status code
118  */
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 );
123
124         switch ( whence ) {
125         case SEEK_SET:
126                 file->pos = offset;
127                 break;
128         case SEEK_CUR:
129                 file->pos += offset;
130                 break;
131         }
132
133         if ( file->filesize < file->pos )
134                 file->filesize = file->pos;
135
136         return 0;
137 }
138
139 /**
140  * Handle deliver_iob() event
141  *
142  * @v xfer              POSIX file data transfer interface
143  * @v iobuf             I/O buffer
144  * @ret rc              Return status code
145  */
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 );
150
151         list_add_tail ( &iobuf->list, &file->data );
152         return 0;
153 }
154
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,
164 };
165
166 /**
167  * Identify file by file descriptor
168  *
169  * @v fd                File descriptor
170  * @ret file            Corresponding file, or NULL
171  */
172 static struct posix_file * posix_fd_to_file ( int fd ) {
173         struct posix_file *file;
174
175         list_for_each_entry ( file, &posix_files, list ) {
176                 if ( file->fd == fd )
177                         return file;
178         }
179         return NULL;
180 }
181
182 /**
183  * Find an available file descriptor
184  *
185  * @ret fd              File descriptor, or negative error number
186  */
187 static int posix_find_free_fd ( void ) {
188         int fd;
189
190         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
191                 if ( ! posix_fd_to_file ( fd ) )
192                         return fd;
193         }
194         return -ENFILE;
195 }
196
197 /**
198  * Open file
199  *
200  * @v uri_string        URI string
201  * @ret fd              File descriptor, or negative error number
202  */
203 int open ( const char *uri_string ) {
204         struct posix_file *file;
205         int fd;
206         int rc;
207
208         /* Find a free file descriptor to use */
209         fd = posix_find_free_fd();
210         if ( fd < 0 )
211                 return fd;
212
213         /* Allocate and initialise structure */
214         file = malloc ( sizeof ( *file ) );
215         if ( ! file )
216                 return -ENOMEM;
217         memset ( file, 0, sizeof ( *file ) );
218         file->refcnt.free = posix_file_free;
219         file->fd = fd;
220         file->rc = -EINPROGRESS;
221         xfer_init ( &file->xfer, &posix_file_xfer_operations,
222                     &file->refcnt );
223         INIT_LIST_HEAD ( &file->data );
224
225         /* Open URI on data transfer interface */
226         if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 )
227                 goto err;
228
229         /* Request data */
230         if ( ( rc = xfer_request_all ( &file->xfer ) ) != 0 )
231                 goto err;
232
233         /* Wait for open to succeed or fail */
234         while ( list_empty ( &file->data ) ) {
235                 step();
236                 if ( file->rc != -EINPROGRESS ) {
237                         rc = file->rc;
238                         goto err;
239                 }
240         }
241
242         /* Add to list of open files.  List takes reference ownership. */
243         list_add ( &file->list, &posix_files );
244         return fd;
245
246  err:
247         posix_file_finished ( file, rc );
248         ref_put ( &file->refcnt );
249         return rc;
250 }
251
252 /**
253  * Read data from file
254  *
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
259  */
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;
265
266         /* Identify file */
267         file = posix_fd_to_file ( fd );
268         if ( ! file )
269                 return -EBADF;
270
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 );
286                         }
287                         file->pos += frag_len;
288                         len += frag_len;
289                         offset += frag_len;
290                         max_len -= frag_len;
291                         break;
292                 }
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 );
299         }
300 }
301
302 /**
303  * Determine file size
304  *
305  * @v fd                File descriptor
306  * @ret size            File size, or negative error number
307  */
308 ssize_t fsize ( int fd ) {
309         struct posix_file *file;
310
311         /* Identify file */
312         file = posix_fd_to_file ( fd );
313         if ( ! file )
314                 return -EBADF;
315
316         return file->filesize;
317 }
318
319 /**
320  * Close file
321  *
322  * @v fd                File descriptor
323  * @ret rc              Return status code
324  */
325 int close ( int fd ) {
326         struct posix_file *file;
327
328         /* Identify file */
329         file = posix_fd_to_file ( fd );
330         if ( ! file )
331                 return -EBADF;
332
333         /* Terminate data transfer */
334         posix_file_finished ( file, 0 );
335
336         /* Remove from list of open files and drop reference */
337         list_del ( &file->list );
338         ref_put ( &file->refcnt );
339         return 0;
340 }