blob: c5da5e3dcbd3471fe194be33517a4dc4f939ee91 [file]
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
/*++
Module Name:
file.c
Abstract:
Implementation of the file functions in the C runtime library that
are Windows specific.
--*/
#include "pal/palinternal.h"
#include "pal/dbgmsg.h"
#include "pal/file.h"
#include "pal/cruntime.h"
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <pthread.h>
#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
#define CLEARERR(f) clearerr((f)->bsdFilePtr)
#else
#define CLEARERR(f)
#endif
SET_DEFAULT_DEBUG_CHANNEL(CRT);
/* Global variables storing the std streams.*/
PAL_FILE PAL_Stdout PAL_GLOBAL;
PAL_FILE PAL_Stdin PAL_GLOBAL;
PAL_FILE PAL_Stderr PAL_GLOBAL;
/*++
Function:
CRTInitStdStreams.
Initilizes the standard streams.
Returns TRUE on success, FALSE otherwise.
--*/
BOOL CRTInitStdStreams()
{
/* stdout */
PAL_Stdout.bsdFilePtr = stdout;
PAL_Stdout.PALferrorCode = PAL_FILE_NOERROR;
PAL_Stdout.bTextMode = TRUE;
/* stdin */
PAL_Stdin.bsdFilePtr = stdin;
PAL_Stdin.PALferrorCode = PAL_FILE_NOERROR;
PAL_Stdin.bTextMode = TRUE;
/* stderr */
PAL_Stderr.bsdFilePtr = stderr;
PAL_Stderr.PALferrorCode = PAL_FILE_NOERROR;
PAL_Stderr.bTextMode = TRUE;
return TRUE;
}
/*++
Function :
MapFileOpenModes
Maps Windows file open modes to Unix fopen modes and validates.
--*/
static LPSTR MapFileOpenModes(LPSTR str , BOOL * bTextMode)
{
LPSTR retval = NULL;
LPSTR temp = NULL;
if (NULL == bTextMode)
{
ASSERT("MapFileOpenModes called with a NULL parameter for bTextMode.\n");
return NULL;
}
*bTextMode = TRUE;
if (NULL == str)
{
ASSERT("MapFileOpenModes called with a NULL parameter for str.\n");
return NULL;
}
/* The PAL behaves differently for some Windows file open modes:
c, n, S, R, and T: these are all hints to the system that aren't supported
by the PAL. Since the user cannot depend on this behavior, it's safe to
simply ignore these modes.
D: specifies a file as temporary. This file is expected to be deleted when
the last file descriptor is closed. The PAL does not support this behavior
and asserts when this mode is used.
t: represents opening in text mode. Calls to fdopen on Unix don't accept
't' so it is silently stripped out. However, the PAL supports the mode by
having the PAL wrappers do the translation of CR-LF to LF and vice versa.
t vs. b: To get binary mode, you must explicitly use 'b'. If neither mode
is specified on Windows, the default mode is defined by the global
variable _fmode. The PAL simply defaults to text mode. After examining
CLR usage patterns, the PAL behavior seems acceptable. */
/* Check if the mode specifies deleting the temporary file
automatically when the last file descriptor is closed.
The PAL does not support this behavior. */
if (NULL != strchr(str,'D'))
{
ASSERT("The PAL doesn't support the 'D' flag for _fdopen and fopen.\n");
return NULL;
}
/* Check if the mode specifies opening in binary.
If so, set the bTextMode to false. */
if(NULL != strchr(str,'b'))
{
*bTextMode = FALSE;
}
retval = (LPSTR)PAL_malloc( ( strlen( str ) + 1 ) * sizeof( CHAR ) );
if (NULL == retval)
{
ERROR("Unable to allocate memory.\n");
return NULL;
}
temp = retval;
while ( *str )
{
if ( *str == 'r' || *str == 'w' || *str == 'a' )
{
*temp = *str;
temp++;
if ( ( ++str != NULL ) && *str == '+' )
{
*temp = *str;
temp++;
str++;
}
}
else
{
str++;
}
}
*temp = '\0';
return retval;
}
#if UNGETC_NOT_RETURN_EOF
/*++
Function :
WriteOnlyMode
Returns TRUE to if a file is opened in write-only mode,
Otherwise FALSE.
--*/
static BOOL WriteOnlyMode(FILE* pFile)
{
INT fd, flags;
if (pFile != NULL)
{
fd = fileno(pFile);
if ((flags = fcntl(fd, F_GETFL)) >= 0)
{
if ((flags & O_ACCMODE) == O_WRONLY)
{
return TRUE;
}
}
}
return FALSE;
}
#endif //UNGETC_NOT_RETURN_EOF
/*++
Function:
_fdopen
see MSDN
--*/
PAL_FILE *
__cdecl
_fdopen(
int handle,
const char *mode)
{
PAL_FILE *f = NULL;
LPSTR supported = NULL;
BOOL bTextMode = TRUE;
PERF_ENTRY(_fdopen);
ENTRY("_fdopen (handle=%d mode=%p (%s))\n", handle, mode, mode);
_ASSERTE(mode != NULL);
f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
if ( f )
{
supported = MapFileOpenModes( (char*)mode , &bTextMode);
if ( !supported )
{
PAL_free(f);
f = NULL;
goto EXIT;
}
f->bsdFilePtr = (FILE *)fdopen( handle, supported );
f->PALferrorCode = PAL_FILE_NOERROR;
/* Make sure fdopen did not fail. */
if ( !f->bsdFilePtr )
{
PAL_free( f );
f = NULL;
}
PAL_free( supported );
supported = NULL;
}
else
{
ERROR( "Unable to allocate memory for the PAL_FILE wrapper!\n" );
}
EXIT:
LOGEXIT( "_fdopen returns FILE* %p\n", f );
PERF_EXIT(_fdopen);
return f;
}
/*++
Function :
fopen
see MSDN doc.
--*/
PAL_FILE *
__cdecl
PAL_fopen(const char * fileName, const char * mode)
{
PAL_FILE *f = NULL;
LPSTR supported = NULL;
LPSTR UnixFileName = NULL;
struct stat stat_data;
BOOL bTextMode = TRUE;
PERF_ENTRY(fopen);
ENTRY("fopen ( fileName=%p (%s) mode=%p (%s))\n", fileName, fileName, mode , mode );
_ASSERTE(fileName != NULL);
_ASSERTE(mode != NULL);
if ( *mode == 'r' || *mode == 'w' || *mode == 'a' )
{
supported = MapFileOpenModes( (char*)mode,&bTextMode);
if ( !supported )
{
goto done;
}
UnixFileName = PAL__strdup(fileName);
if (UnixFileName == NULL )
{
ERROR("PAL__strdup() failed\n");
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto done;
}
FILEDosToUnixPathA( UnixFileName );
/*I am not checking for the case where stat fails
*as fopen will handle the error more gracefully in case
*UnixFileName is invalid*/
if ((stat(UnixFileName, &stat_data) == 0 ) &&
((stat_data.st_mode & S_IFMT) == S_IFDIR))
{
goto done;
}
f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
if ( f )
{
f->bsdFilePtr = (FILE*)fopen( UnixFileName, supported );
f->PALferrorCode = PAL_FILE_NOERROR;
f->bTextMode = bTextMode;
if ( !f->bsdFilePtr )
{
/* Failed */
PAL_free( f );
f = NULL;
}
#if UNGETC_NOT_RETURN_EOF
else
{
f->bWriteOnlyMode = WriteOnlyMode(f->bsdFilePtr);
}
#endif //UNGETC_NOT_RETURN_EOF
}
else
{
ERROR( "Unable to allocate memory to the PAL_FILE wrapper\n" );
}
}
else
{
ERROR( "The mode flags must start with either an a, w, or r.\n" );
}
done:
PAL_free( supported );
supported = NULL;
PAL_free( UnixFileName );
LOGEXIT( "fopen returns FILE* %p\n", f );
PERF_EXIT(fopen);
return f;
}
/*++
Function:
_wfopen
see MSDN doc.
--*/
PAL_FILE *
__cdecl
_wfopen(
const char16_t *fileName,
const char16_t *mode)
{
CHAR mbFileName[ _MAX_PATH ];
CHAR mbMode[ 10 ];
PAL_FILE * filePtr = NULL;
PERF_ENTRY(_wfopen);
ENTRY("_wfopen(fileName:%p (%S), mode:%p (%S))\n", fileName, fileName, mode, mode);
_ASSERTE(fileName != NULL);
_ASSERTE(mode != NULL);
/* Convert the parameters to ASCII and defer to PAL_fopen */
if ( WideCharToMultiByte( CP_ACP, 0, fileName, -1, mbFileName,
sizeof mbFileName, NULL, NULL ) != 0 )
{
if ( WideCharToMultiByte( CP_ACP, 0, mode, -1, mbMode,
sizeof mbMode, NULL, NULL ) != 0 )
{
filePtr = PAL_fopen(mbFileName, mbMode);
}
else
{
ERROR( "An error occurred while converting mode to ANSI.\n" );
}
}
else
{
ERROR( "An error occurred while converting"
" fileName to ANSI string.\n" );
}
LOGEXIT("_wfopen returning FILE* %p\n", filePtr);
PERF_EXIT(_wfopen);
return filePtr;
}
/*++
Function:
_wfsopen
see MSDN doc.
--*/
PAL_FILE *
__cdecl
_wfsopen(
const char16_t *fileName,
const char16_t *mode,
int shflag)
{
// UNIXTODO: Implement this.
ERROR("Needs Implementation!!!");
return NULL;
}
/*++
Function
PAL_get_stdout.
Returns the stdout stream.
--*/
PAL_FILE * __cdecl PAL_get_stdout(int caller)
{
PERF_ENTRY(get_stdout);
ENTRY("PAL_get_stdout\n");
LOGEXIT("PAL_get_stdout returns PAL_FILE * %p\n", &PAL_Stdout );
PERF_EXIT(get_stdout);
return &PAL_Stdout;
}
/*++
Function
PAL_get_stdin.
Returns the stdin stream.
--*/
PAL_FILE * __cdecl PAL_get_stdin(int caller)
{
PERF_ENTRY(get_stdin);
ENTRY("PAL_get_stdin\n");
LOGEXIT("PAL_get_stdin returns PAL_FILE * %p\n", &PAL_Stdin );
PERF_EXIT(get_stdin);
return &PAL_Stdin;
}
/*++
Function
PAL_get_stderr.
Returns the stderr stream.
--*/
PAL_FILE * __cdecl PAL_get_stderr(int caller)
{
PERF_ENTRY(get_stderr);
ENTRY("PAL_get_stderr\n");
LOGEXIT("PAL_get_stderr returns PAL_FILE * %p\n", &PAL_Stderr );
PERF_EXIT(get_stderr);
return &PAL_Stderr;
}
/*++
Function:
_close
See msdn for more details.
--*/
int __cdecl PAL__close(int handle)
{
INT nRetVal = 0;
PERF_ENTRY(_close);
ENTRY( "_close( handle=%d )\n", handle );
nRetVal = close( handle );
LOGEXIT( "_close returning %d.\n", nRetVal );
PERF_EXIT(_close);
return nRetVal;
}
int __cdecl PAL__flushall()
{
return fflush(NULL);
}
char16_t *
__cdecl
PAL_fgetws(char16_t *s, int n, PAL_FILE *f)
{
ASSERT (0);
return NULL;
}
/*++
Function :
fread
See MSDN for more details.
--*/
size_t
__cdecl
PAL_fread(void * buffer, size_t size, size_t count, PAL_FILE * f)
{
size_t nReadBytes = 0;
PERF_ENTRY(fread);
ENTRY( "fread( buffer=%p, size=%d, count=%d, f=%p )\n",
buffer, size, count, f );
_ASSERTE(f != NULL);
CLEARERR(f);
if(f->bTextMode != TRUE)
{
nReadBytes = fread( buffer, size, count, f->bsdFilePtr );
}
else
{
size_t i=0;
if(size > 0)
{
size_t j=0;
LPSTR temp = (LPSTR)buffer;
int nChar = 0;
int nCount =0;
for(i=0; i< count; i++)
{
for(j=0; j< size; j++)
{
if((nChar = PAL_getc(f)) == EOF)
{
nReadBytes = i;
goto done;
}
else
{
temp[nCount++]=nChar;
}
}
}
}
nReadBytes = i;
}
done:
LOGEXIT( "fread returning size_t %d\n", nReadBytes );
PERF_EXIT(fread);
return nReadBytes;
}
/*++
Function :
ferror
See MSDN for more details.
--*/
int
_cdecl
PAL_ferror(PAL_FILE * f)
{
INT nErrorCode = PAL_FILE_NOERROR;
PERF_ENTRY(ferror);
ENTRY( "ferror( f=%p )\n", f );
_ASSERTE(f != NULL);
nErrorCode = ferror( f->bsdFilePtr );
if ( 0 == nErrorCode )
{
/* See if the PAL file error code is set. */
nErrorCode = f->PALferrorCode;
}
LOGEXIT( "ferror returns %d\n", nErrorCode );
PERF_EXIT(ferror);
return nErrorCode;
}
/*++
Function :
fclose
See MSDN for more details.
--*/
int
_cdecl
PAL_fclose(PAL_FILE * f)
{
INT nRetVal = 0;
PERF_ENTRY(fclose);
ENTRY( "fclose( f=%p )\n", f );
_ASSERTE(f != NULL);
CLEARERR(f);
nRetVal = fclose( f->bsdFilePtr );
PAL_free( f );
LOGEXIT( "fclose returning %d\n", nRetVal );
PERF_EXIT(fclose);
return nRetVal;
}
/*++
Function :
setbuf
See MSDN for more details.
--*/
void
_cdecl
PAL_setbuf(PAL_FILE * f, char * buffer)
{
PERF_ENTRY(setbuf);
ENTRY( "setbuf( %p, %p )\n", f, buffer );
_ASSERTE(f != NULL);
setbuf( f->bsdFilePtr, buffer );
LOGEXIT( "setbuf\n" );
PERF_EXIT(setbuf);
}
/*++
Function :
fputs
See MSDN for more details.
--*/
int
_cdecl
PAL_fputs(const char * str, PAL_FILE * f)
{
INT nRetVal = 0;
PERF_ENTRY(fputs);
ENTRY( "fputs( %p (%s), %p )\n", str, str, f);
_ASSERTE(str != NULL);
_ASSERTE(f != NULL);
CLEARERR(f);
nRetVal = fputs( str, f->bsdFilePtr );
LOGEXIT( "fputs returning %d\n", nRetVal );
PERF_EXIT(fputs);
return nRetVal;
}
/*--
Function :
fputc
See MSDN for more details.
--*/
int
_cdecl
PAL_fputc(int c, PAL_FILE * f)
{
INT nRetVal = 0;
PERF_ENTRY(fputc);
ENTRY( "fputc( 0x%x (%c), %p )\n", c, c, f);
_ASSERTE(f != NULL);
CLEARERR(f);
nRetVal = fputc( c, f->bsdFilePtr );
LOGEXIT( "fputc returning %d\n", nRetVal );
PERF_EXIT(fputc);
return nRetVal;
}
/*--
Function :
putchar
See MSDN for more details.
--*/
int
_cdecl
PAL_putchar( int c )
{
INT nRetVal = 0;
PERF_ENTRY(putchar);
ENTRY( "putchar( 0x%x (%c) )\n", c, c);
nRetVal = putchar( c );
LOGEXIT( "putchar returning %d\n", nRetVal );
PERF_EXIT(putchar);
return nRetVal;
}
/*++
Function :
ftell
See MSDN for more details.
--*/
LONG
_cdecl
PAL_ftell(PAL_FILE * f)
{
long lRetVal = 0;
PERF_ENTRY(ftell);
ENTRY( "ftell( %p )\n", f );
_ASSERTE(f != NULL);
lRetVal = ftell( f->bsdFilePtr );
#ifdef BIT64
/* Windows does not set an error if the file pointer's position
is greater than _I32_MAX. It just returns -1. */
if (lRetVal > _I32_MAX)
{
lRetVal = -1;
}
#endif
LOGEXIT( "ftell returning %ld\n", lRetVal );
PERF_EXIT(ftell);
/* This explicit cast to LONG is used to silence any potential warnings
due to implicitly casting the native long lRetVal to LONG when returning. */
return (LONG)lRetVal;
}
/*++
Function:
fgetpos
See msdn for more details.
--*/
int
__cdecl
PAL_fgetpos (
PAL_FILE *f,
PAL_fpos_t *pos
)
{
#ifdef __LINUX__
// TODO: implement for Linux if required
ASSERT(FALSE);
return -1;
#else
int nRetVal = -1;
fpos_t native_pos;
PERF_ENTRY(fgetpos);
ENTRY("fgetpos( f=%p, pos=%p )\n", f, pos);
_ASSERTE(f != NULL);
_ASSERTE(pos != NULL);
if (pos) {
native_pos = *pos;
nRetVal = fgetpos (f->bsdFilePtr, &native_pos);
*pos = native_pos;
}
else {
ERROR ("Error: NULL pos pointer\n");
errno = EINVAL;
}
LOGEXIT( "fgetpos returning error code %d, pos %d\n", nRetVal, pos ? *pos : 0);
PERF_EXIT(fgetpos);
return nRetVal;
#endif // __LINUX__
}
/*++
Function:
fsetpos
See msdn for more details.
--*/
int
__cdecl
PAL_fsetpos (
PAL_FILE *f,
const PAL_fpos_t *pos
)
{
#ifdef __LINUX__
// TODO: implement for Linux if required
ASSERT(FALSE);
return -1;
#else
int nRetVal = -1;
fpos_t native_pos;
PERF_ENTRY(fsetpos);
ENTRY("fsetpos( f=%p, pos=%p )\n", f, pos);
_ASSERTE(f != NULL);
_ASSERTE(pos != NULL);
if (pos) {
native_pos = *pos;
nRetVal = fsetpos (f->bsdFilePtr, &native_pos);
}
else {
ERROR ("Error: NULL pos pointer\n");
errno = EINVAL;
}
LOGEXIT( "fsetpos returning error code %d\n", nRetVal);
PERF_EXIT(fsetpos);
return nRetVal;
#endif // __LINUX__
}
/*++
Function :
feof
See MSDN for more details.
--*/
int
_cdecl
PAL_feof(PAL_FILE * f)
{
INT nRetVal = 0;
PERF_ENTRY(feof);
ENTRY( "feof( %p )\n", f );
_ASSERTE(f != NULL);
nRetVal = feof( f->bsdFilePtr );
LOGEXIT( "feof returning %d\n", nRetVal );
PERF_EXIT(feof);
return nRetVal;
}
/*++
Function :
getc
See MSDN for more details.
--*/
int
_cdecl
PAL_getc(PAL_FILE * f)
{
INT nRetVal = 0;
INT temp =0;
PERF_ENTRY(getc);
ENTRY( "getc( %p )\n", f );
_ASSERTE(f != NULL);
CLEARERR(f);
nRetVal = getc( f->bsdFilePtr );
if ( (f->bTextMode) && (nRetVal == '\r') )
{
if ((temp = getc( f->bsdFilePtr ))== '\n')
{
nRetVal ='\n';
}
else if (EOF == ungetc( temp, f->bsdFilePtr ))
{
ERROR("ungetc operation failed\n");
}
}
LOGEXIT( "getc returning %d\n", nRetVal );
PERF_EXIT(getc);
return nRetVal;
}
/*++
Function :
ungetc
See MSDN for more details.
--*/
int
_cdecl
PAL_ungetc(int c, PAL_FILE * f)
{
INT nRetVal = 0;
PERF_ENTRY(ungetc);
ENTRY( "ungetc( %c, %p )\n", c, f );
_ASSERTE(f != NULL);
#if UNGETC_NOT_RETURN_EOF
/* On some Unix platform such as Solaris, ungetc does not return EOF
on write-only file. */
if (f->bWriteOnlyMode)
{
nRetVal = EOF;
}
else
#endif //UNGETC_NOT_RETURN_EOF
{
CLEARERR(f);
nRetVal = ungetc( c, f->bsdFilePtr );
}
LOGEXIT( "ungetc returning %d\n", nRetVal );
PERF_EXIT(ungetc);
return nRetVal;
}
/*++
Function :
setvbuf
See MSDN for more details.
--*/
int
_cdecl
PAL_setvbuf(PAL_FILE *f, char *buf, int type, size_t size)
{
INT nRetVal = 0;
PERF_ENTRY(setvbuf);
ENTRY( "setvbuf( %p, %p, %d, %ul )\n", f, buf, type, size);
_ASSERTE(f != NULL);
nRetVal = setvbuf(f->bsdFilePtr, buf, type, size);
LOGEXIT( "setvbuf returning %d\n", nRetVal );
PERF_EXIT(setvbuf);
return nRetVal;
}