src/lib/w32/posixio.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  posixio.c: POSIX IO emulation layer for WIN32
00029  * 
00030  *  Known contributors to this file:
00031  *     Ron Koenderink, 2007
00032  */
00033 
00034 /*
00035  * POSIX has just one kind of file descriptors, while Windows has (at
00036  * least) two: one for sockets and one for files, with separate
00037  * functions to operate on them.  To present a more POSIX-like
00038  * interface to our application code, we provide a compatibility layer
00039  * that maps POSIX file descriptors to sockets and file handles behind
00040  * the scenes.  This actual mapping is done by the fdmap.  It doesn't
00041  * implement the finer points of POSIX correctly.  In particular, the
00042  * actual values of the file descriptors usually differ.
00043  */
00044 
00045 #include <config.h>
00046 
00047 #include <errno.h>
00048 #include <fcntl.h>
00049 #include <io.h>
00050 #include <share.h>
00051 #include <stdio.h>
00052 #include <stdarg.h>
00053 /*
00054  * Need to include winsock2.h before ws2tcpip.h.
00055  * Use sys/socket.h to ensure the #undef NS_ALL
00056  * is not missed after including winsock2.h.
00057  */
00058 #include "sys/socket.h"
00059 #include <sys/stat.h>
00060 #include <ws2tcpip.h>
00061 
00062 #include "misc.h"
00063 #include "sys/uio.h"
00064 #include "unistd.h"
00065 
00066 /*
00067  * FD_SETSIZE is the size for the maximum number of sockets.
00068  * The number of file descriptors is in a variable _nhandle
00069  * based on the assertion output.  In order the simplify the
00070  * code and skip dynamic allocation, used double the socket size.
00071  */
00072 #define MAX_FDS (FD_SETSIZE * 2)
00073 
00074 enum fdmap_io_type {
00075     FDMAP_IO_NOTUSED = 0,
00076     FDMAP_IO_FILE,
00077     FDMAP_IO_SOCKET,
00078     FDMAP_IO_ANY /* used for searching only (lookup_handle) */
00079 };
00080 
00081 struct fdmap {
00082     int handle;
00083     enum fdmap_io_type type;
00084 };
00085 
00086 static struct fdmap fdmap[MAX_FDS] = {
00087     {0, FDMAP_IO_FILE},
00088     {1, FDMAP_IO_FILE},
00089     {2, FDMAP_IO_FILE}
00090 };
00091 static int nfd = 3;
00092 
00093 /*
00094  * Allocate a POSIX equivalent file descriptor.
00095  * Note once get_fd() is called either free_fd() or set_fd()
00096  * must be called before thread mutex is released as the
00097  * allocation/deallocation code is not thread safe.
00098  */
00099 static int
00100 get_fd(void)
00101 {
00102     int fd;
00103 
00104     for (fd = 0; fd < nfd && fdmap[fd].type != FDMAP_IO_NOTUSED; fd++) ;
00105     if (fd == MAX_FDS) {
00106         errno = EMFILE;
00107         return -1;
00108     }
00109     if (fd == nfd) {
00110         fdmap[fd].type = FDMAP_IO_NOTUSED;
00111         nfd++;
00112     }
00113     return fd;
00114 }
00115 
00116 /*
00117  * Deallocate a POSIX equivalent file descriptor.
00118  */
00119 static void
00120 free_fd(int fd)
00121 {
00122     fdmap[fd].type = FDMAP_IO_NOTUSED;
00123     for(; fdmap[nfd - 1].type == FDMAP_IO_NOTUSED; nfd--) ;
00124 }
00125 
00126 /*
00127  * Complete the allocation of the file descriptor.
00128  */
00129 static void
00130 set_fd(int fd, enum fdmap_io_type type, int handle)
00131 {
00132     int i;
00133 
00134     fdmap[fd].handle = handle;
00135     fdmap[fd].type = type;
00136 
00137     /*
00138      * Garbage collection for fileno(), currently not
00139      * replacing fclose() and fcloseall() so do not know when
00140      * a stream is closed.
00141      */
00142     for (i = 0; i < nfd; i++) {
00143         if (i != fd && type == fdmap[i].type && handle == fdmap[i].handle)
00144             free_fd(i);
00145     }
00146 }
00147 
00148 /*
00149  * Find the windows handle (file or socket) for file descriptor.
00150  * Return windows handle and type of handle.
00151  * You can search for a specific type (FDMAP_IO_FILE or FDMAP_IO_SOCKET)
00152  * or for both search by using FDMAP_IO_ANY.
00153  * FDMAP_IO_NOTUSED is not valid type to search with.
00154  */
00155 static int
00156 lookup_handle(int fd, enum fdmap_io_type d_type, int error,
00157         enum fdmap_io_type *type_ptr, int *handle_ptr)
00158 {
00159 
00160     if (fd < 0 || fd >= MAX_FDS) {
00161         if (error != 0)
00162             errno = error;
00163         return 0;
00164     } else if ((fdmap[fd].type != d_type && d_type != FDMAP_IO_ANY) ||
00165         (fdmap[fd].type == FDMAP_IO_NOTUSED && d_type == FDMAP_IO_ANY)) {
00166         if (error != 0)
00167             errno = error;
00168         return 0;
00169     }
00170     if (type_ptr != NULL)
00171         *type_ptr = fdmap[fd].type;
00172     if (handle_ptr != NULL)
00173         *handle_ptr = fdmap[fd].handle;
00174     return 1;
00175 }
00176 
00177 /*
00178  * Find and return the file descriptor associated with windows handle.
00179  * You can search for FDMAP_IO_FILE or FDMAP_IO_SOCKET.
00180  * FDMAP_IO_ANY or FDMAP_IO_NOTUSED is not considered valid search
00181  * criteria.
00182  */
00183 static int
00184 lookup_fd(int handle, enum fdmap_io_type d_type)
00185 {
00186     int i;
00187 
00188     for (i = 0; i < nfd; i++)
00189         if (fdmap[i].handle == handle && fdmap[i].type == d_type)
00190             return i;
00191     return -1;
00192 }
00193 
00194 /*
00195  * Get the window socket handle for POSIX file descriptor.
00196  */
00197 int
00198 posix_fd2socket(int fd)
00199 {
00200     int handle;
00201     enum fdmap_io_type type;
00202 
00203     if (!lookup_handle(fd, FDMAP_IO_SOCKET, WSAENOTSOCK,
00204         &type, &handle))
00205         return INVALID_SOCKET;
00206     return handle;
00207 }
00208 
00209 #define SOCKET_FUNCTION(expr)                           \
00210     int result;                                         \
00211     int handle;                                         \
00212                                                         \
00213     if (!lookup_handle(fd, FDMAP_IO_SOCKET,             \
00214         ENOTSOCK, NULL, &handle))                       \
00215         return -1;                                      \
00216                                                         \
00217     result = (expr);                                    \
00218     if (result == SOCKET_ERROR) {                       \
00219         errno = WSAGetLastError();                      \
00220         return -1;                                      \
00221     }                                                   \
00222     return result;
00223 
00224 /*
00225  * POSIX equivalent for accept().
00226  */
00227 #undef accept
00228 int
00229 posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
00230 {
00231     int new_fd;
00232     int handle;
00233     SOCKET new_handle;
00234 
00235     if (!lookup_handle(fd, FDMAP_IO_SOCKET, ENOTSOCK, NULL, &handle))
00236         return -1;
00237 
00238     new_fd = get_fd();
00239     if (new_fd < 0)
00240         return -1;
00241 
00242     new_handle = accept(handle, addr, addrlen);
00243     if (new_handle == INVALID_SOCKET) {
00244         free_fd(new_fd);
00245         errno = WSAGetLastError();
00246         return -1;
00247     }
00248     set_fd(new_fd, FDMAP_IO_SOCKET, (int)new_handle);
00249     return new_fd;
00250 }
00251 
00252 /*
00253  * POSIX equivalent for bind().
00254  */
00255 #undef bind
00256 int
00257 posix_bind(int fd, const struct sockaddr *name, socklen_t namelen)
00258 {
00259     SOCKET_FUNCTION(bind(handle, name, namelen))
00260 }
00261 
00262 /*
00263  * POSIX equivalent for listen().
00264  */
00265 #undef listen
00266 int
00267 posix_listen(int fd, int backlog)
00268 {
00269     SOCKET_FUNCTION(listen(handle, backlog))
00270 }
00271 
00272 /*
00273  * POSIX equivalent for setsockopt().
00274  */
00275 #undef setsockopt
00276 int
00277 posix_setsockopt(int fd, int level, int optname,
00278                       const void *optval, socklen_t optlen)
00279 {
00280     /*
00281      * SO_REUSEADDR requests to permit another bind even when the
00282      * port is still in state TIME_WAIT.  Windows' SO_REUSEADDR is
00283      * broken: it makes bind() succeed no matter what, even if
00284      * there's another server running on the same port.  Luckily,
00285      * bind() seems to be broken as well: it seems to succeed while
00286      * the port is in state TIME_WAIT by default; thus we get the
00287      * behavior we want by not setting SO_REUSEADDR.
00288      */
00289     if (level == SOL_SOCKET && optname == SO_REUSEADDR)
00290         return 0;
00291     {
00292         SOCKET_FUNCTION(setsockopt(handle, level, optname,
00293                         optval, optlen))
00294     }
00295 }
00296 
00297 /*
00298  * POSIX equivalent for shutdown().
00299  */
00300 #undef shutdown
00301 int
00302 posix_shutdown(int fd, int how)
00303 {
00304     SOCKET_FUNCTION(shutdown(handle, how))
00305 }
00306 
00307 /*
00308  * POSIX equivalent for socket().
00309  */
00310 #undef socket
00311 int
00312 posix_socket(int domain, int type, int protocol)
00313 {
00314     SOCKET handle;
00315     int new_fd;
00316     
00317     if ((new_fd = get_fd()) < 0)
00318         return -1;
00319 
00320     handle = socket(domain, type, protocol);
00321     if (handle == INVALID_SOCKET) {
00322         free_fd(new_fd);
00323         errno = WSAGetLastError();
00324         return -1;
00325     }
00326     set_fd(new_fd, FDMAP_IO_SOCKET, (int)handle);
00327     return new_fd;
00328 }
00329 
00330 #ifdef HAVE_GETADDRINFO
00331 const char *
00332 inet_ntop(int af, const void *src, char *dst, socklen_t len)
00333 {
00334     struct sockaddr *sa;
00335     struct sockaddr_in sin;
00336     struct sockaddr_in6 sin6;
00337     size_t salen;
00338 
00339     if (af == AF_INET) {
00340         memset(&sin, 0, sizeof(sin));
00341         sin.sin_family = af;
00342         memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr));
00343         sa = (struct sockaddr *)&sin;
00344         salen = sizeof(sin);
00345     } else if (af == AF_INET6) {
00346         memset(&sin6, 0, sizeof(sin6));
00347         sin6.sin6_family = af;
00348         memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr));
00349         sa = (struct sockaddr *)&sin6;
00350         salen = sizeof(sin6);
00351     } else {
00352         errno = EAFNOSUPPORT;
00353         return NULL;
00354     }
00355 
00356     if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) {
00357         errno = EAFNOSUPPORT;
00358         return NULL;
00359     }
00360 
00361     return dst;
00362 }
00363 #endif
00364 
00365 #define FILE_FUNCTION(type, expr)                               \
00366     int handle;                                                 \
00367                                                                 \
00368     if (!lookup_handle(fd, (type), EBADF, NULL, &handle))       \
00369         return -1;                                              \
00370                                                                 \
00371     return (expr);
00372 
00373 /*
00374  * POSIX equivalent for close().
00375  */
00376 int
00377 posix_close(int fd)
00378 {
00379     int result;
00380     int handle;
00381     enum fdmap_io_type type;
00382 
00383     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
00384         return -1;
00385 
00386     free_fd(fd);
00387     switch (type) {
00388     case FDMAP_IO_SOCKET:
00389         result = closesocket(handle);
00390         if (result == SOCKET_ERROR) {
00391             errno = WSAGetLastError();
00392             return -1;
00393         }
00394         return result;
00395     case FDMAP_IO_FILE:
00396        return _close(handle);
00397     default:
00398         CANT_REACH();
00399         return -1;
00400     }
00401 }
00402 
00403 /*
00404  * posix_fsync forces file sync with the disk.
00405  * In order for the power report report to accurate timestamp,
00406  * the _commit() is to force a sync with disk and therefore
00407  * an update for file time.
00408  */
00409 int
00410 posix_fsync(int fd)
00411 {
00412     FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
00413 }
00414 
00415 /*
00416  * POSIX equivalent for fstat().
00417  * fstat() is used instead of _fstat(),
00418  * otherwise problems with the 32/64 time definitions
00419  * in WIN32.
00420  */
00421 #undef fstat
00422 int
00423 posix_fstat(int fd, struct stat *buffer)
00424 {
00425     FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
00426 }
00427 
00428 /*
00429  * POSIX equivalent for lseek().
00430  */
00431 off_t
00432 posix_lseek(int fd, off_t offset, int origin)
00433 {
00434     FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
00435 }
00436 
00437 /*
00438  * POSIX equivalent for open().
00439  * Implements file locks when opening files to provide equivalent
00440  * F_GETLK/F_SETLK.
00441  */
00442 int
00443 posix_open(const char *fname, int oflag, ...)
00444 {
00445     va_list ap;
00446     int pmode = 0, new_fd;
00447     int handle;
00448 
00449     if (oflag & O_CREAT) {
00450         va_start(ap, oflag);
00451         pmode = va_arg(ap, int);
00452         va_end(ap);
00453     }
00454 
00455     if ((new_fd = get_fd()) < 0)
00456         return -1;
00457 
00458     /*
00459      * We don't implement fcntl() for F_SETLK.  Instead, we lock *all*
00460      * files we open.  Not ideal, but it works for Empire.
00461      */
00462     handle = _sopen(fname, oflag,
00463         oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
00464     if (handle == -1) {
00465         free_fd(new_fd);
00466         return -1;
00467     }
00468     set_fd(new_fd, FDMAP_IO_FILE, handle);
00469     return new_fd;
00470 }
00471 
00472 #define SHARED_FUNCTION(socket_expr, file_expr)                     \
00473     int result;                                                     \
00474     int handle;                                                     \
00475     enum fdmap_io_type type;                                        \
00476                                                                     \
00477     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))    \
00478         return -1;                                                  \
00479                                                                     \
00480     switch (type) {                                                 \
00481     case FDMAP_IO_SOCKET:                                           \
00482         result = (socket_expr);                                     \
00483         if (result == SOCKET_ERROR) {                               \
00484             errno = WSAGetLastError();                              \
00485             return -1;                                              \
00486         }                                                           \
00487         return result;                                              \
00488     case FDMAP_IO_FILE:                                             \
00489         return (file_expr);                                         \
00490     default:                                                        \
00491         CANT_REACH();                                               \
00492         return -1;                                                  \
00493     }
00494 
00495 /*
00496  * POSIX equivalent for read().
00497  */
00498 ssize_t
00499 posix_read(int fd, void *buffer, size_t count)
00500 {
00501     SHARED_FUNCTION(recv(handle, buffer, count, 0),
00502         _read(handle, buffer, count))
00503 }
00504 
00505 /*
00506  * POSIX equivalent for readv
00507  * Modelled after the GNU's libc/sysdeps/posix/readv.c
00508  */
00509 ssize_t
00510 readv(int fd, const struct iovec *iov, int iovcnt)
00511 {
00512     int i;
00513     unsigned char *buffer, *buffer_location;
00514     size_t total_bytes = 0;
00515     int bytes_read;
00516     size_t bytes_left;
00517 
00518     for (i = 0; i < iovcnt; i++) {
00519         total_bytes += iov[i].iov_len;
00520     }
00521 
00522     buffer = malloc(total_bytes);
00523     if (buffer == NULL && total_bytes != 0) {
00524         errno = ENOMEM;
00525         return -1;
00526     }
00527 
00528     bytes_read = posix_read(fd, buffer, total_bytes);
00529     if (bytes_read <= 0) {
00530         free(buffer);
00531         return -1;
00532     }
00533 
00534     bytes_left = bytes_read;
00535     buffer_location = buffer;
00536     for (i = 0; i < iovcnt; i++) {
00537         size_t copy = MIN(iov[i].iov_len, bytes_left);
00538 
00539         memcpy(iov[i].iov_base, buffer_location, copy);
00540 
00541         buffer_location += copy;
00542         bytes_left -= copy;
00543         if (bytes_left == 0)
00544             break;
00545     }
00546 
00547     free(buffer);
00548 
00549     return bytes_read;
00550 }
00551 
00552 /*
00553  * POSIX equivalent for write().
00554  */
00555 ssize_t
00556 posix_write(int fd, const void *buffer, size_t count)
00557 {
00558     SHARED_FUNCTION(send(handle, buffer, count, 0),
00559         _write(handle, buffer, count))
00560 }
00561 
00562 /*
00563  * POSIX equivalent for writev
00564  * Modelled after the GNU's libc/sysdeps/posix/writev.c
00565  */
00566 ssize_t
00567 writev(int fd, const struct iovec *iov, int iovcnt)
00568 {
00569     int i;
00570     unsigned char *buffer, *buffer_location;
00571     size_t total_bytes = 0;
00572     int bytes_written;
00573 
00574     for (i = 0; i < iovcnt; i++)
00575         total_bytes += iov[i].iov_len;
00576 
00577     buffer = malloc(total_bytes);
00578     if (buffer == NULL && total_bytes != 0) {
00579         errno = ENOMEM;
00580         return -1;
00581     }
00582 
00583     buffer_location = buffer;
00584     for (i = 0; i < iovcnt; i++) {
00585         memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
00586         buffer_location += iov[i].iov_len;
00587     }
00588 
00589     bytes_written = posix_write(fd, buffer, total_bytes);
00590 
00591     free(buffer);
00592 
00593     if (bytes_written <= 0)
00594         return -1;
00595     return bytes_written;
00596 }
00597 
00598 /*
00599  * POSIX equivalent for fileno().
00600  * As fopen/fclose/fcloseall are not implemented as POSIX
00601  * equivalent functions, the mapping is done when required
00602  * by a call to fileno().  The garbage collection of the
00603  * file descriptors allocated is done in set_fd() when the
00604  * handle is reused.
00605  */
00606 int
00607 fileno(FILE *stream)
00608 {
00609     int fd;
00610     int handle;
00611 
00612     if (stream == NULL) {
00613         errno = EBADF;
00614         return -1;
00615     }
00616 
00617     handle = _fileno(stream);
00618 
00619     fd = lookup_fd(handle, FDMAP_IO_FILE);
00620     if (fd >= 0)
00621         return fd;
00622 
00623     if ((fd = get_fd()) < 0) {
00624         errno = EBADF;
00625         return -1;
00626     }
00627 
00628     set_fd(fd, FDMAP_IO_FILE, handle);
00629     return fd;
00630 }
00631 
00632 /*
00633  * POSIX equivalent for fcntl().
00634  * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
00635  * Currently ignores F_GETLK/F_SETLK as the file locks are
00636  * implement in open()
00637  */
00638 int
00639 fcntl(int fd, int cmd, ...)
00640 {
00641     va_list ap;
00642     int value;
00643     unsigned int nonblocking;
00644     int result;
00645     long bytes_returned;
00646     int handle;
00647     enum fdmap_io_type type;
00648 
00649     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
00650         return -1;
00651 
00652     switch (cmd)
00653     {
00654     case F_GETFL:
00655         /*
00656          * F_GETFL and F_SETFL only support O_NONBLOCK
00657          * for sockets currently
00658          */
00659         if (type == FDMAP_IO_SOCKET) {
00660             result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
00661                 sizeof (nonblocking), &bytes_returned, NULL, NULL);
00662         
00663             if(result < 0) {
00664                 errno = WSAGetLastError();
00665                 return -1;
00666             }
00667 
00668             if (nonblocking)
00669                 return O_NONBLOCK;
00670             else
00671                 return 0;
00672         }
00673         break;
00674     case F_SETFL:
00675         if (type == FDMAP_IO_SOCKET) {
00676             va_start(ap, cmd);
00677             value = va_arg(ap, int);
00678             va_end(ap);
00679             if (value & O_NONBLOCK)
00680                 nonblocking = 1;
00681             else
00682                 nonblocking = 0;
00683 
00684             result = WSAIoctl(handle, FIONBIO, &nonblocking,
00685                 sizeof (nonblocking), NULL, 0, &bytes_returned,
00686                 NULL, NULL);
00687 
00688             if(result < 0) {
00689                 errno = WSAGetLastError();
00690                 return -1;
00691             }
00692             return result;
00693         }
00694         break;
00695     case F_SETLK:
00696         /*
00697          * The POSIX equivalent is not available in WIN32
00698          * That implement the file locking in the file open
00699          * by using sopen instead of open.
00700          */
00701         return 0;
00702     }
00703     errno = EINVAL;
00704     return -1;
00705 }

Generated on Fri Mar 28 11:01:16 2008 for empserver by  doxygen 1.5.2