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
43 NTSYSAPI
44 ULONG
45 NTAPI
46 RtlNtStatusToDosError(
47 IN NTSTATUS Status
48 );
49
50 NTSYSAPI
51 NTSTATUS
52 NTAPI
53 NtClose(
54 IN HANDLE Handle
55 );
56
57
58 NTSYSAPI
59 NTSTATUS
60 NTAPI
61 NtOpenFile(
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
70 NTSYSAPI
71 NTSTATUS
72 NTAPI
73 NtFlushBuffersFile(
74 IN HANDLE FileHandle,
75 OUT PIO_STATUS_BLOCK IoStatusBlock
76 );
77
78
79 NTSYSAPI
80 NTSTATUS
81 NTAPI
82 NtReadFile(
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
94 NTSYSAPI
95 NTSTATUS
96 NTAPI
97 NtWriteFile(
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
109 NTSYSAPI
110 NTSTATUS
111 NTAPI
112 NtDeviceIoControlFile(
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
125 NTSYSAPI
126 NTSTATUS
127 NTAPI
128 NtFsControlFile(
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
142 NTSYSAPI
143 NTSTATUS
144 NTAPI
145 NtDelayExecution(
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
206 typedef 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
223 static errcode_t nt_open(const char *name, int flags, io_channel *channel);
224 static errcode_t nt_close(io_channel channel);
225 static errcode_t nt_set_blksize(io_channel channel, int blksize);
226 static errcode_t nt_read_blk(io_channel channel, unsigned long block,
227 int count, void *data);
228 static errcode_t nt_write_blk(io_channel channel, unsigned long block,
229 int count, const void *data);
230 static errcode_t nt_flush(io_channel channel);
231
232 static 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
nt_io_manager()247 io_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
260 typedef struct {
261 ULONG WinError;
262 int errnocode;
263 }ERROR_ENTRY;
264
265 static 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
316 static
317 unsigned
_MapDosError(IN ULONG WinError)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
366 static
367 __inline
368 unsigned
_MapNtStatus(IN NTSTATUS Status)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 easier
382 //
383
384 static
385 NTSTATUS
_OpenNtName(IN PCSTR Name,IN BOOLEAN Readonly,OUT PHANDLE Handle,OUT PBOOLEAN OpenedReadonly OPTIONAL)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 input 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; // Unmappable 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
492 static
493 NTSTATUS
_OpenDriveLetter(IN CHAR Letter,IN BOOLEAN ReadOnly,OUT PHANDLE Handle,OUT PBOOLEAN OpenedReadonly OPTIONAL)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
513 static
514 __inline
515 NTSTATUS
_FlushDrive(IN HANDLE Handle)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
529 static
530 __inline
531 NTSTATUS
_LockDrive(IN HANDLE Handle)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
545 static
546 __inline
547 NTSTATUS
_UnlockDrive(IN HANDLE Handle)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
556 static
557 __inline
558 NTSTATUS
_DismountDrive(IN HANDLE Handle)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
572 static
573 __inline
574 BOOLEAN
_IsMounted(IN HANDLE Handle)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
586 static
587 __inline
588 NTSTATUS
_CloseDisk(IN HANDLE Handle)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
603 static
604 PCSTR
_NormalizeDeviceName(IN PCSTR Device,IN PSTR NormalizedDeviceNameBuffer)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 letter 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
745 static
746 VOID
_GetDeviceSize(IN HANDLE h,OUT unsigned __int64 * FsSize)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
811 static
812 BOOLEAN
_Ext2OpenDevice(IN PCSTR Name,IN BOOLEAN ReadOnly,OUT PHANDLE Handle,OUT PBOOLEAN OpenedReadonly OPTIONAL,OUT unsigned * Errno OPTIONAL)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
886 static
887 BOOLEAN
_BlockIo(IN HANDLE Handle,IN LARGE_INTEGER Offset,IN ULONG Bytes,IN OUT PCHAR Buffer,IN BOOLEAN Read,OUT unsigned * Errno)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
942 BOOLEAN
_RawWrite(IN HANDLE Handle,IN LARGE_INTEGER Offset,IN ULONG Bytes,OUT const CHAR * Buffer,OUT unsigned * Errno)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
955 BOOLEAN
_RawRead(IN HANDLE Handle,IN LARGE_INTEGER Offset,IN ULONG Bytes,IN PCHAR Buffer,OUT unsigned * Errno)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
970 BOOLEAN
_SetPartType(IN HANDLE Handle,IN UCHAR Type)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
992 errcode_t
ext2fs_check_if_mounted(const char * file,int * mount_flags)993 ext2fs_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
1022 static __int64 FsSize = 0;
1023 static char knowndevice[1024] = "";
1024
1025
1026 errcode_t
ext2fs_get_device_size(const char * file,int blocksize,blk_t * retblocks)1027 ext2fs_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
1072 static
1073 errcode_t
nt_open(const char * name,int flags,io_channel * channel)1074 nt_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
1218 static
1219 errcode_t
nt_close(io_channel channel)1220 nt_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
1263 static
1264 errcode_t
nt_set_blksize(io_channel channel,int blksize)1265 nt_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
1299 static
1300 errcode_t
nt_read_blk(io_channel channel,unsigned long block,int count,void * buf)1301 nt_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
1381 static
1382 errcode_t
nt_write_blk(io_channel channel,unsigned long block,int count,const void * buf)1383 nt_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
1459 static
1460 errcode_t
nt_flush(io_channel channel)1461 nt_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