Browse Source

Added POSIX-style blocking I/O calls, for use by PXE TFTP API.

tags/v0.9.3
Michael Brown 17 years ago
parent
commit
86a948ccbe
2 changed files with 363 additions and 0 deletions
  1. 332
    0
      src/core/posix_io.c
  2. 31
    0
      src/include/gpxe/posix_io.h

+ 332
- 0
src/core/posix_io.c View File

@@ -0,0 +1,332 @@
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
+
79
+	list_for_each_entry ( iobuf, &file->data, 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
+	xfer_nullify ( &file->xfer );
93
+	xfer_close ( &file->xfer, rc );
94
+	file->rc = rc;
95
+}
96
+
97
+/**
98
+ * Handle close() event
99
+ *
100
+ * @v xfer		POSIX file data transfer interface
101
+ * @v rc		Reason for close
102
+ */
103
+static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) {
104
+	struct posix_file *file =
105
+		container_of ( xfer, struct posix_file, xfer );
106
+
107
+	posix_file_finished ( file, rc );
108
+}
109
+
110
+/**
111
+ * Handle seek() event
112
+ *
113
+ * @v xfer		POSIX file data transfer interface
114
+ * @v pos		New position
115
+ * @ret rc		Return status code
116
+ */
117
+static int posix_file_xfer_seek ( struct xfer_interface *xfer, off_t offset,
118
+				  int whence ) {
119
+	struct posix_file *file =
120
+		container_of ( xfer, struct posix_file, xfer );
121
+
122
+	switch ( whence ) {
123
+	case SEEK_SET:
124
+		file->pos = offset;
125
+		break;
126
+	case SEEK_CUR:
127
+		file->pos += offset;
128
+		break;
129
+	}
130
+
131
+	if ( file->filesize < file->pos )
132
+		file->filesize = file->pos;
133
+
134
+	return 0;
135
+}
136
+
137
+/**
138
+ * Handle deliver_iob() event
139
+ *
140
+ * @v xfer		POSIX file data transfer interface
141
+ * @v iobuf		I/O buffer
142
+ * @ret rc		Return status code
143
+ */
144
+static int posix_file_xfer_deliver_iob ( struct xfer_interface *xfer,
145
+					 struct io_buffer *iobuf ) {
146
+	struct posix_file *file =
147
+		container_of ( xfer, struct posix_file, xfer );
148
+
149
+	list_add_tail ( &iobuf->list, &file->data );
150
+	return 0;
151
+}
152
+
153
+/** POSIX file data transfer interface operations */
154
+static struct xfer_interface_operations posix_file_xfer_operations = {
155
+	.close		= posix_file_xfer_close,
156
+	.vredirect	= xfer_vopen,
157
+	.request	= ignore_xfer_request,
158
+	.seek		= posix_file_xfer_seek,
159
+	.alloc_iob	= default_xfer_alloc_iob,
160
+	.deliver_iob	= posix_file_xfer_deliver_iob,
161
+	.deliver_raw	= xfer_deliver_as_iob,
162
+};
163
+
164
+/**
165
+ * Identify file by file descriptor
166
+ *
167
+ * @v fd		File descriptor
168
+ * @ret file		Corresponding file, or NULL
169
+ */
170
+static struct posix_file * posix_fd_to_file ( int fd ) {
171
+	struct posix_file *file;
172
+
173
+	list_for_each_entry ( file, &posix_files, list ) {
174
+		if ( file->fd == fd )
175
+			return file;
176
+	}
177
+	return NULL;
178
+}
179
+
180
+/**
181
+ * Find an available file descriptor
182
+ *
183
+ * @ret fd		File descriptor, or negative error number
184
+ */
185
+static int posix_find_free_fd ( void ) {
186
+	int fd;
187
+
188
+	for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) {
189
+		if ( ! posix_fd_to_file ( fd ) )
190
+			return fd;
191
+	}
192
+	return -ENFILE;
193
+}
194
+
195
+/**
196
+ * Open file
197
+ *
198
+ * @v uri_string	URI string
199
+ * @ret fd		File descriptor, or negative error number
200
+ */
201
+int open ( const char *uri_string ) {
202
+	struct posix_file *file;
203
+	int fd;
204
+	int rc;
205
+
206
+	/* Find a free file descriptor to use */
207
+	fd = posix_find_free_fd();
208
+	if ( fd < 0 )
209
+		return fd;
210
+
211
+	/* Allocate and initialise structure */
212
+	file = malloc ( sizeof ( *file ) );
213
+	if ( ! file )
214
+		return -ENOMEM;
215
+	memset ( file, 0, sizeof ( *file ) );
216
+	file->refcnt.free = posix_file_free;
217
+	file->fd = fd;
218
+	file->rc = -EINPROGRESS;
219
+	xfer_init ( &file->xfer, &posix_file_xfer_operations,
220
+		    &file->refcnt );
221
+	INIT_LIST_HEAD ( &file->data );
222
+
223
+	/* Open URI on data transfer interface */
224
+	if ( ( rc = xfer_open_uri ( &file->xfer, uri_string ) ) != 0 )
225
+		goto err;
226
+
227
+	/* Wait for open to succeed or fail */
228
+	while ( list_empty ( &file->data ) ) {
229
+		step();
230
+		if ( file->rc != -EINPROGRESS ) {
231
+			rc = file->rc;
232
+			goto err;
233
+		}
234
+	}
235
+
236
+	/* Add to list of open files.  List takes reference ownership. */
237
+	list_add ( &file->list, &posix_files );
238
+	return fd;
239
+
240
+ err:
241
+	posix_file_finished ( file, rc );
242
+	ref_put ( &file->refcnt );
243
+	return rc;
244
+}
245
+
246
+/**
247
+ * Read data from file
248
+ *
249
+ * @v buffer		Data buffer
250
+ * @v offset		Starting offset within data buffer
251
+ * @v len		Maximum length to read
252
+ * @ret len		Actual length read, or negative error number
253
+ */
254
+ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) {
255
+	struct posix_file *file;
256
+	struct io_buffer *iobuf;
257
+	size_t frag_len;
258
+	ssize_t len = 0;
259
+
260
+	/* Identify file */
261
+	file = posix_fd_to_file ( fd );
262
+	if ( ! file )
263
+		return -EBADF;
264
+
265
+	while ( 1 ) {
266
+		/* Try to fetch more data if none available */
267
+		if ( list_empty ( &file->data ) )
268
+			step();
269
+		/* Dequeue at most one received I/O buffer into user buffer */
270
+		list_for_each_entry ( iobuf, &file->data, list ) {
271
+			frag_len = iob_len ( iobuf );
272
+			if ( frag_len > max_len )
273
+				frag_len = max_len;
274
+			copy_to_user ( buffer, offset, iobuf->data,
275
+				       frag_len );
276
+			iob_pull ( iobuf, frag_len );
277
+			if ( ! iob_len ( iobuf ) )
278
+				free_iob ( iobuf );
279
+			file->pos += frag_len;
280
+			len += frag_len;
281
+			offset += frag_len;
282
+			max_len -= frag_len;
283
+			break;
284
+		}
285
+		/* If buffer is full, return */
286
+		if ( ! max_len )
287
+			return len;
288
+		/* If file has completed, return */
289
+		if ( file->rc != -EINPROGRESS )
290
+			return ( file->rc ? file->rc : len );
291
+	}
292
+}
293
+
294
+/**
295
+ * Determine file size
296
+ *
297
+ * @v fd		File descriptor
298
+ * @ret size		File size, or negative error number
299
+ */
300
+ssize_t fsize ( int fd ) {
301
+	struct posix_file *file;
302
+
303
+	/* Identify file */
304
+	file = posix_fd_to_file ( fd );
305
+	if ( ! file )
306
+		return -EBADF;
307
+
308
+	return file->filesize;
309
+}
310
+
311
+/**
312
+ * Close file
313
+ *
314
+ * @v fd		File descriptor
315
+ * @ret rc		Return status code
316
+ */
317
+int close ( int fd ) {
318
+	struct posix_file *file;
319
+
320
+	/* Identify file */
321
+	file = posix_fd_to_file ( fd );
322
+	if ( ! file )
323
+		return -EBADF;
324
+
325
+	/* Terminate data transfer */
326
+	posix_file_finished ( file, 0 );
327
+
328
+	/* Remove from list of open files and drop reference */
329
+	list_del ( &file->list );
330
+	ref_put ( &file->refcnt );
331
+	return 0;
332
+}

+ 31
- 0
src/include/gpxe/posix_io.h View File

@@ -0,0 +1,31 @@
1
+#ifndef _GPXE_POSIX_IO_H
2
+#define _GPXE_POSIX_IO_H
3
+
4
+/** @file
5
+ *
6
+ * POSIX-like I/O
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/uaccess.h>
12
+
13
+extern int open ( const char *uri_string );
14
+extern ssize_t read_user ( int fd, userptr_t buffer,
15
+			   off_t offset, size_t len );
16
+extern ssize_t fsize ( int fd );
17
+extern int close ( int fd );
18
+
19
+/**
20
+ * Read data from file
21
+ *
22
+ * @v fd		File descriptor
23
+ * @v buf		Data buffer
24
+ * @v len		Maximum length to read
25
+ * @ret len		Actual length read, or negative error number
26
+ */
27
+static inline ssize_t read ( int fd, void *buf, size_t len ) {
28
+	return read_user ( fd, virt_to_user ( buf ), 0, len );
29
+}
30
+
31
+#endif /* _GPXE_POSIX_IO_H */

Loading…
Cancel
Save