Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / 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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <ipxe/list.h>
26 #include <ipxe/iobuf.h>
27 #include <ipxe/xfer.h>
28 #include <ipxe/open.h>
29 #include <ipxe/process.h>
30 #include <ipxe/posix_io.h>
31
32 /** @file
33  *
34  * POSIX-like I/O
35  *
36  * These functions provide traditional blocking I/O semantics.  They
37  * are designed to be used by the PXE TFTP API.  Because they block,
38  * they may not be used by most other portions of the iPXE codebase.
39  */
40
41 /** An open file */
42 struct posix_file {
43         /** Reference count for this object */
44         struct refcnt refcnt;
45         /** List of open files */
46         struct list_head list;
47         /** File descriptor */
48         int fd;
49         /** Overall status
50          *
51          * Set to -EINPROGRESS while data transfer is in progress.
52          */
53         int rc;
54         /** Data transfer interface */
55         struct interface xfer;
56         /** Current seek position */
57         size_t pos;
58         /** File size */
59         size_t filesize;
60         /** Received data queue */
61         struct list_head data;
62 };
63
64 /** List of open files */
65 static LIST_HEAD ( posix_files );
66
67 /**
68  * Free open file
69  *
70  * @v refcnt            Reference counter
71  */
72 static void posix_file_free ( struct refcnt *refcnt ) {
73         struct posix_file *file =
74                 container_of ( refcnt, struct posix_file, refcnt );
75         struct io_buffer *iobuf;
76         struct io_buffer *tmp;
77
78         list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
79                 list_del ( &iobuf->list );
80                 free_iob ( iobuf );
81         }
82         free ( file );
83 }
84
85 /**
86  * Terminate file data transfer
87  *
88  * @v file              POSIX file
89  * @v rc                Reason for termination
90  */
91 static void posix_file_finished ( struct posix_file *file, int rc ) {
92         intf_shutdown ( &file->xfer, rc );
93         file->rc = rc;
94 }
95
96 /**
97  * Handle deliver_iob() event
98  *
99  * @v file              POSIX file
100  * @v iobuf             I/O buffer
101  * @v meta              Data transfer metadata
102  * @ret rc              Return status code
103  */
104 static int posix_file_xfer_deliver ( struct posix_file *file,
105                                      struct io_buffer *iobuf,
106                                      struct xfer_metadata *meta ) {
107
108         /* Keep track of file position solely for the filesize */
109         if ( meta->flags & XFER_FL_ABS_OFFSET )
110                 file->pos = 0;
111         file->pos += meta->offset;
112         if ( file->filesize < file->pos )
113                 file->filesize = file->pos;
114
115         if ( iob_len ( iobuf ) ) {
116                 list_add_tail ( &iobuf->list, &file->data );
117         } else {
118                 free_iob ( iobuf );
119         }
120
121         return 0;
122 }
123
124 /** POSIX file data transfer interface operations */
125 static struct interface_operation posix_file_xfer_operations[] = {
126         INTF_OP ( xfer_deliver, struct posix_file *, posix_file_xfer_deliver ),
127         INTF_OP ( intf_close, struct posix_file *, posix_file_finished ),
128 };
129
130 /** POSIX file data transfer interface descriptor */
131 static struct interface_descriptor posix_file_xfer_desc =
132         INTF_DESC ( struct posix_file, xfer, posix_file_xfer_operations );
133
134 /**
135  * Identify file by file descriptor
136  *
137  * @v fd                File descriptor
138  * @ret file            Corresponding file, or NULL
139  */
140 static struct posix_file * posix_fd_to_file ( int fd ) {
141         struct posix_file *file;
142
143         list_for_each_entry ( file, &posix_files, list ) {
144                 if ( file->fd == fd )
145                         return file;
146         }
147         return NULL;
148 }
149
150 /**
151  * Find an available file descriptor
152  *
153  * @ret fd              File descriptor, or negative error number
154  */
155 static int posix_find_free_fd ( void ) {
156         int fd;
157
158         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
159                 if ( ! posix_fd_to_file ( fd ) )
160                         return fd;
161         }
162         DBG ( "POSIX could not find free file descriptor\n" );
163         return -ENFILE;
164 }
165
166 /**
167  * Open file
168  *
169  * @v uri_string        URI string
170  * @ret fd              File descriptor, or negative error number
171  */
172 int open ( const char *uri_string ) {
173         struct posix_file *file;
174         int fd;
175         int rc;
176
177         /* Find a free file descriptor to use */
178         fd = posix_find_free_fd();
179         if ( fd < 0 )
180                 return fd;
181
182         /* Allocate and initialise structure */
183         file = zalloc ( sizeof ( *file ) );
184         if ( ! file )
185                 return -ENOMEM;
186         ref_init ( &file->refcnt, posix_file_free );
187         file->fd = fd;
188         file->rc = -EINPROGRESS;
189         intf_init ( &file->xfer, &posix_file_xfer_desc, &file->refcnt );
190         INIT_LIST_HEAD ( &file->data );
191
192         /* Open URI on data transfer interface */
193         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
194                 goto err;
195
196         /* Wait for open to succeed or fail */
197         while ( list_empty ( &file->data ) ) {
198                 step();
199                 if ( file->rc == 0 )
200                         break;
201                 if ( file->rc != -EINPROGRESS ) {
202                         rc = file->rc;
203                         goto err;
204                 }
205         }
206
207         /* Add to list of open files.  List takes reference ownership. */
208         list_add ( &file->list, &posix_files );
209         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
210         return fd;
211
212  err:
213         posix_file_finished ( file, rc );
214         ref_put ( &file->refcnt );
215         return rc;
216 }
217
218 /**
219  * Check file descriptors for readiness
220  *
221  * @v readfds           File descriptors to check
222  * @v wait              Wait until data is ready
223  * @ret nready          Number of ready file descriptors
224  */
225 int select ( fd_set *readfds, int wait ) {
226         struct posix_file *file;
227         int fd;
228
229         do {
230                 for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
231                         if ( ! FD_ISSET ( fd, readfds ) )
232                                 continue;
233                         file = posix_fd_to_file ( fd );
234                         if ( ! file )
235                                 return -EBADF;
236                         if ( ( list_empty ( &file->data ) ) &&
237                              ( file->rc == -EINPROGRESS ) )
238                                 continue;
239                         /* Data is available or status has changed */
240                         FD_ZERO ( readfds );
241                         FD_SET ( fd, readfds );
242                         return 1;
243                 }
244                 step();
245         } while ( wait );
246
247         return 0;
248 }
249
250 /**
251  * Read data from file
252  *
253  * @v buffer            Data buffer
254  * @v offset            Starting offset within data buffer
255  * @v len               Maximum length to read
256  * @ret len             Actual length read, or negative error number
257  *
258  * This call is non-blocking; if no data is available to read then
259  * -EWOULDBLOCK will be returned.
260  */
261 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
262         struct posix_file *file;
263         struct io_buffer *iobuf;
264         size_t len;
265
266         /* Identify file */
267         file = posix_fd_to_file ( fd );
268         if ( ! file )
269                 return -EBADF;
270
271         /* Try to fetch more data if none available */
272         if ( list_empty ( &file->data ) )
273                 step();
274
275         /* Dequeue at most one received I/O buffer into user buffer */
276         list_for_each_entry ( iobuf, &file->data, list ) {
277                 len = iob_len ( iobuf );
278                 if ( len > max_len )
279                         len = max_len;
280                 copy_to_user ( buffer, offset, iobuf->data, len );
281                 iob_pull ( iobuf, len );
282                 if ( ! iob_len ( iobuf ) ) {
283                         list_del ( &iobuf->list );
284                         free_iob ( iobuf );
285                 }
286                 file->pos += len;
287                 assert ( len != 0 );
288                 return len;
289         }
290
291         /* If file has completed, return (after returning all data) */
292         if ( file->rc != -EINPROGRESS ) {
293                 assert ( list_empty ( &file->data ) );
294                 return file->rc;
295         }
296
297         /* No data ready and file still in progress; return -WOULDBLOCK */
298         return -EWOULDBLOCK;
299 }
300
301 /**
302  * Determine file size
303  *
304  * @v fd                File descriptor
305  * @ret size            File size, or negative error number
306  */
307 ssize_t fsize ( int fd ) {
308         struct posix_file *file;
309
310         /* Identify file */
311         file = posix_fd_to_file ( fd );
312         if ( ! file )
313                 return -EBADF;
314
315         return file->filesize;
316 }
317
318 /**
319  * Close file
320  *
321  * @v fd                File descriptor
322  * @ret rc              Return status code
323  */
324 int close ( int fd ) {
325         struct posix_file *file;
326
327         /* Identify file */
328         file = posix_fd_to_file ( fd );
329         if ( ! file )
330                 return -EBADF;
331
332         /* Terminate data transfer */
333         posix_file_finished ( file, 0 );
334
335         /* Remove from list of open files and drop reference */
336         list_del ( &file->list );
337         ref_put ( &file->refcnt );
338         return 0;
339 }