xref: /haiku/src/tests/system/kernel/file_corruption/driver/checksum_device.cpp (revision fb2d716a991fb0d64f1850074564e27a0ad8b3b3)
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