/
doscalls.c
2859 lines (2344 loc) · 89.1 KB
1
2
3
#include "os2native.h"
#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
static pthread_mutex_t GMutexDosCalls;
42
43
typedef struct ExitListItem
{
44
PFNEXITLIST fn;
45
46
47
48
49
50
uint32 priority;
struct ExitListItem *next;
} ExitListItem;
static ExitListItem *GExitList = NULL;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// 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
{
78
79
80
81
int fd; // unix file descriptor.
ULONG type; // file, pipe, device, etc.
ULONG attr; // for character devices, DAW_*
ULONG flags; // OPEN_FLAGS_*
82
} HFileInfo;
83
84
85
static HFileInfo *HFiles = NULL;
static uint32 MaxHFiles = 0;
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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;
108
109
110
111
112
113
114
115
116
117
118
119
typedef struct
{
DIR *dirp;
ULONG attr;
ULONG level;
char pattern[CCHMAXPATHCOMP];
} DirFinder;
static DirFinder GHDir1;
120
121
122
LX_NATIVE_MODULE_DEINIT({
ExitListItem *next = GExitList;
GExitList = NULL;
123
124
125
126
127
for (ExitListItem *item = next; item; item = next) {
next = item->next;
free(item);
} // for
128
129
130
131
132
for (uint32 i = 0; i < MaxHFiles; i++) {
if (HFiles[i].fd != -1)
close(HFiles[i].fd);
} // for
133
134
free(HFiles);
HFiles = NULL;
135
MaxHFiles = 0;
136
137
pthread_mutex_destroy(&GMutexDosCalls);
138
139
})
140
static int initDoscalls(void)
141
{
142
143
144
145
146
if (pthread_mutex_init(&GMutexDosCalls, NULL) == -1) {
fprintf(stderr, "pthread_mutex_init failed!\n");
return 0;
} // if
147
148
149
150
151
152
153
154
155
156
157
158
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;
159
info->flags = 0;
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
} // 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
181
182
LX_NATIVE_MODULE_INIT({ if (!initDoscalls()) return NULL; })
LX_NATIVE_EXPORT(DosSetMaxFH, 209),
183
LX_NATIVE_EXPORT(DosSetPathInfo, 219),
184
LX_NATIVE_EXPORT(DosQueryPathInfo, 223),
185
186
LX_NATIVE_EXPORT(DosQueryHType, 224),
LX_NATIVE_EXPORT(DosScanEnv, 227),
187
LX_NATIVE_EXPORT(DosSleep, 229),
188
LX_NATIVE_EXPORT(DosGetDateTime, 230),
189
LX_NATIVE_EXPORT(DosDevConfig, 231),
190
LX_NATIVE_EXPORT(DosExit, 234),
191
LX_NATIVE_EXPORT(DosResetBuffer, 254),
192
193
LX_NATIVE_EXPORT(DosSetFilePtr, 256),
LX_NATIVE_EXPORT(DosClose, 257),
194
LX_NATIVE_EXPORT(DosDelete, 259),
195
196
197
LX_NATIVE_EXPORT(DosFindClose, 263),
LX_NATIVE_EXPORT(DosFindFirst, 264),
LX_NATIVE_EXPORT(DosFindNext, 265),
198
LX_NATIVE_EXPORT(DosOpen, 273),
199
LX_NATIVE_EXPORT(DosQueryCurrentDir, 274),
200
LX_NATIVE_EXPORT(DosQueryCurrentDisk, 275),
201
LX_NATIVE_EXPORT(DosQueryFHState, 276),
202
LX_NATIVE_EXPORT(DosQueryFileInfo, 279),
203
LX_NATIVE_EXPORT(DosWaitChild, 280),
204
LX_NATIVE_EXPORT(DosRead, 281),
205
LX_NATIVE_EXPORT(DosWrite, 282),
206
LX_NATIVE_EXPORT(DosExecPgm, 283),
207
LX_NATIVE_EXPORT(DosQueryCp, 291),
208
209
LX_NATIVE_EXPORT(DosExitList, 296),
LX_NATIVE_EXPORT(DosAllocMem, 299),
210
LX_NATIVE_EXPORT(DosFreeMem, 304),
211
LX_NATIVE_EXPORT(DosSetMem, 305),
212
LX_NATIVE_EXPORT(DosCreateThread, 311),
213
LX_NATIVE_EXPORT(DosGetInfoBlocks, 312),
214
LX_NATIVE_EXPORT(DosLoadModule, 318),
215
LX_NATIVE_EXPORT(DosQueryModuleHandle, 319),
216
LX_NATIVE_EXPORT(DosQueryModuleName, 320),
217
LX_NATIVE_EXPORT(DosQueryProcAddr, 321),
218
LX_NATIVE_EXPORT(DosQueryAppType, 323),
219
LX_NATIVE_EXPORT(DosCreateEventSem, 324),
220
221
222
223
224
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),
225
LX_NATIVE_EXPORT(DosCreateMutexSem, 331),
226
227
LX_NATIVE_EXPORT(DosRequestMutexSem, 334),
LX_NATIVE_EXPORT(DosReleaseMutexSem, 335),
228
229
LX_NATIVE_EXPORT(DosSubSetMem, 344),
LX_NATIVE_EXPORT(DosSubAllocMem, 345),
230
LX_NATIVE_EXPORT(DosSubFreeMem, 346),
231
232
LX_NATIVE_EXPORT(DosQuerySysInfo, 348),
LX_NATIVE_EXPORT(DosSetExceptionHandler, 354),
233
LX_NATIVE_EXPORT(DosQuerySysState, 368),
234
LX_NATIVE_EXPORT(DosSetSignalExceptionFocus, 378),
235
236
LX_NATIVE_EXPORT(DosEnterMustComplete, 380),
LX_NATIVE_EXPORT(DosExitMustComplete, 381),
237
LX_NATIVE_EXPORT(DosSetRelMaxFH, 382),
238
LX_NATIVE_EXPORT(DosFlatToSel, 425),
239
240
LX_NATIVE_EXPORT(DosAllocThreadLocalMemory, 454),
LX_NATIVE_EXPORT(DosFreeThreadLocalMemory, 455),
241
242
243
244
LX_NATIVE_EXPORT(DosR3ExitAddr, 553),
LX_NATIVE_EXPORT(DosQueryHeaderInfo, 582),
LX_NATIVE_EXPORT(DosQueryExtLIBPATH, 874),
LX_NATIVE_EXPORT(DosQueryThreadContext, 877),
245
LX_NATIVE_EXPORT(DosOpenL, 981)
246
247
248
LX_NATIVE_MODULE_INIT_END()
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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
289
APIRET DosGetInfoBlocks(PTIB *pptib, PPIB *pppib)
290
{
291
292
TRACE_NATIVE("DosGetInfoBlocks(%p, %p)", pptib, pppib);
293
if (pptib != NULL)
294
*pptib = getTib();
295
296
297
if (pppib != NULL)
*pppib = (PPIB) &GLoaderState->pib;
298
299
300
301
return 0;
} // DosGetInfoBlocks
302
APIRET DosQuerySysInfo(ULONG first, ULONG last, PVOID _buf, ULONG buflen)
303
{
304
305
TRACE_NATIVE("DosQuerySysInfo(%u, %u, %p, %u)", (uint) first, (uint) last, _buf, (uint) buflen);
306
uint32 *buf = (uint32 *) _buf;
307
308
if (last < first) return ERROR_INVALID_PARAMETER;
if ( (buflen / sizeof (uint32)) < ((last - first) + 1) ) return ERROR_BUFFER_OVERFLOW;
309
310
311
312
313
314
315
316
317
318
319
320
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;
321
// !!! FIXME: change the version number in some way so apps can know this isn't actually OS/2.
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
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
374
default: return ERROR_INVALID_PARAMETER;
375
376
377
} // switch
} // for
378
return NO_ERROR;
379
380
381
} // DosQuerySysInfo
382
APIRET DosQueryModuleName(HMODULE hmod, ULONG buflen, PCHAR buf)
383
{
384
385
TRACE_NATIVE("DosQueryModuleName(%u, %u, %p)", (uint) hmod, (uint) buflen, buf);
386
387
const LxModule *lxmod = (LxModule *) hmod;
// !!! FIXME: error 6 ERROR_INVALID_HANDLE
388
if (strlen(lxmod->os2path) >= buflen)
389
return ERROR_BAD_LENGTH;
390
strcpy(buf, lxmod->os2path);
391
return NO_ERROR;
392
393
} // DosQueryModuleName
394
395
APIRET DosScanEnv(PSZ name, PSZ *outval)
396
{
397
398
TRACE_NATIVE("DosScanEnv('%s', %p)", name, outval);
399
char *env = GLoaderState->pib.pib_pchenv;
400
401
402
403
const size_t len = strlen(name);
while (*env) {
if ((strncmp(env, name, len) == 0) && (env[len] == '=')) {
*outval = env + len + 1;
404
return NO_ERROR;
405
406
407
408
} // if
env += strlen(env) + 1;
} // while
409
return ERROR_ENVVAR_NOT_FOUND;
410
411
} // DosScanEnv
412
413
414
415
416
417
418
419
420
421
static int getHFileUnixDescriptor(HFILE h)
{
grabLock(&GMutexDosCalls);
const int fd = (h < MaxHFiles) ? HFiles[h].fd : -1;
ungrabLock(&GMutexDosCalls);
return fd;
} // getHFileUnixDescriptor
422
APIRET DosWrite(HFILE h, PVOID buf, ULONG buflen, PULONG actual)
423
{
424
TRACE_NATIVE("DosWrite(%u, %p, %u, %p)", (uint) h, buf, (uint) buflen, actual);
425
426
427
const int fd = getHFileUnixDescriptor(h);
if (fd == -1)
428
429
return ERROR_INVALID_HANDLE;
430
431
// !!! FIXME: writing to a terminal should probably convert CR/LF to LF.
const int rc = write(fd, buf, buflen);
432
433
if (rc < 0) {
*actual = 0;
434
return ERROR_DISK_FULL; // !!! FIXME: map these errors.
435
} // if
436
437
*actual = (uint32) rc;
438
return NO_ERROR;
439
440
441
442
443
444
445
446
} // 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
447
PFNEXITLIST fn = item->fn;
448
449
450
451
452
453
454
next = item->next;
GExitList = next;
free(item);
fn(why);
} // for
} // runDosExitList
455
VOID DosExit(ULONG action, ULONG exitcode)
456
{
457
458
TRACE_NATIVE("DosExit(%u, %u)", (uint) action, (uint) exitcode);
459
// !!! FIXME: what does a value other than 0 or 1 do here?
460
if (action == EXIT_THREAD) {
461
// !!! FIXME: terminate thread. If last thread: terminate process.
462
FIXME("DosExit(0) should terminate thread, not process");
463
464
465
} // if
// terminate the process.
466
runDosExitList(TC_EXIT);
467
468
469
470
471
472
// !!! FIXME: finalize OS/2 DLLs before killing the process?
// OS/2's docs say this only keeps the lower 16 bits of exitcode.
// !!! FIXME: ...but Unix only keeps the lowest 8 bits. Will have to
// !!! FIXME: tapdance to pass larger values back to OS/2 parent processes.
473
474
475
476
if (exitcode > 255)
FIXME("deal with process exit codes > 255. We clamped this one!");
_exit((int) (exitcode & 0xFF));
477
478
} // DosExit
479
APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn)
480
{
481
482
TRACE_NATIVE("DosExitList(%u, %p)", (uint) ordercode, fn);
483
if (fn == NULL)
484
return ERROR_INVALID_FUNCTION;
485
486
487
488
489
490
491
492
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))
493
return ERROR_INVALID_DATA;
494
495
switch (cmd) {
496
case EXLST_ADD: {
497
498
ExitListItem *newitem = (ExitListItem *) malloc(sizeof (ExitListItem));
if (!newitem)
499
return ERROR_NOT_ENOUGH_MEMORY;
500
newitem->fn = fn;
501
502
503
504
505
506
507
508
509
510
511
512
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
513
return NO_ERROR;
514
515
} // case
516
case EXLST_REMOVE: {
517
518
519
520
521
522
523
for (item = GExitList; item; item = item->next) {
if (item->fn == fn) {
if (prev)
prev->next = item->next;
else
GExitList = item->next;
free(item);
524
return NO_ERROR;
525
526
527
} // if
prev = item;
} // for
528
return ERROR_INVALID_FUNCTION; // !!! FIXME: yeah?
529
530
} // case
531
532
case EXLST_EXIT:
return NO_ERROR; // ... just treat this as a no-op, I guess...?
533
534
default: return ERROR_INVALID_DATA;
535
536
} // switch
537
return NO_ERROR;
538
539
} // DosExitList
540
APIRET DosCreateEventSem(PSZ name, PHEV phev, ULONG attr, BOOL32 state)
541
{
542
TRACE_NATIVE("DosCreateEventSem('%s', %p, %u, %u)", name, phev, (uint) attr, (uint) state);
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
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;
573
return NO_ERROR;
574
575
} // DosCreateEventSem
576
APIRET DosCreateMutexSem(PSZ name, PHMTX phmtx, ULONG attr, BOOL32 state)
577
{
578
TRACE_NATIVE("DosCreateMutexSem('%s', %p, %u, %u)", name, phmtx, (uint) attr, (uint) state);
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
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;
613
return NO_ERROR;
614
615
616
} // DosCreateMutexSem
// !!! FIXME: this is obviously not correct.
617
APIRET DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
618
{
619
620
TRACE_NATIVE("DosSetExceptionHandler(%p)", rec);
return NO_ERROR;
621
622
} // DosSetExceptionHandler
623
ULONG _DosFlatToSel(PVOID ptr)
624
{
625
TRACE_NATIVE("DosFlatToSel(%p)", ptr);
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
uint16 selector = 0;
uint16 offset = 0;
if (!GLoaderState->findSelector((uint32) ptr, &selector, &offset)) {
fprintf(stderr, "Uhoh, ran out of LDT entries?!\n");
return 0; // oh well, crash.
} // if
selector = (selector << 3) | 7;
return (((uint32)selector) << 16) | ((uint32) offset);
} // _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"
);
649
650
APIRET DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes)
651
{
652
653
TRACE_NATIVE("DosSetSignalExceptionFocus(%u, %p)", (uint) flag, pulTimes);
654
655
if (flag == 0) {
if (GLoaderState->main_module->signal_exception_focus_count == 0)
656
return ERROR_ALREADY_RESET;
657
658
659
GLoaderState->main_module->signal_exception_focus_count--;
} else if (flag == 1) {
if (GLoaderState->main_module->signal_exception_focus_count == 0xFFFFFFFF)
660
return ERROR_NESTING_TOO_DEEP;
661
662
663
664
665
666
667
668
669
670
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?
671
return NO_ERROR;
672
673
} // DosSetSignalExceptionFocus
674
APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent)
675
{
676
TRACE_NATIVE("DosSetRelMaxFH(%p, %p)", pincr, pcurrent);
677
678
#if 0
679
680
681
682
683
684
struct rlimit rlim;
int rc = getrlimit(RLIMIT_NOFILE, &rlim);
assert(rc == 0); // this shouldn't fail...right?
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
685
686
if (pincr) {
rlim.rlim_cur = (rlim_t) (((LONG) rlim.rlim_cur) + *pincr);
687
688
689
690
691
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
} // if
} // if
692
693
694
695
696
697
698
#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) {
699
grabLock(&GMutexDosCalls);
700
701
702
703
704
705
706
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;
707
info->flags = 0;
708
709
710
} // for
MaxHFiles += incr;
} // if
711
ungrabLock(&GMutexDosCalls);
712
713
714
715
} // if
} // if
if (pcurrent != NULL) {
716
grabLock(&GMutexDosCalls);
717
*pcurrent = MaxHFiles;
718
ungrabLock(&GMutexDosCalls);
719
} // if
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
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
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
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);
756
757
APIRET retval = NO_ERROR;
758
759
760
761
762
763
764
765
766
767
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);
768
769
return retval;
770
771
772
773
774
775
776
} // 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
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
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)
{
804
return GLoaderState->makeUnixPath(os2path, (uint32 *) err);
805
806
} // makeUnixPath
807
static APIRET doDosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, LONGLONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2)
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
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
{
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;
895
896
897
if (!grabLock(&GMutexDosCalls))
return ERROR_SYS_INTERNAL;
898
for (i = 0; i < MaxHFiles; i++, info++) {
899
900
if (info->fd == -1) { // available?
info->fd = -2;
901
break;
902
} // if
903
904
} // for
905
906
907
ungrabLock(&GMutexDosCalls);
if (i == MaxHFiles) {
908
return ERROR_TOO_MANY_OPEN_FILES;
909
}
910
911
912
913
914
915
916
917
918
919
const HFILE hf = i;
APIRET err = NO_ERROR;
char *unixpath = makeUnixPath(pszFileName, &err);
if (!unixpath) {
info->fd = -1;
return err;
} // if
920
//if (strcmp(pszFileName, unixpath) != 0) { fprintf(stderr, "DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); }
921
922
int fd = open(unixpath, flags, mode);
923
924
925
926
927
928
// 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;
929
if ((fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) {
930
existed = 1;
931
932
fd = open(unixpath, flags & ~O_EXCL, mode);
} else if (fd != -1) {
933
934
935
936
existed = 1;
} // else if
free(unixpath);
937
if (fd != -1) {
938
if (mustNotExist) {
939
close(fd);
940
941
942
943
944
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;
945
close(fd);
946
947
948
949
950
951
info->fd = -1;
errno = e; // let the switch below sort out the OS/2 error code.
} // if
} // else if
} // if
952
if (fd != -1) {
953
954
955
956
if (isReplacing)
FIXME("Replacing a file should delete all its EAs, too");
} // if
957
958
959
info->fd = fd;
if (fd == -1) {
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
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
980
981
info->flags = fsOpenFlags;
982
983
984
985
986
987
988
989
990
if (isReplacing)
*pulAction = FILE_TRUNCATED;
else if (existed)
*pulAction = FILE_EXISTED;
else
*pulAction = FILE_CREATED;
*pHf = hf;
return NO_ERROR;
991
992
993
994
995
996
} // 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);
997
998
} // DosOpen
999
1000
APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
{