diff --git a/src/tests/system/kernel/Jamfile b/src/tests/system/kernel/Jamfile index 545e4c38f7..ce1ef96add 100644 --- a/src/tests/system/kernel/Jamfile +++ b/src/tests/system/kernel/Jamfile @@ -89,6 +89,7 @@ SimpleTest sigsuspend_test : sigsuspend_test.cpp ; SubInclude HAIKU_TOP src tests system kernel cache ; #SubInclude HAIKU_TOP src tests system kernel disk_device_manager ; SubInclude HAIKU_TOP src tests system kernel device_manager ; +SubInclude HAIKU_TOP src tests system kernel file_corruption ; SubInclude HAIKU_TOP src tests system kernel scheduler ; SubInclude HAIKU_TOP src tests system kernel slab ; SubInclude HAIKU_TOP src tests system kernel swap ; diff --git a/src/tests/system/kernel/file_corruption/CheckSum.h b/src/tests/system/kernel/file_corruption/CheckSum.h new file mode 100644 index 0000000000..7c1000d3e0 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/CheckSum.h @@ -0,0 +1,47 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHECK_SUM_H +#define CHECK_SUM_H + + +#include + +#include + + +struct CheckSum { + + bool IsZero() const + { + for (size_t i = 0; i < sizeof(fData); i++) { + if (fData[i] != 0) + return false; + } + + return true; + } + + CheckSum& operator=(const CheckSum& other) + { + memcpy(fData, other.fData, sizeof(fData)); + return *this; + } + + bool operator==(const void* buffer) const + { + return memcmp(fData, buffer, sizeof(fData)) == 0; + } + + bool operator!=(const void* buffer) const + { + return !(*this == buffer); + } + +private: + uint8 fData[SHA_DIGEST_LENGTH]; +}; + + +#endif // CHECK_SUM_H diff --git a/src/tests/system/kernel/file_corruption/Jamfile b/src/tests/system/kernel/file_corruption/Jamfile new file mode 100644 index 0000000000..3ff5c2016a --- /dev/null +++ b/src/tests/system/kernel/file_corruption/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src tests system kernel file_corruption ; + + +SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; +UsePrivateKernelHeaders ; + + +KernelAddon checksum_device : + checksum_device.cpp + + # from src/kits/shared + SHA256.cpp +; + +SEARCH on [ FGristFiles SHA256.cpp ] + = [ FDirName $(HAIKU_TOP) src kits shared ] ; diff --git a/src/tests/system/kernel/file_corruption/checksum_device.cpp b/src/tests/system/kernel/file_corruption/checksum_device.cpp new file mode 100644 index 0000000000..2dda2d3285 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/checksum_device.cpp @@ -0,0 +1,1184 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "checksum_device.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "dma_resources.h" +#include "io_requests.h" +#include "IOSchedulerSimple.h" + +#include "CheckSum.h" + + +// parameters for the DMA resource +static const uint32 kDMAResourceBufferCount = 16; +static const uint32 kDMAResourceBounceBufferCount = 16; + +static const size_t kCheckSumLength = sizeof(CheckSum); +static const uint32 kCheckSumsPerBlock = B_PAGE_SIZE / sizeof(CheckSum); + +static const char* const kDriverModuleName + = "drivers/disk/virtual/checksum_device/driver_v1"; +static const char* const kControlDeviceModuleName + = "drivers/disk/virtual/checksum_device/control/device_v1"; +static const char* const kRawDeviceModuleName + = "drivers/disk/virtual/checksum_device/raw/device_v1"; + +static const char* const kControlDeviceName + = "disk/virtual/checksum_device/control"; +static const char* const kRawDeviceBaseName = "disk/virtual/checksum_device"; + +static const char* const kFilePathItem = "checksum_device/file_path"; + + +struct RawDevice; +typedef DoublyLinkedList RawDeviceList; + +struct device_manager_info* sDeviceManager; + +static RawDeviceList sDeviceList; +static mutex sDeviceListLock = MUTEX_INITIALIZER("checksum device list"); + + +struct CheckSumBlock : public DoublyLinkedListLinkImpl { + uint64 blockIndex; + bool used; + bool dirty; + CheckSum checkSums[kCheckSumsPerBlock]; + + CheckSumBlock() + : + used(false) + { + } +}; + + +struct CheckSumCache { + CheckSumCache() + { + mutex_init(&fLock, "check sum cache"); + } + + ~CheckSumCache() + { + while (CheckSumBlock* block = fBlocks.RemoveHead()) + delete block; + + mutex_destroy(&fLock); + } + + status_t Init(int fd, uint64 blockCount, uint32 cachedBlockCount) + { + fBlockCount = blockCount; + fFD = fd; + + for (uint32 i = 0; i < cachedBlockCount; i++) { + CheckSumBlock* block = new(std::nothrow) CheckSumBlock; + if (block == NULL) + return B_NO_MEMORY; + + fBlocks.Add(block); + } + + return B_OK; + } + + status_t GetCheckSum(uint64 blockIndex, CheckSum& checkSum) + { + ASSERT(blockIndex < fBlockCount); + + MutexLocker locker(fLock); + + CheckSumBlock* block; + status_t error = _GetBlock( + fBlockCount + blockIndex / kCheckSumsPerBlock, block); + if (error != B_OK) + return error; + + checkSum = block->checkSums[blockIndex % kCheckSumsPerBlock]; + + return B_OK; + } + + status_t SetCheckSum(uint64 blockIndex, const CheckSum& checkSum) + { + ASSERT(blockIndex < fBlockCount); + + MutexLocker locker(fLock); + + CheckSumBlock* block; + status_t error = _GetBlock( + fBlockCount + blockIndex / kCheckSumsPerBlock, block); + if (error != B_OK) + return error; + + block->checkSums[blockIndex % kCheckSumsPerBlock] = checkSum; + block->dirty = true; + + return B_OK; + } + +private: + typedef DoublyLinkedList BlockList; + +private: + status_t _GetBlock(uint64 blockIndex, CheckSumBlock*& _block) + { + // check whether we have already cached the block + for (BlockList::Iterator it = fBlocks.GetIterator(); + CheckSumBlock* block = it.Next();) { + if (block->used && block->blockIndex) { + // we know it -- requeue and return + it.Remove(); + fBlocks.Add(block); + _block = block; + return B_OK; + } + } + + // flush the least recently used block and recycle it + CheckSumBlock* block = fBlocks.Head(); + status_t error = _FlushBlock(block); + if (error != B_OK) + return error; + + error = _ReadBlock(block, blockIndex); + if (error != B_OK) + return error; + + // requeue + fBlocks.Remove(block); + fBlocks.Add(block); + + _block = block; + return B_OK; + } + + status_t _FlushBlock(CheckSumBlock* block) + { + if (!block->used || !block->dirty) + return B_OK; + + ssize_t written = pwrite(fFD, block->checkSums, B_PAGE_SIZE, + block->blockIndex * B_PAGE_SIZE); + if (written < 0) + return errno; + if (written != B_PAGE_SIZE) + return B_ERROR; + + block->dirty = false; + return B_OK; + } + + status_t _ReadBlock(CheckSumBlock* block, uint64 blockIndex) + { + // mark unused for the failure cases -- reset later + block->used = false; + + ssize_t bytesRead = pread(fFD, block->checkSums, B_PAGE_SIZE, + blockIndex * B_PAGE_SIZE); + if (bytesRead < 0) + return errno; + if (bytesRead != B_PAGE_SIZE) + return B_ERROR; + + block->blockIndex = blockIndex; + block->used = true; + block->dirty = false; + + return B_OK; + } + +private: + mutex fLock; + uint64 fBlockCount; + int fFD; + BlockList fBlocks; // LRU first +}; + + +struct Device { + Device(device_node* node) + : + fNode(node) + { + mutex_init(&fLock, "checksum device"); + } + + virtual ~Device() + { + mutex_destroy(&fLock); + } + + bool Lock() { mutex_lock(&fLock); return true; } + void Unlock() { mutex_unlock(&fLock); } + + device_node* Node() const { return fNode; } + + virtual status_t PublishDevice() = 0; + +protected: + mutex fLock; + device_node* fNode; +}; + + +struct ControlDevice : Device { + ControlDevice(device_node* node) + : + Device(node) + { + } + + status_t Register(const char* fileName) + { + device_attr attrs[] = { + {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + {string: "Checksum Raw Device"}}, + {kFilePathItem, B_STRING_TYPE, {string: fileName}}, + {NULL} + }; + + return sDeviceManager->register_node( + sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs, + NULL, NULL); + } + + virtual status_t PublishDevice() + { + return sDeviceManager->publish_device(Node(), kControlDeviceName, + kControlDeviceModuleName); + } +}; + + +struct RawDevice : Device, DoublyLinkedListLinkImpl { + RawDevice(device_node* node) + : + Device(node), + fIndex(-1), + fFD(-1), + fFileSize(0), + fDeviceSize(0), + fDeviceName(NULL), + fDMAResource(NULL), + fIOScheduler(NULL), + fTransferBuffer(NULL), + fCheckSumCache(NULL) + { + } + + virtual ~RawDevice() + { + if (fIndex >= 0) { + MutexLocker locker(sDeviceListLock); + sDeviceList.Remove(this); + } + + if (fFD >= 0) + close(fFD); + + free(fDeviceName); + } + + int32 Index() const { return fIndex; } + off_t DeviceSize() const { return fDeviceSize; } + const char* DeviceName() const { return fDeviceName; } + + status_t Init(const char* fileName) + { + // open and stat file + fFD = open(fileName, O_RDWR); + if (fFD < 0) + return errno; + + struct stat st; + if (fstat(fFD, &st) < 0) + return errno; + + fFileSize = st.st_size / B_PAGE_SIZE * B_PAGE_SIZE; + fDeviceSize = fFileSize / (B_PAGE_SIZE + kCheckSumLength) * B_PAGE_SIZE; + + // find a free slot + fIndex = 0; + RawDevice* nextDevice = NULL; + MutexLocker locker(sDeviceListLock); + for (RawDeviceList::Iterator it = sDeviceList.GetIterator(); + (nextDevice = it.Next()) != NULL;) { + if (nextDevice->Index() > fIndex) + break; + fIndex = nextDevice->Index() + 1; + } + + sDeviceList.InsertBefore(nextDevice, this); + + // construct our device path + KPath path(kRawDeviceBaseName); + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fIndex); + + status_t error = path.Append(buffer); + if (error != B_OK) + return error; + + fDeviceName = path.DetachBuffer(); + + return B_OK; + } + + status_t Prepare() + { + fCheckSumCache = new(std::nothrow) CheckSumCache; + if (fCheckSumCache == NULL) { + Unprepare(); + return B_NO_MEMORY; + } + + status_t error = fCheckSumCache->Init(fFD, fDeviceSize / B_PAGE_SIZE, + 16); + if (error != B_OK) { + Unprepare(); + return error; + } + + // no DMA restrictions + const dma_restrictions restrictions = {}; + + fDMAResource = new(std::nothrow) DMAResource; + if (fDMAResource == NULL) { + Unprepare(); + return B_NO_MEMORY; + } + + error = fDMAResource->Init(restrictions, B_PAGE_SIZE, + kDMAResourceBufferCount, kDMAResourceBounceBufferCount); + if (error != B_OK) { + Unprepare(); + return error; + } + + fIOScheduler = new(std::nothrow) IOSchedulerSimple(fDMAResource); + if (fIOScheduler == NULL) { + Unprepare(); + return B_NO_MEMORY; + } + + error = fIOScheduler->Init("checksum device scheduler"); + if (error != B_OK) { + Unprepare(); + return error; + } + + fIOScheduler->SetCallback(&_DoIOEntry, this); + + fTransferBuffer = malloc(B_PAGE_SIZE); + if (fTransferBuffer == NULL) { + Unprepare(); + return B_NO_MEMORY; + } + + return B_OK; + } + + void Unprepare() + { + free(fTransferBuffer); + fTransferBuffer = NULL; + + delete fIOScheduler; + fIOScheduler = NULL; + + delete fDMAResource; + fDMAResource = NULL; + + delete fCheckSumCache; + fCheckSumCache = NULL; + } + + status_t DoIO(IORequest* request) + { + return fIOScheduler->ScheduleRequest(request); + } + + virtual status_t PublishDevice() + { + return sDeviceManager->publish_device(Node(), fDeviceName, + kRawDeviceModuleName); + } + + status_t SetBlockCheckSum(uint64 blockIndex, const CheckSum& checkSum) + { + return fCheckSumCache->SetCheckSum(blockIndex, checkSum); + } + +private: + static status_t _DoIOEntry(void* data, IOOperation* operation) + { + return ((RawDevice*)data)->_DoIO(operation); + } + + status_t _DoIO(IOOperation* operation) + { + off_t offset = operation->Offset(); + generic_size_t length = operation->Length(); + + ASSERT(offset % B_PAGE_SIZE == 0); + ASSERT(length % B_PAGE_SIZE == 0); + + const generic_io_vec* vecs = operation->Vecs(); + generic_size_t vecOffset = 0; + bool isWrite = operation->IsWrite(); + + while (length > 0) { + status_t error = _TransferBlock(vecs, vecOffset, offset, isWrite); + if (error != B_OK) { + fIOScheduler->OperationCompleted(operation, error, 0); + return error; + } + + offset += B_PAGE_SIZE; + length -= B_PAGE_SIZE; + } + + fIOScheduler->OperationCompleted(operation, B_OK, operation->Length()); + return B_OK; + } + + status_t _TransferBlock(const generic_io_vec*& vecs, + generic_size_t& vecOffset, off_t offset, bool isWrite) + { + if (isWrite) { + // write -- copy data to transfer buffer + status_t error = _CopyData(vecs, vecOffset, true); + if (error != B_OK) + return error; + _CheckCheckSum(offset / B_PAGE_SIZE); + } + + ssize_t transferred = isWrite + ? pwrite(fFD, fTransferBuffer, B_PAGE_SIZE, offset) + : pread(fFD, fTransferBuffer, B_PAGE_SIZE, offset); + + if (transferred < 0) + return errno; + if (transferred != B_PAGE_SIZE) + return B_ERROR; + + if (!isWrite) { + // read -- copy data from transfer buffer + status_t error =_CopyData(vecs, vecOffset, false); + if (error != B_OK) + return error; + } + + return B_OK; + } + + status_t _CopyData(const generic_io_vec*& vecs, generic_size_t& vecOffset, + bool toBuffer) + { + uint8* buffer = (uint8*)fTransferBuffer; + size_t length = B_PAGE_SIZE; + while (length > 0) { + size_t toCopy = std::min((generic_size_t)length, + vecs->length - vecOffset); + + if (toCopy == 0) { + vecs++; + vecOffset = 0; + continue; + } + + phys_addr_t vecAddress = vecs->base + vecOffset; + + status_t error = toBuffer + ? vm_memcpy_from_physical(buffer, vecAddress, toCopy, false) + : vm_memcpy_to_physical(vecAddress, buffer, toCopy, false); + if (error != B_OK) + return error; + + buffer += toCopy; + length -= toCopy; + vecOffset += toCopy; + } + + return B_OK; + } + + void _CheckCheckSum(uint64 blockIndex) + { + // get the checksum the block should have + CheckSum expectedCheckSum; + if (fCheckSumCache->GetCheckSum(blockIndex, expectedCheckSum) != B_OK) + return; + + // if the checksum is clear, we aren't supposed to check + if (expectedCheckSum.IsZero()) { + dprintf("checksum_device: skipping check sum check for block %" + B_PRIu64 "\n", blockIndex); + return; + } + + // compute the transfer buffer check sum + fSHA256.Init(); + fSHA256.Update(fTransferBuffer, B_PAGE_SIZE); + + if (expectedCheckSum != fSHA256.Digest()) + panic("Check sum mismatch for block %" B_PRIu64, blockIndex); + } + +private: + int32 fIndex; + int fFD; + off_t fFileSize; + off_t fDeviceSize; + char* fDeviceName; + DMAResource* fDMAResource; + IOScheduler* fIOScheduler; + void* fTransferBuffer; + CheckSumCache* fCheckSumCache; + SHA256 fSHA256; +}; + + +struct RawDeviceCookie { + RawDeviceCookie(RawDevice* device, int openMode) + : + fDevice(device), + fOpenMode(openMode) + { + } + + RawDevice* Device() const { return fDevice; } + int OpenMode() const { return fOpenMode; } + +private: + RawDevice* fDevice; + int fOpenMode; +}; + + +// #pragma mark - + + +static bool +parse_command_line(char* buffer, char**& _argv, int& _argc) +{ + // Process the argument string. We split at whitespace, heeding quotes and + // escaped characters. The processed arguments are written to the given + // buffer, separated by single null chars. + char* start = buffer; + char* out = buffer; + bool pendingArgument = false; + int argc = 0; + while (*start != '\0') { + // ignore whitespace + if (isspace(*start)) { + if (pendingArgument) { + *out = '\0'; + out++; + argc++; + pendingArgument = false; + } + start++; + continue; + } + + pendingArgument = true; + + if (*start == '"' || *start == '\'') { + // quoted text -- continue until closing quote + char quote = *start; + start++; + while (*start != '\0' && *start != quote) { + if (*start == '\\' && quote == '"') { + start++; + if (*start == '\0') + break; + } + *out = *start; + start++; + out++; + } + + if (*start != '\0') + start++; + } else { + // unquoted text + if (*start == '\\') { + // escaped char + start++; + if (start == '\0') + break; + } + + *out = *start; + start++; + out++; + } + } + + if (pendingArgument) { + *out = '\0'; + argc++; + } + + // allocate argument vector + char** argv = new(std::nothrow) char*[argc + 1]; + if (argv == NULL) + return false; + + // fill vector + start = buffer; + for (int i = 0; i < argc; i++) { + argv[i] = start; + start += strlen(start) + 1; + } + argv[argc] = NULL; + + _argv = argv; + _argc = argc; + return true; +} + + +// #pragma mark - driver + + +static float +checksum_driver_supports_device(device_node* parent) +{ + const char* bus = NULL; + if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) + == B_OK && !strcmp(bus, "generic")) + return 0.8; + + return -1; +} + + +static status_t +checksum_driver_register_device(device_node* parent) +{ + device_attr attrs[] = { + {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + {string: "Checksum Control Device"}}, + {NULL} + }; + + return sDeviceManager->register_node(parent, kDriverModuleName, attrs, NULL, + NULL); +} + + +static status_t +checksum_driver_init_driver(device_node* node, void** _driverCookie) +{ + const char* fileName; + if (sDeviceManager->get_attr_string(node, kFilePathItem, &fileName, false) + == B_OK) { + RawDevice* device = new(std::nothrow) RawDevice(node); + if (device == NULL) + return B_NO_MEMORY; + + status_t error = device->Init(fileName); + if (error != B_OK) { + delete device; + return error; + } + + *_driverCookie = (Device*)device; + } else { + ControlDevice* device = new(std::nothrow) ControlDevice(node); + if (device == NULL) + return B_NO_MEMORY; + + *_driverCookie = (Device*)device; + } + + return B_OK; +} + + +static void +checksum_driver_uninit_driver(void* driverCookie) +{ + Device* device = (Device*)driverCookie; + delete device; +} + + +static status_t +checksum_driver_register_child_devices(void* driverCookie) +{ + Device* device = (Device*)driverCookie; + return device->PublishDevice(); +} + + +// #pragma mark - control device + + +static status_t +checksum_control_device_init_device(void* driverCookie, void** _deviceCookie) +{ + *_deviceCookie = driverCookie; + return B_OK; +} + + +static void +checksum_control_device_uninit_device(void* deviceCookie) +{ +} + + +static status_t +checksum_control_device_open(void* deviceCookie, const char* path, int openMode, + void** _cookie) +{ + *_cookie = deviceCookie; + return B_OK; +} + + +static status_t +checksum_control_device_close(void* cookie) +{ + return B_OK; +} + + +static status_t +checksum_control_device_free(void* cookie) +{ + return B_OK; +} + + +static status_t +checksum_control_device_read(void* cookie, off_t position, void* buffer, + size_t* _length) +{ + *_length = 0; + return B_OK; +} + + +static status_t +checksum_control_device_write(void* cookie, off_t position, const void* data, + size_t* _length) +{ + ControlDevice* device = (ControlDevice*)cookie; + + if (position != 0) + return B_BAD_VALUE; + + // copy data to stack buffer + char* buffer = (char*)malloc(*_length + 1); + if (buffer == NULL) + return B_NO_MEMORY; + MemoryDeleter bufferDeleter(buffer); + + if (IS_USER_ADDRESS(data)) { + if (user_memcpy(buffer, data, *_length) != B_OK) + return B_BAD_ADDRESS; + } else + memcpy(buffer, data, *_length); + + buffer[*_length] = '\0'; + + // parse arguments + char** argv; + int argc; + if (!parse_command_line(buffer, argv, argc)) + return B_NO_MEMORY; + ArrayDeleter argvDeleter(argv); + + if (argc == 0) { + dprintf("\"help\" for usage!\n"); + return B_BAD_VALUE; + } + + // execute command + if (strcmp(argv[0], "help") == 0) { + // help + dprintf("register \n"); + dprintf(" Registers file as a new checksum device.\n"); + dprintf("unregister \n"); + dprintf(" Unregisters .\n"); + } else if (strcmp(argv[0], "register") == 0) { + // register + if (argc != 2) { + dprintf("Usage: register \n"); + return B_BAD_VALUE; + } + + return device->Register(argv[1]); + } else if (strcmp(argv[0], "unregister") == 0) { + // unregister + if (argc != 2) { + dprintf("Usage: unregister \n"); + return B_BAD_VALUE; + } + + const char* deviceName = argv[1]; + if (strncmp(deviceName, "/dev/", 5) == 0) + deviceName += 5; + + // find the device in the list and unregister it + MutexLocker locker(sDeviceListLock); + for (RawDeviceList::Iterator it = sDeviceList.GetIterator(); + RawDevice* device = it.Next();) { + if (strcmp(device->DeviceName(), deviceName) == 0) { + // TODO: Race condition: We should mark the device as going to + // be unregistered, so no one else can try the same after we + // unlock! + locker.Unlock(); +// TODO: The following doesn't work! unpublish_device(), as per implementation +// (partially commented out) and unregister_node() returns B_BUSY. + status_t error = sDeviceManager->unpublish_device( + device->Node(), device->DeviceName()); + if (error != B_OK) { + dprintf("Failed to unpublish device \"%s\": %s\n", + deviceName, strerror(error)); + return error; + } + + error = sDeviceManager->unregister_node(device->Node()); + if (error != B_OK) { + dprintf("Failed to unregister node \"%s\": %s\n", + deviceName, strerror(error)); + return error; + } + + return B_OK; + } + } + + dprintf("Device \"%s\" not found!\n", deviceName); + return B_BAD_VALUE; + } else { + dprintf("Invalid command \"%s\"!\n", argv[0]); + return B_BAD_VALUE; + } + + return B_OK; +} + + +static status_t +checksum_control_device_control(void* cookie, uint32 op, void* buffer, + size_t length) +{ + return B_BAD_VALUE; +} + + +// #pragma mark - raw device + + +static status_t +checksum_raw_device_init_device(void* driverCookie, void** _deviceCookie) +{ + RawDevice* device = static_cast((Device*)driverCookie); + + status_t error = device->Prepare(); + if (error != B_OK) + return error; + + *_deviceCookie = device; + return B_OK; +} + + +static void +checksum_raw_device_uninit_device(void* deviceCookie) +{ + RawDevice* device = (RawDevice*)deviceCookie; + device->Unprepare(); +} + + +static status_t +checksum_raw_device_open(void* deviceCookie, const char* path, int openMode, + void** _cookie) +{ + RawDevice* device = (RawDevice*)deviceCookie; + + RawDeviceCookie* cookie = new(std::nothrow) RawDeviceCookie(device, + openMode); + if (cookie == NULL) + return B_NO_MEMORY; + + *_cookie = cookie; + return B_OK; +} + + +static status_t +checksum_raw_device_close(void* cookie) +{ + return B_OK; +} + + +static status_t +checksum_raw_device_free(void* _cookie) +{ + RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; + delete cookie; + return B_OK; +} + + +static status_t +checksum_raw_device_read(void* _cookie, off_t pos, void* buffer, + size_t* _length) +{ + RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; + RawDevice* device = cookie->Device(); + + size_t length = *_length; + + if (pos >= device->DeviceSize()) + return B_BAD_VALUE; + if (pos + length > device->DeviceSize()) + length = device->DeviceSize() - pos; + + IORequest request; + status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); + if (status != B_OK) + return status; + + status = device->DoIO(&request); + if (status != B_OK) + return status; + + status = request.Wait(0, 0); + if (status == B_OK) + *_length = length; + return status; +} + + +static status_t +checksum_raw_device_write(void* _cookie, off_t pos, const void* buffer, + size_t* _length) +{ + RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; + RawDevice* device = cookie->Device(); + + size_t length = *_length; + + if (pos >= device->DeviceSize()) + return B_BAD_VALUE; + if (pos + length > device->DeviceSize()) + length = device->DeviceSize() - pos; + + IORequest request; + status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); + if (status != B_OK) + return status; + + status = device->DoIO(&request); + if (status != B_OK) + return status; + + status = request.Wait(0, 0); + if (status == B_OK) + *_length = length; + + return status; +} + + +static status_t +checksum_raw_device_io(void* _cookie, io_request* request) +{ + RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; + RawDevice* device = cookie->Device(); + + return device->DoIO(request); +} + + +static status_t +checksum_raw_device_control(void* _cookie, uint32 op, void* buffer, + size_t length) +{ + RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie; + RawDevice* device = cookie->Device(); + + switch (op) { + case B_GET_DEVICE_SIZE: + { + size_t size = device->DeviceSize(); + return user_memcpy(buffer, &size, sizeof(size_t)); + } + + case B_SET_NONBLOCKING_IO: + case B_SET_BLOCKING_IO: + return B_OK; + + case B_GET_READ_STATUS: + case B_GET_WRITE_STATUS: + { + bool value = true; + return user_memcpy(buffer, &value, sizeof(bool)); + } + + case B_GET_GEOMETRY: + case B_GET_BIOS_GEOMETRY: + { + device_geometry geometry; + geometry.bytes_per_sector = B_PAGE_SIZE; + geometry.sectors_per_track = 1; + geometry.cylinder_count = device->DeviceSize() / B_PAGE_SIZE; + // TODO: We're limited to 2^32 * B_PAGE_SIZE, if we don't use + // sectors_per_track and head_count. + geometry.head_count = 1; + geometry.device_type = B_DISK; + geometry.removable = true; + geometry.read_only = false; + geometry.write_once = false; + + return user_memcpy(buffer, &geometry, sizeof(device_geometry)); + } + + case B_GET_MEDIA_STATUS: + { + status_t status = B_OK; + return user_memcpy(buffer, &status, sizeof(status_t)); + } + + case B_SET_UNINTERRUPTABLE_IO: + case B_SET_INTERRUPTABLE_IO: + case B_FLUSH_DRIVE_CACHE: + return B_OK; + + case CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM: + { + if (IS_USER_ADDRESS(buffer)) { + checksum_device_ioctl_set_check_sum setCheckSum; + if (user_memcpy(&setCheckSum, buffer, sizeof(setCheckSum)) + != B_OK) { + return B_BAD_ADDRESS; + } + + return device->SetBlockCheckSum(setCheckSum.blockIndex, + setCheckSum.checkSum); + } + + checksum_device_ioctl_set_check_sum* setCheckSum + = (checksum_device_ioctl_set_check_sum*)buffer; + return device->SetBlockCheckSum(setCheckSum->blockIndex, + setCheckSum->checkSum); + } + } + return B_BAD_VALUE; +} + + +// #pragma mark - + + +module_dependency module_dependencies[] = { + {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, + {} +}; + + +static const struct driver_module_info sChecksumDeviceDriverModule = { + { + kDriverModuleName, + 0, + NULL + }, + + checksum_driver_supports_device, + checksum_driver_register_device, + checksum_driver_init_driver, + checksum_driver_uninit_driver, + checksum_driver_register_child_devices +}; + +static const struct device_module_info sChecksumControlDeviceModule = { + { + kControlDeviceModuleName, + 0, + NULL + }, + + checksum_control_device_init_device, + checksum_control_device_uninit_device, + NULL, + + checksum_control_device_open, + checksum_control_device_close, + checksum_control_device_free, + + checksum_control_device_read, + checksum_control_device_write, + NULL, // io + + checksum_control_device_control, + + NULL, // select + NULL // deselect +}; + +static const struct device_module_info sChecksumRawDeviceModule = { + { + kRawDeviceModuleName, + 0, + NULL + }, + + checksum_raw_device_init_device, + checksum_raw_device_uninit_device, + NULL, + + checksum_raw_device_open, + checksum_raw_device_close, + checksum_raw_device_free, + + checksum_raw_device_read, + checksum_raw_device_write, + checksum_raw_device_io, + + checksum_raw_device_control, + + NULL, // select + NULL // deselect +}; + +const module_info* modules[] = { + (module_info*)&sChecksumDeviceDriverModule, + (module_info*)&sChecksumControlDeviceModule, + (module_info*)&sChecksumRawDeviceModule, + NULL +}; diff --git a/src/tests/system/kernel/file_corruption/checksum_device.h b/src/tests/system/kernel/file_corruption/checksum_device.h new file mode 100644 index 0000000000..68b632eff5 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/checksum_device.h @@ -0,0 +1,25 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHECKSUM_DEVICE_H +#define CHECKSUM_DEVICE_H + + +#include + +#include "CheckSum.h" + + +enum { + CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM = B_DEVICE_OP_CODES_END + 0x666 +}; + + +struct checksum_device_ioctl_set_check_sum { + uint64 blockIndex; + CheckSum checkSum; +}; + + +#endif // CHECKSUM_DEVICE_H