MajorFunction_Irp_finish(): Fixed deallocated memory access.
[captive.git] / src / libcaptive / storage / cdrom.c
1 /* $Id$
2  * "\Device\CdRom%d" storage emulation driver for reactos of libcaptive
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
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; exactly version 2 of June 1991 is required
8  * 
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  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/storage.h"    /* self */
23 #include "reactos/ddk/iotypes.h"        /* for DRIVER_OBJECT */
24 #include <glib/gmessages.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include "reactos/ddk/ntddscsi.h"       /* for IO_SCSI_CAPABILITIES */
29 #include "reactos/ddk/class2.h" /* for PDEVICE_EXTENSION */
30 #include "reactos/ddk/status.h" /* for STATUS_INVALID_PARAMETER */
31 #include "reactos/ddk/iofuncs.h"        /* for IoCreateDevice() */
32 #include "captive/unicode.h"
33 #include "captive/macros.h"
34
35
36 static DRIVER_OBJECT cdrom_DriverObject;
37 static DISK_GEOMETRY cdrom_DiskGeometry;
38 static DISK_GEOMETRY cdrom_DiskGeometry_check;  /* for g_assert() checking against foreign modifications */
39 static int Image_fd=-1;
40 static off_t Image_size;        /* FIXME: lseek64() */
41
42
43 static gboolean validate_DeviceObject(DEVICE_OBJECT *DeviceObject)
44 {
45 DEVICE_EXTENSION *DeviceExtension;
46 DISK_GEOMETRY *DiskGeometry;
47
48         g_return_val_if_fail(DeviceObject!=NULL,FALSE);
49         g_return_val_if_fail(DeviceObject->DriverObject==&cdrom_DriverObject,FALSE);
50
51         DeviceExtension=DeviceObject->DeviceExtension;
52         DiskGeometry=DeviceExtension->DiskGeometry;
53         g_return_val_if_fail(DiskGeometry==&cdrom_DiskGeometry,FALSE);
54         g_return_val_if_fail(DiskGeometry->MediaType==cdrom_DiskGeometry_check.MediaType,FALSE);
55         g_return_val_if_fail(DiskGeometry->TracksPerCylinder==cdrom_DiskGeometry_check.TracksPerCylinder,FALSE);
56         g_return_val_if_fail(DiskGeometry->SectorsPerTrack==cdrom_DiskGeometry_check.SectorsPerTrack,FALSE);
57         g_return_val_if_fail(DiskGeometry->BytesPerSector==cdrom_DiskGeometry_check.BytesPerSector,FALSE);
58         g_return_val_if_fail(DiskGeometry->Cylinders.QuadPart==cdrom_DiskGeometry_check.Cylinders.QuadPart,FALSE);
59         g_return_val_if_fail(DeviceExtension->PartitionLength.QuadPart==Image_size,FALSE);
60
61         return TRUE;
62 }
63
64
65 static NTSTATUS MajorFunction_Irp_finish(DEVICE_OBJECT *DeviceObject,IRP *Irp)
66 {
67 NTSTATUS r;
68
69         g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
70         g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
71
72         /* required for removable media only */
73         if (!NT_SUCCESS(Irp->IoStatus.Status) && IoIsErrorUserInduced(Irp->IoStatus.Status)) {
74                 g_assert(Irp->Tail.Overlay.Thread!=NULL);       /* FIXME: Error should be postponed to first !=NULL Irp later */
75                 IoSetHardErrorOrVerifyDevice(Irp,DeviceObject);
76                 Irp->IoStatus.Information=0;    /* may got set during some processing before error occured */
77                 }
78
79         /* IoCompleteRequest() will do 'IoFreeIrp(Irp);'!
80          * 'IoStatus.Status' must be saved before its invocation!
81          */
82         r=Irp->IoStatus.Status;
83         IoCompleteRequest(Irp,IO_NO_INCREMENT);
84         return r;
85 }
86
87
88 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
89  * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
90  */
91 #define MajorFunction_DEVICE_CONTROL ((PDRIVER_DISPATCH)MajorFunction_DEVICE_CONTROL_func)
92 static NTSTATUS CAPTIVE_STDCALL MajorFunction_DEVICE_CONTROL_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
93 {
94 IO_STACK_LOCATION *IrpStack;
95
96         g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
97         g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
98
99         Irp->IoStatus.Information=0;    /* request-specific, may get overriden later */
100         IrpStack=IoGetCurrentIrpStackLocation(Irp);
101         g_assert(IrpStack->MajorFunction==IRP_MJ_DEVICE_CONTROL);
102         g_assert(IrpStack->MinorFunction==0);
103
104         switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
105
106                 case IOCTL_CDROM_GET_LAST_SESSION:
107                         /* Nothing interesting to see, move along.
108                          * FIXME: This call is somehow multisession related - must we care?
109                          */
110                         /* PASSTHRU */
111
112                 case IOCTL_CDROM_READ_TOC: {
113 CDROM_TOC *CdromToc;
114
115                         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(CDROM_TOC)) {
116                                 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
117                                 Irp->IoStatus.Information=sizeof(CDROM_TOC);
118                                 g_assert_not_reached();
119                                 goto done;
120                                 }
121                         CdromToc=(CDROM_TOC *)Irp->AssociatedIrp.SystemBuffer;
122                         CAPTIVE_MEMZERO(CdromToc);
123                         CdromToc->Length[0]=((sizeof(*CdromToc)-2)>>0U)&0xFFU;  /* little-endian */
124                         CdromToc->Length[1]=((sizeof(*CdromToc)-2)>>8U)&0xFFU;
125                         CdromToc->FirstTrack=0; /* one track; TOC_LAST_TRACK does not count */
126                         CdromToc->LastTrack =0; /* one track; TOC_LAST_TRACK does not count */
127                         CdromToc->TrackData[0].Control=TOC_DATA_TRACK;
128                         CdromToc->TrackData[0].Adr=0;   /* Q-subchannel subinfo */
129                         CdromToc->TrackData[0].TrackNumber=0;
130                         CdromToc->TrackData[0].Address[0]=0>>24U;       /* LBA offset; big-endian */
131                         CdromToc->TrackData[0].Address[1]=0>>16U;
132                         CdromToc->TrackData[0].Address[2]=0>> 8U;
133                         CdromToc->TrackData[0].Address[3]=0>> 0U;
134                         CdromToc->TrackData[1].Control=0;
135                         CdromToc->TrackData[1].Adr=0;   /* Q-subchannel subinfo */
136                         CdromToc->TrackData[1].TrackNumber=TOC_LAST_TRACK;
137                         /* FIXME: should we put the Image_size to TOC_LAST_TRACK? */
138                         CdromToc->TrackData[1].Address[0]=(Image_size/512)>>24U;        /* LBA offset; big-endian */
139                         CdromToc->TrackData[1].Address[1]=(Image_size/512)>>16U;
140                         CdromToc->TrackData[1].Address[2]=(Image_size/512)>> 8U;
141                         CdromToc->TrackData[1].Address[3]=(Image_size/512)>> 0U;
142
143                         Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
144                         Irp->IoStatus.Status=STATUS_SUCCESS;
145                         } break;
146
147                 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
148                         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(DISK_GEOMETRY)) {
149                                 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
150                                 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
151                                 g_assert_not_reached();
152                                 goto done;
153                                 }
154                         *(DISK_GEOMETRY *)Irp->AssociatedIrp.SystemBuffer=cdrom_DiskGeometry;
155                         Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
156                         Irp->IoStatus.Status=STATUS_SUCCESS;
157                         break;
158
159                 case IOCTL_CDROM_CHECK_VERIFY: {
160                         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
161                                 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(ULONG)) {
162                                         Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
163                                         Irp->IoStatus.Information=sizeof(ULONG);
164                                         g_assert_not_reached();
165                                         goto done;
166                                         }
167                                 *(ULONG *)Irp->AssociatedIrp.SystemBuffer=0;    /* MediaChangeCount */
168                                 Irp->IoStatus.Information=sizeof(ULONG);
169                                 }
170                         else {
171                                 Irp->IoStatus.Information=0;
172                                 }
173                         Irp->IoStatus.Status=STATUS_SUCCESS;
174                         } break;
175
176                 default:
177                         Irp->IoStatus.Status=STATUS_INVALID_DEVICE_REQUEST;
178                         g_assert_not_reached();
179                         goto done;
180                 }
181         /* PASSTHRU */
182
183 done:   /* 'err:' but we flow here even during success */
184         return MajorFunction_Irp_finish(DeviceObject,Irp);
185 }
186
187
188 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
189  * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
190  */
191 #define MajorFunction_READ ((PDRIVER_DISPATCH)MajorFunction_READ_func)
192 static NTSTATUS CAPTIVE_STDCALL MajorFunction_READ_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
193 {
194 IO_STACK_LOCATION *IrpStack;
195 off_t offset;
196 ssize_t count;
197
198         g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
199         g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
200
201         Irp->IoStatus.Information=0;    /* request-specific, may get overriden later */
202         IrpStack=IoGetCurrentIrpStackLocation(Irp);
203         g_assert(IrpStack->MajorFunction==IRP_MJ_READ);
204         g_assert(IrpStack->MinorFunction==0);
205
206         /* What is 'IrpStack->Parameters.Read.Key'? */
207         g_assert(0==(IrpStack->Parameters.Read.Length%2048));
208         g_assert(0==(IrpStack->Parameters.Read.ByteOffset.QuadPart%2048));
209         g_assert(IrpStack->Parameters.Read.ByteOffset.QuadPart>=0);
210
211         offset=lseek(Image_fd,IrpStack->Parameters.Read.ByteOffset.QuadPart,SEEK_SET);
212         g_assert(offset==IrpStack->Parameters.Read.ByteOffset.QuadPart);
213
214         count=read(Image_fd,Irp->UserBuffer,IrpStack->Parameters.Read.Length);
215         g_assert((ULONG)count==IrpStack->Parameters.Read.Length);
216
217         Irp->IoStatus.Information=IrpStack->Parameters.Read.Length;
218         Irp->IoStatus.Status=STATUS_SUCCESS;
219
220         /* PASSTHRU */
221 /* done: */     /* 'err:' but we flow here even during success */
222         return MajorFunction_Irp_finish(DeviceObject,Irp);
223 }
224
225
226 /* similiar to drivers/storage/cdrom/cdrom.c/DriverEntry()->...
227  * ...->CdromClassCreateDeviceObject()->
228  * ->reactos/drivers/storage/class2/class2.c/ScsiClassCreateDeviceObject()
229  * We should be driving a lower layer PortDevice but currently we
230  * do not provide it, I hope W32 filesystems don't touch it.
231  */
232 static NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
233 {
234 static IO_SCSI_CAPABILITIES PortCapabilities;   /* it is const filled in DriverEntry() */
235 PDEVICE_OBJECT DeviceObject;
236 PDEVICE_EXTENSION DeviceExtension;
237 NTSTATUS err;
238
239         g_return_val_if_fail(DriverObject!=NULL,STATUS_INVALID_PARAMETER);
240         g_return_val_if_fail(RegistryPath!=NULL,STATUS_INVALID_PARAMETER);
241
242         err=IoCreateDevice(
243                         DriverObject,   /* DriverObject */
244                         sizeof(DEVICE_EXTENSION),       /* DeviceExtensionSize; additional storage not used */
245                         captive_utf8_to_UnicodeString_alloca("\\Device\\CdRom0"),       /* DeviceName */
246                         FILE_DEVICE_CD_ROM,     /* DeviceType */
247                         FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE,     /* DeviceCharacteristics */
248                         FALSE,  /* Exclusive */
249                         &DeviceObject); /* DeviceObject */
250         g_return_val_if_fail(NT_SUCCESS(err),FALSE);
251
252         /* CdromClassCreateDeviceObject() sets:
253          *      DeviceObject->Flags|=DO_DIRECT_IO;
254          * but do we need it?
255          */
256         /* should be left from IoCreateDevice(DeviceCharacteristics) above: */
257         g_assert(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
258         /* ignored: DeviceObject->StackSize */
259         /* ignored: DeviceObject->AlignmentRequirement */
260
261         /* from reactos/drivers/storage/scsiport/scsiport.c/ScsiPortCreatePortDevice() */
262         PortCapabilities.Length=sizeof(PortCapabilities);
263         PortCapabilities.MaximumTransferLength=0x10000; /* 64KB */
264         g_assert((PortCapabilities.MaximumTransferLength%PAGE_SIZE)==0);
265         PortCapabilities.MaximumPhysicalPages=PortCapabilities.MaximumTransferLength/PAGE_SIZE;
266         PortCapabilities.SupportedAsynchronousEvents=0;
267         PortCapabilities.AlignmentMask=1;       /* no alignment required by us; speced as "integer multiple" */
268         PortCapabilities.TaggedQueuing=FALSE;
269         PortCapabilities.AdapterScansDown=FALSE;
270         PortCapabilities.AdapterUsesPio=TRUE;
271
272         DeviceExtension=DeviceObject->DeviceExtension;
273         DeviceExtension->MediaChangeCount=0;
274         DeviceExtension->PhysicalDevice=DeviceObject;   /* no real PhysicalDeviceObject */
275         DeviceExtension->LockCount=0;
276         DeviceExtension->DeviceNumber=0;        /* corresponds to the # in "\\Device\\CdRom0" */
277         /* ignored DeviceExtension->PortDeviceObject
278          * as we are the final driver and we don't have any PortDeviceObject
279          */
280         DeviceExtension->PortCapabilities=&PortCapabilities;
281         DeviceExtension->StartingOffset.QuadPart=0;
282         DeviceExtension->PartitionLength.QuadPart=Image_size;
283         DeviceExtension->PortNumber=0;
284         DeviceExtension->PathId=0;
285         DeviceExtension->TargetId=0;
286         DeviceExtension->Lun=0;
287
288         cdrom_DiskGeometry.MediaType=RemovableMedia;
289         cdrom_DiskGeometry.TracksPerCylinder=64;
290         cdrom_DiskGeometry.SectorsPerTrack=32;
291         cdrom_DiskGeometry.BytesPerSector=2048;
292         cdrom_DiskGeometry.Cylinders.QuadPart=Image_size
293                         /cdrom_DiskGeometry.BytesPerSector
294                         /cdrom_DiskGeometry.SectorsPerTrack
295                         /cdrom_DiskGeometry.TracksPerCylinder;
296         /* 'DeviceExtension->DiskGeometry' is NULL! */
297         cdrom_DiskGeometry_check=cdrom_DiskGeometry;    /* for g_assert() checking against foreign modifications */
298         DeviceExtension->DiskGeometry=&cdrom_DiskGeometry;
299
300         DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=MajorFunction_DEVICE_CONTROL;
301         DriverObject->MajorFunction[IRP_MJ_READ          ]=MajorFunction_READ;
302
303         return STATUS_SUCCESS;
304 }
305
306 /**
307  * captive_cdrom_init:
308  * @image_pathname: Host OS file #utf8 pathname of the disk image to provide.
309  * %NULL value is forbidden.
310  *
311  * Creates system device "\Device\CdRom%d" providing readonly access
312  * to the given @image_pathname as emulation of CD-ROM driver.
313  *
314  * captive currently supports just one drive and thus "\Device\CdRom0"
315  * is always created. It is forbidden to call this function twice.
316  *
317  * Returns: %TRUE if the initialization was successful.
318  */
319 gboolean captive_cdrom_init(const gchar *image_pathname)
320 {
321 NTSTATUS err;
322
323         g_return_val_if_fail(image_pathname!=NULL,FALSE);
324
325         Image_fd=open(image_pathname,O_RDONLY
326 #ifdef O_BINARY
327                         |O_BINARY
328 #endif
329                         );      /* FIXME: lseek64() */
330         g_return_val_if_fail(Image_fd!=-1,FALSE);
331
332         Image_size=lseek(Image_fd,0,SEEK_END);  /* FIXME: lseek64() */
333         if (Image_size==(off_t)-1) {
334                 g_assert_not_reached();
335                 goto err_close;
336                 }
337
338         err=DriverEntry(
339                         &cdrom_DriverObject,    /* DriverEntry_DriverObject */
340                         captive_utf8_to_UnicodeString_alloca("\\captive\\storage\\cdrom"));     /* DriverEntry_RegistryPath; ignored */
341         g_return_val_if_fail(NT_SUCCESS(err),FALSE);
342
343         return TRUE;
344
345 err_close:
346         close(Image_fd);
347         Image_fd=-1;
348 /* err: */
349         g_return_val_if_reached(FALSE);
350 }