// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more 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 "pal/thread.hpp" #include "pal/threadsusp.hpp" #include #include #include #include #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_FILE PAL_Stdin; PAL_FILE PAL_Stderr; /*++ 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: _getw Gets an integer from a stream. Return Value _getw returns the integer value read. A return value of EOF indicates either an error or end of file. However, because the EOF value is also a legitimate integer value, use feof or ferror to verify an end-of-file or error condition. Parameter file Pointer to FILE structure --*/ int __cdecl _getw(PAL_FILE *f) { INT ret = 0; PERF_ENTRY(_getw); ENTRY("_getw (f=%p)\n", f); _ASSERTE(f != NULL); CLEARERR(f); ret = getw( f->bsdFilePtr ); LOGEXIT( "returning %d\n", ret ); PERF_EXIT(_getw); return ret; } /*++ 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 wchar_16 *fileName, const wchar_16 *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 wchar_16 *fileName, const wchar_16 *mode, int shflag) { // UNIXTODO: Implement this. ERROR("Needs Implementation!!!"); return NULL; } /*++ Function: _putw Writes an integer to a stream. Return Value _putw returns the value written. A return value of EOF may indicate an error. Because EOF is also a legitimate integer value, use ferror to verify an error. Parameters c Binary integer to be output file Pointer to FILE structure --*/ int __cdecl _putw(int c, PAL_FILE *f) { INT ret = 0; PERF_ENTRY(_putw); ENTRY("_putw (c=0x%x, f=%p)\n", c, f); _ASSERTE(f != NULL); CLEARERR(f); ret = putw(c, f->bsdFilePtr ); LOGEXIT( "returning %d\n", ret ); PERF_EXIT(_putw); return ret; } /*++ 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); } wchar_16 * __cdecl PAL_fgetws(wchar_16 *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 : 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; }