1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 file.c
12
13Abstract:
14
15 Implementation of the file functions in the C runtime library that
16 are Windows specific.
17
18
19
20--*/
21
22#include "pal/palinternal.h"
23#include "pal/dbgmsg.h"
24#include "pal/file.h"
25#include "pal/cruntime.h"
26
27#include "pal/thread.hpp"
28#include "pal/threadsusp.hpp"
29
30#include <unistd.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <pthread.h>
34
35#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
36 #define CLEARERR(f) clearerr((f)->bsdFilePtr)
37#else
38 #define CLEARERR(f)
39#endif
40
41SET_DEFAULT_DEBUG_CHANNEL(CRT);
42
43/* Global variables storing the std streams.*/
44PAL_FILE PAL_Stdout;
45PAL_FILE PAL_Stdin;
46PAL_FILE PAL_Stderr;
47
48/*++
49
50Function:
51
52 CRTInitStdStreams.
53
54 Initilizes the standard streams.
55 Returns TRUE on success, FALSE otherwise.
56--*/
57BOOL CRTInitStdStreams()
58{
59 /* stdout */
60 PAL_Stdout.bsdFilePtr = stdout;
61 PAL_Stdout.PALferrorCode = PAL_FILE_NOERROR;
62 PAL_Stdout.bTextMode = TRUE;
63
64 /* stdin */
65 PAL_Stdin.bsdFilePtr = stdin;
66 PAL_Stdin.PALferrorCode = PAL_FILE_NOERROR;
67 PAL_Stdin.bTextMode = TRUE;
68
69 /* stderr */
70 PAL_Stderr.bsdFilePtr = stderr;
71 PAL_Stderr.PALferrorCode = PAL_FILE_NOERROR;
72 PAL_Stderr.bTextMode = TRUE;
73 return TRUE;
74}
75
76/*++
77Function :
78
79 MapFileOpenModes
80
81 Maps Windows file open modes to Unix fopen modes and validates.
82
83--*/
84static LPSTR MapFileOpenModes(LPSTR str , BOOL * bTextMode)
85{
86 LPSTR retval = NULL;
87 LPSTR temp = NULL;
88
89 if (NULL == bTextMode)
90 {
91 ASSERT("MapFileOpenModes called with a NULL parameter for bTextMode.\n");
92 return NULL;
93 }
94
95 *bTextMode = TRUE;
96
97 if (NULL == str)
98 {
99 ASSERT("MapFileOpenModes called with a NULL parameter for str.\n");
100 return NULL;
101 }
102
103 /* The PAL behaves differently for some Windows file open modes:
104
105 c, n, S, R, and T: these are all hints to the system that aren't supported
106 by the PAL. Since the user cannot depend on this behavior, it's safe to
107 simply ignore these modes.
108
109 D: specifies a file as temporary. This file is expected to be deleted when
110 the last file descriptor is closed. The PAL does not support this behavior
111 and asserts when this mode is used.
112
113 t: represents opening in text mode. Calls to fdopen on Unix don't accept
114 't' so it is silently stripped out. However, the PAL supports the mode by
115 having the PAL wrappers do the translation of CR-LF to LF and vice versa.
116
117 t vs. b: To get binary mode, you must explicitly use 'b'. If neither mode
118 is specified on Windows, the default mode is defined by the global
119 variable _fmode. The PAL simply defaults to text mode. After examining
120 CLR usage patterns, the PAL behavior seems acceptable. */
121
122 /* Check if the mode specifies deleting the temporary file
123 automatically when the last file descriptor is closed.
124 The PAL does not support this behavior. */
125 if (NULL != strchr(str,'D'))
126 {
127 ASSERT("The PAL doesn't support the 'D' flag for _fdopen and fopen.\n");
128 return NULL;
129 }
130
131 /* Check if the mode specifies opening in binary.
132 If so, set the bTextMode to false. */
133 if(NULL != strchr(str,'b'))
134 {
135 *bTextMode = FALSE;
136 }
137
138 retval = (LPSTR)PAL_malloc( ( strlen( str ) + 1 ) * sizeof( CHAR ) );
139 if (NULL == retval)
140 {
141 ERROR("Unable to allocate memory.\n");
142 return NULL;
143 }
144
145 temp = retval;
146 while ( *str )
147 {
148 if ( *str == 'r' || *str == 'w' || *str == 'a' )
149 {
150 *temp = *str;
151 temp++;
152 if ( ( ++str != NULL ) && *str == '+' )
153 {
154 *temp = *str;
155 temp++;
156 str++;
157 }
158 }
159 else
160 {
161 str++;
162 }
163 }
164 *temp = '\0';
165 return retval;
166}
167
168#if UNGETC_NOT_RETURN_EOF
169/*++
170Function :
171
172 WriteOnlyMode
173
174 Returns TRUE to if a file is opened in write-only mode,
175 Otherwise FALSE.
176
177--*/
178static BOOL WriteOnlyMode(FILE* pFile)
179{
180 INT fd, flags;
181
182 if (pFile != NULL)
183 {
184 fd = fileno(pFile);
185 if ((flags = fcntl(fd, F_GETFL)) >= 0)
186 {
187 if ((flags & O_ACCMODE) == O_WRONLY)
188 {
189 return TRUE;
190 }
191 }
192 }
193 return FALSE;
194}
195#endif //UNGETC_NOT_RETURN_EOF
196
197/*++
198Function:
199 _fdopen
200
201see MSDN
202
203--*/
204PAL_FILE *
205__cdecl
206_fdopen(
207 int handle,
208 const char *mode)
209{
210 PAL_FILE *f = NULL;
211 LPSTR supported = NULL;
212 BOOL bTextMode = TRUE;
213
214 PERF_ENTRY(_fdopen);
215 ENTRY("_fdopen (handle=%d mode=%p (%s))\n", handle, mode, mode);
216
217 _ASSERTE(mode != NULL);
218
219 f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
220 if ( f )
221 {
222 supported = MapFileOpenModes( (char*)mode , &bTextMode);
223 if ( !supported )
224 {
225 PAL_free(f);
226 f = NULL;
227 goto EXIT;
228 }
229
230 f->bsdFilePtr = (FILE *)fdopen( handle, supported );
231 f->PALferrorCode = PAL_FILE_NOERROR;
232 /* Make sure fdopen did not fail. */
233 if ( !f->bsdFilePtr )
234 {
235 PAL_free( f );
236 f = NULL;
237 }
238
239 PAL_free( supported );
240 supported = NULL;
241 }
242 else
243 {
244 ERROR( "Unable to allocate memory for the PAL_FILE wrapper!\n" );
245 }
246
247EXIT:
248 LOGEXIT( "_fdopen returns FILE* %p\n", f );
249 PERF_EXIT(_fdopen);
250 return f;
251}
252
253
254/*++
255
256Function :
257 fopen
258
259see MSDN doc.
260
261--*/
262PAL_FILE *
263__cdecl
264PAL_fopen(const char * fileName, const char * mode)
265{
266 PAL_FILE *f = NULL;
267 LPSTR supported = NULL;
268 LPSTR UnixFileName = NULL;
269 struct stat stat_data;
270 BOOL bTextMode = TRUE;
271
272 PERF_ENTRY(fopen);
273 ENTRY("fopen ( fileName=%p (%s) mode=%p (%s))\n", fileName, fileName, mode , mode );
274
275 _ASSERTE(fileName != NULL);
276 _ASSERTE(mode != NULL);
277
278 if ( *mode == 'r' || *mode == 'w' || *mode == 'a' )
279 {
280 supported = MapFileOpenModes( (char*)mode,&bTextMode);
281
282 if ( !supported )
283 {
284 goto done;
285 }
286
287 UnixFileName = PAL__strdup(fileName);
288 if (UnixFileName == NULL )
289 {
290 ERROR("PAL__strdup() failed\n");
291 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
292 goto done;
293 }
294
295 FILEDosToUnixPathA( UnixFileName );
296
297 /*I am not checking for the case where stat fails
298 *as fopen will handle the error more gracefully in case
299 *UnixFileName is invalid*/
300 if ((stat(UnixFileName, &stat_data) == 0 ) &&
301 ((stat_data.st_mode & S_IFMT) == S_IFDIR))
302 {
303 goto done;
304 }
305
306 f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) );
307 if ( f )
308 {
309 f->bsdFilePtr = (FILE*)fopen( UnixFileName, supported );
310 f->PALferrorCode = PAL_FILE_NOERROR;
311 f->bTextMode = bTextMode;
312 if ( !f->bsdFilePtr )
313 {
314 /* Failed */
315 PAL_free( f );
316 f = NULL;
317 }
318#if UNGETC_NOT_RETURN_EOF
319 else
320 {
321 f->bWriteOnlyMode = WriteOnlyMode(f->bsdFilePtr);
322 }
323#endif //UNGETC_NOT_RETURN_EOF
324 }
325 else
326 {
327 ERROR( "Unable to allocate memory to the PAL_FILE wrapper\n" );
328 }
329 }
330 else
331 {
332 ERROR( "The mode flags must start with either an a, w, or r.\n" );
333 }
334
335done:
336 PAL_free( supported );
337 supported = NULL;
338 PAL_free( UnixFileName );
339
340 LOGEXIT( "fopen returns FILE* %p\n", f );
341 PERF_EXIT(fopen);
342 return f;
343}
344
345/*++
346Function:
347 _wfopen
348
349see MSDN doc.
350
351--*/
352PAL_FILE *
353__cdecl
354_wfopen(
355 const wchar_16 *fileName,
356 const wchar_16 *mode)
357{
358 CHAR mbFileName[ _MAX_PATH ];
359 CHAR mbMode[ 10 ];
360 PAL_FILE * filePtr = NULL;
361
362 PERF_ENTRY(_wfopen);
363 ENTRY("_wfopen(fileName:%p (%S), mode:%p (%S))\n", fileName, fileName, mode, mode);
364
365 _ASSERTE(fileName != NULL);
366 _ASSERTE(mode != NULL);
367
368 /* Convert the parameters to ASCII and defer to PAL_fopen */
369 if ( WideCharToMultiByte( CP_ACP, 0, fileName, -1, mbFileName,
370 sizeof mbFileName, NULL, NULL ) != 0 )
371 {
372 if ( WideCharToMultiByte( CP_ACP, 0, mode, -1, mbMode,
373 sizeof mbMode, NULL, NULL ) != 0 )
374 {
375 filePtr = PAL_fopen(mbFileName, mbMode);
376 }
377 else
378 {
379 ERROR( "An error occurred while converting mode to ANSI.\n" );
380 }
381 }
382 else
383 {
384 ERROR( "An error occurred while converting"
385 " fileName to ANSI string.\n" );
386 }
387 LOGEXIT("_wfopen returning FILE* %p\n", filePtr);
388 PERF_EXIT(_wfopen);
389 return filePtr;
390}
391
392/*++
393Function:
394_wfsopen
395
396see MSDN doc.
397
398--*/
399PAL_FILE *
400__cdecl
401_wfsopen(
402 const wchar_16 *fileName,
403 const wchar_16 *mode,
404 int shflag)
405{
406 // UNIXTODO: Implement this.
407 ERROR("Needs Implementation!!!");
408 return NULL;
409}
410
411/*++
412Function
413 PAL_get_stdout.
414
415 Returns the stdout stream.
416--*/
417PAL_FILE * __cdecl PAL_get_stdout(int caller)
418{
419 PERF_ENTRY(get_stdout);
420 ENTRY("PAL_get_stdout\n");
421 LOGEXIT("PAL_get_stdout returns PAL_FILE * %p\n", &PAL_Stdout );
422 PERF_EXIT(get_stdout);
423 return &PAL_Stdout;
424}
425
426/*++
427Function
428 PAL_get_stdin.
429
430 Returns the stdin stream.
431--*/
432PAL_FILE * __cdecl PAL_get_stdin(int caller)
433{
434 PERF_ENTRY(get_stdin);
435 ENTRY("PAL_get_stdin\n");
436 LOGEXIT("PAL_get_stdin returns PAL_FILE * %p\n", &PAL_Stdin );
437 PERF_EXIT(get_stdin);
438 return &PAL_Stdin;
439}
440
441/*++
442Function
443 PAL_get_stderr.
444
445 Returns the stderr stream.
446--*/
447PAL_FILE * __cdecl PAL_get_stderr(int caller)
448{
449 PERF_ENTRY(get_stderr);
450 ENTRY("PAL_get_stderr\n");
451 LOGEXIT("PAL_get_stderr returns PAL_FILE * %p\n", &PAL_Stderr );
452 PERF_EXIT(get_stderr);
453 return &PAL_Stderr;
454}
455
456
457/*++
458
459Function:
460
461 _close
462
463See msdn for more details.
464--*/
465int __cdecl PAL__close(int handle)
466{
467 INT nRetVal = 0;
468
469 PERF_ENTRY(_close);
470 ENTRY( "_close( handle=%d )\n", handle );
471
472 nRetVal = close( handle );
473
474 LOGEXIT( "_close returning %d.\n", nRetVal );
475 PERF_EXIT(_close);
476 return nRetVal;
477}
478
479 int __cdecl PAL__flushall()
480 {
481 return fflush(NULL);
482 }
483
484wchar_16 *
485__cdecl
486PAL_fgetws(wchar_16 *s, int n, PAL_FILE *f)
487{
488 ASSERT (0);
489 return NULL;
490}
491
492
493/*++
494Function :
495
496 fread
497
498 See MSDN for more details.
499--*/
500
501size_t
502__cdecl
503PAL_fread(void * buffer, size_t size, size_t count, PAL_FILE * f)
504{
505 size_t nReadBytes = 0;
506
507 PERF_ENTRY(fread);
508 ENTRY( "fread( buffer=%p, size=%d, count=%d, f=%p )\n",
509 buffer, size, count, f );
510
511 _ASSERTE(f != NULL);
512
513 CLEARERR(f);
514
515 if(f->bTextMode != TRUE)
516 {
517 nReadBytes = fread( buffer, size, count, f->bsdFilePtr );
518 }
519 else
520 {
521 size_t i=0;
522 if(size > 0)
523 {
524 size_t j=0;
525 LPSTR temp = (LPSTR)buffer;
526 int nChar = 0;
527 int nCount =0;
528
529 for(i=0; i< count; i++)
530 {
531 for(j=0; j< size; j++)
532 {
533 if((nChar = PAL_getc(f)) == EOF)
534 {
535 nReadBytes = i;
536 goto done;
537 }
538 else
539 {
540 temp[nCount++]=nChar;
541 }
542 }
543 }
544 }
545 nReadBytes = i;
546 }
547
548done:
549 LOGEXIT( "fread returning size_t %d\n", nReadBytes );
550 PERF_EXIT(fread);
551 return nReadBytes;
552}
553
554
555/*++
556Function :
557
558 ferror
559
560 See MSDN for more details.
561--*/
562int
563_cdecl
564PAL_ferror(PAL_FILE * f)
565{
566 INT nErrorCode = PAL_FILE_NOERROR;
567
568 PERF_ENTRY(ferror);
569 ENTRY( "ferror( f=%p )\n", f );
570
571 _ASSERTE(f != NULL);
572
573 nErrorCode = ferror( f->bsdFilePtr );
574 if ( 0 == nErrorCode )
575 {
576 /* See if the PAL file error code is set. */
577 nErrorCode = f->PALferrorCode;
578 }
579
580 LOGEXIT( "ferror returns %d\n", nErrorCode );
581 PERF_EXIT(ferror);
582 return nErrorCode;
583}
584
585
586/*++
587Function :
588
589 fclose
590
591 See MSDN for more details.
592--*/
593int
594_cdecl
595PAL_fclose(PAL_FILE * f)
596{
597 INT nRetVal = 0;
598
599 PERF_ENTRY(fclose);
600 ENTRY( "fclose( f=%p )\n", f );
601
602 _ASSERTE(f != NULL);
603
604 CLEARERR(f);
605
606 nRetVal = fclose( f->bsdFilePtr );
607 PAL_free( f );
608
609 LOGEXIT( "fclose returning %d\n", nRetVal );
610 PERF_EXIT(fclose);
611 return nRetVal;
612}
613
614/*++
615Function :
616
617 setbuf
618
619 See MSDN for more details.
620--*/
621void
622_cdecl
623PAL_setbuf(PAL_FILE * f, char * buffer)
624{
625 PERF_ENTRY(setbuf);
626 ENTRY( "setbuf( %p, %p )\n", f, buffer );
627
628 _ASSERTE(f != NULL);
629
630 setbuf( f->bsdFilePtr, buffer );
631
632 LOGEXIT( "setbuf\n" );
633 PERF_EXIT(setbuf);
634}
635
636/*++
637Function :
638
639 fputs
640
641 See MSDN for more details.
642--*/
643int
644_cdecl
645PAL_fputs(const char * str, PAL_FILE * f)
646{
647 INT nRetVal = 0;
648
649 PERF_ENTRY(fputs);
650 ENTRY( "fputs( %p (%s), %p )\n", str, str, f);
651
652 _ASSERTE(str != NULL);
653 _ASSERTE(f != NULL);
654
655 CLEARERR(f);
656
657 nRetVal = fputs( str, f->bsdFilePtr );
658
659 LOGEXIT( "fputs returning %d\n", nRetVal );
660 PERF_EXIT(fputs);
661 return nRetVal;
662}
663
664/*--
665Function :
666
667 fputc
668
669 See MSDN for more details.
670--*/
671int
672_cdecl
673PAL_fputc(int c, PAL_FILE * f)
674{
675 INT nRetVal = 0;
676
677 PERF_ENTRY(fputc);
678 ENTRY( "fputc( 0x%x (%c), %p )\n", c, c, f);
679
680 _ASSERTE(f != NULL);
681
682 CLEARERR(f);
683
684 nRetVal = fputc( c, f->bsdFilePtr );
685
686 LOGEXIT( "fputc returning %d\n", nRetVal );
687 PERF_EXIT(fputc);
688 return nRetVal;
689}
690
691/*--
692Function :
693
694 putchar
695
696 See MSDN for more details.
697--*/
698int
699_cdecl
700PAL_putchar( int c )
701{
702 INT nRetVal = 0;
703
704 PERF_ENTRY(putchar);
705 ENTRY( "putchar( 0x%x (%c) )\n", c, c);
706
707 nRetVal = putchar( c );
708
709 LOGEXIT( "putchar returning %d\n", nRetVal );
710 PERF_EXIT(putchar);
711 return nRetVal;
712}
713
714/*++
715Function :
716
717 ftell
718
719 See MSDN for more details.
720--*/
721LONG
722_cdecl
723PAL_ftell(PAL_FILE * f)
724{
725 long lRetVal = 0;
726
727 PERF_ENTRY(ftell);
728 ENTRY( "ftell( %p )\n", f );
729
730 _ASSERTE(f != NULL);
731 lRetVal = ftell( f->bsdFilePtr );
732
733#ifdef BIT64
734 /* Windows does not set an error if the file pointer's position
735 is greater than _I32_MAX. It just returns -1. */
736 if (lRetVal > _I32_MAX)
737 {
738 lRetVal = -1;
739 }
740#endif
741
742 LOGEXIT( "ftell returning %ld\n", lRetVal );
743 PERF_EXIT(ftell);
744 /* This explicit cast to LONG is used to silence any potential warnings
745 due to implicitly casting the native long lRetVal to LONG when returning. */
746 return (LONG)lRetVal;
747}
748
749/*++
750Function :
751
752 feof
753
754 See MSDN for more details.
755--*/
756int
757_cdecl
758PAL_feof(PAL_FILE * f)
759{
760 INT nRetVal = 0;
761
762 PERF_ENTRY(feof);
763 ENTRY( "feof( %p )\n", f );
764
765 _ASSERTE(f != NULL);
766 nRetVal = feof( f->bsdFilePtr );
767
768 LOGEXIT( "feof returning %d\n", nRetVal );
769 PERF_EXIT(feof);
770 return nRetVal;
771}
772
773/*++
774Function :
775
776 getc
777
778 See MSDN for more details.
779--*/
780int
781_cdecl
782PAL_getc(PAL_FILE * f)
783{
784 INT nRetVal = 0;
785 INT temp =0;
786
787 PERF_ENTRY(getc);
788 ENTRY( "getc( %p )\n", f );
789
790 _ASSERTE(f != NULL);
791
792 CLEARERR(f);
793
794 nRetVal = getc( f->bsdFilePtr );
795
796 if ( (f->bTextMode) && (nRetVal == '\r') )
797 {
798 if ((temp = getc( f->bsdFilePtr ))== '\n')
799 {
800 nRetVal ='\n';
801 }
802 else if (EOF == ungetc( temp, f->bsdFilePtr ))
803 {
804 ERROR("ungetc operation failed\n");
805 }
806 }
807
808 LOGEXIT( "getc returning %d\n", nRetVal );
809 PERF_EXIT(getc);
810 return nRetVal;
811}
812
813/*++
814Function :
815
816 ungetc
817
818 See MSDN for more details.
819--*/
820int
821_cdecl
822PAL_ungetc(int c, PAL_FILE * f)
823{
824 INT nRetVal = 0;
825
826 PERF_ENTRY(ungetc);
827 ENTRY( "ungetc( %c, %p )\n", c, f );
828
829 _ASSERTE(f != NULL);
830
831#if UNGETC_NOT_RETURN_EOF
832 /* On some Unix platform such as Solaris, ungetc does not return EOF
833 on write-only file. */
834 if (f->bWriteOnlyMode)
835 {
836 nRetVal = EOF;
837 }
838 else
839#endif //UNGETC_NOT_RETURN_EOF
840 {
841 CLEARERR(f);
842
843 nRetVal = ungetc( c, f->bsdFilePtr );
844 }
845
846 LOGEXIT( "ungetc returning %d\n", nRetVal );
847 PERF_EXIT(ungetc);
848 return nRetVal;
849}
850
851
852
853/*++
854Function :
855
856 setvbuf
857
858 See MSDN for more details.
859--*/
860int
861_cdecl
862PAL_setvbuf(PAL_FILE *f, char *buf, int type, size_t size)
863{
864 INT nRetVal = 0;
865
866 PERF_ENTRY(setvbuf);
867 ENTRY( "setvbuf( %p, %p, %d, %ul )\n", f, buf, type, size);
868
869 _ASSERTE(f != NULL);
870
871 nRetVal = setvbuf(f->bsdFilePtr, buf, type, size);
872
873 LOGEXIT( "setvbuf returning %d\n", nRetVal );
874 PERF_EXIT(setvbuf);
875 return nRetVal;
876}
877