Cleaner solution to plugin-included core files.
[kugel-rb.git] / rbutil / ipodpatcher / fat32format.c
blob0cbfb00754d6ab5cecd3cfe32be6201152b9fee0
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
11 * FAT32 formatting functions. Based on:
13 * Fat32 formatter version 1.03
14 * (c) Tom Thornhill 2005
15 * This software is covered by the GPL.
16 * By using this tool, you agree to absolve Ridgecrop of an liabilities for
17 * lost data.
18 * Please backup any data you value before using this tool.
21 * Modified June 2007 by Dave Chapman for use in ipodpatcher
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
32 ****************************************************************************/
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdbool.h>
38 #include <stdint.h>
40 #include "ipodio.h"
42 static inline uint16_t swap16(uint16_t value)
44 return (value >> 8) | (value << 8);
47 static inline uint32_t swap32(uint32_t value)
49 uint32_t hi = swap16(value >> 16);
50 uint32_t lo = swap16(value & 0xffff);
51 return (lo << 16) | hi;
54 /* The following functions are not the most efficient, but are
55 self-contained and don't require needing to know endianness of CPU
56 at compile-time.
59 uint16_t htole16(uint16_t x)
61 uint16_t test = 0x1234;
62 unsigned char* p = (unsigned char*)&test;
64 if (p[0]==0x12) {
65 /* Big-endian */
66 return swap16(x);
67 } else {
68 return x;
72 uint32_t htole32(uint32_t x)
74 uint32_t test = 0x12345678;
75 unsigned char* p = (unsigned char*)&test;
77 if (p[0]==0x12) {
78 /* Big-endian */
79 return swap32(x);
80 } else {
81 return x;
86 /* A large aligned buffer for disk I/O */
87 extern unsigned char* ipod_sectorbuf;
89 /* TODO: Pass these as parameters to the various create_ functions */
91 /* can be zero for default or 1,2,4,8,16,32 or 64 */
92 static int sectors_per_cluster = 0;
94 /* Recommended values */
95 static uint32_t ReservedSectCount = 32;
96 static uint32_t NumFATs = 2;
97 static uint32_t BackupBootSect = 6;
98 static uint32_t VolumeId=0; /* calculated before format */
100 /* Calculated later */
101 static uint32_t FatSize=0;
102 static uint32_t BytesPerSect=0;
103 static uint32_t SectorsPerCluster=0;
104 static uint32_t TotalSectors=0;
105 static uint32_t SystemAreaSize=0;
106 static uint32_t UserAreaSize=0;
107 static uint8_t VolId[12] = "NO NAME ";
110 struct FAT_BOOTSECTOR32
112 /* Common fields. */
113 uint8_t sJmpBoot[3];
114 char sOEMName[8];
115 uint16_t wBytsPerSec;
116 uint8_t bSecPerClus;
117 uint16_t wRsvdSecCnt;
118 uint8_t bNumFATs;
119 uint16_t wRootEntCnt;
120 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */
121 uint8_t bMedia;
122 uint16_t wFATSz16;
123 uint16_t wSecPerTrk;
124 uint16_t wNumHeads;
125 uint32_t dHiddSec;
126 uint32_t dTotSec32;
128 /* Fat 32/16 only */
129 uint32_t dFATSz32;
130 uint16_t wExtFlags;
131 uint16_t wFSVer;
132 uint32_t dRootClus;
133 uint16_t wFSInfo;
134 uint16_t wBkBootSec;
135 uint8_t Reserved[12];
136 uint8_t bDrvNum;
137 uint8_t Reserved1;
138 uint8_t bBootSig; /* == 0x29 if next three fields are ok */
139 uint32_t dBS_VolID;
140 uint8_t sVolLab[11];
141 uint8_t sBS_FilSysType[8];
142 } __attribute__((packed));
144 struct FAT_FSINFO {
145 uint32_t dLeadSig; // 0x41615252
146 uint8_t sReserved1[480]; // zeros
147 uint32_t dStrucSig; // 0x61417272
148 uint32_t dFree_Count; // 0xFFFFFFFF
149 uint32_t dNxt_Free; // 0xFFFFFFFF
150 uint8_t sReserved2[12]; // zeros
151 uint32_t dTrailSig; // 0xAA550000
152 } __attribute__((packed));
155 /* Write "count" zero sectors, starting at sector "sector" */
156 static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
158 int n;
160 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
161 fprintf(stderr,"[ERR] Seek failed\n");
162 return -1;
165 memset(ipod_sectorbuf, 0, 128 * ipod->sector_size);
167 /* Write 128 sectors at a time */
168 while (count) {
169 if (count >= 128)
170 n = 128;
171 else
172 n = count;
174 if (ipod_write(ipod,ipod_sectorbuf,n * ipod->sector_size) < 0) {
175 perror("[ERR] Write failed in zero_sectors\n");
176 return -1;
179 count -= n;
182 return 0;
187 28.2 CALCULATING THE VOLUME SERIAL NUMBER
189 For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
190 seconds. DOS takes the date and time just before it writes it to the
191 disk.
193 Low order word is calculated: Volume Serial Number is:
194 Month & Day 12/26 0c1ah
195 Sec & Hundrenths 41:94 295eh 3578:1d02
196 -----
197 3578h
199 High order word is calculated:
200 Hours & Minutes 21:55 1537h
201 Year 1995 07cbh
202 -----
203 1d02h
205 static uint32_t get_volume_id ( )
207 /* TODO */
208 #if 0
209 SYSTEMTIME s;
210 uint32_t d;
211 uint16_t lo,hi,tmp;
213 GetLocalTime( &s );
215 lo = s.wDay + ( s.wMonth << 8 );
216 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
217 lo += tmp;
219 hi = s.wMinute + ( s.wHour << 8 );
220 hi += s.wYear;
222 d = lo + (hi << 16);
223 return(d);
224 #endif
225 return(0);
229 This is the Microsoft calculation from FATGEN
231 uint32_t RootDirSectors = 0;
232 uint32_t TmpVal1, TmpVal2, FATSz;
234 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
235 TmpVal2 = (256 * SecPerClus) + NumFATs;
236 TmpVal2 = TmpVal2 / 2;
237 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
239 return( FatSz );
243 static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
244 uint32_t SecPerClus, uint32_t NumFATs,
245 uint32_t BytesPerSect)
247 uint64_t Numerator, Denominator;
248 uint64_t FatElementSize = 4;
249 uint64_t FatSz;
251 /* This is based on
252 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html
253 I've made the obvious changes for FAT32
256 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
257 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
258 FatSz = Numerator / Denominator;
260 /* round up */
261 FatSz += 1;
263 return((uint32_t)FatSz);
266 static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
268 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
269 return( (uint8_t) spc );
272 static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
273 uint32_t BytesPerSect)
275 uint8_t ret = 0x01; /* 1 sector per cluster */
276 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect;
277 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 );
279 /* 512 MB to 8,191 MB 4 KB */
280 if ( DiskSizeMB > 512 )
281 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
283 /* 8,192 MB to 16,383 MB 8 KB */
284 if ( DiskSizeMB > 8192 )
285 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
287 /* 16,384 MB to 32,767 MB 16 KB */
288 if ( DiskSizeMB > 16384 )
289 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
291 /* Larger than 32,768 MB 32 KB */
292 if ( DiskSizeMB > 32768 )
293 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
295 return( ret );
299 static void create_boot_sector(unsigned char* buf,
300 struct ipod_t* ipod, int partition)
302 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
304 /* fill out the boot sector and fs info */
305 pFAT32BootSect->sJmpBoot[0]=0xEB;
306 pFAT32BootSect->sJmpBoot[1]=0x5A;
307 pFAT32BootSect->sJmpBoot[2]=0x90;
308 strcpy( pFAT32BootSect->sOEMName, "MSWIN4.1" );
309 pFAT32BootSect->wBytsPerSec = htole16(BytesPerSect);
310 pFAT32BootSect->bSecPerClus = SectorsPerCluster ;
311 pFAT32BootSect->wRsvdSecCnt = htole16(ReservedSectCount);
312 pFAT32BootSect->bNumFATs = NumFATs;
313 pFAT32BootSect->wRootEntCnt = htole16(0);
314 pFAT32BootSect->wTotSec16 = htole16(0);
315 pFAT32BootSect->bMedia = 0xF8;
316 pFAT32BootSect->wFATSz16 = htole16(0);
317 pFAT32BootSect->wSecPerTrk = htole16(ipod->sectors_per_track);
318 pFAT32BootSect->wNumHeads = htole16(ipod->num_heads);
319 pFAT32BootSect->dHiddSec = htole16(ipod->pinfo[partition].start);
320 pFAT32BootSect->dTotSec32 = htole32(TotalSectors);
321 pFAT32BootSect->dFATSz32 = htole32(FatSize);
322 pFAT32BootSect->wExtFlags = htole16(0);
323 pFAT32BootSect->wFSVer = htole16(0);
324 pFAT32BootSect->dRootClus = htole32(2);
325 pFAT32BootSect->wFSInfo = htole16(1);
326 pFAT32BootSect->wBkBootSec = htole16(BackupBootSect);
327 pFAT32BootSect->bDrvNum = 0x80;
328 pFAT32BootSect->Reserved1 = 0;
329 pFAT32BootSect->bBootSig = 0x29;
330 pFAT32BootSect->dBS_VolID = htole32(VolumeId);
331 memcpy(pFAT32BootSect->sVolLab, VolId, 11);
332 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 );
334 buf[510] = 0x55;
335 buf[511] = 0xaa;
338 static void create_fsinfo(unsigned char* buf)
340 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
342 /* FSInfo sect */
343 pFAT32FsInfo->dLeadSig = htole32(0x41615252);
344 pFAT32FsInfo->dStrucSig = htole32(0x61417272);
345 pFAT32FsInfo->dFree_Count = htole32((uint32_t) -1);
346 pFAT32FsInfo->dNxt_Free = htole32((uint32_t) -1);
347 pFAT32FsInfo->dTrailSig = htole32(0xaa550000);
348 pFAT32FsInfo->dFree_Count = htole32((UserAreaSize/SectorsPerCluster)-1);
350 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
351 pFAT32FsInfo->dNxt_Free = htole32(3);
354 static void create_firstfatsector(unsigned char* buf)
356 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
358 /* First FAT Sector */
359 p[0] = htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */
360 p[1] = htole32(0x0fffffff); /* Reserved cluster 2 EOC */
361 p[2] = htole32(0x0fffffff); /* end of cluster chain for root dir */
364 int format_partition(struct ipod_t* ipod, int partition)
366 uint32_t i;
367 uint64_t qTotalSectors=0;
368 uint64_t FatNeeded;
370 VolumeId = get_volume_id( );
372 /* Only support hard disks at the moment */
373 if ( ipod->sector_size != 512 )
375 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
376 return -1;
378 BytesPerSect = ipod->sector_size;
380 /* Checks on Disk Size */
381 qTotalSectors = ipod->pinfo[partition].size;
383 /* low end limit - 65536 sectors */
384 if ( qTotalSectors < 65536 )
386 /* I suspect that most FAT32 implementations would mount this
387 volume just fine, but the spec says that we shouldn't do
388 this, so we won't */
390 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
391 return -1;
394 if ( qTotalSectors >= 0xffffffff )
396 /* This is a more fundamental limitation on FAT32 - the total
397 sector count in the root dir is 32bit. With a bit of
398 creativity, FAT32 could be extended to handle at least 2^28
399 clusters There would need to be an extra field in the
400 FSInfo sector, and the old sector count could be set to
401 0xffffffff. This is non standard though, the Windows FAT
402 driver FASTFAT.SYS won't understand this. Perhaps a future
403 version of FAT32 and FASTFAT will handle this. */
405 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
408 if ( sectors_per_cluster ) {
409 SectorsPerCluster = sectors_per_cluster;
410 } else {
411 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
412 BytesPerSect );
415 TotalSectors = (uint32_t) qTotalSectors;
417 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
418 SectorsPerCluster, NumFATs, BytesPerSect );
420 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
422 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
423 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
425 /* Work out the Cluster count */
426 FatNeeded = UserAreaSize/SectorsPerCluster;
428 /* check for a cluster count of >2^28, since the upper 4 bits of
429 the cluster values in the FAT are reserved. */
430 if (FatNeeded > 0x0FFFFFFF) {
431 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" );
432 return -1;
435 /* Sanity check, make sure the fat is big enough.
436 Convert the cluster count into a Fat sector count, and check
437 the fat size value we calculated earlier is OK. */
439 FatNeeded *=4;
440 FatNeeded += (BytesPerSect-1);
441 FatNeeded /= BytesPerSect;
443 if ( FatNeeded > FatSize ) {
444 fprintf(stderr,"[ERR] Drive too big to format\n");
445 return -1;
449 Write boot sector, fats
450 Sector 0 Boot Sector
451 Sector 1 FSInfo
452 Sector 2 More boot code - we write zeros here
453 Sector 3 unused
454 Sector 4 unused
455 Sector 5 unused
456 Sector 6 Backup boot sector
457 Sector 7 Backup FSInfo sector
458 Sector 8 Backup 'more boot code'
459 zero'd sectors upto ReservedSectCount
460 FAT1 ReservedSectCount to ReservedSectCount + FatSize
462 FATn ReservedSectCount to ReservedSectCount + FatSize
463 RootDir - allocated to cluster2
466 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track);
467 fprintf(stderr,"[INFO] Size : %lluGB %u sectors\n", ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors );
468 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect );
469 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff );
470 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs );
471 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster );
473 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
475 /* Once zero_sectors has run, any data on the drive is basically lost... */
476 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize );
478 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
480 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
482 /* Create the boot sector structure */
483 create_boot_sector(ipod_sectorbuf, ipod, partition);
484 create_fsinfo(ipod_sectorbuf + 512);
486 /* Write boot sector and fsinfo at start of partition */
487 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) {
488 fprintf(stderr,"[ERR] Seek failed\n");
489 return -1;
491 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
492 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
493 return -1;
496 /* Write backup copy of boot sector and fsinfo */
497 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) {
498 fprintf(stderr,"[ERR] Seek failed\n");
499 return -1;
501 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
502 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
503 return -1;
506 /* Create the first FAT sector */
507 create_firstfatsector(ipod_sectorbuf);
509 /* Write the first fat sector in the right places */
510 for ( i=0; i<NumFATs; i++ ) {
511 int SectorStart = ReservedSectCount + (i * FatSize );
513 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
514 fprintf(stderr,"[ERR] Seek failed\n");
515 return -1;
518 if (ipod_write(ipod,ipod_sectorbuf,512) < 0) {
519 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
520 return -1;
524 fprintf(stderr,"[INFO] Format successful\n");
526 return 0;