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