1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS VFAT filesystem library 4 * FILE: fat16.c 5 * PURPOSE: Fat16 support 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * Eric Kohl 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include "vfatlib.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 18 /* FUNCTIONS ******************************************************************/ 19 20 static NTSTATUS 21 Fat16WriteBootSector(IN HANDLE FileHandle, 22 IN PFAT16_BOOT_SECTOR BootSector, 23 IN OUT PFORMAT_CONTEXT Context) 24 { 25 IO_STATUS_BLOCK IoStatusBlock; 26 NTSTATUS Status; 27 PFAT16_BOOT_SECTOR NewBootSector; 28 LARGE_INTEGER FileOffset; 29 30 /* Allocate buffer for new bootsector */ 31 NewBootSector = (PFAT16_BOOT_SECTOR)RtlAllocateHeap(RtlGetProcessHeap(), 32 0, 33 BootSector->BytesPerSector); 34 if (NewBootSector == NULL) 35 return STATUS_INSUFFICIENT_RESOURCES; 36 37 /* Zero the new bootsector */ 38 RtlZeroMemory(NewBootSector, BootSector->BytesPerSector); 39 40 /* Copy FAT16 BPB to new bootsector */ 41 memcpy(&NewBootSector->Jump[0], 42 &BootSector->Jump[0], 43 FIELD_OFFSET(FAT16_BOOT_SECTOR, Res2) - FIELD_OFFSET(FAT16_BOOT_SECTOR, Jump)); 44 /* FAT16 BPB length (up to (not including) Res2) */ 45 46 /* Write the boot sector signature */ 47 NewBootSector->Signature1 = 0xAA550000; 48 49 /* Write sector 0 */ 50 FileOffset.QuadPart = 0ULL; 51 Status = NtWriteFile(FileHandle, 52 NULL, 53 NULL, 54 NULL, 55 &IoStatusBlock, 56 NewBootSector, 57 BootSector->BytesPerSector, 58 &FileOffset, 59 NULL); 60 if (!NT_SUCCESS(Status)) 61 { 62 DPRINT("NtWriteFile() failed (Status %lx)\n", Status); 63 goto done; 64 } 65 66 UpdateProgress(Context, 1); 67 68 done: 69 /* Free the buffer */ 70 RtlFreeHeap(RtlGetProcessHeap(), 0, NewBootSector); 71 return Status; 72 } 73 74 75 static NTSTATUS 76 Fat16WriteFAT(IN HANDLE FileHandle, 77 IN ULONG SectorOffset, 78 IN PFAT16_BOOT_SECTOR BootSector, 79 IN OUT PFORMAT_CONTEXT Context) 80 { 81 IO_STATUS_BLOCK IoStatusBlock; 82 NTSTATUS Status; 83 PUCHAR Buffer; 84 LARGE_INTEGER FileOffset; 85 ULONG i; 86 ULONG Sectors; 87 88 /* Allocate buffer */ 89 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(), 90 0, 91 32 * 1024); 92 if (Buffer == NULL) 93 return STATUS_INSUFFICIENT_RESOURCES; 94 95 /* Zero the buffer */ 96 RtlZeroMemory(Buffer, 32 * 1024); 97 98 /* FAT cluster 0 */ 99 Buffer[0] = 0xf8; /* Media type */ 100 Buffer[1] = 0xff; 101 102 /* FAT cluster 1 */ 103 Buffer[2] = 0xff; /* Clean shutdown, no disk read/write errors, end-of-cluster (EOC) mark */ 104 Buffer[3] = 0xff; 105 106 /* Write first sector of the FAT */ 107 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors) * BootSector->BytesPerSector; 108 Status = NtWriteFile(FileHandle, 109 NULL, 110 NULL, 111 NULL, 112 &IoStatusBlock, 113 Buffer, 114 BootSector->BytesPerSector, 115 &FileOffset, 116 NULL); 117 if (!NT_SUCCESS(Status)) 118 { 119 DPRINT("NtWriteFile() failed (Status %lx)\n", Status); 120 goto done; 121 } 122 123 UpdateProgress(Context, 1); 124 125 /* Zero the begin of the buffer */ 126 RtlZeroMemory(Buffer, 4); 127 128 /* Zero the rest of the FAT */ 129 Sectors = 32 * 1024 / BootSector->BytesPerSector; 130 for (i = 1; i < (ULONG)BootSector->FATSectors; i += Sectors) 131 { 132 /* Zero some sectors of the FAT */ 133 FileOffset.QuadPart = (SectorOffset + BootSector->ReservedSectors + i) * BootSector->BytesPerSector; 134 135 if (((ULONG)BootSector->FATSectors - i) <= Sectors) 136 { 137 Sectors = (ULONG)BootSector->FATSectors - i; 138 } 139 140 Status = NtWriteFile(FileHandle, 141 NULL, 142 NULL, 143 NULL, 144 &IoStatusBlock, 145 Buffer, 146 Sectors * BootSector->BytesPerSector, 147 &FileOffset, 148 NULL); 149 if (!NT_SUCCESS(Status)) 150 { 151 DPRINT("NtWriteFile() failed (Status %lx)\n", Status); 152 goto done; 153 } 154 155 UpdateProgress(Context, Sectors); 156 } 157 158 done: 159 /* Free the buffer */ 160 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 161 return Status; 162 } 163 164 165 static NTSTATUS 166 Fat16WriteRootDirectory(IN HANDLE FileHandle, 167 IN PFAT16_BOOT_SECTOR BootSector, 168 IN OUT PFORMAT_CONTEXT Context) 169 { 170 IO_STATUS_BLOCK IoStatusBlock; 171 NTSTATUS Status = STATUS_SUCCESS; 172 PUCHAR Buffer; 173 LARGE_INTEGER FileOffset; 174 ULONG FirstRootDirSector; 175 ULONG RootDirSectors; 176 ULONG Sectors; 177 ULONG i; 178 179 DPRINT("BootSector->ReservedSectors = %hu\n", BootSector->ReservedSectors); 180 DPRINT("BootSector->FATSectors = %hu\n", BootSector->FATSectors); 181 DPRINT("BootSector->SectorsPerCluster = %u\n", BootSector->SectorsPerCluster); 182 183 /* Write cluster */ 184 RootDirSectors = ((BootSector->RootEntries * 32) + 185 (BootSector->BytesPerSector - 1)) / BootSector->BytesPerSector; 186 FirstRootDirSector = 187 BootSector->ReservedSectors + (BootSector->FATCount * BootSector->FATSectors); 188 189 DPRINT("RootDirSectors = %lu\n", RootDirSectors); 190 DPRINT("FirstRootDirSector = %lu\n", FirstRootDirSector); 191 192 /* Allocate buffer for the cluster */ 193 Buffer = (PUCHAR)RtlAllocateHeap(RtlGetProcessHeap(), 194 0, 195 32 * 1024); 196 if (Buffer == NULL) 197 return STATUS_INSUFFICIENT_RESOURCES; 198 199 /* Zero the buffer */ 200 RtlZeroMemory(Buffer, 32 * 1024); 201 202 Sectors = 32 * 1024 / BootSector->BytesPerSector; 203 for (i = 0; i < RootDirSectors; i += Sectors) 204 { 205 /* Zero some sectors of the root directory */ 206 FileOffset.QuadPart = (FirstRootDirSector + i) * BootSector->BytesPerSector; 207 208 if ((RootDirSectors - i) <= Sectors) 209 { 210 Sectors = RootDirSectors - i; 211 } 212 213 Status = NtWriteFile(FileHandle, 214 NULL, 215 NULL, 216 NULL, 217 &IoStatusBlock, 218 Buffer, 219 Sectors * BootSector->BytesPerSector, 220 &FileOffset, 221 NULL); 222 if (!NT_SUCCESS(Status)) 223 { 224 DPRINT("NtWriteFile() failed (Status %lx)\n", Status); 225 goto done; 226 } 227 228 UpdateProgress(Context, Sectors); 229 } 230 231 done: 232 /* Free the buffer */ 233 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 234 return Status; 235 } 236 237 238 NTSTATUS 239 Fat16Format(IN HANDLE FileHandle, 240 IN PPARTITION_INFORMATION PartitionInfo, 241 IN PDISK_GEOMETRY DiskGeometry, 242 IN PUNICODE_STRING Label, 243 IN BOOLEAN QuickFormat, 244 IN ULONG ClusterSize, 245 IN OUT PFORMAT_CONTEXT Context) 246 { 247 FAT16_BOOT_SECTOR BootSector; 248 OEM_STRING VolumeLabel; 249 ULONG SectorCount; 250 ULONG RootDirSectors; 251 ULONG TmpVal1; 252 ULONG TmpVal2; 253 ULONG TmpVal3; 254 NTSTATUS Status; 255 256 /* Calculate cluster size */ 257 if (ClusterSize == 0) 258 { 259 if (PartitionInfo->PartitionLength.QuadPart < 16LL * 1024LL * 1024LL) 260 { 261 /* Partition < 16MB ==> 1KB Cluster */ 262 ClusterSize = 1024; 263 } 264 else if (PartitionInfo->PartitionLength.QuadPart < 128LL * 1024LL * 1024LL) 265 { 266 /* Partition < 128MB ==> 2KB Cluster */ 267 ClusterSize = 2048; 268 } 269 else if (PartitionInfo->PartitionLength.QuadPart < 256LL * 1024LL * 1024LL) 270 { 271 /* Partition < 256MB ==> 4KB Cluster */ 272 ClusterSize = 4096; 273 } 274 else 275 { 276 /* Partition >= 256MB (< 512MB) ==> 8KB Cluster */ 277 ClusterSize = 8192; 278 } 279 } 280 281 SectorCount = PartitionInfo->PartitionLength.QuadPart >> 282 GetShiftCount(DiskGeometry->BytesPerSector); /* Use shifting to avoid 64-bit division */ 283 284 RtlZeroMemory(&BootSector, sizeof(FAT16_BOOT_SECTOR)); 285 memcpy(&BootSector.OEMName[0], "MSWIN4.1", 8); 286 /* FIXME: Add dummy bootloader for real */ 287 BootSector.Jump[0] = 0xeb; 288 BootSector.Jump[1] = 0x3c; 289 BootSector.Jump[2] = 0x90; 290 BootSector.BytesPerSector = DiskGeometry->BytesPerSector; 291 BootSector.SectorsPerCluster = ClusterSize / BootSector.BytesPerSector; 292 BootSector.ReservedSectors = 1; 293 BootSector.FATCount = 2; 294 BootSector.RootEntries = 512; 295 BootSector.Sectors = (SectorCount < 0x10000) ? (unsigned short)SectorCount : 0; 296 BootSector.Media = 0xf8; 297 BootSector.FATSectors = 0; /* Set later. See below. */ 298 BootSector.SectorsPerTrack = DiskGeometry->SectorsPerTrack; 299 BootSector.Heads = DiskGeometry->TracksPerCylinder; 300 BootSector.HiddenSectors = PartitionInfo->HiddenSectors; 301 BootSector.SectorsHuge = (SectorCount >= 0x10000) ? (unsigned long)SectorCount : 0; 302 BootSector.Drive = (DiskGeometry->MediaType == FixedMedia) ? 0x80 : 0x00; 303 BootSector.ExtBootSignature = 0x29; 304 BootSector.VolumeID = CalcVolumeSerialNumber(); 305 if ((Label == NULL) || (Label->Buffer == NULL)) 306 { 307 memcpy(&BootSector.VolumeLabel[0], "NO NAME ", 11); 308 } 309 else 310 { 311 RtlUnicodeStringToOemString(&VolumeLabel, Label, TRUE); 312 RtlFillMemory(&BootSector.VolumeLabel[0], 11, ' '); 313 memcpy(&BootSector.VolumeLabel[0], VolumeLabel.Buffer, 314 VolumeLabel.Length < 11 ? VolumeLabel.Length : 11); 315 RtlFreeOemString(&VolumeLabel); 316 } 317 318 memcpy(&BootSector.SysType[0], "FAT16 ", 8); 319 320 DPRINT("BootSector.SectorsHuge = %lx\n", BootSector.SectorsHuge); 321 322 RootDirSectors = ((BootSector.RootEntries * 32) + 323 (BootSector.BytesPerSector - 1)) / BootSector.BytesPerSector; 324 325 /* Calculate number of FAT sectors */ 326 /* (BootSector.BytesPerSector / 2) FAT entries (16bit) fit into one sector */ 327 TmpVal1 = SectorCount - (BootSector.ReservedSectors + RootDirSectors); 328 TmpVal2 = ((BootSector.BytesPerSector / 2) * BootSector.SectorsPerCluster) + BootSector.FATCount; 329 TmpVal3 = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2; 330 BootSector.FATSectors = (unsigned short)(TmpVal3 & 0xffff); 331 DPRINT("BootSector.FATSectors = %hu\n", BootSector.FATSectors); 332 333 /* Init context data */ 334 Context->TotalSectorCount = 335 1 + (BootSector.FATSectors * 2) + RootDirSectors; 336 337 if (!QuickFormat) 338 { 339 Context->TotalSectorCount += SectorCount; 340 341 Status = FatWipeSectors(FileHandle, 342 SectorCount, 343 (ULONG)BootSector.SectorsPerCluster, 344 (ULONG)BootSector.BytesPerSector, 345 Context); 346 if (!NT_SUCCESS(Status)) 347 { 348 DPRINT("FatWipeSectors() failed with status 0x%.08x\n", Status); 349 return Status; 350 } 351 } 352 353 Status = Fat16WriteBootSector(FileHandle, 354 &BootSector, 355 Context); 356 if (!NT_SUCCESS(Status)) 357 { 358 DPRINT("Fat16WriteBootSector() failed with status 0x%.08x\n", Status); 359 return Status; 360 } 361 362 /* Write first FAT copy */ 363 Status = Fat16WriteFAT(FileHandle, 364 0, 365 &BootSector, 366 Context); 367 if (!NT_SUCCESS(Status)) 368 { 369 DPRINT("Fat16WriteFAT() failed with status 0x%.08x\n", Status); 370 return Status; 371 } 372 373 /* Write second FAT copy */ 374 Status = Fat16WriteFAT(FileHandle, 375 (ULONG)BootSector.FATSectors, 376 &BootSector, 377 Context); 378 if (!NT_SUCCESS(Status)) 379 { 380 DPRINT("Fat16WriteFAT() failed with status 0x%.08x.\n", Status); 381 return Status; 382 } 383 384 Status = Fat16WriteRootDirectory(FileHandle, 385 &BootSector, 386 Context); 387 if (!NT_SUCCESS(Status)) 388 { 389 DPRINT("Fat16WriteRootDirectory() failed with status 0x%.08x\n", Status); 390 } 391 392 return Status; 393 } 394