1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /* Windows NT IO module
7  *
8  * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
9  * For LOCAL_SCOPE threads, we're using NT fibers.  For GLOBAL_SCOPE threads
10  * we're using NT-native threads.
11  *
12  * When doing IO, we want to use completion ports for optimal performance
13  * with fibers.  But if we use completion ports for all IO, it is difficult
14  * to project a blocking model with GLOBAL_SCOPE threads.  To handle this
15  * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
16  * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
17  * threads because it means extra context switches, which are really slow
18  * on NT...  Since we're using a single completion port, some IO will
19  * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
20  * extra context switching; but I don't think there is anything I can do
21  * about it.
22  */
23 
24 #include "primpl.h"
25 #include "pprmwait.h"
26 #include <direct.h>
27 #include <mbstring.h>
28 
29 static HANDLE                _pr_completion_port;
30 static PRThread             *_pr_io_completion_thread;
31 
32 #define RECYCLE_SIZE 512
33 static struct _MDLock        _pr_recycle_lock;
34 static PRInt32               _pr_recycle_INET_array[RECYCLE_SIZE];
35 static PRInt32               _pr_recycle_INET_tail = 0;
36 static PRInt32               _pr_recycle_INET6_array[RECYCLE_SIZE];
37 static PRInt32               _pr_recycle_INET6_tail = 0;
38 
39 __declspec(thread) PRThread *_pr_io_restarted_io = NULL;
40 DWORD _pr_io_restartedIOIndex;  /* The thread local storage slot for each
41                                  * thread is initialized to NULL. */
42 
43 PRBool                       _nt_version_gets_lockfile_completion;
44 
45 struct _MDLock               _pr_ioq_lock;
46 extern _MDLock               _nt_idleLock;
47 extern PRCList               _nt_idleList;
48 extern PRUint32              _nt_idleCount;
49 
50 #define CLOSE_TIMEOUT   PR_SecondsToInterval(5)
51 
52 /*
53  * NSPR-to-NT access right mapping table for files.
54  */
55 static DWORD fileAccessTable[] = {
56     FILE_GENERIC_READ,
57     FILE_GENERIC_WRITE,
58     FILE_GENERIC_EXECUTE
59 };
60 
61 /*
62  * NSPR-to-NT access right mapping table for directories.
63  */
64 static DWORD dirAccessTable[] = {
65     FILE_GENERIC_READ,
66     FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
67     FILE_GENERIC_EXECUTE
68 };
69 
70 static PRBool IsPrevCharSlash(const char *str, const char *current);
71 
72 #define _NEED_351_FILE_LOCKING_HACK
73 #ifdef _NEED_351_FILE_LOCKING_HACK
74 #define _PR_LOCAL_FILE 1
75 #define _PR_REMOTE_FILE 2
76 PRBool IsFileLocalInit();
77 PRInt32 IsFileLocal(HANDLE hFile);
78 #endif /* _NEED_351_FILE_LOCKING_HACK */
79 
80 static PRInt32 _md_MakeNonblock(HANDLE);
81 
82 static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime);
83 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime);
84 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime);
85 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
86 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime);
87 static PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
88 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
89 
90 /*
91  * We cannot associate a fd (a socket) with an I/O completion port
92  * if the fd is nonblocking or inheritable.
93  *
94  * Nonblocking socket I/O won't work if the socket is associated with
95  * an I/O completion port.
96  *
97  * An inheritable fd cannot be associated with an I/O completion port
98  * because the completion notification of async I/O initiated by the
99  * child process is still posted to the I/O completion port in the
100  * parent process.
101  */
102 #define _NT_USE_NB_IO(fd) \
103     ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE)
104 
105 /*
106  * UDP support
107  *
108  * UDP is supported on NT by the continuation thread mechanism.
109  * The code is borrowed from ptio.c in pthreads nspr, hence the
110  * PT and pt prefixes.  This mechanism is in fact general and
111  * not limited to UDP.  For now, only UDP's recvfrom and sendto
112  * go through the continuation thread if they get WSAEWOULDBLOCK
113  * on first try.  Recv and send on a connected UDP socket still
114  * goes through asychronous io.
115  */
116 
117 #define PT_DEFAULT_SELECT_MSEC 100
118 
119 typedef struct pt_Continuation pt_Continuation;
120 typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
121 
122 typedef enum pr_ContuationStatus
123 {
124     pt_continuation_sumbitted,
125     pt_continuation_inprogress,
126     pt_continuation_abort,
127     pt_continuation_done
128 } pr_ContuationStatus;
129 
130 struct pt_Continuation
131 {
132     /* These objects are linked in ascending timeout order */
133     pt_Continuation *next, *prev;           /* self linked list of these things */
134 
135     /* The building of the continuation operation */
136     ContinuationFn function;                /* what function to continue */
137     union {
138         SOCKET osfd;
139     } arg1;            /* #1 - the op's fd */
140     union {
141         void* buffer;
142     } arg2;           /* #2 - primary transfer buffer */
143     union {
144         PRIntn amount;
145     } arg3;          /* #3 - size of 'buffer' */
146     union {
147         PRIntn flags;
148     } arg4;           /* #4 - read/write flags */
149     union {
150         PRNetAddr *addr;
151     } arg5;        /* #5 - send/recv address */
152 
153     PRIntervalTime timeout;                 /* representation of the timeout */
154 
155     PRIntn event;                           /* flags for select()'s events */
156 
157     /*
158     ** The representation and notification of the results of the operation.
159     ** These function can either return an int return code or a pointer to
160     ** some object.
161     */
162     union {
163         PRIntn code;
164         void *object;
165     } result;
166 
167     PRIntn syserrno;                        /* in case it failed, why (errno) */
168     pr_ContuationStatus status;             /* the status of the operation */
169     PRCondVar *complete;                    /* to notify the initiating thread */
170 };
171 
172 static struct pt_TimedQueue
173 {
174     PRLock *ml;                             /* a little protection */
175     PRThread *thread;                       /* internal thread's identification */
176     PRCondVar *new_op;                      /* new operation supplied */
177     PRCondVar *finish_op;                   /* an existing operation finished */
178     PRUintn op_count;                       /* number of operations in the list */
179     pt_Continuation *head, *tail;           /* head/tail of list of operations */
180 
181     pt_Continuation *op;                    /* timed operation furthest in future */
182     PRIntervalTime epoch;                   /* the epoch of 'timed' */
183 } pt_tq;
184 
185 #if defined(DEBUG)
186 static struct pt_debug_s
187 {
188     PRIntn predictionsFoiled;
189     PRIntn pollingListMax;
190     PRIntn continuationsServed;
191 } pt_debug;
192 #endif  /* DEBUG */
193 
194 static void ContinuationThread(void *arg);
195 static PRInt32 pt_SendTo(
196     SOCKET osfd, const void *buf,
197     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
198     PRIntn addrlen, PRIntervalTime timeout);
199 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
200                            PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
201 
202 
203 /* The key returned from GetQueuedCompletionStatus() is used to determine what
204  * type of completion we have.  We differentiate between IO completions and
205  * CVAR completions.
206  */
207 #define KEY_IO              0xaaaaaaaa
208 #define KEY_CVAR            0xbbbbbbbb
209 
210 PRInt32
_PR_MD_PAUSE_CPU(PRIntervalTime ticks)211 _PR_MD_PAUSE_CPU(PRIntervalTime ticks)
212 {
213     int awoken = 0;
214     unsigned long bytes, key;
215     int rv;
216     LPOVERLAPPED olp;
217     _MDOverlapped *mdOlp;
218     PRUint32 timeout;
219 
220     if (_nt_idleCount > 0) {
221         PRThread *deadThread;
222 
223         _MD_LOCK(&_nt_idleLock);
224         while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
225             deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
226             PR_REMOVE_LINK(&deadThread->links);
227 
228             PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
229 
230             /* XXXMB - cleanup to do here? */
231             if ( !_PR_IS_NATIVE_THREAD(deadThread) ) {
232                 /* Spinlock while user thread is still running.
233                  * There is no way to use a condition variable here. The thread
234                  * is dead, and we have to wait until we switch off the dead
235                  * thread before we can kill the fiber completely.
236                  */
237                 while ( deadThread->no_sched)
238                     ;
239 
240                 DeleteFiber(deadThread->md.fiber_id);
241             }
242             memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
243             if (!deadThread->threadAllocatedOnStack) {
244                 PR_DELETE(deadThread);
245             }
246             _nt_idleCount--;
247         }
248         _MD_UNLOCK(&_nt_idleLock);
249     }
250 
251     if (ticks == PR_INTERVAL_NO_TIMEOUT)
252 #if 0
253         timeout = INFINITE;
254 #else
255         /*
256          * temporary hack to poll the runq every 5 seconds because of bug in
257          * native threads creating user threads and not poking the right cpu.
258          *
259          * A local thread that was interrupted is bound to its current
260          * cpu but there is no easy way for the interrupter to poke the
261          * right cpu.  This is a hack to poll the runq every 5 seconds.
262          */
263         timeout = 5000;
264 #endif
265     else {
266         timeout = PR_IntervalToMilliseconds(ticks);
267     }
268 
269     /*
270      * The idea of looping here is to complete as many IOs as possible before
271      * returning.  This should minimize trips to the idle thread.
272      */
273     while(1) {
274         rv = GetQueuedCompletionStatus(
275                  _pr_completion_port,
276                  &bytes,
277                  &key,
278                  &olp,
279                  timeout);
280         if (rv == 0 && olp == NULL) {
281             /* Error in GetQueuedCompetionStatus */
282             if (GetLastError() != WAIT_TIMEOUT) {
283                 /* ARGH - what can we do here? Log an error? XXXMB */
284                 return -1;
285             } else {
286                 /* If awoken == 0, then we just had a timeout */
287                 return awoken;
288             }
289         }
290 
291         if (olp == NULL) {
292             return 0;
293         }
294 
295         mdOlp = (_MDOverlapped *)olp;
296 
297         if (mdOlp->ioModel == _MD_MultiWaitIO) {
298             PRRecvWait *desc;
299             PRWaitGroup *group;
300             PRThread *thred = NULL;
301             PRMWStatus mwstatus;
302 
303             desc = mdOlp->data.mw.desc;
304             PR_ASSERT(desc != NULL);
305             mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
306             if (InterlockedCompareExchange((PVOID *)&desc->outcome,
307                                            (PVOID)mwstatus, (PVOID)PR_MW_PENDING)
308                 == (PVOID)PR_MW_PENDING) {
309                 if (mwstatus == PR_MW_SUCCESS) {
310                     desc->bytesRecv = bytes;
311                 } else {
312                     mdOlp->data.mw.error = GetLastError();
313                 }
314             }
315             group = mdOlp->data.mw.group;
316             PR_ASSERT(group != NULL);
317 
318             _PR_MD_LOCK(&group->mdlock);
319             PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
320             PR_ASSERT(desc->fd != NULL);
321             NT_HashRemoveInternal(group, desc->fd);
322             if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
323                 thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
324                 PR_REMOVE_LINK(&thred->waitQLinks);
325             }
326             _PR_MD_UNLOCK(&group->mdlock);
327 
328             if (thred) {
329                 if (!_PR_IS_NATIVE_THREAD(thred)) {
330                     int pri = thred->priority;
331                     _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
332                     _PR_THREAD_LOCK(thred);
333                     if (thred->flags & _PR_ON_PAUSEQ) {
334                         _PR_SLEEPQ_LOCK(thred->cpu);
335                         _PR_DEL_SLEEPQ(thred, PR_TRUE);
336                         _PR_SLEEPQ_UNLOCK(thred->cpu);
337                         _PR_THREAD_UNLOCK(thred);
338                         thred->cpu = lockedCPU;
339                         thred->state = _PR_RUNNABLE;
340                         _PR_RUNQ_LOCK(lockedCPU);
341                         _PR_ADD_RUNQ(thred, lockedCPU, pri);
342                         _PR_RUNQ_UNLOCK(lockedCPU);
343                     } else {
344                         /*
345                          * The thread was just interrupted and moved
346                          * from the pause queue to the run queue.
347                          */
348                         _PR_THREAD_UNLOCK(thred);
349                     }
350                 } else {
351                     _PR_THREAD_LOCK(thred);
352                     thred->state = _PR_RUNNABLE;
353                     _PR_THREAD_UNLOCK(thred);
354                     ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
355                 }
356             }
357         } else {
358             PRThread *completed_io;
359 
360             PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
361             completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
362             completed_io->md.blocked_io_status = rv;
363             if (rv == 0) {
364                 completed_io->md.blocked_io_error = GetLastError();
365             }
366             completed_io->md.blocked_io_bytes = bytes;
367 
368             if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
369                 int pri = completed_io->priority;
370                 _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
371 
372                 /* The KEY_CVAR notification only occurs when a native thread
373                  * is notifying a user thread.  For user-user notifications
374                  * the wakeup occurs by having the notifier place the thread
375                  * on the runq directly; for native-native notifications the
376                  * wakeup occurs by calling ReleaseSemaphore.
377                  */
378                 if ( key == KEY_CVAR ) {
379                     PR_ASSERT(completed_io->io_pending == PR_FALSE);
380                     PR_ASSERT(completed_io->io_suspended == PR_FALSE);
381                     PR_ASSERT(completed_io->md.thr_bound_cpu == NULL);
382 
383                     /* Thread has already been deleted from sleepQ */
384 
385                     /* Switch CPU and add to runQ */
386                     completed_io->cpu = lockedCPU;
387                     completed_io->state = _PR_RUNNABLE;
388                     _PR_RUNQ_LOCK(lockedCPU);
389                     _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
390                     _PR_RUNQ_UNLOCK(lockedCPU);
391                 } else {
392                     PR_ASSERT(key == KEY_IO);
393                     PR_ASSERT(completed_io->io_pending == PR_TRUE);
394 
395                     _PR_THREAD_LOCK(completed_io);
396 
397                     completed_io->io_pending = PR_FALSE;
398 
399                     /* If io_suspended is true, then this IO has already resumed.
400                      * We don't need to do anything; because the thread is
401                      * already running.
402                      */
403                     if (completed_io->io_suspended == PR_FALSE) {
404                         if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
405                             _PR_SLEEPQ_LOCK(completed_io->cpu);
406                             _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
407                             _PR_SLEEPQ_UNLOCK(completed_io->cpu);
408 
409                             _PR_THREAD_UNLOCK(completed_io);
410 
411                             /*
412                              * If an I/O operation is suspended, the thread
413                              * must be running on the same cpu on which the
414                              * I/O operation was issued.
415                              */
416                             PR_ASSERT(!completed_io->md.thr_bound_cpu ||
417                                       (completed_io->cpu == completed_io->md.thr_bound_cpu));
418 
419                             if (!completed_io->md.thr_bound_cpu) {
420                                 completed_io->cpu = lockedCPU;
421                             }
422                             completed_io->state = _PR_RUNNABLE;
423                             _PR_RUNQ_LOCK(completed_io->cpu);
424                             _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri);
425                             _PR_RUNQ_UNLOCK(completed_io->cpu);
426                         } else {
427                             _PR_THREAD_UNLOCK(completed_io);
428                         }
429                     } else {
430                         _PR_THREAD_UNLOCK(completed_io);
431                     }
432                 }
433             } else {
434                 /* For native threads, they are only notified through this loop
435                  * when completing IO.  So, don't worry about this being a CVAR
436                  * notification, because that is not possible.
437                  */
438                 _PR_THREAD_LOCK(completed_io);
439                 completed_io->io_pending = PR_FALSE;
440                 if (completed_io->io_suspended == PR_FALSE) {
441                     completed_io->state = _PR_RUNNABLE;
442                     _PR_THREAD_UNLOCK(completed_io);
443                     rv = ReleaseSemaphore(completed_io->md.blocked_sema,
444                                           1, NULL);
445                     PR_ASSERT(0 != rv);
446                 } else {
447                     _PR_THREAD_UNLOCK(completed_io);
448                 }
449             }
450         }
451 
452         awoken++;
453         timeout = 0;   /* Don't block on subsequent trips through the loop */
454     }
455 
456     /* never reached */
457     return 0;
458 }
459 
460 static PRStatus
_native_thread_md_wait(PRThread * thread,PRIntervalTime ticks)461 _native_thread_md_wait(PRThread *thread, PRIntervalTime ticks)
462 {
463     DWORD rv;
464     PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
465                      INFINITE : PR_IntervalToMilliseconds(ticks);
466 
467     /*
468      * thread waiting for a cvar or a joining thread
469      */
470     rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
471     switch(rv) {
472         case WAIT_OBJECT_0:
473             return PR_SUCCESS;
474             break;
475         case WAIT_TIMEOUT:
476             _PR_THREAD_LOCK(thread);
477             PR_ASSERT (thread->state != _PR_IO_WAIT);
478             if (thread->wait.cvar != NULL) {
479                 PR_ASSERT(thread->state == _PR_COND_WAIT);
480                 thread->wait.cvar = NULL;
481                 thread->state = _PR_RUNNING;
482                 _PR_THREAD_UNLOCK(thread);
483             } else {
484                 /* The CVAR was notified just as the timeout
485                  * occurred.  This left the semaphore in the
486                  * signaled state.  Call WaitForSingleObject()
487                  * to clear the semaphore.
488                  */
489                 _PR_THREAD_UNLOCK(thread);
490                 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
491                 PR_ASSERT(rv == WAIT_OBJECT_0);
492             }
493             return PR_SUCCESS;
494             break;
495         default:
496             return PR_FAILURE;
497             break;
498     }
499 
500     return PR_SUCCESS;
501 }
502 
503 PRStatus
_PR_MD_WAIT(PRThread * thread,PRIntervalTime ticks)504 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
505 {
506     DWORD rv;
507 
508     if (_native_threads_only) {
509         return(_native_thread_md_wait(thread, ticks));
510     }
511     if ( thread->flags & _PR_GLOBAL_SCOPE ) {
512         PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
513                          INFINITE : PR_IntervalToMilliseconds(ticks);
514         rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
515         switch(rv) {
516             case WAIT_OBJECT_0:
517                 return PR_SUCCESS;
518                 break;
519             case WAIT_TIMEOUT:
520                 _PR_THREAD_LOCK(thread);
521                 if (thread->state == _PR_IO_WAIT) {
522                     if (thread->io_pending == PR_TRUE) {
523                         thread->state = _PR_RUNNING;
524                         thread->io_suspended = PR_TRUE;
525                         _PR_THREAD_UNLOCK(thread);
526                     } else {
527                         /* The IO completed just at the same time the timeout
528                          * occurred.  This left the semaphore in the signaled
529                          * state.  Call WaitForSingleObject() to clear the
530                          * semaphore.
531                          */
532                         _PR_THREAD_UNLOCK(thread);
533                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
534                         PR_ASSERT(rv == WAIT_OBJECT_0);
535                     }
536                 } else {
537                     if (thread->wait.cvar != NULL) {
538                         PR_ASSERT(thread->state == _PR_COND_WAIT);
539                         thread->wait.cvar = NULL;
540                         thread->state = _PR_RUNNING;
541                         _PR_THREAD_UNLOCK(thread);
542                     } else {
543                         /* The CVAR was notified just as the timeout
544                          * occurred.  This left the semaphore in the
545                          * signaled state.  Call WaitForSingleObject()
546                          * to clear the semaphore.
547                          */
548                         _PR_THREAD_UNLOCK(thread);
549                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
550                         PR_ASSERT(rv == WAIT_OBJECT_0);
551                     }
552                 }
553                 return PR_SUCCESS;
554                 break;
555             default:
556                 return PR_FAILURE;
557                 break;
558         }
559     } else {
560         PRInt32 is;
561 
562         _PR_INTSOFF(is);
563         _PR_MD_SWITCH_CONTEXT(thread);
564     }
565 
566     return PR_SUCCESS;
567 }
568 
569 static void
_native_thread_io_nowait(PRThread * thread,int rv,int bytes)570 _native_thread_io_nowait(
571     PRThread *thread,
572     int rv,
573     int bytes)
574 {
575     int rc;
576 
577     PR_ASSERT(rv != 0);
578     _PR_THREAD_LOCK(thread);
579     if (thread->state == _PR_IO_WAIT) {
580         PR_ASSERT(thread->io_suspended == PR_FALSE);
581         PR_ASSERT(thread->io_pending == PR_TRUE);
582         thread->state = _PR_RUNNING;
583         thread->io_pending = PR_FALSE;
584         _PR_THREAD_UNLOCK(thread);
585     } else {
586         /* The IO completed just at the same time the
587          * thread was interrupted. This left the semaphore
588          * in the signaled state. Call WaitForSingleObject()
589          * to clear the semaphore.
590          */
591         PR_ASSERT(thread->io_suspended == PR_TRUE);
592         PR_ASSERT(thread->io_pending == PR_TRUE);
593         thread->io_pending = PR_FALSE;
594         _PR_THREAD_UNLOCK(thread);
595         rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
596         PR_ASSERT(rc == WAIT_OBJECT_0);
597     }
598 
599     thread->md.blocked_io_status = rv;
600     thread->md.blocked_io_bytes = bytes;
601     rc = ResetEvent(thread->md.thr_event);
602     PR_ASSERT(rc != 0);
603     return;
604 }
605 
606 static PRStatus
_native_thread_io_wait(PRThread * thread,PRIntervalTime ticks)607 _native_thread_io_wait(PRThread *thread, PRIntervalTime ticks)
608 {
609     DWORD rv, bytes;
610 #define _NATIVE_IO_WAIT_HANDLES     2
611 #define _NATIVE_WAKEUP_EVENT_INDEX  0
612 #define _NATIVE_IO_EVENT_INDEX      1
613 
614     HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES];
615 
616     PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
617                      INFINITE : PR_IntervalToMilliseconds(ticks);
618 
619     PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE);
620 
621     wait_handles[0] = thread->md.blocked_sema;
622     wait_handles[1] = thread->md.thr_event;
623     rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles,
624                                 FALSE, msecs);
625 
626     switch(rv) {
627         case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX:
628             /*
629              * I/O op completed
630              */
631             _PR_THREAD_LOCK(thread);
632             if (thread->state == _PR_IO_WAIT) {
633 
634                 PR_ASSERT(thread->io_suspended == PR_FALSE);
635                 PR_ASSERT(thread->io_pending == PR_TRUE);
636                 thread->state = _PR_RUNNING;
637                 thread->io_pending = PR_FALSE;
638                 _PR_THREAD_UNLOCK(thread);
639             } else {
640                 /* The IO completed just at the same time the
641                  * thread was interrupted. This led to us being
642                  * notified twice. Call WaitForSingleObject()
643                  * to clear the semaphore.
644                  */
645                 PR_ASSERT(thread->io_suspended == PR_TRUE);
646                 PR_ASSERT(thread->io_pending == PR_TRUE);
647                 thread->io_pending = PR_FALSE;
648                 _PR_THREAD_UNLOCK(thread);
649                 rv = WaitForSingleObject(thread->md.blocked_sema,
650                                          INFINITE);
651                 PR_ASSERT(rv == WAIT_OBJECT_0);
652             }
653 
654             rv = GetOverlappedResult((HANDLE) thread->io_fd,
655                                      &thread->md.overlapped.overlapped, &bytes, FALSE);
656 
657             thread->md.blocked_io_status = rv;
658             if (rv != 0) {
659                 thread->md.blocked_io_bytes = bytes;
660             } else {
661                 thread->md.blocked_io_error = GetLastError();
662                 PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error);
663             }
664             rv = ResetEvent(thread->md.thr_event);
665             PR_ASSERT(rv != 0);
666             break;
667         case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX:
668             /*
669              * I/O interrupted;
670              */
671 #ifdef DEBUG
672             _PR_THREAD_LOCK(thread);
673             PR_ASSERT(thread->io_suspended == PR_TRUE);
674             _PR_THREAD_UNLOCK(thread);
675 #endif
676             break;
677         case WAIT_TIMEOUT:
678             _PR_THREAD_LOCK(thread);
679             if (thread->state == _PR_IO_WAIT) {
680                 thread->state = _PR_RUNNING;
681                 thread->io_suspended = PR_TRUE;
682                 _PR_THREAD_UNLOCK(thread);
683             } else {
684                 /*
685                  * The thread was interrupted just as the timeout
686                  * occurred. This left the semaphore in the signaled
687                  * state. Call WaitForSingleObject() to clear the
688                  * semaphore.
689                  */
690                 PR_ASSERT(thread->io_suspended == PR_TRUE);
691                 _PR_THREAD_UNLOCK(thread);
692                 rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
693                 PR_ASSERT(rv == WAIT_OBJECT_0);
694             }
695             break;
696         default:
697             return PR_FAILURE;
698             break;
699     }
700 
701     return PR_SUCCESS;
702 }
703 
704 
705 static PRStatus
_NT_IO_WAIT(PRThread * thread,PRIntervalTime timeout)706 _NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
707 {
708     PRBool fWait = PR_TRUE;
709 
710     if (_native_threads_only) {
711         return(_native_thread_io_wait(thread, timeout));
712     }
713     if (!_PR_IS_NATIVE_THREAD(thread))  {
714 
715         _PR_THREAD_LOCK(thread);
716 
717         /* The IO may have already completed; if so, don't add to sleepQ,
718          * since we are already on the runQ!
719          */
720         if (thread->io_pending == PR_TRUE) {
721             _PR_SLEEPQ_LOCK(thread->cpu);
722             _PR_ADD_SLEEPQ(thread, timeout);
723             _PR_SLEEPQ_UNLOCK(thread->cpu);
724         } else {
725             fWait = PR_FALSE;
726         }
727         _PR_THREAD_UNLOCK(thread);
728     }
729     if (fWait) {
730         return _PR_MD_WAIT(thread, timeout);
731     }
732     else {
733         return PR_SUCCESS;
734     }
735 }
736 
737 /*
738  * Unblock threads waiting for I/O
739  * used when interrupting threads
740  *
741  * NOTE: The thread lock should held when this function is called.
742  * On return, the thread lock is released.
743  */
_PR_Unblock_IO_Wait(PRThread * thr)744 void _PR_Unblock_IO_Wait(PRThread *thr)
745 {
746     PRStatus rv;
747     _PRCPU *cpu = thr->cpu;
748 
749     PR_ASSERT(thr->state == _PR_IO_WAIT);
750     /*
751      * A thread for which an I/O timed out or was interrupted cannot be
752      * in an IO_WAIT state except as a result of calling PR_Close or
753      * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state
754      * is not interruptible
755      */
756     if (thr->md.interrupt_disabled == PR_TRUE) {
757         _PR_THREAD_UNLOCK(thr);
758         return;
759     }
760     thr->io_suspended = PR_TRUE;
761     thr->state = _PR_RUNNABLE;
762 
763     if (!_PR_IS_NATIVE_THREAD(thr)) {
764         PRThread *me = _PR_MD_CURRENT_THREAD();
765         PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
766         _PR_SLEEPQ_LOCK(cpu);
767         _PR_DEL_SLEEPQ(thr, PR_TRUE);
768         _PR_SLEEPQ_UNLOCK(cpu);
769         /*
770          * this thread will continue to run on the same cpu until the
771          * I/O is aborted by closing the FD or calling CancelIO
772          */
773         thr->md.thr_bound_cpu = cpu;
774 
775         PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
776         _PR_AddThreadToRunQ(me, thr);
777     }
778     _PR_THREAD_UNLOCK(thr);
779     rv = _PR_MD_WAKEUP_WAITER(thr);
780     PR_ASSERT(PR_SUCCESS == rv);
781 }
782 
783 /* Resume an outstanding IO; requires that after the switch, we disable */
784 static PRStatus
_NT_ResumeIO(PRThread * thread,PRIntervalTime ticks)785 _NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
786 {
787     PRBool fWait = PR_TRUE;
788 
789     if (!_PR_IS_NATIVE_THREAD(thread)) {
790         if (_pr_use_static_tls) {
791             _pr_io_restarted_io = thread;
792         } else {
793             TlsSetValue(_pr_io_restartedIOIndex, thread);
794         }
795     } else {
796         _PR_THREAD_LOCK(thread);
797         if (!thread->io_pending) {
798             fWait = PR_FALSE;
799         }
800         thread->io_suspended = PR_FALSE;
801 
802         _PR_THREAD_UNLOCK(thread);
803     }
804     /* We don't put ourselves back on the sleepQ yet; until we
805      * set the suspended bit to false, we can't do that.  Just save
806      * the sleep time here, and then continue.  The restarted_io handler
807      * will add us to the sleepQ if needed.
808      */
809     thread->sleep = ticks;
810 
811     if (fWait) {
812         if (!_PR_IS_NATIVE_THREAD(thread)) {
813             return _PR_MD_WAIT(thread, ticks);
814         }
815         else {
816             return _NT_IO_WAIT(thread, ticks);
817         }
818     }
819     return PR_SUCCESS;
820 }
821 
822 PRStatus
_PR_MD_WAKEUP_WAITER(PRThread * thread)823 _PR_MD_WAKEUP_WAITER(PRThread *thread)
824 {
825     if (thread == NULL) {
826         /* If thread is NULL, we aren't waking a thread, we're just poking
827          * idle thread
828          */
829         if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
830                                         KEY_CVAR, NULL) == FALSE) {
831             return PR_FAILURE;
832         }
833         return PR_SUCCESS;
834     }
835 
836     if ( _PR_IS_NATIVE_THREAD(thread) ) {
837         if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) {
838             return PR_FAILURE;
839         }
840         else {
841             return PR_SUCCESS;
842         }
843     } else {
844         PRThread *me = _PR_MD_CURRENT_THREAD();
845 
846         /* When a Native thread has to awaken a user thread, it has to poke
847          * the completion port because all user threads might be idle, and
848          * thus the CPUs are just waiting for a completion.
849          *
850          * XXXMB - can we know when we are truely idle (and not checking
851          *         the runq)?
852          */
853         if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) &&
854             (!thread->md.thr_bound_cpu)) {
855             /* The thread should not be in any queue */
856             PR_ASSERT(thread->queueCount == 0);
857             if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
858                                             KEY_CVAR, &(thread->md.overlapped.overlapped)) == FALSE) {
859                 return PR_FAILURE;
860             }
861         }
862         return PR_SUCCESS;
863     }
864 }
865 
866 void
_PR_MD_INIT_IO()867 _PR_MD_INIT_IO()
868 {
869     WORD WSAVersion = 0x0101;
870     WSADATA WSAData;
871     int err;
872     OSVERSIONINFO OSversion;
873 
874     err = WSAStartup( WSAVersion, &WSAData );
875     PR_ASSERT(0 == err);
876 
877     _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
878                           NULL,
879                           0,
880                           0);
881 
882     _MD_NEW_LOCK(&_pr_recycle_lock);
883     _MD_NEW_LOCK(&_pr_ioq_lock);
884 
885     OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
886     if (GetVersionEx(&OSversion)) {
887         _nt_version_gets_lockfile_completion = PR_FALSE;
888         if (OSversion.dwMajorVersion >= 4) {
889             _nt_version_gets_lockfile_completion = PR_TRUE;
890         }
891     } else {
892         PR_ASSERT(0);
893     }
894 
895 #ifdef _NEED_351_FILE_LOCKING_HACK
896     IsFileLocalInit();
897 #endif /* _NEED_351_FILE_LOCKING_HACK */
898 
899     /*
900      * UDP support: start up the continuation thread
901      */
902 
903     pt_tq.op_count = 0;
904     pt_tq.head = pt_tq.tail = NULL;
905     pt_tq.ml = PR_NewLock();
906     PR_ASSERT(NULL != pt_tq.ml);
907     pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
908     PR_ASSERT(NULL != pt_tq.new_op);
909 #if defined(DEBUG)
910     memset(&pt_debug, 0, sizeof(struct pt_debug_s));
911 #endif
912 
913     pt_tq.thread = PR_CreateThread(
914                        PR_SYSTEM_THREAD, ContinuationThread, NULL,
915                        PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
916 
917     PR_ASSERT(NULL != pt_tq.thread);
918 
919 #ifdef DEBUG
920     /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
921     {
922         SYSTEMTIME systime;
923         union {
924             PRTime prt;
925             FILETIME ft;
926         } filetime;
927         BOOL rv;
928 
929         systime.wYear = 1970;
930         systime.wMonth = 1;
931         /* wDayOfWeek is ignored */
932         systime.wDay = 1;
933         systime.wHour = 0;
934         systime.wMinute = 0;
935         systime.wSecond = 0;
936         systime.wMilliseconds = 0;
937 
938         rv = SystemTimeToFileTime(&systime, &filetime.ft);
939         PR_ASSERT(0 != rv);
940         PR_ASSERT(filetime.prt == _pr_filetime_offset);
941     }
942 #endif /* DEBUG */
943 
944     _PR_NT_InitSids();
945 }
946 
947 /* --- SOCKET IO --------------------------------------------------------- */
948 
949 /* _md_get_recycled_socket()
950  * Get a socket from the recycle bin; if no sockets are in the bin,
951  * create one.  The socket will be passed to AcceptEx() as the
952  * second argument.
953  */
954 static SOCKET
_md_get_recycled_socket(int af)955 _md_get_recycled_socket(int af)
956 {
957     SOCKET rv;
958 
959     _MD_LOCK(&_pr_recycle_lock);
960     if (af == AF_INET && _pr_recycle_INET_tail) {
961         _pr_recycle_INET_tail--;
962         rv = _pr_recycle_INET_array[_pr_recycle_INET_tail];
963         _MD_UNLOCK(&_pr_recycle_lock);
964         return rv;
965     }
966     if (af == AF_INET6 && _pr_recycle_INET6_tail) {
967         _pr_recycle_INET6_tail--;
968         rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail];
969         _MD_UNLOCK(&_pr_recycle_lock);
970         return rv;
971     }
972     _MD_UNLOCK(&_pr_recycle_lock);
973 
974     rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
975     if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
976         closesocket(rv);
977         return INVALID_SOCKET;
978     }
979     return rv;
980 }
981 
982 /* _md_put_recycled_socket()
983  * Add a socket to the recycle bin.
984  */
985 static void
_md_put_recycled_socket(SOCKET newsock,int af)986 _md_put_recycled_socket(SOCKET newsock, int af)
987 {
988     PR_ASSERT(_pr_recycle_INET_tail >= 0);
989     PR_ASSERT(_pr_recycle_INET6_tail >= 0);
990 
991     _MD_LOCK(&_pr_recycle_lock);
992     if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) {
993         _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock;
994         _pr_recycle_INET_tail++;
995         _MD_UNLOCK(&_pr_recycle_lock);
996     } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) {
997         _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock;
998         _pr_recycle_INET6_tail++;
999         _MD_UNLOCK(&_pr_recycle_lock);
1000     } else {
1001         _MD_UNLOCK(&_pr_recycle_lock);
1002         closesocket(newsock);
1003     }
1004 
1005     return;
1006 }
1007 
1008 /* _md_Associate()
1009  * Associates a file with the completion port.
1010  * Returns 0 on failure, 1 on success.
1011  */
1012 PRInt32
_md_Associate(HANDLE file)1013 _md_Associate(HANDLE file)
1014 {
1015     HANDLE port;
1016 
1017     if (!_native_threads_only) {
1018         port = CreateIoCompletionPort((HANDLE)file,
1019                                       _pr_completion_port,
1020                                       KEY_IO,
1021                                       0);
1022 
1023         /* XXX should map error codes on failures */
1024         return (port == _pr_completion_port);
1025     } else {
1026         return 1;
1027     }
1028 }
1029 
1030 /*
1031  * _md_MakeNonblock()
1032  * Make a socket nonblocking.
1033  * Returns 0 on failure, 1 on success.
1034  */
1035 static PRInt32
_md_MakeNonblock(HANDLE file)1036 _md_MakeNonblock(HANDLE file)
1037 {
1038     int rv;
1039     u_long one = 1;
1040 
1041     rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
1042     /* XXX should map error codes on failures */
1043     return (rv == 0);
1044 }
1045 
1046 static int missing_completions = 0;
1047 static int max_wait_loops = 0;
1048 
1049 static PRInt32
_NT_IO_ABORT(PROsfd sock)1050 _NT_IO_ABORT(PROsfd sock)
1051 {
1052     PRThread *me = _PR_MD_CURRENT_THREAD();
1053     PRBool fWait;
1054     PRInt32 rv;
1055     int loop_count;
1056 
1057     /* This is a clumsy way to abort the IO, but it is all we can do.
1058      * It looks a bit racy, but we handle all the cases.
1059      * case 1:  IO completes before calling closesocket
1060      *     case 1a:  fWait is set to PR_FALSE
1061      *           This should e the most likely case.  We'll properly
1062      *           not wait call _NT_IO_WAIT, since the closesocket()
1063      *           won't be forcing a completion.
1064      *     case 1b: fWait is set to PR_TRUE
1065      *           This hopefully won't happen much.  When it does, this
1066      *           thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
1067      *           before cleaning up.
1068      * case 2:  IO does not complete before calling closesocket
1069      *     case 2a: IO never completes
1070      *           This is the likely case.  We'll close it and wait
1071      *           for the completion forced by the close.  Return should
1072      *           be immediate.
1073      *     case 2b: IO completes just after calling closesocket
1074      *           Since the closesocket is issued, we'll either get a
1075      *           completion back for the real IO or for the close.  We
1076      *           don't really care.  It may not even be possible to get
1077      *           a real completion here.  In any event, we'll awaken
1078      *           from NT_IO_WAIT immediately.
1079      */
1080 
1081     _PR_THREAD_LOCK(me);
1082     fWait = me->io_pending;
1083     if (fWait) {
1084         /*
1085          * If there's still I/O pending, it should have already timed
1086          * out once before this function is called.
1087          */
1088         PR_ASSERT(me->io_suspended == PR_TRUE);
1089 
1090         /* Set up to wait for I/O completion again */
1091         me->state = _PR_IO_WAIT;
1092         me->io_suspended = PR_FALSE;
1093         me->md.interrupt_disabled = PR_TRUE;
1094     }
1095     _PR_THREAD_UNLOCK(me);
1096 
1097     /* Close the socket if there is one */
1098     if (sock != INVALID_SOCKET) {
1099         rv = closesocket((SOCKET)sock);
1100     }
1101 
1102     /* If there was I/O pending before the close, wait for it to complete */
1103     if (fWait) {
1104 
1105         /* Wait and wait for the I/O to complete */
1106         for (loop_count = 0; fWait; ++loop_count) {
1107 
1108             _NT_IO_WAIT(me, CLOSE_TIMEOUT);
1109 
1110             _PR_THREAD_LOCK(me);
1111             fWait = me->io_pending;
1112             if (fWait) {
1113                 PR_ASSERT(me->io_suspended == PR_TRUE);
1114                 me->state = _PR_IO_WAIT;
1115                 me->io_suspended = PR_FALSE;
1116             }
1117             _PR_THREAD_UNLOCK(me);
1118 
1119             if (loop_count > max_wait_loops) {
1120                 max_wait_loops = loop_count;
1121             }
1122         }
1123 
1124         if (loop_count > 1) {
1125             ++missing_completions;
1126         }
1127 
1128         me->md.interrupt_disabled = PR_FALSE;
1129         me->io_pending = PR_FALSE;
1130         me->state = _PR_RUNNING;
1131     }
1132 
1133     PR_ASSERT(me->io_pending == PR_FALSE);
1134     me->md.thr_bound_cpu = NULL;
1135     me->io_suspended = PR_FALSE;
1136 
1137     return rv;
1138 }
1139 
1140 
1141 PROsfd
_PR_MD_SOCKET(int af,int type,int flags)1142 _PR_MD_SOCKET(int af, int type, int flags)
1143 {
1144     SOCKET sock;
1145 
1146     sock = socket(af, type, flags);
1147 
1148     if (sock == INVALID_SOCKET) {
1149         _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
1150     }
1151 
1152     return (PROsfd)sock;
1153 }
1154 
1155 struct connect_data_s {
1156     PRInt32 status;
1157     PRInt32 error;
1158     PROsfd  osfd;
1159     struct sockaddr *addr;
1160     PRUint32 addrlen;
1161     PRIntervalTime timeout;
1162 };
1163 
1164 void
_PR_MD_connect_thread(void * cdata)1165 _PR_MD_connect_thread(void *cdata)
1166 {
1167     struct connect_data_s *cd = (struct connect_data_s *)cdata;
1168 
1169     cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
1170 
1171     if (cd->status == SOCKET_ERROR) {
1172         cd->error = WSAGetLastError();
1173     }
1174 
1175     return;
1176 }
1177 
1178 
1179 PRInt32
_PR_MD_CONNECT(PRFileDesc * fd,const PRNetAddr * addr,PRUint32 addrlen,PRIntervalTime timeout)1180 _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
1181                PRIntervalTime timeout)
1182 {
1183     PROsfd osfd = fd->secret->md.osfd;
1184     PRInt32 rv, err;
1185     u_long nbio;
1186     PRInt32 rc;
1187 
1188     if (fd->secret->nonblocking) {
1189         if (!fd->secret->md.io_model_committed) {
1190             rv = _md_MakeNonblock((HANDLE)osfd);
1191             PR_ASSERT(0 != rv);
1192             fd->secret->md.io_model_committed = PR_TRUE;
1193         }
1194 
1195         if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
1196             err = WSAGetLastError();
1197             _PR_MD_MAP_CONNECT_ERROR(err);
1198         }
1199         return rv;
1200     }
1201 
1202     /*
1203      * Temporarily make the socket non-blocking so that we can
1204      * initiate a non-blocking connect and wait for its completion
1205      * (with a timeout) in select.
1206      */
1207     PR_ASSERT(!fd->secret->md.io_model_committed);
1208     nbio = 1;
1209     rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
1210     PR_ASSERT(0 == rv);
1211 
1212     rc = _nt_nonblock_connect(fd, (struct sockaddr *) addr, addrlen, timeout);
1213 
1214     /* Set the socket back to blocking. */
1215     nbio = 0;
1216     rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
1217     PR_ASSERT(0 == rv);
1218 
1219     return rc;
1220 }
1221 
1222 PRInt32
_PR_MD_BIND(PRFileDesc * fd,const PRNetAddr * addr,PRUint32 addrlen)1223 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
1224 {
1225     PRInt32 rv;
1226 #if 0
1227     int one = 1;
1228 #endif
1229 
1230     rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
1231 
1232     if (rv == SOCKET_ERROR) {
1233         _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
1234         return -1;
1235     }
1236 
1237 #if 0
1238     /* Disable nagle- so far unknown if this is good or not...
1239      */
1240     rv = setsockopt(fd->secret->md.osfd,
1241                     SOL_SOCKET,
1242                     TCP_NODELAY,
1243                     (const char *)&one,
1244                     sizeof(one));
1245     PR_ASSERT(rv == 0);
1246 #endif
1247 
1248     return 0;
1249 }
1250 
_PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock,PROsfd listen_sock)1251 void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock, PROsfd listen_sock)
1252 {
1253     /* Sockets accept()'d with AcceptEx need to call this setsockopt before
1254      * calling anything other than ReadFile(), WriteFile(), send(), recv(),
1255      * Transmitfile(), and closesocket().  In order to call any other
1256      * winsock functions, we have to make this setsockopt call.
1257      *
1258      * XXXMB - For the server, we *NEVER* need this in
1259      * the "normal" code path.  But now we have to call it.  This is a waste
1260      * of a system call.  We'd like to only call it before calling the
1261      * obscure socket calls, but since we don't know at that point what the
1262      * original socket was (or even if it is still alive) we can't do it
1263      * at that point...
1264      */
1265     setsockopt((SOCKET)accept_sock,
1266                SOL_SOCKET,
1267                SO_UPDATE_ACCEPT_CONTEXT,
1268                (char *)&listen_sock,
1269                sizeof(listen_sock));
1270 
1271 }
1272 
1273 #define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
1274 PROsfd
_PR_MD_FAST_ACCEPT(PRFileDesc * fd,PRNetAddr * raddr,PRUint32 * rlen,PRIntervalTime timeout,PRBool fast,_PR_AcceptTimeoutCallback callback,void * callbackArg)1275 _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
1276                    PRIntervalTime timeout, PRBool fast,
1277                    _PR_AcceptTimeoutCallback callback, void *callbackArg)
1278 {
1279     PROsfd osfd = fd->secret->md.osfd;
1280     PRThread *me = _PR_MD_CURRENT_THREAD();
1281     SOCKET accept_sock;
1282     int bytes;
1283     PRNetAddr *Laddr;
1284     PRNetAddr *Raddr;
1285     PRUint32 llen, err;
1286     int rv;
1287 
1288     if (_NT_USE_NB_IO(fd)) {
1289         if (!fd->secret->md.io_model_committed) {
1290             rv = _md_MakeNonblock((HANDLE)osfd);
1291             PR_ASSERT(0 != rv);
1292             fd->secret->md.io_model_committed = PR_TRUE;
1293         }
1294         /*
1295          * The accepted socket inherits the nonblocking and
1296          * inheritable (HANDLE_FLAG_INHERIT) attributes of
1297          * the listening socket.
1298          */
1299         accept_sock = _nt_nonblock_accept(fd, (struct sockaddr *)raddr, rlen, timeout);
1300         if (!fd->secret->nonblocking) {
1301             u_long zero = 0;
1302 
1303             rv = ioctlsocket(accept_sock, FIONBIO, &zero);
1304             PR_ASSERT(0 == rv);
1305         }
1306         return accept_sock;
1307     }
1308 
1309     if (me->io_suspended) {
1310         PR_SetError(PR_INVALID_STATE_ERROR, 0);
1311         return -1;
1312     }
1313 
1314     if (!fd->secret->md.io_model_committed) {
1315         rv = _md_Associate((HANDLE)osfd);
1316         PR_ASSERT(0 != rv);
1317         fd->secret->md.io_model_committed = PR_TRUE;
1318     }
1319 
1320     if (!me->md.acceptex_buf) {
1321         me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
1322         if (!me->md.acceptex_buf) {
1323             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1324             return -1;
1325         }
1326     }
1327 
1328     accept_sock = _md_get_recycled_socket(fd->secret->af);
1329     if (accept_sock == INVALID_SOCKET) {
1330         return -1;
1331     }
1332 
1333     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1334     if (_native_threads_only) {
1335         me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1336     }
1337 
1338     _PR_THREAD_LOCK(me);
1339     if (_PR_PENDING_INTERRUPT(me)) {
1340         me->flags &= ~_PR_INTERRUPT;
1341         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1342         _PR_THREAD_UNLOCK(me);
1343         closesocket(accept_sock);
1344         return -1;
1345     }
1346     me->io_pending = PR_TRUE;
1347     me->state = _PR_IO_WAIT;
1348     _PR_THREAD_UNLOCK(me);
1349     me->io_fd = osfd;
1350 
1351     rv = AcceptEx((SOCKET)osfd,
1352                   accept_sock,
1353                   me->md.acceptex_buf,
1354                   0,
1355                   INET_ADDR_PADDED,
1356                   INET_ADDR_PADDED,
1357                   &bytes,
1358                   &(me->md.overlapped.overlapped));
1359 
1360     if ( (rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING))  {
1361         /* Argh! The IO failed */
1362         closesocket(accept_sock);
1363         _PR_THREAD_LOCK(me);
1364         me->io_pending = PR_FALSE;
1365         me->state = _PR_RUNNING;
1366         if (_PR_PENDING_INTERRUPT(me)) {
1367             me->flags &= ~_PR_INTERRUPT;
1368             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1369             _PR_THREAD_UNLOCK(me);
1370             return -1;
1371         }
1372         _PR_THREAD_UNLOCK(me);
1373 
1374         _PR_MD_MAP_ACCEPTEX_ERROR(err);
1375         return -1;
1376     }
1377 
1378     if (_native_threads_only && rv) {
1379         _native_thread_io_nowait(me, rv, bytes);
1380     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1381         PR_ASSERT(0);
1382         closesocket(accept_sock);
1383         return -1;
1384     }
1385 
1386     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1387 
1388     if (me->io_suspended) {
1389         closesocket(accept_sock);
1390         if (_PR_PENDING_INTERRUPT(me)) {
1391             me->flags &= ~_PR_INTERRUPT;
1392             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1393         } else {
1394             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1395         }
1396         return -1;
1397     }
1398 
1399     if (me->md.blocked_io_status == 0) {
1400         closesocket(accept_sock);
1401         _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
1402         return -1;
1403     }
1404 
1405     if (!fast) {
1406         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
1407     }
1408 
1409     /* IO is done */
1410     GetAcceptExSockaddrs(
1411         me->md.acceptex_buf,
1412         0,
1413         INET_ADDR_PADDED,
1414         INET_ADDR_PADDED,
1415         (LPSOCKADDR *)&(Laddr),
1416         &llen,
1417         (LPSOCKADDR *)&(Raddr),
1418         (unsigned int *)rlen);
1419 
1420     if (raddr != NULL) {
1421         memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
1422     }
1423 
1424     PR_ASSERT(me->io_pending == PR_FALSE);
1425 
1426     return accept_sock;
1427 }
1428 
1429 PRInt32
_PR_MD_FAST_ACCEPT_READ(PRFileDesc * sd,PROsfd * newSock,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout,PRBool fast,_PR_AcceptTimeoutCallback callback,void * callbackArg)1430 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, PRNetAddr **raddr,
1431                         void *buf, PRInt32 amount, PRIntervalTime timeout,
1432                         PRBool fast, _PR_AcceptTimeoutCallback callback,
1433                         void *callbackArg)
1434 {
1435     PROsfd sock = sd->secret->md.osfd;
1436     PRThread *me = _PR_MD_CURRENT_THREAD();
1437     int bytes;
1438     PRNetAddr *Laddr;
1439     PRUint32 llen, rlen, err;
1440     int rv;
1441     PRBool isConnected;
1442     PRBool madeCallback = PR_FALSE;
1443 
1444     if (me->io_suspended) {
1445         PR_SetError(PR_INVALID_STATE_ERROR, 0);
1446         return -1;
1447     }
1448 
1449     if (!sd->secret->md.io_model_committed) {
1450         rv = _md_Associate((HANDLE)sock);
1451         PR_ASSERT(0 != rv);
1452         sd->secret->md.io_model_committed = PR_TRUE;
1453     }
1454 
1455     *newSock = _md_get_recycled_socket(sd->secret->af);
1456     if (*newSock == INVALID_SOCKET) {
1457         return -1;
1458     }
1459 
1460     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1461     if (_native_threads_only) {
1462         me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1463     }
1464 
1465     _PR_THREAD_LOCK(me);
1466     if (_PR_PENDING_INTERRUPT(me)) {
1467         me->flags &= ~_PR_INTERRUPT;
1468         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1469         _PR_THREAD_UNLOCK(me);
1470         closesocket(*newSock);
1471         return -1;
1472     }
1473     me->io_pending = PR_TRUE;
1474     me->state = _PR_IO_WAIT;
1475     _PR_THREAD_UNLOCK(me);
1476     me->io_fd = sock;
1477 
1478     rv = AcceptEx((SOCKET)sock,
1479                   *newSock,
1480                   buf,
1481                   amount,
1482                   INET_ADDR_PADDED,
1483                   INET_ADDR_PADDED,
1484                   &bytes,
1485                   &(me->md.overlapped.overlapped));
1486 
1487     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
1488         closesocket(*newSock);
1489         _PR_THREAD_LOCK(me);
1490         me->io_pending = PR_FALSE;
1491         me->state = _PR_RUNNING;
1492         if (_PR_PENDING_INTERRUPT(me)) {
1493             me->flags &= ~_PR_INTERRUPT;
1494             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1495             _PR_THREAD_UNLOCK(me);
1496             return -1;
1497         }
1498         _PR_THREAD_UNLOCK(me);
1499 
1500         _PR_MD_MAP_ACCEPTEX_ERROR(err);
1501         return -1;
1502     }
1503 
1504     if (_native_threads_only && rv) {
1505         _native_thread_io_nowait(me, rv, bytes);
1506     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1507         PR_ASSERT(0);
1508         closesocket(*newSock);
1509         return -1;
1510     }
1511 
1512 retry:
1513     if (me->io_suspended) {
1514         PRInt32 err;
1515         INT seconds;
1516         INT bytes = sizeof(seconds);
1517 
1518         PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
1519 
1520         err = getsockopt(*newSock,
1521                          SOL_SOCKET,
1522                          SO_CONNECT_TIME,
1523                          (char *)&seconds,
1524                          (PINT)&bytes);
1525         if ( err == NO_ERROR ) {
1526             PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
1527 
1528             if (seconds == 0xffffffff) {
1529                 isConnected = PR_FALSE;
1530             }
1531             else {
1532                 isConnected = PR_TRUE;
1533             }
1534 
1535             if (!isConnected) {
1536                 if (madeCallback == PR_FALSE && callback) {
1537                     callback(callbackArg);
1538                 }
1539                 madeCallback = PR_TRUE;
1540                 me->state = _PR_IO_WAIT;
1541                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
1542                     closesocket(*newSock);
1543                     return -1;
1544                 }
1545                 goto retry;
1546             }
1547 
1548             if (elapsed < timeout) {
1549                 /* Socket is connected but time not elapsed, RESUME IO */
1550                 timeout -= elapsed;
1551                 me->state = _PR_IO_WAIT;
1552                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
1553                     closesocket(*newSock);
1554                     return -1;
1555                 }
1556                 goto retry;
1557             }
1558         } else {
1559             /*  What to do here? Assume socket not open?*/
1560             PR_ASSERT(0);
1561             isConnected = PR_FALSE;
1562         }
1563 
1564         rv = _NT_IO_ABORT(*newSock);
1565 
1566         PR_ASSERT(me->io_pending ==  PR_FALSE);
1567         PR_ASSERT(me->io_suspended ==  PR_FALSE);
1568         PR_ASSERT(me->md.thr_bound_cpu ==  NULL);
1569         /* If the IO is still suspended, it means we didn't get any
1570          * completion from NT_IO_WAIT.  This is not disasterous, I hope,
1571          * but it may mean we still have an IO outstanding...  Try to
1572          * recover by just allowing ourselves to continue.
1573          */
1574         me->io_suspended = PR_FALSE;
1575         if (_PR_PENDING_INTERRUPT(me)) {
1576             me->flags &= ~_PR_INTERRUPT;
1577             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1578         } else {
1579             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1580         }
1581         me->state = _PR_RUNNING;
1582         closesocket(*newSock);
1583         return -1;
1584     }
1585 
1586     PR_ASSERT(me->io_pending == PR_FALSE);
1587     PR_ASSERT(me->io_suspended == PR_FALSE);
1588     PR_ASSERT(me->md.thr_bound_cpu == NULL);
1589 
1590     if (me->md.blocked_io_status == 0) {
1591         _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
1592         closesocket(*newSock);
1593         return -1;
1594     }
1595 
1596     if (!fast) {
1597         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
1598     }
1599 
1600     /* IO is done */
1601     GetAcceptExSockaddrs(
1602         buf,
1603         amount,
1604         INET_ADDR_PADDED,
1605         INET_ADDR_PADDED,
1606         (LPSOCKADDR *)&(Laddr),
1607         &llen,
1608         (LPSOCKADDR *)(raddr),
1609         (unsigned int *)&rlen);
1610 
1611     return me->md.blocked_io_bytes;
1612 }
1613 
1614 PRInt32
_PR_MD_SENDFILE(PRFileDesc * sock,PRSendFileData * sfd,PRInt32 flags,PRIntervalTime timeout)1615 _PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd,
1616                 PRInt32 flags, PRIntervalTime timeout)
1617 {
1618     PRThread *me = _PR_MD_CURRENT_THREAD();
1619     PRInt32 tflags;
1620     int rv, err;
1621 
1622     if (me->io_suspended) {
1623         PR_SetError(PR_INVALID_STATE_ERROR, 0);
1624         return -1;
1625     }
1626 
1627     if (!sock->secret->md.io_model_committed) {
1628         rv = _md_Associate((HANDLE)sock->secret->md.osfd);
1629         PR_ASSERT(0 != rv);
1630         sock->secret->md.io_model_committed = PR_TRUE;
1631     }
1632     if (!me->md.xmit_bufs) {
1633         me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
1634         if (!me->md.xmit_bufs) {
1635             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1636             return -1;
1637         }
1638     }
1639     me->md.xmit_bufs->Head       = (void *)sfd->header;
1640     me->md.xmit_bufs->HeadLength = sfd->hlen;
1641     me->md.xmit_bufs->Tail       = (void *)sfd->trailer;
1642     me->md.xmit_bufs->TailLength = sfd->tlen;
1643 
1644     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1645     me->md.overlapped.overlapped.Offset = sfd->file_offset;
1646     if (_native_threads_only) {
1647         me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1648     }
1649 
1650     tflags = 0;
1651     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
1652         tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
1653     }
1654 
1655     _PR_THREAD_LOCK(me);
1656     if (_PR_PENDING_INTERRUPT(me)) {
1657         me->flags &= ~_PR_INTERRUPT;
1658         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1659         _PR_THREAD_UNLOCK(me);
1660         return -1;
1661     }
1662     me->io_pending = PR_TRUE;
1663     me->state = _PR_IO_WAIT;
1664     _PR_THREAD_UNLOCK(me);
1665     me->io_fd = sock->secret->md.osfd;
1666 
1667     rv = TransmitFile((SOCKET)sock->secret->md.osfd,
1668                       (HANDLE)sfd->fd->secret->md.osfd,
1669                       (DWORD)sfd->file_nbytes,
1670                       (DWORD)0,
1671                       (LPOVERLAPPED)&(me->md.overlapped.overlapped),
1672                       (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
1673                       (DWORD)tflags);
1674     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
1675         _PR_THREAD_LOCK(me);
1676         me->io_pending = PR_FALSE;
1677         me->state = _PR_RUNNING;
1678         if (_PR_PENDING_INTERRUPT(me)) {
1679             me->flags &= ~_PR_INTERRUPT;
1680             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1681             _PR_THREAD_UNLOCK(me);
1682             return -1;
1683         }
1684         _PR_THREAD_UNLOCK(me);
1685 
1686         _PR_MD_MAP_TRANSMITFILE_ERROR(err);
1687         return -1;
1688     }
1689 
1690     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1691         PR_ASSERT(0);
1692         return -1;
1693     }
1694 
1695     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1696 
1697     if (me->io_suspended) {
1698         if (_PR_PENDING_INTERRUPT(me)) {
1699             me->flags &= ~_PR_INTERRUPT;
1700             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1701         } else {
1702             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1703         }
1704         return -1;
1705     }
1706 
1707     if (me->md.blocked_io_status == 0) {
1708         _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
1709         return -1;
1710     }
1711 
1712     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
1713         _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af);
1714     }
1715 
1716     PR_ASSERT(me->io_pending == PR_FALSE);
1717 
1718     return me->md.blocked_io_bytes;
1719 }
1720 
1721 PRInt32
_PR_MD_RECV(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)1722 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
1723             PRIntervalTime timeout)
1724 {
1725     PROsfd osfd = fd->secret->md.osfd;
1726     PRThread *me = _PR_MD_CURRENT_THREAD();
1727     int bytes;
1728     int rv, err;
1729 
1730     if (_NT_USE_NB_IO(fd)) {
1731         if (!fd->secret->md.io_model_committed) {
1732             rv = _md_MakeNonblock((HANDLE)osfd);
1733             PR_ASSERT(0 != rv);
1734             fd->secret->md.io_model_committed = PR_TRUE;
1735         }
1736         return _nt_nonblock_recv(fd, buf, amount, flags, timeout);
1737     }
1738 
1739     if (me->io_suspended) {
1740         PR_SetError(PR_INVALID_STATE_ERROR, 0);
1741         return -1;
1742     }
1743 
1744     if (!fd->secret->md.io_model_committed) {
1745         rv = _md_Associate((HANDLE)osfd);
1746         PR_ASSERT(0 != rv);
1747         fd->secret->md.io_model_committed = PR_TRUE;
1748     }
1749 
1750     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1751     if (_native_threads_only) {
1752         me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1753     }
1754 
1755     _PR_THREAD_LOCK(me);
1756     if (_PR_PENDING_INTERRUPT(me)) {
1757         me->flags &= ~_PR_INTERRUPT;
1758         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1759         _PR_THREAD_UNLOCK(me);
1760         return -1;
1761     }
1762     me->io_pending = PR_TRUE;
1763     me->state = _PR_IO_WAIT;
1764     _PR_THREAD_UNLOCK(me);
1765     me->io_fd = osfd;
1766 
1767     rv = ReadFile((HANDLE)osfd,
1768                   buf,
1769                   amount,
1770                   &bytes,
1771                   &(me->md.overlapped.overlapped));
1772     if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
1773         _PR_THREAD_LOCK(me);
1774         me->io_pending = PR_FALSE;
1775         me->state = _PR_RUNNING;
1776         if (_PR_PENDING_INTERRUPT(me)) {
1777             me->flags &= ~_PR_INTERRUPT;
1778             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1779             _PR_THREAD_UNLOCK(me);
1780             return -1;
1781         }
1782         _PR_THREAD_UNLOCK(me);
1783 
1784         if ((err = GetLastError()) == ERROR_HANDLE_EOF) {
1785             return 0;
1786         }
1787         _PR_MD_MAP_READ_ERROR(err);
1788         return -1;
1789     }
1790 
1791     if (_native_threads_only && rv) {
1792         _native_thread_io_nowait(me, rv, bytes);
1793     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1794         PR_ASSERT(0);
1795         return -1;
1796     }
1797 
1798     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1799 
1800     if (me->io_suspended) {
1801         if (_PR_PENDING_INTERRUPT(me)) {
1802             me->flags &= ~_PR_INTERRUPT;
1803             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1804         } else {
1805             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1806         }
1807         return -1;
1808     }
1809 
1810     if (me->md.blocked_io_status == 0) {
1811         if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
1812             return 0;
1813         }
1814         _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
1815         return -1;
1816     }
1817 
1818     PR_ASSERT(me->io_pending == PR_FALSE);
1819 
1820     return me->md.blocked_io_bytes;
1821 }
1822 
1823 PRInt32
_PR_MD_SEND(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)1824 _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
1825             PRIntervalTime timeout)
1826 {
1827     PROsfd osfd = fd->secret->md.osfd;
1828     PRThread *me = _PR_MD_CURRENT_THREAD();
1829     int bytes;
1830     int rv, err;
1831 
1832     if (_NT_USE_NB_IO(fd)) {
1833         if (!fd->secret->md.io_model_committed) {
1834             rv = _md_MakeNonblock((HANDLE)osfd);
1835             PR_ASSERT(0 != rv);
1836             fd->secret->md.io_model_committed = PR_TRUE;
1837         }
1838         return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
1839     }
1840 
1841     if (me->io_suspended) {
1842         PR_SetError(PR_INVALID_STATE_ERROR, 0);
1843         return -1;
1844     }
1845 
1846     if (!fd->secret->md.io_model_committed) {
1847         rv = _md_Associate((HANDLE)osfd);
1848         PR_ASSERT(0 != rv);
1849         fd->secret->md.io_model_committed = PR_TRUE;
1850     }
1851 
1852     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
1853     if (_native_threads_only) {
1854         me->md.overlapped.overlapped.hEvent = me->md.thr_event;
1855     }
1856 
1857     _PR_THREAD_LOCK(me);
1858     if (_PR_PENDING_INTERRUPT(me)) {
1859         me->flags &= ~_PR_INTERRUPT;
1860         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1861         _PR_THREAD_UNLOCK(me);
1862         return -1;
1863     }
1864     me->io_pending = PR_TRUE;
1865     me->state = _PR_IO_WAIT;
1866     _PR_THREAD_UNLOCK(me);
1867     me->io_fd = osfd;
1868 
1869     rv = WriteFile((HANDLE)osfd,
1870                    buf,
1871                    amount,
1872                    &bytes,
1873                    &(me->md.overlapped.overlapped));
1874     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
1875         _PR_THREAD_LOCK(me);
1876         me->io_pending = PR_FALSE;
1877         me->state = _PR_RUNNING;
1878         if (_PR_PENDING_INTERRUPT(me)) {
1879             me->flags &= ~_PR_INTERRUPT;
1880             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1881             _PR_THREAD_UNLOCK(me);
1882             return -1;
1883         }
1884         _PR_THREAD_UNLOCK(me);
1885 
1886         _PR_MD_MAP_WRITE_ERROR(err);
1887         return -1;
1888     }
1889 
1890     if (_native_threads_only && rv) {
1891         _native_thread_io_nowait(me, rv, bytes);
1892     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
1893         PR_ASSERT(0);
1894         return -1;
1895     }
1896 
1897     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
1898 
1899     if (me->io_suspended) {
1900         if (_PR_PENDING_INTERRUPT(me)) {
1901             me->flags &= ~_PR_INTERRUPT;
1902             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
1903         } else {
1904             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
1905         }
1906         return -1;
1907     }
1908 
1909     if (me->md.blocked_io_status == 0) {
1910         _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
1911         return -1;
1912     }
1913 
1914     PR_ASSERT(me->io_pending == PR_FALSE);
1915 
1916     return me->md.blocked_io_bytes;
1917 }
1918 
1919 PRInt32
_PR_MD_SENDTO(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,const PRNetAddr * addr,PRUint32 addrlen,PRIntervalTime timeout)1920 _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
1921               const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
1922 {
1923     PROsfd osfd = fd->secret->md.osfd;
1924     PRInt32 rv;
1925 
1926     if (!fd->secret->md.io_model_committed) {
1927         rv = _md_MakeNonblock((HANDLE)osfd);
1928         PR_ASSERT(0 != rv);
1929         fd->secret->md.io_model_committed = PR_TRUE;
1930     }
1931     if (_NT_USE_NB_IO(fd)) {
1932         return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
1933     }
1934     else {
1935         return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
1936     }
1937 }
1938 
1939 PRInt32
_PR_MD_RECVFROM(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRNetAddr * addr,PRUint32 * addrlen,PRIntervalTime timeout)1940 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
1941                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
1942 {
1943     PROsfd osfd = fd->secret->md.osfd;
1944     PRInt32 rv;
1945 
1946     if (!fd->secret->md.io_model_committed) {
1947         rv = _md_MakeNonblock((HANDLE)osfd);
1948         PR_ASSERT(0 != rv);
1949         fd->secret->md.io_model_committed = PR_TRUE;
1950     }
1951     if (_NT_USE_NB_IO(fd)) {
1952         return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
1953     }
1954     else {
1955         return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
1956     }
1957 }
1958 
1959 /* XXXMB - for now this is a sockets call only */
1960 PRInt32
_PR_MD_WRITEV(PRFileDesc * fd,const PRIOVec * iov,PRInt32 iov_size,PRIntervalTime timeout)1961 _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
1962 {
1963     PROsfd osfd = fd->secret->md.osfd;
1964     int index;
1965     int sent = 0;
1966     int rv;
1967 
1968     if (_NT_USE_NB_IO(fd)) {
1969         if (!fd->secret->md.io_model_committed) {
1970             rv = _md_MakeNonblock((HANDLE)osfd);
1971             PR_ASSERT(0 != rv);
1972             fd->secret->md.io_model_committed = PR_TRUE;
1973         }
1974         return _nt_nonblock_writev(fd, iov, iov_size, timeout);
1975     }
1976 
1977     for (index=0; index<iov_size; index++) {
1978         rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
1979                          timeout);
1980         if (rv > 0) {
1981             sent += rv;
1982         }
1983         if ( rv != iov[index].iov_len ) {
1984             if (sent <= 0) {
1985                 return -1;
1986             }
1987             return -1;
1988         }
1989     }
1990 
1991     return sent;
1992 }
1993 
1994 PRInt32
_PR_MD_LISTEN(PRFileDesc * fd,PRIntn backlog)1995 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
1996 {
1997     PRInt32 rv;
1998 
1999     rv = listen(fd->secret->md.osfd, backlog);
2000     if (rv < 0) {
2001         _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
2002     }
2003     return(rv);
2004 }
2005 
2006 PRInt32
_PR_MD_SHUTDOWN(PRFileDesc * fd,PRIntn how)2007 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
2008 {
2009     PRInt32 rv;
2010 
2011     rv = shutdown(fd->secret->md.osfd, how);
2012     if (rv < 0) {
2013         _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
2014     }
2015     return(rv);
2016 }
2017 
2018 PRStatus
_PR_MD_GETSOCKNAME(PRFileDesc * fd,PRNetAddr * addr,PRUint32 * len)2019 _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
2020 {
2021     PRInt32 rv;
2022 
2023     rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
2024     if (rv==0) {
2025         return PR_SUCCESS;
2026     }
2027     else {
2028         _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
2029         return PR_FAILURE;
2030     }
2031 }
2032 
2033 PRStatus
_PR_MD_GETPEERNAME(PRFileDesc * fd,PRNetAddr * addr,PRUint32 * len)2034 _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
2035 {
2036     PRInt32 rv;
2037 
2038     /*
2039      * NT has a bug that, when invoked on a socket accepted by
2040      * AcceptEx(), getpeername() returns an all-zero peer address.
2041      * To work around this bug, we store the peer's address (returned
2042      * by AcceptEx()) with the socket fd and use the cached peer
2043      * address if the socket is an accepted socket.
2044      */
2045 
2046     if (fd->secret->md.accepted_socket) {
2047         INT seconds;
2048         INT bytes = sizeof(seconds);
2049 
2050         /*
2051          * Determine if the socket is connected.
2052          */
2053 
2054         rv = getsockopt(fd->secret->md.osfd,
2055                         SOL_SOCKET,
2056                         SO_CONNECT_TIME,
2057                         (char *) &seconds,
2058                         (PINT) &bytes);
2059         if (rv == NO_ERROR) {
2060             if (seconds == 0xffffffff) {
2061                 PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
2062                 return PR_FAILURE;
2063             }
2064             *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr);
2065             memcpy(addr, &fd->secret->md.peer_addr, *len);
2066             return PR_SUCCESS;
2067         } else {
2068             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
2069             return PR_FAILURE;
2070         }
2071     } else {
2072         rv = getpeername((SOCKET)fd->secret->md.osfd,
2073                          (struct sockaddr *) addr, len);
2074         if (rv == 0) {
2075             return PR_SUCCESS;
2076         } else {
2077             _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
2078             return PR_FAILURE;
2079         }
2080     }
2081 }
2082 
2083 PRStatus
_PR_MD_GETSOCKOPT(PRFileDesc * fd,PRInt32 level,PRInt32 optname,char * optval,PRInt32 * optlen)2084 _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
2085 {
2086     PRInt32 rv;
2087 
2088     rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
2089     if (rv==0) {
2090         return PR_SUCCESS;
2091     }
2092     else {
2093         _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
2094         return PR_FAILURE;
2095     }
2096 }
2097 
2098 PRStatus
_PR_MD_SETSOCKOPT(PRFileDesc * fd,PRInt32 level,PRInt32 optname,const char * optval,PRInt32 optlen)2099 _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
2100 {
2101     PRInt32 rv;
2102 
2103     rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
2104     if (rv==0) {
2105         return PR_SUCCESS;
2106     }
2107     else {
2108         _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
2109         return PR_FAILURE;
2110     }
2111 }
2112 
2113 /* --- FILE IO ----------------------------------------------------------- */
2114 
2115 PROsfd
_PR_MD_OPEN(const char * name,PRIntn osflags,PRIntn mode)2116 _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
2117 {
2118     HANDLE file;
2119     PRInt32 access = 0;
2120     PRInt32 flags = 0;
2121     PRInt32 flag6 = 0;
2122 
2123     if (osflags & PR_SYNC) {
2124         flag6 = FILE_FLAG_WRITE_THROUGH;
2125     }
2126 
2127     if (osflags & PR_RDONLY || osflags & PR_RDWR) {
2128         access |= GENERIC_READ;
2129     }
2130     if (osflags & PR_WRONLY || osflags & PR_RDWR) {
2131         access |= GENERIC_WRITE;
2132     }
2133 
2134     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
2135         flags = CREATE_NEW;
2136     }
2137     else if (osflags & PR_CREATE_FILE) {
2138         flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
2139     }
2140     else if (osflags & PR_TRUNCATE) {
2141         flags = TRUNCATE_EXISTING;
2142     }
2143     else {
2144         flags = OPEN_EXISTING;
2145     }
2146 
2147 
2148     flag6 |= FILE_FLAG_OVERLAPPED;
2149 
2150     file = CreateFile(name,
2151                       access,
2152                       FILE_SHARE_READ|FILE_SHARE_WRITE,
2153                       NULL,
2154                       flags,
2155                       flag6,
2156                       NULL);
2157     if (file == INVALID_HANDLE_VALUE) {
2158         _PR_MD_MAP_OPEN_ERROR(GetLastError());
2159         return -1;
2160     }
2161 
2162     if (osflags & PR_APPEND) {
2163         if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
2164             _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2165             CloseHandle(file);
2166             return -1;
2167         }
2168     }
2169 
2170     return (PROsfd)file;
2171 }
2172 
2173 PROsfd
_PR_MD_OPEN_FILE(const char * name,PRIntn osflags,PRIntn mode)2174 _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode)
2175 {
2176     HANDLE file;
2177     PRInt32 access = 0;
2178     PRInt32 flags = 0;
2179     PRInt32 flag6 = 0;
2180     SECURITY_ATTRIBUTES sa;
2181     LPSECURITY_ATTRIBUTES lpSA = NULL;
2182     PSECURITY_DESCRIPTOR pSD = NULL;
2183     PACL pACL = NULL;
2184 
2185     if (osflags & PR_SYNC) {
2186         flag6 = FILE_FLAG_WRITE_THROUGH;
2187     }
2188 
2189     if (osflags & PR_RDONLY || osflags & PR_RDWR) {
2190         access |= GENERIC_READ;
2191     }
2192     if (osflags & PR_WRONLY || osflags & PR_RDWR) {
2193         access |= GENERIC_WRITE;
2194     }
2195 
2196     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) {
2197         flags = CREATE_NEW;
2198     }
2199     else if (osflags & PR_CREATE_FILE) {
2200         flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
2201     }
2202     else if (osflags & PR_TRUNCATE) {
2203         flags = TRUNCATE_EXISTING;
2204     }
2205     else {
2206         flags = OPEN_EXISTING;
2207     }
2208 
2209 
2210     flag6 |= FILE_FLAG_OVERLAPPED;
2211 
2212     if (osflags & PR_CREATE_FILE) {
2213         if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
2214                                              &pSD, &pACL) == PR_SUCCESS) {
2215             sa.nLength = sizeof(sa);
2216             sa.lpSecurityDescriptor = pSD;
2217             sa.bInheritHandle = FALSE;
2218             lpSA = &sa;
2219         }
2220     }
2221     file = CreateFile(name,
2222                       access,
2223                       FILE_SHARE_READ|FILE_SHARE_WRITE,
2224                       lpSA,
2225                       flags,
2226                       flag6,
2227                       NULL);
2228     if (lpSA != NULL) {
2229         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
2230     }
2231     if (file == INVALID_HANDLE_VALUE) {
2232         _PR_MD_MAP_OPEN_ERROR(GetLastError());
2233         return -1;
2234     }
2235 
2236     if (osflags & PR_APPEND) {
2237         if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
2238             _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2239             CloseHandle(file);
2240             return -1;
2241         }
2242     }
2243 
2244     return (PROsfd)file;
2245 }
2246 
2247 PRInt32
_PR_MD_READ(PRFileDesc * fd,void * buf,PRInt32 len)2248 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
2249 {
2250     PROsfd f = fd->secret->md.osfd;
2251     PRUint32 bytes;
2252     int rv, err;
2253     LONG hiOffset = 0;
2254     LONG loOffset;
2255     LARGE_INTEGER offset; /* use for a normalized add of len to offset */
2256 
2257     if (!fd->secret->md.sync_file_io) {
2258         PRThread *me = _PR_MD_CURRENT_THREAD();
2259 
2260         if (me->io_suspended) {
2261             PR_SetError(PR_INVALID_STATE_ERROR, 0);
2262             return -1;
2263         }
2264 
2265         memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
2266 
2267         me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
2268         PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
2269 
2270         if (fd->secret->inheritable == _PR_TRI_TRUE) {
2271             rv = ReadFile((HANDLE)f,
2272                           (LPVOID)buf,
2273                           len,
2274                           &bytes,
2275                           &me->md.overlapped.overlapped);
2276             if (rv != 0) {
2277                 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2278                 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2279                 return bytes;
2280             }
2281             err = GetLastError();
2282             if (err == ERROR_IO_PENDING) {
2283                 rv = GetOverlappedResult((HANDLE)f,
2284                                          &me->md.overlapped.overlapped, &bytes, TRUE);
2285                 if (rv != 0) {
2286                     loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2287                     PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2288                     return bytes;
2289                 }
2290                 err = GetLastError();
2291             }
2292             if (err == ERROR_HANDLE_EOF) {
2293                 return 0;
2294             } else {
2295                 _PR_MD_MAP_READ_ERROR(err);
2296                 return -1;
2297             }
2298         } else {
2299             if (!fd->secret->md.io_model_committed) {
2300                 rv = _md_Associate((HANDLE)f);
2301                 PR_ASSERT(rv != 0);
2302                 fd->secret->md.io_model_committed = PR_TRUE;
2303             }
2304 
2305             if (_native_threads_only) {
2306                 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
2307             }
2308 
2309             _PR_THREAD_LOCK(me);
2310             if (_PR_PENDING_INTERRUPT(me)) {
2311                 me->flags &= ~_PR_INTERRUPT;
2312                 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2313                 _PR_THREAD_UNLOCK(me);
2314                 return -1;
2315             }
2316             me->io_pending = PR_TRUE;
2317             me->state = _PR_IO_WAIT;
2318             _PR_THREAD_UNLOCK(me);
2319             me->io_fd = f;
2320 
2321             rv = ReadFile((HANDLE)f,
2322                           (LPVOID)buf,
2323                           len,
2324                           &bytes,
2325                           &me->md.overlapped.overlapped);
2326             if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
2327                 _PR_THREAD_LOCK(me);
2328                 me->io_pending = PR_FALSE;
2329                 me->state = _PR_RUNNING;
2330                 if (_PR_PENDING_INTERRUPT(me)) {
2331                     me->flags &= ~_PR_INTERRUPT;
2332                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2333                     _PR_THREAD_UNLOCK(me);
2334                     return -1;
2335                 }
2336                 _PR_THREAD_UNLOCK(me);
2337 
2338                 if (err == ERROR_HANDLE_EOF) {
2339                     return 0;
2340                 }
2341                 _PR_MD_MAP_READ_ERROR(err);
2342                 return -1;
2343             }
2344 
2345             if (_native_threads_only && rv) {
2346                 _native_thread_io_nowait(me, rv, bytes);
2347             } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
2348                 PR_ASSERT(0);
2349                 return -1;
2350             }
2351 
2352             PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
2353 
2354             if (me->io_suspended) {
2355                 if (_PR_PENDING_INTERRUPT(me)) {
2356                     me->flags &= ~_PR_INTERRUPT;
2357                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2358                 } else {
2359                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
2360                 }
2361                 return -1;
2362             }
2363 
2364             if (me->md.blocked_io_status == 0) {
2365                 if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
2366                     return 0;
2367                 }
2368                 _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
2369                 return -1;
2370             }
2371 
2372             /* Apply the workaround from bug 70765 (see _PR_MD_WRITE)
2373              * to the reading code, too. */
2374 
2375             offset.LowPart = me->md.overlapped.overlapped.Offset;
2376             offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
2377             offset.QuadPart += me->md.blocked_io_bytes;
2378 
2379             SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
2380 
2381             PR_ASSERT(me->io_pending == PR_FALSE);
2382 
2383             return me->md.blocked_io_bytes;
2384         }
2385     } else {
2386 
2387         rv = ReadFile((HANDLE)f,
2388                       (LPVOID)buf,
2389                       len,
2390                       &bytes,
2391                       NULL);
2392         if (rv == 0) {
2393             err = GetLastError();
2394             /* ERROR_HANDLE_EOF can only be returned by async io */
2395             PR_ASSERT(err != ERROR_HANDLE_EOF);
2396             if (err == ERROR_BROKEN_PIPE) {
2397                 /* The write end of the pipe has been closed. */
2398                 return 0;
2399             }
2400             _PR_MD_MAP_READ_ERROR(err);
2401             return -1;
2402         }
2403         return bytes;
2404     }
2405 }
2406 
2407 PRInt32
_PR_MD_WRITE(PRFileDesc * fd,const void * buf,PRInt32 len)2408 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
2409 {
2410     PROsfd f = fd->secret->md.osfd;
2411     PRInt32 bytes;
2412     int rv, err;
2413     LONG hiOffset = 0;
2414     LONG loOffset;
2415     LARGE_INTEGER offset; /* use for the calculation of the new offset */
2416 
2417     if (!fd->secret->md.sync_file_io) {
2418         PRThread *me = _PR_MD_CURRENT_THREAD();
2419 
2420         if (me->io_suspended) {
2421             PR_SetError(PR_INVALID_STATE_ERROR, 0);
2422             return -1;
2423         }
2424 
2425         memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
2426 
2427         me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
2428         PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
2429 
2430         if (fd->secret->inheritable == _PR_TRI_TRUE) {
2431             rv = WriteFile((HANDLE)f,
2432                            (LPVOID)buf,
2433                            len,
2434                            &bytes,
2435                            &me->md.overlapped.overlapped);
2436             if (rv != 0) {
2437                 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2438                 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2439                 return bytes;
2440             }
2441             err = GetLastError();
2442             if (err == ERROR_IO_PENDING) {
2443                 rv = GetOverlappedResult((HANDLE)f,
2444                                          &me->md.overlapped.overlapped, &bytes, TRUE);
2445                 if (rv != 0) {
2446                     loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
2447                     PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
2448                     return bytes;
2449                 }
2450                 err = GetLastError();
2451             }
2452             _PR_MD_MAP_READ_ERROR(err);
2453             return -1;
2454         } else {
2455             if (!fd->secret->md.io_model_committed) {
2456                 rv = _md_Associate((HANDLE)f);
2457                 PR_ASSERT(rv != 0);
2458                 fd->secret->md.io_model_committed = PR_TRUE;
2459             }
2460             if (_native_threads_only) {
2461                 me->md.overlapped.overlapped.hEvent = me->md.thr_event;
2462             }
2463 
2464             _PR_THREAD_LOCK(me);
2465             if (_PR_PENDING_INTERRUPT(me)) {
2466                 me->flags &= ~_PR_INTERRUPT;
2467                 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2468                 _PR_THREAD_UNLOCK(me);
2469                 return -1;
2470             }
2471             me->io_pending = PR_TRUE;
2472             me->state = _PR_IO_WAIT;
2473             _PR_THREAD_UNLOCK(me);
2474             me->io_fd = f;
2475 
2476             rv = WriteFile((HANDLE)f,
2477                            buf,
2478                            len,
2479                            &bytes,
2480                            &(me->md.overlapped.overlapped));
2481             if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
2482                 _PR_THREAD_LOCK(me);
2483                 me->io_pending = PR_FALSE;
2484                 me->state = _PR_RUNNING;
2485                 if (_PR_PENDING_INTERRUPT(me)) {
2486                     me->flags &= ~_PR_INTERRUPT;
2487                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2488                     _PR_THREAD_UNLOCK(me);
2489                     return -1;
2490                 }
2491                 _PR_THREAD_UNLOCK(me);
2492 
2493                 _PR_MD_MAP_WRITE_ERROR(err);
2494                 return -1;
2495             }
2496 
2497             if (_native_threads_only && rv) {
2498                 _native_thread_io_nowait(me, rv, bytes);
2499             } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
2500                 PR_ASSERT(0);
2501                 return -1;
2502             }
2503 
2504             PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
2505 
2506             if (me->io_suspended) {
2507                 if (_PR_PENDING_INTERRUPT(me)) {
2508                     me->flags &= ~_PR_INTERRUPT;
2509                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
2510                 } else {
2511                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
2512                 }
2513                 return -1;
2514             }
2515 
2516             if (me->md.blocked_io_status == 0) {
2517                 _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
2518                 return -1;
2519             }
2520 
2521             /*
2522              * Moving the file pointer by a relative offset (FILE_CURRENT)
2523              * does not work with a file on a network drive exported by a
2524              * Win2K system.  We still don't know why.  A workaround is to
2525              * move the file pointer by an absolute offset (FILE_BEGIN).
2526              * (Bugzilla bug 70765)
2527              */
2528             offset.LowPart = me->md.overlapped.overlapped.Offset;
2529             offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
2530             offset.QuadPart += me->md.blocked_io_bytes;
2531 
2532             SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
2533 
2534             PR_ASSERT(me->io_pending == PR_FALSE);
2535 
2536             return me->md.blocked_io_bytes;
2537         }
2538     } else {
2539         rv = WriteFile((HANDLE)f,
2540                        buf,
2541                        len,
2542                        &bytes,
2543                        NULL);
2544         if (rv == 0) {
2545             _PR_MD_MAP_WRITE_ERROR(GetLastError());
2546             return -1;
2547         }
2548         return bytes;
2549     }
2550 }
2551 
2552 PRInt32
_PR_MD_SOCKETAVAILABLE(PRFileDesc * fd)2553 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
2554 {
2555     PRInt32 result;
2556 
2557     if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
2558         PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
2559         return -1;
2560     }
2561     return result;
2562 }
2563 
2564 PRInt32
_PR_MD_PIPEAVAILABLE(PRFileDesc * fd)2565 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
2566 {
2567     if (NULL == fd) {
2568         PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
2569     }
2570     else {
2571         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
2572     }
2573     return -1;
2574 }
2575 
2576 PROffset32
_PR_MD_LSEEK(PRFileDesc * fd,PROffset32 offset,PRSeekWhence whence)2577 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
2578 {
2579     DWORD moveMethod;
2580     PROffset32 rv;
2581 
2582     switch (whence) {
2583         case PR_SEEK_SET:
2584             moveMethod = FILE_BEGIN;
2585             break;
2586         case PR_SEEK_CUR:
2587             moveMethod = FILE_CURRENT;
2588             break;
2589         case PR_SEEK_END:
2590             moveMethod = FILE_END;
2591             break;
2592         default:
2593             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2594             return -1;
2595     }
2596 
2597     rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
2598 
2599     /*
2600      * If the lpDistanceToMoveHigh argument (third argument) is
2601      * NULL, SetFilePointer returns 0xffffffff on failure.
2602      */
2603     if (-1 == rv) {
2604         _PR_MD_MAP_LSEEK_ERROR(GetLastError());
2605     }
2606     return rv;
2607 }
2608 
2609 PROffset64
_PR_MD_LSEEK64(PRFileDesc * fd,PROffset64 offset,PRSeekWhence whence)2610 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
2611 {
2612     DWORD moveMethod;
2613     LARGE_INTEGER li;
2614     DWORD err;
2615 
2616     switch (whence) {
2617         case PR_SEEK_SET:
2618             moveMethod = FILE_BEGIN;
2619             break;
2620         case PR_SEEK_CUR:
2621             moveMethod = FILE_CURRENT;
2622             break;
2623         case PR_SEEK_END:
2624             moveMethod = FILE_END;
2625             break;
2626         default:
2627             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2628             return -1;
2629     }
2630 
2631     li.QuadPart = offset;
2632     li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
2633                                 li.LowPart, &li.HighPart, moveMethod);
2634 
2635     if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
2636         _PR_MD_MAP_LSEEK_ERROR(err);
2637         li.QuadPart = -1;
2638     }
2639     return li.QuadPart;
2640 }
2641 
2642 /*
2643  * This is documented to succeed on read-only files, but Win32's
2644  * FlushFileBuffers functions fails with "access denied" in such a
2645  * case.  So we only signal an error if the error is *not* "access
2646  * denied".
2647  */
2648 PRInt32
_PR_MD_FSYNC(PRFileDesc * fd)2649 _PR_MD_FSYNC(PRFileDesc *fd)
2650 {
2651     /*
2652      * From the documentation:
2653      *
2654      *     On Windows NT, the function FlushFileBuffers fails if hFile
2655      *     is a handle to console output. That is because console
2656      *     output is not buffered. The function returns FALSE, and
2657      *     GetLastError returns ERROR_INVALID_HANDLE.
2658      *
2659      * On the other hand, on Win95, it returns without error.  I cannot
2660      * assume that 0, 1, and 2 are console, because if someone closes
2661      * System.out and then opens a file, they might get file descriptor
2662      * 1.  An error on *that* version of 1 should be reported, whereas
2663      * an error on System.out (which was the original 1) should be
2664      * ignored.  So I use isatty() to ensure that such an error was
2665      * because of this, and if it was, I ignore the error.
2666      */
2667 
2668     BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
2669 
2670     if (!ok) {
2671         DWORD err = GetLastError();
2672 
2673         if (err != ERROR_ACCESS_DENIED) {   /* from winerror.h */
2674             _PR_MD_MAP_FSYNC_ERROR(err);
2675             return -1;
2676         }
2677     }
2678     return 0;
2679 }
2680 
2681 PRInt32
_PR_MD_CLOSE(PROsfd osfd,PRBool socket)2682 _PR_MD_CLOSE(PROsfd osfd, PRBool socket)
2683 {
2684     PRInt32 rv;
2685     PRThread *me = _PR_MD_CURRENT_THREAD();
2686 
2687     if (socket)  {
2688         rv = closesocket((SOCKET)osfd);
2689         if (rv < 0) {
2690             _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
2691         }
2692     } else {
2693         rv = CloseHandle((HANDLE)osfd)?0:-1;
2694         if (rv < 0) {
2695             _PR_MD_MAP_CLOSE_ERROR(GetLastError());
2696         }
2697     }
2698 
2699     if (rv == 0 && me->io_suspended) {
2700         if (me->io_fd == osfd) {
2701             PRBool fWait;
2702 
2703             _PR_THREAD_LOCK(me);
2704             me->state = _PR_IO_WAIT;
2705             /* The IO could have completed on another thread just after
2706              * calling closesocket while the io_suspended flag was true.
2707              * So we now grab the lock to do a safe check on io_pending to
2708              * see if we need to wait or not.
2709              */
2710             fWait = me->io_pending;
2711             me->io_suspended = PR_FALSE;
2712             me->md.interrupt_disabled = PR_TRUE;
2713             _PR_THREAD_UNLOCK(me);
2714 
2715             if (fWait) {
2716                 _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
2717             }
2718             PR_ASSERT(me->io_suspended ==  PR_FALSE);
2719             PR_ASSERT(me->io_pending ==  PR_FALSE);
2720             /*
2721              * I/O operation is no longer pending; the thread can now
2722              * run on any cpu
2723              */
2724             _PR_THREAD_LOCK(me);
2725             me->md.interrupt_disabled = PR_FALSE;
2726             me->md.thr_bound_cpu = NULL;
2727             me->io_suspended = PR_FALSE;
2728             me->io_pending = PR_FALSE;
2729             me->state = _PR_RUNNING;
2730             _PR_THREAD_UNLOCK(me);
2731         }
2732     }
2733     return rv;
2734 }
2735 
2736 PRStatus
_PR_MD_SET_FD_INHERITABLE(PRFileDesc * fd,PRBool inheritable)2737 _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
2738 {
2739     BOOL rv;
2740 
2741     if (fd->secret->md.io_model_committed) {
2742         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2743         return PR_FAILURE;
2744     }
2745     rv = SetHandleInformation(
2746              (HANDLE)fd->secret->md.osfd,
2747              HANDLE_FLAG_INHERIT,
2748              inheritable ? HANDLE_FLAG_INHERIT : 0);
2749     if (0 == rv) {
2750         _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
2751         return PR_FAILURE;
2752     }
2753     return PR_SUCCESS;
2754 }
2755 
2756 void
_PR_MD_INIT_FD_INHERITABLE(PRFileDesc * fd,PRBool imported)2757 _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
2758 {
2759     if (imported) {
2760         fd->secret->inheritable = _PR_TRI_UNKNOWN;
2761     } else {
2762         fd->secret->inheritable = _PR_TRI_FALSE;
2763     }
2764 }
2765 
2766 void
_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc * fd)2767 _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
2768 {
2769     DWORD flags;
2770 
2771     PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
2772     if (fd->secret->md.io_model_committed) {
2773         return;
2774     }
2775     if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
2776         if (flags & HANDLE_FLAG_INHERIT) {
2777             fd->secret->inheritable = _PR_TRI_TRUE;
2778         } else {
2779             fd->secret->inheritable = _PR_TRI_FALSE;
2780         }
2781     }
2782 }
2783 
2784 
2785 /* --- DIR IO ------------------------------------------------------------ */
2786 #define GetFileFromDIR(d)       (d)->d_entry.cFileName
2787 #define FileIsHidden(d)       ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
2788 
FlipSlashes(char * cp,int len)2789 void FlipSlashes(char *cp, int len)
2790 {
2791     while (--len >= 0) {
2792         if (cp[0] == '/') {
2793             cp[0] = PR_DIRECTORY_SEPARATOR;
2794         }
2795         cp = _mbsinc(cp);
2796     }
2797 } /* end FlipSlashes() */
2798 
2799 /*
2800 **
2801 ** Local implementations of standard Unix RTL functions which are not provided
2802 ** by the VC RTL.
2803 **
2804 */
2805 
2806 PRInt32
_PR_MD_CLOSE_DIR(_MDDir * d)2807 _PR_MD_CLOSE_DIR(_MDDir *d)
2808 {
2809     if ( d ) {
2810         if (FindClose( d->d_hdl )) {
2811             d->magic = (PRUint32)-1;
2812             return 0;
2813         } else {
2814             _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
2815             return -1;
2816         }
2817     }
2818     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2819     return -1;
2820 }
2821 
2822 
2823 PRStatus
_PR_MD_OPEN_DIR(_MDDir * d,const char * name)2824 _PR_MD_OPEN_DIR(_MDDir *d, const char *name)
2825 {
2826     char filename[ MAX_PATH ];
2827     int len;
2828 
2829     len = strlen(name);
2830     /* Need 5 bytes for \*.* and the trailing null byte. */
2831     if (len + 5 > MAX_PATH) {
2832         PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
2833         return PR_FAILURE;
2834     }
2835     strcpy(filename, name);
2836 
2837     /*
2838      * If 'name' ends in a slash or backslash, do not append
2839      * another backslash.
2840      */
2841     if (IsPrevCharSlash(filename, filename + len)) {
2842         len--;
2843     }
2844     strcpy(&filename[len], "\\*.*");
2845     FlipSlashes( filename, strlen(filename) );
2846 
2847     d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
2848     if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
2849         _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
2850         return PR_FAILURE;
2851     }
2852     d->firstEntry = PR_TRUE;
2853     d->magic = _MD_MAGIC_DIR;
2854     return PR_SUCCESS;
2855 }
2856 
2857 char *
_PR_MD_READ_DIR(_MDDir * d,PRIntn flags)2858 _PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
2859 {
2860     PRInt32 err;
2861     BOOL rv;
2862     char *fileName;
2863 
2864     if ( d ) {
2865         while (1) {
2866             if (d->firstEntry) {
2867                 d->firstEntry = PR_FALSE;
2868                 rv = 1;
2869             } else {
2870                 rv = FindNextFile(d->d_hdl, &(d->d_entry));
2871             }
2872             if (rv == 0) {
2873                 break;
2874             }
2875             fileName = GetFileFromDIR(d);
2876             if ( (flags & PR_SKIP_DOT) &&
2877                  (fileName[0] == '.') && (fileName[1] == '\0')) {
2878                 continue;
2879             }
2880             if ( (flags & PR_SKIP_DOT_DOT) &&
2881                  (fileName[0] == '.') && (fileName[1] == '.') &&
2882                  (fileName[2] == '\0')) {
2883                 continue;
2884             }
2885             if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
2886                 continue;
2887             }
2888             return fileName;
2889         }
2890         err = GetLastError();
2891         PR_ASSERT(NO_ERROR != err);
2892         _PR_MD_MAP_READDIR_ERROR(err);
2893         return NULL;
2894     }
2895     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
2896     return NULL;
2897 }
2898 
2899 PRInt32
_PR_MD_DELETE(const char * name)2900 _PR_MD_DELETE(const char *name)
2901 {
2902     if (DeleteFile(name)) {
2903         return 0;
2904     } else {
2905         _PR_MD_MAP_DELETE_ERROR(GetLastError());
2906         return -1;
2907     }
2908 }
2909 
2910 void
_PR_FileTimeToPRTime(const FILETIME * filetime,PRTime * prtm)2911 _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
2912 {
2913     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
2914     CopyMemory(prtm, filetime, sizeof(PRTime));
2915 #ifdef __GNUC__
2916     *prtm = (*prtm - _pr_filetime_offset) / 10LL;
2917 #else
2918     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
2919 #endif
2920 
2921 #ifdef DEBUG
2922     /* Doublecheck our calculation. */
2923     {
2924         SYSTEMTIME systime;
2925         PRExplodedTime etm;
2926         PRTime cmp; /* for comparison */
2927         BOOL rv;
2928 
2929         rv = FileTimeToSystemTime(filetime, &systime);
2930         PR_ASSERT(0 != rv);
2931 
2932         /*
2933          * PR_ImplodeTime ignores wday and yday.
2934          */
2935         etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
2936         etm.tm_sec = systime.wSecond;
2937         etm.tm_min = systime.wMinute;
2938         etm.tm_hour = systime.wHour;
2939         etm.tm_mday = systime.wDay;
2940         etm.tm_month = systime.wMonth - 1;
2941         etm.tm_year = systime.wYear;
2942         /*
2943          * It is not well-documented what time zone the FILETIME's
2944          * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
2945          * But BY_HANDLE_FILE_INFORMATION is unclear about this.
2946          * By our best judgement, we assume that FILETIME is in UTC.
2947          */
2948         etm.tm_params.tp_gmt_offset = 0;
2949         etm.tm_params.tp_dst_offset = 0;
2950         cmp = PR_ImplodeTime(&etm);
2951 
2952         /*
2953          * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
2954          * microseconds to milliseconds before doing the comparison.
2955          */
2956         PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
2957     }
2958 #endif /* DEBUG */
2959 }
2960 
2961 PRInt32
_PR_MD_STAT(const char * fn,struct stat * info)2962 _PR_MD_STAT(const char *fn, struct stat *info)
2963 {
2964     PRInt32 rv;
2965 
2966     rv = _stat(fn, (struct _stat *)info);
2967     if (-1 == rv) {
2968         /*
2969          * Check for MSVC runtime library _stat() bug.
2970          * (It's really a bug in FindFirstFile().)
2971          * If a pathname ends in a backslash or slash,
2972          * e.g., c:\temp\ or c:/temp/, _stat() will fail.
2973          * Note: a pathname ending in a slash (e.g., c:/temp/)
2974          * can be handled by _stat() on NT but not on Win95.
2975          *
2976          * We remove the backslash or slash at the end and
2977          * try again.
2978          */
2979 
2980         int len = strlen(fn);
2981         if (len > 0 && len <= _MAX_PATH
2982             && IsPrevCharSlash(fn, fn + len)) {
2983             char newfn[_MAX_PATH + 1];
2984 
2985             strcpy(newfn, fn);
2986             newfn[len - 1] = '\0';
2987             rv = _stat(newfn, (struct _stat *)info);
2988         }
2989     }
2990 
2991     if (-1 == rv) {
2992         _PR_MD_MAP_STAT_ERROR(errno);
2993     }
2994     return rv;
2995 }
2996 
2997 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
2998 
2999 static PRBool
IsPrevCharSlash(const char * str,const char * current)3000 IsPrevCharSlash(const char *str, const char *current)
3001 {
3002     const char *prev;
3003 
3004     if (str >= current) {
3005         return PR_FALSE;
3006     }
3007     prev = _mbsdec(str, current);
3008     return (prev == current - 1) && _PR_IS_SLASH(*prev);
3009 }
3010 
3011 /*
3012  * IsRootDirectory --
3013  *
3014  * Return PR_TRUE if the pathname 'fn' is a valid root directory,
3015  * else return PR_FALSE.  The char buffer pointed to by 'fn' must
3016  * be writable.  During the execution of this function, the contents
3017  * of the buffer pointed to by 'fn' may be modified, but on return
3018  * the original contents will be restored.  'buflen' is the size of
3019  * the buffer pointed to by 'fn'.
3020  *
3021  * Root directories come in three formats:
3022  * 1. / or \, meaning the root directory of the current drive.
3023  * 2. C:/ or C:\, where C is a drive letter.
3024  * 3. \\<server name>\<share point name>\ or
3025  *    \\<server name>\<share point name>, meaning the root directory
3026  *    of a UNC (Universal Naming Convention) name.
3027  */
3028 
3029 static PRBool
IsRootDirectory(char * fn,size_t buflen)3030 IsRootDirectory(char *fn, size_t buflen)
3031 {
3032     char *p;
3033     PRBool slashAdded = PR_FALSE;
3034     PRBool rv = PR_FALSE;
3035 
3036     if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
3037         return PR_TRUE;
3038     }
3039 
3040     if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
3041         && fn[3] == '\0') {
3042         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
3043         return rv;
3044     }
3045 
3046     /* The UNC root directory */
3047 
3048     if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
3049         /* The 'server' part should have at least one character. */
3050         p = &fn[2];
3051         if (*p == '\0' || _PR_IS_SLASH(*p)) {
3052             return PR_FALSE;
3053         }
3054 
3055         /* look for the next slash */
3056         do {
3057             p = _mbsinc(p);
3058         } while (*p != '\0' && !_PR_IS_SLASH(*p));
3059         if (*p == '\0') {
3060             return PR_FALSE;
3061         }
3062 
3063         /* The 'share' part should have at least one character. */
3064         p++;
3065         if (*p == '\0' || _PR_IS_SLASH(*p)) {
3066             return PR_FALSE;
3067         }
3068 
3069         /* look for the final slash */
3070         do {
3071             p = _mbsinc(p);
3072         } while (*p != '\0' && !_PR_IS_SLASH(*p));
3073         if (_PR_IS_SLASH(*p) && p[1] != '\0') {
3074             return PR_FALSE;
3075         }
3076         if (*p == '\0') {
3077             /*
3078              * GetDriveType() doesn't work correctly if the
3079              * path is of the form \\server\share, so we add
3080              * a final slash temporarily.
3081              */
3082             if ((p + 1) < (fn + buflen)) {
3083                 *p++ = '\\';
3084                 *p = '\0';
3085                 slashAdded = PR_TRUE;
3086             } else {
3087                 return PR_FALSE; /* name too long */
3088             }
3089         }
3090         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
3091         /* restore the 'fn' buffer */
3092         if (slashAdded) {
3093             *--p = '\0';
3094         }
3095     }
3096     return rv;
3097 }
3098 
3099 PRInt32
_PR_MD_GETFILEINFO64(const char * fn,PRFileInfo64 * info)3100 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
3101 {
3102     WIN32_FILE_ATTRIBUTE_DATA findFileData;
3103 
3104     if (NULL == fn || '\0' == *fn) {
3105         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
3106         return -1;
3107     }
3108 
3109     if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) {
3110         _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
3111         return -1;
3112     }
3113 
3114     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3115         info->type = PR_FILE_DIRECTORY;
3116     } else {
3117         info->type = PR_FILE_FILE;
3118     }
3119 
3120     info->size = findFileData.nFileSizeHigh;
3121     info->size = (info->size << 32) + findFileData.nFileSizeLow;
3122 
3123     _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
3124 
3125     if (0 == findFileData.ftCreationTime.dwLowDateTime &&
3126         0 == findFileData.ftCreationTime.dwHighDateTime) {
3127         info->creationTime = info->modifyTime;
3128     } else {
3129         _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
3130                              &info->creationTime);
3131     }
3132 
3133     return 0;
3134 }
3135 
3136 PRInt32
_PR_MD_GETFILEINFO(const char * fn,PRFileInfo * info)3137 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
3138 {
3139     PRFileInfo64 info64;
3140     PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
3141     if (0 == rv)
3142     {
3143         info->type = info64.type;
3144         info->size = (PRUint32) info64.size;
3145         info->modifyTime = info64.modifyTime;
3146         info->creationTime = info64.creationTime;
3147     }
3148     return rv;
3149 }
3150 
3151 PRInt32
_PR_MD_GETOPENFILEINFO64(const PRFileDesc * fd,PRFileInfo64 * info)3152 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
3153 {
3154     int rv;
3155 
3156     BY_HANDLE_FILE_INFORMATION hinfo;
3157 
3158     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
3159     if (rv == FALSE) {
3160         _PR_MD_MAP_FSTAT_ERROR(GetLastError());
3161         return -1;
3162     }
3163 
3164     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3165         info->type = PR_FILE_DIRECTORY;
3166     }
3167     else {
3168         info->type = PR_FILE_FILE;
3169     }
3170 
3171     info->size = hinfo.nFileSizeHigh;
3172     info->size = (info->size << 32) + hinfo.nFileSizeLow;
3173 
3174     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
3175     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
3176 
3177     return 0;
3178 }
3179 
3180 PRInt32
_PR_MD_GETOPENFILEINFO(const PRFileDesc * fd,PRFileInfo * info)3181 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
3182 {
3183     int rv;
3184 
3185     BY_HANDLE_FILE_INFORMATION hinfo;
3186 
3187     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
3188     if (rv == FALSE) {
3189         _PR_MD_MAP_FSTAT_ERROR(GetLastError());
3190         return -1;
3191     }
3192 
3193     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3194         info->type = PR_FILE_DIRECTORY;
3195     }
3196     else {
3197         info->type = PR_FILE_FILE;
3198     }
3199 
3200     info->size = hinfo.nFileSizeLow;
3201 
3202     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
3203     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
3204 
3205     return 0;
3206 }
3207 
3208 PRInt32
_PR_MD_RENAME(const char * from,const char * to)3209 _PR_MD_RENAME(const char *from, const char *to)
3210 {
3211     /* Does this work with dot-relative pathnames? */
3212     if (MoveFile(from, to)) {
3213         return 0;
3214     } else {
3215         _PR_MD_MAP_RENAME_ERROR(GetLastError());
3216         return -1;
3217     }
3218 }
3219 
3220 PRInt32
_PR_MD_ACCESS(const char * name,PRAccessHow how)3221 _PR_MD_ACCESS(const char *name, PRAccessHow how)
3222 {
3223     PRInt32 rv;
3224 
3225     switch (how) {
3226         case PR_ACCESS_WRITE_OK:
3227             rv = _access(name, 02);
3228             break;
3229         case PR_ACCESS_READ_OK:
3230             rv = _access(name, 04);
3231             break;
3232         case PR_ACCESS_EXISTS:
3233             rv = _access(name, 00);
3234             break;
3235         default:
3236             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
3237             return -1;
3238     }
3239     if (rv < 0) {
3240         _PR_MD_MAP_ACCESS_ERROR(errno);
3241     }
3242     return rv;
3243 }
3244 
3245 PRInt32
_PR_MD_MKDIR(const char * name,PRIntn mode)3246 _PR_MD_MKDIR(const char *name, PRIntn mode)
3247 {
3248     /* XXXMB - how to translate the "mode"??? */
3249     if (CreateDirectory(name, NULL)) {
3250         return 0;
3251     } else {
3252         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
3253         return -1;
3254     }
3255 }
3256 
3257 PRInt32
_PR_MD_MAKE_DIR(const char * name,PRIntn mode)3258 _PR_MD_MAKE_DIR(const char *name, PRIntn mode)
3259 {
3260     BOOL rv;
3261     SECURITY_ATTRIBUTES sa;
3262     LPSECURITY_ATTRIBUTES lpSA = NULL;
3263     PSECURITY_DESCRIPTOR pSD = NULL;
3264     PACL pACL = NULL;
3265 
3266     if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
3267                                          &pSD, &pACL) == PR_SUCCESS) {
3268         sa.nLength = sizeof(sa);
3269         sa.lpSecurityDescriptor = pSD;
3270         sa.bInheritHandle = FALSE;
3271         lpSA = &sa;
3272     }
3273     rv = CreateDirectory(name, lpSA);
3274     if (lpSA != NULL) {
3275         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
3276     }
3277     if (rv) {
3278         return 0;
3279     } else {
3280         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
3281         return -1;
3282     }
3283 }
3284 
3285 PRInt32
_PR_MD_RMDIR(const char * name)3286 _PR_MD_RMDIR(const char *name)
3287 {
3288     if (RemoveDirectory(name)) {
3289         return 0;
3290     } else {
3291         _PR_MD_MAP_RMDIR_ERROR(GetLastError());
3292         return -1;
3293     }
3294 }
3295 
3296 PRStatus
_PR_MD_LOCKFILE(PROsfd f)3297 _PR_MD_LOCKFILE(PROsfd f)
3298 {
3299     PRInt32 rv, err;
3300     PRThread *me = _PR_MD_CURRENT_THREAD();
3301 
3302     if (me->io_suspended) {
3303         PR_SetError(PR_INVALID_STATE_ERROR, 0);
3304         return PR_FAILURE;
3305     }
3306 
3307     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3308 
3309     _PR_THREAD_LOCK(me);
3310     if (_PR_PENDING_INTERRUPT(me)) {
3311         me->flags &= ~_PR_INTERRUPT;
3312         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3313         _PR_THREAD_UNLOCK(me);
3314         return -1;
3315     }
3316     me->io_pending = PR_TRUE;
3317     me->state = _PR_IO_WAIT;
3318     _PR_THREAD_UNLOCK(me);
3319 
3320     rv = LockFileEx((HANDLE)f,
3321                     LOCKFILE_EXCLUSIVE_LOCK,
3322                     0,
3323                     0x7fffffff,
3324                     0,
3325                     &me->md.overlapped.overlapped);
3326 
3327     if (_native_threads_only) {
3328         _PR_THREAD_LOCK(me);
3329         me->io_pending = PR_FALSE;
3330         me->state = _PR_RUNNING;
3331         if (_PR_PENDING_INTERRUPT(me)) {
3332             me->flags &= ~_PR_INTERRUPT;
3333             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3334             _PR_THREAD_UNLOCK(me);
3335             return PR_FAILURE;
3336         }
3337         _PR_THREAD_UNLOCK(me);
3338 
3339         if (rv == FALSE) {
3340             err = GetLastError();
3341             PR_ASSERT(err != ERROR_IO_PENDING);
3342             _PR_MD_MAP_LOCKF_ERROR(err);
3343             return PR_FAILURE;
3344         }
3345         return PR_SUCCESS;
3346     }
3347 
3348     /* HACK AROUND NT BUG
3349      * NT 3.51 has a bug.  In NT 3.51, if LockFileEx returns true, you
3350      * don't get any completion on the completion port.  This is a bug.
3351      *
3352      * They fixed it on NT4.0 so that you do get a completion.
3353      *
3354      * If we pretend we won't get a completion, NSPR gets confused later
3355      * when the unexpected completion arrives.  If we assume we do get
3356      * a completion, we hang on 3.51.  Worse, Microsoft informs me that the
3357      * behavior varies on 3.51 depending on if you are using a network
3358      * file system or a local disk!
3359      *
3360      * Solution:  For now, _nt_version_gets_lockfile_completion is set
3361      * depending on whether or not this system is EITHER
3362      *      - running NT 4.0
3363      *      - running NT 3.51 with a service pack greater than 5.
3364      *
3365      * In the meantime, this code may not work on network file systems.
3366      *
3367      */
3368 
3369     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
3370         _PR_THREAD_LOCK(me);
3371         me->io_pending = PR_FALSE;
3372         me->state = _PR_RUNNING;
3373         if (_PR_PENDING_INTERRUPT(me)) {
3374             me->flags &= ~_PR_INTERRUPT;
3375             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3376             _PR_THREAD_UNLOCK(me);
3377             return PR_FAILURE;
3378         }
3379         _PR_THREAD_UNLOCK(me);
3380 
3381         _PR_MD_MAP_LOCKF_ERROR(err);
3382         return PR_FAILURE;
3383     }
3384 #ifdef _NEED_351_FILE_LOCKING_HACK
3385     else if (rv)  {
3386         /* If this is NT 3.51 and the file is local, then we won't get a
3387          * completion back from LockFile when it succeeded.
3388          */
3389         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
3390             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
3391                 me->io_pending = PR_FALSE;
3392                 me->state = _PR_RUNNING;
3393                 return PR_SUCCESS;
3394             }
3395         }
3396     }
3397 #endif /* _NEED_351_FILE_LOCKING_HACK */
3398 
3399     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
3400         _PR_THREAD_LOCK(me);
3401         me->io_pending = PR_FALSE;
3402         me->state = _PR_RUNNING;
3403         _PR_THREAD_UNLOCK(me);
3404         return PR_FAILURE;
3405     }
3406 
3407     if (me->md.blocked_io_status == 0) {
3408         _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
3409         return PR_FAILURE;
3410     }
3411 
3412     return PR_SUCCESS;
3413 }
3414 
3415 PRStatus
_PR_MD_TLOCKFILE(PROsfd f)3416 _PR_MD_TLOCKFILE(PROsfd f)
3417 {
3418     PRInt32 rv, err;
3419     PRThread *me = _PR_MD_CURRENT_THREAD();
3420 
3421     if (me->io_suspended) {
3422         PR_SetError(PR_INVALID_STATE_ERROR, 0);
3423         return PR_FAILURE;
3424     }
3425 
3426     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3427 
3428     _PR_THREAD_LOCK(me);
3429     if (_PR_PENDING_INTERRUPT(me)) {
3430         me->flags &= ~_PR_INTERRUPT;
3431         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3432         _PR_THREAD_UNLOCK(me);
3433         return -1;
3434     }
3435     me->io_pending = PR_TRUE;
3436     me->state = _PR_IO_WAIT;
3437     _PR_THREAD_UNLOCK(me);
3438 
3439     rv = LockFileEx((HANDLE)f,
3440                     LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
3441                     0,
3442                     0x7fffffff,
3443                     0,
3444                     &me->md.overlapped.overlapped);
3445     if (_native_threads_only) {
3446         _PR_THREAD_LOCK(me);
3447         me->io_pending = PR_FALSE;
3448         me->state = _PR_RUNNING;
3449         if (_PR_PENDING_INTERRUPT(me)) {
3450             me->flags &= ~_PR_INTERRUPT;
3451             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3452             _PR_THREAD_UNLOCK(me);
3453             return PR_FAILURE;
3454         }
3455         _PR_THREAD_UNLOCK(me);
3456 
3457         if (rv == FALSE) {
3458             err = GetLastError();
3459             PR_ASSERT(err != ERROR_IO_PENDING);
3460             _PR_MD_MAP_LOCKF_ERROR(err);
3461             return PR_FAILURE;
3462         }
3463         return PR_SUCCESS;
3464     }
3465     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
3466         _PR_THREAD_LOCK(me);
3467         me->io_pending = PR_FALSE;
3468         me->state = _PR_RUNNING;
3469         if (_PR_PENDING_INTERRUPT(me)) {
3470             me->flags &= ~_PR_INTERRUPT;
3471             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3472             _PR_THREAD_UNLOCK(me);
3473             return PR_FAILURE;
3474         }
3475         _PR_THREAD_UNLOCK(me);
3476 
3477         _PR_MD_MAP_LOCKF_ERROR(err);
3478         return PR_FAILURE;
3479     }
3480 #ifdef _NEED_351_FILE_LOCKING_HACK
3481     else if (rv)  {
3482         /* If this is NT 3.51 and the file is local, then we won't get a
3483          * completion back from LockFile when it succeeded.
3484          */
3485         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
3486             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
3487                 _PR_THREAD_LOCK(me);
3488                 me->io_pending = PR_FALSE;
3489                 me->state = _PR_RUNNING;
3490                 if (_PR_PENDING_INTERRUPT(me)) {
3491                     me->flags &= ~_PR_INTERRUPT;
3492                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3493                     _PR_THREAD_UNLOCK(me);
3494                     return PR_FAILURE;
3495                 }
3496                 _PR_THREAD_UNLOCK(me);
3497 
3498                 return PR_SUCCESS;
3499             }
3500         }
3501     }
3502 #endif /* _NEED_351_FILE_LOCKING_HACK */
3503 
3504     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
3505         _PR_THREAD_LOCK(me);
3506         me->io_pending = PR_FALSE;
3507         me->state = _PR_RUNNING;
3508         if (_PR_PENDING_INTERRUPT(me)) {
3509             me->flags &= ~_PR_INTERRUPT;
3510             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
3511             _PR_THREAD_UNLOCK(me);
3512             return PR_FAILURE;
3513         }
3514         _PR_THREAD_UNLOCK(me);
3515 
3516         return PR_FAILURE;
3517     }
3518 
3519     if (me->md.blocked_io_status == 0) {
3520         _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
3521         return PR_FAILURE;
3522     }
3523 
3524     return PR_SUCCESS;
3525 }
3526 
3527 
3528 PRStatus
_PR_MD_UNLOCKFILE(PROsfd f)3529 _PR_MD_UNLOCKFILE(PROsfd f)
3530 {
3531     PRInt32 rv;
3532     PRThread *me = _PR_MD_CURRENT_THREAD();
3533 
3534     if (me->io_suspended) {
3535         PR_SetError(PR_INVALID_STATE_ERROR, 0);
3536         return PR_FAILURE;
3537     }
3538 
3539     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
3540 
3541     rv = UnlockFileEx((HANDLE)f,
3542                       0,
3543                       0x7fffffff,
3544                       0,
3545                       &me->md.overlapped.overlapped);
3546 
3547     if (rv) {
3548         return PR_SUCCESS;
3549     }
3550     else {
3551         int err = GetLastError();
3552         _PR_MD_MAP_LOCKF_ERROR(err);
3553         return PR_FAILURE;
3554     }
3555 }
3556 
3557 void
_PR_MD_MAKE_NONBLOCK(PRFileDesc * f)3558 _PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
3559 {
3560     /*
3561      * On NT, we either call _md_Associate() or _md_MakeNonblock(),
3562      * depending on whether the socket is blocking or not.
3563      *
3564      * Once we associate a socket with the io completion port,
3565      * there is no way to disassociate it from the io completion
3566      * port.  So we have to call _md_Associate/_md_MakeNonblock
3567      * lazily.
3568      */
3569 }
3570 
3571 #ifdef _NEED_351_FILE_LOCKING_HACK
3572 /***************
3573 **
3574 ** Lockfile hacks
3575 **
3576 ** The following code is a hack to work around a microsoft bug with lockfile.
3577 ** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
3578 ** get a completion back for files that are on local disks.  So, we need to
3579 ** know if a file is local or remote so we can tell if we should expect
3580 ** a completion.
3581 **
3582 ** The only way to check if a file is local or remote based on the handle is
3583 ** to get the serial number for the volume it is mounted on and then to
3584 ** compare that with mounted drives.  This code caches the volume numbers of
3585 ** fixed disks and does a relatively quick check.
3586 **
3587 ** Locking:  Since the only thing we ever do when multithreaded is a 32bit
3588 **           assignment, we probably don't need locking.  It is included just
3589 **           case anyway.
3590 **
3591 ** Limitations:  Does not work on floppies because they are too slow
3592 **               Unknown if it will work on wierdo 3rd party file systems
3593 **
3594 ****************
3595 */
3596 
3597 /* There can only be 26 drive letters on NT */
3598 #define _PR_MAX_DRIVES 26
3599 
3600 _MDLock cachedVolumeLock;
3601 DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
3602 DWORD dwLastCachedDrive = 0;
3603 DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
3604 
IsFileLocalInit()3605 PRBool IsFileLocalInit()
3606 {
3607     TCHAR lpBuffer[_PR_MAX_DRIVES*5];
3608     DWORD nBufferLength = _PR_MAX_DRIVES*5;
3609     DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
3610     DWORD dwIndex = 0;
3611     DWORD dwDriveType;
3612     DWORD dwVolumeSerialNumber;
3613     DWORD dwDriveIndex = 0;
3614     DWORD oldmode = (DWORD) -1;
3615 
3616     _MD_NEW_LOCK(&cachedVolumeLock);
3617 
3618     nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
3619     if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength) {
3620         return PR_FALSE;
3621     }
3622 
3623     // Calling GetVolumeInformation on a removeable drive where the
3624     // disk is currently removed will cause a dialog box to the
3625     // console.  This is not good.
3626     // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
3627     // damn dialog.
3628 
3629     dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
3630     oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
3631 
3632     // now loop through the logical drives
3633     while(lpBuffer[dwIndex] != TEXT('\0'))
3634     {
3635         // skip the floppy drives.  This is *SLOW*
3636         if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
3637             /* Skip over floppies */;
3638         else
3639         {
3640             dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
3641 
3642             dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
3643 
3644             switch(dwDriveType)
3645             {
3646                 // Ignore these drive types
3647                 case 0:
3648                 case 1:
3649                 case DRIVE_REMOTE:
3650                 default: // If the drive type is unknown, ignore it.
3651                     break;
3652 
3653                 // Removable media drives can have different serial numbers
3654                 // at different times, so cache the current serial number
3655                 // but keep track of them so they can be rechecked if necessary.
3656                 case DRIVE_REMOVABLE:
3657 
3658                 // CDROM is a removable media
3659                 case DRIVE_CDROM:
3660 
3661                 // no idea if ramdisks can change serial numbers or not
3662                 // but it doesn't hurt to treat them as removable.
3663 
3664                 case DRIVE_RAMDISK:
3665 
3666 
3667                     // Here is where we keep track of removable drives.
3668                     dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
3669 
3670                 // removable drives fall through to fixed drives and get cached.
3671 
3672                 case DRIVE_FIXED:
3673 
3674                     // cache volume serial numbers.
3675                     if (GetVolumeInformation(
3676                             &lpBuffer[dwIndex],
3677                             NULL, 0,
3678                             &dwVolumeSerialNumber,
3679                             NULL, NULL, NULL, 0)
3680                        )
3681                     {
3682                         if (dwLastCachedDrive < dwDriveIndex) {
3683                             dwLastCachedDrive = dwDriveIndex;
3684                         }
3685                         dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
3686                     }
3687 
3688                     break;
3689             }
3690         }
3691 
3692         dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
3693     }
3694 
3695     if (oldmode != (DWORD) -1) {
3696         SetErrorMode(oldmode);
3697         oldmode = (DWORD) -1;
3698     }
3699 
3700     return PR_TRUE;
3701 }
3702 
IsFileLocal(HANDLE hFile)3703 PRInt32 IsFileLocal(HANDLE hFile)
3704 {
3705     DWORD dwIndex = 0, dwMask;
3706     BY_HANDLE_FILE_INFORMATION Info;
3707     TCHAR szDrive[4] = TEXT("C:\\");
3708     DWORD dwVolumeSerialNumber;
3709     DWORD oldmode = (DWORD) -1;
3710     int rv = _PR_REMOTE_FILE;
3711 
3712     if (!GetFileInformationByHandle(hFile, &Info)) {
3713         return -1;
3714     }
3715 
3716     // look to see if the volume serial number has been cached.
3717     _MD_LOCK(&cachedVolumeLock);
3718     while(dwIndex <= dwLastCachedDrive)
3719         if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
3720         {
3721             _MD_UNLOCK(&cachedVolumeLock);
3722             return _PR_LOCAL_FILE;
3723         }
3724     _MD_UNLOCK(&cachedVolumeLock);
3725 
3726     // volume serial number not found in the cache.  Check removable files.
3727     // removable drives are noted as a bitmask.  If the bit associated with
3728     // a specific drive is set, then we should query its volume serial number
3729     // as its possible it has changed.
3730     dwMask = dwRemoveableDrivesToCheck;
3731     dwIndex = 0;
3732 
3733     while(dwMask)
3734     {
3735         while(!(dwMask & 1))
3736         {
3737             dwIndex++;
3738             dwMask = dwMask >> 1;
3739         }
3740 
3741         szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
3742 
3743         // Calling GetVolumeInformation on a removeable drive where the
3744         // disk is currently removed will cause a dialog box to the
3745         // console.  This is not good.
3746         // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
3747         // dialog.
3748 
3749         oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
3750 
3751         if (GetVolumeInformation(
3752                 szDrive,
3753                 NULL, 0,
3754                 &dwVolumeSerialNumber,
3755                 NULL, NULL, NULL, 0)
3756            )
3757         {
3758             if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
3759             {
3760                 _MD_LOCK(&cachedVolumeLock);
3761                 if (dwLastCachedDrive < dwIndex) {
3762                     dwLastCachedDrive = dwIndex;
3763                 }
3764                 dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
3765                 _MD_UNLOCK(&cachedVolumeLock);
3766                 rv = _PR_LOCAL_FILE;
3767             }
3768         }
3769         if (oldmode != (DWORD) -1) {
3770             SetErrorMode(oldmode);
3771             oldmode = (DWORD) -1;
3772         }
3773 
3774         if (rv == _PR_LOCAL_FILE) {
3775             return _PR_LOCAL_FILE;
3776         }
3777 
3778         dwIndex++;
3779         dwMask = dwMask >> 1;
3780     }
3781 
3782     return _PR_REMOTE_FILE;
3783 }
3784 #endif /* _NEED_351_FILE_LOCKING_HACK */
3785 
PR_NT_CancelIo(PRFileDesc * fd)3786 PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc *fd)
3787 {
3788     PRThread *me = _PR_MD_CURRENT_THREAD();
3789     PRBool fWait;
3790     PRFileDesc *bottom;
3791 
3792     bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
3793     if (!me->io_suspended || (NULL == bottom) ||
3794         (me->io_fd != bottom->secret->md.osfd)) {
3795         PR_SetError(PR_INVALID_STATE_ERROR, 0);
3796         return PR_FAILURE;
3797     }
3798     /*
3799      * The CancelIO operation has to be issued by the same NT thread that
3800      * issued the I/O operation
3801      */
3802     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu));
3803     if (me->io_pending) {
3804         if (!CancelIo((HANDLE)bottom->secret->md.osfd)) {
3805             PR_SetError(PR_INVALID_STATE_ERROR, GetLastError());
3806             return PR_FAILURE;
3807         }
3808     }
3809     _PR_THREAD_LOCK(me);
3810     fWait = me->io_pending;
3811     me->io_suspended = PR_FALSE;
3812     me->state = _PR_IO_WAIT;
3813     me->md.interrupt_disabled = PR_TRUE;
3814     _PR_THREAD_UNLOCK(me);
3815     if (fWait) {
3816         _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
3817     }
3818     PR_ASSERT(me->io_suspended ==  PR_FALSE);
3819     PR_ASSERT(me->io_pending ==  PR_FALSE);
3820 
3821     _PR_THREAD_LOCK(me);
3822     me->md.interrupt_disabled = PR_FALSE;
3823     me->md.thr_bound_cpu = NULL;
3824     me->io_suspended = PR_FALSE;
3825     me->io_pending = PR_FALSE;
3826     me->state = _PR_RUNNING;
3827     _PR_THREAD_UNLOCK(me);
3828     return PR_SUCCESS;
3829 }
3830 
_nt_nonblock_accept(PRFileDesc * fd,struct sockaddr * addr,int * addrlen,PRIntervalTime timeout)3831 static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
3832 {
3833     PROsfd osfd = fd->secret->md.osfd;
3834     SOCKET sock;
3835     PRInt32 rv, err;
3836     fd_set rd;
3837     struct timeval tv, *tvp;
3838 
3839     FD_ZERO(&rd);
3840     FD_SET((SOCKET)osfd, &rd);
3841     if (timeout == PR_INTERVAL_NO_TIMEOUT) {
3842         while ((sock = accept(osfd, addr, addrlen)) == -1) {
3843             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3844                 && (!fd->secret->nonblocking)) {
3845                 if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
3846                                                 NULL)) == -1) {
3847                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3848                     break;
3849                 }
3850             } else {
3851                 _PR_MD_MAP_ACCEPT_ERROR(err);
3852                 break;
3853             }
3854         }
3855     } else if (timeout == PR_INTERVAL_NO_WAIT) {
3856         if ((sock = accept(osfd, addr, addrlen)) == -1) {
3857             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3858                 && (!fd->secret->nonblocking)) {
3859                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3860             } else {
3861                 _PR_MD_MAP_ACCEPT_ERROR(err);
3862             }
3863         }
3864     } else {
3865 retry:
3866         if ((sock = accept(osfd, addr, addrlen)) == -1) {
3867             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3868                 && (!fd->secret->nonblocking)) {
3869                 tv.tv_sec = PR_IntervalToSeconds(timeout);
3870                 tv.tv_usec = PR_IntervalToMicroseconds(
3871                                  timeout - PR_SecondsToInterval(tv.tv_sec));
3872                 tvp = &tv;
3873 
3874                 rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp);
3875                 if (rv > 0) {
3876                     goto retry;
3877                 } else if (rv == 0) {
3878                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3879                 } else {
3880                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3881                 }
3882             } else {
3883                 _PR_MD_MAP_ACCEPT_ERROR(err);
3884             }
3885         }
3886     }
3887     return (PROsfd)sock;
3888 }
3889 
_nt_nonblock_connect(PRFileDesc * fd,struct sockaddr * addr,int addrlen,PRIntervalTime timeout)3890 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
3891 {
3892     PROsfd osfd = fd->secret->md.osfd;
3893     PRInt32 rv;
3894     int err;
3895     fd_set wr, ex;
3896     struct timeval tv, *tvp;
3897     int len;
3898 
3899     if ((rv = connect(osfd, addr, addrlen)) == -1) {
3900         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
3901             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
3902                 tvp = NULL;
3903             } else {
3904                 tv.tv_sec = PR_IntervalToSeconds(timeout);
3905                 tv.tv_usec = PR_IntervalToMicroseconds(
3906                                  timeout - PR_SecondsToInterval(tv.tv_sec));
3907                 tvp = &tv;
3908             }
3909             FD_ZERO(&wr);
3910             FD_ZERO(&ex);
3911             FD_SET((SOCKET)osfd, &wr);
3912             FD_SET((SOCKET)osfd, &ex);
3913             if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wr, &ex,
3914                                             tvp)) == -1) {
3915                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3916                 return rv;
3917             }
3918             if (rv == 0) {
3919                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3920                 return -1;
3921             }
3922             /* Call Sleep(0) to work around a Winsock timeing bug. */
3923             Sleep(0);
3924             if (FD_ISSET((SOCKET)osfd, &ex)) {
3925                 len = sizeof(err);
3926                 if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
3927                                (char *) &err, &len) == SOCKET_ERROR) {
3928                     _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
3929                     return -1;
3930                 }
3931                 _PR_MD_MAP_CONNECT_ERROR(err);
3932                 return -1;
3933             }
3934             PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr));
3935             rv = 0;
3936         } else {
3937             _PR_MD_MAP_CONNECT_ERROR(err);
3938         }
3939     }
3940     return rv;
3941 }
3942 
_nt_nonblock_recv(PRFileDesc * fd,char * buf,int len,int flags,PRIntervalTime timeout)3943 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime timeout)
3944 {
3945     PROsfd osfd = fd->secret->md.osfd;
3946     PRInt32 rv, err;
3947     struct timeval tv, *tvp;
3948     fd_set rd;
3949     int osflags;
3950 
3951     if (0 == flags) {
3952         osflags = 0;
3953     } else {
3954         PR_ASSERT(PR_MSG_PEEK == flags);
3955         osflags = MSG_PEEK;
3956     }
3957     while ((rv = recv(osfd,buf,len,osflags)) == -1) {
3958         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3959             && (!fd->secret->nonblocking)) {
3960             FD_ZERO(&rd);
3961             FD_SET((SOCKET)osfd, &rd);
3962             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
3963                 tvp = NULL;
3964             } else {
3965                 tv.tv_sec = PR_IntervalToSeconds(timeout);
3966                 tv.tv_usec = PR_IntervalToMicroseconds(
3967                                  timeout - PR_SecondsToInterval(tv.tv_sec));
3968                 tvp = &tv;
3969             }
3970             if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
3971                                             tvp)) == -1) {
3972                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
3973                 break;
3974             } else if (rv == 0) {
3975                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
3976                 rv = -1;
3977                 break;
3978             }
3979         } else {
3980             _PR_MD_MAP_RECV_ERROR(err);
3981             break;
3982         }
3983     }
3984     return(rv);
3985 }
3986 
_nt_nonblock_send(PRFileDesc * fd,char * buf,int len,PRIntervalTime timeout)3987 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
3988 {
3989     PROsfd osfd = fd->secret->md.osfd;
3990     PRInt32 rv, err;
3991     struct timeval tv, *tvp;
3992     fd_set wd;
3993     PRInt32 bytesSent = 0;
3994 
3995     while(bytesSent < len) {
3996         while ((rv = send(osfd,buf,len,0)) == -1) {
3997             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
3998                 && (!fd->secret->nonblocking)) {
3999                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4000                     tvp = NULL;
4001                 } else {
4002                     tv.tv_sec = PR_IntervalToSeconds(timeout);
4003                     tv.tv_usec = PR_IntervalToMicroseconds(
4004                                      timeout - PR_SecondsToInterval(tv.tv_sec));
4005                     tvp = &tv;
4006                 }
4007                 FD_ZERO(&wd);
4008                 FD_SET((SOCKET)osfd, &wd);
4009                 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4010                                                 tvp)) == -1) {
4011                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4012                     return -1;
4013                 }
4014                 if (rv == 0) {
4015                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4016                     return -1;
4017                 }
4018             } else {
4019                 _PR_MD_MAP_SEND_ERROR(err);
4020                 return -1;
4021             }
4022         }
4023         bytesSent += rv;
4024         if (fd->secret->nonblocking) {
4025             break;
4026         }
4027         if (bytesSent < len) {
4028             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4029                 tvp = NULL;
4030             } else {
4031                 tv.tv_sec = PR_IntervalToSeconds(timeout);
4032                 tv.tv_usec = PR_IntervalToMicroseconds(
4033                                  timeout - PR_SecondsToInterval(tv.tv_sec));
4034                 tvp = &tv;
4035             }
4036             FD_ZERO(&wd);
4037             FD_SET((SOCKET)osfd, &wd);
4038             if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4039                                             tvp)) == -1) {
4040                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4041                 return -1;
4042             }
4043             if (rv == 0) {
4044                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4045                 return -1;
4046             }
4047         }
4048     }
4049     return bytesSent;
4050 }
4051 
_nt_nonblock_writev(PRFileDesc * fd,const PRIOVec * iov,int size,PRIntervalTime timeout)4052 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime timeout)
4053 {
4054     int index;
4055     int sent = 0;
4056     int rv;
4057 
4058     for (index=0; index<size; index++) {
4059         rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
4060         if (rv > 0) {
4061             sent += rv;
4062         }
4063         if ( rv != iov[index].iov_len ) {
4064             if (rv < 0) {
4065                 if (fd->secret->nonblocking
4066                     && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
4067                     && (sent > 0)) {
4068                     return sent;
4069                 } else {
4070                     return -1;
4071                 }
4072             }
4073             /* Only a nonblocking socket can have partial sends */
4074             PR_ASSERT(fd->secret->nonblocking);
4075             return sent;
4076         }
4077     }
4078 
4079     return sent;
4080 }
4081 
_nt_nonblock_sendto(PRFileDesc * fd,const char * buf,int len,const struct sockaddr * addr,int addrlen,PRIntervalTime timeout)4082 static PRInt32 _nt_nonblock_sendto(
4083     PRFileDesc *fd, const char *buf, int len,
4084     const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
4085 {
4086     PROsfd osfd = fd->secret->md.osfd;
4087     PRInt32 rv, err;
4088     struct timeval tv, *tvp;
4089     fd_set wd;
4090     PRInt32 bytesSent = 0;
4091 
4092     while(bytesSent < len) {
4093         while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
4094             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4095                 && (!fd->secret->nonblocking)) {
4096                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4097                     tvp = NULL;
4098                 } else {
4099                     tv.tv_sec = PR_IntervalToSeconds(timeout);
4100                     tv.tv_usec = PR_IntervalToMicroseconds(
4101                                      timeout - PR_SecondsToInterval(tv.tv_sec));
4102                     tvp = &tv;
4103                 }
4104                 FD_ZERO(&wd);
4105                 FD_SET((SOCKET)osfd, &wd);
4106                 if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4107                                                 tvp)) == -1) {
4108                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4109                     return -1;
4110                 }
4111                 if (rv == 0) {
4112                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4113                     return -1;
4114                 }
4115             } else {
4116                 _PR_MD_MAP_SENDTO_ERROR(err);
4117                 return -1;
4118             }
4119         }
4120         bytesSent += rv;
4121         if (fd->secret->nonblocking) {
4122             break;
4123         }
4124         if (bytesSent < len) {
4125             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
4126                 tvp = NULL;
4127             } else {
4128                 tv.tv_sec = PR_IntervalToSeconds(timeout);
4129                 tv.tv_usec = PR_IntervalToMicroseconds(
4130                                  timeout - PR_SecondsToInterval(tv.tv_sec));
4131                 tvp = &tv;
4132             }
4133             FD_ZERO(&wd);
4134             FD_SET((SOCKET)osfd, &wd);
4135             if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL,
4136                                             tvp)) == -1) {
4137                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4138                 return -1;
4139             }
4140             if (rv == 0) {
4141                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4142                 return -1;
4143             }
4144         }
4145     }
4146     return bytesSent;
4147 }
4148 
_nt_nonblock_recvfrom(PRFileDesc * fd,char * buf,int len,struct sockaddr * addr,int * addrlen,PRIntervalTime timeout)4149 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
4150 {
4151     PROsfd osfd = fd->secret->md.osfd;
4152     PRInt32 rv, err;
4153     struct timeval tv, *tvp;
4154     fd_set rd;
4155 
4156     while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
4157         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
4158             && (!fd->secret->nonblocking)) {
4159             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
4160                 tvp = NULL;
4161             } else {
4162                 tv.tv_sec = PR_IntervalToSeconds(timeout);
4163                 tv.tv_usec = PR_IntervalToMicroseconds(
4164                                  timeout - PR_SecondsToInterval(tv.tv_sec));
4165                 tvp = &tv;
4166             }
4167             FD_ZERO(&rd);
4168             FD_SET((SOCKET)osfd, &rd);
4169             if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL,
4170                                             tvp)) == -1) {
4171                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
4172                 break;
4173             } else if (rv == 0) {
4174                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
4175                 rv = -1;
4176                 break;
4177             }
4178         } else {
4179             _PR_MD_MAP_RECVFROM_ERROR(err);
4180             break;
4181         }
4182     }
4183     return(rv);
4184 }
4185 
4186 /*
4187  * UDP support: the continuation thread functions and recvfrom and sendto.
4188  */
4189 
pt_InsertTimedInternal(pt_Continuation * op)4190 static void pt_InsertTimedInternal(pt_Continuation *op)
4191 {
4192     PRInt32 delta = 0;
4193     pt_Continuation *t_op = NULL;
4194     PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
4195 
4196     /*
4197      * If this element operation isn't timed, it gets queued at the
4198      * end of the list (just after pt_tq.tail) and we're
4199      * finishd early.
4200      */
4201     if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
4202     {
4203         t_op = pt_tq.tail;  /* put it at the end */
4204         goto done;
4205     }
4206 
4207     /*
4208      * The rest of this routine actaully deals with timed ops.
4209      */
4210 
4211     if (NULL != pt_tq.op)
4212     {
4213         /*
4214          * To find where in the list to put the new operation, form
4215          * the absolute time the operations in question will expire.
4216          *
4217          * The new operation ('op') will expire at now() + op->timeout.
4218          *
4219          * The operation that will time out furthest in the future will
4220          * do so at pt_tq.epoch + pt_tq.op->timeout.
4221          *
4222          * Subsequently earlier timeouts are computed based on the latter
4223          * knowledge by subracting the timeout deltas that are stored in
4224          * the operation list. There are operation[n]->timeout ticks
4225          * between the expiration of operation[n-1] and operation[n].e e
4226          *
4227          * Therefore, the operation[n-1] will expire operation[n]->timeout
4228          * ticks prior to operation[n].
4229          *
4230          * This should be easy!
4231          */
4232         t_op = pt_tq.op;  /* running pointer to queued op */
4233         op_tmo = now + op->timeout;  /* that's in absolute ticks */
4234         qd_tmo = pt_tq.epoch + t_op->timeout;  /* likewise */
4235 
4236         do
4237         {
4238             /*
4239              * If 'op' expires later than t_op, then insert 'op' just
4240              * ahead of t_op. Otherwise, compute when operation[n-1]
4241              * expires and try again.
4242              *
4243              * The actual different between the expiriation of 'op'
4244              * and the current operation what becomes the new operaton's
4245              * timeout interval. That interval is also subtracted from
4246              * the interval of the operation immediately following where
4247              * we stick 'op' (unless the next one isn't timed). The new
4248              * timeout assigned to 'op' takes into account the values of
4249              * now() and when the previous intervals were compured.
4250              */
4251             delta = op_tmo - qd_tmo;
4252             if (delta >= 0)
4253             {
4254                 op->timeout += (now - pt_tq.epoch);
4255                 goto done;
4256             }
4257 
4258             qd_tmo -= t_op->timeout;  /* previous operaton expiration */
4259             t_op = t_op->prev;  /* point to previous operation */
4260             if (NULL != t_op) {
4261                 qd_tmo += t_op->timeout;
4262             }
4263         } while (NULL != t_op);
4264 
4265         /*
4266          * If we got here we backed off the head of the list. That means that
4267          * this timed entry has to go at the head of the list. This is just
4268          * about like having an empty timer list.
4269          */
4270         delta = op->timeout;  /* $$$ is this right? */
4271     }
4272 
4273 done:
4274 
4275     /*
4276      * Insert 'op' into the queue just after t_op or if t_op is null,
4277      * at the head of the list.
4278      *
4279      * If t_op is NULL, the list is currently empty and this is pretty
4280      * easy.
4281      */
4282     if (NULL == t_op)
4283     {
4284         op->prev = NULL;
4285         op->next = pt_tq.head;
4286         pt_tq.head = op;
4287         if (NULL == pt_tq.tail) {
4288             pt_tq.tail = op;
4289         }
4290         else {
4291             op->next->prev = op;
4292         }
4293     }
4294     else
4295     {
4296         op->prev = t_op;
4297         op->next = t_op->next;
4298         if (NULL != op->prev) {
4299             op->prev->next = op;
4300         }
4301         if (NULL != op->next) {
4302             op->next->prev = op;
4303         }
4304         if (t_op == pt_tq.tail) {
4305             pt_tq.tail = op;
4306         }
4307     }
4308 
4309     /*
4310      * Are we adjusting our epoch, etc? Are we replacing
4311      * what was previously the element due to expire furthest
4312      * out in the future? Is this even a timed operation?
4313      */
4314     if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
4315     {
4316         if ((NULL == pt_tq.op)  /* we're the one and only */
4317             || (t_op == pt_tq.op))  /* we're replacing */
4318         {
4319             pt_tq.op = op;
4320             pt_tq.epoch = now;
4321         }
4322     }
4323 
4324     pt_tq.op_count += 1;
4325 
4326 }  /* pt_InsertTimedInternal */
4327 
4328 /*
4329  * function: pt_FinishTimed
4330  *
4331  * Takes the finished operation out of the timed queue. It
4332  * notifies the initiating thread that the opertions is
4333  * complete and returns to the caller the value of the next
4334  * operation in the list (or NULL).
4335  */
pt_FinishTimedInternal(pt_Continuation * op)4336 static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
4337 {
4338     pt_Continuation *next;
4339 
4340     /* remove this one from the list */
4341     if (NULL == op->prev) {
4342         pt_tq.head = op->next;
4343     }
4344     else {
4345         op->prev->next = op->next;
4346     }
4347     if (NULL == op->next) {
4348         pt_tq.tail = op->prev;
4349     }
4350     else {
4351         op->next->prev = op->prev;
4352     }
4353 
4354     /* did we happen to hit the timed op? */
4355     if (op == pt_tq.op) {
4356         pt_tq.op = op->prev;
4357     }
4358 
4359     next = op->next;
4360     op->next = op->prev = NULL;
4361     op->status = pt_continuation_done;
4362 
4363     pt_tq.op_count -= 1;
4364 #if defined(DEBUG)
4365     pt_debug.continuationsServed += 1;
4366 #endif
4367     PR_NotifyCondVar(op->complete);
4368 
4369     return next;
4370 }  /* pt_FinishTimedInternal */
4371 
ContinuationThread(void * arg)4372 static void ContinuationThread(void *arg)
4373 {
4374     /* initialization */
4375     fd_set readSet, writeSet, exceptSet;
4376     struct timeval tv;
4377     SOCKET *pollingList = 0;                /* list built for polling */
4378     PRIntn pollingListUsed;                 /* # entries used in the list */
4379     PRIntn pollingListNeeded;               /* # entries needed this time */
4380     PRIntn pollingSlotsAllocated = 0;       /* # entries available in list */
4381     PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
4382 
4383     /* do some real work */
4384     while (1)
4385     {
4386         PRIntn rv;
4387         PRStatus status;
4388         PRIntn pollIndex;
4389         pt_Continuation *op;
4390         PRIntervalTime now = PR_IntervalNow();
4391         PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
4392 
4393         PR_Lock(pt_tq.ml);
4394         while (NULL == pt_tq.head)
4395         {
4396             status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
4397             if ((PR_FAILURE == status)
4398                 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
4399                 break;
4400             }
4401         }
4402         pollingListNeeded = pt_tq.op_count;
4403         PR_Unlock(pt_tq.ml);
4404 
4405         /* Okay. We're history */
4406         if ((PR_FAILURE == status)
4407             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
4408             break;
4409         }
4410 
4411         /*
4412          * We are not holding the pt_tq.ml lock now, so more items may
4413          * get added to pt_tq during this window of time.  We hope
4414          * that 10 more spaces in the polling list should be enough.
4415          */
4416 
4417         FD_ZERO(&readSet);
4418         FD_ZERO(&writeSet);
4419         FD_ZERO(&exceptSet);
4420         pollingListNeeded += 10;
4421         if (pollingListNeeded > pollingSlotsAllocated)
4422         {
4423             if (NULL != pollingList) {
4424                 PR_DELETE(pollingList);
4425             }
4426             pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
4427             PR_ASSERT(NULL != pollingList);
4428             pollingSlotsAllocated = pollingListNeeded;
4429         }
4430 
4431 #if defined(DEBUG)
4432         if (pollingListNeeded > pt_debug.pollingListMax) {
4433             pt_debug.pollingListMax = pollingListUsed;
4434         }
4435 #endif
4436 
4437         /*
4438          * Build up a polling list.
4439          * This list is sorted on time. Operations that have been
4440          * interrupted are completed and not included in the list.
4441          * There is an assertion that the operation is in progress.
4442          */
4443         pollingListUsed = 0;
4444         PR_Lock(pt_tq.ml);
4445 
4446         for (op = pt_tq.head; NULL != op;)
4447         {
4448             if (pt_continuation_abort == op->status)
4449             {
4450                 op->result.code = -1;
4451                 op->syserrno = WSAEINTR;
4452                 op = pt_FinishTimedInternal(op);
4453             }
4454             else
4455             {
4456                 PR_ASSERT(pt_continuation_done != op->status);
4457                 op->status = pt_continuation_inprogress;
4458                 if (op->event & PR_POLL_READ) {
4459                     FD_SET(op->arg1.osfd, &readSet);
4460                 }
4461                 if (op->event & PR_POLL_WRITE) {
4462                     FD_SET(op->arg1.osfd, &writeSet);
4463                 }
4464                 if (op->event & PR_POLL_EXCEPT) {
4465                     FD_SET(op->arg1.osfd, &exceptSet);
4466                 }
4467                 pollingList[pollingListUsed] = op->arg1.osfd;
4468                 pollingListUsed += 1;
4469                 if (pollingListUsed == pollingSlotsAllocated) {
4470                     break;
4471                 }
4472                 op = op->next;
4473             }
4474         }
4475 
4476         PR_Unlock(pt_tq.ml);
4477 
4478         /*
4479          * If 'op' isn't NULL at this point, then we didn't get to
4480          * the end of the list. That means that more items got added
4481          * to the list than we anticipated. So, forget this iteration,
4482          * go around the horn again.
4483          * One would hope this doesn't happen all that often.
4484          */
4485         if (NULL != op)
4486         {
4487 #if defined(DEBUG)
4488             pt_debug.predictionsFoiled += 1;  /* keep track */
4489 #endif
4490             continue;  /* make it rethink things */
4491         }
4492 
4493         /* there's a chance that all ops got blown away */
4494         if (NULL == pt_tq.head) {
4495             continue;
4496         }
4497         /* if not, we know this is the shortest timeout */
4498         timeout = pt_tq.head->timeout;
4499 
4500         /*
4501          * We don't want to wait forever on this poll. So keep
4502          * the interval down. The operations, if they are timed,
4503          * still have to timeout, while those that are not timed
4504          * should persist forever. But they may be aborted. That's
4505          * what this anxiety is all about.
4506          */
4507         if (timeout > mx_select_ticks) {
4508             timeout = mx_select_ticks;
4509         }
4510 
4511         if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout) {
4512             pt_tq.head->timeout -= timeout;
4513         }
4514         tv.tv_sec = PR_IntervalToSeconds(timeout);
4515         tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
4516 
4517         rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
4518 
4519         if (0 == rv)  /* poll timed out - what about leading op? */
4520         {
4521             if (0 == pt_tq.head->timeout)
4522             {
4523                 /*
4524                  * The leading element of the timed queue has timed
4525                  * out. Get rid of it. In any case go around the
4526                  * loop again, computing the polling list, checking
4527                  * for interrupted operations.
4528                  */
4529                 PR_Lock(pt_tq.ml);
4530                 do
4531                 {
4532                     pt_tq.head->result.code = -1;
4533                     pt_tq.head->syserrno = WSAETIMEDOUT;
4534                     op = pt_FinishTimedInternal(pt_tq.head);
4535                 } while ((NULL != op) && (0 == op->timeout));
4536                 PR_Unlock(pt_tq.ml);
4537             }
4538             continue;
4539         }
4540 
4541         if (-1 == rv && (WSAGetLastError() == WSAEINTR
4542                          || WSAGetLastError() == WSAEINPROGRESS))
4543         {
4544             continue;               /* go around the loop again */
4545         }
4546 
4547         /*
4548          * select() says that something in our list is ready for some more
4549          * action or is an invalid fd. Find it, load up the operation and
4550          * see what happens.
4551          */
4552 
4553         PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
4554 
4555 
4556         /*
4557          * $$$ There's a problem here. I'm running the operations list
4558          * and I'm not holding any locks. I don't want to hold the lock
4559          * and do the operation, so this is really messed up..
4560          *
4561          * This may work out okay. The rule is that only this thread,
4562          * the continuation thread, can remove elements from the list.
4563          * Therefore, the list is at worst, longer than when we built
4564          * the polling list.
4565          */
4566         op = pt_tq.head;
4567         for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
4568         {
4569             PRInt16 revents = 0;
4570 
4571             PR_ASSERT(NULL != op);
4572 
4573             /*
4574              * This one wants attention. Redo the operation.
4575              * We know that there can only be more elements
4576              * in the op list than we knew about when we created
4577              * the poll list. Therefore, we might have to skip
4578              * a few ops to find the right one to operation on.
4579              */
4580             while (pollingList[pollIndex] != op->arg1.osfd )
4581             {
4582                 op = op->next;
4583                 PR_ASSERT(NULL != op);
4584             }
4585 
4586             if (FD_ISSET(op->arg1.osfd, &readSet)) {
4587                 revents |= PR_POLL_READ;
4588             }
4589             if (FD_ISSET(op->arg1.osfd, &writeSet)) {
4590                 revents |= PR_POLL_WRITE;
4591             }
4592             if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
4593                 revents |= PR_POLL_EXCEPT;
4594             }
4595 
4596             /*
4597              * Sip over all those not in progress. They'll be
4598              * pruned next time we build a polling list. Call
4599              * the continuation function. If it reports completion,
4600              * finish off the operation.
4601              */
4602             if (revents && (pt_continuation_inprogress == op->status)
4603                 && (op->function(op, revents)))
4604             {
4605                 PR_Lock(pt_tq.ml);
4606                 op = pt_FinishTimedInternal(op);
4607                 PR_Unlock(pt_tq.ml);
4608             }
4609         }
4610     }
4611     if (NULL != pollingList) {
4612         PR_DELETE(pollingList);
4613     }
4614 }  /* ContinuationThread */
4615 
pt_Continue(pt_Continuation * op)4616 static int pt_Continue(pt_Continuation *op)
4617 {
4618     PRStatus rv;
4619     /* Finish filling in the blank slots */
4620     op->status = pt_continuation_sumbitted;
4621     op->complete = PR_NewCondVar(pt_tq.ml);
4622 
4623     PR_Lock(pt_tq.ml);  /* we provide the locking */
4624 
4625     pt_InsertTimedInternal(op);  /* insert in the structure */
4626 
4627     PR_NotifyCondVar(pt_tq.new_op);  /* notify the continuation thread */
4628 
4629     while (pt_continuation_done != op->status)  /* wait for completion */
4630     {
4631         rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
4632         /*
4633          * If we get interrupted, we set state the continuation thread will
4634          * see and allow it to finish the I/O operation w/ error. That way
4635          * the rule that only the continuation thread is removing elements
4636          * from the list is still valid.
4637          *
4638          * Don't call interrupt on the continuation thread. That'll just
4639          * piss him off. He's cycling around at least every mx_select_ticks
4640          * anyhow and should notice the request in there.
4641          */
4642         if ((PR_FAILURE == rv)
4643             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
4644             op->status = pt_continuation_abort;    /* our status */
4645         }
4646     }
4647 
4648     PR_Unlock(pt_tq.ml);  /* we provide the locking */
4649 
4650     PR_DestroyCondVar(op->complete);
4651 
4652     return op->result.code;  /* and the primary answer */
4653 }  /* pt_Continue */
4654 
pt_sendto_cont(pt_Continuation * op,PRInt16 revents)4655 static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
4656 {
4657     PRIntn bytes = sendto(
4658                        op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
4659                        (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
4660     op->syserrno = WSAGetLastError();
4661     if (bytes > 0)  /* this is progress */
4662     {
4663         char *bp = op->arg2.buffer;
4664         bp += bytes;  /* adjust the buffer pointer */
4665         op->arg2.buffer = bp;
4666         op->result.code += bytes;  /* accumulate the number sent */
4667         op->arg3.amount -= bytes;  /* and reduce the required count */
4668         return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
4669     }
4670     else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
4671                     PR_FALSE : PR_TRUE;
4672 }  /* pt_sendto_cont */
4673 
pt_recvfrom_cont(pt_Continuation * op,PRInt16 revents)4674 static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
4675 {
4676     PRIntn addr_len = sizeof(*(op->arg5.addr));
4677     op->result.code = recvfrom(
4678                           op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
4679                           op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
4680     op->syserrno = WSAGetLastError();
4681     return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
4682            PR_FALSE : PR_TRUE;
4683 }  /* pt_recvfrom_cont */
4684 
pt_SendTo(SOCKET osfd,const void * buf,PRInt32 amount,PRInt32 flags,const PRNetAddr * addr,PRIntn addrlen,PRIntervalTime timeout)4685 static PRInt32 pt_SendTo(
4686     SOCKET osfd, const void *buf,
4687     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
4688     PRIntn addrlen, PRIntervalTime timeout)
4689 {
4690     PRInt32 bytes = -1, err;
4691     PRBool fNeedContinue = PR_FALSE;
4692 
4693     bytes = sendto(
4694                 osfd, buf, amount, flags,
4695                 (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
4696     if (bytes == -1) {
4697         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
4698             fNeedContinue = PR_TRUE;
4699         }
4700         else {
4701             _PR_MD_MAP_SENDTO_ERROR(err);
4702         }
4703     }
4704     if (fNeedContinue == PR_TRUE)
4705     {
4706         pt_Continuation op;
4707         op.arg1.osfd = osfd;
4708         op.arg2.buffer = (void*)buf;
4709         op.arg3.amount = amount;
4710         op.arg4.flags = flags;
4711         op.arg5.addr = (PRNetAddr*)addr;
4712         op.timeout = timeout;
4713         op.result.code = 0;  /* initialize the number sent */
4714         op.function = pt_sendto_cont;
4715         op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
4716         bytes = pt_Continue(&op);
4717         if (bytes < 0) {
4718             WSASetLastError(op.syserrno);
4719             _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
4720         }
4721     }
4722     return bytes;
4723 }  /* pt_SendTo */
4724 
pt_RecvFrom(SOCKET osfd,void * buf,PRInt32 amount,PRInt32 flags,PRNetAddr * addr,PRIntn * addr_len,PRIntervalTime timeout)4725 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
4726                            PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
4727 {
4728     PRInt32 bytes = -1, err;
4729     PRBool fNeedContinue = PR_FALSE;
4730 
4731     bytes = recvfrom(
4732                 osfd, buf, amount, flags,
4733                 (struct sockaddr*)addr, addr_len);
4734     if (bytes == -1) {
4735         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
4736             fNeedContinue = PR_TRUE;
4737         }
4738         else {
4739             _PR_MD_MAP_RECVFROM_ERROR(err);
4740         }
4741     }
4742 
4743     if (fNeedContinue == PR_TRUE)
4744     {
4745         pt_Continuation op;
4746         op.arg1.osfd = osfd;
4747         op.arg2.buffer = buf;
4748         op.arg3.amount = amount;
4749         op.arg4.flags = flags;
4750         op.arg5.addr = addr;
4751         op.timeout = timeout;
4752         op.function = pt_recvfrom_cont;
4753         op.event = PR_POLL_READ | PR_POLL_EXCEPT;
4754         bytes = pt_Continue(&op);
4755         if (bytes < 0) {
4756             WSASetLastError(op.syserrno);
4757             _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
4758         }
4759     }
4760     return bytes;
4761 }  /* pt_RecvFrom */
4762