Prepare new maemo release
[maemo-rb.git] / rbutil / ipodpatcher / fat32format.c
blob7ee8021cbfd0b2b676b5cbc612dc085b28befa29
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>
39 #include <inttypes.h>
41 #include "ipodio.h"
43 static inline uint16_t swap16(uint16_t value)
45 return (value >> 8) | (value << 8);
48 static inline uint32_t swap32(uint32_t value)
50 uint32_t hi = swap16(value >> 16);
51 uint32_t lo = swap16(value & 0xffff);
52 return (lo << 16) | hi;
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.
59 Note that htole16/htole32 exist on some platforms, so for
60 simplicity we use different names.
64 static uint16_t rb_htole16(uint16_t x)
66 uint16_t test = 0x1234;
67 unsigned char* p = (unsigned char*)&test;
69 if (p[0]==0x12) {
70 /* Big-endian */
71 return swap16(x);
72 } else {
73 return x;
77 static uint32_t rb_htole32(uint32_t x)
79 uint32_t test = 0x12345678;
80 unsigned char* p = (unsigned char*)&test;
82 if (p[0]==0x12) {
83 /* Big-endian */
84 return swap32(x);
85 } else {
86 return x;
91 /* TODO: Pass these as parameters to the various create_ functions */
93 /* can be zero for default or 1,2,4,8,16,32 or 64 */
94 static int sectors_per_cluster = 0;
96 /* Recommended values */
97 static uint32_t ReservedSectCount = 32;
98 static uint32_t NumFATs = 2;
99 static uint32_t BackupBootSect = 6;
100 static uint32_t VolumeId=0; /* calculated before format */
102 /* Calculated later */
103 static uint32_t FatSize=0;
104 static uint32_t BytesPerSect=0;
105 static uint32_t SectorsPerCluster=0;
106 static uint32_t TotalSectors=0;
107 static uint32_t SystemAreaSize=0;
108 static uint32_t UserAreaSize=0;
109 static uint8_t VolId[12] = "NO NAME ";
112 struct FAT_BOOTSECTOR32
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;
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));
146 struct 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));
157 /* Write "count" zero sectors, starting at sector "sector" */
158 static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
160 int n;
162 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
163 fprintf(stderr,"[ERR] Seek failed\n");
164 return -1;
167 memset(ipod->sectorbuf, 0, 128 * ipod->sector_size);
169 /* Write 128 sectors at a time */
170 while (count) {
171 if (count >= 128)
172 n = 128;
173 else
174 n = count;
176 if (ipod_write(ipod,n * ipod->sector_size) < 0) {
177 perror("[ERR] Write failed in zero_sectors\n");
178 return -1;
181 count -= n;
184 return 0;
189 28.2 CALCULATING THE VOLUME SERIAL NUMBER
191 For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
192 seconds. DOS takes the date and time just before it writes it to the
193 disk.
195 Low 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
201 High order word is calculated:
202 Hours & Minutes 21:55 1537h
203 Year 1995 07cbh
204 -----
205 1d02h
207 static uint32_t get_volume_id ( )
209 /* TODO */
210 #if 0
211 SYSTEMTIME s;
212 uint32_t d;
213 uint16_t lo,hi,tmp;
215 GetLocalTime( &s );
217 lo = s.wDay + ( s.wMonth << 8 );
218 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
219 lo += tmp;
221 hi = s.wMinute + ( s.wHour << 8 );
222 hi += s.wYear;
224 d = lo + (hi << 16);
225 return(d);
226 #endif
227 return(0);
231 This is the Microsoft calculation from FATGEN
233 uint32_t RootDirSectors = 0;
234 uint32_t TmpVal1, TmpVal2, FATSz;
236 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
237 TmpVal2 = (256 * SecPerClus) + NumFATs;
238 TmpVal2 = TmpVal2 / 2;
239 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
241 return( FatSz );
245 static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
246 uint32_t SecPerClus, uint32_t NumFATs,
247 uint32_t BytesPerSect)
249 uint64_t Numerator, Denominator;
250 uint64_t FatElementSize = 4;
251 uint64_t FatSz;
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
258 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
259 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
260 FatSz = Numerator / Denominator;
262 /* round up */
263 FatSz += 1;
265 return((uint32_t)FatSz);
268 static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
270 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
271 return( (uint8_t) spc );
274 static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
275 uint32_t BytesPerSect)
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 );
281 /* 512 MB to 8,191 MB 4 KB */
282 if ( DiskSizeMB > 512 )
283 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
285 /* 8,192 MB to 16,383 MB 8 KB */
286 if ( DiskSizeMB > 8192 )
287 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
289 /* 16,384 MB to 32,767 MB 16 KB */
290 if ( DiskSizeMB > 16384 )
291 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
293 /* Larger than 32,768 MB 32 KB */
294 if ( DiskSizeMB > 32768 )
295 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
297 return( ret );
301 static void create_boot_sector(unsigned char* buf,
302 struct ipod_t* ipod, int partition)
304 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
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 );
336 buf[510] = 0x55;
337 buf[511] = 0xaa;
340 static void create_fsinfo(unsigned char* buf)
342 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
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);
352 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
353 pFAT32FsInfo->dNxt_Free = rb_htole32(3);
356 static void create_firstfatsector(unsigned char* buf)
358 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
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 */
366 int format_partition(struct ipod_t* ipod, int partition)
368 uint32_t i;
369 uint64_t qTotalSectors=0;
370 uint64_t FatNeeded;
372 VolumeId = get_volume_id( );
374 /* Only support hard disks at the moment */
375 if ( ipod->sector_size != 512 )
377 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
378 return -1;
380 BytesPerSect = ipod->sector_size;
382 /* Checks on Disk Size */
383 qTotalSectors = ipod->pinfo[partition].size;
385 /* low end limit - 65536 sectors */
386 if ( qTotalSectors < 65536 )
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 */
392 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
393 return -1;
396 if ( qTotalSectors >= 0xffffffff )
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. */
407 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
410 if ( sectors_per_cluster ) {
411 SectorsPerCluster = sectors_per_cluster;
412 } else {
413 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
414 BytesPerSect );
417 TotalSectors = (uint32_t) qTotalSectors;
419 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
420 SectorsPerCluster, NumFATs, BytesPerSect );
422 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
424 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
425 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
427 /* Work out the Cluster count */
428 FatNeeded = UserAreaSize/SectorsPerCluster;
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;
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. */
441 FatNeeded *=4;
442 FatNeeded += (BytesPerSect-1);
443 FatNeeded /= BytesPerSect;
445 if ( FatNeeded > FatSize ) {
446 fprintf(stderr,"[ERR] Drive too big to format\n");
447 return -1;
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
464 FATn ReservedSectCount to ReservedSectCount + FatSize
465 RootDir - allocated to cluster2
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 );
476 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
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 );
481 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
483 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
485 /* Create the boot sector structure */
486 create_boot_sector(ipod->sectorbuf, ipod, partition);
487 create_fsinfo(ipod->sectorbuf + 512);
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;
494 if (ipod_write(ipod,512 * 2) < 0) {
495 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
496 return -1;
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;
504 if (ipod_write(ipod,512 * 2) < 0) {
505 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
506 return -1;
509 /* Create the first FAT sector */
510 create_firstfatsector(ipod->sectorbuf);
512 /* Write the first fat sector in the right places */
513 for ( i=0; i<NumFATs; i++ ) {
514 int SectorStart = ReservedSectCount + (i * FatSize );
516 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
517 fprintf(stderr,"[ERR] Seek failed\n");
518 return -1;
521 if (ipod_write(ipod,512) < 0) {
522 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
523 return -1;
527 fprintf(stderr,"[INFO] Format successful\n");
529 return 0;