3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/msvcrt/io/open.c
6 * PURPOSE: Opens a file and translates handles to fileno
7 * PROGRAMER: Boudewijn Dekker
12 * Some stuff taken from active perl: perl\win32.c (ioinfo stuff)
14 * (c) 1995 Microsoft Corporation. All rights reserved.
15 * Developed by hip communications inc., http://info.hip.com/info/
16 * Portions (c) 1993 Intergraph Corporation. All rights reserved.
18 * You may distribute under the terms of either the GNU General Public
19 * License or the Artistic License, as specified in the README file.
22 * Some functions taken from/based on wine\dlls\msvcrt\file.c:
27 * Copyright 1996,1998 Marcus Meissner
28 * Copyright 1996 Jukka Iivonen
29 * Copyright 1997,2000 Uwe Bonnes
30 * Copyright 2000 Jon Griffiths
31 * Copyright 2004 Eric Pouech
32 * Copyright 2004 Juan Lang
35 // rember to interlock the allocation of fileno when making this thread safe
37 // possibly store extra information at the handle
41 #if !defined(NDEBUG) && defined(DBG)
51 #include <internal/debug.h>
55 FDINFO first_bucket
[FDINFO_ENTRIES_PER_BUCKET
];
56 FDINFO
* __pioinfo
[FDINFO_BUCKETS
] = {first_bucket
};
59 /* This critical section protects the tables MSVCRT_fdesc and MSVCRT_fstreams,
60 * and their related indexes, MSVCRT_fdstart, MSVCRT_fdend,
61 * and MSVCRT_stream_idx, from race conditions.
62 * It doesn't protect against race conditions manipulating the underlying files
63 * or flags; doing so would probably be better accomplished with per-file
64 * protection, rather than locking the whole table for every change.
66 static CRITICAL_SECTION g_file_cs
;
67 #define LOCK_FILES() do { EnterCriticalSection(&g_file_cs); } while (0)
68 #define UNLOCK_FILES() do { LeaveCriticalSection(&g_file_cs); } while (0)
70 /////////////////////////////////////////
72 static int g_fdstart
= 3; /* first unallocated fd */
73 static int g_fdend
= 3; /* highest allocated fd */
79 static __inline FD_INFO* fdinfo(int fd)
81 FD_INFO* bucket = __pioinfo[fd >> FDINFO_ENTRIES_PER_BUCKET_SHIFT];
83 bucket = alloc_init_bucket(fd);
85 return bucket + (fd & (FDINFO_ENTRIES_PER_BUCKET - 1));
93 __inline BOOL
is_valid_fd(int fd
)
95 BOOL b
= (fd
>= 0 && fd
< g_fdend
&& (fdinfo(fd
)->fdflags
& FOPEN
));
98 if (fd
>= 0 && fd
< g_fdend
)
100 DPRINT1("not valid fd %i, g_fdend %i, fdinfo %x, bucket %x, fdflags %x\n",
101 fd
,g_fdend
,fdinfo(fd
),fdinfo_bucket(fd
),fdinfo(fd
)->fdflags
);
105 DPRINT1("not valid fd %i, g_fdend %i\n",fd
,g_fdend
);
116 char split_oflags(int oflags
)
120 if (oflags
& _O_APPEND
) fdflags
|= FAPPEND
;
122 if (oflags
& _O_BINARY
) ;
123 else if (oflags
& _O_TEXT
) fdflags
|= FTEXT
;
124 else if (_fmode
& _O_BINARY
) ;
125 else fdflags
|= FTEXT
; /* default to TEXT*/
127 if (oflags
& _O_NOINHERIT
) fdflags
|= FNOINHERIT
;
129 if (oflags
& ~(_O_BINARY
|_O_TEXT
|_O_APPEND
|_O_TRUNC
|
130 _O_EXCL
|_O_CREAT
|_O_RDWR
|_O_WRONLY
|
131 _O_TEMPORARY
|_O_NOINHERIT
))
132 DPRINT1(":unsupported oflags 0x%04x\n",oflags
);
142 char __is_text_file(FILE* p
)
144 if ( p
== NULL
|| fdinfo_bucket((p
)->_file
) == NULL
)
146 return (!((p
)->_flag
&_IOSTRG
) && (fdinfo((p
)->_file
)->fdflags
& FTEXT
));
152 int _open(const char* _path
, int _oflag
,...)
154 #if !defined(NDEBUG) && defined(DBG)
159 DWORD dwDesiredAccess
= 0;
160 DWORD dwShareMode
= 0;
161 DWORD dwCreationDistribution
= 0;
162 DWORD dwFlagsAndAttributes
= 0;
163 SECURITY_ATTRIBUTES sa
= {sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
165 #if !defined(NDEBUG) && defined(DBG)
166 va_start(arg
, _oflag
);
167 pmode
= va_arg(arg
, int);
171 TRACE("_open('%s', %x, (%x))\n", _path
, _oflag
);
174 if ((_oflag
& S_IREAD
) == S_IREAD
)
175 dwShareMode
= FILE_SHARE_READ
;
176 else if ((_oflag
& S_IWRITE
) == S_IWRITE
) {
177 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
181 * _O_BINARY Opens file in binary (untranslated) mode. (See fopen for a description of binary mode.)
182 * _O_TEXT Opens file in text (translated) mode. (For more information, see Text and Binary Mode File I/O and fopen.)
184 * _O_APPEND Moves file pointer to end of file before every write operation.
187 if ((_oflag
& _O_RDWR
) == _O_RDWR
)
188 dwDesiredAccess
|= GENERIC_WRITE
|GENERIC_READ
;
189 else if ((_oflag
& O_RDONLY
) == O_RDONLY
)
190 dwDesiredAccess
|= GENERIC_READ
;
191 else if ((_oflag
& _O_WRONLY
) == _O_WRONLY
)
192 dwDesiredAccess
|= GENERIC_WRITE
;
194 if ((_oflag
& _O_WRONLY
) == _O_WRONLY
)
195 dwDesiredAccess
|= GENERIC_WRITE
;
196 else if ((_oflag
& _O_RDWR
) == _O_RDWR
)
197 dwDesiredAccess
|= GENERIC_WRITE
|GENERIC_READ
;
198 else //if ((_oflag & O_RDONLY) == O_RDONLY)
199 dwDesiredAccess
|= GENERIC_READ
;
202 if (( _oflag
& (_O_CREAT
| _O_EXCL
)) == (_O_CREAT
| _O_EXCL
))
203 dwCreationDistribution
|= CREATE_NEW
;
205 else if ((_oflag
& O_TRUNC
) == O_TRUNC
) {
206 if ((_oflag
& O_CREAT
) == O_CREAT
)
207 dwCreationDistribution
|= CREATE_ALWAYS
;
208 else if ((_oflag
& O_RDONLY
) != O_RDONLY
)
209 dwCreationDistribution
|= TRUNCATE_EXISTING
;
211 else if ((_oflag
& _O_APPEND
) == _O_APPEND
)
212 dwCreationDistribution
|= OPEN_EXISTING
;
213 else if ((_oflag
& _O_CREAT
) == _O_CREAT
)
214 dwCreationDistribution
|= OPEN_ALWAYS
;
216 dwCreationDistribution
|= OPEN_EXISTING
;
218 if ((_oflag
& _O_RANDOM
) == _O_RANDOM
)
219 dwFlagsAndAttributes
|= FILE_FLAG_RANDOM_ACCESS
;
220 if ((_oflag
& _O_SEQUENTIAL
) == _O_SEQUENTIAL
)
221 dwFlagsAndAttributes
|= FILE_FLAG_SEQUENTIAL_SCAN
;
222 if ((_oflag
& _O_TEMPORARY
) == _O_TEMPORARY
) {
223 dwFlagsAndAttributes
|= FILE_FLAG_DELETE_ON_CLOSE
;
224 DPRINT("FILE_FLAG_DELETE_ON_CLOSE\n");
226 if ((_oflag
& _O_SHORT_LIVED
) == _O_SHORT_LIVED
) {
227 dwFlagsAndAttributes
|= FILE_FLAG_DELETE_ON_CLOSE
;
228 DPRINT("FILE_FLAG_DELETE_ON_CLOSE\n");
230 if (_oflag
& _O_NOINHERIT
)
231 sa
.bInheritHandle
= FALSE
;
233 if (dwCreationDistribution
== OPEN_EXISTING
&&
234 (dwDesiredAccess
& (GENERIC_WRITE
|GENERIC_READ
)) == GENERIC_READ
) {
235 /* Allow always shared read for a file which is opened for read only */
236 dwShareMode
|= FILE_SHARE_READ
;
239 hFile
= CreateFileA(_path
,
243 dwCreationDistribution
,
244 dwFlagsAndAttributes
,
246 if (hFile
== (HANDLE
)-1) {
247 _dosmaperr(GetLastError());
251 if (!(_oflag
& (_O_TEXT
|_O_BINARY
))) {
254 return(alloc_fd(hFile
, split_oflags(_oflag
)));
262 static void init_bucket(FDINFO
* entry
)
267 i
< FDINFO_ENTRIES_PER_BUCKET
;
270 entry
->hFile
= INVALID_HANDLE_VALUE
;
272 entry
->pipechar
= LF
;
273 entry
->lockinitflag
= 0;
280 static BOOL
alloc_init_bucket(int fd
)
282 fdinfo_bucket(fd
) = malloc(FDINFO_ENTRIES_PER_BUCKET
* sizeof(FDINFO
));
283 if (!fdinfo_bucket(fd
)) return FALSE
;
285 init_bucket(fdinfo_bucket(fd
));
295 * Allocate an fd slot from a Win32 HANDLE, starting from fd
296 * caller must hold the files lock
298 static int alloc_fd_from(HANDLE hand
, char flag
, int fd
)
301 if (fd
>= FDINFO_ENTRIES
)
303 DPRINT1("files exhausted!\n");
307 if (!fdinfo_bucket(fd
))
309 if (!alloc_init_bucket(fd
)){
315 fdinfo(fd
)->hFile
= hand
;
316 fdinfo(fd
)->fdflags
= FOPEN
| (flag
& (FNOINHERIT
| FAPPEND
| FTEXT
));
317 fdinfo(fd
)->pipechar
= LF
;
318 fdinfo(fd
)->lockinitflag
= 0;
321 /* locate next free slot */
322 if (fd
== g_fdstart
&& fd
== g_fdend
)
324 g_fdstart
= g_fdend
+ 1;
328 #if 0 /* alternate (untested) impl. maybe a tiny bit faster? -Gunnar */
331 for (bidx
= fdinfo_bucket_idx(g_fdstart
); bidx
< FDINFO_BUCKETS
&& __pioinfo
[bidx
]; bidx
++)
333 for (i
= fdinfo_bucket_entry_idx(g_fdstart
);
334 g_fdstart
< g_fdend
&& fdinfo(g_fdstart
)->fdflags
& FOPEN
&& i
< FDINFO_BUCKET_ENTRIES
;
342 while (g_fdstart
< g_fdend
&&
343 fdinfo_bucket(g_fdstart
) &&
344 (fdinfo(g_fdstart
)->fdflags
& FOPEN
))
351 /* update last fd in use */
355 /* alloc more fdinfo buckets by demand.
356 * FIXME: should we dealloc buckets when they become unused also? */
357 if (!fdinfo_bucket(g_fdstart
) && g_fdstart
< FDINFO_ENTRIES
)
359 alloc_init_bucket(g_fdstart
);
362 DPRINT("fdstart is %d, fdend is %d\n", g_fdstart
, g_fdend
);
366 case 0: SetStdHandle(STD_INPUT_HANDLE
, hand
); break;
367 case 1: SetStdHandle(STD_OUTPUT_HANDLE
, hand
); break;
368 case 2: SetStdHandle(STD_ERROR_HANDLE
, hand
); break;
376 * INTERNAL: Allocate an fd slot from a Win32 HANDLE
378 int alloc_fd(HANDLE hand
, char flag
)
384 // TRACE(":handle (%p) allocating fd (%d)\n",hand,MSVCRT_fdstart);
385 ret
= alloc_fd_from(hand
, flag
, g_fdstart
);
396 char __fileno_getmode(int fd
)
398 if (!is_valid_fd(fd
)) {
402 return fdinfo(fd
)->fdflags
;
414 fdinfo(fd
)->hFile
= INVALID_HANDLE_VALUE
;
415 fdinfo(fd
)->fdflags
= 0;
417 if (fd
< 3) /* don't use 0,1,2 for user files */
421 case 0: SetStdHandle(STD_INPUT_HANDLE
, NULL
); break;
422 case 1: SetStdHandle(STD_OUTPUT_HANDLE
, NULL
); break;
423 case 2: SetStdHandle(STD_ERROR_HANDLE
, NULL
); break;
428 if (fd
== g_fdend
- 1)
442 int _open_osfhandle(long osfhandle
, int oflags
)
446 The _open_osfhandle() function in MSVCRT is expected to take the absence
447 of either _O_TEXT or _O_BINARY to mean _O_BINARY. Currently it defaults to
450 An example of this is MFC's CStdioFile::Open in binary mode - it passes flags
451 of 0 when it wants to write a binary file - under WINE we do text mode conversions!
453 The attached patch ensures that _O_BINARY is set if neither is set in the passed-in
457 * file, so set the write flag. It also only sets _O_TEXT if it wants
458 * text - it never sets _O_BINARY.
460 /* FIXME: handle more flags */
462 flags |= MSVCRT__IOREAD|MSVCRT__IOWRT;
463 if ( !( flags & _O_TEXT ) ) flags |= _O_BINARY;
465 fd = msvcrt_alloc_fd((HANDLE)hand,flags);
466 TRACE(":handle (%ld) fd (%d) flags 0x%08x\n",hand,fd, flags);
468 /* MSVCRT__O_RDONLY (0) always matches, so set the read flag
469 * MFC's CStdioFile clears O_RDONLY (0)! if it wants to write to the
470 * file, so set the write flag. It also only sets MSVCRT__O_TEXT if it wants
471 * text - it never sets MSVCRT__O_BINARY.
473 /* FIXME: handle more flags */
475 LAG TEST SOM TESTER UT ALT DETTE flag tingern
477 if (!(oflags
& (_O_BINARY
| _O_TEXT
)) && (_fmode
& _O_BINARY
))
482 return alloc_fd((HANDLE
)osfhandle
, split_oflags(oflags
));
488 long _get_osfhandle(int fd
)
490 TRACE("_get_osfhandle(%i)",fd
);
492 if (!is_valid_fd(fd
)) {
495 return( (long)fdinfo(fd
)->hFile
);
503 int __fileno_dup2(int handle1
, int handle2
)
508 if (handle1
>= FDINFO_ENTRIES
|| handle1
< 0 || handle2
>= FDINFO_ENTRIES
|| handle2
< 0) {
512 // if (_pioinfo[handle1]->fd == -1) {
513 if (fdinfo(handle1
)->hFile
== INVALID_HANDLE_VALUE
) {
517 if (handle1
== handle2
)
519 // if (_pioinfo[handle2]->fd != -1) {
520 if (fdinfo(handle2
)->hFile
!= INVALID_HANDLE_VALUE
) {
523 hProcess
= GetCurrentProcess();
524 result
= DuplicateHandle(hProcess
,
525 fdinfo(handle1
)->hFile
,
527 &fdinfo(handle2
)->hFile
,
529 fdinfo(handle1
)->fdflags
& FNOINHERIT
? FALSE
: TRUE
,
530 DUPLICATE_SAME_ACCESS
);
532 // _pioinfo[handle2]->fd = handle2;
533 fdinfo(handle2
)->fdflags
= fdinfo(handle1
)->fdflags
;
536 SetStdHandle(STD_INPUT_HANDLE
, fdinfo(handle2
)->hFile
);
539 SetStdHandle(STD_OUTPUT_HANDLE
, fdinfo(handle2
)->hFile
);
542 SetStdHandle(STD_ERROR_HANDLE
, fdinfo(handle2
)->hFile
);
548 __set_errno(EMFILE
); // Is this the correct error no.?
554 void* malloc(size_t sizeObject
);
561 BOOL
__fileno_init(void)
566 init_bucket(first_bucket
);
568 GetStartupInfoA(&si
);
570 if (si
.cbReserved2
!= 0 && si
.lpReserved2
!= NULL
)
575 g_fdend
= *(unsigned*)si
.lpReserved2
;
577 fdflags_ptr
= (char*)(si
.lpReserved2
+ sizeof(unsigned));
578 handle_ptr
= (HANDLE
*)(fdflags_ptr
+ g_fdend
* sizeof(char));
580 g_fdend
= min(g_fdend
, FDINFO_ENTRIES
);
581 for (i
= 0; i
< g_fdend
; i
++)
583 if (!fdinfo_bucket(i
))
585 if (!alloc_init_bucket(i
)){
586 /* FIXME: free other buckets? */
591 if ((*fdflags_ptr
& FOPEN
) && *handle_ptr
!= INVALID_HANDLE_VALUE
)
593 fdinfo(i
)->fdflags
= *fdflags_ptr
;
594 fdinfo(i
)->hFile
= *handle_ptr
;
599 fdinfo(i)->fdflags = 0;
600 fdinfo(i)->hFile = INVALID_HANDLE_VALUE;
603 fdflags_ptr
++; handle_ptr
++;
605 for (g_fdstart
= 3; g_fdstart
< g_fdend
; g_fdstart
++)
606 if (fdinfo(g_fdstart
)->hFile
== INVALID_HANDLE_VALUE
) break;
609 InitializeCriticalSection(&g_file_cs
);
612 if (fdinfo(0)->hFile
== INVALID_HANDLE_VALUE
|| !(fdinfo(0)->fdflags
& FOPEN
)) {
613 fdinfo(0)->hFile
= GetStdHandle(STD_INPUT_HANDLE
);
614 if (fdinfo(0)->hFile
== NULL
)
615 fdinfo(0)->hFile
= INVALID_HANDLE_VALUE
;
616 fdinfo(0)->fdflags
= FOPEN
|FTEXT
;
618 if (fdinfo(1)->hFile
== INVALID_HANDLE_VALUE
|| !(fdinfo(1)->fdflags
& FOPEN
)) {
619 fdinfo(1)->hFile
= GetStdHandle(STD_OUTPUT_HANDLE
);
620 if (fdinfo(1)->hFile
== NULL
)
621 fdinfo(1)->hFile
= INVALID_HANDLE_VALUE
;
622 fdinfo(1)->fdflags
= FOPEN
|FTEXT
;
624 if (fdinfo(2)->hFile
== INVALID_HANDLE_VALUE
|| !(fdinfo(2)->fdflags
& FOPEN
)) {
625 fdinfo(2)->hFile
= GetStdHandle(STD_ERROR_HANDLE
);
626 if (fdinfo(2)->hFile
== NULL
)
627 fdinfo(2)->hFile
= INVALID_HANDLE_VALUE
;
628 fdinfo(2)->fdflags
= FOPEN
|FTEXT
;
634 for (i
= 0; i
< 3; i
++)
636 /* FILE structs for stdin/out/err are static and never deleted */
637 // MSVCRT_fstreams[i] = &MSVCRT__iob[i];
639 // MSVCRT_stream_idx = 3;
646 /* INTERNAL: Create an inheritance data block (for spawned process)
647 * The inheritance block is made of:
648 * 00 int nb of file descriptor (NBFD)
649 * 04 char file flags (wxflag): repeated for each fd
650 * 4+NBFD HANDLE file handle: repeated for each fd
652 unsigned create_io_inherit_block(STARTUPINFOA
* si
)
658 TRACE("create_io_inherit_block(%x)",si
);
660 si
->cbReserved2
= sizeof(unsigned) + (sizeof(char) + sizeof(HANDLE
)) * g_fdend
;
661 si
->lpReserved2
= calloc(si
->cbReserved2
, 1);
662 if (!si
->lpReserved2
)
667 fdflags_ptr
= (char*)si
->lpReserved2
+ sizeof(unsigned);
668 handle_ptr
= (HANDLE
*)(fdflags_ptr
+ g_fdend
* sizeof(char));
670 *(unsigned*)si
->lpReserved2
= g_fdend
;
671 for (fd
= 0; fd
< g_fdend
; fd
++)
673 /* to be inherited, we need it to be open, and that DONTINHERIT isn't set */
674 if ((fdinfo(fd
)->fdflags
& (FOPEN
| FNOINHERIT
)) == FOPEN
)
676 *fdflags_ptr
= fdinfo(fd
)->fdflags
;
677 *handle_ptr
= fdinfo(fd
)->hFile
;
682 *handle_ptr
= INVALID_HANDLE_VALUE
;
684 fdflags_ptr
++; handle_ptr
++;
695 int _setmode(int fd
, int newmode
)
699 TRACE("_setmode(%d, %d)", fd
, newmode
);
701 if (!is_valid_fd(fd
))
703 DPRINT1("_setmode: inval fd (%d)\n",fd
);
708 if (newmode
& ~(_O_TEXT
|_O_BINARY
))
710 DPRINT1("_setmode: fd (%d) mode (0x%08x) unknown\n",fd
,newmode
);
711 /* FIXME: Should we fail with EINVAL here? */
714 prevmode
= fdinfo(fd
)->fdflags
& FTEXT
? _O_TEXT
: _O_BINARY
;
716 if ((newmode
& _O_TEXT
) == _O_TEXT
)
718 fdinfo(fd
)->fdflags
|= FTEXT
;
722 /* FIXME: If both _O_TEXT and _O_BINARY are set, we get here.
723 * Should we fail with EINVAL instead? -Gunnar
725 fdinfo(fd
)->fdflags
&= ~FTEXT
;