| // 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 |