FS#10446: Bug defense in dsp.c, minor tweaks and comments
[kugel-rb/myfork.git] / rbutil / ipodpatcher / fat32format.c
blob3dced355b6e8b38d89d1763c5926e03ca4ce20c0
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.
58 Note that htole16/htole32 exist on some platforms, so for
59 simplicity we use different names.
63 static uint16_t rb_htole16(uint16_t x)
65 uint16_t test = 0x1234;
66 unsigned char* p = (unsigned char*)&test;
68 if (p[0]==0x12) {
69 /* Big-endian */
70 return swap16(x);
71 } else {
72 return x;
76 static uint32_t rb_htole32(uint32_t x)
78 uint32_t test = 0x12345678;
79 unsigned char* p = (unsigned char*)&test;
81 if (p[0]==0x12) {
82 /* Big-endian */
83 return swap32(x);
84 } else {
85 return x;
90 /* A large aligned buffer for disk I/O */
91 extern unsigned char* ipod_sectorbuf;
93 /* TODO: Pass these as parameters to the various create_ functions */
95 /* can be zero for default or 1,2,4,8,16,32 or 64 */
96 static int sectors_per_cluster = 0;
98 /* Recommended values */
99 static uint32_t ReservedSectCount = 32;
100 static uint32_t NumFATs = 2;
101 static uint32_t BackupBootSect = 6;
102 static uint32_t VolumeId=0; /* calculated before format */
104 /* Calculated later */
105 static uint32_t FatSize=0;
106 static uint32_t BytesPerSect=0;
107 static uint32_t SectorsPerCluster=0;
108 static uint32_t TotalSectors=0;
109 static uint32_t SystemAreaSize=0;
110 static uint32_t UserAreaSize=0;
111 static uint8_t VolId[12] = "NO NAME ";
114 struct FAT_BOOTSECTOR32
116 /* Common fields. */
117 uint8_t sJmpBoot[3];
118 char sOEMName[8];
119 uint16_t wBytsPerSec;
120 uint8_t bSecPerClus;
121 uint16_t wRsvdSecCnt;
122 uint8_t bNumFATs;
123 uint16_t wRootEntCnt;
124 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */
125 uint8_t bMedia;
126 uint16_t wFATSz16;
127 uint16_t wSecPerTrk;
128 uint16_t wNumHeads;
129 uint32_t dHiddSec;
130 uint32_t dTotSec32;
132 /* Fat 32/16 only */
133 uint32_t dFATSz32;
134 uint16_t wExtFlags;
135 uint16_t wFSVer;
136 uint32_t dRootClus;
137 uint16_t wFSInfo;
138 uint16_t wBkBootSec;
139 uint8_t Reserved[12];
140 uint8_t bDrvNum;
141 uint8_t Reserved1;
142 uint8_t bBootSig; /* == 0x29 if next three fields are ok */
143 uint32_t dBS_VolID;
144 uint8_t sVolLab[11];
145 uint8_t sBS_FilSysType[8];
146 } __attribute__((packed));
148 struct FAT_FSINFO {
149 uint32_t dLeadSig; // 0x41615252
150 uint8_t sReserved1[480]; // zeros
151 uint32_t dStrucSig; // 0x61417272
152 uint32_t dFree_Count; // 0xFFFFFFFF
153 uint32_t dNxt_Free; // 0xFFFFFFFF
154 uint8_t sReserved2[12]; // zeros
155 uint32_t dTrailSig; // 0xAA550000
156 } __attribute__((packed));
159 /* Write "count" zero sectors, starting at sector "sector" */
160 static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
162 int n;
164 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
165 fprintf(stderr,"[ERR] Seek failed\n");
166 return -1;
169 memset(ipod_sectorbuf, 0, 128 * ipod->sector_size);
171 /* Write 128 sectors at a time */
172 while (count) {
173 if (count >= 128)
174 n = 128;
175 else
176 n = count;
178 if (ipod_write(ipod,ipod_sectorbuf,n * ipod->sector_size) < 0) {
179 perror("[ERR] Write failed in zero_sectors\n");
180 return -1;
183 count -= n;
186 return 0;
191 28.2 CALCULATING THE VOLUME SERIAL NUMBER
193 For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
194 seconds. DOS takes the date and time just before it writes it to the
195 disk.
197 Low order word is calculated: Volume Serial Number is:
198 Month & Day 12/26 0c1ah
199 Sec & Hundrenths 41:94 295eh 3578:1d02
200 -----
201 3578h
203 High order word is calculated:
204 Hours & Minutes 21:55 1537h
205 Year 1995 07cbh
206 -----
207 1d02h
209 static uint32_t get_volume_id ( )
211 /* TODO */
212 #if 0
213 SYSTEMTIME s;
214 uint32_t d;
215 uint16_t lo,hi,tmp;
217 GetLocalTime( &s );
219 lo = s.wDay + ( s.wMonth << 8 );
220 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
221 lo += tmp;
223 hi = s.wMinute + ( s.wHour << 8 );
224 hi += s.wYear;
226 d = lo + (hi << 16);
227 return(d);
228 #endif
229 return(0);
233 This is the Microsoft calculation from FATGEN
235 uint32_t RootDirSectors = 0;
236 uint32_t TmpVal1, TmpVal2, FATSz;
238 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
239 TmpVal2 = (256 * SecPerClus) + NumFATs;
240 TmpVal2 = TmpVal2 / 2;
241 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
243 return( FatSz );
247 static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
248 uint32_t SecPerClus, uint32_t NumFATs,
249 uint32_t BytesPerSect)
251 uint64_t Numerator, Denominator;
252 uint64_t FatElementSize = 4;
253 uint64_t FatSz;
255 /* This is based on
256 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html
257 I've made the obvious changes for FAT32
260 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
261 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
262 FatSz = Numerator / Denominator;
264 /* round up */
265 FatSz += 1;
267 return((uint32_t)FatSz);
270 static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
272 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
273 return( (uint8_t) spc );
276 static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
277 uint32_t BytesPerSect)
279 uint8_t ret = 0x01; /* 1 sector per cluster */
280 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect;
281 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 );
283 /* 512 MB to 8,191 MB 4 KB */
284 if ( DiskSizeMB > 512 )
285 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
287 /* 8,192 MB to 16,383 MB 8 KB */
288 if ( DiskSizeMB > 8192 )
289 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
291 /* 16,384 MB to 32,767 MB 16 KB */
292 if ( DiskSizeMB > 16384 )
293 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
295 /* Larger than 32,768 MB 32 KB */
296 if ( DiskSizeMB > 32768 )
297 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
299 return( ret );
303 static void create_boot_sector(unsigned char* buf,
304 struct ipod_t* ipod, int partition)
306 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
308 /* fill out the boot sector and fs info */
309 pFAT32BootSect->sJmpBoot[0]=0xEB;
310 pFAT32BootSect->sJmpBoot[1]=0x5A;
311 pFAT32BootSect->sJmpBoot[2]=0x90;
312 strcpy( pFAT32BootSect->sOEMName, "MSWIN4.1" );
313 pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect);
314 pFAT32BootSect->bSecPerClus = SectorsPerCluster ;
315 pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount);
316 pFAT32BootSect->bNumFATs = NumFATs;
317 pFAT32BootSect->wRootEntCnt = rb_htole16(0);
318 pFAT32BootSect->wTotSec16 = rb_htole16(0);
319 pFAT32BootSect->bMedia = 0xF8;
320 pFAT32BootSect->wFATSz16 = rb_htole16(0);
321 pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track);
322 pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads);
323 pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start);
324 pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors);
325 pFAT32BootSect->dFATSz32 = rb_htole32(FatSize);
326 pFAT32BootSect->wExtFlags = rb_htole16(0);
327 pFAT32BootSect->wFSVer = rb_htole16(0);
328 pFAT32BootSect->dRootClus = rb_htole32(2);
329 pFAT32BootSect->wFSInfo = rb_htole16(1);
330 pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect);
331 pFAT32BootSect->bDrvNum = 0x80;
332 pFAT32BootSect->Reserved1 = 0;
333 pFAT32BootSect->bBootSig = 0x29;
334 pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId);
335 memcpy(pFAT32BootSect->sVolLab, VolId, 11);
336 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 );
338 buf[510] = 0x55;
339 buf[511] = 0xaa;
342 static void create_fsinfo(unsigned char* buf)
344 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
346 /* FSInfo sect */
347 pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252);
348 pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272);
349 pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1);
350 pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1);
351 pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000);
352 pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1);
354 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
355 pFAT32FsInfo->dNxt_Free = rb_htole32(3);
358 static void create_firstfatsector(unsigned char* buf)
360 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
362 /* First FAT Sector */
363 p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */
364 p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */
365 p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */
368 int format_partition(struct ipod_t* ipod, int partition)
370 uint32_t i;
371 uint64_t qTotalSectors=0;
372 uint64_t FatNeeded;
374 VolumeId = get_volume_id( );
376 /* Only support hard disks at the moment */
377 if ( ipod->sector_size != 512 )
379 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
380 return -1;
382 BytesPerSect = ipod->sector_size;
384 /* Checks on Disk Size */
385 qTotalSectors = ipod->pinfo[partition].size;
387 /* low end limit - 65536 sectors */
388 if ( qTotalSectors < 65536 )
390 /* I suspect that most FAT32 implementations would mount this
391 volume just fine, but the spec says that we shouldn't do
392 this, so we won't */
394 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
395 return -1;
398 if ( qTotalSectors >= 0xffffffff )
400 /* This is a more fundamental limitation on FAT32 - the total
401 sector count in the root dir is 32bit. With a bit of
402 creativity, FAT32 could be extended to handle at least 2^28
403 clusters There would need to be an extra field in the
404 FSInfo sector, and the old sector count could be set to
405 0xffffffff. This is non standard though, the Windows FAT
406 driver FASTFAT.SYS won't understand this. Perhaps a future
407 version of FAT32 and FASTFAT will handle this. */
409 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
412 if ( sectors_per_cluster ) {
413 SectorsPerCluster = sectors_per_cluster;
414 } else {
415 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
416 BytesPerSect );
419 TotalSectors = (uint32_t) qTotalSectors;
421 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
422 SectorsPerCluster, NumFATs, BytesPerSect );
424 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
426 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
427 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
429 /* Work out the Cluster count */
430 FatNeeded = UserAreaSize/SectorsPerCluster;
432 /* check for a cluster count of >2^28, since the upper 4 bits of
433 the cluster values in the FAT are reserved. */
434 if (FatNeeded > 0x0FFFFFFF) {
435 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" );
436 return -1;
439 /* Sanity check, make sure the fat is big enough.
440 Convert the cluster count into a Fat sector count, and check
441 the fat size value we calculated earlier is OK. */
443 FatNeeded *=4;
444 FatNeeded += (BytesPerSect-1);
445 FatNeeded /= BytesPerSect;
447 if ( FatNeeded > FatSize ) {
448 fprintf(stderr,"[ERR] Drive too big to format\n");
449 return -1;
453 Write boot sector, fats
454 Sector 0 Boot Sector
455 Sector 1 FSInfo
456 Sector 2 More boot code - we write zeros here
457 Sector 3 unused
458 Sector 4 unused
459 Sector 5 unused
460 Sector 6 Backup boot sector
461 Sector 7 Backup FSInfo sector
462 Sector 8 Backup 'more boot code'
463 zero'd sectors upto ReservedSectCount
464 FAT1 ReservedSectCount to ReservedSectCount + FatSize
466 FATn ReservedSectCount to ReservedSectCount + FatSize
467 RootDir - allocated to cluster2
470 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track);
471 fprintf(stderr,"[INFO] Size : %lluGB %u sectors\n", ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors );
472 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect );
473 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff );
474 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs );
475 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster );
477 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
479 /* Once zero_sectors has run, any data on the drive is basically lost... */
480 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize );
482 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
484 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
486 /* Create the boot sector structure */
487 create_boot_sector(ipod_sectorbuf, ipod, partition);
488 create_fsinfo(ipod_sectorbuf + 512);
490 /* Write boot sector and fsinfo at start of partition */
491 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) {
492 fprintf(stderr,"[ERR] Seek failed\n");
493 return -1;
495 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
496 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
497 return -1;
500 /* Write backup copy of boot sector and fsinfo */
501 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) {
502 fprintf(stderr,"[ERR] Seek failed\n");
503 return -1;
505 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
506 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
507 return -1;
510 /* Create the first FAT sector */
511 create_firstfatsector(ipod_sectorbuf);
513 /* Write the first fat sector in the right places */
514 for ( i=0; i<NumFATs; i++ ) {
515 int SectorStart = ReservedSectCount + (i * FatSize );
517 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
518 fprintf(stderr,"[ERR] Seek failed\n");
519 return -1;
522 if (ipod_write(ipod,ipod_sectorbuf,512) < 0) {
523 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
524 return -1;
528 fprintf(stderr,"[INFO] Format successful\n");
530 return 0;