Applied a modified version of holger's regparm patches.
[people/adir/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  * @v meta              Data transfer metadata, or NULL
145  * @ret rc              Return status code
146  */
147 static int
148 posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
149                               struct io_buffer *iobuf,
150                               struct xfer_metadata *meta __unused ) {
151         struct posix_file *file =
152                 container_of ( xfer, struct posix_file, xfer );
153
154         list_add_tail ( &iobuf->list, &file->data );
155         return 0;
156 }
157
158 /** POSIX file data transfer interface operations */
159 static struct xfer_interface_operations posix_file_xfer_operations = {
160         .close          = posix_file_xfer_close,
161         .vredirect      = xfer_vopen,
162         .seek           = posix_file_xfer_seek,
163         .window         = unlimited_xfer_window,
164         .alloc_iob      = default_xfer_alloc_iob,
165         .deliver_iob    = posix_file_xfer_deliver_iob,
166         .deliver_raw    = xfer_deliver_as_iob,
167 };
168
169 /**
170  * Identify file by file descriptor
171  *
172  * @v fd                File descriptor
173  * @ret file            Corresponding file, or NULL
174  */
175 static struct posix_file * posix_fd_to_file ( int fd ) {
176         struct posix_file *file;
177
178         list_for_each_entry ( file, &posix_files, list ) {
179                 if ( file->fd == fd )
180                         return file;
181         }
182         return NULL;
183 }
184
185 /**
186  * Find an available file descriptor
187  *
188  * @ret fd              File descriptor, or negative error number
189  */
190 static int posix_find_free_fd ( void ) {
191         int fd;
192
193         for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
194                 if ( ! posix_fd_to_file ( fd ) )
195                         return fd;
196         }
197         DBG ( "POSIX could not find free file descriptor\n" );
198         return -ENFILE;
199 }
200
201 /**
202  * Open file
203  *
204  * @v uri_string        URI string
205  * @ret fd              File descriptor, or negative error number
206  */
207 int open ( const char *uri_string ) {
208         struct posix_file *file;
209         int fd;
210         int rc;
211
212         /* Find a free file descriptor to use */
213         fd = posix_find_free_fd();
214         if ( fd < 0 )
215                 return fd;
216
217         /* Allocate and initialise structure */
218         file = zalloc ( sizeof ( *file ) );
219         if ( ! file )
220                 return -ENOMEM;
221         file->refcnt.free = posix_file_free;
222         file->fd = fd;
223         file->rc = -EINPROGRESS;
224         xfer_init ( &file->xfer, &posix_file_xfer_operations,
225                     &file->refcnt );
226         INIT_LIST_HEAD ( &file->data );
227
228         /* Open URI on data transfer interface */
229         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
230                 goto err;
231
232         /* Wait for open to succeed or fail */
233         while ( list_empty ( &file->data ) ) {
234                 step();
235                 if ( file->rc == 0 )
236                         break;
237                 if ( file->rc != -EINPROGRESS ) {
238                         rc = file->rc;
239                         goto err;
240                 }
241         }
242
243         /* Add to list of open files.  List takes reference ownership. */
244         list_add ( &file->list, &posix_files );
245         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
246         return fd;
247
248  err:
249         posix_file_finished ( file, rc );
250         ref_put ( &file->refcnt );
251         return rc;
252 }
253
254 /**
255  * Read data from file
256  *
257  * @v buffer            Data buffer
258  * @v offset            Starting offset within data buffer
259  * @v len               Maximum length to read
260  * @ret len             Actual length read, or negative error number
261  */
262 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
263         struct posix_file *file;
264         struct io_buffer *iobuf;
265         size_t frag_len;
266         ssize_t len = 0;
267
268         /* Identify file */
269         file = posix_fd_to_file ( fd );
270         if ( ! file )
271                 return -EBADF;
272
273         while ( 1 ) {
274                 /* Try to fetch more data if none available */
275                 if ( list_empty ( &file->data ) )
276                         step();
277                 /* Dequeue at most one received I/O buffer into user buffer */
278                 list_for_each_entry ( iobuf, &file->data, list ) {
279                         frag_len = iob_len ( iobuf );
280                         if ( frag_len > max_len )
281                                 frag_len = max_len;
282                         copy_to_user ( buffer, offset, iobuf->data,
283                                        frag_len );
284                         iob_pull ( iobuf, frag_len );
285                         if ( ! iob_len ( iobuf ) ) {
286                                 list_del ( &iobuf-> list );
287                                 free_iob ( iobuf );
288                         }
289                         file->pos += frag_len;
290                         len += frag_len;
291                         offset += frag_len;
292                         max_len -= frag_len;
293                         break;
294                 }
295                 /* If buffer is full, return */
296                 if ( ! max_len )
297                         return len;
298                 /* If file has completed, return */
299                 if ( file->rc != -EINPROGRESS )
300                         return ( file->rc ? file->rc : len );
301         }
302 }
303
304 /**
305  * Determine file size
306  *
307  * @v fd                File descriptor
308  * @ret size            File size, or negative error number
309  */
310 ssize_t fsize ( int fd ) {
311         struct posix_file *file;
312
313         /* Identify file */
314         file = posix_fd_to_file ( fd );
315         if ( ! file )
316                 return -EBADF;
317
318         return file->filesize;
319 }
320
321 /**
322  * Close file
323  *
324  * @v fd                File descriptor
325  * @ret rc              Return status code
326  */
327 int close ( int fd ) {
328         struct posix_file *file;
329
330         /* Identify file */
331         file = posix_fd_to_file ( fd );
332         if ( ! file )
333                 return -EBADF;
334
335         /* Terminate data transfer */
336         posix_file_finished ( file, 0 );
337
338         /* Remove from list of open files and drop reference */
339         list_del ( &file->list );
340         ref_put ( &file->refcnt );
341         return 0;
342 }