[haiku-commits] r37247 - in haiku/trunk/src/tests/system/kernel: . file_corruption

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Fri, 25 Jun 2010 01:38:18 +0200 (CEST)

Author: bonefish
Date: 2010-06-25 01:38:17 +0200 (Fri, 25 Jun 2010)
New Revision: 37247
Changeset: http://dev.haiku-os.org/changeset/37247/haiku

Added:
   haiku/trunk/src/tests/system/kernel/file_corruption/
   haiku/trunk/src/tests/system/kernel/file_corruption/CheckSum.h
   haiku/trunk/src/tests/system/kernel/file_corruption/Jamfile
   haiku/trunk/src/tests/system/kernel/file_corruption/checksum_device.cpp
   haiku/trunk/src/tests/system/kernel/file_corruption/checksum_device.h
Modified:
   haiku/trunk/src/tests/system/kernel/Jamfile
Log:
Beginnings of a test for tracking down the file corruptions that still seem
to occur in current revisions. The working hypothesis is that the corruptions
happen below the file system (i.e. in file/block cache or VM). The intended
setup for the test is a device driver -- a loop device kind of thing -- that
also stores check sums for all blocks and a file system that communicates the
check sums to expect via an independent channel (ioctl) to the driver.
Whenever a block arrives at the driver for writing, the block is checked
against its expected check sum. Initially Axel's idea, BTW.
The device driver is done and working as far as tested (save for
unregistering devices). The file system part is still to be done.


Modified: haiku/trunk/src/tests/system/kernel/Jamfile
===================================================================
--- haiku/trunk/src/tests/system/kernel/Jamfile 2010-06-24 23:20:39 UTC (rev 
37246)
+++ haiku/trunk/src/tests/system/kernel/Jamfile 2010-06-24 23:38:17 UTC (rev 
37247)
@@ -89,6 +89,7 @@
 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 ;

Added: haiku/trunk/src/tests/system/kernel/file_corruption/CheckSum.h
===================================================================
--- haiku/trunk/src/tests/system/kernel/file_corruption/CheckSum.h              
                (rev 0)
+++ haiku/trunk/src/tests/system/kernel/file_corruption/CheckSum.h      
2010-06-24 23:38:17 UTC (rev 37247)
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef CHECK_SUM_H
+#define CHECK_SUM_H
+
+
+#include <string.h>
+
+#include <SHA256.h>
+
+
+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

Added: haiku/trunk/src/tests/system/kernel/file_corruption/Jamfile
===================================================================
--- haiku/trunk/src/tests/system/kernel/file_corruption/Jamfile                 
        (rev 0)
+++ haiku/trunk/src/tests/system/kernel/file_corruption/Jamfile 2010-06-24 
23:38:17 UTC (rev 37247)
@@ -0,0 +1,16 @@
+SubDir HAIKU_TOP src tests system kernel file_corruption ;
+
+
+SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
+UsePrivateKernelHeaders ;
+
+
+KernelAddon <test_driver>checksum_device :
+       checksum_device.cpp
+
+       # from src/kits/shared
+       SHA256.cpp
+;
+
+SEARCH on [ FGristFiles SHA256.cpp ]
+       = [ FDirName $(HAIKU_TOP) src kits shared ] ;

Added: haiku/trunk/src/tests/system/kernel/file_corruption/checksum_device.cpp
===================================================================
--- haiku/trunk/src/tests/system/kernel/file_corruption/checksum_device.cpp     
                        (rev 0)
+++ haiku/trunk/src/tests/system/kernel/file_corruption/checksum_device.cpp     
2010-06-24 23:38:17 UTC (rev 37247)
@@ -0,0 +1,1184 @@
+/*
+ * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include "checksum_device.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <device_manager.h>
+
+#include <AutoDeleter.h>
+#include <util/AutoLock.h>
+#include <util/DoublyLinkedList.h>
+
+#include <fs/KPath.h>
+#include <lock.h>
+#include <vm/vm.h>
+
+#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<RawDevice> RawDeviceList;
+
+struct device_manager_info* sDeviceManager;
+
+static RawDeviceList sDeviceList;
+static mutex sDeviceListLock = MUTEX_INITIALIZER("checksum device list");
+
+
+struct CheckSumBlock : public DoublyLinkedListLinkImpl<CheckSumBlock> {
+       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<CheckSumBlock> 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> {
+       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<char*> 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 <path>\n");
+               dprintf("  Registers file <path> as a new checksum device.\n");
+               dprintf("unregister <device>\n");
+               dprintf("  Unregisters <device>.\n");
+       } else if (strcmp(argv[0], "register") == 0) {
+               // register
+               if (argc != 2) {
+                       dprintf("Usage: register <path>\n");
+                       return B_BAD_VALUE;
+               }
+
+               return device->Register(argv[1]);
+       } else if (strcmp(argv[0], "unregister") == 0) {
+               // unregister
+               if (argc != 2) {
+                       dprintf("Usage: unregister <device>\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<RawDevice*>((Device*)driverCookie);

[... truncated: 309 lines follow ...]

Other related posts:

  • » [haiku-commits] r37247 - in haiku/trunk/src/tests/system/kernel: . file_corruption - ingo_weinhold