| OLD | NEW |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 #include "util/file/file_io.h" | 15 #include "util/file/file_io.h" |
| 16 | 16 |
| 17 #include <algorithm> |
| 18 #include <limits> |
| 19 |
| 17 #include "base/files/file_path.h" | 20 #include "base/files/file_path.h" |
| 18 #include "base/logging.h" | 21 #include "base/logging.h" |
| 19 #include "base/numerics/safe_conversions.h" | |
| 20 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 21 | 23 |
| 22 namespace { | 24 namespace { |
| 23 | 25 |
| 24 bool IsSocketHandle(HANDLE file) { | 26 bool IsSocketHandle(HANDLE file) { |
| 25 if (GetFileType(file) == FILE_TYPE_PIPE) { | 27 if (GetFileType(file) == FILE_TYPE_PIPE) { |
| 26 // FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous | 28 // FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous |
| 27 // pipe. If we are unable to retrieve the pipe information, we know it's a | 29 // pipe. If we are unable to retrieve the pipe information, we know it's a |
| 28 // socket. | 30 // socket. |
| 29 return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL); | 31 return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL); |
| 30 } | 32 } |
| 31 return false; | 33 return false; |
| 32 } | 34 } |
| 33 | 35 |
| 34 } // namespace | 36 } // namespace |
| 35 | 37 |
| 36 namespace crashpad { | 38 namespace crashpad { |
| 37 | 39 |
| 38 namespace { | 40 namespace { |
| 39 | 41 |
| 42 // kMaxReadWriteSize needs to be limited to the range of DWORD for the calls to |
| 43 // ::ReadFile() and ::WriteFile(), and also limited to the range of |
| 44 // FileOperationResult to be able to adequately express the number of bytes read |
| 45 // and written in the return values from ReadFile() and NativeWriteFile(). In a |
| 46 // 64-bit build, the former will control, and the limit will be (2^32)-1. In a |
| 47 // 32-bit build, the latter will control, and the limit will be (2^31)-1. |
| 48 constexpr size_t kMaxReadWriteSize = std::min( |
| 49 static_cast<size_t>(std::numeric_limits<DWORD>::max()), |
| 50 static_cast<size_t>(std::numeric_limits<FileOperationResult>::max())); |
| 51 |
| 40 FileHandle OpenFileForOutput(DWORD access, | 52 FileHandle OpenFileForOutput(DWORD access, |
| 41 const base::FilePath& path, | 53 const base::FilePath& path, |
| 42 FileWriteMode mode, | 54 FileWriteMode mode, |
| 43 FilePermissions permissions) { | 55 FilePermissions permissions) { |
| 44 DCHECK(access & GENERIC_WRITE); | 56 DCHECK(access & GENERIC_WRITE); |
| 45 DCHECK_EQ(access & ~(GENERIC_READ | GENERIC_WRITE), 0u); | 57 DCHECK_EQ(access & ~(GENERIC_READ | GENERIC_WRITE), 0u); |
| 46 | 58 |
| 47 DWORD disposition = 0; | 59 DWORD disposition = 0; |
| 48 switch (mode) { | 60 switch (mode) { |
| 49 case FileWriteMode::kReuseOrFail: | 61 case FileWriteMode::kReuseOrFail: |
| (...skipping 13 matching lines...) Expand all Loading... |
| 63 access, | 75 access, |
| 64 FILE_SHARE_READ | FILE_SHARE_WRITE, | 76 FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 65 nullptr, | 77 nullptr, |
| 66 disposition, | 78 disposition, |
| 67 FILE_ATTRIBUTE_NORMAL, | 79 FILE_ATTRIBUTE_NORMAL, |
| 68 nullptr); | 80 nullptr); |
| 69 } | 81 } |
| 70 | 82 |
| 71 } // namespace | 83 } // namespace |
| 72 | 84 |
| 73 // TODO(scottmg): Handle > DWORD sized writes if necessary. | 85 namespace internal { |
| 86 |
| 87 const char kNativeReadFunctionName[] = "ReadFile"; |
| 88 const char kNativeWriteFunctionName[] = "WriteFile"; |
| 89 |
| 90 FileOperationResult NativeWriteFile(FileHandle file, |
| 91 const void* buffer, |
| 92 size_t size) { |
| 93 // TODO(scottmg): This might need to handle the limit for pipes across a |
| 94 // network in the future. |
| 95 |
| 96 const DWORD write_size = |
| 97 static_cast<DWORD>(std::min(size, kMaxReadWriteSize)); |
| 98 |
| 99 DWORD bytes_written; |
| 100 if (!::WriteFile(file, buffer, write_size, &bytes_written, nullptr)) |
| 101 return -1; |
| 102 |
| 103 CHECK_NE(bytes_written, static_cast<DWORD>(-1)); |
| 104 DCHECK_LE(static_cast<size_t>(bytes_written), write_size); |
| 105 return bytes_written; |
| 106 } |
| 107 |
| 108 } // namespace internal |
| 74 | 109 |
| 75 FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) { | 110 FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) { |
| 76 DCHECK(!IsSocketHandle(file)); | 111 DCHECK(!IsSocketHandle(file)); |
| 77 DWORD size_dword = base::checked_cast<DWORD>(size); | 112 |
| 78 DWORD total_read = 0; | 113 const DWORD read_size = static_cast<DWORD>(std::min(size, kMaxReadWriteSize)); |
| 79 char* buffer_c = reinterpret_cast<char*>(buffer); | 114 |
| 80 while (size_dword > 0) { | 115 while (true) { |
| 81 DWORD bytes_read; | 116 DWORD bytes_read; |
| 82 BOOL success = ::ReadFile(file, buffer_c, size_dword, &bytes_read, nullptr); | 117 BOOL success = ::ReadFile(file, buffer, read_size, &bytes_read, nullptr); |
| 83 if (!success) { | 118 if (!success) { |
| 84 if (GetLastError() == ERROR_BROKEN_PIPE) { | 119 if (GetLastError() == ERROR_BROKEN_PIPE) { |
| 85 // When reading a pipe and the write handle has been closed, ReadFile | 120 // When reading a pipe and the write handle has been closed, ReadFile |
| 86 // fails with ERROR_BROKEN_PIPE, but only once all pending data has been | 121 // fails with ERROR_BROKEN_PIPE, but only once all pending data has been |
| 87 // read. | 122 // read. Treat this as EOF. |
| 88 break; | 123 return 0; |
| 89 } else if (GetLastError() != ERROR_MORE_DATA) { | |
| 90 return -1; | |
| 91 } | 124 } |
| 92 } else if (bytes_read == 0 && GetFileType(file) != FILE_TYPE_PIPE) { | 125 return -1; |
| 126 } |
| 127 |
| 128 CHECK_NE(bytes_read, static_cast<DWORD>(-1)); |
| 129 DCHECK_LE(bytes_read, read_size); |
| 130 if (bytes_read != 0 || GetFileType(file) != FILE_TYPE_PIPE) { |
| 93 // Zero bytes read for a file indicates reaching EOF. Zero bytes read from | 131 // Zero bytes read for a file indicates reaching EOF. Zero bytes read from |
| 94 // a pipe indicates only that there was a zero byte WriteFile issued on | 132 // a pipe indicates only that there was a zero byte WriteFile issued on |
| 95 // the other end, so continue reading. | 133 // the other end, so continue reading. |
| 96 break; | 134 return bytes_read; |
| 97 } | 135 } |
| 98 | |
| 99 buffer_c += bytes_read; | |
| 100 size_dword -= bytes_read; | |
| 101 total_read += bytes_read; | |
| 102 } | 136 } |
| 103 return total_read; | |
| 104 } | |
| 105 | |
| 106 FileOperationResult WriteFile(FileHandle file, | |
| 107 const void* buffer, | |
| 108 size_t size) { | |
| 109 // TODO(scottmg): This might need to handle the limit for pipes across a | |
| 110 // network in the future. | |
| 111 DWORD size_dword = base::checked_cast<DWORD>(size); | |
| 112 DWORD bytes_written; | |
| 113 BOOL rv = ::WriteFile(file, buffer, size_dword, &bytes_written, nullptr); | |
| 114 if (!rv) | |
| 115 return -1; | |
| 116 CHECK_EQ(bytes_written, size_dword); | |
| 117 return bytes_written; | |
| 118 } | 137 } |
| 119 | 138 |
| 120 FileHandle OpenFileForRead(const base::FilePath& path) { | 139 FileHandle OpenFileForRead(const base::FilePath& path) { |
| 121 return CreateFile(path.value().c_str(), | 140 return CreateFile(path.value().c_str(), |
| 122 GENERIC_READ, | 141 GENERIC_READ, |
| 123 FILE_SHARE_READ | FILE_SHARE_WRITE, | 142 FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 124 nullptr, | 143 nullptr, |
| 125 OPEN_EXISTING, | 144 OPEN_EXISTING, |
| 126 0, | 145 0, |
| 127 nullptr); | 146 nullptr); |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 | 257 |
| 239 FileOffset LoggingFileSizeByHandle(FileHandle file) { | 258 FileOffset LoggingFileSizeByHandle(FileHandle file) { |
| 240 LARGE_INTEGER file_size; | 259 LARGE_INTEGER file_size; |
| 241 if (!GetFileSizeEx(file, &file_size)) { | 260 if (!GetFileSizeEx(file, &file_size)) { |
| 242 PLOG(ERROR) << "GetFileSizeEx"; | 261 PLOG(ERROR) << "GetFileSizeEx"; |
| 243 return -1; | 262 return -1; |
| 244 } | 263 } |
| 245 return file_size.QuadPart; | 264 return file_size.QuadPart; |
| 246 } | 265 } |
| 247 | 266 |
| 267 FileHandle StdioFileHandle(StdioStream stdio_stream) { |
| 268 DWORD standard_handle; |
| 269 switch (stdio_stream) { |
| 270 case StdioStream::kStandardInput: |
| 271 standard_handle = STD_INPUT_HANDLE; |
| 272 break; |
| 273 case StdioStream::kStandardOutput: |
| 274 standard_handle = STD_OUTPUT_HANDLE; |
| 275 break; |
| 276 case StdioStream::kStandardError: |
| 277 standard_handle = STD_ERROR_HANDLE; |
| 278 break; |
| 279 default: |
| 280 NOTREACHED(); |
| 281 return INVALID_HANDLE_VALUE; |
| 282 } |
| 283 |
| 284 HANDLE handle = GetStdHandle(standard_handle); |
| 285 PLOG_IF(ERROR, handle == INVALID_HANDLE_VALUE) << "GetStdHandle"; |
| 286 return handle; |
| 287 } |
| 288 |
| 248 } // namespace crashpad | 289 } // namespace crashpad |
| OLD | NEW |