iPXE
posix_io.c
Go to the documentation of this file.
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  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ipxe/list.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/xfer.h>
32 #include <ipxe/open.h>
33 #include <ipxe/process.h>
34 #include <ipxe/posix_io.h>
35 
36 /** @file
37  *
38  * POSIX-like I/O
39  *
40  * These functions provide traditional blocking I/O semantics. They
41  * are designed to be used by the PXE TFTP API. Because they block,
42  * they may not be used by most other portions of the iPXE codebase.
43  */
44 
45 /** An open file */
46 struct posix_file {
47  /** Reference count for this object */
48  struct refcnt refcnt;
49  /** List of open files */
50  struct list_head list;
51  /** File descriptor */
52  int fd;
53  /** Overall status
54  *
55  * Set to -EINPROGRESS while data transfer is in progress.
56  */
57  int rc;
58  /** Data transfer interface */
59  struct interface xfer;
60  /** Current seek position */
61  size_t pos;
62  /** File size */
63  size_t filesize;
64  /** Received data queue */
65  struct list_head data;
66 };
67 
68 /** List of open files */
69 static LIST_HEAD ( posix_files );
70 
71 /**
72  * Free open file
73  *
74  * @v refcnt Reference counter
75  */
76 static void posix_file_free ( struct refcnt *refcnt ) {
77  struct posix_file *file =
78  container_of ( refcnt, struct posix_file, refcnt );
79  struct io_buffer *iobuf;
80  struct io_buffer *tmp;
81 
82  list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) {
83  list_del ( &iobuf->list );
84  free_iob ( iobuf );
85  }
86  free ( file );
87 }
88 
89 /**
90  * Terminate file data transfer
91  *
92  * @v file POSIX file
93  * @v rc Reason for termination
94  */
95 static void posix_file_finished ( struct posix_file *file, int rc ) {
96  intf_shutdown ( &file->xfer, rc );
97  file->rc = rc;
98 }
99 
100 /**
101  * Handle deliver_iob() event
102  *
103  * @v file POSIX file
104  * @v iobuf I/O buffer
105  * @v meta Data transfer metadata
106  * @ret rc Return status code
107  */
108 static int posix_file_xfer_deliver ( struct posix_file *file,
109  struct io_buffer *iobuf,
110  struct xfer_metadata *meta ) {
111 
112  /* Keep track of file position solely for the filesize */
113  if ( meta->flags & XFER_FL_ABS_OFFSET )
114  file->pos = 0;
115  file->pos += meta->offset;
116  if ( file->filesize < file->pos )
117  file->filesize = file->pos;
118 
119  if ( iob_len ( iobuf ) ) {
120  list_add_tail ( &iobuf->list, &file->data );
121  } else {
122  free_iob ( iobuf );
123  }
124 
125  return 0;
126 }
127 
128 /** POSIX file data transfer interface operations */
132 };
133 
134 /** POSIX file data transfer interface descriptor */
137 
138 /**
139  * Identify file by file descriptor
140  *
141  * @v fd File descriptor
142  * @ret file Corresponding file, or NULL
143  */
144 static struct posix_file * posix_fd_to_file ( int fd ) {
145  struct posix_file *file;
146 
147  list_for_each_entry ( file, &posix_files, list ) {
148  if ( file->fd == fd )
149  return file;
150  }
151  return NULL;
152 }
153 
154 /**
155  * Find an available file descriptor
156  *
157  * @ret fd File descriptor, or negative error number
158  */
159 static int posix_find_free_fd ( void ) {
160  int fd;
161 
162  for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
163  if ( ! posix_fd_to_file ( fd ) )
164  return fd;
165  }
166  DBG ( "POSIX could not find free file descriptor\n" );
167  return -ENFILE;
168 }
169 
170 /**
171  * Open file
172  *
173  * @v uri_string URI string
174  * @ret fd File descriptor, or negative error number
175  */
176 int open ( const char *uri_string ) {
177  struct posix_file *file;
178  int fd;
179  int rc;
180 
181  /* Find a free file descriptor to use */
183  if ( fd < 0 )
184  return fd;
185 
186  /* Allocate and initialise structure */
187  file = zalloc ( sizeof ( *file ) );
188  if ( ! file )
189  return -ENOMEM;
190  ref_init ( &file->refcnt, posix_file_free );
191  file->fd = fd;
192  file->rc = -EINPROGRESS;
193  intf_init ( &file->xfer, &posix_file_xfer_desc, &file->refcnt );
194  INIT_LIST_HEAD ( &file->data );
195 
196  /* Open URI on data transfer interface */
197  if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 )
198  goto err;
199 
200  /* Wait for open to succeed or fail */
201  while ( list_empty ( &file->data ) ) {
202  step();
203  if ( file->rc == 0 )
204  break;
205  if ( file->rc != -EINPROGRESS ) {
206  rc = file->rc;
207  goto err;
208  }
209  }
210 
211  /* Add to list of open files. List takes reference ownership. */
212  list_add ( &file->list, &posix_files );
213  DBG ( "POSIX opened %s as file %d\n", uri_string, fd );
214  return fd;
215 
216  err:
217  posix_file_finished ( file, rc );
218  ref_put ( &file->refcnt );
219  return rc;
220 }
221 
222 /**
223  * Check file descriptors for readiness
224  *
225  * @v readfds File descriptors to check
226  * @v wait Wait until data is ready
227  * @ret nready Number of ready file descriptors
228  */
229 int select ( fd_set *readfds, int wait ) {
230  struct posix_file *file;
231  int fd;
232 
233  do {
234  for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
235  if ( ! FD_ISSET ( fd, readfds ) )
236  continue;
237  file = posix_fd_to_file ( fd );
238  if ( ! file )
239  return -EBADF;
240  if ( ( list_empty ( &file->data ) ) &&
241  ( file->rc == -EINPROGRESS ) )
242  continue;
243  /* Data is available or status has changed */
244  FD_ZERO ( readfds );
245  FD_SET ( fd, readfds );
246  return 1;
247  }
248  step();
249  } while ( wait );
250 
251  return 0;
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  * This call is non-blocking; if no data is available to read then
263  * -EWOULDBLOCK will be returned.
264  */
266  struct posix_file *file;
267  struct io_buffer *iobuf;
268  size_t len;
269 
270  /* Identify file */
271  file = posix_fd_to_file ( fd );
272  if ( ! file )
273  return -EBADF;
274 
275  /* Try to fetch more data if none available */
276  if ( list_empty ( &file->data ) )
277  step();
278 
279  /* Dequeue at most one received I/O buffer into user buffer */
280  list_for_each_entry ( iobuf, &file->data, list ) {
281  len = iob_len ( iobuf );
282  if ( len > max_len )
283  len = max_len;
284  copy_to_user ( buffer, offset, iobuf->data, len );
285  iob_pull ( iobuf, len );
286  if ( ! iob_len ( iobuf ) ) {
287  list_del ( &iobuf->list );
288  free_iob ( iobuf );
289  }
290  file->pos += len;
291  assert ( len != 0 );
292  return len;
293  }
294 
295  /* If file has completed, return (after returning all data) */
296  if ( file->rc != -EINPROGRESS ) {
297  assert ( list_empty ( &file->data ) );
298  return file->rc;
299  }
300 
301  /* No data ready and file still in progress; return -WOULDBLOCK */
302  return -EWOULDBLOCK;
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 }
#define iob_pull(iobuf, len)
Definition: iobuf.h:98
An object interface operation.
Definition: interface.h:17
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
void intf_close(struct interface *intf, int rc)
Close an object interface.
Definition: interface.c:244
struct list_head list
List of open files.
Definition: posix_io.c:50
struct interface xfer
Data transfer interface.
Definition: posix_io.c:59
Data transfer metadata.
Definition: xfer.h:22
void intf_shutdown(struct interface *intf, int rc)
Shut down an object interface.
Definition: interface.c:273
An open file.
Definition: posix_io.c:46
#define list_add(new, head)
Add a new entry to the head of a list.
Definition: list.h:69
static void size_t size_t max_len
Definition: entropy.h:153
#define XFER_FL_ABS_OFFSET
Offset is absolute.
Definition: xfer.h:47
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition: refcnt.h:64
Error codes.
I/O buffers.
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:145
ssize_t read_user(int fd, userptr_t buffer, off_t offset, size_t max_len)
Read data from file.
Definition: posix_io.c:265
int close(int fd)
Close file.
Definition: posix_io.c:328
uint32_t buffer
Buffer index (or NETVSC_RNDIS_NO_BUFFER)
Definition: netvsc.h:16
struct refcnt refcnt
Reference count for this object.
Definition: posix_io.c:48
A doubly-linked list entry (or list head)
Definition: list.h:18
Data transfer interfaces.
A reference counter.
Definition: refcnt.h:26
#define list_empty(list)
Test whether a list is empty.
Definition: list.h:136
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
ssize_t fsize(int fd)
Determine file size.
Definition: posix_io.c:311
#define ENOMEM
Not enough space.
Definition: errno.h:534
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
An object interface.
Definition: interface.h:109
#define EWOULDBLOCK
Operation would block.
Definition: errno.h:679
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition: list.h:420
static int posix_file_xfer_deliver(struct posix_file *file, struct io_buffer *iobuf, struct xfer_metadata *meta)
Handle deliver_iob() event.
Definition: posix_io.c:108
#define list_add_tail(new, head)
Add a new entry to the tail of a list.
Definition: list.h:93
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
static LIST_HEAD(posix_files)
List of open files.
#define EBADF
Bad file descriptor.
Definition: errno.h:328
#define list_for_each_entry_safe(pos, tmp, head, member)
Iterate over entries in a list, safe against deletion of the current entry.
Definition: list.h:447
#define EINPROGRESS
Operation in progress.
Definition: errno.h:418
int meta(WINDOW *, bool)
#define POSIX_FD_MAX
Maximum file descriptor that will ever be allocated.
Definition: posix_io.h:19
Linked lists.
uint32_t fd_set
File descriptor set as used for select()
Definition: posix_io.h:22
An object interface descriptor.
Definition: interface.h:40
static struct interface_operation posix_file_xfer_operations[]
POSIX file data transfer interface operations.
Definition: posix_io.c:129
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:624
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:151
#define POSIX_FD_MIN
Minimum file descriptor that will ever be allocated.
Definition: posix_io.h:16
int fd
File descriptor.
Definition: posix_io.c:52
#define ENFILE
Too many open files in system.
Definition: errno.h:493
uint8_t * tmp
Definition: entropy.h:156
#define INTF_OP(op_type, object_type, op_func)
Define an object interface operation.
Definition: interface.h:32
static __always_inline void copy_to_user(userptr_t dest, off_t dest_off, const void *src, size_t len)
Copy data to user buffer.
Definition: uaccess.h:324
static void posix_file_finished(struct posix_file *file, int rc)
Terminate file data transfer.
Definition: posix_io.c:95
int xfer_deliver(struct interface *intf, struct io_buffer *iobuf, struct xfer_metadata *meta)
Deliver datagram.
Definition: xfer.c:193
Processes.
static void posix_file_free(struct refcnt *refcnt)
Free open file.
Definition: posix_io.c:76
int select(fd_set *readfds, int wait)
Check file descriptors for readiness.
Definition: posix_io.c:229
Data transfer interface opening.
static struct interface_descriptor posix_file_xfer_desc
POSIX file data transfer interface descriptor.
Definition: posix_io.c:135
static struct posix_file * posix_fd_to_file(int fd)
Identify file by file descriptor.
Definition: posix_io.c:144
signed long off_t
Definition: stdint.h:8
#define INIT_LIST_HEAD(list)
Initialise a list head.
Definition: list.h:45
#define INTF_DESC(object_type, intf, operations)
Define an object interface descriptor.
Definition: interface.h:65
struct list_head list
List of which this buffer is a member.
Definition: iobuf.h:39
uint32_t len
Length.
Definition: ena.h:14
struct list_head data
Received data queue.
Definition: posix_io.c:65
void * data
Start of data.
Definition: iobuf.h:44
void step(void)
Single-step a single process.
Definition: process.c:98
int open(const char *uri_string)
Open file.
Definition: posix_io.c:176
size_t pos
Current seek position.
Definition: posix_io.c:61
static int posix_find_free_fd(void)
Find an available file descriptor.
Definition: posix_io.c:159
signed long ssize_t
Definition: stdint.h:7
POSIX-like I/O.
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
static void intf_init(struct interface *intf, struct interface_descriptor *desc, struct refcnt *refcnt)
Initialise an object interface.
Definition: interface.h:173
int xfer_open_uri_string(struct interface *intf, const char *uri_string)
Open URI string.
Definition: open.c:114
size_t filesize
File size.
Definition: posix_io.c:63
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
String functions.
#define ref_put(refcnt)
Drop reference to object.
Definition: refcnt.h:106
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
int rc
Overall status.
Definition: posix_io.c:57
A persistent I/O buffer.
Definition: iobuf.h:32