diff options
Diffstat (limited to 'src/pal/src/file/file.cpp')
-rw-r--r-- | src/pal/src/file/file.cpp | 4900 |
1 files changed, 4900 insertions, 0 deletions
diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp new file mode 100644 index 0000000000..6443a5e7b9 --- /dev/null +++ b/src/pal/src/file/file.cpp @@ -0,0 +1,4900 @@ +// 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 <time.h> +#include <stdio.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <errno.h> +#include <limits.h> + +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<void**>(&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( LPCSTR 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 = strdup(lpPath) ) ) + { + ERROR( "strdup() 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; + } + + free(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(LPCSTR lpPath) +{ + PAL_ERROR palError; + if (ENOENT == errno) + { + FILEGetProperNotFoundError(lpPath, &palError); + } + else + { + palError = FILEGetLastErrorFromErrno(); + } + return palError; +} + +BOOL +CorUnix::RealPathHelper(LPCSTR lpUnixPath, PathCharString& lpBuffer) +{ + StringHolder lpRealPath; + lpRealPath = realpath(lpUnixPath, NULL); + if (lpRealPath.IsNull()) + { + return FALSE; + } + + lpBuffer.Set(lpRealPath, strlen(lpRealPath)); + return TRUE; +} +/*++ +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(). + +--*/ +PAL_ERROR +CorUnix::InternalCanonicalizeRealPath(LPCSTR lpUnixPath, PathCharString& lpBuffer) +{ + PAL_ERROR palError = NO_ERROR; + +#if !REALPATH_SUPPORTS_NONEXISTENT_FILES + StringHolder lpExistingPath; + LPSTR pchSeparator = NULL; + LPSTR lpFilename = NULL; + DWORD cchBuffer = 0; + DWORD cchFilename = 0; +#endif // !REALPATH_SUPPORTS_NONEXISTENT_FILES + + if (lpUnixPath == NULL) + { + ERROR ("Invalid argument to InternalCanonicalizeRealPath\n"); + palError = ERROR_INVALID_PARAMETER; + goto LExit; + } + +#if REALPATH_SUPPORTS_NONEXISTENT_FILES + RealPathHelper(lpUnixPath, lpBuffer); +#else // !REALPATH_SUPPORTS_NONEXISTENT_FILES + + lpExistingPath = strdup(lpUnixPath); + if (lpExistingPath.IsNull()) + { + ERROR ("strdup failed with error %d\n", errno); + palError = ERROR_NOT_ENOUGH_MEMORY; + goto LExit; + } + + pchSeparator = strrchr(lpExistingPath, '/'); + if (pchSeparator == NULL) + { + PathCharString pszCwdBuffer; + + if (GetCurrentDirectoryA(pszCwdBuffer)== 0) + { + WARN("getcwd(NULL) failed with error %d\n", errno); + palError = DIRGetLastErrorFromErrno(); + goto LExit; + } + + if (! RealPathHelper(pszCwdBuffer, lpBuffer)) + { + 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.Clear(); + lpFilename = NULL; + fSetFilename = false; + } + else +#endif // defined(_AMD64_) + *pchSeparator = '\0'; + + if (!RealPathHelper(lpExistingPath, lpBuffer)) + { + 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; + } + +#if defined(_AMD64_) + if (lpFilename == NULL) + goto LExit; +#endif // _AMD64_ + + if (!lpBuffer.Append("/",1) || !lpBuffer.Append(lpFilename, strlen(lpFilename))) + { + ERROR ("Append failed!\n"); + palError = ERROR_INSUFFICIENT_BUFFER; + + // Doing a goto here since we want to exit now. This will work + // incase someone else adds another if clause below us. + goto LExit; + } + +#endif // REALPATH_SUPPORTS_NONEXISTENT_FILES +LExit: + + if ((palError == NO_ERROR) && lpBuffer.IsEmpty()) + { + // 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; + PathCharString lpUnixPath; + 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 = "\\\\.\\"; + PathCharString lpFullUnixPath; + + /* 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 ( strncmp(lpFileName, szNonfilePrefix, strlen(szNonfilePrefix)) == 0 ) + { + ERROR("InternalCreateFile does not support paths beginning with %s\n", szNonfilePrefix); + palError = ERROR_INVALID_PARAMETER; + goto done; + } + + if( !lpUnixPath.Set(lpFileName,strlen(lpFileName))) + { + ERROR("strdup() failed\n"); + palError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + 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); + if (palError != NO_ERROR) + { + goto done; + } + + lpUnixPath.Set(lpFullUnixPath); + + 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<void**>(&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 (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 ); + + if( size == 0 ) + { + namePathString.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + palError = ERROR_INTERNAL_ERROR; + goto done; + } + + namePathString.CloseBuffer(size - 1); + + 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 ); + + if( src_size == 0 ) + { + sourcePathString.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + sourcePathString.CloseBuffer(src_size - 1); + 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 ); + + if( dest_size == 0 ) + { + destPathString.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + destPathString.CloseBuffer(dest_size - 1); + 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; + CPalThread *pThread; + int result; + BOOL bRet = FALSE; + DWORD dwLastError = 0; + PathCharString lpunixFileName; + PathCharString lpFullunixFileName; + + PERF_ENTRY(DeleteFileA); + ENTRY("DeleteFileA(lpFileName=%p (%s))\n", lpFileName?lpFileName:"NULL", lpFileName?lpFileName:"NULL"); + + pThread = InternalGetCurrentThread(); + + if( !lpunixFileName.Set(lpFileName, strlen(lpFileName))) + { + palError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + FILEDosToUnixPathA( lpunixFileName ); + + // 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); + if (palError != NO_ERROR) + { + if (!lpFullunixFileName.Set(lpunixFileName, strlen(lpunixFileName))) + { + palError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + } + + result = unlink( lpFullunixFileName ); + + if (result < 0) + { + TRACE("unlink returns %d\n", result); + dwLastError = FILEGetLastErrorFromErrnoAndFilename(lpFullunixFileName); + } + else + { + bRet = TRUE; + } + +done: + if(dwLastError) + { + pThread->SetLastError( dwLastError ); + } + + 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 ); + + if( size == 0 ) + { + namePS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + bRet = FALSE; + goto done; + } + + namePS.CloseBuffer(size - 1); + 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; + PathCharString source; + PathCharString 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; + } + + + if( !source.Set(lpExistingFileName, strlen(lpExistingFileName))) + { + dwLastError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + FILEDosToUnixPathA( source ); + + if( !dest.Set(lpNewFileName, strlen(lpNewFileName))) + { + dwLastError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + FILEDosToUnixPathA( dest ); + + 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 (lstat(source, &buf) == -1) + { + FILEGetProperNotFoundError(source, &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 ); + if( src_size == 0 ) + { + sourcePS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + sourcePS.CloseBuffer(src_size - 1); + 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 ); + + if( dest_size == 0 ) + { + destPS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + destPS.CloseBuffer(dest_size - 1); + 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; + PathCharString unixFileName; + + 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; + } + + + if( !unixFileName.Set(lpFileName, strlen(lpFileName))) + { + dwLastError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + 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 ); + + if( size == 0 ) + { + filenamePS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + } + else + { + filenamePS.CloseBuffer(size - 1); + 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 ); + + if( size == 0 ) + { + namePS.CloseBuffer(0); + dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + dwLastError = ERROR_INTERNAL_ERROR; + goto done; + } + + namePS.CloseBuffer(size - 1); + 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 ); + + if( size == 0 ) + { + namePS.CloseBuffer(0); + dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + dwLastError = ERROR_INVALID_PARAMETER; + goto done; + } + namePS.CloseBuffer(size - 1); + 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<void**>(&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<void**>(&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<void**>(&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<UINT64>(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<void**>(&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<void**>(&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<void**>(&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<void**>(&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 ); + + if( path_size == 0 ) + { + full_namePS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + uRet = 0; + goto done; + } + + full_namePS.CloseBuffer(path_size - 1); + + 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 ); + + if( prefix_size == 0 ) + { + prefix_stringPS.CloseBuffer(0); + DWORD dwLastError = GetLastError(); + ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError); + pThread->SetLastError(ERROR_INTERNAL_ERROR); + uRet = 0; + goto done; + } + prefix_stringPS.CloseBuffer(prefix_size - 1); + } + + 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 ); + + free(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 = strdup(lpExistingFileName); + if ( lpUnixPath == NULL ) + { + ERROR("strdup() 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; + } + + free(lpUnixPath); + lpUnixPath = strdup(lpNewFileName); + if ( lpUnixPath == NULL ) + { + ERROR("strdup() 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) + { + free(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 = strdup(lpFileName)) == NULL) + { + ERROR("strdup() 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); + } + + free(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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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); + } +} + +/*++ +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<void**>(&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; +} |