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