mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
290 lines
7.6 KiB
C++
290 lines
7.6 KiB
C++
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
||
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
||
|
|
// the WPILib BSD license file in the root directory of this project.
|
||
|
|
|
||
|
|
//===----------------------------------------------------------------------===//
|
||
|
|
//
|
||
|
|
// The LLVM Compiler Infrastructure
|
||
|
|
//
|
||
|
|
// This file is distributed under the University of Illinois Open Source
|
||
|
|
// License. See LICENSE.TXT for details.
|
||
|
|
//
|
||
|
|
//===----------------------------------------------------------------------===//
|
||
|
|
|
||
|
|
#include <cassert>
|
||
|
|
|
||
|
|
#ifdef _WIN32
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <io.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
// Require at least Windows 7 API.
|
||
|
|
#define _WIN32_WINNT 0x0601
|
||
|
|
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
|
||
|
|
#define WIN32_LEAN_AND_MEAN
|
||
|
|
#ifndef NOMINMAX
|
||
|
|
#define NOMINMAX
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define WIN32_NO_STATUS
|
||
|
|
#include <windows.h>
|
||
|
|
#undef WIN32_NO_STATUS
|
||
|
|
#include <winternl.h>
|
||
|
|
#include <ntstatus.h>
|
||
|
|
|
||
|
|
#include <shellapi.h>
|
||
|
|
#include <shlobj.h>
|
||
|
|
|
||
|
|
#include "wpi/WindowsError.h"
|
||
|
|
|
||
|
|
#else // _WIN32
|
||
|
|
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
#endif // _WIN32
|
||
|
|
|
||
|
|
#include "wpi/Errno.h"
|
||
|
|
#include "wpi/ErrorHandling.h"
|
||
|
|
#include "wpi/WindowsError.h"
|
||
|
|
#include "wpi/fs.h"
|
||
|
|
|
||
|
|
namespace fs {
|
||
|
|
|
||
|
|
#ifdef _WIN32
|
||
|
|
|
||
|
|
#ifdef _MSC_VER
|
||
|
|
#pragma comment(lib, "shell32.lib")
|
||
|
|
#pragma comment(lib, "ole32.lib")
|
||
|
|
#pragma warning(push)
|
||
|
|
#pragma warning(disable : 4244 4267 4146)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
const file_t kInvalidFile = INVALID_HANDLE_VALUE;
|
||
|
|
|
||
|
|
static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
|
||
|
|
switch (Disp) {
|
||
|
|
case CD_CreateAlways:
|
||
|
|
return CREATE_ALWAYS;
|
||
|
|
case CD_CreateNew:
|
||
|
|
return CREATE_NEW;
|
||
|
|
case CD_OpenAlways:
|
||
|
|
return OPEN_ALWAYS;
|
||
|
|
case CD_OpenExisting:
|
||
|
|
return OPEN_EXISTING;
|
||
|
|
}
|
||
|
|
wpi_unreachable("unreachable!");
|
||
|
|
}
|
||
|
|
|
||
|
|
static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
|
||
|
|
DWORD Result = 0;
|
||
|
|
if (Access & FA_Read)
|
||
|
|
Result |= GENERIC_READ;
|
||
|
|
if (Access & FA_Write)
|
||
|
|
Result |= GENERIC_WRITE;
|
||
|
|
if (Flags & OF_Delete)
|
||
|
|
Result |= DELETE;
|
||
|
|
if (Flags & OF_UpdateAtime)
|
||
|
|
Result |= FILE_WRITE_ATTRIBUTES;
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static file_t openFileInternal(const path& Path, std::error_code& EC,
|
||
|
|
DWORD Disp, DWORD Access, DWORD Flags,
|
||
|
|
bool Inherit = false) {
|
||
|
|
SECURITY_ATTRIBUTES SA;
|
||
|
|
SA.nLength = sizeof(SA);
|
||
|
|
SA.lpSecurityDescriptor = nullptr;
|
||
|
|
SA.bInheritHandle = Inherit;
|
||
|
|
|
||
|
|
HANDLE H =
|
||
|
|
::CreateFileW(Path.c_str(), Access,
|
||
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
|
||
|
|
Disp, Flags, NULL);
|
||
|
|
if (H == INVALID_HANDLE_VALUE) {
|
||
|
|
DWORD LastError = ::GetLastError();
|
||
|
|
EC = wpi::mapWindowsError(LastError);
|
||
|
|
// Provide a better error message when trying to open directories.
|
||
|
|
// This only runs if we failed to open the file, so there is probably
|
||
|
|
// no performances issues.
|
||
|
|
if (LastError != ERROR_ACCESS_DENIED) {
|
||
|
|
return kInvalidFile;
|
||
|
|
}
|
||
|
|
if (is_directory(Path)) {
|
||
|
|
EC = std::make_error_code(std::errc::is_a_directory);
|
||
|
|
}
|
||
|
|
return kInvalidFile;
|
||
|
|
}
|
||
|
|
EC = std::error_code();
|
||
|
|
return H;
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
|
||
|
|
FILE_DISPOSITION_INFO Disposition;
|
||
|
|
Disposition.DeleteFile = Delete;
|
||
|
|
if (!::SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
|
||
|
|
sizeof(Disposition)))
|
||
|
|
return wpi::mapWindowsError(::GetLastError());
|
||
|
|
return std::error_code();
|
||
|
|
}
|
||
|
|
|
||
|
|
file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
|
||
|
|
FileAccess Access, OpenFlags Flags, unsigned Mode) {
|
||
|
|
// Verify that we don't have both "append" and "excl".
|
||
|
|
assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
|
||
|
|
"Cannot specify both 'CreateNew' and 'Append' file creation flags!");
|
||
|
|
|
||
|
|
DWORD NativeDisp = nativeDisposition(Disp, Flags);
|
||
|
|
DWORD NativeAccess = nativeAccess(Access, Flags);
|
||
|
|
|
||
|
|
bool Inherit = false;
|
||
|
|
if (Flags & OF_ChildInherit) {
|
||
|
|
Inherit = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
file_t Result = openFileInternal(Path, EC, NativeDisp, NativeAccess,
|
||
|
|
FILE_ATTRIBUTE_NORMAL, Inherit);
|
||
|
|
if (EC) {
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Flags & OF_UpdateAtime) {
|
||
|
|
FILETIME FileTime;
|
||
|
|
SYSTEMTIME SystemTime;
|
||
|
|
::GetSystemTime(&SystemTime);
|
||
|
|
if (::SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
|
||
|
|
::SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
|
||
|
|
DWORD LastError = ::GetLastError();
|
||
|
|
::CloseHandle(Result);
|
||
|
|
EC = wpi::mapWindowsError(LastError);
|
||
|
|
return kInvalidFile;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Flags & OF_Delete) {
|
||
|
|
if ((EC = setDeleteDisposition(Result, true))) {
|
||
|
|
::CloseHandle(Result);
|
||
|
|
return kInvalidFile;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
|
||
|
|
return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
|
||
|
|
if (F == kInvalidFile) {
|
||
|
|
EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int CrtOpenFlags = 0;
|
||
|
|
if (Flags & OF_Append) {
|
||
|
|
CrtOpenFlags |= _O_APPEND;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Flags & OF_Text) {
|
||
|
|
CrtOpenFlags |= _O_TEXT;
|
||
|
|
}
|
||
|
|
|
||
|
|
int ResultFD = ::_open_osfhandle(intptr_t(F), CrtOpenFlags);
|
||
|
|
if (ResultFD == -1) {
|
||
|
|
::CloseHandle(F);
|
||
|
|
EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
EC = std::error_code();
|
||
|
|
F = kInvalidFile;
|
||
|
|
return ResultFD;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CloseFile(file_t& F) {
|
||
|
|
::CloseHandle(F);
|
||
|
|
F = kInvalidFile;
|
||
|
|
}
|
||
|
|
|
||
|
|
#else // _WIN32
|
||
|
|
|
||
|
|
const file_t kInvalidFile = -1;
|
||
|
|
|
||
|
|
static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
|
||
|
|
FileAccess Access) {
|
||
|
|
int Result = 0;
|
||
|
|
if (Access == FA_Read) {
|
||
|
|
Result |= O_RDONLY;
|
||
|
|
} else if (Access == FA_Write) {
|
||
|
|
Result |= O_WRONLY;
|
||
|
|
} else if (Access == (FA_Read | FA_Write)) {
|
||
|
|
Result |= O_RDWR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Disp == CD_CreateNew) {
|
||
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
||
|
|
Result |= O_EXCL; // Fail if it does.
|
||
|
|
} else if (Disp == CD_CreateAlways) {
|
||
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
||
|
|
Result |= O_TRUNC; // Truncate if it does.
|
||
|
|
} else if (Disp == CD_OpenAlways) {
|
||
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
||
|
|
} else if (Disp == CD_OpenExisting) {
|
||
|
|
// Nothing special, just don't add O_CREAT and we get these semantics.
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Flags & F_Append) {
|
||
|
|
Result |= O_APPEND;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef O_CLOEXEC
|
||
|
|
if (!(Flags & OF_ChildInherit)) {
|
||
|
|
Result |= O_CLOEXEC;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return Result;
|
||
|
|
}
|
||
|
|
|
||
|
|
file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
|
||
|
|
FileAccess Access, OpenFlags Flags, unsigned Mode) {
|
||
|
|
int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
|
||
|
|
file_t ResultFD = kInvalidFile;
|
||
|
|
|
||
|
|
// Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
|
||
|
|
// when open is overloaded, such as in Bionic.
|
||
|
|
auto Open = [&]() { return ::open(Path.c_str(), OpenFlags, Mode); };
|
||
|
|
if ((ResultFD = wpi::sys::RetryAfterSignal(-1, Open)) < 0) {
|
||
|
|
EC = std::error_code(errno, std::generic_category());
|
||
|
|
return kInvalidFile;
|
||
|
|
}
|
||
|
|
#ifndef O_CLOEXEC
|
||
|
|
if (!(Flags & OF_ChildInherit)) {
|
||
|
|
int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
|
||
|
|
(void)r;
|
||
|
|
assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
EC = std::error_code();
|
||
|
|
return ResultFD;
|
||
|
|
}
|
||
|
|
|
||
|
|
file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
|
||
|
|
return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags, 0666);
|
||
|
|
}
|
||
|
|
|
||
|
|
int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
|
||
|
|
int fd = F;
|
||
|
|
F = kInvalidFile;
|
||
|
|
EC = std::error_code();
|
||
|
|
return fd;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CloseFile(file_t& F) {
|
||
|
|
::close(F);
|
||
|
|
F = kInvalidFile;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // _WIN32
|
||
|
|
|
||
|
|
} // namespace fs
|