1/* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "checksum_device.h" 8 9#include <ctype.h> 10#include <errno.h> 11#include <fcntl.h> 12#include <stdio.h> 13#include <string.h> 14#include <unistd.h> 15 16#include <algorithm> 17 18#include <device_manager.h> 19 20#include <AutoDeleter.h> 21#include <util/AutoLock.h> 22#include <util/DoublyLinkedList.h> 23 24#include <fs/KPath.h> 25#include <lock.h> 26#include <vm/vm.h> 27 28#include "dma_resources.h" 29#include "io_requests.h" 30#include "IOSchedulerSimple.h" 31 32#include "CheckSum.h" 33 34 35//#define TRACE_CHECK_SUM_DEVICE 36#ifdef TRACE_CHECK_SUM_DEVICE 37# define TRACE(x...) dprintf(x) 38#else 39# define TRACE(x) do {} while (false) 40#endif 41 42 43// parameters for the DMA resource 44static const uint32 kDMAResourceBufferCount = 16; 45static const uint32 kDMAResourceBounceBufferCount = 16; 46 47static const size_t kCheckSumLength = sizeof(CheckSum); 48static const uint32 kCheckSumsPerBlock = B_PAGE_SIZE / sizeof(CheckSum); 49 50static const char* const kDriverModuleName 51 = "drivers/disk/virtual/checksum_device/driver_v1"; 52static const char* const kControlDeviceModuleName 53 = "drivers/disk/virtual/checksum_device/control/device_v1"; 54static const char* const kRawDeviceModuleName 55 = "drivers/disk/virtual/checksum_device/raw/device_v1"; 56 57static const char* const kControlDeviceName 58 = "disk/virtual/checksum_device/control"; 59static const char* const kRawDeviceBaseName = "disk/virtual/checksum_device"; 60 61static const char* const kFilePathItem = "checksum_device/file_path"; 62 63 64struct RawDevice; 65typedef DoublyLinkedList<RawDevice> RawDeviceList; 66 67struct device_manager_info* sDeviceManager; 68 69static RawDeviceList sDeviceList; 70static mutex sDeviceListLock = MUTEX_INITIALIZER("checksum device list"); 71 72 73struct CheckSumBlock : public DoublyLinkedListLinkImpl<CheckSumBlock> { 74 uint64 blockIndex; 75 bool used; 76 bool dirty; 77 CheckSum checkSums[kCheckSumsPerBlock]; 78 79 CheckSumBlock() 80 : 81 used(false) 82 { 83 } 84}; 85 86 87struct CheckSumCache { 88 CheckSumCache() 89 { 90 mutex_init(&fLock, "check sum cache"); 91 } 92 93 ~CheckSumCache() 94 { 95 while (CheckSumBlock* block = fBlocks.RemoveHead()) 96 delete block; 97 98 mutex_destroy(&fLock); 99 } 100 101 status_t Init(int fd, uint64 blockCount, uint32 cachedBlockCount) 102 { 103 fBlockCount = blockCount; 104 fFD = fd; 105 106 for (uint32 i = 0; i < cachedBlockCount; i++) { 107 CheckSumBlock* block = new(std::nothrow) CheckSumBlock; 108 if (block == NULL) 109 return B_NO_MEMORY; 110 111 fBlocks.Add(block); 112 } 113 114 return B_OK; 115 } 116 117 status_t GetCheckSum(uint64 blockIndex, CheckSum& checkSum) 118 { 119 ASSERT(blockIndex < fBlockCount); 120 121 MutexLocker locker(fLock); 122 123 CheckSumBlock* block; 124 status_t error = _GetBlock( 125 fBlockCount + blockIndex / kCheckSumsPerBlock, block); 126 if (error != B_OK) 127 return error; 128 129 checkSum = block->checkSums[blockIndex % kCheckSumsPerBlock]; 130 131 return B_OK; 132 } 133 134 status_t SetCheckSum(uint64 blockIndex, const CheckSum& checkSum) 135 { 136 ASSERT(blockIndex < fBlockCount); 137 138 MutexLocker locker(fLock); 139 140 CheckSumBlock* block; 141 status_t error = _GetBlock( 142 fBlockCount + blockIndex / kCheckSumsPerBlock, block); 143 if (error != B_OK) 144 return error; 145 146 block->checkSums[blockIndex % kCheckSumsPerBlock] = checkSum; 147 block->dirty = true; 148 149#ifdef TRACE_CHECK_SUM_DEVICE 150 TRACE("checksum_device: setting check sum of block %" B_PRIu64 " to: ", 151 blockIndex); 152 for (size_t i = 0; i < kCheckSumLength; i++) 153 TRACE("%02x", checkSum.Data()[i]); 154 TRACE("\n"); 155#endif 156 157 return B_OK; 158 } 159 160private: 161 typedef DoublyLinkedList<CheckSumBlock> BlockList; 162 163private: 164 status_t _GetBlock(uint64 blockIndex, CheckSumBlock*& _block) 165 { 166 // check whether we have already cached the block 167 for (BlockList::Iterator it = fBlocks.GetIterator(); 168 CheckSumBlock* block = it.Next();) { 169 if (block->used && blockIndex == block->blockIndex) { 170 // we know it -- requeue and return 171 it.Remove(); 172 fBlocks.Add(block); 173 _block = block; 174 return B_OK; 175 } 176 } 177 178 // flush the least recently used block and recycle it 179 CheckSumBlock* block = fBlocks.Head(); 180 status_t error = _FlushBlock(block); 181 if (error != B_OK) 182 return error; 183 184 error = _ReadBlock(block, blockIndex); 185 if (error != B_OK) 186 return error; 187 188 // requeue 189 fBlocks.Remove(block); 190 fBlocks.Add(block); 191 192 _block = block; 193 return B_OK; 194 } 195 196 status_t _FlushBlock(CheckSumBlock* block) 197 { 198 if (!block->used || !block->dirty) 199 return B_OK; 200 201 ssize_t written = pwrite(fFD, block->checkSums, B_PAGE_SIZE, 202 block->blockIndex * B_PAGE_SIZE); 203 if (written < 0) 204 return errno; 205 if (written != B_PAGE_SIZE) 206 return B_ERROR; 207 208 block->dirty = false; 209 return B_OK; 210 } 211 212 status_t _ReadBlock(CheckSumBlock* block, uint64 blockIndex) 213 { 214 // mark unused for the failure cases -- reset later 215 block->used = false; 216 217 ssize_t bytesRead = pread(fFD, block->checkSums, B_PAGE_SIZE, 218 blockIndex * B_PAGE_SIZE); 219 if (bytesRead < 0) 220 return errno; 221 if (bytesRead != B_PAGE_SIZE) 222 return B_ERROR; 223 224 block->blockIndex = blockIndex; 225 block->used = true; 226 block->dirty = false; 227 228 return B_OK; 229 } 230 231private: 232 mutex fLock; 233 uint64 fBlockCount; 234 int fFD; 235 BlockList fBlocks; // LRU first 236}; 237 238 239struct Device { 240 Device(device_node* node) 241 : 242 fNode(node) 243 { 244 mutex_init(&fLock, "checksum device"); 245 } 246 247 virtual ~Device() 248 { 249 mutex_destroy(&fLock); 250 } 251 252 bool Lock() { mutex_lock(&fLock); return true; } 253 void Unlock() { mutex_unlock(&fLock); } 254 255 device_node* Node() const { return fNode; } 256 257 virtual status_t PublishDevice() = 0; 258 259protected: 260 mutex fLock; 261 device_node* fNode; 262}; 263 264 265struct ControlDevice : Device { 266 ControlDevice(device_node* node) 267 : 268 Device(node) 269 { 270 } 271 272 status_t Register(const char* fileName) 273 { 274 device_attr attrs[] = { 275 {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, 276 {string: "Checksum Raw Device"}}, 277 {kFilePathItem, B_STRING_TYPE, {string: fileName}}, 278 {NULL} 279 }; 280 281 return sDeviceManager->register_node( 282 sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs, 283 NULL, NULL); 284 } 285 286 virtual status_t PublishDevice() 287 { 288 return sDeviceManager->publish_device(Node(), kControlDeviceName, 289 kControlDeviceModuleName); 290 } 291}; 292 293 294struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> { 295 RawDevice(device_node* node) 296 : 297 Device(node), 298 fIndex(-1), 299 fFD(-1), 300 fFileSize(0), 301 fDeviceSize(0), 302 fDeviceName(NULL), 303 fDMAResource(NULL), 304 fIOScheduler(NULL), 305 fTransferBuffer(NULL), 306 fCheckSumCache(NULL) 307 { 308 } 309 310 virtual ~RawDevice() 311 { 312 if (fIndex >= 0) { 313 MutexLocker locker(sDeviceListLock); 314 sDeviceList.Remove(this); 315 } 316 317 if (fFD >= 0) 318 close(fFD); 319 320 free(fDeviceName); 321 } 322 323 int32 Index() const { return fIndex; } 324 off_t DeviceSize() const { return fDeviceSize; } 325 const char* DeviceName() const { return fDeviceName; } 326 327 status_t Init(const char* fileName) 328 { 329 // open file/device 330 fFD = open(fileName, O_RDWR | O_NOCACHE); 331 // TODO: The O_NOCACHE is a work-around for a page writer problem. 332 // Since it collects pages for writing back without regard for 333 // which caches and file systems they belong to, a deadlock can 334 // result when pages from both the underlying file system and the 335 // one using the checksum device are collected in one run. 336 if (fFD < 0) 337 return errno; 338 339 // get the size 340 struct stat st; 341 if (fstat(fFD, &st) < 0) 342 return errno; 343 344 switch (st.st_mode & S_IFMT) { 345 case S_IFREG: 346 fFileSize = st.st_size; 347 break; 348 case S_IFCHR: 349 case S_IFBLK: 350 { 351 device_geometry geometry; 352 if (ioctl(fFD, B_GET_GEOMETRY, &geometry, sizeof(geometry)) < 0) 353 return errno; 354 355 fFileSize = (off_t)geometry.bytes_per_sector 356 * geometry.sectors_per_track 357 * geometry.cylinder_count * geometry.head_count; 358 break; 359 } 360 default: 361 return B_BAD_VALUE; 362 } 363 364 fFileSize = fFileSize / B_PAGE_SIZE * B_PAGE_SIZE; 365 fDeviceSize = fFileSize / (B_PAGE_SIZE + kCheckSumLength) * B_PAGE_SIZE; 366 367 // find a free slot 368 fIndex = 0; 369 RawDevice* nextDevice = NULL; 370 MutexLocker locker(sDeviceListLock); 371 for (RawDeviceList::Iterator it = sDeviceList.GetIterator(); 372 (nextDevice = it.Next()) != NULL;) { 373 if (nextDevice->Index() > fIndex) 374 break; 375 fIndex = nextDevice->Index() + 1; 376 } 377 378 sDeviceList.InsertBefore(nextDevice, this); 379 380 // construct our device path 381 KPath path(kRawDeviceBaseName); 382 char buffer[32]; 383 snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fIndex); 384 385 status_t error = path.Append(buffer); 386 if (error != B_OK) 387 return error; 388 389 fDeviceName = path.DetachBuffer(); 390 391 return B_OK; 392 } 393 394 status_t Prepare() 395 { 396 fCheckSumCache = new(std::nothrow) CheckSumCache; 397 if (fCheckSumCache == NULL) { 398 Unprepare(); 399 return B_NO_MEMORY; 400 } 401 402 status_t error = fCheckSumCache->Init(fFD, fDeviceSize / B_PAGE_SIZE, 403 16); 404 if (error != B_OK) { 405 Unprepare(); 406 return error; 407 } 408 409 // no DMA restrictions 410 const dma_restrictions restrictions = {}; 411 412 fDMAResource = new(std::nothrow) DMAResource; 413 if (fDMAResource == NULL) { 414 Unprepare(); 415 return B_NO_MEMORY; 416 } 417 418 error = fDMAResource->Init(restrictions, B_PAGE_SIZE, 419 kDMAResourceBufferCount, kDMAResourceBounceBufferCount); 420 if (error != B_OK) { 421 Unprepare(); 422 return error; 423 } 424 425 fIOScheduler = new(std::nothrow) IOSchedulerSimple(fDMAResource); 426 if (fIOScheduler == NULL) { 427 Unprepare(); 428 return B_NO_MEMORY; 429 } 430 431 error = fIOScheduler->Init("checksum device scheduler"); 432 if (error != B_OK) { 433 Unprepare(); 434 return error; 435 } 436 437 fIOScheduler->SetCallback(&_DoIOEntry, this); 438 439 fTransferBuffer = malloc(B_PAGE_SIZE); 440 if (fTransferBuffer == NULL) { 441 Unprepare(); 442 return B_NO_MEMORY; 443 } 444 445 return B_OK; 446 } 447 448 void Unprepare() 449 { 450 free(fTransferBuffer); 451 fTransferBuffer = NULL; 452 453 delete fIOScheduler; 454 fIOScheduler = NULL; 455 456 delete fDMAResource; 457 fDMAResource = NULL; 458 459 delete fCheckSumCache; 460 fCheckSumCache = NULL; 461 } 462 463 status_t DoIO(IORequest* request) 464 { 465 return fIOScheduler->ScheduleRequest(request); 466 } 467 468 virtual status_t PublishDevice() 469 { 470 return sDeviceManager->publish_device(Node(), fDeviceName, 471 kRawDeviceModuleName); 472 } 473 474 status_t GetBlockCheckSum(uint64 blockIndex, CheckSum& checkSum) 475 { 476 return fCheckSumCache->GetCheckSum(blockIndex, checkSum); 477 } 478 479 status_t SetBlockCheckSum(uint64 blockIndex, const CheckSum& checkSum) 480 { 481 return fCheckSumCache->SetCheckSum(blockIndex, checkSum); 482 } 483 484private: 485 static status_t _DoIOEntry(void* data, IOOperation* operation) 486 { 487 return ((RawDevice*)data)->_DoIO(operation); 488 } 489 490 status_t _DoIO(IOOperation* operation) 491 { 492 off_t offset = operation->Offset(); 493 generic_size_t length = operation->Length(); 494 495 ASSERT(offset % B_PAGE_SIZE == 0); 496 ASSERT(length % B_PAGE_SIZE == 0); 497 498 const generic_io_vec* vecs = operation->Vecs(); 499 generic_size_t vecOffset = 0; 500 bool isWrite = operation->IsWrite(); 501 502 while (length > 0) { 503 status_t error = _TransferBlock(vecs, vecOffset, offset, isWrite); 504 if (error != B_OK) { 505 fIOScheduler->OperationCompleted(operation, error, 0); 506 return error; 507 } 508 509 offset += B_PAGE_SIZE; 510 length -= B_PAGE_SIZE; 511 } 512 513 fIOScheduler->OperationCompleted(operation, B_OK, operation->Length()); 514 return B_OK; 515 } 516 517 status_t _TransferBlock(const generic_io_vec*& vecs, 518 generic_size_t& vecOffset, off_t offset, bool isWrite) 519 { 520 if (isWrite) { 521 // write -- copy data to transfer buffer 522 status_t error = _CopyData(vecs, vecOffset, true); 523 if (error != B_OK) 524 return error; 525 _CheckCheckSum(offset / B_PAGE_SIZE); 526 } 527 528 ssize_t transferred = isWrite 529 ? pwrite(fFD, fTransferBuffer, B_PAGE_SIZE, offset) 530 : pread(fFD, fTransferBuffer, B_PAGE_SIZE, offset); 531 532 if (transferred < 0) 533 return errno; 534 if (transferred != B_PAGE_SIZE) 535 return B_ERROR; 536 537 if (!isWrite) { 538 // read -- copy data from transfer buffer 539 status_t error =_CopyData(vecs, vecOffset, false); 540 if (error != B_OK) 541 return error; 542 } 543 544 return B_OK; 545 } 546 547 status_t _CopyData(const generic_io_vec*& vecs, generic_size_t& vecOffset, 548 bool toBuffer) 549 { 550 uint8* buffer = (uint8*)fTransferBuffer; 551 size_t length = B_PAGE_SIZE; 552 while (length > 0) { 553 size_t toCopy = std::min((generic_size_t)length, 554 vecs->length - vecOffset); 555 556 if (toCopy == 0) { 557 vecs++; 558 vecOffset = 0; 559 continue; 560 } 561 562 phys_addr_t vecAddress = vecs->base + vecOffset; 563 564 status_t error = toBuffer 565 ? vm_memcpy_from_physical(buffer, vecAddress, toCopy, false) 566 : vm_memcpy_to_physical(vecAddress, buffer, toCopy, false); 567 if (error != B_OK) 568 return error; 569 570 buffer += toCopy; 571 length -= toCopy; 572 vecOffset += toCopy; 573 } 574 575 return B_OK; 576 } 577 578 void _CheckCheckSum(uint64 blockIndex) 579 { 580 // get the checksum the block should have 581 CheckSum expectedCheckSum; 582 if (fCheckSumCache->GetCheckSum(blockIndex, expectedCheckSum) != B_OK) 583 return; 584 585 // if the checksum is clear, we aren't supposed to check 586 if (expectedCheckSum.IsZero()) { 587 dprintf("checksum_device: skipping check sum check for block %" 588 B_PRIu64 "\n", blockIndex); 589 return; 590 } 591 592 // compute the transfer buffer check sum 593 fSHA256.Init(); 594 fSHA256.Update(fTransferBuffer, B_PAGE_SIZE); 595 596 if (expectedCheckSum != fSHA256.Digest()) 597 panic("Check sum mismatch for block %" B_PRIu64 " (exptected at %p" 598 ", actual at %p)", blockIndex, &expectedCheckSum, 599 fSHA256.Digest()); 600 } 601 602private: 603 int32 fIndex; 604 int fFD; 605 off_t fFileSize; 606 off_t fDeviceSize; 607 char* fDeviceName; 608 DMAResource* fDMAResource; 609 IOScheduler* fIOScheduler; 610 void* fTransferBuffer; 611 CheckSumCache* fCheckSumCache; 612 SHA256 fSHA256; 613}; 614 615 616struct RawDeviceCookie { 617 RawDeviceCookie(RawDevice* device, int openMode) 618 : 619 fDevice(device), 620 fOpenMode(openMode) 621 { 622 } 623 624 RawDevice* Device() const { return fDevice; } 625 int OpenMode() const { return fOpenMode; } 626 627private: 628 RawDevice* fDevice; 629 int fOpenMode; 630}; 631 632 633// #pragma mark - 634 635 636static bool 637parse_command_line(char* buffer, char**& _argv, int& _argc) 638{ 639 // Process the argument string. We split at whitespace, heeding quotes and 640 // escaped characters. The processed arguments are written to the given 641 // buffer, separated by single null chars. 642 char* start = buffer; 643 char* out = buffer; 644 bool pendingArgument = false; 645 int argc = 0; 646 while (*start != '\0') { 647 // ignore whitespace 648 if (isspace(*start)) { 649 if (pendingArgument) { 650 *out = '\0'; 651 out++; 652 argc++; 653 pendingArgument = false; 654 } 655 start++; 656 continue; 657 } 658 659 pendingArgument = true; 660 661 if (*start == '"' || *start == '\'') { 662 // quoted text -- continue until closing quote 663 char quote = *start; 664 start++; 665 while (*start != '\0' && *start != quote) { 666 if (*start == '\\' && quote == '"') { 667 start++; 668 if (*start == '\0') 669 break; 670 } 671 *out = *start; 672 start++; 673 out++; 674 } 675 676 if (*start != '\0') 677 start++; 678 } else { 679 // unquoted text 680 if (*start == '\\') { 681 // escaped char 682 start++; 683 if (start == '\0') 684 break; 685 } 686 687 *out = *start; 688 start++; 689 out++; 690 } 691 } 692 693 if (pendingArgument) { 694 *out = '\0'; 695 argc++; 696 } 697 698 // allocate argument vector 699 char** argv = new(std::nothrow) char*[argc + 1]; 700 if (argv == NULL) 701 return false; 702 703 // fill vector 704 start = buffer; 705 for (int i = 0; i < argc; i++) { 706 argv[i] = start; 707 start += strlen(start) + 1; 708 } 709 argv[argc] = NULL; 710 711 _argv = argv; 712 _argc = argc; 713 return true; 714} 715 716 717// #pragma mark - driver 718 719 720static float 721checksum_driver_supports_device(device_node* parent) 722{ 723 const char* bus = NULL; 724 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) 725 == B_OK && !strcmp(bus, "generic")) 726 return 0.8; 727 728 return -1; 729} 730 731 732static status_t 733checksum_driver_register_device(device_node* parent) 734{ 735 device_attr attrs[] = { 736 {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, 737 {string: "Checksum Control Device"}}, 738 {NULL} 739 }; 740 741 return sDeviceManager->register_node(parent, kDriverModuleName, attrs, NULL, 742 NULL); 743} 744 745 746static status_t 747checksum_driver_init_driver(device_node* node, void** _driverCookie) 748{ 749 const char* fileName; 750 if (sDeviceManager->get_attr_string(node, kFilePathItem, &fileName, false) 751 == B_OK) { 752 RawDevice* device = new(std::nothrow) RawDevice(node); 753 if (device == NULL) 754 return B_NO_MEMORY; 755 756 status_t error = device->Init(fileName); 757 if (error != B_OK) { 758 delete device; 759 return error; 760 } 761 762 *_driverCookie = (Device*)device; 763 } else { 764 ControlDevice* device = new(std::nothrow) ControlDevice(node); 765 if (device == NULL) 766 return B_NO_MEMORY; 767 768 *_driverCookie = (Device*)device; 769 } 770 771 return B_OK; 772} 773 774 775static void 776checksum_driver_uninit_driver(void* driverCookie) 777{ 778 Device* device = (Device*)driverCookie; 779 delete device; 780} 781 782 783static status_t 784checksum_driver_register_child_devices(void* driverCookie) 785{ 786 Device* device = (Device*)driverCookie; 787 return device->PublishDevice(); 788} 789 790 791// #pragma mark - control device 792 793 794static status_t 795checksum_control_device_init_device(void* driverCookie, void** _deviceCookie) 796{ 797 *_deviceCookie = driverCookie; 798 return B_OK; 799} 800 801 802static void 803checksum_control_device_uninit_device(void* deviceCookie) 804{ 805} 806 807 808static status_t 809checksum_control_device_open(void* deviceCookie, const char* path, int openMode, 810 void** _cookie) 811{ 812 *_cookie = deviceCookie; 813 return B_OK; 814} 815 816 817static status_t 818checksum_control_device_close(void* cookie) 819{ 820 return B_OK; 821} 822 823 824static status_t 825checksum_control_device_free(void* cookie) 826{ 827 return B_OK; 828} 829 830 831static status_t 832checksum_control_device_read(void* cookie, off_t position, void* buffer, 833 size_t* _length) 834{ 835 *_length = 0; 836 return B_OK; 837} 838 839 840static status_t 841checksum_control_device_write(void* cookie, off_t position, const void* data, 842 size_t* _length) 843{ 844 ControlDevice* device = (ControlDevice*)cookie; 845 846 if (position != 0) 847 return B_BAD_VALUE; 848 849 // copy data to stack buffer 850 char* buffer = (char*)malloc(*_length + 1); 851 if (buffer == NULL) 852 return B_NO_MEMORY; 853 MemoryDeleter bufferDeleter(buffer); 854 855 if (IS_USER_ADDRESS(data)) { 856 if (user_memcpy(buffer, data, *_length) != B_OK) 857 return B_BAD_ADDRESS; 858 } else 859 memcpy(buffer, data, *_length); 860 861 buffer[*_length] = '\0'; 862 863 // parse arguments 864 char** argv; 865 int argc; 866 if (!parse_command_line(buffer, argv, argc)) 867 return B_NO_MEMORY; 868 ArrayDeleter<char*> argvDeleter(argv); 869 870 if (argc == 0) { 871 dprintf("\"help\" for usage!\n"); 872 return B_BAD_VALUE; 873 } 874 875 // execute command 876 if (strcmp(argv[0], "help") == 0) { 877 // help 878 dprintf("register <path>\n"); 879 dprintf(" Registers file <path> as a new checksum device.\n"); 880 dprintf("unregister <device>\n"); 881 dprintf(" Unregisters <device>.\n"); 882 } else if (strcmp(argv[0], "register") == 0) { 883 // register 884 if (argc != 2) { 885 dprintf("Usage: register <path>\n"); 886 return B_BAD_VALUE; 887 } 888 889 return device->Register(argv[1]); 890 } else if (strcmp(argv[0], "unregister") == 0) { 891 // unregister 892 if (argc != 2) { 893 dprintf("Usage: unregister <device>\n"); 894 return B_BAD_VALUE; 895 } 896 897 const char* deviceName = argv[1]; 898 if (strncmp(deviceName, "/dev/", 5) == 0) 899 deviceName += 5; 900 901 // find the device in the list and unregister it 902 MutexLocker locker(sDeviceListLock); 903 for (RawDeviceList::Iterator it = sDeviceList.GetIterator(); 904 RawDevice* device = it.Next();) { 905 if (strcmp(device->DeviceName(), deviceName) == 0) { 906 // TODO: Race condition: We should mark the device as going to 907 // be unregistered, so no one else can try the same after we 908 // unlock! 909 locker.Unlock(); 910// TODO: The following doesn't work! unpublish_device(), as per implementation 911// (partially commented out) and unregister_node() returns B_BUSY. 912 status_t error = sDeviceManager->unpublish_device( 913 device->Node(), device->DeviceName()); 914 if (error != B_OK) { 915 dprintf("Failed to unpublish device \"%s\": %s\n", 916 deviceName, strerror(error)); 917 return error; 918 } 919 920 error = sDeviceManager->unregister_node(device->Node()); 921 if (error != B_OK) { 922 dprintf("Failed to unregister node \"%s\": %s\n", 923 deviceName, strerror(error)); 924 return error; 925 } 926 927 return B_OK; 928 } 929 } 930 931 dprintf("Device \"%s\" not found!\n", deviceName); 932 return B_BAD_VALUE; 933 } else { 934 dprintf("Invalid command \"%s\"!\n", argv[0]); 935 return B_BAD_VALUE; 936 } 937 938 return B_OK; 939} 940 941 942static status_t 943checksum_control_device_control(void* cookie, uint32 op, void* buffer, 944 size_t length) 945{ 946 return B_BAD_VALUE; 947} 948 949 950// #pragma mark - raw device 951 952 953static status_t 954checksum_raw_device_init_device(void* driverCookie, void** _deviceCookie) 955{ 956 RawDevice* device = static_cast<RawDevice*>((Device*)driverCookie); 957 958 status_t error = device->Prepare(); 959 if (error != B_OK) 960 return error; 961 962 *_deviceCookie = device; 963 return B_OK; 964} 965 966 967static void 968checksum_raw_device_uninit_device(void* deviceCookie) 969{ 970 RawDevice* device = (RawDevice*)deviceCookie; 971 device->Unprepare(); 972} 973 974 975static status_t 976checksum_raw_device_open(void* deviceCookie, const char* path, int openMode, 977 void** _cookie) 978{ 979 RawDevice* device = (RawDevice*)deviceCookie; 980 981 RawDeviceCookie* cookie = new(std::nothrow) RawDeviceCookie(device, 982 openMode); 983 if (cookie == NULL) 984 return B_NO_MEMORY; 985 986 *_cookie = cookie; 987 return B_OK; 988} 989 990 991static status_t 992checksum_raw_device_close(void* cookie) 993{ 994 return B_OK; 995} 996 997 998static status_t 999checksum_raw_device_free(void* _cookie) 1000{ 1001 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; 1002 delete cookie; 1003 return B_OK; 1004} 1005 1006 1007static status_t 1008checksum_raw_device_read(void* _cookie, off_t pos, void* buffer, 1009 size_t* _length) 1010{ 1011 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; 1012 RawDevice* device = cookie->Device(); 1013 1014 size_t length = *_length; 1015 1016 if (pos >= device->DeviceSize()) 1017 return B_BAD_VALUE; 1018 if (pos + length > device->DeviceSize()) 1019 length = device->DeviceSize() - pos; 1020 1021 IORequest request; 1022 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); 1023 if (status != B_OK) 1024 return status; 1025 1026 status = device->DoIO(&request); 1027 if (status != B_OK) 1028 return status; 1029 1030 status = request.Wait(0, 0); 1031 if (status == B_OK) 1032 *_length = length; 1033 return status; 1034} 1035 1036 1037static status_t 1038checksum_raw_device_write(void* _cookie, off_t pos, const void* buffer, 1039 size_t* _length) 1040{ 1041 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; 1042 RawDevice* device = cookie->Device(); 1043 1044 size_t length = *_length; 1045 1046 if (pos >= device->DeviceSize()) 1047 return B_BAD_VALUE; 1048 if (pos + length > device->DeviceSize()) 1049 length = device->DeviceSize() - pos; 1050 1051 IORequest request; 1052 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); 1053 if (status != B_OK) 1054 return status; 1055 1056 status = device->DoIO(&request); 1057 if (status != B_OK) 1058 return status; 1059 1060 status = request.Wait(0, 0); 1061 if (status == B_OK) 1062 *_length = length; 1063 1064 return status; 1065} 1066 1067 1068static status_t 1069checksum_raw_device_io(void* _cookie, io_request* request) 1070{ 1071 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; 1072 RawDevice* device = cookie->Device(); 1073 1074 return device->DoIO(request); 1075} 1076 1077 1078static status_t 1079checksum_raw_device_control(void* _cookie, uint32 op, void* buffer, 1080 size_t length) 1081{ 1082 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; 1083 RawDevice* device = cookie->Device(); 1084 1085 switch (op) { 1086 case B_GET_DEVICE_SIZE: 1087 { 1088 size_t size = device->DeviceSize(); 1089 return user_memcpy(buffer, &size, sizeof(size_t)); 1090 } 1091 1092 case B_SET_NONBLOCKING_IO: 1093 case B_SET_BLOCKING_IO: 1094 return B_OK; 1095 1096 case B_GET_READ_STATUS: 1097 case B_GET_WRITE_STATUS: 1098 { 1099 bool value = true; 1100 return user_memcpy(buffer, &value, sizeof(bool)); 1101 } 1102 1103 case B_GET_GEOMETRY: 1104 case B_GET_BIOS_GEOMETRY: 1105 { 1106 device_geometry geometry; 1107 geometry.bytes_per_sector = B_PAGE_SIZE; 1108 geometry.sectors_per_track = 1; 1109 geometry.cylinder_count = device->DeviceSize() / B_PAGE_SIZE; 1110 // TODO: We're limited to 2^32 * B_PAGE_SIZE, if we don't use 1111 // sectors_per_track and head_count. 1112 geometry.head_count = 1; 1113 geometry.device_type = B_DISK; 1114 geometry.removable = true; 1115 geometry.read_only = false; 1116 geometry.write_once = false; 1117 1118 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 1119 } 1120 1121 case B_GET_MEDIA_STATUS: 1122 { 1123 status_t status = B_OK; 1124 return user_memcpy(buffer, &status, sizeof(status_t)); 1125 } 1126 1127 case B_SET_UNINTERRUPTABLE_IO: 1128 case B_SET_INTERRUPTABLE_IO: 1129 case B_FLUSH_DRIVE_CACHE: 1130 return B_OK; 1131 1132 case CHECKSUM_DEVICE_IOCTL_GET_CHECK_SUM: 1133 { 1134 if (IS_USER_ADDRESS(buffer)) { 1135 checksum_device_ioctl_check_sum getCheckSum; 1136 if (user_memcpy(&getCheckSum, buffer, sizeof(getCheckSum)) 1137 != B_OK) { 1138 return B_BAD_ADDRESS; 1139 } 1140 1141 status_t error = device->GetBlockCheckSum( 1142 getCheckSum.blockIndex, getCheckSum.checkSum); 1143 if (error != B_OK) 1144 return error; 1145 1146 return user_memcpy(buffer, &getCheckSum, sizeof(getCheckSum)); 1147 } 1148 1149 checksum_device_ioctl_check_sum* getCheckSum 1150 = (checksum_device_ioctl_check_sum*)buffer; 1151 return device->GetBlockCheckSum(getCheckSum->blockIndex, 1152 getCheckSum->checkSum); 1153 } 1154 1155 case CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM: 1156 { 1157 if (IS_USER_ADDRESS(buffer)) { 1158 checksum_device_ioctl_check_sum setCheckSum; 1159 if (user_memcpy(&setCheckSum, buffer, sizeof(setCheckSum)) 1160 != B_OK) { 1161 return B_BAD_ADDRESS; 1162 } 1163 1164 return device->SetBlockCheckSum(setCheckSum.blockIndex, 1165 setCheckSum.checkSum); 1166 } 1167 1168 checksum_device_ioctl_check_sum* setCheckSum 1169 = (checksum_device_ioctl_check_sum*)buffer; 1170 return device->SetBlockCheckSum(setCheckSum->blockIndex, 1171 setCheckSum->checkSum); 1172 } 1173 } 1174 return B_BAD_VALUE; 1175} 1176 1177 1178// #pragma mark - 1179 1180 1181module_dependency module_dependencies[] = { 1182 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, 1183 {} 1184}; 1185 1186 1187static const struct driver_module_info sChecksumDeviceDriverModule = { 1188 { 1189 kDriverModuleName, 1190 0, 1191 NULL 1192 }, 1193 1194 checksum_driver_supports_device, 1195 checksum_driver_register_device, 1196 checksum_driver_init_driver, 1197 checksum_driver_uninit_driver, 1198 checksum_driver_register_child_devices 1199}; 1200 1201static const struct device_module_info sChecksumControlDeviceModule = { 1202 { 1203 kControlDeviceModuleName, 1204 0, 1205 NULL 1206 }, 1207 1208 checksum_control_device_init_device, 1209 checksum_control_device_uninit_device, 1210 NULL, 1211 1212 checksum_control_device_open, 1213 checksum_control_device_close, 1214 checksum_control_device_free, 1215 1216 checksum_control_device_read, 1217 checksum_control_device_write, 1218 NULL, // io 1219 1220 checksum_control_device_control, 1221 1222 NULL, // select 1223 NULL // deselect 1224}; 1225 1226static const struct device_module_info sChecksumRawDeviceModule = { 1227 { 1228 kRawDeviceModuleName, 1229 0, 1230 NULL 1231 }, 1232 1233 checksum_raw_device_init_device, 1234 checksum_raw_device_uninit_device, 1235 NULL, 1236 1237 checksum_raw_device_open, 1238 checksum_raw_device_close, 1239 checksum_raw_device_free, 1240 1241 checksum_raw_device_read, 1242 checksum_raw_device_write, 1243 checksum_raw_device_io, 1244 1245 checksum_raw_device_control, 1246 1247 NULL, // select 1248 NULL // deselect 1249}; 1250 1251const module_info* modules[] = { 1252 (module_info*)&sChecksumDeviceDriverModule, 1253 (module_info*)&sChecksumControlDeviceModule, 1254 (module_info*)&sChecksumRawDeviceModule, 1255 NULL 1256}; 1257