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