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

Last change on this file since 2303 was 2303, checked in by bird, 16 years ago

kash: new execve implementation for windows. more file ops.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 34.6 KB
Line 
1/* $Id: shfile.c 2303 2009-03-01 07:25:29Z bird $ */
2/** @file
3 *
4 * File management.
5 *
6 * Copyright (c) 2007-2009 knut st. osmundsen <bird-kBuild-spamix@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 <assert.h>
35
36#if K_OS == K_OS_WINDOWS
37# include <limits.h>
38# ifndef PIPE_BUF
39# define PIPE_BUF 512
40# endif
41# include <Windows.h>
42#else
43# include <unistd.h>
44# include <fcntl.h>
45# include <dirent.h>
46#endif
47
48
49/*******************************************************************************
50* Defined Constants And Macros *
51*******************************************************************************/
52/** @def SHFILE_IN_USE
53 * Whether the file descriptor table stuff is actually in use or not.
54 */
55#if K_OS == K_OS_WINDOWS \
56 || !defined(SH_FORKED_MODE)
57# define SHFILE_IN_USE
58#endif
59/** The max file table size. */
60#define SHFILE_MAX 1024
61/** The file table growth rate. */
62#define SHFILE_GROW 64
63/** The min native unix file descriptor. */
64#define SHFILE_UNIX_MIN_FD 32
65/** The path buffer size we use. */
66#define SHFILE_MAX_PATH 4096
67
68/** Set errno and return. Doing a trace in debug build. */
69#define RETURN_ERROR(rc, err, msg) \
70 do { \
71 TRACE2((NULL, "%s: " ## msg ## " - returning %d / %d\n", __FUNCTION__, (rc), (err))); \
72 errno = (err); \
73 return (rc); \
74 } while (0)
75
76#if K_OS == K_OS_WINDOWS
77 /* See msdos.h for description. */
78# define FOPEN 0x01
79# define FEOFLAG 0x02
80# define FCRLF 0x04
81# define FPIPE 0x08
82# define FNOINHERIT 0x10
83# define FAPPEND 0x20
84# define FDEV 0x40
85# define FTEXT 0x80
86#endif
87
88
89#ifdef SHFILE_IN_USE
90
91/**
92 * Close the specified native handle.
93 *
94 * @param native The native file handle.
95 * @param flags The flags in case they might come in handy later.
96 */
97static void shfile_native_close(intptr_t native, unsigned flags)
98{
99#if K_OS == K_OS_WINDOWS
100 BOOL fRc = CloseHandle((HANDLE)native);
101 assert(fRc); (void)fRc;
102#else
103 int s = errno;
104 close(native);
105 errno = s;
106#endif
107 (void)flags;
108}
109
110/**
111 * Inserts the file into the descriptor table.
112 *
113 * If we're out of memory and cannot extend the table, we'll close the
114 * file, set errno to EMFILE and return -1.
115 *
116 * @returns The file descriptor number. -1 and errno on failure.
117 * @param pfdtab The file descriptor table.
118 * @param native The native file handle.
119 * @param flags The flags the it was created with.
120 * @param fdMin The minimum file descriptor number, pass -1 if any is ok.
121 * @param who Who we're doing this for (for logging purposes).
122 */
123static int shfile_insert(shfdtab *pfdtab, intptr_t native, unsigned flags, int fdMin, const char *who)
124{
125 shmtxtmp tmp;
126 int fd;
127 int i;
128
129 /*
130 * Fend of bad stuff.
131 */
132 if (fdMin >= SHFILE_MAX)
133 {
134 errno = EMFILE;
135 return -1;
136 }
137
138 shmtx_enter(&pfdtab->mtx, &tmp);
139
140 /*
141 * Search for a fitting unused location.
142 */
143 fd = -1;
144 for (i = 0; (unsigned)i < pfdtab->size; i++)
145 if ( i >= fdMin
146 && pfdtab->tab[i].fd == -1)
147 {
148 fd = i;
149 break;
150 }
151 if (fd == -1)
152 {
153 /*
154 * Grow the descriptor table.
155 */
156 shfile *new_tab;
157 int new_size = pfdtab->size + SHFILE_GROW;
158 while (new_size < fdMin)
159 new_size += SHFILE_GROW;
160 new_tab = sh_realloc(shthread_get_shell(), pfdtab->tab, new_size * sizeof(shfile));
161 if (new_tab)
162 {
163 for (i = pfdtab->size; i < new_size; i++)
164 {
165 new_tab[i].fd = -1;
166 new_tab[i].flags = 0;
167 new_tab[i].native = -1;
168 }
169
170 fd = pfdtab->size;
171 if (fd < fdMin)
172 fd = fdMin;
173
174 pfdtab->tab = new_tab;
175 pfdtab->size = new_size;
176 }
177 }
178
179 /*
180 * Fill in the entry if we've found one.
181 */
182 if (fd != -1)
183 {
184 pfdtab->tab[fd].fd = fd;
185 pfdtab->tab[fd].flags = flags;
186 pfdtab->tab[fd].cloexec = 0;
187 pfdtab->tab[fd].native = native;
188 }
189 else
190 shfile_native_close(native, flags);
191
192 shmtx_leave(&pfdtab->mtx, &tmp);
193
194 if (fd == -1)
195 errno = EMFILE;
196 (void)who;
197 return fd;
198}
199
200/**
201 * Gets a file descriptor and lock the file descriptor table.
202 *
203 * @returns Pointer to the file and table ownership on success,
204 * NULL and errno set to EBADF on failure.
205 * @param pfdtab The file descriptor table.
206 * @param fd The file descriptor number.
207 * @param ptmp See shmtx_enter.
208 */
209static shfile *shfile_get(shfdtab *pfdtab, int fd, shmtxtmp *ptmp)
210{
211 shfile *file = NULL;
212 if ( fd >= 0
213 && (unsigned)fd < pfdtab->size)
214 {
215 shmtx_enter(&pfdtab->mtx, ptmp);
216 if ((unsigned)fd < pfdtab->size
217 && pfdtab->tab[fd].fd != -1)
218 file = &pfdtab->tab[fd];
219 else
220 shmtx_leave(&pfdtab->mtx, ptmp);
221 }
222 if (!file)
223 errno = EBADF;
224 return file;
225}
226
227/**
228 * Puts back a file descriptor and releases the table ownership.
229 *
230 * @param pfdtab The file descriptor table.
231 * @param file The file.
232 * @param ptmp See shmtx_leave.
233 */
234static void shfile_put(shfdtab *pfdtab, shfile *file, shmtxtmp *ptmp)
235{
236 shmtx_leave(&pfdtab->mtx, ptmp);
237 assert(file);
238 (void)file;
239}
240
241/**
242 * Constructs a path from the current directory and the passed in path.
243 *
244 * @returns 0 on success, -1 and errno set to ENAMETOOLONG or EINVAL on failure.
245 *
246 * @param pfdtab The file descriptor table
247 * @param path The path the caller supplied.
248 * @param buf Where to put the path. This is assumed to be SHFILE_MAX_PATH
249 * chars in size.
250 */
251int shfile_make_path(shfdtab *pfdtab, const char *path, char *buf)
252{
253 size_t path_len = strlen(path);
254 if (path_len == 0)
255 {
256 errno = EINVAL;
257 return -1;
258 }
259 if (path_len >= SHFILE_MAX_PATH)
260 {
261 errno = ENAMETOOLONG;
262 return -1;
263 }
264 if ( *path == '/'
265#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
266 || *path == '\\'
267 || ( *path
268 && path[1] == ':'
269 && ( (*path >= 'A' && *path <= 'Z')
270 || (*path >= 'a' && *path <= 'z')))
271#endif
272 )
273 {
274 memcpy(buf, path, path_len + 1);
275 }
276 else
277 {
278 size_t cwd_len;
279 shmtxtmp tmp;
280
281 shmtx_enter(&pfdtab->mtx, &tmp);
282
283 cwd_len = strlen(pfdtab->cwd);
284 memcpy(buf, pfdtab->cwd, cwd_len);
285
286 shmtx_leave(&pfdtab->mtx, &tmp);
287
288 if (cwd_len + path_len + 1 >= SHFILE_MAX_PATH)
289 {
290 errno = ENAMETOOLONG;
291 return -1;
292 }
293 if ( !cwd_len
294 || buf[cwd_len - 1] != '/')
295 buf[cwd_len++] = '/';
296 memcpy(buf + cwd_len, path, path_len + 1);
297 }
298
299#if K_OS == K_OS_WINDOWS || K_OS == K_OS_OS2
300 if (!strcmp(buf, "/dev/null"))
301 strcpy(buf, "NUL");
302#endif
303 return 0;
304}
305
306# if K_OS == K_OS_WINDOWS
307/**
308 * Converts a DOS(/Windows) error code to errno,
309 * assigning it to errno.
310 *
311 * @returns -1
312 * @param err The DOS error.
313 */
314static int shfile_dos2errno(int err)
315{
316 switch (err)
317 {
318 case ERROR_BAD_ENVIRONMENT: errno = E2BIG; break;
319 case ERROR_ACCESS_DENIED: errno = EACCES; break;
320 case ERROR_CURRENT_DIRECTORY: errno = EACCES; break;
321 case ERROR_LOCK_VIOLATION: errno = EACCES; break;
322 case ERROR_NETWORK_ACCESS_DENIED: errno = EACCES; break;
323 case ERROR_CANNOT_MAKE: errno = EACCES; break;
324 case ERROR_FAIL_I24: errno = EACCES; break;
325 case ERROR_DRIVE_LOCKED: errno = EACCES; break;
326 case ERROR_SEEK_ON_DEVICE: errno = EACCES; break;
327 case ERROR_NOT_LOCKED: errno = EACCES; break;
328 case ERROR_LOCK_FAILED: errno = EACCES; break;
329 case ERROR_NO_PROC_SLOTS: errno = EAGAIN; break;
330 case ERROR_MAX_THRDS_REACHED: errno = EAGAIN; break;
331 case ERROR_NESTING_NOT_ALLOWED: errno = EAGAIN; break;
332 case ERROR_INVALID_HANDLE: errno = EBADF; break;
333 case ERROR_INVALID_TARGET_HANDLE: errno = EBADF; break;
334 case ERROR_DIRECT_ACCESS_HANDLE: errno = EBADF; break;
335 case ERROR_WAIT_NO_CHILDREN: errno = ECHILD; break;
336 case ERROR_CHILD_NOT_COMPLETE: errno = ECHILD; break;
337 case ERROR_FILE_EXISTS: errno = EEXIST; break;
338 case ERROR_ALREADY_EXISTS: errno = EEXIST; break;
339 case ERROR_INVALID_FUNCTION: errno = EINVAL; break;
340 case ERROR_INVALID_ACCESS: errno = EINVAL; break;
341 case ERROR_INVALID_DATA: errno = EINVAL; break;
342 case ERROR_INVALID_PARAMETER: errno = EINVAL; break;
343 case ERROR_NEGATIVE_SEEK: errno = EINVAL; break;
344 case ERROR_TOO_MANY_OPEN_FILES: errno = EMFILE; break;
345 case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
346 case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
347 case ERROR_INVALID_DRIVE: errno = ENOENT; break;
348 case ERROR_NO_MORE_FILES: errno = ENOENT; break;
349 case ERROR_BAD_NETPATH: errno = ENOENT; break;
350 case ERROR_BAD_NET_NAME: errno = ENOENT; break;
351 case ERROR_BAD_PATHNAME: errno = ENOENT; break;
352 case ERROR_FILENAME_EXCED_RANGE: errno = ENOENT; break;
353 case ERROR_BAD_FORMAT: errno = ENOEXEC; break;
354 case ERROR_ARENA_TRASHED: errno = ENOMEM; break;
355 case ERROR_NOT_ENOUGH_MEMORY: errno = ENOMEM; break;
356 case ERROR_INVALID_BLOCK: errno = ENOMEM; break;
357 case ERROR_NOT_ENOUGH_QUOTA: errno = ENOMEM; break;
358 case ERROR_DISK_FULL: errno = ENOSPC; break;
359 case ERROR_DIR_NOT_EMPTY: errno = ENOTEMPTY; break;
360 case ERROR_BROKEN_PIPE: errno = EPIPE; break;
361 case ERROR_NOT_SAME_DEVICE: errno = EXDEV; break;
362 default: errno = EINVAL; break;
363 }
364 return -1;
365}
366# endif /* K_OS == K_OS_WINDOWS */
367
368#endif /* SHFILE_IN_USE */
369
370/**
371 * Initializes a file descriptor table.
372 *
373 * @returns 0 on success, -1 and errno on failure.
374 * @param pfdtab The table to initialize.
375 * @param inherit File descriptor table to inherit from. If not specified
376 * we will inherit from the current process as it were.
377 */
378int shfile_init(shfdtab *pfdtab, shfdtab *inherit)
379{
380 int rc;
381
382 pfdtab->cwd = NULL;
383 pfdtab->size = 0;
384 pfdtab->tab = NULL;
385 rc = shmtx_init(&pfdtab->mtx);
386 if (!rc)
387 {
388#ifdef SHFILE_IN_USE
389 char buf[SHFILE_MAX_PATH];
390 if (getcwd(buf, sizeof(buf)))
391 {
392 pfdtab->cwd = sh_strdup(NULL, buf);
393 if (!inherit)
394 {
395# if K_OS == K_OS_WINDOWS
396 static const struct
397 {
398 DWORD dwStdHandle;
399 unsigned flags;
400 } aStdHandles[3] =
401 {
402 { STD_INPUT_HANDLE, _O_RDONLY },
403 { STD_OUTPUT_HANDLE, _O_WRONLY },
404 { STD_ERROR_HANDLE, _O_WRONLY }
405 };
406 unsigned i;
407 STARTUPINFO Info;
408
409 rc = 0;
410
411 /* Try pick up the Visual C++ CRT file descriptor info. */
412 __try {
413 GetStartupInfo(&Info);
414 } __except (EXCEPTION_EXECUTE_HANDLER) {
415 memset(&Info, 0, sizeof(Info));
416 }
417 if ( Info.cbReserved2 <= sizeof(int)
418 && (uintptr_t)Info.lpReserved2 >= 0x1000
419 && *(int *)Info.lpReserved2 * (sizeof(int) + sizeof(intptr_t)) + 4 <= Info.cbReserved2
420 && *(int *)Info.lpReserved2 <= 2048
421 && *(int *)Info.lpReserved2 >= 1)
422 {
423 unsigned c = *(int *)Info.lpReserved2;
424 char *aosfile = (char *)Info.lpReserved2 + sizeof(int);
425 intptr_t *aosfhnd = (intptr_t *)(aosfile + c);
426
427 /** @todo process */
428
429 }
430
431 /* Check the three standard handles. */
432 for (i = 0; i < 3; i++)
433 {
434 HANDLE hFile = GetStdHandle(aStdHandles[i].dwStdHandle);
435 if ( hFile != INVALID_HANDLE_VALUE
436 && ( (unsigned)i >= pfdtab->size
437 || pfdtab->tab[i].fd == -1))
438 {
439 int fd2 = shfile_insert(pfdtab, (intptr_t)hFile, aStdHandles[i].flags, i, "shtab_init");
440 assert(fd2 == i); (void)fd2;
441 if (fd2 != i)
442 rc = -1;
443 }
444 }
445# else
446# endif
447 }
448 else
449 {
450 /** @todo */
451 errno = ENOSYS;
452 rc = -1;
453 }
454 }
455 else
456 rc = -1;
457#endif
458 }
459 return rc;
460}
461
462#if K_OS == K_OS_WINDOWS && defined(SHFILE_IN_USE)
463
464/**
465 * Helper for shfork.
466 *
467 * @param pfdtab The file descriptor table.
468 * @param set Whether to make all handles inheritable (1) or
469 * to restore them to the rigth state (0).
470 * @param hndls Where to store the three standard handles.
471 */
472void shfile_fork_win(shfdtab *pfdtab, int set, intptr_t *hndls)
473{
474 shmtxtmp tmp;
475 unsigned i;
476 DWORD fFlag = set ? HANDLE_FLAG_INHERIT : 0;
477
478 shmtx_enter(&pfdtab->mtx, &tmp);
479 TRACE2((NULL, "shfile_fork_win:\n"));
480
481 i = pfdtab->size;
482 while (i-- > 0)
483 {
484 if (pfdtab->tab[i].fd == i)
485 {
486 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
487 if (set)
488 TRACE2((NULL, " #%d: native=%#x flags=%#x cloexec=%d\n",
489 i, pfdtab->tab[i].flags, hFile, pfdtab->tab[i].cloexec));
490 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, fFlag))
491 {
492 DWORD err = GetLastError();
493 assert(0);
494 }
495 }
496 }
497
498 if (hndls)
499 {
500 for (i = 0; i < 3; i++)
501 {
502 if ( pfdtab->size > i
503 && pfdtab->tab[i].fd == 0)
504 hndls[i] = pfdtab->tab[i].native;
505 else
506 hndls[i] = (intptr_t)INVALID_HANDLE_VALUE;
507 }
508 }
509
510 shmtx_leave(&pfdtab->mtx, &tmp);
511}
512
513/**
514 * Helper for sh_execve.
515 *
516 * This is called before and after CreateProcess. On the first call it
517 * will mark the non-close-on-exec handles as inheritable and produce
518 * the startup info for the CRT. On the second call, after CreateProcess,
519 * it will restore the handle inheritability properties.
520 *
521 * @returns Pointer to CRT data if prepare is 1, NULL if prepare is 0.
522 * @param pfdtab The file descriptor table.
523 * @param prepare Which call, 1 if before and 0 if after.
524 * @param sizep Where to store the size of the returned data.
525 * @param hndls Where to store the three standard handles.
526 */
527void *shfile_exec_win(shfdtab *pfdtab, int prepare, unsigned short *sizep, intptr_t *hndls)
528{
529 void *pvRet;
530 shmtxtmp tmp;
531 unsigned count;
532 unsigned i;
533
534 shmtx_enter(&pfdtab->mtx, &tmp);
535 TRACE2((NULL, "shfile_fork_win:\n"));
536
537 count = pfdtab->size < (0x10000-4) / (1 + sizeof(HANDLE))
538 ? pfdtab->size
539 : (0x10000-4) / (1 + sizeof(HANDLE));
540
541 if (prepare)
542 {
543 size_t cbData = sizeof(int) + count * (1 + sizeof(HANDLE));
544 uint8_t *pbData = sh_malloc(shthread_get_shell(), cbData);
545 uint8_t *paf = pbData + sizeof(int);
546 HANDLE *pah = (HANDLE *)(paf + count);
547
548 *(int *)pbData = count;
549
550 i = count;
551 while (i-- > 0)
552 {
553 if ( pfdtab->tab[i].fd == i
554 && !pfdtab->tab[i].cloexec)
555 {
556 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
557 TRACE2((NULL, " #%d: native=%#x flags=%#x\n",
558 i, pfdtab->tab[i].flags, hFile));
559
560 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
561 {
562 DWORD err = GetLastError();
563 assert(0);
564 }
565 paf[i] = FOPEN;
566 if (pfdtab->tab[i].flags & _O_APPEND)
567 paf[i] = FAPPEND;
568 if (pfdtab->tab[i].flags & _O_TEXT)
569 paf[i] = FTEXT;
570 pah[i] = hFile;
571 }
572 else
573 {
574 paf[i] = 0;
575 pah[i] = INVALID_HANDLE_VALUE;
576 }
577 }
578
579 for (i = 0; i < 3; i++)
580 {
581 if ( count > i
582 && pfdtab->tab[i].fd == 0)
583 hndls[i] = pfdtab->tab[i].native;
584 else
585 hndls[i] = (intptr_t)INVALID_HANDLE_VALUE;
586 }
587
588 *sizep = (unsigned short)cbData;
589 pvRet = pbData;
590 }
591 else
592 {
593 assert(!hndls);
594 assert(!sizep);
595 i = count;
596 while (i-- > 0)
597 {
598 if ( pfdtab->tab[i].fd == i
599 && !pfdtab->tab[i].cloexec)
600 {
601 HANDLE hFile = (HANDLE)pfdtab->tab[i].native;
602 if (!SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, 0))
603 {
604 DWORD err = GetLastError();
605 assert(0);
606 }
607 }
608 }
609 pvRet = NULL;
610 }
611
612 shmtx_leave(&pfdtab->mtx, &tmp);
613 return pvRet;
614}
615
616#endif /* K_OS_WINDOWS */
617
618
619/**
620 * open().
621 */
622int shfile_open(shfdtab *pfdtab, const char *name, unsigned flags, mode_t mode)
623{
624 int fd;
625#ifdef SHFILE_IN_USE
626 char absname[SHFILE_MAX_PATH];
627# if K_OS == K_OS_WINDOWS
628 HANDLE hFile;
629 DWORD dwDesiredAccess;
630 DWORD dwShareMode;
631 DWORD dwCreationDisposition;
632 DWORD dwFlagsAndAttributes;
633 SECURITY_ATTRIBUTES SecurityAttributes;
634
635# ifndef _O_ACCMODE
636# define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR)
637# endif
638 switch (flags & (_O_ACCMODE | _O_APPEND))
639 {
640 case _O_RDONLY: dwDesiredAccess = GENERIC_READ; break;
641 case _O_RDONLY | _O_APPEND: dwDesiredAccess = GENERIC_READ; break;
642 case _O_WRONLY: dwDesiredAccess = GENERIC_WRITE; break;
643 case _O_WRONLY | _O_APPEND: dwDesiredAccess = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
644 case _O_RDWR: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; break;
645 case _O_RDWR | _O_APPEND: dwDesiredAccess = GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA); break;
646
647 default:
648 RETURN_ERROR(-1, EINVAL, "invalid mode");
649 }
650
651 dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
652
653 SecurityAttributes.nLength = sizeof(SecurityAttributes);
654 SecurityAttributes.lpSecurityDescriptor = NULL;
655 SecurityAttributes.bInheritHandle = FALSE;
656
657 if (flags & _O_CREAT)
658 {
659 if ((flags & (_O_EXCL | _O_TRUNC)) == (_O_EXCL | _O_TRUNC))
660 RETURN_ERROR(-1, EINVAL, "_O_EXCL | _O_TRUNC");
661
662 if (flags & _O_TRUNC)
663 dwCreationDisposition = CREATE_ALWAYS; /* not 100%, but close enough */
664 else if (flags & _O_EXCL)
665 dwCreationDisposition = CREATE_NEW;
666 else
667 dwCreationDisposition = OPEN_ALWAYS;
668 }
669 else if (flags & _O_TRUNC)
670 dwCreationDisposition = TRUNCATE_EXISTING;
671 else
672 dwCreationDisposition = OPEN_EXISTING;
673
674 if (!(flags & _O_CREAT) || (mode & 0222))
675 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
676 else
677 dwFlagsAndAttributes = FILE_ATTRIBUTE_READONLY;
678
679 fd = shfile_make_path(pfdtab, name, &absname[0]);
680 if (!fd)
681 {
682 SetLastError(0);
683 hFile = CreateFileA(absname,
684 dwDesiredAccess,
685 dwShareMode,
686 &SecurityAttributes,
687 dwCreationDisposition,
688 dwFlagsAndAttributes,
689 NULL /* hTemplateFile */);
690 if (hFile != INVALID_HANDLE_VALUE)
691 fd = shfile_insert(pfdtab, (intptr_t)hFile, flags, -1, "shfile_open");
692 else
693 fd = shfile_dos2errno(GetLastError());
694 }
695
696# else /* K_OS != K_OS_WINDOWS */
697 fd = shfile_make_path(pfdtab, name, &absname[0]);
698 if (!fd)
699 {
700 fd = open(absname, flag, mode);
701 if (fd != -1)
702 {
703 int native = fcntl(fd, F_DUPFD, SHFILE_UNIX_MIN_FD);
704 int s = errno;
705 close(fd);
706 errno = s;
707 if (native != -1)
708 fd = shfile_insert(pfdtab, native, flags, -1, "shfile_open");
709 else
710 fd = -1;
711 }
712 }
713
714# endif /* K_OS != K_OS_WINDOWS */
715
716#else
717 fd = open(name, flags, mode);
718#endif
719
720 TRACE2((NULL, "shfile_open(%p:{%s}, %#x, 0%o) -> %d [%d]\n", name, name, flags, mode, fd, errno));
721 return fd;
722}
723
724int shfile_pipe(shfdtab *pfdtab, int fds[2])
725{
726 int rc;
727 int s;
728#ifdef SHFILE_IN_USE
729# if K_OS == K_OS_WINDOWS
730 HANDLE hRead = INVALID_HANDLE_VALUE;
731 HANDLE hWrite = INVALID_HANDLE_VALUE;
732 SECURITY_ATTRIBUTES SecurityAttributes;
733
734 SecurityAttributes.nLength = sizeof(SecurityAttributes);
735 SecurityAttributes.lpSecurityDescriptor = NULL;
736 SecurityAttributes.bInheritHandle = FALSE;
737
738 fds[1] = fds[0] = -1;
739 if (CreatePipe(&hRead, &hWrite, &SecurityAttributes, 4096))
740 {
741 fds[0] = shfile_insert(pfdtab, (intptr_t)hRead, O_RDONLY, -1, "shfile_pipe");
742 if (fds[0] != -1)
743 {
744 fds[1] = shfile_insert(pfdtab, (intptr_t)hWrite, O_WRONLY, -1, "shfile_pipe");
745 if (fds[1] != -1)
746 rc = 0;
747 }
748
749# else
750 int native_fds[2];
751
752 fds[1] = fds[0] = -1;
753 if (!pipe(native_fds))
754 {
755 fds[0] = shfile_insert(pfdtab, native_fds[0], O_RDONLY, -1, "shfile_pipe");
756 if (fds[0] != -1)
757 {
758 fds[1] = shfile_insert(pfdtab, native_fds[1], O_WRONLY, -1, "shfile_pipe");
759 if (fds[1] != -1)
760 rc = 0;
761 }
762# endif
763 s = errno;
764 if (fds[1] == -1)
765 {
766 if (fds[0] != -1)
767 {
768 shmtxtmp tmp;
769 shmtx_enter(&pfdtab->mtx, &tmp);
770 rc = fds[0];
771 pfdtab->tab[rc].fd = -1;
772 pfdtab->tab[rc].flags = 0;
773 pfdtab->tab[rc].native = -1;
774 shmtx_leave(&pfdtab->mtx, &tmp);
775 }
776
777# if K_OS == K_OS_WINDOWS
778 CloseHandle(hRead);
779 CloseHandle(hWrite);
780# else
781 close(native_fds[0]);
782 close(native_fds[1]);
783# endif
784 fds[0] = fds[1] = -1;
785 errno = s;
786 rc = -1;
787 }
788 }
789 else
790 {
791# if K_OS == K_OS_WINDOWS
792 errno = shfile_dos2errno(GetLastError());
793# endif
794 rc = -1;
795 }
796
797#else
798 rc = pipe(fds);
799#endif
800
801 TRACE2((NULL, "shfile_pipe() -> %d{%d,%d} [%d]\n", rc, fds[0], fds[1], errno));
802 return rc;
803}
804
805/**
806 * dup().
807 */
808int shfile_dup(shfdtab *pfdtab, int fd)
809{
810 return shfile_fcntl(pfdtab,fd, F_DUPFD, 0);
811}
812
813/**
814 * close().
815 */
816int shfile_close(shfdtab *pfdtab, unsigned fd)
817{
818 int rc;
819#ifdef SHFILE_IN_USE
820 shmtxtmp tmp;
821 shfile *file = shfile_get(pfdtab, fd, &tmp);
822 if (file)
823 {
824 shfile_native_close(file->native, file->flags);
825
826 file->fd = -1;
827 file->flags = 0;
828 file->native = -1;
829
830 shfile_put(pfdtab, file, &tmp);
831 rc = 0;
832 }
833 else
834 rc = -1;
835
836#else
837 rc = close(fd);
838#endif
839
840 TRACE2((NULL, "shfile_close(%d) -> %d [%d]\n", fd, rc, errno));
841 return rc;
842}
843
844/**
845 * read().
846 */
847long shfile_read(shfdtab *pfdtab, int fd, void *buf, size_t len)
848{
849 long rc;
850#ifdef SHFILE_IN_USE
851 shmtxtmp tmp;
852 shfile *file = shfile_get(pfdtab, fd, &tmp);
853 if (file)
854 {
855# if K_OS == K_OS_WINDOWS
856 DWORD dwRead = 0;
857 if (ReadFile((HANDLE)file->native, buf, (DWORD)len, &dwRead, NULL))
858 rc = dwRead;
859 else
860 rc = shfile_dos2errno(GetLastError());
861# else
862 rc = read(file->native, buf, len);
863# endif
864
865 shfile_put(pfdtab, file, &tmp);
866 }
867 else
868 rc = -1;
869
870#else
871 rc = read(fd, buf, len);
872#endif
873 return rc;
874}
875
876/**
877 * write().
878 */
879long shfile_write(shfdtab *pfdtab, int fd, const void *buf, size_t len)
880{
881 long rc;
882#ifdef SHFILE_IN_USE
883 shmtxtmp tmp;
884 shfile *file = shfile_get(pfdtab, fd, &tmp);
885 if (file)
886 {
887# if K_OS == K_OS_WINDOWS
888 DWORD dwWritten = 0;
889 if (WriteFile((HANDLE)file->native, buf, (DWORD)len, &dwWritten, NULL))
890 rc = dwWritten;
891 else
892 rc = shfile_dos2errno(GetLastError());
893# else
894 rc = write(file->native, buf, len);
895# endif
896
897 shfile_put(pfdtab, file, &tmp);
898 }
899 else
900 rc = -1;
901
902#else
903 rc = write(fd, buf, len);
904#endif
905 return rc;
906}
907
908/**
909 * lseek().
910 */
911long shfile_lseek(shfdtab *pfdtab, int fd, long off, int whench)
912{
913 long rc;
914#ifdef SHFILE_IN_USE
915 shmtxtmp tmp;
916 shfile *file = shfile_get(pfdtab, fd, &tmp);
917 if (file)
918 {
919# if K_OS == K_OS_WINDOWS
920 assert(SEEK_SET == FILE_BEGIN);
921 assert(SEEK_CUR == FILE_CURRENT);
922 assert(SEEK_END == FILE_END);
923 rc = SetFilePointer((HANDLE)file->native, off, NULL, whench);
924 if (rc == INVALID_SET_FILE_POINTER)
925 rc = shfile_dos2errno(GetLastError());
926# else
927 rc = lseek(file->native, off, whench);
928# endif
929
930 shfile_put(pfdtab, file, &tmp);
931 }
932 else
933 rc = -1;
934
935#else
936 rc = lseek(fd, off, whench);
937#endif
938
939 return rc;
940}
941
942int shfile_fcntl(shfdtab *pfdtab, int fd, int cmd, int arg)
943{
944 int rc;
945#ifdef SHFILE_IN_USE
946 shmtxtmp tmp;
947 shfile *file = shfile_get(pfdtab, fd, &tmp);
948 if (file)
949 {
950 switch (cmd)
951 {
952 case F_GETFL:
953 rc = file->flags;
954 break;
955
956 case F_SETFL:
957 {
958 unsigned mask = O_NONBLOCK | O_APPEND | O_BINARY | O_TEXT;
959# ifdef O_DIRECT
960 mask |= O_DIRECT;
961# endif
962# ifdef O_ASYNC
963 mask |= O_ASYNC;
964# endif
965# ifdef O_SYNC
966 mask |= O_SYNC;
967# endif
968 if ((file->flags & mask) == (arg & mask))
969 rc = 0;
970 else
971 {
972# if K_OS == K_OS_WINDOWS
973 assert(0);
974 errno = EINVAL;
975 rc = -1;
976# else
977 rc = fcntl(file->native, F_SETFL, arg);
978 if (rc != -1)
979 file->flags = (file->flags & ~mask) | (arg & mask);
980# endif
981 }
982 break;
983 }
984
985 case F_DUPFD:
986 {
987# if K_OS == K_OS_WINDOWS
988 HANDLE hNew = INVALID_HANDLE_VALUE;
989 if (DuplicateHandle(GetCurrentProcess(),
990 (HANDLE)file->native,
991 GetCurrentProcess(),
992 &hNew,
993 0,
994 FALSE /* bInheritHandle */,
995 DUPLICATE_SAME_ACCESS))
996 rc = shfile_insert(pfdtab, (intptr_t)hNew, file->flags, arg, "shfile_fcntl");
997 else
998 rc = shfile_dos2errno(GetLastError());
999# else
1000 int nativeNew = fcntl(file->native F_DUPFD, SHFILE_UNIX_MIN_FD);
1001 if (nativeNew != -1)
1002 rc = shfile_insert(pfdtab, nativeNew, file->flags, arg, "shfile_fcntl");
1003# endif
1004 break;
1005 }
1006
1007 default:
1008 errno = -EINVAL;
1009 rc = -1;
1010 break;
1011 }
1012
1013 shfile_put(pfdtab, file, &tmp);
1014 }
1015 else
1016 rc = -1;
1017
1018#else
1019 rc = fcntl(fd, cmd, arg);
1020#endif
1021
1022 switch (cmd)
1023 {
1024 case F_GETFL: TRACE2((NULL, "shfile_fcntl(%d,F_GETFL,ignored=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
1025 case F_SETFL: TRACE2((NULL, "shfile_fcntl(%d,F_SETFL,newflags=%#x) -> %d [%d]\n", fd, arg, rc, errno)); break;
1026 case F_DUPFD: TRACE2((NULL, "shfile_fcntl(%d,F_DUPFS,minfd=%d) -> %d [%d]\n", fd, arg, rc, errno)); break;
1027 default: TRACE2((NULL, "shfile_fcntl(%d,%d,%d) -> %d [%d]\n", fd, cmd, arg, rc, errno)); break;
1028 }
1029 return rc;
1030}
1031
1032int shfile_stat(shfdtab *pfdtab, const char *path, struct stat *pst)
1033{
1034#ifdef SHFILE_IN_USE
1035 char abspath[SHFILE_MAX_PATH];
1036 int rc;
1037 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1038 if (!rc)
1039 {
1040# if K_OS == K_OS_WINDOWS
1041 rc = stat(abspath, pst); /** @todo re-implement stat. */
1042# else
1043 rc = stat(abspath, pst);
1044# endif
1045 }
1046 TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d]\n", path, rc, errno));
1047 return rc;
1048#else
1049 return stat(path, pst);
1050#endif
1051}
1052
1053int shfile_lstat(shfdtab *pfdtab, const char *path, struct stat *pst)
1054{
1055 int rc;
1056#ifdef SHFILE_IN_USE
1057 char abspath[SHFILE_MAX_PATH];
1058
1059 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1060 if (!rc)
1061 {
1062# if K_OS == K_OS_WINDOWS
1063 rc = stat(abspath, pst); /** @todo implement lstat. */
1064# else
1065 rc = lstat(abspath, pst);
1066# endif
1067 }
1068#else
1069 rc = stat(path, pst);
1070#endif
1071 TRACE2((NULL, "shfile_stat(,%s,) -> %d [%d]\n", path, rc, errno));
1072 return rc;
1073}
1074
1075/**
1076 * chdir().
1077 */
1078int shfile_chdir(shfdtab *pfdtab, const char *path)
1079{
1080 shinstance *psh = shthread_get_shell();
1081 int rc;
1082#ifdef SHFILE_IN_USE
1083 char abspath[SHFILE_MAX_PATH];
1084
1085 rc = shfile_make_path(pfdtab, path, &abspath[0]);
1086 if (!rc)
1087 {
1088 char *abspath_copy = sh_strdup(psh, abspath);
1089 char *free_me = abspath_copy;
1090 rc = chdir(path);
1091 if (!rc)
1092 {
1093 shmtxtmp tmp;
1094 shmtx_enter(&pfdtab->mtx, &tmp);
1095
1096 free_me = pfdtab->cwd;
1097 pfdtab->cwd = abspath_copy;
1098
1099 shmtx_leave(&pfdtab->mtx, &tmp);
1100 }
1101 sh_free(psh, free_me);
1102 }
1103#else
1104 rc = chdir(path);
1105#endif
1106
1107 TRACE2((psh, "shfile_chdir(,%s) -> %d [%d]\n", path, rc, errno));
1108 return rc;
1109}
1110
1111/**
1112 * getcwd().
1113 */
1114char *shfile_getcwd(shfdtab *pfdtab, char *buf, int size)
1115{
1116 char *ret;
1117#ifdef SHFILE_IN_USE
1118
1119 ret = NULL;
1120 if (buf && !size)
1121 errno = -EINVAL;
1122 else
1123 {
1124 size_t cwd_size;
1125 shmtxtmp tmp;
1126 shmtx_enter(&pfdtab->mtx, &tmp);
1127
1128 cwd_size = strlen(pfdtab->cwd) + 1;
1129 if (buf)
1130 {
1131 if (cwd_size <= (size_t)size)
1132 ret = memcpy(buf, pfdtab->cwd, cwd_size);
1133 else
1134 errno = ERANGE;
1135 }
1136 else
1137 {
1138 if (size < cwd_size)
1139 size = (int)cwd_size;
1140 ret = sh_malloc(shthread_get_shell(), size);
1141 if (ret)
1142 ret = memcpy(ret, pfdtab->cwd, cwd_size);
1143 else
1144 errno = ENOMEM;
1145 }
1146
1147 shmtx_leave(&pfdtab->mtx, &tmp);
1148 }
1149#else
1150 ret = getcwd(buf, size);
1151#endif
1152
1153 TRACE2((NULL, "shfile_getcwd(,%p,%d) -> %s [%d]\n", buf, size, ret, errno));
1154 return ret;
1155}
1156
1157int shfile_access(shfdtab *pfdtab, const char *path, int type)
1158{
1159# ifdef _MSC_VER
1160 type &= ~X_OK;
1161 return access(path, type);
1162# else
1163 return access(path, type);
1164# endif
1165}
1166
1167/**
1168 * isatty()
1169 */
1170int shfile_isatty(shfdtab *pfdtab, int fd)
1171{
1172 int rc;
1173#ifdef SHFILE_IN_USE
1174 shmtxtmp tmp;
1175 shfile *file = shfile_get(pfdtab, fd, &tmp);
1176 if (file)
1177 {
1178# if K_OS == K_OS_WINDOWS
1179 errno = ENOSYS;
1180# else
1181 rc = isatty(file->native);
1182# endif
1183 shfile_put(pfdtab, file, &tmp);
1184 }
1185 else
1186 rc = 0;
1187#else
1188 rc = isatty(fd);
1189#endif
1190
1191 TRACE2((NULL, "isatty(%d) -> %d [%d]\n", fd, rc, errno));
1192 return rc;
1193}
1194
1195/**
1196 * fcntl F_SETFD / FD_CLOEXEC.
1197 */
1198int shfile_cloexec(shfdtab *pfdtab, int fd, int closeit)
1199{
1200 int rc;
1201#ifdef SHFILE_IN_USE
1202 shmtxtmp tmp;
1203 shfile *file = shfile_get(pfdtab, fd, &tmp);
1204 if (file)
1205 {
1206 file->cloexec = !!(closeit);
1207 shfile_put(pfdtab, file, &tmp);
1208 }
1209 else
1210 rc = -1;
1211#else
1212 rc = fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0)
1213 | (closeit ? FD_CLOEXEC : 0));
1214#endif
1215
1216 TRACE2((NULL, "shfile_cloexec(%d, %d) -> %d [%d]\n", fd, closeit, rc, errno));
1217 return rc;
1218}
1219
1220
1221int shfile_ioctl(shfdtab *pfdtab, int fd, unsigned long request, void *buf)
1222{
1223 int rc;
1224#ifdef SHFILE_IN_USE
1225 shmtxtmp tmp;
1226 shfile *file = shfile_get(pfdtab, fd, &tmp);
1227 if (file)
1228 {
1229# if K_OS == K_OS_WINDOWS
1230 rc = -1;
1231 errno = ENOSYS;
1232# else
1233 rc = ioctl(file->native, request, buf);
1234# endif
1235 shfile_put(pfdtab, file, &tmp);
1236 }
1237 else
1238 rc = -1;
1239#else
1240 rc = ioctl(fd, request, buf);
1241#endif
1242
1243 TRACE2((NULL, "ioctl(%d, %#x, %p) -> %d\n", fd, request, buf, rc));
1244 return rc;
1245}
1246
1247
1248mode_t shfile_get_umask(shfdtab *pfdtab)
1249{
1250 /** @todo */
1251 return 022;
1252}
1253
1254void shfile_set_umask(shfdtab *pfdtab, mode_t mask)
1255{
1256 /** @todo */
1257 (void)mask;
1258}
1259
1260
1261shdir *shfile_opendir(shfdtab *pfdtab, const char *dir)
1262{
1263#ifdef SHFILE_IN_USE
1264 errno = ENOSYS;
1265 return NULL;
1266#else
1267 return (shdir *)opendir(dir);
1268#endif
1269}
1270
1271shdirent *shfile_readdir(struct shdir *pdir)
1272{
1273#ifdef SHFILE_IN_USE
1274 errno = ENOSYS;
1275 return NULL;
1276#else
1277 struct dirent *pde = readdir((DIR *)pdir);
1278 return pde ? (shdirent *)&pde->d_name[0] : NULL;
1279#endif
1280}
1281
1282void shfile_closedir(struct shdir *pdir)
1283{
1284#ifdef SHFILE_IN_USE
1285 errno = ENOSYS;
1286#else
1287 closedir((DIR *)pdir);
1288#endif
1289}
1290
Note: See TracBrowser for help on using the repository browser.