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 |