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.cpp
12
13Abstract:
14
15 Implementation of the file WIN API for the PAL
16
17
18
19--*/
20
21#include "pal/dbgmsg.h"
22SET_DEFAULT_DEBUG_CHANNEL(FILE); // some headers have code with asserts, so do this first
23
24#include "pal/thread.hpp"
25#include "pal/file.hpp"
26#include "pal/malloc.hpp"
27#include "pal/stackstring.hpp"
28
29#include "pal/palinternal.h"
30#include "pal/file.h"
31#include "pal/filetime.h"
32#include "pal/utils.h"
33
34#include <time.h>
35#include <stdio.h>
36#include <sys/file.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/param.h>
40#include <sys/mount.h>
41#include <errno.h>
42#include <limits.h>
43
44using namespace CorUnix;
45
46int MaxWCharToAcpLengthFactor = 3;
47
48PAL_ERROR
49InternalSetFilePointerForUnixFd(
50 int iUnixFd,
51 LONG lDistanceToMove,
52 PLONG lpDistanceToMoveHigh,
53 DWORD dwMoveMethod,
54 PLONG lpNewFilePointerLow
55 );
56
57void
58CFileProcessLocalDataCleanupRoutine(
59 CPalThread *pThread,
60 IPalObject *pObjectToCleanup
61 );
62
63void
64FileCleanupRoutine(
65 CPalThread *pThread,
66 IPalObject *pObjectToCleanup,
67 bool fShutdown,
68 bool fCleanupSharedState
69 );
70
71CObjectType CorUnix::otFile(
72 otiFile,
73 FileCleanupRoutine,
74 NULL, // No initialization routine
75 0, // No immutable data
76 NULL, // No immutable data copy routine
77 NULL, // No immutable data cleanup routine
78 sizeof(CFileProcessLocalData),
79 CFileProcessLocalDataCleanupRoutine,
80 0, // No shared data
81 GENERIC_READ|GENERIC_WRITE, // Ignored -- no Win32 object security support
82 CObjectType::SecuritySupported,
83 CObjectType::OSPersistedSecurityInfo,
84 CObjectType::UnnamedObject,
85 CObjectType::LocalDuplicationOnly,
86 CObjectType::UnwaitableObject,
87 CObjectType::SignalingNotApplicable,
88 CObjectType::ThreadReleaseNotApplicable,
89 CObjectType::OwnershipNotApplicable
90 );
91
92CAllowedObjectTypes CorUnix::aotFile(otiFile);
93
94void
95CFileProcessLocalDataCleanupRoutine(
96 CPalThread *pThread,
97 IPalObject *pObjectToCleanup
98 )
99{
100 PAL_ERROR palError;
101 CFileProcessLocalData *pLocalData = NULL;
102 IDataLock *pLocalDataLock = NULL;
103
104 palError = pObjectToCleanup->GetProcessLocalData(
105 pThread,
106 ReadLock,
107 &pLocalDataLock,
108 reinterpret_cast<void**>(&pLocalData)
109 );
110
111 if (NO_ERROR != palError)
112 {
113 ASSERT("Unable to obtain data to cleanup file object");
114 return;
115 }
116
117 free(pLocalData->unix_filename);
118
119 pLocalDataLock->ReleaseLock(pThread, FALSE);
120}
121
122void
123FileCleanupRoutine(
124 CPalThread *pThread,
125 IPalObject *pObjectToCleanup,
126 bool fShutdown,
127 bool fCleanupSharedState
128 )
129{
130 PAL_ERROR palError;
131 CFileProcessLocalData *pLocalData = NULL;
132 IDataLock *pLocalDataLock = NULL;
133
134 palError = pObjectToCleanup->GetProcessLocalData(
135 pThread,
136 ReadLock,
137 &pLocalDataLock,
138 reinterpret_cast<void**>(&pLocalData)
139 );
140
141 if (NO_ERROR != palError)
142 {
143 ASSERT("Unable to obtain data to cleanup file object");
144 return;
145 }
146
147 if (!fShutdown && -1 != pLocalData->unix_fd)
148 {
149 close(pLocalData->unix_fd);
150 }
151
152 pLocalDataLock->ReleaseLock(pThread, FALSE);
153}
154
155typedef enum
156{
157 PIID_STDIN_HANDLE,
158 PIID_STDOUT_HANDLE,
159 PIID_STDERR_HANDLE
160} PROCINFO_ID;
161
162#define PAL_LEGAL_FLAGS_ATTRIBS (FILE_ATTRIBUTE_NORMAL| \
163 FILE_FLAG_SEQUENTIAL_SCAN| \
164 FILE_FLAG_WRITE_THROUGH| \
165 FILE_FLAG_NO_BUFFERING| \
166 FILE_FLAG_RANDOM_ACCESS| \
167 FILE_FLAG_BACKUP_SEMANTICS)
168
169/* Static global. The init function must be called
170before any other functions and if it is not successful,
171no other functions should be done. */
172static HANDLE pStdIn = INVALID_HANDLE_VALUE;
173static HANDLE pStdOut = INVALID_HANDLE_VALUE;
174static HANDLE pStdErr = INVALID_HANDLE_VALUE;
175
176/*++
177Function :
178 FILEGetProperNotFoundError
179
180Returns the proper error code, based on the
181Windows behavior.
182
183 IN LPSTR lpPath - The path to check.
184 LPDWORD lpErrorCode - The error to set.
185*/
186void FILEGetProperNotFoundError( LPCSTR lpPath, LPDWORD lpErrorCode )
187{
188 struct stat stat_data;
189 LPSTR lpDupedPath = NULL;
190 LPSTR lpLastPathSeparator = NULL;
191
192 TRACE( "FILEGetProperNotFoundError( %s )\n", lpPath?lpPath:"(null)" );
193
194 if ( !lpErrorCode )
195 {
196 ASSERT( "lpErrorCode has to be valid\n" );
197 return;
198 }
199
200 if ( NULL == ( lpDupedPath = strdup(lpPath) ) )
201 {
202 ERROR( "strdup() failed!\n" );
203 *lpErrorCode = ERROR_NOT_ENOUGH_MEMORY;
204 return;
205 }
206
207 /* Determine whether it's a file not found or path not found. */
208 lpLastPathSeparator = strrchr( lpDupedPath, '/');
209 if ( lpLastPathSeparator != NULL )
210 {
211 *lpLastPathSeparator = '\0';
212
213 /* If the last path component is a directory,
214 we return file not found. If it's a file or
215 doesn't exist, we return path not found. */
216 if ( '\0' == *lpDupedPath ||
217 ( stat( lpDupedPath, &stat_data ) == 0 &&
218 ( stat_data.st_mode & S_IFMT ) == S_IFDIR ) )
219 {
220 TRACE( "ERROR_FILE_NOT_FOUND\n" );
221 *lpErrorCode = ERROR_FILE_NOT_FOUND;
222 }
223 else
224 {
225 TRACE( "ERROR_PATH_NOT_FOUND\n" );
226 *lpErrorCode = ERROR_PATH_NOT_FOUND;
227 }
228 }
229 else
230 {
231 TRACE( "ERROR_FILE_NOT_FOUND\n" );
232 *lpErrorCode = ERROR_FILE_NOT_FOUND;
233 }
234
235 free(lpDupedPath);
236 lpDupedPath = NULL;
237 TRACE( "FILEGetProperNotFoundError returning TRUE\n" );
238 return;
239}
240
241/*++
242Function :
243 FILEGetLastErrorFromErrnoAndFilename
244
245Returns the proper error code for errno, or, if errno is ENOENT,
246based on the Windows behavior for nonexistent filenames.
247
248 IN LPSTR lpPath - The path to check.
249*/
250PAL_ERROR FILEGetLastErrorFromErrnoAndFilename(LPCSTR lpPath)
251{
252 PAL_ERROR palError;
253 if (ENOENT == errno)
254 {
255 FILEGetProperNotFoundError(lpPath, &palError);
256 }
257 else
258 {
259 palError = FILEGetLastErrorFromErrno();
260 }
261 return palError;
262}
263
264BOOL
265CorUnix::RealPathHelper(LPCSTR lpUnixPath, PathCharString& lpBuffer)
266{
267 StringHolder lpRealPath;
268 lpRealPath = realpath(lpUnixPath, NULL);
269 if (lpRealPath.IsNull())
270 {
271 return FALSE;
272 }
273
274 lpBuffer.Set(lpRealPath, strlen(lpRealPath));
275 return TRUE;
276}
277/*++
278InternalCanonicalizeRealPath
279 Wraps realpath() to hide platform differences. See the man page for
280 realpath(3) for details of how realpath() works.
281
282 On systems on which realpath() allows the last path component to not
283 exist, this is a straight thunk through to realpath(). On other
284 systems, we remove the last path component, then call realpath().
285
286--*/
287PAL_ERROR
288CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, PathCharString& lpBuffer)
289{
290 PAL_ERROR palError = NO_ERROR;
291
292#if !REALPATH_SUPPORTS_NONEXISTENT_FILES
293 StringHolder lpExistingPath;
294 LPSTR pchSeparator = NULL;
295 LPSTR lpFilename = NULL;
296 DWORD cchBuffer = 0;
297 DWORD cchFilename = 0;
298#endif // !REALPATH_SUPPORTS_NONEXISTENT_FILES
299
300 if (lpUnixPath == NULL)
301 {
302 ERROR ("Invalid argument to InternalCanonicalizeRealPath\n");
303 palError = ERROR_INVALID_PARAMETER;
304 goto LExit;
305 }
306
307#if REALPATH_SUPPORTS_NONEXISTENT_FILES
308 RealPathHelper(lpUnixPath, lpBuffer);
309#else // !REALPATH_SUPPORTS_NONEXISTENT_FILES
310
311 lpExistingPath = strdup(lpUnixPath);
312 if (lpExistingPath.IsNull())
313 {
314 ERROR ("strdup failed with error %d\n", errno);
315 palError = ERROR_NOT_ENOUGH_MEMORY;
316 goto LExit;
317 }
318
319 pchSeparator = strrchr(lpExistingPath, '/');
320 if (pchSeparator == NULL)
321 {
322 PathCharString pszCwdBuffer;
323
324 if (GetCurrentDirectoryA(pszCwdBuffer)== 0)
325 {
326 WARN("getcwd(NULL) failed with error %d\n", errno);
327 palError = DIRGetLastErrorFromErrno();
328 goto LExit;
329 }
330
331 if (! RealPathHelper(pszCwdBuffer, lpBuffer))
332 {
333 WARN("realpath() failed with error %d\n", errno);
334 palError = FILEGetLastErrorFromErrno();
335#if defined(_AMD64_)
336 // If we are here, then we tried to invoke realpath
337 // against a directory.
338 //
339 // On Mac64, realpath implementation differs from Mac32
340 // by *not* supporting invalid filenames in the path (while
341 // Mac32 implementation does).
342 //
343 // Thus, if we are here, and the error code we see is
344 // ERROR_FILE_NOT_FOUND, then we should map it to
345 // ERROR_PATH_NOT_FOUND since it was a directory that
346 // was not found (and not a file).
347 if (palError == ERROR_FILE_NOT_FOUND)
348 {
349 // Since lpBuffer can be modified by the realpath call,
350 // and can result in a truncated subset of the original buffer,
351 // we use strstr as a level of safety.
352 if (strstr(pszCwdBuffer, lpBuffer) != 0)
353 {
354 palError = ERROR_PATH_NOT_FOUND;
355 }
356 }
357#endif // defined(_AMD64_)
358
359 goto LExit;
360 }
361 lpFilename = lpExistingPath;
362 }
363 else
364 {
365#if defined(_AMD64_)
366 bool fSetFilename = true;
367 // Since realpath implementation cannot handle inexistent filenames,
368 // check if we are going to truncate the "/" corresponding to the
369 // root folder (e.g. case of "/Volumes"). If so:
370 //
371 // 1) Set the seperator to point to the NULL terminator of the specified
372 // file/folder name.
373 //
374 // 2) Null terminate lpBuffer
375 //
376 // 3) Since there is no explicit filename component in lpExistingPath (as
377 // we only have "/" corresponding to the root), set lpFilename to NULL,
378 // alongwith a flag indicating that it has already been set.
379 if (pchSeparator == lpExistingPath)
380 {
381 pchSeparator = lpExistingPath+strlen(lpExistingPath);
382
383 // Set the lpBuffer to NULL
384 lpBuffer.Clear();
385 lpFilename = NULL;
386 fSetFilename = false;
387 }
388 else
389#endif // defined(_AMD64_)
390 *pchSeparator = '\0';
391
392 if (!RealPathHelper(lpExistingPath, lpBuffer))
393 {
394 WARN("realpath() failed with error %d\n", errno);
395 palError = FILEGetLastErrorFromErrno();
396
397#if defined(_AMD64_)
398 // If we are here, then we tried to invoke realpath
399 // against a directory after stripping out the filename
400 // from the original path.
401 //
402 // On Mac64, realpath implementation differs from Mac32
403 // by *not* supporting invalid filenames in the path (while
404 // Mac32 implementation does).
405 //
406 // Thus, if we are here, and the error code we see is
407 // ERROR_FILE_NOT_FOUND, then we should map it to
408 // ERROR_PATH_NOT_FOUND since it was a directory that
409 // was not found (and not a file).
410 if (palError == ERROR_FILE_NOT_FOUND)
411 {
412 // Since lpBuffer can be modified by the realpath call,
413 // and can result in a truncated subset of the original buffer,
414 // we use strstr as a level of safety.
415 if (strstr(lpExistingPath, lpBuffer) != 0)
416 {
417 palError = ERROR_PATH_NOT_FOUND;
418 }
419 }
420#endif // defined(_AMD64_)
421
422 goto LExit;
423 }
424
425#if defined(_AMD64_)
426 if (fSetFilename == true)
427#endif // defined(_AMD64_)
428 lpFilename = pchSeparator + 1;
429 }
430
431#if defined(_AMD64_)
432 if (lpFilename == NULL)
433 goto LExit;
434#endif // _AMD64_
435
436 if (!lpBuffer.Append("/",1) || !lpBuffer.Append(lpFilename, strlen(lpFilename)))
437 {
438 ERROR ("Append failed!\n");
439 palError = ERROR_INSUFFICIENT_BUFFER;
440
441 // Doing a goto here since we want to exit now. This will work
442 // incase someone else adds another if clause below us.
443 goto LExit;
444 }
445
446#endif // REALPATH_SUPPORTS_NONEXISTENT_FILES
447LExit:
448
449 if ((palError == NO_ERROR) && lpBuffer.IsEmpty())
450 {
451 // convert all these into ERROR_PATH_NOT_FOUND
452 palError = ERROR_PATH_NOT_FOUND;
453 }
454
455 return palError;
456}
457
458PAL_ERROR
459CorUnix::InternalCreateFile(
460 CPalThread *pThread,
461 LPCSTR lpFileName,
462 DWORD dwDesiredAccess,
463 DWORD dwShareMode,
464 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
465 DWORD dwCreationDisposition,
466 DWORD dwFlagsAndAttributes,
467 HANDLE hTemplateFile,
468 HANDLE *phFile
469 )
470{
471 PAL_ERROR palError = 0;
472 IPalObject *pFileObject = NULL;
473 IPalObject *pRegisteredFile = NULL;
474 IDataLock *pDataLock = NULL;
475 CFileProcessLocalData *pLocalData = NULL;
476 CObjectAttributes oaFile(NULL, lpSecurityAttributes);
477 BOOL fFileExists = FALSE;
478
479 BOOL inheritable = FALSE;
480 PathCharString lpUnixPath;
481 int filed = -1;
482 int create_flags = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
483 int open_flags = 0;
484
485 // track whether we've created the file with the intended name,
486 // so that it can be removed on failure exit
487 BOOL bFileCreated = FALSE;
488
489 const char* szNonfilePrefix = "\\\\.\\";
490 PathCharString lpFullUnixPath;
491
492 /* for dwShareMode only three flags are accepted */
493 if ( dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) )
494 {
495 ASSERT( "dwShareMode is invalid\n" );
496 palError = ERROR_INVALID_PARAMETER;
497 goto done;
498 }
499
500 if ( lpFileName == NULL )
501 {
502 ERROR("InternalCreateFile called with NULL filename\n");
503 palError = ERROR_PATH_NOT_FOUND;
504 goto done;
505 }
506
507 if ( strncmp(lpFileName, szNonfilePrefix, strlen(szNonfilePrefix)) == 0 )
508 {
509 ERROR("InternalCreateFile does not support paths beginning with %s\n", szNonfilePrefix);
510 palError = ERROR_INVALID_PARAMETER;
511 goto done;
512 }
513
514 if( !lpUnixPath.Set(lpFileName,strlen(lpFileName)))
515 {
516 ERROR("strdup() failed\n");
517 palError = ERROR_NOT_ENOUGH_MEMORY;
518 goto done;
519 }
520
521 FILEDosToUnixPathA( lpUnixPath );
522
523 // Compute the absolute pathname to the file. This pathname is used
524 // to determine if two file names represent the same file.
525 palError = InternalCanonicalizeRealPath(lpUnixPath, lpFullUnixPath);
526 if (palError != NO_ERROR)
527 {
528 goto done;
529 }
530
531 lpUnixPath.Set(lpFullUnixPath);
532
533 switch( dwDesiredAccess )
534 {
535 case 0:
536 /* Device Query Access was requested. let's use open() with
537 no flags, it's basically the equivalent of O_RDONLY, since
538 O_RDONLY is defined as 0x0000 */
539 break;
540 case( GENERIC_READ ):
541 open_flags |= O_RDONLY;
542 break;
543 case( GENERIC_WRITE ):
544 open_flags |= O_WRONLY;
545 break;
546 case( GENERIC_READ | GENERIC_WRITE ):
547 open_flags |= O_RDWR;
548 break;
549 default:
550 ERROR("dwDesiredAccess value of %d is invalid\n", dwDesiredAccess);
551 palError = ERROR_INVALID_PARAMETER;
552 goto done;
553 }
554
555 TRACE("open flags are 0x%lx\n", open_flags);
556
557 if ( lpSecurityAttributes )
558 {
559 if ( lpSecurityAttributes->nLength != sizeof( SECURITY_ATTRIBUTES ) ||
560 lpSecurityAttributes->lpSecurityDescriptor != NULL ||
561 !lpSecurityAttributes->bInheritHandle )
562 {
563 ASSERT("lpSecurityAttributes points to invalid values.\n");
564 palError = ERROR_INVALID_PARAMETER;
565 goto done;
566 }
567 inheritable = TRUE;
568 }
569
570 if ( (dwFlagsAndAttributes & PAL_LEGAL_FLAGS_ATTRIBS) !=
571 dwFlagsAndAttributes)
572 {
573 ASSERT("Bad dwFlagsAndAttributes\n");
574 palError = ERROR_INVALID_PARAMETER;
575 goto done;
576 }
577 else if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS)
578 {
579 /* Override the open flags, and always open as readonly. This
580 flag is used when opening a directory, to change its
581 creation/modification/access times. On Windows, the directory
582 must be open for write, but on Unix, it needs to be readonly. */
583 open_flags = O_RDONLY;
584 } else {
585 struct stat st;
586
587 if (stat(lpUnixPath, &st) == 0 && (st.st_mode & S_IFDIR))
588 {
589 /* The file exists and it is a directory. Without
590 FILE_FLAG_BACKUP_SEMANTICS, Win32 CreateFile always fails
591 to open directories. */
592 palError = ERROR_ACCESS_DENIED;
593 goto done;
594 }
595 }
596
597 if ( hTemplateFile )
598 {
599 ASSERT("hTemplateFile is not NULL, as it should be.\n");
600 palError = ERROR_INVALID_PARAMETER;
601 goto done;
602 }
603
604 /* NB: According to MSDN docs, When CREATE_ALWAYS or OPEN_ALWAYS is
605 set, CreateFile should SetLastError to ERROR_ALREADY_EXISTS,
606 even though/if CreateFile will be successful.
607 */
608 switch( dwCreationDisposition )
609 {
610 case( CREATE_ALWAYS ):
611 // check whether the file exists
612 if ( access( lpUnixPath, F_OK ) == 0 )
613 {
614 fFileExists = TRUE;
615 }
616 open_flags |= O_CREAT | O_TRUNC;
617 break;
618 case( CREATE_NEW ):
619 open_flags |= O_CREAT | O_EXCL;
620 break;
621 case( OPEN_EXISTING ):
622 /* don't need to do anything here */
623 break;
624 case( OPEN_ALWAYS ):
625 if ( access( lpUnixPath, F_OK ) == 0 )
626 {
627 fFileExists = TRUE;
628 }
629 open_flags |= O_CREAT;
630 break;
631 case( TRUNCATE_EXISTING ):
632 open_flags |= O_TRUNC;
633 break;
634 default:
635 ASSERT("dwCreationDisposition value of %d is not valid\n",
636 dwCreationDisposition);
637 palError = ERROR_INVALID_PARAMETER;
638 goto done;
639 }
640
641 if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING )
642 {
643 TRACE("I/O will be unbuffered\n");
644#ifdef O_DIRECT
645 open_flags |= O_DIRECT;
646#endif
647 }
648 else
649 {
650 TRACE("I/O will be buffered\n");
651 }
652
653 filed = InternalOpen(lpUnixPath, open_flags, create_flags);
654 TRACE("Allocated file descriptor [%d]\n", filed);
655
656 if ( filed < 0 )
657 {
658 TRACE("open() failed; error is %s (%d)\n", strerror(errno), errno);
659 palError = FILEGetLastErrorFromErrnoAndFilename(lpUnixPath);
660 goto done;
661 }
662
663 // Deduce whether we created a file in the previous operation (there's a
664 // small timing window between the access() used to determine fFileExists
665 // and the open() operation, but there's not much we can do about that.
666 bFileCreated = (dwCreationDisposition == CREATE_ALWAYS ||
667 dwCreationDisposition == CREATE_NEW ||
668 dwCreationDisposition == OPEN_ALWAYS) &&
669 !fFileExists;
670
671#ifndef O_DIRECT
672 if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING )
673 {
674#ifdef F_NOCACHE
675 if (-1 == fcntl(filed, F_NOCACHE, 1))
676 {
677 ASSERT("Can't set F_NOCACHE; fcntl() failed. errno is %d (%s)\n",
678 errno, strerror(errno));
679 palError = ERROR_INTERNAL_ERROR;
680 goto done;
681 }
682#else
683#error Insufficient support for uncached I/O on this platform
684#endif
685 }
686#endif
687
688 /* make file descriptor close-on-exec; inheritable handles will get
689 "uncloseonexeced" in CreateProcess if they are actually being inherited*/
690 if(-1 == fcntl(filed,F_SETFD, FD_CLOEXEC))
691 {
692 ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
693 "(%s)\n", errno, strerror(errno));
694 palError = ERROR_INTERNAL_ERROR;
695 goto done;
696 }
697
698 palError = g_pObjectManager->AllocateObject(
699 pThread,
700 &otFile,
701 &oaFile,
702 &pFileObject
703 );
704
705 if (NO_ERROR != palError)
706 {
707 goto done;
708 }
709
710 palError = pFileObject->GetProcessLocalData(
711 pThread,
712 WriteLock,
713 &pDataLock,
714 reinterpret_cast<void**>(&pLocalData)
715 );
716
717 if (NO_ERROR != palError)
718 {
719 goto done;
720 }
721
722 _ASSERTE(pLocalData->unix_filename == NULL);
723 pLocalData->unix_filename = strdup(lpUnixPath);
724 if (pLocalData->unix_filename == NULL)
725 {
726 ASSERT("Unable to copy string\n");
727 palError = ERROR_INTERNAL_ERROR;
728 goto done;
729 }
730
731 pLocalData->inheritable = inheritable;
732 pLocalData->unix_fd = filed;
733 pLocalData->dwDesiredAccess = dwDesiredAccess;
734 pLocalData->open_flags = open_flags;
735 pLocalData->open_flags_deviceaccessonly = (dwDesiredAccess == 0);
736
737 //
738 // We've finished initializing our local data, so release that lock
739 //
740
741 pDataLock->ReleaseLock(pThread, TRUE);
742 pDataLock = NULL;
743
744 palError = g_pObjectManager->RegisterObject(
745 pThread,
746 pFileObject,
747 &aotFile,
748 dwDesiredAccess,
749 phFile,
750 &pRegisteredFile
751 );
752
753 //
754 // pFileObject is invalidated by the call to RegisterObject, so NULL it
755 // out here to ensure that we don't try to release a reference on
756 // it down the line.
757 //
758
759 pFileObject = NULL;
760
761done:
762
763 // At this point, if we've been successful, palError will be NO_ERROR.
764 // CreateFile can return ERROR_ALREADY_EXISTS in some success cases;
765 // those cases are flagged by fFileExists and are handled below.
766 if (NO_ERROR != palError)
767 {
768 if (filed >= 0)
769 {
770 close(filed);
771 }
772 if (bFileCreated)
773 {
774 if (-1 == unlink(lpUnixPath))
775 {
776 WARN("can't delete file; unlink() failed with errno %d (%s)\n",
777 errno, strerror(errno));
778 }
779 }
780 }
781
782 if (NULL != pDataLock)
783 {
784 pDataLock->ReleaseLock(pThread, TRUE);
785 }
786
787 if (NULL != pFileObject)
788 {
789 pFileObject->ReleaseReference(pThread);
790 }
791
792 if (NULL != pRegisteredFile)
793 {
794 pRegisteredFile->ReleaseReference(pThread);
795 }
796
797 if (NO_ERROR == palError && fFileExists)
798 {
799 palError = ERROR_ALREADY_EXISTS;
800 }
801
802 return palError;
803}
804
805/*++
806Function:
807 CreateFileA
808
809Note:
810 Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct.
811 Desired access is READ, WRITE or 0
812 Share mode is READ, WRITE or DELETE
813
814See MSDN doc.
815--*/
816HANDLE
817PALAPI
818CreateFileA(
819 IN LPCSTR lpFileName,
820 IN DWORD dwDesiredAccess,
821 IN DWORD dwShareMode,
822 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
823 IN DWORD dwCreationDisposition,
824 IN DWORD dwFlagsAndAttributes,
825 IN HANDLE hTemplateFile
826 )
827{
828 CPalThread *pThread;
829 PAL_ERROR palError = NO_ERROR;
830 HANDLE hRet = INVALID_HANDLE_VALUE;
831
832 PERF_ENTRY(CreateFileA);
833 ENTRY("CreateFileA(lpFileName=%p (%s), dwAccess=%#x, dwShareMode=%#x, "
834 "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, "
835 "hTemplateFile=%p )\n",lpFileName?lpFileName:"NULL",lpFileName?lpFileName:"NULL", dwDesiredAccess,
836 dwShareMode, lpSecurityAttributes, dwCreationDisposition,
837 dwFlagsAndAttributes, hTemplateFile);
838
839 pThread = InternalGetCurrentThread();
840
841 palError = InternalCreateFile(
842 pThread,
843 lpFileName,
844 dwDesiredAccess,
845 dwShareMode,
846 lpSecurityAttributes,
847 dwCreationDisposition,
848 dwFlagsAndAttributes,
849 hTemplateFile,
850 &hRet
851 );
852
853 //
854 // We always need to set last error, even on success:
855 // we need to protect ourselves from the situation
856 // where last error is set to ERROR_ALREADY_EXISTS on
857 // entry to the function
858 //
859
860 pThread->SetLastError(palError);
861
862 LOGEXIT("CreateFileA returns HANDLE %p\n", hRet);
863 PERF_EXIT(CreateFileA);
864 return hRet;
865}
866
867
868/*++
869Function:
870 CreateFileW
871
872Note:
873 Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct.
874 Desired access is READ, WRITE or 0
875 Share mode is READ, WRITE or DELETE
876
877See MSDN doc.
878--*/
879HANDLE
880PALAPI
881CreateFileW(
882 IN LPCWSTR lpFileName,
883 IN DWORD dwDesiredAccess,
884 IN DWORD dwShareMode,
885 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
886 IN DWORD dwCreationDisposition,
887 IN DWORD dwFlagsAndAttributes,
888 IN HANDLE hTemplateFile)
889{
890 CPalThread *pThread;
891 PAL_ERROR palError = NO_ERROR;
892 PathCharString namePathString;
893 char * name;
894 int size;
895 int length = 0;
896 HANDLE hRet = INVALID_HANDLE_VALUE;
897
898 PERF_ENTRY(CreateFileW);
899 ENTRY("CreateFileW(lpFileName=%p (%S), dwAccess=%#x, dwShareMode=%#x, "
900 "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, hTemplateFile=%p )\n",
901 lpFileName?lpFileName:W16_NULLSTRING,
902 lpFileName?lpFileName:W16_NULLSTRING, dwDesiredAccess, dwShareMode,
903 lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
904 hTemplateFile);
905
906 pThread = InternalGetCurrentThread();
907
908 if (lpFileName != NULL)
909 {
910 length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
911 }
912
913 name = namePathString.OpenStringBuffer(length);
914 if (NULL == name)
915 {
916 palError = ERROR_NOT_ENOUGH_MEMORY;
917 goto done;
918 }
919
920 size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
921 NULL, NULL );
922
923 if( size == 0 )
924 {
925 namePathString.CloseBuffer(0);
926 DWORD dwLastError = GetLastError();
927 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
928 palError = ERROR_INTERNAL_ERROR;
929 goto done;
930 }
931
932 namePathString.CloseBuffer(size - 1);
933
934 palError = InternalCreateFile(
935 pThread,
936 name,
937 dwDesiredAccess,
938 dwShareMode,
939 lpSecurityAttributes,
940 dwCreationDisposition,
941 dwFlagsAndAttributes,
942 hTemplateFile,
943 &hRet
944 );
945
946 //
947 // We always need to set last error, even on success:
948 // we need to protect ourselves from the situation
949 // where last error is set to ERROR_ALREADY_EXISTS on
950 // entry to the function
951 //
952
953done:
954 pThread->SetLastError(palError);
955 LOGEXIT( "CreateFileW returns HANDLE %p\n", hRet );
956 PERF_EXIT(CreateFileW);
957 return hRet;
958}
959
960
961/*++
962Function:
963 CopyFileW
964
965See MSDN doc.
966
967Notes:
968 There are several (most) error paths here that do not call SetLastError().
969This is because we know that CreateFile, ReadFile, and WriteFile will do so,
970and will have a much better idea of the specific error.
971--*/
972BOOL
973PALAPI
974CopyFileW(
975 IN LPCWSTR lpExistingFileName,
976 IN LPCWSTR lpNewFileName,
977 IN BOOL bFailIfExists)
978{
979 CPalThread *pThread;
980 PathCharString sourcePathString;
981 PathCharString destPathString;
982 char * source;
983 char * dest;
984 int src_size, dest_size, length = 0;
985 BOOL bRet = FALSE;
986
987 PERF_ENTRY(CopyFileW);
988 ENTRY("CopyFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), bFailIfExists=%d)\n",
989 lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
990 lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
991 lpNewFileName?lpNewFileName:W16_NULLSTRING,
992 lpNewFileName?lpNewFileName:W16_NULLSTRING, bFailIfExists);
993
994 pThread = InternalGetCurrentThread();
995 if (lpExistingFileName != NULL)
996 {
997 length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor;
998 }
999
1000 source = sourcePathString.OpenStringBuffer(length);
1001 if (NULL == source)
1002 {
1003 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1004 goto done;
1005 }
1006
1007 src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length,
1008 NULL, NULL );
1009
1010 if( src_size == 0 )
1011 {
1012 sourcePathString.CloseBuffer(0);
1013 DWORD dwLastError = GetLastError();
1014 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1015 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1016 goto done;
1017 }
1018
1019 sourcePathString.CloseBuffer(src_size - 1);
1020 length = 0;
1021
1022 if (lpNewFileName != NULL)
1023 {
1024 length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor;
1025 }
1026
1027 dest = destPathString.OpenStringBuffer(length);
1028 if (NULL == dest)
1029 {
1030 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1031 goto done;
1032 }
1033 dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length,
1034 NULL, NULL );
1035
1036 if( dest_size == 0 )
1037 {
1038 destPathString.CloseBuffer(0);
1039 DWORD dwLastError = GetLastError();
1040 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1041 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1042 goto done;
1043 }
1044
1045 destPathString.CloseBuffer(dest_size - 1);
1046 bRet = CopyFileA(source,dest,bFailIfExists);
1047
1048done:
1049 LOGEXIT("CopyFileW returns BOOL %d\n", bRet);
1050 PERF_EXIT(CopyFileW);
1051 return bRet;
1052}
1053
1054
1055/*++
1056Function:
1057 DeleteFileA
1058
1059See MSDN doc.
1060--*/
1061BOOL
1062PALAPI
1063DeleteFileA(
1064 IN LPCSTR lpFileName)
1065{
1066 PAL_ERROR palError = NO_ERROR;
1067 CPalThread *pThread;
1068 int result;
1069 BOOL bRet = FALSE;
1070 DWORD dwLastError = 0;
1071 PathCharString lpunixFileName;
1072 PathCharString lpFullunixFileName;
1073
1074 PERF_ENTRY(DeleteFileA);
1075 ENTRY("DeleteFileA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL");
1076
1077 pThread = InternalGetCurrentThread();
1078
1079 if( !lpunixFileName.Set(lpFileName, strlen(lpFileName)))
1080 {
1081 palError = ERROR_NOT_ENOUGH_MEMORY;
1082 goto done;
1083 }
1084
1085 FILEDosToUnixPathA( lpunixFileName );
1086
1087 // Compute the absolute pathname to the file. This pathname is used
1088 // to determine if two file names represent the same file.
1089 palError = InternalCanonicalizeRealPath(lpunixFileName, lpFullunixFileName);
1090 if (palError != NO_ERROR)
1091 {
1092 if (!lpFullunixFileName.Set(lpunixFileName, strlen(lpunixFileName)))
1093 {
1094 palError = ERROR_NOT_ENOUGH_MEMORY;
1095 goto done;
1096 }
1097 }
1098
1099 result = unlink( lpFullunixFileName );
1100
1101 if (result < 0)
1102 {
1103 TRACE("unlink returns %d\n", result);
1104 dwLastError = FILEGetLastErrorFromErrnoAndFilename(lpFullunixFileName);
1105 }
1106 else
1107 {
1108 bRet = TRUE;
1109 }
1110
1111done:
1112 if(dwLastError)
1113 {
1114 pThread->SetLastError( dwLastError );
1115 }
1116
1117 LOGEXIT("DeleteFileA returns BOOL %d\n", bRet);
1118 PERF_EXIT(DeleteFileA);
1119 return bRet;
1120}
1121
1122/*++
1123Function:
1124 DeleteFileW
1125
1126See MSDN doc.
1127--*/
1128BOOL
1129PALAPI
1130DeleteFileW(
1131 IN LPCWSTR lpFileName)
1132{
1133 CPalThread *pThread;
1134 int size;
1135 PathCharString namePS;
1136 char * name;
1137 int length = 0;
1138 BOOL bRet = FALSE;
1139
1140 PERF_ENTRY(DeleteFileW);
1141 ENTRY("DeleteFileW(lpFileName=%p (%S))\n",
1142 lpFileName?lpFileName:W16_NULLSTRING,
1143 lpFileName?lpFileName:W16_NULLSTRING);
1144
1145 pThread = InternalGetCurrentThread();
1146
1147 if (lpFileName != NULL)
1148 {
1149 length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
1150 }
1151
1152 name = namePS.OpenStringBuffer(length);
1153 if (NULL == name)
1154 {
1155 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1156 goto done;
1157 }
1158
1159 size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
1160 NULL, NULL );
1161
1162 if( size == 0 )
1163 {
1164 namePS.CloseBuffer(0);
1165 DWORD dwLastError = GetLastError();
1166 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1167 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1168 bRet = FALSE;
1169 goto done;
1170 }
1171
1172 namePS.CloseBuffer(size - 1);
1173 bRet = DeleteFileA( name );
1174
1175done:
1176 LOGEXIT("DeleteFileW returns BOOL %d\n", bRet);
1177 PERF_EXIT(DeleteFileW);
1178 return bRet;
1179}
1180
1181
1182/*++
1183Function:
1184 MoveFileExA
1185
1186See MSDN doc.
1187--*/
1188BOOL
1189PALAPI
1190MoveFileExA(
1191 IN LPCSTR lpExistingFileName,
1192 IN LPCSTR lpNewFileName,
1193 IN DWORD dwFlags)
1194{
1195 CPalThread *pThread;
1196 int result;
1197 PathCharString source;
1198 PathCharString dest;
1199 BOOL bRet = TRUE;
1200 DWORD dwLastError = 0;
1201
1202 PERF_ENTRY(MoveFileExA);
1203 ENTRY("MoveFileExA(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), "
1204 "dwFlags=%#x)\n",
1205 lpExistingFileName?lpExistingFileName:"NULL",
1206 lpExistingFileName?lpExistingFileName:"NULL",
1207 lpNewFileName?lpNewFileName:"NULL",
1208 lpNewFileName?lpNewFileName:"NULL", dwFlags);
1209
1210 pThread = InternalGetCurrentThread();
1211 /* only two flags are accepted */
1212 if ( dwFlags & ~(MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) )
1213 {
1214 ASSERT( "dwFlags is invalid\n" );
1215 dwLastError = ERROR_INVALID_PARAMETER;
1216 goto done;
1217 }
1218
1219
1220 if( !source.Set(lpExistingFileName, strlen(lpExistingFileName)))
1221 {
1222 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
1223 goto done;
1224 }
1225
1226 FILEDosToUnixPathA( source );
1227
1228 if( !dest.Set(lpNewFileName, strlen(lpNewFileName)))
1229 {
1230 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
1231 goto done;
1232 }
1233
1234 FILEDosToUnixPathA( dest );
1235
1236 if ( !(dwFlags & MOVEFILE_REPLACE_EXISTING) )
1237 {
1238#if HAVE_CASE_SENSITIVE_FILESYSTEM
1239 if ( strcmp(source, dest) != 0 )
1240#else // HAVE_CASE_SENSITIVE_FILESYSTEM
1241 if ( strcasecmp(source, dest) != 0 )
1242#endif // HAVE_CASE_SENSITIVE_FILESYSTEM
1243 {
1244 // Let things proceed normally if source and
1245 // dest are the same.
1246 if ( access(dest, F_OK) == 0 )
1247 {
1248 dwLastError = ERROR_ALREADY_EXISTS;
1249 goto done;
1250 }
1251 }
1252 }
1253
1254 result = rename( source, dest );
1255 if ((result < 0) && (dwFlags & MOVEFILE_REPLACE_EXISTING) &&
1256 ((errno == ENOTDIR) || (errno == EEXIST)))
1257 {
1258 bRet = DeleteFileA( lpNewFileName );
1259
1260 if ( bRet )
1261 {
1262 result = rename( source, dest );
1263 }
1264 else
1265 {
1266 dwLastError = GetLastError();
1267 }
1268 }
1269
1270 if ( result < 0 )
1271 {
1272 switch( errno )
1273 {
1274 case EXDEV: /* we tried to link across devices */
1275
1276 if ( dwFlags & MOVEFILE_COPY_ALLOWED )
1277 {
1278 BOOL bFailIfExists = !(dwFlags & MOVEFILE_REPLACE_EXISTING);
1279
1280 /* if CopyFile fails here, so should MoveFailEx */
1281 bRet = CopyFileA( lpExistingFileName,
1282 lpNewFileName,
1283 bFailIfExists );
1284 /* CopyFile should set the appropriate error */
1285 if ( !bRet )
1286 {
1287 dwLastError = GetLastError();
1288 }
1289 else
1290 {
1291 if (!DeleteFileA(lpExistingFileName))
1292 {
1293 ERROR("Failed to delete the source file\n");
1294 dwLastError = GetLastError();
1295
1296 /* Delete the destination file if we're unable to delete
1297 the source file */
1298 if (!DeleteFileA(lpNewFileName))
1299 {
1300 ERROR("Failed to delete the destination file\n");
1301 }
1302 }
1303 }
1304 }
1305 else
1306 {
1307 dwLastError = ERROR_ACCESS_DENIED;
1308 }
1309 break;
1310 case EINVAL: // tried to rename "." or ".."
1311 dwLastError = ERROR_SHARING_VIOLATION;
1312 break;
1313 case ENOENT:
1314 {
1315 struct stat buf;
1316 if (lstat(source, &buf) == -1)
1317 {
1318 FILEGetProperNotFoundError(source, &dwLastError);
1319 }
1320 else
1321 {
1322 dwLastError = ERROR_PATH_NOT_FOUND;
1323 }
1324 }
1325 break;
1326 default:
1327 dwLastError = FILEGetLastErrorFromErrno();
1328 break;
1329 }
1330 }
1331
1332done:
1333 if ( dwLastError )
1334 {
1335 pThread->SetLastError( dwLastError );
1336 bRet = FALSE;
1337 }
1338
1339 LOGEXIT( "MoveFileExA returns BOOL %d\n", bRet );
1340 PERF_EXIT(MoveFileExA);
1341 return bRet;
1342}
1343
1344/*++
1345Function:
1346 MoveFileExW
1347
1348See MSDN doc.
1349--*/
1350BOOL
1351PALAPI
1352MoveFileExW(
1353 IN LPCWSTR lpExistingFileName,
1354 IN LPCWSTR lpNewFileName,
1355 IN DWORD dwFlags)
1356{
1357 CPalThread *pThread;
1358 PathCharString sourcePS;
1359 PathCharString destPS;
1360 char * source;
1361 char * dest;
1362 int length = 0;
1363 int src_size,dest_size;
1364 BOOL bRet = FALSE;
1365
1366 PERF_ENTRY(MoveFileExW);
1367 ENTRY("MoveFileExW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), dwFlags=%#x)\n",
1368 lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
1369 lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
1370 lpNewFileName?lpNewFileName:W16_NULLSTRING,
1371 lpNewFileName?lpNewFileName:W16_NULLSTRING, dwFlags);
1372
1373 pThread = InternalGetCurrentThread();
1374
1375 if (lpExistingFileName != NULL)
1376 {
1377 length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor;
1378 }
1379
1380 source = sourcePS.OpenStringBuffer(length);
1381 if (NULL == source)
1382 {
1383 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1384 goto done;
1385 }
1386 src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length,
1387 NULL, NULL );
1388 if( src_size == 0 )
1389 {
1390 sourcePS.CloseBuffer(0);
1391 DWORD dwLastError = GetLastError();
1392 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1393 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1394 goto done;
1395 }
1396
1397 sourcePS.CloseBuffer(src_size - 1);
1398 length = 0;
1399 if (lpNewFileName != NULL)
1400 {
1401 length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor;
1402 }
1403
1404 dest = destPS.OpenStringBuffer(length);
1405 if (NULL == dest)
1406 {
1407 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1408 goto done;
1409 }
1410 dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length,
1411 NULL, NULL );
1412
1413 if( dest_size == 0 )
1414 {
1415 destPS.CloseBuffer(0);
1416 DWORD dwLastError = GetLastError();
1417 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1418 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1419 goto done;
1420 }
1421
1422 destPS.CloseBuffer(dest_size - 1);
1423 bRet = MoveFileExA(source,dest,dwFlags);
1424
1425done:
1426 LOGEXIT("MoveFileExW returns BOOL %d\n", bRet);
1427 PERF_EXIT(MoveFileExW);
1428 return bRet;
1429}
1430
1431/*++
1432Function:
1433 GetFileAttributesA
1434
1435Note:
1436 Checking for directory and read-only file.
1437
1438Caveats:
1439 There are some important things to note about this implementation, which
1440are due to the differences between the FAT filesystem and Unix filesystems:
1441
1442- fifo's, sockets, and symlinks will return -1, and GetLastError() will
1443 return ERROR_ACCESS_DENIED
1444
1445- if a file is write-only, or has no permissions at all, it is treated
1446 the same as if it had mode 'rw'. This is consistent with behaviour on
1447 NTFS files with the same permissions.
1448
1449- the following flags will never be returned:
1450
1451FILE_ATTRIBUTE_SYSTEM
1452FILE_ATTRIBUTE_ARCHIVE
1453FILE_ATTRIBUTE_HIDDEN
1454
1455--*/
1456DWORD
1457PALAPI
1458GetFileAttributesA(
1459 IN LPCSTR lpFileName)
1460{
1461 CPalThread *pThread;
1462 struct stat stat_data;
1463 DWORD dwAttr = 0;
1464 DWORD dwLastError = 0;
1465 PathCharString unixFileName;
1466
1467 PERF_ENTRY(GetFileAttributesA);
1468 ENTRY("GetFileAttributesA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL");
1469
1470 pThread = InternalGetCurrentThread();
1471 if (lpFileName == NULL)
1472 {
1473 dwLastError = ERROR_PATH_NOT_FOUND;
1474 goto done;
1475 }
1476
1477
1478 if( !unixFileName.Set(lpFileName, strlen(lpFileName)))
1479 {
1480 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
1481 goto done;
1482 }
1483
1484 FILEDosToUnixPathA( unixFileName );
1485
1486 if ( stat(unixFileName, &stat_data) != 0 )
1487 {
1488 dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
1489 goto done;
1490 }
1491
1492 if ( (stat_data.st_mode & S_IFMT) == S_IFDIR )
1493 {
1494 dwAttr |= FILE_ATTRIBUTE_DIRECTORY;
1495 }
1496 else if ( (stat_data.st_mode & S_IFMT) != S_IFREG )
1497 {
1498 ERROR("Not a regular file or directory, S_IFMT is %#x\n",
1499 stat_data.st_mode & S_IFMT);
1500 dwLastError = ERROR_ACCESS_DENIED;
1501 goto done;
1502 }
1503
1504 if ( UTIL_IsReadOnlyBitsSet( &stat_data ) )
1505 {
1506 dwAttr |= FILE_ATTRIBUTE_READONLY;
1507 }
1508
1509 /* finally, if nothing is set... */
1510 if ( dwAttr == 0 )
1511 {
1512 dwAttr = FILE_ATTRIBUTE_NORMAL;
1513 }
1514
1515done:
1516 if (dwLastError)
1517 {
1518 pThread->SetLastError(dwLastError);
1519 dwAttr = INVALID_FILE_ATTRIBUTES;
1520 }
1521
1522 LOGEXIT("GetFileAttributesA returns DWORD %#x\n", dwAttr);
1523 PERF_EXIT(GetFileAttributesA);
1524 return dwAttr;
1525}
1526
1527
1528
1529
1530/*++
1531Function:
1532 GetFileAttributesW
1533
1534Note:
1535 Checking for directory and read-only file
1536
1537See MSDN doc.
1538--*/
1539DWORD
1540PALAPI
1541GetFileAttributesW(
1542 IN LPCWSTR lpFileName)
1543{
1544 CPalThread *pThread;
1545 int size;
1546 PathCharString filenamePS;
1547 int length = 0;
1548 char * filename;
1549 DWORD dwRet = (DWORD) -1;
1550
1551 PERF_ENTRY(GetFileAttributesW);
1552 ENTRY("GetFileAttributesW(lpFileName=%p (%S))\n",
1553 lpFileName?lpFileName:W16_NULLSTRING,
1554 lpFileName?lpFileName:W16_NULLSTRING);
1555
1556 pThread = InternalGetCurrentThread();
1557 if (lpFileName == NULL)
1558 {
1559 pThread->SetLastError(ERROR_PATH_NOT_FOUND);
1560 goto done;
1561 }
1562
1563 length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
1564 filename = filenamePS.OpenStringBuffer(length);
1565 if (NULL == filename)
1566 {
1567 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1568 goto done;
1569 }
1570 size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, filename, length,
1571 NULL, NULL );
1572
1573 if( size == 0 )
1574 {
1575 filenamePS.CloseBuffer(0);
1576 DWORD dwLastError = GetLastError();
1577 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1578 pThread->SetLastError(ERROR_INTERNAL_ERROR);
1579 }
1580 else
1581 {
1582 filenamePS.CloseBuffer(size - 1);
1583 dwRet = GetFileAttributesA( filename );
1584 }
1585
1586done:
1587 LOGEXIT("GetFileAttributesW returns DWORD %#x\n", dwRet);
1588 PERF_EXIT(GetFileAttributesW);
1589 return dwRet;
1590}
1591
1592
1593/*++
1594Function:
1595 GetFileAttributesExW
1596
1597See MSDN doc, and notes for GetFileAttributesW.
1598--*/
1599BOOL
1600PALAPI
1601GetFileAttributesExW(
1602 IN LPCWSTR lpFileName,
1603 IN GET_FILEEX_INFO_LEVELS fInfoLevelId,
1604 OUT LPVOID lpFileInformation)
1605{
1606 CPalThread *pThread;
1607 BOOL bRet = FALSE;
1608 DWORD dwLastError = 0;
1609 LPWIN32_FILE_ATTRIBUTE_DATA attr_data;
1610
1611 struct stat stat_data;
1612
1613 char * name;
1614 PathCharString namePS;
1615 int length = 0;
1616 int size;
1617
1618 PERF_ENTRY(GetFileAttributesExW);
1619 ENTRY("GetFileAttributesExW(lpFileName=%p (%S), fInfoLevelId=%d, "
1620 "lpFileInformation=%p)\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING,
1621 fInfoLevelId, lpFileInformation);
1622
1623 pThread = InternalGetCurrentThread();
1624 if ( fInfoLevelId != GetFileExInfoStandard )
1625 {
1626 ASSERT("Unrecognized value for fInfoLevelId=%d\n", fInfoLevelId);
1627 dwLastError = ERROR_INVALID_PARAMETER;
1628 goto done;
1629 }
1630
1631 if ( !lpFileInformation )
1632 {
1633 ASSERT("lpFileInformation is NULL\n");
1634 dwLastError = ERROR_INVALID_PARAMETER;
1635 goto done;
1636 }
1637
1638 if (lpFileName == NULL)
1639 {
1640 dwLastError = ERROR_PATH_NOT_FOUND;
1641 goto done;
1642 }
1643
1644 length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
1645 name = namePS.OpenStringBuffer(length);
1646 if (NULL == name)
1647 {
1648 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
1649 goto done;
1650 }
1651 size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
1652 NULL, NULL );
1653
1654 if( size == 0 )
1655 {
1656 namePS.CloseBuffer(0);
1657 dwLastError = GetLastError();
1658 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1659 dwLastError = ERROR_INTERNAL_ERROR;
1660 goto done;
1661 }
1662
1663 namePS.CloseBuffer(size - 1);
1664 attr_data = (LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation;
1665
1666 attr_data->dwFileAttributes = GetFileAttributesW(lpFileName);
1667 /* assume that GetFileAttributes will call SetLastError appropriately */
1668 if ( attr_data->dwFileAttributes == (DWORD)-1 )
1669 {
1670 goto done;
1671 }
1672
1673 FILEDosToUnixPathA(name);
1674 /* do the stat */
1675 if ( stat(name, &stat_data) != 0 )
1676 {
1677 ERROR("stat failed on %S\n", lpFileName);
1678 dwLastError = FILEGetLastErrorFromErrnoAndFilename(name);
1679 goto done;
1680 }
1681
1682 /* get the file times */
1683 attr_data->ftCreationTime =
1684 FILEUnixTimeToFileTime( stat_data.st_ctime,
1685 ST_CTIME_NSEC(&stat_data) );
1686 attr_data->ftLastAccessTime =
1687 FILEUnixTimeToFileTime( stat_data.st_atime,
1688 ST_ATIME_NSEC(&stat_data) );
1689 attr_data->ftLastWriteTime =
1690 FILEUnixTimeToFileTime( stat_data.st_mtime,
1691 ST_MTIME_NSEC(&stat_data) );
1692
1693 /* if Unix mtime is greater than atime, return mtime
1694 as the last access time */
1695 if (CompareFileTime(&attr_data->ftLastAccessTime,
1696 &attr_data->ftLastWriteTime) < 0)
1697 {
1698 attr_data->ftLastAccessTime = attr_data->ftLastWriteTime;
1699 }
1700
1701 /* if Unix ctime is greater than mtime, return mtime
1702 as the create time */
1703 if (CompareFileTime(&attr_data->ftLastWriteTime,
1704 &attr_data->ftCreationTime) < 0)
1705 {
1706 attr_data->ftCreationTime = attr_data->ftLastWriteTime;
1707 }
1708
1709 /* Get the file size. GetFileSize is not used because it gets the
1710 size of an already-open file */
1711 attr_data->nFileSizeLow = (DWORD) stat_data.st_size;
1712#if SIZEOF_OFF_T > 4
1713 attr_data->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
1714#else
1715 attr_data->nFileSizeHigh = 0;
1716#endif
1717
1718 bRet = TRUE;
1719
1720done:
1721 if (dwLastError) pThread->SetLastError(dwLastError);
1722
1723 LOGEXIT("GetFileAttributesExW returns BOOL %d\n", bRet);
1724 PERF_EXIT(GetFileAttributesExW);
1725 return bRet;
1726}
1727
1728/*++
1729Function:
1730 SetFileAttributesW
1731
1732Notes:
1733 Used for setting read-only attribute on file only.
1734
1735--*/
1736BOOL
1737PALAPI
1738SetFileAttributesW(
1739 IN LPCWSTR lpFileName,
1740 IN DWORD dwFileAttributes)
1741{
1742 CPalThread *pThread;
1743 char * name;
1744 PathCharString namePS;
1745 int length = 0;
1746 int size;
1747
1748 DWORD dwLastError = 0;
1749 BOOL bRet = FALSE;
1750
1751 PERF_ENTRY(SetFileAttributesW);
1752 ENTRY("SetFileAttributesW(lpFileName=%p (%S), dwFileAttributes=%#x)\n",
1753 lpFileName?lpFileName:W16_NULLSTRING,
1754 lpFileName?lpFileName:W16_NULLSTRING, dwFileAttributes);
1755
1756 pThread = InternalGetCurrentThread();
1757 if (lpFileName == NULL)
1758 {
1759 dwLastError = ERROR_PATH_NOT_FOUND;
1760 goto done;
1761 }
1762
1763 length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor;
1764 name = namePS.OpenStringBuffer(length);
1765 if (NULL == name)
1766 {
1767 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
1768 goto done;
1769 }
1770 size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length,
1771 NULL, NULL );
1772
1773 if( size == 0 )
1774 {
1775 namePS.CloseBuffer(0);
1776 dwLastError = GetLastError();
1777 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
1778 dwLastError = ERROR_INVALID_PARAMETER;
1779 goto done;
1780 }
1781 namePS.CloseBuffer(size - 1);
1782 bRet = SetFileAttributesA(name,dwFileAttributes);
1783
1784done:
1785 if (dwLastError) pThread->SetLastError(dwLastError);
1786
1787 LOGEXIT("SetFileAttributes returns BOOL %d\n", bRet);
1788 PERF_EXIT(SetFileAttributesW);
1789 return bRet;
1790}
1791
1792PAL_ERROR
1793CorUnix::InternalWriteFile(
1794 CPalThread *pThread,
1795 HANDLE hFile,
1796 LPCVOID lpBuffer,
1797 DWORD nNumberOfBytesToWrite,
1798 LPDWORD lpNumberOfBytesWritten,
1799 LPOVERLAPPED lpOverlapped
1800 )
1801{
1802 PAL_ERROR palError = 0;
1803 IPalObject *pFileObject = NULL;
1804 CFileProcessLocalData *pLocalData = NULL;
1805 IDataLock *pLocalDataLock = NULL;
1806 int ifd;
1807
1808 LONG writeOffsetStartLow = 0, writeOffsetStartHigh = 0;
1809 int res;
1810
1811 if (NULL != lpNumberOfBytesWritten)
1812 {
1813 //
1814 // This must be set to 0 before any other error checking takes
1815 // place, per MSDN
1816 //
1817
1818 *lpNumberOfBytesWritten = 0;
1819 }
1820 else
1821 {
1822 ASSERT( "lpNumberOfBytesWritten is NULL\n" );
1823 palError = ERROR_INVALID_PARAMETER;
1824 goto done;
1825 }
1826
1827 // Win32 WriteFile disallows writing to STD_INPUT_HANDLE
1828 if (hFile == INVALID_HANDLE_VALUE || hFile == pStdIn)
1829 {
1830 palError = ERROR_INVALID_HANDLE;
1831 goto done;
1832 }
1833 else if ( lpOverlapped )
1834 {
1835 ASSERT( "lpOverlapped is not NULL, as it should be.\n" );
1836 palError = ERROR_INVALID_PARAMETER;
1837 goto done;
1838 }
1839
1840 palError = g_pObjectManager->ReferenceObjectByHandle(
1841 pThread,
1842 hFile,
1843 &aotFile,
1844 GENERIC_WRITE,
1845 &pFileObject
1846 );
1847
1848 if (NO_ERROR != palError)
1849 {
1850 goto done;
1851 }
1852
1853 palError = pFileObject->GetProcessLocalData(
1854 pThread,
1855 ReadLock,
1856 &pLocalDataLock,
1857 reinterpret_cast<void**>(&pLocalData)
1858 );
1859
1860 if (NO_ERROR != palError)
1861 {
1862 goto done;
1863 }
1864
1865 if (pLocalData->open_flags_deviceaccessonly == TRUE)
1866 {
1867 ERROR("File open for device access only\n");
1868 palError = ERROR_ACCESS_DENIED;
1869 goto done;
1870 }
1871
1872 ifd = pLocalData->unix_fd;
1873
1874 //
1875 // Release the data lock before performing the (possibly blocking)
1876 // write call
1877 //
1878
1879 pLocalDataLock->ReleaseLock(pThread, FALSE);
1880 pLocalDataLock = NULL;
1881 pLocalData = NULL;
1882
1883#if WRITE_0_BYTES_HANGS_TTY
1884 if( nNumberOfBytesToWrite == 0 && isatty(ifd) )
1885 {
1886 res = 0;
1887 *lpNumberOfBytesWritten = 0;
1888 goto done;
1889 }
1890#endif
1891
1892 res = write( ifd, lpBuffer, nNumberOfBytesToWrite );
1893 TRACE("write() returns %d\n", res);
1894
1895 if ( res >= 0 )
1896 {
1897 *lpNumberOfBytesWritten = res;
1898 }
1899 else
1900 {
1901 palError = FILEGetLastErrorFromErrno();
1902 }
1903
1904done:
1905
1906 if (NULL != pLocalDataLock)
1907 {
1908 pLocalDataLock->ReleaseLock(pThread, FALSE);
1909 }
1910
1911 if (NULL != pFileObject)
1912 {
1913 pFileObject->ReleaseReference(pThread);
1914 }
1915
1916 return palError;
1917}
1918
1919
1920/*++
1921Function:
1922 WriteFileW
1923
1924Note:
1925 lpOverlapped always NULL.
1926
1927See MSDN doc.
1928--*/
1929BOOL
1930PALAPI
1931WriteFile(
1932 IN HANDLE hFile,
1933 IN LPCVOID lpBuffer,
1934 IN DWORD nNumberOfBytesToWrite,
1935 OUT LPDWORD lpNumberOfBytesWritten,
1936 IN LPOVERLAPPED lpOverlapped)
1937{
1938 PAL_ERROR palError;
1939 CPalThread *pThread;
1940
1941 PERF_ENTRY(WriteFile);
1942 ENTRY("WriteFile(hFile=%p, lpBuffer=%p, nToWrite=%u, lpWritten=%p, "
1943 "lpOverlapped=%p)\n", hFile, lpBuffer, nNumberOfBytesToWrite,
1944 lpNumberOfBytesWritten, lpOverlapped);
1945
1946 pThread = InternalGetCurrentThread();
1947
1948 palError = InternalWriteFile(
1949 pThread,
1950 hFile,
1951 lpBuffer,
1952 nNumberOfBytesToWrite,
1953 lpNumberOfBytesWritten,
1954 lpOverlapped
1955 );
1956
1957 if (NO_ERROR != palError)
1958 {
1959 pThread->SetLastError(palError);
1960 }
1961
1962 LOGEXIT("WriteFile returns BOOL %d\n", NO_ERROR == palError);
1963 PERF_EXIT(WriteFile);
1964 return NO_ERROR == palError;
1965}
1966
1967PAL_ERROR
1968CorUnix::InternalReadFile(
1969 CPalThread *pThread,
1970 HANDLE hFile,
1971 LPVOID lpBuffer,
1972 DWORD nNumberOfBytesToRead,
1973 LPDWORD lpNumberOfBytesRead,
1974 LPOVERLAPPED lpOverlapped
1975 )
1976{
1977 PAL_ERROR palError = 0;
1978 IPalObject *pFileObject = NULL;
1979 CFileProcessLocalData *pLocalData = NULL;
1980 IDataLock *pLocalDataLock = NULL;
1981 int ifd;
1982
1983 LONG readOffsetStartLow = 0, readOffsetStartHigh = 0;
1984 int res;
1985
1986 if (NULL != lpNumberOfBytesRead)
1987 {
1988 //
1989 // This must be set to 0 before any other error checking takes
1990 // place, per MSDN
1991 //
1992
1993 *lpNumberOfBytesRead = 0;
1994 }
1995 else
1996 {
1997 ERROR( "lpNumberOfBytesRead is NULL\n" );
1998 palError = ERROR_INVALID_PARAMETER;
1999 goto done;
2000 }
2001
2002 if (INVALID_HANDLE_VALUE == hFile)
2003 {
2004 ERROR( "Invalid file handle\n" );
2005 palError = ERROR_INVALID_HANDLE;
2006 goto done;
2007 }
2008 else if (NULL != lpOverlapped)
2009 {
2010 ASSERT( "lpOverlapped is not NULL, as it should be.\n" );
2011 palError = ERROR_INVALID_PARAMETER;
2012 goto done;
2013 }
2014 else if (NULL == lpBuffer)
2015 {
2016 ERROR( "Invalid parameter. (lpBuffer:%p)\n", lpBuffer);
2017 palError = ERROR_NOACCESS;
2018 goto done;
2019 }
2020
2021 palError = g_pObjectManager->ReferenceObjectByHandle(
2022 pThread,
2023 hFile,
2024 &aotFile,
2025 GENERIC_READ,
2026 &pFileObject
2027 );
2028
2029 if (NO_ERROR != palError)
2030 {
2031 goto done;
2032 }
2033
2034 palError = pFileObject->GetProcessLocalData(
2035 pThread,
2036 ReadLock,
2037 &pLocalDataLock,
2038 reinterpret_cast<void**>(&pLocalData)
2039 );
2040
2041 if (NO_ERROR != palError)
2042 {
2043 goto done;
2044 }
2045
2046 if (pLocalData->open_flags_deviceaccessonly == TRUE)
2047 {
2048 ERROR("File open for device access only\n");
2049 palError = ERROR_ACCESS_DENIED;
2050 goto done;
2051 }
2052
2053 ifd = pLocalData->unix_fd;
2054
2055 //
2056 // Release the data lock before performing the (possibly blocking)
2057 // read call
2058 //
2059
2060 pLocalDataLock->ReleaseLock(pThread, FALSE);
2061 pLocalDataLock = NULL;
2062 pLocalData = NULL;
2063
2064Read:
2065 TRACE("Reading from file descriptor %d\n", ifd);
2066 res = read(ifd, lpBuffer, nNumberOfBytesToRead);
2067 TRACE("read() returns %d\n", res);
2068
2069 if (res >= 0)
2070 {
2071 *lpNumberOfBytesRead = res;
2072 }
2073 else if (errno == EINTR)
2074 {
2075 // Try to read again.
2076 goto Read;
2077 }
2078 else
2079 {
2080 palError = FILEGetLastErrorFromErrno();
2081 }
2082
2083done:
2084
2085 if (NULL != pLocalDataLock)
2086 {
2087 pLocalDataLock->ReleaseLock(pThread, FALSE);
2088 }
2089
2090 if (NULL != pFileObject)
2091 {
2092 pFileObject->ReleaseReference(pThread);
2093 }
2094
2095 return palError;
2096}
2097
2098/*++
2099Function:
2100 ReadFile
2101
2102Note:
2103 lpOverlapped always NULL.
2104
2105See MSDN doc.
2106--*/
2107BOOL
2108PALAPI
2109ReadFile(
2110 IN HANDLE hFile,
2111 OUT LPVOID lpBuffer,
2112 IN DWORD nNumberOfBytesToRead,
2113 OUT LPDWORD lpNumberOfBytesRead,
2114 IN LPOVERLAPPED lpOverlapped)
2115{
2116 PAL_ERROR palError;
2117 CPalThread *pThread;
2118
2119 PERF_ENTRY(ReadFile);
2120 ENTRY("ReadFile(hFile=%p, lpBuffer=%p, nToRead=%u, "
2121 "lpRead=%p, lpOverlapped=%p)\n",
2122 hFile, lpBuffer, nNumberOfBytesToRead,
2123 lpNumberOfBytesRead, lpOverlapped);
2124
2125 pThread = InternalGetCurrentThread();
2126
2127 palError = InternalReadFile(
2128 pThread,
2129 hFile,
2130 lpBuffer,
2131 nNumberOfBytesToRead,
2132 lpNumberOfBytesRead,
2133 lpOverlapped
2134 );
2135
2136 if (NO_ERROR != palError)
2137 {
2138 pThread->SetLastError(palError);
2139 }
2140
2141 LOGEXIT("ReadFile returns BOOL %d\n", NO_ERROR == palError);
2142 PERF_EXIT(ReadFile);
2143 return NO_ERROR == palError;
2144}
2145
2146
2147/*++
2148Function:
2149 GetStdHandle
2150
2151See MSDN doc.
2152--*/
2153HANDLE
2154PALAPI
2155GetStdHandle(
2156 IN DWORD nStdHandle)
2157{
2158 CPalThread *pThread;
2159 HANDLE hRet = INVALID_HANDLE_VALUE;
2160
2161 PERF_ENTRY(GetStdHandle);
2162 ENTRY("GetStdHandle(nStdHandle=%#x)\n", nStdHandle);
2163
2164 pThread = InternalGetCurrentThread();
2165 switch( nStdHandle )
2166 {
2167 case STD_INPUT_HANDLE:
2168 hRet = pStdIn;
2169 break;
2170 case STD_OUTPUT_HANDLE:
2171 hRet = pStdOut;
2172 break;
2173 case STD_ERROR_HANDLE:
2174 hRet = pStdErr;
2175 break;
2176 default:
2177 ERROR("nStdHandle is invalid\n");
2178 pThread->SetLastError(ERROR_INVALID_PARAMETER);
2179 break;
2180 }
2181
2182 LOGEXIT("GetStdHandle returns HANDLE %p\n", hRet);
2183 PERF_EXIT(GetStdHandle);
2184 return hRet;
2185}
2186
2187PAL_ERROR
2188CorUnix::InternalSetEndOfFile(
2189 CPalThread *pThread,
2190 HANDLE hFile
2191 )
2192{
2193 PAL_ERROR palError = 0;
2194 IPalObject *pFileObject = NULL;
2195 CFileProcessLocalData *pLocalData = NULL;
2196 IDataLock *pLocalDataLock = NULL;
2197
2198 off_t curr = 0;
2199
2200 if (INVALID_HANDLE_VALUE == hFile)
2201 {
2202 ERROR( "Invalid file handle\n" );
2203 palError = ERROR_INVALID_HANDLE;
2204 goto InternalSetEndOfFileExit;
2205 }
2206
2207 palError = g_pObjectManager->ReferenceObjectByHandle(
2208 pThread,
2209 hFile,
2210 &aotFile,
2211 GENERIC_WRITE,
2212 &pFileObject
2213 );
2214
2215 if (NO_ERROR != palError)
2216 {
2217 goto InternalSetEndOfFileExit;
2218 }
2219
2220 palError = pFileObject->GetProcessLocalData(
2221 pThread,
2222 ReadLock,
2223 &pLocalDataLock,
2224 reinterpret_cast<void**>(&pLocalData)
2225 );
2226
2227 if (NO_ERROR != palError)
2228 {
2229 goto InternalSetEndOfFileExit;
2230 }
2231
2232 if (pLocalData->open_flags_deviceaccessonly == TRUE)
2233 {
2234 ERROR("File open for device access only\n");
2235 palError = ERROR_ACCESS_DENIED;
2236 goto InternalSetEndOfFileExit;
2237 }
2238
2239 curr = lseek(pLocalData->unix_fd, 0, SEEK_CUR);
2240
2241 TRACE("current file pointer offset is %u\n", curr);
2242 if ( curr < 0 )
2243 {
2244 ERROR("lseek returned %ld\n", curr);
2245 palError = FILEGetLastErrorFromErrno();
2246 goto InternalSetEndOfFileExit;
2247 }
2248
2249#if SIZEOF_OFF_T > 4
2250#if !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT
2251 // ftruncate will return the wrong value for some large lengths.
2252 // We'll short-circuit the process and simply return failure for
2253 // the set of values that covers those cases, all of which would
2254 // have failed anyway on any standard-sized hard drive.
2255 if (curr >= 0xFFFFFFFF000ULL)
2256 {
2257 ERROR("Skipping ftruncate because the offset is too large\n");
2258 palError = ERROR_INVALID_PARAMETER;
2259 goto InternalSetEndOfFileExit;
2260 }
2261#endif // !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT
2262#endif // SIZEOF_OFF_T
2263
2264#if HAS_FTRUNCATE_LENGTH_ISSUE
2265 // Perform an additional check to make sure that there's likely to be enough free space to satisfy the
2266 // request. Do this because it's been observed on Mac OSX that ftruncate can return failure but still
2267 // extend the file to consume the remainder of free space.
2268 //
2269 struct statfs sFileSystemStats;
2270 off_t cbFreeSpace;
2271 if (fstatfs(pLocalData->unix_fd, &sFileSystemStats) != 0)
2272 {
2273 ERROR("fstatfs failed\n");
2274 palError = FILEGetLastErrorFromErrno();
2275 goto InternalSetEndOfFileExit;
2276 }
2277
2278 // Free space is free blocks times the size of each block in bytes.
2279 cbFreeSpace = (off_t)sFileSystemStats.f_bavail * (off_t)sFileSystemStats.f_bsize;
2280
2281 if (curr > cbFreeSpace)
2282 {
2283 ERROR("Not enough disk space for ftruncate\n");
2284 palError = ERROR_DISK_FULL;
2285 goto InternalSetEndOfFileExit;
2286 }
2287#endif // HAS_FTRUNCATE_LENGTH_ISSUE
2288
2289 if ( ftruncate(pLocalData->unix_fd, curr) != 0 )
2290 {
2291 ERROR("ftruncate failed\n");
2292 if ( errno == EACCES )
2293 {
2294 ERROR("file may not be writable\n");
2295 }
2296 palError = FILEGetLastErrorFromErrno();
2297 goto InternalSetEndOfFileExit;
2298 }
2299
2300
2301InternalSetEndOfFileExit:
2302
2303 // Windows starts returning ERROR_INVALID_PARAMETER at an arbitrary file size (~16TB). The file system
2304 // underneath us may be able to support larger and it would be a shame to prevent that. As a compromise,
2305 // if the operation fails and the file size was above the Windows limit map ERROR_DISK_FULL to
2306 // ERROR_INVALID_PARAMETER.
2307 // curr has been checked to be positive after getting the value from lseek. The following cast is put to
2308 // suppress the compilation warning.
2309 if (palError == ERROR_DISK_FULL && (static_cast<UINT64>(curr) > 0x00000fffffff0000ULL ) )
2310 palError = ERROR_INVALID_PARAMETER;
2311
2312 if (NULL != pLocalDataLock)
2313 {
2314 pLocalDataLock->ReleaseLock(pThread, FALSE);
2315 }
2316
2317 if (NULL != pFileObject)
2318 {
2319 pFileObject->ReleaseReference(pThread);
2320 }
2321
2322 return palError;
2323}
2324
2325
2326
2327/*++
2328Function:
2329 SetEndOfFile
2330
2331See MSDN doc.
2332--*/
2333BOOL
2334PALAPI
2335SetEndOfFile(
2336 IN HANDLE hFile)
2337{
2338 PAL_ERROR palError = NO_ERROR;
2339 CPalThread *pThread;;
2340
2341 PERF_ENTRY(SetEndOfFile);
2342 ENTRY("SetEndOfFile(hFile=%p)\n", hFile);
2343
2344 pThread = InternalGetCurrentThread();
2345
2346 palError = InternalSetEndOfFile(
2347 pThread,
2348 hFile
2349 );
2350
2351 if (NO_ERROR != palError)
2352 {
2353 pThread->SetLastError(palError);
2354 }
2355
2356 LOGEXIT("SetEndOfFile returns BOOL %d\n", NO_ERROR == palError);
2357 PERF_EXIT(SetEndOfFile);
2358 return NO_ERROR == palError;
2359}
2360
2361//
2362// We need to break out the actual mechanics of setting the file pointer
2363// on the unix FD for InternalReadFile and InternalWriteFile, as they
2364// need to call this routine in order to determine the value of the
2365// current file pointer when computing the scope of their transaction
2366// lock. If we didn't break out this logic we'd end up referencing the file
2367// handle multiple times, and, in the process, would attempt to recursively
2368// obtain the local process data lock for the underlying file object.
2369//
2370
2371PAL_ERROR
2372InternalSetFilePointerForUnixFd(
2373 int iUnixFd,
2374 LONG lDistanceToMove,
2375 PLONG lpDistanceToMoveHigh,
2376 DWORD dwMoveMethod,
2377 PLONG lpNewFilePointerLow
2378 )
2379{
2380 PAL_ERROR palError = NO_ERROR;
2381 int seek_whence = 0;
2382 __int64 seek_offset = 0LL;
2383 __int64 seek_res = 0LL;
2384 off_t old_offset;
2385
2386 switch( dwMoveMethod )
2387 {
2388 case FILE_BEGIN:
2389 seek_whence = SEEK_SET;
2390 break;
2391 case FILE_CURRENT:
2392 seek_whence = SEEK_CUR;
2393 break;
2394 case FILE_END:
2395 seek_whence = SEEK_END;
2396 break;
2397 default:
2398 ERROR("dwMoveMethod = %d is invalid\n", dwMoveMethod);
2399 palError = ERROR_INVALID_PARAMETER;
2400 goto done;
2401 }
2402
2403 //
2404 // According to MSDN, if lpDistanceToMoveHigh is not null,
2405 // lDistanceToMove is treated as unsigned;
2406 // it is treated as signed otherwise
2407 //
2408
2409 if ( lpDistanceToMoveHigh )
2410 {
2411 /* set the high 32 bits of the offset */
2412 seek_offset = ((__int64)*lpDistanceToMoveHigh << 32);
2413
2414 /* set the low 32 bits */
2415 /* cast to unsigned long to avoid sign extension */
2416 seek_offset |= (ULONG) lDistanceToMove;
2417 }
2418 else
2419 {
2420 seek_offset |= lDistanceToMove;
2421 }
2422
2423 /* store the current position, in case the lseek moves the pointer
2424 before the beginning of the file */
2425 old_offset = lseek(iUnixFd, 0, SEEK_CUR);
2426 if (old_offset == -1)
2427 {
2428 ERROR("lseek(fd,0,SEEK_CUR) failed errno:%d (%s)\n",
2429 errno, strerror(errno));
2430 palError = ERROR_ACCESS_DENIED;
2431 goto done;
2432 }
2433
2434 // Check to see if we're going to seek to a negative offset.
2435 // If we're seeking from the beginning or the current mark,
2436 // this is simple.
2437 if ((seek_whence == SEEK_SET && seek_offset < 0) ||
2438 (seek_whence == SEEK_CUR && seek_offset + old_offset < 0))
2439 {
2440 palError = ERROR_NEGATIVE_SEEK;
2441 goto done;
2442 }
2443 else if (seek_whence == SEEK_END && seek_offset < 0)
2444 {
2445 // We need to determine if we're seeking past the
2446 // beginning of the file, but we don't want to adjust
2447 // the mark in the process. stat is the only way to
2448 // do that.
2449 struct stat fileData;
2450 int result;
2451
2452 result = fstat(iUnixFd, &fileData);
2453 if (result == -1)
2454 {
2455 // It's a bad fd. This shouldn't happen because
2456 // we've already called lseek on it, but you
2457 // never know. This is the best we can do.
2458 palError = ERROR_ACCESS_DENIED;
2459 goto done;
2460 }
2461 if (fileData.st_size < -seek_offset)
2462 {
2463 // Seeking past the beginning.
2464 palError = ERROR_NEGATIVE_SEEK;
2465 goto done;
2466 }
2467 }
2468
2469 seek_res = (__int64)lseek( iUnixFd,
2470 seek_offset,
2471 seek_whence );
2472 if ( seek_res < 0 )
2473 {
2474 /* lseek() returns -1 on error, but also can seek to negative
2475 file offsets, so -1 can also indicate a successful seek to offset
2476 -1. Win32 doesn't allow negative file offsets, so either case
2477 is an error. */
2478 ERROR("lseek failed errno:%d (%s)\n", errno, strerror(errno));
2479 lseek(iUnixFd, old_offset, SEEK_SET);
2480 palError = ERROR_ACCESS_DENIED;
2481 }
2482 else
2483 {
2484 /* store high-order DWORD */
2485 if ( lpDistanceToMoveHigh )
2486 *lpDistanceToMoveHigh = (DWORD)(seek_res >> 32);
2487
2488 /* return low-order DWORD of seek result */
2489 *lpNewFilePointerLow = (DWORD)seek_res;
2490 }
2491
2492done:
2493
2494 return palError;
2495}
2496
2497PAL_ERROR
2498CorUnix::InternalSetFilePointer(
2499 CPalThread *pThread,
2500 HANDLE hFile,
2501 LONG lDistanceToMove,
2502 PLONG lpDistanceToMoveHigh,
2503 DWORD dwMoveMethod,
2504 PLONG lpNewFilePointerLow
2505 )
2506{
2507 PAL_ERROR palError = NO_ERROR;
2508 IPalObject *pFileObject = NULL;
2509 CFileProcessLocalData *pLocalData = NULL;
2510 IDataLock *pLocalDataLock = NULL;
2511
2512 if (INVALID_HANDLE_VALUE == hFile)
2513 {
2514 ERROR( "Invalid file handle\n" );
2515 palError = ERROR_INVALID_HANDLE;
2516 goto InternalSetFilePointerExit;
2517 }
2518
2519 palError = g_pObjectManager->ReferenceObjectByHandle(
2520 pThread,
2521 hFile,
2522 &aotFile,
2523 GENERIC_READ,
2524 &pFileObject
2525 );
2526
2527 if (NO_ERROR != palError)
2528 {
2529 goto InternalSetFilePointerExit;
2530 }
2531
2532 palError = pFileObject->GetProcessLocalData(
2533 pThread,
2534 ReadLock,
2535 &pLocalDataLock,
2536 reinterpret_cast<void**>(&pLocalData)
2537 );
2538
2539 if (NO_ERROR != palError)
2540 {
2541 goto InternalSetFilePointerExit;
2542 }
2543
2544 palError = InternalSetFilePointerForUnixFd(
2545 pLocalData->unix_fd,
2546 lDistanceToMove,
2547 lpDistanceToMoveHigh,
2548 dwMoveMethod,
2549 lpNewFilePointerLow
2550 );
2551
2552InternalSetFilePointerExit:
2553
2554 if (NULL != pLocalDataLock)
2555 {
2556 pLocalDataLock->ReleaseLock(pThread, FALSE);
2557 }
2558
2559 if (NULL != pFileObject)
2560 {
2561 pFileObject->ReleaseReference(pThread);
2562 }
2563
2564 return palError;
2565}
2566
2567/*++
2568Function:
2569 SetFilePointer
2570
2571See MSDN doc.
2572--*/
2573DWORD
2574PALAPI
2575SetFilePointer(
2576 IN HANDLE hFile,
2577 IN LONG lDistanceToMove,
2578 IN PLONG lpDistanceToMoveHigh,
2579 IN DWORD dwMoveMethod)
2580{
2581 PAL_ERROR palError = NO_ERROR;
2582 CPalThread *pThread;
2583 LONG lNewFilePointerLow = 0;
2584
2585 PERF_ENTRY(SetFilePointer);
2586 ENTRY("SetFilePointer(hFile=%p, lDistance=%d, lpDistanceHigh=%p, "
2587 "dwMoveMethod=%#x)\n", hFile, lDistanceToMove,
2588 lpDistanceToMoveHigh, dwMoveMethod);
2589
2590 pThread = InternalGetCurrentThread();
2591
2592 palError = InternalSetFilePointer(
2593 pThread,
2594 hFile,
2595 lDistanceToMove,
2596 lpDistanceToMoveHigh,
2597 dwMoveMethod,
2598 &lNewFilePointerLow
2599 );
2600
2601 if (NO_ERROR != palError)
2602 {
2603 lNewFilePointerLow = INVALID_SET_FILE_POINTER;
2604 }
2605
2606 /* This function must always call SetLastError - even if successful.
2607 If we seek to a value greater than 2^32 - 1, we will effectively be
2608 returning a negative value from this function. Now, let's say that
2609 returned value is -1. Furthermore, assume that win32error has been
2610 set before even entering this function. Then, when this function
2611 returns to SetFilePointer in win32native.cs, it will have returned
2612 -1 and win32error will have been set, which will cause an error to be
2613 returned. Since -1 may not be an error in this case and since we
2614 can't assume that the win32error is related to SetFilePointer,
2615 we need to always call SetLastError here. That way, if this function
2616 succeeds, SetFilePointer in win32native won't mistakenly determine
2617 that it failed. */
2618 pThread->SetLastError(palError);
2619
2620 LOGEXIT("SetFilePointer returns DWORD %#x\n", lNewFilePointerLow);
2621 PERF_EXIT(SetFilePointer);
2622 return lNewFilePointerLow;
2623}
2624
2625/*++
2626Function:
2627 SetFilePointerEx
2628
2629See MSDN doc.
2630--*/
2631BOOL
2632PALAPI
2633SetFilePointerEx(
2634 IN HANDLE hFile,
2635 IN LARGE_INTEGER liDistanceToMove,
2636 OUT PLARGE_INTEGER lpNewFilePointer,
2637 IN DWORD dwMoveMethod)
2638{
2639 PAL_ERROR palError = NO_ERROR;
2640 CPalThread *pThread;
2641 BOOL Ret = FALSE;
2642
2643 PERF_ENTRY(SetFilePointerEx);
2644 ENTRY("SetFilePointerEx(hFile=%p, liDistanceToMove=0x%llx, "
2645 "lpNewFilePointer=%p (0x%llx), dwMoveMethod=0x%x)\n", hFile,
2646 liDistanceToMove.QuadPart, lpNewFilePointer,
2647 (lpNewFilePointer) ? (*lpNewFilePointer).QuadPart : 0, dwMoveMethod);
2648
2649 LONG lDistanceToMove;
2650 lDistanceToMove = (LONG)liDistanceToMove.u.LowPart;
2651 LONG lDistanceToMoveHigh;
2652 lDistanceToMoveHigh = liDistanceToMove.u.HighPart;
2653
2654 LONG lNewFilePointerLow = 0;
2655
2656 pThread = InternalGetCurrentThread();
2657
2658 palError = InternalSetFilePointer(
2659 pThread,
2660 hFile,
2661 lDistanceToMove,
2662 &lDistanceToMoveHigh,
2663 dwMoveMethod,
2664 &lNewFilePointerLow
2665 );
2666
2667 if (NO_ERROR != palError)
2668 {
2669 pThread->SetLastError(palError);
2670 }
2671 else
2672 {
2673 if (lpNewFilePointer != NULL)
2674 {
2675 lpNewFilePointer->u.LowPart = (DWORD)lNewFilePointerLow;
2676 lpNewFilePointer->u.HighPart = (DWORD)lDistanceToMoveHigh;
2677 }
2678 Ret = TRUE;
2679 }
2680
2681 LOGEXIT("SetFilePointerEx returns BOOL %d\n", Ret);
2682 PERF_EXIT(SetFilePointerEx);
2683 return Ret;
2684}
2685
2686PAL_ERROR
2687CorUnix::InternalGetFileSize(
2688 CPalThread *pThread,
2689 HANDLE hFile,
2690 DWORD *pdwFileSizeLow,
2691 DWORD *pdwFileSizeHigh
2692 )
2693{
2694 PAL_ERROR palError = NO_ERROR;
2695 IPalObject *pFileObject = NULL;
2696 CFileProcessLocalData *pLocalData = NULL;
2697 IDataLock *pLocalDataLock = NULL;
2698
2699 struct stat stat_data;
2700
2701 if (INVALID_HANDLE_VALUE == hFile)
2702 {
2703 ERROR( "Invalid file handle\n" );
2704 palError = ERROR_INVALID_HANDLE;
2705 goto InternalGetFileSizeExit;
2706 }
2707
2708 palError = g_pObjectManager->ReferenceObjectByHandle(
2709 pThread,
2710 hFile,
2711 &aotFile,
2712 GENERIC_READ,
2713 &pFileObject
2714 );
2715
2716 if (NO_ERROR != palError)
2717 {
2718 goto InternalGetFileSizeExit;
2719 }
2720
2721 palError = pFileObject->GetProcessLocalData(
2722 pThread,
2723 ReadLock,
2724 &pLocalDataLock,
2725 reinterpret_cast<void**>(&pLocalData)
2726 );
2727
2728 if (NO_ERROR != palError)
2729 {
2730 goto InternalGetFileSizeExit;
2731 }
2732
2733 if (fstat(pLocalData->unix_fd, &stat_data) != 0)
2734 {
2735 ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd);
2736 palError = FILEGetLastErrorFromErrno();
2737 goto InternalGetFileSizeExit;
2738 }
2739
2740 *pdwFileSizeLow = (DWORD)stat_data.st_size;
2741
2742 if (NULL != pdwFileSizeHigh)
2743 {
2744#if SIZEOF_OFF_T > 4
2745 *pdwFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
2746#else
2747 *pdwFileSizeHigh = 0;
2748#endif
2749 }
2750
2751InternalGetFileSizeExit:
2752
2753 if (NULL != pLocalDataLock)
2754 {
2755 pLocalDataLock->ReleaseLock(pThread, FALSE);
2756 }
2757
2758 if (NULL != pFileObject)
2759 {
2760 pFileObject->ReleaseReference(pThread);
2761 }
2762
2763 return palError;
2764}
2765
2766/*++
2767Function:
2768 GetFileSize
2769
2770See MSDN doc.
2771--*/
2772DWORD
2773PALAPI
2774GetFileSize(
2775 IN HANDLE hFile,
2776 OUT LPDWORD lpFileSizeHigh)
2777{
2778 PAL_ERROR palError = NO_ERROR;
2779 CPalThread *pThread;
2780 DWORD dwFileSizeLow;
2781
2782 PERF_ENTRY(GetFileSize);
2783 ENTRY("GetFileSize(hFile=%p, lpFileSizeHigh=%p)\n", hFile, lpFileSizeHigh);
2784
2785 pThread = InternalGetCurrentThread();
2786
2787 palError = InternalGetFileSize(
2788 pThread,
2789 hFile,
2790 &dwFileSizeLow,
2791 lpFileSizeHigh
2792 );
2793
2794 if (NO_ERROR != palError)
2795 {
2796 pThread->SetLastError(palError);
2797 dwFileSizeLow = INVALID_FILE_SIZE;
2798 }
2799
2800 LOGEXIT("GetFileSize returns DWORD %u\n", dwFileSizeLow);
2801 PERF_EXIT(GetFileSize);
2802 return dwFileSizeLow;
2803}
2804
2805/*++
2806Function:
2807GetFileSizeEx
2808
2809See MSDN doc.
2810--*/
2811BOOL
2812PALAPI GetFileSizeEx(
2813IN HANDLE hFile,
2814OUT PLARGE_INTEGER lpFileSize)
2815{
2816 PAL_ERROR palError = NO_ERROR;
2817 CPalThread *pThread;
2818 DWORD dwFileSizeHigh;
2819 DWORD dwFileSizeLow;
2820
2821 PERF_ENTRY(GetFileSizeEx);
2822 ENTRY("GetFileSizeEx(hFile=%p, lpFileSize=%p)\n", hFile, lpFileSize);
2823
2824 pThread = InternalGetCurrentThread();
2825
2826 if (lpFileSize != NULL)
2827 {
2828 palError = InternalGetFileSize(
2829 pThread,
2830 hFile,
2831 &dwFileSizeLow,
2832 &dwFileSizeHigh
2833 );
2834
2835 if (NO_ERROR == palError)
2836 {
2837 lpFileSize->u.LowPart = dwFileSizeLow;
2838 lpFileSize->u.HighPart = dwFileSizeHigh;
2839 }
2840 }
2841 else
2842 {
2843 palError = ERROR_INVALID_PARAMETER;
2844 }
2845
2846 if (NO_ERROR != palError)
2847 {
2848 pThread->SetLastError(palError);
2849 }
2850
2851 LOGEXIT("GetFileSizeEx returns BOOL %d\n", NO_ERROR == palError);
2852 PERF_EXIT(GetFileSizeEx);
2853 return NO_ERROR == palError;
2854}
2855
2856PAL_ERROR
2857CorUnix::InternalFlushFileBuffers(
2858 CPalThread *pThread,
2859 HANDLE hFile
2860 )
2861{
2862 PAL_ERROR palError = NO_ERROR;
2863 IPalObject *pFileObject = NULL;
2864 CFileProcessLocalData *pLocalData = NULL;
2865 IDataLock *pLocalDataLock = NULL;
2866
2867 if (INVALID_HANDLE_VALUE == hFile)
2868 {
2869 ERROR( "Invalid file handle\n" );
2870 palError = ERROR_INVALID_HANDLE;
2871 goto InternalFlushFileBuffersExit;
2872 }
2873
2874 palError = g_pObjectManager->ReferenceObjectByHandle(
2875 pThread,
2876 hFile,
2877 &aotFile,
2878 GENERIC_WRITE,
2879 &pFileObject
2880 );
2881
2882 if (NO_ERROR != palError)
2883 {
2884 goto InternalFlushFileBuffersExit;
2885 }
2886
2887 palError = pFileObject->GetProcessLocalData(
2888 pThread,
2889 ReadLock,
2890 &pLocalDataLock,
2891 reinterpret_cast<void**>(&pLocalData)
2892 );
2893
2894 if (NO_ERROR != palError)
2895 {
2896 goto InternalFlushFileBuffersExit;
2897 }
2898
2899 if (pLocalData->open_flags_deviceaccessonly == TRUE)
2900 {
2901 ERROR("File open for device access only\n");
2902 palError = ERROR_ACCESS_DENIED;
2903 goto InternalFlushFileBuffersExit;
2904 }
2905
2906#if HAVE_FSYNC || defined(__APPLE__)
2907 do
2908 {
2909
2910#if defined(__APPLE__)
2911 if (fcntl(pLocalData->unix_fd, F_FULLFSYNC) != -1)
2912 break;
2913#else // __APPLE__
2914 if (fsync(pLocalData->unix_fd) == 0)
2915 break;
2916#endif // __APPLE__
2917
2918 switch (errno)
2919 {
2920 case EINTR:
2921 // Execution was interrupted by a signal, so restart.
2922 TRACE("fsync(%d) was interrupted. Restarting\n", pLocalData->unix_fd);
2923 break;
2924
2925 default:
2926 palError = FILEGetLastErrorFromErrno();
2927 WARN("fsync(%d) failed with error %d\n", pLocalData->unix_fd, errno);
2928 break;
2929 }
2930 } while (NO_ERROR == palError);
2931#else // HAVE_FSYNC
2932 /* flush all buffers out to disk - there is no way to flush
2933 an individual file descriptor's buffers out. */
2934 sync();
2935#endif // HAVE_FSYNC else
2936
2937
2938InternalFlushFileBuffersExit:
2939
2940 if (NULL != pLocalDataLock)
2941 {
2942 pLocalDataLock->ReleaseLock(pThread, FALSE);
2943 }
2944
2945 if (NULL != pFileObject)
2946 {
2947 pFileObject->ReleaseReference(pThread);
2948 }
2949
2950 return palError;
2951}
2952
2953
2954/*++
2955Function:
2956 FlushFileBuffers
2957
2958See MSDN doc.
2959--*/
2960BOOL
2961PALAPI
2962FlushFileBuffers(
2963 IN HANDLE hFile)
2964{
2965 PAL_ERROR palError = NO_ERROR;
2966 CPalThread *pThread;
2967
2968 PERF_ENTRY(FlushFileBuffers);
2969 ENTRY("FlushFileBuffers(hFile=%p)\n", hFile);
2970
2971 pThread = InternalGetCurrentThread();
2972
2973 palError = InternalFlushFileBuffers(
2974 pThread,
2975 hFile
2976 );
2977
2978 if (NO_ERROR != palError)
2979 {
2980 pThread->SetLastError(palError);
2981 }
2982
2983 LOGEXIT("FlushFileBuffers returns BOOL %d\n", NO_ERROR == palError);
2984 PERF_EXIT(FlushFileBuffers);
2985 return NO_ERROR == palError;
2986}
2987
2988#define ENSURE_UNIQUE_NOT_ZERO \
2989 if ( uUniqueSeed == 0 ) \
2990 {\
2991 uUniqueSeed++;\
2992 }
2993
2994/*++
2995 Function:
2996 GetTempFileNameA
2997
2998uUnique is always 0.
2999 --*/
3000const int MAX_PREFIX = 3;
3001const int MAX_SEEDSIZE = 8; /* length of "unique portion of
3002 the string, plus extension(FFFF.TMP). */
3003static USHORT uUniqueSeed = 0;
3004static BOOL IsInitialized = FALSE;
3005
3006UINT
3007PALAPI
3008GetTempFileNameA(
3009 IN LPCSTR lpPathName,
3010 IN LPCSTR lpPrefixString,
3011 IN UINT uUnique,
3012 OUT LPSTR lpTempFileName)
3013{
3014 CPalThread *pThread;
3015 CHAR * full_name;
3016 PathCharString full_namePS;
3017 int length;
3018 CHAR * file_template;
3019 PathCharString file_templatePS;
3020 CHAR chLastPathNameChar;
3021
3022 HANDLE hTempFile;
3023 UINT uRet = 0;
3024 DWORD dwError;
3025 USHORT uLoopCounter = 0;
3026
3027 PERF_ENTRY(GetTempFileNameA);
3028 ENTRY("GetTempFileNameA(lpPathName=%p (%s), lpPrefixString=%p (%s), uUnique=%u, "
3029 "lpTempFileName=%p)\n", lpPathName?lpPathName:"NULL", lpPathName?lpPathName:"NULL",
3030 lpPrefixString?lpPrefixString:"NULL",
3031 lpPrefixString?lpPrefixString:"NULL", uUnique,
3032 lpTempFileName?lpTempFileName:"NULL");
3033
3034 pThread = InternalGetCurrentThread();
3035 if ( !IsInitialized )
3036 {
3037 uUniqueSeed = (USHORT)( time( NULL ) );
3038
3039 /* On the off chance 0 is returned.
3040 0 being the error return code. */
3041 ENSURE_UNIQUE_NOT_ZERO
3042 IsInitialized = TRUE;
3043 }
3044
3045 if ( !lpPathName || *lpPathName == '\0' )
3046 {
3047 pThread->SetLastError( ERROR_DIRECTORY );
3048 goto done;
3049 }
3050
3051 if ( NULL == lpTempFileName )
3052 {
3053 ERROR( "lpTempFileName cannot be NULL\n" );
3054 pThread->SetLastError( ERROR_INVALID_PARAMETER );
3055 goto done;
3056 }
3057
3058 if ( strlen( lpPathName ) + MAX_SEEDSIZE + MAX_PREFIX >= MAX_LONGPATH )
3059 {
3060 WARN( "File names larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH );
3061 pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE );
3062 goto done;
3063 }
3064
3065 length = strlen(lpPathName) + MAX_SEEDSIZE + MAX_PREFIX + 10;
3066 file_template = file_templatePS.OpenStringBuffer(length);
3067 if (NULL == file_template)
3068 {
3069 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3070 goto done;
3071 }
3072 *file_template = '\0';
3073 strcat_s( file_template, file_templatePS.GetSizeOf(), lpPathName );
3074 file_templatePS.CloseBuffer(length);
3075
3076 chLastPathNameChar = file_template[strlen(file_template)-1];
3077 if (chLastPathNameChar != '\\' && chLastPathNameChar != '/')
3078 {
3079 strcat_s( file_template, file_templatePS.GetSizeOf(), "\\" );
3080 }
3081
3082 if ( lpPrefixString )
3083 {
3084 strncat_s( file_template, file_templatePS.GetSizeOf(), lpPrefixString, MAX_PREFIX );
3085 }
3086 FILEDosToUnixPathA( file_template );
3087 strncat_s( file_template, file_templatePS.GetSizeOf(), "%.4x.TMP", MAX_SEEDSIZE );
3088
3089 /* Create the file. */
3090 dwError = GetLastError();
3091 pThread->SetLastError( NOERROR );
3092
3093 length = strlen(file_template) + MAX_SEEDSIZE + MAX_PREFIX;
3094 full_name = full_namePS.OpenStringBuffer(length);
3095 if (NULL == full_name)
3096 {
3097 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3098 goto done;
3099 }
3100 sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, (0 == uUnique) ? uUniqueSeed : uUnique);
3101 full_namePS.CloseBuffer(length);
3102
3103 hTempFile = CreateFileA( full_name, GENERIC_WRITE,
3104 FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL );
3105
3106 if (uUnique == 0)
3107 {
3108 /* The USHORT will overflow back to 0 if we go past
3109 65536 files, so break the loop after 65536 iterations.
3110 If the CreateFile call was not successful within that
3111 number of iterations, then there are no temp file names
3112 left for that directory. */
3113 while ( ERROR_PATH_NOT_FOUND != GetLastError() &&
3114 INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF )
3115 {
3116 uUniqueSeed++;
3117 ENSURE_UNIQUE_NOT_ZERO;
3118
3119 pThread->SetLastError( NOERROR );
3120 sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, uUniqueSeed );
3121 hTempFile = CreateFileA( full_name, GENERIC_WRITE,
3122 FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL );
3123 uLoopCounter++;
3124
3125 }
3126 }
3127
3128 /* Reset the error code.*/
3129 if ( NOERROR == GetLastError() )
3130 {
3131 pThread->SetLastError( dwError );
3132 }
3133
3134 /* Windows sets ERROR_FILE_EXISTS,if there
3135 are no available temp files. */
3136 if ( INVALID_HANDLE_VALUE != hTempFile )
3137 {
3138 if (0 == uUnique)
3139 {
3140 uRet = uUniqueSeed;
3141 uUniqueSeed++;
3142 ENSURE_UNIQUE_NOT_ZERO;
3143 }
3144 else
3145 {
3146 uRet = uUnique;
3147 }
3148
3149 if ( CloseHandle( hTempFile ) )
3150 {
3151 if (strcpy_s( lpTempFileName, MAX_LONGPATH, full_name ) != SAFECRT_SUCCESS)
3152 {
3153 ERROR( "strcpy_s failed!\n");
3154 pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE );
3155 *lpTempFileName = '\0';
3156 uRet = 0;
3157 }
3158 }
3159 else
3160 {
3161 ASSERT( "Unable to close the handle %p\n", hTempFile );
3162 pThread->SetLastError( ERROR_INTERNAL_ERROR );
3163 *lpTempFileName = '\0';
3164 uRet = 0;
3165 }
3166 }
3167 else if ( INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF )
3168 {
3169 ERROR( "Unable to create temp file. \n" );
3170 uRet = 0;
3171
3172 if ( ERROR_PATH_NOT_FOUND == GetLastError() )
3173 {
3174 /* CreateFile failed because it could not
3175 find the path. */
3176 pThread->SetLastError( ERROR_DIRECTORY );
3177 } /* else use the lasterror value from CreateFileA */
3178 }
3179 else
3180 {
3181 TRACE( "65535 files already exist in the directory. "
3182 "No temp files available for creation.\n" );
3183 pThread->SetLastError( ERROR_FILE_EXISTS );
3184 }
3185
3186done:
3187 LOGEXIT("GetTempFileNameA returns UINT %u\n", uRet);
3188 PERF_EXIT(GetTempFileNameA);
3189 return uRet;
3190
3191}
3192
3193/*++
3194Function:
3195 GetTempFileNameW
3196
3197uUnique is always 0.
3198--*/
3199UINT
3200PALAPI
3201GetTempFileNameW(
3202 IN LPCWSTR lpPathName,
3203 IN LPCWSTR lpPrefixString,
3204 IN UINT uUnique,
3205 OUT LPWSTR lpTempFileName)
3206{
3207 CPalThread *pThread;
3208 INT path_size = 0;
3209 INT prefix_size = 0;
3210 CHAR * full_name;
3211 CHAR * prefix_string;
3212 CHAR * tempfile_name;
3213 PathCharString full_namePS, prefix_stringPS;
3214 INT length = 0;
3215 UINT uRet;
3216
3217 PERF_ENTRY(GetTempFileNameW);
3218 ENTRY("GetTempFileNameW(lpPathName=%p (%S), lpPrefixString=%p (%S), uUnique=%u, "
3219 "lpTempFileName=%p)\n", lpPathName?lpPathName:W16_NULLSTRING, lpPathName?lpPathName:W16_NULLSTRING,
3220 lpPrefixString?lpPrefixString:W16_NULLSTRING,
3221 lpPrefixString?lpPrefixString:W16_NULLSTRING,uUnique, lpTempFileName);
3222
3223 pThread = InternalGetCurrentThread();
3224 /* Sanity checks. */
3225 if ( !lpPathName || *lpPathName == '\0' )
3226 {
3227 pThread->SetLastError( ERROR_DIRECTORY );
3228 uRet = 0;
3229 goto done;
3230 }
3231
3232 length = (PAL_wcslen(lpPathName)+1) * MaxWCharToAcpLengthFactor;
3233 full_name = full_namePS.OpenStringBuffer(length);
3234 if (NULL == full_name)
3235 {
3236 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3237 uRet = 0;
3238 goto done;
3239 }
3240 path_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, full_name,
3241 length, NULL, NULL );
3242
3243 if( path_size == 0 )
3244 {
3245 full_namePS.CloseBuffer(0);
3246 DWORD dwLastError = GetLastError();
3247 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
3248 pThread->SetLastError(ERROR_INTERNAL_ERROR);
3249 uRet = 0;
3250 goto done;
3251 }
3252
3253 full_namePS.CloseBuffer(path_size - 1);
3254
3255 if (lpPrefixString != NULL)
3256 {
3257 length = (PAL_wcslen(lpPrefixString)+1) * MaxWCharToAcpLengthFactor;
3258 prefix_string = prefix_stringPS.OpenStringBuffer(length);
3259 if (NULL == prefix_string)
3260 {
3261 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3262 uRet = 0;
3263 goto done;
3264 }
3265 prefix_size = WideCharToMultiByte( CP_ACP, 0, lpPrefixString, -1,
3266 prefix_string,
3267 MAX_LONGPATH - path_size - MAX_SEEDSIZE,
3268 NULL, NULL );
3269
3270 if( prefix_size == 0 )
3271 {
3272 prefix_stringPS.CloseBuffer(0);
3273 DWORD dwLastError = GetLastError();
3274 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
3275 pThread->SetLastError(ERROR_INTERNAL_ERROR);
3276 uRet = 0;
3277 goto done;
3278 }
3279 prefix_stringPS.CloseBuffer(prefix_size - 1);
3280 }
3281
3282 tempfile_name = (char*)InternalMalloc(MAX_LONGPATH);
3283 if (tempfile_name == NULL)
3284 {
3285 pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3286 uRet = 0;
3287 goto done;
3288 }
3289
3290 uRet = GetTempFileNameA(full_name,
3291 (lpPrefixString == NULL) ? NULL : prefix_string,
3292 0, tempfile_name);
3293 if (uRet)
3294 {
3295 path_size = MultiByteToWideChar( CP_ACP, 0, tempfile_name, -1,
3296 lpTempFileName, MAX_LONGPATH );
3297
3298 free(tempfile_name);
3299 tempfile_name = NULL;
3300 if (!path_size)
3301 {
3302 DWORD dwLastError = GetLastError();
3303 if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
3304 {
3305 WARN("File names larger than MAX_PATH_FNAME (%d)! \n", MAX_LONGPATH);
3306 dwLastError = ERROR_FILENAME_EXCED_RANGE;
3307 }
3308 else
3309 {
3310 ASSERT("MultiByteToWideChar failure! error is %d", dwLastError);
3311 dwLastError = ERROR_INTERNAL_ERROR;
3312 }
3313 pThread->SetLastError(dwLastError);
3314 uRet = 0;
3315 }
3316 }
3317
3318done:
3319 LOGEXIT("GetTempFileNameW returns UINT %u\n", uRet);
3320 PERF_EXIT(GetTempFileNameW);
3321 return uRet;
3322}
3323
3324/*++
3325Function:
3326 FILEGetLastErrorFromErrno
3327
3328Convert errno into the appropriate win32 error and return it.
3329--*/
3330DWORD FILEGetLastErrorFromErrno( void )
3331{
3332 DWORD dwRet;
3333
3334 switch(errno)
3335 {
3336 case 0:
3337 dwRet = ERROR_SUCCESS;
3338 break;
3339 case ENAMETOOLONG:
3340 dwRet = ERROR_FILENAME_EXCED_RANGE;
3341 break;
3342 case ENOTDIR:
3343 dwRet = ERROR_PATH_NOT_FOUND;
3344 break;
3345 case ENOENT:
3346 dwRet = ERROR_FILE_NOT_FOUND;
3347 break;
3348 case EACCES:
3349 case EPERM:
3350 case EROFS:
3351 case EISDIR:
3352 dwRet = ERROR_ACCESS_DENIED;
3353 break;
3354 case EEXIST:
3355 dwRet = ERROR_ALREADY_EXISTS;
3356 break;
3357 case ENOTEMPTY:
3358 dwRet = ERROR_DIR_NOT_EMPTY;
3359 break;
3360 case EBADF:
3361 dwRet = ERROR_INVALID_HANDLE;
3362 break;
3363 case ENOMEM:
3364 dwRet = ERROR_NOT_ENOUGH_MEMORY;
3365 break;
3366 case EBUSY:
3367 dwRet = ERROR_BUSY;
3368 break;
3369 case ENOSPC:
3370 case EDQUOT:
3371 dwRet = ERROR_DISK_FULL;
3372 break;
3373 case ELOOP:
3374 dwRet = ERROR_BAD_PATHNAME;
3375 break;
3376 case EIO:
3377 dwRet = ERROR_WRITE_FAULT;
3378 break;
3379 case EMFILE:
3380 dwRet = ERROR_TOO_MANY_OPEN_FILES;
3381 break;
3382 case ERANGE:
3383 dwRet = ERROR_BAD_PATHNAME;
3384 break;
3385 default:
3386 ERROR("unexpected errno %d (%s); returning ERROR_GEN_FAILURE\n",
3387 errno, strerror(errno));
3388 dwRet = ERROR_GEN_FAILURE;
3389 }
3390
3391 TRACE("errno = %d (%s), LastError = %d\n", errno, strerror(errno), dwRet);
3392
3393 return dwRet;
3394}
3395
3396/*++
3397Function:
3398 DIRGetLastErrorFromErrno
3399
3400Convert errno into the appropriate win32 error and return it.
3401--*/
3402DWORD DIRGetLastErrorFromErrno( void )
3403{
3404 if (errno == ENOENT)
3405 return ERROR_PATH_NOT_FOUND;
3406 else
3407 return FILEGetLastErrorFromErrno();
3408}
3409
3410
3411/*++
3412Function:
3413 CopyFileA
3414
3415See MSDN doc.
3416
3417Notes:
3418 There are several (most) error paths here that do not call SetLastError().
3419This is because we know that CreateFile, ReadFile, and WriteFile will do so,
3420and will have a much better idea of the specific error.
3421--*/
3422BOOL
3423PALAPI
3424CopyFileA(
3425 IN LPCSTR lpExistingFileName,
3426 IN LPCSTR lpNewFileName,
3427 IN BOOL bFailIfExists)
3428{
3429 CPalThread *pThread;
3430 HANDLE hSource = INVALID_HANDLE_VALUE;
3431 HANDLE hDest = INVALID_HANDLE_VALUE;
3432 DWORD dwDestCreationMode;
3433 BOOL bGood = FALSE;
3434 DWORD dwSrcFileAttributes;
3435 struct stat SrcFileStats;
3436
3437 LPSTR lpUnixPath = NULL;
3438 const int buffer_size = 16*1024;
3439 char *buffer = (char*)alloca(buffer_size);
3440 DWORD bytes_read;
3441 DWORD bytes_written;
3442 int permissions;
3443
3444
3445 PERF_ENTRY(CopyFileA);
3446 ENTRY("CopyFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s), bFailIfExists=%d)\n",
3447 lpExistingFileName?lpExistingFileName:"NULL",
3448 lpExistingFileName?lpExistingFileName:"NULL",
3449 lpNewFileName?lpNewFileName:"NULL",
3450 lpNewFileName?lpNewFileName:"NULL", bFailIfExists);
3451
3452 pThread = InternalGetCurrentThread();
3453 if ( bFailIfExists )
3454 {
3455 dwDestCreationMode = CREATE_NEW;
3456 }
3457 else
3458 {
3459 dwDestCreationMode = CREATE_ALWAYS;
3460 }
3461
3462 hSource = CreateFileA( lpExistingFileName,
3463 GENERIC_READ,
3464 FILE_SHARE_READ,
3465 NULL,
3466 OPEN_EXISTING,
3467 0,
3468 NULL );
3469
3470 if ( hSource == INVALID_HANDLE_VALUE )
3471 {
3472 ERROR("CreateFileA failed for %s\n", lpExistingFileName);
3473 goto done;
3474 }
3475
3476 /* Need to preserve the file attributes */
3477 dwSrcFileAttributes = GetFileAttributes(lpExistingFileName);
3478 if (dwSrcFileAttributes == 0xffffffff)
3479 {
3480 ERROR("GetFileAttributes failed for %s\n", lpExistingFileName);
3481 goto done;
3482 }
3483
3484 /* Need to preserve the owner/group and chmod() flags */
3485 lpUnixPath = strdup(lpExistingFileName);
3486 if ( lpUnixPath == NULL )
3487 {
3488 ERROR("strdup() failed\n");
3489 pThread->SetLastError(FILEGetLastErrorFromErrno());
3490 goto done;
3491 }
3492 FILEDosToUnixPathA(lpUnixPath);
3493 if (stat (lpUnixPath, &SrcFileStats) == -1)
3494 {
3495 ERROR("stat() failed for %s\n", lpExistingFileName);
3496 pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath));
3497 goto done;
3498 }
3499
3500 hDest = CreateFileA( lpNewFileName,
3501 GENERIC_WRITE,
3502 FILE_SHARE_READ,
3503 NULL,
3504 dwDestCreationMode,
3505 0,
3506 NULL );
3507
3508 if ( hDest == INVALID_HANDLE_VALUE )
3509 {
3510 ERROR("CreateFileA failed for %s\n", lpNewFileName);
3511 goto done;
3512 }
3513
3514 free(lpUnixPath);
3515 lpUnixPath = strdup(lpNewFileName);
3516 if ( lpUnixPath == NULL )
3517 {
3518 ERROR("strdup() failed\n");
3519 pThread->SetLastError(FILEGetLastErrorFromErrno());
3520 goto done;
3521 }
3522 FILEDosToUnixPathA( lpUnixPath );
3523
3524
3525 // We don't set file attributes in CreateFile. The only attribute
3526 // that is reflected on disk in Unix is read-only, and we set that
3527 // here.
3528 permissions = (S_IRWXU | S_IRWXG | S_IRWXO);
3529 if ((dwSrcFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
3530 {
3531 permissions &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
3532 }
3533
3534 /* Make sure the new file has the same chmod() flags. */
3535 if (chmod(lpUnixPath, SrcFileStats.st_mode & permissions) == -1)
3536 {
3537 WARN ("chmod() failed to set mode 0x%x on new file\n",
3538 SrcFileStats.st_mode & permissions);
3539 pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath));
3540 goto done;
3541 }
3542
3543 while( (bGood = ReadFile( hSource, buffer, buffer_size, &bytes_read, NULL ))
3544 && bytes_read > 0 )
3545 {
3546 bGood = ( WriteFile( hDest, buffer, bytes_read, &bytes_written, NULL )
3547 && bytes_written == bytes_read);
3548 if (!bGood) break;
3549 }
3550
3551 if (!bGood)
3552 {
3553 ERROR("Copy failed\n");
3554
3555 if ( !CloseHandle(hDest) ||
3556 !DeleteFileA(lpNewFileName) )
3557 {
3558 ERROR("Unable to clean up partial copy\n");
3559 }
3560 hDest = INVALID_HANDLE_VALUE;
3561
3562 goto done;
3563 }
3564
3565done:
3566
3567 if ( hSource != INVALID_HANDLE_VALUE )
3568 {
3569 CloseHandle( hSource );
3570 }
3571 if ( hDest != INVALID_HANDLE_VALUE )
3572 {
3573 CloseHandle( hDest );
3574 }
3575 if (lpUnixPath)
3576 {
3577 free(lpUnixPath);
3578 }
3579
3580 LOGEXIT("CopyFileA returns BOOL %d\n", bGood);
3581 PERF_EXIT(CopyFileA);
3582 return bGood;
3583}
3584
3585
3586/*++
3587Function:
3588 SetFileAttributesA
3589
3590Notes:
3591 Used for setting read-only attribute on file only.
3592
3593--*/
3594BOOL
3595PALAPI
3596SetFileAttributesA(
3597 IN LPCSTR lpFileName,
3598 IN DWORD dwFileAttributes)
3599{
3600 CPalThread *pThread;
3601 struct stat stat_data;
3602 mode_t new_mode;
3603
3604 DWORD dwLastError = 0;
3605 BOOL bRet = FALSE;
3606 LPSTR unixFileName = NULL;
3607
3608 PERF_ENTRY(SetFileAttributesA);
3609 ENTRY("SetFileAttributesA(lpFileName=%p (%s), dwFileAttributes=%#x)\n",
3610 lpFileName?lpFileName:"NULL",
3611 lpFileName?lpFileName:"NULL", dwFileAttributes);
3612
3613 pThread = InternalGetCurrentThread();
3614
3615 /* Windows behavior for SetFileAttributes is that any valid attributes
3616 are set on a file and any invalid attributes are ignored. SetFileAttributes
3617 returns success and does not set an error even if some or all of the
3618 attributes are invalid. If all the attributes are invalid, SetFileAttributes
3619 sets a file's attribute to NORMAL. */
3620
3621 /* If dwFileAttributes does not contain READONLY or NORMAL, set it to NORMAL
3622 and print a warning message. */
3623 if ( !(dwFileAttributes & (FILE_ATTRIBUTE_READONLY |FILE_ATTRIBUTE_NORMAL)) )
3624 {
3625 dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
3626 WARN("dwFileAttributes(%#x) contains attributes that are either not supported "
3627 "or cannot be set via SetFileAttributes.\n");
3628 }
3629
3630 if ( (dwFileAttributes & FILE_ATTRIBUTE_NORMAL) &&
3631 (dwFileAttributes != FILE_ATTRIBUTE_NORMAL) )
3632 {
3633 WARN("Ignoring FILE_ATTRIBUTE_NORMAL -- it must be used alone\n");
3634 }
3635
3636 if (lpFileName == NULL)
3637 {
3638 dwLastError = ERROR_FILE_NOT_FOUND;
3639 goto done;
3640 }
3641
3642 if ((unixFileName = strdup(lpFileName)) == NULL)
3643 {
3644 ERROR("strdup() failed\n");
3645 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
3646 goto done;
3647 }
3648
3649 FILEDosToUnixPathA( unixFileName );
3650 if ( stat(unixFileName, &stat_data) != 0 )
3651 {
3652 TRACE("stat failed on %s; errno is %d (%s)\n",
3653 unixFileName, errno, strerror(errno));
3654 dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
3655 goto done;
3656 }
3657
3658 new_mode = stat_data.st_mode;
3659 TRACE("st_mode is %#x\n", new_mode);
3660
3661 /* if we can't do GetFileAttributes on it, don't do SetFileAttributes */
3662 if ( !(new_mode & S_IFREG) && !(new_mode & S_IFDIR) )
3663 {
3664 ERROR("Not a regular file or directory, S_IFMT is %#x\n",
3665 new_mode & S_IFMT);
3666 dwLastError = ERROR_ACCESS_DENIED;
3667 goto done;
3668 }
3669
3670 /* set or unset the "read-only" attribute */
3671 if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
3672 {
3673 /* remove the write bit from everybody */
3674 new_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
3675 }
3676 else
3677 {
3678 /* give write permission to the owner if the owner
3679 * already has read permission */
3680 if ( new_mode & S_IRUSR )
3681 {
3682 new_mode |= S_IWUSR;
3683 }
3684 }
3685 TRACE("new mode is %#x\n", new_mode);
3686
3687 bRet = TRUE;
3688 if ( new_mode != stat_data.st_mode )
3689 {
3690 if ( chmod(unixFileName, new_mode) != 0 )
3691 {
3692 ERROR("chmod(%s, %#x) failed\n", unixFileName, new_mode);
3693 dwLastError = FILEGetLastErrorFromErrnoAndFilename(unixFileName);
3694 bRet = FALSE;
3695 }
3696 }
3697
3698done:
3699 if (dwLastError)
3700 {
3701 pThread->SetLastError(dwLastError);
3702 }
3703
3704 free(unixFileName);
3705
3706 LOGEXIT("SetFileAttributesA returns BOOL %d\n", bRet);
3707 PERF_EXIT(SetFileAttributesA);
3708 return bRet;
3709}
3710
3711PAL_ERROR
3712CorUnix::InternalCreatePipe(
3713 CPalThread *pThread,
3714 HANDLE *phReadPipe,
3715 HANDLE *phWritePipe,
3716 LPSECURITY_ATTRIBUTES lpPipeAttributes,
3717 DWORD nSize
3718 )
3719{
3720 PAL_ERROR palError = NO_ERROR;
3721 IPalObject *pReadFileObject = NULL;
3722 IPalObject *pReadRegisteredFile = NULL;
3723 IPalObject *pWriteFileObject = NULL;
3724 IPalObject *pWriteRegisteredFile = NULL;
3725 IDataLock *pDataLock = NULL;
3726 CFileProcessLocalData *pLocalData = NULL;
3727 CObjectAttributes oaFile(NULL, lpPipeAttributes);
3728
3729 int readWritePipeDes[2] = {-1, -1};
3730
3731 if ((phReadPipe == NULL) || (phWritePipe == NULL))
3732 {
3733 ERROR("One of the two parameters hReadPipe(%p) and hWritePipe(%p) is Null\n",phReadPipe,phWritePipe);
3734 palError = ERROR_INVALID_PARAMETER;
3735 goto InternalCreatePipeExit;
3736 }
3737
3738 if ((lpPipeAttributes == NULL) ||
3739 (lpPipeAttributes->bInheritHandle == FALSE) ||
3740 (lpPipeAttributes->lpSecurityDescriptor != NULL))
3741 {
3742 ASSERT("invalid security attributes!\n");
3743 palError = ERROR_INVALID_PARAMETER;
3744 goto InternalCreatePipeExit;
3745 }
3746
3747 if (pipe(readWritePipeDes) == -1)
3748 {
3749 ERROR("pipe() call failed errno:%d (%s) \n", errno, strerror(errno));
3750 palError = ERROR_INTERNAL_ERROR;
3751 goto InternalCreatePipeExit;
3752 }
3753
3754 /* enable close-on-exec for both pipes; if one gets passed to CreateProcess
3755 it will be "uncloseonexeced" in order to be inherited */
3756 if(-1 == fcntl(readWritePipeDes[0],F_SETFD,FD_CLOEXEC))
3757 {
3758 ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
3759 "(%s)\n", errno, strerror(errno));
3760 palError = ERROR_INTERNAL_ERROR;
3761 goto InternalCreatePipeExit;
3762 }
3763 if(-1 == fcntl(readWritePipeDes[1],F_SETFD,FD_CLOEXEC))
3764 {
3765 ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d "
3766 "(%s)\n", errno, strerror(errno));
3767 palError = ERROR_INTERNAL_ERROR;
3768 goto InternalCreatePipeExit;
3769 }
3770
3771 //
3772 // Setup the object for the read end of the pipe
3773 //
3774
3775 palError = g_pObjectManager->AllocateObject(
3776 pThread,
3777 &otFile,
3778 &oaFile,
3779 &pReadFileObject
3780 );
3781
3782 if (NO_ERROR != palError)
3783 {
3784 goto InternalCreatePipeExit;
3785 }
3786
3787 palError = pReadFileObject->GetProcessLocalData(
3788 pThread,
3789 WriteLock,
3790 &pDataLock,
3791 reinterpret_cast<void**>(&pLocalData)
3792 );
3793
3794 if (NO_ERROR != palError)
3795 {
3796 goto InternalCreatePipeExit;
3797 }
3798
3799 pLocalData->inheritable = TRUE;
3800 pLocalData->open_flags = O_RDONLY;
3801
3802 //
3803 // After storing the file descriptor in the object's local data
3804 // we want to clear it from the array to prevent a possible double
3805 // close if an error occurs.
3806 //
3807
3808 pLocalData->unix_fd = readWritePipeDes[0];
3809 readWritePipeDes[0] = -1;
3810
3811 pDataLock->ReleaseLock(pThread, TRUE);
3812 pDataLock = NULL;
3813
3814 //
3815 // Setup the object for the write end of the pipe
3816 //
3817
3818 palError = g_pObjectManager->AllocateObject(
3819 pThread,
3820 &otFile,
3821 &oaFile,
3822 &pWriteFileObject
3823 );
3824
3825 if (NO_ERROR != palError)
3826 {
3827 goto InternalCreatePipeExit;
3828 }
3829
3830 palError = pWriteFileObject->GetProcessLocalData(
3831 pThread,
3832 WriteLock,
3833 &pDataLock,
3834 reinterpret_cast<void**>(&pLocalData)
3835 );
3836
3837 if (NO_ERROR != palError)
3838 {
3839 goto InternalCreatePipeExit;
3840 }
3841
3842 pLocalData->inheritable = TRUE;
3843 pLocalData->open_flags = O_WRONLY;
3844
3845 //
3846 // After storing the file descriptor in the object's local data
3847 // we want to clear it from the array to prevent a possible double
3848 // close if an error occurs.
3849 //
3850
3851 pLocalData->unix_fd = readWritePipeDes[1];
3852 readWritePipeDes[1] = -1;
3853
3854 pDataLock->ReleaseLock(pThread, TRUE);
3855 pDataLock = NULL;
3856
3857 //
3858 // Register the pipe objects
3859 //
3860
3861 palError = g_pObjectManager->RegisterObject(
3862 pThread,
3863 pReadFileObject,
3864 &aotFile,
3865 GENERIC_READ,
3866 phReadPipe,
3867 &pReadRegisteredFile
3868 );
3869
3870 //
3871 // pReadFileObject is invalidated by the call to RegisterObject, so NULL it
3872 // out here to ensure that we don't try to release a reference on
3873 // it down the line.
3874 //
3875
3876 pReadFileObject = NULL;
3877
3878 if (NO_ERROR != palError)
3879 {
3880 goto InternalCreatePipeExit;
3881 }
3882
3883 palError = g_pObjectManager->RegisterObject(
3884 pThread,
3885 pWriteFileObject,
3886 &aotFile,
3887 GENERIC_WRITE,
3888 phWritePipe,
3889 &pWriteRegisteredFile
3890 );
3891
3892 //
3893 // pWriteFileObject is invalidated by the call to RegisterObject, so NULL it
3894 // out here to ensure that we don't try to release a reference on
3895 // it down the line.
3896 //
3897
3898 pWriteFileObject = NULL;
3899
3900InternalCreatePipeExit:
3901
3902 if (NO_ERROR != palError)
3903 {
3904 if (-1 != readWritePipeDes[0])
3905 {
3906 close(readWritePipeDes[0]);
3907 }
3908
3909 if (-1 != readWritePipeDes[1])
3910 {
3911 close(readWritePipeDes[1]);
3912 }
3913 }
3914
3915 if (NULL != pReadFileObject)
3916 {
3917 pReadFileObject->ReleaseReference(pThread);
3918 }
3919
3920 if (NULL != pReadRegisteredFile)
3921 {
3922 pReadRegisteredFile->ReleaseReference(pThread);
3923 }
3924
3925 if (NULL != pWriteFileObject)
3926 {
3927 pWriteFileObject->ReleaseReference(pThread);
3928 }
3929
3930 if (NULL != pWriteRegisteredFile)
3931 {
3932 pWriteRegisteredFile->ReleaseReference(pThread);
3933 }
3934
3935 return palError;
3936}
3937
3938/*++
3939Function:
3940 CreatePipe
3941
3942See MSDN doc.
3943--*/
3944PALIMPORT
3945BOOL
3946PALAPI
3947CreatePipe(
3948 OUT PHANDLE hReadPipe,
3949 OUT PHANDLE hWritePipe,
3950 IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
3951 IN DWORD nSize)
3952{
3953 PAL_ERROR palError;
3954 CPalThread *pThread;
3955
3956 PERF_ENTRY(CreatePipe);
3957 ENTRY("CreatePipe(hReadPipe:%p, hWritePipe:%p, lpPipeAttributes:%p, nSize:%d\n",
3958 hReadPipe, hWritePipe, lpPipeAttributes, nSize);
3959
3960 pThread = InternalGetCurrentThread();
3961
3962 palError = InternalCreatePipe(
3963 pThread,
3964 hReadPipe,
3965 hWritePipe,
3966 lpPipeAttributes,
3967 nSize
3968 );
3969
3970 if (NO_ERROR != palError)
3971 {
3972 pThread->SetLastError(palError);
3973 }
3974
3975 LOGEXIT("CreatePipe return %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
3976 PERF_EXIT(CreatePipe);
3977 return NO_ERROR == palError;
3978}
3979
3980/*++
3981init_std_handle [static]
3982
3983utility function for FILEInitStdHandles. do the work that is common to all
3984three standard handles
3985
3986Parameters:
3987 HANDLE pStd : Defines which standard handle to assign
3988 FILE *stream : file stream to associate to handle
3989
3990Return value:
3991 handle for specified stream, or INVALID_HANDLE_VALUE on failure
3992--*/
3993static HANDLE init_std_handle(HANDLE * pStd, FILE *stream)
3994{
3995 CPalThread *pThread = InternalGetCurrentThread();
3996 PAL_ERROR palError = NO_ERROR;
3997 IPalObject *pFileObject = NULL;
3998 IPalObject *pRegisteredFile = NULL;
3999 IDataLock *pDataLock = NULL;
4000 CFileProcessLocalData *pLocalData = NULL;
4001 CObjectAttributes oa;
4002
4003 HANDLE hFile = INVALID_HANDLE_VALUE;
4004 int new_fd = -1;
4005
4006 /* duplicate the FILE *, so that we can fclose() in FILECloseHandle without
4007 closing the original */
4008 new_fd = fcntl(fileno(stream), F_DUPFD_CLOEXEC, 0); // dup, but with CLOEXEC
4009 if(-1 == new_fd)
4010 {
4011 ERROR("dup() failed; errno is %d (%s)\n", errno, strerror(errno));
4012 goto done;
4013 }
4014
4015 palError = g_pObjectManager->AllocateObject(
4016 pThread,
4017 &otFile,
4018 &oa,
4019 &pFileObject
4020 );
4021
4022 if (NO_ERROR != palError)
4023 {
4024 goto done;
4025 }
4026
4027 palError = pFileObject->GetProcessLocalData(
4028 pThread,
4029 WriteLock,
4030 &pDataLock,
4031 reinterpret_cast<void**>(&pLocalData)
4032 );
4033
4034 if (NO_ERROR != palError)
4035 {
4036 goto done;
4037 }
4038
4039 pLocalData->inheritable = TRUE;
4040 pLocalData->unix_fd = new_fd;
4041 pLocalData->dwDesiredAccess = 0;
4042 pLocalData->open_flags = 0;
4043 pLocalData->open_flags_deviceaccessonly = FALSE;
4044
4045 //
4046 // We've finished initializing our local data, so release that lock
4047 //
4048
4049 pDataLock->ReleaseLock(pThread, TRUE);
4050 pDataLock = NULL;
4051
4052 palError = g_pObjectManager->RegisterObject(
4053 pThread,
4054 pFileObject,
4055 &aotFile,
4056 0,
4057 &hFile,
4058 &pRegisteredFile
4059 );
4060
4061 //
4062 // pFileObject is invalidated by the call to RegisterObject, so NULL it
4063 // out here to ensure that we don't try to release a reference on
4064 // it down the line.
4065 //
4066
4067 pFileObject = NULL;
4068
4069done:
4070
4071 if (NULL != pDataLock)
4072 {
4073 pDataLock->ReleaseLock(pThread, TRUE);
4074 }
4075
4076 if (NULL != pFileObject)
4077 {
4078 pFileObject->ReleaseReference(pThread);
4079 }
4080
4081 if (NULL != pRegisteredFile)
4082 {
4083 pRegisteredFile->ReleaseReference(pThread);
4084 }
4085
4086 if (NO_ERROR == palError)
4087 {
4088 *pStd = hFile;
4089 }
4090 else if (-1 != new_fd)
4091 {
4092 close(new_fd);
4093 }
4094
4095 return hFile;
4096}
4097
4098
4099/*++
4100FILEInitStdHandles
4101
4102Create handle objects for stdin, stdout and stderr
4103
4104(no parameters)
4105
4106Return value:
4107 TRUE on success, FALSE on failure
4108--*/
4109BOOL FILEInitStdHandles(void)
4110{
4111 HANDLE stdin_handle;
4112 HANDLE stdout_handle;
4113 HANDLE stderr_handle;
4114
4115 TRACE("creating handle objects for stdin, stdout, stderr\n");
4116
4117 stdin_handle = init_std_handle(&pStdIn, stdin);
4118 if(INVALID_HANDLE_VALUE == stdin_handle)
4119 {
4120 ERROR("failed to create stdin handle\n");
4121 goto fail;
4122 }
4123
4124 stdout_handle = init_std_handle(&pStdOut, stdout);
4125 if(INVALID_HANDLE_VALUE == stdout_handle)
4126 {
4127 ERROR("failed to create stdout handle\n");
4128 CloseHandle(stdin_handle);
4129 goto fail;
4130 }
4131
4132 stderr_handle = init_std_handle(&pStdErr, stderr);
4133 if(INVALID_HANDLE_VALUE == stderr_handle)
4134 {
4135 ERROR("failed to create stderr handle\n");
4136 CloseHandle(stdin_handle);
4137 CloseHandle(stdout_handle);
4138 goto fail;
4139 }
4140 return TRUE;
4141
4142fail:
4143 pStdIn = INVALID_HANDLE_VALUE;
4144 pStdOut = INVALID_HANDLE_VALUE;
4145 pStdErr = INVALID_HANDLE_VALUE;
4146 return FALSE;
4147}
4148
4149/*++
4150FILECleanupStdHandles
4151
4152Remove all regions, locked by a file pointer, from shared memory
4153
4154(no parameters)
4155
4156--*/
4157void FILECleanupStdHandles(void)
4158{
4159 HANDLE stdin_handle;
4160 HANDLE stdout_handle;
4161 HANDLE stderr_handle;
4162
4163 TRACE("closing standard handles\n");
4164 stdin_handle = pStdIn;
4165 stdout_handle = pStdOut;
4166 stderr_handle = pStdErr;
4167
4168 pStdIn = INVALID_HANDLE_VALUE;
4169 pStdOut = INVALID_HANDLE_VALUE;
4170 pStdErr = INVALID_HANDLE_VALUE;
4171
4172 if (stdin_handle != INVALID_HANDLE_VALUE)
4173 {
4174 CloseHandle(stdin_handle);
4175 }
4176
4177 if (stdout_handle != INVALID_HANDLE_VALUE)
4178 {
4179 CloseHandle(stdout_handle);
4180 }
4181
4182 if (stderr_handle != INVALID_HANDLE_VALUE)
4183 {
4184 CloseHandle(stderr_handle);
4185 }
4186}
4187
4188/*++
4189Function:
4190 GetFileInformationByHandle
4191
4192See MSDN doc.
4193--*/
4194BOOL
4195PALAPI
4196GetFileInformationByHandle(
4197 IN HANDLE hFile,
4198 OUT LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
4199{
4200 CPalThread *pThread;
4201 BOOL bRet = FALSE;
4202 DWORD dwLastError = 0;
4203
4204 IPalObject *pFileObject = NULL;
4205 CFileProcessLocalData *pLocalData = NULL;
4206 IDataLock *pLocalDataLock = NULL;
4207
4208 DWORD dwAttr = 0;
4209 struct stat stat_data;
4210
4211 PERF_ENTRY(GetFileInformationByHandle);
4212 ENTRY("GetFileInformationByHandle(hFile=%p, lpFileInformation=%p)\n",
4213 hFile, lpFileInformation);
4214
4215 pThread = InternalGetCurrentThread();
4216
4217 if (INVALID_HANDLE_VALUE == hFile)
4218 {
4219 ERROR( "Invalid file handle\n" );
4220 dwLastError = ERROR_INVALID_HANDLE;
4221 goto done;
4222 }
4223
4224 dwLastError = g_pObjectManager->ReferenceObjectByHandle(
4225 pThread,
4226 hFile,
4227 &aotFile,
4228 GENERIC_READ,
4229 &pFileObject
4230 );
4231
4232 if (NO_ERROR != dwLastError)
4233 {
4234 goto done;
4235 }
4236
4237 dwLastError = pFileObject->GetProcessLocalData(
4238 pThread,
4239 ReadLock,
4240 &pLocalDataLock,
4241 reinterpret_cast<void**>(&pLocalData)
4242 );
4243
4244 if (NO_ERROR != dwLastError)
4245 {
4246 goto done;
4247 }
4248
4249 if ( fstat(pLocalData->unix_fd, &stat_data) != 0 )
4250 {
4251 if ((dwLastError = FILEGetLastErrorFromErrno()) == ERROR_INTERNAL_ERROR)
4252 {
4253 ASSERT("fstat() not expected to fail with errno:%d (%s)\n",
4254 errno, strerror(errno));
4255 }
4256 goto done;
4257 }
4258
4259 if ( (stat_data.st_mode & S_IFMT) == S_IFDIR )
4260 {
4261 dwAttr |= FILE_ATTRIBUTE_DIRECTORY;
4262 }
4263 else if ( (stat_data.st_mode & S_IFMT) != S_IFREG )
4264 {
4265 ERROR("Not a regular file or directory, S_IFMT is %#x\n",
4266 stat_data.st_mode & S_IFMT);
4267 dwLastError = ERROR_ACCESS_DENIED;
4268 goto done;
4269 }
4270
4271 if ( UTIL_IsReadOnlyBitsSet( &stat_data ) )
4272 {
4273 dwAttr |= FILE_ATTRIBUTE_READONLY;
4274 }
4275
4276 /* finally, if nothing is set... */
4277 if ( dwAttr == 0 )
4278 {
4279 dwAttr = FILE_ATTRIBUTE_NORMAL;
4280 }
4281
4282 lpFileInformation->dwFileAttributes = dwAttr;
4283
4284 /* get the file times */
4285 lpFileInformation->ftCreationTime =
4286 FILEUnixTimeToFileTime( stat_data.st_ctime,
4287 ST_CTIME_NSEC(&stat_data) );
4288 lpFileInformation->ftLastAccessTime =
4289 FILEUnixTimeToFileTime( stat_data.st_atime,
4290 ST_ATIME_NSEC(&stat_data) );
4291 lpFileInformation->ftLastWriteTime =
4292 FILEUnixTimeToFileTime( stat_data.st_mtime,
4293 ST_MTIME_NSEC(&stat_data) );
4294
4295 /* if Unix mtime is greater than atime, return mtime
4296 as the last access time */
4297 if (CompareFileTime(&lpFileInformation->ftLastAccessTime,
4298 &lpFileInformation->ftLastWriteTime) < 0)
4299 {
4300 lpFileInformation->ftLastAccessTime = lpFileInformation->ftLastWriteTime;
4301 }
4302
4303 /* if Unix ctime is greater than mtime, return mtime
4304 as the create time */
4305 if (CompareFileTime(&lpFileInformation->ftLastWriteTime,
4306 &lpFileInformation->ftCreationTime) < 0)
4307 {
4308 lpFileInformation->ftCreationTime = lpFileInformation->ftLastWriteTime;
4309 }
4310
4311 lpFileInformation->dwVolumeSerialNumber = stat_data.st_dev;
4312
4313 /* Get the file size. GetFileSize is not used because it gets the
4314 size of an already-open file */
4315 lpFileInformation->nFileSizeLow = (DWORD) stat_data.st_size;
4316#if SIZEOF_OFF_T > 4
4317 lpFileInformation->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32);
4318#else
4319 lpFileInformation->nFileSizeHigh = 0;
4320#endif
4321
4322 lpFileInformation->nNumberOfLinks = stat_data.st_nlink;
4323 lpFileInformation->nFileIndexHigh = 0;
4324 lpFileInformation->nFileIndexLow = stat_data.st_ino;
4325
4326 bRet = TRUE;
4327
4328done:
4329 if (NULL != pLocalDataLock)
4330 {
4331 pLocalDataLock->ReleaseLock(pThread, FALSE);
4332 }
4333
4334 if (NULL != pFileObject)
4335 {
4336 pFileObject->ReleaseReference(pThread);
4337 }
4338
4339 if (dwLastError) pThread->SetLastError(dwLastError);
4340
4341 LOGEXIT("GetFileInformationByHandle returns BOOL %d\n", bRet);
4342 PERF_EXIT(GetFileInformationByHandle);
4343 return bRet;
4344}
4345