/* $Id: shfile.c 3542 2022-01-29 01:36:00Z bird $ */ /** @file * * File management. * * Copyright (c) 2007-2010 knut st. osmundsen * * * This file is part of kBuild. * * kBuild is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kBuild is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kBuild; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "shfile.h" #include "shinstance.h" /* TRACE2 */ #include #include #include #if K_OS == K_OS_WINDOWS # include # define WIN32_NO_STATUS # include # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0502 /* Windows Server 2003 */ # endif # include //NTSTATUS #else # include # include # include #endif /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def SHFILE_IN_USE * Whether the file descriptor table stuff is actually in use or not. */ #if K_OS == K_OS_WINDOWS \ || K_OS == K_OS_OPENBSD /* because of ugly pthread library pipe hacks */ \ || !defined(SH_FORKED_MODE) # define SHFILE_IN_USE #endif /** The max file table size. */ #define SHFILE_MAX 1024 /** The file table growth rate. */ #define SHFILE_GROW 64 /** The min native unix file descriptor. */ #define SHFILE_UNIX_MIN_FD 32 /** The path buffer size we use. */ #define SHFILE_MAX_PATH 4096 /** Set errno and return. Doing a trace in debug build. */ #define RETURN_ERROR(rc, err, msg) \ do { \ TRACE2((NULL, "%s: " ## msg ## " - returning %d / %d\n", __FUNCTION__, (rc), (err))); \ errno = (err); \ return (rc); \ } while (0) #if K_OS == K_OS_WINDOWS /* See msdos.h for description. */ # define FOPEN 0x01 # define FEOFLAG 0x02 # define FCRLF 0x04 # define FPIPE 0x08 # define FNOINHERIT 0x10 # define FAPPEND 0x20 # define FDEV 0x40 # define FTEXT 0x80 # define MY_ObjectBasicInformation 0 # define MY_FileNamesInformation 12 typedef struct { ULONG Attributes; ACCESS_MASK GrantedAccess; ULONG HandleCount; ULONG PointerCount; ULONG PagedPoolUsage; ULONG NonPagedPoolUsage; ULONG Reserved[3]; ULONG NameInformationLength; ULONG TypeInformationLength; ULONG SecurityDescriptorLength; LARGE_INTEGER CreateTime; } MY_OBJECT_BASIC_INFORMATION; #if 0 typedef struct { union { LONG Status; PVOID Pointer; }; ULONG_PTR Information; } MY_IO_STATUS_BLOCK; #else typedef IO_STATUS_BLOCK MY_IO_STATUS_BLOCK; #endif typedef MY_IO_STATUS_BLOCK *PMY_IO_STATUS_BLOCK; typedef struct { ULONG NextEntryOffset; ULONG FileIndex; ULONG FileNameLength; WCHAR FileName[1]; } MY_FILE_NAMES_INFORMATION, *PMY_FILE_NAMES_INFORMATION; typedef NTSTATUS (NTAPI * PFN_NtQueryObject)(HANDLE, int, void *, size_t, size_t *); typedef NTSTATUS (NTAPI * PFN_NtQueryDirectoryFile)(HANDLE, HANDLE, void *, void *, PMY_IO_STATUS_BLOCK, void *, ULONG, int, int, PUNICODE_STRING, int); typedef NTSTATUS (NTAPI * PFN_RtlUnicodeStringToAnsiString)(PANSI_STRING, PCUNICODE_STRING, int); #endif /* K_OS_WINDOWS */ /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ #if K_OS == K_OS_WINDOWS static int g_shfile_globals_initialized = 0; static PFN_NtQueryObject g_pfnNtQueryObject = NULL; static PFN_NtQueryDirectoryFile g_pfnNtQueryDirectoryFile = NULL; static PFN_RtlUnicodeStringToAnsiString g_pfnRtlUnicodeStringToAnsiString = NULL; # ifdef KASH_ASYNC_CLOSE_HANDLE /** Data for the asynchronous CloseHandle machinery. */ static struct shfileasyncclose { /** Mutex protecting the asynchronous CloseHandle stuff. */ shmtx mtx; /** Handle to event that the closer-threads are waiting on. */ HANDLE evt_workers; /** The ring buffer read index (for closer-threads). */ unsigned volatile idx_read; /** The ring buffer write index (for shfile_native_close). * When idx_read and idx_write are the same, the ring buffer is empty. */ unsigned volatile idx_write; /** Number of handles currently being pending closure (handles + current * CloseHandle calls). */ unsigned volatile num_pending; /** Set if the threads should terminate. */ KBOOL volatile terminate_threads; /** Set if one or more shell threads have requested evt_sync to be signalled * when there are no more pending requests. */ KBOOL volatile signal_sync; /** Number of threads that have been spawned. */ KU8 num_threads; /** Handle to event that the shell threads are waiting on to sync. */ HANDLE evt_sync; /** Ring buffer containing handles to be closed. */ HANDLE handles[32]; /** Worker threads doing the asynchronous closing. */ HANDLE threads[8]; } g_shfile_async_close; # endif #endif /* K_OS_WINDOWS */ /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ #ifdef SHFILE_IN_USE # if K_OS == K_OS_WINDOWS static HANDLE shfile_set_inherit_win(shfile *pfd, int set); # endif #endif #ifdef SHFILE_IN_USE # ifdef DEBUG # if K_OS == K_OS_WINDOWS static KU64 shfile_nano_ts(void) { static KBOOL volatile s_has_factor = K_FALSE; static double volatile s_factor; double factor; LARGE_INTEGER now; if (s_has_factor) factor = s_factor; else { QueryPerformanceFrequency(&now); s_factor = factor = (double)1000000000.0 / now.QuadPart; s_has_factor = K_TRUE; } QueryPerformanceCounter(&now); return (KU64)(now.QuadPart * factor); } # endif /* K_OS_WINDOWS */ # endif /* DEBUG */ # if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) /** * The closer thread function. */ static unsigned __stdcall shfile_async_close_handle_thread(void *ignored) { KBOOL decrement_pending = K_FALSE; shthread_set_name("Async CloseHandle"); while (!g_shfile_async_close.terminate_threads) { HANDLE toclose; unsigned idx; shmtxtmp tmp; /* * Grab a handle if there is one: */ shmtx_enter(&g_shfile_async_close.mtx, &tmp); if (decrement_pending) { kHlpAssert(g_shfile_async_close.num_pending > 0); g_shfile_async_close.num_pending -= 1; } idx = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles); if (idx != g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles)) { kHlpAssert(g_shfile_async_close.num_pending > 0); toclose = g_shfile_async_close.handles[idx]; kHlpAssert(toclose); g_shfile_async_close.handles[idx] = NULL; g_shfile_async_close.idx_read = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles); } else { /* Signal waiters if requested and we've reached zero pending requests. */ if (g_shfile_async_close.signal_sync && g_shfile_async_close.num_pending == 0) { BOOL rc = SetEvent(g_shfile_async_close.evt_sync); kHlpAssert(rc); g_shfile_async_close.signal_sync = FALSE; } toclose = NULL; } shmtx_leave(&g_shfile_async_close.mtx, &tmp); /* Do the closing if we have something to close, otherwise wait for work to arrive. */ if (toclose != NULL) { BOOL rc = CloseHandle(toclose); kHlpAssert(rc); decrement_pending = K_TRUE; } else { DWORD dwRet = WaitForSingleObject(g_shfile_async_close.evt_workers, 10000 /*ms*/); kHlpAssert(dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT); decrement_pending = K_FALSE; } } K_NOREF(ignored); return 0; } /** * Try submit a handle for automatic closing. * * @returns K_TRUE if submitted successfully, K_FALSE if not. */ static KBOOL shfile_async_close_submit(HANDLE toclose) { KBOOL ret; unsigned idx; unsigned idx_next; unsigned idx_read; unsigned num_pending = 0; unsigned num_threads = 0; shmtxtmp tmp; shmtx_enter(&g_shfile_async_close.mtx, &tmp); /* Get the write index and check that there is a free slot in the buffer: */ idx = g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles); idx_next = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles); idx_read = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles); if (idx_next != idx_read) { /* Write the handle to the ring buffer: */ kHlpAssert(g_shfile_async_close.handles[idx] == NULL); g_shfile_async_close.handles[idx] = toclose; g_shfile_async_close.idx_write = idx_next; num_pending = g_shfile_async_close.num_pending + 1; g_shfile_async_close.num_pending = num_pending; ret = SetEvent(g_shfile_async_close.evt_workers); kHlpAssert(ret); if (ret) { /* If we have more pending requests than threads, create a new thread. */ num_threads = g_shfile_async_close.num_threads; if (num_pending > num_threads && num_threads < K_ELEMENTS(g_shfile_async_close.threads)) { int const savederrno = errno; unsigned tid = 0; intptr_t hThread = _beginthreadex(NULL /*security*/, 0 /*stack_size*/, shfile_async_close_handle_thread, NULL /*arg*/, 0 /*initflags*/, &tid); kHlpAssert(hThread != -1); if (hThread != -1) { g_shfile_async_close.threads[num_threads] = (HANDLE)hThread; g_shfile_async_close.num_threads = ++num_threads; } else { TRACE2((NULL, "shfile_async_close_submit: _beginthreadex failed: %d\n", errno)); if (num_threads == 0) ret = K_FALSE; } errno = savederrno; } } else TRACE2((NULL, "shfile_async_close_submit: SetEvent(%p) failed: %u\n", g_shfile_async_close.evt_workers, GetLastError())); /* cleanup on failure. */ if (ret) { /* likely */ } else { g_shfile_async_close.handles[idx] = NULL; g_shfile_async_close.idx_write = idx; g_shfile_async_close.num_pending = num_pending - 1; } } else ret = K_FALSE; shmtx_leave(&g_shfile_async_close.mtx, &tmp); TRACE2((NULL, "shfile_async_close_submit: toclose=%p idx=%d #pending=%u #thread=%u -> %d\n", toclose, idx, num_pending, num_threads, ret)); return ret; } /** * Wait for all pending CloseHandle calls to complete. */ void shfile_async_close_sync(void) { shmtxtmp tmp; shmtx_enter(&g_shfile_async_close.mtx, &tmp); if (g_shfile_async_close.num_pending > 0) { DWORD dwRet; /** @todo help out? */ if (!g_shfile_async_close.signal_sync) { BOOL rc = ResetEvent(g_shfile_async_close.evt_sync); kHlpAssert(rc); K_NOREF(rc); g_shfile_async_close.signal_sync = K_TRUE; } shmtx_leave(&g_shfile_async_close.mtx, &tmp); TRACE2((NULL, "shfile_async_close_sync: Calling WaitForSingleObject...\n")); dwRet = WaitForSingleObject(g_shfile_async_close.evt_sync, 10000 /*ms*/); kHlpAssert(dwRet == WAIT_OBJECT_0); kHlpAssert(g_shfile_async_close.num_pending == 0); TRACE2((NULL, "shfile_async_close_sync: WaitForSingleObject returned %u...\n", dwRet)); } else shmtx_leave(&g_shfile_async_close.mtx, &tmp); } # endif /* K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) */ /** * Close the specified native handle. * * @param native The native file handle. * @param file The file table entry if available. * @param inheritable The inheritability of the handle on windows, K_FALSE elsewhere. */ static void shfile_native_close(intptr_t native, shfile *file, KBOOL inheritable) { # if K_OS == K_OS_WINDOWS # ifdef KASH_ASYNC_CLOSE_HANDLE /* * CloseHandle may take several milliseconds on NTFS after we've appended * a few bytes to a file. When a script uses lots of 'echo line-text >> file' * we end up executing very slowly, even if 'echo' is builtin and the statement * requires no subshells to be spawned. * * So, detect problematic handles and do CloseHandle asynchronously. When * executing a child process, we probably will have to make sure the CloseHandle * operation has completed before we do ResumeThread on the child to make 100% * sure we can't have any sharing conflicts or end up with incorrect CRT stat() * results. Sharing conflicts are not a problem for builtin commands, and for * stat we do not use the CRT code but ntstat.c/h and it seems to work fine * (might be a tiny bit slower, so (TODO) might be worth reducing what we ask for). * * If child processes are spawned using handle inheritance and the handle in * question is inheritable, we will have to fix the inheriability before pushing * on the async-close queue. This shouldn't have the CloseHandle issues. */ if ( file && (file->shflags & (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_TYPE_MASK)) == (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_FILE)) { if (inheritable) native = (intptr_t)shfile_set_inherit_win(file, 0); if (shfile_async_close_submit((HANDLE)native)) return; } /* * Otherwise close it right here: */ # endif { # ifdef DEBUG KU64 ns = shfile_nano_ts(); BOOL fRc = CloseHandle((HANDLE)native); kHlpAssert(fRc); K_NOREF(fRc); ns = shfile_nano_ts() - ns; if (ns > 1000000) TRACE2((NULL, "shfile_native_close: %u ns %p oflags=%#x %s\n", ns, native, file ? file->oflags : 0, file ? file->dbgname : NULL)); # else BOOL fRc = CloseHandle((HANDLE)native); kHlpAssert(fRc); K_NOREF(fRc); # endif } # else /* K_OS != K_OS_WINDOWS */ int s = errno; close(native); errno = s; # endif /* K_OS != K_OS_WINDOWS */ K_NOREF(file); } /** * Grows the descriptor table, making sure that it can hold @a fdMin, * * @returns The max(fdMin, fdFirstNew) on success, -1 on failure. * @param pfdtab The table to grow. * @param fdMin Grow to include this index. */ static int shfile_grow_tab_locked(shfdtab *pfdtab, int fdMin) { /* * Grow the descriptor table. */ int fdRet = -1; shfile *new_tab; int new_size = pfdtab->size + SHFILE_GROW; while (new_size < fdMin) new_size += SHFILE_GROW; TRACE2((NULL, "shfile_grow_tab_locked: old %p / %d entries; new size: %d\n", pfdtab->tab, pfdtab->size, new_size)); new_tab = sh_realloc(shthread_get_shell(), pfdtab->tab, new_size * sizeof(shfile)); if (new_tab) { int i; for (i = pfdtab->size; i < new_size; i++) { new_tab[i].fd = -1; new_tab[i].oflags = 0; new_tab[i].shflags = 0; new_tab[i].native = -1; # ifdef DEBUG new_tab[i].dbgname = NULL; # endif } fdRet = pfdtab->size; if (fdRet < fdMin) fdRet = fdMin; pfdtab->tab = new_tab; pfdtab->size = new_size; TRACE2((NULL, "shfile_grow_tab_locked: new %p / %d entries\n", pfdtab->tab, pfdtab->size)); } return fdRet; } /** * Inserts the file into the descriptor table. * * If we're out of memory and cannot extend the table, we'll close the * file, set errno to EMFILE and return -1. * * @returns The file descriptor number. -1 and errno on failure. * @param pfdtab The file descriptor table. * @param native The native file handle. * @param oflags The flags the it was opened/created with. * @param shflags The shell file flags. * @param fdMin The minimum file descriptor number, pass -1 if any is ok. * @param who Who we're doing this for (for logging purposes). * @param dbgname The filename, if applicable/available. */ static int shfile_insert(shfdtab *pfdtab, intptr_t native, unsigned oflags, unsigned shflags, int fdMin, const char *who, const char *dbgname) { shmtxtmp tmp; int fd; int i; /* * Fend of bad stuff. */ if (fdMin >= SHFILE_MAX) { TRACE2((NULL, "shfile_insert: fdMin=%d is out of bounds; native=%p %s\n", fdMin, native, dbgname)); shfile_native_close(native, NULL, K_FALSE); errno = EMFILE; return -1; } # if K_OS != K_OS_WINDOWS if (fcntl((int)native, F_SETFD, fcntl((int)native, F_GETFD, 0) | FD_CLOEXEC) == -1) { int e = errno; TRACE2((NULL, "shfile_insert: F_SETFD failed %d; native=%p %s\n", e, native, dbgname)); close((int)native); errno = e; return -1; } # endif shmtx_enter(&pfdtab->mtx, &tmp); /* * Search for a fitting unused location. */ fd = -1; for (i = fdMin >= 0 ? fdMin : 0; (unsigned)i < pfdtab->size; i++) if (pfdtab->tab[i].fd == -1) { fd = i; break; } if (fd == -1) fd = shfile_grow_tab_locked(pfdtab, fdMin); /* * Fill in the entry if we've found one. */ if (fd != -1) { pfdtab->tab[fd].fd = fd; pfdtab->tab[fd].oflags = oflags; pfdtab->tab[fd].shflags = shflags; pfdtab->tab[fd].native = native; #ifdef DEBUG pfdtab->tab[fd].dbgname = dbgname ? sh_strdup(NULL, dbgname) : NULL; #endif TRACE2((NULL, "shfile_insert: #%d: native=%p oflags=%#x shflags=%#x %s\n", fd, native, oflags, shflags, dbgname)); } else shfile_native_close(native, NULL, K_FALSE); shmtx_leave(&pfdtab->mtx, &tmp); if (fd == -1) errno = EMFILE; (void)who; return fd; } # if K_OS != K_OS_WINDOWS /** * Makes a copy of the native file, closes the original, and inserts the copy * into the descriptor table. * * If we're out of memory and cannot extend the table, we'll close the * file, set errno to EMFILE and return -1. * * @returns The file descriptor number. -1 and errno on failure. * @param pfdtab The file descriptor table. * @param pnative The native file handle on input, -1 on output. * @param oflags The flags the it was opened/created with. * @param shflags The shell file flags. * @param fdMin The minimum file descriptor number, pass -1 if any is ok. * @param who Who we're doing this for (for logging purposes). * @param dbgname The filename, if applicable/available. */ static int shfile_copy_insert_and_close(shfdtab *pfdtab, int *pnative, unsigned oflags, unsigned shflags, int fdMin, const char *who, const char *dbgname) { int fd = -1; int s = errno; int native_copy = fcntl(*pnative, F_DUPFD, SHFILE_UNIX_MIN_FD); close(*pnative); *pnative = -1; errno = s; if (native_copy != -1) fd = shfile_insert(pfdtab, native_copy, oflags, shflags, fdMin, who, dbgname); return fd; } # endif /* !K_OS_WINDOWS */ /** * Gets a file descriptor and lock the file descriptor table. * * @returns Pointer to the file and table ownership on success, * NULL and errno set to EBADF on failure. * @param pfdtab The file descriptor table. * @param fd The file descriptor number. * @param ptmp See shmtx_enter. */ static shfile *shfile_get(shfdtab *pfdtab, int fd, shmtxtmp *ptmp) { shfile *file = NULL; if ( fd >= 0 && (unsigned)fd < pfdtab->size) { shmtx_enter(&pfdtab->mtx, ptmp); if ((unsigned)fd < pfdtab->size && pfdtab->tab[fd].fd != -1) file = &pfdtab->tab[fd]; else shmtx_leave(&pfdtab->mtx, ptmp); } if (!file) errno = EBADF; return file; } /** * Puts back a file descriptor and releases the table ownership. * * @param pfdtab The file descriptor table. * @param file The file. * @param ptmp See shmtx_leave. */ static void shfile_put(shfdtab *pfdtab, shfile *file, shmtxtmp *ptmp) { shmtx_leave(&pfdtab->mtx, ptmp); kHlpAssert(file); (void)file; } /** * Constructs a path from the current directory and the passed in path. * * @returns 0 on success, -1 and errno set to ENAMETOOLONG or EINVAL on failure. * * @param pfdtab The file descriptor table * @param path The path the caller supplied. * @param buf Where to put the path. This is assumed to be SHFILE_MAX_PATH * chars in size. */ int shfile_make_path(shfdtab *pfdtab, const char *path, char *buf) { size_t path_len = strlen(path); if (path_len == 0) { errno = EINVAL; return -1; } if (path_len >= SHFILE_MAX_PATH) { errno = ENAMETOOLONG; return -1; } if ( *path == '/' # if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2 || *path == '\\' || ( *path && path[1] == ':' && ( (*path >= 'A' && *path <= 'Z') || (*path >= 'a' && *path <= 'z'))) # endif ) { memcpy(buf, path, path_len + 1); } else { size_t cwd_len; shmtxtmp tmp; shmtx_enter(&pfdtab->mtx, &tmp); cwd_len = strlen(pfdtab->cwd); memcpy(buf, pfdtab->cwd, cwd_len); shmtx_leave(&pfdtab->mtx, &tmp); if (cwd_len + path_len + 1 >= SHFILE_MAX_PATH) { errno = ENAMETOOLONG; return -1; } if ( !cwd_len || buf[cwd_len - 1] != '/') buf[cwd_len++] = '/'; memcpy(buf + cwd_len, path, path_len + 1); } # if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2 if (!strcmp(buf, "/dev/null")) strcpy(buf, "NUL"); # endif return 0; } # if K_OS == K_OS_WINDOWS /** * Adjusts the file name if it ends with a trailing directory slash. * * Windows APIs doesn't like trailing slashes. * * @returns 1 if it has a directory slash, 0 if not. * * @param abspath The path to adjust (SHFILE_MAX_PATH). */ static int shfile_trailing_slash_hack(char *abspath) { /* * Anything worth adjust here? */ size_t path_len = strlen(abspath); if ( path_len == 0 || ( abspath[path_len - 1] != '/' # if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2 && abspath[path_len - 1] != '\\' # endif ) ) return 0; /* * Ok, make the adjustment. */ if (path_len + 2 <= SHFILE_MAX_PATH) { /* Add a '.' to the end. */ abspath[path_len++] = '.'; abspath[path_len] = '\0'; } else { /* No space for a dot, remove the slash if it's alone or just remove one and add a dot like above. */ if ( abspath[path_len - 2] != '/' # if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2 && abspath[path_len - 2] != '\\' # endif ) abspath[--path_len] = '\0'; else abspath[path_len - 1] = '.'; } return 1; } /** * Converts a DOS(/Windows) error code to errno, * assigning it to errno. * * @returns -1 * @param err The DOS error. */ static int shfile_dos2errno(int err) { switch (err) { case ERROR_BAD_ENVIRONMENT: errno = E2BIG; break; case ERROR_ACCESS_DENIED: errno = EACCES; break; case ERROR_CURRENT_DIRECTORY: errno = EACCES; break; case ERROR_LOCK_VIOLATION: errno = EACCES; break; case ERROR_NETWORK_ACCESS_DENIED: errno = EACCES; break; case ERROR_CANNOT_MAKE: errno = EACCES; break; case ERROR_FAIL_I24: errno = EACCES; break; case ERROR_DRIVE_LOCKED: errno = EACCES; break; case ERROR_SEEK_ON_DEVICE: errno = EACCES; break; case ERROR_NOT_LOCKED: errno = EACCES; break; case ERROR_LOCK_FAILED: errno = EACCES; break; case ERROR_NO_PROC_SLOTS: errno = EAGAIN; break; case ERROR_MAX_THRDS_REACHED: errno = EAGAIN; break; case ERROR_NESTING_NOT_ALLOWED: errno = EAGAIN; break; case ERROR_INVALID_HANDLE: errno = EBADF; break; case ERROR_INVALID_TARGET_HANDLE: errno = EBADF; break; case ERROR_DIRECT_ACCESS_HANDLE: errno = EBADF; break; case ERROR_WAIT_NO_CHILDREN: errno = ECHILD; break; case ERROR_CHILD_NOT_COMPLETE: errno = ECHILD; break; case ERROR_FILE_EXISTS: errno = EEXIST; break; case ERROR_ALREADY_EXISTS: errno = EEXIST; break; case ERROR_INVALID_FUNCTION: errno = EINVAL; break; case ERROR_INVALID_ACCESS: errno = EINVAL; break; case ERROR_INVALID_DATA: errno = EINVAL; break; case ERROR_INVALID_PARAMETER: errno = EINVAL; break; case ERROR_NEGATIVE_SEEK: errno = EINVAL; break; case ERROR_TOO_MANY_OPEN_FILES: errno = EMFILE; break; case ERROR_FILE_NOT_FOUND: errno = ENOENT; break; case ERROR_PATH_NOT_FOUND: errno = ENOENT; break; case ERROR_INVALID_DRIVE: errno = ENOENT; break; case ERROR_NO_MORE_FILES: errno = ENOENT; break; case ERROR_BAD_NETPATH: errno = ENOENT; break; case ERROR_BAD_NET_NAME: errno = ENOENT; break; case ERROR_BAD_PATHNAME: errno = ENOENT; break; case ERROR_FILENAME_EXCED_RANGE: errno = ENOENT; break; case ERROR_BAD_FORMAT: errno = ENOEXEC; break; case ERROR_ARENA_TRASHED: errno = ENOMEM; break; case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break; case ERROR_INVALID_BLOCK: errno = ENOMEM; break; case ERROR_NOT_ENOUGH_QUOTA: errno = ENOMEM; break; case ERROR_DISK_FULL: errno = ENOSPC; break; case ERROR_DIR_NOT_EMPTY: errno = ENOTEMPTY; break; case ERROR_BROKEN_PIPE: errno = EPIPE; break; case ERROR_NOT_SAME_DEVICE: errno = EXDEV; break; default: errno = EINVAL; break; } return -1; } /** * Converts an NT status code to errno, * assigning it to errno. * * @returns -1 * @param rcNt The NT status code. */ static int shfile_nt2errno(NTSTATUS rcNt) { switch (rcNt) { default: errno = EINVAL; break; } return -1; } DWORD shfile_query_handle_access_mask(HANDLE h, PACCESS_MASK pMask) { MY_OBJECT_BASIC_INFORMATION BasicInfo; NTSTATUS rcNt; if (!g_pfnNtQueryObject) return ERROR_NOT_SUPPORTED; rcNt = g_pfnNtQueryObject(h, MY_ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL); if (rcNt >= 0) { *pMask = BasicInfo.GrantedAccess; return NO_ERROR; } if (rcNt != STATUS_INVALID_HANDLE) return ERROR_GEN_FAILURE; return ERROR_INVALID_HANDLE; } # endif /* K_OS == K_OS_WINDOWS */ #endif /* SHFILE_IN_USE */ /** * Converts DOS slashes to UNIX slashes if necessary. * * @param pszPath The path to fix. */ K_INLINE void shfile_fix_slashes(char *pszPath) { #if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2 while ((pszPath = strchr(pszPath, '\\'))) *pszPath++ = '/'; #else (void)pszPath; #endif } /** * Initializes the global variables in this file. */ static void shfile_init_globals(void) { #if K_OS == K_OS_WINDOWS if (!g_shfile_globals_initialized) { HMODULE hNtDll = GetModuleHandle("NTDLL"); g_pfnNtQueryObject = (PFN_NtQueryObject) GetProcAddress(hNtDll, "NtQueryObject"); g_pfnNtQueryDirectoryFile = (PFN_NtQueryDirectoryFile)GetProcAddress(hNtDll, "NtQueryDirectoryFile"); g_pfnRtlUnicodeStringToAnsiString = (PFN_RtlUnicodeStringToAnsiString)GetProcAddress(hNtDll, "RtlUnicodeStringToAnsiString"); if ( !g_pfnRtlUnicodeStringToAnsiString || !g_pfnNtQueryDirectoryFile) { /* fatal error */ } # ifdef KASH_ASYNC_CLOSE_HANDLE /* * Init the async CloseHandle state. */ shmtx_init(&g_shfile_async_close.mtx); g_shfile_async_close.evt_workers = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/); g_shfile_async_close.evt_sync = CreateEventW(NULL, TRUE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/); if ( !g_shfile_async_close.evt_workers || !g_shfile_async_close.evt_sync) { fprintf(stderr, "fatal error: CreateEventW failed: %u\n", GetLastError()); _exit(19); } g_shfile_async_close.idx_read = 0; g_shfile_async_close.idx_write = 0; g_shfile_async_close.num_pending = 0; g_shfile_async_close.terminate_threads = K_FALSE; g_shfile_async_close.signal_sync = K_FALSE; g_shfile_async_close.num_threads = 0; { unsigned i = K_ELEMENTS(g_shfile_async_close.handles); while (i-- > 0) g_shfile_async_close.handles[i] = NULL; i = K_ELEMENTS(g_shfile_async_close.threads); while (i-- > 0) g_shfile_async_close.threads[i] = NULL; } # endif g_shfile_globals_initialized = 1; } #endif } /** * Initializes a file descriptor table. * * @returns 0 on success, -1 and errno on failure. * @param pfdtab The table to initialize. * @param inherit File descriptor table to inherit from. If not specified * we will inherit from the current process as it were. */ int shfile_init(shfdtab *pfdtab, shfdtab *inherit) { int rc; shfile_init_globals(); pfdtab->cwd = NULL; pfdtab->size = 0; pfdtab->tab = NULL; rc = shmtx_init(&pfdtab->mtx); if (!rc) { #ifdef SHFILE_IN_USE /* Get CWD with unix slashes. */ if (!inherit) { char buf[SHFILE_MAX_PATH]; if (getcwd(buf, sizeof(buf))) { shfile_fix_slashes(buf); pfdtab->cwd = sh_strdup(NULL, buf); } if (pfdtab->cwd) { # if K_OS == K_OS_WINDOWS static const struct { DWORD dwStdHandle; unsigned fFlags; } aStdHandles[3] = { { STD_INPUT_HANDLE, _O_RDONLY }, { STD_OUTPUT_HANDLE, _O_WRONLY }, { STD_ERROR_HANDLE, _O_WRONLY } }; int i; STARTUPINFO Info; ACCESS_MASK Mask; DWORD dwErr; rc = 0; /* Try pick up the Visual C++ CRT file descriptor info. */ __try { GetStartupInfo(&Info); } __except (EXCEPTION_EXECUTE_HANDLER) { memset(&Info, 0, sizeof(Info)); } if ( Info.cbReserved2 > sizeof(int) && (uintptr_t)Info.lpReserved2 >= 0x1000 && (i = *(int *)Info.lpReserved2) >= 1 && i <= 2048 && ( Info.cbReserved2 == i * 5 + 4 //|| Info.cbReserved2 == i * 5 + 1 - check the cygwin sources. || Info.cbReserved2 == i * 9 + 4)) { uint8_t *paf = (uint8_t *)Info.lpReserved2 + sizeof(int); int dwPerH = 1 + (Info.cbReserved2 == i * 9 + 4); DWORD *ph = (DWORD *)(paf + i) + dwPerH * i; HANDLE aStdHandles2[3]; int j; //if (Info.cbReserved2 == i * 5 + 1) - check the cygwin sources. // i--; for (j = 0; j < 3; j++) aStdHandles2[j] = GetStdHandle(aStdHandles[j].dwStdHandle); while (i-- > 0) { ph -= dwPerH; if ( (paf[i] & (FOPEN | FNOINHERIT)) == FOPEN && *ph != (uint32_t)INVALID_HANDLE_VALUE && *ph != 0) { HANDLE h = (HANDLE)(intptr_t)*ph; int fd2; int fFlags; int fFlags2; if ( h == aStdHandles2[j = 0] || h == aStdHandles2[j = 1] || h == aStdHandles2[j = 2]) fFlags = aStdHandles[j].fFlags; else { dwErr = shfile_query_handle_access_mask(h, &Mask); if (dwErr == ERROR_INVALID_HANDLE) continue; if (dwErr == NO_ERROR) { fFlags = 0; if ( (Mask & (GENERIC_READ | FILE_READ_DATA)) && (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA))) fFlags |= O_RDWR; else if (Mask & (GENERIC_READ | FILE_READ_DATA)) fFlags |= O_RDONLY; else if (Mask & (GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) fFlags |= O_WRONLY; else fFlags |= O_RDWR; if ((Mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) == FILE_APPEND_DATA) fFlags |= O_APPEND; } else fFlags = O_RDWR; } if (paf[i] & FPIPE) fFlags2 = SHFILE_FLAGS_PIPE; else if (paf[i] & FDEV) fFlags2 = SHFILE_FLAGS_TTY; else fFlags2 = 0; fd2 = shfile_insert(pfdtab, (intptr_t)h, fFlags, fFlags2, i, "shtab_init", NULL); kHlpAssert(fd2 == i); (void)fd2; if (fd2 != i) rc = -1; } } } /* Check the three standard handles. */ for (i = 0; i < 3; i++) if ( (unsigned)i >= pfdtab->size || pfdtab->tab[i].fd == -1) { HANDLE hFile = GetStdHandle(aStdHandles[i].dwStdHandle); if ( hFile != INVALID_HANDLE_VALUE && hFile != NULL) { DWORD dwType = GetFileType(hFile); unsigned fFlags = aStdHandles[i].fFlags; unsigned fFlags2; int fd2; if (dwType == FILE_TYPE_CHAR) fFlags2 = SHFILE_FLAGS_TTY; else if (dwType == FILE_TYPE_PIPE) fFlags2 = SHFILE_FLAGS_PIPE; else fFlags2 = SHFILE_FLAGS_FILE; fd2 = shfile_insert(pfdtab, (intptr_t)hFile, fFlags, fFlags2, i, "shtab_init", NULL); kHlpAssert(fd2 == i); (void)fd2; if (fd2 != i) rc = -1; } } # else /* * Annoying... */ int fd; for (fd = 0; fd < 10; fd++) { int oflags = fcntl(fd, F_GETFL, 0); if (oflags != -1) { int cox = fcntl(fd, F_GETFD, 0); struct stat st; if ( cox != -1 && fstat(fd, &st) != -1) { int native; int fd2; int fFlags2 = 0; if (cox & FD_CLOEXEC) fFlags2 |= SHFILE_FLAGS_CLOSE_ON_EXEC; if (S_ISREG(st.st_mode)) fFlags2 |= SHFILE_FLAGS_FILE; else if (S_ISDIR(st.st_mode)) fFlags2 |= SHFILE_FLAGS_DIR; else if (S_ISCHR(st.st_mode)) fFlags2 |= SHFILE_FLAGS_TTY; else if (S_ISFIFO(st.st_mode)) fFlags2 |= SHFILE_FLAGS_PIPE; else fFlags2 |= SHFILE_FLAGS_TTY; native = fcntl(fd, F_DUPFD, SHFILE_UNIX_MIN_FD); if (native == -1) native = fd; fd2 = shfile_insert(pfdtab, native, oflags, fFlags2, fd, "shtab_init", NULL); kHlpAssert(fd2 == fd); (void)fd2; if (fd2 != fd) rc = -1; if (native != fd) close(fd); } } } # endif } else rc = -1; } else { /* * Inherit from parent shell's file table. */ shfile const *src; shfile *dst; shmtxtmp tmp; unsigned fdcount; unsigned fd; shmtx_enter(&inherit->mtx, &tmp); /* allocate table and cwd: */ fdcount = inherit->size; pfdtab->tab = dst = (shfile *)(fdcount ? sh_calloc(NULL, sizeof(pfdtab->tab[0]), fdcount) : NULL); pfdtab->cwd = sh_strdup(NULL, inherit->cwd); if ( pfdtab->cwd && (pfdtab->tab || fdcount == 0)) { /* duplicate table entries: */ for (fd = 0, src = inherit->tab; fd < fdcount; fd++, src++, dst++) if (src->fd == -1) dst->native = dst->fd = -1; else { # if K_OS == K_OS_WINDOWS # ifdef SH_FORKED_MODE KBOOL const cox = !!(src->shflags & SHFILE_FLAGS_CLOSE_ON_EXEC); # else KBOOL const cox = K_TRUE; # endif # endif *dst = *src; # ifdef DEBUG if (src->dbgname) dst->dbgname = sh_strdup(NULL, src->dbgname); # endif # if K_OS == K_OS_WINDOWS if (DuplicateHandle(GetCurrentProcess(), (HANDLE)src->native, GetCurrentProcess(), (HANDLE *)&dst->native, 0, FALSE /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) TRACE2((NULL, "shfile_init: %d (%#x, %#x) %p (was %p)\n", dst->fd, dst->oflags, dst->shflags, dst->native, src->native)); else { dst->native = (intptr_t)INVALID_HANDLE_VALUE; rc = shfile_dos2errno(GetLastError()); TRACE2((NULL, "shfile_init: %d (%#x, %#x) %p - failed %d / %u!\n", dst->fd, dst->oflags, dst->shflags, src->native, rc, GetLastError())); break; } # 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 */ dst->native = dup3(src->native, -1, cox ? O_CLOEXEC : 0); # else if (cox) { # ifndef SH_FORKED_MODE shmtxtmp tmp2; shmtx_enter(&global_exec_something, &tmp) # endif dst->native = dup2(src->native, -1); if (dst->native >= 0) rc = fcntl(dst->native, F_SETFD, FD_CLOEXEC); # ifndef SH_FORKED_MODE shmtx_leave(&global_exec_something, &tmp) # endif if (rc != 0) break; } else dst->native = dup2(src->native, -1); if (dst->native < 0) { rc = -1; break; } # endif } } else rc = -1; pfdtab->size = fd; shmtx_leave(&inherit->mtx, &tmp); } /* inherit != NULL */ #endif } return rc; } /** * Deletes the file descriptor table. * * Safe to call more than once. */ void shfile_uninit(shfdtab *pfdtab, int tracefd) { if (!pfdtab) return; if (pfdtab->tab) { unsigned left = pfdtab->size; struct shfile *pfd = pfdtab->tab; unsigned tracefdfound = 0; while (left-- > 0) { if (pfd->fd != -1) { if (pfd->fd != tracefd) { #if K_OS == K_OS_WINDOWS BOOL rc = CloseHandle((HANDLE)pfd->native); kHlpAssert(rc == TRUE); K_NOREF(rc); #else int rc = close((int)pfd->native); kHlpAssert(rc == 0); K_NOREF(rc); #endif pfd->fd = -1; pfd->native = -1; } else tracefdfound++; /* there is only the one */ } pfd++; } if (!tracefdfound) { /* likely */ } else return; sh_free(NULL, pfdtab->tab); pfdtab->tab = NULL; } shmtx_delete(&pfdtab->mtx); sh_free(NULL, pfdtab->cwd); pfdtab->cwd = NULL; } #if K_OS == K_OS_WINDOWS && defined(SHFILE_IN_USE) /** * Changes the inheritability of a file descriptor, taking console handles into * account. * * @note This MAY change the native handle for the entry. * * @returns The native handle. * @param pfd The file descriptor to change. * @param set If set, make child processes inherit the handle, if clear * make them not inherit it. */ static HANDLE shfile_set_inherit_win(shfile *pfd, int set) { HANDLE hFile = (HANDLE)pfd->native; if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, set ? HANDLE_FLAG_INHERIT : 0)) { /* SetHandleInformation doesn't work for console handles, so we have to duplicate the handle to change the inheritability. */ DWORD err = GetLastError(); if ( err == ERROR_INVALID_PARAMETER && DuplicateHandle(GetCurrentProcess(), hFile, GetCurrentProcess(), &hFile, 0, set ? TRUE : FALSE /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) { TRACE2((NULL, "shfile_set_inherit_win: %p -> %p (set=%d)\n", pfd->native, hFile, set)); if (!CloseHandle((HANDLE)pfd->native)) kHlpAssert(0); pfd->native = (intptr_t)hFile; } else { err = GetLastError(); kHlpAssert(0); hFile = (HANDLE)pfd->native; } } return hFile; } # ifdef SH_FORKED_MODE /** * Helper for shfork. * * @param pfdtab The file descriptor table. * @param set Whether to make all handles inheritable (1) or * to restore them to the rigth state (0). * @param hndls Where to store the three standard handles. */ void shfile_fork_win(shfdtab *pfdtab, int set, intptr_t *hndls) { shmtxtmp tmp; unsigned i; shmtx_enter(&pfdtab->mtx, &tmp); TRACE2((NULL, "shfile_fork_win: set=%d\n", set)); i = pfdtab->size; while (i-- > 0) { if (pfdtab->tab[i].fd == i) { shfile_set_inherit_win(&pfdtab->tab[i], set); if (set) TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n", i, pfdtab->tab[i].native, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags)); } } if (hndls) { for (i = 0; i < 3; i++) { if ( pfdtab->size > i && pfdtab->tab[i].fd == i) hndls[i] = pfdtab->tab[i].native; else hndls[i] = (intptr_t)INVALID_HANDLE_VALUE; TRACE2((NULL, "shfile_fork_win: i=%d size=%d fd=%d native=%d hndls[%d]=%p\n", i, pfdtab->size, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, hndls[i])); } } shmtx_leave(&pfdtab->mtx, &tmp); } # endif /* SH_FORKED_MODE */ /** shfile_exec_win helper that make sure there are no _O_APPEND handles. */ static KBOOL shfile_exec_win_no_append(shfdtab *pfdtab, unsigned count) { unsigned i; for (i = 0; i < count; i++) if ( (pfdtab->tab[i].oflags & _O_APPEND) && (pfdtab->tab[i].oflags & (_O_WRONLY | _O_RDWR))) return K_FALSE; return K_TRUE; } /** * Helper for sh_execve. * * This is called before and after CreateProcess. On the first call it * will mark the non-close-on-exec handles as inheritable and produce * the startup info for the CRT. On the second call, after CreateProcess, * it will restore the handle inheritability properties. * * @returns 0 on success, non-zero on failure. * @param pfdtab The file descriptor table. * @param prepare Which call, 1 if before, 0 if after and success, -1 if after on failure. * @param info The info structure. */ int shfile_exec_win(shfdtab *pfdtab, int prepare, shfdexecwin *info) { STARTUPINFOA *strtinfo = (STARTUPINFOA *)info->strtinfo; int rc = 0; shmtxtmp tmp; unsigned count; unsigned i; shmtx_enter(&pfdtab->mtx, &tmp); TRACE2((NULL, "shfile_exec_win: prepare=%p\n", prepare)); count = pfdtab->size < (0x10000-4) / (1 + sizeof(HANDLE)) ? pfdtab->size : (0x10000-4) / (1 + sizeof(HANDLE)); while ( count > 3 && ( pfdtab->tab[count - 1].fd == -1 || (pfdtab->tab[count - 1].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC))) count--; if (prepare > 0) { if (count <= 3 && shfile_exec_win_no_append(pfdtab, count)) { info->inherithandles = 0; info->startsuspended = 1; strtinfo->cbReserved2 = 0; strtinfo->lpReserved2 = NULL; } else { size_t cbData = sizeof(int) + count * (1 + sizeof(HANDLE)); uint8_t *pbData = sh_malloc(shthread_get_shell(), cbData); uint8_t *paf = pbData + sizeof(int); HANDLE *pah = (HANDLE *)(paf + count); info->inherithandles = 1; # ifdef KASH_ASYNC_CLOSE_HANDLE info->startsuspended = g_shfile_async_close.num_pending > 0; # else info->startsuspended = 0; # endif strtinfo->cbReserved2 = (unsigned short)cbData; strtinfo->lpReserved2 = pbData; # ifndef SH_FORKED_MODE shmtx_leave(&pfdtab->mtx, &tmp); /* should be harmless as this isn't really necessary at all. */ shmtx_enter(&g_sh_exec_inherit_mtx, &info->tmp); shmtx_enter(&pfdtab->mtx, &tmp); # endif *(int *)pbData = count; i = count; while (i-- > 0) { if ( pfdtab->tab[i].fd == i && !(pfdtab->tab[i].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC)) { HANDLE hFile = shfile_set_inherit_win(&pfdtab->tab[i], 1); TRACE2((NULL, " #%d: native=%#x oflags=%#x shflags=%#x\n", i, hFile, pfdtab->tab[i].oflags, pfdtab->tab[i].shflags)); paf[i] = FOPEN; if (pfdtab->tab[i].oflags & _O_APPEND) paf[i] |= FAPPEND; if (pfdtab->tab[i].oflags & _O_TEXT) paf[i] |= FTEXT; switch (pfdtab->tab[i].shflags & SHFILE_FLAGS_TYPE_MASK) { case SHFILE_FLAGS_TTY: paf[i] |= FDEV; break; case SHFILE_FLAGS_PIPE: paf[i] |= FPIPE; break; } pah[i] = hFile; } else { paf[i] = 0; pah[i] = INVALID_HANDLE_VALUE; } } } for (i = 0; i < 3; i++) { if ( i < count && pfdtab->tab[i].fd == i) { info->replacehandles[i] = 1; info->handles[i] = pfdtab->tab[i].native; } else { info->replacehandles[i] = 0; info->handles[i] = (intptr_t)INVALID_HANDLE_VALUE; } TRACE2((NULL, "shfile_exec_win: i=%d count=%d fd=%d native=%d hndls[%d]=\n", i, count, pfdtab->tab[i].fd, pfdtab->tab[i].native, i, info->handles[i])); } } else { shfile *file = pfdtab->tab; sh_free(NULL, strtinfo->lpReserved2); strtinfo->lpReserved2 = NULL; i = count; if (prepare == 0) for (i = 0; i < count; i++, file++) { if ( file->fd == i && !(file->shflags & SHFILE_FLAGS_TRACE)) { shfile_native_close(file->native, file, info->inherithandles); file->fd = -1; file->oflags = 0; file->shflags = 0; file->native = -1; # ifdef DEBUG sh_free(NULL, file->dbgname); file->dbgname = NULL; # endif } } else if (info->inherithandles) for (i = 0; i < count; i++, file++) if ( file->fd == i && !(file->shflags & SHFILE_FLAGS_CLOSE_ON_EXEC)) shfile_set_inherit_win(file, 0); # ifndef SH_FORKED_MODE if (info->inherithandles) shmtx_leave(&g_sh_exec_inherit_mtx, &info->tmp); # endif } shmtx_leave(&pfdtab->mtx, &tmp); return rc; } #endif /* K_OS_WINDOWS */ #if K_OS != K_OS_WINDOWS /** * Prepare file handles for inherting before a execve call. * * This is only used in the normal mode, so we've forked and need not worry * about cleaning anything up after us. Nor do we need think about locking. * * @returns 0 on success, -1 on failure. */ int shfile_exec_unix(shfdtab *pfdtab) { int rc = 0; # ifdef SHFILE_IN_USE unsigned fd; for (fd = 0; fd < pfdtab->size; fd++) { if ( pfdtab->tab[fd].fd != -1 && !(pfdtab->tab[fd].shflags & SHFILE_FLAGS_CLOSE_ON_EXEC) ) { TRACE2((NULL, "shfile_exec_unix: %d => %d\n", pfdtab->tab[fd].native, fd)); if (dup2(pfdtab->tab[fd].native, fd) < 0) { /* fatal_error(NULL, "shfile_exec_unix: failed to move %d to %d", pfdtab->tab[fd].fd, fd); */ rc = -1; } } } # endif return rc; } #endif /* !K_OS_WINDOWS */ /** * open(). */ int shfile_open(shfdtab *pfdtab, const char *name, unsigned flags, mode_t mode) { int fd; #ifdef SHFILE_IN_USE char absname[SHFILE_MAX_PATH]; # if K_OS == K_OS_WINDOWS HANDLE hFile; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes; SECURITY_ATTRIBUTES SecurityAttributes; # ifndef _O_ACCMODE # define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR) # endif switch (flags & (_O_ACCMODE | _O_APPEND)) { case _O_RDONLY: dwDesiredAccess = GENERIC_READ; break; case _O_RDONLY | _O_APPEND: dwDesiredAccess = GENERIC_READ; break; case _O_WRONLY: dwDesiredAccess = GENERIC_WRITE; break; case _O_WRONLY | _O_APPEND: dwDesiredAccess = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break; case _O_RDWR: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; break; case _O_RDWR | _O_APPEND: dwDesiredAccess = GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break; default: RETURN_ERROR(-1, EINVAL, "invalid mode"); } dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.lpSecurityDescriptor = NULL; SecurityAttributes.bInheritHandle = FALSE; if (flags & _O_CREAT) { if ((flags & (_O_EXCL | _O_TRUNC)) == (_O_EXCL | _O_TRUNC)) RETURN_ERROR(-1, EINVAL, "_O_EXCL | _O_TRUNC"); if (flags & _O_TRUNC) dwCreationDisposition = CREATE_ALWAYS; /* not 100%, but close enough */ else if (flags & _O_EXCL) dwCreationDisposition = CREATE_NEW; else dwCreationDisposition = OPEN_ALWAYS; } else if (flags & _O_TRUNC) dwCreationDisposition = TRUNCATE_EXISTING; else dwCreationDisposition = OPEN_EXISTING; if (!(flags & _O_CREAT) || (mode & 0222)) dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; else dwFlagsAndAttributes = FILE_ATTRIBUTE_READONLY; fd = shfile_make_path(pfdtab, name, &absname[0]); if (!fd) { # ifdef DEBUG KU64 ns = shfile_nano_ts(); # endif SetLastError(0); hFile = CreateFileA(absname, dwDesiredAccess, dwShareMode, &SecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, NULL /* hTemplateFile */); # ifdef DEBUG ns = shfile_nano_ts() - ns; if (ns > 1000000) TRACE2((NULL, "shfile_open: %u ns hFile=%p (%d) %s\n", ns, hFile, GetLastError(), absname)); # endif if (hFile != INVALID_HANDLE_VALUE) fd = shfile_insert(pfdtab, (intptr_t)hFile, flags, 0, -1, "shfile_open", absname); else fd = shfile_dos2errno(GetLastError()); } # else /* K_OS != K_OS_WINDOWS */ fd = shfile_make_path(pfdtab, name, &absname[0]); if (!fd) { fd = open(absname, flags, mode); if (fd != -1) fd = shfile_copy_insert_and_close(pfdtab, &fd, flags, 0, -1, "shfile_open", absname); } # endif /* K_OS != K_OS_WINDOWS */ #else fd = open(name, flags, mode); #endif TRACE2((NULL, "shfile_open(%p:{%s}, %#x, 0%o) -> %d [%d]\n", name, name, flags, mode, fd, errno)); return fd; } int shfile_pipe(shfdtab *pfdtab, int fds[2]) { int rc = -1; #ifdef SHFILE_IN_USE # if K_OS == K_OS_WINDOWS HANDLE hRead = INVALID_HANDLE_VALUE; HANDLE hWrite = INVALID_HANDLE_VALUE; SECURITY_ATTRIBUTES SecurityAttributes; SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.lpSecurityDescriptor = NULL; SecurityAttributes.bInheritHandle = FALSE; fds[1] = fds[0] = -1; if (CreatePipe(&hRead, &hWrite, &SecurityAttributes, SHFILE_PIPE_SIZE)) { fds[0] = shfile_insert(pfdtab, (intptr_t)hRead, O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-rd"); if (fds[0] != -1) { fds[1] = shfile_insert(pfdtab, (intptr_t)hWrite, O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-wr"); if (fds[1] != -1) rc = 0; } # else int native_fds[2]; fds[1] = fds[0] = -1; if (!pipe(native_fds)) { fds[0] = shfile_copy_insert_and_close(pfdtab, &native_fds[0], O_RDONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-rd"); if (fds[0] != -1) { fds[1] = shfile_copy_insert_and_close(pfdtab, &native_fds[1], O_WRONLY, SHFILE_FLAGS_PIPE, -1, "shfile_pipe", "pipe-wr"); if (fds[1] != -1) rc = 0; } # endif if (fds[1] == -1) { int s = errno; if (fds[0] != -1) { shmtxtmp tmp; shmtx_enter(&pfdtab->mtx, &tmp); rc = fds[0]; pfdtab->tab[rc].fd = -1; pfdtab->tab[rc].oflags = 0; pfdtab->tab[rc].shflags = 0; pfdtab->tab[rc].native = -1; shmtx_leave(&pfdtab->mtx, &tmp); } # if K_OS == K_OS_WINDOWS CloseHandle(hRead); CloseHandle(hWrite); # else close(native_fds[0]); close(native_fds[1]); # endif fds[0] = fds[1] = -1; errno = s; rc = -1; } } else { # if K_OS == K_OS_WINDOWS errno = shfile_dos2errno(GetLastError()); # endif rc = -1; } #else rc = pipe(fds); #endif TRACE2((NULL, "shfile_pipe() -> %d{%d,%d} [%d]\n", rc, fds[0], fds[1], errno)); return rc; } /** * dup(). */ int shfile_dup(shfdtab *pfdtab, int fd) { return shfile_fcntl(pfdtab,fd, F_DUPFD, 0); } /** * Move the file descriptor, closing any existing descriptor at @a fdto. * * @returns fdto on success, -1 and errno on failure. * @param pfdtab The file descriptor table. * @param fdfrom The descriptor to move. * @param fdto Where to move it. */ int shfile_movefd(shfdtab *pfdtab, int fdfrom, int fdto) { #ifdef SHFILE_IN_USE int rc; shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fdfrom, &tmp); if (file) { /* prepare the new entry */ if ((unsigned)fdto >= pfdtab->size) shfile_grow_tab_locked(pfdtab, fdto); if ((unsigned)fdto < pfdtab->size) { if (pfdtab->tab[fdto].fd != -1) shfile_native_close(pfdtab->tab[fdto].native, &pfdtab->tab[fdto], K_FALSE); /* setup the target. */ pfdtab->tab[fdto].fd = fdto; pfdtab->tab[fdto].oflags = file->oflags; pfdtab->tab[fdto].shflags = file->shflags; pfdtab->tab[fdto].native = file->native; # ifdef DEBUG pfdtab->tab[fdto].dbgname = file->dbgname; # endif /* close the source. */ file->fd = -1; file->oflags = 0; file->shflags = 0; file->native = -1; # ifdef DEBUG file->dbgname = NULL; # endif rc = fdto; } else { errno = EMFILE; rc = -1; } shfile_put(pfdtab, file, &tmp); } else rc = -1; return rc; #else int fdnew = dup2(fdfrom, fdto); if (fdnew >= 0) close(fdfrom); return fdnew; #endif } /** * Move the file descriptor to somewhere at @a fdMin or above. * * @returns the new file descriptor success, -1 and errno on failure. * @param pfdtab The file descriptor table. * @param fdfrom The descriptor to move. * @param fdMin The minimum descriptor. */ int shfile_movefd_above(shfdtab *pfdtab, int fdfrom, int fdMin) { #ifdef SHFILE_IN_USE int fdto; shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fdfrom, &tmp); if (file) { /* find a new place */ int i; fdto = -1; for (i = fdMin; (unsigned)i < pfdtab->size; i++) if (pfdtab->tab[i].fd == -1) { fdto = i; break; } if (fdto == -1) fdto = shfile_grow_tab_locked(pfdtab, fdMin); if (fdto != -1) { /* setup the target. */ pfdtab->tab[fdto].fd = fdto; pfdtab->tab[fdto].oflags = file->oflags; pfdtab->tab[fdto].shflags = file->shflags; pfdtab->tab[fdto].native = file->native; # ifdef DEBUG pfdtab->tab[fdto].dbgname = file->dbgname; # endif /* close the source. */ file->fd = -1; file->oflags = 0; file->shflags = 0; file->native = -1; # ifdef DEBUG file->dbgname = NULL; # endif } else { errno = EMFILE; fdto = -1; } shfile_put(pfdtab, file, &tmp); } else fdto = -1; return fdto; #else int fdnew = fcntl(fdfrom, F_DUPFD, fdMin); if (fdnew >= 0) close(fdfrom); return fdnew; #endif } /** * close(). */ int shfile_close(shfdtab *pfdtab, unsigned fd) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { shfile_native_close(file->native, file, K_FALSE); file->fd = -1; file->oflags = 0; file->shflags = 0; file->native = -1; # ifdef DEBUG sh_free(NULL, file->dbgname); file->dbgname = NULL; # endif shfile_put(pfdtab, file, &tmp); rc = 0; } else rc = -1; #else rc = close(fd); #endif TRACE2((NULL, "shfile_close(%d) -> %d [%d]\n", fd, rc, errno)); return rc; } /** * read(). */ long shfile_read(shfdtab *pfdtab, int fd, void *buf, size_t len) { long rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { # if K_OS == K_OS_WINDOWS DWORD dwRead = 0; if (ReadFile((HANDLE)file->native, buf, (DWORD)len, &dwRead, NULL)) rc = dwRead; else rc = shfile_dos2errno(GetLastError()); # else rc = read(file->native, buf, len); # endif shfile_put(pfdtab, file, &tmp); } else rc = -1; #else rc = read(fd, buf, len); #endif return rc; } /** * write(). */ long shfile_write(shfdtab *pfdtab, int fd, const void *buf, size_t len) { long rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { # if K_OS == K_OS_WINDOWS DWORD dwWritten = 0; if (WriteFile((HANDLE)file->native, buf, (DWORD)len, &dwWritten, NULL)) rc = dwWritten; else rc = shfile_dos2errno(GetLastError()); # else rc = write(file->native, buf, len); # endif file->shflags |= SHFILE_FLAGS_DIRTY; /* there should be no concurrent access, so this is safe. */ shfile_put(pfdtab, file, &tmp); } else rc = -1; # ifdef DEBUG if (fd != shthread_get_shell()->tracefd) TRACE2((NULL, "shfile_write(%d,,%d) -> %d [%d]\n", fd, len, rc, errno)); # endif #else if (fd != shthread_get_shell()->tracefd) { int iSavedErrno = errno; struct stat s; int x; x = fstat(fd, &s); TRACE2((NULL, "shfile_write(%d) - %lu bytes (%d) - pos %lu - before; %o\n", fd, (long)s.st_size, x, (long)lseek(fd, 0, SEEK_CUR), s.st_mode )); K_NOREF(x); errno = iSavedErrno; } rc = write(fd, buf, len); #endif return rc; } /** * lseek(). */ long shfile_lseek(shfdtab *pfdtab, int fd, long off, int whench) { long rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { # if K_OS == K_OS_WINDOWS kHlpAssert(SEEK_SET == FILE_BEGIN); kHlpAssert(SEEK_CUR == FILE_CURRENT); kHlpAssert(SEEK_END == FILE_END); rc = SetFilePointer((HANDLE)file->native, off, NULL, whench); if (rc == INVALID_SET_FILE_POINTER) rc = shfile_dos2errno(GetLastError()); # else rc = lseek(file->native, off, whench); # endif shfile_put(pfdtab, file, &tmp); } else rc = -1; #else rc = lseek(fd, off, whench); #endif return rc; } int shfile_fcntl(shfdtab *pfdtab, int fd, int cmd, int arg) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { switch (cmd) { case F_GETFL: rc = file->oflags; break; case F_SETFL: { unsigned mask = O_NONBLOCK | O_APPEND | O_BINARY | O_TEXT; # ifdef O_DIRECT mask |= O_DIRECT; # endif # ifdef O_ASYNC mask |= O_ASYNC; # endif # ifdef O_SYNC mask |= O_SYNC; # endif if ((file->oflags & mask) == (arg & mask)) rc = 0; else { # if K_OS == K_OS_WINDOWS kHlpAssert(0); errno = EINVAL; rc = -1; # else rc = fcntl(file->native, F_SETFL, arg); if (rc != -1) file->oflags = (file->oflags & ~mask) | (arg & mask); # endif } break; } case F_DUPFD: { # if K_OS == K_OS_WINDOWS HANDLE hNew = INVALID_HANDLE_VALUE; if (DuplicateHandle(GetCurrentProcess(), (HANDLE)file->native, GetCurrentProcess(), &hNew, 0, FALSE /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) rc = shfile_insert(pfdtab, (intptr_t)hNew, file->oflags, file->shflags, arg, "shfile_fcntl", SHFILE_DBGNAME(file->dbgname)); else rc = shfile_dos2errno(GetLastError()); # else int nativeNew = fcntl(file->native, F_DUPFD, SHFILE_UNIX_MIN_FD); if (nativeNew != -1) rc = shfile_insert(pfdtab, nativeNew, file->oflags, file->shflags, arg, "shfile_fcntl", SHFILE_DBGNAME(file->dbgname)); else rc = -1; # endif break; } default: errno = -EINVAL; rc = -1; break; } shfile_put(pfdtab, file, &tmp); } else rc = -1; #else rc = fcntl(fd, cmd, arg); #endif switch (cmd) { case F_GETFL: TRACE2((NULL, "shfile_fcntl(%d,F_GETFL,ignored=%d) -> %d [%d]\n", fd, arg, rc, errno)); break; case F_SETFL: TRACE2((NULL, "shfile_fcntl(%d,F_SETFL,newflags=%#x) -> %d [%d]\n", fd, arg, rc, errno)); break; case F_DUPFD: TRACE2((NULL, "shfile_fcntl(%d,F_DUPFD,minfd=%d) -> %d [%d]\n", fd, arg, rc, errno)); break; default: TRACE2((NULL, "shfile_fcntl(%d,%d,%d) -> %d [%d]\n", fd, cmd, arg, rc, errno)); break; } return rc; } int shfile_stat(shfdtab *pfdtab, const char *path, struct stat *pst) { #ifdef SHFILE_IN_USE char abspath[SHFILE_MAX_PATH]; int rc; rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) { # if K_OS == K_OS_WINDOWS # if 1 rc = birdStatFollowLink(abspath, pst); # else int dir_slash = shfile_trailing_slash_hack(abspath); rc = stat(abspath, pst); /** @todo re-implement stat. */ if (!rc && dir_slash && !S_ISDIR(pst->st_mode)) { rc = -1; errno = ENOTDIR; } # endif # else rc = stat(abspath, pst); # endif } TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d] st_size=%llu st_mode=%o\n", path, rc, errno, (unsigned long long)pst->st_size, pst->st_mode)); return rc; #else return stat(path, pst); #endif } /** * @retval 1 if regular file. * @retval 0 if found but not a regular file. * @retval -1 and errno on failure */ int shfile_stat_isreg(shfdtab *pfdtab, const char *path) { #if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS char abspath[SHFILE_MAX_PATH]; KU16 mode = 0; int rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) { rc = birdStatModeOnly(abspath, &mode, 0 /*fFollowLink*/); if (rc >= 0) rc = S_ISREG(mode) ? 1 : 0; } TRACE2((NULL, "shfile_stat_isreg(,%s,) -> %d [%d] st_mode=%o\n", path, rc, errno, mode)); return rc; #else struct stat st; int rc = shfile_stat(pfdtab, path, &st); if (rc >= 0) rc = S_ISREG(st.st_mode) ? 1 : 0; return rc; #endif } /** * Same as shfile_stat, but without the data structure. */ int shfile_stat_exists(shfdtab *pfdtab, const char *path) { #if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS char abspath[SHFILE_MAX_PATH]; KU16 mode = 0; int rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) rc = birdStatModeOnly(abspath, &mode, 0 /*fFollowLink*/); TRACE2((NULL, "shfile_stat_exists(,%s,) -> %d [%d] st_mode=%o\n", path, rc, errno, mode)); return rc; #else struct stat ignored; return shfile_stat(pfdtab, path, &ignored); #endif } int shfile_lstat(shfdtab *pfdtab, const char *path, struct stat *pst) { int rc; #ifdef SHFILE_IN_USE char abspath[SHFILE_MAX_PATH]; rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) { # if K_OS == K_OS_WINDOWS # if 1 rc = birdStatOnLink(abspath, pst); # else int dir_slash = shfile_trailing_slash_hack(abspath); rc = stat(abspath, pst); /** @todo re-implement stat. */ if (!rc && dir_slash && !S_ISDIR(pst->st_mode)) { rc = -1; errno = ENOTDIR; } # endif # else rc = lstat(abspath, pst); # endif } #else rc = stat(path, pst); #endif TRACE2((NULL, "shfile_lstat(,%s,) -> %d [%d] st_size=%llu st_mode=%o\n", path, rc, errno, (unsigned long long)pst->st_size, pst->st_mode)); return rc; } /** * chdir(). */ int shfile_chdir(shfdtab *pfdtab, const char *path) { int rc; #ifdef SHFILE_IN_USE shinstance *psh = shthread_get_shell(); char abspath[SHFILE_MAX_PATH]; rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) { char *abspath_copy = sh_strdup(psh, abspath); char *free_me = abspath_copy; rc = chdir(abspath); if (!rc) { shmtxtmp tmp; shmtx_enter(&pfdtab->mtx, &tmp); shfile_fix_slashes(abspath_copy); free_me = pfdtab->cwd; pfdtab->cwd = abspath_copy; shmtx_leave(&pfdtab->mtx, &tmp); } sh_free(psh, free_me); } else rc = -1; #else rc = chdir(path); #endif TRACE2((NULL, "shfile_chdir(,%s) -> %d [%d]\n", path, rc, errno)); return rc; } /** * getcwd(). */ char *shfile_getcwd(shfdtab *pfdtab, char *buf, int size) { char *ret; #ifdef SHFILE_IN_USE ret = NULL; if (buf && !size) errno = -EINVAL; else { size_t cwd_size; shmtxtmp tmp; shmtx_enter(&pfdtab->mtx, &tmp); cwd_size = strlen(pfdtab->cwd) + 1; if (buf) { if (cwd_size <= (size_t)size) ret = memcpy(buf, pfdtab->cwd, cwd_size); else errno = ERANGE; } else { if ((size_t)size < cwd_size) size = (int)cwd_size; ret = sh_malloc(shthread_get_shell(), size); if (ret) ret = memcpy(ret, pfdtab->cwd, cwd_size); else errno = ENOMEM; } shmtx_leave(&pfdtab->mtx, &tmp); } #else ret = getcwd(buf, size); #endif TRACE2((NULL, "shfile_getcwd(,%p,%d) -> %s [%d]\n", buf, size, ret, errno)); return ret; } /** * access(). */ int shfile_access(shfdtab *pfdtab, const char *path, int type) { int rc; #ifdef SHFILE_IN_USE char abspath[SHFILE_MAX_PATH]; rc = shfile_make_path(pfdtab, path, &abspath[0]); if (!rc) { # ifdef _MSC_VER if (type & X_OK) type = (type & ~X_OK) | R_OK; # endif rc = access(abspath, type); } #else # ifdef _MSC_VER if (type & X_OK) type = (type & ~X_OK) | R_OK; # endif rc = access(path, type); #endif TRACE2((NULL, "shfile_access(,%s,%#x) -> %d [%d]\n", path, type, rc, errno)); return rc; } /** * isatty() */ int shfile_isatty(shfdtab *pfdtab, int fd) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { # if K_OS == K_OS_WINDOWS rc = (file->shflags & SHFILE_FLAGS_TYPE_MASK) == SHFILE_FLAGS_TTY; # else rc = isatty(file->native); # endif shfile_put(pfdtab, file, &tmp); } else rc = 0; #else rc = isatty(fd); #endif TRACE2((NULL, "isatty(%d) -> %d [%d]\n", fd, rc, errno)); return rc; } /** * fcntl F_SETFD / FD_CLOEXEC. */ int shfile_cloexec(shfdtab *pfdtab, int fd, int closeit) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { if (closeit) file->shflags |= SHFILE_FLAGS_CLOSE_ON_EXEC; else file->shflags &= ~SHFILE_FLAGS_CLOSE_ON_EXEC; shfile_put(pfdtab, file, &tmp); rc = 0; } else rc = -1; #else rc = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | (closeit ? FD_CLOEXEC : 0)); #endif TRACE2((NULL, "shfile_cloexec(%d, %d) -> %d [%d]\n", fd, closeit, rc, errno)); return rc; } /** * Sets the SHFILE_FLAGS_TRACE flag. */ int shfile_set_trace(shfdtab *pfdtab, int fd) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { file->shflags |= SHFILE_FLAGS_TRACE; shfile_put(pfdtab, file, &tmp); rc = 0; } else rc = -1; #else rc = 0; #endif TRACE2((NULL, "shfile_set_trace(%d) -> %d\n", fd, rc)); return rc; } int shfile_ioctl(shfdtab *pfdtab, int fd, unsigned long request, void *buf) { int rc; #ifdef SHFILE_IN_USE shmtxtmp tmp; shfile *file = shfile_get(pfdtab, fd, &tmp); if (file) { # if K_OS == K_OS_WINDOWS rc = -1; errno = ENOSYS; # else rc = ioctl(file->native, request, buf); # endif shfile_put(pfdtab, file, &tmp); } else rc = -1; #else rc = ioctl(fd, request, buf); #endif TRACE2((NULL, "ioctl(%d, %#x, %p) -> %d\n", fd, request, buf, rc)); return rc; } mode_t shfile_get_umask(shfdtab *pfdtab) { /** @todo */ return 022; } void shfile_set_umask(shfdtab *pfdtab, mode_t mask) { /** @todo */ (void)mask; } shdir *shfile_opendir(shfdtab *pfdtab, const char *dir) { #if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS shdir *pdir = NULL; TRACE2((NULL, "shfile_opendir: dir='%s'\n", dir)); shfile_init_globals(); if (g_pfnNtQueryDirectoryFile) { char abspath[SHFILE_MAX_PATH]; if (shfile_make_path(pfdtab, dir, &abspath[0]) == 0) { HANDLE hFile; SECURITY_ATTRIBUTES SecurityAttributes; SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.lpSecurityDescriptor = NULL; SecurityAttributes.bInheritHandle = FALSE; hFile = CreateFileA(abspath, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, &SecurityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS, NULL /* hTemplateFile */); if (hFile != INVALID_HANDLE_VALUE) { pdir = (shdir *)sh_malloc(shthread_get_shell(), sizeof(*pdir)); if (pdir) { pdir->pfdtab = pfdtab; pdir->native = hFile; pdir->off = ~(size_t)0; } else CloseHandle(hFile); } else { errno = shfile_dos2errno(GetLastError()); TRACE2((NULL, "shfile_opendir: CreateFileA(%s) -> %d/%d\n", abspath, GetLastError(), errno)); } } } else errno = ENOSYS; return pdir; #else TRACE2((NULL, "shfile_opendir: dir='%s'\n", dir)); return (shdir *)opendir(dir); #endif } shdirent *shfile_readdir(struct shdir *pdir) { #if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS if (pdir) { NTSTATUS rcNt; if ( pdir->off == ~(size_t)0 || pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) >= pdir->cb) { MY_IO_STATUS_BLOCK Ios; memset(&Ios, 0, sizeof(Ios)); rcNt = g_pfnNtQueryDirectoryFile(pdir->native, NULL /*Event*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/, &Ios, &pdir->buf[0], sizeof(pdir->buf), MY_FileNamesInformation, FALSE /*ReturnSingleEntry*/, NULL /*FileName*/, pdir->off == ~(size_t)0 /*RestartScan*/); if (rcNt >= 0 && rcNt != STATUS_PENDING) { pdir->cb = Ios.Information; pdir->off = 0; } else if (rcNt == STATUS_NO_MORE_FILES) errno = 0; /* wrong? */ else shfile_nt2errno(rcNt); } if ( pdir->off != ~(size_t)0 && pdir->off + sizeof(MY_FILE_NAMES_INFORMATION) <= pdir->cb) { PMY_FILE_NAMES_INFORMATION pcur = (PMY_FILE_NAMES_INFORMATION)&pdir->buf[pdir->off]; ANSI_STRING astr; UNICODE_STRING ustr; astr.Length = astr.MaximumLength = sizeof(pdir->ent.name); astr.Buffer = &pdir->ent.name[0]; ustr.Length = ustr.MaximumLength = pcur->FileNameLength < ~(USHORT)0 ? (USHORT)pcur->FileNameLength : ~(USHORT)0; ustr.Buffer = &pcur->FileName[0]; rcNt = g_pfnRtlUnicodeStringToAnsiString(&astr, &ustr, 0/*AllocateDestinationString*/); if (rcNt < 0) sprintf(pdir->ent.name, "conversion-failed-%08x-rcNt=%08x-len=%u", pcur->FileIndex, rcNt, pcur->FileNameLength); if (pcur->NextEntryOffset) pdir->off += pcur->NextEntryOffset; else pdir->off = pdir->cb; return &pdir->ent; } } else errno = EINVAL; return NULL; #else struct dirent *pde = readdir((DIR *)pdir); return pde ? (shdirent *)&pde->d_name[0] : NULL; #endif } void shfile_closedir(struct shdir *pdir) { #if defined(SHFILE_IN_USE) && K_OS == K_OS_WINDOWS if (pdir) { CloseHandle(pdir->native); pdir->pfdtab = NULL; pdir->native = INVALID_HANDLE_VALUE; sh_free(shthread_get_shell(), pdir); } else errno = EINVAL; #else closedir((DIR *)pdir); #endif }