1/*
2 * nt_io.c --- This is the Nt I/O interface to the I/O manager.
3 *
4 * Implements a one-block write-through cache.
5 *
6 * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
7 * Copyright (C) 1998 Andrey Shedel (andreys@ns.cr.cyco.com)
8 *
9 * %Begin-Header%
10 * This file may be redistributed under the terms of the GNU Library
11 * General Public License, version 2.
12 * %End-Header%
13 */
14
15#ifdef HAVE_CONFIG_H
16#endif
17
18
19//
20// I need some warnings to disable...
21//
22
23
24#pragma warning(disable:4514) // unreferenced inline function has been removed
25#pragma warning(push,4)
26
27#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union)
28#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
29#pragma warning(disable:4115) // named type definition in parentheses
30
31#include <ntddk.h>
32#include <ntdddisk.h>
33#include <ntstatus.h>
34
35#pragma warning(pop)
36
37
38//
39// Some native APIs.
40//
41
42NTSYSAPI
43ULONG
44NTAPI
45RtlNtStatusToDosError(
46    IN NTSTATUS Status
47   );
48
49NTSYSAPI
50NTSTATUS
51NTAPI
52NtClose(
53    IN HANDLE Handle
54   );
55
56
57NTSYSAPI
58NTSTATUS
59NTAPI
60NtOpenFile(
61    OUT PHANDLE FileHandle,
62    IN ACCESS_MASK DesiredAccess,
63    IN POBJECT_ATTRIBUTES ObjectAttributes,
64    OUT PIO_STATUS_BLOCK IoStatusBlock,
65    IN ULONG ShareAccess,
66    IN ULONG OpenOptions
67    );
68
69NTSYSAPI
70NTSTATUS
71NTAPI
72NtFlushBuffersFile(
73    IN HANDLE FileHandle,
74    OUT PIO_STATUS_BLOCK IoStatusBlock
75   );
76
77
78NTSYSAPI
79NTSTATUS
80NTAPI
81NtReadFile(
82    IN HANDLE FileHandle,
83    IN HANDLE Event OPTIONAL,
84    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
85    IN PVOID ApcContext OPTIONAL,
86    OUT PIO_STATUS_BLOCK IoStatusBlock,
87    OUT PVOID Buffer,
88    IN ULONG Length,
89    IN PLARGE_INTEGER ByteOffset OPTIONAL,
90    IN PULONG Key OPTIONAL
91    );
92
93NTSYSAPI
94NTSTATUS
95NTAPI
96NtWriteFile(
97    IN HANDLE FileHandle,
98    IN HANDLE Event OPTIONAL,
99    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
100    IN PVOID ApcContext OPTIONAL,
101    OUT PIO_STATUS_BLOCK IoStatusBlock,
102    IN PVOID Buffer,
103    IN ULONG Length,
104    IN PLARGE_INTEGER ByteOffset OPTIONAL,
105    IN PULONG Key OPTIONAL
106    );
107
108NTSYSAPI
109NTSTATUS
110NTAPI
111NtDeviceIoControlFile(
112    IN HANDLE FileHandle,
113    IN HANDLE Event OPTIONAL,
114    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
115    IN PVOID ApcContext OPTIONAL,
116    OUT PIO_STATUS_BLOCK IoStatusBlock,
117    IN ULONG IoControlCode,
118    IN PVOID InputBuffer OPTIONAL,
119    IN ULONG InputBufferLength,
120    OUT PVOID OutputBuffer OPTIONAL,
121    IN ULONG OutputBufferLength
122    );
123
124NTSYSAPI
125NTSTATUS
126NTAPI
127NtFsControlFile(
128    IN HANDLE FileHandle,
129    IN HANDLE Event OPTIONAL,
130    IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
131    IN PVOID ApcContext OPTIONAL,
132    OUT PIO_STATUS_BLOCK IoStatusBlock,
133    IN ULONG IoControlCode,
134    IN PVOID InputBuffer OPTIONAL,
135    IN ULONG InputBufferLength,
136    OUT PVOID OutputBuffer OPTIONAL,
137    IN ULONG OutputBufferLength
138    );
139
140
141NTSYSAPI
142NTSTATUS
143NTAPI
144NtDelayExecution(
145    IN BOOLEAN Alertable,
146    IN PLARGE_INTEGER Interval
147    );
148
149
150#define FSCTL_LOCK_VOLUME               CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
151#define FSCTL_UNLOCK_VOLUME             CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
152#define FSCTL_DISMOUNT_VOLUME           CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
153#define FSCTL_IS_VOLUME_MOUNTED         CTL_CODE(FILE_DEVICE_FILE_SYSTEM,10, METHOD_BUFFERED, FILE_ANY_ACCESS)
154
155
156//
157// useful macros
158//
159
160#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0)))
161
162
163//
164// Include Win32 error codes.
165//
166
167#include <winerror.h>
168
169//
170// standard stuff
171//
172
173#include <assert.h>
174#include <stdio.h>
175#include <string.h>
176#include <stdlib.h>
177#include <malloc.h>
178
179#include <linux/types.h>
180#include "ext2_fs.h"
181#include <errno.h>
182
183#include "et/com_err.h"
184#include "ext2fs/ext2fs.h"
185#include "ext2fs/ext2_err.h"
186
187
188
189
190//
191// For checking structure magic numbers...
192//
193
194
195#define EXT2_CHECK_MAGIC(struct, code) \
196	  if ((struct)->magic != (code)) return (code)
197
198#define EXT2_ET_MAGIC_NT_IO_CHANNEL  0x10ed
199
200
201//
202// Private data block
203//
204
205typedef struct _NT_PRIVATE_DATA {
206	int	   magic;
207	HANDLE Handle;
208	int	   Flags;
209	PCHAR  Buffer;
210	__u32  BufferBlockNumber;
211	ULONG  BufferSize;
212	BOOLEAN OpenedReadonly;
213	BOOLEAN Written;
214}NT_PRIVATE_DATA, *PNT_PRIVATE_DATA;
215
216
217
218//
219// Standard interface prototypes
220//
221
222static errcode_t nt_open(const char *name, int flags, io_channel *channel);
223static errcode_t nt_close(io_channel channel);
224static errcode_t nt_set_blksize(io_channel channel, int blksize);
225static errcode_t nt_read_blk(io_channel channel, unsigned long block,
226			       int count, void *data);
227static errcode_t nt_write_blk(io_channel channel, unsigned long block,
228				int count, const void *data);
229static errcode_t nt_flush(io_channel channel);
230
231static struct struct_io_manager struct_nt_manager = {
232	EXT2_ET_MAGIC_IO_MANAGER,
233	"NT I/O Manager",
234	nt_open,
235	nt_close,
236	nt_set_blksize,
237	nt_read_blk,
238	nt_write_blk,
239	nt_flush
240};
241
242
243
244//
245// function to get API
246//
247
248io_manager nt_io_manager()
249{
250	return &struct_nt_manager;
251}
252
253
254
255
256
257//
258// This is a code to convert Win32 errors to unix errno
259//
260
261typedef struct {
262	ULONG WinError;
263	int errnocode;
264}ERROR_ENTRY;
265
266static ERROR_ENTRY ErrorTable[] = {
267        {  ERROR_INVALID_FUNCTION,       EINVAL    },
268        {  ERROR_FILE_NOT_FOUND,         ENOENT    },
269        {  ERROR_PATH_NOT_FOUND,         ENOENT    },
270        {  ERROR_TOO_MANY_OPEN_FILES,    EMFILE    },
271        {  ERROR_ACCESS_DENIED,          EACCES    },
272        {  ERROR_INVALID_HANDLE,         EBADF     },
273        {  ERROR_ARENA_TRASHED,          ENOMEM    },
274        {  ERROR_NOT_ENOUGH_MEMORY,      ENOMEM    },
275        {  ERROR_INVALID_BLOCK,          ENOMEM    },
276        {  ERROR_BAD_ENVIRONMENT,        E2BIG     },
277        {  ERROR_BAD_FORMAT,             ENOEXEC   },
278        {  ERROR_INVALID_ACCESS,         EINVAL    },
279        {  ERROR_INVALID_DATA,           EINVAL    },
280        {  ERROR_INVALID_DRIVE,          ENOENT    },
281        {  ERROR_CURRENT_DIRECTORY,      EACCES    },
282        {  ERROR_NOT_SAME_DEVICE,        EXDEV     },
283        {  ERROR_NO_MORE_FILES,          ENOENT    },
284        {  ERROR_LOCK_VIOLATION,         EACCES    },
285        {  ERROR_BAD_NETPATH,            ENOENT    },
286        {  ERROR_NETWORK_ACCESS_DENIED,  EACCES    },
287        {  ERROR_BAD_NET_NAME,           ENOENT    },
288        {  ERROR_FILE_EXISTS,            EEXIST    },
289        {  ERROR_CANNOT_MAKE,            EACCES    },
290        {  ERROR_FAIL_I24,               EACCES    },
291        {  ERROR_INVALID_PARAMETER,      EINVAL    },
292        {  ERROR_NO_PROC_SLOTS,          EAGAIN    },
293        {  ERROR_DRIVE_LOCKED,           EACCES    },
294        {  ERROR_BROKEN_PIPE,            EPIPE     },
295        {  ERROR_DISK_FULL,              ENOSPC    },
296        {  ERROR_INVALID_TARGET_HANDLE,  EBADF     },
297        {  ERROR_INVALID_HANDLE,         EINVAL    },
298        {  ERROR_WAIT_NO_CHILDREN,       ECHILD    },
299        {  ERROR_CHILD_NOT_COMPLETE,     ECHILD    },
300        {  ERROR_DIRECT_ACCESS_HANDLE,   EBADF     },
301        {  ERROR_NEGATIVE_SEEK,          EINVAL    },
302        {  ERROR_SEEK_ON_DEVICE,         EACCES    },
303        {  ERROR_DIR_NOT_EMPTY,          ENOTEMPTY },
304        {  ERROR_NOT_LOCKED,             EACCES    },
305        {  ERROR_BAD_PATHNAME,           ENOENT    },
306        {  ERROR_MAX_THRDS_REACHED,      EAGAIN    },
307        {  ERROR_LOCK_FAILED,            EACCES    },
308        {  ERROR_ALREADY_EXISTS,         EEXIST    },
309        {  ERROR_FILENAME_EXCED_RANGE,   ENOENT    },
310        {  ERROR_NESTING_NOT_ALLOWED,    EAGAIN    },
311        {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }
312};
313
314
315
316
317static
318unsigned
319_MapDosError (
320    IN ULONG WinError
321   )
322{
323	int i;
324
325	//
326	// Lookup
327	//
328
329	for (i = 0; i < (sizeof(ErrorTable)/sizeof(ErrorTable[0])); ++i)
330	{
331		if (WinError == ErrorTable[i].WinError)
332		{
333			return ErrorTable[i].errnocode;
334		}
335	}
336
337	//
338	// not in table. Check ranges
339	//
340
341	if ((WinError >= ERROR_WRITE_PROTECT) &&
342		(WinError <= ERROR_SHARING_BUFFER_EXCEEDED))
343	{
344		return EACCES;
345	}
346	else if ((WinError >= ERROR_INVALID_STARTING_CODESEG) &&
347			 (WinError <= ERROR_INFLOOP_IN_RELOC_CHAIN))
348	{
349		return ENOEXEC;
350	}
351	else
352	{
353		return EINVAL;
354	}
355}
356
357
358
359
360
361
362
363//
364// Function to map NT status to dos error.
365//
366
367static
368__inline
369unsigned
370_MapNtStatus(
371    IN NTSTATUS Status
372   )
373{
374	return _MapDosError(RtlNtStatusToDosError(Status));
375}
376
377
378
379
380
381//
382// Helper functions to make things easyer
383//
384
385static
386NTSTATUS
387_OpenNtName(
388    IN PCSTR Name,
389    IN BOOLEAN Readonly,
390    OUT PHANDLE Handle,
391    OUT PBOOLEAN OpenedReadonly OPTIONAL
392   )
393{
394	UNICODE_STRING UnicodeString;
395	ANSI_STRING    AnsiString;
396	WCHAR Buffer[512];
397	NTSTATUS Status;
398	OBJECT_ATTRIBUTES ObjectAttributes;
399	IO_STATUS_BLOCK IoStatusBlock;
400
401	//
402	// Make Unicode name from inlut string
403	//
404
405	UnicodeString.Buffer = &Buffer[0];
406	UnicodeString.Length = 0;
407	UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!!
408
409	RtlInitAnsiString(&AnsiString, Name);
410
411	Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
412
413	if(!NT_SUCCESS(Status))
414	{
415		return Status; // Unpappable character?
416	}
417
418	//
419	// Initialize object
420	//
421
422	InitializeObjectAttributes(&ObjectAttributes,
423							   &UnicodeString,
424							   OBJ_CASE_INSENSITIVE,
425							   NULL,
426							   NULL );
427
428	//
429	// Try to open it in initial mode
430	//
431
432	if(ARGUMENT_PRESENT(OpenedReadonly))
433	{
434		*OpenedReadonly = Readonly;
435	}
436
437
438	Status = NtOpenFile(Handle,
439						SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
440						&ObjectAttributes,
441						&IoStatusBlock,
442						FILE_SHARE_WRITE | FILE_SHARE_READ,
443						FILE_SYNCHRONOUS_IO_NONALERT);
444
445	if(!NT_SUCCESS(Status))
446	{
447		//
448		// Maybe was just mounted? wait 0.5 sec and retry.
449		//
450
451		LARGE_INTEGER Interval;
452		Interval.QuadPart = -5000000; // 0.5 sec. from now
453
454		NtDelayExecution(FALSE, &Interval);
455
456		Status = NtOpenFile(Handle,
457							SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
458							&ObjectAttributes,
459							&IoStatusBlock,
460							FILE_SHARE_WRITE | FILE_SHARE_READ,
461							FILE_SYNCHRONOUS_IO_NONALERT);
462
463		//
464		// Try to satisfy mode
465		//
466
467		if((STATUS_ACCESS_DENIED == Status) && !Readonly)
468		{
469			if(ARGUMENT_PRESENT(OpenedReadonly))
470			{
471				*OpenedReadonly = TRUE;
472			}
473
474			Status = NtOpenFile(Handle,
475							SYNCHRONIZE | FILE_READ_DATA,
476							&ObjectAttributes,
477							&IoStatusBlock,
478							FILE_SHARE_WRITE | FILE_SHARE_READ,
479							FILE_SYNCHRONOUS_IO_NONALERT);
480		}
481	}
482
483
484
485	//
486	// done
487	//
488
489	return Status;
490}
491
492
493static
494NTSTATUS
495_OpenDriveLetter(
496    IN CHAR Letter,
497    IN BOOLEAN ReadOnly,
498    OUT PHANDLE Handle,
499    OUT PBOOLEAN OpenedReadonly OPTIONAL
500   )
501{
502	CHAR Buffer[100];
503
504	sprintf(Buffer, "\\DosDevices\\%c:", Letter);
505
506	return _OpenNtName(Buffer, ReadOnly, Handle, OpenedReadonly);
507}
508
509
510//
511// Flush device
512//
513
514static
515__inline
516NTSTATUS
517_FlushDrive(
518		IN HANDLE Handle
519		)
520{
521	IO_STATUS_BLOCK IoStatusBlock;
522	return NtFlushBuffersFile(Handle, &IoStatusBlock);
523}
524
525
526//
527// lock drive
528//
529
530static
531__inline
532NTSTATUS
533_LockDrive(
534		IN HANDLE Handle
535		)
536{
537	IO_STATUS_BLOCK IoStatusBlock;
538	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_LOCK_VOLUME, 0, 0, 0, 0);
539}
540
541
542//
543// unlock drive
544//
545
546static
547__inline
548NTSTATUS
549_UnlockDrive(
550	IN HANDLE Handle
551	)
552{
553	IO_STATUS_BLOCK IoStatusBlock;
554	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0);
555}
556
557static
558__inline
559NTSTATUS
560_DismountDrive(
561	IN HANDLE Handle
562	)
563{
564	IO_STATUS_BLOCK IoStatusBlock;
565	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0);
566}
567
568
569//
570// is mounted
571//
572
573static
574__inline
575BOOLEAN
576_IsMounted(
577	IN HANDLE Handle
578	)
579{
580	IO_STATUS_BLOCK IoStatusBlock;
581	NTSTATUS Status;
582	Status = NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_IS_VOLUME_MOUNTED, 0, 0, 0, 0);
583	return (BOOLEAN)(STATUS_SUCCESS == Status);
584}
585
586
587static
588__inline
589NTSTATUS
590_CloseDisk(
591		IN HANDLE Handle
592		)
593{
594	return NtClose(Handle);
595}
596
597
598
599
600//
601// Make NT name from any recognized name
602//
603
604static
605PCSTR
606_NormalizeDeviceName(
607    IN PCSTR Device,
608    IN PSTR NormalizedDeviceNameBuffer
609   )
610{
611	int PartitionNumber = -1;
612	UCHAR DiskNumber;
613	PSTR p;
614
615
616	//
617	// Do not try to parse NT name
618	//
619
620	if('\\' == *Device)
621		return Device;
622
623
624
625	//
626	// Strip leading '/dev/' if any
627	//
628
629	if(('/' == *(Device)) &&
630		('d' == *(Device + 1)) &&
631		('e' == *(Device + 2)) &&
632		('v' == *(Device + 3)) &&
633		('/' == *(Device + 4)))
634	{
635		Device += 5;
636	}
637
638	if('\0' == *Device)
639	{
640		return NULL;
641	}
642
643
644	//
645	// forms: hda[n], fd[n]
646	//
647
648	if('d' != *(Device + 1))
649	{
650		return NULL;
651	}
652
653	if('h' == *Device)
654	{
655		if((*(Device + 2) < 'a') || (*(Device + 2) > ('a' + 9)) ||
656		   ((*(Device + 3) != '\0') &&
657			((*(Device + 4) != '\0') ||
658			 ((*(Device + 3) < '0') || (*(Device + 3) > '9'))
659			)
660		   )
661		  )
662		{
663			return NULL;
664		}
665
666		DiskNumber = (UCHAR)(*(Device + 2) - 'a');
667
668		if(*(Device + 3) != '\0')
669		{
670			PartitionNumber = (*(Device + 3) - '0');
671		}
672
673	}
674	else if('f' == *Device)
675	{
676		//
677		// 3-d letted should be a digit.
678		//
679
680		if((*(Device + 3) != '\0') ||
681		   (*(Device + 2) < '0') || (*(Device + 2) > '9'))
682		{
683			return NULL;
684		}
685
686		DiskNumber = (UCHAR)(*(Device + 2) - '0');
687
688	}
689	else
690	{
691		//
692		// invalid prefix
693		//
694
695		return NULL;
696	}
697
698
699
700	//
701	// Prefix
702	//
703
704	strcpy(NormalizedDeviceNameBuffer, "\\Device\\");
705
706	//
707	// Media name
708	//
709
710	switch(*Device)
711	{
712
713	case 'f':
714		strcat(NormalizedDeviceNameBuffer, "Floppy0");
715		break;
716
717	case 'h':
718		strcat(NormalizedDeviceNameBuffer, "Harddisk0");
719		break;
720	}
721
722
723	p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
724	*p = (CHAR)(*p + DiskNumber);
725
726
727	//
728	// Partition nr.
729	//
730
731	if(PartitionNumber >= 0)
732	{
733		strcat(NormalizedDeviceNameBuffer, "\\Partition0");
734
735		p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
736		*p = (CHAR)(*p + PartitionNumber);
737	}
738
739
740	return NormalizedDeviceNameBuffer;
741}
742
743
744
745
746static
747VOID
748_GetDeviceSize(
749    IN HANDLE h,
750    OUT unsigned __int64 *FsSize
751   )
752{
753	PARTITION_INFORMATION pi;
754	DISK_GEOMETRY gi;
755	NTSTATUS Status;
756	IO_STATUS_BLOCK IoStatusBlock;
757
758	//
759	// Zero it
760	//
761
762	*FsSize = 0;
763
764	//
765	// Call driver
766	//
767
768	RtlZeroMemory(&pi, sizeof(PARTITION_INFORMATION));
769
770	Status = NtDeviceIoControlFile(
771		h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO,
772		&pi, sizeof(PARTITION_INFORMATION),
773		&pi, sizeof(PARTITION_INFORMATION));
774
775
776	if(NT_SUCCESS(Status))
777	{
778		*FsSize = pi.PartitionLength.QuadPart;
779	}
780	else if(STATUS_INVALID_DEVICE_REQUEST == Status)
781	{
782		//
783		// No partitions: get device info.
784		//
785
786		RtlZeroMemory(&gi, sizeof(DISK_GEOMETRY));
787
788		Status = NtDeviceIoControlFile(
789				h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY,
790				&gi, sizeof(DISK_GEOMETRY),
791				&gi, sizeof(DISK_GEOMETRY));
792
793
794		if(NT_SUCCESS(Status))
795		{
796			*FsSize =
797				gi.BytesPerSector *
798				gi.SectorsPerTrack *
799				gi.TracksPerCylinder *
800				gi.Cylinders.QuadPart;
801		}
802
803	}
804}
805
806
807
808//
809// Open device by name.
810//
811
812static
813BOOLEAN
814_Ext2OpenDevice(
815    IN PCSTR Name,
816    IN BOOLEAN ReadOnly,
817    OUT PHANDLE Handle,
818    OUT PBOOLEAN OpenedReadonly OPTIONAL,
819    OUT unsigned *Errno OPTIONAL
820   )
821{
822	CHAR NormalizedDeviceName[512];
823	NTSTATUS Status;
824
825	if(NULL == Name)
826	{
827		//
828		// Set not found
829		//
830
831		if(ARGUMENT_PRESENT(Errno))
832			*Errno = ENOENT;
833
834		return FALSE;
835	}
836
837
838	if((((*Name) | 0x20) >= 'a') && (((*Name) | 0x20) <= 'z') &&
839		(':' == *(Name + 1)) && ('\0' == *(Name + 2)))
840	{
841		Status = _OpenDriveLetter(*Name, ReadOnly, Handle, OpenedReadonly);
842	}
843	else
844	{
845		//
846		// Make name
847		//
848
849		Name = _NormalizeDeviceName(Name, NormalizedDeviceName);
850
851		if(NULL == Name)
852		{
853			//
854			// Set not found
855			//
856
857			if(ARGUMENT_PRESENT(Errno))
858				*Errno = ENOENT;
859
860			return FALSE;
861		}
862
863		//
864		// Try to open it
865		//
866
867		Status = _OpenNtName(Name, ReadOnly, Handle, OpenedReadonly);
868	}
869
870
871	if(!NT_SUCCESS(Status))
872	{
873		if(ARGUMENT_PRESENT(Errno))
874			*Errno = _MapNtStatus(Status);
875
876		return FALSE;
877	}
878
879	return TRUE;
880}
881
882
883//
884// Raw block io. Sets dos errno
885//
886
887static
888BOOLEAN
889_BlockIo(
890    IN HANDLE Handle,
891    IN LARGE_INTEGER Offset,
892    IN ULONG Bytes,
893    IN OUT PCHAR Buffer,
894    IN BOOLEAN Read,
895    OUT unsigned* Errno
896   )
897{
898	IO_STATUS_BLOCK IoStatusBlock;
899	NTSTATUS Status;
900
901	//
902	// Should be aligned
903	//
904
905	ASSERT(0 == (Bytes % 512));
906	ASSERT(0 == (Offset.LowPart % 512));
907
908
909	//
910	// perform io
911	//
912
913	if(Read)
914	{
915		Status = NtReadFile(Handle, NULL, NULL, NULL,
916			&IoStatusBlock, Buffer, Bytes, &Offset, NULL);
917	}
918	else
919	{
920		Status = NtWriteFile(Handle, NULL, NULL, NULL,
921			&IoStatusBlock, Buffer, Bytes, &Offset, NULL);
922	}
923
924
925	//
926	// translate error
927	//
928
929	if(NT_SUCCESS(Status))
930	{
931		*Errno = 0;
932		return TRUE;
933	}
934
935	*Errno = _MapNtStatus(Status);
936
937	return FALSE;
938}
939
940
941
942__inline
943BOOLEAN
944_RawWrite(
945    IN HANDLE Handle,
946    IN LARGE_INTEGER Offset,
947    IN ULONG Bytes,
948    OUT const CHAR* Buffer,
949    OUT unsigned* Errno
950   )
951{
952	return _BlockIo(Handle, Offset, Bytes, (PCHAR)Buffer, FALSE, Errno);
953}
954
955__inline
956BOOLEAN
957_RawRead(
958    IN HANDLE Handle,
959    IN LARGE_INTEGER Offset,
960    IN ULONG Bytes,
961    IN PCHAR Buffer,
962    OUT unsigned* Errno
963   )
964{
965	return _BlockIo(Handle, Offset, Bytes, Buffer, TRUE, Errno);
966}
967
968
969
970__inline
971BOOLEAN
972_SetPartType(
973    IN HANDLE Handle,
974    IN UCHAR Type
975   )
976{
977	IO_STATUS_BLOCK IoStatusBlock;
978	return STATUS_SUCCESS == NtDeviceIoControlFile(
979												   Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_SET_PARTITION_INFO,
980												   &Type, sizeof(Type),
981												   NULL, 0);
982}
983
984
985
986//--------------------- interface part
987
988//
989// Interface functions.
990// Is_mounted is set to 1 if the device is mounted, 0 otherwise
991//
992
993errcode_t
994ext2fs_check_if_mounted(const char *file, int *mount_flags)
995{
996	HANDLE h;
997	BOOLEAN Readonly;
998
999	*mount_flags = 0;
1000
1001	if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
1002	{
1003		return 0;
1004	}
1005
1006
1007	__try{
1008		*mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0;
1009	}
1010	__finally{
1011		_CloseDisk(h);
1012	}
1013
1014	return 0;
1015}
1016
1017
1018
1019//
1020// Returns the number of blocks in a partition
1021//
1022
1023static __int64 FsSize = 0;
1024static char knowndevice[1024] = "";
1025
1026
1027errcode_t
1028ext2fs_get_device_size(const char *file, int blocksize,
1029				 blk_t *retblocks)
1030{
1031	HANDLE h;
1032	BOOLEAN Readonly;
1033
1034	if((0 == FsSize) || (0 != strcmp(knowndevice, file)))
1035	{
1036
1037		if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
1038		{
1039			return 0;
1040		}
1041
1042
1043		__try{
1044
1045			//
1046			// Get size
1047			//
1048
1049			_GetDeviceSize(h, &FsSize);
1050			strcpy(knowndevice, file);
1051		}
1052		__finally{
1053			_CloseDisk(h);
1054		}
1055
1056	}
1057
1058	*retblocks = (blk_t)(unsigned __int64)(FsSize / blocksize);
1059	UNREFERENCED_PARAMETER(file);
1060	return 0;
1061}
1062
1063
1064
1065
1066
1067
1068//
1069// Table elements
1070//
1071
1072
1073static
1074errcode_t
1075nt_open(const char *name, int flags, io_channel *channel)
1076{
1077	io_channel      io = NULL;
1078	PNT_PRIVATE_DATA NtData = NULL;
1079	errcode_t Errno = 0;
1080
1081	//
1082	// Check name
1083	//
1084
1085	if (NULL == name)
1086	{
1087		return EXT2_ET_BAD_DEVICE_NAME;
1088	}
1089
1090	__try{
1091
1092		//
1093		// Allocate channel handle
1094		//
1095
1096		io = (io_channel) malloc(sizeof(struct struct_io_channel));
1097
1098		if (NULL == io)
1099		{
1100			Errno = ENOMEM;
1101			__leave;
1102		}
1103
1104		RtlZeroMemory(io, sizeof(struct struct_io_channel));
1105		io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
1106
1107		NtData = (PNT_PRIVATE_DATA)malloc(sizeof(NT_PRIVATE_DATA));
1108
1109		if (NULL == NtData)
1110		{
1111			Errno = ENOMEM;
1112			__leave;
1113		}
1114
1115
1116		io->manager = nt_io_manager();
1117		io->name = malloc(strlen(name) + 1);
1118		if (NULL == io->name)
1119		{
1120			Errno = ENOMEM;
1121			__leave;
1122		}
1123
1124		strcpy(io->name, name);
1125		io->private_data = NtData;
1126		io->block_size = 1024;
1127		io->read_error = 0;
1128		io->write_error = 0;
1129		io->refcount = 1;
1130
1131		//
1132		// Initialize data
1133		//
1134
1135		RtlZeroMemory(NtData, sizeof(NT_PRIVATE_DATA));
1136
1137		NtData->magic = EXT2_ET_MAGIC_NT_IO_CHANNEL;
1138		NtData->BufferBlockNumber = 0xffffffff;
1139		NtData->BufferSize = 1024;
1140		NtData->Buffer = malloc(NtData->BufferSize);
1141
1142		if (NULL == NtData->Buffer)
1143		{
1144			Errno = ENOMEM;
1145			__leave;
1146		}
1147
1148		//
1149		// Open it
1150		//
1151
1152		if(!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &NtData->Handle, &NtData->OpenedReadonly, &Errno))
1153		{
1154			__leave;
1155		}
1156
1157
1158		//
1159		// get size
1160		//
1161
1162		_GetDeviceSize(NtData->Handle, &FsSize);
1163		strcpy(knowndevice, name);
1164
1165
1166		//
1167		// Lock/dismount
1168		//
1169
1170		if(!NT_SUCCESS(_LockDrive(NtData->Handle)) /*|| !NT_SUCCESS(_DismountDrive(NtData->Handle))*/)
1171		{
1172			NtData->OpenedReadonly = TRUE;
1173		}
1174
1175		//
1176		// Done
1177		//
1178
1179		*channel = io;
1180
1181
1182	}
1183	__finally{
1184
1185		if(0 != Errno)
1186		{
1187			//
1188			// Cleanup
1189			//
1190
1191			if (NULL != io)
1192			{
1193				free(io->name);
1194				free(io);
1195			}
1196
1197			if (NULL != NtData)
1198			{
1199				if(NULL != NtData->Handle)
1200				{
1201					_UnlockDrive(NtData->Handle);
1202					_CloseDisk(NtData->Handle);
1203				}
1204
1205				free(NtData->Buffer);
1206				free(NtData);
1207			}
1208		}
1209	}
1210
1211	return Errno;
1212}
1213
1214
1215//
1216// Close api
1217//
1218
1219static
1220errcode_t
1221nt_close(io_channel channel)
1222{
1223	PNT_PRIVATE_DATA NtData = NULL;
1224
1225	if(NULL == channel)
1226	{
1227		return 0;
1228	}
1229
1230	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1231	NtData = (PNT_PRIVATE_DATA) channel->private_data;
1232	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
1233
1234	if (--channel->refcount > 0)
1235	{
1236		return 0;
1237	}
1238
1239	free(channel->name);
1240	free(channel);
1241
1242	if (NULL != NtData)
1243	{
1244		if(NULL != NtData->Handle)
1245		{
1246			_DismountDrive(NtData->Handle);
1247			_UnlockDrive(NtData->Handle);
1248			_CloseDisk(NtData->Handle);
1249		}
1250
1251		free(NtData->Buffer);
1252		free(NtData);
1253	}
1254
1255	return 0;
1256}
1257
1258
1259
1260//
1261// set block size
1262//
1263
1264static
1265errcode_t
1266nt_set_blksize(io_channel channel, int blksize)
1267{
1268	PNT_PRIVATE_DATA NtData = NULL;
1269
1270	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1271	NtData = (PNT_PRIVATE_DATA) channel->private_data;
1272	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
1273
1274	if (channel->block_size != blksize)
1275	{
1276		channel->block_size = blksize;
1277
1278		free(NtData->Buffer);
1279		NtData->BufferBlockNumber = 0xffffffff;
1280		NtData->BufferSize = channel->block_size;
1281		ASSERT(0 == (NtData->BufferSize % 512));
1282
1283		NtData->Buffer = malloc(NtData->BufferSize);
1284
1285		if (NULL == NtData->Buffer)
1286		{
1287			return ENOMEM;
1288		}
1289
1290	}
1291
1292	return 0;
1293}
1294
1295
1296//
1297// read block
1298//
1299
1300static
1301errcode_t
1302nt_read_blk(io_channel channel, unsigned long block,
1303			       int count, void *buf)
1304{
1305	PVOID BufferToRead;
1306	ULONG SizeToRead;
1307	ULONG Size;
1308	LARGE_INTEGER Offset;
1309	PNT_PRIVATE_DATA NtData = NULL;
1310	unsigned Errno = 0;
1311
1312	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1313	NtData = (PNT_PRIVATE_DATA) channel->private_data;
1314	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
1315
1316	//
1317	// If it's in the cache, use it!
1318	//
1319
1320	if ((1 == count) &&
1321		(block == NtData->BufferBlockNumber) &&
1322		(NtData->BufferBlockNumber != 0xffffffff))
1323	{
1324		memcpy(buf, NtData->Buffer, channel->block_size);
1325		return 0;
1326	}
1327
1328	Size = (count < 0) ? (ULONG)(-count) : (ULONG)(count * channel->block_size);
1329
1330	Offset.QuadPart = block * channel->block_size;
1331
1332	//
1333	// If not fit to the block
1334	//
1335
1336	if(Size <= NtData->BufferSize)
1337	{
1338		//
1339		// Update the cache
1340		//
1341
1342		NtData->BufferBlockNumber = block;
1343		BufferToRead = NtData->Buffer;
1344		SizeToRead = NtData->BufferSize;
1345	}
1346	else
1347	{
1348		SizeToRead = Size;
1349		BufferToRead = buf;
1350		ASSERT(0 == (SizeToRead % channel->block_size));
1351	}
1352
1353	if(!_RawRead(NtData->Handle, Offset, SizeToRead, BufferToRead, &Errno))
1354	{
1355
1356		if (channel->read_error)
1357		{
1358			return (channel->read_error)(channel, block, count, buf,
1359					       Size, 0, Errno);
1360		}
1361		else
1362		{
1363			return Errno;
1364		}
1365	}
1366
1367
1368	if(BufferToRead != buf)
1369	{
1370		ASSERT(Size <= SizeToRead);
1371		memcpy(buf, BufferToRead, Size);
1372	}
1373
1374	return 0;
1375}
1376
1377
1378//
1379// write block
1380//
1381
1382static
1383errcode_t
1384nt_write_blk(io_channel channel, unsigned long block,
1385				int count, const void *buf)
1386{
1387	ULONG SizeToWrite;
1388	LARGE_INTEGER Offset;
1389	PNT_PRIVATE_DATA NtData = NULL;
1390	unsigned Errno = 0;
1391
1392	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1393	NtData = (PNT_PRIVATE_DATA) channel->private_data;
1394	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
1395
1396	if(NtData->OpenedReadonly)
1397	{
1398		return EACCES;
1399	}
1400
1401	if (count == 1)
1402	{
1403		SizeToWrite = channel->block_size;
1404	}
1405	else
1406	{
1407		NtData->BufferBlockNumber = 0xffffffff;
1408
1409		if (count < 0)
1410		{
1411			SizeToWrite = (ULONG)(-count);
1412		}
1413		else
1414		{
1415			SizeToWrite = (ULONG)(count * channel->block_size);
1416		}
1417	}
1418
1419
1420	ASSERT(0 == (SizeToWrite % 512));
1421	Offset.QuadPart = block * channel->block_size;
1422
1423	if(!_RawWrite(NtData->Handle, Offset, SizeToWrite, buf, &Errno))
1424	{
1425		if (channel->write_error)
1426		{
1427			return (channel->write_error)(channel, block, count, buf,
1428						SizeToWrite, 0, Errno);
1429		}
1430		else
1431		{
1432			return Errno;
1433		}
1434	}
1435
1436
1437	//
1438	// Stash a copy.
1439	//
1440
1441	if(SizeToWrite >= NtData->BufferSize)
1442	{
1443		NtData->BufferBlockNumber = block;
1444		memcpy(NtData->Buffer, buf, NtData->BufferSize);
1445	}
1446
1447	NtData->Written = TRUE;
1448
1449	return 0;
1450
1451}
1452
1453
1454
1455//
1456// Flush data buffers to disk.  Since we are currently using a
1457// write-through cache, this is a no-op.
1458//
1459
1460static
1461errcode_t
1462nt_flush(io_channel channel)
1463{
1464	PNT_PRIVATE_DATA NtData = NULL;
1465
1466	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
1467	NtData = (PNT_PRIVATE_DATA) channel->private_data;
1468	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
1469
1470	if(NtData->OpenedReadonly)
1471	{
1472		return 0; // EACCESS;
1473	}
1474
1475
1476	//
1477	// Flush file buffers.
1478	//
1479
1480	_FlushDrive(NtData->Handle);
1481
1482
1483	//
1484	// Test and correct partition type.
1485	//
1486
1487	if(NtData->Written)
1488	{
1489		_SetPartType(NtData->Handle, 0x83);
1490	}
1491
1492	return 0;
1493}
1494
1495
1496