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 filecrt.cpp
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/thread.hpp"
23#include "pal/file.hpp"
24
25#include "pal/palinternal.h"
26#include "pal/dbgmsg.h"
27#include "pal/file.h"
28#include "pal/cruntime.h"
29
30#include <unistd.h>
31#include <errno.h>
32#include <sys/stat.h>
33
34#ifdef __APPLE__
35#include <sys/syscall.h>
36#endif // __APPLE__
37
38using namespace CorUnix;
39
40SET_DEFAULT_DEBUG_CHANNEL(CRT);
41
42/*++
43Function:
44 _open_osfhandle
45
46See MSDN doc.
47--*/
48int
49__cdecl
50_open_osfhandle( INT_PTR osfhandle, int flags )
51{
52 PAL_ERROR palError = NO_ERROR;
53 CPalThread *pthrCurrent = NULL;
54 IPalObject *pobjFile = NULL;
55 CFileProcessLocalData *pLocalData = NULL;
56 IDataLock *pDataLock = NULL;
57 INT nRetVal = -1;
58 INT openFlags = 0;
59
60 PERF_ENTRY(_open_osfhandle);
61 ENTRY( "_open_osfhandle (osfhandle=%#x, flags=%#x)\n", osfhandle, flags );
62
63 pthrCurrent = InternalGetCurrentThread();
64
65 if (flags != _O_RDONLY)
66 {
67 ASSERT("flag(%#x) not supported\n", flags);
68 goto EXIT;
69 }
70
71 openFlags |= O_RDONLY;
72
73 palError = g_pObjectManager->ReferenceObjectByHandle(
74 pthrCurrent,
75 reinterpret_cast<HANDLE>(osfhandle),
76 &aotFile,
77 0,
78 &pobjFile
79 );
80
81 if (NO_ERROR != palError)
82 {
83 ERROR("Error dereferencing file handle\n");
84 goto EXIT;
85 }
86
87 palError = pobjFile->GetProcessLocalData(
88 pthrCurrent,
89 ReadLock,
90 &pDataLock,
91 reinterpret_cast<void **>(&pLocalData)
92 );
93
94 if (NO_ERROR == palError)
95 {
96 if (NULL != pLocalData->unix_filename)
97 {
98 nRetVal = InternalOpen(pLocalData->unix_filename, openFlags);
99 }
100 else /* the only file object with no unix_filename is a pipe */
101 {
102 /* check if the file pipe descrptor is for read or write */
103 if (pLocalData->open_flags == O_WRONLY)
104 {
105 ERROR( "Couldn't open a write pipe on read mode\n");
106 goto EXIT;
107 }
108
109 nRetVal = pLocalData->unix_fd;
110 }
111
112 if ( nRetVal == -1 )
113 {
114 ERROR( "Error: %s.\n", strerror( errno ) );
115 }
116 }
117 else
118 {
119 ASSERT("Unable to access file data");
120 }
121
122EXIT:
123
124 if (NULL != pDataLock)
125 {
126 pDataLock->ReleaseLock(pthrCurrent, FALSE);
127 }
128
129 if (NULL != pobjFile)
130 {
131 pobjFile->ReleaseReference(pthrCurrent);
132 }
133
134 LOGEXIT( "_open_osfhandle return nRetVal:%d\n", nRetVal);
135 PERF_EXIT(_open_osfhandle);
136 return nRetVal;
137}
138
139
140/*++
141Function:
142 PAL_fflush
143
144See MSDN for more details.
145--*/
146int
147_cdecl
148PAL_fflush( PAL_FILE *stream )
149{
150 int nRetVal = 0;
151
152 PERF_ENTRY(fflush);
153 ENTRY( "fflush( %p )\n", stream );
154
155 nRetVal = fflush(stream ? stream->bsdFilePtr : NULL);
156
157 LOGEXIT( "fflush returning %d\n", nRetVal );
158 PERF_EXIT(fflush);
159 return nRetVal;
160}
161
162
163/*++
164PAL__getcwd
165
166Wrapper function for getcwd.
167
168Input parameters:
169
170szBuf = a copy of the absolute pathname of the current working directory
171is copied into szBuf.
172nSize = size, in bytes, of the array referenced by szBuf.
173
174Return value:
175 A pointer to the pathname if successful, otherwise NULL is returned
176--*/
177char *
178__cdecl
179PAL__getcwd(
180 char *szBuf,
181 size_t nSize
182 )
183{
184 return (char *)getcwd(szBuf, nSize);
185}
186
187
188/*++
189PAL_mkstemp
190
191Wrapper function for InternalMkstemp.
192
193Input parameters:
194
195szNameTemplate = template to follow when naming the created file
196
197Return value:
198 Open file descriptor on success, -1 if file could not be created
199--*/
200int
201__cdecl
202PAL_mkstemp(char *szNameTemplate)
203{
204 return InternalMkstemp(szNameTemplate);
205}
206
207/*++
208InternalMkstemp
209
210Wrapper for mkstemp.
211
212Input parameters:
213
214szNameTemplate = template to follow when naming the created file
215
216Return value:
217 Open file descriptor on success, -1 if file could not be created
218--*/
219int
220CorUnix::InternalMkstemp(
221 char *szNameTemplate
222 )
223{
224 int nRet = -1;
225#if MKSTEMP64_IS_USED_INSTEAD_OF_MKSTEMP
226 nRet = mkstemp64(szNameTemplate);
227#else
228 nRet = mkstemp(szNameTemplate);
229#endif
230 return nRet;
231}
232
233
234/*++
235PAL__open
236
237Wrapper function for InternalOpen.
238
239Input parameters:
240
241szPath = pointer to a pathname of a file to be opened
242nFlags = arguments that control how the file should be accessed
243mode = file permission settings that are used only when a file is created
244
245Return value:
246 File descriptor on success, -1 on failure
247--*/
248int
249__cdecl
250PAL__open(
251 const char *szPath,
252 int nFlags,
253 ...
254 )
255{
256 int nRet = -1;
257 int mode = 0;
258 va_list ap;
259
260 // If nFlags does not contain O_CREAT, the mode parameter will be ignored.
261 if (nFlags & O_CREAT)
262 {
263 va_start(ap, nFlags);
264 mode = va_arg(ap, int);
265 va_end(ap);
266 }
267
268 nRet = InternalOpen(szPath, nFlags, mode);
269 return nRet;
270}
271
272/*++
273InternalOpen
274
275Wrapper for open.
276
277Input parameters:
278
279szPath = pointer to a pathname of a file to be opened
280nFlags = arguments that control how the file should be accessed
281mode = file permission settings that are used only when a file is created
282
283Return value:
284 File descriptor on success, -1 on failure
285--*/
286int
287CorUnix::InternalOpen(
288 const char *szPath,
289 int nFlags,
290 ...
291 )
292{
293 int nRet = -1;
294 int mode = 0;
295 va_list ap;
296
297 // If nFlags does not contain O_CREAT, the mode parameter will be ignored.
298 if (nFlags & O_CREAT)
299 {
300 va_start(ap, nFlags);
301 mode = va_arg(ap, int);
302 va_end(ap);
303 }
304
305#if OPEN64_IS_USED_INSTEAD_OF_OPEN
306 nRet = open64(szPath, nFlags, mode);
307#else
308 nRet = open(szPath, nFlags, mode);
309#endif
310 return nRet;
311}
312
313
314/*++
315PAL_rename
316
317Wrapper function for rename.
318
319Input parameters:
320
321szOldName = pointer to the pathname of the file to be renamed
322szNewName = pointer to the new pathname of the file
323
324Return value:
325 Returns 0 on success and -1 on failure
326--*/
327int
328__cdecl
329PAL_rename(
330 const char *szOldName,
331 const char *szNewName
332 )
333{
334 return rename(szOldName, szNewName);
335}
336
337
338/*++
339PAL_fgets
340
341Wrapper function for InternalFgets.
342
343Input parameters:
344
345sz = stores characters read from the given file stream
346nSize = number of characters to be read
347pf = stream to read characters from
348
349Return value:
350 Returns a pointer to the string storing the characters on success
351 and NULL on failure.
352--*/
353char *
354__cdecl
355PAL_fgets(
356 char *sz,
357 int nSize,
358 PAL_FILE *pf
359 )
360{
361 char * szBuf;
362
363 PERF_ENTRY(fgets);
364 ENTRY( "fgets(sz=%p (%s) nSize=%d pf=%p)\n", sz, sz, nSize, pf);
365
366 if (pf != NULL)
367 {
368 szBuf = InternalFgets(sz, nSize, pf->bsdFilePtr, pf->bTextMode);
369 }
370 else
371 {
372 szBuf = NULL;
373 }
374
375 LOGEXIT("fgets() returns %p\n", szBuf);
376 PERF_EXIT(fgets);
377
378 return szBuf;
379}
380
381/*++
382InternalFgets
383
384Wrapper for fgets.
385
386Input parameters:
387
388sz = stores characters read from the given file stream
389nSize = number of characters to be read
390f = stream to read characters from
391fTextMode = flag that indicates if file contents are text or binary
392
393Return value:
394 Returns a pointer to the string storing the characters on success
395 and NULL on failure.
396
397Notes:
398In Unix systems, fgets() can return an error if it gets interrupted by a
399signal before reading anything, and errno is set to EINTR. When this
400happens, it is SOP to call fgets again.
401--*/
402char *
403CorUnix::InternalFgets(
404 char *sz,
405 int nSize,
406 FILE *f,
407 bool fTextMode
408 )
409{
410 char *retval = NULL;
411
412 _ASSERTE(sz != NULL);
413 _ASSERTE(f != NULL);
414
415#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
416 clearerr(f);
417#endif
418
419 do
420 {
421 retval = fgets(sz, nSize, f);
422 if (NULL==retval)
423 {
424 if (feof(f))
425 {
426 TRACE("Reached EOF\n");
427 break;
428 }
429 /* The man page suggests using ferror and feof to distinguish
430 between error and EOF, but feof and errno is sufficient.
431 Not all cases that set errno also flag ferror, so just
432 checking errno is the best solution. */
433 if (EINTR != errno)
434 {
435 WARN("got error; errno is %d (%s)\n",errno, strerror(errno));
436 break;
437 }
438 /* we ignored a EINTR error, reset the stream's error state */
439 clearerr(f);
440 TRACE("call got interrupted (EINTR), trying again\n");
441 }
442 if (fTextMode)
443 {
444 int len = strlen(sz);
445 if ((len>=2) && (sz[len-1]=='\n') && (sz[len-2]=='\r'))
446 {
447 sz[len-2]='\n';
448 sz[len-1]='\0';
449 }
450 }
451 } while(NULL == retval);
452
453 return retval;
454}
455
456/*++
457PAL_fwrite
458
459Wrapper function for InternalFwrite.
460
461Input parameters:
462
463pvBuffer = array of objects to write to the given file stream
464nSize = size of a object in bytes
465nCount = number of objects to write
466pf = stream to write characters to
467
468Return value:
469 Returns the number of objects written.
470--*/
471size_t
472__cdecl
473PAL_fwrite(
474 const void *pvBuffer,
475 size_t nSize,
476 size_t nCount,
477 PAL_FILE *pf
478 )
479{
480 size_t nWrittenBytes = 0;
481
482 PERF_ENTRY(fwrite);
483 ENTRY( "fwrite( pvBuffer=%p, nSize=%d, nCount=%d, pf=%p )\n",
484 pvBuffer, nSize, nCount, pf);
485 _ASSERTE(pf != NULL);
486
487 nWrittenBytes = InternalFwrite(pvBuffer, nSize, nCount, pf->bsdFilePtr, &pf->PALferrorCode);
488
489 LOGEXIT( "fwrite returning size_t %d\n", nWrittenBytes );
490 PERF_EXIT(fwrite);
491 return nWrittenBytes;
492}
493
494/*++
495InternalFwrite
496
497Wrapper for fwrite.
498
499Input parameters:
500
501pvBuffer = array of objects to write to the given file stream
502nSize = size of a object in bytes
503nCount = number of objects to write
504f = stream to write characters to
505pnErrorCode = reference to a PAL_FILE's fwrite error code field
506
507Return value:
508 Returns the number of objects written.
509--*/
510size_t
511CorUnix::InternalFwrite(
512 const void *pvBuffer,
513 size_t nSize,
514 size_t nCount,
515 FILE *f,
516 INT *pnErrorCode
517 )
518{
519 size_t nWrittenBytes = 0;
520 _ASSERTE(f != NULL);
521
522#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
523 clearerr(f);
524#endif
525
526 nWrittenBytes = fwrite(pvBuffer, nSize, nCount, f);
527
528 // Make sure no error ocurred.
529 if ( nWrittenBytes < nCount )
530 {
531 // Set the FILE* error code
532 *pnErrorCode = PAL_FILE_ERROR;
533 }
534
535 return nWrittenBytes;
536}
537
538
539/*++
540PAL_fseek
541
542Wrapper function for fseek.
543
544Input parameters:
545
546pf = a given file stream
547lOffset = distance from position to set file-position indicator
548nWhence = method used to determine the file_position indicator location relative to lOffset
549
550Return value:
551 0 on success, -1 on failure.
552--*/
553int
554_cdecl
555PAL_fseek(
556 PAL_FILE * pf,
557 LONG lOffset,
558 int nWhence
559 )
560{
561 int nRet = 0;
562
563 PERF_ENTRY(fseek);
564 ENTRY( "fseek( %p, %ld, %d )\n", pf, lOffset, nWhence );
565
566 nRet = fseek(pf ? pf->bsdFilePtr : NULL, lOffset, nWhence);
567
568 LOGEXIT("fseek returning %d\n", nRet);
569 PERF_EXIT(fseek);
570 return nRet;
571}
572