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