Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Crashpad Authors. All rights reserved. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (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 | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 | |
| 15 #include "client/crash_report_database.h" | |
| 16 | |
| 17 #include <fcntl.h> | |
| 18 #import <Foundation/Foundation.h> | |
| 19 #include <stdio.h> | |
| 20 #include <sys/errno.h> | |
|
Mark Mentovai
2015/01/08 22:38:10
Just <errno.h>
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 21 #include <sys/stat.h> | |
| 22 #include <sys/types.h> | |
| 23 #include <unistd.h> | |
| 24 #include <uuid/uuid.h> | |
| 25 | |
| 26 #include "base/logging.h" | |
| 27 #include "base/posix/eintr_wrapper.h" | |
| 28 #include "base/scoped_generic.h" | |
| 29 #include "base/strings/string_piece.h" | |
| 30 #include "base/strings/stringprintf.h" | |
| 31 #include "base/strings/sys_string_conversions.h" | |
| 32 #include "util/mac/xattr.h" | |
| 33 | |
| 34 namespace crashpad { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 const char kDatabaseDirectoryName[] = "Crashpad Database"; | |
| 39 | |
| 40 const char kWriteDirectory[] = "In Progress"; | |
|
Mark Mentovai
2015/01/08 22:38:10
If I saw these three directories side-by-side on t
Robert Sesek
2015/01/13 16:18:23
This seems self-contradictory to say that it shoul
| |
| 41 | |
| 42 const char kUploadPendingDirectory[] = "Reports"; | |
| 43 | |
| 44 const char kUploadedDirectory[] = "Uploaded"; | |
| 45 | |
| 46 const char kPlaceholderReportFileExtension[] = "placeholder"; | |
| 47 const char kCrashReportFileExtension[] = "dmp"; | |
| 48 | |
| 49 const char kXattrUUID[] = "uuid"; | |
| 50 const char kXattrCollectorID[] = "id"; | |
| 51 const char kXattrIsUploaded[] = "uploaded"; | |
| 52 const char kXattrLastUploadTime[] = "last_upload_time"; | |
| 53 const char kXattrUploadAttemptCount[] = "upload_count"; | |
| 54 | |
| 55 const char kXattrDatabaseInitialized[] = "initialized"; | |
| 56 | |
| 57 struct ScopedFileLockTraits { | |
| 58 static int InvalidValue() { | |
| 59 return 0; | |
| 60 } | |
| 61 | |
| 62 static void Free(int fd) { | |
| 63 PCHECK(IGNORE_EINTR(close(fd)) == 0) << "close"; | |
| 64 } | |
| 65 }; | |
| 66 | |
| 67 using ScopedFileLock = base::ScopedGeneric<int, ScopedFileLockTraits>; | |
| 68 | |
| 69 class CrashReportDatabaseMac : public CrashReportDatabase { | |
|
Mark Mentovai
2015/01/08 22:38:10
Either this file or this class should have some co
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 70 public: | |
| 71 explicit CrashReportDatabaseMac(const base::FilePath& path); | |
| 72 virtual ~CrashReportDatabaseMac(); | |
| 73 | |
| 74 bool Initialize(); | |
| 75 | |
| 76 // CrashReportDatabase: | |
| 77 OperationStatus PrepareNewCrashReport(Report* report) override; | |
| 78 OperationStatus FinishedWritingCrashReport(const UUID& uuid) override; | |
| 79 OperationStatus LookUpCrashReport(const UUID& uuid, | |
| 80 Report* report) override; | |
| 81 OperationStatus GetNotUploadedReports( | |
| 82 std::vector<const Report>* reports) override; | |
| 83 OperationStatus GetUploadedReports( | |
| 84 std::vector<const Report>* reports) override; | |
| 85 OperationStatus RecordUploadAttempt(const UUID& uuid, | |
| 86 bool successful, | |
| 87 const std::string& id) override; | |
| 88 | |
| 89 private: | |
| 90 // Locates a crash report by |uuid| in the database and returns the full path | |
| 91 // to the report file, or an empty path if it cannot be found. | |
| 92 base::FilePath LocateCrashReport(const UUID& uuid); | |
| 93 | |
| 94 // Obtains an advisory lock on the file at |path|. The flock is used to | |
| 95 // prevent cross-process concurrent metadata reads or writes. While xattrs | |
| 96 // do not observe the lock, if the lock-then-mutate protocol is observed by | |
| 97 // all clients of the database, it still enforces synchronization. | |
| 98 ScopedFileLock ObtainReportLock(const base::FilePath& path); | |
| 99 | |
| 100 // Reads all the database xattrs from a file at |path| into the |report|. The | |
| 101 // flock must be obtained before calling this. | |
| 102 bool ReadReportMetadataLocked(const base::FilePath& path, | |
| 103 Report* report); | |
| 104 | |
| 105 // Reads the metadata from all the reports in a database subdirectory. | |
| 106 // Invalid reports are skipped. | |
| 107 OperationStatus ReportsInDirectory(const base::FilePath& path, | |
| 108 std::vector<const Report>* reports); | |
| 109 | |
| 110 | |
| 111 // Creates a database xattr name from the short constant name. | |
| 112 static std::string XattrName(const base::StringPiece& name); | |
| 113 | |
| 114 base::FilePath base_dir_; | |
| 115 | |
| 116 DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac); | |
| 117 }; | |
| 118 | |
| 119 CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) | |
| 120 : CrashReportDatabase(), base_dir_(path) { | |
| 121 } | |
| 122 | |
| 123 CrashReportDatabaseMac::~CrashReportDatabaseMac() {} | |
| 124 | |
| 125 bool CrashReportDatabaseMac::Initialize() { | |
| 126 NSFileManager* file_manager = [NSFileManager defaultManager]; | |
| 127 | |
| 128 // Check if the database already exists. | |
| 129 BOOL is_dir = NO; | |
| 130 if ([file_manager fileExistsAtPath:base::SysUTF8ToNSString(base_dir_.value()) | |
| 131 isDirectory:&is_dir]) { | |
| 132 if (!is_dir) { | |
| 133 LOG(ERROR) << "Database exists at " << base_dir_.value() | |
| 134 << " but is not a directory"; | |
| 135 return false; | |
| 136 } | |
| 137 } else { | |
| 138 // The database directory does not exist, so create it. | |
| 139 if (mkdir(base_dir_.value().c_str(), S_IRWXU | S_IRWXG)) { | |
|
Mark Mentovai
2015/01/08 22:38:10
Why are you giving group (but not other) permissio
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 140 PLOG(ERROR) << "mkdir " << base_dir_.value(); | |
| 141 return false; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 // Create the three processing directories for the database. | |
| 146 const char* paths[] = { | |
| 147 kWriteDirectory, | |
| 148 kUploadPendingDirectory, | |
| 149 kUploadedDirectory | |
| 150 }; | |
| 151 for (size_t i = 0; i < arraysize(paths); ++i) { | |
| 152 base::FilePath path = base_dir_.Append(paths[i]); | |
| 153 BOOL is_dir = NO; | |
| 154 if ([file_manager fileExistsAtPath:base::SysUTF8ToNSString(path.value()) | |
|
Mark Mentovai
2015/01/08 22:38:10
You’ve got this pattern twice now, maybe you want
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 155 isDirectory:&is_dir] && | |
| 156 is_dir) { | |
| 157 continue; | |
| 158 } | |
| 159 | |
| 160 if (mkdir(path.value().c_str(), S_IRWXU | S_IRWXG)) { | |
| 161 PLOG(ERROR) << "mkdir " << path.value(); | |
| 162 return false; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 // Write an xattr as the last step, to ensure the filesystem has support for | |
| 167 // them. This attribute will never be read. | |
|
Mark Mentovai
2015/01/08 22:38:10
Smart!
| |
| 168 return WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true); | |
| 169 } | |
| 170 | |
| 171 CrashReportDatabase::OperationStatus | |
| 172 CrashReportDatabaseMac::PrepareNewCrashReport( | |
| 173 CrashReportDatabase::Report* report) { | |
| 174 *report = Report(); | |
| 175 uuid_t uuid; | |
| 176 uuid_generate(uuid); | |
| 177 report->uuid.InitializeFromBytes(uuid); | |
| 178 | |
| 179 base::FilePath write_dir = base_dir_.Append(kWriteDirectory); | |
| 180 | |
| 181 // Create a placeholder file that will be used to store a record of this | |
| 182 // operation. The crash report file is written by a client of this class. | |
| 183 base::FilePath placeholder = write_dir.Append( | |
| 184 report->uuid.ToString() + "." + kPlaceholderReportFileExtension); | |
| 185 NSFileManager* manager = [NSFileManager defaultManager]; | |
| 186 if (![manager createFileAtPath:base::SysUTF8ToNSString(placeholder.value()) | |
|
Mark Mentovai
2015/01/08 22:38:10
This doesn’t have O_EXCL semantics, but it probabl
Mark Mentovai
2015/01/08 22:38:10
Honestly, I was kinda hoping that the on-disk file
Robert Sesek
2015/01/13 16:18:23
I'm not sure the PID is useful. Timestamp definite
| |
| 187 contents:[NSData data] | |
| 188 attributes:nil]) { | |
| 189 LOG(ERROR) << "Failed to create report placeholder file " | |
| 190 << placeholder.value(); | |
| 191 return kFileSystemError; | |
| 192 } | |
| 193 | |
| 194 report->file_path = write_dir.Append( | |
|
Mark Mentovai
2015/01/08 22:38:10
Why is there a separate placeholder file? Why don’
Robert Sesek
2015/01/13 16:18:23
I'm glad you suggested returning the FD. I went ba
| |
| 195 report->uuid.ToString() + "." + kCrashReportFileExtension); | |
| 196 | |
| 197 return kNoError; | |
| 198 } | |
| 199 | |
| 200 CrashReportDatabase::OperationStatus | |
| 201 CrashReportDatabaseMac::FinishedWritingCrashReport(const UUID& uuid) { | |
| 202 NSFileManager* manager = [NSFileManager defaultManager]; | |
| 203 base::FilePath write_dir = base_dir_.Append(kWriteDirectory); | |
| 204 | |
| 205 // Look up the placeholder file to ensure this UUID was prepared. | |
| 206 base::FilePath placeholder = write_dir.Append( | |
| 207 uuid.ToString() + "." + kPlaceholderReportFileExtension); | |
| 208 if (![manager fileExistsAtPath:base::SysUTF8ToNSString(placeholder.value()) | |
| 209 isDirectory:nullptr]) { | |
| 210 LOG(ERROR) << "Failed to find placeholder report " << placeholder.value(); | |
| 211 return kReportNotFound; | |
| 212 } | |
| 213 | |
| 214 // Delete the placeholder. | |
| 215 if (unlink(placeholder.value().c_str())) { | |
| 216 PLOG(ERROR) << "unlink " << placeholder.value(); | |
|
Mark Mentovai
2015/01/08 22:38:09
For things that you’re tolerating, WARNING is prob
| |
| 217 // While not a good sign, continue processing if possible. | |
| 218 } | |
| 219 | |
| 220 // Now make sure the crash report was actually written. | |
| 221 base::FilePath report_path = write_dir.Append( | |
| 222 uuid.ToString() + "." + kCrashReportFileExtension); | |
| 223 if (![manager fileExistsAtPath:base::SysUTF8ToNSString(report_path.value()) | |
|
Mark Mentovai
2015/01/08 22:38:10
I don’t know if you need any of these file-exists
Robert Sesek
2015/01/13 16:18:22
Done.
| |
| 224 isDirectory:nullptr]) { | |
| 225 LOG(ERROR) << "Failed to find crash report " << report_path.value(); | |
| 226 return kReportNotFound; | |
| 227 } | |
| 228 | |
| 229 ScopedFileLock lock(ObtainReportLock(report_path)); | |
| 230 | |
| 231 // Record the UUID of this crash report. | |
| 232 if (!WriteXattr(report_path, XattrName(kXattrUUID), uuid.ToString())) { | |
| 233 return kDatabaseError; | |
| 234 } | |
| 235 | |
| 236 // Move the report to its new location for uploading. | |
| 237 base::FilePath new_path = | |
| 238 base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName()); | |
| 239 if (rename(report_path.value().c_str(), new_path.value().c_str())) { | |
| 240 PLOG(ERROR) << "rename " << report_path.value() << " to " | |
| 241 << new_path.value(); | |
| 242 return kFileSystemError; | |
| 243 } | |
| 244 | |
| 245 return kNoError; | |
| 246 } | |
| 247 | |
| 248 CrashReportDatabase::OperationStatus | |
| 249 CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid, | |
| 250 CrashReportDatabase::Report* report) { | |
| 251 base::FilePath path = LocateCrashReport(uuid); | |
| 252 if (path.empty()) | |
| 253 return kReportNotFound; | |
| 254 | |
| 255 ScopedFileLock lock(ObtainReportLock(path)); | |
| 256 | |
| 257 *report = Report(); | |
| 258 report->file_path = path; | |
| 259 if (!ReadReportMetadataLocked(path, report)) | |
| 260 return kDatabaseError; | |
|
Mark Mentovai
2015/01/08 22:38:10
The interface documentation said that this return
Robert Sesek
2015/01/13 16:18:22
ReadXattr and friends, do, though. Do you want mor
| |
| 261 | |
| 262 return kNoError; | |
| 263 } | |
| 264 | |
| 265 CrashReportDatabase::OperationStatus | |
| 266 CrashReportDatabaseMac::GetNotUploadedReports( | |
| 267 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 268 return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports); | |
| 269 } | |
| 270 | |
| 271 CrashReportDatabase::OperationStatus | |
| 272 CrashReportDatabaseMac::GetUploadedReports( | |
| 273 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 274 return ReportsInDirectory(base_dir_.Append(kUploadedDirectory), reports); | |
| 275 } | |
| 276 | |
| 277 CrashReportDatabase::OperationStatus | |
| 278 CrashReportDatabaseMac::RecordUploadAttempt(const UUID& uuid, | |
| 279 bool successful, | |
| 280 const std::string& id) { | |
| 281 DCHECK(successful || id.empty()); | |
| 282 | |
| 283 base::FilePath report_path = LocateCrashReport(uuid); | |
| 284 if (report_path.empty()) | |
| 285 return kReportNotFound; | |
| 286 | |
| 287 ScopedFileLock lock(ObtainReportLock(report_path)); | |
|
Mark Mentovai
2015/01/08 22:38:10
The file needs to stay locked while the uploader i
Robert Sesek
2015/01/13 16:18:23
Done. Now the caller has a two-phase call like wit
| |
| 288 | |
| 289 if (successful) { | |
| 290 base::FilePath new_path = | |
| 291 base_dir_.Append(kUploadedDirectory).Append(report_path.BaseName()); | |
| 292 if (rename(report_path.value().c_str(), new_path.value().c_str())) { | |
| 293 PLOG(ERROR) << "rename " << report_path.value() << " to " | |
| 294 << new_path.value(); | |
| 295 return kFileSystemError; | |
| 296 } | |
| 297 report_path = new_path; | |
| 298 } | |
| 299 | |
| 300 if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) { | |
| 301 return kDatabaseError; | |
| 302 } | |
| 303 if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) { | |
| 304 return kDatabaseError; | |
| 305 } | |
| 306 | |
| 307 int upload_attempts = 0; | |
| 308 std::string name = XattrName(kXattrUploadAttemptCount); | |
| 309 if (HasXattr(report_path, name) && | |
| 310 !ReadXattrInt(report_path, name, &upload_attempts)) { | |
| 311 return kDatabaseError; | |
| 312 } | |
| 313 if (!WriteXattrInt(report_path, name, ++upload_attempts)) { | |
| 314 return kDatabaseError; | |
| 315 } | |
| 316 | |
| 317 if (!WriteXattrTimeT(report_path, | |
|
Mark Mentovai
2015/01/08 22:38:10
I’d set the time_t before doing the upload_attempt
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 318 XattrName(kXattrLastUploadTime), | |
| 319 time(nullptr))) { | |
| 320 return kDatabaseError; | |
| 321 } | |
| 322 | |
| 323 return kNoError; | |
| 324 } | |
| 325 | |
| 326 base::FilePath CrashReportDatabaseMac::LocateCrashReport(const UUID& uuid) { | |
| 327 const std::string target_uuid = uuid.ToString(); | |
| 328 NSString* path_ns_string = base::SysUTF8ToNSString(base_dir_.value()); | |
| 329 NSString* dump_extension = | |
| 330 [NSString stringWithUTF8String:kCrashReportFileExtension]; | |
| 331 NSDirectoryEnumerator* dir_it = | |
| 332 [[NSFileManager defaultManager] enumeratorAtPath:path_ns_string]; | |
|
Mark Mentovai
2015/01/08 22:38:09
Directory enumeration is kind of heavy. Why isn’t
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 333 NSString* file = nil; | |
| 334 while ((file = [dir_it nextObject])) { | |
| 335 if (![[file pathExtension] isEqualToString:dump_extension]) | |
| 336 continue; | |
| 337 | |
| 338 base::FilePath full_path = | |
| 339 base_dir_.Append([file fileSystemRepresentation]); | |
| 340 | |
| 341 ScopedFileLock lock(ObtainReportLock(full_path)); | |
| 342 std::string uuid_string; | |
| 343 if (ReadXattr(full_path, XattrName(kXattrUUID), &uuid_string) && | |
| 344 uuid_string == target_uuid) { | |
| 345 return full_path; | |
| 346 } | |
| 347 } | |
| 348 return base::FilePath(); | |
| 349 } | |
| 350 | |
| 351 ScopedFileLock CrashReportDatabaseMac::ObtainReportLock( | |
| 352 const base::FilePath& path) { | |
| 353 int fd = HANDLE_EINTR(open(path.value().c_str(), | |
| 354 O_RDONLY | O_EXLOCK | O_CLOEXEC)); | |
| 355 PCHECK(fd >= 0) << "open lock " << path.value(); | |
|
Mark Mentovai
2015/01/08 22:38:10
CHECK is a little harsh here:
1. The filesystem m
Robert Sesek
2015/01/13 16:18:23
1) Callers now check the result of ObtainReportLoc
| |
| 356 return ScopedFileLock(fd); | |
| 357 } | |
| 358 | |
| 359 bool CrashReportDatabaseMac::ReadReportMetadataLocked( | |
|
Mark Mentovai
2015/01/08 22:38:09
This one can be static.
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 360 const base::FilePath& path, Report* report) { | |
| 361 std::string uuid_string; | |
| 362 if (!ReadXattr(path, XattrName(kXattrUUID), &uuid_string) || | |
| 363 !report->uuid.InitializeFromString(uuid_string)) { | |
| 364 return false; | |
| 365 } | |
| 366 if (!HasXattr(path, XattrName(kXattrCollectorID))) { | |
|
Mark Mentovai
2015/01/08 22:38:10
HasXattr(), ReadXattr(), HasXattr(), ReadXattr(),
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 367 report->id = std::string(); | |
| 368 } else if (!ReadXattr(path, XattrName(kXattrCollectorID), &report->id)) { | |
| 369 return false; | |
| 370 } | |
| 371 if (!HasXattr(path, XattrName(kXattrIsUploaded))) { | |
| 372 report->uploaded = false; | |
| 373 } else if (!ReadXattrBool(path, XattrName(kXattrIsUploaded), | |
| 374 &report->uploaded)) { | |
| 375 return false; | |
| 376 } | |
| 377 if (!HasXattr(path, XattrName(kXattrLastUploadTime))) { | |
| 378 report->last_upload_attempt_time = 0; | |
| 379 } else if (!ReadXattrTimeT(path, XattrName(kXattrLastUploadTime), | |
| 380 &report->last_upload_attempt_time)) { | |
| 381 return false; | |
| 382 } | |
| 383 if (!HasXattr(path, XattrName(kXattrUploadAttemptCount))) { | |
| 384 report->upload_attempts = 0; | |
| 385 } else if (!ReadXattrInt(path, XattrName(kXattrUploadAttemptCount), | |
| 386 &report->upload_attempts)) { | |
| 387 return false; | |
| 388 } | |
| 389 | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 CrashReportDatabase::OperationStatus | |
| 394 CrashReportDatabaseMac::ReportsInDirectory( | |
|
Mark Mentovai
2015/01/08 22:38:09
This one can be static too.
Robert Sesek
2015/01/13 16:18:22
Done.
| |
| 395 const base::FilePath& path, | |
| 396 std::vector<const CrashReportDatabase::Report>* reports) { | |
| 397 DCHECK(reports->empty()); | |
| 398 | |
| 399 NSError* error = nil; | |
| 400 NSArray* paths = [[NSFileManager defaultManager] | |
| 401 contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value()) | |
| 402 error:&error]; | |
|
Mark Mentovai
2015/01/08 22:38:10
Not really sure that this is any better than opend
Robert Sesek
2015/01/13 16:18:23
I don't have to manage the DIR* or handle dot-dirs
| |
| 403 if (error) { | |
| 404 LOG(ERROR) << "Failed to enumerate reports in directory " << path.value() | |
| 405 << ": " << [[error description] UTF8String]; | |
| 406 return kFileSystemError; | |
| 407 } | |
| 408 | |
| 409 reports->reserve([paths count]); | |
| 410 for (NSString* entry in paths) { | |
| 411 base::FilePath report_path = path.Append([entry fileSystemRepresentation]); | |
| 412 ScopedFileLock lock(ObtainReportLock(report_path)); | |
| 413 Report report; | |
| 414 if (!ReadReportMetadataLocked(report_path, &report)) { | |
| 415 LOG(ERROR) << "Failed to read report metadata for " | |
|
Mark Mentovai
2015/01/08 22:38:09
Since you continue on past this, it’s more of a WA
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 416 << report_path.value(); | |
| 417 continue; | |
| 418 } | |
| 419 reports->push_back(report); | |
| 420 } | |
| 421 | |
| 422 return kNoError; | |
| 423 } | |
| 424 | |
| 425 // static | |
| 426 std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) { | |
| 427 return base::StringPrintf("com.google.crashpad.%s", name.data()); | |
|
Mark Mentovai
2015/01/08 22:38:10
org.googlecode
Mark Mentovai
2015/01/09 14:26:09
I wrote:
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 428 } | |
| 429 | |
| 430 } // namespace | |
| 431 | |
| 432 // static | |
| 433 scoped_ptr<CrashReportDatabase> CrashReportDatabase::Initialize( | |
| 434 const base::FilePath& path) { | |
| 435 scoped_ptr<CrashReportDatabase> database; | |
| 436 | |
| 437 NSFileManager* file_manager = [NSFileManager defaultManager]; | |
|
Mark Mentovai
2015/01/08 22:38:10
I don’t think you need this check, because CrashRe
Robert Sesek
2015/01/13 16:18:23
Done.
| |
| 438 BOOL is_dir = NO; | |
| 439 if ([file_manager fileExistsAtPath:base::SysUTF8ToNSString(path.value()) | |
| 440 isDirectory:&is_dir] && | |
| 441 !is_dir) { | |
| 442 LOG(ERROR) << "File exists at " << path.value() | |
| 443 << " but is not a directory"; | |
| 444 return database; | |
| 445 } | |
| 446 | |
| 447 scoped_ptr<CrashReportDatabaseMac> database_mac( | |
| 448 new CrashReportDatabaseMac(path.Append(kDatabaseDirectoryName))); | |
| 449 if (!database_mac->Initialize()) | |
| 450 database_mac.reset(); | |
| 451 | |
| 452 database.reset(database_mac.release()); | |
| 453 return database; | |
| 454 } | |
| 455 | |
| 456 } // namespace crashpad | |
| OLD | NEW |