diff --git a/tools/ipodpatcher/Makefile b/tools/ipodpatcher/Makefile new file mode 100644 index 0000000000..6eb3dce75e --- /dev/null +++ b/tools/ipodpatcher/Makefile @@ -0,0 +1,8 @@ +all: ipodpatcher.exe + +ipodpatcher.exe: ipodpatcher.c + i586-mingw32msvc-gcc -Wall -o ipodpatcher.exe ipodpatcher.c + i586-mingw32msvc-strip ipodpatcher.exe + +clean: + rm -f ipodpatcher.exe *~ diff --git a/tools/ipodpatcher/ipodpatcher.c b/tools/ipodpatcher/ipodpatcher.c new file mode 100644 index 0000000000..7d311c835a --- /dev/null +++ b/tools/ipodpatcher/ipodpatcher.c @@ -0,0 +1,443 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Dave Chapman + * + * error(), lock_volume() and unlock_volume() functions and inspiration taken + * from: + * RawDisk - Direct Disk Read/Write Access for NT/2000/XP + * Copyright (c) 2003 Jan Kiszka + * http://www.stud.uni-hannover.de/user/73174/RawDisk/ + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parttypes.h" + +/* Windows requires the buffer for disk I/O to be aligned in memory on a + multiple of the disk volume size - so we use a single global variable + and initialise it in main() +*/ +unsigned char* sectorbuf; + +char* get_parttype(int pt) +{ + int i; + static char unknown[]="Unknown"; + + i=0; + while (parttypes[i].name != NULL) { + if (parttypes[i].type == pt) { + return (parttypes[i].name); + } + i++; + } + + return unknown; +} + +void error(char* msg) +{ + char* pMsgBuf; + + printf(msg); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pMsgBuf, + 0, NULL); + printf(pMsgBuf); + LocalFree(pMsgBuf); +} + +int lock_volume(HANDLE hDisk) +{ + DWORD dummy; + + return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, + &dummy, NULL); +} + +int unlock_volume(HANDLE hDisk) +{ + DWORD dummy; + + return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, + &dummy, NULL); +} + +off_t filesize(int fd) { + struct stat buf; + + if (fstat(fd,&buf) < 0) { + perror("[ERR] Checking filesize of input file"); + return -1; + } else { + return(buf.st_size); + } +} + + +/* Size of buffer for disk I/O */ +#define BUFFER_SIZE 32*1024 + +/* Partition table parsing code taken from Rockbox */ + +#define SECTOR_SIZE 512 + +struct partinfo { + unsigned long start; /* first sector (LBA) */ + unsigned long size; /* number of sectors */ + unsigned char type; +}; + +#define BYTES2INT32(array,pos) \ + ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ + ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) + +void display_partinfo(struct partinfo* pinfo) +{ + int i; + + printf("Part Start Sector End Sector Size (MB) Type\n"); + for ( i = 0; i < 4; i++ ) { + if (pinfo[i].start != 0) { + printf(" %d %10ld %10ld %10.1f %s (0x%02x)\n",i,pinfo[i].start,pinfo[i].start+pinfo[i].size-1,pinfo[i].size/2048.0,get_parttype(pinfo[i].type),pinfo[i].type); + } + } +} + + +int read_partinfo(HANDLE dh, struct partinfo* pinfo) +{ + int i; + unsigned char sector[SECTOR_SIZE]; + unsigned long count; + + if (!ReadFile(dh, sector, SECTOR_SIZE, &count, NULL)) { + error(" Error reading from disk: "); + return -1; + } + + /* check that the boot sector is initialized */ + if ( (sector[510] != 0x55) || + (sector[511] != 0xaa)) { + fprintf(stderr,"Bad boot sector signature\n"); + return 0; + } + + if (memcmp(§or[71],"iPod",4) != 0) { + fprintf(stderr,"Drive is not an iPod, aborting\n"); + return -1; + } + + /* parse partitions */ + for ( i = 0; i < 4; i++ ) { + unsigned char* ptr = sector + 0x1be + 16*i; + pinfo[i].type = ptr[4]; + pinfo[i].start = BYTES2INT32(ptr, 8); + pinfo[i].size = BYTES2INT32(ptr, 12); + + /* extended? */ + if ( pinfo[i].type == 5 ) { + /* not handled yet */ + } + } + + return 0; +} + +int disk_read(HANDLE dh, int outfile,unsigned long start, unsigned long count) +{ + int res; + unsigned long n; + int bytesleft; + int chunksize; + + fprintf(stderr,"[INFO] Seeking to sector %ld\n",start); + + if (SetFilePointer(dh, start*SECTOR_SIZE, NULL, FILE_BEGIN)==0xffffffff) { + error(" Seek error "); + return -1; + } + + fprintf(stderr,"[INFO] Writing %ld sectors to output file\n",count); + + bytesleft = count * SECTOR_SIZE; + while (bytesleft > 0) { + if (bytesleft > BUFFER_SIZE) { + chunksize = BUFFER_SIZE; + } else { + chunksize = bytesleft; + } + + if (!ReadFile(dh, sectorbuf, chunksize, &n, NULL)) { + error("[ERR] read in disk_read"); + return -1; + } + + if (n < chunksize) { + fprintf(stderr,"[ERR] Short read in disk_read() - requested %d, got %lu\n",chunksize,n); + return -1; + } + + bytesleft -= n; + + res = write(outfile,sectorbuf,n); + + if (res < 0) { + perror("[ERR] write in disk_read"); + return -1; + } + + if (res != n) { + fprintf(stderr,"Short write - requested %d, received %d - aborting.\n",SECTOR_SIZE,res); + return -1; + } + } + + fprintf(stderr,"[INFO] Done.\n"); + return 0; +} + +int disk_write(HANDLE dh, int infile,unsigned long start) +{ + unsigned long res; + int n; + int bytesread; + int byteswritten = 0; + int eof; + int padding = 0; + + if (SetFilePointer(dh, start*SECTOR_SIZE, NULL, FILE_BEGIN)==0xffffffff) { + error(" Seek error "); + return -1; + } + + fprintf(stderr,"[INFO] Writing input file to device\n"); + bytesread = 0; + eof = 0; + while (!eof) { + n = read(infile,sectorbuf,BUFFER_SIZE); + + if (n < 0) { + perror("[ERR] read in disk_write"); + return -1; + } + + if (n < BUFFER_SIZE) { + eof = 1; + /* We need to pad the last write to a multiple of SECTOR_SIZE */ + if ((n % SECTOR_SIZE) != 0) { + padding = (SECTOR_SIZE-(n % SECTOR_SIZE)); + n += padding; + } + } + + bytesread += n; + + if (!WriteFile(dh, sectorbuf, n, &res, NULL)) { + error(" Error writing to disk: "); + fprintf(stderr,"Bytes written: %d\n",byteswritten); + return -1; + } + + if (res != n) { + fprintf(stderr,"Short write - requested %d, received %lu - aborting.\n",n,res); + return -1; + } + + byteswritten += res; + } + + fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",byteswritten-padding,padding); + return 0; +} + + +void print_usage(void) { + fprintf(stderr,"Usage: ipodpatcher [-i|r|w] DISKNO [file]\n"); + fprintf(stderr," -i Display iPod's partition information (default)\n"); + fprintf(stderr," -r Read firmware partition to file\n"); + fprintf(stderr," -w Write file to firmware partition\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your ipod's hard disk.\n"); + fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk0, the next disk\n"); + fprintf(stderr,"will be disk 1 etc. ipodpatcher will refuse to access a disk unless it\n"); + fprintf(stderr,"can identify it as being an ipod.\n"); + fprintf(stderr,"\n"); +} + +enum { + NONE, + SHOW_INFO, + READ, + WRITE +}; + +int main(int argc, char* argv[]) +{ + int i; + struct partinfo pinfo[4]; /* space for 4 partitions on 1 drive */ + int res; + int outfile; + int infile; + int mode = SHOW_INFO; + int p = 0; + int diskno = -1; + char diskname[32]; + HANDLE dh; + char* filename = NULL; + off_t inputsize; + + fprintf(stderr,"ipodpatcher v0.2 - (C) Dave Chapman 2005\n"); + fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); + fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); + + if (argc <= 1) { + print_usage(); + return 1; + } + + i = 1; + while (i < argc) { + if (strncmp(argv[i],"-i",2)==0) { + mode=SHOW_INFO; + } else if (strncmp(argv[i],"-r",2)==0) { + mode = READ; + } else if (strncmp(argv[i],"-w",2)==0) { + mode = WRITE; + } else { + if (argv[i][0] == '-') { + fprintf(stderr,"Unknown option %s\n",argv[i]); + return 1; + } else { + if (diskno == -1) { + diskno = atoi(argv[i]); + } else if (filename==NULL) { + filename = argv[i]; + } else { + fprintf(stderr,"Too many arguments: %s\n",argv[i]); + return 1; + } + } + } + i++; + } + + if ((mode==NONE) || (diskno==-1) || ((mode!=SHOW_INFO) && (filename==NULL))) { + print_usage(); + return 1; + } + + snprintf(diskname,sizeof(diskname),"\\\\.\\PhysicalDrive%d",diskno); + + fprintf(stderr,"[INFO] Reading partition table from %s\n",diskname); + + /* The ReadFile function requires a memory buffer aligned to a multiple of + the disk sector size. */ + sectorbuf = VirtualAlloc(NULL, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE); + if (sectorbuf == NULL) { + error(" Error allocating a buffer: "); + return 2; + } + + dh = CreateFile(diskname, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); + + if (dh == INVALID_HANDLE_VALUE) { + error(" Error opening disk: "); + return 2; + } + + if (!lock_volume(dh)) { + error(" Error locking disk: "); + return 2; + } + + if (read_partinfo(dh,pinfo) < 0) { + return 2; + } + + display_partinfo(pinfo); + + if (pinfo[p].start==0) { + fprintf(stderr,"[ERR] Specified partition (%d) does not exist:\n",p); + display_partinfo(pinfo); + return 3; + } + + if (mode==READ) { + outfile = open(filename,O_CREAT|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); + if (outfile < 0) { + perror(filename); + return 4; + } + + res = disk_read(dh,outfile,pinfo[p].start,pinfo[p].size); + + close(outfile); + } else if (mode==WRITE) { + /* Close existing file and re-open for writing */ + unlock_volume(dh); + CloseHandle(dh); + + dh = CreateFile(diskname, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); + + if (dh == INVALID_HANDLE_VALUE) { + error(" Error opening disk: "); + return 2; + } + + if (!lock_volume(dh)) { + error(" Error locking disk: "); + return 2; + } + + infile = open(filename,O_RDONLY|O_BINARY); + if (infile < 0) { + perror(filename); + return 2; + } + + /* Check filesize is <= partition size */ + inputsize=filesize(infile); + if (inputsize > 0) { + if (inputsize <= (pinfo[p].size*SECTOR_SIZE)) { + fprintf(stderr,"[INFO] Input file is %lu bytes\n",inputsize); + res = disk_write(dh,infile,pinfo[p].start); + } else { + fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n"); + } + } + + close(infile); + } + + unlock_volume(dh); + CloseHandle(dh); + return 0; +} diff --git a/tools/ipodpatcher/parttypes.h b/tools/ipodpatcher/parttypes.h new file mode 100644 index 0000000000..f8de303553 --- /dev/null +++ b/tools/ipodpatcher/parttypes.h @@ -0,0 +1,109 @@ +/* DOS partition types - taken from fdisk */ + +struct parttype { + unsigned char type; + char *name; +}; + +struct parttype parttypes[] = { + {0x00, "Empty"}, + {0x01, "FAT12"}, + {0x02, "XENIX root"}, + {0x03, "XENIX usr"}, + {0x04, "FAT16 <32M"}, + {0x05, "Extended"}, /* DOS 3.3+ extended partition */ + {0x06, "FAT16"}, /* DOS 16-bit >=32M */ + {0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ + {0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ + {0x09, "AIX bootable"}, /* AIX data or Coherent */ + {0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */ + {0x0b, "W95 FAT32"}, + {0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */ + {0x0e, "W95 FAT16 (LBA)"}, + {0x0f, "W95 Ext'd (LBA)"}, + {0x10, "OPUS"}, + {0x11, "Hidden FAT12"}, + {0x12, "Compaq diagnostics"}, + {0x14, "Hidden FAT16 <32M"}, + {0x16, "Hidden FAT16"}, + {0x17, "Hidden HPFS/NTFS"}, + {0x18, "AST SmartSleep"}, + {0x1b, "Hidden W95 FAT32"}, + {0x1c, "Hidden W95 FAT32 (LBA)"}, + {0x1e, "Hidden W95 FAT16 (LBA)"}, + {0x24, "NEC DOS"}, + {0x39, "Plan 9"}, + {0x3c, "PartitionMagic recovery"}, + {0x40, "Venix 80286"}, + {0x41, "PPC PReP Boot"}, + {0x42, "SFS"}, + {0x4d, "QNX4.x"}, + {0x4e, "QNX4.x 2nd part"}, + {0x4f, "QNX4.x 3rd part"}, + {0x50, "OnTrack DM"}, + {0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */ + {0x52, "CP/M"}, /* CP/M or Microport SysV/AT */ + {0x53, "OnTrack DM6 Aux3"}, + {0x54, "OnTrackDM6"}, + {0x55, "EZ-Drive"}, + {0x56, "Golden Bow"}, + {0x5c, "Priam Edisk"}, + {0x61, "SpeedStor"}, + {0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ + {0x64, "Novell Netware 286"}, + {0x65, "Novell Netware 386"}, + {0x70, "DiskSecure Multi-Boot"}, + {0x75, "PC/IX"}, + {0x80, "Old Minix"}, /* Minix 1.4a and earlier */ + {0x81, "Minix / old Linux"},/* Minix 1.4b and later */ + {0x82, "Linux swap / Solaris"}, + {0x83, "Linux"}, + {0x84, "OS/2 hidden C: drive"}, + {0x85, "Linux extended"}, + {0x86, "NTFS volume set"}, + {0x87, "NTFS volume set"}, + {0x88, "Linux plaintext"}, + {0x8e, "Linux LVM"}, + {0x93, "Amoeba"}, + {0x94, "Amoeba BBT"}, /* (bad block table) */ + {0x9f, "BSD/OS"}, /* BSDI */ + {0xa0, "IBM Thinkpad hibernation"}, + {0xa5, "FreeBSD"}, /* various BSD flavours */ + {0xa6, "OpenBSD"}, + {0xa7, "NeXTSTEP"}, + {0xa8, "Darwin UFS"}, + {0xa9, "NetBSD"}, + {0xab, "Darwin boot"}, + {0xb7, "BSDI fs"}, + {0xb8, "BSDI swap"}, + {0xbb, "Boot Wizard hidden"}, + {0xbe, "Solaris boot"}, + {0xbf, "Solaris"}, + {0xc1, "DRDOS/sec (FAT-12)"}, + {0xc4, "DRDOS/sec (FAT-16 < 32M)"}, + {0xc6, "DRDOS/sec (FAT-16)"}, + {0xc7, "Syrinx"}, + {0xda, "Non-FS data"}, + {0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or + Concurrent DOS or CTOS */ + {0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */ + {0xdf, "BootIt"}, /* BootIt EMBRM */ + {0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT + extended partition */ + {0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */ + {0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended + partition < 1024 cyl. */ + {0xeb, "BeOS fs"}, + {0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */ + {0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */ + {0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */ + {0xf1, "SpeedStor"}, + {0xf4, "SpeedStor"}, /* SpeedStor large partition */ + {0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */ + {0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with + autodetect using persistent + superblock */ + {0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */ + {0xff, "BBT"}, /* Xenix Bad Block Table */ + { 0, 0 } +};