]> git.pond.sub.org Git - empserver/blob - src/lib/w32/posixio.c
(setsockopt): Fix type of argument optval to conform to spec.
[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 fdmap_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 void *optval, int optlen)
273 {
274     /*
275      * SO_REUSEADDR requests from tcp_listen.c
276      * to permit another bind even when the
277      * port is still in state TIME_WAIT.  Windows' SO_REUSEADDR is
278      * broken: it makes bind() succeed no matter what, even if
279      * there's another server running on the same port.  Luckily,
280      * bind() seems to be broken as well: it seems to succeed while
281      * the port is in state TIME_WAIT by default; thus we get the
282      * behavior we want by not setting SO_REUSEADDR.
283      */
284     if (level == SOL_SOCKET && optname == SO_REUSEADDR)
285         return 0;
286     {
287         SOCKET_FUNCTION(setsockopt(handle, level, optname,
288                         optval, optlen))
289     }
290 }
291
292 /*
293  * POSIX equivalent for shutdown().
294  */
295 #undef shutdown
296 int
297 posix_shutdown(int fd, int how)
298 {
299     SOCKET_FUNCTION(shutdown(handle, how))
300 }
301
302 /*
303  * POSIX equivalent for socket().
304  */
305 #undef socket
306 int
307 posix_socket(int domain, int type, int protocol)
308 {
309     int handle;
310     int new_fd;
311     
312     if ((new_fd = get_fd()) < 0)
313         return -1;
314
315     handle = socket(domain, type, protocol);
316     if (handle == INVALID_SOCKET) {
317         free_fd(new_fd);
318         errno = WSAGetLastError();
319         return -1;
320     }
321     set_fd(new_fd, FDMAP_IO_SOCKET, handle);
322     return new_fd;
323 }
324
325 #define FILE_FUNCTION(type, expr)                               \
326     int handle;                                                 \
327                                                                 \
328     if (!lookup_handle(fd, (type), EBADF, NULL, &handle))       \
329         return -1;                                              \
330                                                                 \
331     return (expr);
332
333 /*
334  * POSIX equivalent for close().
335  */
336 int
337 posix_close(int fd)
338 {
339     int result;
340     int handle;
341     enum fdmap_io_type type;
342
343     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
344         return -1;
345
346     free_fd(fd);
347     switch (type) {
348     case FDMAP_IO_SOCKET:
349         result = closesocket(handle);
350         if (result == SOCKET_ERROR) {
351             errno = WSAGetLastError();
352             return -1;
353         }
354         return result;
355     case FDMAP_IO_FILE:
356        return _close(handle);
357     }
358     CANT_REACH();
359     return -1;
360 }
361
362 /*
363  * posix_fsync forces file sync with the disk.
364  * In order for the power report report to accurate timestamp,
365  * the _commit() is to force a sync with disk and therefore
366  * an update for file time.
367  */
368 int
369 posix_fsync(int fd)
370 {
371     FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
372 }
373
374 /*
375  * POSIX equivalent for fstat().
376  * fstat() is used instead of _fstat(),
377  * otherwise problems with the 32/64 time definitions
378  * in WIN32.
379  */
380 #undef fstat
381 int
382 posix_fstat(int fd, struct stat *buffer)
383 {
384     FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
385 }
386
387 /*
388  * POSIX equivalent for lseek().
389  */
390 int
391 posix_lseek(int fd, long offset, int origin)
392 {
393     FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
394 }
395
396 /*
397  * POSIX equivalent for open().
398  * Implements file locks when opening files to provide equivalent
399  * F_GETLK/F_SETLK.
400  */
401 int
402 posix_open(const char *fname, int oflag, ...)
403 {
404     va_list ap;
405     int pmode = 0, new_fd;
406     int handle;
407
408     if (oflag & O_CREAT) {
409         va_start(ap, oflag);
410         pmode = va_arg(ap, int);
411         va_end(ap);
412     }
413
414     if ((new_fd = get_fd()) < 0)
415         return -1;
416
417     /*
418      * We don't implement fcntl() for F_SETLK.  Instead, we lock *all*
419      * files we open.  Not ideal, but it works for Empire.
420      */
421     handle = _sopen(fname, oflag,
422         oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
423     if (handle == -1) {
424         free_fd(new_fd);
425         return -1;
426     }
427     set_fd(new_fd, FDMAP_IO_FILE, handle);
428     return new_fd;
429 }
430
431 #define SHARED_FUNCTION(socket_expr, file_expr)                     \
432     int result;                                                     \
433     int handle;                                                     \
434     enum fdmap_io_type type;                                        \
435                                                                     \
436     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))    \
437         return -1;                                                  \
438                                                                     \
439     switch (type) {                                                 \
440     case FDMAP_IO_SOCKET:                                           \
441         result = (socket_expr);                                     \
442         if (result == SOCKET_ERROR) {                               \
443             errno = WSAGetLastError();                              \
444             return -1;                                              \
445         }                                                           \
446         return result;                                              \
447     case FDMAP_IO_FILE:                                             \
448         return (file_expr);                                         \
449     }                                                               \
450     CANT_REACH();                                                   \
451     return -1;
452
453 /*
454  * POSIX equivalent for read().
455  */
456 int
457 posix_read(int fd, void *buffer, unsigned int count)
458 {
459     SHARED_FUNCTION(recv(handle, buffer, count, 0),
460         _read(handle, buffer, count))
461 }
462
463 /*
464  * POSIX equivalent for readv
465  * Modelled after the GNU's libc/sysdeps/posix/readv.c
466  */
467 ssize_t
468 readv(int fd, const struct iovec *iov, int iovcnt)
469 {
470     int i;
471     unsigned char *buffer, *buffer_location;
472     size_t total_bytes = 0;
473     int bytes_read;
474     size_t bytes_left;
475
476     for (i = 0; i < iovcnt; i++) {
477         total_bytes += iov[i].iov_len;
478     }
479
480     buffer = malloc(total_bytes);
481     if (buffer == NULL)
482         return -1;
483
484     bytes_read = posix_read(fd, buffer, total_bytes);
485     if (bytes_read <= 0) {
486         free(buffer);
487         return -1;
488     }
489
490     bytes_left = bytes_read;
491     buffer_location = buffer;
492     for (i = 0; i < iovcnt; i++) {
493         size_t copy = MIN(iov[i].iov_len, bytes_left);
494
495         memcpy(iov[i].iov_base, buffer_location, copy);
496
497         buffer_location += copy;
498         bytes_left -= copy;
499         if (bytes_left == 0)
500             break;
501     }
502
503     free(buffer);
504
505     return bytes_read;
506 }
507
508 /*
509  * POSIX equivalent for write().
510  */
511 int
512 posix_write(int fd, const void *buffer, unsigned int count)
513 {
514     SHARED_FUNCTION(send(handle, buffer, count, 0),
515         _write(handle, buffer, count))
516 }
517
518 /*
519  * POSIX equivalent for writev
520  * Modelled after the GNU's libc/sysdeps/posix/writev.c
521  */
522 ssize_t
523 writev(int fd, const struct iovec *iov, int iovcnt)
524 {
525     int i;
526     unsigned char *buffer, *buffer_location;
527     size_t total_bytes = 0;
528     int bytes_written;
529
530     for (i = 0; i < iovcnt; i++)
531         total_bytes += iov[i].iov_len;
532
533     if (total_bytes == 0)
534         return 0;
535
536     buffer = malloc(total_bytes);
537     if (buffer == NULL)
538         return -1;
539
540     buffer_location = buffer;
541     for (i = 0; i < iovcnt; i++) {
542         memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
543         buffer_location += iov[i].iov_len;
544     }
545
546     bytes_written = posix_write(fd, buffer, total_bytes);
547
548     free(buffer);
549
550     if (bytes_written <= 0)
551         return -1;
552     return bytes_written;
553 }
554
555 /*
556  * POSIX equivalent for fileno().
557  * As fopen/fclose/fcloseall are not implemented as POSIX
558  * equivalent functions, the mapping is done when required
559  * by a call to fileno().  The garbage collection of the
560  * file descriptors allocated is done in set_fd() when the
561  * handle is reused.
562  */
563 int
564 posix_fileno(FILE *stream)
565 {
566     int fd;
567     int handle;
568
569     if (stream == NULL) {
570         errno = EBADF;
571         return -1;
572     }
573
574     handle = _fileno(stream);
575
576     fd = lookup_fd(handle, FDMAP_IO_FILE);
577     if (fd >= 0)
578         return fd;
579
580     if ((fd = get_fd()) < 0) {
581         errno = EBADF;
582         return -1;
583     }
584
585     set_fd(fd, FDMAP_IO_FILE, handle);
586     return fd;
587 }
588
589 /*
590  * POSIX equivalent for fcntl().
591  * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
592  * Currently ignores F_GETLK/F_SETLK as the file locks are
593  * implement in open()
594  */
595 int
596 fcntl(int fd, int cmd, ...)
597 {
598     va_list ap;
599     int value;
600     unsigned int nonblocking;
601     int result;
602     long bytes_returned;
603     int handle;
604     enum fdmap_io_type type;
605
606     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
607         return -1;
608
609     switch (cmd)
610     {
611     case F_GETFL:
612         /*
613          * F_GETFL and F_SETFL only support O_NONBLOCK
614          * for sockets currently
615          */
616         if (type == FDMAP_IO_SOCKET) {
617             result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
618                 sizeof (nonblocking), &bytes_returned, NULL, NULL);
619         
620             if(result < 0) {
621                 errno = WSAGetLastError();
622                 return -1;
623             }
624
625             if (nonblocking)
626                 return O_NONBLOCK;
627             else
628                 return 0;
629         }
630         break;
631     case F_SETFL:
632         if (type == FDMAP_IO_SOCKET) {
633             va_start(ap, cmd);
634             value = va_arg(ap, int);
635             va_end(ap);
636             if (value & O_NONBLOCK)
637                 nonblocking = 1;
638             else
639                 nonblocking = 0;
640
641             result = WSAIoctl(handle, FIONBIO, &nonblocking,
642                 sizeof (nonblocking), NULL, 0, &bytes_returned,
643                 NULL, NULL);
644
645             if(result < 0) {
646                 errno = WSAGetLastError();
647                 return -1;
648             }
649             return result;
650         }
651         break;
652     case F_SETLK:
653         /*
654          * The POSIX equivalent is not available in WIN32
655          * That implement the file locking in the file open
656          * by using sopen instead of open.
657          */
658         return 0;
659     }
660     errno = EINVAL;
661     return -1;
662 }