summaryrefslogtreecommitdiff
path: root/utils/ipodpatcher/fat32format.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipodpatcher/fat32format.c')
-rw-r--r--utils/ipodpatcher/fat32format.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/utils/ipodpatcher/fat32format.c b/utils/ipodpatcher/fat32format.c
new file mode 100644
index 0000000000..7ee8021cbf
--- /dev/null
+++ b/utils/ipodpatcher/fat32format.c
@@ -0,0 +1,530 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 *
11 * FAT32 formatting functions. Based on:
12 *
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.
19 *
20 *
21 * Modified June 2007 by Dave Chapman for use in ipodpatcher
22 *
23 *
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.
28 *
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
31 *
32 ****************************************************************************/
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <stdbool.h>
38#include <stdint.h>
39#include <inttypes.h>
40
41#include "ipodio.h"
42
43static inline uint16_t swap16(uint16_t value)
44{
45 return (value >> 8) | (value << 8);
46}
47
48static inline uint32_t swap32(uint32_t value)
49{
50 uint32_t hi = swap16(value >> 16);
51 uint32_t lo = swap16(value & 0xffff);
52 return (lo << 16) | hi;
53}
54
55/* The following functions are not the most efficient, but are
56 self-contained and don't require needing to know endianness of CPU
57 at compile-time.
58
59 Note that htole16/htole32 exist on some platforms, so for
60 simplicity we use different names.
61
62*/
63
64static uint16_t rb_htole16(uint16_t x)
65{
66 uint16_t test = 0x1234;
67 unsigned char* p = (unsigned char*)&test;
68
69 if (p[0]==0x12) {
70 /* Big-endian */
71 return swap16(x);
72 } else {
73 return x;
74 }
75}
76
77static uint32_t rb_htole32(uint32_t x)
78{
79 uint32_t test = 0x12345678;
80 unsigned char* p = (unsigned char*)&test;
81
82 if (p[0]==0x12) {
83 /* Big-endian */
84 return swap32(x);
85 } else {
86 return x;
87 }
88}
89
90
91/* TODO: Pass these as parameters to the various create_ functions */
92
93/* can be zero for default or 1,2,4,8,16,32 or 64 */
94static int sectors_per_cluster = 0;
95
96/* Recommended values */
97static uint32_t ReservedSectCount = 32;
98static uint32_t NumFATs = 2;
99static uint32_t BackupBootSect = 6;
100static uint32_t VolumeId=0; /* calculated before format */
101
102/* Calculated later */
103static uint32_t FatSize=0;
104static uint32_t BytesPerSect=0;
105static uint32_t SectorsPerCluster=0;
106static uint32_t TotalSectors=0;
107static uint32_t SystemAreaSize=0;
108static uint32_t UserAreaSize=0;
109static uint8_t VolId[12] = "NO NAME ";
110
111
112struct FAT_BOOTSECTOR32
113{
114 /* Common fields. */
115 uint8_t sJmpBoot[3];
116 char sOEMName[8];
117 uint16_t wBytsPerSec;
118 uint8_t bSecPerClus;
119 uint16_t wRsvdSecCnt;
120 uint8_t bNumFATs;
121 uint16_t wRootEntCnt;
122 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */
123 uint8_t bMedia;
124 uint16_t wFATSz16;
125 uint16_t wSecPerTrk;
126 uint16_t wNumHeads;
127 uint32_t dHiddSec;
128 uint32_t dTotSec32;
129
130 /* Fat 32/16 only */
131 uint32_t dFATSz32;
132 uint16_t wExtFlags;
133 uint16_t wFSVer;
134 uint32_t dRootClus;
135 uint16_t wFSInfo;
136 uint16_t wBkBootSec;
137 uint8_t Reserved[12];
138 uint8_t bDrvNum;
139 uint8_t Reserved1;
140 uint8_t bBootSig; /* == 0x29 if next three fields are ok */
141 uint32_t dBS_VolID;
142 uint8_t sVolLab[11];
143 uint8_t sBS_FilSysType[8];
144} __attribute__((packed));
145
146struct FAT_FSINFO {
147 uint32_t dLeadSig; // 0x41615252
148 uint8_t sReserved1[480]; // zeros
149 uint32_t dStrucSig; // 0x61417272
150 uint32_t dFree_Count; // 0xFFFFFFFF
151 uint32_t dNxt_Free; // 0xFFFFFFFF
152 uint8_t sReserved2[12]; // zeros
153 uint32_t dTrailSig; // 0xAA550000
154} __attribute__((packed));
155
156
157/* Write "count" zero sectors, starting at sector "sector" */
158static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
159{
160 int n;
161
162 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
163 fprintf(stderr,"[ERR] Seek failed\n");
164 return -1;
165 }
166
167 memset(ipod->sectorbuf, 0, 128 * ipod->sector_size);
168
169 /* Write 128 sectors at a time */
170 while (count) {
171 if (count >= 128)
172 n = 128;
173 else
174 n = count;
175
176 if (ipod_write(ipod,n * ipod->sector_size) < 0) {
177 perror("[ERR] Write failed in zero_sectors\n");
178 return -1;
179 }
180
181 count -= n;
182 }
183
184 return 0;
185}
186
187
188/*
18928.2 CALCULATING THE VOLUME SERIAL NUMBER
190
191For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
192seconds. DOS takes the date and time just before it writes it to the
193disk.
194
195Low order word is calculated: Volume Serial Number is:
196 Month & Day 12/26 0c1ah
197 Sec & Hundrenths 41:94 295eh 3578:1d02
198 -----
199 3578h
200
201High order word is calculated:
202 Hours & Minutes 21:55 1537h
203 Year 1995 07cbh
204 -----
205 1d02h
206*/
207static uint32_t get_volume_id ( )
208{
209 /* TODO */
210#if 0
211 SYSTEMTIME s;
212 uint32_t d;
213 uint16_t lo,hi,tmp;
214
215 GetLocalTime( &s );
216
217 lo = s.wDay + ( s.wMonth << 8 );
218 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
219 lo += tmp;
220
221 hi = s.wMinute + ( s.wHour << 8 );
222 hi += s.wYear;
223
224 d = lo + (hi << 16);
225 return(d);
226#endif
227 return(0);
228}
229
230/*
231This is the Microsoft calculation from FATGEN
232
233 uint32_t RootDirSectors = 0;
234 uint32_t TmpVal1, TmpVal2, FATSz;
235
236 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
237 TmpVal2 = (256 * SecPerClus) + NumFATs;
238 TmpVal2 = TmpVal2 / 2;
239 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
240
241 return( FatSz );
242*/
243
244
245static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
246 uint32_t SecPerClus, uint32_t NumFATs,
247 uint32_t BytesPerSect)
248{
249 uint64_t Numerator, Denominator;
250 uint64_t FatElementSize = 4;
251 uint64_t FatSz;
252
253 /* This is based on
254 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html
255 I've made the obvious changes for FAT32
256 */
257
258 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
259 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
260 FatSz = Numerator / Denominator;
261
262 /* round up */
263 FatSz += 1;
264
265 return((uint32_t)FatSz);
266}
267
268static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
269{
270 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
271 return( (uint8_t) spc );
272}
273
274static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
275 uint32_t BytesPerSect)
276{
277 uint8_t ret = 0x01; /* 1 sector per cluster */
278 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect;
279 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 );
280
281 /* 512 MB to 8,191 MB 4 KB */
282 if ( DiskSizeMB > 512 )
283 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
284
285 /* 8,192 MB to 16,383 MB 8 KB */
286 if ( DiskSizeMB > 8192 )
287 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
288
289 /* 16,384 MB to 32,767 MB 16 KB */
290 if ( DiskSizeMB > 16384 )
291 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
292
293 /* Larger than 32,768 MB 32 KB */
294 if ( DiskSizeMB > 32768 )
295 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
296
297 return( ret );
298
299}
300
301static void create_boot_sector(unsigned char* buf,
302 struct ipod_t* ipod, int partition)
303{
304 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
305
306 /* fill out the boot sector and fs info */
307 pFAT32BootSect->sJmpBoot[0]=0xEB;
308 pFAT32BootSect->sJmpBoot[1]=0x5A;
309 pFAT32BootSect->sJmpBoot[2]=0x90;
310 memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8 );
311 pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect);
312 pFAT32BootSect->bSecPerClus = SectorsPerCluster ;
313 pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount);
314 pFAT32BootSect->bNumFATs = NumFATs;
315 pFAT32BootSect->wRootEntCnt = rb_htole16(0);
316 pFAT32BootSect->wTotSec16 = rb_htole16(0);
317 pFAT32BootSect->bMedia = 0xF8;
318 pFAT32BootSect->wFATSz16 = rb_htole16(0);
319 pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track);
320 pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads);
321 pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start);
322 pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors);
323 pFAT32BootSect->dFATSz32 = rb_htole32(FatSize);
324 pFAT32BootSect->wExtFlags = rb_htole16(0);
325 pFAT32BootSect->wFSVer = rb_htole16(0);
326 pFAT32BootSect->dRootClus = rb_htole32(2);
327 pFAT32BootSect->wFSInfo = rb_htole16(1);
328 pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect);
329 pFAT32BootSect->bDrvNum = 0x80;
330 pFAT32BootSect->Reserved1 = 0;
331 pFAT32BootSect->bBootSig = 0x29;
332 pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId);
333 memcpy(pFAT32BootSect->sVolLab, VolId, 11);
334 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 );
335
336 buf[510] = 0x55;
337 buf[511] = 0xaa;
338}
339
340static void create_fsinfo(unsigned char* buf)
341{
342 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
343
344 /* FSInfo sect */
345 pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252);
346 pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272);
347 pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1);
348 pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1);
349 pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000);
350 pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1);
351
352 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
353 pFAT32FsInfo->dNxt_Free = rb_htole32(3);
354}
355
356static void create_firstfatsector(unsigned char* buf)
357{
358 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
359
360 /* First FAT Sector */
361 p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */
362 p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */
363 p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */
364}
365
366int format_partition(struct ipod_t* ipod, int partition)
367{
368 uint32_t i;
369 uint64_t qTotalSectors=0;
370 uint64_t FatNeeded;
371
372 VolumeId = get_volume_id( );
373
374 /* Only support hard disks at the moment */
375 if ( ipod->sector_size != 512 )
376 {
377 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
378 return -1;
379 }
380 BytesPerSect = ipod->sector_size;
381
382 /* Checks on Disk Size */
383 qTotalSectors = ipod->pinfo[partition].size;
384
385 /* low end limit - 65536 sectors */
386 if ( qTotalSectors < 65536 )
387 {
388 /* I suspect that most FAT32 implementations would mount this
389 volume just fine, but the spec says that we shouldn't do
390 this, so we won't */
391
392 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
393 return -1;
394 }
395
396 if ( qTotalSectors >= 0xffffffff )
397 {
398 /* This is a more fundamental limitation on FAT32 - the total
399 sector count in the root dir is 32bit. With a bit of
400 creativity, FAT32 could be extended to handle at least 2^28
401 clusters There would need to be an extra field in the
402 FSInfo sector, and the old sector count could be set to
403 0xffffffff. This is non standard though, the Windows FAT
404 driver FASTFAT.SYS won't understand this. Perhaps a future
405 version of FAT32 and FASTFAT will handle this. */
406
407 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
408 }
409
410 if ( sectors_per_cluster ) {
411 SectorsPerCluster = sectors_per_cluster;
412 } else {
413 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
414 BytesPerSect );
415 }
416
417 TotalSectors = (uint32_t) qTotalSectors;
418
419 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
420 SectorsPerCluster, NumFATs, BytesPerSect );
421
422 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
423
424 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
425 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
426
427 /* Work out the Cluster count */
428 FatNeeded = UserAreaSize/SectorsPerCluster;
429
430 /* check for a cluster count of >2^28, since the upper 4 bits of
431 the cluster values in the FAT are reserved. */
432 if (FatNeeded > 0x0FFFFFFF) {
433 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" );
434 return -1;
435 }
436
437 /* Sanity check, make sure the fat is big enough.
438 Convert the cluster count into a Fat sector count, and check
439 the fat size value we calculated earlier is OK. */
440
441 FatNeeded *=4;
442 FatNeeded += (BytesPerSect-1);
443 FatNeeded /= BytesPerSect;
444
445 if ( FatNeeded > FatSize ) {
446 fprintf(stderr,"[ERR] Drive too big to format\n");
447 return -1;
448 }
449
450 /*
451 Write boot sector, fats
452 Sector 0 Boot Sector
453 Sector 1 FSInfo
454 Sector 2 More boot code - we write zeros here
455 Sector 3 unused
456 Sector 4 unused
457 Sector 5 unused
458 Sector 6 Backup boot sector
459 Sector 7 Backup FSInfo sector
460 Sector 8 Backup 'more boot code'
461 zero'd sectors upto ReservedSectCount
462 FAT1 ReservedSectCount to ReservedSectCount + FatSize
463 ...
464 FATn ReservedSectCount to ReservedSectCount + FatSize
465 RootDir - allocated to cluster2
466 */
467
468 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track);
469 fprintf(stderr,"[INFO] Size : %" PRIu64 "GB %u sectors\n",
470 ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors );
471 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect );
472 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff );
473 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs );
474 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster );
475
476 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
477
478 /* Once zero_sectors has run, any data on the drive is basically lost... */
479 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize );
480
481 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
482
483 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
484
485 /* Create the boot sector structure */
486 create_boot_sector(ipod->sectorbuf, ipod, partition);
487 create_fsinfo(ipod->sectorbuf + 512);
488
489 /* Write boot sector and fsinfo at start of partition */
490 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) {
491 fprintf(stderr,"[ERR] Seek failed\n");
492 return -1;
493 }
494 if (ipod_write(ipod,512 * 2) < 0) {
495 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
496 return -1;
497 }
498
499 /* Write backup copy of boot sector and fsinfo */
500 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) {
501 fprintf(stderr,"[ERR] Seek failed\n");
502 return -1;
503 }
504 if (ipod_write(ipod,512 * 2) < 0) {
505 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
506 return -1;
507 }
508
509 /* Create the first FAT sector */
510 create_firstfatsector(ipod->sectorbuf);
511
512 /* Write the first fat sector in the right places */
513 for ( i=0; i<NumFATs; i++ ) {
514 int SectorStart = ReservedSectCount + (i * FatSize );
515
516 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
517 fprintf(stderr,"[ERR] Seek failed\n");
518 return -1;
519 }
520
521 if (ipod_write(ipod,512) < 0) {
522 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
523 return -1;
524 }
525 }
526
527 fprintf(stderr,"[INFO] Format successful\n");
528
529 return 0;
530}