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

Last change on this file since 3465 was 3465, checked in by bird, 5 years ago

kash: Keep the filename around in debug builds. Added profiling of CloseHandle in debug builds, verifying that is is _slow_ when modifying (e.g. appending to) existing files.

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