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