/
doscalls.c
3738 lines (3114 loc) · 119 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
static pthread_mutex_t GMutexDosCalls;
28
static pthread_mutex_t GMutexDosBeep;
29
30
31
typedef struct ExitListItem
{
32
PFNEXITLIST fn;
33
34
35
36
37
38
uint32 priority;
struct ExitListItem *next;
} ExitListItem;
static ExitListItem *GExitList = NULL;
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
65
// 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
{
66
67
68
69
int fd; // unix file descriptor.
ULONG type; // file, pipe, device, etc.
ULONG attr; // for character devices, DAW_*
ULONG flags; // OPEN_FLAGS_*
70
} HFileInfo;
71
72
73
static HFileInfo *HFiles = NULL;
static uint32 MaxHFiles = 0;
74
75
76
77
78
79
80
typedef struct Thread
{
pthread_t thread;
size_t stacklen;
PFNTHREAD fn;
ULONG fnarg;
81
uint16 selector;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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;
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
static GINFOSEG *ginfo;
static SEL ginfosel;
static LINFOSEG *linfo;
static SEL linfosel;
static HDIR *hdir16 = 0;
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
static void initHFileInfoFromUnixFd(const int fd, HFileInfo *info)
{
struct stat statbuf;
if ((fd != -1) && (fstat(fd, &statbuf) == 0)) {
ULONG type = 0;
switch (statbuf.st_mode & S_IFMT) {
case S_IFREG:
type = 0;
break;
case S_IFBLK:
case S_IFCHR:
type = 1;
break;
case S_IFIFO:
case S_IFSOCK:
default:
type = 2;
break;
} // switch
info->fd = fd;
info->type = type;
FIXME("What should this be?");
info->attr = DAW_STDIN | DAW_STDOUT | DAW_LEVEL1 | DAW_CHARACTER;
info->flags = 0;
} else {
info->fd = -1;
info->type = 0;
info->attr = 0;
info->flags = 0;
} // else
} // initHFileInfoFromUnixFd
170
APIRET DosGetInfoBlocks(PTIB *pptib, PPIB *pppib)
171
{
172
173
TRACE_NATIVE("DosGetInfoBlocks(%p, %p)", pptib, pppib);
174
if (pptib != NULL)
175
*pptib = (PTIB) LX_GETTIB();
176
177
if (pppib != NULL)
178
*pppib = (PPIB) &GLoaderState.pib;
179
180
181
182
return 0;
} // DosGetInfoBlocks
183
APIRET DosQuerySysInfo(ULONG first, ULONG last, PVOID _buf, ULONG buflen)
184
{
185
186
TRACE_NATIVE("DosQuerySysInfo(%u, %u, %p, %u)", (uint) first, (uint) last, _buf, (uint) buflen);
187
uint32 *buf = (uint32 *) _buf;
188
189
if (last < first) return ERROR_INVALID_PARAMETER;
if ( (buflen / sizeof (uint32)) < ((last - first) + 1) ) return ERROR_BUFFER_OVERFLOW;
190
191
192
193
194
195
196
197
198
199
200
201
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;
202
// !!! FIXME: change the version number in some way so apps can know this isn't actually OS/2.
203
204
205
case QSV_VERSION_MAJOR: *(buf++) = 20; break; // OS/2 Warp 4.5
case QSV_VERSION_MINOR: *(buf++) = 45; break; // OS/2 Warp 4.5
case QSV_VERSION_REVISION: *(buf++) = 0; break; // OS/2 Warp 4.5
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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
255
default: return ERROR_INVALID_PARAMETER;
256
257
258
} // switch
} // for
259
return NO_ERROR;
260
261
262
} // DosQuerySysInfo
263
APIRET DosQueryModuleName(HMODULE hmod, ULONG buflen, PCHAR buf)
264
{
265
266
TRACE_NATIVE("DosQueryModuleName(%u, %u, %p)", (uint) hmod, (uint) buflen, buf);
267
268
const LxModule *lxmod = (LxModule *) hmod;
// !!! FIXME: error 6 ERROR_INVALID_HANDLE
269
if (strlen(lxmod->os2path) >= buflen)
270
return ERROR_BAD_LENGTH;
271
strcpy(buf, lxmod->os2path);
272
return NO_ERROR;
273
274
} // DosQueryModuleName
275
276
APIRET DosScanEnv(PSZ name, PSZ *outval)
277
{
278
279
TRACE_NATIVE("DosScanEnv('%s', %p)", name, outval);
280
char *env = GLoaderState.pib.pib_pchenv;
281
282
283
284
const size_t len = strlen(name);
while (*env) {
if ((strncmp(env, name, len) == 0) && (env[len] == '=')) {
*outval = env + len + 1;
285
return NO_ERROR;
286
287
288
289
} // if
env += strlen(env) + 1;
} // while
290
return ERROR_ENVVAR_NOT_FOUND;
291
292
} // DosScanEnv
293
294
295
296
297
298
299
300
301
static int getHFileUnixDescriptor(HFILE h)
{
grabLock(&GMutexDosCalls);
const int fd = (h < MaxHFiles) ? HFiles[h].fd : -1;
ungrabLock(&GMutexDosCalls);
return fd;
} // getHFileUnixDescriptor
302
static APIRET DosWrite_implementation(HFILE h, PVOID buf, ULONG buflen, PULONG actual)
303
{
304
305
const int fd = getHFileUnixDescriptor(h);
if (fd == -1)
306
307
return ERROR_INVALID_HANDLE;
308
309
// !!! FIXME: writing to a terminal should probably convert CR/LF to LF.
const int rc = write(fd, buf, buflen);
310
311
if (rc < 0) {
*actual = 0;
312
return ERROR_DISK_FULL; // !!! FIXME: map these errors.
313
} // if
314
315
*actual = (uint32) rc;
316
return NO_ERROR;
317
318
319
320
321
322
} // DosWrite_implementation
APIRET DosWrite(HFILE h, PVOID buf, ULONG buflen, PULONG actual)
{
TRACE_NATIVE("DosWrite(%u, %p, %u, %p)", (uint) h, buf, (uint) buflen, actual);
return DosWrite_implementation(h, buf, buflen, actual);
323
324
325
326
327
328
329
330
} // 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
331
PFNEXITLIST fn = item->fn;
332
333
334
next = item->next;
GExitList = next;
free(item);
335
//printf("calling dosexitlist item %p\n", fn); fflush(stdout);
336
fn(why);
337
//printf("dosexitlist item %p returned!\n", fn); fflush(stdout);
338
339
340
} // for
} // runDosExitList
341
static VOID DosExit_implementation(ULONG action, ULONG exitcode)
342
343
{
// !!! FIXME: what does a value other than 0 or 1 do here?
344
if (action == EXIT_THREAD) {
345
346
347
// !!! 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.
348
349
350
} // if
// terminate the process.
351
runDosExitList(TC_EXIT);
352
353
GLoaderState.terminate(exitcode);
354
355
356
357
358
359
} // DosExit_implementation
VOID DosExit(ULONG action, ULONG exitcode)
{
TRACE_NATIVE("DosExit(%u, %u)", (uint) action, (uint) exitcode);
DosExit_implementation(action, exitcode);
360
361
} // DosExit
362
APIRET DosExitList(ULONG ordercode, PFNEXITLIST fn)
363
{
364
365
TRACE_NATIVE("DosExitList(%u, %p)", (uint) ordercode, fn);
366
if (fn == NULL)
367
return ERROR_INVALID_FUNCTION;
368
369
370
371
372
373
374
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.
375
if ((cmd != EXLST_ADD) && (arg != 0))
376
return ERROR_INVALID_DATA;
377
378
switch (cmd) {
379
case EXLST_ADD: {
380
381
ExitListItem *newitem = (ExitListItem *) malloc(sizeof (ExitListItem));
if (!newitem)
382
return ERROR_NOT_ENOUGH_MEMORY;
383
newitem->fn = fn;
384
385
386
387
388
389
390
391
392
393
394
395
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
396
return NO_ERROR;
397
398
} // case
399
case EXLST_REMOVE: {
400
401
402
403
404
405
406
for (item = GExitList; item; item = item->next) {
if (item->fn == fn) {
if (prev)
prev->next = item->next;
else
GExitList = item->next;
free(item);
407
return NO_ERROR;
408
409
410
} // if
prev = item;
} // for
411
return ERROR_INVALID_FUNCTION; // !!! FIXME: yeah?
412
413
} // case
414
415
case EXLST_EXIT:
return NO_ERROR; // ... just treat this as a no-op, I guess...?
416
417
default: return ERROR_INVALID_DATA;
418
419
} // switch
420
return NO_ERROR;
421
422
} // DosExitList
423
APIRET DosCreateEventSem(PSZ name, PHEV phev, ULONG attr, BOOL32 state)
424
{
425
TRACE_NATIVE("DosCreateEventSem('%s', %p, %u, %u)", name, phev, (uint) attr, (uint) state);
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
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;
456
return NO_ERROR;
457
458
} // DosCreateEventSem
459
APIRET DosCreateMutexSem(PSZ name, PHMTX phmtx, ULONG attr, BOOL32 state)
460
{
461
TRACE_NATIVE("DosCreateMutexSem('%s', %p, %u, %u)", name, phmtx, (uint) attr, (uint) state);
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
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;
496
return NO_ERROR;
497
498
499
} // DosCreateMutexSem
// !!! FIXME: this is obviously not correct.
500
APIRET DosSetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
501
{
502
503
TRACE_NATIVE("DosSetExceptionHandler(%p)", rec);
return NO_ERROR;
504
505
} // DosSetExceptionHandler
506
507
508
509
510
511
512
// !!! FIXME: this is obviously not correct.
APIRET DosUnsetExceptionHandler(PEXCEPTIONREGISTRATIONRECORD rec)
{
TRACE_NATIVE("DosUnsetExceptionHandler(%p)", rec);
return NO_ERROR;
} // DosSetExceptionHandler
513
514
515
#if !LX_LEGACY
ULONG DosFlatToSel(VOID) { return 0; }
#else
516
ULONG _DosFlatToSel(PVOID ptr)
517
{
518
TRACE_NATIVE("DosFlatToSel(%p)", ptr);
519
return GLoaderState.convert32to1616(ptr);
520
521
522
} // _DosFlatToSel
// DosFlatToSel() passes its argument in %eax, so a little asm to bridge that...
523
524
525
// 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.
526
527
528
529
__asm__ (
".globl DosFlatToSel \n\t"
".type DosFlatToSel, @function \n\t"
"DosFlatToSel: \n\t"
530
531
532
533
534
535
" 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.
536
" call _DosFlatToSel \n\t"
537
538
539
540
541
542
" 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"
543
544
545
" ret \n\t"
".size _DosFlatToSel, .-_DosFlatToSel \n\t"
);
546
#endif
547
548
APIRET DosSetSignalExceptionFocus(BOOL32 flag, PULONG pulTimes)
549
{
550
551
TRACE_NATIVE("DosSetSignalExceptionFocus(%u, %p)", (uint) flag, pulTimes);
552
if (flag == 0) {
553
if (GLoaderState.main_module->signal_exception_focus_count == 0)
554
return ERROR_ALREADY_RESET;
555
GLoaderState.main_module->signal_exception_focus_count--;
556
} else if (flag == 1) {
557
if (GLoaderState.main_module->signal_exception_focus_count == 0xFFFFFFFF)
558
return ERROR_NESTING_TOO_DEEP;
559
GLoaderState.main_module->signal_exception_focus_count++;
560
561
562
563
564
} else {
// !!! FIXME: does OS/2 do something if flag != 0 or 1?
} // else
if (pulTimes)
565
*pulTimes = GLoaderState.main_module->signal_exception_focus_count;
566
567
568
// !!! FIXME: I guess enable/disable SIGINT handler here?
569
return NO_ERROR;
570
571
} // DosSetSignalExceptionFocus
572
APIRET DosSetRelMaxFH(PLONG pincr, PULONG pcurrent)
573
{
574
TRACE_NATIVE("DosSetRelMaxFH(%p, %p)", pincr, pcurrent);
575
576
#if 0
577
578
579
580
581
582
struct rlimit rlim;
int rc = getrlimit(RLIMIT_NOFILE, &rlim);
assert(rc == 0); // this shouldn't fail...right?
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
583
584
if (pincr) {
rlim.rlim_cur = (rlim_t) (((LONG) rlim.rlim_cur) + *pincr);
585
586
587
588
589
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (pcurrent)
*pcurrent = (ULONG) rlim.rlim_cur;
} // if
} // if
590
591
592
593
594
595
596
#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) {
597
grabLock(&GMutexDosCalls);
598
599
600
HFileInfo *info = (HFileInfo *) realloc(HFiles, sizeof (HFileInfo) * (MaxHFiles + incr));
if (info != NULL) {
HFiles = info;
601
FIXME("this needs to initialize the end of the array, not the front");
602
603
604
605
for (LONG i = 0; i < incr; i++, info++) {
info->fd = -1;
info->type = 0;
info->attr = 0;
606
info->flags = 0;
607
608
609
} // for
MaxHFiles += incr;
} // if
610
ungrabLock(&GMutexDosCalls);
611
612
613
614
} // if
} // if
if (pcurrent != NULL) {
615
grabLock(&GMutexDosCalls);
616
*pcurrent = MaxHFiles;
617
ungrabLock(&GMutexDosCalls);
618
} // if
619
620
621
622
623
624
625
626
627
628
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.
629
*ppb = calloc(1, cb);
630
631
632
633
634
if (!*ppb)
return ERROR_NOT_ENOUGH_MEMORY;
return NO_ERROR;
} // DosAllocMem
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
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
652
static APIRET DosQueryHType_implementation(HFILE hFile, PULONG pType, PULONG pAttr)
653
{
654
APIRET retval = NO_ERROR;
655
656
657
658
659
660
661
662
663
664
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);
665
666
return retval;
667
668
669
670
671
672
} // DosQueryHType_implementation
APIRET DosQueryHType(HFILE hFile, PULONG pType, PULONG pAttr)
{
TRACE_NATIVE("DosQueryHType(%u, %p, %p)", (uint) hFile, pType, pAttr);
return DosQueryHType_implementation(hFile, pType, pAttr);
673
674
675
676
677
678
679
} // 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
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
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)
{
707
return GLoaderState.makeUnixPath(os2path, (uint32 *) err);
708
709
} // makeUnixPath
710
static APIRET doDosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, LONGLONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2)
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
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
{
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;
798
799
800
if (!grabLock(&GMutexDosCalls))
return ERROR_SYS_INTERNAL;
801
for (i = 0; i < MaxHFiles; i++, info++) {
802
803
if (info->fd == -1) { // available?
info->fd = -2;
804
break;
805
} // if
806
807
} // for
808
809
810
ungrabLock(&GMutexDosCalls);
if (i == MaxHFiles) {
811
return ERROR_TOO_MANY_OPEN_FILES;
812
}
813
814
815
816
817
818
819
820
821
822
const HFILE hf = i;
APIRET err = NO_ERROR;
char *unixpath = makeUnixPath(pszFileName, &err);
if (!unixpath) {
info->fd = -1;
return err;
} // if
823
//if (strcmp(pszFileName, unixpath) != 0) { fprintf(stderr, "DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); }
824
825
int fd = open(unixpath, flags, mode);
826
827
828
829
830
831
// 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;
832
if ((fd == -1) && (flags & (O_CREAT|O_EXCL)) && (errno == EEXIST) && !isExclusive) {
833
existed = 1;
834
835
fd = open(unixpath, flags & ~O_EXCL, mode);
} else if (fd != -1) {
836
837
838
839
existed = 1;
} // else if
free(unixpath);
840
if (fd != -1) {
841
if (mustNotExist) {
842
close(fd);
843
844
845
846
847
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;
848
close(fd);
849
850
851
852
853
854
info->fd = -1;
errno = e; // let the switch below sort out the OS/2 error code.
} // if
} // else if
} // if
855
if (fd != -1) {
856
857
858
859
if (isReplacing)
FIXME("Replacing a file should delete all its EAs, too");
} // if
860
initHFileInfoFromUnixFd(fd, info);
861
862
if (fd == -1) {
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
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
883
884
info->flags = fsOpenFlags;
885
886
887
888
889
890
891
892
893
if (isReplacing)
*pulAction = FILE_TRUNCATED;
else if (existed)
*pulAction = FILE_EXISTED;
else
*pulAction = FILE_CREATED;
*pHf = hf;
return NO_ERROR;
894
895
896
897
898
899
} // 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);
900
901
} // DosOpen
902
903
904
APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout)
{
TRACE_NATIVE("DosRequestMutexSem(%u, %u)", (uint) hmtx, (uint) ulTimeout);
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
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.
931
932
933
934
935
} // DosRequestMutexSem
APIRET DosReleaseMutexSem(HMTX hmtx)
{
TRACE_NATIVE("DosReleaseMutexSem(%u)", (uint) hmtx);
936
937
938
939
940
941
942
943
944
945
946
947
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;
948
949
} // DosReleaseMutexSem
950
static APIRET DosSetFilePtr_implementation(HFILE hFile, LONG ib, ULONG method, PULONG ibActual)
951
952
953
954
955
956
957
958
959
{
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
960
961
const int fd = getHFileUnixDescriptor(hFile);
if (fd == -1)
962
963
return ERROR_INVALID_HANDLE;
964
const off_t pos = lseek(fd, (off_t) ib, whence);
965
966
967
968
969
970
971
972
973
974
if (pos == -1) {
if (errno == EINVAL)
return ERROR_NEGATIVE_SEEK;
return ERROR_INVALID_FUNCTION; // !!! FIXME: ?
} // if
if (ibActual)
*ibActual = (ULONG) pos;
return NO_ERROR;
975
} // DosSetFilePtr_implementation
976
977
APIRET DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual)
978
{
979
980
981
TRACE_NATIVE("DosSetFilePtr(%u, %d, %u, %p)", (uint) hFile, (int) ib, (uint) method, ibActual);
return DosSetFilePtr_implementation(hFile, ib, method, ibActual);
} // DosSetFilePtr
982
983
984
static APIRET DosRead_implementation(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual)
{
985
986
const int fd = getHFileUnixDescriptor(hFile);
if (fd == -1)
987
988
return ERROR_INVALID_HANDLE;
989
const ssize_t br = read(fd, pBuffer, cbRead);
990
991
992
993
994
995
996
if (br == -1)
return ERROR_INVALID_FUNCTION; // !!! FIXME: ?
if (pcbActual)
*pcbActual = (ULONG) br;
return NO_ERROR;
997
} // DosRead_implementation
998
999
APIRET DosRead(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual)
1000
{