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 defined(__linux__) && __linux__ 44#include "third_party/lss/linux_syscall_support.h" 45#endif 46 47namespace google_breakpad { 48 49const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1); 50 51MinidumpFileWriter::MinidumpFileWriter() 52 : file_(-1), 53 close_file_when_destroyed_(true), 54 position_(0), 55 size_(0) { 56} 57 58MinidumpFileWriter::~MinidumpFileWriter() { 59 if (close_file_when_destroyed_) 60 Close(); 61} 62 63bool MinidumpFileWriter::Open(const char *path) { 64 assert(file_ == -1); 65#if defined(__linux__) && __linux__ 66 file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); 67#else 68 file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); 69#endif 70 71 return file_ != -1; 72} 73 74void MinidumpFileWriter::SetFile(const int file) { 75 assert(file_ == -1); 76 file_ = file; 77 close_file_when_destroyed_ = false; 78} 79 80bool MinidumpFileWriter::Close() { 81 bool result = true; 82 83 if (file_ != -1) { 84 if (-1 == ftruncate(file_, position_)) { 85 return false; 86 } 87#if defined(__linux__) && __linux__ 88 result = (sys_close(file_) == 0); 89#else 90 result = (close(file_) == 0); 91#endif 92 file_ = -1; 93 } 94 95 return result; 96} 97 98bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str, 99 unsigned int length, 100 TypedMDRVA<MDString> *mdstring) { 101 bool result = true; 102 if (sizeof(wchar_t) == sizeof(uint16_t)) { 103 // Shortcut if wchar_t is the same size as MDString's buffer 104 result = mdstring->Copy(str, mdstring->get()->length); 105 } else { 106 uint16_t out[2]; 107 int out_idx = 0; 108 109 // Copy the string character by character 110 while (length && result) { 111 UTF32ToUTF16Char(*str, out); 112 if (!out[0]) 113 return false; 114 115 // Process one character at a time 116 --length; 117 ++str; 118 119 // Append the one or two UTF-16 characters. The first one will be non- 120 // zero, but the second one may be zero, depending on the conversion from 121 // UTF-32. 122 int out_count = out[1] ? 2 : 1; 123 size_t out_size = sizeof(uint16_t) * out_count; 124 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); 125 out_idx += out_count; 126 } 127 } 128 return result; 129} 130 131bool MinidumpFileWriter::CopyStringToMDString(const char *str, 132 unsigned int length, 133 TypedMDRVA<MDString> *mdstring) { 134 bool result = true; 135 uint16_t out[2]; 136 int out_idx = 0; 137 138 // Copy the string character by character 139 while (length && result) { 140 int conversion_count = UTF8ToUTF16Char(str, length, out); 141 if (!conversion_count) 142 return false; 143 144 // Move the pointer along based on the nubmer of converted characters 145 length -= conversion_count; 146 str += conversion_count; 147 148 // Append the one or two UTF-16 characters 149 int out_count = out[1] ? 2 : 1; 150 size_t out_size = sizeof(uint16_t) * out_count; 151 result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); 152 out_idx += out_count; 153 } 154 return result; 155} 156 157template <typename CharType> 158bool MinidumpFileWriter::WriteStringCore(const CharType *str, 159 unsigned int length, 160 MDLocationDescriptor *location) { 161 assert(str); 162 assert(location); 163 // Calculate the mdstring length by either limiting to |length| as passed in 164 // or by finding the location of the NULL character. 165 unsigned int mdstring_length = 0; 166 if (!length) 167 length = INT_MAX; 168 for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) 169 ; 170 171 // Allocate the string buffer 172 TypedMDRVA<MDString> mdstring(this); 173 if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t))) 174 return false; 175 176 // Set length excluding the NULL and copy the string 177 mdstring.get()->length = 178 static_cast<uint32_t>(mdstring_length * sizeof(uint16_t)); 179 bool result = CopyStringToMDString(str, mdstring_length, &mdstring); 180 181 // NULL terminate 182 if (result) { 183 uint16_t ch = 0; 184 result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); 185 186 if (result) 187 *location = mdstring.location(); 188 } 189 190 return result; 191} 192 193bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length, 194 MDLocationDescriptor *location) { 195 return WriteStringCore(str, length, location); 196} 197 198bool MinidumpFileWriter::WriteString(const char *str, unsigned int length, 199 MDLocationDescriptor *location) { 200 return WriteStringCore(str, length, location); 201} 202 203bool MinidumpFileWriter::WriteMemory(const void *src, size_t size, 204 MDMemoryDescriptor *output) { 205 assert(src); 206 assert(output); 207 UntypedMDRVA mem(this); 208 209 if (!mem.Allocate(size)) 210 return false; 211 if (!mem.Copy(src, mem.size())) 212 return false; 213 214 output->start_of_memory_range = reinterpret_cast<uint64_t>(src); 215 output->memory = mem.location(); 216 217 return true; 218} 219 220MDRVA MinidumpFileWriter::Allocate(size_t size) { 221 assert(size); 222 assert(file_ != -1); 223 size_t aligned_size = (size + 7) & ~7; // 64-bit alignment 224 225 if (position_ + aligned_size > size_) { 226 size_t growth = aligned_size; 227 size_t minimal_growth = getpagesize(); 228 229 // Ensure that the file grows by at least the size of a memory page 230 if (growth < minimal_growth) 231 growth = minimal_growth; 232 233 size_t new_size = size_ + growth; 234 if (ftruncate(file_, new_size) != 0) 235 return kInvalidMDRVA; 236 237 size_ = new_size; 238 } 239 240 MDRVA current_position = position_; 241 position_ += static_cast<MDRVA>(aligned_size); 242 243 return current_position; 244} 245 246bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { 247 assert(src); 248 assert(size); 249 assert(file_ != -1); 250 251 // Ensure that the data will fit in the allocated space 252 if (static_cast<size_t>(size + position) > size_) 253 return false; 254 255 // Seek and write the data 256#if defined(__linux__) && __linux__ 257 if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { 258 if (sys_write(file_, src, size) == size) { 259#else 260 if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { 261 if (write(file_, src, size) == size) { 262#endif 263 return true; 264 } 265 } 266 267 return false; 268} 269 270bool UntypedMDRVA::Allocate(size_t size) { 271 assert(size_ == 0); 272 size_ = size; 273 position_ = writer_->Allocate(size_); 274 return position_ != MinidumpFileWriter::kInvalidMDRVA; 275} 276 277bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) { 278 assert(src); 279 assert(size); 280 assert(pos + size <= position_ + size_); 281 return writer_->Copy(pos, src, size); 282} 283 284} // namespace google_breakpad 285