xref: /reactos/drivers/filesystems/ntfs/blockdev.c (revision 8540ab04)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
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; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * COPYRIGHT:        See COPYING in the top level directory
20  * PROJECT:          ReactOS kernel
21  * FILE:             drivers/filesystem/ntfs/blockdev.c
22  * PURPOSE:          NTFS filesystem driver
23  * PROGRAMMERS:      Eric Kohl
24  *                   Trevor Thompson
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include "ntfs.h"
30 
31 #define NDEBUG
32 #include <debug.h>
33 
34 /* FUNCTIONS ****************************************************************/
35 
36 NTSTATUS
37 NtfsReadDisk(IN PDEVICE_OBJECT DeviceObject,
38              IN LONGLONG StartingOffset,
39              IN ULONG Length,
40              IN ULONG SectorSize,
41              IN OUT PUCHAR Buffer,
42              IN BOOLEAN Override)
43 {
44     PIO_STACK_LOCATION Stack;
45     IO_STATUS_BLOCK IoStatus;
46     LARGE_INTEGER Offset;
47     KEVENT Event;
48     PIRP Irp;
49     NTSTATUS Status;
50     ULONGLONG RealReadOffset;
51     ULONG RealLength;
52     BOOLEAN AllocatedBuffer = FALSE;
53     PUCHAR ReadBuffer = Buffer;
54 
55     DPRINT("NtfsReadDisk(%p, %I64x, %lu, %lu, %p, %d)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer, Override);
56 
57     KeInitializeEvent(&Event,
58                       NotificationEvent,
59                       FALSE);
60 
61     RealReadOffset = (ULONGLONG)StartingOffset;
62     RealLength = Length;
63 
64     if ((RealReadOffset % SectorSize) != 0 || (RealLength % SectorSize) != 0)
65     {
66         RealReadOffset = ROUND_DOWN(StartingOffset, SectorSize);
67         RealLength = ROUND_UP(Length, SectorSize);
68 
69         ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + SectorSize, TAG_NTFS);
70         if (ReadBuffer == NULL)
71         {
72             DPRINT1("Not enough memory!\n");
73             return STATUS_INSUFFICIENT_RESOURCES;
74         }
75         AllocatedBuffer = TRUE;
76     }
77 
78     Offset.QuadPart = RealReadOffset;
79 
80     DPRINT("Building synchronous FSD Request...\n");
81     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
82                                        DeviceObject,
83                                        ReadBuffer,
84                                        RealLength,
85                                        &Offset,
86                                        &Event,
87                                        &IoStatus);
88     if (Irp == NULL)
89     {
90         DPRINT("IoBuildSynchronousFsdRequest failed\n");
91 
92         if (AllocatedBuffer)
93         {
94             ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
95         }
96 
97         return STATUS_INSUFFICIENT_RESOURCES;
98     }
99 
100     if (Override)
101     {
102         Stack = IoGetNextIrpStackLocation(Irp);
103         Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
104     }
105 
106     DPRINT("Calling IO Driver... with irp %p\n", Irp);
107     Status = IoCallDriver(DeviceObject, Irp);
108 
109     DPRINT("Waiting for IO Operation for %p\n", Irp);
110     if (Status == STATUS_PENDING)
111     {
112         DPRINT("Operation pending\n");
113         KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
114         DPRINT("Getting IO Status... for %p\n", Irp);
115         Status = IoStatus.Status;
116     }
117 
118     if (AllocatedBuffer)
119     {
120         if (NT_SUCCESS(Status))
121         {
122             RtlCopyMemory(Buffer, ReadBuffer + (StartingOffset - RealReadOffset), Length);
123         }
124 
125         ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
126     }
127 
128     DPRINT("NtfsReadDisk() done (Status %x)\n", Status);
129 
130     return Status;
131 }
132 
133 /**
134 * @name NtfsWriteDisk
135 * @implemented
136 *
137 * Writes data from the given buffer to the given DeviceObject.
138 *
139 * @param DeviceObject
140 * Device to write to
141 *
142 * @param StartingOffset
143 * Offset, in bytes, from the start of the device object where the data will be written
144 *
145 * @param Length
146 * How much data will be written, in bytes
147 *
148 * @param SectorSize
149 * Size of the sector on the disk that the write must be aligned to
150 *
151 * @param Buffer
152 * The data that's being written to the device
153 *
154 * @return
155 * STATUS_SUCCESS in case of success, STATUS_INSUFFICIENT_RESOURCES if a memory allocation failed,
156 * or whatever status IoCallDriver() sets.
157 *
158 * @remarks Called by NtfsWriteFile(). May perform a read-modify-write operation if the
159 * requested write is not sector-aligned.
160 *
161 */
162 NTSTATUS
163 NtfsWriteDisk(IN PDEVICE_OBJECT DeviceObject,
164               IN LONGLONG StartingOffset,
165               IN ULONG Length,
166               IN ULONG SectorSize,
167               IN const PUCHAR Buffer)
168 {
169     IO_STATUS_BLOCK IoStatus;
170     LARGE_INTEGER Offset;
171     KEVENT Event;
172     PIRP Irp;
173     NTSTATUS Status;
174     ULONGLONG RealWriteOffset;
175     ULONG RealLength;
176     BOOLEAN AllocatedBuffer = FALSE;
177     PUCHAR TempBuffer = NULL;
178 
179     DPRINT("NtfsWriteDisk(%p, %I64x, %lu, %lu, %p)\n", DeviceObject, StartingOffset, Length, SectorSize, Buffer);
180 
181     if (Length == 0)
182         return STATUS_SUCCESS;
183 
184     RealWriteOffset = (ULONGLONG)StartingOffset;
185     RealLength = Length;
186 
187     // Does the write need to be adjusted to be sector-aligned?
188     if ((RealWriteOffset % SectorSize) != 0 || (RealLength % SectorSize) != 0)
189     {
190         ULONGLONG relativeOffset;
191 
192         // We need to do a read-modify-write. We'll start be copying the entire
193         // contents of every sector that will be overwritten.
194         // TODO: Optimize (read no more than necessary)
195 
196         RealWriteOffset = ROUND_DOWN(StartingOffset, SectorSize);
197         RealLength = ROUND_UP(Length, SectorSize);
198 
199         // Would the end of our sector-aligned write fall short of the requested write?
200         if (RealWriteOffset + RealLength < StartingOffset + Length)
201         {
202             RealLength += SectorSize;
203         }
204 
205         // Did we underestimate the memory required somehow?
206         if (RealLength + RealWriteOffset < StartingOffset + Length)
207         {
208             DPRINT1("\a\t\t\t\t\tFIXME: calculated less memory than needed!\n");
209             DPRINT1("StartingOffset: %lu\tLength: %lu\tRealWriteOffset: %lu\tRealLength: %lu\n",
210                     StartingOffset, Length, RealWriteOffset, RealLength);
211 
212             RealLength += SectorSize;
213         }
214 
215         // Allocate a buffer to copy the existing data to
216         TempBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength, TAG_NTFS);
217 
218         // Did we fail to allocate it?
219         if (TempBuffer == NULL)
220         {
221             DPRINT1("Not enough memory!\n");
222 
223             return STATUS_INSUFFICIENT_RESOURCES;
224         }
225 
226         // Read the sectors we'll be overwriting into TempBuffer
227         Status = NtfsReadDisk(DeviceObject, RealWriteOffset, RealLength, SectorSize, TempBuffer, FALSE);
228 
229         // Did we fail the read?
230         if (!NT_SUCCESS(Status))
231         {
232             RtlSecureZeroMemory(TempBuffer, RealLength);
233             ExFreePoolWithTag(TempBuffer, TAG_NTFS);
234             return Status;
235         }
236 
237         // Calculate where the new data should be written to, relative to the start of TempBuffer
238         relativeOffset = StartingOffset - RealWriteOffset;
239 
240         // Modify the tempbuffer with the data being read
241         RtlCopyMemory(TempBuffer + relativeOffset, Buffer, Length);
242 
243         AllocatedBuffer = TRUE;
244     }
245 
246     // set the destination offset
247     Offset.QuadPart = RealWriteOffset;
248 
249     // setup the notification event for the write
250     KeInitializeEvent(&Event,
251                       NotificationEvent,
252                       FALSE);
253 
254     DPRINT("Building synchronous FSD Request...\n");
255 
256     // Build an IRP requesting the lower-level [disk] driver to perform the write
257     // TODO: Forward the existing IRP instead
258     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE,
259                                        DeviceObject,
260                                        // if we allocated a temp buffer, use that instead of the Buffer parameter
261                                        ((AllocatedBuffer) ? TempBuffer : Buffer),
262                                        RealLength,
263                                        &Offset,
264                                        &Event,
265                                        &IoStatus);
266     // Did we fail to build the IRP?
267     if (Irp == NULL)
268     {
269         DPRINT1("IoBuildSynchronousFsdRequest failed\n");
270 
271         if (AllocatedBuffer)
272         {
273             RtlSecureZeroMemory(TempBuffer, RealLength);
274             ExFreePoolWithTag(TempBuffer, TAG_NTFS);
275         }
276 
277         return STATUS_INSUFFICIENT_RESOURCES;
278     }
279 
280     // Call the next-lower driver to perform the write
281     DPRINT("Calling IO Driver with irp %p\n", Irp);
282     Status = IoCallDriver(DeviceObject, Irp);
283 
284     // Wait until the next-lower driver has completed the IRP
285     DPRINT("Waiting for IO Operation for %p\n", Irp);
286     if (Status == STATUS_PENDING)
287     {
288         DPRINT("Operation pending\n");
289         KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
290         DPRINT("Getting IO Status... for %p\n", Irp);
291         Status = IoStatus.Status;
292     }
293 
294     if (AllocatedBuffer)
295     {
296         // zero the buffer before freeing it, so private user data can't be snooped
297         RtlSecureZeroMemory(TempBuffer, RealLength);
298 
299         ExFreePoolWithTag(TempBuffer, TAG_NTFS);
300     }
301 
302     DPRINT("NtfsWriteDisk() done (Status %x)\n", Status);
303 
304     return Status;
305 }
306 
307 NTSTATUS
308 NtfsReadSectors(IN PDEVICE_OBJECT DeviceObject,
309                 IN ULONG DiskSector,
310                 IN ULONG SectorCount,
311                 IN ULONG SectorSize,
312                 IN OUT PUCHAR Buffer,
313                 IN BOOLEAN Override)
314 {
315     LONGLONG Offset;
316     ULONG BlockSize;
317 
318     Offset = (LONGLONG)DiskSector * (LONGLONG)SectorSize;
319     BlockSize = SectorCount * SectorSize;
320 
321     return NtfsReadDisk(DeviceObject, Offset, BlockSize, SectorSize, Buffer, Override);
322 }
323 
324 
325 NTSTATUS
326 NtfsDeviceIoControl(IN PDEVICE_OBJECT DeviceObject,
327                     IN ULONG ControlCode,
328                     IN PVOID InputBuffer,
329                     IN ULONG InputBufferSize,
330                     IN OUT PVOID OutputBuffer,
331                     IN OUT PULONG OutputBufferSize,
332                     IN BOOLEAN Override)
333 {
334     PIO_STACK_LOCATION Stack;
335     IO_STATUS_BLOCK IoStatus;
336     KEVENT Event;
337     PIRP Irp;
338     NTSTATUS Status;
339 
340     KeInitializeEvent(&Event, NotificationEvent, FALSE);
341 
342     DPRINT("Building device I/O control request ...\n");
343     Irp = IoBuildDeviceIoControlRequest(ControlCode,
344                                         DeviceObject,
345                                         InputBuffer,
346                                         InputBufferSize,
347                                         OutputBuffer,
348                                         (OutputBufferSize) ? *OutputBufferSize : 0,
349                                         FALSE,
350                                         &Event,
351                                         &IoStatus);
352     if (Irp == NULL)
353     {
354         DPRINT("IoBuildDeviceIoControlRequest() failed\n");
355         return STATUS_INSUFFICIENT_RESOURCES;
356     }
357 
358     if (Override)
359     {
360         Stack = IoGetNextIrpStackLocation(Irp);
361         Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
362     }
363 
364     DPRINT("Calling IO Driver... with irp %p\n", Irp);
365     Status = IoCallDriver(DeviceObject, Irp);
366     if (Status == STATUS_PENDING)
367     {
368         KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
369         Status = IoStatus.Status;
370     }
371 
372     if (OutputBufferSize)
373     {
374         *OutputBufferSize = IoStatus.Information;
375     }
376 
377     return Status;
378 }
379 
380 /* EOF */
381