// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*++ Module Name: file.cpp Abstract: Implementation of the file WIN API for the PAL --*/ #include "pal/thread.hpp" #include "pal/file.hpp" #include "shmfilelockmgr.hpp" #include "pal/malloc.hpp" #include "pal/stackstring.hpp" #include "pal/palinternal.h" #include "pal/dbgmsg.h" #include "pal/file.h" #include "pal/filetime.h" #include "pal/utils.h" #include #include #include #include #include #include #include #include #include using namespace CorUnix; SET_DEFAULT_DEBUG_CHANNEL(FILE); int MaxWCharToAcpLengthFactor = 3; PAL_ERROR InternalSetFilePointerForUnixFd( int iUnixFd, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod, PLONG lpNewFilePointerLow ); void FileCleanupRoutine( CPalThread *pThread, IPalObject *pObjectToCleanup, bool fShutdown, bool fCleanupSharedState ); CObjectType CorUnix::otFile( otiFile, FileCleanupRoutine, NULL, // No initialization routine 0, // No immutable data sizeof(CFileProcessLocalData), 0, // No shared data GENERIC_READ|GENERIC_WRITE, // Ignored -- no Win32 object security support CObjectType::SecuritySupported, CObjectType::OSPersistedSecurityInfo, CObjectType::UnnamedObject, CObjectType::LocalDuplicationOnly, CObjectType::UnwaitableObject, CObjectType::SignalingNotApplicable, CObjectType::ThreadReleaseNotApplicable, CObjectType::OwnershipNotApplicable ); CAllowedObjectTypes CorUnix::aotFile(otiFile); static CSharedMemoryFileLockMgr _FileLockManager; IFileLockManager *CorUnix::g_pFileLockManager = &_FileLockManager; void FileCleanupRoutine( CPalThread *pThread, IPalObject *pObjectToCleanup, bool fShutdown, bool fCleanupSharedState ) { PAL_ERROR palError; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; palError = pObjectToCleanup->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { ASSERT("Unable to obtain data to cleanup file object"); return; } if (pLocalData->pLockController != NULL) { pLocalData->pLockController->ReleaseController(); } if (!fShutdown && -1 != pLocalData->unix_fd) { close(pLocalData->unix_fd); } pLocalDataLock->ReleaseLock(pThread, FALSE); } typedef enum { PIID_STDIN_HANDLE, PIID_STDOUT_HANDLE, PIID_STDERR_HANDLE } PROCINFO_ID; #define PAL_LEGAL_FLAGS_ATTRIBS (FILE_ATTRIBUTE_NORMAL| \ FILE_FLAG_SEQUENTIAL_SCAN| \ FILE_FLAG_WRITE_THROUGH| \ FILE_FLAG_NO_BUFFERING| \ FILE_FLAG_RANDOM_ACCESS| \ FILE_FLAG_BACKUP_SEMANTICS) /* Static global. The init function must be called before any other functions and if it is not successful, no other functions should be done. */ static HANDLE pStdIn = INVALID_HANDLE_VALUE; static HANDLE pStdOut = INVALID_HANDLE_VALUE; static HANDLE pStdErr = INVALID_HANDLE_VALUE; /*++ Function : FILEGetProperNotFoundError Returns the proper error code, based on the Windows behavior. IN LPSTR lpPath - The path to check. LPDWORD lpErrorCode - The error to set. */ void FILEGetProperNotFoundError( LPSTR lpPath, LPDWORD lpErrorCode ) { struct stat stat_data; LPSTR lpDupedPath = NULL; LPSTR lpLastPathSeparator = NULL; TRACE( "FILEGetProperNotFoundError( %s )\n", lpPath?lpPath:"(null)" ); if ( !lpErrorCode ) { ASSERT( "lpErrorCode has to be valid\n" ); return; } if ( NULL == ( lpDupedPath = InternalStrdup( lpPath ) ) ) { ERROR( "InternalStrdup() failed!\n" ); *lpErrorCode = ERROR_NOT_ENOUGH_MEMORY; return; } /* Determine whether it's a file not found or path not found. */ lpLastPathSeparator = strrchr( lpDupedPath, '/'); if ( lpLastPathSeparator != NULL ) { *lpLastPathSeparator = '\0'; /* If the last path component is a directory, we return file not found. If it's a file or doesn't exist, we return path not found. */ if ( '\0' == *lpDupedPath || ( stat( lpDupedPath, &stat_data ) == 0 && ( stat_data.st_mode & S_IFMT ) == S_IFDIR ) ) { TRACE( "ERROR_FILE_NOT_FOUND\n" ); *lpErrorCode = ERROR_FILE_NOT_FOUND; } else { TRACE( "ERROR_PATH_NOT_FOUND\n" ); *lpErrorCode = ERROR_PATH_NOT_FOUND; } } else { TRACE( "ERROR_FILE_NOT_FOUND\n" ); *lpErrorCode = ERROR_FILE_NOT_FOUND; } InternalFree(lpDupedPath); lpDupedPath = NULL; TRACE( "FILEGetProperNotFoundError returning TRUE\n" ); return; } /*++ Function : FILEGetLastErrorFromErrnoAndFilename Returns the proper error code for errno, or, if errno is ENOENT, based on the Windows behavior for nonexistent filenames. IN LPSTR lpPath - The path to check. */ PAL_ERROR FILEGetLastErrorFromErrnoAndFilename(LPSTR lpPath) { PAL_ERROR palError; if (ENOENT == errno) { FILEGetProperNotFoundError(lpPath, &palError); } else { palError = FILEGetLastErrorFromErrno(); } return palError; } /*++ InternalCanonicalizeRealPath Wraps realpath() to hide platform differences. See the man page for realpath(3) for details of how realpath() works. On systems on which realpath() allows the last path component to not exist, this is a straight thunk through to realpath(). On other systems, we remove the last path component, then call realpath(). cch is the size of lpBuffer and has to be atleast PATH_MAX (since realpath() requires the buffer to be atleast PATH_MAX). --*/ PAL_ERROR CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, LPSTR lpBuffer, DWORD cch) { PAL_ERROR palError = NO_ERROR; LPSTR lpRealPath = NULL; #if !REALPATH_SUPPORTS_NONEXISTENT_FILES LPSTR lpExistingPath = NULL; LPSTR pchSeparator = NULL; LPSTR lpFilename = NULL; DWORD cchBuffer = 0; DWORD cchFilename = 0; #endif // !REALPATH_SUPPORTS_NONEXISTENT_FILES if ( (lpUnixPath == NULL) || (lpBuffer == NULL) || (cch < PATH_MAX) ) { ERROR ("Invalid argument to InternalCanonicalizeRealPath\n"); palError = ERROR_INVALID_PARAMETER; goto LExitDontFree; } #if REALPATH_SUPPORTS_NONEXISTENT_FILES lpRealPath = realpath(lpUnixPath, lpBuffer); #else // !REALPATH_SUPPORTS_NONEXISTENT_FILES lpExistingPath = InternalStrdup(lpUnixPath); if (lpExistingPath == NULL) { ERROR ("InternalStrdup failed with error %d\n", errno); palError = ERROR_NOT_ENOUGH_MEMORY; goto LExit; } pchSeparator = strrchr(lpExistingPath, '/'); if (pchSeparator == NULL) { char pszCwdBuffer[MAXPATHLEN+1]; // MAXPATHLEN is for getcwd() DWORD cchCwdBuffer = sizeof(pszCwdBuffer)/sizeof(pszCwdBuffer[0]); if (getcwd(pszCwdBuffer, cchCwdBuffer) == NULL) { WARN("getcwd(NULL) failed with error %d\n", errno); palError = DIRGetLastErrorFromErrno(); goto LExit; } lpRealPath = realpath(pszCwdBuffer, lpBuffer); if (lpRealPath == NULL) { WARN("realpath() failed with error %d\n", errno); palError = FILEGetLastErrorFromErrno(); #if defined(_AMD64_) // If we are here, then we tried to invoke realpath // against a directory. // // On Mac64, realpath implementation differs from Mac32 // by *not* supporting invalid filenames in the path (while // Mac32 implementation does). // // Thus, if we are here, and the error code we see is // ERROR_FILE_NOT_FOUND, then we should map it to // ERROR_PATH_NOT_FOUND since it was a directory that // was not found (and not a file). if (palError == ERROR_FILE_NOT_FOUND) { // Since lpBuffer can be modified by the realpath call, // and can result in a truncated subset of the original buffer, // we use strstr as a level of safety. if (strstr(pszCwdBuffer, lpBuffer) != 0) { palError = ERROR_PATH_NOT_FOUND; } } #endif // defined(_AMD64_) goto LExit; } lpFilename = lpExistingPath; } else { #if defined(_AMD64_) bool fSetFilename = true; // Since realpath implementation cannot handle inexistent filenames, // check if we are going to truncate the "/" corresponding to the // root folder (e.g. case of "/Volumes"). If so: // // 1) Set the seperator to point to the NULL terminator of the specified // file/folder name. // // 2) Null terminate lpBuffer // // 3) Since there is no explicit filename component in lpExistingPath (as // we only have "/" corresponding to the root), set lpFilename to NULL, // alongwith a flag indicating that it has already been set. if (pchSeparator == lpExistingPath) { pchSeparator = lpExistingPath+strlen(lpExistingPath); // Set the lpBuffer to NULL lpBuffer[0] = '\0'; lpFilename = NULL; fSetFilename = false; } else #endif // defined(_AMD64_) *pchSeparator = '\0'; lpRealPath = realpath(lpExistingPath, lpBuffer); if (lpRealPath == NULL) { WARN("realpath() failed with error %d\n", errno); palError = FILEGetLastErrorFromErrno(); #if defined(_AMD64_) // If we are here, then we tried to invoke realpath // against a directory after stripping out the filename // from the original path. // // On Mac64, realpath implementation differs from Mac32 // by *not* supporting invalid filenames in the path (while // Mac32 implementation does). // // Thus, if we are here, and the error code we see is // ERROR_FILE_NOT_FOUND, then we should map it to // ERROR_PATH_NOT_FOUND since it was a directory that // was not found (and not a file). if (palError == ERROR_FILE_NOT_FOUND) { // Since lpBuffer can be modified by the realpath call, // and can result in a truncated subset of the original buffer, // we use strstr as a level of safety. if (strstr(lpExistingPath, lpBuffer) != 0) { palError = ERROR_PATH_NOT_FOUND; } } #endif // defined(_AMD64_) goto LExit; } #if defined(_AMD64_) if (fSetFilename == true) #endif // defined(_AMD64_) lpFilename = pchSeparator + 1; } cchBuffer = strlen(lpBuffer); #if defined(_AMD64_) if (lpFilename == NULL) cchFilename = 0; else #endif // _AMD64_ cchFilename = strlen(lpFilename); // Append the file name. // 2 is for "/" and NULL if (cchBuffer + cchFilename + 2 > cch) { ERROR ("Provided buffer size (%d) is smaller than the required (%d)\n", cch, cchBuffer + cchFilename + 2); palError = ERROR_INSUFFICIENT_BUFFER; lpRealPath = NULL; goto LExit; } lpBuffer[cchBuffer] = '/'; lpBuffer[cchBuffer+1] = '\0'; #if defined(_AMD64_) if (lpFilename == NULL) goto LExit; #endif // _AMD64_ if (strcat_s(lpBuffer, cch, lpFilename) != SAFECRT_SUCCESS) { ERROR ("strcat_s failed!\n"); palError = ERROR_INSUFFICIENT_BUFFER; lpRealPath = NULL; // Doing a goto here since we want to exit now. This will work // incase someone else adds another if clause below us. goto LExit; } LExit: if (lpExistingPath != NULL) { InternalFree(lpExistingPath); } #endif // REALPATH_SUPPORTS_NONEXISTENT_FILES LExitDontFree: if ((palError == NO_ERROR) && (lpRealPath == NULL)) { // convert all these into ERROR_PATH_NOT_FOUND palError = ERROR_PATH_NOT_FOUND; } return palError; } PAL_ERROR CorUnix::InternalCreateFile( CPalThread *pThread, LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, HANDLE *phFile ) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; IPalObject *pRegisteredFile = NULL; IDataLock *pDataLock = NULL; CFileProcessLocalData *pLocalData = NULL; IFileLockController *pLockController = NULL; CObjectAttributes oaFile(NULL, lpSecurityAttributes); BOOL fFileExists = FALSE; BOOL inheritable = FALSE; LPSTR lpUnixPath = NULL; int filed = -1; int create_flags = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); int open_flags = 0; int lock_mode = LOCK_SH; // track whether we've created the file with the intended name, // so that it can be removed on failure exit BOOL bFileCreated = FALSE; const char* szNonfilePrefix = "\\\\.\\"; LPSTR lpFullUnixPath = NULL; DWORD cchFullUnixPath = PATH_MAX+1; // InternalCanonicalizeRealPath requires this to be atleast PATH_MAX /* for dwShareMode only three flags are accepted */ if ( dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) ) { ASSERT( "dwShareMode is invalid\n" ); palError = ERROR_INVALID_PARAMETER; goto done; } if ( lpFileName == NULL ) { ERROR("InternalCreateFile called with NULL filename\n"); palError = ERROR_PATH_NOT_FOUND; goto done; } if ( strlen(lpFileName) >= MAX_LONGPATH ) { WARN("InternalCreateFile called with a filename whose size is " "%d >= MAX_LONGPATH (%d)\n", strlen(lpFileName), MAX_LONGPATH); palError = ERROR_FILENAME_EXCED_RANGE; goto done; } if ( strncmp(lpFileName, szNonfilePrefix, strlen(szNonfilePrefix)) == 0 ) { ERROR("InternalCreateFile does not support paths beginning with %s\n", szNonfilePrefix); palError = ERROR_INVALID_PARAMETER; goto done; } lpUnixPath = InternalStrdup(lpFileName); if ( lpUnixPath == NULL ) { ERROR("InternalStrdup() failed\n"); palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } lpFullUnixPath = reinterpret_cast(InternalMalloc(cchFullUnixPath)); if ( lpFullUnixPath == NULL ) { ERROR("InternalMalloc() failed\n"); palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } // Initialize the path to zeroes... ZeroMemory(lpFullUnixPath, cchFullUnixPath); FILEDosToUnixPathA( lpUnixPath ); // Compute the absolute pathname to the file. This pathname is used // to determine if two file names represent the same file. palError = InternalCanonicalizeRealPath(lpUnixPath, lpFullUnixPath, cchFullUnixPath); if (palError != NO_ERROR) { goto done; } InternalFree(lpUnixPath); lpUnixPath = lpFullUnixPath; lpFullUnixPath = NULL; switch( dwDesiredAccess ) { case 0: /* Device Query Access was requested. let's use open() with no flags, it's basically the equivalent of O_RDONLY, since O_RDONLY is defined as 0x0000 */ break; case( GENERIC_READ ): open_flags |= O_RDONLY; break; case( GENERIC_WRITE ): open_flags |= O_WRONLY; break; case( GENERIC_READ | GENERIC_WRITE ): open_flags |= O_RDWR; break; default: ERROR("dwDesiredAccess value of %d is invalid\n", dwDesiredAccess); palError = ERROR_INVALID_PARAMETER; goto done; } TRACE("open flags are 0x%lx\n", open_flags); if ( lpSecurityAttributes ) { if ( lpSecurityAttributes->nLength != sizeof( SECURITY_ATTRIBUTES ) || lpSecurityAttributes->lpSecurityDescriptor != NULL || !lpSecurityAttributes->bInheritHandle ) { ASSERT("lpSecurityAttributes points to invalid values.\n"); palError = ERROR_INVALID_PARAMETER; goto done; } inheritable = TRUE; } if ( (dwFlagsAndAttributes & PAL_LEGAL_FLAGS_ATTRIBS) != dwFlagsAndAttributes) { ASSERT("Bad dwFlagsAndAttributes\n"); palError = ERROR_INVALID_PARAMETER; goto done; } else if (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS) { /* Override the open flags, and always open as readonly. This flag is used when opening a directory, to change its creation/modification/access times. On Windows, the directory must be open for write, but on Unix, it needs to be readonly. */ open_flags = O_RDONLY; } else { struct stat st; if (stat(lpUnixPath, &st) == 0 && (st.st_mode & S_IFDIR)) { /* The file exists and it is a directory. Without FILE_FLAG_BACKUP_SEMANTICS, Win32 CreateFile always fails to open directories. */ palError = ERROR_ACCESS_DENIED; goto done; } } if ( hTemplateFile ) { ASSERT("hTemplateFile is not NULL, as it should be.\n"); palError = ERROR_INVALID_PARAMETER; goto done; } // // The file sharing mode checks are performed by the lock manager so we need // to get the lock controller for this file. // Do this before modifying the file system since we wouldn't want to, for // instance, truncate a file before finding out if we have write access to it. // It may seem odd that in some cases we will acquire a lock on a file that // doesn't exist yet but the lock manager does not care -- files are // abstract entities represented by a name from its point of view. // palError = g_pFileLockManager->GetLockControllerForFile( pThread, lpUnixPath, dwDesiredAccess, dwShareMode, &pLockController ); if (NO_ERROR != palError) { goto done; } /* NB: According to MSDN docs, When CREATE_ALWAYS or OPEN_ALWAYS is set, CreateFile should SetLastError to ERROR_ALREADY_EXISTS, even though/if CreateFile will be successful. */ switch( dwCreationDisposition ) { case( CREATE_ALWAYS ): // check whether the file exists if ( access( lpUnixPath, F_OK ) == 0 ) { fFileExists = TRUE; } open_flags |= O_CREAT | O_TRUNC; break; case( CREATE_NEW ): open_flags |= O_CREAT | O_EXCL; break; case( OPEN_EXISTING ): /* don't need to do anything here */ break; case( OPEN_ALWAYS ): if ( access( lpUnixPath, F_OK ) == 0 ) { fFileExists = TRUE; } open_flags |= O_CREAT; break; case( TRUNCATE_EXISTING ): open_flags |= O_TRUNC; break; default: ASSERT("dwCreationDisposition value of %d is not valid\n", dwCreationDisposition); palError = ERROR_INVALID_PARAMETER; goto done; } if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING ) { TRACE("I/O will be unbuffered\n"); #ifdef O_DIRECT open_flags |= O_DIRECT; #endif } else { TRACE("I/O will be buffered\n"); } filed = InternalOpen(lpUnixPath, open_flags, create_flags); TRACE("Allocated file descriptor [%d]\n", filed); if ( filed < 0 ) { TRACE("open() failed; error is %s (%d)\n", strerror(errno), errno); palError = FILEGetLastErrorFromErrnoAndFilename(lpUnixPath); goto done; } // Deduce whether we created a file in the previous operation (there's a // small timing window between the access() used to determine fFileExists // and the open() operation, but there's not much we can do about that. bFileCreated = (dwCreationDisposition == CREATE_ALWAYS || dwCreationDisposition == CREATE_NEW || dwCreationDisposition == OPEN_ALWAYS) && !fFileExists; // While the lock manager is able to provide support for share modes within an instance of // the PAL, other PALs will ignore these locks. In order to support a basic level of cross // process locking, we'll use advisory locks. FILE_SHARE_NONE implies a exclusive lock on the // file and all other modes use a shared lock. While this is not as granular as Windows, // you can atleast implement a lock file using this. lock_mode = (dwShareMode == 0 /* FILE_SHARE_NONE */) ? LOCK_EX : LOCK_SH; if(flock(filed, lock_mode | LOCK_NB) != 0) { TRACE("flock() failed; error is %s (%d)\n", strerror(errno), errno); if (errno == EWOULDBLOCK) { palError = ERROR_SHARING_VIOLATION; } else { palError = FILEGetLastErrorFromErrno(); } goto done; } #ifndef O_DIRECT if ( dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING ) { #ifdef F_NOCACHE if (-1 == fcntl(filed, F_NOCACHE, 1)) { ASSERT("Can't set F_NOCACHE; fcntl() failed. errno is %d (%s)\n", errno, strerror(errno)); palError = ERROR_INTERNAL_ERROR; goto done; } #else #error Insufficient support for uncached I/O on this platform #endif } #endif /* make file descriptor close-on-exec; inheritable handles will get "uncloseonexeced" in CreateProcess if they are actually being inherited*/ if(-1 == fcntl(filed,F_SETFD,1)) { ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d " "(%s)\n", errno, strerror(errno)); palError = ERROR_INTERNAL_ERROR; goto done; } palError = g_pObjectManager->AllocateObject( pThread, &otFile, &oaFile, &pFileObject ); if (NO_ERROR != palError) { goto done; } palError = pFileObject->GetProcessLocalData( pThread, WriteLock, &pDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto done; } if (strcpy_s(pLocalData->unix_filename, sizeof(pLocalData->unix_filename), lpUnixPath) != SAFECRT_SUCCESS) { palError = ERROR_INSUFFICIENT_BUFFER; TRACE("strcpy_s failed!\n"); goto done; } pLocalData->inheritable = inheritable; pLocalData->unix_fd = filed; pLocalData->dwDesiredAccess = dwDesiredAccess; pLocalData->open_flags = open_flags; pLocalData->open_flags_deviceaccessonly = (dwDesiredAccess == 0); // // Transfer the lock controller reference from our local variable // to the local file data // pLocalData->pLockController = pLockController; pLockController = NULL; // // We've finished initializing our local data, so release that lock // pDataLock->ReleaseLock(pThread, TRUE); pDataLock = NULL; palError = g_pObjectManager->RegisterObject( pThread, pFileObject, &aotFile, dwDesiredAccess, phFile, &pRegisteredFile ); // // pFileObject is invalidated by the call to RegisterObject, so NULL it // out here to ensure that we don't try to release a reference on // it down the line. // pFileObject = NULL; done: // At this point, if we've been successful, palError will be NO_ERROR. // CreateFile can return ERROR_ALREADY_EXISTS in some success cases; // those cases are flagged by fFileExists and are handled below. if (NO_ERROR != palError) { if (filed >= 0) { close(filed); } if (bFileCreated) { if (-1 == unlink(lpUnixPath)) { WARN("can't delete file; unlink() failed with errno %d (%s)\n", errno, strerror(errno)); } } } if (NULL != pLockController) { pLockController->ReleaseController(); } if (NULL != pDataLock) { pDataLock->ReleaseLock(pThread, TRUE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } if (NULL != pRegisteredFile) { pRegisteredFile->ReleaseReference(pThread); } if (NULL != lpUnixPath) { InternalFree(lpUnixPath); } if (NULL != lpFullUnixPath) { InternalFree(lpFullUnixPath); } if (NO_ERROR == palError && fFileExists) { palError = ERROR_ALREADY_EXISTS; } return palError; } /*++ Function: CreateFileA Note: Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct. Desired access is READ, WRITE or 0 Share mode is READ, WRITE or DELETE See MSDN doc. --*/ HANDLE PALAPI CreateFileA( IN LPCSTR lpFileName, IN DWORD dwDesiredAccess, IN DWORD dwShareMode, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwCreationDisposition, IN DWORD dwFlagsAndAttributes, IN HANDLE hTemplateFile ) { CPalThread *pThread; PAL_ERROR palError = NO_ERROR; HANDLE hRet = INVALID_HANDLE_VALUE; PERF_ENTRY(CreateFileA); ENTRY("CreateFileA(lpFileName=%p (%s), dwAccess=%#x, dwShareMode=%#x, " "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, " "hTemplateFile=%p )\n",lpFileName?lpFileName:"NULL",lpFileName?lpFileName:"NULL", dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); pThread = InternalGetCurrentThread(); palError = InternalCreateFile( pThread, lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, &hRet ); // // We always need to set last error, even on success: // we need to protect ourselves from the situation // where last error is set to ERROR_ALREADY_EXISTS on // entry to the function // pThread->SetLastError(palError); LOGEXIT("CreateFileA returns HANDLE %p\n", hRet); PERF_EXIT(CreateFileA); return hRet; } /*++ Function: CreateFileW Note: Only bInherit flag is used from the LPSECURITY_ATTRIBUTES struct. Desired access is READ, WRITE or 0 Share mode is READ, WRITE or DELETE See MSDN doc. --*/ HANDLE PALAPI CreateFileW( IN LPCWSTR lpFileName, IN DWORD dwDesiredAccess, IN DWORD dwShareMode, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwCreationDisposition, IN DWORD dwFlagsAndAttributes, IN HANDLE hTemplateFile) { CPalThread *pThread; PAL_ERROR palError = NO_ERROR; PathCharString namePathString; char * name; int size; int length = 0; HANDLE hRet = INVALID_HANDLE_VALUE; PERF_ENTRY(CreateFileW); ENTRY("CreateFileW(lpFileName=%p (%S), dwAccess=%#x, dwShareMode=%#x, " "lpSecurityAttr=%p, dwDisposition=%#x, dwFlags=%#x, hTemplateFile=%p )\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); pThread = InternalGetCurrentThread(); if (lpFileName != NULL) { length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor; } name = namePathString.OpenStringBuffer(length); if (NULL == name) { palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length, NULL, NULL ); namePathString.CloseBuffer(size); if( size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); palError = ERROR_FILENAME_EXCED_RANGE; } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); palError = ERROR_INTERNAL_ERROR; } goto done; } palError = InternalCreateFile( pThread, name, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, &hRet ); // // We always need to set last error, even on success: // we need to protect ourselves from the situation // where last error is set to ERROR_ALREADY_EXISTS on // entry to the function // done: pThread->SetLastError(palError); LOGEXIT( "CreateFileW returns HANDLE %p\n", hRet ); PERF_EXIT(CreateFileW); return hRet; } /*++ Function: CopyFileW See MSDN doc. Notes: There are several (most) error paths here that do not call SetLastError(). This is because we know that CreateFile, ReadFile, and WriteFile will do so, and will have a much better idea of the specific error. --*/ BOOL PALAPI CopyFileW( IN LPCWSTR lpExistingFileName, IN LPCWSTR lpNewFileName, IN BOOL bFailIfExists) { CPalThread *pThread; PathCharString sourcePathString; PathCharString destPathString; char * source; char * dest; int src_size, dest_size, length = 0; BOOL bRet = FALSE; PERF_ENTRY(CopyFileW); ENTRY("CopyFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), bFailIfExists=%d)\n", lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING, bFailIfExists); pThread = InternalGetCurrentThread(); if (lpExistingFileName != NULL) { length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor; } source = sourcePathString.OpenStringBuffer(length); if (NULL == source) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length, NULL, NULL ); sourcePathString.CloseBuffer(src_size); if( src_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpExistingFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } goto done; } length = 0; if (lpNewFileName != NULL) { length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor; } dest = destPathString.OpenStringBuffer(length); if (NULL == dest) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length, NULL, NULL ); destPathString.CloseBuffer(dest_size); if( dest_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpNewFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } goto done; } bRet = CopyFileA(source,dest,bFailIfExists); done: LOGEXIT("CopyFileW returns BOOL %d\n", bRet); PERF_EXIT(CopyFileW); return bRet; } /*++ Function: DeleteFileA See MSDN doc. --*/ BOOL PALAPI DeleteFileA( IN LPCSTR lpFileName) { PAL_ERROR palError = NO_ERROR; DWORD dwShareMode = SHARE_MODE_NOT_INITALIZED; CPalThread *pThread; int result; BOOL bRet = FALSE; DWORD dwLastError = 0; char * lpUnixFileName; int length; PathCharString lpUnixFileNamePS; LPSTR lpFullUnixFileName = NULL; DWORD cchFullUnixFileName = MAX_LONGPATH+1;// InternalCanonicalizeRealPath requires this to be atleast PATH_MAX PERF_ENTRY(DeleteFileA); ENTRY("DeleteFileA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL"); pThread = InternalGetCurrentThread(); length = strlen(lpFileName); lpUnixFileName = lpUnixFileNamePS.OpenStringBuffer(length); if (NULL == lpUnixFileName) { palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } strcpy_s( lpUnixFileName, lpUnixFileNamePS.GetSizeOf(), lpFileName); lpUnixFileNamePS.CloseBuffer(length); FILEDosToUnixPathA( lpUnixFileName ); if ( !FILEGetFileNameFromSymLink(lpUnixFileName)) { dwLastError = FILEGetLastErrorFromErrnoAndFilename(lpUnixFileName); goto done; } lpFullUnixFileName = reinterpret_cast(InternalMalloc(cchFullUnixFileName)); if ( lpFullUnixFileName == NULL ) { ERROR("InternalMalloc() failed\n"); palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } // Initialize the path to zeroes... ZeroMemory(lpFullUnixFileName, cchFullUnixFileName); // Compute the absolute pathname to the file. This pathname is used // to determine if two file names represent the same file. palError = InternalCanonicalizeRealPath(lpUnixFileName, lpFullUnixFileName, cchFullUnixFileName); if (palError != NO_ERROR) { InternalFree(lpFullUnixFileName); lpFullUnixFileName = InternalStrdup(lpUnixFileName); if (!lpFullUnixFileName) { palError = ERROR_NOT_ENOUGH_MEMORY; goto done; } } palError = g_pFileLockManager->GetFileShareModeForFile(lpFullUnixFileName, &dwShareMode); // Use unlink if we succesfully found the file to be opened with // a FILE_SHARE_DELETE mode. // Note that there is a window here where a race condition can occur: // the check for the sharing mode and the unlink are two separate actions // (not a single atomic action). So it's possible that between the check // happening and the unlink happening, the file may have been closed. If // it is just closed and not re-opened, no problems. // If it is closed and re-opened without any sharing, we should be calling // InternalDelete instead which would have failed. // Instead, we call unlink which will succeed. if (palError == NO_ERROR && dwShareMode != SHARE_MODE_NOT_INITALIZED && (dwShareMode & FILE_SHARE_DELETE) != 0) { result = unlink( lpFullUnixFileName ); } else { result = InternalDeleteFile( lpFullUnixFileName ); } if ( result < 0 ) { TRACE("unlink returns %d\n", result); dwLastError = FILEGetLastErrorFromErrnoAndFilename(lpFullUnixFileName); } else { bRet = TRUE; } done: if(dwLastError) { pThread->SetLastError( dwLastError ); } if (NULL != lpFullUnixFileName) { InternalFree(lpFullUnixFileName); } LOGEXIT("DeleteFileA returns BOOL %d\n", bRet); PERF_EXIT(DeleteFileA); return bRet; } /*++ Function: DeleteFileW See MSDN doc. --*/ BOOL PALAPI DeleteFileW( IN LPCWSTR lpFileName) { CPalThread *pThread; int size; PathCharString namePS; char * name; int length = 0; BOOL bRet = FALSE; PERF_ENTRY(DeleteFileW); ENTRY("DeleteFileW(lpFileName=%p (%S))\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING); pThread = InternalGetCurrentThread(); if (lpFileName != NULL) { length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor; } name = namePS.OpenStringBuffer(length); if (NULL == name) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length, NULL, NULL ); namePS.CloseBuffer(size); if( size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpFilePathName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } bRet = FALSE; goto done; } bRet = DeleteFileA( name ); done: LOGEXIT("DeleteFileW returns BOOL %d\n", bRet); PERF_EXIT(DeleteFileW); return bRet; } /*++ Function: MoveFileA See MSDN doc. --*/ BOOL PALAPI MoveFileA( IN LPCSTR lpExistingFileName, IN LPCSTR lpNewFileName) { BOOL bRet; PERF_ENTRY(MoveFileA); ENTRY("MoveFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s))\n", lpExistingFileName?lpExistingFileName:"NULL", lpExistingFileName?lpExistingFileName:"NULL", lpNewFileName?lpNewFileName:"NULL", lpNewFileName?lpNewFileName:"NULL"); bRet = MoveFileExA( lpExistingFileName, lpNewFileName, MOVEFILE_COPY_ALLOWED ); LOGEXIT("MoveFileA returns BOOL %d\n", bRet); PERF_EXIT(MoveFileA); return bRet; } /*++ Function: MoveFileW See MSDN doc. --*/ BOOL PALAPI MoveFileW( IN LPCWSTR lpExistingFileName, IN LPCWSTR lpNewFileName) { BOOL bRet; PERF_ENTRY(MoveFileW); ENTRY("MoveFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S))\n", lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING); bRet = MoveFileExW( lpExistingFileName, lpNewFileName, MOVEFILE_COPY_ALLOWED ); LOGEXIT("MoveFileW returns BOOL %d\n", bRet); PERF_EXIT(MoveFileW); return bRet; } /*++ Function: MoveFileExA See MSDN doc. --*/ BOOL PALAPI MoveFileExA( IN LPCSTR lpExistingFileName, IN LPCSTR lpNewFileName, IN DWORD dwFlags) { CPalThread *pThread; int result; int length = 0; PathCharString sourcePS; PathCharString destPS; char * source; char * dest; BOOL bRet = TRUE; DWORD dwLastError = 0; PERF_ENTRY(MoveFileExA); ENTRY("MoveFileExA(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), " "dwFlags=%#x)\n", lpExistingFileName?lpExistingFileName:"NULL", lpExistingFileName?lpExistingFileName:"NULL", lpNewFileName?lpNewFileName:"NULL", lpNewFileName?lpNewFileName:"NULL", dwFlags); pThread = InternalGetCurrentThread(); /* only two flags are accepted */ if ( dwFlags & ~(MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) ) { ASSERT( "dwFlags is invalid\n" ); dwLastError = ERROR_INVALID_PARAMETER; goto done; } length = strlen(lpExistingFileName); source = sourcePS.OpenStringBuffer(length); if (NULL == source) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } strcpy_s( source, sourcePS.GetSizeOf(), lpExistingFileName); sourcePS.CloseBuffer(length); FILEDosToUnixPathA( source ); length = strlen(lpNewFileName); dest = destPS.OpenStringBuffer(length); if (NULL == dest) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } strcpy_s( dest, destPS.GetSizeOf(), lpNewFileName); destPS.CloseBuffer(length); FILEDosToUnixPathA( dest ); if ( !FILEGetFileNameFromSymLink(source)) { TRACE( "FILEGetFileNameFromSymLink failed\n" ); dwLastError = FILEGetLastErrorFromErrnoAndFilename(source); goto done; } if ( !(dwFlags & MOVEFILE_REPLACE_EXISTING) ) { #if HAVE_CASE_SENSITIVE_FILESYSTEM if ( strcmp(source, dest) != 0 ) #else // HAVE_CASE_SENSITIVE_FILESYSTEM if ( strcasecmp(source, dest) != 0 ) #endif // HAVE_CASE_SENSITIVE_FILESYSTEM { // Let things proceed normally if source and // dest are the same. if ( access(dest, F_OK) == 0 ) { dwLastError = ERROR_ALREADY_EXISTS; goto done; } } } result = rename( source, dest ); if ((result < 0) && (dwFlags & MOVEFILE_REPLACE_EXISTING) && ((errno == ENOTDIR) || (errno == EEXIST))) { bRet = DeleteFileA( lpNewFileName ); if ( bRet ) { result = rename( source, dest ); } else { dwLastError = GetLastError(); } } if ( result < 0 ) { switch( errno ) { case EXDEV: /* we tried to link across devices */ if ( dwFlags & MOVEFILE_COPY_ALLOWED ) { BOOL bFailIfExists = !(dwFlags & MOVEFILE_REPLACE_EXISTING); /* if CopyFile fails here, so should MoveFailEx */ bRet = CopyFileA( lpExistingFileName, lpNewFileName, bFailIfExists ); /* CopyFile should set the appropriate error */ if ( !bRet ) { dwLastError = GetLastError(); } else { if (!DeleteFileA(lpExistingFileName)) { ERROR("Failed to delete the source file\n"); dwLastError = GetLastError(); /* Delete the destination file if we're unable to delete the source file */ if (!DeleteFileA(lpNewFileName)) { ERROR("Failed to delete the destination file\n"); } } } } else { dwLastError = ERROR_ACCESS_DENIED; } break; case EINVAL: // tried to rename "." or ".." dwLastError = ERROR_SHARING_VIOLATION; break; case ENOENT: { struct stat buf; if (stat(source, &buf) == -1) { FILEGetProperNotFoundError(dest, &dwLastError); } else { dwLastError = ERROR_PATH_NOT_FOUND; } } break; default: dwLastError = FILEGetLastErrorFromErrno(); break; } } done: if ( dwLastError ) { pThread->SetLastError( dwLastError ); bRet = FALSE; } LOGEXIT( "MoveFileExA returns BOOL %d\n", bRet ); PERF_EXIT(MoveFileExA); return bRet; } /*++ Function: MoveFileExW See MSDN doc. --*/ BOOL PALAPI MoveFileExW( IN LPCWSTR lpExistingFileName, IN LPCWSTR lpNewFileName, IN DWORD dwFlags) { CPalThread *pThread; PathCharString sourcePS; PathCharString destPS; char * source; char * dest; int length = 0; int src_size,dest_size; BOOL bRet = FALSE; PERF_ENTRY(MoveFileExW); ENTRY("MoveFileExW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S), dwFlags=%#x)\n", lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpExistingFileName?lpExistingFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING, lpNewFileName?lpNewFileName:W16_NULLSTRING, dwFlags); pThread = InternalGetCurrentThread(); if (lpExistingFileName != NULL) { length = (PAL_wcslen(lpExistingFileName)+1) * MaxWCharToAcpLengthFactor; } source = sourcePS.OpenStringBuffer(length); if (NULL == source) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } src_size = WideCharToMultiByte( CP_ACP, 0, lpExistingFileName, -1, source, length, NULL, NULL ); sourcePS.CloseBuffer(src_size); if( src_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpExistingFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } goto done; } length = 0; if (lpNewFileName != NULL) { length = (PAL_wcslen(lpNewFileName)+1) * MaxWCharToAcpLengthFactor; } dest = destPS.OpenStringBuffer(length); if (NULL == dest) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } dest_size = WideCharToMultiByte( CP_ACP, 0, lpNewFileName, -1, dest, length, NULL, NULL ); destPS.CloseBuffer(dest_size); if( dest_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpNewFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } goto done; } bRet = MoveFileExA(source,dest,dwFlags); done: LOGEXIT("MoveFileExW returns BOOL %d\n", bRet); PERF_EXIT(MoveFileExW); return bRet; } /*++ Function: GetFileAttributesA Note: Checking for directory and read-only file, according to Rotor spec. Caveats: There are some important things to note about this implementation, which are due to the differences between the FAT filesystem and Unix filesystems: - fifo's, sockets, and symlinks will return -1, and GetLastError() will return ERROR_ACCESS_DENIED - if a file is write-only, or has no permissions at all, it is treated the same as if it had mode 'rw'. This is consistent with behaviour on NTFS files with the same permissions. - the following flags will never be returned: FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_HIDDEN --*/ DWORD PALAPI GetFileAttributesA( IN LPCSTR lpFileName) { CPalThread *pThread; struct stat stat_data; DWORD dwAttr = 0; DWORD dwLastError = 0; CHAR * UnixFileName; int length = 0; PathCharString UnixFileNamePS; PERF_ENTRY(GetFileAttributesA); ENTRY("GetFileAttributesA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL"); pThread = InternalGetCurrentThread(); if (lpFileName == NULL) { dwLastError = ERROR_PATH_NOT_FOUND; goto done; } length = strlen(lpFileName); UnixFileName = UnixFileNamePS.OpenStringBuffer(length); if (NULL == UnixFileName) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } strcpy_s( UnixFileName, UnixFileNamePS.GetSizeOf(), lpFileName ); UnixFileNamePS.CloseBuffer(length); FILEDosToUnixPathA( UnixFileName ); if ( stat(UnixFileName, &stat_data) != 0 ) { dwLastError = FILEGetLastErrorFromErrnoAndFilename(UnixFileName); goto done; } if ( (stat_data.st_mode & S_IFMT) == S_IFDIR ) { dwAttr |= FILE_ATTRIBUTE_DIRECTORY; } else if ( (stat_data.st_mode & S_IFMT) != S_IFREG ) { ERROR("Not a regular file or directory, S_IFMT is %#x\n", stat_data.st_mode & S_IFMT); dwLastError = ERROR_ACCESS_DENIED; goto done; } if ( UTIL_IsReadOnlyBitsSet( &stat_data ) ) { dwAttr |= FILE_ATTRIBUTE_READONLY; } /* finally, if nothing is set... */ if ( dwAttr == 0 ) { dwAttr = FILE_ATTRIBUTE_NORMAL; } done: if (dwLastError) { pThread->SetLastError(dwLastError); dwAttr = INVALID_FILE_ATTRIBUTES; } LOGEXIT("GetFileAttributesA returns DWORD %#x\n", dwAttr); PERF_EXIT(GetFileAttributesA); return dwAttr; } /*++ Function: GetFileAttributesW Note: Checking for directory and read-only file See MSDN doc. --*/ DWORD PALAPI GetFileAttributesW( IN LPCWSTR lpFileName) { CPalThread *pThread; int size; PathCharString filenamePS; int length = 0; char * filename; DWORD dwRet = (DWORD) -1; PERF_ENTRY(GetFileAttributesW); ENTRY("GetFileAttributesW(lpFileName=%p (%S))\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING); pThread = InternalGetCurrentThread(); if (lpFileName == NULL) { pThread->SetLastError(ERROR_PATH_NOT_FOUND); goto done; } length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor; filename = filenamePS.OpenStringBuffer(length); if (NULL == filename) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, filename, length, NULL, NULL ); filenamePS.CloseBuffer(size); if( size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } } else dwRet = GetFileAttributesA( filename ); done: LOGEXIT("GetFileAttributesW returns DWORD %#x\n", dwRet); PERF_EXIT(GetFileAttributesW); return dwRet; } /*++ Function: GetFileAttributesExW See MSDN doc, and notes for GetFileAttributesW. --*/ BOOL PALAPI GetFileAttributesExW( IN LPCWSTR lpFileName, IN GET_FILEEX_INFO_LEVELS fInfoLevelId, OUT LPVOID lpFileInformation) { CPalThread *pThread; BOOL bRet = FALSE; DWORD dwLastError = 0; LPWIN32_FILE_ATTRIBUTE_DATA attr_data; struct stat stat_data; char * name; PathCharString namePS; int length = 0; int size; PERF_ENTRY(GetFileAttributesExW); ENTRY("GetFileAttributesExW(lpFileName=%p (%S), fInfoLevelId=%d, " "lpFileInformation=%p)\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING, fInfoLevelId, lpFileInformation); pThread = InternalGetCurrentThread(); if ( fInfoLevelId != GetFileExInfoStandard ) { ASSERT("Unrecognized value for fInfoLevelId=%d\n", fInfoLevelId); dwLastError = ERROR_INVALID_PARAMETER; goto done; } if ( !lpFileInformation ) { ASSERT("lpFileInformation is NULL\n"); dwLastError = ERROR_INVALID_PARAMETER; goto done; } if (lpFileName == NULL) { dwLastError = ERROR_PATH_NOT_FOUND; goto done; } length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor; name = namePS.OpenStringBuffer(length); if (NULL == name) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length, NULL, NULL ); namePS.CloseBuffer(size); if( size == 0 ) { dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); dwLastError = ERROR_FILENAME_EXCED_RANGE; } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); dwLastError = ERROR_INTERNAL_ERROR; } goto done; } attr_data = (LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation; attr_data->dwFileAttributes = GetFileAttributesW(lpFileName); /* assume that GetFileAttributes will call SetLastError appropriately */ if ( attr_data->dwFileAttributes == (DWORD)-1 ) { goto done; } FILEDosToUnixPathA(name); /* do the stat */ if ( stat(name, &stat_data) != 0 ) { ERROR("stat failed on %S\n", lpFileName); dwLastError = FILEGetLastErrorFromErrnoAndFilename(name); goto done; } /* get the file times */ attr_data->ftCreationTime = FILEUnixTimeToFileTime( stat_data.st_ctime, ST_CTIME_NSEC(&stat_data) ); attr_data->ftLastAccessTime = FILEUnixTimeToFileTime( stat_data.st_atime, ST_ATIME_NSEC(&stat_data) ); attr_data->ftLastWriteTime = FILEUnixTimeToFileTime( stat_data.st_mtime, ST_MTIME_NSEC(&stat_data) ); /* Get the file size. GetFileSize is not used because it gets the size of an already-open file */ attr_data->nFileSizeLow = (DWORD) stat_data.st_size; #if SIZEOF_OFF_T > 4 attr_data->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32); #else attr_data->nFileSizeHigh = 0; #endif bRet = TRUE; done: if (dwLastError) pThread->SetLastError(dwLastError); LOGEXIT("GetFileAttributesExW returns BOOL %d\n", bRet); PERF_EXIT(GetFileAttributesExW); return bRet; } /*++ Function: SetFileAttributesW Notes: Used for setting read-only attribute on file only. --*/ BOOL PALAPI SetFileAttributesW( IN LPCWSTR lpFileName, IN DWORD dwFileAttributes) { CPalThread *pThread; char * name; PathCharString namePS; int length = 0; int size; DWORD dwLastError = 0; BOOL bRet = FALSE; PERF_ENTRY(SetFileAttributesW); ENTRY("SetFileAttributesW(lpFileName=%p (%S), dwFileAttributes=%#x)\n", lpFileName?lpFileName:W16_NULLSTRING, lpFileName?lpFileName:W16_NULLSTRING, dwFileAttributes); pThread = InternalGetCurrentThread(); if (lpFileName == NULL) { dwLastError = ERROR_PATH_NOT_FOUND; goto done; } length = (PAL_wcslen(lpFileName)+1) * MaxWCharToAcpLengthFactor; name = namePS.OpenStringBuffer(length); if (NULL == name) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } size = WideCharToMultiByte( CP_ACP, 0, lpFileName, -1, name, length, NULL, NULL ); namePS.CloseBuffer(size); if( size == 0 ) { dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpFileName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); dwLastError = ERROR_FILENAME_EXCED_RANGE; } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); dwLastError = ERROR_INVALID_PARAMETER; } goto done; } bRet = SetFileAttributesA(name,dwFileAttributes); done: if (dwLastError) pThread->SetLastError(dwLastError); LOGEXIT("SetFileAttributes returns BOOL %d\n", bRet); PERF_EXIT(SetFileAttributesW); return bRet; } PAL_ERROR CorUnix::InternalWriteFile( CPalThread *pThread, HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; IFileTransactionLock *pTransactionLock = NULL; int ifd; LONG writeOffsetStartLow = 0, writeOffsetStartHigh = 0; int res; if (NULL != lpNumberOfBytesWritten) { // // This must be set to 0 before any other error checking takes // place, per MSDN // *lpNumberOfBytesWritten = 0; } else { ASSERT( "lpNumberOfBytesWritten is NULL\n" ); palError = ERROR_INVALID_PARAMETER; goto done; } // Win32 WriteFile disallows writing to STD_INPUT_HANDLE if (hFile == INVALID_HANDLE_VALUE || hFile == pStdIn) { palError = ERROR_INVALID_HANDLE; goto done; } else if ( lpOverlapped ) { ASSERT( "lpOverlapped is not NULL, as it should be.\n" ); palError = ERROR_INVALID_PARAMETER; goto done; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_WRITE, &pFileObject ); if (NO_ERROR != palError) { goto done; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto done; } if (pLocalData->open_flags_deviceaccessonly == TRUE) { ERROR("File open for device access only\n"); palError = ERROR_ACCESS_DENIED; goto done; } ifd = pLocalData->unix_fd; // // Inform the lock controller for this file (if any) of our intention // to perform a write. (Note that pipes don't have lock controllers.) // if (NULL != pLocalData->pLockController) { /* Get the current file position to calculate the region to lock */ palError = InternalSetFilePointerForUnixFd( ifd, 0, &writeOffsetStartHigh, FILE_CURRENT, &writeOffsetStartLow ); if (NO_ERROR != palError) { ASSERT("Failed to get the current file position\n"); palError = ERROR_INTERNAL_ERROR; goto done; } palError = pLocalData->pLockController->GetTransactionLock( pThread, IFileLockController::WriteLock, writeOffsetStartLow, writeOffsetStartHigh, nNumberOfBytesToWrite, 0, &pTransactionLock ); if (NO_ERROR != palError) { ERROR("Unable to obtain write transaction lock"); goto done; } } // // Release the data lock before performing the (possibly blocking) // write call // pLocalDataLock->ReleaseLock(pThread, FALSE); pLocalDataLock = NULL; pLocalData = NULL; #if WRITE_0_BYTES_HANGS_TTY if( nNumberOfBytesToWrite == 0 && isatty(ifd) ) { res = 0; *lpNumberOfBytesWritten = 0; goto done; } #endif res = write( ifd, lpBuffer, nNumberOfBytesToWrite ); TRACE("write() returns %d\n", res); if ( res >= 0 ) { *lpNumberOfBytesWritten = res; } else { palError = FILEGetLastErrorFromErrno(); } done: if (NULL != pTransactionLock) { pTransactionLock->ReleaseLock(); } if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: WriteFileW Note: lpOverlapped always NULL. See MSDN doc. --*/ BOOL PALAPI WriteFile( IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped) { PAL_ERROR palError; CPalThread *pThread; PERF_ENTRY(WriteFile); ENTRY("WriteFile(hFile=%p, lpBuffer=%p, nToWrite=%u, lpWritten=%p, " "lpOverlapped=%p)\n", hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); pThread = InternalGetCurrentThread(); palError = InternalWriteFile( pThread, hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("WriteFile returns BOOL %d\n", NO_ERROR == palError); PERF_EXIT(WriteFile); return NO_ERROR == palError; } PAL_ERROR CorUnix::InternalReadFile( CPalThread *pThread, HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; IFileTransactionLock *pTransactionLock = NULL; int ifd; LONG readOffsetStartLow = 0, readOffsetStartHigh = 0; int res; if (NULL != lpNumberOfBytesRead) { // // This must be set to 0 before any other error checking takes // place, per MSDN // *lpNumberOfBytesRead = 0; } else { ERROR( "lpNumberOfBytesRead is NULL\n" ); palError = ERROR_INVALID_PARAMETER; goto done; } if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto done; } else if (NULL != lpOverlapped) { ASSERT( "lpOverlapped is not NULL, as it should be.\n" ); palError = ERROR_INVALID_PARAMETER; goto done; } else if (NULL == lpBuffer) { ERROR( "Invalid parameter. (lpBuffer:%p)\n", lpBuffer); palError = ERROR_NOACCESS; goto done; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto done; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto done; } if (pLocalData->open_flags_deviceaccessonly == TRUE) { ERROR("File open for device access only\n"); palError = ERROR_ACCESS_DENIED; goto done; } ifd = pLocalData->unix_fd; // // Inform the lock controller for this file (if any) of our intention // to perform a read. (Note that pipes don't have lock controllers.) // if (NULL != pLocalData->pLockController) { /* Get the current file position to calculate the region to lock */ palError = InternalSetFilePointerForUnixFd( ifd, 0, &readOffsetStartHigh, FILE_CURRENT, &readOffsetStartLow ); if (NO_ERROR != palError) { ASSERT("Failed to get the current file position\n"); palError = ERROR_INTERNAL_ERROR; goto done; } palError = pLocalData->pLockController->GetTransactionLock( pThread, IFileLockController::ReadLock, readOffsetStartLow, readOffsetStartHigh, nNumberOfBytesToRead, 0, &pTransactionLock ); if (NO_ERROR != palError) { ERROR("Unable to obtain read transaction lock"); goto done; } } // // Release the data lock before performing the (possibly blocking) // read call // pLocalDataLock->ReleaseLock(pThread, FALSE); pLocalDataLock = NULL; pLocalData = NULL; Read: TRACE("Reading from file descriptor %d\n", ifd); res = read(ifd, lpBuffer, nNumberOfBytesToRead); TRACE("read() returns %d\n", res); if (res >= 0) { *lpNumberOfBytesRead = res; } else if (errno == EINTR) { // Try to read again. goto Read; } else { palError = FILEGetLastErrorFromErrno(); } done: if (NULL != pTransactionLock) { pTransactionLock->ReleaseLock(); } if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: ReadFile Note: lpOverlapped always NULL. See MSDN doc. --*/ BOOL PALAPI ReadFile( IN HANDLE hFile, OUT LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead, IN LPOVERLAPPED lpOverlapped) { PAL_ERROR palError; CPalThread *pThread; PERF_ENTRY(ReadFile); ENTRY("ReadFile(hFile=%p, lpBuffer=%p, nToRead=%u, " "lpRead=%p, lpOverlapped=%p)\n", hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); pThread = InternalGetCurrentThread(); palError = InternalReadFile( pThread, hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("ReadFile returns BOOL %d\n", NO_ERROR == palError); PERF_EXIT(ReadFile); return NO_ERROR == palError; } /*++ Function: GetStdHandle See MSDN doc. --*/ HANDLE PALAPI GetStdHandle( IN DWORD nStdHandle) { CPalThread *pThread; HANDLE hRet = INVALID_HANDLE_VALUE; PERF_ENTRY(GetStdHandle); ENTRY("GetStdHandle(nStdHandle=%#x)\n", nStdHandle); pThread = InternalGetCurrentThread(); switch( nStdHandle ) { case STD_INPUT_HANDLE: hRet = pStdIn; break; case STD_OUTPUT_HANDLE: hRet = pStdOut; break; case STD_ERROR_HANDLE: hRet = pStdErr; break; default: ERROR("nStdHandle is invalid\n"); pThread->SetLastError(ERROR_INVALID_PARAMETER); break; } LOGEXIT("GetStdHandle returns HANDLE %p\n", hRet); PERF_EXIT(GetStdHandle); return hRet; } PAL_ERROR CorUnix::InternalSetEndOfFile( CPalThread *pThread, HANDLE hFile ) { PAL_ERROR palError = 0; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; off_t curr = 0; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalSetEndOfFileExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_WRITE, &pFileObject ); if (NO_ERROR != palError) { goto InternalSetEndOfFileExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalSetEndOfFileExit; } if (pLocalData->open_flags_deviceaccessonly == TRUE) { ERROR("File open for device access only\n"); palError = ERROR_ACCESS_DENIED; goto InternalSetEndOfFileExit; } curr = lseek(pLocalData->unix_fd, 0, SEEK_CUR); TRACE("current file pointer offset is %u\n", curr); if ( curr < 0 ) { ERROR("lseek returned %ld\n", curr); palError = FILEGetLastErrorFromErrno(); goto InternalSetEndOfFileExit; } #if SIZEOF_OFF_T > 4 #if !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT // ftruncate will return the wrong value for some large lengths. // We'll short-circuit the process and simply return failure for // the set of values that covers those cases, all of which would // have failed anyway on any standard-sized hard drive. if (curr >= 0xFFFFFFFF000ULL) { ERROR("Skipping ftruncate because the offset is too large\n"); palError = ERROR_INVALID_PARAMETER; goto InternalSetEndOfFileExit; } #endif // !HAVE_FTRUNCATE_LARGE_LENGTH_SUPPORT #endif // SIZEOF_OFF_T #if HAS_FTRUNCATE_LENGTH_ISSUE // Perform an additional check to make sure that there's likely to be enough free space to satisfy the // request. Do this because it's been observed on Mac OSX that ftruncate can return failure but still // extend the file to consume the remainder of free space. // struct statfs sFileSystemStats; off_t cbFreeSpace; if (fstatfs(pLocalData->unix_fd, &sFileSystemStats) != 0) { ERROR("fstatfs failed\n"); palError = FILEGetLastErrorFromErrno(); goto InternalSetEndOfFileExit; } // Free space is free blocks times the size of each block in bytes. cbFreeSpace = (off_t)sFileSystemStats.f_bavail * (off_t)sFileSystemStats.f_bsize; if (curr > cbFreeSpace) { ERROR("Not enough disk space for ftruncate\n"); palError = ERROR_DISK_FULL; goto InternalSetEndOfFileExit; } #endif // HAS_FTRUNCATE_LENGTH_ISSUE if ( ftruncate(pLocalData->unix_fd, curr) != 0 ) { ERROR("ftruncate failed\n"); if ( errno == EACCES ) { ERROR("file may not be writable\n"); } palError = FILEGetLastErrorFromErrno(); goto InternalSetEndOfFileExit; } InternalSetEndOfFileExit: // Windows starts returning ERROR_INVALID_PARAMETER at an arbitrary file size (~16TB). The file system // underneath us may be able to support larger and it would be a shame to prevent that. As a compromise, // if the operation fails and the file size was above the Windows limit map ERROR_DISK_FULL to // ERROR_INVALID_PARAMETER. // curr has been checked to be positive after getting the value from lseek. The following cast is put to // suppress the compilation warning. if (palError == ERROR_DISK_FULL && (static_cast(curr) > 0x00000fffffff0000ULL ) ) palError = ERROR_INVALID_PARAMETER; if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: SetEndOfFile See MSDN doc. --*/ BOOL PALAPI SetEndOfFile( IN HANDLE hFile) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread;; PERF_ENTRY(SetEndOfFile); ENTRY("SetEndOfFile(hFile=%p)\n", hFile); pThread = InternalGetCurrentThread(); palError = InternalSetEndOfFile( pThread, hFile ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("SetEndOfFile returns BOOL %d\n", NO_ERROR == palError); PERF_EXIT(SetEndOfFile); return NO_ERROR == palError; } // // We need to break out the actual mechanics of setting the file pointer // on the unix FD for InternalReadFile and InternalWriteFile, as they // need to call this routine in order to determine the value of the // current file pointer when computing the scope of their transaction // lock. If we didn't break out this logic we'd end up referencing the file // handle multiple times, and, in the process, would attempt to recursively // obtain the local process data lock for the underlying file object. // PAL_ERROR InternalSetFilePointerForUnixFd( int iUnixFd, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod, PLONG lpNewFilePointerLow ) { PAL_ERROR palError = NO_ERROR; int seek_whence = 0; __int64 seek_offset = 0LL; __int64 seek_res = 0LL; off_t old_offset; switch( dwMoveMethod ) { case FILE_BEGIN: seek_whence = SEEK_SET; break; case FILE_CURRENT: seek_whence = SEEK_CUR; break; case FILE_END: seek_whence = SEEK_END; break; default: ERROR("dwMoveMethod = %d is invalid\n", dwMoveMethod); palError = ERROR_INVALID_PARAMETER; goto done; } // // According to MSDN, if lpDistanceToMoveHigh is not null, // lDistanceToMove is treated as unsigned; // it is treated as signed otherwise // if ( lpDistanceToMoveHigh ) { /* set the high 32 bits of the offset */ seek_offset = ((__int64)*lpDistanceToMoveHigh << 32); /* set the low 32 bits */ /* cast to unsigned long to avoid sign extension */ seek_offset |= (ULONG) lDistanceToMove; } else { seek_offset |= lDistanceToMove; } /* store the current position, in case the lseek moves the pointer before the beginning of the file */ old_offset = lseek(iUnixFd, 0, SEEK_CUR); if (old_offset == -1) { ERROR("lseek(fd,0,SEEK_CUR) failed errno:%d (%s)\n", errno, strerror(errno)); palError = ERROR_ACCESS_DENIED; goto done; } // Check to see if we're going to seek to a negative offset. // If we're seeking from the beginning or the current mark, // this is simple. if ((seek_whence == SEEK_SET && seek_offset < 0) || (seek_whence == SEEK_CUR && seek_offset + old_offset < 0)) { palError = ERROR_NEGATIVE_SEEK; goto done; } else if (seek_whence == SEEK_END && seek_offset < 0) { // We need to determine if we're seeking past the // beginning of the file, but we don't want to adjust // the mark in the process. stat is the only way to // do that. struct stat fileData; int result; result = fstat(iUnixFd, &fileData); if (result == -1) { // It's a bad fd. This shouldn't happen because // we've already called lseek on it, but you // never know. This is the best we can do. palError = ERROR_ACCESS_DENIED; goto done; } if (fileData.st_size < -seek_offset) { // Seeking past the beginning. palError = ERROR_NEGATIVE_SEEK; goto done; } } seek_res = (__int64)lseek( iUnixFd, seek_offset, seek_whence ); if ( seek_res < 0 ) { /* lseek() returns -1 on error, but also can seek to negative file offsets, so -1 can also indicate a successful seek to offset -1. Win32 doesn't allow negative file offsets, so either case is an error. */ ERROR("lseek failed errno:%d (%s)\n", errno, strerror(errno)); lseek(iUnixFd, old_offset, SEEK_SET); palError = ERROR_ACCESS_DENIED; } else { /* store high-order DWORD */ if ( lpDistanceToMoveHigh ) *lpDistanceToMoveHigh = (DWORD)(seek_res >> 32); /* return low-order DWORD of seek result */ *lpNewFilePointerLow = (DWORD)seek_res; } done: return palError; } PAL_ERROR CorUnix::InternalSetFilePointer( CPalThread *pThread, HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod, PLONG lpNewFilePointerLow ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalSetFilePointerExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto InternalSetFilePointerExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalSetFilePointerExit; } palError = InternalSetFilePointerForUnixFd( pLocalData->unix_fd, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod, lpNewFilePointerLow ); InternalSetFilePointerExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: SetFilePointer See MSDN doc. --*/ DWORD PALAPI SetFilePointer( IN HANDLE hFile, IN LONG lDistanceToMove, IN PLONG lpDistanceToMoveHigh, IN DWORD dwMoveMethod) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; LONG lNewFilePointerLow = 0; PERF_ENTRY(SetFilePointer); ENTRY("SetFilePointer(hFile=%p, lDistance=%d, lpDistanceHigh=%p, " "dwMoveMethod=%#x)\n", hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod); pThread = InternalGetCurrentThread(); palError = InternalSetFilePointer( pThread, hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod, &lNewFilePointerLow ); if (NO_ERROR != palError) { lNewFilePointerLow = INVALID_SET_FILE_POINTER; } /* This function must always call SetLastError - even if successful. If we seek to a value greater than 2^32 - 1, we will effectively be returning a negative value from this function. Now, let's say that returned value is -1. Furthermore, assume that win32error has been set before even entering this function. Then, when this function returns to SetFilePointer in win32native.cs, it will have returned -1 and win32error will have been set, which will cause an error to be returned. Since -1 may not be an error in this case and since we can't assume that the win32error is related to SetFilePointer, we need to always call SetLastError here. That way, if this function succeeds, SetFilePointer in win32native won't mistakenly determine that it failed. */ pThread->SetLastError(palError); LOGEXIT("SetFilePointer returns DWORD %#x\n", lNewFilePointerLow); PERF_EXIT(SetFilePointer); return lNewFilePointerLow; } /*++ Function: SetFilePointerEx See MSDN doc. --*/ BOOL PALAPI SetFilePointerEx( IN HANDLE hFile, IN LARGE_INTEGER liDistanceToMove, OUT PLARGE_INTEGER lpNewFilePointer, IN DWORD dwMoveMethod) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; BOOL Ret = FALSE; PERF_ENTRY(SetFilePointerEx); ENTRY("SetFilePointerEx(hFile=%p, liDistanceToMove=0x%llx, " "lpNewFilePointer=%p (0x%llx), dwMoveMethod=0x%x)\n", hFile, liDistanceToMove.QuadPart, lpNewFilePointer, (lpNewFilePointer) ? (*lpNewFilePointer).QuadPart : 0, dwMoveMethod); LONG lDistanceToMove; lDistanceToMove = (LONG)liDistanceToMove.u.LowPart; LONG lDistanceToMoveHigh; lDistanceToMoveHigh = liDistanceToMove.u.HighPart; LONG lNewFilePointerLow = 0; pThread = InternalGetCurrentThread(); palError = InternalSetFilePointer( pThread, hFile, lDistanceToMove, &lDistanceToMoveHigh, dwMoveMethod, &lNewFilePointerLow ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } else { if (lpNewFilePointer != NULL) { lpNewFilePointer->u.LowPart = (DWORD)lNewFilePointerLow; lpNewFilePointer->u.HighPart = (DWORD)lDistanceToMoveHigh; } Ret = TRUE; } LOGEXIT("SetFilePointerEx returns BOOL %d\n", Ret); PERF_EXIT(SetFilePointerEx); return Ret; } PAL_ERROR CorUnix::InternalGetFileSize( CPalThread *pThread, HANDLE hFile, DWORD *pdwFileSizeLow, DWORD *pdwFileSizeHigh ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; struct stat stat_data; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalGetFileSizeExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto InternalGetFileSizeExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalGetFileSizeExit; } if (fstat(pLocalData->unix_fd, &stat_data) != 0) { ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd); palError = FILEGetLastErrorFromErrno(); goto InternalGetFileSizeExit; } *pdwFileSizeLow = (DWORD)stat_data.st_size; if (NULL != pdwFileSizeHigh) { #if SIZEOF_OFF_T > 4 *pdwFileSizeHigh = (DWORD)(stat_data.st_size >> 32); #else *pdwFileSizeHigh = 0; #endif } InternalGetFileSizeExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: GetFileSize See MSDN doc. --*/ DWORD PALAPI GetFileSize( IN HANDLE hFile, OUT LPDWORD lpFileSizeHigh) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; DWORD dwFileSizeLow; PERF_ENTRY(GetFileSize); ENTRY("GetFileSize(hFile=%p, lpFileSizeHigh=%p)\n", hFile, lpFileSizeHigh); pThread = InternalGetCurrentThread(); palError = InternalGetFileSize( pThread, hFile, &dwFileSizeLow, lpFileSizeHigh ); if (NO_ERROR != palError) { pThread->SetLastError(palError); dwFileSizeLow = INVALID_FILE_SIZE; } LOGEXIT("GetFileSize returns DWORD %u\n", dwFileSizeLow); PERF_EXIT(GetFileSize); return dwFileSizeLow; } /*++ Function: GetFileSizeEx See MSDN doc. --*/ BOOL PALAPI GetFileSizeEx( IN HANDLE hFile, OUT PLARGE_INTEGER lpFileSize) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; DWORD dwFileSizeHigh; DWORD dwFileSizeLow; PERF_ENTRY(GetFileSizeEx); ENTRY("GetFileSizeEx(hFile=%p, lpFileSize=%p)\n", hFile, lpFileSize); pThread = InternalGetCurrentThread(); if (lpFileSize != NULL) { palError = InternalGetFileSize( pThread, hFile, &dwFileSizeLow, &dwFileSizeHigh ); lpFileSize->u.LowPart = dwFileSizeLow; lpFileSize->u.HighPart = dwFileSizeHigh; } else { palError = ERROR_INVALID_PARAMETER; } if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("GetFileSizeEx returns BOOL %d\n", NO_ERROR == palError); PERF_EXIT(GetFileSizeEx); return NO_ERROR == palError; } PAL_ERROR CorUnix::InternalFlushFileBuffers( CPalThread *pThread, HANDLE hFile ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalFlushFileBuffersExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_WRITE, &pFileObject ); if (NO_ERROR != palError) { goto InternalFlushFileBuffersExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalFlushFileBuffersExit; } if (pLocalData->open_flags_deviceaccessonly == TRUE) { ERROR("File open for device access only\n"); palError = ERROR_ACCESS_DENIED; goto InternalFlushFileBuffersExit; } #if HAVE_FSYNC || defined(__APPLE__) do { #if defined(__APPLE__) if (fcntl(pLocalData->unix_fd, F_FULLFSYNC) != -1) break; #else // __APPLE__ if (fsync(pLocalData->unix_fd) == 0) break; #endif // __APPLE__ switch (errno) { case EINTR: // Execution was interrupted by a signal, so restart. TRACE("fsync(%d) was interrupted. Restarting\n", pLocalData->unix_fd); break; default: palError = FILEGetLastErrorFromErrno(); WARN("fsync(%d) failed with error %d\n", pLocalData->unix_fd, errno); break; } } while (NO_ERROR == palError); #else // HAVE_FSYNC /* flush all buffers out to disk - there is no way to flush an individual file descriptor's buffers out. */ sync(); #endif // HAVE_FSYNC else InternalFlushFileBuffersExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: FlushFileBuffers See MSDN doc. --*/ BOOL PALAPI FlushFileBuffers( IN HANDLE hFile) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; PERF_ENTRY(FlushFileBuffers); ENTRY("FlushFileBuffers(hFile=%p)\n", hFile); pThread = InternalGetCurrentThread(); palError = InternalFlushFileBuffers( pThread, hFile ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("FlushFileBuffers returns BOOL %d\n", NO_ERROR == palError); PERF_EXIT(FlushFileBuffers); return NO_ERROR == palError; } PAL_ERROR CorUnix::InternalGetFileType( CPalThread *pThread, HANDLE hFile, DWORD *pdwFileType ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; struct stat stat_data; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalGetFileTypeExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto InternalGetFileTypeExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalGetFileTypeExit; } if (pLocalData->open_flags_deviceaccessonly == TRUE) { ERROR("File open for device access only\n"); palError = ERROR_ACCESS_DENIED; goto InternalGetFileTypeExit; } if (fstat(pLocalData->unix_fd, &stat_data) != 0) { ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd); palError = FILEGetLastErrorFromErrno(); goto InternalGetFileTypeExit; } TRACE("st_mode & S_IFMT = %#x\n", stat_data.st_mode & S_IFMT); if (S_ISREG(stat_data.st_mode) || S_ISDIR(stat_data.st_mode)) { *pdwFileType = FILE_TYPE_DISK; } else if (S_ISCHR(stat_data.st_mode)) { *pdwFileType = FILE_TYPE_CHAR; } else if (S_ISFIFO(stat_data.st_mode)) { *pdwFileType = FILE_TYPE_PIPE; } else { *pdwFileType = FILE_TYPE_UNKNOWN; } InternalGetFileTypeExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: GetFileType See MSDN doc. --*/ DWORD PALAPI GetFileType( IN HANDLE hFile) { PAL_ERROR palError = NO_ERROR; CPalThread *pThread; DWORD dwFileType; PERF_ENTRY(GetFileType); ENTRY("GetFileType(hFile=%p)\n", hFile); pThread = InternalGetCurrentThread(); palError = InternalGetFileType( pThread, hFile, &dwFileType ); if (NO_ERROR != palError) { dwFileType = FILE_TYPE_UNKNOWN; pThread->SetLastError(palError); } else if (FILE_TYPE_UNKNOWN == dwFileType) { pThread->SetLastError(palError); } LOGEXIT("GetFileType returns DWORD %#x\n", dwFileType); PERF_EXIT(GetFileType); return dwFileType; } #define ENSURE_UNIQUE_NOT_ZERO \ if ( uUniqueSeed == 0 ) \ {\ uUniqueSeed++;\ } /*++ Function: GetTempFileNameA uUnique is always 0. --*/ const int MAX_PREFIX = 3; const int MAX_SEEDSIZE = 8; /* length of "unique portion of the string, plus extension(FFFF.TMP). */ static USHORT uUniqueSeed = 0; static BOOL IsInitialized = FALSE; UINT PALAPI GetTempFileNameA( IN LPCSTR lpPathName, IN LPCSTR lpPrefixString, IN UINT uUnique, OUT LPSTR lpTempFileName) { CPalThread *pThread; CHAR * full_name; PathCharString full_namePS; int length; CHAR * file_template; PathCharString file_templatePS; CHAR chLastPathNameChar; HANDLE hTempFile; UINT uRet = 0; DWORD dwError; USHORT uLoopCounter = 0; PERF_ENTRY(GetTempFileNameA); ENTRY("GetTempFileNameA(lpPathName=%p (%s), lpPrefixString=%p (%s), uUnique=%u, " "lpTempFileName=%p)\n", lpPathName?lpPathName:"NULL", lpPathName?lpPathName:"NULL", lpPrefixString?lpPrefixString:"NULL", lpPrefixString?lpPrefixString:"NULL", uUnique, lpTempFileName?lpTempFileName:"NULL"); pThread = InternalGetCurrentThread(); if ( !IsInitialized ) { uUniqueSeed = (USHORT)( time( NULL ) ); /* On the off chance 0 is returned. 0 being the error return code. */ ENSURE_UNIQUE_NOT_ZERO IsInitialized = TRUE; } if ( !lpPathName || *lpPathName == '\0' ) { pThread->SetLastError( ERROR_DIRECTORY ); goto done; } if ( NULL == lpTempFileName ) { ERROR( "lpTempFileName cannot be NULL\n" ); pThread->SetLastError( ERROR_INVALID_PARAMETER ); goto done; } if ( strlen( lpPathName ) + MAX_SEEDSIZE + MAX_PREFIX >= MAX_LONGPATH ) { WARN( "File names larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH ); pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE ); goto done; } length = strlen(lpPathName) + MAX_SEEDSIZE + MAX_PREFIX + 10; file_template = file_templatePS.OpenStringBuffer(length); if (NULL == file_template) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } *file_template = '\0'; strcat_s( file_template, file_templatePS.GetSizeOf(), lpPathName ); file_templatePS.CloseBuffer(length); chLastPathNameChar = file_template[strlen(file_template)-1]; if (chLastPathNameChar != '\\' && chLastPathNameChar != '/') { strcat_s( file_template, file_templatePS.GetSizeOf(), "\\" ); } if ( lpPrefixString ) { strncat_s( file_template, file_templatePS.GetSizeOf(), lpPrefixString, MAX_PREFIX ); } FILEDosToUnixPathA( file_template ); strncat_s( file_template, file_templatePS.GetSizeOf(), "%.4x.TMP", MAX_SEEDSIZE ); /* Create the file. */ dwError = GetLastError(); pThread->SetLastError( NOERROR ); length = strlen(file_template) + MAX_SEEDSIZE + MAX_PREFIX; full_name = full_namePS.OpenStringBuffer(length); if (NULL == full_name) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, (0 == uUnique) ? uUniqueSeed : uUnique); full_namePS.CloseBuffer(length); hTempFile = CreateFileA( full_name, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL ); if (uUnique == 0) { /* The USHORT will overflow back to 0 if we go past 65536 files, so break the loop after 65536 iterations. If the CreateFile call was not successful within that number of iterations, then there are no temp file names left for that directory. */ while ( ERROR_PATH_NOT_FOUND != GetLastError() && INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF ) { uUniqueSeed++; ENSURE_UNIQUE_NOT_ZERO; pThread->SetLastError( NOERROR ); sprintf_s( full_name, full_namePS.GetSizeOf(), file_template, uUniqueSeed ); hTempFile = CreateFileA( full_name, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, 0, NULL ); uLoopCounter++; } } /* Reset the error code.*/ if ( NOERROR == GetLastError() ) { pThread->SetLastError( dwError ); } /* Windows sets ERROR_FILE_EXISTS,if there are no available temp files. */ if ( INVALID_HANDLE_VALUE != hTempFile ) { if (0 == uUnique) { uRet = uUniqueSeed; uUniqueSeed++; ENSURE_UNIQUE_NOT_ZERO; } else { uRet = uUnique; } if ( CloseHandle( hTempFile ) ) { if (strcpy_s( lpTempFileName, MAX_LONGPATH, full_name ) != SAFECRT_SUCCESS) { ERROR( "strcpy_s failed!\n"); pThread->SetLastError( ERROR_FILENAME_EXCED_RANGE ); *lpTempFileName = '\0'; uRet = 0; } } else { ASSERT( "Unable to close the handle %p\n", hTempFile ); pThread->SetLastError( ERROR_INTERNAL_ERROR ); *lpTempFileName = '\0'; uRet = 0; } } else if ( INVALID_HANDLE_VALUE == hTempFile && uLoopCounter < 0xFFFF ) { ERROR( "Unable to create temp file. \n" ); uRet = 0; if ( ERROR_PATH_NOT_FOUND == GetLastError() ) { /* CreateFile failed because it could not find the path. */ pThread->SetLastError( ERROR_DIRECTORY ); } /* else use the lasterror value from CreateFileA */ } else { TRACE( "65535 files already exist in the directory. " "No temp files available for creation.\n" ); pThread->SetLastError( ERROR_FILE_EXISTS ); } done: LOGEXIT("GetTempFileNameA returns UINT %u\n", uRet); PERF_EXIT(GetTempFileNameA); return uRet; } /*++ Function: GetTempFileNameW uUnique is always 0. --*/ UINT PALAPI GetTempFileNameW( IN LPCWSTR lpPathName, IN LPCWSTR lpPrefixString, IN UINT uUnique, OUT LPWSTR lpTempFileName) { CPalThread *pThread; INT path_size = 0; INT prefix_size = 0; CHAR * full_name; CHAR * prefix_string; CHAR * tempfile_name; PathCharString full_namePS, prefix_stringPS; INT length = 0; UINT uRet; PERF_ENTRY(GetTempFileNameW); ENTRY("GetTempFileNameW(lpPathName=%p (%S), lpPrefixString=%p (%S), uUnique=%u, " "lpTempFileName=%p)\n", lpPathName?lpPathName:W16_NULLSTRING, lpPathName?lpPathName:W16_NULLSTRING, lpPrefixString?lpPrefixString:W16_NULLSTRING, lpPrefixString?lpPrefixString:W16_NULLSTRING,uUnique, lpTempFileName); pThread = InternalGetCurrentThread(); /* Sanity checks. */ if ( !lpPathName || *lpPathName == '\0' ) { pThread->SetLastError( ERROR_DIRECTORY ); uRet = 0; goto done; } length = (PAL_wcslen(lpPathName)+1) * MaxWCharToAcpLengthFactor; full_name = full_namePS.OpenStringBuffer(length); if (NULL == full_name) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); uRet = 0; goto done; } path_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, full_name, length, NULL, NULL ); full_namePS.CloseBuffer(path_size); if( path_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("lpPathName is larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } uRet = 0; goto done; } if (lpPrefixString != NULL) { length = (PAL_wcslen(lpPrefixString)+1) * MaxWCharToAcpLengthFactor; prefix_string = prefix_stringPS.OpenStringBuffer(length); if (NULL == prefix_string) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); uRet = 0; goto done; } prefix_size = WideCharToMultiByte( CP_ACP, 0, lpPrefixString, -1, prefix_string, MAX_LONGPATH - path_size - MAX_SEEDSIZE, NULL, NULL ); prefix_stringPS.CloseBuffer(prefix_size); if( prefix_size == 0 ) { DWORD dwLastError = GetLastError(); if( dwLastError == ERROR_INSUFFICIENT_BUFFER ) { WARN("Full name would be larger than MAX_LONGPATH (%d)!\n", MAX_LONGPATH); pThread->SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); pThread->SetLastError(ERROR_INTERNAL_ERROR); } uRet = 0; goto done; } } tempfile_name = (char*)InternalMalloc(MAX_LONGPATH); if (tempfile_name == NULL) { pThread->SetLastError(ERROR_NOT_ENOUGH_MEMORY); uRet = 0; goto done; } uRet = GetTempFileNameA(full_name, (lpPrefixString == NULL) ? NULL : prefix_string, 0, tempfile_name); if (uRet) { path_size = MultiByteToWideChar( CP_ACP, 0, tempfile_name, -1, lpTempFileName, MAX_LONGPATH ); InternalFree(tempfile_name); tempfile_name = NULL; if (!path_size) { DWORD dwLastError = GetLastError(); if (dwLastError == ERROR_INSUFFICIENT_BUFFER) { WARN("File names larger than MAX_PATH_FNAME (%d)! \n", MAX_LONGPATH); dwLastError = ERROR_FILENAME_EXCED_RANGE; } else { ASSERT("MultiByteToWideChar failure! error is %d", dwLastError); dwLastError = ERROR_INTERNAL_ERROR; } pThread->SetLastError(dwLastError); uRet = 0; } } done: LOGEXIT("GetTempFileNameW returns UINT %u\n", uRet); PERF_EXIT(GetTempFileNameW); return uRet; } /*++ Function: FILEGetLastErrorFromErrno Convert errno into the appropriate win32 error and return it. --*/ DWORD FILEGetLastErrorFromErrno( void ) { DWORD dwRet; switch(errno) { case 0: dwRet = ERROR_SUCCESS; break; case ENAMETOOLONG: dwRet = ERROR_FILENAME_EXCED_RANGE; break; case ENOTDIR: dwRet = ERROR_PATH_NOT_FOUND; break; case ENOENT: dwRet = ERROR_FILE_NOT_FOUND; break; case EACCES: case EPERM: case EROFS: case EISDIR: dwRet = ERROR_ACCESS_DENIED; break; case EEXIST: dwRet = ERROR_ALREADY_EXISTS; break; #if !defined(_AIX) // ENOTEMPTY is the same as EEXIST on AIX. Meaningful when involving directory operations case ENOTEMPTY: dwRet = ERROR_DIR_NOT_EMPTY; break; #endif case EBADF: dwRet = ERROR_INVALID_HANDLE; break; case ENOMEM: dwRet = ERROR_NOT_ENOUGH_MEMORY; break; case EBUSY: dwRet = ERROR_BUSY; break; case ENOSPC: case EDQUOT: dwRet = ERROR_DISK_FULL; break; case ELOOP: dwRet = ERROR_BAD_PATHNAME; break; case EIO: dwRet = ERROR_WRITE_FAULT; break; case ERANGE: dwRet = ERROR_BAD_PATHNAME; break; default: ERROR("unexpected errno %d (%s); returning ERROR_GEN_FAILURE\n", errno, strerror(errno)); dwRet = ERROR_GEN_FAILURE; } TRACE("errno = %d (%s), LastError = %d\n", errno, strerror(errno), dwRet); return dwRet; } /*++ Function: DIRGetLastErrorFromErrno Convert errno into the appropriate win32 error and return it. --*/ DWORD DIRGetLastErrorFromErrno( void ) { if (errno == ENOENT) return ERROR_PATH_NOT_FOUND; else return FILEGetLastErrorFromErrno(); } /*++ Function: CopyFileA See MSDN doc. Notes: There are several (most) error paths here that do not call SetLastError(). This is because we know that CreateFile, ReadFile, and WriteFile will do so, and will have a much better idea of the specific error. --*/ BOOL PALAPI CopyFileA( IN LPCSTR lpExistingFileName, IN LPCSTR lpNewFileName, IN BOOL bFailIfExists) { CPalThread *pThread; HANDLE hSource = INVALID_HANDLE_VALUE; HANDLE hDest = INVALID_HANDLE_VALUE; DWORD dwDestCreationMode; BOOL bGood = FALSE; DWORD dwSrcFileAttributes; struct stat SrcFileStats; LPSTR lpUnixPath = NULL; const int buffer_size = 16*1024; char *buffer = (char*)alloca(buffer_size); DWORD bytes_read; DWORD bytes_written; int permissions; PERF_ENTRY(CopyFileA); ENTRY("CopyFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s), bFailIfExists=%d)\n", lpExistingFileName?lpExistingFileName:"NULL", lpExistingFileName?lpExistingFileName:"NULL", lpNewFileName?lpNewFileName:"NULL", lpNewFileName?lpNewFileName:"NULL", bFailIfExists); pThread = InternalGetCurrentThread(); if ( bFailIfExists ) { dwDestCreationMode = CREATE_NEW; } else { dwDestCreationMode = CREATE_ALWAYS; } hSource = CreateFileA( lpExistingFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if ( hSource == INVALID_HANDLE_VALUE ) { ERROR("CreateFileA failed for %s\n", lpExistingFileName); goto done; } /* Need to preserve the file attributes */ dwSrcFileAttributes = GetFileAttributes(lpExistingFileName); if (dwSrcFileAttributes == 0xffffffff) { ERROR("GetFileAttributes failed for %s\n", lpExistingFileName); goto done; } /* Need to preserve the owner/group and chmod() flags */ lpUnixPath = InternalStrdup(lpExistingFileName); if ( lpUnixPath == NULL ) { ERROR("InternalStrdup() failed\n"); pThread->SetLastError(FILEGetLastErrorFromErrno()); goto done; } FILEDosToUnixPathA(lpUnixPath); if (stat (lpUnixPath, &SrcFileStats) == -1) { ERROR("stat() failed for %s\n", lpExistingFileName); pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath)); goto done; } hDest = CreateFileA( lpNewFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, dwDestCreationMode, 0, NULL ); if ( hDest == INVALID_HANDLE_VALUE ) { ERROR("CreateFileA failed for %s\n", lpNewFileName); goto done; } InternalFree(lpUnixPath); lpUnixPath = InternalStrdup(lpNewFileName); if ( lpUnixPath == NULL ) { ERROR("InternalStrdup() failed\n"); pThread->SetLastError(FILEGetLastErrorFromErrno()); goto done; } FILEDosToUnixPathA( lpUnixPath ); // We don't set file attributes in CreateFile. The only attribute // that is reflected on disk in Unix is read-only, and we set that // here. permissions = (S_IRWXU | S_IRWXG | S_IRWXO); if ((dwSrcFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) { permissions &= ~(S_IWUSR | S_IWGRP | S_IWOTH); } /* Make sure the new file has the same chmod() flags. */ if (chmod(lpUnixPath, SrcFileStats.st_mode & permissions) == -1) { WARN ("chmod() failed to set mode 0x%x on new file\n", SrcFileStats.st_mode & permissions); pThread->SetLastError(FILEGetLastErrorFromErrnoAndFilename(lpUnixPath)); goto done; } while( (bGood = ReadFile( hSource, buffer, buffer_size, &bytes_read, NULL )) && bytes_read > 0 ) { bGood = ( WriteFile( hDest, buffer, bytes_read, &bytes_written, NULL ) && bytes_written == bytes_read); if (!bGood) break; } if (!bGood) { ERROR("Copy failed\n"); if ( !CloseHandle(hDest) || !DeleteFileA(lpNewFileName) ) { ERROR("Unable to clean up partial copy\n"); } hDest = INVALID_HANDLE_VALUE; goto done; } done: if ( hSource != INVALID_HANDLE_VALUE ) { CloseHandle( hSource ); } if ( hDest != INVALID_HANDLE_VALUE ) { CloseHandle( hDest ); } if (lpUnixPath) { InternalFree(lpUnixPath); } LOGEXIT("CopyFileA returns BOOL %d\n", bGood); PERF_EXIT(CopyFileA); return bGood; } /*++ Function: SetFileAttributesA Notes: Used for setting read-only attribute on file only. --*/ BOOL PALAPI SetFileAttributesA( IN LPCSTR lpFileName, IN DWORD dwFileAttributes) { CPalThread *pThread; struct stat stat_data; mode_t new_mode; DWORD dwLastError = 0; BOOL bRet = FALSE; LPSTR UnixFileName = NULL; PERF_ENTRY(SetFileAttributesA); ENTRY("SetFileAttributesA(lpFileName=%p (%s), dwFileAttributes=%#x)\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL", dwFileAttributes); pThread = InternalGetCurrentThread(); /* Windows behavior for SetFileAttributes is that any valid attributes are set on a file and any invalid attributes are ignored. SetFileAttributes returns success and does not set an error even if some or all of the attributes are invalid. If all the attributes are invalid, SetFileAttributes sets a file's attribute to NORMAL. */ /* If dwFileAttributes does not contain READONLY or NORMAL, set it to NORMAL and print a warning message. */ if ( !(dwFileAttributes & (FILE_ATTRIBUTE_READONLY |FILE_ATTRIBUTE_NORMAL)) ) { dwFileAttributes = FILE_ATTRIBUTE_NORMAL; WARN("dwFileAttributes(%#x) contains attributes that are either not supported " "or cannot be set via SetFileAttributes.\n"); } if ( (dwFileAttributes & FILE_ATTRIBUTE_NORMAL) && (dwFileAttributes != FILE_ATTRIBUTE_NORMAL) ) { WARN("Ignoring FILE_ATTRIBUTE_NORMAL -- it must be used alone\n"); } if (lpFileName == NULL) { dwLastError = ERROR_FILE_NOT_FOUND; goto done; } if ((UnixFileName = InternalStrdup(lpFileName)) == NULL) { ERROR("InternalStrdup() failed\n"); dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto done; } FILEDosToUnixPathA( UnixFileName ); if ( stat(UnixFileName, &stat_data) != 0 ) { TRACE("stat failed on %s; errno is %d (%s)\n", UnixFileName, errno, strerror(errno)); dwLastError = FILEGetLastErrorFromErrnoAndFilename(UnixFileName); goto done; } new_mode = stat_data.st_mode; TRACE("st_mode is %#x\n", new_mode); /* if we can't do GetFileAttributes on it, don't do SetFileAttributes */ if ( !(new_mode & S_IFREG) && !(new_mode & S_IFDIR) ) { ERROR("Not a regular file or directory, S_IFMT is %#x\n", new_mode & S_IFMT); dwLastError = ERROR_ACCESS_DENIED; goto done; } /* set or unset the "read-only" attribute */ if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) { /* remove the write bit from everybody */ new_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); } else { /* give write permission to the owner if the owner * already has read permission */ if ( new_mode & S_IRUSR ) { new_mode |= S_IWUSR; } } TRACE("new mode is %#x\n", new_mode); bRet = TRUE; if ( new_mode != stat_data.st_mode ) { if ( chmod(UnixFileName, new_mode) != 0 ) { ERROR("chmod(%s, %#x) failed\n", UnixFileName, new_mode); dwLastError = FILEGetLastErrorFromErrnoAndFilename(UnixFileName); bRet = FALSE; } } done: if (dwLastError) { pThread->SetLastError(dwLastError); } InternalFree(UnixFileName); LOGEXIT("SetFileAttributesA returns BOOL %d\n", bRet); PERF_EXIT(SetFileAttributesA); return bRet; } PAL_ERROR CorUnix::InternalCreatePipe( CPalThread *pThread, HANDLE *phReadPipe, HANDLE *phWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize ) { PAL_ERROR palError = NO_ERROR; IPalObject *pReadFileObject = NULL; IPalObject *pReadRegisteredFile = NULL; IPalObject *pWriteFileObject = NULL; IPalObject *pWriteRegisteredFile = NULL; IDataLock *pDataLock = NULL; CFileProcessLocalData *pLocalData = NULL; CObjectAttributes oaFile(NULL, lpPipeAttributes); int readWritePipeDes[2] = {-1, -1}; if ((phReadPipe == NULL) || (phWritePipe == NULL)) { ERROR("One of the two parameters hReadPipe(%p) and hWritePipe(%p) is Null\n",phReadPipe,phWritePipe); palError = ERROR_INVALID_PARAMETER; goto InternalCreatePipeExit; } if ((lpPipeAttributes == NULL) || (lpPipeAttributes->bInheritHandle == FALSE) || (lpPipeAttributes->lpSecurityDescriptor != NULL)) { ASSERT("invalid security attributes!\n"); palError = ERROR_INVALID_PARAMETER; goto InternalCreatePipeExit; } if (pipe(readWritePipeDes) == -1) { ERROR("pipe() call failed errno:%d (%s) \n", errno, strerror(errno)); palError = ERROR_INTERNAL_ERROR; goto InternalCreatePipeExit; } /* enable close-on-exec for both pipes; if one gets passed to CreateProcess it will be "uncloseonexeced" in order to be inherited */ if(-1 == fcntl(readWritePipeDes[0],F_SETFD,1)) { ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d " "(%s)\n", errno, strerror(errno)); palError = ERROR_INTERNAL_ERROR; goto InternalCreatePipeExit; } if(-1 == fcntl(readWritePipeDes[1],F_SETFD,1)) { ASSERT("can't set close-on-exec flag; fcntl() failed. errno is %d " "(%s)\n", errno, strerror(errno)); palError = ERROR_INTERNAL_ERROR; goto InternalCreatePipeExit; } // // Setup the object for the read end of the pipe // palError = g_pObjectManager->AllocateObject( pThread, &otFile, &oaFile, &pReadFileObject ); if (NO_ERROR != palError) { goto InternalCreatePipeExit; } palError = pReadFileObject->GetProcessLocalData( pThread, WriteLock, &pDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalCreatePipeExit; } pLocalData->inheritable = TRUE; pLocalData->open_flags = O_RDONLY; // // After storing the file descriptor in the object's local data // we want to clear it from the array to prevent a possible double // close if an error occurs. // pLocalData->unix_fd = readWritePipeDes[0]; readWritePipeDes[0] = -1; pDataLock->ReleaseLock(pThread, TRUE); pDataLock = NULL; // // Setup the object for the write end of the pipe // palError = g_pObjectManager->AllocateObject( pThread, &otFile, &oaFile, &pWriteFileObject ); if (NO_ERROR != palError) { goto InternalCreatePipeExit; } palError = pWriteFileObject->GetProcessLocalData( pThread, WriteLock, &pDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalCreatePipeExit; } pLocalData->inheritable = TRUE; pLocalData->open_flags = O_WRONLY; // // After storing the file descriptor in the object's local data // we want to clear it from the array to prevent a possible double // close if an error occurs. // pLocalData->unix_fd = readWritePipeDes[1]; readWritePipeDes[1] = -1; pDataLock->ReleaseLock(pThread, TRUE); pDataLock = NULL; // // Register the pipe objects // palError = g_pObjectManager->RegisterObject( pThread, pReadFileObject, &aotFile, GENERIC_READ, phReadPipe, &pReadRegisteredFile ); // // pReadFileObject is invalidated by the call to RegisterObject, so NULL it // out here to ensure that we don't try to release a reference on // it down the line. // pReadFileObject = NULL; if (NO_ERROR != palError) { goto InternalCreatePipeExit; } palError = g_pObjectManager->RegisterObject( pThread, pWriteFileObject, &aotFile, GENERIC_WRITE, phWritePipe, &pWriteRegisteredFile ); // // pWriteFileObject is invalidated by the call to RegisterObject, so NULL it // out here to ensure that we don't try to release a reference on // it down the line. // pWriteFileObject = NULL; InternalCreatePipeExit: if (NO_ERROR != palError) { if (-1 != readWritePipeDes[0]) { close(readWritePipeDes[0]); } if (-1 != readWritePipeDes[1]) { close(readWritePipeDes[1]); } } if (NULL != pReadFileObject) { pReadFileObject->ReleaseReference(pThread); } if (NULL != pReadRegisteredFile) { pReadRegisteredFile->ReleaseReference(pThread); } if (NULL != pWriteFileObject) { pWriteFileObject->ReleaseReference(pThread); } if (NULL != pWriteRegisteredFile) { pWriteRegisteredFile->ReleaseReference(pThread); } return palError; } /*++ Function: CreatePipe See MSDN doc. --*/ PALIMPORT BOOL PALAPI CreatePipe( OUT PHANDLE hReadPipe, OUT PHANDLE hWritePipe, IN LPSECURITY_ATTRIBUTES lpPipeAttributes, IN DWORD nSize) { PAL_ERROR palError; CPalThread *pThread; PERF_ENTRY(CreatePipe); ENTRY("CreatePipe(hReadPipe:%p, hWritePipe:%p, lpPipeAttributes:%p, nSize:%d\n", hReadPipe, hWritePipe, lpPipeAttributes, nSize); pThread = InternalGetCurrentThread(); palError = InternalCreatePipe( pThread, hReadPipe, hWritePipe, lpPipeAttributes, nSize ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("CreatePipe return %s\n", NO_ERROR == palError ? "TRUE":"FALSE"); PERF_EXIT(CreatePipe); return NO_ERROR == palError; } PAL_ERROR CorUnix::InternalLockFile( CPalThread *pThread, HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalLockFileExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto InternalLockFileExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalLockFileExit; } if (NULL != pLocalData->pLockController) { palError = pLocalData->pLockController->CreateFileLock( pThread, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh, IFileLockController::ExclusiveFileLock, IFileLockController::FailImmediately ); } else { // // This isn't a lockable file (e.g., it may be a pipe) // palError = ERROR_ACCESS_DENIED; goto InternalLockFileExit; } InternalLockFileExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: LockFile See MSDN doc. --*/ PALIMPORT BOOL PALAPI LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh) { CPalThread *pThread; PAL_ERROR palError = NO_ERROR; PERF_ENTRY(LockFile); ENTRY("LockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u," " nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh); pThread = InternalGetCurrentThread(); palError = InternalLockFile( pThread, hFile, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("LockFile returns %s\n", NO_ERROR == palError ? "TRUE":"FALSE"); PERF_EXIT(LockFile); return NO_ERROR == palError; } PAL_ERROR CorUnix::InternalUnlockFile( CPalThread *pThread, HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh ) { PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); palError = ERROR_INVALID_HANDLE; goto InternalUnlockFileExit; } palError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != palError) { goto InternalUnlockFileExit; } palError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto InternalUnlockFileExit; } if (NULL != pLocalData->pLockController) { palError = pLocalData->pLockController->ReleaseFileLock( pThread, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh ); } else { // // This isn't a lockable file (e.g., it may be a pipe) // palError = ERROR_ACCESS_DENIED; goto InternalUnlockFileExit; } InternalUnlockFileExit: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } return palError; } /*++ Function: UnlockFile See MSDN doc. --*/ PALIMPORT BOOL PALAPI UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh) { CPalThread *pThread; PAL_ERROR palError = NO_ERROR; PERF_ENTRY(UnlockFile); ENTRY("UnlockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u," "nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh); pThread = InternalGetCurrentThread(); palError = InternalUnlockFile( pThread, hFile, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh ); if (NO_ERROR != palError) { pThread->SetLastError(palError); } LOGEXIT("UnlockFile returns %s\n", NO_ERROR == palError ? "TRUE" : "FALSE"); PERF_EXIT(UnlockFile); return NO_ERROR == palError; } /*++ init_std_handle [static] utility function for FILEInitStdHandles. do the work that is common to all three standard handles Parameters: HANDLE pStd : Defines which standard handle to assign FILE *stream : file stream to associate to handle Return value: handle for specified stream, or INVALID_HANDLE_VALUE on failure --*/ static HANDLE init_std_handle(HANDLE * pStd, FILE *stream) { CPalThread *pThread = InternalGetCurrentThread(); PAL_ERROR palError = NO_ERROR; IPalObject *pFileObject = NULL; IPalObject *pRegisteredFile = NULL; IDataLock *pDataLock = NULL; CFileProcessLocalData *pLocalData = NULL; IFileLockController *pLockController = NULL; CObjectAttributes oa; HANDLE hFile = INVALID_HANDLE_VALUE; int new_fd = -1; /* duplicate the FILE *, so that we can fclose() in FILECloseHandle without closing the original */ new_fd = dup(fileno(stream)); if(-1 == new_fd) { ERROR("dup() failed; errno is %d (%s)\n", errno, strerror(errno)); goto done; } palError = g_pObjectManager->AllocateObject( pThread, &otFile, &oa, &pFileObject ); if (NO_ERROR != palError) { goto done; } palError = pFileObject->GetProcessLocalData( pThread, WriteLock, &pDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != palError) { goto done; } pLocalData->inheritable = TRUE; pLocalData->unix_fd = new_fd; pLocalData->dwDesiredAccess = 0; pLocalData->open_flags = 0; pLocalData->open_flags_deviceaccessonly = FALSE; // // Transfer the lock controller reference from our local variable // to the local file data // pLocalData->pLockController = pLockController; pLockController = NULL; // // We've finished initializing our local data, so release that lock // pDataLock->ReleaseLock(pThread, TRUE); pDataLock = NULL; palError = g_pObjectManager->RegisterObject( pThread, pFileObject, &aotFile, 0, &hFile, &pRegisteredFile ); // // pFileObject is invalidated by the call to RegisterObject, so NULL it // out here to ensure that we don't try to release a reference on // it down the line. // pFileObject = NULL; done: if (NULL != pLockController) { pLockController->ReleaseController(); } if (NULL != pDataLock) { pDataLock->ReleaseLock(pThread, TRUE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } if (NULL != pRegisteredFile) { pRegisteredFile->ReleaseReference(pThread); } if (NO_ERROR == palError) { *pStd = hFile; } else if (-1 != new_fd) { close(new_fd); } return hFile; } /*++ FILEInitStdHandles Create handle objects for stdin, stdout and stderr (no parameters) Return value: TRUE on success, FALSE on failure --*/ BOOL FILEInitStdHandles(void) { HANDLE stdin_handle; HANDLE stdout_handle; HANDLE stderr_handle; TRACE("creating handle objects for stdin, stdout, stderr\n"); stdin_handle = init_std_handle(&pStdIn, stdin); if(INVALID_HANDLE_VALUE == stdin_handle) { ERROR("failed to create stdin handle\n"); goto fail; } stdout_handle = init_std_handle(&pStdOut, stdout); if(INVALID_HANDLE_VALUE == stdout_handle) { ERROR("failed to create stdout handle\n"); CloseHandle(stdin_handle); goto fail; } stderr_handle = init_std_handle(&pStdErr, stderr); if(INVALID_HANDLE_VALUE == stderr_handle) { ERROR("failed to create stderr handle\n"); CloseHandle(stdin_handle); CloseHandle(stdout_handle); goto fail; } return TRUE; fail: pStdIn = INVALID_HANDLE_VALUE; pStdOut = INVALID_HANDLE_VALUE; pStdErr = INVALID_HANDLE_VALUE; return FALSE; } /*++ FILECleanupStdHandles Remove all regions, locked by a file pointer, from shared memory (no parameters) --*/ void FILECleanupStdHandles(void) { HANDLE stdin_handle; HANDLE stdout_handle; HANDLE stderr_handle; TRACE("closing standard handles\n"); stdin_handle = pStdIn; stdout_handle = pStdOut; stderr_handle = pStdErr; pStdIn = INVALID_HANDLE_VALUE; pStdOut = INVALID_HANDLE_VALUE; pStdErr = INVALID_HANDLE_VALUE; if (stdin_handle != INVALID_HANDLE_VALUE) { CloseHandle(stdin_handle); } if (stdout_handle != INVALID_HANDLE_VALUE) { CloseHandle(stdout_handle); } if (stderr_handle != INVALID_HANDLE_VALUE) { CloseHandle(stderr_handle); } } /*++ FILEGetFileNameFromSymLink Input parameters: source = path to the file on input, path to the file with all symbolic links traversed on return Note: Assumes the maximum size of the source is MAX_LONGPATH Return value: TRUE on success, FALSE on failure --*/ BOOL FILEGetFileNameFromSymLink(char *source) { int ret; char * sLinkData = (char*)InternalMalloc(MAX_LONGPATH); do { ret = readlink(source, sLinkData, MAX_LONGPATH); if (ret>0) { sLinkData[ret] = '\0'; strcpy_s(source, sizeof(char)*(MAX_LONGPATH), sLinkData); } } while (ret > 0); InternalFree(sLinkData); return (errno == EINVAL); } /*++ Function: GetFileInformationByHandle See MSDN doc. --*/ BOOL PALAPI GetFileInformationByHandle( IN HANDLE hFile, OUT LPBY_HANDLE_FILE_INFORMATION lpFileInformation) { CPalThread *pThread; BOOL bRet = FALSE; DWORD dwLastError = 0; IPalObject *pFileObject = NULL; CFileProcessLocalData *pLocalData = NULL; IDataLock *pLocalDataLock = NULL; DWORD dwAttr = 0; struct stat stat_data; PERF_ENTRY(GetFileInformationByHandle); ENTRY("GetFileInformationByHandle(hFile=%p, lpFileInformation=%p)\n", hFile, lpFileInformation); pThread = InternalGetCurrentThread(); if (INVALID_HANDLE_VALUE == hFile) { ERROR( "Invalid file handle\n" ); dwLastError = ERROR_INVALID_HANDLE; goto done; } dwLastError = g_pObjectManager->ReferenceObjectByHandle( pThread, hFile, &aotFile, GENERIC_READ, &pFileObject ); if (NO_ERROR != dwLastError) { goto done; } dwLastError = pFileObject->GetProcessLocalData( pThread, ReadLock, &pLocalDataLock, reinterpret_cast(&pLocalData) ); if (NO_ERROR != dwLastError) { goto done; } if ( fstat(pLocalData->unix_fd, &stat_data) != 0 ) { if ((dwLastError = FILEGetLastErrorFromErrno()) == ERROR_INTERNAL_ERROR) { ASSERT("fstat() not expected to fail with errno:%d (%s)\n", errno, strerror(errno)); } goto done; } if ( (stat_data.st_mode & S_IFMT) == S_IFDIR ) { dwAttr |= FILE_ATTRIBUTE_DIRECTORY; } else if ( (stat_data.st_mode & S_IFMT) != S_IFREG ) { ERROR("Not a regular file or directory, S_IFMT is %#x\n", stat_data.st_mode & S_IFMT); dwLastError = ERROR_ACCESS_DENIED; goto done; } if ( UTIL_IsReadOnlyBitsSet( &stat_data ) ) { dwAttr |= FILE_ATTRIBUTE_READONLY; } /* finally, if nothing is set... */ if ( dwAttr == 0 ) { dwAttr = FILE_ATTRIBUTE_NORMAL; } lpFileInformation->dwFileAttributes = dwAttr; /* get the file times */ lpFileInformation->ftCreationTime = FILEUnixTimeToFileTime( stat_data.st_ctime, ST_CTIME_NSEC(&stat_data) ); lpFileInformation->ftLastAccessTime = FILEUnixTimeToFileTime( stat_data.st_atime, ST_ATIME_NSEC(&stat_data) ); lpFileInformation->ftLastWriteTime = FILEUnixTimeToFileTime( stat_data.st_mtime, ST_MTIME_NSEC(&stat_data) ); lpFileInformation->dwVolumeSerialNumber = stat_data.st_dev; /* Get the file size. GetFileSize is not used because it gets the size of an already-open file */ lpFileInformation->nFileSizeLow = (DWORD) stat_data.st_size; #if SIZEOF_OFF_T > 4 lpFileInformation->nFileSizeHigh = (DWORD)(stat_data.st_size >> 32); #else lpFileInformation->nFileSizeHigh = 0; #endif lpFileInformation->nNumberOfLinks = stat_data.st_nlink; lpFileInformation->nFileIndexHigh = 0; lpFileInformation->nFileIndexLow = stat_data.st_ino; bRet = TRUE; done: if (NULL != pLocalDataLock) { pLocalDataLock->ReleaseLock(pThread, FALSE); } if (NULL != pFileObject) { pFileObject->ReleaseReference(pThread); } if (dwLastError) pThread->SetLastError(dwLastError); LOGEXIT("GetFileInformationByHandle returns BOOL %d\n", bRet); PERF_EXIT(GetFileInformationByHandle); return bRet; }