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