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