From c876d3bbefe0dc00c27ca0c12d29da5874946962 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Wed, 15 Dec 2021 21:04:28 +0100 Subject: rbutil: Merge rbutil with utils folder. rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21 --- utils/ipodpatcher/Makefile | 53 + utils/ipodpatcher/arc4.c | 108 ++ utils/ipodpatcher/arc4.h | 47 + utils/ipodpatcher/fat32format.c | 530 +++++++ utils/ipodpatcher/ipodio-posix.c | 409 ++++++ utils/ipodpatcher/ipodio-win32-scsi.c | 147 ++ utils/ipodpatcher/ipodio-win32.c | 226 +++ utils/ipodpatcher/ipodio.h | 115 ++ utils/ipodpatcher/ipodpatcher.c | 2350 ++++++++++++++++++++++++++++++++ utils/ipodpatcher/ipodpatcher.h | 84 ++ utils/ipodpatcher/ipodpatcher.manifest | 13 + utils/ipodpatcher/ipodpatcher.pro | 47 + utils/ipodpatcher/ipodpatcher.rc | 1 + utils/ipodpatcher/main.c | 622 +++++++++ utils/ipodpatcher/parttypes.h | 109 ++ 15 files changed, 4861 insertions(+) create mode 100644 utils/ipodpatcher/Makefile create mode 100644 utils/ipodpatcher/arc4.c create mode 100644 utils/ipodpatcher/arc4.h create mode 100644 utils/ipodpatcher/fat32format.c create mode 100644 utils/ipodpatcher/ipodio-posix.c create mode 100644 utils/ipodpatcher/ipodio-win32-scsi.c create mode 100644 utils/ipodpatcher/ipodio-win32.c create mode 100644 utils/ipodpatcher/ipodio.h create mode 100644 utils/ipodpatcher/ipodpatcher.c create mode 100644 utils/ipodpatcher/ipodpatcher.h create mode 100644 utils/ipodpatcher/ipodpatcher.manifest create mode 100644 utils/ipodpatcher/ipodpatcher.pro create mode 100644 utils/ipodpatcher/ipodpatcher.rc create mode 100644 utils/ipodpatcher/main.c create mode 100644 utils/ipodpatcher/parttypes.h (limited to 'utils/ipodpatcher') diff --git a/utils/ipodpatcher/Makefile b/utils/ipodpatcher/Makefile new file mode 100644 index 0000000000..4254995d22 --- /dev/null +++ b/utils/ipodpatcher/Makefile @@ -0,0 +1,53 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +CFLAGS += -Wall -W + +# Build with "make BOOTOBJS=1" to build with embedded bootloaders and the +# --install option and interactive mode. You need the full set of Rockbox +# bootloaders in this directory - download them from +# http://download.rockbox.org/bootloader/ipod/bootloaders.zip + +# Releases of ipodpatcher are created with "make RELEASE=1". This +# enables BOOTOBJS and uses the VERSION string defined in main.c +ifdef RELEASE +CFLAGS += -DRELEASE +BOOTOBJS=1 +endif + +ifdef BOOTOBJS +BOOTSRC = ipod1g2g.c ipod3g.c ipod4g.c ipodcolor.c ipodmini1g.c \ + ipodmini2g.c ipodnano1g.c ipodvideo.c ipodnano2g.c +CFLAGS += -DWITH_BOOTOBJS +endif + +# additional frameworks to link on on OS X +LDOPTS_OSX = -framework CoreFoundation -framework IOKit + +LIBSOURCES = ipodpatcher.c fat32format.c arc4.c \ + ipodio-posix.c ipodio-win32-scsi.c ipodio-win32.c +SOURCES = main.c $(BOOTSRC) +ipodpatcher: SOURCES+= ipodio-posix.c + +OUTPUT = ipodpatcher +include ../libtools.make + +ipodpatcher.exe: $(OBJDIR)ipodpatcher-rc.o +$(OBJDIR)ipodpatcher-rc.o: ipodpatcher.rc ipodpatcher.manifest + @echo WINDRES $(notdir $<) + $(SILENT)$(CROSS)$(WINDRES) -i $< -o $@ + +%.c: bootloader-%.ipod $(BIN2C) + @echo BIN2C $< + $(SILENT)$(BIN2C) -i $< $* + +%.c: bootloader-%.ipodx $(BIN2C) + @echo BIN2C $< + $(SILENT)$(BIN2C) -i $< $* + diff --git a/utils/ipodpatcher/arc4.c b/utils/ipodpatcher/arc4.c new file mode 100644 index 0000000000..75b1862b89 --- /dev/null +++ b/utils/ipodpatcher/arc4.c @@ -0,0 +1,108 @@ +/* + * arc4.c + * Release $Name: MATRIXSSL_1_8_3_OPEN $ + * + * ARC4 stream cipher implementation + */ +/* + * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. + * The latest version of this code is available at http://www.matrixssl.org + * + * This software is open source; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This General Public License does NOT permit incorporating this software + * into proprietary programs. If you are unable to comply with the GPL, a + * commercial license for this software may be purchased from PeerSec Networks + * at http://www.peersec.com + * + * This program is distributed in WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * http://www.gnu.org/copyleft/gpl.html + */ +/******************************************************************************/ + +#include "arc4.h" + +/* + Some accounts, such as O'Reilly's Secure Programming Cookbook say that no + more than 2^30 bytes should be processed without rekeying, so we + enforce that limit here. FYI, this is equal to 1GB of data transferred. +*/ +#define ARC4_MAX_BYTES 0x40000000 + +/******************************************************************************/ +/* + SSL_RSA_WITH_RC4_* cipher callbacks + */ +void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen) +{ + unsigned char index1, index2, tmp, *state; + int16_t counter; + + ctx->byteCount = 0; + state = &ctx->state[0]; + + for (counter = 0; counter < 256; counter++) { + state[counter] = (unsigned char)counter; + } + ctx->x = 0; + ctx->y = 0; + index1 = 0; + index2 = 0; + + for (counter = 0; counter < 256; counter++) { + index2 = (key[index1] + state[counter] + index2) & 0xff; + + tmp = state[counter]; + state[counter] = state[index2]; + state[index2] = tmp; + + index1 = (index1 + 1) % keylen; + } +} + +int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, + unsigned char *out, int32_t len) +{ + unsigned char x, y, *state, xorIndex, tmp; + int counter; /* NOTE BY DAVE CHAPMAN: This was a short in + the original code, which caused a segfault + when attempting to process data > 32767 + bytes. */ + + ctx->byteCount += len; + if (ctx->byteCount > ARC4_MAX_BYTES) { + return -1; + } + + x = ctx->x; + y = ctx->y; + state = &ctx->state[0]; + for (counter = 0; counter < len; counter++) { + x = (x + 1) & 0xff; + y = (state[x] + y) & 0xff; + + tmp = state[x]; + state[x] = state[y]; + state[y] = tmp; + + xorIndex = (state[x] + state[y]) & 0xff; + + tmp = in[counter]; + tmp ^= state[xorIndex]; + out[counter] = tmp; + } + ctx->x = x; + ctx->y = y; + return len; +} + +/*****************************************************************************/ diff --git a/utils/ipodpatcher/arc4.h b/utils/ipodpatcher/arc4.h new file mode 100644 index 0000000000..8bff0e2dc1 --- /dev/null +++ b/utils/ipodpatcher/arc4.h @@ -0,0 +1,47 @@ +/* + arc4.h - based on matrixssl-1-8-3-open + +*/ + +/* + * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. + * The latest version of this code is available at http://www.matrixssl.org + * + * This software is open source; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This General Public License does NOT permit incorporating this software + * into proprietary programs. If you are unable to comply with the GPL, a + * commercial license for this software may be purchased from PeerSec Networks + * at http://www.peersec.com + * + * This program is distributed in WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * http://www.gnu.org/copyleft/gpl.html + */ +/*****************************************************************************/ + +#ifndef _ARC4_H + +#include + +struct rc4_key_t +{ + unsigned char state[256]; + uint32_t byteCount; + unsigned char x; + unsigned char y; +}; + +void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen); +int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, + unsigned char *out, int32_t len); + +#endif 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * + * FAT32 formatting functions. Based on: + * + * Fat32 formatter version 1.03 + * (c) Tom Thornhill 2005 + * This software is covered by the GPL. + * By using this tool, you agree to absolve Ridgecrop of an liabilities for + * lost data. + * Please backup any data you value before using this tool. + * + * + * Modified June 2007 by Dave Chapman for use in ipodpatcher + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 "ipodio.h" + +static inline uint16_t swap16(uint16_t value) +{ + return (value >> 8) | (value << 8); +} + +static inline uint32_t swap32(uint32_t value) +{ + uint32_t hi = swap16(value >> 16); + uint32_t lo = swap16(value & 0xffff); + return (lo << 16) | hi; +} + +/* The following functions are not the most efficient, but are + self-contained and don't require needing to know endianness of CPU + at compile-time. + + Note that htole16/htole32 exist on some platforms, so for + simplicity we use different names. + +*/ + +static uint16_t rb_htole16(uint16_t x) +{ + uint16_t test = 0x1234; + unsigned char* p = (unsigned char*)&test; + + if (p[0]==0x12) { + /* Big-endian */ + return swap16(x); + } else { + return x; + } +} + +static uint32_t rb_htole32(uint32_t x) +{ + uint32_t test = 0x12345678; + unsigned char* p = (unsigned char*)&test; + + if (p[0]==0x12) { + /* Big-endian */ + return swap32(x); + } else { + return x; + } +} + + +/* TODO: Pass these as parameters to the various create_ functions */ + +/* can be zero for default or 1,2,4,8,16,32 or 64 */ +static int sectors_per_cluster = 0; + +/* Recommended values */ +static uint32_t ReservedSectCount = 32; +static uint32_t NumFATs = 2; +static uint32_t BackupBootSect = 6; +static uint32_t VolumeId=0; /* calculated before format */ + +/* Calculated later */ +static uint32_t FatSize=0; +static uint32_t BytesPerSect=0; +static uint32_t SectorsPerCluster=0; +static uint32_t TotalSectors=0; +static uint32_t SystemAreaSize=0; +static uint32_t UserAreaSize=0; +static uint8_t VolId[12] = "NO NAME "; + + +struct FAT_BOOTSECTOR32 +{ + /* Common fields. */ + uint8_t sJmpBoot[3]; + char sOEMName[8]; + uint16_t wBytsPerSec; + uint8_t bSecPerClus; + uint16_t wRsvdSecCnt; + uint8_t bNumFATs; + uint16_t wRootEntCnt; + uint16_t wTotSec16; /* if zero, use dTotSec32 instead */ + uint8_t bMedia; + uint16_t wFATSz16; + uint16_t wSecPerTrk; + uint16_t wNumHeads; + uint32_t dHiddSec; + uint32_t dTotSec32; + + /* Fat 32/16 only */ + uint32_t dFATSz32; + uint16_t wExtFlags; + uint16_t wFSVer; + uint32_t dRootClus; + uint16_t wFSInfo; + uint16_t wBkBootSec; + uint8_t Reserved[12]; + uint8_t bDrvNum; + uint8_t Reserved1; + uint8_t bBootSig; /* == 0x29 if next three fields are ok */ + uint32_t dBS_VolID; + uint8_t sVolLab[11]; + uint8_t sBS_FilSysType[8]; +} __attribute__((packed)); + +struct FAT_FSINFO { + uint32_t dLeadSig; // 0x41615252 + uint8_t sReserved1[480]; // zeros + uint32_t dStrucSig; // 0x61417272 + uint32_t dFree_Count; // 0xFFFFFFFF + uint32_t dNxt_Free; // 0xFFFFFFFF + uint8_t sReserved2[12]; // zeros + uint32_t dTrailSig; // 0xAA550000 +} __attribute__((packed)); + + +/* Write "count" zero sectors, starting at sector "sector" */ +static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count) +{ + int n; + + if (ipod_seek(ipod, sector * ipod->sector_size) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + memset(ipod->sectorbuf, 0, 128 * ipod->sector_size); + + /* Write 128 sectors at a time */ + while (count) { + if (count >= 128) + n = 128; + else + n = count; + + if (ipod_write(ipod,n * ipod->sector_size) < 0) { + perror("[ERR] Write failed in zero_sectors\n"); + return -1; + } + + count -= n; + } + + return 0; +} + + +/* +28.2 CALCULATING THE VOLUME SERIAL NUMBER + +For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94 +seconds. DOS takes the date and time just before it writes it to the +disk. + +Low order word is calculated: Volume Serial Number is: + Month & Day 12/26 0c1ah + Sec & Hundrenths 41:94 295eh 3578:1d02 + ----- + 3578h + +High order word is calculated: + Hours & Minutes 21:55 1537h + Year 1995 07cbh + ----- + 1d02h +*/ +static uint32_t get_volume_id ( ) +{ + /* TODO */ +#if 0 + SYSTEMTIME s; + uint32_t d; + uint16_t lo,hi,tmp; + + GetLocalTime( &s ); + + lo = s.wDay + ( s.wMonth << 8 ); + tmp = (s.wMilliseconds/10) + (s.wSecond << 8 ); + lo += tmp; + + hi = s.wMinute + ( s.wHour << 8 ); + hi += s.wYear; + + d = lo + (hi << 16); + return(d); +#endif + return(0); +} + +/* +This is the Microsoft calculation from FATGEN + + uint32_t RootDirSectors = 0; + uint32_t TmpVal1, TmpVal2, FATSz; + + TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors); + TmpVal2 = (256 * SecPerClus) + NumFATs; + TmpVal2 = TmpVal2 / 2; + FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2; + + return( FatSz ); +*/ + + +static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt, + uint32_t SecPerClus, uint32_t NumFATs, + uint32_t BytesPerSect) +{ + uint64_t Numerator, Denominator; + uint64_t FatElementSize = 4; + uint64_t FatSz; + + /* This is based on + http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html + I've made the obvious changes for FAT32 + */ + + Numerator = FatElementSize * ( DskSize - ReservedSecCnt ); + Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs ); + FatSz = Numerator / Denominator; + + /* round up */ + FatSz += 1; + + return((uint32_t)FatSz); +} + +static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect) +{ + uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect; + return( (uint8_t) spc ); +} + +static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors, + uint32_t BytesPerSect) +{ + uint8_t ret = 0x01; /* 1 sector per cluster */ + uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect; + int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 ); + + /* 512 MB to 8,191 MB 4 KB */ + if ( DiskSizeMB > 512 ) + ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */ + + /* 8,192 MB to 16,383 MB 8 KB */ + if ( DiskSizeMB > 8192 ) + ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */ + + /* 16,384 MB to 32,767 MB 16 KB */ + if ( DiskSizeMB > 16384 ) + ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */ + + /* Larger than 32,768 MB 32 KB */ + if ( DiskSizeMB > 32768 ) + ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */ + + return( ret ); + +} + +static void create_boot_sector(unsigned char* buf, + struct ipod_t* ipod, int partition) +{ + struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf; + + /* fill out the boot sector and fs info */ + pFAT32BootSect->sJmpBoot[0]=0xEB; + pFAT32BootSect->sJmpBoot[1]=0x5A; + pFAT32BootSect->sJmpBoot[2]=0x90; + memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8 ); + pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect); + pFAT32BootSect->bSecPerClus = SectorsPerCluster ; + pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount); + pFAT32BootSect->bNumFATs = NumFATs; + pFAT32BootSect->wRootEntCnt = rb_htole16(0); + pFAT32BootSect->wTotSec16 = rb_htole16(0); + pFAT32BootSect->bMedia = 0xF8; + pFAT32BootSect->wFATSz16 = rb_htole16(0); + pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track); + pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads); + pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start); + pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors); + pFAT32BootSect->dFATSz32 = rb_htole32(FatSize); + pFAT32BootSect->wExtFlags = rb_htole16(0); + pFAT32BootSect->wFSVer = rb_htole16(0); + pFAT32BootSect->dRootClus = rb_htole32(2); + pFAT32BootSect->wFSInfo = rb_htole16(1); + pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect); + pFAT32BootSect->bDrvNum = 0x80; + pFAT32BootSect->Reserved1 = 0; + pFAT32BootSect->bBootSig = 0x29; + pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId); + memcpy(pFAT32BootSect->sVolLab, VolId, 11); + memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 ); + + buf[510] = 0x55; + buf[511] = 0xaa; +} + +static void create_fsinfo(unsigned char* buf) +{ + struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf; + + /* FSInfo sect */ + pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252); + pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272); + pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1); + pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1); + pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000); + pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1); + + /* clusters 0-1 reserved, we used cluster 2 for the root dir */ + pFAT32FsInfo->dNxt_Free = rb_htole32(3); +} + +static void create_firstfatsector(unsigned char* buf) +{ + uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */ + + /* First FAT Sector */ + p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */ + p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */ + p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */ +} + +int format_partition(struct ipod_t* ipod, int partition) +{ + uint32_t i; + uint64_t qTotalSectors=0; + uint64_t FatNeeded; + + VolumeId = get_volume_id( ); + + /* Only support hard disks at the moment */ + if ( ipod->sector_size != 512 ) + { + fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n"); + return -1; + } + BytesPerSect = ipod->sector_size; + + /* Checks on Disk Size */ + qTotalSectors = ipod->pinfo[partition].size; + + /* low end limit - 65536 sectors */ + if ( qTotalSectors < 65536 ) + { + /* I suspect that most FAT32 implementations would mount this + volume just fine, but the spec says that we shouldn't do + this, so we won't */ + + fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" ); + return -1; + } + + if ( qTotalSectors >= 0xffffffff ) + { + /* This is a more fundamental limitation on FAT32 - the total + sector count in the root dir is 32bit. With a bit of + creativity, FAT32 could be extended to handle at least 2^28 + clusters There would need to be an extra field in the + FSInfo sector, and the old sector count could be set to + 0xffffffff. This is non standard though, the Windows FAT + driver FASTFAT.SYS won't understand this. Perhaps a future + version of FAT32 and FASTFAT will handle this. */ + + fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n"); + } + + if ( sectors_per_cluster ) { + SectorsPerCluster = sectors_per_cluster; + } else { + SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size, + BytesPerSect ); + } + + TotalSectors = (uint32_t) qTotalSectors; + + FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount, + SectorsPerCluster, NumFATs, BytesPerSect ); + + UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize); + + /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */ + SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster); + + /* Work out the Cluster count */ + FatNeeded = UserAreaSize/SectorsPerCluster; + + /* check for a cluster count of >2^28, since the upper 4 bits of + the cluster values in the FAT are reserved. */ + if (FatNeeded > 0x0FFFFFFF) { + fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" ); + return -1; + } + + /* Sanity check, make sure the fat is big enough. + Convert the cluster count into a Fat sector count, and check + the fat size value we calculated earlier is OK. */ + + FatNeeded *=4; + FatNeeded += (BytesPerSect-1); + FatNeeded /= BytesPerSect; + + if ( FatNeeded > FatSize ) { + fprintf(stderr,"[ERR] Drive too big to format\n"); + return -1; + } + + /* + Write boot sector, fats + Sector 0 Boot Sector + Sector 1 FSInfo + Sector 2 More boot code - we write zeros here + Sector 3 unused + Sector 4 unused + Sector 5 unused + Sector 6 Backup boot sector + Sector 7 Backup FSInfo sector + Sector 8 Backup 'more boot code' + zero'd sectors upto ReservedSectCount + FAT1 ReservedSectCount to ReservedSectCount + FatSize + ... + FATn ReservedSectCount to ReservedSectCount + FatSize + RootDir - allocated to cluster2 + */ + + fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track); + fprintf(stderr,"[INFO] Size : %" PRIu64 "GB %u sectors\n", + ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors ); + fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect ); + fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff ); + fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs ); + fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster ); + + fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition); + + /* Once zero_sectors has run, any data on the drive is basically lost... */ + fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize ); + + zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize); + + fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" ); + + /* Create the boot sector structure */ + create_boot_sector(ipod->sectorbuf, ipod, partition); + create_fsinfo(ipod->sectorbuf + 512); + + /* Write boot sector and fsinfo at start of partition */ + if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + if (ipod_write(ipod,512 * 2) < 0) { + perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); + return -1; + } + + /* Write backup copy of boot sector and fsinfo */ + if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + if (ipod_write(ipod,512 * 2) < 0) { + perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); + return -1; + } + + /* Create the first FAT sector */ + create_firstfatsector(ipod->sectorbuf); + + /* Write the first fat sector in the right places */ + for ( i=0; ipinfo[partition].start + SectorStart) * ipod->sector_size) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if (ipod_write(ipod,512) < 0) { + perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); + return -1; + } + } + + fprintf(stderr,"[INFO] Format successful\n"); + + return 0; +} diff --git a/utils/ipodpatcher/ipodio-posix.c b/utils/ipodpatcher/ipodio-posix.c new file mode 100644 index 0000000000..9b386d994f --- /dev/null +++ b/utils/ipodpatcher/ipodio-posix.c @@ -0,0 +1,409 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#if !defined(_WIN32) /* all non-Windows platforms are considered POSIX. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipodio.h" + +#if defined(linux) || defined (__linux) +#include +#include +#include +#include + +#define IPOD_SECTORSIZE_IOCTL BLKSSZGET + +static void get_geometry(struct ipod_t* ipod) +{ + struct hd_geometry geometry; + + if (!ioctl(ipod->dh, HDIO_GETGEO, &geometry)) { + /* never use geometry.cylinders - it is truncated */ + ipod->num_heads = geometry.heads; + ipod->sectors_per_track = geometry.sectors; + } else { + ipod->num_heads = 0; + ipod->sectors_per_track = 0; + } +} + +/* Linux SCSI Inquiry code based on the documentation and example code from + http://www.ibm.com/developerworks/linux/library/l-scsi-api/index.html +*/ + +int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, + unsigned char* buf, int bufsize) +{ + unsigned char cdb[6]; + struct sg_io_hdr hdr; + unsigned char sense_buffer[255]; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.interface_id = 'S'; /* this is the only choice we have! */ + hdr.flags = SG_FLAG_LUN_INHIBIT; /* this would put the LUN to 2nd byte of cdb*/ + + /* Set xfer data */ + hdr.dxferp = buf; + hdr.dxfer_len = bufsize; + + /* Set sense data */ + hdr.sbp = sense_buffer; + hdr.mx_sb_len = sizeof(sense_buffer); + + /* Set the cdb format */ + cdb[0] = 0x12; + cdb[1] = 1; /* Enable Vital Product Data (EVPD) */ + cdb[2] = page_code & 0xff; + cdb[3] = 0; + cdb[4] = 0xff; + cdb[5] = 0; /* For control filed, just use 0 */ + + hdr.dxfer_direction = SG_DXFER_FROM_DEV; + hdr.cmdp = cdb; + hdr.cmd_len = 6; + + int ret = ioctl(ipod->dh, SG_IO, &hdr); + + if (ret < 0) { + return -1; + } else { + return 0; + } +} + +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__bsdi__) || defined(__DragonFly__) +#include +#define IPOD_SECTORSIZE_IOCTL DIOCGSECTORSIZE + +/* TODO: Implement this function for BSD */ +static void get_geometry(struct ipod_t* ipod) +{ + /* Are these universal for all ipods? */ + ipod->num_heads = 255; + ipod->sectors_per_track = 63; +} + +int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, + unsigned char* buf, int bufsize) +{ + /* TODO: Implement for BSD */ + (void)ipod; + (void)page_code; + (void)buf; + (void)bufsize; + return -1; +} + +#elif defined(__APPLE__) && defined(__MACH__) +/* OS X IOKit includes don't like VERSION being defined! */ +#undef VERSION +#include +#include +#include +#include +#include +#define IPOD_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE + +/* TODO: Implement this function for Mac OS X */ +static void get_geometry(struct ipod_t* ipod) +{ + /* Are these universal for all ipods? */ + ipod->num_heads = 255; + ipod->sectors_per_track = 63; +} + +int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, + unsigned char* buf, int bufsize) +{ + /* OS X doesn't allow to simply send out a SCSI inquiry request but + * requires registering an interface handler first. + * Currently this is done on each inquiry request which is somewhat + * inefficient but the current ipodpatcher API doesn't really fit here. + * Based on the documentation in Apple's document + * "SCSI Architecture Model Device Interface Guide". + * + * WARNING: this code currently doesn't take the selected device into + * account. It simply looks for an Ipod on the system and uses + * the first match. + */ + (void)ipod; + int result = 0; + /* first, create a dictionary to match the device. This is needed to get the + * service. */ + CFMutableDictionaryRef match_dict; + match_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); + if(match_dict == NULL) + return -1; + + /* set value to match. In case of the Ipod this is "iPodUserClientDevice". */ + CFMutableDictionaryRef sub_dict; + sub_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); + if(sub_dict == NULL) + return -1; + CFDictionarySetValue(sub_dict, CFSTR(kIOPropertySCSITaskDeviceCategory), + CFSTR("iPodUserClientDevice")); + CFDictionarySetValue(match_dict, CFSTR(kIOPropertyMatchKey), sub_dict); + + /* get an iterator for searching for the service. */ + kern_return_t kr; + io_iterator_t iterator = IO_OBJECT_NULL; + /* get matching services from IO registry. Consumes one reference to + * the dictionary, so no need to release that. */ + kr = IOServiceGetMatchingServices(kIOMasterPortDefault, match_dict, &iterator); + + if(!iterator | (kr != kIOReturnSuccess)) + return -1; + + /* get interface and obtain exclusive access */ + SInt32 score; + HRESULT herr; + kern_return_t err; + IOCFPlugInInterface **plugin_interface = NULL; + SCSITaskDeviceInterface **interface = NULL; + io_service_t device = IO_OBJECT_NULL; + device = IOIteratorNext(iterator); + + err = IOCreatePlugInInterfaceForService(device, kIOSCSITaskDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugin_interface, + &score); + + if(err != noErr) { + return -1; + } + /* query the plugin interface for task interface */ + herr = (*plugin_interface)->QueryInterface(plugin_interface, + CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID), (LPVOID*)&interface); + if(herr != S_OK) { + IODestroyPlugInInterface(plugin_interface); + return -1; + } + + err = (*interface)->ObtainExclusiveAccess(interface); + if(err != noErr) { + (*interface)->Release(interface); + IODestroyPlugInInterface(plugin_interface); + return -1; + } + + /* do the inquiry */ + SCSITaskInterface **task = NULL; + + task = (*interface)->CreateSCSITask(interface); + if(task != NULL) { + kern_return_t err; + SCSITaskStatus task_status; + IOVirtualRange* range; + SCSI_Sense_Data sense_data; + SCSICommandDescriptorBlock cdb; + UInt64 transfer_count = 0; + memset(buf, 0, bufsize); + /* allocate virtual range for buffer. */ + range = (IOVirtualRange*) malloc(sizeof(IOVirtualRange)); + memset(&sense_data, 0, sizeof(sense_data)); + memset(cdb, 0, sizeof(cdb)); + /* set up range. address is buffer address, length is request size. */ + range->address = (IOVirtualAddress)buf; + range->length = bufsize; + /* setup CDB */ + cdb[0] = 0x12; /* inquiry */ + cdb[1] = 1; + cdb[2] = page_code; + cdb[4] = bufsize; + + /* set cdb in task */ + err = (*task)->SetCommandDescriptorBlock(task, cdb, kSCSICDBSize_6Byte); + if(err != kIOReturnSuccess) { + result = -1; + goto cleanup; + } + err = (*task)->SetScatterGatherEntries(task, range, 1, bufsize, + kSCSIDataTransfer_FromTargetToInitiator); + if(err != kIOReturnSuccess) { + result = -1; + goto cleanup; + } + /* set timeout */ + err = (*task)->SetTimeoutDuration(task, 10000); + if(err != kIOReturnSuccess) { + result = -1; + goto cleanup; + } + + /* request data */ + err = (*task)->ExecuteTaskSync(task, &sense_data, &task_status, &transfer_count); + if(err != kIOReturnSuccess) { + result = -1; + goto cleanup; + } + /* cleanup */ + free(range); + + /* release task interface */ + (*task)->Release(task); + } + else { + result = -1; + } +cleanup: + /* cleanup interface */ + (*interface)->ReleaseExclusiveAccess(interface); + (*interface)->Release(interface); + IODestroyPlugInInterface(plugin_interface); + + return result; +} + +#else + #error No sector-size detection implemented for this platform +#endif + +#if defined(__APPLE__) && defined(__MACH__) +static int ipod_unmount(struct ipod_t* ipod) +{ + char cmd[4096]; + int res; + + sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss2\"",ipod->diskname); + fprintf(stderr,"[INFO] "); + res = system(cmd); + + if (res==0) { + return 0; + } else { + perror("Unmount failed"); + return -1; + } +} +#endif + +void ipod_print_error(char* msg) +{ + perror(msg); +} + +int ipod_open(struct ipod_t* ipod, int silent) +{ + ipod->dh=open(ipod->diskname,O_RDONLY); + if (ipod->dh < 0) { + if (!silent) perror(ipod->diskname); + if(errno == EACCES) return -2; + else return -1; + } + + /* Read information about the disk */ + + if(ioctl(ipod->dh,IPOD_SECTORSIZE_IOCTL,&ipod->sector_size) < 0) { + ipod->sector_size=512; + if (!silent) { + fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n" + ,ipod->sector_size); + } + } + + get_geometry(ipod); + + return 0; +} + + +int ipod_reopen_rw(struct ipod_t* ipod) +{ +#if defined(__APPLE__) && defined(__MACH__) + if (ipod_unmount(ipod) < 0) + return -1; +#endif + + close(ipod->dh); + ipod->dh=open(ipod->diskname,O_RDWR); + if (ipod->dh < 0) { + perror(ipod->diskname); + return -1; + } + return 0; +} + +int ipod_close(struct ipod_t* ipod) +{ + close(ipod->dh); + return 0; +} + +int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize) +{ + ipod->sectorbuf = malloc(bufsize); + if (ipod->sectorbuf== NULL) { + return -1; + } + return 0; +} + +int ipod_dealloc_buffer(struct ipod_t* ipod) +{ + if (ipod->sectorbuf == NULL) { + return -1; + } + free(ipod->sectorbuf); + ipod->sectorbuf = NULL; + return 0; +} + +int ipod_seek(struct ipod_t* ipod, unsigned long pos) +{ + off_t res; + + res = lseek(ipod->dh, pos, SEEK_SET); + + if (res == -1) { + return -1; + } + return 0; +} + +ssize_t ipod_read(struct ipod_t* ipod, int nbytes) +{ + if(ipod->sectorbuf == NULL) { + return -1; + } + return read(ipod->dh, ipod->sectorbuf, nbytes); +} + +ssize_t ipod_write(struct ipod_t* ipod, int nbytes) +{ + if(ipod->sectorbuf == NULL) { + return -1; + } + return write(ipod->dh, ipod->sectorbuf, nbytes); +} + +#endif + diff --git a/utils/ipodpatcher/ipodio-win32-scsi.c b/utils/ipodpatcher/ipodio-win32-scsi.c new file mode 100644 index 0000000000..16460cfba3 --- /dev/null +++ b/utils/ipodpatcher/ipodio-win32-scsi.c @@ -0,0 +1,147 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * + * Based on the getCapsUsingSCSIPassThrough() function from "cddrv.cpp": + * - http://www.farmanager.com/svn/trunk/unicode_far/cddrv.cpp + * + * Copyright (c) 1996 Eugene Roshal + * Copyright (c) 2000 Far Group + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#if defined(_WIN32) +#include +#include +#include + +#include "ipodio.h" + +/* from ddk/ntddscsi.h */ +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +#define IOCTL_SCSI_PASS_THROUGH \ + CTL_CODE(FILE_DEVICE_CONTROLLER, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _SCSI_PASS_THROUGH { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG_PTR DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; + +typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS { + SCSI_PASS_THROUGH Spt; + ULONG Filler; /* realign buffers to double word boundary */ + UCHAR SenseBuf[32]; + UCHAR DataBuf[512]; +} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS; + +int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, + unsigned char* buf, int bufsize) +{ + SCSI_PASS_THROUGH_WITH_BUFFERS sptwb; + ULONG length; + DWORD returned; + BOOL status; + + if (bufsize > 255) { + fprintf(stderr,"[ERR] Invalid bufsize in ipod_scsi_inquiry\n"); + return -1; + } + + memset(&sptwb, 0, sizeof(sptwb)); + + sptwb.Spt.Length = sizeof(SCSI_PASS_THROUGH); + sptwb.Spt.PathId = 0; + sptwb.Spt.TargetId = 1; + sptwb.Spt.Lun = 0; + sptwb.Spt.CdbLength = 6; + sptwb.Spt.SenseInfoLength = 32; /* sbuf size */; + sptwb.Spt.DataIn = SCSI_IOCTL_DATA_IN; + sptwb.Spt.DataTransferLength = bufsize; + sptwb.Spt.TimeOutValue = 2; /* 2 seconds */ + sptwb.Spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf); + sptwb.Spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseBuf); + length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf) + + sptwb.Spt.DataTransferLength; + + /* Set cdb info */ + sptwb.Spt.Cdb[0] = 0x12; /* SCSI Inquiry */ + sptwb.Spt.Cdb[1] = 1; + sptwb.Spt.Cdb[2] = page_code; + sptwb.Spt.Cdb[3] = 0; + sptwb.Spt.Cdb[4] = bufsize; + sptwb.Spt.Cdb[5] = 0; + + status = DeviceIoControl(ipod->dh, + IOCTL_SCSI_PASS_THROUGH, + &sptwb, + sizeof(SCSI_PASS_THROUGH), + &sptwb, + length, + &returned, + FALSE); + + if (status) { + /* W32 sometimes returns more bytes with additional garbage. + * Make sure to not copy that garbage. */ + memcpy(buf, sptwb.DataBuf, + (DWORD)bufsize >= returned ? returned : (DWORD)bufsize); + return 0; + } else { + return -1; + } +} +#endif + diff --git a/utils/ipodpatcher/ipodio-win32.c b/utils/ipodpatcher/ipodio-win32.c new file mode 100644 index 0000000000..cea218774a --- /dev/null +++ b/utils/ipodpatcher/ipodio-win32.c @@ -0,0 +1,226 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 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/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#if defined(_WIN32) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipodio.h" + +static int lock_volume(HANDLE hDisk) +{ + DWORD dummy; + + return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, + &dummy, NULL); +} + +static int unlock_volume(HANDLE hDisk) +{ + DWORD dummy; + + return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, + &dummy, NULL); +} + +void ipod_print_error(char* msg) +{ + LPSTR pMsgBuf = NULL; + + printf(msg); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), pMsgBuf, + 0, NULL); + printf(pMsgBuf); + LocalFree(pMsgBuf); +} + +int ipod_open(struct ipod_t* ipod, int silent) +{ + DISK_GEOMETRY_EX diskgeometry_ex; + DISK_GEOMETRY diskgeometry; + unsigned long n; + + ipod->dh = CreateFileA(ipod->diskname, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); + + if (ipod->dh == INVALID_HANDLE_VALUE) { + if (!silent) ipod_print_error(" Error opening disk: "); + if(GetLastError() == ERROR_ACCESS_DENIED) + return -2; + else + return -1; + } + + if (!lock_volume(ipod->dh)) { + if (!silent) ipod_print_error(" Error locking disk: "); + return -1; + } + + /* Defaults */ + ipod->num_heads = 0; + ipod->sectors_per_track = 0; + + if (!DeviceIoControl(ipod->dh, + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + NULL, + 0, + &diskgeometry_ex, + sizeof(diskgeometry_ex), + &n, + NULL)) { + if (!DeviceIoControl(ipod->dh, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, + 0, + &diskgeometry, + sizeof(diskgeometry), + &n, + NULL)) { + if (!silent) ipod_print_error(" Error reading disk geometry: "); + return -1; + } else { + ipod->sector_size = diskgeometry.BytesPerSector; + ipod->num_heads = diskgeometry.TracksPerCylinder; + ipod->sectors_per_track = diskgeometry.SectorsPerTrack; + } + } else { + ipod->sector_size = diskgeometry_ex.Geometry.BytesPerSector; + ipod->num_heads = diskgeometry_ex.Geometry.TracksPerCylinder; + ipod->sectors_per_track = diskgeometry_ex.Geometry.SectorsPerTrack; + } + + return 0; +} + +int ipod_reopen_rw(struct ipod_t* ipod) +{ + /* Close existing file and re-open for writing */ + unlock_volume(ipod->dh); + CloseHandle(ipod->dh); + + ipod->dh = CreateFileA(ipod->diskname, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); + + if (ipod->dh == INVALID_HANDLE_VALUE) { + ipod_print_error(" Error opening disk: "); + return -1; + } + + if (!lock_volume(ipod->dh)) { + ipod_print_error(" Error locking disk: "); + return -1; + } + + return 0; +} + +int ipod_close(struct ipod_t* ipod) +{ + unlock_volume(ipod->dh); + CloseHandle(ipod->dh); + return 0; +} + +int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize) +{ + /* The ReadFile function requires a memory buffer aligned to a multiple of + the disk sector size. */ + ipod->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE); + if (ipod->sectorbuf== NULL) { + ipod_print_error(" Error allocating a buffer: "); + return -1; + } + return 0; +} + +int ipod_dealloc_buffer(struct ipod_t* ipod) +{ + if (ipod->sectorbuf == NULL) { + return -1; + } + if(!VirtualFree(ipod->sectorbuf, 0, MEM_RELEASE)) { + ipod_print_error(" Error releasing buffer "); + return -1; + } + ipod->sectorbuf = NULL; + return 0; +} + +int ipod_seek(struct ipod_t* ipod, unsigned long pos) +{ + if (SetFilePointer(ipod->dh, pos, NULL, FILE_BEGIN)==0xffffffff) { + ipod_print_error(" Seek error "); + return -1; + } + return 0; +} + +ssize_t ipod_read(struct ipod_t* ipod, int nbytes) +{ + unsigned long count; + + if(ipod->sectorbuf == NULL) { + return -1; + } + if (!ReadFile(ipod->dh, ipod->sectorbuf, nbytes, &count, NULL)) { + ipod_print_error(" Error reading from disk: "); + return -1; + } + + return count; +} + +ssize_t ipod_write(struct ipod_t* ipod, int nbytes) +{ + unsigned long count; + + if(ipod->sectorbuf == NULL) { + return -1; + } + if (!WriteFile(ipod->dh, ipod->sectorbuf, nbytes, &count, NULL)) { + ipod_print_error(" Error writing to disk: "); + return -1; + } + + return count; +} + +#endif + diff --git a/utils/ipodpatcher/ipodio.h b/utils/ipodpatcher/ipodio.h new file mode 100644 index 0000000000..4f1a35dd09 --- /dev/null +++ b/utils/ipodpatcher/ipodio.h @@ -0,0 +1,115 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __IPODIO_H +#define __IPODIO_H + +#include +#if !defined(_WIN32) +#include +#elif defined(_MSC_VER) +/* MSVC uses a different name for ssize_t */ +#define ssize_t SSIZE_T +#endif + +#if defined(__WIN32__) || defined(_WIN32) +#include +#else +#define HANDLE int +#define O_BINARY 0 +#endif + +/* The maximum number of images in a firmware partition - a guess... */ +#define MAX_IMAGES 10 + +enum firmwaretype_t { + FTYPE_OSOS = 0, + FTYPE_RSRC, + FTYPE_AUPD, + FTYPE_HIBE, + FTYPE_OSBK +}; + +struct ipod_directory_t { + enum firmwaretype_t ftype; + int id; + uint32_t devOffset; /* Offset of image relative to one sector into bootpart*/ + uint32_t len; + uint32_t addr; + uint32_t entryOffset; + uint32_t chksum; + uint32_t vers; + uint32_t loadAddr; +}; + +/* A fake partition type - DOS partition tables can't include HFS partitions */ +#define PARTTYPE_HFS 0xffff + +struct partinfo_t { + uint32_t start; /* first sector (LBA) */ + uint32_t size; /* number of sectors */ + uint32_t type; +}; + +struct ipod_t { + unsigned char* sectorbuf; + HANDLE dh; + char diskname[4096]; + int sector_size; + int sectors_per_track; + int num_heads; + struct ipod_directory_t ipod_directory[MAX_IMAGES]; + int nimages; + int ososimage; + off_t diroffset; + off_t start; /* Offset in bytes of firmware partition from start of disk */ + off_t fwoffset; /* Offset in bytes of start of firmware images from start of disk */ + struct partinfo_t pinfo[4]; + int modelnum; + char* modelname; + char* modelstr; + char* targetname; + int macpod; + char* xmlinfo; /* The XML Device Information (if available) */ + int xmlinfo_len; + int ramsize; /* The amount of RAM in the ipod (if available) */ +#ifdef WITH_BOOTOBJS + unsigned char* bootloader; + int bootloader_len; +#endif +}; + +void ipod_print_error(char* msg); +int ipod_open(struct ipod_t* ipod, int silent); +int ipod_reopen_rw(struct ipod_t* ipod); +int ipod_close(struct ipod_t* ipod); +int ipod_seek(struct ipod_t* ipod, unsigned long pos); +int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, + unsigned char* buf, int bufsize); +ssize_t ipod_read(struct ipod_t* ipod, int nbytes); +ssize_t ipod_write(struct ipod_t* ipod, int nbytes); +int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize); +int ipod_dealloc_buffer(struct ipod_t* ipod); + +/* In fat32format.c */ +int format_partition(struct ipod_t* ipod, int partition); + +#endif diff --git a/utils/ipodpatcher/ipodpatcher.c b/utils/ipodpatcher/ipodpatcher.c new file mode 100644 index 0000000000..e047e52abe --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.c @@ -0,0 +1,2350 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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" +#include "ipodio.h" +#include "ipodpatcher.h" + +#ifdef WITH_BOOTOBJS +#include "ipod1g2g.h" +#include "ipod3g.h" +#include "ipod4g.h" +#include "ipodmini1g.h" +#include "ipodmini2g.h" +#include "ipodcolor.h" +#include "ipodnano1g.h" +#include "ipodvideo.h" +#include "ipodnano2g.h" +#endif + +#ifndef RBUTIL +#include "arc4.h" +#endif + +int ipod_verbose = 0; + + +/* The following string appears at the start of the firmware partition */ +static const char apple_stop_sign[] = "{{~~ /-----\\ "\ + "{{~~ / \\ "\ + "{{~~| | "\ + "{{~~| S T O P | "\ + "{{~~| | "\ + "{{~~ \\ / "\ + "{{~~ \\-----/ "\ + "Copyright(C) 200"\ + "1 Apple Computer"\ + ", Inc.----------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "---------------"; + +/* 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 with ipod_alloc_buf() +*/ + +char* get_parttype(unsigned int pt) +{ + int i; + static char unknown[]="Unknown"; + + if (pt == PARTTYPE_HFS) { + return "HFS/HFS+"; + } + + i=0; + while (parttypes[i].name != NULL) { + if (parttypes[i].type == pt) { + return (parttypes[i].name); + } + i++; + } + + return unknown; +} + +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); + } +} + +/* Partition table parsing code taken from Rockbox */ + +#define MAX_SECTOR_SIZE 2048 +#define SECTOR_SIZE 512 + +static inline unsigned short le2ushort(unsigned char* buf) +{ + unsigned short res = (buf[1] << 8) | buf[0]; + + return res; +} + +static inline int le2int(unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +static inline int be2int(unsigned char* buf) +{ + int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + return res; +} + +static inline int getint16le(char* buf) +{ + int16_t res = (buf[1] << 8) | buf[0]; + + return res; +} + +static inline void short2le(unsigned short val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; +} + +static inline void int2le(unsigned int val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +} + +static inline void int2be(unsigned int val, unsigned char* addr) +{ + addr[0] = (val >> 24) & 0xff; + addr[1] = (val >> 16) & 0xff; + addr[2] = (val >> 8) & 0xff; + addr[3] = val & 0xFF; +} + + +#define BYTES2INT32(array,pos)\ + ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ + ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) + +int read_partinfo(struct ipod_t* ipod, int silent) +{ + int i; + unsigned long count; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + count = ipod_read(ipod,ipod->sector_size); + + if (count <= 0) { + ipod_print_error(" Error reading from disk: "); + return -1; + } + + memset(ipod->pinfo, 0, sizeof(ipod->pinfo)); + + if ((ipod->sectorbuf[510] == 0x55) && (ipod->sectorbuf[511] == 0xaa)) { + /* DOS partition table */ + ipod->macpod = 0; + /* parse partitions */ + for ( i = 0; i < 4; i++ ) { + unsigned char* ptr = ipod->sectorbuf + 0x1be + 16*i; + ipod->pinfo[i].type = ptr[4]; + ipod->pinfo[i].start = BYTES2INT32(ptr, 8); + ipod->pinfo[i].size = BYTES2INT32(ptr, 12); + + /* extended? */ + if ( ipod->pinfo[i].type == 5 ) { + /* not handled yet */ + } + } + } else if ((ipod->sectorbuf[0] == 'E') && (ipod->sectorbuf[1] == 'R')) { + /* Apple Partition Map */ + + /* APM parsing code based on the check_mac_partitions() function in + ipodloader2 - written by Thomas Tempelmann and released + under the GPL. */ + + int blkNo = 1; + int partBlkCount = 1; + int partBlkSizMul = ipod->sectorbuf[2] / 2; + + int pmMapBlkCnt; /* # of blks in partition map */ + int pmPyPartStart; /* physical start blk of partition */ + int pmPartBlkCnt; /* # of blks in this partition */ + int i = 0; + + ipod->macpod = 1; + + memset(ipod->pinfo,0,sizeof(ipod->pinfo)); + + while (blkNo <= partBlkCount) { + if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) { + fprintf(stderr,"[ERR] Seek failed whilst reading APM\n"); + return -1; + } + + count = ipod_read(ipod, ipod->sector_size); + + if (count <= 0) { + ipod_print_error(" Error reading from disk: "); + return -1; + } + + /* see if it's a partition entry */ + if ((ipod->sectorbuf[0] != 'P') || (ipod->sectorbuf[1] != 'M')) { + /* end of partition table -> leave the loop */ + break; + } + + /* Extract the interesting entries */ + pmMapBlkCnt = be2int(ipod->sectorbuf + 4); + pmPyPartStart = be2int(ipod->sectorbuf + 8); + pmPartBlkCnt = be2int(ipod->sectorbuf + 12); + + /* update the number of part map blocks */ + partBlkCount = pmMapBlkCnt; + + if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_MDFW", 32)==0) { + /* A Firmware partition */ + ipod->pinfo[i].start = pmPyPartStart; + ipod->pinfo[i].size = pmPartBlkCnt; + ipod->pinfo[i].type = 0; + i++; + } else if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_HFS", 32)==0) { + /* A HFS partition */ + ipod->pinfo[i].start = pmPyPartStart; + ipod->pinfo[i].size = pmPartBlkCnt; + ipod->pinfo[i].type = PARTTYPE_HFS; + i++; + } + + blkNo++; /* read next partition map entry */ + } + } else { + if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); + return -1; + } + + /* Check that the partition table looks like an ipod: + 1) Partition 1 is of type 0 (Empty) but isn't empty. + 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod) + */ + if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) || + ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) && + (ipod->pinfo[1].type != PARTTYPE_HFS))) { + if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n"); + return -1; + } + + ipod->start = ipod->pinfo[0].start*ipod->sector_size; + return 0; +} + +int read_partition(struct ipod_t* ipod, int outfile) +{ + int res; + ssize_t n; + int bytesleft; + int chunksize; + int count = ipod->pinfo[0].size; + + if (ipod_seek(ipod, ipod->start) < 0) { + return -1; + } + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count); + + bytesleft = count * ipod->sector_size; + while (bytesleft > 0) { + if (bytesleft > BUFFER_SIZE) { + chunksize = BUFFER_SIZE; + } else { + chunksize = bytesleft; + } + + n = ipod_read(ipod, chunksize); + + if (n < 0) { + return -1; + } + + if (n < chunksize) { + fprintf(stderr, + "[ERR] Short read in disk_read() - requested %d, got %d\n", + chunksize,(int)n); + return -1; + } + + bytesleft -= n; + + res = write(outfile,ipod->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",(int)n,res); + return -1; + } + } + + fprintf(stderr,"[INFO] Done.\n"); + return 0; +} + +int write_partition(struct ipod_t* ipod, int infile) +{ + ssize_t res; + int n; + int bytesread; + int byteswritten = 0; + int eof; + int padding = 0; + + if (ipod_seek(ipod, ipod->start) < 0) { + return -1; + } + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + fprintf(stderr,"[INFO] Writing input file to device\n"); + bytesread = 0; + eof = 0; + while (!eof) { + n = read(infile,ipod->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 % ipod->sector_size) != 0) { + padding = (ipod->sector_size-(n % ipod->sector_size)); + n += padding; + } + } + + bytesread += n; + + res = ipod_write(ipod, n); + + if (res < 0) { + ipod_print_error(" Error writing to disk: "); + fprintf(stderr,"Bytes written: %d\n",byteswritten); + return -1; + } + + if (res != n) { + fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res); + return -1; + } + + byteswritten += res; + } + + fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n", + byteswritten-padding,padding); + return 0; +} + +char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE", "OSBK" }; + +int diskmove(struct ipod_t* ipod, int delta) +{ + int src_start; + int src_end; + int bytesleft; + int chunksize; + int n; + + src_start = ipod->ipod_directory[1].devOffset; + src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + + ipod->ipod_directory[ipod->nimages-1].len + + (ipod->sector_size-1)) & ~(ipod->sector_size-1); + bytesleft = src_end - src_start; + + if (ipod_verbose) { + fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta); + fprintf(stderr,"[VERB] src_start = %08x\n",src_start); + fprintf(stderr,"[VERB] src_end = %08x\n",src_end); + fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta); + fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta); + fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft); + } + + while (bytesleft > 0) { + if (bytesleft <= BUFFER_SIZE) { + chunksize = bytesleft; + } else { + chunksize = BUFFER_SIZE; + } + + if (ipod_verbose) { + fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n", + chunksize, + src_end-chunksize, + src_end-chunksize+delta, + (unsigned int)(ipod->start+src_end-chunksize), + (unsigned int)(ipod->start+src_end-chunksize+delta)); + } + + + if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(ipod,chunksize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < chunksize) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + chunksize,n); + return -1; + } + + if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,chunksize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < chunksize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,chunksize,n); + return -1; + } + + src_end -= chunksize; + bytesleft -= chunksize; + } + + return 0; +} + +static int rename_image(struct ipod_t* ipod, char* from, char* to) +{ + int n; + int x; + int found; + int i; + unsigned char* p; + + /* diroffset may not be sector-aligned */ + x = ipod->diroffset % ipod->sector_size; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + + p = ipod->sectorbuf + x; + + /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ + if (p[0] == 0) + { + /* Adjust diroffset */ + ipod->diroffset += ipod->sector_size - x; + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + p = ipod->sectorbuf; + } + + found = 0; + for (i=0 ; !found && i < MAX_IMAGES; i++) { + if (memcmp(p + 4, from, 4) == 0) { + memcpy(p + 4, to, 4); + + found = 1; + } + p += 40; + } + + if (!found) { + fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", from); + return -1; + } + + /* Write directory back to disk */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Write of directory failed in rename_image.\n"); + return -1; + } + + return 0; +} + +static int delete_image(struct ipod_t* ipod, char* name) +{ + int n; + int x; + int found; + int i; + unsigned char* p; + + /* diroffset may not be sector-aligned */ + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + + p = ipod->sectorbuf + x; + + /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ + if (p[0] == 0) + { + /* Adjust diroffset */ + ipod->diroffset += ipod->sector_size - x; + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + p = ipod->sectorbuf; + } + + found = 0; + for (i=0 ; !found && i < MAX_IMAGES; i++) { + if (memcmp(p + 4, name, 4) == 0) { + memset(p, 0, 40); /* Delete directory entry */ + found = 1; + } + p += 40; + } + + if (!found) { + fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", name); + return -1; + } + + /* Write directory back to disk */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Write of directory failed in delete_image.\n"); + return -1; + } + + return 0; +} + +int add_new_image(struct ipod_t* ipod, char* imagename, char* filename, int type) +{ + int length; + int found; + int i; + int x; + int n; + int infile; + int newsize; + unsigned long chksum=0; + unsigned long filechksum=0; + unsigned long offset; + unsigned char header[8]; /* Header for .ipod file */ + unsigned char* p; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); + length = ipod->bootloader_len; + infile = -1; + } + else +#endif + { + /* First check that the input file is the correct type for this ipod. */ + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + n = read(infile,header,8); + if (n < 8) { + fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); + close(infile); + return -1; + } + + if (memcmp(header+4, ipod->modelname,4)!=0) { + fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", + header[4],header[5],header[6],header[7], ipod->modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length = filesize(infile)-8; + } else { + length = filesize(infile); + } + } + + newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", + length,newsize); + + if (newsize > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + if (infile >= 0) close(infile); + return -1; + } + + /* TODO: Check if we have enough space in the partition for the new image */ + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); + } + else +#endif + { + fprintf(stderr,"[INFO] Reading input file...\n"); + + n = read(infile,ipod->sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + } + + /* Pad the data with zeros */ + memset(ipod->sectorbuf+length,0,newsize-length); + + if (type==FILETYPE_DOT_IPOD) { + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + if (chksum == filechksum) { + fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); + } else { + fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); + return -1; + } + } + + + offset = ipod->fwoffset + ipod->ipod_directory[ipod->nimages - 1].devOffset + + ipod->ipod_directory[ipod->nimages - 1].len + ipod->sector_size; + + /* 2nd Gen Nano has encrypted firmware, and the sector + preceeding the firmware contains hashes that need to be + preserved. Nano 2G images include these extra 2048 (0x800) + bytes + */ + if (ipod_seek(ipod, offset - (ipod->modelnum == 62 ? 0x800 : 0)) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,newsize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < newsize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,newsize,n); + return -1; + } + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); + + /* Now we need to create a new directory entry + + NOTE: On the Nano 2G, the checksum is the checksum of the + unencrypted firmware. But this isn't checked by the NOR + bootloader (there are cryptographic hashes in the + firmware itself), so it doesn't matter that this is + wrong. + */ + chksum = 0; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + /* Create a new directory entry */ + + /* Copy OSOS or OSBK details - we assume one of them exists */ + p = ipod->sectorbuf + x; + found = 0; + for (i = 0; !found && i < ipod->nimages; i++) { + if ((memcmp(p + 4, "soso", 4)==0) || (memcmp(p + 4, "kbso", 4)==0)) { + found = 1; + } else { + p += 40; + } + } + + if (!found) { + fprintf(stderr,"[ERR] No OSOS or OSBK image to copy directory from\n"); + return -1; + } + + /* Copy directory image */ + memcpy(ipod->sectorbuf + x + (ipod->nimages * 40), p, 40); + p = ipod->sectorbuf + x + (ipod->nimages * 40); + + /* Modify directory. */ + memcpy(p + 4, imagename, 4); + int2le(offset - ipod->fwoffset, p + 12); /* devOffset */ + int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); /* len */ + int2le(chksum, p + 28); /* checksum */ + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + + +int ipod_has_bootloader(struct ipod_t* ipod) +{ + /* The 2nd gen Nano is installed differently */ + if (ipod->modelnum == 62) { + int i; + int has_osbk = 0; + /* Check if we have an OSBK image */ + for (i = 0; i < ipod->nimages; i++) { + if (ipod->ipod_directory[i].ftype==FTYPE_OSBK) { + has_osbk = 1; + } + } + return has_osbk; + } + else { + return (ipod->ipod_directory[0].entryOffset != 0); + } +} + + +/* + Bootloader installation on the Nano2G consists of renaming the + OSOS image to OSBK and then writing the Rockbox bootloader as a + new OSOS image. + + Maybe this approach can/should be adapted for other ipods, as it + prevents the Apple bootloader loading the original firmware into + RAM along with the Rockbox bootloader (and hence will give a + faster boot when the user just wants to start Rockbox). + +*/ + +static int add_bootloader_nano2g(struct ipod_t* ipod, char* filename, int type) +{ + /* Check if we already have an OSBK image */ + if (ipod_has_bootloader(ipod) == 0) { + /* First-time install - rename OSOS to OSBK and create new OSOS for bootloader */ + fprintf(stderr,"[INFO] Creating OSBK backup image of original firmware\n"); + + if (rename_image(ipod, "soso", "kbso") < 0) { + fprintf(stderr,"[ERR] Could not rename OSOS image\n"); + return -1; + } + + /* Add our bootloader as a brand new image */ + return add_new_image(ipod, "soso", filename, type); + } else { + /* This is an update, just replace OSOS with our bootloader */ + + return write_firmware(ipod, filename, type); + } +} + + +static int delete_bootloader_nano2g(struct ipod_t* ipod) +{ + /* Check if we have an OSBK image */ + if (ipod_has_bootloader(ipod) == 0) { + fprintf(stderr,"[ERR] No OSBK image found - nothing to uninstall\n"); + return -1; + } else { + /* Delete our bootloader image */ + if (delete_image(ipod, "soso") < 0) { + fprintf(stderr,"[WARN] Could not delete OSOS image\n"); + } else { + fprintf(stderr,"[INFO] OSOS image deleted\n"); + } + + if (rename_image(ipod, "kbso", "soso") < 0) { + fprintf(stderr,"[ERR] Could not rename OSBK image\n"); + return -1; + } + + + fprintf(stderr,"[INFO] OSBK image renamed to OSOS - bootloader uninstalled.\n"); + return 0; + } +} + + +int add_bootloader(struct ipod_t* ipod, char* filename, int type) +{ + int length; + int i; + int x; + int n; + int infile; + int paddedlength; + int entryOffset; + int delta = 0; + unsigned long chksum=0; + unsigned long filechksum=0; + unsigned char header[8]; /* Header for .ipod file */ + unsigned char* bootloader_buf; + + /* The 2nd gen Nano is installed differently */ + if (ipod->modelnum == 62) { + return add_bootloader_nano2g(ipod, filename, type); + } + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + /* Calculate the position in the OSOS image where our bootloader will go. */ + if (ipod->ipod_directory[0].entryOffset>0) { + /* Keep the same entryOffset */ + entryOffset = ipod->ipod_directory[0].entryOffset; + } else { + entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1); + } + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); + memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); + length = ipod->bootloader_len; + paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1); + } + else +#endif + { + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + /* First check that the input file is the correct type for this ipod. */ + n = read(infile,header,8); + if (n < 8) { + fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); + close(infile); + return -1; + } + + if (memcmp(header+4, ipod->modelname,4)!=0) { + fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", + header[4],header[5],header[6],header[7], ipod->modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length=filesize(infile)-8; + } else { + length=filesize(infile); + } + paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + bootloader_buf = malloc(length); + if (bootloader_buf == NULL) { + fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n"); + return -1; + } + /* Now read our bootloader - we need to check it before modifying the partition*/ + n = read(infile,bootloader_buf,length); + close(infile); + + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + /* Calculate and confirm bootloader checksum */ + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += bootloader_buf[i]; + } + + if (chksum == filechksum) { + fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); + } else { + fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); + return -1; + } + } + } + + if (entryOffset+paddedlength > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + return -1; + } + + if (ipod_verbose) { + fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size); + fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset); + fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength); + } + + /* Check if we have enough space */ + /* TODO: Check the size of the partition. */ + if (ipod->nimages > 1) { + if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) > + ipod->ipod_directory[1].devOffset) { + fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); + delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength + - ipod->ipod_directory[1].devOffset + ipod->sector_size; + + if (diskmove(ipod, delta) < 0) { + fprintf(stderr,"[ERR] Image movement failed.\n"); + return -1; + } + } + } + + + /* We have moved the partitions, now we can write our bootloader */ + + /* Firstly read the original firmware into ipod->sectorbuf */ + fprintf(stderr,"[INFO] Reading original firmware...\n"); + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(ipod,entryOffset)) < 0) { + perror("[ERR] Read failed\n"); + return -1; + } + + if (n < entryOffset) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + ,entryOffset,n); + return -1; + } + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); + } + else +#endif + { + memcpy(ipod->sectorbuf+entryOffset,bootloader_buf,length); + free(bootloader_buf); + } + + /* Calculate new checksum for combined image */ + chksum = 0; + for (i=0;isectorbuf[i]; + } + + /* Now write the combined firmware image to the disk */ + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,entryOffset+paddedlength)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < (entryOffset+paddedlength)) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + ,entryOffset+paddedlength,n); + return -1; + } + + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Directory read failed\n"); + return -1; + } + + /* Update entries for image 0 */ + int2le(entryOffset+length,ipod->sectorbuf+x+16); + int2le(entryOffset,ipod->sectorbuf+x+24); + int2le(chksum,ipod->sectorbuf+x+28); + int2le(0xffffffff,ipod->sectorbuf+x+36); /* loadAddr */ + + /* Update devOffset entries for other images, if we have moved them */ + if (delta > 0) { + for (i=1;inimages;i++) { + int2le(le2int(ipod->sectorbuf+x+i*40+12)+delta,ipod->sectorbuf+x+i*40+12); + } + } + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x)); + return -1; + } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Directory write failed\n"); + return -1; + } + + return 0; +} + +int delete_bootloader(struct ipod_t* ipod) +{ + int length; + int i; + int x; + int n; + unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ + + /* The 2nd gen Nano is installed differently */ + if (ipod->modelnum == 62) { + return delete_bootloader_nano2g(ipod); + } + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + /* Removing the bootloader involves adjusting the "length", + "chksum" and "entryOffset" values in the osos image's directory + entry. */ + + /* Firstly check we have a bootloader... */ + + if (ipod_has_bootloader(ipod) == 0) { + fprintf(stderr,"[ERR] No bootloader found.\n"); + return -1; + } + + length = ipod->ipod_directory[0].entryOffset; + + /* Read the firmware so we can calculate the checksum */ + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + return -1; + } + + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", + length,i); + + if ((n = ipod_read(ipod,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + chksum = 0; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + /* Now write back the updated directory entry */ + + fprintf(stderr,"[INFO] Updating firmware checksum\n"); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update entries for image 0 */ + int2le(length,ipod->sectorbuf+x+16); + int2le(0,ipod->sectorbuf+x+24); + int2le(chksum,ipod->sectorbuf+x+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +int write_firmware(struct ipod_t* ipod, char* filename, int type) +{ + int length; + int i; + int x; + int n; + int infile; + int newsize; + int bytesavailable; + unsigned long chksum=0; + unsigned long filechksum=0; + unsigned long offset; + unsigned char header[8]; /* Header for .ipod file */ + unsigned char* p; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); + length = ipod->bootloader_len; + infile = -1; + } + else +#endif + { + /* First check that the input file is the correct type for this ipod. */ + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + n = read(infile,header,8); + if (n < 8) { + fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); + close(infile); + return -1; + } + + if (memcmp(header+4, ipod->modelname,4)!=0) { + fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", + header[4],header[5],header[6],header[7], ipod->modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length = filesize(infile)-8; + } else { + length = filesize(infile); + } + } + + newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", + length,newsize); + + if (newsize > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + if (infile >= 0) close(infile); + return -1; + } + + /* Check if we have enough space */ + /* TODO: Check the size of the partition. */ + if (ipod->nimages > 1) { + bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset; + if (bytesavailable < newsize) { + fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); + + /* TODO: Implement image movement */ + fprintf(stderr,"[ERR] Image movement not yet implemented.\n"); + close(infile); + return -1; + } + } + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); + } + else +#endif + { + fprintf(stderr,"[INFO] Reading input file...\n"); + /* We now know we have enough space, so write it. */ + n = read(infile,ipod->sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + } + + /* Pad the data with zeros */ + memset(ipod->sectorbuf+length,0,newsize-length); + + if (type==FILETYPE_DOT_IPOD) { + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + if (chksum == filechksum) { + fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); + } else { + fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); + return -1; + } + } + + + offset = ipod->fwoffset+ipod->ipod_directory[ipod->ososimage].devOffset; + + if (ipod->modelnum==62) { + + /* 2nd Gen Nano has encrypted firmware, and the sector + preceeding the firmware contains hashes that need to be + preserved. Nano 2G images include these extra 2048 (0x800) + bytes + */ + + offset -= 0x800; + + /* TODO: The above checks need to take into account this 0x800 bytes */ + } + + if (ipod_seek(ipod, offset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,newsize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < newsize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,newsize,n); + return -1; + } + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); + + /* Now we need to update the "len", "entryOffset" and "chksum" fields + + NOTE: On the Nano 2G, the checksum is the checksum of the + unencrypted firmware. But this isn't checked by the NOR + bootloader (there are cryptographic hashes in the + firmware itself), so it doesn't matter that this is + wrong. + */ + chksum = 0; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update entries for image */ + p = ipod->sectorbuf + x + (ipod->ososimage * 40); + int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); + int2le(0, p + 24); + int2le(chksum, p + 28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +int read_firmware(struct ipod_t* ipod, char* filename, int type) +{ + int length; + int i; + int outfile; + int n; + unsigned long offset; + unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ + unsigned char header[8]; /* Header for .ipod file */ + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + if (ipod->ipod_directory[ipod->ososimage].entryOffset != 0) { + /* We have a bootloader... */ + length = ipod->ipod_directory[ipod->ososimage].entryOffset; + } else { + length = ipod->ipod_directory[ipod->ososimage].len; + } + + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + offset = ipod->fwoffset + ipod->ipod_directory[ipod->ososimage].devOffset; + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", + length,i); + + if (ipod->modelnum==62) { + /* 2nd Gen Nano has encrypted firmware, and we need to dump the + sector preceeding the image - it contains hashes */ + offset -= 0x800; + length += 0x800; + i += 0x800; + } + + if (ipod_seek(ipod, offset)) { + return -1; + } + + if ((n = ipod_read(ipod,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); + if (outfile < 0) { + fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); + return -1; + } + + if (type == FILETYPE_DOT_IPOD) { + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + int2be(chksum,header); + memcpy(header+4, ipod->modelname,4); + + n = write(outfile,header,8); + if (n != 8) { + fprintf(stderr,"[ERR] Write error - %d\n",n); + } + } + + n = write(outfile,ipod->sectorbuf,length); + if (n != length) { + fprintf(stderr,"[ERR] Write error - %d\n",n); + } + close(outfile); + + return 0; +} + +int read_directory(struct ipod_t* ipod) +{ + ssize_t n; + int x; + unsigned char* p; + unsigned short version; + + ipod->nimages=0; + + /* Read firmware partition header (first 512 bytes of disk - but + let's read a whole sector) */ + + if (ipod_seek(ipod, ipod->start) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n", + (unsigned int)(ipod->start)); + return -1; + } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] ipod_read(ipod,0x%08x) failed in read_directory()\n", ipod->sector_size); + return -1; + } + + if (memcmp(ipod->sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) { + fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n"); + return -1; + } + + if (memcmp(ipod->sectorbuf+0x100,"]ih[",4)!=0) { + fprintf(stderr,"[ERR] Bad firmware directory\n"); + return -1; + } + + version = le2ushort(ipod->sectorbuf+0x10a); + if ((version != 2) && (version != 3)) { + fprintf(stderr,"[ERR] Unknown firmware format version %04x\n", + version); + } + ipod->diroffset=le2int(ipod->sectorbuf+0x104) + 0x200; + + /* diroffset may not be sector-aligned */ + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + + p = ipod->sectorbuf + x; + + /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ + if (p[0] == 0) + { + /* Adjust diroffset */ + ipod->diroffset += ipod->sector_size - x; + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + p = ipod->sectorbuf; + } + + ipod->ososimage = -1; + while ((ipod->nimages < MAX_IMAGES) && (p < (ipod->sectorbuf + x + 400)) && + ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) { + p+=4; + if (memcmp(p,"soso",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS; + ipod->ososimage = ipod->nimages; + } else if (memcmp(p,"crsr",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC; + } else if (memcmp(p,"dpua",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD; + } else if (memcmp(p,"kbso",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSBK; + } else if (memcmp(p,"ebih",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE; + } else { + fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n", + p[0],p[1],p[2],p[3]); + } + p+=4; + ipod->ipod_directory[ipod->nimages].id=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].devOffset=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].len=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].addr=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].chksum=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].vers=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p); + p+=4; + ipod->nimages++; + } + + if (ipod->ososimage < 0) { + fprintf(stderr,"[ERR] No OSOS image found.\n"); + return -1; + } + + if ((ipod->nimages > 1) && (version==2)) { + /* The 3g firmware image doesn't appear to have a version, so + let's make one up... Note that this is never written back to the + ipod, so it's OK to do. */ + + if (ipod->ipod_directory[ipod->ososimage].vers == 0) { ipod->ipod_directory[ipod->ososimage].vers = 3; } + + ipod->fwoffset = ipod->start; + } else { + ipod->fwoffset = ipod->start + ipod->sector_size; + } + + return 0; +} + +int list_images(struct ipod_t* ipod) +{ + int i; + + if (ipod_verbose) { + printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); + for (i = 0 ; i < ipod->nimages; i++) { + printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, + ftypename[ipod->ipod_directory[i].ftype], + ipod->ipod_directory[i].id, + ipod->ipod_directory[i].devOffset, + ipod->ipod_directory[i].len, + ipod->ipod_directory[i].addr, + ipod->ipod_directory[i].entryOffset, + ipod->ipod_directory[i].chksum, + ipod->ipod_directory[i].vers, + ipod->ipod_directory[i].loadAddr, + ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1))); + } + } + + printf("\n"); + printf("Listing firmware partition contents:\n"); + printf("\n"); + + for (i = 0 ; i < ipod->nimages; i++) { + printf("Image %d:\n",i+1); + switch(ipod->ipod_directory[i].ftype) { + case FTYPE_OSOS: + if (ipod->ipod_directory[i].entryOffset==0) { + printf(" Main firmware - %d bytes\n", + ipod->ipod_directory[i].len); + } else { + printf(" Main firmware - %d bytes\n", + ipod->ipod_directory[i].entryOffset); + printf(" Third-party bootloader - %d bytes\n", + ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset); + } + break; + default: + printf(" %s - %d bytes\n", + ftypename[ipod->ipod_directory[i].ftype], + ipod->ipod_directory[i].len); + } + } + printf("\n"); + + return 0; +} + +int getmodel(struct ipod_t* ipod, int ipod_version) +{ + switch (ipod_version) { + case 0x01: + ipod->modelstr="1st or 2nd Generation"; + ipod->modelnum = 19; + ipod->modelname = "1g2g"; + ipod->targetname = "ipod1g2g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipod1g2g; + ipod->bootloader_len = LEN_ipod1g2g; +#endif + break; + case 0x02: + ipod->modelstr="3rd Generation"; + ipod->modelnum = 7; + ipod->modelname = "ip3g"; + ipod->targetname = "ipod3g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipod3g; + ipod->bootloader_len = LEN_ipod3g; +#endif + break; + case 0x40: + ipod->modelstr="1st Generation Mini"; + ipod->modelnum = 9; + ipod->modelname = "mini"; + ipod->targetname = "ipodmini1g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodmini1g; + ipod->bootloader_len = LEN_ipodmini1g; +#endif + break; + case 0x50: + ipod->modelstr="4th Generation"; + ipod->modelnum = 8; + ipod->modelname = "ip4g"; + ipod->targetname = "ipod4gray"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipod4g; + ipod->bootloader_len = LEN_ipod4g; +#endif + break; + case 0x60: + ipod->modelstr="Photo/Color"; + ipod->modelnum = 3; + ipod->modelname = "ipco"; + ipod->targetname = "ipodcolor"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodcolor; + ipod->bootloader_len = LEN_ipodcolor; +#endif + break; + case 0x70: + ipod->modelstr="2nd Generation Mini"; + ipod->modelnum = 11; + ipod->modelname = "mn2g"; + ipod->targetname = "ipodmini2g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodmini2g; + ipod->bootloader_len = LEN_ipodmini2g; +#endif + break; + case 0xc0: + ipod->modelstr="1st Generation Nano"; + ipod->modelnum = 4; + ipod->modelname = "nano"; + ipod->targetname = "ipodnano1g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodnano1g; + ipod->bootloader_len = LEN_ipodnano1g; +#endif + break; + case 0xb0: + ipod->modelstr="Video (aka 5th Generation)"; + ipod->modelnum = 5; + ipod->modelname = "ipvd"; + ipod->targetname = "ipodvideo"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodvideo; + ipod->bootloader_len = LEN_ipodvideo; +#endif + break; + case 0x100: + ipod->modelstr="2nd Generation Nano"; + ipod->modelnum = 62; + ipod->modelname = "nn2x"; + ipod->targetname = "ipodnano2g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodnano2g; + ipod->bootloader_len = LEN_ipodnano2g; +#endif + break; + default: + ipod->modelname = NULL; + ipod->modelnum = 0; + ipod->targetname = NULL; +#ifdef WITH_BOOTOBJS + ipod->bootloader = NULL; + ipod->bootloader_len = 0; +#endif + return -1; + } + return 0; +} + +/* returns number of found ipods or -1 if no ipods found and permission + * for raw disc access was denied. */ +int ipod_scan(struct ipod_t* ipod) +{ + int i; + int n = 0; + int ipod_version; + struct ipod_t ipod_found; + int denied = 0; + int result; + + printf("[INFO] Scanning disk devices...\n"); + + for (i = 0; i <= 25 ; i++) { +#ifdef __WIN32__ + sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i); +#elif defined(linux) || defined (__linux) + sprintf(ipod->diskname,"/dev/sd%c",'a'+i); +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__bsdi__) || defined(__DragonFly__) + sprintf(ipod->diskname,"/dev/da%d",i); +#elif defined(__APPLE__) && defined(__MACH__) + sprintf(ipod->diskname,"/dev/disk%d",i); +#else + #error No disk paths defined for this platform +#endif + if ((result = ipod_open(ipod, 1)) < 0) { + if(result == -2) { + denied++; + } + ipod_close(ipod); + continue; + } + + if (read_partinfo(ipod,1) < 0) { + ipod_close(ipod); + continue; + } + + if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) { + ipod_close(ipod); + continue; + } + + if (read_directory(ipod) < 0) { + ipod_close(ipod); + continue; + } + + ipod_version=(ipod->ipod_directory[ipod->ososimage].vers>>8); + ipod->ramsize = 0; +#ifdef __WIN32__ + /* Windows requires the ipod in R/W mode for SCSI Inquiry. + * ipod_reopen_rw does unmount the player on OS X so do this on + * W32 only during scanning. */ + ipod_reopen_rw(ipod); +#endif + ipod_get_xmlinfo(ipod); + ipod_get_ramsize(ipod); + if (getmodel(ipod,ipod_version) < 0) { + ipod_close(ipod); + continue; + } + +#ifdef __WIN32__ + printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n", + ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i); +#else + printf("[INFO] Ipod found - %s (\"%s\") - %s\n", + ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname); +#endif + n++; + /* save the complete ipod_t structure for match. The for loop might + * overwrite it, so we need to restore it later if only one found. */ + memcpy(&ipod_found, ipod, sizeof(struct ipod_t)); + ipod_close(ipod); + } + + if (n==1) { + /* restore the ipod_t structure, it might have been overwritten */ + memcpy(ipod, &ipod_found, sizeof(struct ipod_t)); + } + else if(n == 0 && denied) { + printf("[ERR] FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied); +#ifdef __WIN32__ + printf("[ERR] You need to run this program with administrator priviledges!\n"); +#else + printf("[ERR] You need permissions for raw disc access for this program to work!\n"); +#endif + } + return (n == 0 && denied) ? -1 : n; +} + +static void put_int32le(uint32_t x, unsigned char* p) +{ + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +int write_dos_partition_table(struct ipod_t* ipod) +{ + unsigned char* p; + int i, n; + uint32_t type; + + /* Only support 512-byte sectors at the moment */ + if ( ipod->sector_size != 512 ) + { + fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n"); + return -1; + } + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + + /* Firstly zero the entire MBR */ + memset(ipod->sectorbuf, 0, ipod->sector_size); + + /* Now add the partition info */ + for (i=0; i < 4 ; i++) + { + p = ipod->sectorbuf + 0x1be + i*16; + + /* Ensure first partition is type 0, and second is 0xb */ + if (i==0) { type = 0; } + else if (i==1) { type = 0xb; } + else { type = ipod->pinfo[i].type; } + + put_int32le(type, p + 4); + put_int32le(ipod->pinfo[i].start, p + 8); + put_int32le(ipod->pinfo[i].size, p + 12); + } + + /* Finally add the magic */ + ipod->sectorbuf[0x1fe] = 0x55; + ipod->sectorbuf[0x1ff] = 0xaa; + + if (ipod_seek(ipod, 0) < 0) { + fprintf(stderr,"[ERR] Seek failed writing MBR\n"); + return -1; + } + + /* Write MBR */ + if ((n = ipod_write(ipod, ipod->sector_size)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + return 0; +} + +/* Get the XML Device Information, as documented here: + + http://www.ipodlinux.org/wiki/Device_Information +*/ + +int ipod_get_xmlinfo(struct ipod_t* ipod) +{ + unsigned char hdr[255]; + unsigned char buf[255]; + char* p; + int psize; + int npages; + int i; + + if (ipod_scsi_inquiry(ipod, 0xc0, buf, sizeof(buf)) < 0) + { + fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); + return -1; + } + + /* Reading directly into hdr[] causes problems (for an unknown reason) on + win32 */ + memcpy(hdr, buf, sizeof(hdr)); + + npages = hdr[3]; + + psize = npages * 0xf8; /* Hopefully this is enough. */ + + ipod->xmlinfo = malloc(psize); + ipod->xmlinfo_len = 0; + + if (ipod->xmlinfo == NULL) { + fprintf(stderr,"[ERR] Could not allocate RAM for xmlinfo\n"); + return -1; + } + + p = ipod->xmlinfo; + + for (i=0; i < npages; i++) { + if (ipod_scsi_inquiry(ipod, hdr[i+4], buf, sizeof(buf)) < 0) { + fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); + return -1; + } + + if ((buf[3] + ipod->xmlinfo_len) > psize) { + fprintf(stderr,"[ERR] Ran out of memory reading xmlinfo\n"); + free(ipod->xmlinfo); + ipod->xmlinfo = NULL; + ipod->xmlinfo_len = 0; + return -1; + } + + memcpy(p, buf + 4, buf[3]); + p += buf[3]; + ipod->xmlinfo_len += buf[3]; + } + + /* NULL-terminate the XML info */ + *p = 0; + + fprintf(stderr,"[INFO] Read XML info (%d bytes)\n",ipod->xmlinfo_len); + + return 0; +} + +void ipod_get_ramsize(struct ipod_t* ipod) +{ + const char needle[] = "RAM\n"; + char* p; + + if (ipod->xmlinfo == NULL) + return; + + p = strstr(ipod->xmlinfo, needle); + + if (p) { + ipod->ramsize = atoi(p + sizeof(needle) - 1); + } +} + +#ifndef RBUTIL + +static inline uint32_t getuint32le(unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +/* testMarker and GetSecurityBlockKey based on code from BadBlocks and + Kingstone, posted at http://ipodlinux.org/Flash_Decryption + +*/ + +static bool testMarker(int marker) +{ + int mask, decrypt, temp1, temp2; + + mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24); + decrypt = marker ^ mask; + temp1=(int)((unsigned int)decrypt>>24); + temp2=decrypt<<8; + + if (temp1==0) + return false; + + temp2=(int)((unsigned int)temp2>>24); + decrypt=decrypt<<16; + decrypt=(int)((unsigned int)decrypt>>24); + + if ((temp1 < temp2) && (temp2 < decrypt)) + { + temp1 = temp1 & 0xf; + temp2 = temp2 & 0xf; + decrypt = decrypt & 0xf; + + if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0)) + { + return true; + } + } + return false; +} + +static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key) +{ + int constant = 0x54c3a298; + int key=0; + int nkeys = 0; + int aMarker=0; + int pos=0; + int c, count; + int temp1; + static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34}; + + for (c = 0; c < 8; c++) + { + pos = offset[c]*4; + aMarker = getuint32le(data + pos); + + if (testMarker(aMarker)) + { + if (c<7) + pos =(offset[c+1]*4)+4; + else + pos =(offset[0]*4)+4; + + key=0; + + temp1=aMarker; + + for (count=0;count<2;count++){ + int word = getuint32le(data + pos); + temp1 = aMarker; + temp1 = temp1^word; + temp1 = temp1^constant; + key = temp1; + pos = pos+4; + } + int r1=0x6f; + int r2=0; + int r12; + int r14; + unsigned int r_tmp; + + for (count=2;count<128;count=count+2){ + r2=getuint32le(data+count*4); + r12=getuint32le(data+(count*4)+4); + r_tmp=(unsigned int)r12>>16; + r14=r2 | ((int)r_tmp); + r2=r2&0xffff; + r2=r2 | r12; + r1=r1^r14; + r1=r1+r2; + } + key=key^r1; + + // Invert key, little endian + this_key[0] = key & 0xff; + this_key[1] = (key >> 8) & 0xff; + this_key[2] = (key >> 16) & 0xff; + this_key[3] = (key >> 24) & 0xff; + nkeys++; + } + } + return nkeys; +} + +static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key) +{ + int n; + + /* Firstly read the security block and find the RC4 key. This is + in the sector preceeding the AUPD image. */ + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size); + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) { + return -1; + } + + if ((n = ipod_read(ipod, 512)) < 0) { + return -1; + } + + n = GetSecurityBlockKey(ipod->sectorbuf, key); + + if (n != 1) + { + fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n); + return -1; + } + + return 0; +} + +int read_aupd(struct ipod_t* ipod, char* filename) +{ + int length; + int i; + int outfile; + int n; + int aupd; + struct rc4_key_t rc4; + unsigned char key[4]; + unsigned long chksum=0; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + aupd = 0; + while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) + { + aupd++; + } + + if (aupd == ipod->nimages) + { + fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); + return -1; + } + + length = ipod->ipod_directory[aupd].len; + + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + if (find_key(ipod, aupd, key) < 0) + { + return -1; + } + + fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { + return -1; + } + + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + + if ((n = ipod_read(ipod,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + /* Perform the decryption - this is standard (A)RC4 */ + matrixArc4Init(&rc4, key, 4); + matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); + + chksum = 0; + for (i = 0; i < (int)length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + if (chksum != ipod->ipod_directory[aupd].chksum) + { + fprintf(stderr,"[ERR] Decryption failed - checksum error\n"); + return -1; + } + fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n"); + + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); + if (outfile < 0) { + fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); + return -1; + } + + n = write(outfile,ipod->sectorbuf,length); + if (n != length) { + fprintf(stderr,"[ERR] Write error - %d\n",n); + } + close(outfile); + + return 0; +} + +int write_aupd(struct ipod_t* ipod, char* filename) +{ + unsigned int length; + int i; + int x; + int n; + int infile; + int newsize; + int aupd; + unsigned long chksum=0; + struct rc4_key_t rc4; + unsigned char key[4]; + + if(ipod->sectorbuf == NULL) { + fprintf(stderr,"[ERR] Buffer not initialized."); + return -1; + } + /* First check that the input file is the correct type for this ipod. */ + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + length = filesize(infile); + newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", + length,newsize); + + if (newsize > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + if (infile >= 0) close(infile); + return -1; + } + + /* Find aupd image number */ + aupd = 0; + while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) + { + aupd++; + } + + if (aupd == ipod->nimages) + { + fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); + return -1; + } + + if (length != ipod->ipod_directory[aupd].len) + { + fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n", + ipod->ipod_directory[aupd].len, filename, length); + return -1; + } + + if (find_key(ipod, aupd, key) < 0) + { + return -1; + } + + fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); + + /* We now know we have enough space, so write it. */ + + fprintf(stderr,"[INFO] Reading input file...\n"); + n = read(infile,ipod->sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + + /* Pad the data with zeros */ + memset(ipod->sectorbuf+length,0,newsize-length); + + /* Calculate the new checksum (before we encrypt) */ + chksum = 0; + for (i = 0; i < (int)length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += ipod->sectorbuf[i]; + } + + /* Perform the encryption - this is standard (A)RC4 */ + matrixArc4Init(&rc4, key, 4); + matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,newsize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < newsize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,newsize,n); + return -1; + } + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update checksum */ + fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(ipod->sectorbuf + x + aupd*40 + 28)); + int2le(chksum,ipod->sectorbuf+x+aupd*40+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +#endif diff --git a/utils/ipodpatcher/ipodpatcher.h b/utils/ipodpatcher/ipodpatcher.h new file mode 100644 index 0000000000..2cd2331666 --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef IPODPATCHER_H +#define IPODPATCHER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ipodio.h" + +/* exit codes */ +#define IPOD_OK 0 +#define IPOD_WRONG_ARGUMENTS 1 +#define IPOD_OPEN_INFILE_FAILED 2 +#define IPOD_PARTITION_ERROR 3 +#define IPOD_OPEN_OUTFILE_FAILED 4 +#define IPOD_CANNOT_REOPEN 5 +#define IPOD_ACCESS_DENIED 10 +#define IPOD_NOT_FOUND 11 +#define IPOD_WRONG_DEVICE_COUNT 12 +#define IPOD_IMAGE_ERROR 13 +#define IPOD_DUMP_FAILED 14 +#define IPOD_MULTIPLE_DEVICES 15 +#define IPOD_WRONG_TYPE 16 +#define IPOD_UNKNOWN_FW_VERSION -1 + +/* Size of buffer for disk I/O - 8MB is large enough for any version + of the Apple firmware, but not the Nano's RSRC image. */ +#define BUFFER_SIZE 8*1024*1024 + +extern int ipod_verbose; + +#define FILETYPE_DOT_IPOD 0 +#define FILETYPE_DOT_BIN 1 +#ifdef WITH_BOOTOBJS + #define FILETYPE_INTERNAL 2 +#endif + +char* get_parttype(unsigned int pt); +int read_partinfo(struct ipod_t* ipod, int silent); +int read_partition(struct ipod_t* ipod, int outfile); +int write_partition(struct ipod_t* ipod, int infile); +int diskmove(struct ipod_t* ipod, int delta); +int add_bootloader(struct ipod_t* ipod, char* filename, int type); +int delete_bootloader(struct ipod_t* ipod); +int write_firmware(struct ipod_t* ipod, char* filename, int type); +int read_firmware(struct ipod_t* ipod, char* filename, int type); +int read_directory(struct ipod_t* ipod); +int list_images(struct ipod_t* ipod); +int getmodel(struct ipod_t* ipod, int ipod_version); +int ipod_scan(struct ipod_t* ipod); +int write_dos_partition_table(struct ipod_t* ipod); +int ipod_get_xmlinfo(struct ipod_t* ipod); +void ipod_get_ramsize(struct ipod_t* ipod); +int read_aupd(struct ipod_t* ipod, char* filename); +int write_aupd(struct ipod_t* ipod, char* filename); +off_t filesize(int fd); +int ipod_has_bootloader(struct ipod_t* ipod); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/utils/ipodpatcher/ipodpatcher.manifest b/utils/ipodpatcher/ipodpatcher.manifest new file mode 100644 index 0000000000..695ecb26ea --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.manifest @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/utils/ipodpatcher/ipodpatcher.pro b/utils/ipodpatcher/ipodpatcher.pro new file mode 100644 index 0000000000..fac1e35f00 --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.pro @@ -0,0 +1,47 @@ +# +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# +# 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. +# + +TEMPLATE = app +TARGET = ipodpatcher +QT -= core + +SOURCES += \ + main.c \ + ipodpatcher.c \ + ipodio-posix.c \ + ipodio-win32-scsi.c \ + ipodio-win32.c \ + fat32format.c \ + arc4.c \ + +HEADERS += \ + arc4.h \ + ipodio.h \ + ipodpatcher.h \ + parttypes.h \ + +DEFINES += RELEASE=1 _LARGEFILE64_SOURCE + +RC_FILE = ipodpatcher.rc + +macx { + LIBS += -framework CoreFoundation -framework IOKit +} + + +unix { + target.path = /usr/local/bin + INSTALLS += target +} diff --git a/utils/ipodpatcher/ipodpatcher.rc b/utils/ipodpatcher/ipodpatcher.rc new file mode 100644 index 0000000000..e440b51271 --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.rc @@ -0,0 +1 @@ +1 24 MOVEABLE PURE "ipodpatcher.manifest" diff --git a/utils/ipodpatcher/main.c b/utils/ipodpatcher/main.c new file mode 100644 index 0000000000..7b0a909178 --- /dev/null +++ b/utils/ipodpatcher/main.c @@ -0,0 +1,622 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 "ipodpatcher.h" +#include "ipodio.h" + +#ifdef RELEASE +#undef VERSION +#define VERSION "5.0 with v4.0 bootloaders (v1.0 for 2nd Gen Nano)" +#endif + + +enum { + NONE, +#ifdef WITH_BOOTOBJS + INSTALL, +#endif + INTERACTIVE, + SHOW_INFO, + LIST_IMAGES, + DELETE_BOOTLOADER, + ADD_BOOTLOADER, + READ_FIRMWARE, + WRITE_FIRMWARE, + READ_AUPD, + WRITE_AUPD, + READ_PARTITION, + WRITE_PARTITION, + FORMAT_PARTITION, + DUMP_XML, + CONVERT_TO_FAT32 +}; + +void print_macpod_warning(void) +{ + printf("[INFO] ************************************************************************\n"); + printf("[INFO] *** WARNING FOR ROCKBOX USERS\n"); + printf("[INFO] *** You must convert this ipod to FAT32 format (aka a \"winpod\")\n"); + printf("[INFO] *** if you want to run Rockbox. Rockbox WILL NOT work on this ipod.\n"); + printf("[INFO] *** See http://www.rockbox.org/wiki/IpodConversionToFAT32\n"); + printf("[INFO] ************************************************************************\n"); +} + +void print_usage(void) +{ + fprintf(stderr,"Usage: ipodpatcher --scan\n"); +#ifdef __WIN32__ + fprintf(stderr," or ipodpatcher [DISKNO] [action]\n"); +#else + fprintf(stderr," or ipodpatcher [device] [action]\n"); +#endif + fprintf(stderr,"\n"); + fprintf(stderr,"Where [action] is one of the following options:\n"); +#ifdef WITH_BOOTOBJS + fprintf(stderr," --install\n"); +#endif + fprintf(stderr," -l, --list\n"); + fprintf(stderr," -r, --read-partition bootpartition.bin\n"); + fprintf(stderr," -w, --write-partition bootpartition.bin\n"); + fprintf(stderr," -rf, --read-firmware filename.ipod[x]\n"); + fprintf(stderr," -rfb, --read-firmware-bin filename.bin\n"); + fprintf(stderr," -wf, --write-firmware filename.ipod[x]\n"); + fprintf(stderr," -wfb, --write-firmware-bin filename.bin\n"); +#ifdef WITH_BOOTOBJS + fprintf(stderr," -we, --write-embedded\n"); +#endif + fprintf(stderr," -a, --add-bootloader filename.ipod[x]\n"); + fprintf(stderr," -ab, --add-bootloader-bin filename.bin\n"); + fprintf(stderr," -d, --delete-bootloader\n"); + fprintf(stderr," -f, --format\n"); + fprintf(stderr," -c, --convert\n"); + fprintf(stderr," --read-aupd filename.bin\n"); + fprintf(stderr," --write-aupd filename.bin\n"); + fprintf(stderr," -x --dump-xml filename.xml\n"); + fprintf(stderr,"\n"); + + fprintf(stderr,"The .ipodx extension is used for encrypted images for the 2nd Gen Nano.\n\n"); + +#ifdef __WIN32__ + 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 disk 0, 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"); +#else +#if defined(linux) || defined (__linux) + fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your ipod.\n"); +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your ipod.\n"); +#elif defined(__APPLE__) && defined(__MACH__) + fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your ipod.\n"); +#endif + fprintf(stderr,"ipodpatcher will refuse to access a disk unless it can identify it as being\n"); + fprintf(stderr,"an ipod.\n"); +#endif +} + +void display_partinfo(struct ipod_t* ipod) +{ + int i; + double sectors_per_MB = (1024.0*1024.0)/ipod->sector_size; + + printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); + for ( i = 0; i < 4; i++ ) { + if (ipod->pinfo[i].start != 0) { + printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", + i, + (long int)ipod->pinfo[i].start, + (long int)ipod->pinfo[i].start+ipod->pinfo[i].size-1, + ipod->pinfo[i].size/sectors_per_MB, + get_parttype(ipod->pinfo[i].type), + (int)ipod->pinfo[i].type); + } + } +} + + +int main(int argc, char* argv[]) +{ + char yesno[4]; + int i; + int n; + int infile, outfile; + unsigned int inputsize; + char* filename; + int action = SHOW_INFO; + int type; + struct ipod_t ipod; + + fprintf(stderr,"ipodpatcher " VERSION "\n"); + fprintf(stderr,"(C) Dave Chapman 2006-2009\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) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) { + print_usage(); + return IPOD_OK; + } + + if (ipod_alloc_buffer(&ipod,BUFFER_SIZE) < 0) { + fprintf(stderr,"Failed to allocate memory buffer\n"); + } + + if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) { + if (ipod_scan(&ipod) == 0) + fprintf(stderr,"[ERR] No ipods found.\n"); + return IPOD_NOT_FOUND; + } + + /* If the first parameter doesn't start with -, then we interpret it as a device */ + if ((argc > 1) && (argv[1][0] != '-')) { + ipod.diskname[0]=0; +#ifdef __WIN32__ + snprintf(ipod.diskname,sizeof(ipod.diskname),"\\\\.\\PhysicalDrive%s",argv[1]); +#else + strncpy(ipod.diskname,argv[1],sizeof(ipod.diskname)); +#endif + i = 2; + } else { + /* Autoscan for ipods */ + n = ipod_scan(&ipod); + if (n==0) { + fprintf(stderr,"[ERR] No ipods found, aborting\n"); + fprintf(stderr,"[ERR] Please connect your ipod and ensure it is in disk mode\n"); +#if defined(__APPLE__) && defined(__MACH__) + fprintf(stderr,"[ERR] Also ensure that itunes is closed, and that your ipod is not mounted.\n"); +#elif !defined(__WIN32__) + if (geteuid()!=0) { + fprintf(stderr,"[ERR] You may also need to run ipodpatcher as root.\n"); + } +#endif + fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n"); + } else if (n > 1) { + fprintf(stderr,"[ERR] %d ipods found, aborting\n",n); + fprintf(stderr,"[ERR] Please connect only one ipod and re-run ipodpatcher.\n"); + return IPOD_MULTIPLE_DEVICES; + } else if (n == 1 && ipod.macpod) { + return IPOD_WRONG_TYPE; + } + + if (n != 1) { +#ifdef WITH_BOOTOBJS + if (argc==1) { + printf("\nPress ENTER to exit ipodpatcher :"); + fgets(yesno,4,stdin); + } +#endif + return IPOD_NOT_FOUND; + } + + i = 1; + } + +#ifdef WITH_BOOTOBJS + action = INTERACTIVE; +#else + action = NONE; +#endif + + while (i < argc) { + if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { + action = LIST_IMAGES; + i++; +#ifdef WITH_BOOTOBJS + } else if (strcmp(argv[i],"--install")==0) { + action = INSTALL; + i++; +#endif + } else if ((strcmp(argv[i],"-d")==0) || + (strcmp(argv[i],"--delete-bootloader")==0)) { + action = DELETE_BOOTLOADER; + i++; + } else if ((strcmp(argv[i],"-a")==0) || + (strcmp(argv[i],"--add-bootloader")==0)) { + action = ADD_BOOTLOADER; + type = FILETYPE_DOT_IPOD; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-ab")==0) || + (strcmp(argv[i],"--add-bootloader-bin")==0)) { + action = ADD_BOOTLOADER; + type = FILETYPE_DOT_BIN; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-rf")==0) || + (strcmp(argv[i],"--read-firmware")==0)) { + action = READ_FIRMWARE; + type = FILETYPE_DOT_IPOD; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-rfb")==0) || + (strcmp(argv[i],"--read-firmware-bin")==0)) { + action = READ_FIRMWARE; + type = FILETYPE_DOT_BIN; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; +#ifdef WITH_BOOTOBJS + } else if ((strcmp(argv[i],"-we")==0) || + (strcmp(argv[i],"--write-embedded")==0)) { + action = WRITE_FIRMWARE; + type = FILETYPE_INTERNAL; + filename="[embedded bootloader]"; /* Only displayed for user */ + i++; +#endif + } else if ((strcmp(argv[i],"-wf")==0) || + (strcmp(argv[i],"--write-firmware")==0)) { + action = WRITE_FIRMWARE; + type = FILETYPE_DOT_IPOD; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-wfb")==0) || + (strcmp(argv[i],"--write-firmware-bin")==0)) { + action = WRITE_FIRMWARE; + type = FILETYPE_DOT_BIN; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-r")==0) || + (strcmp(argv[i],"--read-partition")==0)) { + action = READ_PARTITION; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-w")==0) || + (strcmp(argv[i],"--write-partition")==0)) { + action = WRITE_PARTITION; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-v")==0) || + (strcmp(argv[i],"--verbose")==0)) { + ipod_verbose++; + i++; + } else if ((strcmp(argv[i],"-f")==0) || + (strcmp(argv[i],"--format")==0)) { + action = FORMAT_PARTITION; + i++; + } else if (strcmp(argv[i],"--read-aupd")==0) { + action = READ_AUPD; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if (strcmp(argv[i],"--write-aupd")==0) { + action = WRITE_AUPD; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-x")==0) || + (strcmp(argv[i],"--dump-xml")==0)) { + action = DUMP_XML; + i++; + if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-c")==0) || + (strcmp(argv[i],"--convert")==0)) { + action = CONVERT_TO_FAT32; + i++; + } else { + print_usage(); return IPOD_WRONG_ARGUMENTS; + } + } + + if (ipod.diskname[0]==0) { + print_usage(); + return 1; + } + + if (ipod_open(&ipod, 0) < 0) { + return IPOD_ACCESS_DENIED; + } + + fprintf(stderr,"[INFO] Reading partition table from %s\n",ipod.diskname); + fprintf(stderr,"[INFO] Sector size is %d bytes\n",ipod.sector_size); + + if (read_partinfo(&ipod,0) < 0) { + return IPOD_PARTITION_ERROR; + } + + display_partinfo(&ipod); + + if (ipod.pinfo[0].start==0) { + fprintf(stderr,"[ERR] No partition 0 on disk:\n"); + display_partinfo(&ipod); + return IPOD_PARTITION_ERROR; + } + + read_directory(&ipod); + + if (ipod.nimages <= 0) { + fprintf(stderr,"[ERR] Failed to read firmware directory - nimages=%d\n",ipod.nimages); + return IPOD_IMAGE_ERROR; + } + + if (getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) { + fprintf(stderr,"[ERR] Unknown version number in firmware (%08x)\n", + ipod.ipod_directory[ipod.ososimage].vers); + return IPOD_UNKNOWN_FW_VERSION; + } + +#ifdef __WIN32__ + /* Windows requires the ipod in R/W mode for SCSI Inquiry */ + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } +#endif + + + /* Read the XML info, and if successful, look for the ramsize + (only available for some models - set to 0 if not known) */ + + ipod.ramsize = 0; + + if (ipod_get_xmlinfo(&ipod) == 0) { + ipod_get_ramsize(&ipod); + } + + printf("[INFO] Ipod model: %s ",ipod.modelstr); + if (ipod.ramsize > 0) { printf("(%dMB RAM) ",ipod.ramsize); } + printf("(\"%s\")\n",ipod.macpod ? "macpod" : "winpod"); + + if (ipod.macpod) { + print_macpod_warning(); + } + + if (action==LIST_IMAGES) { + list_images(&ipod); +#ifdef WITH_BOOTOBJS + } else if (action==INTERACTIVE) { + + printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :"); + + if (fgets(yesno,4,stdin)) { + if (yesno[0]=='i') { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (add_bootloader(&ipod, NULL, FILETYPE_INTERNAL)==0) { + fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); + } else { + fprintf(stderr,"[ERR] --install failed.\n"); + } + } else if (yesno[0]=='u') { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (delete_bootloader(&ipod)==0) { + fprintf(stderr,"[INFO] Bootloader removed.\n"); + } else { + fprintf(stderr,"[ERR] Bootloader removal failed.\n"); + } + } + } +#endif + } else if (action==DELETE_BOOTLOADER) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (ipod.ipod_directory[0].entryOffset==0) { + fprintf(stderr,"[ERR] No bootloader detected.\n"); + } else { + if (delete_bootloader(&ipod)==0) { + fprintf(stderr,"[INFO] Bootloader removed.\n"); + } else { + fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); + } + } + } else if (action==ADD_BOOTLOADER) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (add_bootloader(&ipod, filename, type)==0) { + fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); + } else { + fprintf(stderr,"[ERR] --add-bootloader failed.\n"); + } +#ifdef WITH_BOOTOBJS + } else if (action==INSTALL) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (add_bootloader(&ipod, NULL, FILETYPE_INTERNAL)==0) { + fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); + } else { + fprintf(stderr,"[ERR] --install failed.\n"); + } +#endif + } else if (action==WRITE_FIRMWARE) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (write_firmware(&ipod, filename,type)==0) { + fprintf(stderr,"[INFO] Firmware %s written to device.\n",filename); + } else { + fprintf(stderr,"[ERR] --write-firmware failed.\n"); + } + } else if (action==READ_FIRMWARE) { + if (read_firmware(&ipod, filename, type)==0) { + fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); + } else { + fprintf(stderr,"[ERR] --read-firmware failed.\n"); + } + } else if (action==READ_AUPD) { + if (read_aupd(&ipod, filename)==0) { + fprintf(stderr,"[INFO] AUPD image read to file %s.\n",filename); + } else { + fprintf(stderr,"[ERR] --read-aupd failed.\n"); + } + } else if (action==WRITE_AUPD) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (write_aupd(&ipod, filename)==0) { + fprintf(stderr,"[INFO] AUPD image %s written to device.\n",filename); + } else { + fprintf(stderr,"[ERR] --write-aupd failed.\n"); + } + } else if (action==DUMP_XML) { + if (ipod.xmlinfo == NULL) { + fprintf(stderr,"[ERR] No XML to write\n"); + return IPOD_DUMP_FAILED; + } + + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); + if (outfile < 0) { + perror(filename); + return IPOD_OPEN_OUTFILE_FAILED; + } + + if (write(outfile, ipod.xmlinfo, ipod.xmlinfo_len) < 0) { + fprintf(stderr,"[ERR] --dump-xml failed.\n"); + } else { + fprintf(stderr,"[INFO] XML info written to %s.\n",filename); + } + close(outfile); + } else if (action==READ_PARTITION) { + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); + if (outfile < 0) { + perror(filename); + return IPOD_OPEN_OUTFILE_FAILED; + } + + if (read_partition(&ipod, outfile) < 0) { + fprintf(stderr,"[ERR] --read-partition failed.\n"); + } else { + fprintf(stderr,"[INFO] Partition extracted to %s.\n",filename); + } + close(outfile); + } else if (action==WRITE_PARTITION) { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + infile = open(filename,O_RDONLY|O_BINARY); + if (infile < 0) { + perror(filename); + return IPOD_OPEN_INFILE_FAILED; + } + + /* Check filesize is <= partition size */ + inputsize=filesize(infile); + if (inputsize > 0) { + if (inputsize <= (ipod.pinfo[0].size*ipod.sector_size)) { + fprintf(stderr,"[INFO] Input file is %u bytes\n",inputsize); + if (write_partition(&ipod,infile) < 0) { + fprintf(stderr,"[ERR] --write-partition failed.\n"); + } else { + fprintf(stderr,"[INFO] %s restored to partition\n",filename); + } + } else { + fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n"); + } + } + + close(infile); + } else if (action==FORMAT_PARTITION) { + printf("WARNING!!! YOU ARE ABOUT TO USE AN EXPERIMENTAL FEATURE.\n"); + printf("ALL DATA ON YOUR IPOD WILL BE ERASED.\n"); + printf("Are you sure you want to format your ipod? (y/n):"); + + if (fgets(yesno,4,stdin)) { + if (yesno[0]=='y') { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (format_partition(&ipod,1) < 0) { + fprintf(stderr,"[ERR] Format failed.\n"); + } + } else { + fprintf(stderr,"[INFO] Format cancelled.\n"); + } + } + } else if (action==CONVERT_TO_FAT32) { + if (!ipod.macpod) { + printf("[ERR] Ipod is already FAT32, aborting\n"); + } else { + printf("WARNING!!! YOU ARE ABOUT TO USE AN EXPERIMENTAL FEATURE.\n"); + printf("ALL DATA ON YOUR IPOD WILL BE ERASED.\n"); + printf("Are you sure you want to convert your ipod to FAT32? (y/n):"); + + if (fgets(yesno,4,stdin)) { + if (yesno[0]=='y') { + if (ipod_reopen_rw(&ipod) < 0) { + return IPOD_CANNOT_REOPEN; + } + + if (write_dos_partition_table(&ipod) < 0) { + fprintf(stderr,"[ERR] Partition conversion failed.\n"); + } + + if (format_partition(&ipod,1) < 0) { + fprintf(stderr,"[ERR] Format failed.\n"); + } + } else { + fprintf(stderr,"[INFO] Format cancelled.\n"); + } + } + } + } + + ipod_close(&ipod); + +#ifdef WITH_BOOTOBJS + if (action==INTERACTIVE) { + printf("Press ENTER to exit ipodpatcher :"); + fgets(yesno,4,stdin); + } +#endif + + ipod_dealloc_buffer(&ipod); + return IPOD_OK; +} diff --git a/utils/ipodpatcher/parttypes.h b/utils/ipodpatcher/parttypes.h new file mode 100644 index 0000000000..f8de303553 --- /dev/null +++ b/utils/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 } +}; -- cgit v1.2.3