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