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