]> git.pond.sub.org Git - empserver/blob - src/lib/w32/posixio.c
[_WIN32] (readv, writev, iovec): New. POSIX equivalents.
[empserver] / src / lib / w32 / posixio.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  posixio.c: POSIX IO emulation layer for WIN32
29  * 
30  *  Known contributors to this file:
31  *     Ron Koenderink, 2007
32  */
33
34 /*
35  * POSIX has just one kind of file descriptors, while Windows has (at
36  * least) two: one for sockets and one for files, with separate
37  * functions to operate on them.  To present a more POSIX-like
38  * interface to our application code, we provide a compatibility layer
39  * that maps POSIX file descriptors to sockets and file handles behind
40  * the scenes.  This actual mapping is done by the fdmap.  It doesn't
41  * implement the finer points of POSIX correctly.  In particular, the
42  * actual values of the file descriptors usually differ.
43  */
44
45 #include <config.h>
46 #include <winsock2.h>
47 #undef NS_ALL
48 #include <io.h>
49 #include <direct.h>
50 #include <sys/stat.h>
51 #include <fcntl.h>
52 #include <share.h>
53
54 #include <errno.h>
55
56 #include "unistd.h"
57 #include "sys/uio.h"
58 #include "empio.h"
59 #include "prototypes.h"
60
61 /*
62  * FD_SETSIZE is the size for the maximum number of sockets.
63  * The number of file descriptors is in a variable _nhandle
64  * based on the assertion output.  In order the simplify the
65  * code and skip dynamic allocation, used double the socket size.
66  */
67 #define MAX_FDS (FD_SETSIZE * 2)
68
69 enum fdmap_io_type {
70     FDMAP_IO_NOTUSED = 0,
71     FDMAP_IO_FILE,
72     FDMAP_IO_SOCKET,
73     FDMAP_IO_ANY /* used for searching only (lookup_handle) */
74 };
75
76 struct fdmap {
77     int handle;
78     enum fdmap_io_type type;
79 };
80
81 static struct fdmap fdmap[MAX_FDS] = {
82     {0, FDMAP_IO_FILE},
83     {1, FDMAP_IO_FILE},
84     {2, FDMAP_IO_FILE}
85 };
86 static int nfd = 3;
87
88 /*
89  * Allocate a POSIX equivalent file descriptor.
90  * Note once get_fd() is called either free_fd() or set_fd()
91  * must be called before thread mutex is released as the
92  * allocation/deallocation code is not thread safe.
93  */
94 static int
95 get_fd(void)
96 {
97     int fd;
98
99     for (fd = 0; fd < nfd && fdmap[fd].type != FDMAP_IO_NOTUSED; fd++) ;
100     if (fd == MAX_FDS) {
101         errno = EMFILE;
102         return -1;
103     }
104     if (fd == nfd) {
105         fdmap[fd].type = FDMAP_IO_NOTUSED;
106         nfd++;
107     }
108     return fd;
109 }
110
111 /*
112  * Deallocate a POSIX equivalent file descriptor.
113  */
114 static void
115 free_fd(int fd)
116 {
117     fdmap[fd].type = FDMAP_IO_NOTUSED;
118     for(; fdmap[nfd - 1].type == FDMAP_IO_NOTUSED; nfd--) ;
119 }
120
121 /*
122  * Complete the allocation of the file descriptor.
123  */
124 static void
125 set_fd(int fd, enum fdmap_io_type type, int handle)
126 {
127     int i;
128
129     fdmap[fd].handle = handle;
130     fdmap[fd].type = type;
131
132     /*
133      * Garbage collection for posix_fileno(), currently not
134      * replacing fclose() and fcloseall() so do not know when
135      * a stream is closed.
136      */
137     for (i = 0; i < nfd; i++) {
138         if (i != fd && type == fdmap[i].type && handle == fdmap[i].handle)
139             free_fd(i);
140     }
141 }
142
143 /*
144  * Find the windows handle (file or socket) for file descriptor.
145  * Return windows handle and type of handle.
146  * You can search for a specific type (FDMAP_IO_FILE or FDMAP_IO_SOCKET)
147  * or for both search by using FDMAP_IO_ANY.
148  * FDMAP_IO_NOTUSED is not valid type to search with.
149  */
150 static int
151 lookup_handle(int fd, enum fdmap_io_type d_type, int error,
152         enum fdmap_io_type *type_ptr, int *handle_ptr)
153 {
154
155     if (fd < 0 || fd >= MAX_FDS) {
156         if (error != 0)
157             errno = error;
158         return 0;
159     } else if ((fdmap[fd].type != d_type && d_type != FDMAP_IO_ANY) ||
160         (fdmap[fd].type == FDMAP_IO_NOTUSED && d_type == FDMAP_IO_ANY)) {
161         if (error != 0)
162             errno = error;
163         return 0;
164     }
165     if (type_ptr != NULL)
166         *type_ptr = fdmap[fd].type;
167     if (handle_ptr != NULL)
168         *handle_ptr = fdmap[fd].handle;
169     return 1;
170 }
171
172 /*
173  * Find and return the file descriptor associated with windows handle.
174  * You can search for FDMAP_IO_FILE or FDMAP_IO_SOCKET.
175  * FDMAP_IO_ANY or FDMAP_IO_NOTUSED is not considered valid search
176  * criteria.
177  */
178 static int
179 lookup_fd(int handle, enum fdmap_io_type d_type)
180 {
181     int i;
182
183     for (i = 0; i < nfd; i++)
184         if (fdmap[i].handle == handle && fdmap[i].type == d_type)
185             return i;
186     return -1;
187 }
188
189 /*
190  * Get the window socket handle for POSIX file descriptor.
191  */
192 int
193 posix_fd2socket(int fd)
194 {
195     int handle;
196     enum dmap_io_type type;
197
198     if (!lookup_handle(fd, FDMAP_IO_SOCKET, WSAENOTSOCK,
199         &type, &handle))
200         return INVALID_SOCKET;
201     return handle;
202 }
203
204 #define SOCKET_FUNCTION(expr)                           \
205     int result;                                         \
206     int handle;                                         \
207                                                         \
208     if (!lookup_handle(fd, FDMAP_IO_SOCKET,             \
209         ENOTSOCK, NULL, &handle))                       \
210         return -1;                                      \
211                                                         \
212     result = (expr);                                    \
213     if (result == SOCKET_ERROR) {                       \
214         errno = WSAGetLastError();                      \
215         return -1;                                      \
216     }                                                   \
217     return result;
218
219 /*
220  * POSIX equivalent for accept().
221  */
222 #undef accept
223 int
224 posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
225 {
226     int new_fd;
227     int handle, new_handle;
228
229     if (!lookup_handle(fd, FDMAP_IO_SOCKET, ENOTSOCK, NULL, &handle))
230         return -1;
231
232     new_fd = get_fd();
233     if (new_fd < 0)
234         return -1;
235
236     new_handle = accept(handle, addr, addrlen);
237     if (new_handle == INVALID_SOCKET) {
238         free_fd(new_fd);
239         errno = WSAGetLastError();
240         return -1;
241     }
242     set_fd(new_fd, FDMAP_IO_SOCKET, new_handle);
243     return new_fd;
244 }
245
246 /*
247  * POSIX equivalent for bind().
248  */
249 #undef bind
250 int
251 posix_bind(int fd, const struct sockaddr *name, int namelen)
252 {
253     SOCKET_FUNCTION(bind(handle, name, namelen))
254 }
255
256 /*
257  * POSIX equivalent for listen().
258  */
259 #undef listen
260 int
261 posix_listen(int fd, int backlog)
262 {
263     SOCKET_FUNCTION(listen(handle, backlog))
264 }
265
266 /*
267  * POSIX equivalent for setsockopt().
268  */
269 #undef setsockopt
270 int
271 posix_setsockopt(int fd, int level, int optname,
272                       const char *optval, int optlen)
273 {
274     SOCKET_FUNCTION(setsockopt(handle, level, optname,
275                     optval, optlen))
276 }
277
278 /*
279  * POSIX equivalent for shutdown().
280  */
281 #undef shutdown
282 int
283 posix_shutdown(int fd, int how)
284 {
285     SOCKET_FUNCTION(shutdown(handle, how))
286 }
287
288 /*
289  * POSIX equivalent for socket().
290  */
291 #undef socket
292 int
293 posix_socket(int domain, int type, int protocol)
294 {
295     int handle;
296     int new_fd;
297     
298     if ((new_fd = get_fd()) < 0)
299         return -1;
300
301     handle = socket(domain, type, protocol);
302     if (handle == INVALID_SOCKET) {
303         free_fd(new_fd);
304         errno = WSAGetLastError();
305         return -1;
306     }
307     set_fd(new_fd, FDMAP_IO_SOCKET, handle);
308     return new_fd;
309 }
310
311 #define FILE_FUNCTION(type, expr)                               \
312     int handle;                                                 \
313                                                                 \
314     if (!lookup_handle(fd, (type), EBADF, NULL, &handle))       \
315         return -1;                                              \
316                                                                 \
317     return (expr);
318
319 /*
320  * POSIX equivalent for close().
321  */
322 int
323 posix_close(int fd)
324 {
325     int result;
326     int handle;
327     enum fdmap_io_type type;
328
329     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
330         return -1;
331
332     free_fd(fd);
333     switch (type) {
334     case FDMAP_IO_SOCKET:
335         result = closesocket(handle);
336         if (result == SOCKET_ERROR) {
337             errno = WSAGetLastError();
338             return -1;
339         }
340         return result;
341     case FDMAP_IO_FILE:
342        return _close(handle);
343     }
344     CANT_REACH();
345     return -1;
346 }
347
348 /*
349  * posix_fsync forces file sync with the disk.
350  * In order for the power report report to accurate timestamp,
351  * the _commit() is to force a sync with disk and therefore
352  * an update for file time.
353  */
354 int
355 posix_fsync(int fd)
356 {
357     FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
358 }
359
360 /*
361  * POSIX equivalent for fstat().
362  * fstat() is used instead of _fstat(),
363  * otherwise problems with the 32/64 time definitions
364  * in WIN32.
365  */
366 #undef fstat
367 int
368 posix_fstat(int fd, struct stat *buffer)
369 {
370     FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
371 }
372
373 /*
374  * POSIX equivalent for lseek().
375  */
376 int
377 posix_lseek(int fd, long offset, int origin)
378 {
379     FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
380 }
381
382 /*
383  * POSIX equivalent for open().
384  * Implements file locks when opening files to provide equivalent
385  * F_GETLK/F_SETLK.
386  */
387 int
388 posix_open(const char *fname, int oflag, ...)
389 {
390     va_list ap;
391     int pmode = 0, new_fd;
392     int handle;
393
394     if (oflag & O_CREAT) {
395         va_start(ap, oflag);
396         pmode = va_arg(ap, int);
397         va_end(ap);
398     }
399
400     if ((new_fd = get_fd()) < 0)
401         return -1;
402
403     /*
404      * We don't implement fcntl() for F_SETLK.  Instead, we lock *all*
405      * files we open.  Not ideal, but it works for Empire.
406      */
407     _sopen_s(&handle, fname, oflag,
408         oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
409     if (handle == -1) {
410         free_fd(new_fd);
411         return -1;
412     }
413     set_fd(new_fd, FDMAP_IO_FILE, handle);
414     return new_fd;
415 }
416
417 #define SHARED_FUNCTION(socket_expr, file_expr)                     \
418     int result;                                                     \
419     int handle;                                                     \
420     enum fdmap_io_type type;                                        \
421                                                                     \
422     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))    \
423         return -1;                                                  \
424                                                                     \
425     switch (type) {                                                 \
426     case FDMAP_IO_SOCKET:                                           \
427         result = (socket_expr);                                     \
428         if (result == SOCKET_ERROR) {                               \
429             errno = WSAGetLastError();                              \
430             return -1;                                              \
431         }                                                           \
432         return result;                                              \
433     case FDMAP_IO_FILE:                                             \
434         return (file_expr);                                         \
435     }                                                               \
436     CANT_REACH();                                                   \
437     return -1;
438
439 /*
440  * POSIX equivalent for read().
441  */
442 int
443 posix_read(int fd, void *buffer, unsigned int count)
444 {
445     SHARED_FUNCTION(recv(handle, buffer, count, 0),
446         _read(handle, buffer, count))
447 }
448
449 /*
450  * POSIX equivalent for readv
451  * Modelled after the GNU's libc/sysdeps/posix/readv.c
452  */
453 ssize_t
454 readv(int fd, const struct iovec *iov, int iovcnt)
455 {
456     int i;
457     unsigned char *buffer, *buffer_location;
458     size_t total_bytes = 0;
459     int bytes_read;
460     size_t bytes_left;
461
462     for (i = 0; i < iovcnt; i++) {
463         total_bytes += iov[i].iov_len;
464     }
465
466     buffer = malloc(total_bytes);
467     if (buffer == NULL)
468         return -1;
469
470     bytes_read = posix_read(fd, buffer, total_bytes);
471     if (bytes_read <= 0) {
472         free(buffer);
473         return -1;
474     }
475
476     bytes_left = bytes_read;
477     buffer_location = buffer;
478     for (i = 0; i < iovcnt; i++) {
479         size_t copy = MIN(iov[i].iov_len, bytes_left);
480
481         memcpy(iov[i].iov_base, buffer_location, copy);
482
483         buffer_location += copy;
484         bytes_left -= copy;
485         if (bytes_left == 0)
486             break;
487     }
488
489     free(buffer);
490
491     return bytes_read;
492 }
493
494 /*
495  * POSIX equivalent for write().
496  */
497 int
498 posix_write(int fd, const void *buffer, unsigned int count)
499 {
500     SHARED_FUNCTION(send(handle, buffer, count, 0),
501         _write(handle, buffer, count))
502 }
503
504 /*
505  * POSIX equivalent for writev
506  * Modelled after the GNU's libc/sysdeps/posix/writev.c
507  */
508 ssize_t
509 writev(int fd, const struct iovec *iov, int iovcnt)
510 {
511     int i;
512     unsigned char *buffer, *buffer_location;
513     size_t total_bytes = 0;
514     int bytes_written;
515
516     for (i = 0; i < iovcnt; i++)
517         total_bytes += iov[i].iov_len;
518
519     if (total_bytes == 0)
520         return 0;
521
522     buffer = malloc(total_bytes);
523     if (buffer == NULL)
524         return -1;
525
526     buffer_location = buffer;
527     for (i = 0; i < iovcnt; i++) {
528         memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
529         buffer_location += iov[i].iov_len;
530     }
531
532     bytes_written = posix_write(fd, buffer, total_bytes);
533
534     free(buffer);
535
536     if (bytes_written <= 0)
537         return -1;
538     return bytes_written;
539 }
540
541 /*
542  * POSIX equivalent for fileno().
543  * As fopen/fclose/fcloseall are not implemented as POSIX
544  * equivalent functions, the mapping is done when required
545  * by a call to fileno().  The garbage collection of the
546  * file descriptors allocated is done in set_fd() when the
547  * handle is reused.
548  */
549 int
550 posix_fileno(FILE *stream)
551 {
552     int fd;
553     int handle;
554
555     if (stream == NULL) {
556         errno = EBADF;
557         return -1;
558     }
559
560     handle = _fileno(stream);
561
562     fd = lookup_fd(handle, FDMAP_IO_FILE);
563     if (fd >= 0)
564         return fd;
565
566     if ((fd = get_fd()) < 0) {
567         errno = EBADF;
568         return -1;
569     }
570
571     set_fd(fd, FDMAP_IO_FILE, handle);
572     return fd;
573 }
574
575 /*
576  * POSIX equivalent for fcntl().
577  * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
578  * Currently ignores F_GETLK/F_SETLK as the file locks are
579  * implement in open()
580  */
581 int
582 fcntl(int fd, int cmd, ...)
583 {
584     va_list ap;
585     int value;
586     unsigned int nonblocking;
587     int result;
588     long bytes_returned;
589     int handle;
590     enum fdmap_io_type type;
591
592     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
593         return -1;
594
595     switch (cmd)
596     {
597     case F_GETFL:
598         /*
599          * F_GETFL and F_SETFL only support O_NONBLOCK
600          * for sockets currently
601          */
602         if (type == FDMAP_IO_SOCKET) {
603             result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
604                 sizeof (nonblocking), &bytes_returned, NULL, NULL);
605         
606             if(result < 0) {
607                 errno = WSAGetLastError();
608                 return -1;
609             }
610
611             if (nonblocking)
612                 return O_NONBLOCK;
613             else
614                 return 0;
615         }
616         break;
617     case F_SETFL:
618         if (type == FDMAP_IO_SOCKET) {
619             va_start(ap, cmd);
620             value = va_arg(ap, int);
621             va_end(ap);
622             if (value & O_NONBLOCK)
623                 nonblocking = 1;
624             else
625                 nonblocking = 0;
626
627             result = WSAIoctl(handle, FIONBIO, &nonblocking,
628                 sizeof (nonblocking), NULL, 0, &bytes_returned,
629                 NULL, NULL);
630
631             if(result < 0) {
632                 errno = WSAGetLastError();
633                 return -1;
634             }
635             return result;
636         }
637         break;
638     case F_SETLK:
639         /*
640          * The POSIX equivalent is not available in WIN32
641          * That implement the file locking in the file open
642          * by using sopen instead of open.
643          */
644         return 0;
645     }
646     errno = EINVAL;
647     return -1;
648 }