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