VirtualBox

source: kBuild/trunk/src/kash/shfile.c@ 3646

Last change on this file since 3646 was 3640, checked in by bird, 7 months ago

kash: warning

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 82.0 KB
Line 
1/* $Id: shfile.c 3640 2024-11-02 23:39:14Z bird $ */
2/** @file
3 *
4 * File management.
5 *
6 * Copyright (c) 2007-2010 knut st. osmundsen <[email protected]>
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
36#if K_OS == K_OS_WINDOWS
37# include <ntstatus.h>
38# define WIN32_NO_STATUS
39# include <Windows.h>
40# if !defined(_WIN32_WINNT)
41# define _WIN32_WINNT 0x0502 /* Windows Server 2003 */
42# endif
43# include <winternl.h> //NTSTATUS
44#else
45# include <unistd.h>
46# include <fcntl.h>
47# include <dirent.h>
48#endif
49
50
51/*******************************************************************************
52* Defined Constants And Macros *
53*******************************************************************************/
54/** @def SHFILE_IN_USE
55 * Whether the file descriptor table stuff is actually in use or not.
56 */
57#if K_OS == K_OS_WINDOWS \
58 || K_OS == K_OS_OPENBSD /* because of ugly pthread library pipe hacks */ \
59 || !defined(SH_FORKED_MODE)
60# define SHFILE_IN_USE
61#endif
62/** The max file table size. */
63#define SHFILE_MAX 1024
64/** The file table growth rate. */
65#define SHFILE_GROW 64
66/** The min native unix file descriptor. */
67#define SHFILE_UNIX_MIN_FD 32
68/** The path buffer size we use. */
69#define SHFILE_MAX_PATH 4096
70
71/** Set errno and return. Doing a trace in debug build. */
72#define RETURN_ERROR(rc, err, msg) \
73 do { \
74 TRACE2((NULL, "%s: " ## msg ## " - returning %d / %d\n", __FUNCTION__, (rc), (err))); \
75 errno = (err); \
76 return (rc); \
77 } while (0)
78
79#if K_OS == K_OS_WINDOWS
80 /* See msdos.h for description. */
81# define FOPEN 0x01
82# define FEOFLAG 0x02
83# define FCRLF 0x04
84# define FPIPE 0x08
85# define FNOINHERIT 0x10
86# define FAPPEND 0x20
87# define FDEV 0x40
88# define FTEXT 0x80
89
90# define MY_ObjectBasicInformation 0
91# define MY_FileNamesInformation 12
92
93typedef struct
94{
95 ULONG Attributes;
96 ACCESS_MASK GrantedAccess;
97 ULONG HandleCount;
98 ULONG PointerCount;
99 ULONG PagedPoolUsage;
100 ULONG NonPagedPoolUsage;
101 ULONG Reserved[3];
102 ULONG NameInformationLength;
103 ULONG TypeInformationLength;
104 ULONG SecurityDescriptorLength;
105 LARGE_INTEGER CreateTime;
106} MY_OBJECT_BASIC_INFORMATION;
107
108#if 0
109typedef struct
110{
111 union
112 {
113 LONG Status;
114 PVOID Pointer;
115 };
116 ULONG_PTR Information;
117} MY_IO_STATUS_BLOCK;
118#else
119typedef IO_STATUS_BLOCK MY_IO_STATUS_BLOCK;
120#endif
121typedef MY_IO_STATUS_BLOCK *PMY_IO_STATUS_BLOCK;
122
123typedef struct
124{
125 ULONG NextEntryOffset;
126 ULONG FileIndex;
127 ULONG FileNameLength;
128 WCHAR FileName[1];
129} MY_FILE_NAMES_INFORMATION, *PMY_FILE_NAMES_INFORMATION;
130
131typedef NTSTATUS (NTAPI * PFN_NtQueryObject)(HANDLE, int, void *, size_t, size_t *);
132typedef NTSTATUS (NTAPI * PFN_NtQueryDirectoryFile)(HANDLE, HANDLE, void *, void *, PMY_IO_STATUS_BLOCK, void *,
133 ULONG, int, int, PUNICODE_STRING, int);
134typedef NTSTATUS (NTAPI * PFN_RtlUnicodeStringToAnsiString)(PANSI_STRING, PCUNICODE_STRING, int);
135
136
137#endif /* K_OS_WINDOWS */
138
139
140/*********************************************************************************************************************************
141* Global Variables *
142*********************************************************************************************************************************/
143#if K_OS == K_OS_WINDOWS
144static int g_shfile_globals_initialized = 0;
145static PFN_NtQueryObject g_pfnNtQueryObject = NULL;
146static PFN_NtQueryDirectoryFile g_pfnNtQueryDirectoryFile = NULL;
147static PFN_RtlUnicodeStringToAnsiString g_pfnRtlUnicodeStringToAnsiString = NULL;
148# ifdef KASH_ASYNC_CLOSE_HANDLE
149/** Data for the asynchronous CloseHandle machinery. */
150static struct shfileasyncclose
151{
152 /** Mutex protecting the asynchronous CloseHandle stuff. */
153 shmtx mtx;
154 /** Handle to event that the closer-threads are waiting on. */
155 HANDLE evt_workers;
156 /** The ring buffer read index (for closer-threads). */
157 unsigned volatile idx_read;
158 /** The ring buffer write index (for shfile_native_close).
159 * When idx_read and idx_write are the same, the ring buffer is empty. */
160 unsigned volatile idx_write;
161 /** Number of handles currently being pending closure (handles + current
162 * CloseHandle calls). */
163 unsigned volatile num_pending;
164 /** Set if the threads should terminate. */
165 KBOOL volatile terminate_threads;
166 /** Set if one or more shell threads have requested evt_sync to be signalled
167 * when there are no more pending requests. */
168 KBOOL volatile signal_sync;
169 /** Number of threads that have been spawned. */
170 KU8 num_threads;
171 /** Handle to event that the shell threads are waiting on to sync. */
172 HANDLE evt_sync;
173 /** Ring buffer containing handles to be closed. */
174 HANDLE handles[32];
175 /** Worker threads doing the asynchronous closing. */
176 HANDLE threads[8];
177} g_shfile_async_close;
178# endif
179#endif /* K_OS_WINDOWS */
180
181
182/*********************************************************************************************************************************
183* Internal Functions *
184*********************************************************************************************************************************/
185#ifdef SHFILE_IN_USE
186# if K_OS == K_OS_WINDOWS
187static HANDLE shfile_set_inherit_win(shfile *pfd, int set);
188# endif
189#endif
190
191
192#ifdef SHFILE_IN_USE
193
194# ifdef DEBUG
195# if K_OS == K_OS_WINDOWS
196static KU64 shfile_nano_ts(void)
197{
198 static KBOOL volatile s_has_factor = K_FALSE;
199 static double volatile s_factor;
200 double factor;
201 LARGE_INTEGER now;
202 if (s_has_factor)
203 factor = s_factor;
204 else
205 {
206 QueryPerformanceFrequency(&now);
207 s_factor = factor = (double)1000000000.0 / now.QuadPart;
208 s_has_factor = K_TRUE;
209 }
210 QueryPerformanceCounter(&now);
211 return (KU64)(now.QuadPart * factor);
212}
213# endif /* K_OS_WINDOWS */
214# endif /* DEBUG */
215
216# if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE)
217/**
218 * The closer thread function.
219 */
220static unsigned __stdcall shfile_async_close_handle_thread(void *ignored)
221{
222 KBOOL decrement_pending = K_FALSE;
223 shthread_set_name("Async CloseHandle");
224 while (!g_shfile_async_close.terminate_threads)
225 {
226 HANDLE toclose;
227 unsigned idx;
228 shmtxtmp tmp;
229
230 /*
231 * Grab a handle if there is one:
232 */
233 shmtx_enter(&g_shfile_async_close.mtx, &tmp);
234
235 if (decrement_pending)
236 {
237 kHlpAssert(g_shfile_async_close.num_pending > 0);
238 g_shfile_async_close.num_pending -= 1;
239 }
240
241 idx = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles);
242 if (idx != g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles))
243 {
244 kHlpAssert(g_shfile_async_close.num_pending > 0);
245 toclose = g_shfile_async_close.handles[idx];
246 kHlpAssert(toclose);
247 g_shfile_async_close.handles[idx] = NULL;
248 g_shfile_async_close.idx_read = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles);
249 }
250 else
251 {
252 /* Signal waiters if requested and we've reached zero pending requests. */
253 if (g_shfile_async_close.signal_sync && g_shfile_async_close.num_pending == 0)
254 {
255 BOOL rc = SetEvent(g_shfile_async_close.evt_sync);
256 kHlpAssert(rc);
257 g_shfile_async_close.signal_sync = FALSE;
258 }
259 toclose = NULL;
260 }
261 shmtx_leave(&g_shfile_async_close.mtx, &tmp);
262
263 /* Do the closing if we have something to close, otherwise wait for work to arrive. */
264 if (toclose != NULL)
265 {
266 BOOL rc = CloseHandle(toclose);
267 kHlpAssert(rc);
268 decrement_pending = K_TRUE;
269 }
270 else
271 {
272 DWORD dwRet = WaitForSingleObject(g_shfile_async_close.evt_workers, 10000 /*ms*/);
273 kHlpAssert(dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT);
274 decrement_pending = K_FALSE;
275 }
276 }
277
278 K_NOREF(ignored);
279 return 0;
280}
281
282/**
283 * Try submit a handle for automatic closing.
284 *
285 * @returns K_TRUE if submitted successfully, K_FALSE if not.
286 */
287static KBOOL shfile_async_close_submit(HANDLE toclose)
288{
289 KBOOL ret;
290 unsigned idx;
291 unsigned idx_next;
292 unsigned idx_read;
293 unsigned num_pending = 0;
294 unsigned num_threads = 0;
295 shmtxtmp tmp;
296 shmtx_enter(&g_shfile_async_close.mtx, &tmp);
297
298 /* Get the write index and check that there is a free slot in the buffer: */
299 idx = g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles);
300 idx_next = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles);
301 idx_read = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles);
302 if (idx_next != idx_read)
303 {
304 /* Write the handle to the ring buffer: */
305 kHlpAssert(g_shfile_async_close.handles[idx] == NULL);
306 g_shfile_async_close.handles[idx] = toclose;
307 g_shfile_async_close.idx_write = idx_next;
308
309 num_pending = g_shfile_async_close.num_pending + 1;
310 g_shfile_async_close.num_pending = num_pending;
311
312 ret = SetEvent(g_shfile_async_close.evt_workers);
313 kHlpAssert(ret);
314 if (ret)
315 {
316 /* If we have more pending requests than threads, create a new thread. */
317 num_threads = g_shfile_async_close.num_threads;
318 if (num_pending > num_threads && num_threads < K_ELEMENTS(g_shfile_async_close.threads))
319 {
320 int const savederrno = errno;
321 unsigned tid = 0;
322 intptr_t hThread = _beginthreadex(NULL /*security*/, 0 /*stack_size*/, shfile_async_close_handle_thread,
323 NULL /*arg*/, 0 /*initflags*/, &tid);
324 kHlpAssert(hThread != -1);
325 if (hThread != -1)
326 {
327 g_shfile_async_close.threads[num_threads] = (HANDLE)hThread;
328 g_shfile_async_close.num_threads = ++num_threads;
329 }
330 else
331 {
332 TRACE2((NULL, "shfile_async_close_submit: _beginthreadex failed: %d\n", errno));
333 if (num_threads == 0)
334 ret = K_FALSE;
335 }
336 errno = savederrno;
337 }
338 }
339 else
340 TRACE2((NULL, "shfile_async_close_submit: SetEvent(%p) failed: %u\n", g_shfile_async_close.evt_workers, GetLastError()));
341
342 /* cleanup on failure. */
343 if (ret)
344 { /* likely */ }
345 else
346 {
347 g_shfile_async_close.handles[idx] = NULL;
348 g_shfile_async_close.idx_write = idx;
349 g_shfile_async_close.num_pending = num_pending - 1;
350 }
351 }
352 else
353 ret = K_FALSE;
354
355 shmtx_leave(&g_shfile_async_close.mtx, &tmp);
356 TRACE2((NULL, "shfile_async_close_submit: toclose=%p idx=%d #pending=%u #thread=%u -> %d\n",
357 toclose, idx, num_pending, num_threads, ret));
358 return ret;
359}
360
361/**
362 * Wait for all pending CloseHandle calls to complete.
363 */
364void shfile_async_close_sync(void)
365{
366 shmtxtmp tmp;
367 shmtx_enter(&g_shfile_async_close.mtx, &tmp);
368
369 if (g_shfile_async_close.num_pending > 0)
370 {
371 DWORD dwRet;
372
373/** @todo help out? */
374 if (!g_shfile_async_close.signal_sync)
375 {
376 BOOL rc = ResetEvent(g_shfile_async_close.evt_sync);
377 kHlpAssert(rc); K_NOREF(rc);
378
379 g_shfile_async_close.signal_sync = K_TRUE;
380 }
381
382 shmtx_leave(&g_shfile_async_close.mtx, &tmp);
383
384 TRACE2((NULL, "shfile_async_close_sync: Calling WaitForSingleObject...\n"));
385 dwRet = WaitForSingleObject(g_shfile_async_close.evt_sync, 10000 /*ms*/);
386 kHlpAssert(dwRet == WAIT_OBJECT_0);
387 kHlpAssert(g_shfile_async_close.num_pending == 0);
388 TRACE2((NULL, "shfile_async_close_sync: WaitForSingleObject returned %u...\n", dwRet));
389 }
390 else
391 shmtx_leave(&g_shfile_async_close.mtx, &tmp);
392}
393
394# endif /* K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) */
395
396/**
397 * Close the specified native handle.
398 *
399 * @param native The native file handle.
400 * @param file The file table entry if available.
401 * @param inheritable The inheritability of the handle on windows, K_FALSE elsewhere.
402 */
403static void shfile_native_close(intptr_t native, shfile *file, KBOOL inheritable)
404{
405# if K_OS == K_OS_WINDOWS
406# ifdef KASH_ASYNC_CLOSE_HANDLE
407 /*
408 * CloseHandle may take several milliseconds on NTFS after we've appended
409 * a few bytes to a file. When a script uses lots of 'echo line-text >> file'
410 * we end up executing very slowly, even if 'echo' is builtin and the statement
411 * requires no subshells to be spawned.
412 *
413 * So, detect problematic handles and do CloseHandle asynchronously. When
414 * executing a child process, we probably will have to make sure the CloseHandle
415 * operation has completed before we do ResumeThread on the child to make 100%
416 * sure we can't have any sharing conflicts or end up with incorrect CRT stat()
417 * results. Sharing conflicts are not a problem for builtin commands, and for
418 * stat we do not use the CRT code but ntstat.c/h and it seems to work fine
419 * (might be a tiny bit slower, so (TODO) might be worth reducing what we ask for).
420 *
421 * If child processes are spawned using handle inheritance and the handle in
422 * question is inheritable, we will have to fix the inheriability before pushing
423 * on the async-close queue. This shouldn't have the CloseHandle issues.
424 */
425 if ( file
426 && (file->shflags & (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_TYPE_MASK))
427 == (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_FILE))
428 {
429 if (inheritable)
430 native = (intptr_t)shfile_set_inherit_win(file, 0);
431 if (shfile_async_close_submit((HANDLE)native))
432 return;
433 }
434
435 /*
436 * Otherwise close it right here:
437 */
438# endif
439 {
440# ifdef DEBUG
441 KU64 ns = shfile_nano_ts();
442 BOOL fRc = CloseHandle((HANDLE)native);
443 kHlpAssert(fRc); K_NOREF(fRc);
444 ns = shfile_nano_ts() - ns;
445 if (ns > 1000000)
446 TRACE2((NULL, "shfile_native_close: %u ns %p oflags=%#x %s\n",
447 ns, native, file ? file->oflags : 0, file ? file->dbgname : NULL));
448# else
449 BOOL fRc = CloseHandle((HANDLE)native);
450 kHlpAssert(fRc); K_NOREF(fRc);
451# endif
452 }
453
454# else /* K_OS != K_OS_WINDOWS */
455 int s = errno;
456 close(native);
457 errno = s;
458# endif /* K_OS != K_OS_WINDOWS */
459 K_NOREF(file);
460}
461
462/**
463 * Grows the descriptor table, making sure that it can hold @a fdMin,
464 *
465 * @returns The max(fdMin, fdFirstNew) on success, -1 on failure.
466 * @param pfdtab The table to grow.
467 * @param fdMin Grow to include this index.
468 */
469static int shfile_grow_tab_locked(shfdtab *pfdtab, int fdMin)
470{
471 /*
472 * Grow the descriptor table.
473 */
474 int fdRet = -1;
475 shfile *new_tab;
476 int new_size = pfdtab->size + SHFILE_GROW;
477 while (new_size < fdMin)
478 new_size += SHFILE_GROW;
479 TRACE2((NULL, "shfile_grow_tab_locked: old %p / %d entries; new size: %d\n", pfdtab->tab, pfdtab->size, new_size));
480 new_tab = sh_realloc(shthread_get_shell(), pfdtab->tab, new_size * sizeof(shfile));
481 if (new_tab)
482 {
483 int i;
484 for (i = pfdtab->size; i < new_size; i++)
485 {
486 new_tab[i].fd = -1;
487 new_tab[i].oflags = 0;
488 new_tab[i].shflags = 0;
489 new_tab[i].native = -1;
490# ifdef DEBUG
491 new_tab[i].dbgname = NULL;
492# endif
493 }
494
495 fdRet = pfdtab->size;
496 if (fdRet < fdMin)
497 fdRet = fdMin;
498
499 pfdtab->tab = new_tab;
500 pfdtab->size = new_size;
501
502 TRACE2((NULL, "shfile_grow_tab_locked: new %p / %d entries\n", pfdtab->tab, pfdtab->size));
503 }
504
505 return fdRet;
506}
507
508/**
509 * Inserts the file into the descriptor table.
510 *
511 * If we're out of memory and cannot extend the table, we'll close the
512 * file, set errno to EMFILE and return -1.
513 *
514 * @returns The file descriptor number. -1 and errno on failure.
515 * @param pfdtab The file descriptor table.
516 * @param native The native file handle.
517 * @param oflags The flags the it was opened/created with.
518 * @param shflags The shell file flags.
519 * @param fdMin The minimum file descriptor number, pass -1 if any is ok.
520 * @param who Who we're doing this for (for logging purposes).
521 * @param dbgname The filename, if applicable/available.
522 */
523static int shfile_insert(shfdtab *pfdtab, intptr_t native, unsigned oflags, unsigned shflags, int fdMin,
524 const char *who, const char *dbgname)
525{
526 shmtxtmp tmp;
527 int fd;
528 int i;
529
530 /*
531 * Fend of bad stuff.
532 */
533 if (fdMin >= SHFILE_MAX)
534 {
535 TRACE2((NULL, "shfile_insert: fdMin=%d is out of bounds; native=%p %s\n", fdMin, native, dbgname));
536 shfile_native_close(native, NULL, K_FALSE);
537 errno = EMFILE;
538 return -1;
539 }
540# if K_OS != K_OS_WINDOWS
541 if (fcntl((int)native, F_SETFD, fcntl((int)native, F_GETFD, 0) | FD_CLOEXEC) == -1)
542 {
543 int e = errno;
544 TRACE2((NULL, "shfile_insert: F_SETFD failed %d; native=%p %s\n", e, native, dbgname));
545 close((int)native);
546 errno = e;
547 return -1;
548 }
549# endif
550
551 shmtx_enter(&pfdtab->mtx, &tmp);
552
553 /*
554 * Search for a fitting unused location.
555 */
556 fd = -1;
557 for (i = fdMin >= 0 ? fdMin : 0; (unsigned)i < pfdtab->size; i++)
558 if (pfdtab->tab[i].fd == -1)
559 {
560 fd = i;
561 break;
562 }
563 if (fd == -1)
564 fd = shfile_grow_tab_locked(pfdtab, fdMin);
565
566 /*
567 * Fill in the entry if we've found one.
568 */
569 if (fd != -1)
570 {
571 pfdtab->tab[fd].fd = fd;
572 pfdtab->tab[fd].oflags = oflags;
573 pfdtab->tab[fd].shflags = shflags;
574 pfdtab->tab[fd].native = native;
575#ifdef DEBUG
576 pfdtab->tab[fd].dbgname = dbgname ? sh_strdup(NULL, dbgname) : NULL;
577#endif
578 TRACE2((NULL, "shfile_insert: #%d: native=%p oflags=%#x shflags=%#x %s\n", fd, native, oflags, shflags, dbgname));
579 }
580 else
581 shfile_native_close(native, NULL, K_FALSE);
582
583 shmtx_leave(&pfdtab->mtx, &tmp);
584
585 if (fd == -1)
586 errno = EMFILE;
587 (void)who;
588 return fd;
589}
590
591# if K_OS != K_OS_WINDOWS
592/**
593 * Makes a copy of the native file, closes the original, and inserts the copy
594 * into the descriptor table.
595 *
596 * If we're out of memory and cannot extend the table, we'll close the
597 * file, set errno to EMFILE and return -1.
598 *
599 * @returns The file descriptor number. -1 and errno on failure.
600 * @param pfdtab The file descriptor table.
601 * @param pnative The native file handle on input, -1 on output.
602 * @param oflags The flags the it was opened/created with.
603 * @param shflags The shell file flags.
604 * @param fdMin The minimum file descriptor number, pass -1 if any is ok.
605 * @param who Who we're doing this for (for logging purposes).
606 * @param dbgname The filename, if applicable/available.
607 */
608static int shfile_copy_insert_and_close(shfdtab *pfdtab, int *pnative, unsigned oflags, unsigned shflags, int fdMin,
609 const char *who, const char *dbgname)
610{
611 int fd = -1;
612 int s = errno;
613 int native_copy = fcntl(*pnative, F_DUPFD, SHFILE_UNIX_MIN_FD);
614 close(*pnative);
615 *pnative = -1;
616 errno = s;
617
618 if (native_copy != -1)
619 fd = shfile_insert(pfdtab, native_copy, oflags, shflags, fdMin, who, dbgname);
620 return fd;
621}
622# endif /* !K_OS_WINDOWS */
623
624/**
625 * Gets a file descriptor and lock the file descriptor table.
626 *
627 * @returns Pointer to the file and table ownership on success,
628 * NULL and errno set to EBADF on failure.
629 * @param pfdtab The file descriptor table.
630 * @param fd The file descriptor number.
631 * @param ptmp See shmtx_enter.
632 */
633static shfile *shfile_get(shfdtab *pfdtab, int fd, shmtxtmp *ptmp)
634{
635 shfile *file = NULL;
636 if ( fd >= 0
637 && (unsigned)fd < pfdtab->size)
638 {
639 shmtx_enter(&pfdtab->mtx, ptmp);
640 if ((unsigned)fd < pfdtab->size
641 && pfdtab->tab[fd].fd != -1)
642 file = &pfdtab->tab[fd];
643 else
644 shmtx_leave(&pfdtab->mtx, ptmp);
645 }
646 if (!file)
647 errno = EBADF;
648 return file;
649}
650
651/**
652 * Puts back a file descriptor and releases the table ownership.
653 *
654 * @param pfdtab The file descriptor table.
655 * @param file The file.
656 * @param ptmp See shmtx_leave.
657 */
658static void shfile_put(shfdtab *pfdtab, shfile *file, shmtxtmp *ptmp)
659{
660 shmtx_leave(&pfdtab->mtx, ptmp);
661 kHlpAssert(file);
662 (void)file;
663}
664
665/**
666 * Constructs a path from the current directory and the passed in path.
667 *
668 * @returns 0 on success, -1 and errno set to ENAMETOOLONG or EINVAL on failure.
669 *
670 * @param pfdtab The file descriptor table
671 * @param path The path the caller supplied.
672 * @param buf Where to put the path. This is assumed to be SHFILE_MAX_PATH
673 * chars in size.
674 */
675int shfile_make_path(shfdtab *pfdtab, const char *path, char *buf)
676{
677 size_t path_len = strlen(path);
678 if (path_len == 0)
679 {
680 errno = EINVAL;
681 return -1;
682 }
683 if (path_len >= SHFILE_MAX_PATH)
684 {
685 errno = ENAMETOOLONG;
686 return -1;
687 }
688 if ( *path == '/'
689# if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
690 || *path == '\\'
691 || ( *path
692 && path[1] == ':'
693 && ( (*path >= 'A' && *path <= 'Z')
694 || (*path >= 'a' && *path <= 'z')))
695# endif
696 )
697 {
698 memcpy(buf, path, path_len + 1);
699 }
700 else
701 {
702 size_t cwd_len;
703 shmtxtmp tmp;
704
705 shmtx_enter(&pfdtab->mtx, &tmp);
706
707 cwd_len = strlen(pfdtab->cwd);
708 memcpy(buf, pfdtab->cwd, cwd_len);
709
710 shmtx_leave(&pfdtab->mtx, &tmp);
711
712 if (cwd_len + path_len + 1 >= SHFILE_MAX_PATH)
713 {
714 errno = ENAMETOOLONG;
715 return -1;
716 }
717 if ( !cwd_len
718 || buf[cwd_len - 1] != '/')
719 buf[cwd_len++] = '/';
720 memcpy(buf + cwd_len, path, path_len + 1);
721 }
722
723# if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
724 if (!strcmp(buf, "/dev/null"))
725 strcpy(buf, "NUL");
726# endif
727 return 0;
728}
729
730# if K_OS == K_OS_WINDOWS
731
732/**
733 * Adjusts the file name if it ends with a trailing directory slash.
734 *
735 * Windows APIs doesn't like trailing slashes.
736 *
737 * @returns 1 if it has a directory slash, 0 if not.
738 *
739 * @param abspath The path to adjust (SHFILE_MAX_PATH).
740 */
741static int shfile_trailing_slash_hack(char *abspath)
742{
743 /*
744 * Anything worth adjust here?
745 */
746 size_t path_len = strlen(abspath);
747 if ( path_len == 0
748 || ( abspath[path_len - 1] != '/'
749# if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
750 && abspath[path_len - 1] != '\\'
751# endif
752 )
753 )
754 return 0;
755
756 /*
757 * Ok, make the adjustment.
758 */
759 if (path_len + 2 <= SHFILE_MAX_PATH)
760 {
761 /* Add a '.' to the end. */
762 abspath[path_len++] = '.';
763 abspath[path_len] = '\0';
764 }
765 else
766 {
767 /* No space for a dot, remove the slash if it's alone or just remove
768 one and add a dot like above. */
769 if ( abspath[path_len - 2] != '/'
770# if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
771 && abspath[path_len - 2] != '\\'
772# endif
773 )
774 abspath[--path_len] = '\0';
775 else
776 abspath[path_len - 1] = '.';
777 }
778
779 return 1;
780}
781
782
783/**
784 * Converts a DOS(/Windows) error code to errno,
785 * assigning it to errno.
786 *
787 * @returns -1
788 * @param err The DOS error.
789 */
790static int shfile_dos2errno(int err)
791{
792 switch (err)
793 {
794 case ERROR_BAD_ENVIRONMENT: errno = E2BIG; break;
795 case ERROR_ACCESS_DENIED: errno = EACCES; break;
796 case ERROR_CURRENT_DIRECTORY: errno = EACCES; break;
797 case ERROR_LOCK_VIOLATION: errno = EACCES; break;
798 case ERROR_NETWORK_ACCESS_DENIED: errno = EACCES; break;
799 case ERROR_CANNOT_MAKE: errno = EACCES; break;
800 case ERROR_FAIL_I24: errno = EACCES; break;
801 case ERROR_DRIVE_LOCKED: errno = EACCES; break;
802 case ERROR_SEEK_ON_DEVICE: errno = EACCES; break;
803 case ERROR_NOT_LOCKED: errno = EACCES; break;
804 case ERROR_LOCK_FAILED: errno = EACCES; break;
805 case ERROR_NO_PROC_SLOTS: errno = EAGAIN; break;
806 case ERROR_MAX_THRDS_REACHED: errno = EAGAIN; break;
807 case ERROR_NESTING_NOT_ALLOWED: errno = EAGAIN; break;
808 case ERROR_INVALID_HANDLE: errno = EBADF; break;
809 case ERROR_INVALID_TARGET_HANDLE: errno = EBADF; break;
810 case ERROR_DIRECT_ACCESS_HANDLE: errno = EBADF; break;
811 case ERROR_WAIT_NO_CHILDREN: errno = ECHILD; break;
812 case ERROR_CHILD_NOT_COMPLETE: errno = ECHILD; break;
813 case ERROR_FILE_EXISTS: errno = EEXIST; break;
814 case ERROR_ALREADY_EXISTS: errno = EEXIST; break;
815 case ERROR_INVALID_FUNCTION: errno = EINVAL; break;
816 case ERROR_INVALID_ACCESS: errno = EINVAL; break;
817 case ERROR_INVALID_DATA: errno = EINVAL; break;
818 case ERROR_INVALID_PARAMETER: errno = EINVAL; break;
819 case ERROR_NEGATIVE_SEEK: errno = EINVAL; break;
820 case ERROR_TOO_MANY_OPEN_FILES: errno = EMFILE; break;
821 case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
822 case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
823 case ERROR_INVALID_DRIVE: errno = ENOENT; break;
824 case ERROR_NO_MORE_FILES: errno = ENOENT; break;
825 case ERROR_BAD_NETPATH: errno = ENOENT; break;
826 case ERROR_BAD_NET_NAME: errno = ENOENT; break;
827 case ERROR_BAD_PATHNAME: errno = ENOENT; break;
828 case ERROR_FILENAME_EXCED_RANGE: errno = ENOENT; break;
829 case ERROR_BAD_FORMAT: errno = ENOEXEC; break;
830 case ERROR_ARENA_TRASHED: errno = ENOMEM; break;
831 case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break;
832 case ERROR_INVALID_BLOCK: errno = ENOMEM; break;
833 case ERROR_NOT_ENOUGH_QUOTA: errno = ENOMEM; break;
834 case ERROR_DISK_FULL: errno = ENOSPC; break;
835 case ERROR_DIR_NOT_EMPTY: errno = ENOTEMPTY; break;
836 case ERROR_BROKEN_PIPE: errno = EPIPE; break;
837 case ERROR_NOT_SAME_DEVICE: errno = EXDEV; break;
838 default: errno = EINVAL; break;
839 }
840 return -1;
841}
842
843/**
844 * Converts an NT status code to errno,
845 * assigning it to errno.
846 *
847 * @returns -1
848 * @param rcNt The NT status code.
849 */
850static int shfile_nt2errno(NTSTATUS rcNt)
851{
852 switch (rcNt)
853 {
854 default: errno = EINVAL; break;
855 }
856 return -1;
857}
858
859DWORD shfile_query_handle_access_mask(HANDLE h, PACCESS_MASK pMask)
860{
861 MY_OBJECT_BASIC_INFORMATION BasicInfo;
862 NTSTATUS rcNt;
863
864 if (!g_pfnNtQueryObject)
865 return ERROR_NOT_SUPPORTED;
866
867 rcNt = g_pfnNtQueryObject(h, MY_ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL);
868 if (rcNt >= 0)
869 {
870 *pMask = BasicInfo.GrantedAccess;
871 return NO_ERROR;
872 }
873 if (rcNt != STATUS_INVALID_HANDLE)
874 return ERROR_GEN_FAILURE;
875 return ERROR_INVALID_HANDLE;
876}
877
878# endif /* K_OS == K_OS_WINDOWS */
879
880#endif /* SHFILE_IN_USE */
881
882/**
883 * Converts DOS slashes to UNIX slashes if necessary.
884 *
885 * @param pszPath The path to fix.
886 */
887K_INLINE void shfile_fix_slashes(char *pszPath)
888{
889#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
890 while ((pszPath = strchr(pszPath, '\\')))
891 *pszPath++ = '/';
892#else
893 (void)pszPath;
894#endif
895}
896
897/**
898 * Initializes the global variables in this file.
899 */
900static void shfile_init_globals(void)
901{
902#if K_OS == K_OS_WINDOWS
903 if (!g_shfile_globals_initialized)
904 {
905 HMODULE hNtDll = GetModuleHandle("NTDLL");
906 g_pfnNtQueryObject = (PFN_NtQueryObject) GetProcAddress(hNtDll, "NtQueryObject");
907 g_pfnNtQueryDirectoryFile = (PFN_NtQueryDirectoryFile)GetProcAddress(hNtDll, "NtQueryDirectoryFile");
908 g_pfnRtlUnicodeStringToAnsiString = (PFN_RtlUnicodeStringToAnsiString)GetProcAddress(hNtDll, "RtlUnicodeStringToAnsiString");
909 if ( !g_pfnRtlUnicodeStringToAnsiString
910 || !g_pfnNtQueryDirectoryFile)
911 {
912 /* fatal error */
913 }
914
915# ifdef KASH_ASYNC_CLOSE_HANDLE
916 /*
917 * Init the async CloseHandle state.
918 */
919 shmtx_init(&g_shfile_async_close.mtx);
920 g_shfile_async_close.evt_workers = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/);
921 g_shfile_async_close.evt_sync = CreateEventW(NULL, TRUE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/);
922 if ( !g_shfile_async_close.evt_workers
923 || !g_shfile_async_close.evt_sync)
924 {
925 fprintf(stderr, "fatal error: CreateEventW failed: %u\n", GetLastError());
926 _exit(19);
927 }
928 g_shfile_async_close.idx_read = 0;
929 g_shfile_async_close.idx_write = 0;
930 g_shfile_async_close.num_pending = 0;
931 g_shfile_async_close.terminate_threads = K_FALSE;
932 g_shfile_async_close.signal_sync = K_FALSE;
933 g_shfile_async_close.num_threads = 0;
934 {
935 unsigned i = K_ELEMENTS(g_shfile_async_close.handles);
936 while (i-- > 0)
937 g_shfile_async_close.handles[i] = NULL;
938 i = K_ELEMENTS(g_shfile_async_close.threads);
939 while (i-- > 0)
940 g_shfile_async_close.threads[i] = NULL;
941 }
942# endif
943
944 g_shfile_globals_initialized = 1;
945 }
946#endif
947}
948
949/**
950 * Initializes a file descriptor table.
951 *
952 * @returns 0 on success, -1 and errno on failure.
953 * @param pfdtab The table to initialize.
954 * @param inherit File descriptor table to inherit from. If not specified
955 * we will inherit from the current process as it were.
956 */
957int shfile_init(shfdtab *pfdtab, shfdtab *inherit)
958{
959 int rc;
960
961 shfile_init_globals();
962
963 pfdtab->cwd = NULL;
964 pfdtab->size = 0;
965 pfdtab->tab = NULL;
966 rc = shmtx_init(&pfdtab->mtx);
967 if (!rc)
968 {
969#ifdef SHFILE_IN_USE
970 /* Get CWD with unix slashes. */
971 if (!inherit)
972 {
973 char buf[SHFILE_MAX_PATH];
974 if (getcwd(buf, sizeof(buf)))
975 {
976 shfile_fix_slashes(buf);
977 pfdtab->cwd = sh_strdup(NULL, buf);
978 }
979 if (pfdtab->cwd)
980 {
981# if K_OS == K_OS_WINDOWS
982 static const struct
983 {
984 DWORD dwStdHandle;
985 unsigned fFlags;
986 } aStdHandles[3] =
987 {
988 { STD_INPUT_HANDLE, _O_RDONLY },
989 { STD_OUTPUT_HANDLE, _O_WRONLY },
990 { STD_ERROR_HANDLE, _O_WRONLY }
991 };
992 int i;
993 STARTUPINFO Info;
994 ACCESS_MASK Mask;
995 DWORD dwErr;
996
997 rc = 0;
998
999 /* Try pick up the Visual C++ CRT file descriptor info. */
1000 __try {
1001 GetStartupInfo(&Info);
1002 } __except (EXCEPTION_EXECUTE_HANDLER) {
1003 memset(&Info, 0, sizeof(Info));
1004 }
1005
1006 if ( Info.cbReserved2 > sizeof(int)
1007 && (uintptr_t)Info.lpReserved2 >= 0x1000
1008 && (i = *(int *)Info.lpReserved2) >= 1
1009 && i <= 2048
1010 && ( Info.cbReserved2 == i * 5 + 4
1011 //|| Info.cbReserved2 == i * 5 + 1 - check the cygwin sources.
1012 || Info.cbReserved2 == i * 9 + 4))
1013 {
1014 uint8_t *paf = (uint8_t *)Info.lpReserved2 + sizeof(int);
1015 int dwPerH = 1 + (Info.cbReserved2 == i * 9 + 4);
1016 DWORD *ph = (DWORD *)(paf + i) + dwPerH * i;
1017 HANDLE aStdHandles2[3];
1018 int j;
1019
1020 //if (Info.cbReserved2 == i * 5 + 1) - check the cygwin sources.
1021 // i--;
1022
1023 for (j = 0; j < 3; j++)
1024 aStdHandles2[j] = GetStdHandle(aStdHandles[j].dwStdHandle);
1025
1026 while (i-- > 0)
1027 {
1028 ph -= dwPerH;
1029
1030 if ( (paf[i] & (FOPEN | FNOINHERIT)) == FOPEN
1031 && *ph != (uint32_t)(uintptr_t)INVALID_HANDLE_VALUE
1032 && *ph != 0)
1033 {
1034 HANDLE h = (HANDLE)(intptr_t)*ph;
1035 int fd2;
1036 int fFlags;
1037 int fFlags2;
1038
1039 if ( h == aStdHandles2[j = 0]
1040 || h == aStdHandles2[j = 1]
1041 || h == aStdHandles2[j = 2])
1042 fFlags = aStdHandles[j].fFlags;
1043 else
1044 {
1045 dwErr = shfile_query_handle_access_mask(h, &Mask);
1046 if (dwErr == ERROR_INVALID_HANDLE)
1047 continue;
1048 if (dwErr == NO_ERROR)
1049 {
1050 fFlags = 0;
1051 if ( (Mask & (GENERIC_READ | FILE_READ_DATA))
1052 && (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)))
1053 fFlags |= O_RDWR;
1054 else if (Mask & (GENERIC_READ | FILE_READ_DATA))
1055 fFlags |= O_RDONLY;
1056 else if (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA))
1057 fFlags |= O_WRONLY;
1058 else
1059 fFlags |= O_RDWR;
1060 if ((Mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) == FILE_APPEND_DATA)
1061 fFlags |= O_APPEND;
1062 }
1063 else
1064 fFlags = O_RDWR;
1065 }
1066
1067 if (paf[i] & FPIPE)
1068 fFlags2 = SHFILE_FLAGS_PIPE;
1069 else if (paf[i] & FDEV)
1070 fFlags2 = SHFILE_FLAGS_TTY;
1071 else
1072 fFlags2 = 0;
1073
1074 fd2 = shfile_insert(pfdtab, (intptr_t)h, fFlags, fFlags2, i, "shtab_init", NULL);
1075 kHlpAssert(fd2 == i); (void)fd2;
1076 if (fd2 != i)
1077 rc = -1;
1078 }
1079 }
1080 }
1081
1082 /* Check the three standard handles. */
1083 for (i = 0; i < 3; i++)
1084 if ( (unsigned)i >= pfdtab->size
1085 || pfdtab->tab[i].fd == -1)
1086 {
1087 HANDLE hFile = GetStdHandle(aStdHandles[i].dwStdHandle);
1088 if ( hFile != INVALID_HANDLE_VALUE
1089 && hFile != NULL)
1090 {
1091 DWORD dwType = GetFileType(hFile);
1092 unsigned fFlags = aStdHandles[i].fFlags;
1093 unsigned fFlags2;
1094 int fd2;
1095 if (dwType == FILE_TYPE_CHAR)
1096 fFlags2 = SHFILE_FLAGS_TTY;
1097 else if (dwType == FILE_TYPE_PIPE)
1098 fFlags2 = SHFILE_FLAGS_PIPE;
1099 else
1100 fFlags2 = SHFILE_FLAGS_FILE;
1101 fd2 = shfile_insert(pfdtab, (intptr_t)hFile, fFlags, fFlags2, i, "shtab_init", NULL);
1102 kHlpAssert(fd2 == i); (void)fd2;
1103 if (fd2 != i)
1104 rc = -1;
1105 }
1106 }
1107# else
1108 /*
1109 * Annoying...
1110 */
1111 int fd;
1112
1113 for (fd = 0; fd < 10; fd++)
1114 {
1115 int oflags = fcntl(fd, F_GETFL, 0);
1116 if (oflags != -1)
1117 {
1118 int cox = fcntl(fd, F_GETFD, 0);
1119 struct stat st;
1120 if ( cox != -1
1121 && fstat(fd, &st) != -1)
1122 {
1123 int native;
1124 int fd2;
1125 int fFlags2 = 0;
1126 if (cox & FD_CLOEXEC)
1127 fFlags2 |= SHFILE_FLAGS_CLOSE_ON_EXEC;
1128 if (S_ISREG(st.st_mode))
1129 fFlags2 |= SHFILE_FLAGS_FILE;
1130 else if (S_ISDIR(st.st_mode))
1131 fFlags2 |= SHFILE_FLAGS_DIR;
1132 else if (S_ISCHR(st.st_mode))
1133 fFlags2 |= SHFILE_FLAGS_TTY;
1134 else if (S_ISFIFO(st.st_mode))
1135 fFlags2 |= SHFILE_FLAGS_PIPE;
1136 else
1137 fFlags2 |= SHFILE_FLAGS_TTY;
1138
1139 native = fcntl(fd, F_DUPFD, SHFILE_UNIX_MIN_FD);
1140 if (native == -1)
1141 native = fd;
1142 fd2 = shfile_insert(pfdtab, native, oflags, fFlags2, fd, "shtab_init", NULL);
1143 kHlpAssert(fd2 == fd); (void)fd2;
1144 if (fd2 != fd)
1145 rc = -1;
1146 if (native != fd)
1147 close(fd);
1148 }
1149 }
1150 }
1151
1152# endif
1153 }
1154 else
1155 rc = -1;
1156 }
1157 else
1158 {
1159 /*
1160 * Inherit from parent shell's file table.
1161 */
1162 shfile const *src;
1163 shfile *dst;
1164 shmtxtmp tmp;
1165 unsigned fdcount;
1166 unsigned fd;
1167
1168 shmtx_enter(&inherit->mtx, &tmp);
1169
1170 /* allocate table and cwd: */
1171 fdcount = inherit->size;
1172 pfdtab->tab = dst = (shfile *)(fdcount ? sh_calloc(NULL, sizeof(pfdtab->tab[0]), fdcount) : NULL);
1173 pfdtab->cwd = sh_strdup(NULL, inherit->cwd);
1174 if ( pfdtab->cwd
1175 && (pfdtab->tab || fdcount == 0))
1176 {
1177 /* duplicate table entries: */
1178 for (fd = 0, src = inherit->tab; fd < fdcount; fd++, src++, dst++)
1179 if (src->fd == -1)
1180 dst->native = dst->fd = -1;
1181 else
1182 {
1183# if K_OS == K_OS_WINDOWS
1184# ifdef SH_FORKED_MODE
1185 KBOOL const cox = !!(src->shflags & SHFILE_FLAGS_CLOSE_ON_EXEC);
1186# else
1187 KBOOL const cox = K_TRUE;
1188# endif
1189# endif
1190 *dst = *src;
1191# ifdef DEBUG
1192 if (src->dbgname)
1193 dst->dbgname = sh_strdup(NULL, src->dbgname);
1194# endif
1195# if K_OS == K_OS_WINDOWS
1196 if (DuplicateHandle(GetCurrentProcess(),
1197 (HANDLE)src->native,
1198 GetCurrentProcess(),
1199 (HANDLE *)&dst->native,
1200 0,
1201 FALSE /* bInheritHandle */,
1202 DUPLICATE_SAME_ACCESS))
1203 TRACE2((NULL, "shfile_init: %d (%#x, %#x) %p (was %p)\n",
1204 dst->fd, dst->oflags, dst->shflags, dst->native, src->native));
1205 else
1206 {
1207 dst->native = (intptr_t)INVALID_HANDLE_VALUE;
1208 rc = shfile_dos2errno(GetLastError());
1209 TRACE2((NULL, "shfile_init: %d (%#x, %#x) %p - failed %d / %u!\n",
1210 dst->fd, dst->oflags, dst->shflags, src->native, rc, GetLastError()));
1211 break;
1212 }
1213
1214# elif K_OS == K_OS_LINUX /* 2.6.27 / glibc 2.9 */ || K_OS == K_OS_FREEBSD /* 10.0 */ || K_OS == K_OS_NETBSD /* 6.0 */
1215 dst->native = dup3(src->native, -1, cox ? O_CLOEXEC : 0);
1216# else
1217 if (cox)
1218 {
1219# ifndef SH_FORKED_MODE
1220 shmtxtmp tmp2;
1221 shmtx_enter(&global_exec_something, &tmp)
1222# endif
1223 dst->native = dup2(src->native, -1);
1224 if (dst->native >= 0)
1225 rc = fcntl(dst->native, F_SETFD, FD_CLOEXEC);
1226# ifndef SH_FORKED_MODE
1227 shmtx_leave(&global_exec_something, &tmp)
1228# endif
1229 if (rc != 0)
1230 break;
1231 }
1232 else
1233 dst->native = dup2(src->native, -1);
1234 if (dst->native < 0)
1235 {
1236 rc = -1;
1237 break;
1238 }
1239# endif
1240 }
1241 }
1242 else
1243 rc = -1;
1244 pfdtab->size = fd;
1245 shmtx_leave(&inherit->mtx, &tmp);
1246 } /* inherit != NULL */
1247#endif
1248 }
1249 return rc;
1250}
1251
1252/**
1253 * Deletes the file descriptor table.
1254 *
1255 * Safe to call more than once.
1256 */
1257void shfile_uninit(shfdtab *pfdtab, int tracefd)
1258{
1259 if (!pfdtab)
1260 return;
1261
1262 if (pfdtab->tab)
1263 {
1264 unsigned left = pfdtab->size;
1265 struct shfile *pfd = pfdtab->tab;
1266 unsigned tracefdfound = 0;
1267 while (left-- > 0)
1268 {
1269 if (pfd->fd != -1)
1270 {
1271 if (pfd->fd != tracefd)
1272 {
1273#if K_OS == K_OS_WINDOWS
1274 BOOL rc = CloseHandle((HANDLE)pfd->native);
1275 kHlpAssert(rc == TRUE); K_NOREF(rc);
1276#else
1277 int rc = close((int)pfd->native);
1278 kHlpAssert(rc == 0); K_NOREF(rc);
1279#endif
1280 pfd->fd = -1;
1281 pfd->native = -1;
1282 }
1283 else
1284 tracefdfound++; /* there is only the one */
1285 }
1286 pfd++;
1287 }
1288
1289 if (!tracefdfound)
1290 { /* likely */ }
1291 else
1292 return;
1293
1294 sh_free(NULL, pfdtab->tab);
1295 pfdtab->tab = NULL;
1296 }
1297
1298 shmtx_delete(&pfdtab->mtx);
1299
1300 sh_free(NULL, pfdtab->cwd);
1301 pfdtab->cwd = NULL;
1302}
1303
1304#if K_OS == K_OS_WINDOWS && defined(SHFILE_IN_USE)
1305
1306/**
1307 * Changes the inheritability of a file descriptor, taking console handles into
1308 * account.
1309 *
1310 * @note This MAY change the native handle for the entry.
1311 *
1312 * @returns The native handle.
1313 * @param pfd The file descriptor to change.
1314 * @param set If set, make child processes inherit the handle, if clear
1315 * make them not inherit it.
1316 */
1317static HANDLE shfile_set_inherit_win(shfile *pfd, int set)
1318{
1319 HANDLE hFile = (HANDLE)pfd->native;
1320 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, set ? HANDLE_FLAG_INHERIT : 0))
1321 {
1322 /* SetHandleInformation doesn't work for console handles,
1323 so we have to duplicate the handle to change the
1324 inheritability. */
1325 DWORD err = GetLastError();
1326 if ( err == ERROR_INVALID_PARAMETER
1327 && DuplicateHandle(GetCurrentProcess(),
1328 hFile,
1329 GetCurrentProcess(),
1330 &hFile,
1331 0,
1332 set ? TRUE : FALSE /* bInheritHandle */,
1333 DUPLICATE_SAME_ACCESS))
1334 {
1335 TRACE2((NULL, "shfile_set_inherit_win: %p -> %p (set=%d)\n", pfd->native, hFile, set));
1336 if (!CloseHandle((HANDLE)pfd->native))
1337 kHlpAssert(0);
1338 pfd->native = (intptr_t)hFile;
1339 }
1340 else
1341 {
1342 err = GetLastError();
1343 kHlpAssert(0);
1344 hFile = (HANDLE)pfd->native;
1345 }
1346 }
1347 return hFile;
1348}
1349
1350# ifdef SH_FORKED_MODE
1351/**
1352 * Helper for shfork.
1353 *
1354 * @param pfdtab The file descriptor table.
1355 * @param set Whether to make all handles inheritable (1) or
1356 * to restore them to the rigth state (0).
1357 * @param hndls Where to store the three standard handles.
1358 */
1359void shfile_fork_win(shfdtab *pfdtab, int set, intptr_t *hndls)
1360{
1361 shmtxtmp tmp;
1362 unsigned i;
1363
1364 shmtx_enter(&pfdtab->mtx, &tmp);
1365 TRACE2((NULL, "shfile_fork_win: set=%d\n", set));
1366
1367 i = pfdtab->size;
1368 while (i-- > 0)
1369 {
1370 if (pfdtab->tab[i].fd == i)
1371 {
1372 shfile_set_inherit_win(&pfdtab->tab[i], set);
1373 if (set)
1374 TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n",
1375 i, pfdtab->tab[i].native, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags));
1376 }
1377 }
1378
1379 if (hndls)
1380 {
1381 for (i = 0; i < 3; i++)
1382 {
1383 if ( pfdtab->size > i
1384 && pfdtab->tab[i].fd == i)
1385 hndls[i] = pfdtab->tab[i].native;
1386 else
1387 hndls[i] = (intptr_t)INVALID_HANDLE_VALUE;
1388 TRACE2((NULL, "shfile_fork_win: i=%d size=%d fd=%d native=%d hndls[%d]=%p\n",
1389 i, pfdtab->size, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, hndls[i]));
1390 }
1391 }
1392
1393 shmtx_leave(&pfdtab->mtx, &tmp);
1394}
1395# endif /* SH_FORKED_MODE */
1396
1397/** shfile_exec_win helper that make sure there are no _O_APPEND handles. */
1398static KBOOL shfile_exec_win_no_append(shfdtab *pfdtab, unsigned count)
1399{
1400 unsigned i;
1401 for (i = 0; i < count; i++)
1402 if ( (pfdtab->tab[i].oflags & _O_APPEND)
1403 && (pfdtab->tab[i].oflags & (_O_WRONLY | _O_RDWR)))
1404 return K_FALSE;
1405 return K_TRUE;
1406}
1407
1408/**
1409 * Helper for sh_execve.
1410 *
1411 * This is called before and after CreateProcess. On the first call it
1412 * will mark the non-close-on-exec handles as inheritable and produce
1413 * the startup info for the CRT. On the second call, after CreateProcess,
1414 * it will restore the handle inheritability properties.
1415 *
1416 * @returns 0 on success, non-zero on failure.
1417 * @param pfdtab The file descriptor table.
1418 * @param prepare Which call, 1 if before, 0 if after and success, -1 if after on failure.
1419 * @param info The info structure.
1420 */
1421int shfile_exec_win(shfdtab *pfdtab, int prepare, shfdexecwin *info)
1422{
1423 STARTUPINFOA *strtinfo = (STARTUPINFOA *)info->strtinfo;
1424 int rc = 0;
1425 shmtxtmp tmp;
1426 unsigned count;
1427 unsigned i;
1428
1429 shmtx_enter(&pfdtab->mtx, &tmp);
1430 TRACE2((NULL, "shfile_exec_win: prepare=%p\n", prepare));
1431
1432 count = pfdtab->size < (0x10000-4) / (1 + sizeof(HANDLE))
1433 ? pfdtab->size
1434 : (0x10000-4) / (1 + sizeof(HANDLE));
1435 while ( count > 3
1436 && ( pfdtab->tab[count - 1].fd == -1
1437 || (pfdtab->tab[count - 1].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC)))
1438 count--;
1439
1440 if (prepare > 0)
1441 {
1442 if (count <= 3 && shfile_exec_win_no_append(pfdtab, count))
1443 {
1444 info->inherithandles = 0;
1445 info->startsuspended = 1;
1446 strtinfo->cbReserved2 = 0;
1447 strtinfo->lpReserved2 = NULL;
1448 }
1449 else
1450 {
1451 size_t cbData = sizeof(int) + count * (1 + sizeof(HANDLE));
1452 uint8_t *pbData = sh_malloc(shthread_get_shell(), cbData);
1453 uint8_t *paf = pbData + sizeof(int);
1454 HANDLE *pah = (HANDLE *)(paf + count);
1455
1456 info->inherithandles = 1;
1457# ifdef KASH_ASYNC_CLOSE_HANDLE
1458 info->startsuspended = g_shfile_async_close.num_pending > 0;
1459# else
1460 info->startsuspended = 0;
1461# endif
1462 strtinfo->cbReserved2 = (unsigned short)cbData;
1463 strtinfo->lpReserved2 = pbData;
1464
1465# ifndef SH_FORKED_MODE
1466 shmtx_leave(&pfdtab->mtx, &tmp); /* should be harmless as this isn't really necessary at all. */
1467 shmtx_enter(&g_sh_exec_inherit_mtx, &info->tmp);
1468 shmtx_enter(&pfdtab->mtx, &tmp);
1469# endif
1470
1471 *(int *)pbData = count;
1472
1473 i = count;
1474 while (i-- > 0)
1475 {
1476 if ( pfdtab->tab[i].fd == i
1477 && !(pfdtab->tab[i].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC))
1478 {
1479 HANDLE hFile = shfile_set_inherit_win(&pfdtab->tab[i], 1);
1480 TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n",
1481 i, hFile, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags));
1482 paf[i] = FOPEN;
1483 if (pfdtab->tab[i].oflags & _O_APPEND)
1484 paf[i] |= FAPPEND;
1485 if (pfdtab->tab[i].oflags & _O_TEXT)
1486 paf[i] |= FTEXT;
1487 switch (pfdtab->tab[i].shflags & SHFILE_FLAGS_TYPE_MASK)
1488 {
1489 case SHFILE_FLAGS_TTY: paf[i] |= FDEV; break;
1490 case SHFILE_FLAGS_PIPE: paf[i] |= FPIPE; break;
1491 }
1492 pah[i] = hFile;
1493 }
1494 else
1495 {
1496 paf[i] = 0;
1497 pah[i] = INVALID_HANDLE_VALUE;
1498 }
1499 }
1500 }
1501
1502 for (i = 0; i < 3; i++)
1503 {
1504 if ( i < count
1505 && pfdtab->tab[i].fd == i)
1506 {
1507 info->replacehandles[i] = 1;
1508 info->handles[i] = pfdtab->tab[i].native;
1509 }
1510 else
1511 {
1512 info->replacehandles[i] = 0;
1513 info->handles[i] = (intptr_t)INVALID_HANDLE_VALUE;
1514 }
1515 TRACE2((NULL, "shfile_exec_win: i=%d count=%d fd=%d native=%d hndls[%d]=\n",
1516 i, count, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, info->handles[i]));
1517 }
1518 }
1519 else
1520 {
1521 shfile *file = pfdtab->tab;
1522
1523 sh_free(NULL, strtinfo->lpReserved2);
1524 strtinfo->lpReserved2 = NULL;
1525
1526 i = count;
1527 if (prepare == 0)
1528 for (i = 0; i < count; i++, file++)
1529 {
1530 if ( file->fd == i
1531 && !(file->shflags & SHFILE_FLAGS_TRACE))
1532 {
1533 shfile_native_close(file->native, file, info->inherithandles);
1534
1535 file->fd = -1;
1536 file->oflags = 0;
1537 file->shflags = 0;
1538 file->native = -1;
1539# ifdef DEBUG
1540 sh_free(NULL, file->dbgname);
1541 file->dbgname = NULL;
1542# endif
1543 }
1544 }
1545 else if (info->inherithandles)
1546 for (i = 0; i < count; i++, file++)
1547 if ( file->fd == i
1548 && !(file->shflags & SHFILE_FLAGS_CLOSE_ON_EXEC))
1549 shfile_set_inherit_win(file, 0);
1550
1551# ifndef SH_FORKED_MODE
1552 if (info->inherithandles)
1553 shmtx_leave(&g_sh_exec_inherit_mtx, &info->tmp);
1554# endif
1555 }
1556
1557 shmtx_leave(&pfdtab->mtx, &tmp);
1558 return rc;
1559}
1560
1561#endif /* K_OS_WINDOWS */
1562
1563#if K_OS != K_OS_WINDOWS
1564/**
1565 * Prepare file handles for inherting before a execve call.
1566 *
1567 * This is only used in the normal mode, so we've forked and need not worry
1568 * about cleaning anything up after us. Nor do we need think about locking.
1569 *
1570 * @returns 0 on success, -1 on failure.
1571 */
1572int shfile_exec_unix(shfdtab *pfdtab)
1573{
1574 int rc = 0;
1575# ifdef SHFILE_IN_USE
1576 unsigned fd;
1577
1578 for (fd = 0; fd < pfdtab->size; fd++)
1579 {
1580 if ( pfdtab->tab[fd].fd != -1
1581 && !(pfdtab->tab[fd].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC) )
1582 {
1583 TRACE2((NULL, "shfile_exec_unix: %d => %d\n", pfdtab->tab[fd].native, fd));
1584 if (dup2(pfdtab->tab[fd].native, fd) < 0)
1585 {
1586 /* fatal_error(NULL, "shfile_exec_unix: failed to move %d to %d", pfdtab->tab[fd].fd, fd); */
1587 rc = -1;
1588 }
1589 }
1590 }
1591# endif
1592 return rc;
1593}
1594#endif /* !K_OS_WINDOWS */
1595
1596/**
1597 * open().
1598 */
1599int shfile_open(shfdtab *pfdtab, const char *name, unsigned flags, mode_t mode)
1600{
1601 int fd;
1602#ifdef SHFILE_IN_USE
1603 char absname[SHFILE_MAX_PATH];
1604# if K_OS == K_OS_WINDOWS
1605 HANDLE hFile;
1606 DWORD dwDesiredAccess;
1607 DWORD dwShareMode;
1608 DWORD dwCreationDisposition;
1609 DWORD dwFlagsAndAttributes;
1610 SECURITY_ATTRIBUTES SecurityAttributes;
1611
1612# ifndef _O_ACCMODE
1613# define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR)
1614# endif
1615 switch (flags & (_O_ACCMODE | _O_APPEND))
1616 {
1617 case _O_RDONLY: dwDesiredAccess = GENERIC_READ; break;
1618 case _O_RDONLY | _O_APPEND: dwDesiredAccess = GENERIC_READ; break;
1619 case _O_WRONLY: dwDesiredAccess = GENERIC_WRITE; break;
1620 case _O_WRONLY | _O_APPEND: dwDesiredAccess = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
1621 case _O_RDWR: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; break;
1622 case _O_RDWR | _O_APPEND: dwDesiredAccess = GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
1623
1624 default:
1625 RETURN_ERROR(-1, EINVAL, "invalid mode");
1626 }
1627
1628 dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
1629
1630 SecurityAttributes.nLength = sizeof(SecurityAttributes);
1631 SecurityAttributes.lpSecurityDescriptor = NULL;
1632 SecurityAttributes.bInheritHandle = FALSE;
1633
1634 if (flags & _O_CREAT)
1635 {
1636 if ((flags & (_O_EXCL | _O_TRUNC)) == (_O_EXCL | _O_TRUNC))
1637 RETURN_ERROR(-1, EINVAL, "_O_EXCL | _O_TRUNC");
1638
1639 if (flags & _O_TRUNC)
1640 dwCreationDisposition = CREATE_ALWAYS; /* not 100%, but close enough */
1641 else if (flags & _O_EXCL)
1642 dwCreationDisposition = CREATE_NEW;
1643 else
1644 dwCreationDisposition = OPEN_ALWAYS;
1645 }
1646 else if (flags & _O_TRUNC)
1647 dwCreationDisposition = TRUNCATE_EXISTING;
1648 else
1649 dwCreationDisposition = OPEN_EXISTING;
1650
1651 if (!(flags & _O_CREAT) || (mode & 0222))
1652 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
1653 else
1654 dwFlagsAndAttributes = FILE_ATTRIBUTE_READONLY;
1655
1656 fd = shfile_make_path(pfdtab, name, &absname[0]);
1657 if (!fd)
1658 {
1659# ifdef DEBUG
1660 KU64 ns = shfile_nano_ts();
1661# endif
1662 SetLastError(0);
1663 hFile = CreateFileA(absname,
1664 dwDesiredAccess,
1665 dwShareMode,
1666 &SecurityAttributes,
1667 dwCreationDisposition,
1668 dwFlagsAndAttributes,
1669 NULL /* hTemplateFile */);
1670# ifdef DEBUG
1671 ns = shfile_nano_ts() - ns;
1672 if (ns > 1000000)
1673 TRACE2((NULL, "shfile_open: %u ns hFile=%p (%d) %s\n", ns, hFile, GetLastError(), absname));
1674# endif
1675 if (hFile != INVALID_HANDLE_VALUE)
1676 fd = shfile_insert(pfdtab, (intptr_t)hFile, flags, 0, -1, "shfile_open", absname);
1677 else
1678 fd = shfile_dos2errno(GetLastError());
1679 }
1680
1681# else /* K_OS != K_OS_WINDOWS */
1682 fd = shfile_make_path(pfdtab, name, &absname[0]);
1683 if (!fd)
1684 {
1685 fd = open(absname, flags, mode);
1686 if (fd != -1)
1687 fd = shfile_copy_insert_and_close(pfdtab, &fd, flags, 0, -1, "shfile_open", absname);
1688 }
1689
1690# endif /* K_OS != K_OS_WINDOWS */
1691
1692#else
1693 fd = open(name, flags, mode);
1694#endif
1695
1696 TRACE2((NULL, "shfile_open(%p:{%s}, %#x, 0%o) -> %d [%d]\n", name, name, flags, mode, fd, errno));
1697 return fd;
1698}
1699
1700int shfile_pipe(shfdtab *pfdtab, int fds[2])
1701{
1702 int rc = -1;
1703#ifdef SHFILE_IN_USE
1704# if K_OS == K_OS_WINDOWS
1705 HANDLE hRead = INVALID_HANDLE_VALUE;
1706 HANDLE hWrite = INVALID_HANDLE_VALUE;
1707 SECURITY_ATTRIBUTES SecurityAttributes;
1708
1709 SecurityAttributes.nLength = sizeof(SecurityAttributes);
1710 SecurityAttributes.lpSecurityDescriptor = NULL;
1711 SecurityAttributes.bInheritHandle = FALSE;
1712
1713 fds[1] = fds[0] = -1;
1714 if (CreatePipe(&hRead, &hWrite, &SecurityAttributes, SHFILE_PIPE_SIZE))
1715 {
1716 fds[0] = shfile_insert(pfdtab, (intptr_t)hRead, O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-rd");
1717 if (fds[0] != -1)
1718 {
1719 fds[1] = shfile_insert(pfdtab, (intptr_t)hWrite, O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-wr");
1720 if (fds[1] != -1)
1721 rc = 0;
1722 }
1723
1724# else
1725 int native_fds[2];
1726
1727 fds[1] = fds[0] = -1;
1728 if (!pipe(native_fds))
1729 {
1730 fds[0] = shfile_copy_insert_and_close(pfdtab, &native_fds[0], O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-rd");
1731 if (fds[0] != -1)
1732 {
1733 fds[1] = shfile_copy_insert_and_close(pfdtab, &native_fds[1], O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-wr");
1734 if (fds[1] != -1)
1735 rc = 0;
1736 }
1737# endif
1738 if (fds[1] == -1)
1739 {
1740 int s = errno;
1741 if (fds[0] != -1)
1742 {
1743 shmtxtmp tmp;
1744 shmtx_enter(&pfdtab->mtx, &tmp);
1745 rc = fds[0];
1746 pfdtab->tab[rc].fd = -1;
1747 pfdtab->tab[rc].oflags = 0;
1748 pfdtab->tab[rc].shflags = 0;
1749 pfdtab->tab[rc].native = -1;
1750 shmtx_leave(&pfdtab->mtx, &tmp);
1751 }
1752
1753# if K_OS == K_OS_WINDOWS
1754 CloseHandle(hRead);
1755 CloseHandle(hWrite);
1756# else
1757 close(native_fds[0]);
1758 close(native_fds[1]);
1759# endif
1760 fds[0] = fds[1] = -1;
1761 errno = s;
1762 rc = -1;
1763 }
1764 }
1765 else
1766 {
1767# if K_OS == K_OS_WINDOWS
1768 errno = shfile_dos2errno(GetLastError());
1769# endif
1770 rc = -1;
1771 }
1772
1773#else
1774 rc = pipe(fds);
1775#endif
1776
1777 TRACE2((NULL, "shfile_pipe() -> %d{%d,%d} [%d]\n", rc, fds[0], fds[1], errno));
1778 return rc;
1779}
1780
1781/**
1782 * dup().
1783 */
1784int shfile_dup(shfdtab *pfdtab, int fd)
1785{
1786 return shfile_fcntl(pfdtab,fd, F_DUPFD, 0);
1787}
1788
1789/**
1790 * Move the file descriptor, closing any existing descriptor at @a fdto.
1791 *
1792 * @returns fdto on success, -1 and errno on failure.
1793 * @param pfdtab The file descriptor table.
1794 * @param fdfrom The descriptor to move.
1795 * @param fdto Where to move it.
1796 */
1797int shfile_movefd(shfdtab *pfdtab, int fdfrom, int fdto)
1798{
1799#ifdef SHFILE_IN_USE
1800 int rc;
1801 shmtxtmp tmp;
1802 shfile *file = shfile_get(pfdtab, fdfrom, &tmp);
1803 if (file)
1804 {
1805 /* prepare the new entry */
1806 if ((unsigned)fdto >= pfdtab->size)
1807 shfile_grow_tab_locked(pfdtab, fdto);
1808 if ((unsigned)fdto < pfdtab->size)
1809 {
1810 if (pfdtab->tab[fdto].fd != -1)
1811 shfile_native_close(pfdtab->tab[fdto].native, &pfdtab->tab[fdto], K_FALSE);
1812
1813 /* setup the target. */
1814 pfdtab->tab[fdto].fd = fdto;
1815 pfdtab->tab[fdto].oflags = file->oflags;
1816 pfdtab->tab[fdto].shflags = file->shflags;
1817 pfdtab->tab[fdto].native = file->native;
1818# ifdef DEBUG
1819 pfdtab->tab[fdto].dbgname = file->dbgname;
1820# endif
1821
1822 /* close the source. */
1823 file->fd = -1;
1824 file->oflags = 0;
1825 file->shflags = 0;
1826 file->native = -1;
1827# ifdef DEBUG
1828 file->dbgname = NULL;
1829# endif
1830
1831 rc = fdto;
1832 }
1833 else
1834 {
1835 errno = EMFILE;
1836 rc = -1;
1837 }
1838
1839 shfile_put(pfdtab, file, &tmp);
1840 }
1841 else
1842 rc = -1;
1843 return rc;
1844
1845#else
1846 int fdnew = dup2(fdfrom, fdto);
1847 if (fdnew >= 0)
1848 close(fdfrom);
1849 return fdnew;
1850#endif
1851}
1852
1853/**
1854 * Move the file descriptor to somewhere at @a fdMin or above.
1855 *
1856 * @returns the new file descriptor success, -1 and errno on failure.
1857 * @param pfdtab The file descriptor table.
1858 * @param fdfrom The descriptor to move.
1859 * @param fdMin The minimum descriptor.
1860 */
1861int shfile_movefd_above(shfdtab *pfdtab, int fdfrom, int fdMin)
1862{
1863#ifdef SHFILE_IN_USE
1864 int fdto;
1865 shmtxtmp tmp;
1866 shfile *file = shfile_get(pfdtab, fdfrom, &tmp);
1867 if (file)
1868 {
1869 /* find a new place */
1870 int i;
1871 fdto = -1;
1872 for (i = fdMin; (unsigned)i < pfdtab->size; i++)
1873 if (pfdtab->tab[i].fd == -1)
1874 {
1875 fdto = i;
1876 break;
1877 }
1878 if (fdto == -1)
1879 fdto = shfile_grow_tab_locked(pfdtab, fdMin);
1880 if (fdto != -1)
1881 {
1882 /* setup the target. */
1883 pfdtab->tab[fdto].fd = fdto;
1884 pfdtab->tab[fdto].oflags = file->oflags;
1885 pfdtab->tab[fdto].shflags = file->shflags;
1886 pfdtab->tab[fdto].native = file->native;
1887# ifdef DEBUG
1888 pfdtab->tab[fdto].dbgname = file->dbgname;
1889# endif
1890
1891 /* close the source. */
1892 file->fd = -1;
1893 file->oflags = 0;
1894 file->shflags = 0;
1895 file->native = -1;
1896# ifdef DEBUG
1897 file->dbgname = NULL;
1898# endif
1899 }
1900 else
1901 {
1902 errno = EMFILE;
1903 fdto = -1;
1904 }
1905
1906 shfile_put(pfdtab, file, &tmp);
1907 }
1908 else
1909 fdto = -1;
1910 return fdto;
1911
1912#else
1913 int fdnew = fcntl(fdfrom, F_DUPFD, fdMin);
1914 if (fdnew >= 0)
1915 close(fdfrom);
1916 return fdnew;
1917#endif
1918}
1919
1920/**
1921 * close().
1922 */
1923int shfile_close(shfdtab *pfdtab, unsigned fd)
1924{
1925 int rc;
1926#ifdef SHFILE_IN_USE
1927 shmtxtmp tmp;
1928 shfile *file = shfile_get(pfdtab, fd, &tmp);
1929 if (file)
1930 {
1931 shfile_native_close(file->native, file, K_FALSE);
1932
1933 file->fd = -1;
1934 file->oflags = 0;
1935 file->shflags = 0;
1936 file->native = -1;
1937# ifdef DEBUG
1938 sh_free(NULL, file->dbgname);
1939 file->dbgname = NULL;
1940# endif
1941
1942 shfile_put(pfdtab, file, &tmp);
1943 rc = 0;
1944 }
1945 else
1946 rc = -1;
1947
1948#else
1949 rc = close(fd);
1950#endif
1951
1952 TRACE2((NULL, "shfile_close(%d) -> %d [%d]\n", fd, rc, errno));
1953 return rc;
1954}
1955
1956/**
1957 * read().
1958 */
1959long shfile_read(shfdtab *pfdtab, int fd, void *buf, size_t len)
1960{
1961 long rc;
1962#ifdef SHFILE_IN_USE
1963 shmtxtmp tmp;
1964 shfile *file = shfile_get(pfdtab, fd, &tmp);
1965 if (file)
1966 {
1967# if K_OS == K_OS_WINDOWS
1968 DWORD dwRead = 0;
1969 if (ReadFile((HANDLE)file->native, buf, (DWORD)len, &dwRead, NULL))
1970 rc = dwRead;
1971 else
1972 rc = shfile_dos2errno(GetLastError());
1973# else
1974 rc = read(file->native, buf, len);
1975# endif
1976
1977 shfile_put(pfdtab, file, &tmp);
1978 }
1979 else
1980 rc = -1;
1981
1982#else
1983 rc = read(fd, buf, len);
1984#endif
1985 return rc;
1986}
1987
1988/**
1989 * write().
1990 */
1991long shfile_write(shfdtab *pfdtab, int fd, const void *buf, size_t len)
1992{
1993 long rc;
1994#ifdef SHFILE_IN_USE
1995 shmtxtmp tmp;
1996 shfile *file = shfile_get(pfdtab, fd, &tmp);
1997 if (file)
1998 {
1999# if K_OS == K_OS_WINDOWS
2000 DWORD dwWritten = 0;
2001 if (WriteFile((HANDLE)file->native, buf, (DWORD)len, &dwWritten, NULL))
2002 rc = dwWritten;
2003 else
2004 rc = shfile_dos2errno(GetLastError());
2005# else
2006 rc = write(file->native, buf, len);
2007# endif
2008
2009 file->shflags |= SHFILE_FLAGS_DIRTY; /* there should be no concurrent access, so this is safe. */
2010 shfile_put(pfdtab, file, &tmp);
2011 }
2012 else
2013 rc = -1;
2014
2015# ifdef DEBUG
2016 if (fd != shthread_get_shell()->tracefd)
2017 TRACE2((NULL, "shfile_write(%d,,%d) -> %d [%d]\n", fd, len, rc, errno));
2018# endif
2019
2020#else
2021 if (fd != shthread_get_shell()->tracefd)
2022 {
2023 int iSavedErrno = errno;
2024 struct stat s;
2025 int x;
2026 x = fstat(fd, &s);
2027 TRACE2((NULL, "shfile_write(%d) - %lu bytes (%d) - pos %lu - before; %o\n",
2028 fd, (long)s.st_size, x, (long)lseek(fd, 0, SEEK_CUR), s.st_mode ));
2029 K_NOREF(x);
2030 errno = iSavedErrno;
2031 }
2032
2033 rc = write(fd, buf, len);
2034#endif
2035 return rc;
2036}
2037
2038/**
2039 * lseek().
2040 */
2041long shfile_lseek(shfdtab *pfdtab, int fd, long off, int whench)
2042{
2043 long rc;
2044#ifdef SHFILE_IN_USE
2045 shmtxtmp tmp;
2046 shfile *file = shfile_get(pfdtab, fd, &tmp);
2047 if (file)
2048 {
2049# if K_OS == K_OS_WINDOWS
2050 kHlpAssert(SEEK_SET == FILE_BEGIN);
2051 kHlpAssert(SEEK_CUR == FILE_CURRENT);
2052 kHlpAssert(SEEK_END == FILE_END);
2053 rc = SetFilePointer((HANDLE)file->native, off, NULL, whench);
2054 if (rc == INVALID_SET_FILE_POINTER)
2055 rc = shfile_dos2errno(GetLastError());
2056# else
2057 rc = lseek(file->native, off, whench);
2058# endif
2059
2060 shfile_put(pfdtab, file, &tmp);
2061 }
2062 else
2063 rc = -1;
2064
2065#else
2066 rc = lseek(fd, off, whench);
2067#endif
2068
2069 return rc;
2070}
2071
2072int shfile_fcntl(shfdtab *pfdtab, int fd, int cmd, int arg)
2073{
2074 int rc;
2075#ifdef SHFILE_IN_USE
2076 shmtxtmp tmp;
2077 shfile *file = shfile_get(pfdtab, fd, &tmp);
2078 if (file)
2079 {
2080 switch (cmd)
2081 {
2082 case F_GETFL:
2083 rc = file->oflags;
2084 break;
2085
2086 case F_SETFL:
2087 {
2088 unsigned mask = O_NONBLOCK | O_APPEND | O_BINARY | O_TEXT;
2089# ifdef O_DIRECT
2090 mask |= O_DIRECT;
2091# endif
2092# ifdef O_ASYNC
2093 mask |= O_ASYNC;
2094# endif
2095# ifdef O_SYNC
2096 mask |= O_SYNC;
2097# endif
2098 if ((file->oflags & mask) == (arg & mask))
2099 rc = 0;
2100 else
2101 {
2102# if K_OS == K_OS_WINDOWS
2103 kHlpAssert(0);
2104 errno = EINVAL;
2105 rc = -1;
2106# else
2107 rc = fcntl(file->native, F_SETFL, arg);
2108 if (rc != -1)
2109 file->oflags = (file->oflags & ~mask) | (arg & mask);
2110# endif
2111 }
2112 break;
2113 }
2114
2115 case F_DUPFD:
2116 {
2117# if K_OS == K_OS_WINDOWS
2118 HANDLE hNew = INVALID_HANDLE_VALUE;
2119 if (DuplicateHandle(GetCurrentProcess(),
2120 (HANDLE)file->native,
2121 GetCurrentProcess(),
2122 &hNew,
2123 0,
2124 FALSE /* bInheritHandle */,
2125 DUPLICATE_SAME_ACCESS))
2126 rc = shfile_insert(pfdtab, (intptr_t)hNew, file->oflags, file->shflags, arg,
2127 "shfile_fcntl", SHFILE_DBGNAME(file->dbgname));
2128 else
2129 rc = shfile_dos2errno(GetLastError());
2130# else
2131 int nativeNew = fcntl(file->native, F_DUPFD, SHFILE_UNIX_MIN_FD);
2132 if (nativeNew != -1)
2133 rc = shfile_insert(pfdtab, nativeNew, file->oflags, file->shflags, arg,
2134 "shfile_fcntl", SHFILE_DBGNAME(file->dbgname));
2135 else
2136 rc = -1;
2137# endif
2138 break;
2139 }
2140
2141 default:
2142 errno = -EINVAL;
2143 rc = -1;
2144 break;
2145 }
2146
2147 shfile_put(pfdtab, file, &tmp);
2148 }
2149 else
2150 rc = -1;
2151
2152#else
2153 rc = fcntl(fd, cmd, arg);
2154#endif
2155
2156 switch (cmd)
2157 {
2158 case F_GETFL: TRACE2((NULL, "shfile_fcntl(%d,F_GETFL,ignored=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
2159 case F_SETFL: TRACE2((NULL, "shfile_fcntl(%d,F_SETFL,newflags=%#x) -> %d [%d]\n", fd, arg, rc, errno)); break;
2160 case F_DUPFD: TRACE2((NULL, "shfile_fcntl(%d,F_DUPFD,minfd=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
2161 default: TRACE2((NULL, "shfile_fcntl(%d,%d,%d) -> %d [%d]\n", fd, cmd, arg, rc, errno)); break;
2162 }
2163 return rc;
2164}
2165
2166int shfile_stat(shfdtab *pfdtab, const char *path, struct stat *pst)
2167{
2168#ifdef SHFILE_IN_USE
2169 char abspath[SHFILE_MAX_PATH];
2170 int rc;
2171 rc = shfile_make_path(pfdtab, path, &abspath[0]);
2172 if (!rc)
2173 {
2174# if K_OS == K_OS_WINDOWS
2175# if 1
2176 rc = birdStatFollowLink(abspath, pst);
2177# else
2178 int dir_slash = shfile_trailing_slash_hack(abspath);
2179 rc = stat(abspath, pst); /** @todo re-implement stat. */
2180 if (!rc && dir_slash && !S_ISDIR(pst->st_mode))
2181 {
2182 rc = -1;
2183 errno = ENOTDIR;
2184
2185 }
2186# endif
2187# else
2188 rc = stat(abspath, pst);
2189# endif
2190 }
2191 TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d] st_size=%llu st_mode=%o\n",
2192 path, rc, errno, (unsigned long long)pst->st_size, pst->st_mode));
2193 return rc;
2194#else
2195 return stat(path, pst);
2196#endif
2197}
2198
2199/**
2200 * @retval 1 if regular file.
2201 * @retval 0 if found but not a regular file.
2202 * @retval -1 and errno on failure
2203 */
2204int shfile_stat_isreg(shfdtab *pfdtab, const char *path)
2205{
2206#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
2207 char abspath[SHFILE_MAX_PATH];
2208 KU16 mode = 0;
2209 int rc = shfile_make_path(pfdtab, path, &abspath[0]);
2210 if (!rc)
2211 {
2212 rc = birdStatModeOnly(abspath, &mode, 0 /*fFollowLink*/);
2213 if (rc >= 0)
2214 rc = S_ISREG(mode) ? 1 : 0;
2215 }
2216 TRACE2((NULL, "shfile_stat_isreg(,%s,) -> %d [%d] st_mode=%o\n", path, rc, errno, mode));
2217 return rc;
2218#else
2219 struct stat st;
2220 int rc = shfile_stat(pfdtab, path, &st);
2221 if (rc >= 0)
2222 rc = S_ISREG(st.st_mode) ? 1 : 0;
2223 return rc;
2224#endif
2225}
2226
2227/**
2228 * Same as shfile_stat, but without the data structure.
2229 */
2230int shfile_stat_exists(shfdtab *pfdtab, const char *path)
2231{
2232#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
2233 char abspath[SHFILE_MAX_PATH];
2234 KU16 mode = 0;
2235 int rc = shfile_make_path(pfdtab, path, &abspath[0]);
2236 if (!rc)
2237 rc = birdStatModeOnly(abspath, &mode, 0 /*fFollowLink*/);
2238 TRACE2((NULL, "shfile_stat_exists(,%s,) -> %d [%d] st_mode=%o\n", path, rc, errno, mode));
2239 return rc;
2240#else
2241 struct stat ignored;
2242 return shfile_stat(pfdtab, path, &ignored);
2243#endif
2244}
2245
2246int shfile_lstat(shfdtab *pfdtab, const char *path, struct stat *pst)
2247{
2248 int rc;
2249#ifdef SHFILE_IN_USE
2250 char abspath[SHFILE_MAX_PATH];
2251
2252 rc = shfile_make_path(pfdtab, path, &abspath[0]);
2253 if (!rc)
2254 {
2255# if K_OS == K_OS_WINDOWS
2256# if 1
2257 rc = birdStatOnLink(abspath, pst);
2258# else
2259 int dir_slash = shfile_trailing_slash_hack(abspath);
2260 rc = stat(abspath, pst); /** @todo re-implement stat. */
2261 if (!rc && dir_slash && !S_ISDIR(pst->st_mode))
2262 {
2263 rc = -1;
2264 errno = ENOTDIR;
2265 }
2266# endif
2267# else
2268 rc = lstat(abspath, pst);
2269# endif
2270 }
2271#else
2272 rc = stat(path, pst);
2273#endif
2274 TRACE2((NULL, "shfile_lstat(,%s,) -> %d [%d] st_size=%llu st_mode=%o\n",
2275 path, rc, errno, (unsigned long long)pst->st_size, pst->st_mode));
2276 return rc;
2277}
2278
2279/**
2280 * chdir().
2281 */
2282int shfile_chdir(shfdtab *pfdtab, const char *path)
2283{
2284 int rc;
2285#ifdef SHFILE_IN_USE
2286 shinstance *psh = shthread_get_shell();
2287 char abspath[SHFILE_MAX_PATH];
2288
2289 rc = shfile_make_path(pfdtab, path, &abspath[0]);
2290 if (!rc)
2291 {
2292 char *abspath_copy = sh_strdup(psh, abspath);
2293 char *free_me = abspath_copy;
2294 rc = chdir(abspath);
2295 if (!rc)
2296 {
2297 shmtxtmp tmp;
2298 shmtx_enter(&pfdtab->mtx, &tmp);
2299
2300 shfile_fix_slashes(abspath_copy);
2301 free_me = pfdtab->cwd;
2302 pfdtab->cwd = abspath_copy;
2303
2304 shmtx_leave(&pfdtab->mtx, &tmp);
2305 }
2306 sh_free(psh, free_me);
2307 }
2308 else
2309 rc = -1;
2310#else
2311 rc = chdir(path);
2312#endif
2313
2314 TRACE2((NULL, "shfile_chdir(,%s) -> %d [%d]\n", path, rc, errno));
2315 return rc;
2316}
2317
2318/**
2319 * getcwd().
2320 */
2321char *shfile_getcwd(shfdtab *pfdtab, char *buf, int size)
2322{
2323 char *ret;
2324#ifdef SHFILE_IN_USE
2325
2326 ret = NULL;
2327 if (buf && !size)
2328 errno = -EINVAL;
2329 else
2330 {
2331 size_t cwd_size;
2332 shmtxtmp tmp;
2333 shmtx_enter(&pfdtab->mtx, &tmp);
2334
2335 cwd_size = strlen(pfdtab->cwd) + 1;
2336 if (buf)
2337 {
2338 if (cwd_size <= (size_t)size)
2339 ret = memcpy(buf, pfdtab->cwd, cwd_size);
2340 else
2341 errno = ERANGE;
2342 }
2343 else
2344 {
2345 if ((size_t)size < cwd_size)
2346 size = (int)cwd_size;
2347 ret = sh_malloc(shthread_get_shell(), size);
2348 if (ret)
2349 ret = memcpy(ret, pfdtab->cwd, cwd_size);
2350 else
2351 errno = ENOMEM;
2352 }
2353
2354 shmtx_leave(&pfdtab->mtx, &tmp);
2355 }
2356#else
2357 ret = getcwd(buf, size);
2358#endif
2359
2360 TRACE2((NULL, "shfile_getcwd(,%p,%d) -> %s [%d]\n", buf, size, ret, errno));
2361 return ret;
2362}
2363
2364/**
2365 * access().
2366 */
2367int shfile_access(shfdtab *pfdtab, const char *path, int type)
2368{
2369 int rc;
2370#ifdef SHFILE_IN_USE
2371 char abspath[SHFILE_MAX_PATH];
2372
2373 rc = shfile_make_path(pfdtab, path, &abspath[0]);
2374 if (!rc)
2375 {
2376# ifdef _MSC_VER
2377 if (type & X_OK)
2378 type = (type & ~X_OK) | R_OK;
2379# endif
2380 rc = access(abspath, type);
2381 }
2382#else
2383# ifdef _MSC_VER
2384 if (type & X_OK)
2385 type = (type & ~X_OK) | R_OK;
2386# endif
2387 rc = access(path, type);
2388#endif
2389
2390 TRACE2((NULL, "shfile_access(,%s,%#x) -> %d [%d]\n", path, type, rc, errno));
2391 return rc;
2392}
2393
2394/**
2395 * isatty()
2396 */
2397int shfile_isatty(shfdtab *pfdtab, int fd)
2398{
2399 int rc;
2400#ifdef SHFILE_IN_USE
2401 shmtxtmp tmp;
2402 shfile *file = shfile_get(pfdtab, fd, &tmp);
2403 if (file)
2404 {
2405# if K_OS == K_OS_WINDOWS
2406 rc = (file->shflags & SHFILE_FLAGS_TYPE_MASK) == SHFILE_FLAGS_TTY;
2407# else
2408 rc = isatty(file->native);
2409# endif
2410 shfile_put(pfdtab, file, &tmp);
2411 }
2412 else
2413 rc = 0;
2414#else
2415 rc = isatty(fd);
2416#endif
2417
2418 TRACE2((NULL, "isatty(%d) -> %d [%d]\n", fd, rc, errno));
2419 return rc;
2420}
2421
2422/**
2423 * fcntl F_SETFD / FD_CLOEXEC.
2424 */
2425int shfile_cloexec(shfdtab *pfdtab, int fd, int closeit)
2426{
2427 int rc;
2428#ifdef SHFILE_IN_USE
2429 shmtxtmp tmp;
2430 shfile *file = shfile_get(pfdtab, fd, &tmp);
2431 if (file)
2432 {
2433 if (closeit)
2434 file->shflags |= SHFILE_FLAGS_CLOSE_ON_EXEC;
2435 else
2436 file->shflags &= ~SHFILE_FLAGS_CLOSE_ON_EXEC;
2437 shfile_put(pfdtab, file, &tmp);
2438 rc = 0;
2439 }
2440 else
2441 rc = -1;
2442#else
2443 rc = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0)
2444 | (closeit ? FD_CLOEXEC : 0));
2445#endif
2446
2447 TRACE2((NULL, "shfile_cloexec(%d, %d) -> %d [%d]\n", fd, closeit, rc, errno));
2448 return rc;
2449}
2450
2451/**
2452 * Sets the SHFILE_FLAGS_TRACE flag.
2453 */
2454int shfile_set_trace(shfdtab *pfdtab, int fd)
2455{
2456 int rc;
2457#ifdef SHFILE_IN_USE
2458 shmtxtmp tmp;
2459 shfile *file = shfile_get(pfdtab, fd, &tmp);
2460 if (file)
2461 {
2462 file->shflags |= SHFILE_FLAGS_TRACE;
2463 shfile_put(pfdtab, file, &tmp);
2464 rc = 0;
2465 }
2466 else
2467 rc = -1;
2468#else
2469 rc = 0;
2470#endif
2471
2472 TRACE2((NULL, "shfile_set_trace(%d) -> %d\n", fd, rc));
2473 return rc;
2474}
2475
2476
2477int shfile_ioctl(shfdtab *pfdtab, int fd, unsigned long request, void *buf)
2478{
2479 int rc;
2480#ifdef SHFILE_IN_USE
2481 shmtxtmp tmp;
2482 shfile *file = shfile_get(pfdtab, fd, &tmp);
2483 if (file)
2484 {
2485# if K_OS == K_OS_WINDOWS
2486 rc = -1;
2487 errno = ENOSYS;
2488# else
2489 rc = ioctl(file->native, request, buf);
2490# endif
2491 shfile_put(pfdtab, file, &tmp);
2492 }
2493 else
2494 rc = -1;
2495#else
2496 rc = ioctl(fd, request, buf);
2497#endif
2498
2499 TRACE2((NULL, "ioctl(%d, %#x, %p) -> %d\n", fd, request, buf, rc));
2500 return rc;
2501}
2502
2503
2504mode_t shfile_get_umask(shfdtab *pfdtab)
2505{
2506 /** @todo */
2507 return 022;
2508}
2509
2510void shfile_set_umask(shfdtab *pfdtab, mode_t mask)
2511{
2512 /** @todo */
2513 (void)mask;
2514}
2515
2516
2517
2518shdir *shfile_opendir(shfdtab *pfdtab, const char *dir)
2519{
2520#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
2521 shdir *pdir = NULL;
2522
2523 TRACE2((NULL, "shfile_opendir: dir='%s'\n", dir));
2524 shfile_init_globals();
2525 if (g_pfnNtQueryDirectoryFile)
2526 {
2527 char abspath[SHFILE_MAX_PATH];
2528 if (shfile_make_path(pfdtab, dir, &abspath[0]) == 0)
2529 {
2530 HANDLE hFile;
2531 SECURITY_ATTRIBUTES SecurityAttributes;
2532
2533 SecurityAttributes.nLength = sizeof(SecurityAttributes);
2534 SecurityAttributes.lpSecurityDescriptor = NULL;
2535 SecurityAttributes.bInheritHandle = FALSE;
2536
2537 hFile = CreateFileA(abspath,
2538 GENERIC_READ,
2539 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
2540 &SecurityAttributes,
2541 OPEN_EXISTING,
2542 FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS,
2543 NULL /* hTemplateFile */);
2544 if (hFile != INVALID_HANDLE_VALUE)
2545 {
2546 pdir = (shdir *)sh_malloc(shthread_get_shell(), sizeof(*pdir));
2547 if (pdir)
2548 {
2549 pdir->pfdtab = pfdtab;
2550 pdir->native = hFile;
2551 pdir->off = ~(size_t)0;
2552 }
2553 else
2554 CloseHandle(hFile);
2555 }
2556 else
2557 {
2558 errno = shfile_dos2errno(GetLastError());
2559 TRACE2((NULL, "shfile_opendir: CreateFileA(%s) -> %d/%d\n", abspath, GetLastError(), errno));
2560 }
2561 }
2562 }
2563 else
2564 errno = ENOSYS;
2565 return pdir;
2566#else
2567 TRACE2((NULL, "shfile_opendir: dir='%s'\n", dir));
2568 return (shdir *)opendir(dir);
2569#endif
2570}
2571
2572shdirent *shfile_readdir(struct shdir *pdir)
2573{
2574#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
2575 if (pdir)
2576 {
2577 NTSTATUS rcNt;
2578
2579 if ( pdir->off == ~(size_t)0
2580 || pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) >= pdir->cb)
2581 {
2582 MY_IO_STATUS_BLOCK Ios;
2583
2584 memset(&Ios, 0, sizeof(Ios));
2585 rcNt = g_pfnNtQueryDirectoryFile(pdir->native,
2586 NULL /*Event*/,
2587 NULL /*ApcRoutine*/,
2588 NULL /*ApcContext*/,
2589 &Ios,
2590 &pdir->buf[0],
2591 sizeof(pdir->buf),
2592 MY_FileNamesInformation,
2593 FALSE /*ReturnSingleEntry*/,
2594 NULL /*FileName*/,
2595 pdir->off == ~(size_t)0 /*RestartScan*/);
2596 if (rcNt >= 0 && rcNt != STATUS_PENDING)
2597 {
2598 pdir->cb = Ios.Information;
2599 pdir->off = 0;
2600 }
2601 else if (rcNt == STATUS_NO_MORE_FILES)
2602 errno = 0; /* wrong? */
2603 else
2604 shfile_nt2errno(rcNt);
2605 }
2606
2607 if ( pdir->off != ~(size_t)0
2608 && pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) <= pdir->cb)
2609 {
2610 PMY_FILE_NAMES_INFORMATION pcur = (PMY_FILE_NAMES_INFORMATION)&pdir->buf[pdir->off];
2611 ANSI_STRING astr;
2612 UNICODE_STRING ustr;
2613
2614 astr.Length = astr.MaximumLength = sizeof(pdir->ent.name);
2615 astr.Buffer = &pdir->ent.name[0];
2616
2617 ustr.Length = ustr.MaximumLength = pcur->FileNameLength < ~(USHORT)0 ? (USHORT)pcur->FileNameLength : ~(USHORT)0;
2618 ustr.Buffer = &pcur->FileName[0];
2619
2620 rcNt = g_pfnRtlUnicodeStringToAnsiString(&astr, &ustr, 0/*AllocateDestinationString*/);
2621 if (rcNt < 0)
2622 sprintf(pdir->ent.name, "conversion-failed-%08x-rcNt=%08x-len=%u",
2623 pcur->FileIndex, rcNt, pcur->FileNameLength);
2624 if (pcur->NextEntryOffset)
2625 pdir->off += pcur->NextEntryOffset;
2626 else
2627 pdir->off = pdir->cb;
2628 return &pdir->ent;
2629 }
2630 }
2631 else
2632 errno = EINVAL;
2633 return NULL;
2634#else
2635 struct dirent *pde = readdir((DIR *)pdir);
2636 return pde ? (shdirent *)&pde->d_name[0] : NULL;
2637#endif
2638}
2639
2640void shfile_closedir(struct shdir *pdir)
2641{
2642#if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS
2643 if (pdir)
2644 {
2645 CloseHandle(pdir->native);
2646 pdir->pfdtab = NULL;
2647 pdir->native = INVALID_HANDLE_VALUE;
2648 sh_free(shthread_get_shell(), pdir);
2649 }
2650 else
2651 errno = EINVAL;
2652#else
2653 closedir((DIR *)pdir);
2654#endif
2655}
2656
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette