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