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