]> git.pond.sub.org Git - empserver/blob - src/lib/w32/posixio.c
Update copyright notice
[empserver] / src / lib / w32 / posixio.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, 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
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <io.h>
50 #include <share.h>
51 #include <stdio.h>
52 #include <stdarg.h>
53 /*
54  * Need to include winsock2.h before ws2tcpip.h.
55  * Use sys/socket.h to ensure the #undef NS_ALL
56  * is not missed after including winsock2.h.
57  */
58 #include "sys/socket.h"
59 #include <sys/stat.h>
60 #include <ws2tcpip.h>
61
62 #include "misc.h"
63 #include "sys/uio.h"
64 #include "unistd.h"
65
66 /*
67  * FD_SETSIZE is the size for the maximum number of sockets.
68  * The number of file descriptors is in a variable _nhandle
69  * based on the assertion output.  In order the simplify the
70  * code and skip dynamic allocation, used double the socket size.
71  */
72 #define MAX_FDS (FD_SETSIZE * 2)
73
74 enum fdmap_io_type {
75     FDMAP_IO_NOTUSED = 0,
76     FDMAP_IO_FILE,
77     FDMAP_IO_SOCKET,
78     FDMAP_IO_ANY /* used for searching only (lookup_handle) */
79 };
80
81 struct fdmap {
82     int handle;
83     enum fdmap_io_type type;
84 };
85
86 static struct fdmap fdmap[MAX_FDS] = {
87     {0, FDMAP_IO_FILE},
88     {1, FDMAP_IO_FILE},
89     {2, FDMAP_IO_FILE}
90 };
91 static int nfd = 3;
92
93 /*
94  * Allocate a POSIX equivalent file descriptor.
95  * Note once get_fd() is called either free_fd() or set_fd()
96  * must be called before thread mutex is released as the
97  * allocation/deallocation code is not thread safe.
98  */
99 static int
100 get_fd(void)
101 {
102     int fd;
103
104     for (fd = 0; fd < nfd && fdmap[fd].type != FDMAP_IO_NOTUSED; fd++) ;
105     if (fd == MAX_FDS) {
106         errno = EMFILE;
107         return -1;
108     }
109     if (fd == nfd) {
110         fdmap[fd].type = FDMAP_IO_NOTUSED;
111         nfd++;
112     }
113     return fd;
114 }
115
116 /*
117  * Deallocate a POSIX equivalent file descriptor.
118  */
119 static void
120 free_fd(int fd)
121 {
122     fdmap[fd].type = FDMAP_IO_NOTUSED;
123     for(; fdmap[nfd - 1].type == FDMAP_IO_NOTUSED; nfd--) ;
124 }
125
126 /*
127  * Complete the allocation of the file descriptor.
128  */
129 static void
130 set_fd(int fd, enum fdmap_io_type type, int handle)
131 {
132     int i;
133
134     fdmap[fd].handle = handle;
135     fdmap[fd].type = type;
136
137     /*
138      * Garbage collection for fileno(), currently not
139      * replacing fclose() and fcloseall() so do not know when
140      * a stream is closed.
141      */
142     for (i = 0; i < nfd; i++) {
143         if (i != fd && type == fdmap[i].type && handle == fdmap[i].handle)
144             free_fd(i);
145     }
146 }
147
148 /*
149  * Find the windows handle (file or socket) for file descriptor.
150  * Return windows handle and type of handle.
151  * You can search for a specific type (FDMAP_IO_FILE or FDMAP_IO_SOCKET)
152  * or for both search by using FDMAP_IO_ANY.
153  * FDMAP_IO_NOTUSED is not valid type to search with.
154  */
155 static int
156 lookup_handle(int fd, enum fdmap_io_type d_type, int error,
157         enum fdmap_io_type *type_ptr, int *handle_ptr)
158 {
159
160     if (fd < 0 || fd >= MAX_FDS) {
161         if (error != 0)
162             errno = error;
163         return 0;
164     } else if ((fdmap[fd].type != d_type && d_type != FDMAP_IO_ANY) ||
165         (fdmap[fd].type == FDMAP_IO_NOTUSED && d_type == FDMAP_IO_ANY)) {
166         if (error != 0)
167             errno = error;
168         return 0;
169     }
170     if (type_ptr != NULL)
171         *type_ptr = fdmap[fd].type;
172     if (handle_ptr != NULL)
173         *handle_ptr = fdmap[fd].handle;
174     return 1;
175 }
176
177 /*
178  * Find and return the file descriptor associated with windows handle.
179  * You can search for FDMAP_IO_FILE or FDMAP_IO_SOCKET.
180  * FDMAP_IO_ANY or FDMAP_IO_NOTUSED is not considered valid search
181  * criteria.
182  */
183 static int
184 lookup_fd(int handle, enum fdmap_io_type d_type)
185 {
186     int i;
187
188     for (i = 0; i < nfd; i++)
189         if (fdmap[i].handle == handle && fdmap[i].type == d_type)
190             return i;
191     return -1;
192 }
193
194 /*
195  * Get the window socket handle for POSIX file descriptor.
196  */
197 int
198 posix_fd2socket(int fd)
199 {
200     int handle;
201     enum fdmap_io_type type;
202
203     if (!lookup_handle(fd, FDMAP_IO_SOCKET, WSAENOTSOCK,
204         &type, &handle))
205         return INVALID_SOCKET;
206     return handle;
207 }
208
209 #define SOCKET_FUNCTION(expr)                           \
210     int result;                                         \
211     int handle;                                         \
212                                                         \
213     if (!lookup_handle(fd, FDMAP_IO_SOCKET,             \
214         ENOTSOCK, NULL, &handle))                       \
215         return -1;                                      \
216                                                         \
217     result = (expr);                                    \
218     if (result == SOCKET_ERROR) {                       \
219         errno = WSAGetLastError();                      \
220         return -1;                                      \
221     }                                                   \
222     return result;
223
224 /*
225  * POSIX equivalent for accept().
226  */
227 #undef accept
228 int
229 posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
230 {
231     int new_fd;
232     int handle;
233     SOCKET new_handle;
234
235     if (!lookup_handle(fd, FDMAP_IO_SOCKET, ENOTSOCK, NULL, &handle))
236         return -1;
237
238     new_fd = get_fd();
239     if (new_fd < 0)
240         return -1;
241
242     new_handle = accept(handle, addr, addrlen);
243     if (new_handle == INVALID_SOCKET) {
244         free_fd(new_fd);
245         errno = WSAGetLastError();
246         return -1;
247     }
248     set_fd(new_fd, FDMAP_IO_SOCKET, (int)new_handle);
249     return new_fd;
250 }
251
252 /*
253  * POSIX equivalent for bind().
254  */
255 #undef bind
256 int
257 posix_bind(int fd, const struct sockaddr *name, socklen_t namelen)
258 {
259     SOCKET_FUNCTION(bind(handle, name, namelen))
260 }
261
262 /*
263  * POSIX equivalent for listen().
264  */
265 #undef listen
266 int
267 posix_listen(int fd, int backlog)
268 {
269     SOCKET_FUNCTION(listen(handle, backlog))
270 }
271
272 /*
273  * POSIX equivalent for setsockopt().
274  */
275 #undef setsockopt
276 int
277 posix_setsockopt(int fd, int level, int optname,
278                       const void *optval, socklen_t optlen)
279 {
280     /*
281      * SO_REUSEADDR requests to permit another bind even when the
282      * port is still in state TIME_WAIT.  Windows' SO_REUSEADDR is
283      * broken: it makes bind() succeed no matter what, even if
284      * there's another server running on the same port.  Luckily,
285      * bind() seems to be broken as well: it seems to succeed while
286      * the port is in state TIME_WAIT by default; thus we get the
287      * behavior we want by not setting SO_REUSEADDR.
288      */
289     if (level == SOL_SOCKET && optname == SO_REUSEADDR)
290         return 0;
291     {
292         SOCKET_FUNCTION(setsockopt(handle, level, optname,
293                         optval, optlen))
294     }
295 }
296
297 /*
298  * POSIX equivalent for shutdown().
299  */
300 #undef shutdown
301 int
302 posix_shutdown(int fd, int how)
303 {
304     SOCKET_FUNCTION(shutdown(handle, how))
305 }
306
307 /*
308  * POSIX equivalent for socket().
309  */
310 #undef socket
311 int
312 posix_socket(int domain, int type, int protocol)
313 {
314     SOCKET handle;
315     int new_fd;
316     
317     if ((new_fd = get_fd()) < 0)
318         return -1;
319
320     handle = socket(domain, type, protocol);
321     if (handle == INVALID_SOCKET) {
322         free_fd(new_fd);
323         errno = WSAGetLastError();
324         return -1;
325     }
326     set_fd(new_fd, FDMAP_IO_SOCKET, (int)handle);
327     return new_fd;
328 }
329
330 #ifdef HAVE_GETADDRINFO
331 const char *
332 inet_ntop(int af, const void *src, char *dst, socklen_t len)
333 {
334     struct sockaddr *sa;
335     struct sockaddr_in sin;
336     struct sockaddr_in6 sin6;
337     size_t salen;
338
339     if (af == AF_INET) {
340         memset(&sin, 0, sizeof(sin));
341         sin.sin_family = af;
342         memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr));
343         sa = (struct sockaddr *)&sin;
344         salen = sizeof(sin);
345     } else if (af == AF_INET6) {
346         memset(&sin6, 0, sizeof(sin6));
347         sin6.sin6_family = af;
348         memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr));
349         sa = (struct sockaddr *)&sin6;
350         salen = sizeof(sin6);
351     } else {
352         errno = EAFNOSUPPORT;
353         return NULL;
354     }
355
356     if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) {
357         errno = EAFNOSUPPORT;
358         return NULL;
359     }
360
361     return dst;
362 }
363 #endif
364
365 #define FILE_FUNCTION(type, expr)                               \
366     int handle;                                                 \
367                                                                 \
368     if (!lookup_handle(fd, (type), EBADF, NULL, &handle))       \
369         return -1;                                              \
370                                                                 \
371     return (expr);
372
373 /*
374  * POSIX equivalent for close().
375  */
376 int
377 posix_close(int fd)
378 {
379     int result;
380     int handle;
381     enum fdmap_io_type type;
382
383     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
384         return -1;
385
386     free_fd(fd);
387     switch (type) {
388     case FDMAP_IO_SOCKET:
389         result = closesocket(handle);
390         if (result == SOCKET_ERROR) {
391             errno = WSAGetLastError();
392             return -1;
393         }
394         return result;
395     case FDMAP_IO_FILE:
396        return _close(handle);
397     default:
398         CANT_REACH();
399         return -1;
400     }
401 }
402
403 /*
404  * posix_fsync forces file sync with the disk.
405  * In order for the power report report to accurate timestamp,
406  * the _commit() is to force a sync with disk and therefore
407  * an update for file time.
408  */
409 int
410 posix_fsync(int fd)
411 {
412     FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
413 }
414
415 /*
416  * POSIX equivalent for fstat().
417  * fstat() is used instead of _fstat(),
418  * otherwise problems with the 32/64 time definitions
419  * in WIN32.
420  */
421 #undef fstat
422 int
423 posix_fstat(int fd, struct stat *buffer)
424 {
425     FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
426 }
427
428 /*
429  * POSIX equivalent for lseek().
430  */
431 off_t
432 posix_lseek(int fd, off_t offset, int origin)
433 {
434     FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
435 }
436
437 /*
438  * POSIX equivalent for open().
439  * Implements file locks when opening files to provide equivalent
440  * F_GETLK/F_SETLK.
441  */
442 int
443 posix_open(const char *fname, int oflag, ...)
444 {
445     va_list ap;
446     int pmode = 0, new_fd;
447     int handle;
448
449     if (oflag & O_CREAT) {
450         va_start(ap, oflag);
451         pmode = va_arg(ap, int);
452         va_end(ap);
453     }
454
455     if ((new_fd = get_fd()) < 0)
456         return -1;
457
458     /*
459      * We don't implement fcntl() for F_SETLK.  Instead, we lock *all*
460      * files we open.  Not ideal, but it works for Empire.
461      */
462     handle = _sopen(fname, oflag,
463         oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
464     if (handle == -1) {
465         free_fd(new_fd);
466         return -1;
467     }
468     set_fd(new_fd, FDMAP_IO_FILE, handle);
469     return new_fd;
470 }
471
472 #define SHARED_FUNCTION(socket_expr, file_expr)                     \
473     int result;                                                     \
474     int handle;                                                     \
475     enum fdmap_io_type type;                                        \
476                                                                     \
477     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))    \
478         return -1;                                                  \
479                                                                     \
480     switch (type) {                                                 \
481     case FDMAP_IO_SOCKET:                                           \
482         result = (socket_expr);                                     \
483         if (result == SOCKET_ERROR) {                               \
484             errno = WSAGetLastError();                              \
485             return -1;                                              \
486         }                                                           \
487         return result;                                              \
488     case FDMAP_IO_FILE:                                             \
489         return (file_expr);                                         \
490     default:                                                        \
491         CANT_REACH();                                               \
492         return -1;                                                  \
493     }
494
495 /*
496  * POSIX equivalent for read().
497  */
498 ssize_t
499 posix_read(int fd, void *buffer, size_t count)
500 {
501     SHARED_FUNCTION(recv(handle, buffer, count, 0),
502         _read(handle, buffer, count))
503 }
504
505 /*
506  * POSIX equivalent for readv
507  * Modelled after the GNU's libc/sysdeps/posix/readv.c
508  */
509 ssize_t
510 readv(int fd, const struct iovec *iov, int iovcnt)
511 {
512     int i;
513     unsigned char *buffer, *buffer_location;
514     size_t total_bytes = 0;
515     int bytes_read;
516     size_t bytes_left;
517
518     for (i = 0; i < iovcnt; i++) {
519         total_bytes += iov[i].iov_len;
520     }
521
522     buffer = malloc(total_bytes);
523     if (buffer == NULL && total_bytes != 0) {
524         errno = ENOMEM;
525         return -1;
526     }
527
528     bytes_read = posix_read(fd, buffer, total_bytes);
529     if (bytes_read <= 0) {
530         free(buffer);
531         return -1;
532     }
533
534     bytes_left = bytes_read;
535     buffer_location = buffer;
536     for (i = 0; i < iovcnt; i++) {
537         size_t copy = MIN(iov[i].iov_len, bytes_left);
538
539         memcpy(iov[i].iov_base, buffer_location, copy);
540
541         buffer_location += copy;
542         bytes_left -= copy;
543         if (bytes_left == 0)
544             break;
545     }
546
547     free(buffer);
548
549     return bytes_read;
550 }
551
552 /*
553  * POSIX equivalent for write().
554  */
555 ssize_t
556 posix_write(int fd, const void *buffer, size_t count)
557 {
558     SHARED_FUNCTION(send(handle, buffer, count, 0),
559         _write(handle, buffer, count))
560 }
561
562 /*
563  * POSIX equivalent for writev
564  * Modelled after the GNU's libc/sysdeps/posix/writev.c
565  */
566 ssize_t
567 writev(int fd, const struct iovec *iov, int iovcnt)
568 {
569     int i;
570     unsigned char *buffer, *buffer_location;
571     size_t total_bytes = 0;
572     int bytes_written;
573
574     for (i = 0; i < iovcnt; i++)
575         total_bytes += iov[i].iov_len;
576
577     buffer = malloc(total_bytes);
578     if (buffer == NULL && total_bytes != 0) {
579         errno = ENOMEM;
580         return -1;
581     }
582
583     buffer_location = buffer;
584     for (i = 0; i < iovcnt; i++) {
585         memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
586         buffer_location += iov[i].iov_len;
587     }
588
589     bytes_written = posix_write(fd, buffer, total_bytes);
590
591     free(buffer);
592
593     if (bytes_written <= 0)
594         return -1;
595     return bytes_written;
596 }
597
598 /*
599  * POSIX equivalent for fileno().
600  * As fopen/fclose/fcloseall are not implemented as POSIX
601  * equivalent functions, the mapping is done when required
602  * by a call to fileno().  The garbage collection of the
603  * file descriptors allocated is done in set_fd() when the
604  * handle is reused.
605  */
606 int
607 fileno(FILE *stream)
608 {
609     int fd;
610     int handle;
611
612     if (stream == NULL) {
613         errno = EBADF;
614         return -1;
615     }
616
617     handle = _fileno(stream);
618
619     fd = lookup_fd(handle, FDMAP_IO_FILE);
620     if (fd >= 0)
621         return fd;
622
623     if ((fd = get_fd()) < 0) {
624         errno = EBADF;
625         return -1;
626     }
627
628     set_fd(fd, FDMAP_IO_FILE, handle);
629     return fd;
630 }
631
632 /*
633  * POSIX equivalent for fcntl().
634  * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
635  * Currently ignores F_GETLK/F_SETLK as the file locks are
636  * implement in open()
637  */
638 int
639 fcntl(int fd, int cmd, ...)
640 {
641     va_list ap;
642     int value;
643     unsigned int nonblocking;
644     int result;
645     long bytes_returned;
646     int handle;
647     enum fdmap_io_type type;
648
649     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
650         return -1;
651
652     switch (cmd)
653     {
654     case F_GETFL:
655         /*
656          * F_GETFL and F_SETFL only support O_NONBLOCK
657          * for sockets currently
658          */
659         if (type == FDMAP_IO_SOCKET) {
660             result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
661                 sizeof (nonblocking), &bytes_returned, NULL, NULL);
662         
663             if(result < 0) {
664                 errno = WSAGetLastError();
665                 return -1;
666             }
667
668             if (nonblocking)
669                 return O_NONBLOCK;
670             else
671                 return 0;
672         }
673         break;
674     case F_SETFL:
675         if (type == FDMAP_IO_SOCKET) {
676             va_start(ap, cmd);
677             value = va_arg(ap, int);
678             va_end(ap);
679             if (value & O_NONBLOCK)
680                 nonblocking = 1;
681             else
682                 nonblocking = 0;
683
684             result = WSAIoctl(handle, FIONBIO, &nonblocking,
685                 sizeof (nonblocking), NULL, 0, &bytes_returned,
686                 NULL, NULL);
687
688             if(result < 0) {
689                 errno = WSAGetLastError();
690                 return -1;
691             }
692             return result;
693         }
694         break;
695     case F_SETLK:
696         /*
697          * The POSIX equivalent is not available in WIN32
698          * That implement the file locking in the file open
699          * by using sopen instead of open.
700          */
701         return 0;
702     }
703     errno = EINVAL;
704     return -1;
705 }