source: trunk/src/kash/shfile.c@ 2424

Last change on this file since 2424 was 2424, checked in by bird, 15 years ago

kash: Some S-bahn optimizations.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 55.7 KB
Line 
1/* $Id: shfile.c 2424 2010-10-18 08:52:03Z bird $ */
2/** @file
3 *
4 * File management.
5 *
6 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
7 *
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "shfile.h"
31#include "shinstance.h" /* TRACE2 */
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
35#include <assert.h>
36
37#if K_OS == K_OS_WINDOWS
38# include <limits.h>
39# ifndef PIPE_BUF
40# define PIPE_BUF 512
41# endif
42# include <ntstatus.h>
43# define WIN32_NO_STATUS
44# include <Windows.h>
45# if !defined(_WIN32_WINNT)
46# define _WIN32_WINNT 0x0502 /* Windows Server 2003 */
47# endif
48# include <winternl.h> //NTSTATUS
49#else
50# include <unistd.h>
51# include <fcntl.h>
52# include <dirent.h>
53#endif
54
55
56/*******************************************************************************
57* Defined Constants And Macros *
58*******************************************************************************/
59/** @def SHFILE_IN_USE
60 * Whether the file descriptor table stuff is actually in use or not.
61 */
62#if K_OS == K_OS_WINDOWS \
63 || K_OS == K_OS_OPENBSD /* because of ugly pthread library pipe hacks */ \
64 || !defined(SH_FORKED_MODE)
65# define SHFILE_IN_USE
66#endif
67# define SHFILE_IN_USE
68/** The max file table size. */
69#define SHFILE_MAX 1024
70/** The file table growth rate. */
71#define SHFILE_GROW 64
72/** The min native unix file descriptor. */
73#define SHFILE_UNIX_MIN_FD 32
74/** The path buffer size we use. */
75#define SHFILE_MAX_PATH 4096
76
77/** Set errno and return. Doing a trace in debug build. */
78#define RETURN_ERROR(rc, err, msg) \
79 do { \
80 TRACE2((NULL, "%s: " ## msg ## " - returning %d / %d\n", __FUNCTION__, (rc), (err))); \
81 errno = (err); \
82 return (rc); \
83 } while (0)
84
85#if K_OS == K_OS_WINDOWS
86 /* See msdos.h for description. */
87# define FOPEN 0x01
88# define FEOFLAG 0x02
89# define FCRLF 0x04
90# define FPIPE 0x08
91# define FNOINHERIT 0x10
92# define FAPPEND 0x20
93# define FDEV 0x40
94# define FTEXT 0x80
95
96# define MY_ObjectBasicInformation 0
97# define MY_FileNamesInformation 12
98
99typedef struct
100{
101 ULONG Attributes;
102 ACCESS_MASK GrantedAccess;
103 ULONG HandleCount;
104 ULONG PointerCount;
105 ULONG PagedPoolUsage;
106 ULONG NonPagedPoolUsage;
107 ULONG Reserved[3];
108 ULONG NameInformationLength;
109 ULONG TypeInformationLength;
110 ULONG SecurityDescriptorLength;
111 LARGE_INTEGER CreateTime;
112} MY_OBJECT_BASIC_INFORMATION;
113
114#if 0
115typedef struct
116{
117 union
118 {
119 LONG Status;
120 PVOID Pointer;
121 };
122 ULONG_PTR Information;
123} MY_IO_STATUS_BLOCK;
124#else
125typedef IO_STATUS_BLOCK MY_IO_STATUS_BLOCK;
126#endif
127typedef MY_IO_STATUS_BLOCK *PMY_IO_STATUS_BLOCK;
128
129typedef struct
130{
131 ULONG NextEntryOffset;
132 ULONG FileIndex;
133 ULONG FileNameLength;
134 WCHAR FileName[1];
135} MY_FILE_NAMES_INFORMATION, *PMY_FILE_NAMES_INFORMATION;
136
137typedef NTSTATUS (NTAPI * PFN_NtQueryObject)(HANDLE, int, void *, size_t, size_t *);
138typedef NTSTATUS (NTAPI * PFN_NtQueryDirectoryFile)(HANDLE, HANDLE, void *, void *, PMY_IO_STATUS_BLOCK, void *,
139 ULONG, int, int, PUNICODE_STRING, int);
140typedef NTSTATUS (NTAPI * PFN_RtlUnicodeStringToAnsiString)(PANSI_STRING, PCUNICODE_STRING, int);
141
142
143#endif /* K_OS_WINDOWS */
144
145
146/*******************************************************************************
147* Global Variables *
148*******************************************************************************/
149#if K_OS == K_OS_WINDOWS
150static int g_shfile_globals_initialized = 0;
151static PFN_NtQueryObject g_pfnNtQueryObject = NULL;
152static PFN_NtQueryDirectoryFile g_pfnNtQueryDirectoryFile = NULL;
153static PFN_RtlUnicodeStringToAnsiString g_pfnRtlUnicodeStringToAnsiString = NULL;
154#endif /* K_OS_WINDOWS */
155
156
157#ifdef SHFILE_IN_USE
158
159/**
160 * Close the specified native handle.
161 *
162 * @param native The native file handle.
163 * @param flags The flags in case they might come in handy later.
164 */
165static void shfile_native_close(intptr_t native, unsigned flags)
166{
167#if K_OS == K_OS_WINDOWS
168 BOOL fRc = CloseHandle((HANDLE)native);
169 assert(fRc); (void)fRc;
170#else
171 int s = errno;
172 close(native);
173 errno = s;
174#endif
175 (void)flags;
176}
177
178/**
179 * Grows the descriptor table, making sure that it can hold @a fdMin,
180 *
181 * @returns The max(fdMin, fdFirstNew) on success, -1 on failure.
182 * @param pfdtab The table to grow.
183 * @param fdMin Grow to include this index.
184 */
185static int shfile_grow_tab_locked(shfdtab *pfdtab, int fdMin)
186{
187 /*
188 * Grow the descriptor table.
189 */
190 int fdRet = -1;
191 shfile *new_tab;
192 int new_size = pfdtab->size + SHFILE_GROW;
193 while (new_size < fdMin)
194 new_size += SHFILE_GROW;
195 new_tab = sh_realloc(shthread_get_shell(), pfdtab->tab, new_size * sizeof(shfile));
196 if (new_tab)
197 {
198 int i;
199 for (i = pfdtab->size; i < new_size; i++)
200 {
201 new_tab[i].fd = -1;
202 new_tab[i].oflags = 0;
203 new_tab[i].shflags = 0;
204 new_tab[i].native = -1;
205 }
206
207 fdRet = pfdtab->size;
208 if (fdRet < fdMin)
209 fdRet = fdMin;
210
211 pfdtab->tab = new_tab;
212 pfdtab->size = new_size;
213 }
214
215 return fdRet;
216}
217
218/**
219 * Inserts the file into the descriptor table.
220 *
221 * If we're out of memory and cannot extend the table, we'll close the
222 * file, set errno to EMFILE and return -1.
223 *
224 * @returns The file descriptor number. -1 and errno on failure.
225 * @param pfdtab The file descriptor table.
226 * @param native The native file handle.
227 * @param oflags The flags the it was opened/created with.
228 * @param shflags The shell file flags.
229 * @param fdMin The minimum file descriptor number, pass -1 if any is ok.
230 * @param who Who we're doing this for (for logging purposes).
231 */
232static int shfile_insert(shfdtab *pfdtab, intptr_t native, unsigned oflags, unsigned shflags, int fdMin, const char *who)
233{
234 shmtxtmp tmp;
235 int fd;
236 int i;
237
238 /*
239 * Fend of bad stuff.
240 */
241 if (fdMin >= SHFILE_MAX)
242 {
243 errno = EMFILE;
244 return -1;
245 }
246# if K_OS != K_OS_WINDOWS
247 if (fcntl((int)native, F_SETFD, fcntl((int)native, F_GETFD, 0) | FD_CLOEXEC) == -1)
248 {
249 int e = errno;
250 close((int)native);
251 errno = e;
252 return -1;
253 }
254# endif
255
256 shmtx_enter(&pfdtab->mtx, &tmp);
257
258 /*
259 * Search for a fitting unused location.
260 */
261 fd = -1;
262 for (i = fdMin >= 0 ? fdMin : 0; (unsigned)i < pfdtab->size; i++)
263 if (pfdtab->tab[i].fd == -1)
264 {
265 fd = i;
266 break;
267 }
268 if (fd == -1)
269 fd = shfile_grow_tab_locked(pfdtab, fdMin);
270
271 /*
272 * Fill in the entry if we've found one.
273 */
274 if (fd != -1)
275 {
276 pfdtab->tab[fd].fd = fd;
277 pfdtab->tab[fd].oflags = oflags;
278 pfdtab->tab[fd].shflags = shflags;
279 pfdtab->tab[fd].native = native;
280 }
281 else
282 shfile_native_close(native, oflags);
283
284 shmtx_leave(&pfdtab->mtx, &tmp);
285
286 if (fd == -1)
287 errno = EMFILE;
288 (void)who;
289 return fd;
290}
291
292#if K_OS != K_OS_WINDOWS
293/**
294 * Makes a copy of the native file, closes the original, and inserts the copy
295 * into the descriptor table.
296 *
297 * If we're out of memory and cannot extend the table, we'll close the
298 * file, set errno to EMFILE and return -1.
299 *
300 * @returns The file descriptor number. -1 and errno on failure.
301 * @param pfdtab The file descriptor table.
302 * @param pnative The native file handle on input, -1 on output.
303 * @param oflags The flags the it was opened/created with.
304 * @param shflags The shell file flags.
305 * @param fdMin The minimum file descriptor number, pass -1 if any is ok.
306 * @param who Who we're doing this for (for logging purposes).
307 */
308static int shfile_copy_insert_and_close(shfdtab *pfdtab, int *pnative, unsigned oflags, unsigned shflags, int fdMin, const char *who)
309{
310 int fd = -1;
311 int s = errno;
312 int native_copy = fcntl(*pnative, F_DUPFD, SHFILE_UNIX_MIN_FD);
313 close(*pnative);
314 *pnative = -1;
315 errno = s;
316
317 if (native_copy != -1)
318 fd = shfile_insert(pfdtab, native_copy, oflags, shflags, fdMin, who);
319 return fd;
320}
321#endif /* !K_OS_WINDOWS */
322
323/**
324 * Gets a file descriptor and lock the file descriptor table.
325 *
326 * @returns Pointer to the file and table ownership on success,
327 * NULL and errno set to EBADF on failure.
328 * @param pfdtab The file descriptor table.
329 * @param fd The file descriptor number.
330 * @param ptmp See shmtx_enter.
331 */
332static shfile *shfile_get(shfdtab *pfdtab, int fd, shmtxtmp *ptmp)
333{
334 shfile *file = NULL;
335 if ( fd >= 0
336 && (unsigned)fd < pfdtab->size)
337 {
338 shmtx_enter(&pfdtab->mtx, ptmp);
339 if ((unsigned)fd < pfdtab->size
340 && pfdtab->tab[fd].fd != -1)
341 file = &pfdtab->tab[fd];
342 else
343 shmtx_leave(&pfdtab->mtx, ptmp);
344 }
345 if (!file)
346 errno = EBADF;
347 return file;
348}
349
350/**
351 * Puts back a file descriptor and releases the table ownership.
352 *
353 * @param pfdtab The file descriptor table.
354 * @param file The file.
355 * @param ptmp See shmtx_leave.
356 */
357static void shfile_put(shfdtab *pfdtab, shfile *file, shmtxtmp *ptmp)
358{
359 shmtx_leave(&pfdtab->mtx, ptmp);
360 assert(file);
361 (void)file;
362}
363
364/**
365 * Constructs a path from the current directory and the passed in path.
366 *
367 * @returns 0 on success, -1 and errno set to ENAMETOOLONG or EINVAL on failure.
368 *
369 * @param pfdtab The file descriptor table
370 * @param path The path the caller supplied.
371 * @param buf Where to put the path. This is assumed to be SHFILE_MAX_PATH
372 * chars in size.
373 */
374int shfile_make_path(shfdtab *pfdtab, const char *path, char *buf)
375{
376 size_t path_len = strlen(path);
377 if (path_len == 0)
378 {
379 errno = EINVAL;
380 return -1;
381 }
382 if (path_len >= SHFILE_MAX_PATH)
383 {
384 errno = ENAMETOOLONG;
385 return -1;
386 }
387 if ( *path == '/'
388#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
389 || *path == '\\'
390 || ( *path
391 && path[1] == ':'
392 && ( (*path >= 'A' && *path <= 'Z')
393 || (*path >= 'a' && *path <= 'z')))
394#endif
395 )
396 {
397 memcpy(buf, path, path_len + 1);
398 }
399 else
400 {
401 size_t cwd_len;
402 shmtxtmp tmp;
403
404 shmtx_enter(&pfdtab->mtx, &tmp);
405
406 cwd_len = strlen(pfdtab->cwd);
407 memcpy(buf, pfdtab->cwd, cwd_len);
408
409 shmtx_leave(&pfdtab->mtx, &tmp);
410
411 if (cwd_len + path_len + 1 >= SHFILE_MAX_PATH)
412 {
413 errno = ENAMETOOLONG;
414 return -1;
415 }
416 if ( !cwd_len
417 || buf[cwd_len - 1] != '/')
418 buf[cwd_len++] = '/';
419 memcpy(buf + cwd_len, path, path_len + 1);
420 }
421
422#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
423 if (!strcmp(buf, "/dev/null"))
424 strcpy(buf, "NUL");
425#endif
426 return 0;
427}
428
429# if K_OS == K_OS_WINDOWS
430
431/**
432 * Converts a DOS(/Windows) error code to errno,
433 * assigning it to errno.
434 *
435 * @returns -1
436 * @param err The DOS error.
437 */
438static int shfile_dos2errno(int err)
439{
440 switch (err)
441 {
442 case ERROR_BAD_ENVIRONMENT: errno = E2BIG; break;
443 case ERROR_ACCESS_DENIED: errno = EACCES; break;
444 case ERROR_CURRENT_DIRECTORY: errno = EACCES; break;
445 case ERROR_LOCK_VIOLATION: errno = EACCES; break;
446 case ERROR_NETWORK_ACCESS_DENIED: errno = EACCES; break;
447 case ERROR_CANNOT_MAKE: errno = EACCES; break;
448 case ERROR_FAIL_I24: errno = EACCES; break;
449 case ERROR_DRIVE_LOCKED: errno = EACCES; break;
450 case ERROR_SEEK_ON_DEVICE: errno = EACCES; break;
451 case ERROR_NOT_LOCKED: errno = EACCES; break;
452 case ERROR_LOCK_FAILED: errno = EACCES; break;
453 case ERROR_NO_PROC_SLOTS: errno = EAGAIN; break;
454 case ERROR_MAX_THRDS_REACHED: errno = EAGAIN; break;
455 case ERROR_NESTING_NOT_ALLOWED: errno = EAGAIN; break;
456 case ERROR_INVALID_HANDLE: errno = EBADF; break;
457 case ERROR_INVALID_TARGET_HANDLE: errno = EBADF; break;
458 case ERROR_DIRECT_ACCESS_HANDLE: errno = EBADF; break;
459 case ERROR_WAIT_NO_CHILDREN: errno = ECHILD; break;
460 case ERROR_CHILD_NOT_COMPLETE: errno = ECHILD; break;
461 case ERROR_FILE_EXISTS: errno = EEXIST; break;
462 case ERROR_ALREADY_EXISTS: errno = EEXIST; break;
463 case ERROR_INVALID_FUNCTION: errno = EINVAL; break;
464 case ERROR_INVALID_ACCESS: errno = EINVAL; break;
465 case ERROR_INVALID_DATA: errno = EINVAL; break;
466 case ERROR_INVALID_PARAMETER: errno = EINVAL; break;
467 case ERROR_NEGATIVE_SEEK: errno = EINVAL; break;
468 case ERROR_TOO_MANY_OPEN_FILES: errno = EMFILE; break;
469 case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
470 case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
471 case ERROR_INVALID_DRIVE: errno = ENOENT; break;
472 case ERROR_NO_MORE_FILES: errno = ENOENT; break;
473 case ERROR_BAD_NETPATH: errno = ENOENT; break;
474 case ERROR_BAD_NET_NAME: errno = ENOENT; break;
475 case ERROR_BAD_PATHNAME: errno = ENOENT; break;
476 case ERROR_FILENAME_EXCED_RANGE: errno = ENOENT; break;
477 case ERROR_BAD_FORMAT: errno = ENOEXEC; break;
478 case ERROR_ARENA_TRASHED: errno = ENOMEM; break;
479 case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break;
480 case ERROR_INVALID_BLOCK: errno = ENOMEM; break;
481 case ERROR_NOT_ENOUGH_QUOTA: errno = ENOMEM; break;
482 case ERROR_DISK_FULL: errno = ENOSPC; break;
483 case ERROR_DIR_NOT_EMPTY: errno = ENOTEMPTY; break;
484 case ERROR_BROKEN_PIPE: errno = EPIPE; break;
485 case ERROR_NOT_SAME_DEVICE: errno = EXDEV; break;
486 default: errno = EINVAL; break;
487 }
488 return -1;
489}
490
491/**
492 * Converts an NT status code to errno,
493 * assigning it to errno.
494 *
495 * @returns -1
496 * @param rcNt The NT status code.
497 */
498static int shfile_nt2errno(NTSTATUS rcNt)
499{
500 switch (rcNt)
501 {
502 default: errno = EINVAL; break;
503 }
504 return -1;
505}
506
507DWORD shfile_query_handle_access_mask(HANDLE h, PACCESS_MASK pMask)
508{
509 MY_OBJECT_BASIC_INFORMATION BasicInfo;
510 NTSTATUS rcNt;
511
512 if (!g_pfnNtQueryObject)
513 return ERROR_NOT_SUPPORTED;
514
515 rcNt = g_pfnNtQueryObject(h, MY_ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
516 if (rcNt >= 0)
517 {
518 *pMask = BasicInfo.GrantedAccess;
519 return NO_ERROR;
520 }
521 if (rcNt != STATUS_INVALID_HANDLE)
522 return ERROR_GEN_FAILURE;
523 return ERROR_INVALID_HANDLE;
524}
525
526# endif /* K_OS == K_OS_WINDOWS */
527
528#endif /* SHFILE_IN_USE */
529
530/**
531 * Initializes the global variables in this file.
532 */
533static void shfile_init_globals(void)
534{
535#if K_OS == K_OS_WINDOWS
536 if (!g_shfile_globals_initialized)
537 {
538 HMODULE hNtDll = GetModuleHandle("NTDLL");
539 g_pfnNtQueryObject = (PFN_NtQueryObject) GetProcAddress(hNtDll, "NtQueryObject");
540 g_pfnNtQueryDirectoryFile = (PFN_NtQueryDirectoryFile)GetProcAddress(hNtDll, "NtQueryDirectoryFile");
541 g_pfnRtlUnicodeStringToAnsiString = (PFN_RtlUnicodeStringToAnsiString)GetProcAddress(hNtDll, "RtlUnicodeStringToAnsiString");
542 if ( !g_pfnRtlUnicodeStringToAnsiString
543 || !g_pfnNtQueryDirectoryFile)
544 {
545 /* fatal error */
546 }
547 g_shfile_globals_initialized = 1;
548 }
549#endif
550}
551
552/**
553 * Initializes a file descriptor table.
554 *
555 * @returns 0 on success, -1 and errno on failure.
556 * @param pfdtab The table to initialize.
557 * @param inherit File descriptor table to inherit from. If not specified
558 * we will inherit from the current process as it were.
559 */
560int shfile_init(shfdtab *pfdtab, shfdtab *inherit)
561{
562 int rc;
563
564 shfile_init_globals();
565
566 pfdtab->cwd = NULL;
567 pfdtab->size = 0;
568 pfdtab->tab = NULL;
569 rc = shmtx_init(&pfdtab->mtx);
570 if (!rc)
571 {
572#ifdef SHFILE_IN_USE
573 char buf[SHFILE_MAX_PATH];
574 if (getcwd(buf, sizeof(buf)))
575 {
576 pfdtab->cwd = sh_strdup(NULL, buf);
577 if (!inherit)
578 {
579# if K_OS == K_OS_WINDOWS
580 static const struct
581 {
582 DWORD dwStdHandle;
583 unsigned fFlags;
584 } aStdHandles[3] =
585 {
586 { STD_INPUT_HANDLE, _O_RDONLY },
587 { STD_OUTPUT_HANDLE, _O_WRONLY },
588 { STD_ERROR_HANDLE, _O_WRONLY }
589 };
590 int i;
591 STARTUPINFO Info;
592 ACCESS_MASK Mask;
593 DWORD dwErr;
594
595 rc = 0;
596
597 /* Try pick up the Visual C++ CRT file descriptor info. */
598 __try {
599 GetStartupInfo(&Info);
600 } __except (EXCEPTION_EXECUTE_HANDLER) {
601 memset(&Info, 0, sizeof(Info));
602 }
603
604 if ( Info.cbReserved2 > sizeof(int)
605 && (uintptr_t)Info.lpReserved2 >= 0x1000
606 && (i = *(int *)Info.lpReserved2) >= 1
607 && i <= 2048
608 && ( Info.cbReserved2 == i * 5 + 4
609 //|| Info.cbReserved2 == i * 5 + 1 - check the cygwin sources.
610 || Info.cbReserved2 == i * 9 + 4))
611 {
612 uint8_t *paf = (uint8_t *)Info.lpReserved2 + sizeof(int);
613 int dwPerH = 1 + (Info.cbReserved2 == i * 9 + 4);
614 DWORD *ph = (DWORD *)(paf + i) + dwPerH * i;
615 HANDLE aStdHandles2[3];
616 int j;
617
618 //if (Info.cbReserved2 == i * 5 + 1) - check the cygwin sources.
619 // i--;
620
621 for (j = 0; j < 3; j++)
622 aStdHandles2[j] = GetStdHandle(aStdHandles[j].dwStdHandle);
623
624 while (i-- > 0)
625 {
626 ph -= dwPerH;
627
628 if ( (paf[i] & (FOPEN | FNOINHERIT)) == FOPEN
629 && *ph != (uint32_t)INVALID_HANDLE_VALUE)
630 {
631 HANDLE h = (HANDLE)(intptr_t)*ph;
632 int fd2;
633 int fFlags;
634 int fFlags2;
635
636 if ( h == aStdHandles2[j = 0]
637 || h == aStdHandles2[j = 1]
638 || h == aStdHandles2[j = 2])
639 fFlags = aStdHandles[j].fFlags;
640 else
641 {
642 dwErr = shfile_query_handle_access_mask(h, &Mask);
643 if (dwErr == ERROR_INVALID_HANDLE)
644 continue;
645 else if (dwErr == NO_ERROR)
646 {
647 fFlags = 0;
648 if ( (Mask & (GENERIC_READ | FILE_READ_DATA))
649 && (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)))
650 fFlags |= O_RDWR;
651 else if (Mask & (GENERIC_READ | FILE_READ_DATA))
652 fFlags |= O_RDONLY;
653 else if (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA))
654 fFlags |= O_WRONLY;
655 else
656 fFlags |= O_RDWR;
657 if ((Mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) == FILE_APPEND_DATA)
658 fFlags |= O_APPEND;
659 }
660 else
661 fFlags = O_RDWR;
662 }
663
664 if (paf[i] & FPIPE)
665 fFlags2 = SHFILE_FLAGS_PIPE;
666 else if (paf[i] & FDEV)
667 fFlags2 = SHFILE_FLAGS_TTY;
668 else
669 fFlags2 = 0;
670
671 fd2 = shfile_insert(pfdtab, (intptr_t)h, fFlags, fFlags2, i, "shtab_init");
672 assert(fd2 == i); (void)fd2;
673 if (fd2 != i)
674 rc = -1;
675 }
676 }
677 }
678
679 /* Check the three standard handles. */
680 for (i = 0; i < 3; i++)
681 if ( (unsigned)i >= pfdtab->size
682 || pfdtab->tab[i].fd == -1)
683 {
684 HANDLE hFile = GetStdHandle(aStdHandles[i].dwStdHandle);
685 if (hFile != INVALID_HANDLE_VALUE)
686 {
687 DWORD dwType = GetFileType(hFile);
688 unsigned fFlags = aStdHandles[i].fFlags;
689 unsigned fFlags2;
690 int fd2;
691 if (dwType == FILE_TYPE_CHAR)
692 fFlags2 = SHFILE_FLAGS_TTY;
693 else if (dwType == FILE_TYPE_PIPE)
694 fFlags2 = SHFILE_FLAGS_PIPE;
695 else
696 fFlags2 = SHFILE_FLAGS_FILE;
697 fd2 = shfile_insert(pfdtab, (intptr_t)hFile, fFlags, fFlags2, i, "shtab_init");
698 assert(fd2 == i); (void)fd2;
699 if (fd2 != i)
700 rc = -1;
701 }
702 }
703# else
704 /*
705 * Annoying...
706 */
707 int fd;
708
709 for (fd = 0; fd < 10; fd++)
710 {
711 int oflags = fcntl(fd, F_GETFL, 0);
712 if (oflags != -1)
713 {
714 int cox = fcntl(fd, F_GETFD, 0);
715 struct stat st;
716 if ( cox != -1
717 && fstat(fd, &st) != -1)
718 {
719 int native;
720 int fd2;
721 int fFlags2 = 0;
722 if (cox & FD_CLOEXEC)
723 fFlags2 |= SHFILE_FLAGS_CLOSE_ON_EXEC;
724 if (S_ISREG(st.st_mode))
725 fFlags2 |= SHFILE_FLAGS_FILE;
726 else if (S_ISDIR(st.st_mode))
727 fFlags2 |= SHFILE_FLAGS_DIR;
728 else if (S_ISCHR(st.st_mode))
729 fFlags2 |= SHFILE_FLAGS_TTY;
730 else if (S_ISFIFO(st.st_mode))
731 fFlags2 |= SHFILE_FLAGS_PIPE;
732 else
733 fFlags2 |= SHFILE_FLAGS_TTY;
734
735 native = fcntl(fd, F_DUPFD, SHFILE_UNIX_MIN_FD);
736 if (native == -1)
737 native = fd;
738 fd2 = shfile_insert(pfdtab, native, oflags, fFlags2, fd, "shtab_init");
739 assert(fd2 == fd); (void)fd2;
740 if (fd2 != fd)
741 rc = -1;
742 if (native != fd)
743 close(fd);
744 }
745 }
746 }
747
748# endif
749 }
750 else
751 {
752 /** @todo */
753 errno = ENOSYS;
754 rc = -1;
755 }
756 }
757 else
758 rc = -1;
759#endif
760 }
761 return rc;
762}
763
764#if K_OS == K_OS_WINDOWS && defined(SHFILE_IN_USE)
765
766/**
767 * Helper for shfork.
768 *
769 * @param pfdtab The file descriptor table.
770 * @param set Whether to make all handles inheritable (1) or
771 * to restore them to the rigth state (0).
772 * @param hndls Where to store the three standard handles.
773 */
774void shfile_fork_win(shfdtab *pfdtab, int set, intptr_t *hndls)
775{
776 shmtxtmp tmp;
777 unsigned i;
778 DWORD fFlag = set ? HANDLE_FLAG_INHERIT : 0;
779
780 shmtx_enter(&pfdtab->mtx, &tmp);
781 TRACE2((NULL, "shfile_fork_win: set=%d\n", set));
782
783 i = pfdtab->size;
784 while (i-- > 0)
785 {
786 if (pfdtab->tab[i].fd == i)
787 {
788 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
789 if (set)
790 TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n",
791 i, hFile, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags));
792 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, fFlag))
793 {
794 DWORD err = GetLastError();
795 assert(0);
796 }
797 }
798 }
799
800 if (hndls)
801 {
802 for (i = 0; i < 3; i++)
803 {
804 if ( pfdtab->size > i
805 && pfdtab->tab[i].fd == i)
806 hndls[i] = pfdtab->tab[i].native;
807 else
808 hndls[i] = (intptr_t)INVALID_HANDLE_VALUE;
809 TRACE2((NULL, "shfile_fork_win: i=%d size=%d fd=%d native=%d hndls[%d]=%p\n",
810 i, pfdtab->size, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, hndls[i]));
811 }
812 }
813
814 shmtx_leave(&pfdtab->mtx, &tmp);
815}
816
817/**
818 * Helper for sh_execve.
819 *
820 * This is called before and after CreateProcess. On the first call it
821 * will mark the non-close-on-exec handles as inheritable and produce
822 * the startup info for the CRT. On the second call, after CreateProcess,
823 * it will restore the handle inheritability properties.
824 *
825 * @returns Pointer to CRT data if prepare is 1, NULL if prepare is 0.
826 * @param pfdtab The file descriptor table.
827 * @param prepare Which call, 1 if before and 0 if after.
828 * @param sizep Where to store the size of the returned data.
829 * @param hndls Where to store the three standard handles.
830 */
831void *shfile_exec_win(shfdtab *pfdtab, int prepare, unsigned short *sizep, intptr_t *hndls)
832{
833 void *pvRet;
834 shmtxtmp tmp;
835 unsigned count;
836 unsigned i;
837
838 shmtx_enter(&pfdtab->mtx, &tmp);
839 TRACE2((NULL, "shfile_exec_win: prepare=%p\n", prepare));
840
841 count = pfdtab->size < (0x10000-4) / (1 + sizeof(HANDLE))
842 ? pfdtab->size
843 : (0x10000-4) / (1 + sizeof(HANDLE));
844 while (count > 3 && pfdtab->tab[count - 1].fd == -1)
845 count--;
846
847 if (prepare)
848 {
849 size_t cbData = sizeof(int) + count * (1 + sizeof(HANDLE));
850 uint8_t *pbData = sh_malloc(shthread_get_shell(), cbData);
851 uint8_t *paf = pbData + sizeof(int);
852 HANDLE *pah = (HANDLE *)(paf + count);
853
854 *(int *)pbData = count;
855
856 i = count;
857 while (i-- > 0)
858 {
859 if ( pfdtab->tab[i].fd == i
860 && !(pfdtab->tab[i].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC))
861 {
862 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
863 TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n",
864 i, hFile, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags));
865
866 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
867 {
868 DWORD err = GetLastError();
869 assert(0);
870 }
871 paf[i] = FOPEN;
872 if (pfdtab->tab[i].oflags & _O_APPEND)
873 paf[i] |= FAPPEND;
874 if (pfdtab->tab[i].oflags & _O_TEXT)
875 paf[i] |= FTEXT;
876 switch (pfdtab->tab[i].shflags & SHFILE_FLAGS_TYPE_MASK)
877 {
878 case SHFILE_FLAGS_TTY: paf[i] |= FDEV; break;
879 case SHFILE_FLAGS_PIPE: paf[i] |= FPIPE; break;
880 }
881 pah[i] = hFile;
882 }
883 else
884 {
885 paf[i] = 0;
886 pah[i] = INVALID_HANDLE_VALUE;
887 }
888 }
889
890 for (i = 0; i < 3; i++)
891 {
892 if ( i < count
893 && pfdtab->tab[i].fd == i)
894 hndls[i] = pfdtab->tab[i].native;
895 else
896 hndls[i] = (intptr_t)INVALID_HANDLE_VALUE;
897 TRACE2((NULL, "shfile_exec_win: i=%d count=%d fd=%d native=%d hndls[%d]=\n",
898 i, count, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, hndls[i]));
899 }
900
901 *sizep = (unsigned short)cbData;
902 pvRet = pbData;
903 }
904 else
905 {
906 assert(!hndls);
907 assert(!sizep);
908 i = count;
909 while (i-- > 0)
910 {
911 if ( pfdtab->tab[i].fd == i
912 && !(pfdtab->tab[i].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC))
913 {
914 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
915 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, 0))
916 {
917 DWORD err = GetLastError();
918 assert(0);
919 }
920 }
921 }
922 pvRet = NULL;
923 }
924
925 shmtx_leave(&pfdtab->mtx, &tmp);
926 return pvRet;
927}
928
929#endif /* K_OS_WINDOWS */
930
931#if K_OS != K_OS_WINDOWS
932/**
933 * Prepare file handles for inherting before a execve call.
934 *
935 * This is only used in the normal mode, so we've forked and need not worry
936 * about cleaning anything up after us. Nor do we need think about locking.
937 *
938 * @returns 0 on success, -1 on failure.
939 */
940int shfile_exec_unix(shfdtab *pfdtab)
941{
942 int rc = 0;
943# ifdef SHFILE_IN_USE
944 unsigned fd;
945
946 for (fd = 0; fd < pfdtab->size; fd++)
947 {
948 if ( pfdtab->tab[fd].fd != -1
949 && !(pfdtab->tab[fd].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC) )
950 {
951 TRACE2((NULL, "shfile_exec_unix: %d => %d\n", pfdtab->tab[fd].native, fd));
952 if (dup2(pfdtab->tab[fd].native, fd) < 0)
953 {
954 /* fatal_error(NULL, "shfile_exec_unix: failed to move %d to %d", pfdtab->tab[fd].fd, fd); */
955 rc = -1;
956 }
957 }
958 }
959# endif
960 return rc;
961}
962#endif /* !K_OS_WINDOWS */
963
964/**
965 * open().
966 */
967int shfile_open(shfdtab *pfdtab, const char *name, unsigned flags, mode_t mode)
968{
969 int fd;
970#ifdef SHFILE_IN_USE
971 char absname[SHFILE_MAX_PATH];
972# if K_OS == K_OS_WINDOWS
973 HANDLE hFile;
974 DWORD dwDesiredAccess;
975 DWORD dwShareMode;
976 DWORD dwCreationDisposition;
977 DWORD dwFlagsAndAttributes;
978 SECURITY_ATTRIBUTES SecurityAttributes;
979
980# ifndef _O_ACCMODE
981# define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR)
982# endif
983 switch (flags & (_O_ACCMODE | _O_APPEND))
984 {
985 case _O_RDONLY: dwDesiredAccess = GENERIC_READ; break;
986 case _O_RDONLY | _O_APPEND: dwDesiredAccess = GENERIC_READ; break;
987 case _O_WRONLY: dwDesiredAccess = GENERIC_WRITE; break;
988 case _O_WRONLY | _O_APPEND: dwDesiredAccess = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
989 case _O_RDWR: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; break;
990 case _O_RDWR | _O_APPEND: dwDesiredAccess = GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
991
992 default:
993 RETURN_ERROR(-1, EINVAL, "invalid mode");
994 }
995
996 dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
997
998 SecurityAttributes.nLength = sizeof(SecurityAttributes);
999 SecurityAttributes.lpSecurityDescriptor = NULL;
1000 SecurityAttributes.bInheritHandle = FALSE;
1001
1002 if (flags & _O_CREAT)
1003 {
1004 if ((flags & (_O_EXCL | _O_TRUNC)) == (_O_EXCL | _O_TRUNC))
1005 RETURN_ERROR(-1, EINVAL, "_O_EXCL | _O_TRUNC");
1006
1007 if (flags & _O_TRUNC)
1008 dwCreationDisposition = CREATE_ALWAYS; /* not 100%, but close enough */
1009 else if (flags & _O_EXCL)
1010 dwCreationDisposition = CREATE_NEW;
1011 else
1012 dwCreationDisposition = OPEN_ALWAYS;
1013 }
1014 else if (flags & _O_TRUNC)
1015 dwCreationDisposition = TRUNCATE_EXISTING;
1016 else
1017 dwCreationDisposition = OPEN_EXISTING;
1018
1019 if (!(flags & _O_CREAT) || (mode & 0222))
1020 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
1021 else
1022 dwFlagsAndAttributes = FILE_ATTRIBUTE_READONLY;
1023
1024 fd = shfile_make_path(pfdtab, name, &absname[0]);
1025 if (!fd)
1026 {
1027 SetLastError(0);
1028 hFile = CreateFileA(absname,
1029 dwDesiredAccess,
1030 dwShareMode,
1031 &SecurityAttributes,
1032 dwCreationDisposition,
1033 dwFlagsAndAttributes,
1034 NULL /* hTemplateFile */);
1035 if (hFile != INVALID_HANDLE_VALUE)
1036 fd = shfile_insert(pfdtab, (intptr_t)hFile, flags, 0, -1, "shfile_open");
1037 else
1038 fd = shfile_dos2errno(GetLastError());
1039 }
1040
1041# else /* K_OS != K_OS_WINDOWS */
1042 fd = shfile_make_path(pfdtab, name, &absname[0]);
1043 if (!fd)
1044 {
1045 fd = open(absname, flags, mode);
1046 if (fd != -1)
1047 fd = shfile_copy_insert_and_close(pfdtab, &fd, flags, 0, -1, "shfile_open");
1048 }
1049
1050# endif /* K_OS != K_OS_WINDOWS */
1051
1052#else
1053 fd = open(name, flags, mode);
1054#endif
1055
1056 TRACE2((NULL, "shfile_open(%p:{%s}, %#x, 0%o) -> %d [%d]\n", name, name, flags, mode, fd, errno));
1057 return fd;
1058}
1059
1060int shfile_pipe(shfdtab *pfdtab, int fds[2])
1061{
1062 int rc = -1;
1063#ifdef SHFILE_IN_USE
1064# if K_OS == K_OS_WINDOWS
1065 HANDLE hRead = INVALID_HANDLE_VALUE;
1066 HANDLE hWrite = INVALID_HANDLE_VALUE;
1067 SECURITY_ATTRIBUTES SecurityAttributes;
1068
1069 SecurityAttributes.nLength = sizeof(SecurityAttributes);
1070 SecurityAttributes.lpSecurityDescriptor = NULL;
1071 SecurityAttributes.bInheritHandle = FALSE;
1072
1073 fds[1] = fds[0] = -1;
1074 if (CreatePipe(&hRead, &hWrite, &SecurityAttributes, 4096))
1075 {
1076 fds[0] = shfile_insert(pfdtab, (intptr_t)hRead, O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe");
1077 if (fds[0] != -1)
1078 {
1079 fds[1] = shfile_insert(pfdtab, (intptr_t)hWrite, O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe");
1080 if (fds[1] != -1)
1081 rc = 0;
1082 }
1083
1084# else
1085 int native_fds[2];
1086
1087 fds[1] = fds[0] = -1;
1088 if (!pipe(native_fds))
1089 {
1090 fds[0] = shfile_copy_insert_and_close(pfdtab, &native_fds[0], O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe");
1091 if (fds[0] != -1)
1092 {
1093 fds[1] = shfile_copy_insert_and_close(pfdtab, &native_fds[1], O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe");
1094 if (fds[1] != -1)
1095 rc = 0;
1096 }
1097# endif
1098 if (fds[1] == -1)
1099 {
1100 int s = errno;
1101 if (fds[0] != -1)
1102 {
1103 shmtxtmp tmp;
1104 shmtx_enter(&pfdtab->mtx, &tmp);
1105 rc = fds[0];
1106 pfdtab->tab[rc].fd = -1;
1107 pfdtab->tab[rc].oflags = 0;
1108 pfdtab->tab[rc].shflags = 0;
1109 pfdtab->tab[rc].native = -1;
1110 shmtx_leave(&pfdtab->mtx, &tmp);
1111 }
1112
1113# if K_OS == K_OS_WINDOWS
1114 CloseHandle(hRead);
1115 CloseHandle(hWrite);
1116# else
1117 close(native_fds[0]);
1118 close(native_fds[1]);
1119# endif
1120 fds[0] = fds[1] = -1;
1121 errno = s;
1122 rc = -1;
1123 }
1124 }
1125 else
1126 {
1127# if K_OS == K_OS_WINDOWS
1128 errno = shfile_dos2errno(GetLastError());
1129# endif
1130 rc = -1;
1131 }
1132
1133#else
1134 rc = pipe(fds);
1135#endif
1136
1137 TRACE2((NULL, "shfile_pipe() -> %d{%d,%d} [%d]\n", rc, fds[0], fds[1], errno));
1138 return rc;
1139}
1140
1141/**
1142 * dup().
1143 */
1144int shfile_dup(shfdtab *pfdtab, int fd)
1145{
1146 return shfile_fcntl(pfdtab,fd, F_DUPFD, 0);
1147}
1148
1149/**
1150 * Move the file descriptor, closing any existing descriptor at @a fdto.
1151 *
1152 * @returns fdto on success, -1 and errno on failure.
1153 * @param pfdtab The file descriptor table.
1154 * @param fdfrom The descriptor to move.
1155 * @param fdto Where to move it.
1156 */
1157int shfile_movefd(shfdtab *pfdtab, int fdfrom, int fdto)
1158{
1159#ifdef SHFILE_IN_USE
1160 int rc;
1161 shmtxtmp tmp;
1162 shfile *file = shfile_get(pfdtab, fdfrom, &tmp);
1163 if (file)
1164 {
1165 /* prepare the new entry */
1166 if (fdto >= (int)pfdtab->size)
1167 shfile_grow_tab_locked(pfdtab, fdto);
1168 if (fdto < (int)pfdtab->size)
1169 {
1170 if (pfdtab->tab[fdto].fd != -1)
1171 shfile_native_close(pfdtab->tab[fdto].native, pfdtab->tab[fdto].oflags);
1172
1173 /* setup the target. */
1174 pfdtab->tab[fdto].fd = fdto;
1175 pfdtab->tab[fdto].oflags = file->oflags;
1176 pfdtab->tab[fdto].shflags = file->shflags;
1177 pfdtab->tab[fdto].native = file->native;
1178
1179 /* close the source. */
1180 file->fd = -1;
1181 file->oflags = 0;
1182 file->shflags = 0;
1183 file->native = -1;
1184
1185 rc = fdto;
1186 }
1187 else
1188 {
1189 errno = EMFILE;
1190 rc = -1;
1191 }
1192
1193 shfile_put(pfdtab, file, &tmp);
1194 }
1195 else
1196 rc = -1;
1197 return rc;
1198
1199#else
1200 return dup2(fdfrom, fdto);
1201#endif
1202}
1203
1204/**
1205 * Move the file descriptor to somewhere at @a fdMin or above.
1206 *
1207 * @returns the new file descriptor success, -1 and errno on failure.
1208 * @param pfdtab The file descriptor table.
1209 * @param fdfrom The descriptor to move.
1210 * @param fdMin The minimum descriptor.
1211 */
1212int shfile_movefd_above(shfdtab *pfdtab, int fdfrom, int fdMin)
1213{
1214#ifdef SHFILE_IN_USE
1215 int fdto;
1216 shmtxtmp tmp;
1217 shfile *file = shfile_get(pfdtab, fdfrom, &tmp);
1218 if (file)
1219 {
1220 /* find a new place */
1221 int i;
1222 fdto = -1;
1223 for (i = fdMin; (unsigned)i < pfdtab->size; i++)
1224 if (pfdtab->tab[i].fd == -1)
1225 {
1226 fdto = i;
1227 break;
1228 }
1229 if (fdto == -1)
1230 fdto = shfile_grow_tab_locked(pfdtab, fdMin);
1231 if (fdto != -1)
1232 {
1233 /* setup the target. */
1234 pfdtab->tab[fdto].fd = fdto;
1235 pfdtab->tab[fdto].oflags = file->oflags;
1236 pfdtab->tab[fdto].shflags = file->shflags;
1237 pfdtab->tab[fdto].native = file->native;
1238
1239 /* close the source. */
1240 file->fd = -1;
1241 file->oflags = 0;
1242 file->shflags = 0;
1243 file->native = -1;
1244 }
1245 else
1246 {
1247 errno = EMFILE;
1248 fdto = -1;
1249 }
1250
1251 shfile_put(pfdtab, file, &tmp);
1252 }
1253 else
1254 fdto = -1;
1255 return fdto;
1256
1257#else
1258 int fdnew = fcntl(fdfrom, F_DUPFD, fdMin);
1259 if (fdnew >= 0)
1260 close(fdfrom);
1261 return fdnew;
1262#endif
1263}
1264
1265/**
1266 * close().
1267 */
1268int shfile_close(shfdtab *pfdtab, unsigned fd)
1269{
1270 int rc;
1271#ifdef SHFILE_IN_USE
1272 shmtxtmp tmp;
1273 shfile *file = shfile_get(pfdtab, fd, &tmp);
1274 if (file)
1275 {
1276 shfile_native_close(file->native, file->oflags);
1277
1278 file->fd = -1;
1279 file->oflags = 0;
1280 file->shflags = 0;
1281 file->native = -1;
1282
1283 shfile_put(pfdtab, file, &tmp);
1284 rc = 0;
1285 }
1286 else
1287 rc = -1;
1288
1289#else
1290 rc = close(fd);
1291#endif
1292
1293 TRACE2((NULL, "shfile_close(%d) -> %d [%d]\n", fd, rc, errno));
1294 return rc;
1295}
1296
1297/**
1298 * read().
1299 */
1300long shfile_read(shfdtab *pfdtab, int fd, void *buf, size_t len)
1301{
1302 long rc;
1303#ifdef SHFILE_IN_USE
1304 shmtxtmp tmp;
1305 shfile *file = shfile_get(pfdtab, fd, &tmp);
1306 if (file)
1307 {
1308# if K_OS == K_OS_WINDOWS
1309 DWORD dwRead = 0;
1310 if (ReadFile((HANDLE)file->native, buf, (DWORD)len, &dwRead, NULL))
1311 rc = dwRead;
1312 else
1313 rc = shfile_dos2errno(GetLastError());
1314# else
1315 rc = read(file->native, buf, len);
1316# endif
1317
1318 shfile_put(pfdtab, file, &tmp);
1319 }
1320 else
1321 rc = -1;
1322
1323#else
1324 rc = read(fd, buf, len);
1325#endif
1326 return rc;
1327}
1328
1329/**
1330 * write().
1331 */
1332long shfile_write(shfdtab *pfdtab, int fd, const void *buf, size_t len)
1333{
1334 long rc;
1335#ifdef SHFILE_IN_USE
1336 shmtxtmp tmp;
1337 shfile *file = shfile_get(pfdtab, fd, &tmp);
1338 if (file)
1339 {
1340# if K_OS == K_OS_WINDOWS
1341 DWORD dwWritten = 0;
1342 if (WriteFile((HANDLE)file->native, buf, (DWORD)len, &dwWritten, NULL))
1343 rc = dwWritten;
1344 else
1345 rc = shfile_dos2errno(GetLastError());
1346# else
1347 rc = write(file->native, buf, len);
1348# endif
1349
1350 shfile_put(pfdtab, file, &tmp);
1351 }
1352 else
1353 rc = -1;
1354
1355#else
1356 if (fd != shthread_get_shell()->tracefd)
1357 {
1358 struct stat s;
1359 int x;
1360 x = fstat(fd, &s);
1361 TRACE2((NULL, "shfile_write(%d) - %lu bytes (%d) - pos %lu - before; %o\n",
1362 fd, (long)s.st_size, x, (long)lseek(fd, 0, SEEK_CUR), s.st_mode ));
1363 errno = 0;
1364 }
1365
1366 rc = write(fd, buf, len);
1367#endif
1368
1369#ifdef DEBUG
1370 if (fd != shthread_get_shell()->tracefd)
1371 {
1372 struct stat s;
1373 int x;
1374 TRACE2((NULL, "shfile_write(%d,,%d) -> %d [%d]\n", fd, len, rc, errno));
1375 x=fstat(fd, &s);
1376 TRACE2((NULL, "shfile_write(%d) - %lu bytes (%d) - pos %lu - after\n", fd, (long)s.st_size, x, (long)lseek(fd, 0, SEEK_CUR) ));
1377 }
1378#endif
1379 return rc;
1380}
1381
1382/**
1383 * lseek().
1384 */
1385long shfile_lseek(shfdtab *pfdtab, int fd, long off, int whench)
1386{
1387 long rc;
1388#ifdef SHFILE_IN_USE
1389 shmtxtmp tmp;
1390 shfile *file = shfile_get(pfdtab, fd, &tmp);
1391 if (file)
1392 {
1393# if K_OS == K_OS_WINDOWS
1394 assert(SEEK_SET == FILE_BEGIN);
1395 assert(SEEK_CUR == FILE_CURRENT);
1396 assert(SEEK_END == FILE_END);
1397 rc = SetFilePointer((HANDLE)file->native, off, NULL, whench);
1398 if (rc == INVALID_SET_FILE_POINTER)
1399 rc = shfile_dos2errno(GetLastError());
1400# else
1401 rc = lseek(file->native, off, whench);
1402# endif
1403
1404 shfile_put(pfdtab, file, &tmp);
1405 }
1406 else
1407 rc = -1;
1408
1409#else
1410 rc = lseek(fd, off, whench);
1411#endif
1412
1413 return rc;
1414}
1415
1416int shfile_fcntl(shfdtab *pfdtab, int fd, int cmd, int arg)
1417{
1418 int rc;
1419#ifdef SHFILE_IN_USE
1420 shmtxtmp tmp;
1421 shfile *file = shfile_get(pfdtab, fd, &tmp);
1422 if (file)
1423 {
1424 switch (cmd)
1425 {
1426 case F_GETFL:
1427 rc = file->oflags;
1428 break;
1429
1430 case F_SETFL:
1431 {
1432 unsigned mask = O_NONBLOCK | O_APPEND | O_BINARY | O_TEXT;
1433# ifdef O_DIRECT
1434 mask |= O_DIRECT;
1435# endif
1436# ifdef O_ASYNC
1437 mask |= O_ASYNC;
1438# endif
1439# ifdef O_SYNC
1440 mask |= O_SYNC;
1441# endif
1442 if ((file->oflags & mask) == (arg & mask))
1443 rc = 0;
1444 else
1445 {
1446# if K_OS == K_OS_WINDOWS
1447 assert(0);
1448 errno = EINVAL;
1449 rc = -1;
1450# else
1451 rc = fcntl(file->native, F_SETFL, arg);
1452 if (rc != -1)
1453 file->oflags = (file->oflags & ~mask) | (arg & mask);
1454# endif
1455 }
1456 break;
1457 }
1458
1459 case F_DUPFD:
1460 {
1461# if K_OS == K_OS_WINDOWS
1462 HANDLE hNew = INVALID_HANDLE_VALUE;
1463 if (DuplicateHandle(GetCurrentProcess(),
1464 (HANDLE)file->native,
1465 GetCurrentProcess(),
1466 &hNew,
1467 0,
1468 FALSE /* bInheritHandle */,
1469 DUPLICATE_SAME_ACCESS))
1470 rc = shfile_insert(pfdtab, (intptr_t)hNew, file->oflags, file->shflags, arg, "shfile_fcntl");
1471 else
1472 rc = shfile_dos2errno(GetLastError());
1473# else
1474 int nativeNew = fcntl(file->native, F_DUPFD, SHFILE_UNIX_MIN_FD);
1475 if (nativeNew != -1)
1476 rc = shfile_insert(pfdtab, nativeNew, file->oflags, file->shflags, arg, "shfile_fcntl");
1477 else
1478 rc = -1;
1479# endif
1480 break;
1481 }
1482
1483 default:
1484 errno = -EINVAL;
1485 rc = -1;
1486 break;
1487 }
1488
1489 shfile_put(pfdtab, file, &tmp);
1490 }
1491 else
1492 rc = -1;
1493
1494#else
1495 rc = fcntl(fd, cmd, arg);
1496#endif
1497
1498 switch (cmd)
1499 {
1500 case F_GETFL: TRACE2((NULL, "shfile_fcntl(%d,F_GETFL,ignored=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
1501 case F_SETFL: TRACE2((NULL, "shfile_fcntl(%d,F_SETFL,newflags=%#x) -> %d [%d]\n", fd, arg, rc, errno)); break;
1502 case F_DUPFD: TRACE2((NULL, "shfile_fcntl(%d,F_DUPFS,minfd=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
1503 default: TRACE2((NULL, "shfile_fcntl(%d,%d,%d) -> %d [%d]\n", fd, cmd, arg, rc, errno)); break;
1504 }
1505 return rc;
1506}
1507
1508int shfile_stat(shfdtab *pfdtab, const char *path, struct stat *pst)
1509{
1510#ifdef SHFILE_IN_USE
1511 char abspath[SHFILE_MAX_PATH];
1512 int rc;
1513 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1514 if (!rc)
1515 {
1516# if K_OS == K_OS_WINDOWS
1517 rc = stat(abspath, pst); /** @todo re-implement stat. */
1518# else
1519 rc = stat(abspath, pst);
1520# endif
1521 }
1522 TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d]\n", path, rc, errno));
1523 return rc;
1524#else
1525 return stat(path, pst);
1526#endif
1527}
1528
1529int shfile_lstat(shfdtab *pfdtab, const char *path, struct stat *pst)
1530{
1531 int rc;
1532#ifdef SHFILE_IN_USE
1533 char abspath[SHFILE_MAX_PATH];
1534
1535 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1536 if (!rc)
1537 {
1538# if K_OS == K_OS_WINDOWS
1539 rc = stat(abspath, pst); /** @todo implement lstat. */
1540# else
1541 rc = lstat(abspath, pst);
1542# endif
1543 }
1544#else
1545 rc = stat(path, pst);
1546#endif
1547 TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d]\n", path, rc, errno));
1548 return rc;
1549}
1550
1551/**
1552 * chdir().
1553 */
1554int shfile_chdir(shfdtab *pfdtab, const char *path)
1555{
1556 int rc;
1557#ifdef SHFILE_IN_USE
1558 shinstance *psh = shthread_get_shell();
1559 char abspath[SHFILE_MAX_PATH];
1560
1561 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1562 if (!rc)
1563 {
1564 char *abspath_copy = sh_strdup(psh, abspath);
1565 char *free_me = abspath_copy;
1566 rc = chdir(path);
1567 if (!rc)
1568 {
1569 shmtxtmp tmp;
1570 shmtx_enter(&pfdtab->mtx, &tmp);
1571
1572 free_me = pfdtab->cwd;
1573 pfdtab->cwd = abspath_copy;
1574
1575 shmtx_leave(&pfdtab->mtx, &tmp);
1576 }
1577 sh_free(psh, free_me);
1578 }
1579 else
1580 rc = -1;
1581#else
1582 rc = chdir(path);
1583#endif
1584
1585 TRACE2((NULL, "shfile_chdir(,%s) -> %d [%d]\n", path, rc, errno));
1586 return rc;
1587}
1588
1589/**
1590 * getcwd().
1591 */
1592char *shfile_getcwd(shfdtab *pfdtab, char *buf, int size)
1593{
1594 char *ret;
1595#ifdef SHFILE_IN_USE
1596
1597 ret = NULL;
1598 if (buf && !size)
1599 errno = -EINVAL;
1600 else
1601 {
1602 size_t cwd_size;
1603 shmtxtmp tmp;
1604 shmtx_enter(&pfdtab->mtx, &tmp);
1605
1606 cwd_size = strlen(pfdtab->cwd) + 1;
1607 if (buf)
1608 {
1609 if (cwd_size <= (size_t)size)
1610 ret = memcpy(buf, pfdtab->cwd, cwd_size);
1611 else
1612 errno = ERANGE;
1613 }
1614 else
1615 {
1616 if ((size_t)size < cwd_size)
1617 size = (int)cwd_size;
1618 ret = sh_malloc(shthread_get_shell(), size);
1619 if (ret)
1620 ret = memcpy(ret, pfdtab->cwd, cwd_size);
1621 else
1622 errno = ENOMEM;
1623 }
1624
1625 shmtx_leave(&pfdtab->mtx, &tmp);
1626 }
1627#else
1628 ret = getcwd(buf, size);
1629#endif
1630
1631 TRACE2((NULL, "shfile_getcwd(,%p,%d) -> %s [%d]\n", buf, size, ret, errno));
1632 return ret;
1633}
1634
1635/**
1636 * access().
1637 */
1638int shfile_access(shfdtab *pfdtab, const char *path, int type)
1639{
1640 int rc;
1641#ifdef SHFILE_IN_USE
1642 char abspath[SHFILE_MAX_PATH];
1643
1644 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1645 if (!rc)
1646 {
1647# ifdef _MSC_VER
1648 if (type & X_OK)
1649 type = (type & ~X_OK) | R_OK;
1650# endif
1651 rc = access(abspath, type);
1652 }
1653#else
1654# ifdef _MSC_VER
1655 if (type & X_OK)
1656 type = (type & ~X_OK) | R_OK;
1657# endif
1658 rc = access(path, type);
1659#endif
1660
1661 TRACE2((NULL, "shfile_access(,%s,%#x) -> %d [%d]\n", path, type, rc, errno));
1662 return rc;
1663}
1664
1665/**
1666 * isatty()
1667 */
1668int shfile_isatty(shfdtab *pfdtab, int fd)
1669{
1670 int rc;
1671#ifdef SHFILE_IN_USE
1672 shmtxtmp tmp;
1673 shfile *file = shfile_get(pfdtab, fd, &tmp);
1674 if (file)
1675 {
1676# if K_OS == K_OS_WINDOWS
1677 rc = (file->shflags & SHFILE_FLAGS_TYPE_MASK) == SHFILE_FLAGS_TTY;
1678# else
1679 rc = isatty(file->native);
1680# endif
1681 shfile_put(pfdtab, file, &tmp);
1682 }
1683 else
1684 rc = 0;
1685#else
1686 rc = isatty(fd);
1687#endif
1688
1689 TRACE2((NULL, "isatty(%d) -> %d [%d]\n", fd, rc, errno));
1690 return rc;
1691}
1692
1693/**
1694 * fcntl F_SETFD / FD_CLOEXEC.
1695 */
1696int shfile_cloexec(shfdtab *pfdtab, int fd, int closeit)
1697{
1698 int rc;
1699#ifdef SHFILE_IN_USE
1700 shmtxtmp tmp;
1701 shfile *file = shfile_get(pfdtab, fd, &tmp);
1702 if (file)
1703 {
1704 if (closeit)
1705 file->shflags |= SHFILE_FLAGS_CLOSE_ON_EXEC;
1706 else
1707 file->shflags &= ~SHFILE_FLAGS_CLOSE_ON_EXEC;
1708 shfile_put(pfdtab, file, &tmp);
1709 rc = 0;
1710 }
1711 else
1712 rc = -1;
1713#else
1714 rc = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0)
1715 | (closeit ? FD_CLOEXEC : 0));
1716#endif
1717
1718 TRACE2((NULL, "shfile_cloexec(%d, %d) -> %d [%d]\n", fd, closeit, rc, errno));
1719 return rc;
1720}
1721
1722
1723int shfile_ioctl(shfdtab *pfdtab, int fd, unsigned long request, void *buf)
1724{
1725 int rc;
1726#ifdef SHFILE_IN_USE
1727 shmtxtmp tmp;
1728 shfile *file = shfile_get(pfdtab, fd, &tmp);
1729 if (file)
1730 {
1731# if K_OS == K_OS_WINDOWS
1732 rc = -1;
1733 errno = ENOSYS;
1734# else
1735 rc = ioctl(file->native, request, buf);
1736# endif
1737 shfile_put(pfdtab, file, &tmp);
1738 }
1739 else
1740 rc = -1;
1741#else
1742 rc = ioctl(fd, request, buf);
1743#endif
1744
1745 TRACE2((NULL, "ioctl(%d, %#x, %p) -> %d\n", fd, request, buf, rc));
1746 return rc;
1747}
1748
1749
1750mode_t shfile_get_umask(shfdtab *pfdtab)
1751{
1752 /** @todo */
1753 return 022;
1754}
1755
1756void shfile_set_umask(shfdtab *pfdtab, mode_t mask)
1757{
1758 /** @todo */
1759 (void)mask;
1760}
1761
1762
1763
1764shdir *shfile_opendir(shfdtab *pfdtab, const char *dir)
1765{
1766#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
1767 shdir *pdir = NULL;
1768
1769 if (g_pfnNtQueryDirectoryFile)
1770 {
1771 char abspath[SHFILE_MAX_PATH];
1772 if (shfile_make_path(pfdtab, dir, &abspath[0]) == 0)
1773 {
1774 HANDLE hFile;
1775 SECURITY_ATTRIBUTES SecurityAttributes;
1776
1777 SecurityAttributes.nLength = sizeof(SecurityAttributes);
1778 SecurityAttributes.lpSecurityDescriptor = NULL;
1779 SecurityAttributes.bInheritHandle = FALSE;
1780
1781 hFile = CreateFileA(abspath,
1782 GENERIC_READ,
1783 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1784 &SecurityAttributes,
1785 OPEN_EXISTING,
1786 FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS,
1787 NULL /* hTemplateFile */);
1788 if (hFile != INVALID_HANDLE_VALUE)
1789 {
1790 pdir = (shdir *)sh_malloc(shthread_get_shell(), sizeof(*pdir));
1791 if (pdir)
1792 {
1793 pdir->pfdtab = pfdtab;
1794 pdir->native = hFile;
1795 pdir->off = ~(size_t)0;
1796 }
1797 else
1798 CloseHandle(hFile);
1799 }
1800 else
1801 shfile_dos2errno(GetLastError());
1802 }
1803 }
1804 else
1805 errno = ENOSYS;
1806 return pdir;
1807#else
1808 return (shdir *)opendir(dir);
1809#endif
1810}
1811
1812shdirent *shfile_readdir(struct shdir *pdir)
1813{
1814#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
1815 if (pdir)
1816 {
1817 NTSTATUS rcNt;
1818
1819 if ( pdir->off == ~(size_t)0
1820 || pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) >= pdir->cb)
1821 {
1822 MY_IO_STATUS_BLOCK Ios;
1823
1824 memset(&Ios, 0, sizeof(Ios));
1825 rcNt = g_pfnNtQueryDirectoryFile(pdir->native,
1826 NULL /*Event*/,
1827 NULL /*ApcRoutine*/,
1828 NULL /*ApcContext*/,
1829 &Ios,
1830 &pdir->buf[0],
1831 sizeof(pdir->buf),
1832 MY_FileNamesInformation,
1833 FALSE /*ReturnSingleEntry*/,
1834 NULL /*FileName*/,
1835 pdir->off == ~(size_t)0 /*RestartScan*/);
1836 if (rcNt >= 0 && rcNt != STATUS_PENDING)
1837 {
1838 pdir->cb = Ios.Information;
1839 pdir->off = 0;
1840 }
1841 else if (rcNt == STATUS_NO_MORE_FILES)
1842 errno = 0; /* wrong? */
1843 else
1844 shfile_nt2errno(rcNt);
1845 }
1846
1847 if ( pdir->off != ~(size_t)0
1848 && pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) <= pdir->cb)
1849 {
1850 PMY_FILE_NAMES_INFORMATION pcur = (PMY_FILE_NAMES_INFORMATION)&pdir->buf[pdir->off];
1851 ANSI_STRING astr;
1852 UNICODE_STRING ustr;
1853
1854 astr.Length = astr.MaximumLength = sizeof(pdir->ent.name);
1855 astr.Buffer = &pdir->ent.name[0];
1856
1857 ustr.Length = ustr.MaximumLength = pcur->FileNameLength < ~(USHORT)0 ? (USHORT)pcur->FileNameLength : ~(USHORT)0;
1858 ustr.Buffer = &pcur->FileName[0];
1859
1860 rcNt = g_pfnRtlUnicodeStringToAnsiString(&astr, &ustr, 0/*AllocateDestinationString*/);
1861 if (rcNt < 0)
1862 sprintf(pdir->ent.name, "conversion-failed-%08x-rcNt=%08x-len=%u",
1863 pcur->FileIndex, rcNt, pcur->FileNameLength);
1864 if (pcur->NextEntryOffset)
1865 pdir->off += pcur->NextEntryOffset;
1866 else
1867 pdir->off = pdir->cb;
1868 return &pdir->ent;
1869 }
1870 }
1871 else
1872 errno = EINVAL;
1873 return NULL;
1874#else
1875 struct dirent *pde = readdir((DIR *)pdir);
1876 return pde ? (shdirent *)&pde->d_name[0] : NULL;
1877#endif
1878}
1879
1880void shfile_closedir(struct shdir *pdir)
1881{
1882#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
1883 if (pdir)
1884 {
1885 CloseHandle(pdir->native);
1886 pdir->pfdtab = NULL;
1887 pdir->native = INVALID_HANDLE_VALUE;
1888 sh_free(shthread_get_shell(), pdir);
1889 }
1890 else
1891 errno = EINVAL;
1892#else
1893 closedir((DIR *)pdir);
1894#endif
1895}
1896
Note: See TracBrowser for help on using the repository browser.