/
doscalls.c
2944 lines (2412 loc) · 90.6 KB
1
2
3
4
5
6
7
8
/**
* 2ine; an OS/2 emulator for Linux.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
9
#include "os2native16.h"
10
11
#include "doscalls.h"
12
13
#include <unistd.h>
#include <limits.h>
14
#include <time.h>
15
16
#include <dirent.h>
#include <fcntl.h>
17
#include <sys/types.h>
18
#include <sys/wait.h>
19
#include <sys/stat.h>
20
21
#include <sys/sysinfo.h>
#include <sys/time.h>
22
#include <sys/resource.h>
23
#include <sys/stat.h>
24
25
#include "doscalls-lx.h"
26
27
28
static pthread_mutex_t GMutexDosCalls;
29
30
typedef struct ExitListItem
{
31
PFNEXITLIST fn;
32
33
34
35
36
37
uint32 priority;
struct ExitListItem *next;
} ExitListItem;
static ExitListItem *GExitList = NULL;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Flags returned for character devices from DosQueryHType()...
enum {
DAW_STDIN = (1 << 0),
DAW_STDOUT = (1 << 1),
DAW_NUL = (1 << 2),
DAW_CLOCK = (1 << 3),
DAW_SPEC = (1 << 4),
DAW_ADD_ON = (1 << 5),
DAW_GIOCTL = (1 << 6),
DAW_LEVEL0 = 0,
DAW_LEVEL1 = (1 << 7),
DAW_LEVEL2 = (1 << 8),
DAW_LEVEL3 = (1 << 7) | (1 << 8),
DAW_FCNLEV = (1 << 7) | (1 << 8) | (1 << 9),
// ((1 << 10) is reserved, I think.)
DAW_OPENCLOSE = (1 << 11),
DAW_SHARE = (1 << 12),
DAW_NONIBM = (1 << 13),
DAW_IDC = (1 << 14),
DAW_CHARACTER = (1 << 15)
};
typedef struct HFileInfo
{
65
66
67
68
int fd; // unix file descriptor.
ULONG type; // file, pipe, device, etc.
ULONG attr; // for character devices, DAW_*
ULONG flags; // OPEN_FLAGS_*
69
} HFileInfo;
70
71
72
static HFileInfo *HFiles = NULL;
static uint32 MaxHFiles = 0;
73
74
75
76
77
78
79
typedef struct Thread
{
pthread_t thread;
size_t stacklen;
PFNTHREAD fn;
ULONG fnarg;
80
uint16 selector;
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
struct Thread *prev;
struct Thread *next;
} Thread;
static Thread *GDeadThreads = NULL;
typedef struct
{
pthread_mutex_t mutex;
pthread_cond_t condition;
volatile int posted;
volatile int waiting;
} EventSem;
96
97
98
99
100
101
102
103
104
105
106
107
typedef struct
{
DIR *dirp;
ULONG attr;
ULONG level;
char pattern[CCHMAXPATHCOMP];
} DirFinder;
static DirFinder GHDir1;
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
static int grabLock(pthread_mutex_t *lock)
{
return (pthread_mutex_lock(lock) == 0);
} // grabLock
static void ungrabLock(pthread_mutex_t *lock)
{
pthread_mutex_unlock(lock);
} // ungrabLock
static void timespecNowPlusMilliseconds(struct timespec *waittime, const ULONG ulTimeout)
{
clock_gettime(CLOCK_REALTIME, waittime);
waittime->tv_sec += ulTimeout / 1000;
waittime->tv_nsec += (ulTimeout % 1000) * 1000000;
if (waittime->tv_nsec >= 1000000000) {
waittime->tv_sec++;
waittime->tv_nsec -= 1000000000;
assert(waittime->tv_nsec < 1000000000);
} // if
} // timespecNowPlusMilliseconds
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
static PTIB2 getTib2(void)
{
// just read the FS register, since we have to stick it there anyhow...
PTIB2 ptib2;
__asm__ __volatile__ ( "movl %%fs:0xC, %0 \n\t" : "=r" (ptib2) );
return ptib2;
} // getTib2
static PTIB getTib(void)
{
// we store the TIB2 struct right after the TIB struct on the stack,
// so get the TIB2's linear address from %fs:0xC, then step back
// to the TIB's linear address.
uint8 *ptib2 = (uint8 *) getTib2();
return (PTIB) (ptib2 - sizeof (TIB));
} // getTib
148
APIRET DosGetInfoBlocks(PTIB *pptib, PPIB *pppib)
149
{
150
151
TRACE_NATIVE("DosGetInfoBlocks(%p, %p)", pptib, pppib);
152
if (pptib != NULL)
153
*pptib = getTib();
154
155
156
if (pppib != NULL)
*pppib = (PPIB) &GLoaderState->pib;
157
158
159
160
return 0;
} // DosGetInfoBlocks
161
APIRET DosQuerySysInfo(ULONG first, ULONG last, PVOID _buf, ULONG buflen)
162
{
163
164
TRACE_NATIVE("DosQuerySysInfo(%u, %u, %p, %u)", (uint) first, (uint) last, _buf, (uint) buflen);
165
uint32 *buf = (uint32 *) _buf;
166
167
if (last < first) return ERROR_INVALID_PARAMETER;
if ( (buflen / sizeof (uint32)) < ((last - first) + 1) ) return ERROR_BUFFER_OVERFLOW;
168
169
170
171
172
173
174
175
176
177
178
179
for (uint32 varid = first; varid <= last; varid++) {
switch (varid) {
case QSV_MAX_PATH_LENGTH: *(buf++) = PATH_MAX; break;
case QSV_MAX_TEXT_SESSIONS: *(buf++) = 999999; break;
case QSV_MAX_PM_SESSIONS: *(buf++) = 999999; break;
case QSV_MAX_VDM_SESSIONS: *(buf++) = 0; break;
case QSV_BOOT_DRIVE: *(buf++) = 3; break; // "C:"
case QSV_DYN_PRI_VARIATION: *(buf++) = 1; break;
case QSV_MAX_WAIT: *(buf++) = 1; break;
case QSV_MIN_SLICE: *(buf++) = 1; break;
case QSV_MAX_SLICE: *(buf++) = 10; break;
case QSV_PAGE_SIZE: *(buf++) = 4096; break;
180
// !!! FIXME: change the version number in some way so apps can know this isn't actually OS/2.
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
case QSV_VERSION_MAJOR: *(buf++) = 20; break; // OS/2 Warp 4.0
case QSV_VERSION_MINOR: *(buf++) = 40; break; // OS/2 Warp 4.0
case QSV_VERSION_REVISION: *(buf++) = 0; break; // OS/2 Warp 4.0
case QSV_MS_COUNT: {
static long startoffset = 0;
if (startoffset == 0) {
struct timespec ts;
struct sysinfo info;
sysinfo(&info);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
startoffset = info.uptime - ((long) ts.tv_sec);
} // if
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
*(buf++) = (uint32) (((long) ts.tv_sec) - startoffset);
break;
} // case
case QSV_TIME_LOW: *(buf++) = (uint32) time(NULL); break;
case QSV_TIME_HIGH: *(buf++) = 0; break; // !!! FIXME: 32-bit Linux time_t is 32-bits!
case QSV_TOTPHYSMEM: {
struct sysinfo info;
sysinfo(&info);
*(buf++) = (uint32) info.totalram;
break;
} // case
case QSV_TOTRESMEM: {
struct sysinfo info;
sysinfo(&info);
*(buf++) = (uint32) info.totalram - info.freeram; // !!! FIXME: probably not right?
break;
} // case
case QSV_TOTAVAILMEM: {
struct sysinfo info;
sysinfo(&info);
*(buf++) = (uint32) info.freeram;
break;
} // case
case QSV_MAXPRMEM: *(buf++) = 0x7FFFFFFF; break; // !!! FIXME: should I list all 4 gigs? Also: what about machines with < 2 gigs?
case QSV_MAXSHMEM: *(buf++) = 0x7FFFFFFF; break; // !!! FIXME: should I list all 4 gigs? Also: what about machines with < 2 gigs?
case QSV_TIMER_INTERVAL: *(buf++) = 10; break;
case QSV_MAX_COMP_LENGTH: *(buf++) = NAME_MAX; break;
case QSV_FOREGROUND_FS_SESSION: *(buf++) = 1; break; // !!! FIXME
case QSV_FOREGROUND_PROCESS: *(buf++) = 1; break; // !!! FIXME
233
default: return ERROR_INVALID_PARAMETER;
234
235
236
} // switch
} // for
237
return NO_ERROR;
238
239
240
} // DosQuerySysInfo
241
APIRET DosQueryModuleName(HMODULE hmod, ULONG buflen, PCHAR buf)
242
{
243
244
TRACE_NATIVE("DosQueryModuleName(%u, %u, %p)", (uint) hmod, (uint) buflen, buf);
245
246
const LxModule *lxmod = (LxModule *) hmod;
// !!! FIXME: error 6 ERROR_INVALID_HANDLE
247
if (strlen(lxmod->os2path) >= buflen)
248
return ERROR_BAD_LENGTH;
249
strcpy(buf, lxmod->os2path);
250
return NO_ERROR;
251
252
} // DosQueryModuleName
253
254
APIRET DosScanEnv(PSZ name, PSZ *outval)
255
{
256
257
TRACE_NATIVE("DosScanEnv('%s', %p)", name, outval);
258
char *env = GLoaderState->pib.pib_pchenv;
259
260
261
262
const size_t len = strlen(name);
while (*env) {
if ((strncmp(env, name, len) == 0) && (env[len] == '=')) {
*outval = env + len + 1;
263
return NO_ERROR;
264
265
266
267
} // if
env += strlen(env) + 1;
} // while
268
return ERROR_ENVVAR_NOT_FOUND;
269
270
} // DosScanEnv
271
272
273
274
275
276
277
278
279
280
static int getHFileUnixDescriptor(HFILE h)
{
grabLock(&GMutexDosCalls);
const int fd = (h < MaxHFiles) ? HFiles[h].fd : -1;
ungrabLock(&GMutexDosCalls);
return fd;
} // getHFileUnixDescriptor
281
APIRET DosWrite(HFILE h, PVOID buf, ULONG buflen, PULONG actual)
282
{
283
TRACE_NATIVE("DosWrite(%u, %p, %u, %p)", (uint) h, buf, (uint) buflen, actual);
284
285
286
const int fd = getHFileUnixDescriptor(h);
if (fd == -1)
287
288
return ERROR_INVALID_HANDLE;
289
290
// !!! FIXME: writing to a terminal should probably convert CR/LF to LF.
const int rc = write(fd, buf, buflen);
291
292
if (rc < 0) {
*actual = 0;
293
return ERROR_DISK_FULL; // !!! FIXME: map these errors.
294
} // if
295
296
*actual = (uint32) rc;
297
return NO_ERROR;
298
299
300
301
302
303
304
305
} // DosWrite
static void runDosExitList(const uint32 why)
{
ExitListItem *next = NULL;
for (ExitListItem *item = GExitList; item; item = next) {
// don't run any of these more than once:
// http://www.verycomputer.com/3_c1f6e02c06ed108e_1.htm
306
PFNEXITLIST fn = item->fn;
307
308
309
next = item->next;
GExitList = next;
free(item);
310
//printf("calling dosexitlist item %p\n", fn); fflush(stdout);
311
fn(why);
312
//printf("dosexitlist item %p returned!\n", fn); fflush(stdout);
313
314
315
} // for
} // runDosExitList
316
VOID DosExit(ULONG action, ULONG exitcode)
317
{
318
319
TRACE_NATIVE("DosExit(%u, %u)", (uint) action, (uint) exitcode);
320
// !!! FIXME: what does a value other than 0 or 1 do here?
321
if (action == EXIT_THREAD) {
322
323
324
// !!! FIXME: If last thread: terminate process.
FIXME("if last thread, treat as DosExit(1)"); // right now it will terminate process like exit(0), without running the exit list or reporting exitcode.
pthread_exit(NULL); // DosWaitThread doesn't report exit codes.
325
326
327
} // if
// terminate the process.
328
runDosExitList(TC_EXIT);
329
330
GLoaderState->terminate(exitcode);
331
332
} // DosExit
333
APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn)
334
{
335
336
TRACE_NATIVE("DosExitList(%u, %p)", (uint) ordercode, fn);
337
if (fn == NULL)
338
return ERROR_INVALID_FUNCTION;
339
340
341
342
343
344
345
const uint8 cmd = ordercode & 0xFF;
const uint8 arg = (ordercode >> 8) & 0xFF;
ExitListItem *prev = NULL;
ExitListItem *item = NULL;
// !!! FIXME: docs say this is illegal, but have to check what OS/2 actually does here.
346
if ((cmd != EXLST_ADD) && (arg != 0))
347
return ERROR_INVALID_DATA;
348
349
switch (cmd) {
350
case EXLST_ADD: {
351
352
ExitListItem *newitem = (ExitListItem *) malloc(sizeof (ExitListItem));
if (!newitem)
353
return ERROR_NOT_ENOUGH_MEMORY;
354
newitem->fn = fn;
355
356
357
358
359
360
361
362
363
364
365
366
for (item = GExitList; item; item = item->next) {
if (item->priority >= ((uint32) arg))
break;
prev = item;
} // for
if (prev) {
prev->next = newitem;
newitem->next = item;
} else {
newitem->next = GExitList;
GExitList = newitem;
} // else
367
return NO_ERROR;
368
369
} // case
370
case EXLST_REMOVE: {
371
372
373
374
375
376
377
for (item = GExitList; item; item = item->next) {
if (item->fn == fn) {
if (prev)
prev->next = item->next;
else
GExitList = item->next;
free(item);
378
return NO_ERROR;
379
380
381
} // if
prev = item;
} // for
382
return ERROR_INVALID_FUNCTION; // !!! FIXME: yeah?
383
384
} // case
385
386
case EXLST_EXIT:
return NO_ERROR; // ... just treat this as a no-op, I guess...?
387
388
default: return ERROR_INVALID_DATA;
389
390
} // switch
391
return NO_ERROR;
392
393
} // DosExitList
394
APIRET DosCreateEventSem(PSZ name, PHEV phev, ULONG attr, BOOL32 state)
395
{
396
TRACE_NATIVE("DosCreateEventSem('%s', %p, %u, %u)", name, phev, (uint) attr, (uint) state);
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
if (name != NULL) {
FIXME("implement named semaphores");
return ERROR_NOT_ENOUGH_MEMORY;
} // if
if (attr & DC_SEM_SHARED) {
FIXME("implement shared semaphores");
} // if
assert(sizeof (HEV) == sizeof (EventSem *));
EventSem *sem = (EventSem *) malloc(sizeof (EventSem));
if (sem == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
if (pthread_mutex_init(&sem->mutex, NULL) == -1) {
free(sem);
return ERROR_NOT_ENOUGH_MEMORY;
} // if
if (pthread_cond_init(&sem->condition, NULL) == -1) {
pthread_mutex_destroy(&sem->mutex);
free(sem);
return ERROR_NOT_ENOUGH_MEMORY;
} // if
sem->posted = state ? 1 : 0;
*phev = (HEV) sem;
427
return NO_ERROR;
428
429
} // DosCreateEventSem
430
APIRET DosCreateMutexSem(PSZ name, PHMTX phmtx, ULONG attr, BOOL32 state)
431
{
432
TRACE_NATIVE("DosCreateMutexSem('%s', %p, %u, %u)", name, phmtx, (uint) attr, (uint) state);
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
if (name != NULL) {
FIXME("implement named mutexes");
return ERROR_NOT_ENOUGH_MEMORY;
} // if
if (attr & DC_SEM_SHARED) {
FIXME("implement shared mutexes");
} // if
assert(sizeof (HMTX) == sizeof (pthread_mutex_t *));
pthread_mutex_t *mtx = (pthread_mutex_t *) malloc(sizeof (pthread_mutex_t));
if (mtx == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// OS/2 mutexes are recursive.
pthread_mutexattr_t muxattr;
pthread_mutexattr_init(&muxattr);
pthread_mutexattr_settype(&muxattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutexattr_setrobust(&muxattr, PTHREAD_MUTEX_ROBUST);
const int rc = pthread_mutex_init(mtx, &muxattr);
pthread_mutexattr_destroy(&muxattr);
if (rc == -1) {
free(mtx);
return ERROR_NOT_ENOUGH_MEMORY;
} // if
if (state)
pthread_mutex_lock(mtx);
*phmtx = (HMTX) mtx;
467
return NO_ERROR;
468
469
470
} // DosCreateMutexSem
// !!! FIXME: this is obviously not correct.
471
APIRET DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
472
{
473
474
TRACE_NATIVE("DosSetExceptionHandler(%p)", rec);
return NO_ERROR;
475
476
} // DosSetExceptionHandler
477
478
479
480
481
482
483
// !!! FIXME: this is obviously not correct.
APIRET DosUnsetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
{
TRACE_NATIVE("DosUnsetExceptionHandler(%p)", rec);
return NO_ERROR;
} // DosSetExceptionHandler
484
ULONG _DosFlatToSel(PVOID ptr)
485
{
486
TRACE_NATIVE("DosFlatToSel(%p)", ptr);
487
return GLoaderState->convert32to1616(ptr);
488
489
490
} // _DosFlatToSel
// DosFlatToSel() passes its argument in %eax, so a little asm to bridge that...
491
492
493
// Note that this isn't syscall calling conventions at all; CSet/2 generates
// thunking code that expects this function to preserve %ecx, so we'll just
// go ahead and save a bunch of stuff, just in case.
494
495
496
497
__asm__ (
".globl DosFlatToSel \n\t"
".type DosFlatToSel, @function \n\t"
"DosFlatToSel: \n\t"
498
499
500
501
502
503
" pushl %ebx \n\t" // save off a bunch of stuff. Better safe than sorry.
" pushl %ecx \n\t"
" pushl %edx \n\t"
" pushl %edi \n\t"
" pushl %esi \n\t"
" pushl %eax \n\t" // the actual argument to DosFlatToSel.
504
" call _DosFlatToSel \n\t"
505
506
507
508
509
510
" addl $4, %esp \n\t" // clear argument to DosFlatToSel.
" popl %esi \n\t"
" popl %edi \n\t"
" popl %edx \n\t" // save off a bunch of stuff.
" popl %ecx \n\t"
" popl %ebx \n\t"
511
512
513
" ret \n\t"
".size _DosFlatToSel, .-_DosFlatToSel \n\t"
);
514
515
APIRET DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes)
516
{
517
518
TRACE_NATIVE("DosSetSignalExceptionFocus(%u, %p)", (uint) flag, pulTimes);
519
520
if (flag == 0) {
if (GLoaderState->main_module->signal_exception_focus_count == 0)
521
return ERROR_ALREADY_RESET;
522
523
524
GLoaderState->main_module->signal_exception_focus_count--;
} else if (flag == 1) {
if (GLoaderState->main_module->signal_exception_focus_count == 0xFFFFFFFF)
525
return ERROR_NESTING_TOO_DEEP;
526
527
528
529
530
531
532
533
534
535
GLoaderState->main_module->signal_exception_focus_count++;
} else {
// !!! FIXME: does OS/2 do something if flag != 0 or 1?
} // else
if (pulTimes)
*pulTimes = GLoaderState->main_module->signal_exception_focus_count;
// !!! FIXME: I guess enable/disable SIGINT handler here?
536
return NO_ERROR;
537
538
} // DosSetSignalExceptionFocus
539
APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent)
540
{
541
TRACE_NATIVE("DosSetRelMaxFH(%p, %p)", pincr, pcurrent);
542
543
#if 0
544
545
546
547
548
549
struct rlimit rlim;
int rc = getrlimit(RLIMIT_NOFILE, &rlim);
assert(rc == 0); // this shouldn't fail...right?
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
550
551
if (pincr) {
rlim.rlim_cur = (rlim_t) (((LONG) rlim.rlim_cur) + *pincr);
552
553
554
555
556
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
} // if
} // if
557
558
559
560
561
562
563
#endif
if (pincr != NULL) {
const LONG incr = *pincr;
// OS/2 API docs say reductions in file handles are advisory, so we
// ignore them outright. Fight me.
if (incr > 0) {
564
grabLock(&GMutexDosCalls);
565
566
567
568
569
570
571
HFileInfo *info = (HFileInfo *) realloc(HFiles, sizeof (HFileInfo) * (MaxHFiles + incr));
if (info != NULL) {
HFiles = info;
for (LONG i = 0; i < incr; i++, info++) {
info->fd = -1;
info->type = 0;
info->attr = 0;
572
info->flags = 0;
573
574
575
} // for
MaxHFiles += incr;
} // if
576
ungrabLock(&GMutexDosCalls);
577
578
579
580
} // if
} // if
if (pcurrent != NULL) {
581
grabLock(&GMutexDosCalls);
582
*pcurrent = MaxHFiles;
583
ungrabLock(&GMutexDosCalls);
584
} // if
585
586
587
588
589
590
591
592
593
594
return NO_ERROR; // always returns NO_ERROR even if it fails.
} // DosSetRelMaxFH
APIRET DosAllocMem(PPVOID ppb, ULONG cb, ULONG flag)
{
TRACE_NATIVE("DosAllocMem(%p, %u, %u)", ppb, (uint) cb, (uint) flag);
// !!! FIXME: this API is actually much more complicated than this.
595
*ppb = calloc(1, cb);
596
597
598
599
600
if (!*ppb)
return ERROR_NOT_ENOUGH_MEMORY;
return NO_ERROR;
} // DosAllocMem
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
APIRET DosSubSetMem(PVOID pbBase, ULONG flag, ULONG cb)
{
TRACE_NATIVE("DosSubSetMem(%p, %u, %u)", pbBase, (uint) flag, (uint) cb);
return NO_ERROR; // !!! FIXME: obviously wrong
} // DosSubSetMem
APIRET DosSubAllocMem(PVOID pbBase, PPVOID ppb, ULONG cb)
{
TRACE_NATIVE("DosSubAllocMem(%p, %p, %u)", pbBase, ppb, (uint) cb);
*ppb = malloc(cb); // !!! FIXME: obviously wrong
if (!*ppb)
return ERROR_NOT_ENOUGH_MEMORY;
return NO_ERROR;
} // DosSubAllocMem
APIRET DosQueryHType(HFILE hFile, PULONG pType, PULONG pAttr)
{
TRACE_NATIVE("DosQueryHType(%u, %p, %p)", (uint) hFile, pType, pAttr);
621
622
APIRET retval = NO_ERROR;
623
624
625
626
627
628
629
630
631
632
grabLock(&GMutexDosCalls);
if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1))
retval = ERROR_INVALID_HANDLE;
else {
// OS/2 will dereference a NULL pointer here, but I can't do it...!!!
if (pType) *pType = HFiles[hFile].type;
if (pAttr) *pAttr = HFiles[hFile].attr;
} // else
ungrabLock(&GMutexDosCalls);
633
634
return retval;
635
636
637
638
639
640
641
} // DosQueryHType
APIRET DosSetMem(PVOID pb, ULONG cb, ULONG flag)
{
TRACE_NATIVE("DosSetMem(%p, %u, %u)", pb, (uint) cb, (uint) flag);
return NO_ERROR; // !!! FIXME: obviously wrong.
} // DosSetMem
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
APIRET DosGetDateTime(PDATETIME pdt)
{
TRACE_NATIVE("DosGetDateTime(%p)", pdt);
// we can't use time() for this, since we need sub-second resolution.
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm ltm;
localtime_r(&tv.tv_sec, <m);
pdt->hours = ltm.tm_hour;
pdt->minutes = ltm.tm_min;
pdt->seconds = (ltm.tm_sec == 60) ? 59 : ltm.tm_sec; // !!! FIXME: I assume OS/2 doesn't support leap seconds...
pdt->hundredths = tv.tv_usec / 10000000; // microseconds to hundreths.
pdt->day = ltm.tm_mday;
pdt->month = ltm.tm_mon + 1;
pdt->year = ltm.tm_year + 1900;
pdt->timezone = -(ltm.tm_gmtoff / 60); // OS/2 is minutes west of GMT, Unix is seconds east. Convert and flip!
pdt->weekday = ltm.tm_wday;
return NO_ERROR; // never returns failure.
} // DosGetDateTime
static char *makeUnixPath(const char *os2path, APIRET *err)
{
669
return GLoaderState->makeUnixPath(os2path, (uint32 *) err);
670
671
} // makeUnixPath
672
static APIRET doDosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, LONGLONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2)
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
{
int isReadOnly = 0;
int flags = 0;
const ULONG access_mask = OPEN_ACCESS_READONLY | OPEN_ACCESS_WRITEONLY | OPEN_ACCESS_READWRITE;
switch (fsOpenMode & access_mask) {
case OPEN_ACCESS_READONLY: isReadOnly = 1; flags |= O_RDONLY; break;
case OPEN_ACCESS_WRITEONLY: flags |= O_WRONLY; break;
case OPEN_ACCESS_READWRITE: flags |= O_RDWR; break;
default: return ERROR_INVALID_PARAMETER;
} // switch
if (isReadOnly && (cbFile != 0))
return ERROR_INVALID_PARAMETER;
else if (ulAttribute & FILE_DIRECTORY)
return ERROR_INVALID_PARAMETER; // !!! FIXME: I assume you can't create a directory with DosOpen...right?
else if (isReadOnly && ((fsOpenFlags & 0xF0) == OPEN_ACTION_CREATE_IF_NEW))
return ERROR_INVALID_PARAMETER; // !!! FIXME: is this invalid on OS/2?
else if (isReadOnly && ((fsOpenFlags & 0x0F) == OPEN_ACTION_REPLACE_IF_EXISTS))
return ERROR_INVALID_PARAMETER; // !!! FIXME: is this invalid on OS/2?
else if (fsOpenFlags & 0xfffffe00) // bits (1<<8) through (1<<31) are reserved, must be zero.
return ERROR_INVALID_PARAMETER;
else if (fsOpenFlags & OPEN_FLAGS_DASD) // no good is going to come of this.
return ERROR_OPEN_FAILED;
else if (fsOpenMode & 0xffff0000) // bits (1<<16) through (1<<31) are reserved, must be zero.
return ERROR_INVALID_PARAMETER;
if (peaop2 != NULL) {
FIXME("EAs not yet implemented");
return ERROR_OPEN_FAILED;
}
switch (fsOpenFlags & 0xF0) {
case OPEN_ACTION_FAIL_IF_NEW:
break; // just don't O_CREAT and you're fine.
case OPEN_ACTION_CREATE_IF_NEW:
flags |= O_CREAT | O_EXCL; // the O_EXCL is intentional! see below.
break;
} // switch
int mustNotExist = 0;
int isExclusive = 0;
int isReplacing = 0;
switch (fsOpenFlags & 0x0F) {
case OPEN_ACTION_FAIL_IF_EXISTS:
if (isReadOnly)
mustNotExist = 1;
else {
isExclusive = 1;
flags |= O_CREAT | O_EXCL;
} // else
break;
case OPEN_ACTION_OPEN_IF_EXISTS:
break; // nothing to do here.
case OPEN_ACTION_REPLACE_IF_EXISTS: // right now, we already failed above if readonly.
isReplacing = 1;
flags |= O_TRUNC; // have the open call nuke it, and then we'll ftruncate to grow it if necessary.
break;
} // switch
if (fsOpenMode & OPEN_FLAGS_WRITE_THROUGH)
flags |= O_SYNC; // !!! FIXME: this flag is supposed to drop when inherited by children, but fcntl() can't do that on Linux.
if (fsOpenMode & OPEN_FLAGS_NOINHERIT)
flags |= O_CLOEXEC;
if (fsOpenMode & OPEN_FLAGS_FAIL_ON_ERROR)
FIXME("need other file i/o APIs to do a system dialog box if this handle has i/o failures!");
// O_DIRECT isn't supported on ZFS-on-Linux, so I'm already not on board,
// but also: do we care if programs intended to run on a system with a
// few megabytes of RAM--on an OS that didn't have a resizeable file
// cache!--writes into a multi-gigabyte machine's flexible cache? I don't!
//if (fsOpenMode & OPEN_FLAGS_NO_CACHE)
// flags |= O_DIRECT;
FIXME("There are fsOpenMode flags we currently ignore (like sharing support)");
const mode_t mode = S_IRUSR | ((ulAttribute & FILE_READONLY) ? 0 : S_IWUSR);
FIXME("Most of the file attributes don't make sense on Unix, but we could stuff them in EAs");
HFileInfo *info = HFiles;
uint32 i;
760
761
762
if (!grabLock(&GMutexDosCalls))
return ERROR_SYS_INTERNAL;
763
for (i = 0; i < MaxHFiles; i++, info++) {
764
765
if (info->fd == -1) { // available?
info->fd = -2;
766
break;
767
} // if
768
769
} // for
770
771
772
ungrabLock(&GMutexDosCalls);
if (i == MaxHFiles) {
773
return ERROR_TOO_MANY_OPEN_FILES;
774
}
775
776
777
778
779
780
781
782
783
784
const HFILE hf = i;
APIRET err = NO_ERROR;
char *unixpath = makeUnixPath(pszFileName, &err);
if (!unixpath) {
info->fd = -1;
return err;
} // if
785
//if (strcmp(pszFileName, unixpath) != 0) { fprintf(stderr, "DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); }
786
787
int fd = open(unixpath, flags, mode);
788
789
790
791
792
793
// if failed trying exclusive-create because file already exists, but we
// didn't explicitly _need_ exclusivity, retry without O_EXCL. We always
// try O_EXCL at first when using O_CREAT, so we can tell atomically if
// we were the one that created the file, to satisfy pulAction.
int existed = 0;
794
if ((fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) {
795
existed = 1;
796
797
fd = open(unixpath, flags & ~O_EXCL, mode);
} else if (fd != -1) {
798
799
800
801
existed = 1;
} // else if
free(unixpath);
802
if (fd != -1) {
803
if (mustNotExist) {
804
close(fd);
805
806
807
808
809
info->fd = -1;
return ERROR_OPEN_FAILED; // !!! FIXME: what error does OS/2 return for this?
} else if ( (!existed || isReplacing) && (cbFile > 0) ) {
if (ftruncate(info->fd, cbFile) == -1) {
const int e = errno;
810
close(fd);
811
812
813
814
815
816
info->fd = -1;
errno = e; // let the switch below sort out the OS/2 error code.
} // if
} // else if
} // if
817
if (fd != -1) {
818
819
820
821
if (isReplacing)
FIXME("Replacing a file should delete all its EAs, too");
} // if
822
823
824
info->fd = fd;
if (fd == -1) {
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
switch (errno) {
case EACCES: return ERROR_ACCESS_DENIED;
case EDQUOT: return ERROR_CANNOT_MAKE;
case EEXIST: return ERROR_CANNOT_MAKE;
case EISDIR: return ERROR_ACCESS_DENIED;
case EMFILE: return ERROR_TOO_MANY_OPEN_FILES;
case ENFILE: return ERROR_TOO_MANY_OPEN_FILES;
case ENAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE;
case ENOSPC: return ERROR_DISK_FULL;
case ENOMEM: return ERROR_NOT_ENOUGH_MEMORY;
case ENOENT: return ERROR_FILE_NOT_FOUND; // !!! FIXME: could be PATH_NOT_FOUND too, depending on circumstances.
case ENOTDIR: return ERROR_PATH_NOT_FOUND;
case EPERM: return ERROR_ACCESS_DENIED;
case EROFS: return ERROR_ACCESS_DENIED;
case ETXTBSY: return ERROR_ACCESS_DENIED;
default: return ERROR_OPEN_FAILED; // !!! FIXME: debug logging about missin errno case.
} // switch
__builtin_unreachable();
} // if
845
846
info->flags = fsOpenFlags;
847
848
849
850
851
852
853
854
855
if (isReplacing)
*pulAction = FILE_TRUNCATED;
else if (existed)
*pulAction = FILE_EXISTED;
else
*pulAction = FILE_CREATED;
*pHf = hf;
return NO_ERROR;
856
857
858
859
860
861
} // doDosOpen
APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2)
{
TRACE_NATIVE("DosOpen('%s', %p, %p, %u, %u, %u, %u, %p)", pszFileName, pHf, pulAction, (uint) cbFile, (uint) ulAttribute, (uint) fsOpenFlags, (uint) fsOpenMode, peaop2);
return doDosOpen(pszFileName, pHf, pulAction, (LONGLONG) cbFile, ulAttribute, fsOpenFlags, fsOpenMode, peaop2);
862
863
} // DosOpen
864
865
866
APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
{
TRACE_NATIVE("DosRequestMutexSem(%u, %u)", (uint) hmtx, (uint) ulTimeout);
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
if (!hmtx)
return ERROR_INVALID_HANDLE;
pthread_mutex_t *mutex = (pthread_mutex_t *) hmtx;
int rc;
if (ulTimeout == SEM_IMMEDIATE_RETURN) {
rc = pthread_mutex_trylock(mutex);
} else if (ulTimeout == SEM_INDEFINITE_WAIT) {
rc = pthread_mutex_lock(mutex);
} else {
struct timespec waittime;
timespecNowPlusMilliseconds(&waittime, ulTimeout);
rc = pthread_mutex_timedlock(mutex, &waittime);
} // else
switch (rc) {
case 0: return NO_ERROR;
case EAGAIN: return ERROR_TOO_MANY_SEM_REQUESTS;
case EOWNERDEAD: return ERROR_SEM_OWNER_DIED;
case ETIMEDOUT: return ERROR_TIMEOUT;
default: break;
} // switch
return ERROR_INVALID_HANDLE; // oh well.
893
894
895
896
897
} // DosRequestMutexSem
APIRET DosReleaseMutexSem(HMTX hmtx)
{
TRACE_NATIVE("DosReleaseMutexSem(%u)", (uint) hmtx);
898
899
900
901
902
903
904
905
906
907
908
909
if (!hmtx)
return ERROR_INVALID_HANDLE;
pthread_mutex_t *mutex = (pthread_mutex_t *) hmtx;
const int rc = pthread_mutex_unlock(mutex);
if (rc == 0)
return NO_ERROR;
else if (rc == EPERM)
return ERROR_NOT_OWNER;
return ERROR_INVALID_HANDLE;
910
911
912
913
914
915
916
917
918
919
920
921
922
923
} // DosReleaseMutexSem
APIRET DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual)
{
TRACE_NATIVE("DosSetFilePtr(%u, %d, %u, %p)", (uint) hFile, (int) ib, (uint) method, ibActual);
int whence;
switch (method) {
case FILE_BEGIN: whence = SEEK_SET; break;
case FILE_CURRENT: whence = SEEK_CUR; break;
case FILE_END: whence = SEEK_END; break;
default: return ERROR_INVALID_FUNCTION;
} // switch
924
925
const int fd = getHFileUnixDescriptor(hFile);
if (fd == -1)
926
927
return ERROR_INVALID_HANDLE;
928
const off_t pos = lseek(fd, (off_t) ib, whence);
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
if (pos == -1) {
if (errno == EINVAL)
return ERROR_NEGATIVE_SEEK;
return ERROR_INVALID_FUNCTION; // !!! FIXME: ?
} // if
if (ibActual)
*ibActual = (ULONG) pos;
return NO_ERROR;
} // DosSetFilePtr
APIRET DosRead(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual)
{
TRACE_NATIVE("DosRead(%u, %p, %u, %p)", (uint) hFile, pBuffer, (uint) cbRead, pcbActual);
945
946
const int fd = getHFileUnixDescriptor(hFile);
if (fd == -1)
947
948
return ERROR_INVALID_HANDLE;
949
const ssize_t br = read(fd, pBuffer, cbRead);
950
951
952
953
954
955
956
957
958
959
960
961
962
if (br == -1)
return ERROR_INVALID_FUNCTION; // !!! FIXME: ?
if (pcbActual)
*pcbActual = (ULONG) br;
return NO_ERROR;
} // DosRead
APIRET DosClose(HFILE hFile)
{
TRACE_NATIVE("DosClose(%u)", (uint) hFile);
963
964
const int fd = getHFileUnixDescriptor(hFile);
if (fd == -1)
965
966
return ERROR_INVALID_HANDLE;
967
968
969
if (fd <= 2) { FIXME("remove me"); return NO_ERROR; } // for debugging purposes, remove me later.
const int rc = close(fd);
970
971
972
if (rc == -1)
return ERROR_ACCESS_DENIED; // !!! FIXME: ?
973
grabLock(&GMutexDosCalls);
974
975
976
HFiles[hFile].fd = -1;
HFiles[hFile].type = 0;
HFiles[hFile].attr = 0;
977
978
ungrabLock(&GMutexDosCalls);
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
return NO_ERROR;
} // DosClose
APIRET DosEnterMustComplete(PULONG pulNesting)
{
TRACE_NATIVE("DosEnterMustComplete(%p)", pulNesting);
PTIB2 tib2 = getTib2();
if (tib2->tib2_usMCCount == 0xFFFF) {
return ERROR_NESTING_TOO_DEEP;
} else if (tib2->tib2_usMCCount == 0) {
FIXME("block signals here");
}
tib2->tib2_usMCCount++;
if (pulNesting)
*pulNesting = tib2->tib2_usMCCount;
return NO_ERROR;
} // DosEnterMustComplete