Add concept of transfer metadata, to be used by UDP in order to
[people/holger/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         .request        = ignore_xfer_request,
163         .seek           = posix_file_xfer_seek,
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 = malloc ( sizeof ( *file ) );
219         if ( ! file )
220                 return -ENOMEM;
221         memset ( file, 0, sizeof ( *file ) );
222         file->refcnt.free = posix_file_free;
223         file->fd = fd;
224         file->rc = -EINPROGRESS;
225         xfer_init ( &file->xfer, &posix_file_xfer_operations,
226                     &file->refcnt );
227         INIT_LIST_HEAD ( &file->data );
228
229         /* Open URI on data transfer interface */
230         if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 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 == 0 )
237                         break;
238                 if ( file->rc != -EINPROGRESS ) {
239                         rc = file->rc;
240                         goto err;
241                 }
242         }
243
244         /* Add to list of open files.  List takes reference ownership. */
245         list_add ( &file->list, &posix_files );
246         DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
247         return fd;
248
249  err:
250         posix_file_finished ( file, rc );
251         ref_put ( &file->refcnt );
252         return rc;
253 }
254
255 /**
256  * Read data from file
257  *
258  * @v buffer            Data buffer
259  * @v offset            Starting offset within data buffer
260  * @v len               Maximum length to read
261  * @ret len             Actual length read, or negative error number
262  */
263 ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
264         struct posix_file *file;
265         struct io_buffer *iobuf;
266         size_t frag_len;
267         ssize_t len = 0;
268
269         /* Identify file */
270         file = posix_fd_to_file ( fd );
271         if ( ! file )
272                 return -EBADF;
273
274         while ( 1 ) {
275                 /* Try to fetch more data if none available */
276                 if ( list_empty ( &file->data ) )
277                         step();
278                 /* Dequeue at most one received I/O buffer into user buffer */
279                 list_for_each_entry ( iobuf, &file->data, list ) {
280                         frag_len = iob_len ( iobuf );
281                         if ( frag_len > max_len )
282                                 frag_len = max_len;
283                         copy_to_user ( buffer, offset, iobuf->data,
284                                        frag_len );
285                         iob_pull ( iobuf, frag_len );
286                         if ( ! iob_len ( iobuf ) ) {
287                                 list_del ( &iobuf-> list );
288                                 free_iob ( iobuf );
289                         }
290                         file->pos += frag_len;
291                         len += frag_len;
292                         offset += frag_len;
293                         max_len -= frag_len;
294                         break;
295                 }
296                 /* If buffer is full, return */
297                 if ( ! max_len )
298                         return len;
299                 /* If file has completed, return */
300                 if ( file->rc != -EINPROGRESS )
301                         return ( file->rc ? file->rc : len );
302         }
303 }
304
305 /**
306  * Determine file size
307  *
308  * @v fd                File descriptor
309  * @ret size            File size, or negative error number
310  */
311 ssize_t fsize ( int fd ) {
312         struct posix_file *file;
313
314         /* Identify file */
315         file = posix_fd_to_file ( fd );
316         if ( ! file )
317                 return -EBADF;
318
319         return file->filesize;
320 }
321
322 /**
323  * Close file
324  *
325  * @v fd                File descriptor
326  * @ret rc              Return status code
327  */
328 int close ( int fd ) {
329         struct posix_file *file;
330
331         /* Identify file */
332         file = posix_fd_to_file ( fd );
333         if ( ! file )
334                 return -EBADF;
335
336         /* Terminate data transfer */
337         posix_file_finished ( file, 0 );
338
339         /* Remove from list of open files and drop reference */
340         list_del ( &file->list );
341         ref_put ( &file->refcnt );
342         return 0;
343 }