[httpdisk/bus] Add pre-made devices to the bus
[people/sha0/winvblock.git] / src / httpdisk / httpdisk.c
1 /*
2     HTTP Virtual Disk.
3     Copyright (C) 2006 Bo Brantén.
4     Portions copyright (C) 2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18 #include <ntddk.h>
19 #include <ntdddisk.h>
20 #include <ntddcdrm.h>
21 #include <ntverp.h>
22 #include "ksocket.h"
23
24 //
25 // We include some stuff from newer DDK:s here so that one
26 // version of the driver for all versions of Windows can
27 // be compiled with the Windows NT 4.0 DDK.
28 //
29 #if (VER_PRODUCTBUILD < 2195)
30
31 #define FILE_DEVICE_MASS_STORAGE            0x0000002d
32 #define IOCTL_STORAGE_CHECK_VERIFY2         CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS)
33
34 #endif // (VER_PRODUCTBUILD < 2195)
35
36 #if (VER_PRODUCTBUILD < 2600)
37
38 #define IOCTL_DISK_GET_PARTITION_INFO_EX    CTL_CODE(IOCTL_DISK_BASE, 0x0012, METHOD_BUFFERED, FILE_ANY_ACCESS)
39 #define IOCTL_DISK_GET_LENGTH_INFO          CTL_CODE(IOCTL_DISK_BASE, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS)
40
41 typedef enum _PARTITION_STYLE {
42     PARTITION_STYLE_MBR,
43     PARTITION_STYLE_GPT
44 } PARTITION_STYLE;
45
46 typedef unsigned __int64 ULONG64, *PULONG64;
47
48 typedef struct _PARTITION_INFORMATION_MBR {
49     UCHAR   PartitionType;
50     BOOLEAN BootIndicator;
51     BOOLEAN RecognizedPartition;
52     ULONG   HiddenSectors;
53 } PARTITION_INFORMATION_MBR, *PPARTITION_INFORMATION_MBR;
54
55 typedef struct _PARTITION_INFORMATION_GPT {
56     GUID    PartitionType;
57     GUID    PartitionId;
58     ULONG64 Attributes;
59     WCHAR   Name[36];
60 } PARTITION_INFORMATION_GPT, *PPARTITION_INFORMATION_GPT;
61
62 typedef struct _PARTITION_INFORMATION_EX {
63     PARTITION_STYLE PartitionStyle;
64     LARGE_INTEGER   StartingOffset;
65     LARGE_INTEGER   PartitionLength;
66     ULONG           PartitionNumber;
67     BOOLEAN         RewritePartition;
68     union {
69         PARTITION_INFORMATION_MBR Mbr;
70         PARTITION_INFORMATION_GPT Gpt;
71     };
72 } PARTITION_INFORMATION_EX, *PPARTITION_INFORMATION_EX;
73
74 typedef struct _GET_LENGTH_INFORMATION {
75     LARGE_INTEGER Length;
76 } GET_LENGTH_INFORMATION, *PGET_LENGTH_INFORMATION;
77
78 #endif // (VER_PRODUCTBUILD < 2600)
79
80 //
81 // For backward compatibility with Windows NT 4.0 by Bruce Engle.
82 //
83 #ifndef MmGetSystemAddressForMdlSafe
84 #define MmGetSystemAddressForMdlSafe(MDL, PRIORITY) MmGetSystemAddressForMdlPrettySafe(MDL)
85
86 PVOID
87 MmGetSystemAddressForMdlPrettySafe (
88     PMDL Mdl
89     )
90 {
91     CSHORT  MdlMappingCanFail;
92     PVOID   MappedSystemVa;
93
94     MdlMappingCanFail = Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL;
95
96     Mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
97
98     MappedSystemVa = MmGetSystemAddressForMdl(Mdl);
99
100     if (MdlMappingCanFail == 0)
101     {
102         Mdl->MdlFlags &= ~MDL_MAPPING_CAN_FAIL;
103     }
104
105     return MappedSystemVa;
106 }
107 #endif
108
109 #include "portable.h"
110 #include "winvblock.h"
111 #include "bus.h"
112 #include "httpdisk.h"
113 #include "debug.h"
114
115 /* From bus.c */
116 extern NTSTATUS STDCALL HttpdiskBusEstablish(void);
117 extern VOID HttpdiskBusCleanup(void);
118 extern DRIVER_ADD_DEVICE HttpdiskBusAttach;
119 extern DRIVER_DISPATCH HttpdiskBusIrp;
120
121 /* For this file. */
122 #define PARAMETER_KEY           L"\\Parameters"
123
124 #define NUMBEROFDEVICES_VALUE   L"NumberOfDevices"
125
126 #define DEFAULT_NUMBEROFDEVICES 4
127
128 #define SECTOR_SIZE             512
129
130 #define TOC_DATA_TRACK          0x04
131
132 #define BUFFER_SIZE             (4096 * 4)
133
134 HANDLE dir_handle;
135 PDRIVER_OBJECT HttpdiskDriverObj = NULL;
136
137 typedef struct _HTTP_HEADER {
138     LARGE_INTEGER ContentLength;
139 } HTTP_HEADER, *PHTTP_HEADER;
140
141 NTSTATUS
142 DriverEntry (
143     IN PDRIVER_OBJECT   DriverObject,
144     IN PUNICODE_STRING  RegistryPath
145 );
146
147 NTSTATUS
148 HttpDiskCreateDevice (
149     IN PDRIVER_OBJECT   DriverObject,
150     IN ULONG            Number,
151     IN DEVICE_TYPE      DeviceType
152 );
153
154 VOID
155 HttpDiskUnload (
156     IN PDRIVER_OBJECT   DriverObject
157 );
158
159 PDEVICE_OBJECT
160 HttpDiskDeleteDevice (
161     IN PDEVICE_OBJECT   DeviceObject
162 );
163
164 static
165   __drv_dispatchType(IRP_MJ_CREATE)
166   __drv_dispatchType(IRP_MJ_CLOSE)
167   DRIVER_DISPATCH HttpdiskIrpCreateClose_;
168
169 static
170   __drv_dispatchType(IRP_MJ_READ)
171   __drv_dispatchType(IRP_MJ_WRITE)
172   DRIVER_DISPATCH HttpdiskIrpReadWrite_;
173
174 static
175   __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
176   DRIVER_DISPATCH HttpdiskIrpDevCtl_;
177
178 VOID
179 HttpDiskThread (
180     IN PVOID            Context
181 );
182
183 NTSTATUS
184 HttpDiskConnect (
185     IN PDEVICE_OBJECT   DeviceObject,
186     IN PIRP             Irp
187 );
188
189 NTSTATUS
190 HttpDiskDisconnect (
191     IN PDEVICE_OBJECT   DeviceObject,
192     IN PIRP             Irp
193 );
194
195 NTSTATUS
196 HttpDiskGetHeader (
197     IN ULONG                Address,
198     IN USHORT               Port,
199     IN PUCHAR               HostName,
200     IN PUCHAR               FileName,
201     OUT PIO_STATUS_BLOCK    IoStatus,
202     OUT PHTTP_HEADER        HttpHeader
203 );
204
205 NTSTATUS
206 HttpDiskGetBlock (
207     IN int                  *Socket,
208     IN ULONG                Address,
209     IN USHORT               Port,
210     IN PUCHAR               HostName,
211     IN PUCHAR               FileName,
212     IN PLARGE_INTEGER       Offset,
213     IN ULONG                Length,
214     OUT PIO_STATUS_BLOCK    IoStatus,
215     OUT PVOID               SystemBuffer
216 );
217
218 __int64 __cdecl _atoi64(const char *);
219 int __cdecl _snprintf(char *, size_t, const char *, ...);
220 int __cdecl swprintf(wchar_t *, const wchar_t *, ...);
221
222 /** Memory allocation functions. */
223 PVOID HttpDiskMalloc(SIZE_T size) {
224     return ExAllocatePoolWithTag(NonPagedPool, size, 'DHvW');
225   }
226
227 PVOID HttpDiskPalloc(SIZE_T size) {
228     /*
229      * The call-points for this function merely place-hold for where
230      * Bo's original work allocated from paged pool.  Since this
231      * version is intended for booting, we don't use paged pool.
232      */
233     return ExAllocatePoolWithTag(NonPagedPool, size, 'DHvW');
234   }
235
236 #pragma code_seg("INIT")
237
238 NTSTATUS
239 DriverEntry (
240     IN PDRIVER_OBJECT   DriverObject,
241     IN PUNICODE_STRING  RegistryPath
242     )
243 {
244     UNICODE_STRING              parameter_path;
245     RTL_QUERY_REGISTRY_TABLE    query_table[2];
246     ULONG                       n_devices;
247     NTSTATUS                    status;
248     UNICODE_STRING              device_dir_name;
249     OBJECT_ATTRIBUTES           object_attributes;
250     ULONG                       n;
251     USHORT                      n_created_devices;
252
253     HttpdiskDriverObj = DriverObject;
254
255     parameter_path.Length = 0;
256
257     parameter_path.MaximumLength = RegistryPath->Length + sizeof(PARAMETER_KEY);
258
259     parameter_path.Buffer = HttpDiskPalloc(parameter_path.MaximumLength);
260
261     if (parameter_path.Buffer == NULL)
262     {
263         return STATUS_INSUFFICIENT_RESOURCES;
264     }
265
266     RtlCopyUnicodeString(&parameter_path, RegistryPath);
267
268     RtlAppendUnicodeToString(&parameter_path, PARAMETER_KEY);
269
270     RtlZeroMemory(&query_table[0], sizeof(query_table));
271
272     query_table[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
273     query_table[0].Name = NUMBEROFDEVICES_VALUE;
274     query_table[0].EntryContext = &n_devices;
275
276     status = RtlQueryRegistryValues(
277         RTL_REGISTRY_ABSOLUTE,
278         parameter_path.Buffer,
279         &query_table[0],
280         NULL,
281         NULL
282         );
283
284     ExFreePool(parameter_path.Buffer);
285
286     if (!NT_SUCCESS(status))
287     {
288         DbgPrint("HttpDisk: Query registry failed, using default values.\n");
289         n_devices = DEFAULT_NUMBEROFDEVICES;
290     }
291
292     RtlInitUnicodeString(&device_dir_name, DEVICE_DIR_NAME);
293
294     InitializeObjectAttributes(
295         &object_attributes,
296         &device_dir_name,
297         OBJ_PERMANENT,
298         NULL,
299         NULL
300         );
301
302     status = ZwCreateDirectoryObject(
303         &dir_handle,
304         DIRECTORY_ALL_ACCESS,
305         &object_attributes
306         );
307
308     if (!NT_SUCCESS(status))
309     {
310         return status;
311     }
312
313     ZwMakeTemporaryObject(dir_handle);
314
315     for (n = 0, n_created_devices = 0; n < n_devices; n++)
316     {
317         status = HttpDiskCreateDevice(DriverObject, n, FILE_DEVICE_DISK);
318
319         if (NT_SUCCESS(status))
320         {
321             n_created_devices++;
322         }
323     }
324
325     for (n = 0; n < n_devices; n++)
326     {
327         status = HttpDiskCreateDevice(DriverObject, n, FILE_DEVICE_CD_ROM);
328
329         if (NT_SUCCESS(status))
330         {
331             n_created_devices++;
332         }
333     }
334
335     if (n_created_devices == 0)
336     {
337         ZwClose(dir_handle);
338         return status;
339     }
340
341     DriverObject->MajorFunction[IRP_MJ_PNP] = HttpdiskBusIrp;
342     DriverObject->MajorFunction[IRP_MJ_POWER] = HttpdiskBusIrp;
343     DriverObject->MajorFunction[IRP_MJ_CREATE] = HttpdiskIrpCreateClose_;
344     DriverObject->MajorFunction[IRP_MJ_CLOSE] = HttpdiskIrpCreateClose_;
345     DriverObject->MajorFunction[IRP_MJ_READ] = HttpdiskIrpReadWrite_;
346     DriverObject->MajorFunction[IRP_MJ_WRITE] = HttpdiskIrpReadWrite_;
347     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HttpdiskIrpDevCtl_;
348     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HttpdiskBusIrp;
349
350     DriverObject->DriverUnload = HttpDiskUnload;
351     DriverObject->DriverExtension->AddDevice = HttpdiskBusAttach;
352
353     status = HttpdiskBusEstablish();
354     if (!NT_SUCCESS(status))
355       HttpDiskUnload(DriverObject);
356
357     DBG("Success.\n");
358     return status;
359 }
360
361 NTSTATUS
362 HttpDiskCreateDevice (
363     IN PDRIVER_OBJECT   DriverObject,
364     IN ULONG            Number,
365     IN DEVICE_TYPE      DeviceType
366     )
367 {
368     WCHAR               device_name_buffer[MAXIMUM_FILENAME_LENGTH];
369     UNICODE_STRING      device_name;
370     NTSTATUS            status;
371     PDEVICE_OBJECT      device_object;
372     HTTPDISK_SP_DEV   device_extension;
373     HANDLE              thread_handle;
374
375     ASSERT(DriverObject != NULL);
376
377     if (DeviceType == FILE_DEVICE_CD_ROM)
378     {
379         swprintf(
380             device_name_buffer,
381             DEVICE_NAME_PREFIX L"Cd" L"%u",
382             Number
383             );
384     }
385     else
386     {
387         swprintf(
388             device_name_buffer,
389             DEVICE_NAME_PREFIX L"Disk" L"%u",
390             Number
391             );
392     }
393
394     RtlInitUnicodeString(&device_name, device_name_buffer);
395
396     status = IoCreateDevice(
397         DriverObject,
398         sizeof (HTTPDISK_S_DEV),
399         &device_name,
400         DeviceType,
401         0,
402         FALSE,
403         &device_object
404         );
405
406     if (!NT_SUCCESS(status))
407     {
408         return status;
409     }
410
411     device_object->Flags |= DO_DIRECT_IO;
412
413     device_extension = (HTTPDISK_SP_DEV) device_object->DeviceExtension;
414
415     device_extension->media_in_device = FALSE;
416
417     device_extension->host_name = NULL;
418
419     device_extension->file_name = NULL;
420
421     device_extension->socket = -1;
422
423     device_object->Characteristics |= FILE_READ_ONLY_DEVICE;
424
425     InitializeListHead(&device_extension->list_head);
426
427     KeInitializeSpinLock(&device_extension->list_lock);
428
429     KeInitializeEvent(
430         &device_extension->request_event,
431         SynchronizationEvent,
432         FALSE
433         );
434
435     device_extension->terminate_thread = FALSE;
436
437     device_extension->bus = FALSE;
438
439     status = PsCreateSystemThread(
440         &thread_handle,
441         (ACCESS_MASK) 0L,
442         NULL,
443         NULL,
444         NULL,
445         HttpDiskThread,
446         device_object
447         );
448
449     if (!NT_SUCCESS(status))
450     {
451         IoDeleteDevice(device_object);
452         return status;
453     }
454
455     status = ObReferenceObjectByHandle(
456         thread_handle,
457         THREAD_ALL_ACCESS,
458         NULL,
459         KernelMode,
460         &device_extension->thread_pointer,
461         NULL
462         );
463
464     if (!NT_SUCCESS(status))
465     {
466         ZwClose(thread_handle);
467
468         device_extension->terminate_thread = TRUE;
469
470         KeSetEvent(
471             &device_extension->request_event,
472             (KPRIORITY) 0,
473             FALSE
474             );
475
476         IoDeleteDevice(device_object);
477
478         return status;
479     }
480
481     ZwClose(thread_handle);
482
483     return STATUS_SUCCESS;
484 }
485
486 #pragma code_seg("PAGE")
487
488 VOID
489 HttpDiskUnload (
490     IN PDRIVER_OBJECT DriverObject
491     )
492 {
493     PDEVICE_OBJECT device_object;
494
495     HttpdiskBusCleanup();
496
497     device_object = DriverObject->DeviceObject;
498
499     while (device_object)
500     {
501         device_object = HttpDiskDeleteDevice(device_object);
502     }
503
504     ZwClose(dir_handle);
505 }
506
507 PDEVICE_OBJECT
508 HttpDiskDeleteDevice (
509     IN PDEVICE_OBJECT DeviceObject
510     )
511 {
512     HTTPDISK_SP_DEV   device_extension;
513     PDEVICE_OBJECT      next_device_object;
514
515     ASSERT(DeviceObject != NULL);
516
517     device_extension = (HTTPDISK_SP_DEV) DeviceObject->DeviceExtension;
518
519     device_extension->terminate_thread = TRUE;
520
521     KeSetEvent(
522         &device_extension->request_event,
523         (KPRIORITY) 0,
524         FALSE
525         );
526
527     KeWaitForSingleObject(
528         device_extension->thread_pointer,
529         Executive,
530         KernelMode,
531         FALSE,
532         NULL
533         );
534
535     ObDereferenceObject(device_extension->thread_pointer);
536
537     next_device_object = DeviceObject->NextDevice;
538
539     IoDeleteDevice(DeviceObject);
540
541     return next_device_object;
542 }
543
544 static NTSTATUS HttpdiskIrpCreateClose_(
545     IN PDEVICE_OBJECT   DeviceObject,
546     IN PIRP             Irp
547   ) {
548     Irp->IoStatus.Status = STATUS_SUCCESS;
549     Irp->IoStatus.Information = FILE_OPENED;
550
551     IoCompleteRequest(Irp, IO_NO_INCREMENT);
552
553     return STATUS_SUCCESS;
554   }
555
556 #pragma code_seg()
557
558 static NTSTATUS HttpdiskIrpReadWrite_(
559     IN PDEVICE_OBJECT   DeviceObject,
560     IN PIRP             Irp
561   ) {
562     HTTPDISK_SP_DEV   device_extension;
563     PIO_STACK_LOCATION  io_stack;
564
565     device_extension = (HTTPDISK_SP_DEV) DeviceObject->DeviceExtension;
566
567     /* Check for a bus IRP. */
568     if (device_extension->bus)
569       return HttpdiskBusIrp(DeviceObject, Irp);
570
571     if (!device_extension->media_in_device)
572     {
573         Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
574         Irp->IoStatus.Information = 0;
575
576         IoCompleteRequest(Irp, IO_NO_INCREMENT);
577
578         return STATUS_NO_MEDIA_IN_DEVICE;
579     }
580
581     io_stack = IoGetCurrentIrpStackLocation(Irp);
582
583     if (io_stack->Parameters.Read.Length == 0)
584     {
585         Irp->IoStatus.Status = STATUS_SUCCESS;
586         Irp->IoStatus.Information = 0;
587
588         IoCompleteRequest(Irp, IO_NO_INCREMENT);
589
590         return STATUS_SUCCESS;
591     }
592
593     IoMarkIrpPending(Irp);
594
595     ExInterlockedInsertTailList(
596         &device_extension->list_head,
597         &Irp->Tail.Overlay.ListEntry,
598         &device_extension->list_lock
599         );
600
601     KeSetEvent(
602         &device_extension->request_event,
603         (KPRIORITY) 0,
604         FALSE
605         );
606
607     return STATUS_PENDING;
608   }
609
610 static NTSTATUS HttpdiskIrpDevCtl_(
611     IN PDEVICE_OBJECT   DeviceObject,
612     IN PIRP             Irp
613   ) {
614     HTTPDISK_SP_DEV   device_extension;
615     PIO_STACK_LOCATION  io_stack;
616     NTSTATUS            status;
617
618     device_extension = (HTTPDISK_SP_DEV) DeviceObject->DeviceExtension;
619
620     /* Check for a bus IRP. */
621     if (device_extension->bus)
622       return HttpdiskBusIrp(DeviceObject, Irp);
623
624     io_stack = IoGetCurrentIrpStackLocation(Irp);
625
626     if (!device_extension->media_in_device &&
627         io_stack->Parameters.DeviceIoControl.IoControlCode !=
628         IOCTL_HTTP_DISK_CONNECT)
629     {
630         Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
631         Irp->IoStatus.Information = 0;
632
633         IoCompleteRequest(Irp, IO_NO_INCREMENT);
634
635         return STATUS_NO_MEDIA_IN_DEVICE;
636     }
637
638     switch (io_stack->Parameters.DeviceIoControl.IoControlCode)
639     {
640     case IOCTL_HTTP_DISK_CONNECT:
641         {
642             if (device_extension->media_in_device)
643             {
644                 DbgPrint("HttpDisk: IOCTL_HTTP_DISK_CONNECT: Media already opened.\n");
645
646                 status = STATUS_INVALID_DEVICE_REQUEST;
647                 Irp->IoStatus.Information = 0;
648                 break;
649             }
650
651             if (io_stack->Parameters.DeviceIoControl.InputBufferLength <
652                 sizeof(HTTP_DISK_INFORMATION))
653             {
654                 status = STATUS_INVALID_PARAMETER;
655                 Irp->IoStatus.Information = 0;
656                 break;
657             }
658
659             if (io_stack->Parameters.DeviceIoControl.InputBufferLength <
660                 sizeof(HTTP_DISK_INFORMATION) +
661                 ((PHTTP_DISK_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->FileNameLength -
662                 sizeof(UCHAR))
663             {
664                 status = STATUS_INVALID_PARAMETER;
665                 Irp->IoStatus.Information = 0;
666                 break;
667             }
668
669             IoMarkIrpPending(Irp);
670
671             ExInterlockedInsertTailList(
672                 &device_extension->list_head,
673                 &Irp->Tail.Overlay.ListEntry,
674                 &device_extension->list_lock
675                 );
676
677             KeSetEvent(
678                 &device_extension->request_event,
679                 (KPRIORITY) 0,
680                 FALSE
681                 );
682
683             status = STATUS_PENDING;
684
685             break;
686         }
687
688     case IOCTL_HTTP_DISK_DISCONNECT:
689         {
690             IoMarkIrpPending(Irp);
691
692             ExInterlockedInsertTailList(
693                 &device_extension->list_head,
694                 &Irp->Tail.Overlay.ListEntry,
695                 &device_extension->list_lock
696                 );
697
698             KeSetEvent(
699                 &device_extension->request_event,
700                 (KPRIORITY) 0,
701                 FALSE
702                 );
703
704             status = STATUS_PENDING;
705
706             break;
707         }
708
709     case IOCTL_DISK_CHECK_VERIFY:
710     case IOCTL_CDROM_CHECK_VERIFY:
711     case IOCTL_STORAGE_CHECK_VERIFY:
712     case IOCTL_STORAGE_CHECK_VERIFY2:
713         {
714             status = STATUS_SUCCESS;
715             Irp->IoStatus.Information = 0;
716             break;
717         }
718
719     case IOCTL_DISK_GET_DRIVE_GEOMETRY:
720     case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
721         {
722             PDISK_GEOMETRY  disk_geometry;
723             ULONGLONG       length;
724
725             if (io_stack->Parameters.DeviceIoControl.OutputBufferLength <
726                 sizeof(DISK_GEOMETRY))
727             {
728                 status = STATUS_BUFFER_TOO_SMALL;
729                 Irp->IoStatus.Information = 0;
730                 break;
731             }
732
733             disk_geometry = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
734
735             length = device_extension->file_size.QuadPart;
736
737             disk_geometry->Cylinders.QuadPart = length / SECTOR_SIZE / 32 / 2;
738             disk_geometry->MediaType = FixedMedia;
739             disk_geometry->TracksPerCylinder = 2;
740             disk_geometry->SectorsPerTrack = 32;
741             disk_geometry->BytesPerSector = SECTOR_SIZE;
742
743             status = STATUS_SUCCESS;
744             Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
745
746             break;
747         }
748
749     case IOCTL_DISK_GET_LENGTH_INFO:
750         {
751             PGET_LENGTH_INFORMATION get_length_information;
752
753             if (io_stack->Parameters.DeviceIoControl.OutputBufferLength <
754                 sizeof(GET_LENGTH_INFORMATION))
755             {
756                 status = STATUS_BUFFER_TOO_SMALL;
757                 Irp->IoStatus.Information = 0;
758                 break;
759             }
760
761             get_length_information = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
762
763             get_length_information->Length.QuadPart = device_extension->file_size.QuadPart;
764
765             status = STATUS_SUCCESS;
766             Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
767
768         break;
769         }
770
771     case IOCTL_DISK_GET_PARTITION_INFO:
772         {
773             PPARTITION_INFORMATION  partition_information;
774             ULONGLONG               length;
775
776             if (io_stack->Parameters.DeviceIoControl.OutputBufferLength <
777                 sizeof(PARTITION_INFORMATION))
778             {
779                 status = STATUS_BUFFER_TOO_SMALL;
780                 Irp->IoStatus.Information = 0;
781                 break;
782             }
783
784             partition_information = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
785
786             length = device_extension->file_size.QuadPart;
787
788             partition_information->StartingOffset.QuadPart = 0;
789             partition_information->PartitionLength.QuadPart = length;
790             partition_information->HiddenSectors = 1;
791             partition_information->PartitionNumber = 0;
792             partition_information->PartitionType = 0;
793             partition_information->BootIndicator = FALSE;
794             partition_information->RecognizedPartition = FALSE;
795             partition_information->RewritePartition = FALSE;
796
797             status = STATUS_SUCCESS;
798             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
799
800             break;
801         }
802
803     case IOCTL_DISK_GET_PARTITION_INFO_EX:
804         {
805             PPARTITION_INFORMATION_EX   partition_information_ex;
806             ULONGLONG                   length;
807
808             if (io_stack->Parameters.DeviceIoControl.OutputBufferLength <
809                 sizeof(PARTITION_INFORMATION_EX))
810             {
811                 status = STATUS_BUFFER_TOO_SMALL;
812                 Irp->IoStatus.Information = 0;
813                 break;
814             }
815
816             partition_information_ex = (PPARTITION_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer;
817
818             length = device_extension->file_size.QuadPart;
819
820             partition_information_ex->PartitionStyle = PARTITION_STYLE_MBR;
821             partition_information_ex->StartingOffset.QuadPart = 0;
822             partition_information_ex->PartitionLength.QuadPart = length;
823             partition_information_ex->PartitionNumber = 0;
824             partition_information_ex->RewritePartition = FALSE;
825             partition_information_ex->Mbr.PartitionType = 0;
826             partition_information_ex->Mbr.BootIndicator = FALSE;
827             partition_information_ex->Mbr.RecognizedPartition = FALSE;
828             partition_information_ex->Mbr.HiddenSectors = 1;
829
830             status = STATUS_SUCCESS;
831             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
832
833             break;
834         }
835
836     case IOCTL_DISK_IS_WRITABLE:
837         {
838             status = STATUS_MEDIA_WRITE_PROTECTED;
839             Irp->IoStatus.Information = 0;
840             break;
841         }
842
843     case IOCTL_DISK_MEDIA_REMOVAL:
844     case IOCTL_STORAGE_MEDIA_REMOVAL:
845         {
846             status = STATUS_SUCCESS;
847             Irp->IoStatus.Information = 0;
848             break;
849         }
850
851     case IOCTL_CDROM_READ_TOC:
852         {
853             PCDROM_TOC cdrom_toc;
854
855             if (io_stack->Parameters.DeviceIoControl.OutputBufferLength <
856                 sizeof(CDROM_TOC))
857             {
858                 status = STATUS_BUFFER_TOO_SMALL;
859                 Irp->IoStatus.Information = 0;
860                 break;
861             }
862
863             cdrom_toc = (PCDROM_TOC) Irp->AssociatedIrp.SystemBuffer;
864
865             RtlZeroMemory(cdrom_toc, sizeof(CDROM_TOC));
866
867             cdrom_toc->FirstTrack = 1;
868             cdrom_toc->LastTrack = 1;
869             cdrom_toc->TrackData[0].Control = TOC_DATA_TRACK;
870
871             status = STATUS_SUCCESS;
872             Irp->IoStatus.Information = sizeof(CDROM_TOC);
873
874             break;
875         }
876
877     case IOCTL_DISK_SET_PARTITION_INFO:
878         {
879             status = STATUS_MEDIA_WRITE_PROTECTED;
880             Irp->IoStatus.Information = 0;
881             break;
882         }
883
884     case IOCTL_DISK_VERIFY:
885         {
886             PVERIFY_INFORMATION verify_information;
887
888             if (io_stack->Parameters.DeviceIoControl.InputBufferLength <
889                 sizeof(VERIFY_INFORMATION))
890             {
891                 status = STATUS_INVALID_PARAMETER;
892                 Irp->IoStatus.Information = 0;
893                 break;
894             }
895
896             verify_information = (PVERIFY_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
897
898             status = STATUS_SUCCESS;
899             Irp->IoStatus.Information = verify_information->Length;
900
901             break;
902         }
903
904     default:
905         {
906             KdPrint((
907                 "HttpDisk: Unknown IoControlCode: %#x\n",
908                 io_stack->Parameters.DeviceIoControl.IoControlCode
909                 ));
910
911             status = STATUS_INVALID_DEVICE_REQUEST;
912             Irp->IoStatus.Information = 0;
913         }
914     }
915
916     if (status != STATUS_PENDING)
917     {
918         Irp->IoStatus.Status = status;
919
920         IoCompleteRequest(Irp, IO_NO_INCREMENT);
921     }
922
923     return status;
924   }
925
926 #pragma code_seg("PAGE")
927
928 VOID
929 HttpDiskThread (
930     IN PVOID Context
931     )
932 {
933     PDEVICE_OBJECT      device_object;
934     HTTPDISK_SP_DEV   device_extension;
935     PLIST_ENTRY         request;
936     PIRP                irp;
937     PIO_STACK_LOCATION  io_stack;
938
939     ASSERT(Context != NULL);
940
941     device_object = (PDEVICE_OBJECT) Context;
942
943     device_extension = (HTTPDISK_SP_DEV) device_object->DeviceExtension;
944
945     KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
946
947     for (;;)
948     {
949         KeWaitForSingleObject(
950             &device_extension->request_event,
951             Executive,
952             KernelMode,
953             FALSE,
954             NULL
955             );
956
957         if (device_extension->terminate_thread)
958         {
959             PsTerminateSystemThread(STATUS_SUCCESS);
960         }
961
962         while (request = ExInterlockedRemoveHeadList(
963             &device_extension->list_head,
964             &device_extension->list_lock
965             ))
966         {
967             irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);
968
969             io_stack = IoGetCurrentIrpStackLocation(irp);
970
971             switch (io_stack->MajorFunction)
972             {
973             case IRP_MJ_READ:
974                 HttpDiskGetBlock(
975                     &device_extension->socket,
976                     device_extension->address,
977                     device_extension->port,
978                     device_extension->host_name,
979                     device_extension->file_name,
980                     &io_stack->Parameters.Read.ByteOffset,
981                     io_stack->Parameters.Read.Length,
982                     &irp->IoStatus,
983                     MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority)
984                     );
985                 if (!NT_SUCCESS(irp->IoStatus.Status))
986                 {
987                     HttpDiskGetBlock(
988                         &device_extension->socket,
989                         device_extension->address,
990                         device_extension->port,
991                         device_extension->host_name,
992                         device_extension->file_name,
993                         &io_stack->Parameters.Read.ByteOffset,
994                         io_stack->Parameters.Read.Length,
995                         &irp->IoStatus,
996                         MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority)
997                         );
998                 }
999                 break;
1000
1001             case IRP_MJ_WRITE:
1002                 irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED;
1003                 irp->IoStatus.Information = 0;
1004                 break;
1005
1006             case IRP_MJ_DEVICE_CONTROL:
1007                 switch (io_stack->Parameters.DeviceIoControl.IoControlCode)
1008                 {
1009                 case IOCTL_HTTP_DISK_CONNECT:
1010                     irp->IoStatus.Status = HttpDiskConnect(device_object, irp);
1011                     break;
1012
1013                 case IOCTL_HTTP_DISK_DISCONNECT:
1014                     irp->IoStatus.Status = HttpDiskDisconnect(device_object, irp);
1015                     break;
1016
1017                 default:
1018                     irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
1019                 }
1020                 break;
1021
1022             default:
1023                 irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
1024             }
1025
1026             IoCompleteRequest(
1027                 irp,
1028                 (CCHAR) (NT_SUCCESS(irp->IoStatus.Status) ?
1029                 IO_DISK_INCREMENT : IO_NO_INCREMENT)
1030                 );
1031         }
1032     }
1033 }
1034
1035 NTSTATUS
1036 HttpDiskConnect (
1037     IN PDEVICE_OBJECT   DeviceObject,
1038     IN PIRP             Irp
1039     )
1040 {
1041     HTTPDISK_SP_DEV       device_extension;
1042     PHTTP_DISK_INFORMATION  http_disk_information;
1043     HTTP_HEADER             http_header;
1044
1045     ASSERT(DeviceObject != NULL);
1046     ASSERT(Irp != NULL);
1047
1048     device_extension = (HTTPDISK_SP_DEV) DeviceObject->DeviceExtension;
1049
1050     http_disk_information = (PHTTP_DISK_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
1051
1052     device_extension->address = http_disk_information->Address;
1053
1054     device_extension->port = http_disk_information->Port;
1055
1056     device_extension->host_name = HttpDiskMalloc(
1057         http_disk_information->HostNameLength + 1
1058       );
1059
1060     if (device_extension->host_name == NULL)
1061     {
1062         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1063         return Irp->IoStatus.Status;
1064     }
1065
1066     RtlCopyMemory(
1067         device_extension->host_name,
1068         http_disk_information->HostName,
1069         http_disk_information->HostNameLength
1070         );
1071
1072     device_extension->host_name[http_disk_information->HostNameLength] = '\0';
1073
1074     device_extension->file_name = HttpDiskMalloc(
1075         http_disk_information->FileNameLength + 1
1076       );
1077
1078     if (device_extension->file_name == NULL)
1079     {
1080         if (device_extension->host_name != NULL)
1081         {
1082             ExFreePool(device_extension->host_name);
1083             device_extension->host_name = NULL;
1084         }
1085
1086         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1087         return Irp->IoStatus.Status;
1088     }
1089
1090     RtlCopyMemory(
1091         device_extension->file_name,
1092         http_disk_information->FileName,
1093         http_disk_information->FileNameLength
1094         );
1095
1096     device_extension->file_name[http_disk_information->FileNameLength] = '\0';
1097
1098     HttpDiskGetHeader(
1099         device_extension->address,
1100         device_extension->port,
1101         device_extension->host_name,
1102         device_extension->file_name,
1103         &Irp->IoStatus,
1104         &http_header
1105         );
1106
1107     if (!NT_SUCCESS(Irp->IoStatus.Status))
1108     {
1109         HttpDiskGetHeader(
1110             device_extension->address,
1111             device_extension->port,
1112             device_extension->host_name,
1113             device_extension->file_name,
1114             &Irp->IoStatus,
1115             &http_header
1116             );
1117     }
1118
1119     if (!NT_SUCCESS(Irp->IoStatus.Status))
1120     {
1121         if (device_extension->host_name != NULL)
1122         {
1123             ExFreePool(device_extension->host_name);
1124             device_extension->host_name = NULL;
1125         }
1126
1127         if (device_extension->file_name != NULL)
1128         {
1129             ExFreePool(device_extension->file_name);
1130             device_extension->file_name = NULL;
1131         }
1132
1133         return Irp->IoStatus.Status;
1134     }
1135
1136     device_extension->file_size.QuadPart = http_header.ContentLength.QuadPart;
1137
1138     device_extension->media_in_device = TRUE;
1139
1140     return Irp->IoStatus.Status;
1141 }
1142
1143 NTSTATUS
1144 HttpDiskDisconnect (
1145     IN PDEVICE_OBJECT   DeviceObject,
1146     IN PIRP             Irp
1147     )
1148 {
1149     HTTPDISK_SP_DEV device_extension;
1150
1151     ASSERT(DeviceObject != NULL);
1152     ASSERT(Irp != NULL);
1153
1154     device_extension = (HTTPDISK_SP_DEV) DeviceObject->DeviceExtension;
1155
1156     device_extension->media_in_device = FALSE;
1157
1158     if (device_extension->host_name != NULL)
1159     {
1160         ExFreePool(device_extension->host_name);
1161         device_extension->host_name = NULL;
1162     }
1163
1164     if (device_extension->file_name != NULL)
1165     {
1166         ExFreePool(device_extension->file_name);
1167         device_extension->file_name = NULL;
1168     }
1169
1170     if (device_extension->socket > 0)
1171     {
1172         close(device_extension->socket);
1173         device_extension->socket = -1;
1174     }
1175
1176     Irp->IoStatus.Status = STATUS_SUCCESS;
1177     Irp->IoStatus.Information = 0;
1178
1179     return STATUS_SUCCESS;
1180 }
1181
1182 NTSTATUS
1183 HttpDiskGetHeader (
1184     IN ULONG                Address,
1185     IN USHORT               Port,
1186     IN PUCHAR               HostName,
1187     IN PUCHAR               FileName,
1188     OUT PIO_STATUS_BLOCK    IoStatus,
1189     OUT PHTTP_HEADER        HttpHeader
1190     )
1191 {
1192     int                 kSocket;
1193     struct sockaddr_in  toAddr;
1194     int                 status, nSent, nRecv;
1195     char                *buffer, *pStr;
1196
1197     ASSERT(HostName != NULL);
1198     ASSERT(FileName != NULL);
1199     ASSERT(IoStatus != NULL);
1200     ASSERT(HttpHeader != NULL);
1201
1202     buffer = HttpDiskPalloc(BUFFER_SIZE);
1203
1204     if (buffer == NULL)
1205     {
1206         IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
1207         return IoStatus->Status;
1208     }
1209
1210     kSocket = socket(AF_INET, SOCK_STREAM, 0);
1211
1212     if (kSocket < 0)
1213     {
1214         DbgPrint("HttpDisk: socket() error: %#x\n", kSocket);
1215         ExFreePool(buffer);
1216         IoStatus->Status = kSocket;
1217         return IoStatus->Status;
1218     }
1219
1220     toAddr.sin_family = AF_INET;
1221     toAddr.sin_port = Port;
1222     toAddr.sin_addr.s_addr = Address;
1223
1224     status = connect(kSocket, (struct sockaddr*) &toAddr, sizeof(toAddr));
1225
1226     if (status < 0)
1227     {
1228         DbgPrint("HttpDisk: connect() error: %#x\n", status);
1229         ExFreePool(buffer);
1230         close(kSocket);
1231         IoStatus->Status = status;
1232         return IoStatus->Status;
1233     }
1234
1235     // Example request:
1236     //  HEAD 'FileName' HTTP/1.1
1237     //  Host: 'HostName'
1238     //  Accept: */*
1239     //  User-Agent: HttpDisk/1.2
1240     //  Connection: close
1241     //
1242     // Interesting lines in answer:
1243     //  HTTP/1.1 200 OK
1244     //  Accept-Ranges: bytes
1245     //  Content-Length: 'total file size'
1246
1247     _snprintf(
1248         buffer,
1249         BUFFER_SIZE,
1250         "HEAD %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nUser-Agent: HttpDisk/1.2\r\nConnection: close\r\n\r\n",
1251         FileName,
1252         HostName
1253         );
1254
1255     nSent = send(kSocket, buffer, strlen(buffer), 0);
1256
1257     if (nSent < 0)
1258     {
1259         DbgPrint("HttpDisk: send() error: %#x\n", nSent);
1260         ExFreePool(buffer);
1261         close(kSocket);
1262         IoStatus->Status = nSent;
1263         return IoStatus->Status;
1264     }
1265
1266     nRecv = recv(kSocket, buffer, BUFFER_SIZE, 0);
1267
1268     if (nRecv < 0)
1269     {
1270         DbgPrint("HttpDisk: recv() error: %#x\n", nRecv);
1271         ExFreePool(buffer);
1272         close(kSocket);
1273         IoStatus->Status = nRecv;
1274         return IoStatus->Status;
1275     }
1276
1277     close(kSocket);
1278
1279     buffer[BUFFER_SIZE - 1] = '\0';
1280
1281     if (_strnicmp(buffer, "HTTP/1.1 200 OK", 15))
1282     {
1283         DbgPrint("HttpDisk: Invalid HTTP response:\n%s", buffer);
1284         ExFreePool(buffer);
1285         IoStatus->Status = STATUS_NO_SUCH_FILE;
1286         return IoStatus->Status;
1287     }
1288
1289     pStr = strstr(buffer, "Content-Length:");
1290
1291     if (pStr == NULL || pStr + 16 >= buffer + BUFFER_SIZE)
1292     {
1293         DbgPrint("HttpDisk: Invalid HTTP response:\n%s", buffer);
1294         ExFreePool(buffer);
1295         IoStatus->Status = STATUS_NO_SUCH_FILE;
1296         return IoStatus->Status;
1297     }
1298
1299     HttpHeader->ContentLength.QuadPart = _atoi64(pStr + 16);
1300
1301     if (HttpHeader->ContentLength.QuadPart == 0)
1302     {
1303         DbgPrint("HttpDisk: Invalid HTTP response:\n%s", buffer);
1304         ExFreePool(buffer);
1305         IoStatus->Status = STATUS_NO_SUCH_FILE;
1306         return IoStatus->Status;
1307     }
1308
1309     ExFreePool(buffer);
1310
1311     IoStatus->Status = STATUS_SUCCESS;
1312     IoStatus->Information = 0;
1313
1314     return STATUS_SUCCESS;
1315 }
1316
1317 NTSTATUS
1318 HttpDiskGetBlock (
1319     IN int                  *Socket,
1320     IN ULONG                Address,
1321     IN USHORT               Port,
1322     IN PUCHAR               HostName,
1323     IN PUCHAR               FileName,
1324     IN PLARGE_INTEGER       Offset,
1325     IN ULONG                Length,
1326     OUT PIO_STATUS_BLOCK    IoStatus,
1327     OUT PVOID               SystemBuffer
1328     )
1329 {
1330     struct sockaddr_in  toAddr;
1331     int                 status, nSent, nRecv;
1332     unsigned int        dataLen;
1333     char                *buffer, *pData;
1334
1335     ASSERT(Socket != NULL);
1336     ASSERT(HostName != NULL);
1337     ASSERT(FileName != NULL);
1338     ASSERT(Offset != NULL);
1339     ASSERT(IoStatus != NULL);
1340     ASSERT(SystemBuffer != NULL);
1341
1342     IoStatus->Information = 0;
1343
1344     buffer = HttpDiskPalloc(BUFFER_SIZE + 1);
1345
1346     if (buffer == NULL)
1347     {
1348         IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
1349         return IoStatus->Status;
1350     }
1351
1352     // Example request:
1353     //  GET 'FileName' HTTP/1.1
1354     //  Host: 'HostName'
1355     //  Range: bytes='Offset'-'Offset + Length - 1'
1356     //  Accept: */*
1357     //  User-Agent: HttpDisk/1.2
1358     //
1359     // Interesting lines in answer:
1360     //  HTTP/1.1 206 Partial content
1361     //  Content-Length: 'requested size'
1362     //  Content-Range: bytes 'start'-'end'/'total file size'
1363     //  Data follows after '\r\n\r\n'
1364
1365     _snprintf(
1366         buffer,
1367         BUFFER_SIZE,
1368         "GET %s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%I64u-%I64u\r\nAccept: */*\r\nUser-Agent: HttpDisk/1.2\r\n\r\n",
1369         FileName,
1370         HostName,
1371         Offset->QuadPart,
1372         Offset->QuadPart + Length - 1
1373         );
1374
1375     if (*Socket < 0)
1376     {
1377         *Socket = socket(AF_INET, SOCK_STREAM, 0);
1378
1379         if (*Socket < 0)
1380         {
1381             ExFreePool(buffer);
1382             *Socket = -1;
1383             IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
1384             return IoStatus->Status;
1385         }
1386
1387         toAddr.sin_family = AF_INET;
1388         toAddr.sin_port = Port;
1389         toAddr.sin_addr.s_addr = Address;
1390
1391         status = connect(*Socket, (struct sockaddr*) &toAddr, sizeof(toAddr));
1392
1393         if (status < 0)
1394         {
1395             DbgPrint("HttpDisk: connect() error: %#x\n", status);
1396             ExFreePool(buffer);
1397             close(*Socket);
1398             *Socket = -1;
1399             IoStatus->Status = status;
1400             return IoStatus->Status;
1401         }
1402     }
1403
1404     nSent = send(*Socket, buffer, strlen(buffer), 0);
1405
1406     if (nSent < 0)
1407     {
1408         KdPrint(("HttpDisk: send() error: %#x\n", nSent));
1409
1410         close(*Socket);
1411
1412         *Socket = socket(AF_INET, SOCK_STREAM, 0);
1413
1414         if (*Socket < 0)
1415         {
1416             ExFreePool(buffer);
1417             *Socket = -1;
1418             IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
1419             return IoStatus->Status;
1420         }
1421
1422         toAddr.sin_family = AF_INET;
1423         toAddr.sin_port = Port;
1424         toAddr.sin_addr.s_addr = Address;
1425
1426         status = connect(*Socket, (struct sockaddr*) &toAddr, sizeof(toAddr));
1427
1428         if (status < 0)
1429         {
1430             DbgPrint("HttpDisk: connect() error: %#x\n", status);
1431             ExFreePool(buffer);
1432             close(*Socket);
1433             *Socket = -1;
1434             IoStatus->Status = status;
1435             return IoStatus->Status;
1436         }
1437
1438         nSent = send(*Socket, buffer, strlen(buffer), 0);
1439
1440         if (nSent < 0)
1441         {
1442             DbgPrint("HttpDisk: send() error: %#x\n", nSent);
1443             ExFreePool(buffer);
1444             close(*Socket);
1445             *Socket = -1;
1446             IoStatus->Status = nSent;
1447             return IoStatus->Status;
1448         }
1449     }
1450
1451     nRecv = recv(*Socket, buffer, BUFFER_SIZE, 0);
1452
1453     if (nRecv < 0)
1454     {
1455         KdPrint(("HttpDisk: recv() error: %#x\n", nRecv));
1456
1457         close(*Socket);
1458
1459         *Socket = socket(AF_INET, SOCK_STREAM, 0);
1460
1461         if (*Socket < 0)
1462         {
1463             ExFreePool(buffer);
1464             *Socket = -1;
1465             IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
1466             return IoStatus->Status;
1467         }
1468
1469         toAddr.sin_family = AF_INET;
1470         toAddr.sin_port = Port;
1471         toAddr.sin_addr.s_addr = Address;
1472
1473         status = connect(*Socket, (struct sockaddr*) &toAddr, sizeof(toAddr));
1474
1475         if (status < 0)
1476         {
1477             DbgPrint("HttpDisk: connect() error: %#x\n", status);
1478             ExFreePool(buffer);
1479             close(*Socket);
1480             *Socket = -1;
1481             IoStatus->Status = status;
1482             return IoStatus->Status;
1483         }
1484
1485         nSent = send(*Socket, buffer, strlen(buffer), 0);
1486
1487         if (nSent < 0)
1488         {
1489             DbgPrint("HttpDisk: send() error: %#x\n", nSent);
1490             ExFreePool(buffer);
1491             close(*Socket);
1492             *Socket = -1;
1493             IoStatus->Status = nSent;
1494             return IoStatus->Status;
1495         }
1496
1497         nRecv = recv(*Socket, buffer, BUFFER_SIZE, 0);
1498
1499         if (nRecv < 0)
1500         {
1501             DbgPrint("HttpDisk: recv() error: %#x\n", nRecv);
1502             ExFreePool(buffer);
1503             close(*Socket);
1504             *Socket = -1;
1505             IoStatus->Status = nRecv;
1506             return IoStatus->Status;
1507         }
1508     }
1509
1510     buffer[BUFFER_SIZE] = '\0';
1511
1512     if (_strnicmp(buffer, "HTTP/1.1 206 Partial Content", 28))
1513     {
1514         DbgPrint("HttpDisk: Invalid HTTP response:\n%s", buffer);
1515         ExFreePool(buffer);
1516         close(*Socket);
1517         *Socket = -1;
1518         IoStatus->Status = STATUS_UNSUCCESSFUL;
1519         return IoStatus->Status;
1520     }
1521
1522     pData = strstr(buffer, "\r\n\r\n") + 4;
1523
1524     if (pData == NULL || pData < buffer || pData >= buffer + BUFFER_SIZE)
1525     {
1526         DbgPrint("HttpDisk: Invalid HTTP response:\n%s", buffer);
1527         ExFreePool(buffer);
1528         close(*Socket);
1529         *Socket = -1;
1530         IoStatus->Status = STATUS_UNSUCCESSFUL;
1531         return IoStatus->Status;
1532     }
1533
1534     dataLen = nRecv - (pData - buffer);
1535
1536     if (dataLen > Length || pData + dataLen > buffer + BUFFER_SIZE)
1537     {
1538         DbgPrint("HttpDisk: Invalid data length %u in HTTP response:\n%s", dataLen, buffer);
1539         ExFreePool(buffer);
1540         close(*Socket);
1541         *Socket = -1;
1542         IoStatus->Status = STATUS_UNSUCCESSFUL;
1543         return IoStatus->Status;
1544     }
1545
1546     if (dataLen > 0)
1547     {
1548         RtlCopyMemory(
1549             SystemBuffer,
1550             pData,
1551             dataLen
1552             );
1553     }
1554
1555     while (dataLen < Length)
1556     {
1557         nRecv = recv(*Socket, buffer, BUFFER_SIZE, 0);
1558         if (nRecv < 0)
1559         {
1560             DbgPrint("HttpDisk: recv() error: %#x\n", nRecv);
1561             close(*Socket);
1562             *Socket = -1;
1563             break;
1564         }
1565         if (nRecv < 1 || dataLen + nRecv > Length || nRecv > BUFFER_SIZE)
1566         {
1567             DbgPrint("HttpDisk: Invalid data length %u+%u\n", dataLen, nRecv);
1568             close(*Socket);
1569             *Socket = -1;
1570             break;
1571         }
1572         RtlCopyMemory(
1573             (PVOID)((PUCHAR) SystemBuffer + dataLen),
1574             buffer,
1575             nRecv
1576         );
1577         dataLen += nRecv;
1578     }
1579
1580     if (dataLen != Length)
1581     {
1582         DbgPrint("HttpDisk: received data length: %u, expected data length: %u\n", dataLen, Length);
1583     }
1584
1585     ExFreePool(buffer);
1586     IoStatus->Status = STATUS_SUCCESS;
1587     IoStatus->Information = dataLen;
1588     return IoStatus->Status;
1589 }