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