1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "BreakpointManager.h" 7 8 #include <algorithm> 9 10 #include <AutoDeleter.h> 11 12 #include <commpage_defs.h> 13 #include <kernel.h> 14 #include <util/AutoLock.h> 15 #include <vm/vm.h> 16 17 18 //#define TRACE_BREAKPOINT_MANAGER 19 #ifdef TRACE_BREAKPOINT_MANAGER 20 # define TRACE(x...) dprintf(x) 21 #else 22 # define TRACE(x...) do {} while (false) 23 #endif 24 25 26 // soft limit for the number of breakpoints 27 const int32 kMaxBreakpointCount = 10240; 28 29 30 BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address) 31 : 32 breakpoint(NULL), 33 address(address) 34 { 35 } 36 37 38 // #pragma mark - 39 40 41 BreakpointManager::BreakpointManager() 42 : 43 fBreakpointCount(0), 44 fWatchpointCount(0) 45 { 46 rw_lock_init(&fLock, "breakpoint manager"); 47 } 48 49 50 BreakpointManager::~BreakpointManager() 51 { 52 WriteLocker locker(fLock); 53 54 // delete the installed breakpoint objects 55 BreakpointTree::Iterator it = fBreakpoints.GetIterator(); 56 while (InstalledBreakpoint* installedBreakpoint = it.Next()) { 57 it.Remove(); 58 59 // delete underlying software breakpoint 60 if (installedBreakpoint->breakpoint->software) 61 delete installedBreakpoint->breakpoint; 62 63 delete installedBreakpoint; 64 } 65 66 // delete the watchpoints 67 while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) 68 delete watchpoint; 69 70 // delete the hardware breakpoint objects 71 while (Breakpoint* breakpoint = fHardwareBreakpoints.RemoveHead()) 72 delete breakpoint; 73 74 rw_lock_destroy(&fLock); 75 } 76 77 78 status_t 79 BreakpointManager::Init() 80 { 81 // create objects for the hardware breakpoints 82 for (int32 i = 0; i < DEBUG_MAX_BREAKPOINTS; i++) { 83 Breakpoint* breakpoint = new(std::nothrow) Breakpoint; 84 if (breakpoint == NULL) 85 return B_NO_MEMORY; 86 87 breakpoint->address = 0; 88 breakpoint->installedBreakpoint = NULL; 89 breakpoint->used = false; 90 breakpoint->software = false; 91 92 fHardwareBreakpoints.Add(breakpoint); 93 } 94 95 return B_OK; 96 } 97 98 99 status_t 100 BreakpointManager::InstallBreakpoint(void* _address) 101 { 102 const addr_t address = (addr_t)_address; 103 104 WriteLocker locker(fLock); 105 106 if (fBreakpointCount >= kMaxBreakpointCount) 107 return B_BUSY; 108 109 // check whether there's already a breakpoint at the address 110 InstalledBreakpoint* installed = fBreakpoints.Lookup(address); 111 if (installed != NULL) 112 return B_BAD_VALUE; 113 114 // create the breakpoint object 115 installed = new(std::nothrow) InstalledBreakpoint(address); 116 if (installed == NULL) 117 return B_NO_MEMORY; 118 ObjectDeleter<InstalledBreakpoint> installedDeleter(installed); 119 120 // If we still have enough hardware breakpoints left, install a hardware 121 // breakpoint. 122 Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(false); 123 if (breakpoint != NULL) { 124 status_t error = _InstallHardwareBreakpoint(breakpoint, address); 125 if (error != B_OK) 126 return error; 127 128 breakpoint->installedBreakpoint = installed; 129 installed->breakpoint = breakpoint; 130 } else { 131 // install a software breakpoint 132 status_t error = _InstallSoftwareBreakpoint(installed, address); 133 if (error != B_OK) 134 return error; 135 } 136 137 fBreakpoints.Insert(installed); 138 installedDeleter.Detach(); 139 fBreakpointCount++; 140 141 return B_OK; 142 } 143 144 145 status_t 146 BreakpointManager::UninstallBreakpoint(void* _address) 147 { 148 const addr_t address = (addr_t)_address; 149 150 WriteLocker locker(fLock); 151 152 InstalledBreakpoint* installed = fBreakpoints.Lookup(address); 153 if (installed == NULL) 154 return B_BAD_VALUE; 155 156 if (installed->breakpoint->software) 157 _UninstallSoftwareBreakpoint(installed->breakpoint); 158 else 159 _UninstallHardwareBreakpoint(installed->breakpoint); 160 161 fBreakpoints.Remove(installed); 162 delete installed; 163 fBreakpointCount--; 164 165 return B_OK; 166 } 167 168 169 status_t 170 BreakpointManager::InstallWatchpoint(void* _address, uint32 type, int32 length) 171 { 172 const addr_t address = (addr_t)_address; 173 174 WriteLocker locker(fLock); 175 176 InstalledWatchpoint* watchpoint = _FindWatchpoint(address); 177 if (watchpoint != NULL) 178 return B_BAD_VALUE; 179 180 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS 181 // We need at least one hardware breakpoint for our breakpoint management. 182 if (fWatchpointCount + 1 >= DEBUG_MAX_WATCHPOINTS) 183 return B_BUSY; 184 #else 185 if (fWatchpointCount >= DEBUG_MAX_WATCHPOINTS) 186 return B_BUSY; 187 #endif 188 189 watchpoint = new(std::nothrow) InstalledWatchpoint; 190 if (watchpoint == NULL) 191 return B_NO_MEMORY; 192 ObjectDeleter<InstalledWatchpoint> watchpointDeleter(watchpoint); 193 194 status_t error = _InstallWatchpoint(watchpoint, address, type, length); 195 if (error != B_OK) 196 return error; 197 198 fWatchpoints.Add(watchpointDeleter.Detach()); 199 fWatchpointCount++; 200 return B_OK; 201 } 202 203 204 status_t 205 BreakpointManager::UninstallWatchpoint(void* address) 206 { 207 WriteLocker locker(fLock); 208 209 InstalledWatchpoint* watchpoint = _FindWatchpoint((addr_t)address); 210 if (watchpoint == NULL) 211 return B_BAD_VALUE; 212 213 ObjectDeleter<InstalledWatchpoint> deleter(watchpoint); 214 fWatchpoints.Remove(watchpoint); 215 fWatchpointCount--; 216 217 return _UninstallWatchpoint(watchpoint); 218 } 219 220 221 void 222 BreakpointManager::RemoveAllBreakpoints() 223 { 224 WriteLocker locker(fLock); 225 226 // remove the breakpoints 227 BreakpointTree::Iterator it = fBreakpoints.GetIterator(); 228 while (InstalledBreakpoint* installedBreakpoint = it.Next()) { 229 it.Remove(); 230 231 // uninstall underlying hard/software breakpoint 232 if (installedBreakpoint->breakpoint->software) 233 _UninstallSoftwareBreakpoint(installedBreakpoint->breakpoint); 234 else 235 _UninstallHardwareBreakpoint(installedBreakpoint->breakpoint); 236 237 delete installedBreakpoint; 238 } 239 240 // remove the watchpoints 241 while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) { 242 _UninstallWatchpoint(watchpoint); 243 delete watchpoint; 244 } 245 } 246 247 248 /*! \brief Returns whether the given address can be accessed in principle. 249 No check whether there's an actually accessible area is performed, though. 250 */ 251 /*static*/ bool 252 BreakpointManager::CanAccessAddress(const void* _address, bool write) 253 { 254 const addr_t address = (addr_t)_address; 255 256 // user addresses are always fine 257 if (IS_USER_ADDRESS(address)) 258 return true; 259 260 // a commpage address can at least be read 261 if (address >= USER_COMMPAGE_ADDR 262 && address < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) { 263 return !write; 264 } 265 266 return false; 267 } 268 269 270 /*! \brief Reads data from user memory. 271 272 Tries to read \a size bytes of data from user memory address \a address 273 into the supplied buffer \a buffer. If only a part could be read the 274 function won't fail. The number of bytes actually read is return through 275 \a bytesRead. 276 277 \param address The user memory address from which to read. 278 \param buffer The buffer into which to write. 279 \param size The number of bytes to read. 280 \param bytesRead Will be set to the number of bytes actually read. 281 \return \c B_OK, if reading went fine. Then \a bytesRead will be set to 282 the amount of data actually read. An error indicates that nothing 283 has been read. 284 */ 285 status_t 286 BreakpointManager::ReadMemory(const void* _address, void* buffer, size_t size, 287 size_t& bytesRead) 288 { 289 const addr_t address = (addr_t)_address; 290 291 ReadLocker locker(fLock); 292 293 status_t error = _ReadMemory(address, buffer, size, bytesRead); 294 if (error != B_OK) 295 return error; 296 297 // If we have software breakpoints installed, fix the buffer not to contain 298 // any of them. 299 300 // address of the first possibly intersecting software breakpoint 301 const addr_t startAddress 302 = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1) 303 - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1); 304 305 BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true, 306 true); 307 while (InstalledBreakpoint* installed = it.Next()) { 308 Breakpoint* breakpoint = installed->breakpoint; 309 if (breakpoint->address >= address + size) 310 break; 311 312 if (breakpoint->software) { 313 // Software breakpoint intersects -- replace the read data with 314 // the data saved in the breakpoint object. 315 addr_t minAddress = std::max(breakpoint->address, address); 316 size_t toCopy = std::min(address + size, 317 breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE) 318 - minAddress; 319 memcpy((uint8*)buffer + (minAddress - address), 320 breakpoint->softwareData + (minAddress - breakpoint->address), 321 toCopy); 322 } 323 } 324 325 return B_OK; 326 } 327 328 329 status_t 330 BreakpointManager::WriteMemory(void* _address, const void* _buffer, size_t size, 331 size_t& bytesWritten) 332 { 333 bytesWritten = 0; 334 335 if (size == 0) 336 return B_OK; 337 338 addr_t address = (addr_t)_address; 339 const uint8* buffer = (uint8*)_buffer; 340 341 WriteLocker locker(fLock); 342 343 // We don't want to overwrite software breakpoints, so things are a bit more 344 // complicated. We iterate through the intersecting software breakpoints, 345 // writing the memory between them normally, but skipping the breakpoints 346 // itself. We write into their softwareData instead. 347 348 // Get the first breakpoint -- if it starts before the address, we'll 349 // handle it separately to make things in the main loop simpler. 350 const addr_t startAddress 351 = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1) 352 - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1); 353 354 BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true, 355 true); 356 InstalledBreakpoint* installed = it.Next(); 357 while (installed != NULL) { 358 Breakpoint* breakpoint = installed->breakpoint; 359 if (breakpoint->address >= address) 360 break; 361 362 if (breakpoint->software) { 363 // We've got a breakpoint that is partially intersecting with the 364 // beginning of the address range to write. 365 size_t toCopy = std::min(address + size, 366 breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE) 367 - address; 368 memcpy(breakpoint->softwareData + (address - breakpoint->address), 369 buffer, toCopy); 370 371 address += toCopy; 372 size -= toCopy; 373 bytesWritten += toCopy; 374 buffer += toCopy; 375 } 376 377 installed = it.Next(); 378 } 379 380 // loop through the breakpoints intersecting with the range 381 while (installed != NULL) { 382 Breakpoint* breakpoint = installed->breakpoint; 383 if (breakpoint->address >= address + size) 384 break; 385 386 if (breakpoint->software) { 387 // write the data up to the breakpoint (if any) 388 size_t toCopy = breakpoint->address - address; 389 if (toCopy > 0) { 390 size_t chunkWritten; 391 status_t error = _WriteMemory(address, buffer, toCopy, 392 chunkWritten); 393 if (error != B_OK) 394 return bytesWritten > 0 ? B_OK : error; 395 396 address += chunkWritten; 397 size -= chunkWritten; 398 bytesWritten += chunkWritten; 399 buffer += chunkWritten; 400 401 if (chunkWritten < toCopy) 402 return B_OK; 403 } 404 405 // write to the breakpoint data 406 toCopy = std::min(size, (size_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE); 407 memcpy(breakpoint->softwareData, buffer, toCopy); 408 409 address += toCopy; 410 size -= toCopy; 411 bytesWritten += toCopy; 412 buffer += toCopy; 413 } 414 415 installed = it.Next(); 416 } 417 418 // write remaining data 419 if (size > 0) { 420 size_t chunkWritten; 421 status_t error = _WriteMemory(address, buffer, size, chunkWritten); 422 if (error != B_OK) 423 return bytesWritten > 0 ? B_OK : error; 424 425 bytesWritten += chunkWritten; 426 } 427 428 return B_OK; 429 } 430 431 432 void 433 BreakpointManager::PrepareToContinue(void* _address) 434 { 435 const addr_t address = (addr_t)_address; 436 437 WriteLocker locker(fLock); 438 439 // Check whether there's a software breakpoint at the continuation address. 440 InstalledBreakpoint* installed = fBreakpoints.Lookup(address); 441 if (installed == NULL || !installed->breakpoint->software) 442 return; 443 444 // We need to replace the software breakpoint by a hardware one, or 445 // we can't continue the thread. 446 Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(true); 447 if (breakpoint == NULL) { 448 dprintf("Failed to allocate a hardware breakpoint.\n"); 449 return; 450 } 451 452 status_t error = _InstallHardwareBreakpoint(breakpoint, address); 453 if (error != B_OK) 454 return; 455 456 _UninstallSoftwareBreakpoint(installed->breakpoint); 457 458 breakpoint->installedBreakpoint = installed; 459 installed->breakpoint = breakpoint; 460 } 461 462 463 BreakpointManager::Breakpoint* 464 BreakpointManager::_GetUnusedHardwareBreakpoint(bool force) 465 { 466 // try to find a free one first 467 for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator(); 468 Breakpoint* breakpoint = it.Next();) { 469 if (!breakpoint->used) 470 return breakpoint; 471 } 472 473 if (!force) 474 return NULL; 475 476 // replace one by a software breakpoint 477 for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator(); 478 Breakpoint* breakpoint = it.Next();) { 479 if (breakpoint->installedBreakpoint == NULL) 480 continue; 481 482 status_t error = _InstallSoftwareBreakpoint( 483 breakpoint->installedBreakpoint, breakpoint->address); 484 if (error != B_OK) 485 continue; 486 487 if (_UninstallHardwareBreakpoint(breakpoint) == B_OK) 488 return breakpoint; 489 } 490 491 return NULL; 492 } 493 494 495 status_t 496 BreakpointManager::_InstallSoftwareBreakpoint(InstalledBreakpoint* installed, 497 addr_t address) 498 { 499 Breakpoint* breakpoint = new(std::nothrow) Breakpoint; 500 if (breakpoint == NULL) 501 return B_NO_MEMORY; 502 ObjectDeleter<Breakpoint> breakpointDeleter(breakpoint); 503 504 breakpoint->address = address; 505 breakpoint->installedBreakpoint = installed; 506 breakpoint->used = true; 507 breakpoint->software = true; 508 509 // save the memory where the software breakpoint shall be installed 510 size_t bytesTransferred; 511 status_t error = _ReadMemory(address, breakpoint->softwareData, 512 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred); 513 if (error != B_OK) 514 return error; 515 if (bytesTransferred != DEBUG_SOFTWARE_BREAKPOINT_SIZE) 516 return B_BAD_ADDRESS; 517 518 // write the breakpoint code 519 error = _WriteMemory(address, DEBUG_SOFTWARE_BREAKPOINT, 520 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred); 521 if (error != B_OK) 522 return error; 523 524 if (bytesTransferred < DEBUG_SOFTWARE_BREAKPOINT_SIZE) { 525 // breakpoint written partially only -- undo the written part 526 if (bytesTransferred > 0) { 527 size_t dummy; 528 _WriteMemory(address, breakpoint->softwareData, bytesTransferred, 529 dummy); 530 } 531 return B_BAD_ADDRESS; 532 } 533 534 installed->breakpoint = breakpoint; 535 breakpointDeleter.Detach(); 536 537 TRACE("installed software breakpoint at %#lx\n", address); 538 539 return B_OK; 540 } 541 542 543 status_t 544 BreakpointManager::_UninstallSoftwareBreakpoint(Breakpoint* breakpoint) 545 { 546 size_t bytesWritten; 547 _WriteMemory(breakpoint->address, breakpoint->softwareData, 548 DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesWritten); 549 550 TRACE("uninstalled software breakpoint at %#lx\n", breakpoint->address); 551 552 delete breakpoint; 553 return B_OK; 554 } 555 556 557 status_t 558 BreakpointManager::_InstallHardwareBreakpoint(Breakpoint* breakpoint, 559 addr_t address) 560 { 561 status_t error = arch_set_breakpoint((void*)address); 562 if (error != B_OK) 563 return error; 564 565 // move to the tail of the list 566 fHardwareBreakpoints.Remove(breakpoint); 567 fHardwareBreakpoints.Add(breakpoint); 568 569 TRACE("installed hardware breakpoint at %#lx\n", address); 570 571 breakpoint->address = address; 572 breakpoint->used = true; 573 return B_OK; 574 } 575 576 577 status_t 578 BreakpointManager::_UninstallHardwareBreakpoint(Breakpoint* breakpoint) 579 { 580 status_t error = arch_clear_breakpoint((void*)breakpoint->address); 581 if (error != B_OK) 582 return error; 583 584 TRACE("uninstalled hardware breakpoint at %#lx\n", breakpoint->address); 585 586 breakpoint->used = false; 587 breakpoint->installedBreakpoint = NULL; 588 return B_OK; 589 } 590 591 592 BreakpointManager::InstalledWatchpoint* 593 BreakpointManager::_FindWatchpoint(addr_t address) const 594 { 595 for (InstalledWatchpointList::ConstIterator it = fWatchpoints.GetIterator(); 596 InstalledWatchpoint* watchpoint = it.Next();) { 597 if (address == watchpoint->address) 598 return watchpoint; 599 } 600 601 return NULL; 602 } 603 604 605 status_t 606 BreakpointManager::_InstallWatchpoint(InstalledWatchpoint* watchpoint, 607 addr_t address, uint32 type, int32 length) 608 { 609 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS 610 // We need a hardware breakpoint. 611 watchpoint->breakpoint = _GetUnusedHardwareBreakpoint(true); 612 if (watchpoint->breakpoint == NULL) { 613 dprintf("Failed to allocate a hardware breakpoint for watchpoint.\n"); 614 return B_BUSY; 615 } 616 #endif 617 618 status_t error = arch_set_watchpoint((void*)address, type, length); 619 if (error != B_OK) 620 return error; 621 622 watchpoint->address = address; 623 624 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS 625 watchpoint->breakpoint->used = true; 626 #endif 627 628 return B_OK; 629 } 630 631 632 status_t 633 BreakpointManager::_UninstallWatchpoint(InstalledWatchpoint* watchpoint) 634 { 635 #if DEBUG_SHARED_BREAK_AND_WATCHPOINTS 636 watchpoint->breakpoint->used = false; 637 #endif 638 639 return arch_clear_watchpoint((void*)watchpoint->address); 640 } 641 642 643 status_t 644 BreakpointManager::_ReadMemory(const addr_t _address, void* _buffer, 645 size_t size, size_t& bytesRead) 646 { 647 const uint8* address = (const uint8*)_address; 648 uint8* buffer = (uint8*)_buffer; 649 650 // check the parameters 651 if (!CanAccessAddress(address, false)) 652 return B_BAD_ADDRESS; 653 if (size <= 0) 654 return B_BAD_VALUE; 655 656 // If the region to be read crosses page boundaries, we split it up into 657 // smaller chunks. 658 status_t error = B_OK; 659 bytesRead = 0; 660 while (size > 0) { 661 // check whether we're still in user address space 662 if (!CanAccessAddress(address, false)) { 663 error = B_BAD_ADDRESS; 664 break; 665 } 666 667 // don't cross page boundaries in a single read 668 int32 toRead = size; 669 int32 maxRead = B_PAGE_SIZE - (addr_t)address % B_PAGE_SIZE; 670 if (toRead > maxRead) 671 toRead = maxRead; 672 673 error = user_memcpy(buffer, address, toRead); 674 if (error != B_OK) 675 break; 676 677 bytesRead += toRead; 678 address += toRead; 679 buffer += toRead; 680 size -= toRead; 681 } 682 683 // If reading fails, we only fail, if we haven't read anything yet. 684 if (error != B_OK) { 685 if (bytesRead > 0) 686 return B_OK; 687 return error; 688 } 689 690 return B_OK; 691 } 692 693 694 status_t 695 BreakpointManager::_WriteMemory(addr_t _address, const void* _buffer, 696 size_t size, size_t& bytesWritten) 697 { 698 uint8* address = (uint8*)_address; 699 const uint8* buffer = (const uint8*)_buffer; 700 701 // check the parameters 702 if (!CanAccessAddress(address, true)) 703 return B_BAD_ADDRESS; 704 if (size <= 0) 705 return B_BAD_VALUE; 706 707 // If the region to be written crosses area boundaries, we split it up into 708 // smaller chunks. 709 status_t error = B_OK; 710 bytesWritten = 0; 711 while (size > 0) { 712 // check whether we're still in user address space 713 if (!CanAccessAddress(address, true)) { 714 error = B_BAD_ADDRESS; 715 break; 716 } 717 718 // get the area for the address (we need to use _user_area_for(), since 719 // we're looking for a user area) 720 area_id area = _user_area_for(address); 721 if (area < 0) { 722 TRACE("BreakpointManager::_WriteMemory(): area not found for " 723 "address: %p: %lx\n", address, area); 724 error = area; 725 break; 726 } 727 728 area_info areaInfo; 729 status_t error = get_area_info(area, &areaInfo); 730 if (error != B_OK) { 731 TRACE("BreakpointManager::_WriteMemory(): failed to get info for " 732 "area %ld: %lx\n", area, error); 733 error = B_BAD_ADDRESS; 734 break; 735 } 736 737 // restrict this round of writing to the found area 738 int32 toWrite = size; 739 int32 maxWrite = (uint8*)areaInfo.address + areaInfo.size - address; 740 if (toWrite > maxWrite) 741 toWrite = maxWrite; 742 743 // if the area is read-only, we temporarily need to make it writable 744 bool protectionChanged = false; 745 if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) { 746 error = set_area_protection(area, 747 areaInfo.protection | B_WRITE_AREA); 748 if (error != B_OK) { 749 TRACE("BreakpointManager::_WriteMemory(): failed to set new " 750 "protection for area %ld: %lx\n", area, error); 751 break; 752 } 753 protectionChanged = true; 754 } 755 756 // copy the memory 757 error = user_memcpy(address, buffer, toWrite); 758 759 // reset the area protection 760 if (protectionChanged) 761 set_area_protection(area, areaInfo.protection); 762 763 if (error != B_OK) { 764 TRACE("BreakpointManager::_WriteMemory(): user_memcpy() failed: " 765 "%lx\n", error); 766 break; 767 } 768 769 bytesWritten += toWrite; 770 address += toWrite; 771 buffer += toWrite; 772 size -= toWrite; 773 } 774 775 // If writing fails, we only fail, if we haven't written anything yet. 776 if (error != B_OK) { 777 if (bytesWritten > 0) 778 return B_OK; 779 return error; 780 } 781 782 return B_OK; 783 } 784