blob: de07d48bd6571164ab42c10dcc4839a1ac06393e [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/win32/file_internal.h"
#include <sys/stat.h>
#include <windows.h>
#include "starboard/common/log.h"
#include "starboard/memory.h"
#include "starboard/shared/win32/error_utils.h"
#include "starboard/shared/win32/wchar_utils.h"
namespace sbwin32 = starboard::shared::win32;
namespace starboard {
namespace shared {
namespace win32 {
namespace {
const char kUnixSep[] = "/";
const char kWin32Sep[] = "\\";
const wchar_t kUnixSepW[] = L"/";
const wchar_t kWin32SepW[] = L"\\";
bool IsPathNormalized(const std::string& string) {
return string.find(kUnixSep) == std::string::npos;
}
bool IsPathNormalized(const std::wstring& string) {
return string.find(kUnixSepW) == std::wstring::npos;
}
std::string NormalizePathSeparator(std::string str) {
size_t start_pos = 0;
while ((start_pos = str.find(kUnixSep, start_pos)) != std::string::npos) {
str.replace(start_pos, sizeof(kUnixSep) - 1, kWin32Sep);
start_pos += sizeof(kWin32Sep) - 1;
}
return str;
}
std::wstring NormalizePathSeparator(std::wstring str) {
size_t start_pos = 0;
while ((start_pos = str.find(kUnixSepW, start_pos)) != std::wstring::npos) {
str.replace(start_pos, sizeof(kUnixSepW) / 2 - 1, kWin32SepW);
start_pos += sizeof(kWin32SepW) / 2 - 1;
}
return str;
}
bool StringCanNarrow(const std::wstring& str) {
for (wchar_t value : str) {
char narrow_val = static_cast<char>(value);
if (value != narrow_val) {
return false;
}
}
return true;
}
} // namespace
std::wstring NormalizeWin32Path(std::string str) {
return NormalizeWin32Path(CStringToWString(str.c_str()));
}
std::wstring NormalizeWin32Path(std::wstring str) {
return NormalizePathSeparator(str);
}
HANDLE OpenFileOrDirectory(const char* path,
int flags,
bool* out_created,
SbFileError* out_error) {
// Note that FILE_SHARE_DELETE allows a file to be deleted while there
// are other handles open for read/write. This is necessary for the
// Async file tests which, due to system timing, will sometimes have
// outstanding handles open and fail to delete, failing the test.
const DWORD share_mode =
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
DWORD creation_disposition = 0;
if (flags & kSbFileCreateOnly) {
SB_DCHECK(!creation_disposition);
SB_DCHECK(!(flags & kSbFileCreateAlways));
creation_disposition = CREATE_NEW;
}
if (out_created) {
*out_created = false;
}
if (flags & kSbFileCreateAlways) {
SB_DCHECK(!creation_disposition);
SB_DCHECK(!(flags & kSbFileCreateOnly));
creation_disposition = CREATE_ALWAYS;
}
if (flags & kSbFileOpenTruncated) {
SB_DCHECK(!creation_disposition);
SB_DCHECK(flags & kSbFileWrite);
creation_disposition = TRUNCATE_EXISTING;
}
if (flags & kSbFileOpenOnly) {
SB_DCHECK(!(flags & kSbFileOpenAlways));
creation_disposition = OPEN_EXISTING;
}
if (flags & kSbFileOpenAlways) {
SB_DCHECK(!(flags & kSbFileOpenOnly));
creation_disposition = OPEN_ALWAYS;
}
if (!creation_disposition && !(flags & kSbFileOpenOnly) &&
!(flags & kSbFileOpenAlways)) {
SB_NOTREACHED();
errno = ENOTSUP;
if (out_error) {
*out_error = kSbFileErrorFailed;
}
return kSbFileInvalid;
}
DWORD desired_access = 0;
if (flags & kSbFileRead) {
desired_access |= GENERIC_READ;
}
const bool open_file_in_write_mode = flags & kSbFileWrite;
if (open_file_in_write_mode) {
desired_access |= GENERIC_WRITE;
}
// TODO: Support asynchronous IO, if necessary.
SB_DCHECK(!(flags & kSbFileAsync));
SB_DCHECK(desired_access != 0) << "Invalid permission flag.";
std::wstring path_wstring = NormalizeWin32Path(path);
CREATEFILE2_EXTENDED_PARAMETERS create_ex_params = {0};
// Enabling |FILE_FLAG_BACKUP_SEMANTICS| allows us to figure out if the path
// is a directory.
create_ex_params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
create_ex_params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS;
create_ex_params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
create_ex_params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
create_ex_params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
struct stat info;
const bool file_exists_prior_to_open = stat(path, &info) == 0;
HANDLE file_handle =
CreateFile2(path_wstring.c_str(), desired_access, share_mode,
creation_disposition, &create_ex_params);
const bool file_exists_after_open = stat(path, &info) == 0;
if (out_created && starboard::shared::win32::IsValidHandle(file_handle)) {
if (flags & kSbFileCreateAlways) {
*out_created = file_exists_after_open;
} else {
*out_created = (!file_exists_prior_to_open && file_exists_after_open);
}
}
const DWORD last_error = GetLastError();
if (out_error) {
if (starboard::shared::win32::IsValidHandle(file_handle)) {
*out_error = kSbFileOk;
} else {
switch (last_error) {
case ERROR_ACCESS_DENIED:
*out_error = kSbFileErrorAccessDenied;
break;
case ERROR_FILE_EXISTS: {
if (flags & kSbFileCreateOnly) {
*out_error = kSbFileErrorExists;
} else {
*out_error = kSbFileErrorAccessDenied;
}
break;
}
case ERROR_FILE_NOT_FOUND:
*out_error = kSbFileErrorNotFound;
break;
default:
*out_error = kSbFileErrorFailed;
}
}
}
return file_handle;
}
} // namespace win32
} // namespace shared
} // namespace starboard