1 // Copyright (c) 2006, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // minidump_file_writer.cc: Minidump file writer implementation.
31 //
32 // See minidump_file_writer.h for documentation.
33
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "client/minidump_file_writer-inl.h"
41 #include "common/linux/linux_libc_support.h"
42 #include "common/string_conversion.h"
43 #if __linux__
44 #include "third_party/lss/linux_syscall_support.h"
45 #endif
46
47 namespace google_breakpad {
48
49 const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
50
MinidumpFileWriter()51 MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) {
52 }
53
~MinidumpFileWriter()54 MinidumpFileWriter::~MinidumpFileWriter() {
55 Close();
56 }
57
Open(const char * path)58 bool MinidumpFileWriter::Open(const char *path) {
59 assert(file_ == -1);
60 #if __linux__
61 file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
62 #else
63 file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
64 #endif
65
66 return file_ != -1;
67 }
68
Close()69 bool MinidumpFileWriter::Close() {
70 bool result = true;
71
72 if (file_ != -1) {
73 if (-1 == ftruncate(file_, position_)) {
74 return false;
75 }
76 #if __linux__
77 result = (sys_close(file_) == 0);
78 #else
79 result = (close(file_) == 0);
80 #endif
81 file_ = -1;
82 }
83
84 return result;
85 }
86
CopyStringToMDString(const wchar_t * str,unsigned int length,TypedMDRVA<MDString> * mdstring)87 bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
88 unsigned int length,
89 TypedMDRVA<MDString> *mdstring) {
90 bool result = true;
91 if (sizeof(wchar_t) == sizeof(u_int16_t)) {
92 // Shortcut if wchar_t is the same size as MDString's buffer
93 result = mdstring->Copy(str, mdstring->get()->length);
94 } else {
95 u_int16_t out[2];
96 int out_idx = 0;
97
98 // Copy the string character by character
99 while (length && result) {
100 UTF32ToUTF16Char(*str, out);
101 if (!out[0])
102 return false;
103
104 // Process one character at a time
105 --length;
106 ++str;
107
108 // Append the one or two UTF-16 characters. The first one will be non-
109 // zero, but the second one may be zero, depending on the conversion from
110 // UTF-32.
111 int out_count = out[1] ? 2 : 1;
112 size_t out_size = sizeof(u_int16_t) * out_count;
113 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
114 out_idx += out_count;
115 }
116 }
117 return result;
118 }
119
CopyStringToMDString(const char * str,unsigned int length,TypedMDRVA<MDString> * mdstring)120 bool MinidumpFileWriter::CopyStringToMDString(const char *str,
121 unsigned int length,
122 TypedMDRVA<MDString> *mdstring) {
123 bool result = true;
124 u_int16_t out[2];
125 int out_idx = 0;
126
127 // Copy the string character by character
128 while (length && result) {
129 int conversion_count = UTF8ToUTF16Char(str, length, out);
130 if (!conversion_count)
131 return false;
132
133 // Move the pointer along based on the nubmer of converted characters
134 length -= conversion_count;
135 str += conversion_count;
136
137 // Append the one or two UTF-16 characters
138 int out_count = out[1] ? 2 : 1;
139 size_t out_size = sizeof(u_int16_t) * out_count;
140 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
141 out_idx += out_count;
142 }
143 return result;
144 }
145
146 template <typename CharType>
WriteStringCore(const CharType * str,unsigned int length,MDLocationDescriptor * location)147 bool MinidumpFileWriter::WriteStringCore(const CharType *str,
148 unsigned int length,
149 MDLocationDescriptor *location) {
150 assert(str);
151 assert(location);
152 // Calculate the mdstring length by either limiting to |length| as passed in
153 // or by finding the location of the NULL character.
154 unsigned int mdstring_length = 0;
155 if (!length)
156 length = INT_MAX;
157 for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
158 ;
159
160 // Allocate the string buffer
161 TypedMDRVA<MDString> mdstring(this);
162 if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t)))
163 return false;
164
165 // Set length excluding the NULL and copy the string
166 mdstring.get()->length =
167 static_cast<u_int32_t>(mdstring_length * sizeof(u_int16_t));
168 bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
169
170 // NULL terminate
171 if (result) {
172 u_int16_t ch = 0;
173 result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
174
175 if (result)
176 *location = mdstring.location();
177 }
178
179 return result;
180 }
181
WriteString(const wchar_t * str,unsigned int length,MDLocationDescriptor * location)182 bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
183 MDLocationDescriptor *location) {
184 return WriteStringCore(str, length, location);
185 }
186
WriteString(const char * str,unsigned int length,MDLocationDescriptor * location)187 bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
188 MDLocationDescriptor *location) {
189 return WriteStringCore(str, length, location);
190 }
191
WriteMemory(const void * src,size_t size,MDMemoryDescriptor * output)192 bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
193 MDMemoryDescriptor *output) {
194 assert(src);
195 assert(output);
196 UntypedMDRVA mem(this);
197
198 if (!mem.Allocate(size))
199 return false;
200 if (!mem.Copy(src, mem.size()))
201 return false;
202
203 output->start_of_memory_range = reinterpret_cast<u_int64_t>(src);
204 output->memory = mem.location();
205
206 return true;
207 }
208
Allocate(size_t size)209 MDRVA MinidumpFileWriter::Allocate(size_t size) {
210 assert(size);
211 assert(file_ != -1);
212 size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
213
214 if (position_ + aligned_size > size_) {
215 size_t growth = aligned_size;
216 size_t minimal_growth = getpagesize();
217
218 // Ensure that the file grows by at least the size of a memory page
219 if (growth < minimal_growth)
220 growth = minimal_growth;
221
222 size_t new_size = size_ + growth;
223 if (ftruncate(file_, new_size) != 0)
224 return kInvalidMDRVA;
225
226 size_ = new_size;
227 }
228
229 MDRVA current_position = position_;
230 position_ += static_cast<MDRVA>(aligned_size);
231
232 return current_position;
233 }
234
Copy(MDRVA position,const void * src,ssize_t size)235 bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
236 assert(src);
237 assert(size);
238 assert(file_ != -1);
239
240 // Ensure that the data will fit in the allocated space
241 if (static_cast<size_t>(size + position) > size_)
242 return false;
243
244 // Seek and write the data
245 #if __linux__
246 if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
247 if (sys_write(file_, src, size) == size) {
248 #else
249 if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
250 if (write(file_, src, size) == size) {
251 #endif
252 return true;
253 }
254 }
255
256 return false;
257 }
258
259 bool UntypedMDRVA::Allocate(size_t size) {
260 assert(size_ == 0);
261 size_ = size;
262 position_ = writer_->Allocate(size_);
263 return position_ != MinidumpFileWriter::kInvalidMDRVA;
264 }
265
266 bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
267 assert(src);
268 assert(size);
269 assert(pos + size <= position_ + size_);
270 return writer_->Copy(pos, src, size);
271 }
272
273 } // namespace google_breakpad
274