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
44 static const uint32 kDMAResourceBufferCount = 16;
45 static const uint32 kDMAResourceBounceBufferCount = 16;
46
47 static const size_t kCheckSumLength = sizeof(CheckSum);
48 static const uint32 kCheckSumsPerBlock = B_PAGE_SIZE / sizeof(CheckSum);
49
50 static const char* const kDriverModuleName
51 = "drivers/disk/virtual/checksum_device/driver_v1";
52 static const char* const kControlDeviceModuleName
53 = "drivers/disk/virtual/checksum_device/control/device_v1";
54 static const char* const kRawDeviceModuleName
55 = "drivers/disk/virtual/checksum_device/raw/device_v1";
56
57 static const char* const kControlDeviceName
58 = "disk/virtual/checksum_device/control";
59 static const char* const kRawDeviceBaseName = "disk/virtual/checksum_device";
60
61 static const char* const kFilePathItem = "checksum_device/file_path";
62
63
64 struct RawDevice;
65 typedef DoublyLinkedList<RawDevice> RawDeviceList;
66
67 struct device_manager_info* sDeviceManager;
68
69 static RawDeviceList sDeviceList;
70 static mutex sDeviceListLock = MUTEX_INITIALIZER("checksum device list");
71
72
73 struct CheckSumBlock : public DoublyLinkedListLinkImpl<CheckSumBlock> {
74 uint64 blockIndex;
75 bool used;
76 bool dirty;
77 CheckSum checkSums[kCheckSumsPerBlock];
78
CheckSumBlockCheckSumBlock79 CheckSumBlock()
80 :
81 used(false)
82 {
83 }
84 };
85
86
87 struct CheckSumCache {
CheckSumCacheCheckSumCache88 CheckSumCache()
89 {
90 mutex_init(&fLock, "check sum cache");
91 }
92
~CheckSumCacheCheckSumCache93 ~CheckSumCache()
94 {
95 while (CheckSumBlock* block = fBlocks.RemoveHead())
96 delete block;
97
98 mutex_destroy(&fLock);
99 }
100
InitCheckSumCache101 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
GetCheckSumCheckSumCache117 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
SetCheckSumCheckSumCache134 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
160 private:
161 typedef DoublyLinkedList<CheckSumBlock> BlockList;
162
163 private:
_GetBlockCheckSumCache164 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
_FlushBlockCheckSumCache196 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
_ReadBlockCheckSumCache212 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
231 private:
232 mutex fLock;
233 uint64 fBlockCount;
234 int fFD;
235 BlockList fBlocks; // LRU first
236 };
237
238
239 struct Device {
DeviceDevice240 Device(device_node* node)
241 :
242 fNode(node)
243 {
244 mutex_init(&fLock, "checksum device");
245 }
246
~DeviceDevice247 virtual ~Device()
248 {
249 mutex_destroy(&fLock);
250 }
251
LockDevice252 bool Lock() { mutex_lock(&fLock); return true; }
UnlockDevice253 void Unlock() { mutex_unlock(&fLock); }
254
NodeDevice255 device_node* Node() const { return fNode; }
256
257 virtual status_t PublishDevice() = 0;
258
259 protected:
260 mutex fLock;
261 device_node* fNode;
262 };
263
264
265 struct ControlDevice : Device {
ControlDeviceControlDevice266 ControlDevice(device_node* node)
267 :
268 Device(node)
269 {
270 }
271
RegisterControlDevice272 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
PublishDeviceControlDevice286 virtual status_t PublishDevice()
287 {
288 return sDeviceManager->publish_device(Node(), kControlDeviceName,
289 kControlDeviceModuleName);
290 }
291 };
292
293
294 struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
RawDeviceRawDevice295 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
~RawDeviceRawDevice310 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
IndexRawDevice323 int32 Index() const { return fIndex; }
DeviceSizeRawDevice324 off_t DeviceSize() const { return fDeviceSize; }
DeviceNameRawDevice325 const char* DeviceName() const { return fDeviceName; }
326
InitRawDevice327 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
PrepareRawDevice394 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
UnprepareRawDevice448 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
DoIORawDevice463 status_t DoIO(IORequest* request)
464 {
465 return fIOScheduler->ScheduleRequest(request);
466 }
467
PublishDeviceRawDevice468 virtual status_t PublishDevice()
469 {
470 return sDeviceManager->publish_device(Node(), fDeviceName,
471 kRawDeviceModuleName);
472 }
473
GetBlockCheckSumRawDevice474 status_t GetBlockCheckSum(uint64 blockIndex, CheckSum& checkSum)
475 {
476 return fCheckSumCache->GetCheckSum(blockIndex, checkSum);
477 }
478
SetBlockCheckSumRawDevice479 status_t SetBlockCheckSum(uint64 blockIndex, const CheckSum& checkSum)
480 {
481 return fCheckSumCache->SetCheckSum(blockIndex, checkSum);
482 }
483
484 private:
_DoIOEntryRawDevice485 static status_t _DoIOEntry(void* data, IOOperation* operation)
486 {
487 return ((RawDevice*)data)->_DoIO(operation);
488 }
489
_DoIORawDevice490 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
_TransferBlockRawDevice517 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
_CopyDataRawDevice547 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
_CheckCheckSumRawDevice578 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
602 private:
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
616 struct RawDeviceCookie {
RawDeviceCookieRawDeviceCookie617 RawDeviceCookie(RawDevice* device, int openMode)
618 :
619 fDevice(device),
620 fOpenMode(openMode)
621 {
622 }
623
DeviceRawDeviceCookie624 RawDevice* Device() const { return fDevice; }
OpenModeRawDeviceCookie625 int OpenMode() const { return fOpenMode; }
626
627 private:
628 RawDevice* fDevice;
629 int fOpenMode;
630 };
631
632
633 // #pragma mark -
634
635
636 static bool
parse_command_line(char * buffer,char ** & _argv,int & _argc)637 parse_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
720 static float
checksum_driver_supports_device(device_node * parent)721 checksum_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
732 static status_t
checksum_driver_register_device(device_node * parent)733 checksum_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
746 static status_t
checksum_driver_init_driver(device_node * node,void ** _driverCookie)747 checksum_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
775 static void
checksum_driver_uninit_driver(void * driverCookie)776 checksum_driver_uninit_driver(void* driverCookie)
777 {
778 Device* device = (Device*)driverCookie;
779 delete device;
780 }
781
782
783 static status_t
checksum_driver_register_child_devices(void * driverCookie)784 checksum_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
794 static status_t
checksum_control_device_init_device(void * driverCookie,void ** _deviceCookie)795 checksum_control_device_init_device(void* driverCookie, void** _deviceCookie)
796 {
797 *_deviceCookie = driverCookie;
798 return B_OK;
799 }
800
801
802 static void
checksum_control_device_uninit_device(void * deviceCookie)803 checksum_control_device_uninit_device(void* deviceCookie)
804 {
805 }
806
807
808 static status_t
checksum_control_device_open(void * deviceCookie,const char * path,int openMode,void ** _cookie)809 checksum_control_device_open(void* deviceCookie, const char* path, int openMode,
810 void** _cookie)
811 {
812 *_cookie = deviceCookie;
813 return B_OK;
814 }
815
816
817 static status_t
checksum_control_device_close(void * cookie)818 checksum_control_device_close(void* cookie)
819 {
820 return B_OK;
821 }
822
823
824 static status_t
checksum_control_device_free(void * cookie)825 checksum_control_device_free(void* cookie)
826 {
827 return B_OK;
828 }
829
830
831 static status_t
checksum_control_device_read(void * cookie,off_t position,void * buffer,size_t * _length)832 checksum_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
840 static status_t
checksum_control_device_write(void * cookie,off_t position,const void * data,size_t * _length)841 checksum_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
942 static status_t
checksum_control_device_control(void * cookie,uint32 op,void * buffer,size_t length)943 checksum_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
953 static status_t
checksum_raw_device_init_device(void * driverCookie,void ** _deviceCookie)954 checksum_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
967 static void
checksum_raw_device_uninit_device(void * deviceCookie)968 checksum_raw_device_uninit_device(void* deviceCookie)
969 {
970 RawDevice* device = (RawDevice*)deviceCookie;
971 device->Unprepare();
972 }
973
974
975 static status_t
checksum_raw_device_open(void * deviceCookie,const char * path,int openMode,void ** _cookie)976 checksum_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
991 static status_t
checksum_raw_device_close(void * cookie)992 checksum_raw_device_close(void* cookie)
993 {
994 return B_OK;
995 }
996
997
998 static status_t
checksum_raw_device_free(void * _cookie)999 checksum_raw_device_free(void* _cookie)
1000 {
1001 RawDeviceCookie* cookie = (RawDeviceCookie*)_cookie;
1002 delete cookie;
1003 return B_OK;
1004 }
1005
1006
1007 static status_t
checksum_raw_device_read(void * _cookie,off_t pos,void * buffer,size_t * _length)1008 checksum_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
1037 static status_t
checksum_raw_device_write(void * _cookie,off_t pos,const void * buffer,size_t * _length)1038 checksum_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
1068 static status_t
checksum_raw_device_io(void * _cookie,io_request * request)1069 checksum_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
1078 static status_t
checksum_raw_device_control(void * _cookie,uint32 op,void * buffer,size_t length)1079 checksum_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
1181 module_dependency module_dependencies[] = {
1182 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1183 {}
1184 };
1185
1186
1187 static 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
1201 static 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
1226 static 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
1251 const module_info* modules[] = {
1252 (module_info*)&sChecksumDeviceDriverModule,
1253 (module_info*)&sChecksumControlDeviceModule,
1254 (module_info*)&sChecksumRawDeviceModule,
1255 NULL
1256 };
1257