source: vendor/emx/current/src/os2/fileio.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 54.4 KB
Line 
1/* fileio.c -- Managing files, pipes, and such
2 Copyright (c) 1994-1998 by Eberhard Mattes
3
4This file is part of emx.
5
6emx is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emx is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emx; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
20
21As special exception, emx.dll can be distributed without source code
22unless it has been changed. If you modify emx.dll, this exception
23no longer applies and you must remove this paragraph from all source
24files for emx.dll. */
25
26
27#define INCL_DOSDEVIOCTL
28#define INCL_DOSSEMAPHORES
29#define INCL_DOSERRORS
30#include <os2emx.h>
31#include <emx/syscalls.h>
32#include <sys/fcntl.h>
33#include <sys/ioctl.h>
34#include <sys/nls.h>
35#include <sys/termio.h>
36#include <sys/errno.h>
37#include "emxdll.h"
38#include "files.h"
39#include "tcpip.h"
40#include "clib.h"
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/time.h>
44
45/* Evaluate to true if P points to the path name of a pty.
46 xf86sup.sys currently supports 32 ptys, /dev/ptyp0 through
47 /dev/ptyqf. */
48
49#define IS_PTY_NAME(p) \
50 (strncmp ((p), "/dev/pty", 8) == 0 \
51 && ((p)[8] == 'p' || (p)[8] == 'q') \
52 && (((p)[9] >= '0' && (p)[9] <= '9') \
53 || ((p)[9] >= 'a' && (p)[9] <= 'f')) \
54 && (p)[10] == 0)
55
56/* The first implementation of umask() was quite broken; to avoid
57 breaking old programs using that broken version of umask(), we keep
58 (and ignore) a separate umask value for __umask1(). */
59
60ULONG umask_bits1;
61
62/* This is the current umask. Only the 0200 bit is used. */
63
64ULONG umask_bits;
65
66/* Number of file handles. */
67
68ULONG handle_count;
69
70/* Array of handle flags. */
71
72ULONG *handle_flags;
73
74/* This array maps file handles to file descriptions (index into
75 files[]). */
76
77ULONG *handle_file;
78
79/* This array contains file descriptions. Multiple file handles may
80 point to one file descriptions if dup() is used. Unfortunately, we
81 cannot (efficiently, or at all, for devices) find out whether two
82 inherited file handles refer to the same file. Therefore, every
83 inherited file handle has its own file description. */
84
85my_file *files;
86
87/* This macro is used for accessing FDATE or FTIME values as
88 USHORT. */
89
90#define XUSHORT(x) (*(USHORT *)&(x))
91
92/* We fake inode numbers by assigning a `unique' inode number every
93 time an inode number is fetched. When querying the inode number of
94 a file twice, you'll get two different numbers. This variable
95 holds the inode number to be used for the next file. After using
96 the value, it will be incremented. When wrapping around to 0, skip
97 to 1 (0 seems to be a reserved value). */
98
99long ino_number;
100
101/* `handle_flags' is protected by this Mutex semaphore. */
102
103HMTX files_access;
104
105
106/* Prototypes. */
107
108static ULONG new_handle (ULONG handle);
109
110
111/* Initialize the data structures related to file handles, and other
112 variables. */
113
114void init_fileio (void)
115{
116 LONG req_count;
117 ULONG rc, i;
118
119 create_mutex_sem (&files_access);
120
121 /* Retrieve the maximum number of file handles. */
122
123 req_count = 0;
124 rc = DosSetRelMaxFH (&req_count, &handle_count);
125 if (rc != 0)
126 error (rc, "DosSetRelMaxFH");
127
128 /* Allocate an array which holds a word of flag bits for each
129 handle, including inactive handles. Allocate an array of file
130 descriptions, and an array which maps file handles to file
131 descriptions. */
132
133 handle_flags = checked_private_alloc (handle_count * sizeof (*handle_flags));
134 handle_file = checked_private_alloc (handle_count * sizeof (*handle_file));
135 files = checked_private_alloc (handle_count * sizeof (*files));
136
137 /* Initialize the file descriptions. */
138
139 for (i = 0; i < handle_count; ++i)
140 {
141 files[i].flags = 0;
142 files[i].ref_count = 0;
143 files[i].tio_buf = NULL;
144 SET_INVALID_FILE (i);
145 }
146
147 /* Initialize the flag bits and the file descripions for all
148 handles. Unfortunately, we cannot find out (efficiently, or at
149 all, for devices) whether two inherited file handles refer to the
150 same file. Therefore, every inherited file handle has its own
151 file description. */
152
153 for (i = 0; i < handle_count; ++i)
154 new_handle (i);
155
156 /* Initialize other variables. */
157
158 umask_bits = 0022;
159 umask_bits1 = 0644;
160 ino_number = 0x100000;
161}
162
163
164/* Set HANDLE's OPEN_FLAGS_NOINHERIT iff FD_FLAGS contains FD_CLOEXEC.
165 Return zero or the OS/2 error code. */
166
167static ULONG set_no_inherit (ULONG handle, ULONG fd_flags)
168{
169 ULONG rc, state, new_state;
170
171 rc = DosQueryFHState (handle, &state);
172 if (rc != 0) return rc;
173 if (fd_flags & FD_CLOEXEC)
174 new_state = state | OPEN_FLAGS_NOINHERIT;
175 else
176 new_state = state & ~OPEN_FLAGS_NOINHERIT;
177 if (new_state != state)
178 rc = DosSetFHState (handle, new_state & 0x7f88);
179 return rc;
180}
181
182
183/* Make all user file handles inheritable and save the original
184 FD_CLOEXEC state in the HF_NOINHERIT bit. */
185
186void fileio_fork_parent_init (void)
187{
188 ULONG i, state;
189
190 for (i = 0; i < handle_count; ++i)
191 if ((handle_flags[i] & (HF_OPEN|HF_SOCKET)) == HF_OPEN
192 && DosQueryFHState (i, &state) == 0
193 && (state & OPEN_FLAGS_NOINHERIT))
194 {
195 handle_flags[i] |= HF_NOINHERIT;
196 set_no_inherit (i, 0);
197 }
198 else
199 handle_flags[i] &= ~HF_NOINHERIT;
200}
201
202
203/* Undo fileio_fork_parent_init(), that is, restore the file handle
204 state from the HF_NOINHERIT bit. */
205
206void fileio_fork_parent_restore (void)
207{
208 ULONG i;
209
210 for (i = 0; i < handle_count; ++i)
211 if ((handle_flags[i] & (HF_OPEN|HF_SOCKET|HF_NOINHERIT))
212 == (HF_OPEN|HF_NOINHERIT))
213 set_no_inherit (i, FD_CLOEXEC);
214}
215
216
217/* Fill-in file-related fields of structure for the child process. */
218
219void fileio_fork_parent_fillin (struct fork_data_done *p)
220{
221 ULONG i, f;
222
223 if (p->size + handle_count * sizeof (ULONG) <= FORK_OBJ_SIZE)
224 p->handle_count = handle_count;
225 else
226 p->handle_count = (FORK_OBJ_SIZE - p->size) / sizeof (ULONG);
227 p->fd_flags_array = (ULONG *)((char *)p + p->size);
228 p->size += p->handle_count * sizeof (ULONG);
229
230 for (i = 0; i < p->handle_count; ++i)
231 {
232 f = 0;
233 if (IS_SOCKET (i))
234 {
235 if (IS_VALID_FILE (i))
236 f = (ULONG)GET_FILE (i)->x.socket.fd_flags;
237 }
238 else if ((handle_flags[i] & (HF_OPEN|HF_NOINHERIT))
239 == (HF_OPEN|HF_NOINHERIT))
240 f = FD_CLOEXEC;
241 p->fd_flags_array[i] = f;
242 }
243}
244
245
246/* Initialize file handles in a forked process. */
247
248void fileio_fork_child (struct fork_data_done *p)
249{
250 ULONG i, n;
251
252 n = (p->handle_count < handle_count ? p->handle_count : handle_count);
253 for (i = 0; i < n; ++i)
254 if ((p->fd_flags_array[i] & FD_CLOEXEC) && IS_VALID_FILE (i))
255 {
256 if (IS_SOCKET (i))
257 GET_FILE (i)->x.socket.fd_flags = (int)p->fd_flags_array[i];
258 else
259 set_no_inherit (i, FD_CLOEXEC);
260 }
261}
262
263
264/* Call this function instead of DosQueryPathInfo, to be able to work
265 around OS/2 bugs.
266
267 If DEBUG_QPINFO2 is set (-!64), DosQueryPathInfo will be called
268 twice: Sometimes, DosQueryPathInfo reports directories on CDROMs as
269 files. Calling DosQueryPathInfo seems to help. We don't call
270 DosQueryPathInfo twice if ulInfoLevel is not FIL_STANDARD, as
271 pInfoBuffer is an input/output parameter if ulInfoLevel is
272 FIL_QUERYEASFROMLIST. (Currently, ulInfoLevel is always
273 FIL_STANDARD.) */
274
275ULONG query_path_info (PCSZ pszPathName, ULONG ulInfoLevel,
276 PVOID pInfoBuffer, ULONG ulInfoLength)
277{
278 ULONG rc;
279
280 rc = DosQueryPathInfo (pszPathName, ulInfoLevel, pInfoBuffer, ulInfoLength);
281 if (ulInfoLevel == FIL_STANDARD && (debug_flags & DEBUG_QPINFO2))
282 rc = DosQueryPathInfo (pszPathName, ulInfoLevel, pInfoBuffer,
283 ulInfoLength);
284 return rc;
285}
286
287
288/* Compare the first element of the path PATH to DIR. DIR must not
289 contain upper case letters. */
290
291static int dir_check (const char *path, const char *dir)
292{
293 while (*path != 0 && *dir != 0 && tolower (*path) == *dir)
294 ++path, ++dir;
295 return *dir == 0 && (*path == '/' || *path == '\\');
296}
297
298
299/* Avoid OS/2 internal processing error: If the path name S starts
300 with \pipe\ or /pipe/, return true. */
301
302int check_npipe (const char *s)
303{
304 return (*s == '/' || *s == '\\') && dir_check (s + 1, "pipe");
305}
306
307
308/* Truncate the path name SRC, placing the result into the buffer
309 pointed to by DST. At most 512 characters (including the
310 terminating null character) are copied to DST. */
311
312void truncate_name (char *dst, const char *src)
313{
314 int dst_count;
315 ULONG trunc_bit;
316
317 dst_count = 512 - 1; /* Buffer size sans null character */
318
319 /* Prepend drive letter if the -r option is used, the path name
320 starts with a slash but is not a UNC path, and the first path
321 element is not `dev' or `pipe'. */
322
323 trunc_bit = TRUNC_ALL;
324 if (src[0] == '/' || src[0] == '\\')
325 {
326 if (src[1] == '/' || src[1] == '\\')
327 {
328 /* It's a UNC path: don't prepend drive letter; check
329 `-t/'. */
330 trunc_bit = TRUNC_UNC;
331 }
332 else if (dir_check (src + 1, "dev") || dir_check (src + 1, "pipe"))
333 {
334 /* It's a device or pipe name. Do not prepend drive letter;
335 do not truncate. */
336 trunc_bit = 0;
337 }
338 else if (opt_drive != 0)
339 {
340 /* Prepend drive letter; check `-t' for that drive
341 letter. */
342 *dst++ = opt_drive;
343 *dst++ = ':';
344 dst_count -= 2;
345 trunc_bit = TRUNC_DRV (opt_drive);
346 }
347 }
348 else if (((src[0] >= 'A' && src[0] <= 'Z')
349 || (src[0] >= 'a' && src[0] <= 'z'))
350 && src[1] == ':')
351 trunc_bit = TRUNC_DRV (src[0]);
352
353 if (trunc_bit == TRUNC_ALL && opt_trunc != 0 && opt_trunc != TRUNC_ALL)
354 {
355 ULONG drv, map;
356
357 DosQueryCurrentDisk (&drv, &map);
358 trunc_bit = TRUNC_DRV (drv);
359 }
360
361 if (opt_trunc & trunc_bit)
362 {
363 unsigned char rest, dot;
364
365 /* The -t option applies; truncate all elements of the pathname
366 to 8.3 format. */
367
368 rest = 8; dot = FALSE;
369 while (*src != 0 && dst_count != 0)
370 {
371 if (_nls_is_dbcs_lead ((unsigned char)*src) && src[1] != 0)
372 {
373 /* We encountered a valid DBCS character (note that a
374 DBCS lead byte followed by zero is invalid and will
375 be handled as an ordinary character). Avoid
376 truncating between the two bytes. */
377
378 if (rest >= 2 && dst_count >= 2)
379 {
380 *dst++ = *src++;
381 *dst++ = *src;
382 dst_count -= 2; rest -= 2;
383 }
384 else
385 ++src;
386 }
387 else if (*src == ':' || *src == '/' || *src == '\\')
388 {
389 *dst++ = *src;
390 --dst_count;
391 rest = 8; dot = FALSE;
392 }
393 else if (*src == '.')
394 {
395 if (rest == 8)
396 {
397 /* An element starts with a dot -- process it like
398 an ordinary character. This makes ".." work.
399 ".emacs", for instance, is left alone. */
400
401 for (;;)
402 {
403 *dst++ = *src;
404 --dst_count; --rest;
405 if (src[1] != '.' || dst_count == 0)
406 break;
407 ++src;
408 }
409 }
410 else if (dot)
411 {
412 /* If the element has two or more dots, truncate at
413 the second dot. This does not apply to ".."
414 which is handled above. */
415
416 rest = 0;
417 }
418 else
419 {
420 /* Start of a suffix. */
421
422 rest = 3; dot = TRUE;
423 *dst++ = *src;
424 --dst_count;
425 }
426 }
427 else if (rest != 0)
428 {
429 /* Copy an ordinary character unless the truncation
430 limit (8 or 3 characters) has been reached. */
431
432 *dst++ = *src;
433 --dst_count; --rest;
434 }
435 ++src;
436 }
437 }
438 else
439 {
440 /* The -t option is not used, don't truncate the file name to
441 8.3 format (however, truncate it to avoid overflowing the
442 destination buffer. */
443
444 while (*src != 0 && dst_count != 0)
445 {
446 *dst++ = *src++;
447 --dst_count;
448 }
449 }
450
451 /* Add the terminating null character. */
452
453 *dst = 0;
454}
455
456
457/* Update the flag bits of file handle HANDLE, the new value is FLAGS.
458 Don't crash if HANDLE is out of range. */
459
460void set_handle_flags (ULONG handle, ULONG flags)
461{
462 if (handle < handle_count)
463 {
464 handle_flags[handle] = flags;
465 if (IS_VALID_FILE (handle))
466 GET_FILE (handle)->flags = flags;
467 }
468}
469
470
471/* Allocate a file description for HANDLE. */
472
473void alloc_file_description (ULONG handle)
474{
475 ULONG i;
476
477 LOCK_FILES;
478
479 /* File 0 is reserved for the CON device. */
480
481 for (i = 1; i < handle_count; ++i)
482 if (files[i].ref_count == 0)
483 {
484 handle_file[handle] = i;
485 files[i].ref_count = 1;
486 UNLOCK_FILES;
487 return;
488 }
489 SET_INVALID_FILE (handle);
490 UNLOCK_FILES;
491}
492
493
494/* Relocate the OS/2 file handle HANDLE if there is already a socket
495 with that handle; then, if CALL_NEW_HANDLE is true, apply
496 new_handle(). Update the object pointed to by TARGET with the new
497 handle. Return errno. */
498
499static int reloc_handle (HFILE handle, HFILE *target, int call_new_handle)
500{
501 ULONG i, rc, htype, hflags;
502
503 *target = handle;
504 if (!IS_SOCKET (handle))
505 return (call_new_handle ? new_handle (handle) : 0);
506
507 LOCK_FILES;
508 for (i = 0; i < handle_count; ++i)
509 if (!(handle_flags[i] & HF_OPEN)
510 && DosQueryHType (i, &htype, &hflags) == ERROR_INVALID_HANDLE)
511 break;
512 if (i >= handle_count)
513 {
514 UNLOCK_FILES;
515 return EMFILE;
516 }
517 *target = i;
518 xf86sup_maybe_enadup (handle, TRUE);
519 rc = DosDupHandle (handle, &i);
520 xf86sup_maybe_enadup (handle, FALSE);
521 if (rc != 0)
522 {
523 UNLOCK_FILES;
524 return set_error (rc);
525 }
526 if (i != *target) /* Can this happen? */
527 {
528 UNLOCK_FILES;
529 DosClose (i);
530 return EMFILE;
531 }
532 rc = (call_new_handle ? new_handle (i) : 0);
533 UNLOCK_FILES;
534 if (rc != 0)
535 {
536 DosClose (i);
537 return rc;
538 }
539 DosClose (handle);
540 return 0;
541}
542
543
544/* Update our tables when closing a file handle. */
545
546void close_handle (ULONG handle)
547{
548 if (handle >= handle_count) return;
549 if (IS_VALID_FILE (handle))
550 {
551 my_file *d;
552
553 LOCK_FILES;
554 d = GET_FILE (handle);
555 SET_INVALID_FILE (handle);
556 if (d->ref_count != 0)
557 {
558 --d->ref_count;
559 if (d->ref_count == 0)
560 {
561 /* File description no longer referenced. */
562 if (d->tio_buf != NULL)
563 {
564 private_free (d->tio_buf);
565 d->tio_buf = NULL;
566 }
567 }
568 }
569 UNLOCK_FILES;
570 }
571 handle_flags[handle] = 0;
572}
573
574
575/* Find out the type of the new file handle HANDLE, update the flag
576 bits in `handle_flags', and add a file description. Return the
577 OS/2 error code. */
578
579static ULONG new_handle (ULONG handle)
580{
581 ULONG rc, htype, hflags, pstate, flags;
582
583 if (handle >= handle_count) return ERROR_INVALID_HANDLE;
584 close_handle (handle);
585 rc = DosQueryHType (handle, &htype, &hflags);
586 if (rc != 0)
587 return rc;
588
589 switch (htype & 0xff) /* Class */
590 {
591 case HANDTYPE_DEVICE:
592 if (hflags & 3) /* KBD or VIO */
593 {
594 /* All handles for the CON device (KBD and VIO) share the
595 same file description: file table entry 0. Do not use
596 alloc_file_description(). */
597
598 ++files[0].ref_count;
599 handle_file[handle] = 0;
600 flags = HF_OPEN | HF_DEV | HF_CON;
601 init_termio (handle);
602 }
603 else
604 {
605 alloc_file_description (handle);
606 if (hflags & 4)
607 flags = HF_OPEN | HF_DEV | HF_NUL;
608 else if (hflags & 8)
609 flags = HF_OPEN | HF_DEV | HF_CLK;
610 else if (!IS_VALID_FILE (handle))
611 flags = HF_OPEN | HF_DEV;
612 else if (!(debug_flags & DEBUG_NOXF86SUP)
613 && xf86sup_query (handle, &flags) == 0)
614 flags |= HF_OPEN | HF_DEV;
615 else if (query_async (handle) == 0
616 && translate_async (handle) == 0)
617 flags = HF_OPEN | HF_DEV | HF_ASYNC;
618 else
619 flags = HF_OPEN | HF_DEV;
620 }
621 break;
622
623 case HANDTYPE_PIPE:
624 alloc_file_description (handle);
625 rc = DosQueryNPHState (handle, &pstate);
626 if (rc == ERROR_PIPE_NOT_CONNECTED)
627 flags = HF_OPEN | HF_NPIPE;
628 else if (rc != 0) /* Unnamed pipe */
629 flags = HF_OPEN | HF_UPIPE;
630 else if (pstate & NP_NOWAIT)
631 flags = HF_OPEN | HF_NPIPE | HF_NDELAY;
632 else
633 flags = HF_OPEN | HF_NPIPE;
634 break;
635
636 case HANDTYPE_FILE:
637 alloc_file_description (handle);
638 flags = HF_OPEN | HF_FILE;
639 break;
640
641 default:
642 /* Future extension. */
643 alloc_file_description (handle);
644 flags = HF_OPEN;
645 break;
646 }
647 set_handle_flags (handle, flags);
648 return 0;
649}
650
651
652/* Return the number of bytes available for reading from pipe HANDLE.
653 Return -1 and set *ERRNOP on error. Set *ERRNOP to zero on
654 success. */
655
656static int npipe_avail (ULONG handle, int *errnop)
657{
658 ULONG rc, state, nread;
659 AVAILDATA avail;
660 char buffer;
661
662 rc = DosPeekNPipe (handle, &buffer, 0, &nread, &avail, &state);
663 if (rc != 0)
664 {
665 *errnop = set_error (rc);
666 return -1;
667 }
668 *errnop = 0;
669 return avail.cbpipe;
670}
671
672
673/* Find the COUNT lowest numbered, unused file handles and store them
674 to the array pointed to by DST, in increasing order. Return the
675 number of unused file handles found. */
676
677int find_unused_handles (ULONG *dst, int count)
678{
679 ULONG i, htype, hflags;
680 int n;
681
682 n = 0;
683 for (i = 0; i < handle_count && n < count; ++i)
684 if (DosQueryHType (i, &htype, &hflags) == ERROR_INVALID_HANDLE)
685 dst[n++] = i;
686 return n;
687}
688
689
690/* This function implements the __open() system call of emx 0.8e and
691 later. Return the file handle, or -1 on error. */
692
693int sys_open (const char *path, unsigned flags, unsigned long size,
694 int *errnop)
695{
696 char fname[512];
697 ULONG rc;
698 ULONG open_mode, attr, action, open_flag;
699 HFILE handle, nh;
700 int fail_errno, e;
701 char is_pty;
702
703 /* Interpret "/dev/null" and "/dev/tty" as the name of the null
704 device "NUL" or of the console "CON", respectively. Copy the
705 path name to `fname', with modifications according to the -r and
706 -t options. */
707
708 is_pty = FALSE;
709 if (strcmp (path, "/dev/null") == 0)
710 strcpy (fname, "nul");
711 else if (strcmp (path, "/dev/tty") == 0)
712 strcpy (fname, "con");
713 else if (!(debug_flags & DEBUG_NOXF86SUP) && IS_PTY_NAME (path))
714 {
715 is_pty = TRUE;
716 strcpy (fname, path);
717 }
718 else
719 truncate_name (fname, path);
720
721 /* Extract the access mode and sharing mode bits. */
722
723 open_mode = flags & 0x77;
724
725 /* Handle O_NOINHERIT and O_SYNC. */
726
727 if (flags & _SO_NOINHERIT)
728 open_mode |= OPEN_FLAGS_NOINHERIT;
729 if (flags & _SO_SYNC)
730 open_mode |= OPEN_FLAGS_WRITE_THROUGH;
731
732 /* Set OPEN_FLAGS_FAIL_ON_ERROR for /dev/ptyXX as otherwise opening
733 such a device would generate an error popup if the pty is already
734 in use. With OPEN_FLAGS_FAIL_ON_ERROR, we'll get return code
735 ERROR_NOT_READY, which will be translated to EIO. */
736
737 if (is_pty)
738 open_mode |= OPEN_FLAGS_FAIL_ON_ERROR;
739
740 /* Extract the file attribute bits. */
741
742 attr = (flags >> 8) & 0xff;
743 if (umask_bits & 0200)
744 attr |= 1;
745
746 /* Translate ERROR_OPEN_FAILED to ENOENT unless O_EXCL is set (see
747 below). */
748
749 fail_errno = ENOENT;
750
751 /* Compute `open_flag' depending on `flags'. Note that _SO_CREAT is
752 set for O_CREAT. */
753
754 if (flags & _SO_CREAT)
755 {
756 if (flags & _SO_EXCL)
757 {
758 open_flag = OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
759 fail_errno = EEXIST;
760 }
761 else if (flags & _SO_TRUNC)
762 open_flag = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
763 else
764 open_flag = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
765 }
766 else if (flags & _SO_TRUNC)
767 open_flag = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
768 else
769 open_flag = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
770
771 /* Ignore SIZE unless _SO_SIZE is set. */
772
773 if (!(flags & _SO_SIZE))
774 size = 0;
775
776 /* Try to open the file and handle errors. */
777
778 rc = DosOpen (fname, &handle, &action, size, attr, open_flag, open_mode,
779 NULL);
780 if (rc == ERROR_OPEN_FAILED)
781 {
782 set_error (rc);
783 *errnop = fail_errno;
784 return -1;
785 }
786 if (rc != 0)
787 {
788 *errnop = set_error (rc);
789 return -1;
790 }
791
792 /* Relocate the handle and update the flag bits and the file
793 description for the new file handle. */
794
795 e = reloc_handle (handle, &nh, TRUE);
796 if (e != 0)
797 {
798 DosClose (handle);
799 *errnop = e;
800 return -1;
801 }
802
803 /* Return the handle and tell the caller not to modify `errno'. */
804
805 *errnop = 0;
806 return nh;
807}
808
809
810/* This function emulates DOS function 0x3c. That function may still
811 be used by ancient programs. */
812
813int old_creat (const char *path, ULONG attr, int *errnop)
814{
815 char fname[512];
816 ULONG rc, action;
817 HFILE hf, nh;
818 int e;
819
820 /* Copy the path name to `fname', with modifications according to
821 the -r and -t options. */
822
823 truncate_name (fname, path);
824
825 /* Extract the file attribute bits. */
826
827 attr &= 0x27;
828 if (umask_bits & S_IWRITE)
829 attr |= 1; /* Set read-only attribute */
830
831 /* Try to open the file and handle errors. */
832
833 rc = DosOpen (fname, &hf, &action, 0, attr,
834 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS,
835 OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE,
836 NULL);
837 if (rc != 0)
838 {
839 *errnop = set_error (rc);
840 return -1;
841 }
842
843 /* Relocate the handle and update the flag bits and the file
844 description for the new file handle. */
845
846 e = reloc_handle (hf, &nh, TRUE);
847 if (e != 0)
848 {
849 DosClose (hf);
850 *errnop = e;
851 return -1;
852 }
853
854 /* Return the handle and tell the caller not to modify `errno'. */
855
856 *errnop = 0;
857 return nh;
858}
859
860
861/* This function emulates DOS function 0x3d. That function may still
862 be used by ancient programs. */
863
864int old_open (const char *path, ULONG mode, int *errnop)
865{
866 char fname[512];
867 ULONG rc, action, new_mode;
868 HFILE hf, nh;
869 int e;
870
871 /* Copy the path name to `fname', with modifications according to
872 the -r and -t options. */
873
874 truncate_name (fname, path);
875
876 /* Extract the access mode and the sharing mode. */
877
878 if (mode & 0x70) /* Sharing mode */
879 new_mode = mode & 0x70;
880 else
881 new_mode = 0x40; /* Compatibility -> DENYNONE */
882 new_mode |= mode & 0x83; /* Inheritance, access code */
883
884 /* Try to open the file and handle errors. */
885
886 rc = DosOpen (fname, &hf, &action, 0, FILE_NORMAL,
887 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
888 new_mode, NULL);
889 if (rc != 0)
890 {
891 *errnop = set_error (rc);
892 return -1;
893 }
894
895 /* Relocate the handle and update the flag bits and the file
896 description for the new file handle. */
897
898 e = reloc_handle (hf, &nh, TRUE);
899 if (e != 0)
900 {
901 DosClose (hf);
902 *errnop = e;
903 return -1;
904 }
905
906 /* Return the handle and tell the caller not to modify `errno'. */
907
908 *errnop = 0;
909 return nh;
910}
911
912
913/* This function implements the __fcntl() system call. */
914
915int do_fcntl (ULONG handle, ULONG request, ULONG arg, int *errnop)
916{
917 ULONG rc, ht, diff, state;
918 int result;
919
920 switch (request)
921 {
922 case F_GETFD:
923
924 /* Return the close-on-exec flag. It's the complement of OS/2's
925 OPEN_FLAGS_NOINHERIT bit. For sockets, the flag is stored in
926 the `my_file' structure. */
927
928 if (IS_SOCKET (handle))
929 {
930 if (!IS_VALID_FILE (handle))
931 {
932 *errnop = EBADF;
933 return -1;
934 }
935 *errnop = 0;
936 return GET_FILE (handle)->x.socket.fd_flags;
937 }
938
939 rc = DosQueryFHState (handle, &state);
940 if (rc != 0)
941 {
942 *errnop = set_error (rc);
943 return -1;
944 }
945 *errnop = 0;
946 return ((state & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0);
947
948 case F_SETFD:
949
950 /* Set the close-on-exec flag. */
951
952 if (IS_SOCKET (handle))
953 {
954 if (!IS_VALID_FILE (handle))
955 {
956 *errnop = EBADF;
957 return -1;
958 }
959 GET_FILE (handle)->x.socket.fd_flags = arg;
960 *errnop = 0;
961 return 0;
962 }
963
964 rc = set_no_inherit (handle, arg);
965 if (rc != 0)
966 {
967 *errnop = set_error (rc);
968 return -1;
969 }
970 break;
971
972 case F_GETFL:
973
974 /* Return O_APPEND and/or O_NDELAY. */
975
976 if (handle >= handle_count || !(handle_flags[handle] & HF_OPEN))
977 {
978 *errnop = EBADF;
979 return -1;
980 }
981 ht = handle_flags[handle];
982 result = 0;
983 if (ht & HF_NDELAY)
984 result |= O_NDELAY;
985 if (ht & HF_APPEND)
986 result |= O_APPEND;
987 *errnop = 0;
988 return result;
989
990 case F_SETFL:
991
992 /* Copy O_NDELAY and O_APPEND as HF_NDELAY and HF_APPEND to
993 `handle_flags'. If O_NDELAY changed for a named pipe, change
994 pipe state. If O_NDELAY is changed for a socket or pty,
995 perform a FIONBIO ioctl. */
996
997 if (arg & ~(O_NDELAY | O_APPEND))
998 {
999 *errnop = EINVAL;
1000 return -1;
1001 }
1002 if (handle >= handle_count || !(handle_flags[handle] & HF_OPEN))
1003 {
1004 *errnop = EBADF;
1005 return -1;
1006 }
1007 ht = handle_flags[handle];
1008 ht &= ~(HF_NDELAY | HF_APPEND);
1009 if (arg & O_NDELAY)
1010 ht |= HF_NDELAY;
1011 if (arg & O_APPEND)
1012 ht |= HF_APPEND;
1013
1014 diff = handle_flags[handle] ^ ht; /* Changed flags */
1015 handle_flags[handle] = ht;
1016
1017 /* Change the handle state if HF_NDELAY changed for a named
1018 pipe. */
1019
1020 if ((diff & HF_NDELAY) && (ht & HF_NPIPE))
1021 {
1022 state = 0;
1023 if (ht & HF_NDELAY)
1024 state |= NP_NOWAIT;
1025 rc = DosSetNPHState (handle, state);
1026 if (rc != 0)
1027 {
1028 *errnop = set_error (rc);
1029 return -1;
1030 }
1031 }
1032
1033 /* Change the handle state if HF_NDELAY changed for a socket. */
1034
1035 if ((diff & HF_NDELAY) && (ht & HF_SOCKET))
1036 if (tcpip_fcntl (handle, F_SETFL, arg, errnop) != 0)
1037 return -1;
1038
1039 /* Change the handle state if HF_NDELAY changed for a xf86sup
1040 device. */
1041
1042 if ((diff & HF_NDELAY) && (ht & HF_XF86SUP))
1043 if (xf86sup_fcntl (handle, F_SETFL, arg, errnop) != 0)
1044 return -1;
1045 break;
1046
1047 default:
1048
1049 /* Set errno to EINVAL for unknown request codes. */
1050
1051 *errnop = EINVAL;
1052 return -1;
1053 }
1054
1055 /* Function successfully completed. */
1056
1057 *errnop = 0;
1058 return 0;
1059}
1060
1061
1062/* This function emulates the DOS function 0x4400 (IOCTL, get device
1063 data). */
1064
1065int do_ioctl1 (ULONG handle, int *errnop)
1066{
1067 ULONG rc, htype, hflags;
1068 int result;
1069
1070 /* This function is not implemented for sockets. */
1071
1072 if (IS_SOCKET (handle))
1073 {
1074 *errnop = EINVAL;
1075 return -1;
1076 }
1077
1078 /* First, get the type of the file handle (pipe, device, or
1079 file). */
1080
1081 rc = DosQueryHType (handle, &htype, &hflags);
1082 if (rc != 0)
1083 {
1084 *errnop = set_error (rc);
1085 return -1;
1086 }
1087
1088 /* Set `result' depending on the file type. */
1089
1090 result = 0;
1091 switch (htype & 0xff)
1092 {
1093 case HANDTYPE_PIPE:
1094 result = 0x80; /* Device or pipe */
1095 break;
1096
1097 case HANDTYPE_DEVICE:
1098 result = (hflags & 0x0f) | 0x80; /* kbd, scr, nul, clock */
1099 break;
1100
1101 case HANDTYPE_FILE:
1102 /* Insert drive letter (not implemented). */
1103 break;
1104
1105 default:
1106 /* Future extension. */
1107 break;
1108 }
1109
1110 *errnop = 0;
1111 return result;
1112}
1113
1114
1115/* This function implements the __ioctl2() system call. */
1116
1117int do_ioctl2 (ULONG handle, ULONG request, ULONG arg, int *errnop)
1118{
1119 ULONG ht;
1120 int n;
1121
1122 /* This function is the first one called by the startup code which
1123 requires init_fileio(). If emx.dll is used by a DLL which is
1124 used by a program that does not use emx.dll, init_fileio() has
1125 not yet been called. Do that now. */
1126
1127 if (handle_flags == NULL)
1128 init_fileio ();
1129
1130 /* Reject invalid handles and handles which are not open. */
1131
1132 if (handle >= handle_count || !(handle_flags[handle] & HF_OPEN))
1133 {
1134 *errnop = EBADF;
1135 return -1;
1136 }
1137
1138 /* Get the flag bits for the handle. */
1139
1140 ht = handle_flags[handle];
1141
1142 /* Handle sockets specially. */
1143
1144 if (ht & HF_SOCKET)
1145 return tcpip_ioctl (handle, request, arg, errnop);
1146
1147 /* Handle xf86sup devices specially. */
1148
1149 if (ht & HF_XF86SUP)
1150 return xf86sup_ioctl (handle, request, arg, errnop);
1151
1152 switch (request)
1153 {
1154 case TCGETA:
1155
1156 /* Get `struct termio' of the file handle. */
1157
1158 if (!(ht & (HF_CON|HF_ASYNC)) || !IS_VALID_FILE (handle))
1159 {
1160 *errnop = EINVAL;
1161 return -1;
1162 }
1163 termio_get (handle, (void *)arg);
1164 break;
1165
1166 case TCSETA:
1167 case TCSETAF:
1168 case TCSETAW:
1169
1170 /* Set `struct termio' of the file handle. */
1171
1172 if (!(ht & (HF_CON|HF_ASYNC)) || !IS_VALID_FILE (handle))
1173 {
1174 *errnop = EINVAL;
1175 return -1;
1176 }
1177 if (request == TCSETAF)
1178 termio_flush (handle);
1179 termio_set (handle, (void *)arg);
1180 break;
1181
1182 case TCFLSH:
1183
1184 /* Flush the input and/or output queues. */
1185
1186 if (!(ht & (HF_CON|HF_ASYNC)) || !IS_VALID_FILE (handle) || arg > 2)
1187 {
1188 *errnop = EINVAL;
1189 return -1;
1190 }
1191 if (arg == 0 || arg == 2)
1192 termio_flush (handle); /* Flush input queue. */
1193#if 0
1194 if (arg == 1 || arg == 2)
1195 {
1196 /* Flush output queue; not implemented. */
1197 }
1198#endif
1199 break;
1200
1201 case TCSBRK:
1202
1203 /* Send break. */
1204
1205 *errnop = 0;
1206 return 0;
1207
1208 case TCXONC:
1209
1210 /* Flow control. */
1211
1212 *errnop = 0;
1213 return 0;
1214
1215 case _TCGA:
1216
1217 /* Get `struct termios' of the file handle, for tcgetattr(). */
1218
1219 if (!(ht & (HF_CON|HF_ASYNC)) || !IS_VALID_FILE (handle))
1220 {
1221 *errnop = xlate_errno (ENOTTY);
1222 return -1;
1223 }
1224 termios_get (handle, (void *)arg);
1225 break;
1226
1227 case _TCSANOW:
1228 case _TCSADRAIN:
1229 case _TCSAFLUSH:
1230
1231 /* Set `struct termios' of the file handle, for tcsetattr(). */
1232
1233 if (!(ht & (HF_CON|HF_ASYNC)) || !IS_VALID_FILE (handle))
1234 {
1235 *errnop = xlate_errno (ENOTTY);
1236 return -1;
1237 }
1238 if (request == _TCSAFLUSH)
1239 termio_flush (handle);
1240 termios_set (handle, (void *)arg);
1241 break;
1242
1243 case FIONREAD:
1244
1245 /* Get the number of characters available for reading. */
1246
1247 if ((ht & (HF_CON|HF_ASYNC)) && IS_VALID_FILE (handle))
1248 n = termio_avail (handle);
1249 else if (ht & HF_NPIPE)
1250 {
1251 n = npipe_avail (handle, errnop);
1252 if (n == -1)
1253 return -1;
1254 }
1255 else
1256 {
1257 *errnop = EINVAL;
1258 return -1;
1259 }
1260 *(int *)arg = n;
1261 break;
1262
1263 case FGETHTYPE:
1264
1265 /* Get the file type. */
1266
1267 if (ht & HF_UPIPE)
1268 n = HT_UPIPE;
1269 else if (ht & HF_NPIPE)
1270 n = HT_NPIPE;
1271 else if (ht & HF_SOCKET)
1272 n = HT_SOCKET;
1273 else if (!(ht & HF_DEV))
1274 n = HT_FILE;
1275 else if (ht & HF_CON)
1276 n = HT_DEV_CON;
1277 else if (ht & HF_NUL)
1278 n = HT_DEV_NUL;
1279 else if (ht & HF_CLK)
1280 n = HT_DEV_CLK;
1281 else
1282 n = HT_DEV_OTHER;
1283 *(int *)arg = n;
1284 break;
1285
1286 default:
1287 *errnop = EINVAL;
1288 return -1;
1289 }
1290
1291 /* Function successfully completed. */
1292
1293 *errnop = 0;
1294 return 0;
1295}
1296
1297
1298/* Duplicate a file handle. */
1299
1300int do_dup (ULONG src_fd, ULONG req_target, int *errnop)
1301{
1302 ULONG rc, state, new_fd;
1303
1304 /* Handle socket handles specially. */
1305
1306 if (IS_SOCKET (src_fd))
1307 return tcpip_dup (src_fd, req_target, errnop);
1308
1309 if (src_fd == req_target)
1310 {
1311 rc = DosQueryFHState (src_fd, &state); /* Check SRC_FD */
1312 if (rc != 0)
1313 {
1314 *errnop = set_error (rc);
1315 return -1;
1316 }
1317 *errnop = 0;
1318 return req_target;
1319 }
1320
1321 new_fd = req_target;
1322 LOCK_FILES;
1323 xf86sup_maybe_enadup (src_fd, TRUE);
1324 rc = DosDupHandle (src_fd, &new_fd);
1325 xf86sup_maybe_enadup (src_fd, FALSE);
1326 if (rc != 0)
1327 {
1328 UNLOCK_FILES;
1329 *errnop = set_error (rc);
1330 return -1;
1331 }
1332
1333 /* Reject handles which are out of the supported range. */
1334
1335 if (new_fd >= handle_count)
1336 {
1337 DosClose (new_fd);
1338 set_error (0);
1339 UNLOCK_FILES;
1340 *errnop = EMFILE;
1341 return -1;
1342 }
1343
1344 /* A successful dup2() call closes the target handle. Update our
1345 tables. */
1346
1347 if (new_fd == req_target)
1348 {
1349 /* DosDupHandle doesn't know about sockets, so we have to close
1350 sockets here. tcpip_close() calls close_handle(). */
1351
1352 if (IS_SOCKET (new_fd))
1353 tcpip_close (new_fd);
1354 else
1355 close_handle (new_fd);
1356 }
1357
1358 /* If the new handle hit a faked socket handle, relocate the new
1359 handle. This can happen only for dup(), not for dup2(), because
1360 the target handle of dup2() has been closed above. */
1361
1362 if (handle_flags[new_fd] & HF_SOCKET)
1363 {
1364 ULONG nf;
1365
1366 if (reloc_handle (new_fd, &nf, FALSE) != 0)
1367 {
1368 DosClose (new_fd);
1369 UNLOCK_FILES;
1370 set_error (0);
1371 *errnop = EMFILE;
1372 return -1;
1373 }
1374 new_fd = nf;
1375 }
1376
1377 /* Copy the source file handle's data to the new handle. */
1378
1379 handle_file[new_fd] = handle_file[src_fd];
1380 handle_flags[new_fd] = handle_flags[src_fd];
1381
1382 if (IS_VALID_FILE (src_fd))
1383 GET_FILE (src_fd)->ref_count += 1;
1384 else
1385 {
1386 SET_INVALID_FILE (new_fd);
1387 new_handle (new_fd);
1388 }
1389 UNLOCK_FILES;
1390 *errnop = 0;
1391 return new_fd;
1392}
1393
1394
1395/* Close a file handle. */
1396
1397int do_close (ULONG handle)
1398{
1399 ULONG rc;
1400
1401 if (IS_SOCKET (handle))
1402 return tcpip_close (handle);
1403
1404 close_handle (handle);
1405 rc = DosClose (handle);
1406 if (rc != 0)
1407 return set_error (rc);
1408 return 0;
1409}
1410
1411
1412/* Read from a file handle. */
1413
1414ULONG do_read (ULONG handle, void *dst, ULONG count, int *errnop)
1415{
1416 ULONG hflags, rc, nread;
1417
1418 hflags = (handle < handle_count ? handle_flags[handle] : 0);
1419 if ((hflags & (HF_CON|HF_ASYNC)) && IS_VALID_FILE (handle)
1420 && !(GET_FILE (handle)->c_lflag & IDEFAULT))
1421 return termio_read (handle, dst, count, errnop);
1422 else if ((hflags & HF_SOCKET) && IS_VALID_FILE (handle))
1423 return tcpip_read (handle, dst, count, errnop);
1424 else
1425 {
1426 if (exe_heap)
1427 touch (dst, count);
1428 rc = DosRead (handle, dst, count, &nread);
1429 if (rc != 0)
1430 {
1431 *errnop = set_error (rc);
1432 return (ULONG)-1;
1433 }
1434 *errnop = 0;
1435 return nread;
1436 }
1437}
1438
1439
1440/* Write to a file handle. */
1441
1442ULONG do_write (ULONG handle, void *src, ULONG count, int *errnop)
1443{
1444 ULONG rc, nwritten;
1445
1446 if (IS_SOCKET (handle) && IS_VALID_FILE (handle))
1447 return tcpip_write (handle, src, count, errnop);
1448
1449 if (exe_heap)
1450 touch (src, count);
1451 rc = DosWrite (handle, src, count, &nwritten);
1452 if (rc != 0)
1453 {
1454 *errnop = set_error (rc);
1455 return (ULONG)-1;
1456 }
1457 *errnop = 0;
1458 return nwritten;
1459}
1460
1461
1462/* Move the file pointer of a file handle. */
1463
1464ULONG do_seek (ULONG handle, ULONG origin, LONG distance, int *errnop)
1465{
1466 ULONG rc, new_pos;
1467
1468 if (IS_SOCKET (handle))
1469 {
1470 *errnop = ESPIPE;
1471 return (ULONG)-1;
1472 }
1473
1474 rc = DosSetFilePtr (handle, distance, origin, &new_pos);
1475 if (rc != 0)
1476 {
1477 *errnop = set_error (rc);
1478 return (ULONG)-1;
1479 }
1480 *errnop = 0;
1481 return new_pos;
1482}
1483
1484
1485/* Create a pipe. DST points to two int's which will receive the
1486 handles. PIPESIZE is the size of the pipe. Return errno. */
1487
1488int do_pipe (int *dst, ULONG pipesize)
1489{
1490 HPIPE r_handle, w_handle, nh;
1491 ULONG rc, action, pn;
1492 char fname[128];
1493 int e;
1494
1495 /* Create unique a pipe name (unique on this system). */
1496
1497 LOCK_COMMON;
1498 pn = ++pipe_number;
1499 UNLOCK_COMMON;
1500 sprintf (fname, "/pipe/emx/pipes/%.8x.pip", (unsigned)pn);
1501
1502 /* Create the pipe. */
1503
1504 rc = DosCreateNPipe (fname, &r_handle,
1505 NP_ACCESS_INBOUND,
1506 1 | NP_NOWAIT | NP_TYPE_BYTE | NP_READMODE_BYTE,
1507 pipesize, pipesize, 0);
1508 if (rc != 0)
1509 return set_error (rc);
1510
1511 /* Connect to the pipe. */
1512
1513 rc = DosConnectNPipe (r_handle);
1514 if (rc != 0 && rc != ERROR_PIPE_NOT_CONNECTED)
1515 {
1516 DosClose (r_handle);
1517 return set_error (rc);
1518 }
1519
1520 /* Turn on NP_WAIT. */
1521
1522 rc = DosSetNPHState (r_handle, NP_WAIT | NP_READMODE_BYTE);
1523 if (rc != 0)
1524 {
1525 DosClose (r_handle);
1526 return set_error (rc);
1527 }
1528
1529 /* Open the write side of the pipe. */
1530
1531 rc = DosOpen (fname, &w_handle, &action, 0, FILE_NORMAL,
1532 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
1533 OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE,
1534 NULL);
1535 if (rc != 0)
1536 {
1537 DosClose (r_handle);
1538 return set_error (rc);
1539 }
1540
1541 /* Update the file handle table. */
1542
1543 e = reloc_handle (r_handle, &nh, TRUE);
1544 if (e != 0)
1545 {
1546 DosClose (r_handle);
1547 DosClose (w_handle);
1548 return e;
1549 }
1550 r_handle = nh;
1551
1552 e = reloc_handle (w_handle, &nh, TRUE);
1553 if (e != 0)
1554 {
1555 DosClose (r_handle);
1556 DosClose (w_handle);
1557 return e;
1558 }
1559 w_handle = nh;
1560
1561 /* Set DST handles. */
1562
1563 dst[0] = r_handle;
1564 dst[1] = w_handle;
1565 return 0;
1566}
1567
1568
1569/* Truncate file HANDLE to FSIZE bytes. Return errno. */
1570
1571int do_ftruncate (ULONG handle, ULONG fsize)
1572{
1573 FILESTATUS3 info;
1574 ULONG rc;
1575
1576 if (IS_SOCKET (handle))
1577 return EINVAL;
1578
1579 rc = DosQueryFileInfo (handle, FIL_STANDARD, &info, sizeof (info));
1580 if (rc != 0)
1581 return set_error (rc);
1582 if (fsize >= info.cbFile)
1583 return 0;
1584 rc = DosSetFileSize (handle, fsize);
1585 if (rc != 0)
1586 return set_error (rc);
1587 return 0;
1588}
1589
1590
1591/* Change size of file HANDLE to FSIZE bytes. Return errno. */
1592
1593int do_chsize (ULONG handle, ULONG fsize)
1594{
1595 ULONG rc;
1596
1597 if (IS_SOCKET (handle))
1598 return EINVAL;
1599
1600 rc = DosSetFileSize (handle, fsize);
1601 if (rc != 0)
1602 return set_error (rc);
1603 return 0;
1604}
1605
1606
1607/* Flush the buffers of a file. */
1608
1609int do_fsync (ULONG handle, int *errnop)
1610{
1611 ULONG rc;
1612
1613 if (IS_SOCKET (handle))
1614 return EINVAL;
1615
1616 rc = DosResetBuffer (handle);
1617 if (rc != 0)
1618 {
1619 *errnop = set_error (rc);
1620 return -1;
1621 }
1622 *errnop = 0;
1623 return 0;
1624}
1625
1626
1627/* Store the name of the file-system driver of drive DRV_NAME to DST
1628 (up to DST_SIZE bytes, including the terminating null character).
1629 Store errno to *ERRNOP. Return 0 on success, -1 on error. */
1630
1631int do_filesys (char *dst, ULONG dst_size, const char *drv_name, int *errnop)
1632{
1633 char buf[sizeof (PFSQBUFFER2) + 128];
1634 PFSQBUFFER2 pfsq = (PFSQBUFFER2)buf;
1635 ULONG rc, len;
1636
1637 len = sizeof (buf);
1638 rc = DosQueryFSAttach (drv_name, 1, FSAIL_QUERYNAME, pfsq, &len);
1639 if (rc != 0)
1640 {
1641 *errnop = set_error (rc);
1642 return -1;
1643 }
1644 if (pfsq->iType != FSAT_LOCALDRV && pfsq->iType != FSAT_REMOTEDRV)
1645 {
1646 *errnop = EINVAL;
1647 return -1;
1648 }
1649 if (pfsq->cbFSDName >= dst_size)
1650 {
1651 *errnop = E2BIG;
1652 return -1;
1653 }
1654 strcpy (dst, pfsq->szFSDName + pfsq->cbName);
1655 *errnop = 0;
1656 return 0;
1657}
1658
1659
1660/* Create a directory. */
1661
1662int do_mkdir (const char *path)
1663{
1664 char fname[512];
1665 ULONG rc;
1666
1667 /* Copy the path name to `fname', with modifications according to
1668 the -r and -t options. */
1669
1670 truncate_name (fname, path);
1671
1672 /* Create the directory and handle errors. */
1673
1674 rc = DosCreateDir (fname, NULL);
1675 if (rc != 0)
1676 return set_error (rc);
1677 return 0;
1678}
1679
1680
1681/* Remove a directory. */
1682
1683int do_rmdir (const char *path)
1684{
1685 char fname[512];
1686 ULONG rc;
1687
1688 /* Copy the path name to `fname', with modifications according to
1689 the -r and -t options. */
1690
1691 truncate_name (fname, path);
1692
1693 /* Remove the directory and handle errors. */
1694
1695 rc = DosDeleteDir (fname);
1696 if (rc != 0)
1697 return set_error (rc);
1698 return 0;
1699}
1700
1701
1702/* Change the current working directory (of a disk drive). */
1703
1704int do_chdir (const char *path)
1705{
1706 char fname[512];
1707 ULONG rc;
1708
1709 /* Copy the path name to `fname', with modifications according to
1710 the -r and -t options. */
1711
1712 truncate_name (fname, path);
1713
1714 /* Change the current working directory and handle errors. */
1715
1716 rc = DosSetCurrentDir (fname);
1717 if (rc != 0)
1718 return set_error (rc);
1719 return 0;
1720}
1721
1722
1723/* Delete a file. */
1724
1725int do_delete (const char *path)
1726{
1727 char fname[512];
1728 ULONG rc;
1729
1730 /* Copy the path name to `fname', with modifications according to
1731 the -r and -t options. */
1732
1733 truncate_name (fname, path);
1734
1735 /* Delete the file and handle errors. */
1736
1737 rc = DosDelete (fname);
1738 if (rc != 0)
1739 return set_error (rc);
1740 return 0;
1741}
1742
1743
1744/* Rename or move a file. */
1745
1746int do_rename (const char *old_path, const char *new_path)
1747{
1748 char old_fname[512], new_fname[512];
1749 ULONG rc;
1750
1751 /* Copy the path names to `fname1' and `fname2', respectively, with
1752 modifications according to the -r and -t options. */
1753
1754 truncate_name (old_fname, old_path);
1755 truncate_name (new_fname, new_path);
1756
1757 /* Rename the file handle errors. */
1758
1759 rc = DosMove (old_fname, new_fname);
1760 if (rc != 0)
1761 return set_error (rc);
1762 return 0;
1763}
1764
1765
1766/* Return the index of the current disk, 0 = A:. */
1767
1768UCHAR do_getdrive (void)
1769{
1770 ULONG drv;
1771 ULONG map;
1772
1773 DosQueryCurrentDisk (&drv, &map);
1774 return (UCHAR)(drv - 1);
1775}
1776
1777
1778/* Select disk DRV (0 = A:) as current disk. */
1779
1780ULONG do_selectdisk (ULONG drv)
1781{
1782 ULONG rc;
1783
1784 rc = DosSetDefaultDisk (drv + 1);
1785 if (rc != 0)
1786 return set_error (rc);
1787 return 0;
1788}
1789
1790
1791/* Return the file attributes of PATH. */
1792
1793ULONG get_attr (const char *path, int *errnop)
1794{
1795 char fname[512];
1796 FILESTATUS3 status;
1797 ULONG rc;
1798
1799 /* This function cannot be applied to named pipes. */
1800
1801 if (check_npipe (path))
1802 {
1803 *errnop = ENOENT;
1804 return 0;
1805 }
1806
1807 /* Copy the path name to `fname', with modifications according to
1808 the -r and -t options. */
1809
1810 truncate_name (fname, path);
1811
1812 /* Get the file attributes and handle errors. */
1813
1814 rc = query_path_info (fname, FIL_STANDARD, &status, sizeof (status));
1815 if (rc != 0)
1816 {
1817 *errnop = set_error (rc);
1818 return 0;
1819 }
1820 *errnop = 0;
1821 return status.attrFile;
1822}
1823
1824
1825/* Set the file attributes of PATH. */
1826
1827ULONG set_attr (const char *path, ULONG attr)
1828{
1829 char fname[512];
1830 FILESTATUS3 status;
1831 ULONG rc;
1832
1833 /* This function cannot be applied to named pipes. */
1834
1835 if (check_npipe (path))
1836 return ENOENT;
1837
1838 /* Copy the path name to `fname', with modifications according to
1839 the -r and -t options. */
1840
1841 truncate_name (fname, path);
1842
1843 /* Get the current file attributes and handle errors. */
1844
1845 rc = query_path_info (fname, FIL_STANDARD, &status, sizeof (status));
1846 if (rc != 0)
1847 return set_error (rc);
1848
1849 /* Update the file attributes and handle errors. */
1850
1851 status.attrFile = attr;
1852 rc = DosSetPathInfo (fname, FIL_STANDARD, &status, sizeof (status), 0);
1853 if (rc != 0)
1854 return set_error (rc);
1855 return 0;
1856}
1857
1858
1859/* Get the current working directory of disk DRIVE. */
1860
1861ULONG do_getcwd (char *dst, int drive)
1862{
1863 ULONG rc, len;
1864
1865 len = 512;
1866 rc = DosQueryCurrentDir (drive, dst, &len);
1867 if (rc != 0)
1868 return set_error (rc);
1869 return 0;
1870}
1871
1872
1873static ULONG find_conv (thread_data *td, struct _find *dst);
1874static void find_close (thread_data *td);
1875
1876/* This function implements the __findfirst() system call. */
1877
1878ULONG do_find_first (const char *path, ULONG attr, struct _find *dst)
1879{
1880 char fname[512];
1881 thread_data *td;
1882 ULONG rc;
1883
1884 td = get_thread ();
1885
1886 /* Copy the path name to `fname', with modifications according to
1887 the -r and -t options. */
1888
1889 truncate_name (fname, path);
1890
1891 if (td->find_handle != HDIR_CREATE)
1892 {
1893 /* Closing the handle is not strictly required as DosFindFirst
1894 can reuse an open handle. However, this simplifies error
1895 handling below (will DosFindFirst close the handle on error
1896 if it is open?). */
1897
1898 DosFindClose (td->find_handle);
1899 td->find_handle = HDIR_CREATE;
1900 }
1901
1902 td->find_count = ((debug_flags & DEBUG_FIND1) ? 1 : FIND_COUNT);
1903 rc = DosFindFirst (fname, &td->find_handle, attr, &td->find_buf[0],
1904 sizeof (td->find_buf), &td->find_count, FIL_STANDARD);
1905 if (rc != 0)
1906 {
1907 td->find_handle = HDIR_CREATE; /* Perhaps modified by DosFindFirst */
1908 td->find_count = 0;
1909 td->find_next = NULL;
1910 return set_error (rc);
1911 }
1912 td->find_next = &td->find_buf[0];
1913 return find_conv (td, dst);
1914}
1915
1916
1917/* This function implements the __findnext() system call. */
1918
1919ULONG do_find_next (struct _find *dst)
1920{
1921 thread_data *td;
1922 ULONG rc;
1923
1924 td = get_thread ();
1925 if (td->find_count < 1)
1926 {
1927 td->find_count = ((debug_flags & DEBUG_FIND1) ? 1 : FIND_COUNT);
1928 rc = DosFindNext (td->find_handle, &td->find_buf[0],
1929 sizeof (td->find_buf), &td->find_count);
1930 if (rc != 0)
1931 {
1932 find_close (td);
1933 return set_error (rc);
1934 }
1935 td->find_next = &td->find_buf[0];
1936 }
1937
1938 return find_conv (td, dst);
1939}
1940
1941
1942/* Build a `struct _find' structure from a FILEFINDBUF3 structure and
1943 move to the next one. */
1944
1945static ULONG find_conv (thread_data *td, struct _find *dst)
1946{
1947 const FILEFINDBUF3 *src;
1948
1949 /* Check if there are any entries. Close the handle and return
1950 ENOENT if there are no entries. (Checking SRC is redundant.) */
1951
1952 src = td->find_next;
1953 if (td->find_count < 1 || src == NULL)
1954 {
1955 find_close (td);
1956 return ENOENT;
1957 }
1958
1959 /* Fill-in target object. */
1960
1961 dst->attr = src->attrFile;
1962 dst->time = XUSHORT (src->ftimeLastWrite);
1963 dst->date = XUSHORT (src->fdateLastWrite);
1964 dst->size_lo = (USHORT)src->cbFile;
1965 dst->size_hi = (USHORT)(src->cbFile >> 16);
1966 strcpy (dst->name, src->achName);
1967
1968 /* Move to the next entry. */
1969
1970 if (src->oNextEntryOffset == 0)
1971 {
1972 td->find_next = NULL;
1973 td->find_count = 0;
1974 }
1975 else
1976 {
1977 td->find_next = (FILEFINDBUF3 *)((char *)src + src->oNextEntryOffset);
1978 td->find_count -= 1;
1979 }
1980 return 0;
1981}
1982
1983
1984/* Close the directory handle. */
1985
1986static void find_close (thread_data *td)
1987{
1988 if (td->find_handle != HDIR_CREATE)
1989 {
1990 DosFindClose (td->find_handle);
1991 td->find_handle = HDIR_CREATE;
1992 }
1993 td->find_count = 0;
1994 td->find_next = NULL;
1995}
1996
1997
1998/* Get the timestamp (last modification) of HANDLE. */
1999
2000ULONG do_get_timestamp (ULONG handle, ULONG *time, ULONG *date)
2001{
2002 FILESTATUS3 status;
2003 ULONG rc;
2004
2005 if (IS_SOCKET (handle))
2006 return EINVAL;
2007
2008 rc = DosQueryFileInfo (handle, FIL_STANDARD, &status, sizeof (status));
2009 if (rc != 0)
2010 return set_error (rc);
2011 *time = XUSHORT (status.ftimeLastWrite);
2012 *date = XUSHORT (status.fdateLastWrite);
2013 return 0;
2014}
2015
2016
2017/* Set the timestamp (last modification) of HANDLE. */
2018
2019ULONG do_set_timestamp (ULONG handle, ULONG time, ULONG date)
2020{
2021 FILESTATUS3 status;
2022 ULONG rc;
2023
2024 if (IS_SOCKET (handle))
2025 return EINVAL;
2026
2027 rc = DosQueryFileInfo (handle, FIL_STANDARD, &status, sizeof (status));
2028 if (rc != 0)
2029 return set_error (rc);
2030 XUSHORT (status.ftimeLastWrite) = (USHORT)time;
2031 XUSHORT (status.fdateLastWrite) = (USHORT)date;
2032 rc = DosSetFileInfo (handle, FIL_STANDARD, &status, sizeof (status));
2033 if (rc != 0)
2034 return set_error (rc);
2035 return 0;
2036}
2037
2038
2039/* Convert a timestamp in Unix format to DOS format. */
2040
2041static void unix2dosdt (FDATE *pdate, FTIME *ptime, ULONG ut)
2042{
2043 struct my_datetime mdt;
2044
2045 unix2time (&mdt, ut);
2046 ptime->twosecs = mdt.seconds / 2;
2047 ptime->minutes = mdt.minutes;
2048 ptime->hours = mdt.hours;
2049 pdate->day = mdt.day;
2050 pdate->month = mdt.month;
2051 pdate->year = mdt.year - 1980;
2052}
2053
2054
2055/* This function implements the __utimes() system call. */
2056
2057int do_utimes (const char *path, const struct timeval *tv, int *errnop)
2058{
2059 char fname[512];
2060 FILESTATUS3 info;
2061 ULONG rc;
2062
2063 /* This function cannot be applied to named pipes. */
2064
2065 if (check_npipe (path))
2066 {
2067 *errnop = ENOENT;
2068 return -1;
2069 }
2070
2071 /* Copy the path name to `fname', with modifications according to
2072 the -r and -t options. */
2073
2074 truncate_name (fname, path);
2075
2076 /* Get the file status and handle errors. */
2077
2078 rc = query_path_info (fname, FIL_STANDARD, &info, sizeof (info));
2079 if (rc != 0)
2080 {
2081 *errnop = set_error (rc);
2082 return -1;
2083 }
2084
2085 /* Update the modification time and date. */
2086
2087 unix2dosdt (&info.fdateLastAccess, &info.ftimeLastAccess, tv[0].tv_sec);
2088 unix2dosdt (&info.fdateLastWrite, &info.ftimeLastWrite, tv[1].tv_sec);
2089
2090 /* Set the file status and handle errors. */
2091
2092 rc = DosSetPathInfo (fname, FIL_STANDARD, &info, sizeof (info), 0);
2093 if (rc != 0)
2094 {
2095 *errnop = set_error (rc);
2096 return -1;
2097 }
2098
2099 /* Function successfully completed. */
2100
2101 *errnop = 0;
2102 return 0;
2103}
2104
2105
2106/* This function implements the __stat() system call. */
2107
2108int do_stat (const char *path, struct stat *dst, int *errnop)
2109{
2110 FILESTATUS3 info;
2111 char fname[512];
2112 ULONG rc;
2113
2114 /* __stat() cannot be applied to named pipes. */
2115
2116 if (check_npipe (path))
2117 {
2118 *errnop = ENOENT;
2119 return -1;
2120 }
2121
2122 /* Copy the path name to `fname', with modifications according to
2123 the -r and -t options. */
2124
2125 truncate_name (fname, path);
2126
2127 /* Get the file status and handle errors. */
2128
2129 memset (dst, 0, sizeof (struct stat));
2130 rc = query_path_info (fname, FIL_STANDARD, &info, sizeof (info));
2131 if (rc != 0)
2132 {
2133 *errnop = set_error (rc);
2134 return -1;
2135 }
2136
2137 dst->st_attr = info.attrFile;
2138 dst->st_reserved = 0;
2139
2140 dst->st_mtime = packed2unix (info.fdateLastWrite, info.ftimeLastWrite);
2141 if (XUSHORT (info.fdateCreation) == 0 && XUSHORT (info.ftimeCreation) == 0)
2142 dst->st_ctime = dst->st_mtime;
2143 else
2144 dst->st_ctime = packed2unix (info.fdateCreation, info.ftimeCreation);
2145 if (XUSHORT (info.fdateLastAccess) == 0
2146 && XUSHORT (info.ftimeLastAccess) == 0)
2147 dst->st_atime = dst->st_mtime;
2148 else
2149 dst->st_atime = packed2unix (info.fdateLastAccess, info.ftimeLastAccess);
2150
2151 if (info.attrFile & 0x10)
2152 {
2153 /* It's a directory. */
2154 dst->st_size = 0;
2155 dst->st_mode = S_IFDIR | MAKEPERM (S_IREAD|S_IWRITE|S_IEXEC);
2156 }
2157 else
2158 {
2159 /* It's a regular file. */
2160 dst->st_size = info.cbFile;
2161 if (info.attrFile & 0x01)
2162 dst->st_mode = S_IFREG | MAKEPERM (S_IREAD);
2163 else
2164 dst->st_mode = S_IFREG | MAKEPERM (S_IREAD|S_IWRITE);
2165 }
2166 dst->st_dev = 0;
2167 dst->st_uid = 0; /* root */
2168 dst->st_gid = 0; /* root */
2169 dst->st_ino = ino_number;
2170 if (++ino_number == 0)
2171 ino_number = 1;
2172 dst->st_rdev = dst->st_dev;
2173 dst->st_nlink = 1;
2174 *errnop = 0;
2175 return 0;
2176}
2177
2178
2179/* This function implements the __fstat() system call. */
2180
2181int do_fstat (ULONG handle, struct stat *dst, int *errnop)
2182{
2183 FILESTATUS3 info;
2184 ULONG rc, flags, htype, state;
2185
2186 if (IS_SOCKET (handle))
2187 return tcpip_fstat (handle, dst, errnop);
2188
2189 memset (dst, 0, sizeof (struct stat));
2190 rc = DosQueryHType (handle, &htype, &flags);
2191 if (rc != 0)
2192 {
2193 *errnop = set_error (rc);
2194 return -1;
2195 }
2196
2197 switch (htype & 0xff)
2198 {
2199 case HANDTYPE_DEVICE: /* Character device */
2200 case HANDTYPE_PIPE: /* Pipe */
2201 rc = DosQueryFHState (handle, &state);
2202 if (rc != 0)
2203 {
2204 *errnop = set_error (rc);
2205 return -1;
2206 }
2207 if ((state & 7) == OPEN_ACCESS_READONLY)
2208 dst->st_mode = MAKEPERM (S_IREAD);
2209 else
2210 dst->st_mode = MAKEPERM (S_IREAD|S_IWRITE);
2211 if ((htype & 0xff) == HANDTYPE_DEVICE)
2212 dst->st_mode |= S_IFCHR;
2213 else
2214 dst->st_mode |= S_IFIFO;
2215 dst->st_size = 0;
2216 break;
2217
2218 default:
2219 rc = DosQueryFileInfo (handle, FIL_STANDARD, &info, sizeof (info));
2220 if (rc != 0)
2221 {
2222 *errnop = set_error (rc);
2223 return -1;
2224 }
2225 dst->st_attr = info.attrFile;
2226 dst->st_reserved = 0;
2227
2228 dst->st_mtime = packed2unix (info.fdateLastWrite, info.ftimeLastWrite);
2229 if (XUSHORT (info.fdateCreation) == 0
2230 && XUSHORT (info.ftimeCreation) == 0)
2231 dst->st_ctime = dst->st_mtime;
2232 else
2233 dst->st_ctime = packed2unix (info.fdateCreation, info.ftimeCreation);
2234 if (XUSHORT (info.fdateLastAccess) == 0
2235 && XUSHORT (info.ftimeLastAccess) == 0)
2236 dst->st_atime = dst->st_mtime;
2237 else
2238 dst->st_atime = packed2unix (info.fdateLastAccess,
2239 info.ftimeLastAccess);
2240
2241 dst->st_size = info.cbFile;
2242 if (info.attrFile & 0x01)
2243 dst->st_mode = S_IFREG | MAKEPERM (S_IREAD);
2244 else
2245 dst->st_mode = S_IFREG | MAKEPERM (S_IREAD|S_IWRITE);
2246 break;
2247 }
2248 dst->st_dev = 0;
2249 dst->st_uid = 0; /* root */
2250 dst->st_gid = 0; /* root */
2251 dst->st_ino = ino_number;
2252 if (++ino_number == 0)
2253 ino_number = 1;
2254 dst->st_rdev = dst->st_dev;
2255 dst->st_nlink = 1;
2256 *errnop = 0;
2257 return 0;
2258}
2259
2260
2261/* Send the character CHR to standard output. */
2262
2263void conout (UCHAR chr)
2264{
2265 ULONG written;
2266
2267 DosWrite (1, &chr, 1, &written);
2268}
2269
2270
2271/* This function implements the __imphandle() system call: Import an
2272 OS/2 handle. */
2273
2274ULONG do_imphandle (ULONG handle, int *errnop)
2275{
2276 ULONG nh;
2277 int e;
2278
2279 e = reloc_handle (handle, &nh, TRUE);
2280 if (e != 0)
2281 {
2282 *errnop = e;
2283 return (ULONG)-1;
2284 }
2285 *errnop = 0;
2286 return nh;
2287}
2288
2289
2290/* Store the name of the device HANDLE to DST (up to DST_SIZE bytes,
2291 including the terminating null character). Store errno to *ERRNOP.
2292 Return 0 on success, -1 on error. */
2293
2294int do_ttyname (char *dst, ULONG dst_size, ULONG handle, int *errnop)
2295{
2296 if (handle >= handle_count || !(handle_flags[handle] & HF_OPEN))
2297 {
2298 *errnop = EBADF;
2299 return -1;
2300 }
2301 if (handle_flags[handle] & HF_CON)
2302 return set_ttyname (dst, dst_size, "/dev/con", errnop);
2303 else if (handle_flags[handle] & HF_NUL)
2304 return set_ttyname (dst, dst_size, "/dev/nul", errnop);
2305 else if (handle_flags[handle] & HF_CLK)
2306 return set_ttyname (dst, dst_size, "/dev/clock$", errnop);
2307 else if (handle_flags[handle] & HF_XF86SUP)
2308 return xf86sup_ttyname (dst, dst_size, handle, errnop);
2309 else
2310 {
2311 *errnop = ENODEV;
2312 return -1;
2313 }
2314}
2315
2316
2317int set_ttyname (char *dst, ULONG dst_size, const char *name, int *errnop)
2318{
2319 if (strlen (name) >= dst_size)
2320 {
2321 *errnop = ENAMETOOLONG;
2322 return -1;
2323 }
2324 strcpy (dst, name);
2325 *errnop = 0;
2326 return 0;
2327}
Note: See TracBrowser for help on using the repository browser.