/
doscalls.c
2999 lines (2463 loc) · 93.2 KB
1
#include "os2native16.h"
2
3
#include "doscalls.h"
4
5
#include <unistd.h>
#include <limits.h>
6
#include <time.h>
7
8
#include <dirent.h>
#include <fcntl.h>
9
#include <sys/types.h>
10
#include <sys/wait.h>
11
#include <sys/stat.h>
12
13
#include <sys/sysinfo.h>
#include <sys/time.h>
14
#include <sys/resource.h>
15
#include <sys/stat.h>
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// DosQueryHeaderInfo() is an undocumented OS/2 API that is exported from DOSCALLS. Java uses it.
// This isn't mentioned in the docs or the SDK (beyond the ordinal being listed). I got the basic details
// from Odin32, which lists it in their os2newapi.h header.
enum
{
QHINF_EXEINFO = 1,
QHINF_READRSRCTBL,
QHINF_READFILE,
QHINF_LIBPATHLENGTH,
QHINF_LIBPATH,
QHINF_FIXENTRY,
QHINF_STE,
QHINF_MAPSEL
};
APIRET OS2API DosQueryHeaderInfo(HMODULE hmod, ULONG ulIndex, PVOID pvBuffer, ULONG cbBuffer, ULONG ulSubFunction);
// This is also undocumented (thanks, EDM/2!). Of course, Java uses it.
APIRET OS2API DosQuerySysState(ULONG func, ULONG arg1, ULONG pid, ULONG _res_, PVOID buf, ULONG bufsz);
// This is also undocumented (no idea about this at all, including function params). Of course, Java uses it.
APIRET DosR3ExitAddr(void);
39
40
41
42
43
44
45
46
47
48
49
// These are 16-bit APIs that aren't in the 4.5 toolkit headers. Yeah, Java uses them! Winging it.
typedef int HSEM16, *PHSEM16;
static APIRET16 DosSemRequest(PHSEM16 sem, LONG ms);
static APIRET16 DosSemClear(PHSEM16 sem);
static APIRET16 DosSemWait(PHSEM16 sem, LONG ms);
static APIRET16 DosSemSet(PHSEM16 sem);
static APIRET16 bridge16to32_DosSemRequest(uint8 *args);
static APIRET16 bridge16to32_DosSemClear(uint8 *args);
static APIRET16 bridge16to32_DosSemWait(uint8 *args);
static APIRET16 bridge16to32_DosSemSet(uint8 *args);
50
51
52
static pthread_mutex_t GMutexDosCalls;
53
54
typedef struct ExitListItem
{
55
PFNEXITLIST fn;
56
57
58
59
60
61
uint32 priority;
struct ExitListItem *next;
} ExitListItem;
static ExitListItem *GExitList = NULL;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// 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
{
89
90
91
92
int fd; // unix file descriptor.
ULONG type; // file, pipe, device, etc.
ULONG attr; // for character devices, DAW_*
ULONG flags; // OPEN_FLAGS_*
93
} HFileInfo;
94
95
96
static HFileInfo *HFiles = NULL;
static uint32 MaxHFiles = 0;
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
typedef struct Thread
{
pthread_t thread;
size_t stacklen;
PFNTHREAD fn;
ULONG fnarg;
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;
119
120
121
122
123
124
125
126
127
128
129
130
typedef struct
{
DIR *dirp;
ULONG attr;
ULONG level;
char pattern[CCHMAXPATHCOMP];
} DirFinder;
static DirFinder GHDir1;
131
132
133
134
135
136
137
LX_NATIVE_MODULE_16BIT_SUPPORT()
LX_NATIVE_MODULE_16BIT_API(DosSemRequest)
LX_NATIVE_MODULE_16BIT_API(DosSemClear)
LX_NATIVE_MODULE_16BIT_API(DosSemWait)
LX_NATIVE_MODULE_16BIT_API(DosSemSet)
LX_NATIVE_MODULE_16BIT_SUPPORT_END()
138
LX_NATIVE_MODULE_DEINIT({
139
140
GLoaderState->dosExit = NULL;
141
142
ExitListItem *next = GExitList;
GExitList = NULL;
143
144
145
146
147
for (ExitListItem *item = next; item; item = next) {
next = item->next;
free(item);
} // for
148
149
150
151
152
for (uint32 i = 0; i < MaxHFiles; i++) {
if (HFiles[i].fd != -1)
close(HFiles[i].fd);
} // for
153
154
free(HFiles);
HFiles = NULL;
155
MaxHFiles = 0;
156
157
pthread_mutex_destroy(&GMutexDosCalls);
158
159
LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT();
160
161
})
162
static int initDoscalls(void)
163
{
164
165
166
167
168
169
170
LX_NATIVE_MODULE_INIT_16BIT_SUPPORT()
LX_NATIVE_INIT_16BIT_BRIDGE(DosSemRequest, 8)
LX_NATIVE_INIT_16BIT_BRIDGE(DosSemClear, 4)
LX_NATIVE_INIT_16BIT_BRIDGE(DosSemWait, 8)
LX_NATIVE_INIT_16BIT_BRIDGE(DosSemSet, 4)
LX_NATIVE_MODULE_INIT_16BIT_SUPPORT_END()
171
172
GLoaderState->dosExit = DosExit;
173
174
175
176
177
if (pthread_mutex_init(&GMutexDosCalls, NULL) == -1) {
fprintf(stderr, "pthread_mutex_init failed!\n");
return 0;
} // if
178
179
180
181
182
183
184
185
186
187
188
189
MaxHFiles = 20; // seems to be OS/2's default.
HFiles = (HFileInfo *) malloc(sizeof (HFileInfo) * MaxHFiles);
if (!HFiles) {
fprintf(stderr, "Out of memory!\n");
return 0;
} // if
HFileInfo *info = HFiles;
for (uint32 i = 0; i < MaxHFiles; i++, info++) {
info->fd = -1;
info->type = 0;
info->attr = 0;
190
info->flags = 0;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
} // for
// launching a Hello World program from CMD.EXE seems to inherit several
// file handles. 0, 1, 2 seem to map to stdin, stdout, stderr (and will
// be character devices (maybe CON?) by default, unless you redirect
// to a file in which case they're physical files, and using '|' in
// CMD.EXE will make handle 1 into a Pipe, of course.
// Handles, 4, 6 and 9 were also in use (all character devices, attributes
// 51585, 51392, 51392), but I don't know what these are, if they are
// inherited from CMD.EXE or supplied by OS/2 for whatever purpose.
// For now, we just wire up stdio.
for (int i = 0; i <= 2; i++) {
HFiles[i].fd = i;
HFiles[i].type = 1; // !!! FIXME: could be a pipe or file.
HFiles[i].attr = DAW_STDIN | DAW_STDOUT | DAW_LEVEL1 | DAW_CHARACTER;
} // for
return 1;
} // initDoscalls
212
LX_NATIVE_MODULE_INIT({ if (!initDoscalls()) return NULL; })
213
214
215
216
LX_NATIVE_EXPORT16(DosSemRequest, 140),
LX_NATIVE_EXPORT16(DosSemClear, 141),
LX_NATIVE_EXPORT16(DosSemWait, 142),
LX_NATIVE_EXPORT16(DosSemSet, 143),
217
LX_NATIVE_EXPORT(DosSetMaxFH, 209),
218
LX_NATIVE_EXPORT(DosSetPathInfo, 219),
219
LX_NATIVE_EXPORT(DosQueryPathInfo, 223),
220
221
LX_NATIVE_EXPORT(DosQueryHType, 224),
LX_NATIVE_EXPORT(DosScanEnv, 227),
222
LX_NATIVE_EXPORT(DosSleep, 229),
223
LX_NATIVE_EXPORT(DosGetDateTime, 230),
224
LX_NATIVE_EXPORT(DosDevConfig, 231),
225
LX_NATIVE_EXPORT(DosExit, 234),
226
LX_NATIVE_EXPORT(DosResetBuffer, 254),
227
228
LX_NATIVE_EXPORT(DosSetFilePtr, 256),
LX_NATIVE_EXPORT(DosClose, 257),
229
LX_NATIVE_EXPORT(DosDelete, 259),
230
231
232
LX_NATIVE_EXPORT(DosFindClose, 263),
LX_NATIVE_EXPORT(DosFindFirst, 264),
LX_NATIVE_EXPORT(DosFindNext, 265),
233
LX_NATIVE_EXPORT(DosOpen, 273),
234
LX_NATIVE_EXPORT(DosQueryCurrentDir, 274),
235
LX_NATIVE_EXPORT(DosQueryCurrentDisk, 275),
236
LX_NATIVE_EXPORT(DosQueryFHState, 276),
237
LX_NATIVE_EXPORT(DosQueryFileInfo, 279),
238
LX_NATIVE_EXPORT(DosWaitChild, 280),
239
LX_NATIVE_EXPORT(DosRead, 281),
240
LX_NATIVE_EXPORT(DosWrite, 282),
241
LX_NATIVE_EXPORT(DosExecPgm, 283),
242
LX_NATIVE_EXPORT(DosQueryCp, 291),
243
244
LX_NATIVE_EXPORT(DosExitList, 296),
LX_NATIVE_EXPORT(DosAllocMem, 299),
245
LX_NATIVE_EXPORT(DosFreeMem, 304),
246
LX_NATIVE_EXPORT(DosSetMem, 305),
247
LX_NATIVE_EXPORT(DosCreateThread, 311),
248
LX_NATIVE_EXPORT(DosGetInfoBlocks, 312),
249
LX_NATIVE_EXPORT(DosLoadModule, 318),
250
LX_NATIVE_EXPORT(DosQueryModuleHandle, 319),
251
LX_NATIVE_EXPORT(DosQueryModuleName, 320),
252
LX_NATIVE_EXPORT(DosQueryProcAddr, 321),
253
LX_NATIVE_EXPORT(DosQueryAppType, 323),
254
LX_NATIVE_EXPORT(DosCreateEventSem, 324),
255
256
257
258
259
LX_NATIVE_EXPORT(DosCloseEventSem, 326),
LX_NATIVE_EXPORT(DosResetEventSem, 327),
LX_NATIVE_EXPORT(DosPostEventSem, 328),
LX_NATIVE_EXPORT(DosWaitEventSem, 329),
LX_NATIVE_EXPORT(DosQueryEventSem, 330),
260
LX_NATIVE_EXPORT(DosCreateMutexSem, 331),
261
262
LX_NATIVE_EXPORT(DosRequestMutexSem, 334),
LX_NATIVE_EXPORT(DosReleaseMutexSem, 335),
263
264
LX_NATIVE_EXPORT(DosSubSetMem, 344),
LX_NATIVE_EXPORT(DosSubAllocMem, 345),
265
LX_NATIVE_EXPORT(DosSubFreeMem, 346),
266
267
LX_NATIVE_EXPORT(DosQuerySysInfo, 348),
LX_NATIVE_EXPORT(DosSetExceptionHandler, 354),
268
LX_NATIVE_EXPORT(DosQuerySysState, 368),
269
LX_NATIVE_EXPORT(DosSetSignalExceptionFocus, 378),
270
271
LX_NATIVE_EXPORT(DosEnterMustComplete, 380),
LX_NATIVE_EXPORT(DosExitMustComplete, 381),
272
LX_NATIVE_EXPORT(DosSetRelMaxFH, 382),
273
LX_NATIVE_EXPORT(DosFlatToSel, 425),
274
LX_NATIVE_EXPORT(DosSelToFlat, 426),
275
276
LX_NATIVE_EXPORT(DosAllocThreadLocalMemory, 454),
LX_NATIVE_EXPORT(DosFreeThreadLocalMemory, 455),
277
278
279
280
LX_NATIVE_EXPORT(DosR3ExitAddr, 553),
LX_NATIVE_EXPORT(DosQueryHeaderInfo, 582),
LX_NATIVE_EXPORT(DosQueryExtLIBPATH, 874),
LX_NATIVE_EXPORT(DosQueryThreadContext, 877),
281
LX_NATIVE_EXPORT(DosOpenL, 981)
282
283
284
LX_NATIVE_MODULE_INIT_END()
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
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
325
APIRET DosGetInfoBlocks(PTIB *pptib, PPIB *pppib)
326
{
327
328
TRACE_NATIVE("DosGetInfoBlocks(%p, %p)", pptib, pppib);
329
if (pptib != NULL)
330
*pptib = getTib();
331
332
333
if (pppib != NULL)
*pppib = (PPIB) &GLoaderState->pib;
334
335
336
337
return 0;
} // DosGetInfoBlocks
338
APIRET DosQuerySysInfo(ULONG first, ULONG last, PVOID _buf, ULONG buflen)
339
{
340
341
TRACE_NATIVE("DosQuerySysInfo(%u, %u, %p, %u)", (uint) first, (uint) last, _buf, (uint) buflen);
342
uint32 *buf = (uint32 *) _buf;
343
344
if (last < first) return ERROR_INVALID_PARAMETER;
if ( (buflen / sizeof (uint32)) < ((last - first) + 1) ) return ERROR_BUFFER_OVERFLOW;
345
346
347
348
349
350
351
352
353
354
355
356
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;
357
// !!! FIXME: change the version number in some way so apps can know this isn't actually OS/2.
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
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
410
default: return ERROR_INVALID_PARAMETER;
411
412
413
} // switch
} // for
414
return NO_ERROR;
415
416
417
} // DosQuerySysInfo
418
APIRET DosQueryModuleName(HMODULE hmod, ULONG buflen, PCHAR buf)
419
{
420
421
TRACE_NATIVE("DosQueryModuleName(%u, %u, %p)", (uint) hmod, (uint) buflen, buf);
422
423
const LxModule *lxmod = (LxModule *) hmod;
// !!! FIXME: error 6 ERROR_INVALID_HANDLE
424
if (strlen(lxmod->os2path) >= buflen)
425
return ERROR_BAD_LENGTH;
426
strcpy(buf, lxmod->os2path);
427
return NO_ERROR;
428
429
} // DosQueryModuleName
430
431
APIRET DosScanEnv(PSZ name, PSZ *outval)
432
{
433
434
TRACE_NATIVE("DosScanEnv('%s', %p)", name, outval);
435
char *env = GLoaderState->pib.pib_pchenv;
436
437
438
439
const size_t len = strlen(name);
while (*env) {
if ((strncmp(env, name, len) == 0) && (env[len] == '=')) {
*outval = env + len + 1;
440
return NO_ERROR;
441
442
443
444
} // if
env += strlen(env) + 1;
} // while
445
return ERROR_ENVVAR_NOT_FOUND;
446
447
} // DosScanEnv
448
449
450
451
452
453
454
455
456
457
static int getHFileUnixDescriptor(HFILE h)
{
grabLock(&GMutexDosCalls);
const int fd = (h < MaxHFiles) ? HFiles[h].fd : -1;
ungrabLock(&GMutexDosCalls);
return fd;
} // getHFileUnixDescriptor
458
APIRET DosWrite(HFILE h, PVOID buf, ULONG buflen, PULONG actual)
459
{
460
TRACE_NATIVE("DosWrite(%u, %p, %u, %p)", (uint) h, buf, (uint) buflen, actual);
461
462
463
const int fd = getHFileUnixDescriptor(h);
if (fd == -1)
464
465
return ERROR_INVALID_HANDLE;
466
467
// !!! FIXME: writing to a terminal should probably convert CR/LF to LF.
const int rc = write(fd, buf, buflen);
468
469
if (rc < 0) {
*actual = 0;
470
return ERROR_DISK_FULL; // !!! FIXME: map these errors.
471
} // if
472
473
*actual = (uint32) rc;
474
return NO_ERROR;
475
476
477
478
479
480
481
482
} // 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
483
PFNEXITLIST fn = item->fn;
484
485
486
487
488
489
490
next = item->next;
GExitList = next;
free(item);
fn(why);
} // for
} // runDosExitList
491
VOID DosExit(ULONG action, ULONG exitcode)
492
{
493
494
TRACE_NATIVE("DosExit(%u, %u)", (uint) action, (uint) exitcode);
495
// !!! FIXME: what does a value other than 0 or 1 do here?
496
if (action == EXIT_THREAD) {
497
// !!! FIXME: terminate thread. If last thread: terminate process.
498
FIXME("DosExit(0) should terminate thread, not process");
499
500
501
} // if
// terminate the process.
502
runDosExitList(TC_EXIT);
503
504
GLoaderState->terminate(exitcode);
505
506
} // DosExit
507
APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn)
508
{
509
510
TRACE_NATIVE("DosExitList(%u, %p)", (uint) ordercode, fn);
511
if (fn == NULL)
512
return ERROR_INVALID_FUNCTION;
513
514
515
516
517
518
519
520
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.
if ((cmd != 1) && (arg != 0))
521
return ERROR_INVALID_DATA;
522
523
switch (cmd) {
524
case EXLST_ADD: {
525
526
ExitListItem *newitem = (ExitListItem *) malloc(sizeof (ExitListItem));
if (!newitem)
527
return ERROR_NOT_ENOUGH_MEMORY;
528
newitem->fn = fn;
529
530
531
532
533
534
535
536
537
538
539
540
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
541
return NO_ERROR;
542
543
} // case
544
case EXLST_REMOVE: {
545
546
547
548
549
550
551
for (item = GExitList; item; item = item->next) {
if (item->fn == fn) {
if (prev)
prev->next = item->next;
else
GExitList = item->next;
free(item);
552
return NO_ERROR;
553
554
555
} // if
prev = item;
} // for
556
return ERROR_INVALID_FUNCTION; // !!! FIXME: yeah?
557
558
} // case
559
560
case EXLST_EXIT:
return NO_ERROR; // ... just treat this as a no-op, I guess...?
561
562
default: return ERROR_INVALID_DATA;
563
564
} // switch
565
return NO_ERROR;
566
567
} // DosExitList
568
APIRET DosCreateEventSem(PSZ name, PHEV phev, ULONG attr, BOOL32 state)
569
{
570
TRACE_NATIVE("DosCreateEventSem('%s', %p, %u, %u)", name, phev, (uint) attr, (uint) state);
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
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;
601
return NO_ERROR;
602
603
} // DosCreateEventSem
604
APIRET DosCreateMutexSem(PSZ name, PHMTX phmtx, ULONG attr, BOOL32 state)
605
{
606
TRACE_NATIVE("DosCreateMutexSem('%s', %p, %u, %u)", name, phmtx, (uint) attr, (uint) state);
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
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;
641
return NO_ERROR;
642
643
644
} // DosCreateMutexSem
// !!! FIXME: this is obviously not correct.
645
APIRET DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
646
{
647
648
TRACE_NATIVE("DosSetExceptionHandler(%p)", rec);
return NO_ERROR;
649
650
} // DosSetExceptionHandler
651
ULONG _DosFlatToSel(PVOID ptr)
652
{
653
TRACE_NATIVE("DosFlatToSel(%p)", ptr);
654
return GLoaderState->convert32to1616(ptr);
655
656
657
658
659
660
661
662
663
664
665
666
667
} // _DosFlatToSel
// DosFlatToSel() passes its argument in %eax, so a little asm to bridge that...
__asm__ (
".globl DosFlatToSel \n\t"
".type DosFlatToSel, @function \n\t"
"DosFlatToSel: \n\t"
" pushl %eax \n\t"
" call _DosFlatToSel \n\t"
" addl $4, %esp \n\t"
" ret \n\t"
".size _DosFlatToSel, .-_DosFlatToSel \n\t"
);
668
669
APIRET DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes)
670
{
671
672
TRACE_NATIVE("DosSetSignalExceptionFocus(%u, %p)", (uint) flag, pulTimes);
673
674
if (flag == 0) {
if (GLoaderState->main_module->signal_exception_focus_count == 0)
675
return ERROR_ALREADY_RESET;
676
677
678
GLoaderState->main_module->signal_exception_focus_count--;
} else if (flag == 1) {
if (GLoaderState->main_module->signal_exception_focus_count == 0xFFFFFFFF)
679
return ERROR_NESTING_TOO_DEEP;
680
681
682
683
684
685
686
687
688
689
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?
690
return NO_ERROR;
691
692
} // DosSetSignalExceptionFocus
693
APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent)
694
{
695
TRACE_NATIVE("DosSetRelMaxFH(%p, %p)", pincr, pcurrent);
696
697
#if 0
698
699
700
701
702
703
struct rlimit rlim;
int rc = getrlimit(RLIMIT_NOFILE, &rlim);
assert(rc == 0); // this shouldn't fail...right?
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
704
705
if (pincr) {
rlim.rlim_cur = (rlim_t) (((LONG) rlim.rlim_cur) + *pincr);
706
707
708
709
710
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
} // if
} // if
711
712
713
714
715
716
717
#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) {
718
grabLock(&GMutexDosCalls);
719
720
721
722
723
724
725
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;
726
info->flags = 0;
727
728
729
} // for
MaxHFiles += incr;
} // if
730
ungrabLock(&GMutexDosCalls);
731
732
733
734
} // if
} // if
if (pcurrent != NULL) {
735
grabLock(&GMutexDosCalls);
736
*pcurrent = MaxHFiles;
737
ungrabLock(&GMutexDosCalls);
738
} // if
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
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.
*ppb = malloc(cb);
if (!*ppb)
return ERROR_NOT_ENOUGH_MEMORY;
return NO_ERROR;
} // DosAllocMem
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
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);
775
776
APIRET retval = NO_ERROR;
777
778
779
780
781
782
783
784
785
786
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);
787
788
return retval;
789
790
791
792
793
794
795
} // 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
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
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)
{
823
return GLoaderState->makeUnixPath(os2path, (uint32 *) err);
824
825
} // makeUnixPath
826
static APIRET doDosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, LONGLONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2)
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
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
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
{
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;
914
915
916
if (!grabLock(&GMutexDosCalls))
return ERROR_SYS_INTERNAL;
917
for (i = 0; i < MaxHFiles; i++, info++) {
918
919
if (info->fd == -1) { // available?
info->fd = -2;
920
break;
921
} // if
922
923
} // for
924
925
926
ungrabLock(&GMutexDosCalls);
if (i == MaxHFiles) {
927
return ERROR_TOO_MANY_OPEN_FILES;
928
}
929
930
931
932
933
934
935
936
937
938
const HFILE hf = i;
APIRET err = NO_ERROR;
char *unixpath = makeUnixPath(pszFileName, &err);
if (!unixpath) {
info->fd = -1;
return err;
} // if
939
//if (strcmp(pszFileName, unixpath) != 0) { fprintf(stderr, "DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); }
940
941
int fd = open(unixpath, flags, mode);
942
943
944
945
946
947
// 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;
948
if ((fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) {
949
existed = 1;
950
951
fd = open(unixpath, flags & ~O_EXCL, mode);
} else if (fd != -1) {
952
953
954
955
existed = 1;
} // else if
free(unixpath);
956
if (fd != -1) {
957
if (mustNotExist) {
958
close(fd);
959
960
961
962
963
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;
964
close(fd);
965
966
967
968
969
970
info->fd = -1;
errno = e; // let the switch below sort out the OS/2 error code.
} // if
} // else if
} // if
971
if (fd != -1) {
972
973
974
975
if (isReplacing)
FIXME("Replacing a file should delete all its EAs, too");
} // if
976
977
978
info->fd = fd;
if (fd == -1) {
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
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
999
1000
info->flags = fsOpenFlags;