diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-15 21:04:28 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-24 18:05:53 +0100 |
commit | c876d3bbefe0dc00c27ca0c12d29da5874946962 (patch) | |
tree | 69f468a185a369b01998314bc3ecc19b70f4fcaa /utils/ipodpatcher | |
parent | 6c6f0757d7a902feb293be165d1490c42bc8e7ad (diff) | |
download | rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.tar.gz rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.zip |
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
Diffstat (limited to 'utils/ipodpatcher')
-rw-r--r-- | utils/ipodpatcher/Makefile | 53 | ||||
-rw-r--r-- | utils/ipodpatcher/arc4.c | 108 | ||||
-rw-r--r-- | utils/ipodpatcher/arc4.h | 47 | ||||
-rw-r--r-- | utils/ipodpatcher/fat32format.c | 530 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodio-posix.c | 409 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodio-win32-scsi.c | 147 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodio-win32.c | 226 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodio.h | 115 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.c | 2350 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.h | 84 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.manifest | 13 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.pro | 47 | ||||
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.rc | 1 | ||||
-rw-r--r-- | utils/ipodpatcher/main.c | 622 | ||||
-rw-r--r-- | utils/ipodpatcher/parttypes.h | 109 |
15 files changed, 4861 insertions, 0 deletions
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 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | CFLAGS += -Wall -W | ||
11 | |||
12 | # Build with "make BOOTOBJS=1" to build with embedded bootloaders and the | ||
13 | # --install option and interactive mode. You need the full set of Rockbox | ||
14 | # bootloaders in this directory - download them from | ||
15 | # http://download.rockbox.org/bootloader/ipod/bootloaders.zip | ||
16 | |||
17 | # Releases of ipodpatcher are created with "make RELEASE=1". This | ||
18 | # enables BOOTOBJS and uses the VERSION string defined in main.c | ||
19 | ifdef RELEASE | ||
20 | CFLAGS += -DRELEASE | ||
21 | BOOTOBJS=1 | ||
22 | endif | ||
23 | |||
24 | ifdef BOOTOBJS | ||
25 | BOOTSRC = ipod1g2g.c ipod3g.c ipod4g.c ipodcolor.c ipodmini1g.c \ | ||
26 | ipodmini2g.c ipodnano1g.c ipodvideo.c ipodnano2g.c | ||
27 | CFLAGS += -DWITH_BOOTOBJS | ||
28 | endif | ||
29 | |||
30 | # additional frameworks to link on on OS X | ||
31 | LDOPTS_OSX = -framework CoreFoundation -framework IOKit | ||
32 | |||
33 | LIBSOURCES = ipodpatcher.c fat32format.c arc4.c \ | ||
34 | ipodio-posix.c ipodio-win32-scsi.c ipodio-win32.c | ||
35 | SOURCES = main.c $(BOOTSRC) | ||
36 | ipodpatcher: SOURCES+= ipodio-posix.c | ||
37 | |||
38 | OUTPUT = ipodpatcher | ||
39 | include ../libtools.make | ||
40 | |||
41 | ipodpatcher.exe: $(OBJDIR)ipodpatcher-rc.o | ||
42 | $(OBJDIR)ipodpatcher-rc.o: ipodpatcher.rc ipodpatcher.manifest | ||
43 | @echo WINDRES $(notdir $<) | ||
44 | $(SILENT)$(CROSS)$(WINDRES) -i $< -o $@ | ||
45 | |||
46 | %.c: bootloader-%.ipod $(BIN2C) | ||
47 | @echo BIN2C $< | ||
48 | $(SILENT)$(BIN2C) -i $< $* | ||
49 | |||
50 | %.c: bootloader-%.ipodx $(BIN2C) | ||
51 | @echo BIN2C $< | ||
52 | $(SILENT)$(BIN2C) -i $< $* | ||
53 | |||
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 @@ | |||
1 | /* | ||
2 | * arc4.c | ||
3 | * Release $Name: MATRIXSSL_1_8_3_OPEN $ | ||
4 | * | ||
5 | * ARC4 stream cipher implementation | ||
6 | */ | ||
7 | /* | ||
8 | * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. | ||
9 | * The latest version of this code is available at http://www.matrixssl.org | ||
10 | * | ||
11 | * This software is open source; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This General Public License does NOT permit incorporating this software | ||
17 | * into proprietary programs. If you are unable to comply with the GPL, a | ||
18 | * commercial license for this software may be purchased from PeerSec Networks | ||
19 | * at http://www.peersec.com | ||
20 | * | ||
21 | * This program is distributed in WITHOUT ANY WARRANTY; without even the | ||
22 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
23 | * See the GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | * http://www.gnu.org/copyleft/gpl.html | ||
29 | */ | ||
30 | /******************************************************************************/ | ||
31 | |||
32 | #include "arc4.h" | ||
33 | |||
34 | /* | ||
35 | Some accounts, such as O'Reilly's Secure Programming Cookbook say that no | ||
36 | more than 2^30 bytes should be processed without rekeying, so we | ||
37 | enforce that limit here. FYI, this is equal to 1GB of data transferred. | ||
38 | */ | ||
39 | #define ARC4_MAX_BYTES 0x40000000 | ||
40 | |||
41 | /******************************************************************************/ | ||
42 | /* | ||
43 | SSL_RSA_WITH_RC4_* cipher callbacks | ||
44 | */ | ||
45 | void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen) | ||
46 | { | ||
47 | unsigned char index1, index2, tmp, *state; | ||
48 | int16_t counter; | ||
49 | |||
50 | ctx->byteCount = 0; | ||
51 | state = &ctx->state[0]; | ||
52 | |||
53 | for (counter = 0; counter < 256; counter++) { | ||
54 | state[counter] = (unsigned char)counter; | ||
55 | } | ||
56 | ctx->x = 0; | ||
57 | ctx->y = 0; | ||
58 | index1 = 0; | ||
59 | index2 = 0; | ||
60 | |||
61 | for (counter = 0; counter < 256; counter++) { | ||
62 | index2 = (key[index1] + state[counter] + index2) & 0xff; | ||
63 | |||
64 | tmp = state[counter]; | ||
65 | state[counter] = state[index2]; | ||
66 | state[index2] = tmp; | ||
67 | |||
68 | index1 = (index1 + 1) % keylen; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, | ||
73 | unsigned char *out, int32_t len) | ||
74 | { | ||
75 | unsigned char x, y, *state, xorIndex, tmp; | ||
76 | int counter; /* NOTE BY DAVE CHAPMAN: This was a short in | ||
77 | the original code, which caused a segfault | ||
78 | when attempting to process data > 32767 | ||
79 | bytes. */ | ||
80 | |||
81 | ctx->byteCount += len; | ||
82 | if (ctx->byteCount > ARC4_MAX_BYTES) { | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | x = ctx->x; | ||
87 | y = ctx->y; | ||
88 | state = &ctx->state[0]; | ||
89 | for (counter = 0; counter < len; counter++) { | ||
90 | x = (x + 1) & 0xff; | ||
91 | y = (state[x] + y) & 0xff; | ||
92 | |||
93 | tmp = state[x]; | ||
94 | state[x] = state[y]; | ||
95 | state[y] = tmp; | ||
96 | |||
97 | xorIndex = (state[x] + state[y]) & 0xff; | ||
98 | |||
99 | tmp = in[counter]; | ||
100 | tmp ^= state[xorIndex]; | ||
101 | out[counter] = tmp; | ||
102 | } | ||
103 | ctx->x = x; | ||
104 | ctx->y = y; | ||
105 | return len; | ||
106 | } | ||
107 | |||
108 | /*****************************************************************************/ | ||
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 @@ | |||
1 | /* | ||
2 | arc4.h - based on matrixssl-1-8-3-open | ||
3 | |||
4 | */ | ||
5 | |||
6 | /* | ||
7 | * Copyright (c) PeerSec Networks, 2002-2007. All Rights Reserved. | ||
8 | * The latest version of this code is available at http://www.matrixssl.org | ||
9 | * | ||
10 | * This software is open source; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This General Public License does NOT permit incorporating this software | ||
16 | * into proprietary programs. If you are unable to comply with the GPL, a | ||
17 | * commercial license for this software may be purchased from PeerSec Networks | ||
18 | * at http://www.peersec.com | ||
19 | * | ||
20 | * This program is distributed in WITHOUT ANY WARRANTY; without even the | ||
21 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
22 | * See the GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
27 | * http://www.gnu.org/copyleft/gpl.html | ||
28 | */ | ||
29 | /*****************************************************************************/ | ||
30 | |||
31 | #ifndef _ARC4_H | ||
32 | |||
33 | #include <stdint.h> | ||
34 | |||
35 | struct rc4_key_t | ||
36 | { | ||
37 | unsigned char state[256]; | ||
38 | uint32_t byteCount; | ||
39 | unsigned char x; | ||
40 | unsigned char y; | ||
41 | }; | ||
42 | |||
43 | void matrixArc4Init(struct rc4_key_t *ctx, unsigned char *key, int32_t keylen); | ||
44 | int32_t matrixArc4(struct rc4_key_t *ctx, unsigned char *in, | ||
45 | unsigned char *out, int32_t len); | ||
46 | |||
47 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * | ||
11 | * FAT32 formatting functions. Based on: | ||
12 | * | ||
13 | * Fat32 formatter version 1.03 | ||
14 | * (c) Tom Thornhill 2005 | ||
15 | * This software is covered by the GPL. | ||
16 | * By using this tool, you agree to absolve Ridgecrop of an liabilities for | ||
17 | * lost data. | ||
18 | * Please backup any data you value before using this tool. | ||
19 | * | ||
20 | * | ||
21 | * Modified June 2007 by Dave Chapman for use in ipodpatcher | ||
22 | * | ||
23 | * | ||
24 | * This program is free software; you can redistribute it and/or | ||
25 | * modify it under the terms of the GNU General Public License | ||
26 | * as published by the Free Software Foundation; either version 2 | ||
27 | * of the License, or (at your option) any later version. | ||
28 | * | ||
29 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
30 | * KIND, either express or implied. | ||
31 | * | ||
32 | ****************************************************************************/ | ||
33 | |||
34 | #include <stdio.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <string.h> | ||
37 | #include <stdbool.h> | ||
38 | #include <stdint.h> | ||
39 | #include <inttypes.h> | ||
40 | |||
41 | #include "ipodio.h" | ||
42 | |||
43 | static inline uint16_t swap16(uint16_t value) | ||
44 | { | ||
45 | return (value >> 8) | (value << 8); | ||
46 | } | ||
47 | |||
48 | static inline uint32_t swap32(uint32_t value) | ||
49 | { | ||
50 | uint32_t hi = swap16(value >> 16); | ||
51 | uint32_t lo = swap16(value & 0xffff); | ||
52 | return (lo << 16) | hi; | ||
53 | } | ||
54 | |||
55 | /* The following functions are not the most efficient, but are | ||
56 | self-contained and don't require needing to know endianness of CPU | ||
57 | at compile-time. | ||
58 | |||
59 | Note that htole16/htole32 exist on some platforms, so for | ||
60 | simplicity we use different names. | ||
61 | |||
62 | */ | ||
63 | |||
64 | static uint16_t rb_htole16(uint16_t x) | ||
65 | { | ||
66 | uint16_t test = 0x1234; | ||
67 | unsigned char* p = (unsigned char*)&test; | ||
68 | |||
69 | if (p[0]==0x12) { | ||
70 | /* Big-endian */ | ||
71 | return swap16(x); | ||
72 | } else { | ||
73 | return x; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | static uint32_t rb_htole32(uint32_t x) | ||
78 | { | ||
79 | uint32_t test = 0x12345678; | ||
80 | unsigned char* p = (unsigned char*)&test; | ||
81 | |||
82 | if (p[0]==0x12) { | ||
83 | /* Big-endian */ | ||
84 | return swap32(x); | ||
85 | } else { | ||
86 | return x; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | |||
91 | /* TODO: Pass these as parameters to the various create_ functions */ | ||
92 | |||
93 | /* can be zero for default or 1,2,4,8,16,32 or 64 */ | ||
94 | static int sectors_per_cluster = 0; | ||
95 | |||
96 | /* Recommended values */ | ||
97 | static uint32_t ReservedSectCount = 32; | ||
98 | static uint32_t NumFATs = 2; | ||
99 | static uint32_t BackupBootSect = 6; | ||
100 | static uint32_t VolumeId=0; /* calculated before format */ | ||
101 | |||
102 | /* Calculated later */ | ||
103 | static uint32_t FatSize=0; | ||
104 | static uint32_t BytesPerSect=0; | ||
105 | static uint32_t SectorsPerCluster=0; | ||
106 | static uint32_t TotalSectors=0; | ||
107 | static uint32_t SystemAreaSize=0; | ||
108 | static uint32_t UserAreaSize=0; | ||
109 | static uint8_t VolId[12] = "NO NAME "; | ||
110 | |||
111 | |||
112 | struct FAT_BOOTSECTOR32 | ||
113 | { | ||
114 | /* Common fields. */ | ||
115 | uint8_t sJmpBoot[3]; | ||
116 | char sOEMName[8]; | ||
117 | uint16_t wBytsPerSec; | ||
118 | uint8_t bSecPerClus; | ||
119 | uint16_t wRsvdSecCnt; | ||
120 | uint8_t bNumFATs; | ||
121 | uint16_t wRootEntCnt; | ||
122 | uint16_t wTotSec16; /* if zero, use dTotSec32 instead */ | ||
123 | uint8_t bMedia; | ||
124 | uint16_t wFATSz16; | ||
125 | uint16_t wSecPerTrk; | ||
126 | uint16_t wNumHeads; | ||
127 | uint32_t dHiddSec; | ||
128 | uint32_t dTotSec32; | ||
129 | |||
130 | /* Fat 32/16 only */ | ||
131 | uint32_t dFATSz32; | ||
132 | uint16_t wExtFlags; | ||
133 | uint16_t wFSVer; | ||
134 | uint32_t dRootClus; | ||
135 | uint16_t wFSInfo; | ||
136 | uint16_t wBkBootSec; | ||
137 | uint8_t Reserved[12]; | ||
138 | uint8_t bDrvNum; | ||
139 | uint8_t Reserved1; | ||
140 | uint8_t bBootSig; /* == 0x29 if next three fields are ok */ | ||
141 | uint32_t dBS_VolID; | ||
142 | uint8_t sVolLab[11]; | ||
143 | uint8_t sBS_FilSysType[8]; | ||
144 | } __attribute__((packed)); | ||
145 | |||
146 | struct FAT_FSINFO { | ||
147 | uint32_t dLeadSig; // 0x41615252 | ||
148 | uint8_t sReserved1[480]; // zeros | ||
149 | uint32_t dStrucSig; // 0x61417272 | ||
150 | uint32_t dFree_Count; // 0xFFFFFFFF | ||
151 | uint32_t dNxt_Free; // 0xFFFFFFFF | ||
152 | uint8_t sReserved2[12]; // zeros | ||
153 | uint32_t dTrailSig; // 0xAA550000 | ||
154 | } __attribute__((packed)); | ||
155 | |||
156 | |||
157 | /* Write "count" zero sectors, starting at sector "sector" */ | ||
158 | static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count) | ||
159 | { | ||
160 | int n; | ||
161 | |||
162 | if (ipod_seek(ipod, sector * ipod->sector_size) < 0) { | ||
163 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
164 | return -1; | ||
165 | } | ||
166 | |||
167 | memset(ipod->sectorbuf, 0, 128 * ipod->sector_size); | ||
168 | |||
169 | /* Write 128 sectors at a time */ | ||
170 | while (count) { | ||
171 | if (count >= 128) | ||
172 | n = 128; | ||
173 | else | ||
174 | n = count; | ||
175 | |||
176 | if (ipod_write(ipod,n * ipod->sector_size) < 0) { | ||
177 | perror("[ERR] Write failed in zero_sectors\n"); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | count -= n; | ||
182 | } | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | |||
188 | /* | ||
189 | 28.2 CALCULATING THE VOLUME SERIAL NUMBER | ||
190 | |||
191 | For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94 | ||
192 | seconds. DOS takes the date and time just before it writes it to the | ||
193 | disk. | ||
194 | |||
195 | Low order word is calculated: Volume Serial Number is: | ||
196 | Month & Day 12/26 0c1ah | ||
197 | Sec & Hundrenths 41:94 295eh 3578:1d02 | ||
198 | ----- | ||
199 | 3578h | ||
200 | |||
201 | High order word is calculated: | ||
202 | Hours & Minutes 21:55 1537h | ||
203 | Year 1995 07cbh | ||
204 | ----- | ||
205 | 1d02h | ||
206 | */ | ||
207 | static uint32_t get_volume_id ( ) | ||
208 | { | ||
209 | /* TODO */ | ||
210 | #if 0 | ||
211 | SYSTEMTIME s; | ||
212 | uint32_t d; | ||
213 | uint16_t lo,hi,tmp; | ||
214 | |||
215 | GetLocalTime( &s ); | ||
216 | |||
217 | lo = s.wDay + ( s.wMonth << 8 ); | ||
218 | tmp = (s.wMilliseconds/10) + (s.wSecond << 8 ); | ||
219 | lo += tmp; | ||
220 | |||
221 | hi = s.wMinute + ( s.wHour << 8 ); | ||
222 | hi += s.wYear; | ||
223 | |||
224 | d = lo + (hi << 16); | ||
225 | return(d); | ||
226 | #endif | ||
227 | return(0); | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | This is the Microsoft calculation from FATGEN | ||
232 | |||
233 | uint32_t RootDirSectors = 0; | ||
234 | uint32_t TmpVal1, TmpVal2, FATSz; | ||
235 | |||
236 | TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors); | ||
237 | TmpVal2 = (256 * SecPerClus) + NumFATs; | ||
238 | TmpVal2 = TmpVal2 / 2; | ||
239 | FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2; | ||
240 | |||
241 | return( FatSz ); | ||
242 | */ | ||
243 | |||
244 | |||
245 | static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt, | ||
246 | uint32_t SecPerClus, uint32_t NumFATs, | ||
247 | uint32_t BytesPerSect) | ||
248 | { | ||
249 | uint64_t Numerator, Denominator; | ||
250 | uint64_t FatElementSize = 4; | ||
251 | uint64_t FatSz; | ||
252 | |||
253 | /* This is based on | ||
254 | http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html | ||
255 | I've made the obvious changes for FAT32 | ||
256 | */ | ||
257 | |||
258 | Numerator = FatElementSize * ( DskSize - ReservedSecCnt ); | ||
259 | Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs ); | ||
260 | FatSz = Numerator / Denominator; | ||
261 | |||
262 | /* round up */ | ||
263 | FatSz += 1; | ||
264 | |||
265 | return((uint32_t)FatSz); | ||
266 | } | ||
267 | |||
268 | static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect) | ||
269 | { | ||
270 | uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect; | ||
271 | return( (uint8_t) spc ); | ||
272 | } | ||
273 | |||
274 | static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors, | ||
275 | uint32_t BytesPerSect) | ||
276 | { | ||
277 | uint8_t ret = 0x01; /* 1 sector per cluster */ | ||
278 | uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect; | ||
279 | int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 ); | ||
280 | |||
281 | /* 512 MB to 8,191 MB 4 KB */ | ||
282 | if ( DiskSizeMB > 512 ) | ||
283 | ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */ | ||
284 | |||
285 | /* 8,192 MB to 16,383 MB 8 KB */ | ||
286 | if ( DiskSizeMB > 8192 ) | ||
287 | ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */ | ||
288 | |||
289 | /* 16,384 MB to 32,767 MB 16 KB */ | ||
290 | if ( DiskSizeMB > 16384 ) | ||
291 | ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */ | ||
292 | |||
293 | /* Larger than 32,768 MB 32 KB */ | ||
294 | if ( DiskSizeMB > 32768 ) | ||
295 | ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */ | ||
296 | |||
297 | return( ret ); | ||
298 | |||
299 | } | ||
300 | |||
301 | static void create_boot_sector(unsigned char* buf, | ||
302 | struct ipod_t* ipod, int partition) | ||
303 | { | ||
304 | struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf; | ||
305 | |||
306 | /* fill out the boot sector and fs info */ | ||
307 | pFAT32BootSect->sJmpBoot[0]=0xEB; | ||
308 | pFAT32BootSect->sJmpBoot[1]=0x5A; | ||
309 | pFAT32BootSect->sJmpBoot[2]=0x90; | ||
310 | memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8 ); | ||
311 | pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect); | ||
312 | pFAT32BootSect->bSecPerClus = SectorsPerCluster ; | ||
313 | pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount); | ||
314 | pFAT32BootSect->bNumFATs = NumFATs; | ||
315 | pFAT32BootSect->wRootEntCnt = rb_htole16(0); | ||
316 | pFAT32BootSect->wTotSec16 = rb_htole16(0); | ||
317 | pFAT32BootSect->bMedia = 0xF8; | ||
318 | pFAT32BootSect->wFATSz16 = rb_htole16(0); | ||
319 | pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track); | ||
320 | pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads); | ||
321 | pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start); | ||
322 | pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors); | ||
323 | pFAT32BootSect->dFATSz32 = rb_htole32(FatSize); | ||
324 | pFAT32BootSect->wExtFlags = rb_htole16(0); | ||
325 | pFAT32BootSect->wFSVer = rb_htole16(0); | ||
326 | pFAT32BootSect->dRootClus = rb_htole32(2); | ||
327 | pFAT32BootSect->wFSInfo = rb_htole16(1); | ||
328 | pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect); | ||
329 | pFAT32BootSect->bDrvNum = 0x80; | ||
330 | pFAT32BootSect->Reserved1 = 0; | ||
331 | pFAT32BootSect->bBootSig = 0x29; | ||
332 | pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId); | ||
333 | memcpy(pFAT32BootSect->sVolLab, VolId, 11); | ||
334 | memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 ); | ||
335 | |||
336 | buf[510] = 0x55; | ||
337 | buf[511] = 0xaa; | ||
338 | } | ||
339 | |||
340 | static void create_fsinfo(unsigned char* buf) | ||
341 | { | ||
342 | struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf; | ||
343 | |||
344 | /* FSInfo sect */ | ||
345 | pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252); | ||
346 | pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272); | ||
347 | pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1); | ||
348 | pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1); | ||
349 | pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000); | ||
350 | pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1); | ||
351 | |||
352 | /* clusters 0-1 reserved, we used cluster 2 for the root dir */ | ||
353 | pFAT32FsInfo->dNxt_Free = rb_htole32(3); | ||
354 | } | ||
355 | |||
356 | static void create_firstfatsector(unsigned char* buf) | ||
357 | { | ||
358 | uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */ | ||
359 | |||
360 | /* First FAT Sector */ | ||
361 | p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */ | ||
362 | p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */ | ||
363 | p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */ | ||
364 | } | ||
365 | |||
366 | int format_partition(struct ipod_t* ipod, int partition) | ||
367 | { | ||
368 | uint32_t i; | ||
369 | uint64_t qTotalSectors=0; | ||
370 | uint64_t FatNeeded; | ||
371 | |||
372 | VolumeId = get_volume_id( ); | ||
373 | |||
374 | /* Only support hard disks at the moment */ | ||
375 | if ( ipod->sector_size != 512 ) | ||
376 | { | ||
377 | fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n"); | ||
378 | return -1; | ||
379 | } | ||
380 | BytesPerSect = ipod->sector_size; | ||
381 | |||
382 | /* Checks on Disk Size */ | ||
383 | qTotalSectors = ipod->pinfo[partition].size; | ||
384 | |||
385 | /* low end limit - 65536 sectors */ | ||
386 | if ( qTotalSectors < 65536 ) | ||
387 | { | ||
388 | /* I suspect that most FAT32 implementations would mount this | ||
389 | volume just fine, but the spec says that we shouldn't do | ||
390 | this, so we won't */ | ||
391 | |||
392 | fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" ); | ||
393 | return -1; | ||
394 | } | ||
395 | |||
396 | if ( qTotalSectors >= 0xffffffff ) | ||
397 | { | ||
398 | /* This is a more fundamental limitation on FAT32 - the total | ||
399 | sector count in the root dir is 32bit. With a bit of | ||
400 | creativity, FAT32 could be extended to handle at least 2^28 | ||
401 | clusters There would need to be an extra field in the | ||
402 | FSInfo sector, and the old sector count could be set to | ||
403 | 0xffffffff. This is non standard though, the Windows FAT | ||
404 | driver FASTFAT.SYS won't understand this. Perhaps a future | ||
405 | version of FAT32 and FASTFAT will handle this. */ | ||
406 | |||
407 | fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n"); | ||
408 | } | ||
409 | |||
410 | if ( sectors_per_cluster ) { | ||
411 | SectorsPerCluster = sectors_per_cluster; | ||
412 | } else { | ||
413 | SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size, | ||
414 | BytesPerSect ); | ||
415 | } | ||
416 | |||
417 | TotalSectors = (uint32_t) qTotalSectors; | ||
418 | |||
419 | FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount, | ||
420 | SectorsPerCluster, NumFATs, BytesPerSect ); | ||
421 | |||
422 | UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize); | ||
423 | |||
424 | /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */ | ||
425 | SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster); | ||
426 | |||
427 | /* Work out the Cluster count */ | ||
428 | FatNeeded = UserAreaSize/SectorsPerCluster; | ||
429 | |||
430 | /* check for a cluster count of >2^28, since the upper 4 bits of | ||
431 | the cluster values in the FAT are reserved. */ | ||
432 | if (FatNeeded > 0x0FFFFFFF) { | ||
433 | fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" ); | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | /* Sanity check, make sure the fat is big enough. | ||
438 | Convert the cluster count into a Fat sector count, and check | ||
439 | the fat size value we calculated earlier is OK. */ | ||
440 | |||
441 | FatNeeded *=4; | ||
442 | FatNeeded += (BytesPerSect-1); | ||
443 | FatNeeded /= BytesPerSect; | ||
444 | |||
445 | if ( FatNeeded > FatSize ) { | ||
446 | fprintf(stderr,"[ERR] Drive too big to format\n"); | ||
447 | return -1; | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | Write boot sector, fats | ||
452 | Sector 0 Boot Sector | ||
453 | Sector 1 FSInfo | ||
454 | Sector 2 More boot code - we write zeros here | ||
455 | Sector 3 unused | ||
456 | Sector 4 unused | ||
457 | Sector 5 unused | ||
458 | Sector 6 Backup boot sector | ||
459 | Sector 7 Backup FSInfo sector | ||
460 | Sector 8 Backup 'more boot code' | ||
461 | zero'd sectors upto ReservedSectCount | ||
462 | FAT1 ReservedSectCount to ReservedSectCount + FatSize | ||
463 | ... | ||
464 | FATn ReservedSectCount to ReservedSectCount + FatSize | ||
465 | RootDir - allocated to cluster2 | ||
466 | */ | ||
467 | |||
468 | fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track); | ||
469 | fprintf(stderr,"[INFO] Size : %" PRIu64 "GB %u sectors\n", | ||
470 | ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors ); | ||
471 | fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect ); | ||
472 | fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff ); | ||
473 | fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs ); | ||
474 | fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster ); | ||
475 | |||
476 | fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition); | ||
477 | |||
478 | /* Once zero_sectors has run, any data on the drive is basically lost... */ | ||
479 | fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize ); | ||
480 | |||
481 | zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize); | ||
482 | |||
483 | fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" ); | ||
484 | |||
485 | /* Create the boot sector structure */ | ||
486 | create_boot_sector(ipod->sectorbuf, ipod, partition); | ||
487 | create_fsinfo(ipod->sectorbuf + 512); | ||
488 | |||
489 | /* Write boot sector and fsinfo at start of partition */ | ||
490 | if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) { | ||
491 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
492 | return -1; | ||
493 | } | ||
494 | if (ipod_write(ipod,512 * 2) < 0) { | ||
495 | perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); | ||
496 | return -1; | ||
497 | } | ||
498 | |||
499 | /* Write backup copy of boot sector and fsinfo */ | ||
500 | if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) { | ||
501 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
502 | return -1; | ||
503 | } | ||
504 | if (ipod_write(ipod,512 * 2) < 0) { | ||
505 | perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); | ||
506 | return -1; | ||
507 | } | ||
508 | |||
509 | /* Create the first FAT sector */ | ||
510 | create_firstfatsector(ipod->sectorbuf); | ||
511 | |||
512 | /* Write the first fat sector in the right places */ | ||
513 | for ( i=0; i<NumFATs; i++ ) { | ||
514 | int SectorStart = ReservedSectCount + (i * FatSize ); | ||
515 | |||
516 | if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) { | ||
517 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
518 | return -1; | ||
519 | } | ||
520 | |||
521 | if (ipod_write(ipod,512) < 0) { | ||
522 | perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); | ||
523 | return -1; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | fprintf(stderr,"[INFO] Format successful\n"); | ||
528 | |||
529 | return 0; | ||
530 | } | ||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #if !defined(_WIN32) /* all non-Windows platforms are considered POSIX. */ | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <unistd.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <string.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <sys/types.h> | ||
30 | #include <sys/stat.h> | ||
31 | #include <sys/ioctl.h> | ||
32 | #include <errno.h> | ||
33 | |||
34 | #include "ipodio.h" | ||
35 | |||
36 | #if defined(linux) || defined (__linux) | ||
37 | #include <sys/mount.h> | ||
38 | #include <linux/hdreg.h> | ||
39 | #include <scsi/scsi_ioctl.h> | ||
40 | #include <scsi/sg.h> | ||
41 | |||
42 | #define IPOD_SECTORSIZE_IOCTL BLKSSZGET | ||
43 | |||
44 | static void get_geometry(struct ipod_t* ipod) | ||
45 | { | ||
46 | struct hd_geometry geometry; | ||
47 | |||
48 | if (!ioctl(ipod->dh, HDIO_GETGEO, &geometry)) { | ||
49 | /* never use geometry.cylinders - it is truncated */ | ||
50 | ipod->num_heads = geometry.heads; | ||
51 | ipod->sectors_per_track = geometry.sectors; | ||
52 | } else { | ||
53 | ipod->num_heads = 0; | ||
54 | ipod->sectors_per_track = 0; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /* Linux SCSI Inquiry code based on the documentation and example code from | ||
59 | http://www.ibm.com/developerworks/linux/library/l-scsi-api/index.html | ||
60 | */ | ||
61 | |||
62 | int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, | ||
63 | unsigned char* buf, int bufsize) | ||
64 | { | ||
65 | unsigned char cdb[6]; | ||
66 | struct sg_io_hdr hdr; | ||
67 | unsigned char sense_buffer[255]; | ||
68 | |||
69 | memset(&hdr, 0, sizeof(hdr)); | ||
70 | |||
71 | hdr.interface_id = 'S'; /* this is the only choice we have! */ | ||
72 | hdr.flags = SG_FLAG_LUN_INHIBIT; /* this would put the LUN to 2nd byte of cdb*/ | ||
73 | |||
74 | /* Set xfer data */ | ||
75 | hdr.dxferp = buf; | ||
76 | hdr.dxfer_len = bufsize; | ||
77 | |||
78 | /* Set sense data */ | ||
79 | hdr.sbp = sense_buffer; | ||
80 | hdr.mx_sb_len = sizeof(sense_buffer); | ||
81 | |||
82 | /* Set the cdb format */ | ||
83 | cdb[0] = 0x12; | ||
84 | cdb[1] = 1; /* Enable Vital Product Data (EVPD) */ | ||
85 | cdb[2] = page_code & 0xff; | ||
86 | cdb[3] = 0; | ||
87 | cdb[4] = 0xff; | ||
88 | cdb[5] = 0; /* For control filed, just use 0 */ | ||
89 | |||
90 | hdr.dxfer_direction = SG_DXFER_FROM_DEV; | ||
91 | hdr.cmdp = cdb; | ||
92 | hdr.cmd_len = 6; | ||
93 | |||
94 | int ret = ioctl(ipod->dh, SG_IO, &hdr); | ||
95 | |||
96 | if (ret < 0) { | ||
97 | return -1; | ||
98 | } else { | ||
99 | return 0; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
104 | || defined(__bsdi__) || defined(__DragonFly__) | ||
105 | #include <sys/disk.h> | ||
106 | #define IPOD_SECTORSIZE_IOCTL DIOCGSECTORSIZE | ||
107 | |||
108 | /* TODO: Implement this function for BSD */ | ||
109 | static void get_geometry(struct ipod_t* ipod) | ||
110 | { | ||
111 | /* Are these universal for all ipods? */ | ||
112 | ipod->num_heads = 255; | ||
113 | ipod->sectors_per_track = 63; | ||
114 | } | ||
115 | |||
116 | int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, | ||
117 | unsigned char* buf, int bufsize) | ||
118 | { | ||
119 | /* TODO: Implement for BSD */ | ||
120 | (void)ipod; | ||
121 | (void)page_code; | ||
122 | (void)buf; | ||
123 | (void)bufsize; | ||
124 | return -1; | ||
125 | } | ||
126 | |||
127 | #elif defined(__APPLE__) && defined(__MACH__) | ||
128 | /* OS X IOKit includes don't like VERSION being defined! */ | ||
129 | #undef VERSION | ||
130 | #include <sys/disk.h> | ||
131 | #include <CoreFoundation/CoreFoundation.h> | ||
132 | #include <IOKit/IOKitLib.h> | ||
133 | #include <IOKit/scsi/SCSITaskLib.h> | ||
134 | #include <IOKit/scsi/SCSICommandOperationCodes.h> | ||
135 | #define IPOD_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE | ||
136 | |||
137 | /* TODO: Implement this function for Mac OS X */ | ||
138 | static void get_geometry(struct ipod_t* ipod) | ||
139 | { | ||
140 | /* Are these universal for all ipods? */ | ||
141 | ipod->num_heads = 255; | ||
142 | ipod->sectors_per_track = 63; | ||
143 | } | ||
144 | |||
145 | int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, | ||
146 | unsigned char* buf, int bufsize) | ||
147 | { | ||
148 | /* OS X doesn't allow to simply send out a SCSI inquiry request but | ||
149 | * requires registering an interface handler first. | ||
150 | * Currently this is done on each inquiry request which is somewhat | ||
151 | * inefficient but the current ipodpatcher API doesn't really fit here. | ||
152 | * Based on the documentation in Apple's document | ||
153 | * "SCSI Architecture Model Device Interface Guide". | ||
154 | * | ||
155 | * WARNING: this code currently doesn't take the selected device into | ||
156 | * account. It simply looks for an Ipod on the system and uses | ||
157 | * the first match. | ||
158 | */ | ||
159 | (void)ipod; | ||
160 | int result = 0; | ||
161 | /* first, create a dictionary to match the device. This is needed to get the | ||
162 | * service. */ | ||
163 | CFMutableDictionaryRef match_dict; | ||
164 | match_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); | ||
165 | if(match_dict == NULL) | ||
166 | return -1; | ||
167 | |||
168 | /* set value to match. In case of the Ipod this is "iPodUserClientDevice". */ | ||
169 | CFMutableDictionaryRef sub_dict; | ||
170 | sub_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); | ||
171 | if(sub_dict == NULL) | ||
172 | return -1; | ||
173 | CFDictionarySetValue(sub_dict, CFSTR(kIOPropertySCSITaskDeviceCategory), | ||
174 | CFSTR("iPodUserClientDevice")); | ||
175 | CFDictionarySetValue(match_dict, CFSTR(kIOPropertyMatchKey), sub_dict); | ||
176 | |||
177 | /* get an iterator for searching for the service. */ | ||
178 | kern_return_t kr; | ||
179 | io_iterator_t iterator = IO_OBJECT_NULL; | ||
180 | /* get matching services from IO registry. Consumes one reference to | ||
181 | * the dictionary, so no need to release that. */ | ||
182 | kr = IOServiceGetMatchingServices(kIOMasterPortDefault, match_dict, &iterator); | ||
183 | |||
184 | if(!iterator | (kr != kIOReturnSuccess)) | ||
185 | return -1; | ||
186 | |||
187 | /* get interface and obtain exclusive access */ | ||
188 | SInt32 score; | ||
189 | HRESULT herr; | ||
190 | kern_return_t err; | ||
191 | IOCFPlugInInterface **plugin_interface = NULL; | ||
192 | SCSITaskDeviceInterface **interface = NULL; | ||
193 | io_service_t device = IO_OBJECT_NULL; | ||
194 | device = IOIteratorNext(iterator); | ||
195 | |||
196 | err = IOCreatePlugInInterfaceForService(device, kIOSCSITaskDeviceUserClientTypeID, | ||
197 | kIOCFPlugInInterfaceID, &plugin_interface, | ||
198 | &score); | ||
199 | |||
200 | if(err != noErr) { | ||
201 | return -1; | ||
202 | } | ||
203 | /* query the plugin interface for task interface */ | ||
204 | herr = (*plugin_interface)->QueryInterface(plugin_interface, | ||
205 | CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID), (LPVOID*)&interface); | ||
206 | if(herr != S_OK) { | ||
207 | IODestroyPlugInInterface(plugin_interface); | ||
208 | return -1; | ||
209 | } | ||
210 | |||
211 | err = (*interface)->ObtainExclusiveAccess(interface); | ||
212 | if(err != noErr) { | ||
213 | (*interface)->Release(interface); | ||
214 | IODestroyPlugInInterface(plugin_interface); | ||
215 | return -1; | ||
216 | } | ||
217 | |||
218 | /* do the inquiry */ | ||
219 | SCSITaskInterface **task = NULL; | ||
220 | |||
221 | task = (*interface)->CreateSCSITask(interface); | ||
222 | if(task != NULL) { | ||
223 | kern_return_t err; | ||
224 | SCSITaskStatus task_status; | ||
225 | IOVirtualRange* range; | ||
226 | SCSI_Sense_Data sense_data; | ||
227 | SCSICommandDescriptorBlock cdb; | ||
228 | UInt64 transfer_count = 0; | ||
229 | memset(buf, 0, bufsize); | ||
230 | /* allocate virtual range for buffer. */ | ||
231 | range = (IOVirtualRange*) malloc(sizeof(IOVirtualRange)); | ||
232 | memset(&sense_data, 0, sizeof(sense_data)); | ||
233 | memset(cdb, 0, sizeof(cdb)); | ||
234 | /* set up range. address is buffer address, length is request size. */ | ||
235 | range->address = (IOVirtualAddress)buf; | ||
236 | range->length = bufsize; | ||
237 | /* setup CDB */ | ||
238 | cdb[0] = 0x12; /* inquiry */ | ||
239 | cdb[1] = 1; | ||
240 | cdb[2] = page_code; | ||
241 | cdb[4] = bufsize; | ||
242 | |||
243 | /* set cdb in task */ | ||
244 | err = (*task)->SetCommandDescriptorBlock(task, cdb, kSCSICDBSize_6Byte); | ||
245 | if(err != kIOReturnSuccess) { | ||
246 | result = -1; | ||
247 | goto cleanup; | ||
248 | } | ||
249 | err = (*task)->SetScatterGatherEntries(task, range, 1, bufsize, | ||
250 | kSCSIDataTransfer_FromTargetToInitiator); | ||
251 | if(err != kIOReturnSuccess) { | ||
252 | result = -1; | ||
253 | goto cleanup; | ||
254 | } | ||
255 | /* set timeout */ | ||
256 | err = (*task)->SetTimeoutDuration(task, 10000); | ||
257 | if(err != kIOReturnSuccess) { | ||
258 | result = -1; | ||
259 | goto cleanup; | ||
260 | } | ||
261 | |||
262 | /* request data */ | ||
263 | err = (*task)->ExecuteTaskSync(task, &sense_data, &task_status, &transfer_count); | ||
264 | if(err != kIOReturnSuccess) { | ||
265 | result = -1; | ||
266 | goto cleanup; | ||
267 | } | ||
268 | /* cleanup */ | ||
269 | free(range); | ||
270 | |||
271 | /* release task interface */ | ||
272 | (*task)->Release(task); | ||
273 | } | ||
274 | else { | ||
275 | result = -1; | ||
276 | } | ||
277 | cleanup: | ||
278 | /* cleanup interface */ | ||
279 | (*interface)->ReleaseExclusiveAccess(interface); | ||
280 | (*interface)->Release(interface); | ||
281 | IODestroyPlugInInterface(plugin_interface); | ||
282 | |||
283 | return result; | ||
284 | } | ||
285 | |||
286 | #else | ||
287 | #error No sector-size detection implemented for this platform | ||
288 | #endif | ||
289 | |||
290 | #if defined(__APPLE__) && defined(__MACH__) | ||
291 | static int ipod_unmount(struct ipod_t* ipod) | ||
292 | { | ||
293 | char cmd[4096]; | ||
294 | int res; | ||
295 | |||
296 | sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss2\"",ipod->diskname); | ||
297 | fprintf(stderr,"[INFO] "); | ||
298 | res = system(cmd); | ||
299 | |||
300 | if (res==0) { | ||
301 | return 0; | ||
302 | } else { | ||
303 | perror("Unmount failed"); | ||
304 | return -1; | ||
305 | } | ||
306 | } | ||
307 | #endif | ||
308 | |||
309 | void ipod_print_error(char* msg) | ||
310 | { | ||
311 | perror(msg); | ||
312 | } | ||
313 | |||
314 | int ipod_open(struct ipod_t* ipod, int silent) | ||
315 | { | ||
316 | ipod->dh=open(ipod->diskname,O_RDONLY); | ||
317 | if (ipod->dh < 0) { | ||
318 | if (!silent) perror(ipod->diskname); | ||
319 | if(errno == EACCES) return -2; | ||
320 | else return -1; | ||
321 | } | ||
322 | |||
323 | /* Read information about the disk */ | ||
324 | |||
325 | if(ioctl(ipod->dh,IPOD_SECTORSIZE_IOCTL,&ipod->sector_size) < 0) { | ||
326 | ipod->sector_size=512; | ||
327 | if (!silent) { | ||
328 | fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n" | ||
329 | ,ipod->sector_size); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | get_geometry(ipod); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | |||
339 | int ipod_reopen_rw(struct ipod_t* ipod) | ||
340 | { | ||
341 | #if defined(__APPLE__) && defined(__MACH__) | ||
342 | if (ipod_unmount(ipod) < 0) | ||
343 | return -1; | ||
344 | #endif | ||
345 | |||
346 | close(ipod->dh); | ||
347 | ipod->dh=open(ipod->diskname,O_RDWR); | ||
348 | if (ipod->dh < 0) { | ||
349 | perror(ipod->diskname); | ||
350 | return -1; | ||
351 | } | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | int ipod_close(struct ipod_t* ipod) | ||
356 | { | ||
357 | close(ipod->dh); | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize) | ||
362 | { | ||
363 | ipod->sectorbuf = malloc(bufsize); | ||
364 | if (ipod->sectorbuf== NULL) { | ||
365 | return -1; | ||
366 | } | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | int ipod_dealloc_buffer(struct ipod_t* ipod) | ||
371 | { | ||
372 | if (ipod->sectorbuf == NULL) { | ||
373 | return -1; | ||
374 | } | ||
375 | free(ipod->sectorbuf); | ||
376 | ipod->sectorbuf = NULL; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | int ipod_seek(struct ipod_t* ipod, unsigned long pos) | ||
381 | { | ||
382 | off_t res; | ||
383 | |||
384 | res = lseek(ipod->dh, pos, SEEK_SET); | ||
385 | |||
386 | if (res == -1) { | ||
387 | return -1; | ||
388 | } | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | ssize_t ipod_read(struct ipod_t* ipod, int nbytes) | ||
393 | { | ||
394 | if(ipod->sectorbuf == NULL) { | ||
395 | return -1; | ||
396 | } | ||
397 | return read(ipod->dh, ipod->sectorbuf, nbytes); | ||
398 | } | ||
399 | |||
400 | ssize_t ipod_write(struct ipod_t* ipod, int nbytes) | ||
401 | { | ||
402 | if(ipod->sectorbuf == NULL) { | ||
403 | return -1; | ||
404 | } | ||
405 | return write(ipod->dh, ipod->sectorbuf, nbytes); | ||
406 | } | ||
407 | |||
408 | #endif | ||
409 | |||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | * | ||
21 | * Based on the getCapsUsingSCSIPassThrough() function from "cddrv.cpp": | ||
22 | * - http://www.farmanager.com/svn/trunk/unicode_far/cddrv.cpp | ||
23 | * | ||
24 | * Copyright (c) 1996 Eugene Roshal | ||
25 | * Copyright (c) 2000 Far Group | ||
26 | * All rights reserved. | ||
27 | * | ||
28 | * Redistribution and use in source and binary forms, with or without | ||
29 | * modification, are permitted provided that the following conditions | ||
30 | * are met: | ||
31 | * 1. Redistributions of source code must retain the above copyright | ||
32 | * notice, this list of conditions and the following disclaimer. | ||
33 | * 2. Redistributions in binary form must reproduce the above copyright | ||
34 | * notice, this list of conditions and the following disclaimer in the | ||
35 | * documentation and/or other materials provided with the distribution. | ||
36 | * 3. The name of the authors may not be used to endorse or promote products | ||
37 | * derived from this software without specific prior written permission. | ||
38 | * | ||
39 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
40 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
41 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
42 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
43 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
44 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
45 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
46 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
47 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
48 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
49 | * | ||
50 | ****************************************************************************/ | ||
51 | |||
52 | #if defined(_WIN32) | ||
53 | #include <windows.h> | ||
54 | #include <stddef.h> | ||
55 | #include <stdio.h> | ||
56 | |||
57 | #include "ipodio.h" | ||
58 | |||
59 | /* from ddk/ntddscsi.h */ | ||
60 | #define SCSI_IOCTL_DATA_OUT 0 | ||
61 | #define SCSI_IOCTL_DATA_IN 1 | ||
62 | #define SCSI_IOCTL_DATA_UNSPECIFIED 2 | ||
63 | |||
64 | #define IOCTL_SCSI_PASS_THROUGH \ | ||
65 | CTL_CODE(FILE_DEVICE_CONTROLLER, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) | ||
66 | |||
67 | typedef struct _SCSI_PASS_THROUGH { | ||
68 | USHORT Length; | ||
69 | UCHAR ScsiStatus; | ||
70 | UCHAR PathId; | ||
71 | UCHAR TargetId; | ||
72 | UCHAR Lun; | ||
73 | UCHAR CdbLength; | ||
74 | UCHAR SenseInfoLength; | ||
75 | UCHAR DataIn; | ||
76 | ULONG DataTransferLength; | ||
77 | ULONG TimeOutValue; | ||
78 | ULONG_PTR DataBufferOffset; | ||
79 | ULONG SenseInfoOffset; | ||
80 | UCHAR Cdb[16]; | ||
81 | } SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; | ||
82 | |||
83 | typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS { | ||
84 | SCSI_PASS_THROUGH Spt; | ||
85 | ULONG Filler; /* realign buffers to double word boundary */ | ||
86 | UCHAR SenseBuf[32]; | ||
87 | UCHAR DataBuf[512]; | ||
88 | } SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS; | ||
89 | |||
90 | int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, | ||
91 | unsigned char* buf, int bufsize) | ||
92 | { | ||
93 | SCSI_PASS_THROUGH_WITH_BUFFERS sptwb; | ||
94 | ULONG length; | ||
95 | DWORD returned; | ||
96 | BOOL status; | ||
97 | |||
98 | if (bufsize > 255) { | ||
99 | fprintf(stderr,"[ERR] Invalid bufsize in ipod_scsi_inquiry\n"); | ||
100 | return -1; | ||
101 | } | ||
102 | |||
103 | memset(&sptwb, 0, sizeof(sptwb)); | ||
104 | |||
105 | sptwb.Spt.Length = sizeof(SCSI_PASS_THROUGH); | ||
106 | sptwb.Spt.PathId = 0; | ||
107 | sptwb.Spt.TargetId = 1; | ||
108 | sptwb.Spt.Lun = 0; | ||
109 | sptwb.Spt.CdbLength = 6; | ||
110 | sptwb.Spt.SenseInfoLength = 32; /* sbuf size */; | ||
111 | sptwb.Spt.DataIn = SCSI_IOCTL_DATA_IN; | ||
112 | sptwb.Spt.DataTransferLength = bufsize; | ||
113 | sptwb.Spt.TimeOutValue = 2; /* 2 seconds */ | ||
114 | sptwb.Spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf); | ||
115 | sptwb.Spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseBuf); | ||
116 | length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf) + | ||
117 | sptwb.Spt.DataTransferLength; | ||
118 | |||
119 | /* Set cdb info */ | ||
120 | sptwb.Spt.Cdb[0] = 0x12; /* SCSI Inquiry */ | ||
121 | sptwb.Spt.Cdb[1] = 1; | ||
122 | sptwb.Spt.Cdb[2] = page_code; | ||
123 | sptwb.Spt.Cdb[3] = 0; | ||
124 | sptwb.Spt.Cdb[4] = bufsize; | ||
125 | sptwb.Spt.Cdb[5] = 0; | ||
126 | |||
127 | status = DeviceIoControl(ipod->dh, | ||
128 | IOCTL_SCSI_PASS_THROUGH, | ||
129 | &sptwb, | ||
130 | sizeof(SCSI_PASS_THROUGH), | ||
131 | &sptwb, | ||
132 | length, | ||
133 | &returned, | ||
134 | FALSE); | ||
135 | |||
136 | if (status) { | ||
137 | /* W32 sometimes returns more bytes with additional garbage. | ||
138 | * Make sure to not copy that garbage. */ | ||
139 | memcpy(buf, sptwb.DataBuf, | ||
140 | (DWORD)bufsize >= returned ? returned : (DWORD)bufsize); | ||
141 | return 0; | ||
142 | } else { | ||
143 | return -1; | ||
144 | } | ||
145 | } | ||
146 | #endif | ||
147 | |||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * error(), lock_volume() and unlock_volume() functions and inspiration taken | ||
13 | * from: | ||
14 | * RawDisk - Direct Disk Read/Write Access for NT/2000/XP | ||
15 | * Copyright (c) 2003 Jan Kiszka | ||
16 | * http://www.stud.uni-hannover.de/user/73174/RawDisk/ | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or | ||
19 | * modify it under the terms of the GNU General Public License | ||
20 | * as published by the Free Software Foundation; either version 2 | ||
21 | * of the License, or (at your option) any later version. | ||
22 | * | ||
23 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
24 | * KIND, either express or implied. | ||
25 | * | ||
26 | ****************************************************************************/ | ||
27 | |||
28 | #if defined(_WIN32) | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include <unistd.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <string.h> | ||
34 | #include <stdlib.h> | ||
35 | #include <sys/types.h> | ||
36 | #include <sys/stat.h> | ||
37 | #include <windows.h> | ||
38 | #include <stddef.h> | ||
39 | #include <winioctl.h> | ||
40 | |||
41 | #include "ipodio.h" | ||
42 | |||
43 | static int lock_volume(HANDLE hDisk) | ||
44 | { | ||
45 | DWORD dummy; | ||
46 | |||
47 | return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, | ||
48 | &dummy, NULL); | ||
49 | } | ||
50 | |||
51 | static int unlock_volume(HANDLE hDisk) | ||
52 | { | ||
53 | DWORD dummy; | ||
54 | |||
55 | return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, | ||
56 | &dummy, NULL); | ||
57 | } | ||
58 | |||
59 | void ipod_print_error(char* msg) | ||
60 | { | ||
61 | LPSTR pMsgBuf = NULL; | ||
62 | |||
63 | printf(msg); | ||
64 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | ||
65 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), | ||
66 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), pMsgBuf, | ||
67 | 0, NULL); | ||
68 | printf(pMsgBuf); | ||
69 | LocalFree(pMsgBuf); | ||
70 | } | ||
71 | |||
72 | int ipod_open(struct ipod_t* ipod, int silent) | ||
73 | { | ||
74 | DISK_GEOMETRY_EX diskgeometry_ex; | ||
75 | DISK_GEOMETRY diskgeometry; | ||
76 | unsigned long n; | ||
77 | |||
78 | ipod->dh = CreateFileA(ipod->diskname, GENERIC_READ, | ||
79 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
80 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
81 | |||
82 | if (ipod->dh == INVALID_HANDLE_VALUE) { | ||
83 | if (!silent) ipod_print_error(" Error opening disk: "); | ||
84 | if(GetLastError() == ERROR_ACCESS_DENIED) | ||
85 | return -2; | ||
86 | else | ||
87 | return -1; | ||
88 | } | ||
89 | |||
90 | if (!lock_volume(ipod->dh)) { | ||
91 | if (!silent) ipod_print_error(" Error locking disk: "); | ||
92 | return -1; | ||
93 | } | ||
94 | |||
95 | /* Defaults */ | ||
96 | ipod->num_heads = 0; | ||
97 | ipod->sectors_per_track = 0; | ||
98 | |||
99 | if (!DeviceIoControl(ipod->dh, | ||
100 | IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, | ||
101 | NULL, | ||
102 | 0, | ||
103 | &diskgeometry_ex, | ||
104 | sizeof(diskgeometry_ex), | ||
105 | &n, | ||
106 | NULL)) { | ||
107 | if (!DeviceIoControl(ipod->dh, | ||
108 | IOCTL_DISK_GET_DRIVE_GEOMETRY, | ||
109 | NULL, | ||
110 | 0, | ||
111 | &diskgeometry, | ||
112 | sizeof(diskgeometry), | ||
113 | &n, | ||
114 | NULL)) { | ||
115 | if (!silent) ipod_print_error(" Error reading disk geometry: "); | ||
116 | return -1; | ||
117 | } else { | ||
118 | ipod->sector_size = diskgeometry.BytesPerSector; | ||
119 | ipod->num_heads = diskgeometry.TracksPerCylinder; | ||
120 | ipod->sectors_per_track = diskgeometry.SectorsPerTrack; | ||
121 | } | ||
122 | } else { | ||
123 | ipod->sector_size = diskgeometry_ex.Geometry.BytesPerSector; | ||
124 | ipod->num_heads = diskgeometry_ex.Geometry.TracksPerCylinder; | ||
125 | ipod->sectors_per_track = diskgeometry_ex.Geometry.SectorsPerTrack; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int ipod_reopen_rw(struct ipod_t* ipod) | ||
132 | { | ||
133 | /* Close existing file and re-open for writing */ | ||
134 | unlock_volume(ipod->dh); | ||
135 | CloseHandle(ipod->dh); | ||
136 | |||
137 | ipod->dh = CreateFileA(ipod->diskname, GENERIC_READ | GENERIC_WRITE, | ||
138 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
139 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
140 | |||
141 | if (ipod->dh == INVALID_HANDLE_VALUE) { | ||
142 | ipod_print_error(" Error opening disk: "); | ||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | if (!lock_volume(ipod->dh)) { | ||
147 | ipod_print_error(" Error locking disk: "); | ||
148 | return -1; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | int ipod_close(struct ipod_t* ipod) | ||
155 | { | ||
156 | unlock_volume(ipod->dh); | ||
157 | CloseHandle(ipod->dh); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize) | ||
162 | { | ||
163 | /* The ReadFile function requires a memory buffer aligned to a multiple of | ||
164 | the disk sector size. */ | ||
165 | ipod->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE); | ||
166 | if (ipod->sectorbuf== NULL) { | ||
167 | ipod_print_error(" Error allocating a buffer: "); | ||
168 | return -1; | ||
169 | } | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int ipod_dealloc_buffer(struct ipod_t* ipod) | ||
174 | { | ||
175 | if (ipod->sectorbuf == NULL) { | ||
176 | return -1; | ||
177 | } | ||
178 | if(!VirtualFree(ipod->sectorbuf, 0, MEM_RELEASE)) { | ||
179 | ipod_print_error(" Error releasing buffer "); | ||
180 | return -1; | ||
181 | } | ||
182 | ipod->sectorbuf = NULL; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | int ipod_seek(struct ipod_t* ipod, unsigned long pos) | ||
187 | { | ||
188 | if (SetFilePointer(ipod->dh, pos, NULL, FILE_BEGIN)==0xffffffff) { | ||
189 | ipod_print_error(" Seek error "); | ||
190 | return -1; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | ssize_t ipod_read(struct ipod_t* ipod, int nbytes) | ||
196 | { | ||
197 | unsigned long count; | ||
198 | |||
199 | if(ipod->sectorbuf == NULL) { | ||
200 | return -1; | ||
201 | } | ||
202 | if (!ReadFile(ipod->dh, ipod->sectorbuf, nbytes, &count, NULL)) { | ||
203 | ipod_print_error(" Error reading from disk: "); | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | return count; | ||
208 | } | ||
209 | |||
210 | ssize_t ipod_write(struct ipod_t* ipod, int nbytes) | ||
211 | { | ||
212 | unsigned long count; | ||
213 | |||
214 | if(ipod->sectorbuf == NULL) { | ||
215 | return -1; | ||
216 | } | ||
217 | if (!WriteFile(ipod->dh, ipod->sectorbuf, nbytes, &count, NULL)) { | ||
218 | ipod_print_error(" Error writing to disk: "); | ||
219 | return -1; | ||
220 | } | ||
221 | |||
222 | return count; | ||
223 | } | ||
224 | |||
225 | #endif | ||
226 | |||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __IPODIO_H | ||
23 | #define __IPODIO_H | ||
24 | |||
25 | #include <stdint.h> | ||
26 | #if !defined(_WIN32) | ||
27 | #include <unistd.h> | ||
28 | #elif defined(_MSC_VER) | ||
29 | /* MSVC uses a different name for ssize_t */ | ||
30 | #define ssize_t SSIZE_T | ||
31 | #endif | ||
32 | |||
33 | #if defined(__WIN32__) || defined(_WIN32) | ||
34 | #include <windows.h> | ||
35 | #else | ||
36 | #define HANDLE int | ||
37 | #define O_BINARY 0 | ||
38 | #endif | ||
39 | |||
40 | /* The maximum number of images in a firmware partition - a guess... */ | ||
41 | #define MAX_IMAGES 10 | ||
42 | |||
43 | enum firmwaretype_t { | ||
44 | FTYPE_OSOS = 0, | ||
45 | FTYPE_RSRC, | ||
46 | FTYPE_AUPD, | ||
47 | FTYPE_HIBE, | ||
48 | FTYPE_OSBK | ||
49 | }; | ||
50 | |||
51 | struct ipod_directory_t { | ||
52 | enum firmwaretype_t ftype; | ||
53 | int id; | ||
54 | uint32_t devOffset; /* Offset of image relative to one sector into bootpart*/ | ||
55 | uint32_t len; | ||
56 | uint32_t addr; | ||
57 | uint32_t entryOffset; | ||
58 | uint32_t chksum; | ||
59 | uint32_t vers; | ||
60 | uint32_t loadAddr; | ||
61 | }; | ||
62 | |||
63 | /* A fake partition type - DOS partition tables can't include HFS partitions */ | ||
64 | #define PARTTYPE_HFS 0xffff | ||
65 | |||
66 | struct partinfo_t { | ||
67 | uint32_t start; /* first sector (LBA) */ | ||
68 | uint32_t size; /* number of sectors */ | ||
69 | uint32_t type; | ||
70 | }; | ||
71 | |||
72 | struct ipod_t { | ||
73 | unsigned char* sectorbuf; | ||
74 | HANDLE dh; | ||
75 | char diskname[4096]; | ||
76 | int sector_size; | ||
77 | int sectors_per_track; | ||
78 | int num_heads; | ||
79 | struct ipod_directory_t ipod_directory[MAX_IMAGES]; | ||
80 | int nimages; | ||
81 | int ososimage; | ||
82 | off_t diroffset; | ||
83 | off_t start; /* Offset in bytes of firmware partition from start of disk */ | ||
84 | off_t fwoffset; /* Offset in bytes of start of firmware images from start of disk */ | ||
85 | struct partinfo_t pinfo[4]; | ||
86 | int modelnum; | ||
87 | char* modelname; | ||
88 | char* modelstr; | ||
89 | char* targetname; | ||
90 | int macpod; | ||
91 | char* xmlinfo; /* The XML Device Information (if available) */ | ||
92 | int xmlinfo_len; | ||
93 | int ramsize; /* The amount of RAM in the ipod (if available) */ | ||
94 | #ifdef WITH_BOOTOBJS | ||
95 | unsigned char* bootloader; | ||
96 | int bootloader_len; | ||
97 | #endif | ||
98 | }; | ||
99 | |||
100 | void ipod_print_error(char* msg); | ||
101 | int ipod_open(struct ipod_t* ipod, int silent); | ||
102 | int ipod_reopen_rw(struct ipod_t* ipod); | ||
103 | int ipod_close(struct ipod_t* ipod); | ||
104 | int ipod_seek(struct ipod_t* ipod, unsigned long pos); | ||
105 | int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code, | ||
106 | unsigned char* buf, int bufsize); | ||
107 | ssize_t ipod_read(struct ipod_t* ipod, int nbytes); | ||
108 | ssize_t ipod_write(struct ipod_t* ipod, int nbytes); | ||
109 | int ipod_alloc_buffer(struct ipod_t* ipod, int bufsize); | ||
110 | int ipod_dealloc_buffer(struct ipod_t* ipod); | ||
111 | |||
112 | /* In fat32format.c */ | ||
113 | int format_partition(struct ipod_t* ipod, int partition); | ||
114 | |||
115 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <unistd.h> | ||
24 | #include <fcntl.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <inttypes.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <sys/types.h> | ||
30 | #include <sys/stat.h> | ||
31 | |||
32 | #include "parttypes.h" | ||
33 | #include "ipodio.h" | ||
34 | #include "ipodpatcher.h" | ||
35 | |||
36 | #ifdef WITH_BOOTOBJS | ||
37 | #include "ipod1g2g.h" | ||
38 | #include "ipod3g.h" | ||
39 | #include "ipod4g.h" | ||
40 | #include "ipodmini1g.h" | ||
41 | #include "ipodmini2g.h" | ||
42 | #include "ipodcolor.h" | ||
43 | #include "ipodnano1g.h" | ||
44 | #include "ipodvideo.h" | ||
45 | #include "ipodnano2g.h" | ||
46 | #endif | ||
47 | |||
48 | #ifndef RBUTIL | ||
49 | #include "arc4.h" | ||
50 | #endif | ||
51 | |||
52 | int ipod_verbose = 0; | ||
53 | |||
54 | |||
55 | /* The following string appears at the start of the firmware partition */ | ||
56 | static const char apple_stop_sign[] = "{{~~ /-----\\ "\ | ||
57 | "{{~~ / \\ "\ | ||
58 | "{{~~| | "\ | ||
59 | "{{~~| S T O P | "\ | ||
60 | "{{~~| | "\ | ||
61 | "{{~~ \\ / "\ | ||
62 | "{{~~ \\-----/ "\ | ||
63 | "Copyright(C) 200"\ | ||
64 | "1 Apple Computer"\ | ||
65 | ", Inc.----------"\ | ||
66 | "----------------"\ | ||
67 | "----------------"\ | ||
68 | "----------------"\ | ||
69 | "----------------"\ | ||
70 | "----------------"\ | ||
71 | "---------------"; | ||
72 | |||
73 | /* Windows requires the buffer for disk I/O to be aligned in memory on a | ||
74 | multiple of the disk volume size - so we use a single global variable | ||
75 | and initialise it with ipod_alloc_buf() | ||
76 | */ | ||
77 | |||
78 | char* get_parttype(unsigned int pt) | ||
79 | { | ||
80 | int i; | ||
81 | static char unknown[]="Unknown"; | ||
82 | |||
83 | if (pt == PARTTYPE_HFS) { | ||
84 | return "HFS/HFS+"; | ||
85 | } | ||
86 | |||
87 | i=0; | ||
88 | while (parttypes[i].name != NULL) { | ||
89 | if (parttypes[i].type == pt) { | ||
90 | return (parttypes[i].name); | ||
91 | } | ||
92 | i++; | ||
93 | } | ||
94 | |||
95 | return unknown; | ||
96 | } | ||
97 | |||
98 | off_t filesize(int fd) { | ||
99 | struct stat buf; | ||
100 | |||
101 | if (fstat(fd,&buf) < 0) { | ||
102 | perror("[ERR] Checking filesize of input file"); | ||
103 | return -1; | ||
104 | } else { | ||
105 | return(buf.st_size); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* Partition table parsing code taken from Rockbox */ | ||
110 | |||
111 | #define MAX_SECTOR_SIZE 2048 | ||
112 | #define SECTOR_SIZE 512 | ||
113 | |||
114 | static inline unsigned short le2ushort(unsigned char* buf) | ||
115 | { | ||
116 | unsigned short res = (buf[1] << 8) | buf[0]; | ||
117 | |||
118 | return res; | ||
119 | } | ||
120 | |||
121 | static inline int le2int(unsigned char* buf) | ||
122 | { | ||
123 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
124 | |||
125 | return res; | ||
126 | } | ||
127 | |||
128 | static inline int be2int(unsigned char* buf) | ||
129 | { | ||
130 | int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
131 | |||
132 | return res; | ||
133 | } | ||
134 | |||
135 | static inline int getint16le(char* buf) | ||
136 | { | ||
137 | int16_t res = (buf[1] << 8) | buf[0]; | ||
138 | |||
139 | return res; | ||
140 | } | ||
141 | |||
142 | static inline void short2le(unsigned short val, unsigned char* addr) | ||
143 | { | ||
144 | addr[0] = val & 0xFF; | ||
145 | addr[1] = (val >> 8) & 0xff; | ||
146 | } | ||
147 | |||
148 | static inline void int2le(unsigned int val, unsigned char* addr) | ||
149 | { | ||
150 | addr[0] = val & 0xFF; | ||
151 | addr[1] = (val >> 8) & 0xff; | ||
152 | addr[2] = (val >> 16) & 0xff; | ||
153 | addr[3] = (val >> 24) & 0xff; | ||
154 | } | ||
155 | |||
156 | static inline void int2be(unsigned int val, unsigned char* addr) | ||
157 | { | ||
158 | addr[0] = (val >> 24) & 0xff; | ||
159 | addr[1] = (val >> 16) & 0xff; | ||
160 | addr[2] = (val >> 8) & 0xff; | ||
161 | addr[3] = val & 0xFF; | ||
162 | } | ||
163 | |||
164 | |||
165 | #define BYTES2INT32(array,pos)\ | ||
166 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ | ||
167 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | ||
168 | |||
169 | int read_partinfo(struct ipod_t* ipod, int silent) | ||
170 | { | ||
171 | int i; | ||
172 | unsigned long count; | ||
173 | |||
174 | if(ipod->sectorbuf == NULL) { | ||
175 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
176 | return -1; | ||
177 | } | ||
178 | |||
179 | count = ipod_read(ipod,ipod->sector_size); | ||
180 | |||
181 | if (count <= 0) { | ||
182 | ipod_print_error(" Error reading from disk: "); | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | memset(ipod->pinfo, 0, sizeof(ipod->pinfo)); | ||
187 | |||
188 | if ((ipod->sectorbuf[510] == 0x55) && (ipod->sectorbuf[511] == 0xaa)) { | ||
189 | /* DOS partition table */ | ||
190 | ipod->macpod = 0; | ||
191 | /* parse partitions */ | ||
192 | for ( i = 0; i < 4; i++ ) { | ||
193 | unsigned char* ptr = ipod->sectorbuf + 0x1be + 16*i; | ||
194 | ipod->pinfo[i].type = ptr[4]; | ||
195 | ipod->pinfo[i].start = BYTES2INT32(ptr, 8); | ||
196 | ipod->pinfo[i].size = BYTES2INT32(ptr, 12); | ||
197 | |||
198 | /* extended? */ | ||
199 | if ( ipod->pinfo[i].type == 5 ) { | ||
200 | /* not handled yet */ | ||
201 | } | ||
202 | } | ||
203 | } else if ((ipod->sectorbuf[0] == 'E') && (ipod->sectorbuf[1] == 'R')) { | ||
204 | /* Apple Partition Map */ | ||
205 | |||
206 | /* APM parsing code based on the check_mac_partitions() function in | ||
207 | ipodloader2 - written by Thomas Tempelmann and released | ||
208 | under the GPL. */ | ||
209 | |||
210 | int blkNo = 1; | ||
211 | int partBlkCount = 1; | ||
212 | int partBlkSizMul = ipod->sectorbuf[2] / 2; | ||
213 | |||
214 | int pmMapBlkCnt; /* # of blks in partition map */ | ||
215 | int pmPyPartStart; /* physical start blk of partition */ | ||
216 | int pmPartBlkCnt; /* # of blks in this partition */ | ||
217 | int i = 0; | ||
218 | |||
219 | ipod->macpod = 1; | ||
220 | |||
221 | memset(ipod->pinfo,0,sizeof(ipod->pinfo)); | ||
222 | |||
223 | while (blkNo <= partBlkCount) { | ||
224 | if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) { | ||
225 | fprintf(stderr,"[ERR] Seek failed whilst reading APM\n"); | ||
226 | return -1; | ||
227 | } | ||
228 | |||
229 | count = ipod_read(ipod, ipod->sector_size); | ||
230 | |||
231 | if (count <= 0) { | ||
232 | ipod_print_error(" Error reading from disk: "); | ||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | /* see if it's a partition entry */ | ||
237 | if ((ipod->sectorbuf[0] != 'P') || (ipod->sectorbuf[1] != 'M')) { | ||
238 | /* end of partition table -> leave the loop */ | ||
239 | break; | ||
240 | } | ||
241 | |||
242 | /* Extract the interesting entries */ | ||
243 | pmMapBlkCnt = be2int(ipod->sectorbuf + 4); | ||
244 | pmPyPartStart = be2int(ipod->sectorbuf + 8); | ||
245 | pmPartBlkCnt = be2int(ipod->sectorbuf + 12); | ||
246 | |||
247 | /* update the number of part map blocks */ | ||
248 | partBlkCount = pmMapBlkCnt; | ||
249 | |||
250 | if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_MDFW", 32)==0) { | ||
251 | /* A Firmware partition */ | ||
252 | ipod->pinfo[i].start = pmPyPartStart; | ||
253 | ipod->pinfo[i].size = pmPartBlkCnt; | ||
254 | ipod->pinfo[i].type = 0; | ||
255 | i++; | ||
256 | } else if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_HFS", 32)==0) { | ||
257 | /* A HFS partition */ | ||
258 | ipod->pinfo[i].start = pmPyPartStart; | ||
259 | ipod->pinfo[i].size = pmPartBlkCnt; | ||
260 | ipod->pinfo[i].type = PARTTYPE_HFS; | ||
261 | i++; | ||
262 | } | ||
263 | |||
264 | blkNo++; /* read next partition map entry */ | ||
265 | } | ||
266 | } else { | ||
267 | if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); | ||
268 | return -1; | ||
269 | } | ||
270 | |||
271 | /* Check that the partition table looks like an ipod: | ||
272 | 1) Partition 1 is of type 0 (Empty) but isn't empty. | ||
273 | 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod) | ||
274 | */ | ||
275 | if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) || | ||
276 | ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) && | ||
277 | (ipod->pinfo[1].type != PARTTYPE_HFS))) { | ||
278 | if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n"); | ||
279 | return -1; | ||
280 | } | ||
281 | |||
282 | ipod->start = ipod->pinfo[0].start*ipod->sector_size; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | int read_partition(struct ipod_t* ipod, int outfile) | ||
287 | { | ||
288 | int res; | ||
289 | ssize_t n; | ||
290 | int bytesleft; | ||
291 | int chunksize; | ||
292 | int count = ipod->pinfo[0].size; | ||
293 | |||
294 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
295 | return -1; | ||
296 | } | ||
297 | if(ipod->sectorbuf == NULL) { | ||
298 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
299 | return -1; | ||
300 | } | ||
301 | |||
302 | fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count); | ||
303 | |||
304 | bytesleft = count * ipod->sector_size; | ||
305 | while (bytesleft > 0) { | ||
306 | if (bytesleft > BUFFER_SIZE) { | ||
307 | chunksize = BUFFER_SIZE; | ||
308 | } else { | ||
309 | chunksize = bytesleft; | ||
310 | } | ||
311 | |||
312 | n = ipod_read(ipod, chunksize); | ||
313 | |||
314 | if (n < 0) { | ||
315 | return -1; | ||
316 | } | ||
317 | |||
318 | if (n < chunksize) { | ||
319 | fprintf(stderr, | ||
320 | "[ERR] Short read in disk_read() - requested %d, got %d\n", | ||
321 | chunksize,(int)n); | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | bytesleft -= n; | ||
326 | |||
327 | res = write(outfile,ipod->sectorbuf,n); | ||
328 | |||
329 | if (res < 0) { | ||
330 | perror("[ERR] write in disk_read"); | ||
331 | return -1; | ||
332 | } | ||
333 | |||
334 | if (res != n) { | ||
335 | fprintf(stderr, | ||
336 | "Short write - requested %d, received %d - aborting.\n",(int)n,res); | ||
337 | return -1; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | fprintf(stderr,"[INFO] Done.\n"); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | int write_partition(struct ipod_t* ipod, int infile) | ||
346 | { | ||
347 | ssize_t res; | ||
348 | int n; | ||
349 | int bytesread; | ||
350 | int byteswritten = 0; | ||
351 | int eof; | ||
352 | int padding = 0; | ||
353 | |||
354 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
355 | return -1; | ||
356 | } | ||
357 | if(ipod->sectorbuf == NULL) { | ||
358 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
359 | return -1; | ||
360 | } | ||
361 | |||
362 | fprintf(stderr,"[INFO] Writing input file to device\n"); | ||
363 | bytesread = 0; | ||
364 | eof = 0; | ||
365 | while (!eof) { | ||
366 | n = read(infile,ipod->sectorbuf,BUFFER_SIZE); | ||
367 | |||
368 | if (n < 0) { | ||
369 | perror("[ERR] read in disk_write"); | ||
370 | return -1; | ||
371 | } | ||
372 | |||
373 | if (n < BUFFER_SIZE) { | ||
374 | eof = 1; | ||
375 | /* We need to pad the last write to a multiple of SECTOR_SIZE */ | ||
376 | if ((n % ipod->sector_size) != 0) { | ||
377 | padding = (ipod->sector_size-(n % ipod->sector_size)); | ||
378 | n += padding; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | bytesread += n; | ||
383 | |||
384 | res = ipod_write(ipod, n); | ||
385 | |||
386 | if (res < 0) { | ||
387 | ipod_print_error(" Error writing to disk: "); | ||
388 | fprintf(stderr,"Bytes written: %d\n",byteswritten); | ||
389 | return -1; | ||
390 | } | ||
391 | |||
392 | if (res != n) { | ||
393 | fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res); | ||
394 | return -1; | ||
395 | } | ||
396 | |||
397 | byteswritten += res; | ||
398 | } | ||
399 | |||
400 | fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n", | ||
401 | byteswritten-padding,padding); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE", "OSBK" }; | ||
406 | |||
407 | int diskmove(struct ipod_t* ipod, int delta) | ||
408 | { | ||
409 | int src_start; | ||
410 | int src_end; | ||
411 | int bytesleft; | ||
412 | int chunksize; | ||
413 | int n; | ||
414 | |||
415 | src_start = ipod->ipod_directory[1].devOffset; | ||
416 | src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + | ||
417 | ipod->ipod_directory[ipod->nimages-1].len + | ||
418 | (ipod->sector_size-1)) & ~(ipod->sector_size-1); | ||
419 | bytesleft = src_end - src_start; | ||
420 | |||
421 | if (ipod_verbose) { | ||
422 | fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta); | ||
423 | fprintf(stderr,"[VERB] src_start = %08x\n",src_start); | ||
424 | fprintf(stderr,"[VERB] src_end = %08x\n",src_end); | ||
425 | fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta); | ||
426 | fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta); | ||
427 | fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft); | ||
428 | } | ||
429 | |||
430 | while (bytesleft > 0) { | ||
431 | if (bytesleft <= BUFFER_SIZE) { | ||
432 | chunksize = bytesleft; | ||
433 | } else { | ||
434 | chunksize = BUFFER_SIZE; | ||
435 | } | ||
436 | |||
437 | if (ipod_verbose) { | ||
438 | fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n", | ||
439 | chunksize, | ||
440 | src_end-chunksize, | ||
441 | src_end-chunksize+delta, | ||
442 | (unsigned int)(ipod->start+src_end-chunksize), | ||
443 | (unsigned int)(ipod->start+src_end-chunksize+delta)); | ||
444 | } | ||
445 | |||
446 | |||
447 | if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) { | ||
448 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
449 | return -1; | ||
450 | } | ||
451 | |||
452 | if ((n = ipod_read(ipod,chunksize)) < 0) { | ||
453 | perror("[ERR] Write failed\n"); | ||
454 | return -1; | ||
455 | } | ||
456 | |||
457 | if (n < chunksize) { | ||
458 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
459 | chunksize,n); | ||
460 | return -1; | ||
461 | } | ||
462 | |||
463 | if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) { | ||
464 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
465 | return -1; | ||
466 | } | ||
467 | |||
468 | if ((n = ipod_write(ipod,chunksize)) < 0) { | ||
469 | perror("[ERR] Write failed\n"); | ||
470 | return -1; | ||
471 | } | ||
472 | |||
473 | if (n < chunksize) { | ||
474 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
475 | ,chunksize,n); | ||
476 | return -1; | ||
477 | } | ||
478 | |||
479 | src_end -= chunksize; | ||
480 | bytesleft -= chunksize; | ||
481 | } | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int rename_image(struct ipod_t* ipod, char* from, char* to) | ||
487 | { | ||
488 | int n; | ||
489 | int x; | ||
490 | int found; | ||
491 | int i; | ||
492 | unsigned char* p; | ||
493 | |||
494 | /* diroffset may not be sector-aligned */ | ||
495 | x = ipod->diroffset % ipod->sector_size; | ||
496 | |||
497 | if(ipod->sectorbuf == NULL) { | ||
498 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
499 | return -1; | ||
500 | } | ||
501 | /* Read directory */ | ||
502 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
503 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
504 | return -1; | ||
505 | } | ||
506 | |||
507 | n=ipod_read(ipod, ipod->sector_size); | ||
508 | if (n < 0) { | ||
509 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
510 | return -1; | ||
511 | } | ||
512 | |||
513 | p = ipod->sectorbuf + x; | ||
514 | |||
515 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
516 | if (p[0] == 0) | ||
517 | { | ||
518 | /* Adjust diroffset */ | ||
519 | ipod->diroffset += ipod->sector_size - x; | ||
520 | |||
521 | n=ipod_read(ipod, ipod->sector_size); | ||
522 | if (n < 0) { | ||
523 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
524 | return -1; | ||
525 | } | ||
526 | p = ipod->sectorbuf; | ||
527 | } | ||
528 | |||
529 | found = 0; | ||
530 | for (i=0 ; !found && i < MAX_IMAGES; i++) { | ||
531 | if (memcmp(p + 4, from, 4) == 0) { | ||
532 | memcpy(p + 4, to, 4); | ||
533 | |||
534 | found = 1; | ||
535 | } | ||
536 | p += 40; | ||
537 | } | ||
538 | |||
539 | if (!found) { | ||
540 | fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", from); | ||
541 | return -1; | ||
542 | } | ||
543 | |||
544 | /* Write directory back to disk */ | ||
545 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
546 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
547 | return -1; | ||
548 | } | ||
549 | |||
550 | n=ipod_write(ipod, ipod->sector_size); | ||
551 | if (n < 0) { | ||
552 | fprintf(stderr,"[ERR] Write of directory failed in rename_image.\n"); | ||
553 | return -1; | ||
554 | } | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int delete_image(struct ipod_t* ipod, char* name) | ||
560 | { | ||
561 | int n; | ||
562 | int x; | ||
563 | int found; | ||
564 | int i; | ||
565 | unsigned char* p; | ||
566 | |||
567 | /* diroffset may not be sector-aligned */ | ||
568 | x = ipod->diroffset % ipod->sector_size; | ||
569 | |||
570 | /* Read directory */ | ||
571 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
572 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
573 | return -1; | ||
574 | } | ||
575 | |||
576 | n=ipod_read(ipod, ipod->sector_size); | ||
577 | if (n < 0) { | ||
578 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
579 | return -1; | ||
580 | } | ||
581 | |||
582 | p = ipod->sectorbuf + x; | ||
583 | |||
584 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
585 | if (p[0] == 0) | ||
586 | { | ||
587 | /* Adjust diroffset */ | ||
588 | ipod->diroffset += ipod->sector_size - x; | ||
589 | |||
590 | n=ipod_read(ipod, ipod->sector_size); | ||
591 | if (n < 0) { | ||
592 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
593 | return -1; | ||
594 | } | ||
595 | p = ipod->sectorbuf; | ||
596 | } | ||
597 | |||
598 | found = 0; | ||
599 | for (i=0 ; !found && i < MAX_IMAGES; i++) { | ||
600 | if (memcmp(p + 4, name, 4) == 0) { | ||
601 | memset(p, 0, 40); /* Delete directory entry */ | ||
602 | found = 1; | ||
603 | } | ||
604 | p += 40; | ||
605 | } | ||
606 | |||
607 | if (!found) { | ||
608 | fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", name); | ||
609 | return -1; | ||
610 | } | ||
611 | |||
612 | /* Write directory back to disk */ | ||
613 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
614 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
615 | return -1; | ||
616 | } | ||
617 | |||
618 | n=ipod_write(ipod, ipod->sector_size); | ||
619 | if (n < 0) { | ||
620 | fprintf(stderr,"[ERR] Write of directory failed in delete_image.\n"); | ||
621 | return -1; | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | int add_new_image(struct ipod_t* ipod, char* imagename, char* filename, int type) | ||
628 | { | ||
629 | int length; | ||
630 | int found; | ||
631 | int i; | ||
632 | int x; | ||
633 | int n; | ||
634 | int infile; | ||
635 | int newsize; | ||
636 | unsigned long chksum=0; | ||
637 | unsigned long filechksum=0; | ||
638 | unsigned long offset; | ||
639 | unsigned char header[8]; /* Header for .ipod file */ | ||
640 | unsigned char* p; | ||
641 | |||
642 | if(ipod->sectorbuf == NULL) { | ||
643 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
644 | return -1; | ||
645 | } | ||
646 | #ifdef WITH_BOOTOBJS | ||
647 | if (type == FILETYPE_INTERNAL) { | ||
648 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
649 | length = ipod->bootloader_len; | ||
650 | infile = -1; | ||
651 | } | ||
652 | else | ||
653 | #endif | ||
654 | { | ||
655 | /* First check that the input file is the correct type for this ipod. */ | ||
656 | infile=open(filename,O_RDONLY); | ||
657 | if (infile < 0) { | ||
658 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
659 | return -1; | ||
660 | } | ||
661 | |||
662 | if (type==FILETYPE_DOT_IPOD) { | ||
663 | n = read(infile,header,8); | ||
664 | if (n < 8) { | ||
665 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
666 | close(infile); | ||
667 | return -1; | ||
668 | } | ||
669 | |||
670 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
671 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
672 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
673 | close(infile); | ||
674 | return -1; | ||
675 | } | ||
676 | |||
677 | filechksum = be2int(header); | ||
678 | |||
679 | length = filesize(infile)-8; | ||
680 | } else { | ||
681 | length = filesize(infile); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
686 | |||
687 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
688 | length,newsize); | ||
689 | |||
690 | if (newsize > BUFFER_SIZE) { | ||
691 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
692 | if (infile >= 0) close(infile); | ||
693 | return -1; | ||
694 | } | ||
695 | |||
696 | /* TODO: Check if we have enough space in the partition for the new image */ | ||
697 | |||
698 | #ifdef WITH_BOOTOBJS | ||
699 | if (type == FILETYPE_INTERNAL) { | ||
700 | memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); | ||
701 | } | ||
702 | else | ||
703 | #endif | ||
704 | { | ||
705 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
706 | |||
707 | n = read(infile,ipod->sectorbuf,length); | ||
708 | if (n < 0) { | ||
709 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
710 | close(infile); | ||
711 | return -1; | ||
712 | } | ||
713 | close(infile); | ||
714 | } | ||
715 | |||
716 | /* Pad the data with zeros */ | ||
717 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
718 | |||
719 | if (type==FILETYPE_DOT_IPOD) { | ||
720 | chksum = ipod->modelnum; | ||
721 | for (i = 0; i < length; i++) { | ||
722 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
723 | chksum += ipod->sectorbuf[i]; | ||
724 | } | ||
725 | |||
726 | if (chksum == filechksum) { | ||
727 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
728 | } else { | ||
729 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
730 | return -1; | ||
731 | } | ||
732 | } | ||
733 | |||
734 | |||
735 | offset = ipod->fwoffset + ipod->ipod_directory[ipod->nimages - 1].devOffset + | ||
736 | ipod->ipod_directory[ipod->nimages - 1].len + ipod->sector_size; | ||
737 | |||
738 | /* 2nd Gen Nano has encrypted firmware, and the sector | ||
739 | preceeding the firmware contains hashes that need to be | ||
740 | preserved. Nano 2G images include these extra 2048 (0x800) | ||
741 | bytes | ||
742 | */ | ||
743 | if (ipod_seek(ipod, offset - (ipod->modelnum == 62 ? 0x800 : 0)) < 0) { | ||
744 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
745 | return -1; | ||
746 | } | ||
747 | |||
748 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
749 | perror("[ERR] Write failed\n"); | ||
750 | return -1; | ||
751 | } | ||
752 | |||
753 | if (n < newsize) { | ||
754 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
755 | ,newsize,n); | ||
756 | return -1; | ||
757 | } | ||
758 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
759 | |||
760 | /* Now we need to create a new directory entry | ||
761 | |||
762 | NOTE: On the Nano 2G, the checksum is the checksum of the | ||
763 | unencrypted firmware. But this isn't checked by the NOR | ||
764 | bootloader (there are cryptographic hashes in the | ||
765 | firmware itself), so it doesn't matter that this is | ||
766 | wrong. | ||
767 | */ | ||
768 | chksum = 0; | ||
769 | for (i = 0; i < length; i++) { | ||
770 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
771 | chksum += ipod->sectorbuf[i]; | ||
772 | } | ||
773 | |||
774 | x = ipod->diroffset % ipod->sector_size; | ||
775 | |||
776 | /* Read directory */ | ||
777 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
778 | |||
779 | n=ipod_read(ipod, ipod->sector_size); | ||
780 | if (n < 0) { return -1; } | ||
781 | |||
782 | /* Create a new directory entry */ | ||
783 | |||
784 | /* Copy OSOS or OSBK details - we assume one of them exists */ | ||
785 | p = ipod->sectorbuf + x; | ||
786 | found = 0; | ||
787 | for (i = 0; !found && i < ipod->nimages; i++) { | ||
788 | if ((memcmp(p + 4, "soso", 4)==0) || (memcmp(p + 4, "kbso", 4)==0)) { | ||
789 | found = 1; | ||
790 | } else { | ||
791 | p += 40; | ||
792 | } | ||
793 | } | ||
794 | |||
795 | if (!found) { | ||
796 | fprintf(stderr,"[ERR] No OSOS or OSBK image to copy directory from\n"); | ||
797 | return -1; | ||
798 | } | ||
799 | |||
800 | /* Copy directory image */ | ||
801 | memcpy(ipod->sectorbuf + x + (ipod->nimages * 40), p, 40); | ||
802 | p = ipod->sectorbuf + x + (ipod->nimages * 40); | ||
803 | |||
804 | /* Modify directory. */ | ||
805 | memcpy(p + 4, imagename, 4); | ||
806 | int2le(offset - ipod->fwoffset, p + 12); /* devOffset */ | ||
807 | int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); /* len */ | ||
808 | int2le(chksum, p + 28); /* checksum */ | ||
809 | |||
810 | /* Write directory */ | ||
811 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
812 | n=ipod_write(ipod, ipod->sector_size); | ||
813 | if (n < 0) { return -1; } | ||
814 | |||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | |||
819 | int ipod_has_bootloader(struct ipod_t* ipod) | ||
820 | { | ||
821 | /* The 2nd gen Nano is installed differently */ | ||
822 | if (ipod->modelnum == 62) { | ||
823 | int i; | ||
824 | int has_osbk = 0; | ||
825 | /* Check if we have an OSBK image */ | ||
826 | for (i = 0; i < ipod->nimages; i++) { | ||
827 | if (ipod->ipod_directory[i].ftype==FTYPE_OSBK) { | ||
828 | has_osbk = 1; | ||
829 | } | ||
830 | } | ||
831 | return has_osbk; | ||
832 | } | ||
833 | else { | ||
834 | return (ipod->ipod_directory[0].entryOffset != 0); | ||
835 | } | ||
836 | } | ||
837 | |||
838 | |||
839 | /* | ||
840 | Bootloader installation on the Nano2G consists of renaming the | ||
841 | OSOS image to OSBK and then writing the Rockbox bootloader as a | ||
842 | new OSOS image. | ||
843 | |||
844 | Maybe this approach can/should be adapted for other ipods, as it | ||
845 | prevents the Apple bootloader loading the original firmware into | ||
846 | RAM along with the Rockbox bootloader (and hence will give a | ||
847 | faster boot when the user just wants to start Rockbox). | ||
848 | |||
849 | */ | ||
850 | |||
851 | static int add_bootloader_nano2g(struct ipod_t* ipod, char* filename, int type) | ||
852 | { | ||
853 | /* Check if we already have an OSBK image */ | ||
854 | if (ipod_has_bootloader(ipod) == 0) { | ||
855 | /* First-time install - rename OSOS to OSBK and create new OSOS for bootloader */ | ||
856 | fprintf(stderr,"[INFO] Creating OSBK backup image of original firmware\n"); | ||
857 | |||
858 | if (rename_image(ipod, "soso", "kbso") < 0) { | ||
859 | fprintf(stderr,"[ERR] Could not rename OSOS image\n"); | ||
860 | return -1; | ||
861 | } | ||
862 | |||
863 | /* Add our bootloader as a brand new image */ | ||
864 | return add_new_image(ipod, "soso", filename, type); | ||
865 | } else { | ||
866 | /* This is an update, just replace OSOS with our bootloader */ | ||
867 | |||
868 | return write_firmware(ipod, filename, type); | ||
869 | } | ||
870 | } | ||
871 | |||
872 | |||
873 | static int delete_bootloader_nano2g(struct ipod_t* ipod) | ||
874 | { | ||
875 | /* Check if we have an OSBK image */ | ||
876 | if (ipod_has_bootloader(ipod) == 0) { | ||
877 | fprintf(stderr,"[ERR] No OSBK image found - nothing to uninstall\n"); | ||
878 | return -1; | ||
879 | } else { | ||
880 | /* Delete our bootloader image */ | ||
881 | if (delete_image(ipod, "soso") < 0) { | ||
882 | fprintf(stderr,"[WARN] Could not delete OSOS image\n"); | ||
883 | } else { | ||
884 | fprintf(stderr,"[INFO] OSOS image deleted\n"); | ||
885 | } | ||
886 | |||
887 | if (rename_image(ipod, "kbso", "soso") < 0) { | ||
888 | fprintf(stderr,"[ERR] Could not rename OSBK image\n"); | ||
889 | return -1; | ||
890 | } | ||
891 | |||
892 | |||
893 | fprintf(stderr,"[INFO] OSBK image renamed to OSOS - bootloader uninstalled.\n"); | ||
894 | return 0; | ||
895 | } | ||
896 | } | ||
897 | |||
898 | |||
899 | int add_bootloader(struct ipod_t* ipod, char* filename, int type) | ||
900 | { | ||
901 | int length; | ||
902 | int i; | ||
903 | int x; | ||
904 | int n; | ||
905 | int infile; | ||
906 | int paddedlength; | ||
907 | int entryOffset; | ||
908 | int delta = 0; | ||
909 | unsigned long chksum=0; | ||
910 | unsigned long filechksum=0; | ||
911 | unsigned char header[8]; /* Header for .ipod file */ | ||
912 | unsigned char* bootloader_buf; | ||
913 | |||
914 | /* The 2nd gen Nano is installed differently */ | ||
915 | if (ipod->modelnum == 62) { | ||
916 | return add_bootloader_nano2g(ipod, filename, type); | ||
917 | } | ||
918 | if(ipod->sectorbuf == NULL) { | ||
919 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
920 | return -1; | ||
921 | } | ||
922 | |||
923 | /* Calculate the position in the OSOS image where our bootloader will go. */ | ||
924 | if (ipod->ipod_directory[0].entryOffset>0) { | ||
925 | /* Keep the same entryOffset */ | ||
926 | entryOffset = ipod->ipod_directory[0].entryOffset; | ||
927 | } else { | ||
928 | entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
929 | } | ||
930 | |||
931 | #ifdef WITH_BOOTOBJS | ||
932 | if (type == FILETYPE_INTERNAL) { | ||
933 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
934 | memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); | ||
935 | length = ipod->bootloader_len; | ||
936 | paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
937 | } | ||
938 | else | ||
939 | #endif | ||
940 | { | ||
941 | infile=open(filename,O_RDONLY); | ||
942 | if (infile < 0) { | ||
943 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
944 | return -1; | ||
945 | } | ||
946 | |||
947 | if (type==FILETYPE_DOT_IPOD) { | ||
948 | /* First check that the input file is the correct type for this ipod. */ | ||
949 | n = read(infile,header,8); | ||
950 | if (n < 8) { | ||
951 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
952 | close(infile); | ||
953 | return -1; | ||
954 | } | ||
955 | |||
956 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
957 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
958 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
959 | close(infile); | ||
960 | return -1; | ||
961 | } | ||
962 | |||
963 | filechksum = be2int(header); | ||
964 | |||
965 | length=filesize(infile)-8; | ||
966 | } else { | ||
967 | length=filesize(infile); | ||
968 | } | ||
969 | paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
970 | |||
971 | bootloader_buf = malloc(length); | ||
972 | if (bootloader_buf == NULL) { | ||
973 | fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n"); | ||
974 | return -1; | ||
975 | } | ||
976 | /* Now read our bootloader - we need to check it before modifying the partition*/ | ||
977 | n = read(infile,bootloader_buf,length); | ||
978 | close(infile); | ||
979 | |||
980 | if (n < 0) { | ||
981 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
982 | return -1; | ||
983 | } | ||
984 | |||
985 | if (type==FILETYPE_DOT_IPOD) { | ||
986 | /* Calculate and confirm bootloader checksum */ | ||
987 | chksum = ipod->modelnum; | ||
988 | for (i = 0; i < length; i++) { | ||
989 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
990 | chksum += bootloader_buf[i]; | ||
991 | } | ||
992 | |||
993 | if (chksum == filechksum) { | ||
994 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
995 | } else { | ||
996 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
997 | return -1; | ||
998 | } | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | if (entryOffset+paddedlength > BUFFER_SIZE) { | ||
1003 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
1004 | return -1; | ||
1005 | } | ||
1006 | |||
1007 | if (ipod_verbose) { | ||
1008 | fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size); | ||
1009 | fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset); | ||
1010 | fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength); | ||
1011 | } | ||
1012 | |||
1013 | /* Check if we have enough space */ | ||
1014 | /* TODO: Check the size of the partition. */ | ||
1015 | if (ipod->nimages > 1) { | ||
1016 | if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) > | ||
1017 | ipod->ipod_directory[1].devOffset) { | ||
1018 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); | ||
1019 | delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength | ||
1020 | - ipod->ipod_directory[1].devOffset + ipod->sector_size; | ||
1021 | |||
1022 | if (diskmove(ipod, delta) < 0) { | ||
1023 | fprintf(stderr,"[ERR] Image movement failed.\n"); | ||
1024 | return -1; | ||
1025 | } | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | |||
1030 | /* We have moved the partitions, now we can write our bootloader */ | ||
1031 | |||
1032 | /* Firstly read the original firmware into ipod->sectorbuf */ | ||
1033 | fprintf(stderr,"[INFO] Reading original firmware...\n"); | ||
1034 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1035 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1036 | return -1; | ||
1037 | } | ||
1038 | |||
1039 | if ((n = ipod_read(ipod,entryOffset)) < 0) { | ||
1040 | perror("[ERR] Read failed\n"); | ||
1041 | return -1; | ||
1042 | } | ||
1043 | |||
1044 | if (n < entryOffset) { | ||
1045 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
1046 | ,entryOffset,n); | ||
1047 | return -1; | ||
1048 | } | ||
1049 | |||
1050 | #ifdef WITH_BOOTOBJS | ||
1051 | if (type == FILETYPE_INTERNAL) { | ||
1052 | memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); | ||
1053 | } | ||
1054 | else | ||
1055 | #endif | ||
1056 | { | ||
1057 | memcpy(ipod->sectorbuf+entryOffset,bootloader_buf,length); | ||
1058 | free(bootloader_buf); | ||
1059 | } | ||
1060 | |||
1061 | /* Calculate new checksum for combined image */ | ||
1062 | chksum = 0; | ||
1063 | for (i=0;i<entryOffset + length; i++) { | ||
1064 | chksum += ipod->sectorbuf[i]; | ||
1065 | } | ||
1066 | |||
1067 | /* Now write the combined firmware image to the disk */ | ||
1068 | |||
1069 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1070 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1071 | return -1; | ||
1072 | } | ||
1073 | |||
1074 | if ((n = ipod_write(ipod,entryOffset+paddedlength)) < 0) { | ||
1075 | perror("[ERR] Write failed\n"); | ||
1076 | return -1; | ||
1077 | } | ||
1078 | |||
1079 | if (n < (entryOffset+paddedlength)) { | ||
1080 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
1081 | ,entryOffset+paddedlength,n); | ||
1082 | return -1; | ||
1083 | } | ||
1084 | |||
1085 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength); | ||
1086 | |||
1087 | x = ipod->diroffset % ipod->sector_size; | ||
1088 | |||
1089 | /* Read directory */ | ||
1090 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1091 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1092 | return -1; | ||
1093 | } | ||
1094 | |||
1095 | n=ipod_read(ipod, ipod->sector_size); | ||
1096 | if (n < 0) { | ||
1097 | fprintf(stderr,"[ERR] Directory read failed\n"); | ||
1098 | return -1; | ||
1099 | } | ||
1100 | |||
1101 | /* Update entries for image 0 */ | ||
1102 | int2le(entryOffset+length,ipod->sectorbuf+x+16); | ||
1103 | int2le(entryOffset,ipod->sectorbuf+x+24); | ||
1104 | int2le(chksum,ipod->sectorbuf+x+28); | ||
1105 | int2le(0xffffffff,ipod->sectorbuf+x+36); /* loadAddr */ | ||
1106 | |||
1107 | /* Update devOffset entries for other images, if we have moved them */ | ||
1108 | if (delta > 0) { | ||
1109 | for (i=1;i<ipod->nimages;i++) { | ||
1110 | int2le(le2int(ipod->sectorbuf+x+i*40+12)+delta,ipod->sectorbuf+x+i*40+12); | ||
1111 | } | ||
1112 | } | ||
1113 | |||
1114 | /* Write directory */ | ||
1115 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1116 | fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x)); | ||
1117 | return -1; | ||
1118 | } | ||
1119 | n=ipod_write(ipod, ipod->sector_size); | ||
1120 | if (n < 0) { | ||
1121 | fprintf(stderr,"[ERR] Directory write failed\n"); | ||
1122 | return -1; | ||
1123 | } | ||
1124 | |||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | int delete_bootloader(struct ipod_t* ipod) | ||
1129 | { | ||
1130 | int length; | ||
1131 | int i; | ||
1132 | int x; | ||
1133 | int n; | ||
1134 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ | ||
1135 | |||
1136 | /* The 2nd gen Nano is installed differently */ | ||
1137 | if (ipod->modelnum == 62) { | ||
1138 | return delete_bootloader_nano2g(ipod); | ||
1139 | } | ||
1140 | if(ipod->sectorbuf == NULL) { | ||
1141 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1142 | return -1; | ||
1143 | } | ||
1144 | |||
1145 | /* Removing the bootloader involves adjusting the "length", | ||
1146 | "chksum" and "entryOffset" values in the osos image's directory | ||
1147 | entry. */ | ||
1148 | |||
1149 | /* Firstly check we have a bootloader... */ | ||
1150 | |||
1151 | if (ipod_has_bootloader(ipod) == 0) { | ||
1152 | fprintf(stderr,"[ERR] No bootloader found.\n"); | ||
1153 | return -1; | ||
1154 | } | ||
1155 | |||
1156 | length = ipod->ipod_directory[0].entryOffset; | ||
1157 | |||
1158 | /* Read the firmware so we can calculate the checksum */ | ||
1159 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
1160 | |||
1161 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1162 | return -1; | ||
1163 | } | ||
1164 | |||
1165 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
1166 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", | ||
1167 | length,i); | ||
1168 | |||
1169 | if ((n = ipod_read(ipod,i)) < 0) { | ||
1170 | return -1; | ||
1171 | } | ||
1172 | |||
1173 | if (n < i) { | ||
1174 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
1175 | i,n); | ||
1176 | return -1; | ||
1177 | } | ||
1178 | |||
1179 | chksum = 0; | ||
1180 | for (i = 0; i < length; i++) { | ||
1181 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1182 | chksum += ipod->sectorbuf[i]; | ||
1183 | } | ||
1184 | |||
1185 | /* Now write back the updated directory entry */ | ||
1186 | |||
1187 | fprintf(stderr,"[INFO] Updating firmware checksum\n"); | ||
1188 | |||
1189 | x = ipod->diroffset % ipod->sector_size; | ||
1190 | |||
1191 | /* Read directory */ | ||
1192 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1193 | |||
1194 | n=ipod_read(ipod, ipod->sector_size); | ||
1195 | if (n < 0) { return -1; } | ||
1196 | |||
1197 | /* Update entries for image 0 */ | ||
1198 | int2le(length,ipod->sectorbuf+x+16); | ||
1199 | int2le(0,ipod->sectorbuf+x+24); | ||
1200 | int2le(chksum,ipod->sectorbuf+x+28); | ||
1201 | |||
1202 | /* Write directory */ | ||
1203 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1204 | n=ipod_write(ipod, ipod->sector_size); | ||
1205 | if (n < 0) { return -1; } | ||
1206 | |||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1210 | int write_firmware(struct ipod_t* ipod, char* filename, int type) | ||
1211 | { | ||
1212 | int length; | ||
1213 | int i; | ||
1214 | int x; | ||
1215 | int n; | ||
1216 | int infile; | ||
1217 | int newsize; | ||
1218 | int bytesavailable; | ||
1219 | unsigned long chksum=0; | ||
1220 | unsigned long filechksum=0; | ||
1221 | unsigned long offset; | ||
1222 | unsigned char header[8]; /* Header for .ipod file */ | ||
1223 | unsigned char* p; | ||
1224 | |||
1225 | if(ipod->sectorbuf == NULL) { | ||
1226 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1227 | return -1; | ||
1228 | } | ||
1229 | #ifdef WITH_BOOTOBJS | ||
1230 | if (type == FILETYPE_INTERNAL) { | ||
1231 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
1232 | length = ipod->bootloader_len; | ||
1233 | infile = -1; | ||
1234 | } | ||
1235 | else | ||
1236 | #endif | ||
1237 | { | ||
1238 | /* First check that the input file is the correct type for this ipod. */ | ||
1239 | infile=open(filename,O_RDONLY); | ||
1240 | if (infile < 0) { | ||
1241 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
1242 | return -1; | ||
1243 | } | ||
1244 | |||
1245 | if (type==FILETYPE_DOT_IPOD) { | ||
1246 | n = read(infile,header,8); | ||
1247 | if (n < 8) { | ||
1248 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
1249 | close(infile); | ||
1250 | return -1; | ||
1251 | } | ||
1252 | |||
1253 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
1254 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
1255 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
1256 | close(infile); | ||
1257 | return -1; | ||
1258 | } | ||
1259 | |||
1260 | filechksum = be2int(header); | ||
1261 | |||
1262 | length = filesize(infile)-8; | ||
1263 | } else { | ||
1264 | length = filesize(infile); | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1268 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
1269 | |||
1270 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
1271 | length,newsize); | ||
1272 | |||
1273 | if (newsize > BUFFER_SIZE) { | ||
1274 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
1275 | if (infile >= 0) close(infile); | ||
1276 | return -1; | ||
1277 | } | ||
1278 | |||
1279 | /* Check if we have enough space */ | ||
1280 | /* TODO: Check the size of the partition. */ | ||
1281 | if (ipod->nimages > 1) { | ||
1282 | bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset; | ||
1283 | if (bytesavailable < newsize) { | ||
1284 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); | ||
1285 | |||
1286 | /* TODO: Implement image movement */ | ||
1287 | fprintf(stderr,"[ERR] Image movement not yet implemented.\n"); | ||
1288 | close(infile); | ||
1289 | return -1; | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | #ifdef WITH_BOOTOBJS | ||
1294 | if (type == FILETYPE_INTERNAL) { | ||
1295 | memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); | ||
1296 | } | ||
1297 | else | ||
1298 | #endif | ||
1299 | { | ||
1300 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
1301 | /* We now know we have enough space, so write it. */ | ||
1302 | n = read(infile,ipod->sectorbuf,length); | ||
1303 | if (n < 0) { | ||
1304 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
1305 | close(infile); | ||
1306 | return -1; | ||
1307 | } | ||
1308 | close(infile); | ||
1309 | } | ||
1310 | |||
1311 | /* Pad the data with zeros */ | ||
1312 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
1313 | |||
1314 | if (type==FILETYPE_DOT_IPOD) { | ||
1315 | chksum = ipod->modelnum; | ||
1316 | for (i = 0; i < length; i++) { | ||
1317 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1318 | chksum += ipod->sectorbuf[i]; | ||
1319 | } | ||
1320 | |||
1321 | if (chksum == filechksum) { | ||
1322 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
1323 | } else { | ||
1324 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
1325 | return -1; | ||
1326 | } | ||
1327 | } | ||
1328 | |||
1329 | |||
1330 | offset = ipod->fwoffset+ipod->ipod_directory[ipod->ososimage].devOffset; | ||
1331 | |||
1332 | if (ipod->modelnum==62) { | ||
1333 | |||
1334 | /* 2nd Gen Nano has encrypted firmware, and the sector | ||
1335 | preceeding the firmware contains hashes that need to be | ||
1336 | preserved. Nano 2G images include these extra 2048 (0x800) | ||
1337 | bytes | ||
1338 | */ | ||
1339 | |||
1340 | offset -= 0x800; | ||
1341 | |||
1342 | /* TODO: The above checks need to take into account this 0x800 bytes */ | ||
1343 | } | ||
1344 | |||
1345 | if (ipod_seek(ipod, offset) < 0) { | ||
1346 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1347 | return -1; | ||
1348 | } | ||
1349 | |||
1350 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
1351 | perror("[ERR] Write failed\n"); | ||
1352 | return -1; | ||
1353 | } | ||
1354 | |||
1355 | if (n < newsize) { | ||
1356 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
1357 | ,newsize,n); | ||
1358 | return -1; | ||
1359 | } | ||
1360 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
1361 | |||
1362 | /* Now we need to update the "len", "entryOffset" and "chksum" fields | ||
1363 | |||
1364 | NOTE: On the Nano 2G, the checksum is the checksum of the | ||
1365 | unencrypted firmware. But this isn't checked by the NOR | ||
1366 | bootloader (there are cryptographic hashes in the | ||
1367 | firmware itself), so it doesn't matter that this is | ||
1368 | wrong. | ||
1369 | */ | ||
1370 | chksum = 0; | ||
1371 | for (i = 0; i < length; i++) { | ||
1372 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1373 | chksum += ipod->sectorbuf[i]; | ||
1374 | } | ||
1375 | |||
1376 | x = ipod->diroffset % ipod->sector_size; | ||
1377 | |||
1378 | /* Read directory */ | ||
1379 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1380 | |||
1381 | n=ipod_read(ipod, ipod->sector_size); | ||
1382 | if (n < 0) { return -1; } | ||
1383 | |||
1384 | /* Update entries for image */ | ||
1385 | p = ipod->sectorbuf + x + (ipod->ososimage * 40); | ||
1386 | int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); | ||
1387 | int2le(0, p + 24); | ||
1388 | int2le(chksum, p + 28); | ||
1389 | |||
1390 | /* Write directory */ | ||
1391 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1392 | n=ipod_write(ipod, ipod->sector_size); | ||
1393 | if (n < 0) { return -1; } | ||
1394 | |||
1395 | return 0; | ||
1396 | } | ||
1397 | |||
1398 | int read_firmware(struct ipod_t* ipod, char* filename, int type) | ||
1399 | { | ||
1400 | int length; | ||
1401 | int i; | ||
1402 | int outfile; | ||
1403 | int n; | ||
1404 | unsigned long offset; | ||
1405 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ | ||
1406 | unsigned char header[8]; /* Header for .ipod file */ | ||
1407 | |||
1408 | if(ipod->sectorbuf == NULL) { | ||
1409 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1410 | return -1; | ||
1411 | } | ||
1412 | if (ipod->ipod_directory[ipod->ososimage].entryOffset != 0) { | ||
1413 | /* We have a bootloader... */ | ||
1414 | length = ipod->ipod_directory[ipod->ososimage].entryOffset; | ||
1415 | } else { | ||
1416 | length = ipod->ipod_directory[ipod->ososimage].len; | ||
1417 | } | ||
1418 | |||
1419 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
1420 | |||
1421 | offset = ipod->fwoffset + ipod->ipod_directory[ipod->ososimage].devOffset; | ||
1422 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
1423 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", | ||
1424 | length,i); | ||
1425 | |||
1426 | if (ipod->modelnum==62) { | ||
1427 | /* 2nd Gen Nano has encrypted firmware, and we need to dump the | ||
1428 | sector preceeding the image - it contains hashes */ | ||
1429 | offset -= 0x800; | ||
1430 | length += 0x800; | ||
1431 | i += 0x800; | ||
1432 | } | ||
1433 | |||
1434 | if (ipod_seek(ipod, offset)) { | ||
1435 | return -1; | ||
1436 | } | ||
1437 | |||
1438 | if ((n = ipod_read(ipod,i)) < 0) { | ||
1439 | return -1; | ||
1440 | } | ||
1441 | |||
1442 | if (n < i) { | ||
1443 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
1444 | i,n); | ||
1445 | return -1; | ||
1446 | } | ||
1447 | |||
1448 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
1449 | if (outfile < 0) { | ||
1450 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
1451 | return -1; | ||
1452 | } | ||
1453 | |||
1454 | if (type == FILETYPE_DOT_IPOD) { | ||
1455 | chksum = ipod->modelnum; | ||
1456 | for (i = 0; i < length; i++) { | ||
1457 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1458 | chksum += ipod->sectorbuf[i]; | ||
1459 | } | ||
1460 | |||
1461 | int2be(chksum,header); | ||
1462 | memcpy(header+4, ipod->modelname,4); | ||
1463 | |||
1464 | n = write(outfile,header,8); | ||
1465 | if (n != 8) { | ||
1466 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1470 | n = write(outfile,ipod->sectorbuf,length); | ||
1471 | if (n != length) { | ||
1472 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
1473 | } | ||
1474 | close(outfile); | ||
1475 | |||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | int read_directory(struct ipod_t* ipod) | ||
1480 | { | ||
1481 | ssize_t n; | ||
1482 | int x; | ||
1483 | unsigned char* p; | ||
1484 | unsigned short version; | ||
1485 | |||
1486 | ipod->nimages=0; | ||
1487 | |||
1488 | /* Read firmware partition header (first 512 bytes of disk - but | ||
1489 | let's read a whole sector) */ | ||
1490 | |||
1491 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
1492 | fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n", | ||
1493 | (unsigned int)(ipod->start)); | ||
1494 | return -1; | ||
1495 | } | ||
1496 | |||
1497 | n=ipod_read(ipod, ipod->sector_size); | ||
1498 | if (n < 0) { | ||
1499 | fprintf(stderr,"[ERR] ipod_read(ipod,0x%08x) failed in read_directory()\n", ipod->sector_size); | ||
1500 | return -1; | ||
1501 | } | ||
1502 | |||
1503 | if (memcmp(ipod->sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) { | ||
1504 | fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n"); | ||
1505 | return -1; | ||
1506 | } | ||
1507 | |||
1508 | if (memcmp(ipod->sectorbuf+0x100,"]ih[",4)!=0) { | ||
1509 | fprintf(stderr,"[ERR] Bad firmware directory\n"); | ||
1510 | return -1; | ||
1511 | } | ||
1512 | |||
1513 | version = le2ushort(ipod->sectorbuf+0x10a); | ||
1514 | if ((version != 2) && (version != 3)) { | ||
1515 | fprintf(stderr,"[ERR] Unknown firmware format version %04x\n", | ||
1516 | version); | ||
1517 | } | ||
1518 | ipod->diroffset=le2int(ipod->sectorbuf+0x104) + 0x200; | ||
1519 | |||
1520 | /* diroffset may not be sector-aligned */ | ||
1521 | x = ipod->diroffset % ipod->sector_size; | ||
1522 | |||
1523 | /* Read directory */ | ||
1524 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1525 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
1526 | return -1; | ||
1527 | } | ||
1528 | |||
1529 | n=ipod_read(ipod, ipod->sector_size); | ||
1530 | if (n < 0) { | ||
1531 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
1532 | return -1; | ||
1533 | } | ||
1534 | |||
1535 | p = ipod->sectorbuf + x; | ||
1536 | |||
1537 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
1538 | if (p[0] == 0) | ||
1539 | { | ||
1540 | /* Adjust diroffset */ | ||
1541 | ipod->diroffset += ipod->sector_size - x; | ||
1542 | |||
1543 | n=ipod_read(ipod, ipod->sector_size); | ||
1544 | if (n < 0) { | ||
1545 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
1546 | return -1; | ||
1547 | } | ||
1548 | p = ipod->sectorbuf; | ||
1549 | } | ||
1550 | |||
1551 | ipod->ososimage = -1; | ||
1552 | while ((ipod->nimages < MAX_IMAGES) && (p < (ipod->sectorbuf + x + 400)) && | ||
1553 | ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) { | ||
1554 | p+=4; | ||
1555 | if (memcmp(p,"soso",4)==0) { | ||
1556 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS; | ||
1557 | ipod->ososimage = ipod->nimages; | ||
1558 | } else if (memcmp(p,"crsr",4)==0) { | ||
1559 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC; | ||
1560 | } else if (memcmp(p,"dpua",4)==0) { | ||
1561 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD; | ||
1562 | } else if (memcmp(p,"kbso",4)==0) { | ||
1563 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSBK; | ||
1564 | } else if (memcmp(p,"ebih",4)==0) { | ||
1565 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE; | ||
1566 | } else { | ||
1567 | fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n", | ||
1568 | p[0],p[1],p[2],p[3]); | ||
1569 | } | ||
1570 | p+=4; | ||
1571 | ipod->ipod_directory[ipod->nimages].id=le2int(p); | ||
1572 | p+=4; | ||
1573 | ipod->ipod_directory[ipod->nimages].devOffset=le2int(p); | ||
1574 | p+=4; | ||
1575 | ipod->ipod_directory[ipod->nimages].len=le2int(p); | ||
1576 | p+=4; | ||
1577 | ipod->ipod_directory[ipod->nimages].addr=le2int(p); | ||
1578 | p+=4; | ||
1579 | ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p); | ||
1580 | p+=4; | ||
1581 | ipod->ipod_directory[ipod->nimages].chksum=le2int(p); | ||
1582 | p+=4; | ||
1583 | ipod->ipod_directory[ipod->nimages].vers=le2int(p); | ||
1584 | p+=4; | ||
1585 | ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p); | ||
1586 | p+=4; | ||
1587 | ipod->nimages++; | ||
1588 | } | ||
1589 | |||
1590 | if (ipod->ososimage < 0) { | ||
1591 | fprintf(stderr,"[ERR] No OSOS image found.\n"); | ||
1592 | return -1; | ||
1593 | } | ||
1594 | |||
1595 | if ((ipod->nimages > 1) && (version==2)) { | ||
1596 | /* The 3g firmware image doesn't appear to have a version, so | ||
1597 | let's make one up... Note that this is never written back to the | ||
1598 | ipod, so it's OK to do. */ | ||
1599 | |||
1600 | if (ipod->ipod_directory[ipod->ososimage].vers == 0) { ipod->ipod_directory[ipod->ososimage].vers = 3; } | ||
1601 | |||
1602 | ipod->fwoffset = ipod->start; | ||
1603 | } else { | ||
1604 | ipod->fwoffset = ipod->start + ipod->sector_size; | ||
1605 | } | ||
1606 | |||
1607 | return 0; | ||
1608 | } | ||
1609 | |||
1610 | int list_images(struct ipod_t* ipod) | ||
1611 | { | ||
1612 | int i; | ||
1613 | |||
1614 | if (ipod_verbose) { | ||
1615 | printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); | ||
1616 | for (i = 0 ; i < ipod->nimages; i++) { | ||
1617 | printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, | ||
1618 | ftypename[ipod->ipod_directory[i].ftype], | ||
1619 | ipod->ipod_directory[i].id, | ||
1620 | ipod->ipod_directory[i].devOffset, | ||
1621 | ipod->ipod_directory[i].len, | ||
1622 | ipod->ipod_directory[i].addr, | ||
1623 | ipod->ipod_directory[i].entryOffset, | ||
1624 | ipod->ipod_directory[i].chksum, | ||
1625 | ipod->ipod_directory[i].vers, | ||
1626 | ipod->ipod_directory[i].loadAddr, | ||
1627 | ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1))); | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | printf("\n"); | ||
1632 | printf("Listing firmware partition contents:\n"); | ||
1633 | printf("\n"); | ||
1634 | |||
1635 | for (i = 0 ; i < ipod->nimages; i++) { | ||
1636 | printf("Image %d:\n",i+1); | ||
1637 | switch(ipod->ipod_directory[i].ftype) { | ||
1638 | case FTYPE_OSOS: | ||
1639 | if (ipod->ipod_directory[i].entryOffset==0) { | ||
1640 | printf(" Main firmware - %d bytes\n", | ||
1641 | ipod->ipod_directory[i].len); | ||
1642 | } else { | ||
1643 | printf(" Main firmware - %d bytes\n", | ||
1644 | ipod->ipod_directory[i].entryOffset); | ||
1645 | printf(" Third-party bootloader - %d bytes\n", | ||
1646 | ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset); | ||
1647 | } | ||
1648 | break; | ||
1649 | default: | ||
1650 | printf(" %s - %d bytes\n", | ||
1651 | ftypename[ipod->ipod_directory[i].ftype], | ||
1652 | ipod->ipod_directory[i].len); | ||
1653 | } | ||
1654 | } | ||
1655 | printf("\n"); | ||
1656 | |||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | int getmodel(struct ipod_t* ipod, int ipod_version) | ||
1661 | { | ||
1662 | switch (ipod_version) { | ||
1663 | case 0x01: | ||
1664 | ipod->modelstr="1st or 2nd Generation"; | ||
1665 | ipod->modelnum = 19; | ||
1666 | ipod->modelname = "1g2g"; | ||
1667 | ipod->targetname = "ipod1g2g"; | ||
1668 | #ifdef WITH_BOOTOBJS | ||
1669 | ipod->bootloader = ipod1g2g; | ||
1670 | ipod->bootloader_len = LEN_ipod1g2g; | ||
1671 | #endif | ||
1672 | break; | ||
1673 | case 0x02: | ||
1674 | ipod->modelstr="3rd Generation"; | ||
1675 | ipod->modelnum = 7; | ||
1676 | ipod->modelname = "ip3g"; | ||
1677 | ipod->targetname = "ipod3g"; | ||
1678 | #ifdef WITH_BOOTOBJS | ||
1679 | ipod->bootloader = ipod3g; | ||
1680 | ipod->bootloader_len = LEN_ipod3g; | ||
1681 | #endif | ||
1682 | break; | ||
1683 | case 0x40: | ||
1684 | ipod->modelstr="1st Generation Mini"; | ||
1685 | ipod->modelnum = 9; | ||
1686 | ipod->modelname = "mini"; | ||
1687 | ipod->targetname = "ipodmini1g"; | ||
1688 | #ifdef WITH_BOOTOBJS | ||
1689 | ipod->bootloader = ipodmini1g; | ||
1690 | ipod->bootloader_len = LEN_ipodmini1g; | ||
1691 | #endif | ||
1692 | break; | ||
1693 | case 0x50: | ||
1694 | ipod->modelstr="4th Generation"; | ||
1695 | ipod->modelnum = 8; | ||
1696 | ipod->modelname = "ip4g"; | ||
1697 | ipod->targetname = "ipod4gray"; | ||
1698 | #ifdef WITH_BOOTOBJS | ||
1699 | ipod->bootloader = ipod4g; | ||
1700 | ipod->bootloader_len = LEN_ipod4g; | ||
1701 | #endif | ||
1702 | break; | ||
1703 | case 0x60: | ||
1704 | ipod->modelstr="Photo/Color"; | ||
1705 | ipod->modelnum = 3; | ||
1706 | ipod->modelname = "ipco"; | ||
1707 | ipod->targetname = "ipodcolor"; | ||
1708 | #ifdef WITH_BOOTOBJS | ||
1709 | ipod->bootloader = ipodcolor; | ||
1710 | ipod->bootloader_len = LEN_ipodcolor; | ||
1711 | #endif | ||
1712 | break; | ||
1713 | case 0x70: | ||
1714 | ipod->modelstr="2nd Generation Mini"; | ||
1715 | ipod->modelnum = 11; | ||
1716 | ipod->modelname = "mn2g"; | ||
1717 | ipod->targetname = "ipodmini2g"; | ||
1718 | #ifdef WITH_BOOTOBJS | ||
1719 | ipod->bootloader = ipodmini2g; | ||
1720 | ipod->bootloader_len = LEN_ipodmini2g; | ||
1721 | #endif | ||
1722 | break; | ||
1723 | case 0xc0: | ||
1724 | ipod->modelstr="1st Generation Nano"; | ||
1725 | ipod->modelnum = 4; | ||
1726 | ipod->modelname = "nano"; | ||
1727 | ipod->targetname = "ipodnano1g"; | ||
1728 | #ifdef WITH_BOOTOBJS | ||
1729 | ipod->bootloader = ipodnano1g; | ||
1730 | ipod->bootloader_len = LEN_ipodnano1g; | ||
1731 | #endif | ||
1732 | break; | ||
1733 | case 0xb0: | ||
1734 | ipod->modelstr="Video (aka 5th Generation)"; | ||
1735 | ipod->modelnum = 5; | ||
1736 | ipod->modelname = "ipvd"; | ||
1737 | ipod->targetname = "ipodvideo"; | ||
1738 | #ifdef WITH_BOOTOBJS | ||
1739 | ipod->bootloader = ipodvideo; | ||
1740 | ipod->bootloader_len = LEN_ipodvideo; | ||
1741 | #endif | ||
1742 | break; | ||
1743 | case 0x100: | ||
1744 | ipod->modelstr="2nd Generation Nano"; | ||
1745 | ipod->modelnum = 62; | ||
1746 | ipod->modelname = "nn2x"; | ||
1747 | ipod->targetname = "ipodnano2g"; | ||
1748 | #ifdef WITH_BOOTOBJS | ||
1749 | ipod->bootloader = ipodnano2g; | ||
1750 | ipod->bootloader_len = LEN_ipodnano2g; | ||
1751 | #endif | ||
1752 | break; | ||
1753 | default: | ||
1754 | ipod->modelname = NULL; | ||
1755 | ipod->modelnum = 0; | ||
1756 | ipod->targetname = NULL; | ||
1757 | #ifdef WITH_BOOTOBJS | ||
1758 | ipod->bootloader = NULL; | ||
1759 | ipod->bootloader_len = 0; | ||
1760 | #endif | ||
1761 | return -1; | ||
1762 | } | ||
1763 | return 0; | ||
1764 | } | ||
1765 | |||
1766 | /* returns number of found ipods or -1 if no ipods found and permission | ||
1767 | * for raw disc access was denied. */ | ||
1768 | int ipod_scan(struct ipod_t* ipod) | ||
1769 | { | ||
1770 | int i; | ||
1771 | int n = 0; | ||
1772 | int ipod_version; | ||
1773 | struct ipod_t ipod_found; | ||
1774 | int denied = 0; | ||
1775 | int result; | ||
1776 | |||
1777 | printf("[INFO] Scanning disk devices...\n"); | ||
1778 | |||
1779 | for (i = 0; i <= 25 ; i++) { | ||
1780 | #ifdef __WIN32__ | ||
1781 | sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i); | ||
1782 | #elif defined(linux) || defined (__linux) | ||
1783 | sprintf(ipod->diskname,"/dev/sd%c",'a'+i); | ||
1784 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
1785 | || defined(__bsdi__) || defined(__DragonFly__) | ||
1786 | sprintf(ipod->diskname,"/dev/da%d",i); | ||
1787 | #elif defined(__APPLE__) && defined(__MACH__) | ||
1788 | sprintf(ipod->diskname,"/dev/disk%d",i); | ||
1789 | #else | ||
1790 | #error No disk paths defined for this platform | ||
1791 | #endif | ||
1792 | if ((result = ipod_open(ipod, 1)) < 0) { | ||
1793 | if(result == -2) { | ||
1794 | denied++; | ||
1795 | } | ||
1796 | ipod_close(ipod); | ||
1797 | continue; | ||
1798 | } | ||
1799 | |||
1800 | if (read_partinfo(ipod,1) < 0) { | ||
1801 | ipod_close(ipod); | ||
1802 | continue; | ||
1803 | } | ||
1804 | |||
1805 | if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) { | ||
1806 | ipod_close(ipod); | ||
1807 | continue; | ||
1808 | } | ||
1809 | |||
1810 | if (read_directory(ipod) < 0) { | ||
1811 | ipod_close(ipod); | ||
1812 | continue; | ||
1813 | } | ||
1814 | |||
1815 | ipod_version=(ipod->ipod_directory[ipod->ososimage].vers>>8); | ||
1816 | ipod->ramsize = 0; | ||
1817 | #ifdef __WIN32__ | ||
1818 | /* Windows requires the ipod in R/W mode for SCSI Inquiry. | ||
1819 | * ipod_reopen_rw does unmount the player on OS X so do this on | ||
1820 | * W32 only during scanning. */ | ||
1821 | ipod_reopen_rw(ipod); | ||
1822 | #endif | ||
1823 | ipod_get_xmlinfo(ipod); | ||
1824 | ipod_get_ramsize(ipod); | ||
1825 | if (getmodel(ipod,ipod_version) < 0) { | ||
1826 | ipod_close(ipod); | ||
1827 | continue; | ||
1828 | } | ||
1829 | |||
1830 | #ifdef __WIN32__ | ||
1831 | printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n", | ||
1832 | ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i); | ||
1833 | #else | ||
1834 | printf("[INFO] Ipod found - %s (\"%s\") - %s\n", | ||
1835 | ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname); | ||
1836 | #endif | ||
1837 | n++; | ||
1838 | /* save the complete ipod_t structure for match. The for loop might | ||
1839 | * overwrite it, so we need to restore it later if only one found. */ | ||
1840 | memcpy(&ipod_found, ipod, sizeof(struct ipod_t)); | ||
1841 | ipod_close(ipod); | ||
1842 | } | ||
1843 | |||
1844 | if (n==1) { | ||
1845 | /* restore the ipod_t structure, it might have been overwritten */ | ||
1846 | memcpy(ipod, &ipod_found, sizeof(struct ipod_t)); | ||
1847 | } | ||
1848 | else if(n == 0 && denied) { | ||
1849 | printf("[ERR] FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied); | ||
1850 | #ifdef __WIN32__ | ||
1851 | printf("[ERR] You need to run this program with administrator priviledges!\n"); | ||
1852 | #else | ||
1853 | printf("[ERR] You need permissions for raw disc access for this program to work!\n"); | ||
1854 | #endif | ||
1855 | } | ||
1856 | return (n == 0 && denied) ? -1 : n; | ||
1857 | } | ||
1858 | |||
1859 | static void put_int32le(uint32_t x, unsigned char* p) | ||
1860 | { | ||
1861 | p[0] = x & 0xff; | ||
1862 | p[1] = (x >> 8) & 0xff; | ||
1863 | p[2] = (x >> 16) & 0xff; | ||
1864 | p[3] = (x >> 24) & 0xff; | ||
1865 | } | ||
1866 | |||
1867 | int write_dos_partition_table(struct ipod_t* ipod) | ||
1868 | { | ||
1869 | unsigned char* p; | ||
1870 | int i, n; | ||
1871 | uint32_t type; | ||
1872 | |||
1873 | /* Only support 512-byte sectors at the moment */ | ||
1874 | if ( ipod->sector_size != 512 ) | ||
1875 | { | ||
1876 | fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n"); | ||
1877 | return -1; | ||
1878 | } | ||
1879 | if(ipod->sectorbuf == NULL) { | ||
1880 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1881 | return -1; | ||
1882 | } | ||
1883 | |||
1884 | /* Firstly zero the entire MBR */ | ||
1885 | memset(ipod->sectorbuf, 0, ipod->sector_size); | ||
1886 | |||
1887 | /* Now add the partition info */ | ||
1888 | for (i=0; i < 4 ; i++) | ||
1889 | { | ||
1890 | p = ipod->sectorbuf + 0x1be + i*16; | ||
1891 | |||
1892 | /* Ensure first partition is type 0, and second is 0xb */ | ||
1893 | if (i==0) { type = 0; } | ||
1894 | else if (i==1) { type = 0xb; } | ||
1895 | else { type = ipod->pinfo[i].type; } | ||
1896 | |||
1897 | put_int32le(type, p + 4); | ||
1898 | put_int32le(ipod->pinfo[i].start, p + 8); | ||
1899 | put_int32le(ipod->pinfo[i].size, p + 12); | ||
1900 | } | ||
1901 | |||
1902 | /* Finally add the magic */ | ||
1903 | ipod->sectorbuf[0x1fe] = 0x55; | ||
1904 | ipod->sectorbuf[0x1ff] = 0xaa; | ||
1905 | |||
1906 | if (ipod_seek(ipod, 0) < 0) { | ||
1907 | fprintf(stderr,"[ERR] Seek failed writing MBR\n"); | ||
1908 | return -1; | ||
1909 | } | ||
1910 | |||
1911 | /* Write MBR */ | ||
1912 | if ((n = ipod_write(ipod, ipod->sector_size)) < 0) { | ||
1913 | perror("[ERR] Write failed\n"); | ||
1914 | return -1; | ||
1915 | } | ||
1916 | |||
1917 | return 0; | ||
1918 | } | ||
1919 | |||
1920 | /* Get the XML Device Information, as documented here: | ||
1921 | |||
1922 | http://www.ipodlinux.org/wiki/Device_Information | ||
1923 | */ | ||
1924 | |||
1925 | int ipod_get_xmlinfo(struct ipod_t* ipod) | ||
1926 | { | ||
1927 | unsigned char hdr[255]; | ||
1928 | unsigned char buf[255]; | ||
1929 | char* p; | ||
1930 | int psize; | ||
1931 | int npages; | ||
1932 | int i; | ||
1933 | |||
1934 | if (ipod_scsi_inquiry(ipod, 0xc0, buf, sizeof(buf)) < 0) | ||
1935 | { | ||
1936 | fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); | ||
1937 | return -1; | ||
1938 | } | ||
1939 | |||
1940 | /* Reading directly into hdr[] causes problems (for an unknown reason) on | ||
1941 | win32 */ | ||
1942 | memcpy(hdr, buf, sizeof(hdr)); | ||
1943 | |||
1944 | npages = hdr[3]; | ||
1945 | |||
1946 | psize = npages * 0xf8; /* Hopefully this is enough. */ | ||
1947 | |||
1948 | ipod->xmlinfo = malloc(psize); | ||
1949 | ipod->xmlinfo_len = 0; | ||
1950 | |||
1951 | if (ipod->xmlinfo == NULL) { | ||
1952 | fprintf(stderr,"[ERR] Could not allocate RAM for xmlinfo\n"); | ||
1953 | return -1; | ||
1954 | } | ||
1955 | |||
1956 | p = ipod->xmlinfo; | ||
1957 | |||
1958 | for (i=0; i < npages; i++) { | ||
1959 | if (ipod_scsi_inquiry(ipod, hdr[i+4], buf, sizeof(buf)) < 0) { | ||
1960 | fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); | ||
1961 | return -1; | ||
1962 | } | ||
1963 | |||
1964 | if ((buf[3] + ipod->xmlinfo_len) > psize) { | ||
1965 | fprintf(stderr,"[ERR] Ran out of memory reading xmlinfo\n"); | ||
1966 | free(ipod->xmlinfo); | ||
1967 | ipod->xmlinfo = NULL; | ||
1968 | ipod->xmlinfo_len = 0; | ||
1969 | return -1; | ||
1970 | } | ||
1971 | |||
1972 | memcpy(p, buf + 4, buf[3]); | ||
1973 | p += buf[3]; | ||
1974 | ipod->xmlinfo_len += buf[3]; | ||
1975 | } | ||
1976 | |||
1977 | /* NULL-terminate the XML info */ | ||
1978 | *p = 0; | ||
1979 | |||
1980 | fprintf(stderr,"[INFO] Read XML info (%d bytes)\n",ipod->xmlinfo_len); | ||
1981 | |||
1982 | return 0; | ||
1983 | } | ||
1984 | |||
1985 | void ipod_get_ramsize(struct ipod_t* ipod) | ||
1986 | { | ||
1987 | const char needle[] = "<key>RAM</key>\n<integer>"; | ||
1988 | char* p; | ||
1989 | |||
1990 | if (ipod->xmlinfo == NULL) | ||
1991 | return; | ||
1992 | |||
1993 | p = strstr(ipod->xmlinfo, needle); | ||
1994 | |||
1995 | if (p) { | ||
1996 | ipod->ramsize = atoi(p + sizeof(needle) - 1); | ||
1997 | } | ||
1998 | } | ||
1999 | |||
2000 | #ifndef RBUTIL | ||
2001 | |||
2002 | static inline uint32_t getuint32le(unsigned char* buf) | ||
2003 | { | ||
2004 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
2005 | |||
2006 | return res; | ||
2007 | } | ||
2008 | |||
2009 | /* testMarker and GetSecurityBlockKey based on code from BadBlocks and | ||
2010 | Kingstone, posted at http://ipodlinux.org/Flash_Decryption | ||
2011 | |||
2012 | */ | ||
2013 | |||
2014 | static bool testMarker(int marker) | ||
2015 | { | ||
2016 | int mask, decrypt, temp1, temp2; | ||
2017 | |||
2018 | mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24); | ||
2019 | decrypt = marker ^ mask; | ||
2020 | temp1=(int)((unsigned int)decrypt>>24); | ||
2021 | temp2=decrypt<<8; | ||
2022 | |||
2023 | if (temp1==0) | ||
2024 | return false; | ||
2025 | |||
2026 | temp2=(int)((unsigned int)temp2>>24); | ||
2027 | decrypt=decrypt<<16; | ||
2028 | decrypt=(int)((unsigned int)decrypt>>24); | ||
2029 | |||
2030 | if ((temp1 < temp2) && (temp2 < decrypt)) | ||
2031 | { | ||
2032 | temp1 = temp1 & 0xf; | ||
2033 | temp2 = temp2 & 0xf; | ||
2034 | decrypt = decrypt & 0xf; | ||
2035 | |||
2036 | if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0)) | ||
2037 | { | ||
2038 | return true; | ||
2039 | } | ||
2040 | } | ||
2041 | return false; | ||
2042 | } | ||
2043 | |||
2044 | static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key) | ||
2045 | { | ||
2046 | int constant = 0x54c3a298; | ||
2047 | int key=0; | ||
2048 | int nkeys = 0; | ||
2049 | int aMarker=0; | ||
2050 | int pos=0; | ||
2051 | int c, count; | ||
2052 | int temp1; | ||
2053 | static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34}; | ||
2054 | |||
2055 | for (c = 0; c < 8; c++) | ||
2056 | { | ||
2057 | pos = offset[c]*4; | ||
2058 | aMarker = getuint32le(data + pos); | ||
2059 | |||
2060 | if (testMarker(aMarker)) | ||
2061 | { | ||
2062 | if (c<7) | ||
2063 | pos =(offset[c+1]*4)+4; | ||
2064 | else | ||
2065 | pos =(offset[0]*4)+4; | ||
2066 | |||
2067 | key=0; | ||
2068 | |||
2069 | temp1=aMarker; | ||
2070 | |||
2071 | for (count=0;count<2;count++){ | ||
2072 | int word = getuint32le(data + pos); | ||
2073 | temp1 = aMarker; | ||
2074 | temp1 = temp1^word; | ||
2075 | temp1 = temp1^constant; | ||
2076 | key = temp1; | ||
2077 | pos = pos+4; | ||
2078 | } | ||
2079 | int r1=0x6f; | ||
2080 | int r2=0; | ||
2081 | int r12; | ||
2082 | int r14; | ||
2083 | unsigned int r_tmp; | ||
2084 | |||
2085 | for (count=2;count<128;count=count+2){ | ||
2086 | r2=getuint32le(data+count*4); | ||
2087 | r12=getuint32le(data+(count*4)+4); | ||
2088 | r_tmp=(unsigned int)r12>>16; | ||
2089 | r14=r2 | ((int)r_tmp); | ||
2090 | r2=r2&0xffff; | ||
2091 | r2=r2 | r12; | ||
2092 | r1=r1^r14; | ||
2093 | r1=r1+r2; | ||
2094 | } | ||
2095 | key=key^r1; | ||
2096 | |||
2097 | // Invert key, little endian | ||
2098 | this_key[0] = key & 0xff; | ||
2099 | this_key[1] = (key >> 8) & 0xff; | ||
2100 | this_key[2] = (key >> 16) & 0xff; | ||
2101 | this_key[3] = (key >> 24) & 0xff; | ||
2102 | nkeys++; | ||
2103 | } | ||
2104 | } | ||
2105 | return nkeys; | ||
2106 | } | ||
2107 | |||
2108 | static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key) | ||
2109 | { | ||
2110 | int n; | ||
2111 | |||
2112 | /* Firstly read the security block and find the RC4 key. This is | ||
2113 | in the sector preceeding the AUPD image. */ | ||
2114 | |||
2115 | if(ipod->sectorbuf == NULL) { | ||
2116 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2117 | return -1; | ||
2118 | } | ||
2119 | fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size); | ||
2120 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) { | ||
2121 | return -1; | ||
2122 | } | ||
2123 | |||
2124 | if ((n = ipod_read(ipod, 512)) < 0) { | ||
2125 | return -1; | ||
2126 | } | ||
2127 | |||
2128 | n = GetSecurityBlockKey(ipod->sectorbuf, key); | ||
2129 | |||
2130 | if (n != 1) | ||
2131 | { | ||
2132 | fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n); | ||
2133 | return -1; | ||
2134 | } | ||
2135 | |||
2136 | return 0; | ||
2137 | } | ||
2138 | |||
2139 | int read_aupd(struct ipod_t* ipod, char* filename) | ||
2140 | { | ||
2141 | int length; | ||
2142 | int i; | ||
2143 | int outfile; | ||
2144 | int n; | ||
2145 | int aupd; | ||
2146 | struct rc4_key_t rc4; | ||
2147 | unsigned char key[4]; | ||
2148 | unsigned long chksum=0; | ||
2149 | |||
2150 | if(ipod->sectorbuf == NULL) { | ||
2151 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2152 | return -1; | ||
2153 | } | ||
2154 | aupd = 0; | ||
2155 | while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) | ||
2156 | { | ||
2157 | aupd++; | ||
2158 | } | ||
2159 | |||
2160 | if (aupd == ipod->nimages) | ||
2161 | { | ||
2162 | fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); | ||
2163 | return -1; | ||
2164 | } | ||
2165 | |||
2166 | length = ipod->ipod_directory[aupd].len; | ||
2167 | |||
2168 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
2169 | |||
2170 | if (find_key(ipod, aupd, key) < 0) | ||
2171 | { | ||
2172 | return -1; | ||
2173 | } | ||
2174 | |||
2175 | fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); | ||
2176 | |||
2177 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { | ||
2178 | return -1; | ||
2179 | } | ||
2180 | |||
2181 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
2182 | |||
2183 | if ((n = ipod_read(ipod,i)) < 0) { | ||
2184 | return -1; | ||
2185 | } | ||
2186 | |||
2187 | if (n < i) { | ||
2188 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
2189 | i,n); | ||
2190 | return -1; | ||
2191 | } | ||
2192 | |||
2193 | /* Perform the decryption - this is standard (A)RC4 */ | ||
2194 | matrixArc4Init(&rc4, key, 4); | ||
2195 | matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); | ||
2196 | |||
2197 | chksum = 0; | ||
2198 | for (i = 0; i < (int)length; i++) { | ||
2199 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
2200 | chksum += ipod->sectorbuf[i]; | ||
2201 | } | ||
2202 | |||
2203 | if (chksum != ipod->ipod_directory[aupd].chksum) | ||
2204 | { | ||
2205 | fprintf(stderr,"[ERR] Decryption failed - checksum error\n"); | ||
2206 | return -1; | ||
2207 | } | ||
2208 | fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n"); | ||
2209 | |||
2210 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
2211 | if (outfile < 0) { | ||
2212 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
2213 | return -1; | ||
2214 | } | ||
2215 | |||
2216 | n = write(outfile,ipod->sectorbuf,length); | ||
2217 | if (n != length) { | ||
2218 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
2219 | } | ||
2220 | close(outfile); | ||
2221 | |||
2222 | return 0; | ||
2223 | } | ||
2224 | |||
2225 | int write_aupd(struct ipod_t* ipod, char* filename) | ||
2226 | { | ||
2227 | unsigned int length; | ||
2228 | int i; | ||
2229 | int x; | ||
2230 | int n; | ||
2231 | int infile; | ||
2232 | int newsize; | ||
2233 | int aupd; | ||
2234 | unsigned long chksum=0; | ||
2235 | struct rc4_key_t rc4; | ||
2236 | unsigned char key[4]; | ||
2237 | |||
2238 | if(ipod->sectorbuf == NULL) { | ||
2239 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2240 | return -1; | ||
2241 | } | ||
2242 | /* First check that the input file is the correct type for this ipod. */ | ||
2243 | infile=open(filename,O_RDONLY); | ||
2244 | if (infile < 0) { | ||
2245 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
2246 | return -1; | ||
2247 | } | ||
2248 | |||
2249 | length = filesize(infile); | ||
2250 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
2251 | |||
2252 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
2253 | length,newsize); | ||
2254 | |||
2255 | if (newsize > BUFFER_SIZE) { | ||
2256 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
2257 | if (infile >= 0) close(infile); | ||
2258 | return -1; | ||
2259 | } | ||
2260 | |||
2261 | /* Find aupd image number */ | ||
2262 | aupd = 0; | ||
2263 | while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) | ||
2264 | { | ||
2265 | aupd++; | ||
2266 | } | ||
2267 | |||
2268 | if (aupd == ipod->nimages) | ||
2269 | { | ||
2270 | fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); | ||
2271 | return -1; | ||
2272 | } | ||
2273 | |||
2274 | if (length != ipod->ipod_directory[aupd].len) | ||
2275 | { | ||
2276 | fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n", | ||
2277 | ipod->ipod_directory[aupd].len, filename, length); | ||
2278 | return -1; | ||
2279 | } | ||
2280 | |||
2281 | if (find_key(ipod, aupd, key) < 0) | ||
2282 | { | ||
2283 | return -1; | ||
2284 | } | ||
2285 | |||
2286 | fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); | ||
2287 | |||
2288 | /* We now know we have enough space, so write it. */ | ||
2289 | |||
2290 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
2291 | n = read(infile,ipod->sectorbuf,length); | ||
2292 | if (n < 0) { | ||
2293 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
2294 | close(infile); | ||
2295 | return -1; | ||
2296 | } | ||
2297 | close(infile); | ||
2298 | |||
2299 | /* Pad the data with zeros */ | ||
2300 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
2301 | |||
2302 | /* Calculate the new checksum (before we encrypt) */ | ||
2303 | chksum = 0; | ||
2304 | for (i = 0; i < (int)length; i++) { | ||
2305 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
2306 | chksum += ipod->sectorbuf[i]; | ||
2307 | } | ||
2308 | |||
2309 | /* Perform the encryption - this is standard (A)RC4 */ | ||
2310 | matrixArc4Init(&rc4, key, 4); | ||
2311 | matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); | ||
2312 | |||
2313 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { | ||
2314 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
2315 | return -1; | ||
2316 | } | ||
2317 | |||
2318 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
2319 | perror("[ERR] Write failed\n"); | ||
2320 | return -1; | ||
2321 | } | ||
2322 | |||
2323 | if (n < newsize) { | ||
2324 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
2325 | ,newsize,n); | ||
2326 | return -1; | ||
2327 | } | ||
2328 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
2329 | |||
2330 | x = ipod->diroffset % ipod->sector_size; | ||
2331 | |||
2332 | /* Read directory */ | ||
2333 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
2334 | |||
2335 | n=ipod_read(ipod, ipod->sector_size); | ||
2336 | if (n < 0) { return -1; } | ||
2337 | |||
2338 | /* Update checksum */ | ||
2339 | fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(ipod->sectorbuf + x + aupd*40 + 28)); | ||
2340 | int2le(chksum,ipod->sectorbuf+x+aupd*40+28); | ||
2341 | |||
2342 | /* Write directory */ | ||
2343 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
2344 | n=ipod_write(ipod, ipod->sector_size); | ||
2345 | if (n < 0) { return -1; } | ||
2346 | |||
2347 | return 0; | ||
2348 | } | ||
2349 | |||
2350 | #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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef IPODPATCHER_H | ||
23 | #define IPODPATCHER_H | ||
24 | |||
25 | #ifdef __cplusplus | ||
26 | extern "C" { | ||
27 | #endif | ||
28 | |||
29 | #include "ipodio.h" | ||
30 | |||
31 | /* exit codes */ | ||
32 | #define IPOD_OK 0 | ||
33 | #define IPOD_WRONG_ARGUMENTS 1 | ||
34 | #define IPOD_OPEN_INFILE_FAILED 2 | ||
35 | #define IPOD_PARTITION_ERROR 3 | ||
36 | #define IPOD_OPEN_OUTFILE_FAILED 4 | ||
37 | #define IPOD_CANNOT_REOPEN 5 | ||
38 | #define IPOD_ACCESS_DENIED 10 | ||
39 | #define IPOD_NOT_FOUND 11 | ||
40 | #define IPOD_WRONG_DEVICE_COUNT 12 | ||
41 | #define IPOD_IMAGE_ERROR 13 | ||
42 | #define IPOD_DUMP_FAILED 14 | ||
43 | #define IPOD_MULTIPLE_DEVICES 15 | ||
44 | #define IPOD_WRONG_TYPE 16 | ||
45 | #define IPOD_UNKNOWN_FW_VERSION -1 | ||
46 | |||
47 | /* Size of buffer for disk I/O - 8MB is large enough for any version | ||
48 | of the Apple firmware, but not the Nano's RSRC image. */ | ||
49 | #define BUFFER_SIZE 8*1024*1024 | ||
50 | |||
51 | extern int ipod_verbose; | ||
52 | |||
53 | #define FILETYPE_DOT_IPOD 0 | ||
54 | #define FILETYPE_DOT_BIN 1 | ||
55 | #ifdef WITH_BOOTOBJS | ||
56 | #define FILETYPE_INTERNAL 2 | ||
57 | #endif | ||
58 | |||
59 | char* get_parttype(unsigned int pt); | ||
60 | int read_partinfo(struct ipod_t* ipod, int silent); | ||
61 | int read_partition(struct ipod_t* ipod, int outfile); | ||
62 | int write_partition(struct ipod_t* ipod, int infile); | ||
63 | int diskmove(struct ipod_t* ipod, int delta); | ||
64 | int add_bootloader(struct ipod_t* ipod, char* filename, int type); | ||
65 | int delete_bootloader(struct ipod_t* ipod); | ||
66 | int write_firmware(struct ipod_t* ipod, char* filename, int type); | ||
67 | int read_firmware(struct ipod_t* ipod, char* filename, int type); | ||
68 | int read_directory(struct ipod_t* ipod); | ||
69 | int list_images(struct ipod_t* ipod); | ||
70 | int getmodel(struct ipod_t* ipod, int ipod_version); | ||
71 | int ipod_scan(struct ipod_t* ipod); | ||
72 | int write_dos_partition_table(struct ipod_t* ipod); | ||
73 | int ipod_get_xmlinfo(struct ipod_t* ipod); | ||
74 | void ipod_get_ramsize(struct ipod_t* ipod); | ||
75 | int read_aupd(struct ipod_t* ipod, char* filename); | ||
76 | int write_aupd(struct ipod_t* ipod, char* filename); | ||
77 | off_t filesize(int fd); | ||
78 | int ipod_has_bootloader(struct ipod_t* ipod); | ||
79 | |||
80 | #ifdef __cplusplus | ||
81 | } | ||
82 | #endif | ||
83 | #endif | ||
84 | |||
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 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
3 | <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="ipodpatcher.exe" type="win32"/> | ||
4 | |||
5 | <!-- Identify the application security requirements. --> | ||
6 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
7 | <security> | ||
8 | <requestedPrivileges> | ||
9 | <requestedExecutionLevel level="requireAdministrator"/> | ||
10 | </requestedPrivileges> | ||
11 | </security> | ||
12 | </trustInfo> | ||
13 | </assembly> | ||
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 @@ | |||
1 | # | ||
2 | # __________ __ ___. | ||
3 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | # \/ \/ \/ \/ \/ | ||
8 | # | ||
9 | # All files in this archive are subject to the GNU General Public License. | ||
10 | # See the file COPYING in the source tree root for full license agreement. | ||
11 | # | ||
12 | # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
13 | # KIND, either express or implied. | ||
14 | # | ||
15 | |||
16 | TEMPLATE = app | ||
17 | TARGET = ipodpatcher | ||
18 | QT -= core | ||
19 | |||
20 | SOURCES += \ | ||
21 | main.c \ | ||
22 | ipodpatcher.c \ | ||
23 | ipodio-posix.c \ | ||
24 | ipodio-win32-scsi.c \ | ||
25 | ipodio-win32.c \ | ||
26 | fat32format.c \ | ||
27 | arc4.c \ | ||
28 | |||
29 | HEADERS += \ | ||
30 | arc4.h \ | ||
31 | ipodio.h \ | ||
32 | ipodpatcher.h \ | ||
33 | parttypes.h \ | ||
34 | |||
35 | DEFINES += RELEASE=1 _LARGEFILE64_SOURCE | ||
36 | |||
37 | RC_FILE = ipodpatcher.rc | ||
38 | |||
39 | macx { | ||
40 | LIBS += -framework CoreFoundation -framework IOKit | ||
41 | } | ||
42 | |||
43 | |||
44 | unix { | ||
45 | target.path = /usr/local/bin | ||
46 | INSTALLS += target | ||
47 | } | ||
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 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <unistd.h> | ||
24 | #include <fcntl.h> | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <inttypes.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | |||
31 | #include "ipodpatcher.h" | ||
32 | #include "ipodio.h" | ||
33 | |||
34 | #ifdef RELEASE | ||
35 | #undef VERSION | ||
36 | #define VERSION "5.0 with v4.0 bootloaders (v1.0 for 2nd Gen Nano)" | ||
37 | #endif | ||
38 | |||
39 | |||
40 | enum { | ||
41 | NONE, | ||
42 | #ifdef WITH_BOOTOBJS | ||
43 | INSTALL, | ||
44 | #endif | ||
45 | INTERACTIVE, | ||
46 | SHOW_INFO, | ||
47 | LIST_IMAGES, | ||
48 | DELETE_BOOTLOADER, | ||
49 | ADD_BOOTLOADER, | ||
50 | READ_FIRMWARE, | ||
51 | WRITE_FIRMWARE, | ||
52 | READ_AUPD, | ||
53 | WRITE_AUPD, | ||
54 | READ_PARTITION, | ||
55 | WRITE_PARTITION, | ||
56 | FORMAT_PARTITION, | ||
57 | DUMP_XML, | ||
58 | CONVERT_TO_FAT32 | ||
59 | }; | ||
60 | |||
61 | void print_macpod_warning(void) | ||
62 | { | ||
63 | printf("[INFO] ************************************************************************\n"); | ||
64 | printf("[INFO] *** WARNING FOR ROCKBOX USERS\n"); | ||
65 | printf("[INFO] *** You must convert this ipod to FAT32 format (aka a \"winpod\")\n"); | ||
66 | printf("[INFO] *** if you want to run Rockbox. Rockbox WILL NOT work on this ipod.\n"); | ||
67 | printf("[INFO] *** See http://www.rockbox.org/wiki/IpodConversionToFAT32\n"); | ||
68 | printf("[INFO] ************************************************************************\n"); | ||
69 | } | ||
70 | |||
71 | void print_usage(void) | ||
72 | { | ||
73 | fprintf(stderr,"Usage: ipodpatcher --scan\n"); | ||
74 | #ifdef __WIN32__ | ||
75 | fprintf(stderr," or ipodpatcher [DISKNO] [action]\n"); | ||
76 | #else | ||
77 | fprintf(stderr," or ipodpatcher [device] [action]\n"); | ||
78 | #endif | ||
79 | fprintf(stderr,"\n"); | ||
80 | fprintf(stderr,"Where [action] is one of the following options:\n"); | ||
81 | #ifdef WITH_BOOTOBJS | ||
82 | fprintf(stderr," --install\n"); | ||
83 | #endif | ||
84 | fprintf(stderr," -l, --list\n"); | ||
85 | fprintf(stderr," -r, --read-partition bootpartition.bin\n"); | ||
86 | fprintf(stderr," -w, --write-partition bootpartition.bin\n"); | ||
87 | fprintf(stderr," -rf, --read-firmware filename.ipod[x]\n"); | ||
88 | fprintf(stderr," -rfb, --read-firmware-bin filename.bin\n"); | ||
89 | fprintf(stderr," -wf, --write-firmware filename.ipod[x]\n"); | ||
90 | fprintf(stderr," -wfb, --write-firmware-bin filename.bin\n"); | ||
91 | #ifdef WITH_BOOTOBJS | ||
92 | fprintf(stderr," -we, --write-embedded\n"); | ||
93 | #endif | ||
94 | fprintf(stderr," -a, --add-bootloader filename.ipod[x]\n"); | ||
95 | fprintf(stderr," -ab, --add-bootloader-bin filename.bin\n"); | ||
96 | fprintf(stderr," -d, --delete-bootloader\n"); | ||
97 | fprintf(stderr," -f, --format\n"); | ||
98 | fprintf(stderr," -c, --convert\n"); | ||
99 | fprintf(stderr," --read-aupd filename.bin\n"); | ||
100 | fprintf(stderr," --write-aupd filename.bin\n"); | ||
101 | fprintf(stderr," -x --dump-xml filename.xml\n"); | ||
102 | fprintf(stderr,"\n"); | ||
103 | |||
104 | fprintf(stderr,"The .ipodx extension is used for encrypted images for the 2nd Gen Nano.\n\n"); | ||
105 | |||
106 | #ifdef __WIN32__ | ||
107 | fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your ipod's hard disk.\n"); | ||
108 | fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n"); | ||
109 | fprintf(stderr,"will be disk 1 etc. ipodpatcher will refuse to access a disk unless it\n"); | ||
110 | fprintf(stderr,"can identify it as being an ipod.\n"); | ||
111 | fprintf(stderr,"\n"); | ||
112 | #else | ||
113 | #if defined(linux) || defined (__linux) | ||
114 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your ipod.\n"); | ||
115 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) | ||
116 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your ipod.\n"); | ||
117 | #elif defined(__APPLE__) && defined(__MACH__) | ||
118 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your ipod.\n"); | ||
119 | #endif | ||
120 | fprintf(stderr,"ipodpatcher will refuse to access a disk unless it can identify it as being\n"); | ||
121 | fprintf(stderr,"an ipod.\n"); | ||
122 | #endif | ||
123 | } | ||
124 | |||
125 | void display_partinfo(struct ipod_t* ipod) | ||
126 | { | ||
127 | int i; | ||
128 | double sectors_per_MB = (1024.0*1024.0)/ipod->sector_size; | ||
129 | |||
130 | printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); | ||
131 | for ( i = 0; i < 4; i++ ) { | ||
132 | if (ipod->pinfo[i].start != 0) { | ||
133 | printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", | ||
134 | i, | ||
135 | (long int)ipod->pinfo[i].start, | ||
136 | (long int)ipod->pinfo[i].start+ipod->pinfo[i].size-1, | ||
137 | ipod->pinfo[i].size/sectors_per_MB, | ||
138 | get_parttype(ipod->pinfo[i].type), | ||
139 | (int)ipod->pinfo[i].type); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | |||
145 | int main(int argc, char* argv[]) | ||
146 | { | ||
147 | char yesno[4]; | ||
148 | int i; | ||
149 | int n; | ||
150 | int infile, outfile; | ||
151 | unsigned int inputsize; | ||
152 | char* filename; | ||
153 | int action = SHOW_INFO; | ||
154 | int type; | ||
155 | struct ipod_t ipod; | ||
156 | |||
157 | fprintf(stderr,"ipodpatcher " VERSION "\n"); | ||
158 | fprintf(stderr,"(C) Dave Chapman 2006-2009\n"); | ||
159 | fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); | ||
160 | fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | ||
161 | |||
162 | if ((argc > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) { | ||
163 | print_usage(); | ||
164 | return IPOD_OK; | ||
165 | } | ||
166 | |||
167 | if (ipod_alloc_buffer(&ipod,BUFFER_SIZE) < 0) { | ||
168 | fprintf(stderr,"Failed to allocate memory buffer\n"); | ||
169 | } | ||
170 | |||
171 | if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) { | ||
172 | if (ipod_scan(&ipod) == 0) | ||
173 | fprintf(stderr,"[ERR] No ipods found.\n"); | ||
174 | return IPOD_NOT_FOUND; | ||
175 | } | ||
176 | |||
177 | /* If the first parameter doesn't start with -, then we interpret it as a device */ | ||
178 | if ((argc > 1) && (argv[1][0] != '-')) { | ||
179 | ipod.diskname[0]=0; | ||
180 | #ifdef __WIN32__ | ||
181 | snprintf(ipod.diskname,sizeof(ipod.diskname),"\\\\.\\PhysicalDrive%s",argv[1]); | ||
182 | #else | ||
183 | strncpy(ipod.diskname,argv[1],sizeof(ipod.diskname)); | ||
184 | #endif | ||
185 | i = 2; | ||
186 | } else { | ||
187 | /* Autoscan for ipods */ | ||
188 | n = ipod_scan(&ipod); | ||
189 | if (n==0) { | ||
190 | fprintf(stderr,"[ERR] No ipods found, aborting\n"); | ||
191 | fprintf(stderr,"[ERR] Please connect your ipod and ensure it is in disk mode\n"); | ||
192 | #if defined(__APPLE__) && defined(__MACH__) | ||
193 | fprintf(stderr,"[ERR] Also ensure that itunes is closed, and that your ipod is not mounted.\n"); | ||
194 | #elif !defined(__WIN32__) | ||
195 | if (geteuid()!=0) { | ||
196 | fprintf(stderr,"[ERR] You may also need to run ipodpatcher as root.\n"); | ||
197 | } | ||
198 | #endif | ||
199 | fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n"); | ||
200 | } else if (n > 1) { | ||
201 | fprintf(stderr,"[ERR] %d ipods found, aborting\n",n); | ||
202 | fprintf(stderr,"[ERR] Please connect only one ipod and re-run ipodpatcher.\n"); | ||
203 | return IPOD_MULTIPLE_DEVICES; | ||
204 | } else if (n == 1 && ipod.macpod) { | ||
205 | return IPOD_WRONG_TYPE; | ||
206 | } | ||
207 | |||
208 | if (n != 1) { | ||
209 | #ifdef WITH_BOOTOBJS | ||
210 | if (argc==1) { | ||
211 | printf("\nPress ENTER to exit ipodpatcher :"); | ||
212 | fgets(yesno,4,stdin); | ||
213 | } | ||
214 | #endif | ||
215 | return IPOD_NOT_FOUND; | ||
216 | } | ||
217 | |||
218 | i = 1; | ||
219 | } | ||
220 | |||
221 | #ifdef WITH_BOOTOBJS | ||
222 | action = INTERACTIVE; | ||
223 | #else | ||
224 | action = NONE; | ||
225 | #endif | ||
226 | |||
227 | while (i < argc) { | ||
228 | if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { | ||
229 | action = LIST_IMAGES; | ||
230 | i++; | ||
231 | #ifdef WITH_BOOTOBJS | ||
232 | } else if (strcmp(argv[i],"--install")==0) { | ||
233 | action = INSTALL; | ||
234 | i++; | ||
235 | #endif | ||
236 | } else if ((strcmp(argv[i],"-d")==0) || | ||
237 | (strcmp(argv[i],"--delete-bootloader")==0)) { | ||
238 | action = DELETE_BOOTLOADER; | ||
239 | i++; | ||
240 | } else if ((strcmp(argv[i],"-a")==0) || | ||
241 | (strcmp(argv[i],"--add-bootloader")==0)) { | ||
242 | action = ADD_BOOTLOADER; | ||
243 | type = FILETYPE_DOT_IPOD; | ||
244 | i++; | ||
245 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
246 | filename=argv[i]; | ||
247 | i++; | ||
248 | } else if ((strcmp(argv[i],"-ab")==0) || | ||
249 | (strcmp(argv[i],"--add-bootloader-bin")==0)) { | ||
250 | action = ADD_BOOTLOADER; | ||
251 | type = FILETYPE_DOT_BIN; | ||
252 | i++; | ||
253 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
254 | filename=argv[i]; | ||
255 | i++; | ||
256 | } else if ((strcmp(argv[i],"-rf")==0) || | ||
257 | (strcmp(argv[i],"--read-firmware")==0)) { | ||
258 | action = READ_FIRMWARE; | ||
259 | type = FILETYPE_DOT_IPOD; | ||
260 | i++; | ||
261 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
262 | filename=argv[i]; | ||
263 | i++; | ||
264 | } else if ((strcmp(argv[i],"-rfb")==0) || | ||
265 | (strcmp(argv[i],"--read-firmware-bin")==0)) { | ||
266 | action = READ_FIRMWARE; | ||
267 | type = FILETYPE_DOT_BIN; | ||
268 | i++; | ||
269 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
270 | filename=argv[i]; | ||
271 | i++; | ||
272 | #ifdef WITH_BOOTOBJS | ||
273 | } else if ((strcmp(argv[i],"-we")==0) || | ||
274 | (strcmp(argv[i],"--write-embedded")==0)) { | ||
275 | action = WRITE_FIRMWARE; | ||
276 | type = FILETYPE_INTERNAL; | ||
277 | filename="[embedded bootloader]"; /* Only displayed for user */ | ||
278 | i++; | ||
279 | #endif | ||
280 | } else if ((strcmp(argv[i],"-wf")==0) || | ||
281 | (strcmp(argv[i],"--write-firmware")==0)) { | ||
282 | action = WRITE_FIRMWARE; | ||
283 | type = FILETYPE_DOT_IPOD; | ||
284 | i++; | ||
285 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
286 | filename=argv[i]; | ||
287 | i++; | ||
288 | } else if ((strcmp(argv[i],"-wfb")==0) || | ||
289 | (strcmp(argv[i],"--write-firmware-bin")==0)) { | ||
290 | action = WRITE_FIRMWARE; | ||
291 | type = FILETYPE_DOT_BIN; | ||
292 | i++; | ||
293 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
294 | filename=argv[i]; | ||
295 | i++; | ||
296 | } else if ((strcmp(argv[i],"-r")==0) || | ||
297 | (strcmp(argv[i],"--read-partition")==0)) { | ||
298 | action = READ_PARTITION; | ||
299 | i++; | ||
300 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
301 | filename=argv[i]; | ||
302 | i++; | ||
303 | } else if ((strcmp(argv[i],"-w")==0) || | ||
304 | (strcmp(argv[i],"--write-partition")==0)) { | ||
305 | action = WRITE_PARTITION; | ||
306 | i++; | ||
307 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
308 | filename=argv[i]; | ||
309 | i++; | ||
310 | } else if ((strcmp(argv[i],"-v")==0) || | ||
311 | (strcmp(argv[i],"--verbose")==0)) { | ||
312 | ipod_verbose++; | ||
313 | i++; | ||
314 | } else if ((strcmp(argv[i],"-f")==0) || | ||
315 | (strcmp(argv[i],"--format")==0)) { | ||
316 | action = FORMAT_PARTITION; | ||
317 | i++; | ||
318 | } else if (strcmp(argv[i],"--read-aupd")==0) { | ||
319 | action = READ_AUPD; | ||
320 | i++; | ||
321 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
322 | filename=argv[i]; | ||
323 | i++; | ||
324 | } else if (strcmp(argv[i],"--write-aupd")==0) { | ||
325 | action = WRITE_AUPD; | ||
326 | i++; | ||
327 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
328 | filename=argv[i]; | ||
329 | i++; | ||
330 | } else if ((strcmp(argv[i],"-x")==0) || | ||
331 | (strcmp(argv[i],"--dump-xml")==0)) { | ||
332 | action = DUMP_XML; | ||
333 | i++; | ||
334 | if (i == argc) { print_usage(); return IPOD_WRONG_ARGUMENTS; } | ||
335 | filename=argv[i]; | ||
336 | i++; | ||
337 | } else if ((strcmp(argv[i],"-c")==0) || | ||
338 | (strcmp(argv[i],"--convert")==0)) { | ||
339 | action = CONVERT_TO_FAT32; | ||
340 | i++; | ||
341 | } else { | ||
342 | print_usage(); return IPOD_WRONG_ARGUMENTS; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | if (ipod.diskname[0]==0) { | ||
347 | print_usage(); | ||
348 | return 1; | ||
349 | } | ||
350 | |||
351 | if (ipod_open(&ipod, 0) < 0) { | ||
352 | return IPOD_ACCESS_DENIED; | ||
353 | } | ||
354 | |||
355 | fprintf(stderr,"[INFO] Reading partition table from %s\n",ipod.diskname); | ||
356 | fprintf(stderr,"[INFO] Sector size is %d bytes\n",ipod.sector_size); | ||
357 | |||
358 | if (read_partinfo(&ipod,0) < 0) { | ||
359 | return IPOD_PARTITION_ERROR; | ||
360 | } | ||
361 | |||
362 | display_partinfo(&ipod); | ||
363 | |||
364 | if (ipod.pinfo[0].start==0) { | ||
365 | fprintf(stderr,"[ERR] No partition 0 on disk:\n"); | ||
366 | display_partinfo(&ipod); | ||
367 | return IPOD_PARTITION_ERROR; | ||
368 | } | ||
369 | |||
370 | read_directory(&ipod); | ||
371 | |||
372 | if (ipod.nimages <= 0) { | ||
373 | fprintf(stderr,"[ERR] Failed to read firmware directory - nimages=%d\n",ipod.nimages); | ||
374 | return IPOD_IMAGE_ERROR; | ||
375 | } | ||
376 | |||
377 | if (getmodel(&ipod,(ipod.ipod_directory[ipod.ososimage].vers>>8)) < 0) { | ||
378 | fprintf(stderr,"[ERR] Unknown version number in firmware (%08x)\n", | ||
379 | ipod.ipod_directory[ipod.ososimage].vers); | ||
380 | return IPOD_UNKNOWN_FW_VERSION; | ||
381 | } | ||
382 | |||
383 | #ifdef __WIN32__ | ||
384 | /* Windows requires the ipod in R/W mode for SCSI Inquiry */ | ||
385 | if (ipod_reopen_rw(&ipod) < 0) { | ||
386 | return IPOD_CANNOT_REOPEN; | ||
387 | } | ||
388 | #endif | ||
389 | |||
390 | |||
391 | /* Read the XML info, and if successful, look for the ramsize | ||
392 | (only available for some models - set to 0 if not known) */ | ||
393 | |||
394 | ipod.ramsize = 0; | ||
395 | |||
396 | if (ipod_get_xmlinfo(&ipod) == 0) { | ||
397 | ipod_get_ramsize(&ipod); | ||
398 | } | ||
399 | |||
400 | printf("[INFO] Ipod model: %s ",ipod.modelstr); | ||
401 | if (ipod.ramsize > 0) { printf("(%dMB RAM) ",ipod.ramsize); } | ||
402 | printf("(\"%s\")\n",ipod.macpod ? "macpod" : "winpod"); | ||
403 | |||
404 | if (ipod.macpod) { | ||
405 | print_macpod_warning(); | ||
406 | } | ||
407 | |||
408 | if (action==LIST_IMAGES) { | ||
409 | list_images(&ipod); | ||
410 | #ifdef WITH_BOOTOBJS | ||
411 | } else if (action==INTERACTIVE) { | ||
412 | |||
413 | printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :"); | ||
414 | |||
415 | if (fgets(yesno,4,stdin)) { | ||
416 | if (yesno[0]=='i') { | ||
417 | if (ipod_reopen_rw(&ipod) < 0) { | ||
418 | return IPOD_CANNOT_REOPEN; | ||
419 | } | ||
420 | |||
421 | if (add_bootloader(&ipod, NULL, FILETYPE_INTERNAL)==0) { | ||
422 | fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); | ||
423 | } else { | ||
424 | fprintf(stderr,"[ERR] --install failed.\n"); | ||
425 | } | ||
426 | } else if (yesno[0]=='u') { | ||
427 | if (ipod_reopen_rw(&ipod) < 0) { | ||
428 | return IPOD_CANNOT_REOPEN; | ||
429 | } | ||
430 | |||
431 | if (delete_bootloader(&ipod)==0) { | ||
432 | fprintf(stderr,"[INFO] Bootloader removed.\n"); | ||
433 | } else { | ||
434 | fprintf(stderr,"[ERR] Bootloader removal failed.\n"); | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | #endif | ||
439 | } else if (action==DELETE_BOOTLOADER) { | ||
440 | if (ipod_reopen_rw(&ipod) < 0) { | ||
441 | return IPOD_CANNOT_REOPEN; | ||
442 | } | ||
443 | |||
444 | if (ipod.ipod_directory[0].entryOffset==0) { | ||
445 | fprintf(stderr,"[ERR] No bootloader detected.\n"); | ||
446 | } else { | ||
447 | if (delete_bootloader(&ipod)==0) { | ||
448 | fprintf(stderr,"[INFO] Bootloader removed.\n"); | ||
449 | } else { | ||
450 | fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); | ||
451 | } | ||
452 | } | ||
453 | } else if (action==ADD_BOOTLOADER) { | ||
454 | if (ipod_reopen_rw(&ipod) < 0) { | ||
455 | return IPOD_CANNOT_REOPEN; | ||
456 | } | ||
457 | |||
458 | if (add_bootloader(&ipod, filename, type)==0) { | ||
459 | fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); | ||
460 | } else { | ||
461 | fprintf(stderr,"[ERR] --add-bootloader failed.\n"); | ||
462 | } | ||
463 | #ifdef WITH_BOOTOBJS | ||
464 | } else if (action==INSTALL) { | ||
465 | if (ipod_reopen_rw(&ipod) < 0) { | ||
466 | return IPOD_CANNOT_REOPEN; | ||
467 | } | ||
468 | |||
469 | if (add_bootloader(&ipod, NULL, FILETYPE_INTERNAL)==0) { | ||
470 | fprintf(stderr,"[INFO] Bootloader installed successfully.\n"); | ||
471 | } else { | ||
472 | fprintf(stderr,"[ERR] --install failed.\n"); | ||
473 | } | ||
474 | #endif | ||
475 | } else if (action==WRITE_FIRMWARE) { | ||
476 | if (ipod_reopen_rw(&ipod) < 0) { | ||
477 | return IPOD_CANNOT_REOPEN; | ||
478 | } | ||
479 | |||
480 | if (write_firmware(&ipod, filename,type)==0) { | ||
481 | fprintf(stderr,"[INFO] Firmware %s written to device.\n",filename); | ||
482 | } else { | ||
483 | fprintf(stderr,"[ERR] --write-firmware failed.\n"); | ||
484 | } | ||
485 | } else if (action==READ_FIRMWARE) { | ||
486 | if (read_firmware(&ipod, filename, type)==0) { | ||
487 | fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); | ||
488 | } else { | ||
489 | fprintf(stderr,"[ERR] --read-firmware failed.\n"); | ||
490 | } | ||
491 | } else if (action==READ_AUPD) { | ||
492 | if (read_aupd(&ipod, filename)==0) { | ||
493 | fprintf(stderr,"[INFO] AUPD image read to file %s.\n",filename); | ||
494 | } else { | ||
495 | fprintf(stderr,"[ERR] --read-aupd failed.\n"); | ||
496 | } | ||
497 | } else if (action==WRITE_AUPD) { | ||
498 | if (ipod_reopen_rw(&ipod) < 0) { | ||
499 | return IPOD_CANNOT_REOPEN; | ||
500 | } | ||
501 | |||
502 | if (write_aupd(&ipod, filename)==0) { | ||
503 | fprintf(stderr,"[INFO] AUPD image %s written to device.\n",filename); | ||
504 | } else { | ||
505 | fprintf(stderr,"[ERR] --write-aupd failed.\n"); | ||
506 | } | ||
507 | } else if (action==DUMP_XML) { | ||
508 | if (ipod.xmlinfo == NULL) { | ||
509 | fprintf(stderr,"[ERR] No XML to write\n"); | ||
510 | return IPOD_DUMP_FAILED; | ||
511 | } | ||
512 | |||
513 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); | ||
514 | if (outfile < 0) { | ||
515 | perror(filename); | ||
516 | return IPOD_OPEN_OUTFILE_FAILED; | ||
517 | } | ||
518 | |||
519 | if (write(outfile, ipod.xmlinfo, ipod.xmlinfo_len) < 0) { | ||
520 | fprintf(stderr,"[ERR] --dump-xml failed.\n"); | ||
521 | } else { | ||
522 | fprintf(stderr,"[INFO] XML info written to %s.\n",filename); | ||
523 | } | ||
524 | close(outfile); | ||
525 | } else if (action==READ_PARTITION) { | ||
526 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); | ||
527 | if (outfile < 0) { | ||
528 | perror(filename); | ||
529 | return IPOD_OPEN_OUTFILE_FAILED; | ||
530 | } | ||
531 | |||
532 | if (read_partition(&ipod, outfile) < 0) { | ||
533 | fprintf(stderr,"[ERR] --read-partition failed.\n"); | ||
534 | } else { | ||
535 | fprintf(stderr,"[INFO] Partition extracted to %s.\n",filename); | ||
536 | } | ||
537 | close(outfile); | ||
538 | } else if (action==WRITE_PARTITION) { | ||
539 | if (ipod_reopen_rw(&ipod) < 0) { | ||
540 | return IPOD_CANNOT_REOPEN; | ||
541 | } | ||
542 | |||
543 | infile = open(filename,O_RDONLY|O_BINARY); | ||
544 | if (infile < 0) { | ||
545 | perror(filename); | ||
546 | return IPOD_OPEN_INFILE_FAILED; | ||
547 | } | ||
548 | |||
549 | /* Check filesize is <= partition size */ | ||
550 | inputsize=filesize(infile); | ||
551 | if (inputsize > 0) { | ||
552 | if (inputsize <= (ipod.pinfo[0].size*ipod.sector_size)) { | ||
553 | fprintf(stderr,"[INFO] Input file is %u bytes\n",inputsize); | ||
554 | if (write_partition(&ipod,infile) < 0) { | ||
555 | fprintf(stderr,"[ERR] --write-partition failed.\n"); | ||
556 | } else { | ||
557 | fprintf(stderr,"[INFO] %s restored to partition\n",filename); | ||
558 | } | ||
559 | } else { | ||
560 | fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n"); | ||
561 | } | ||
562 | } | ||
563 | |||
564 | close(infile); | ||
565 | } else if (action==FORMAT_PARTITION) { | ||
566 | printf("WARNING!!! YOU ARE ABOUT TO USE AN EXPERIMENTAL FEATURE.\n"); | ||
567 | printf("ALL DATA ON YOUR IPOD WILL BE ERASED.\n"); | ||
568 | printf("Are you sure you want to format your ipod? (y/n):"); | ||
569 | |||
570 | if (fgets(yesno,4,stdin)) { | ||
571 | if (yesno[0]=='y') { | ||
572 | if (ipod_reopen_rw(&ipod) < 0) { | ||
573 | return IPOD_CANNOT_REOPEN; | ||
574 | } | ||
575 | |||
576 | if (format_partition(&ipod,1) < 0) { | ||
577 | fprintf(stderr,"[ERR] Format failed.\n"); | ||
578 | } | ||
579 | } else { | ||
580 | fprintf(stderr,"[INFO] Format cancelled.\n"); | ||
581 | } | ||
582 | } | ||
583 | } else if (action==CONVERT_TO_FAT32) { | ||
584 | if (!ipod.macpod) { | ||
585 | printf("[ERR] Ipod is already FAT32, aborting\n"); | ||
586 | } else { | ||
587 | printf("WARNING!!! YOU ARE ABOUT TO USE AN EXPERIMENTAL FEATURE.\n"); | ||
588 | printf("ALL DATA ON YOUR IPOD WILL BE ERASED.\n"); | ||
589 | printf("Are you sure you want to convert your ipod to FAT32? (y/n):"); | ||
590 | |||
591 | if (fgets(yesno,4,stdin)) { | ||
592 | if (yesno[0]=='y') { | ||
593 | if (ipod_reopen_rw(&ipod) < 0) { | ||
594 | return IPOD_CANNOT_REOPEN; | ||
595 | } | ||
596 | |||
597 | if (write_dos_partition_table(&ipod) < 0) { | ||
598 | fprintf(stderr,"[ERR] Partition conversion failed.\n"); | ||
599 | } | ||
600 | |||
601 | if (format_partition(&ipod,1) < 0) { | ||
602 | fprintf(stderr,"[ERR] Format failed.\n"); | ||
603 | } | ||
604 | } else { | ||
605 | fprintf(stderr,"[INFO] Format cancelled.\n"); | ||
606 | } | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | |||
611 | ipod_close(&ipod); | ||
612 | |||
613 | #ifdef WITH_BOOTOBJS | ||
614 | if (action==INTERACTIVE) { | ||
615 | printf("Press ENTER to exit ipodpatcher :"); | ||
616 | fgets(yesno,4,stdin); | ||
617 | } | ||
618 | #endif | ||
619 | |||
620 | ipod_dealloc_buffer(&ipod); | ||
621 | return IPOD_OK; | ||
622 | } | ||
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 @@ | |||
1 | /* DOS partition types - taken from fdisk */ | ||
2 | |||
3 | struct parttype { | ||
4 | unsigned char type; | ||
5 | char *name; | ||
6 | }; | ||
7 | |||
8 | struct parttype parttypes[] = { | ||
9 | {0x00, "Empty"}, | ||
10 | {0x01, "FAT12"}, | ||
11 | {0x02, "XENIX root"}, | ||
12 | {0x03, "XENIX usr"}, | ||
13 | {0x04, "FAT16 <32M"}, | ||
14 | {0x05, "Extended"}, /* DOS 3.3+ extended partition */ | ||
15 | {0x06, "FAT16"}, /* DOS 16-bit >=32M */ | ||
16 | {0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */ | ||
17 | {0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */ | ||
18 | {0x09, "AIX bootable"}, /* AIX data or Coherent */ | ||
19 | {0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */ | ||
20 | {0x0b, "W95 FAT32"}, | ||
21 | {0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */ | ||
22 | {0x0e, "W95 FAT16 (LBA)"}, | ||
23 | {0x0f, "W95 Ext'd (LBA)"}, | ||
24 | {0x10, "OPUS"}, | ||
25 | {0x11, "Hidden FAT12"}, | ||
26 | {0x12, "Compaq diagnostics"}, | ||
27 | {0x14, "Hidden FAT16 <32M"}, | ||
28 | {0x16, "Hidden FAT16"}, | ||
29 | {0x17, "Hidden HPFS/NTFS"}, | ||
30 | {0x18, "AST SmartSleep"}, | ||
31 | {0x1b, "Hidden W95 FAT32"}, | ||
32 | {0x1c, "Hidden W95 FAT32 (LBA)"}, | ||
33 | {0x1e, "Hidden W95 FAT16 (LBA)"}, | ||
34 | {0x24, "NEC DOS"}, | ||
35 | {0x39, "Plan 9"}, | ||
36 | {0x3c, "PartitionMagic recovery"}, | ||
37 | {0x40, "Venix 80286"}, | ||
38 | {0x41, "PPC PReP Boot"}, | ||
39 | {0x42, "SFS"}, | ||
40 | {0x4d, "QNX4.x"}, | ||
41 | {0x4e, "QNX4.x 2nd part"}, | ||
42 | {0x4f, "QNX4.x 3rd part"}, | ||
43 | {0x50, "OnTrack DM"}, | ||
44 | {0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */ | ||
45 | {0x52, "CP/M"}, /* CP/M or Microport SysV/AT */ | ||
46 | {0x53, "OnTrack DM6 Aux3"}, | ||
47 | {0x54, "OnTrackDM6"}, | ||
48 | {0x55, "EZ-Drive"}, | ||
49 | {0x56, "Golden Bow"}, | ||
50 | {0x5c, "Priam Edisk"}, | ||
51 | {0x61, "SpeedStor"}, | ||
52 | {0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */ | ||
53 | {0x64, "Novell Netware 286"}, | ||
54 | {0x65, "Novell Netware 386"}, | ||
55 | {0x70, "DiskSecure Multi-Boot"}, | ||
56 | {0x75, "PC/IX"}, | ||
57 | {0x80, "Old Minix"}, /* Minix 1.4a and earlier */ | ||
58 | {0x81, "Minix / old Linux"},/* Minix 1.4b and later */ | ||
59 | {0x82, "Linux swap / Solaris"}, | ||
60 | {0x83, "Linux"}, | ||
61 | {0x84, "OS/2 hidden C: drive"}, | ||
62 | {0x85, "Linux extended"}, | ||
63 | {0x86, "NTFS volume set"}, | ||
64 | {0x87, "NTFS volume set"}, | ||
65 | {0x88, "Linux plaintext"}, | ||
66 | {0x8e, "Linux LVM"}, | ||
67 | {0x93, "Amoeba"}, | ||
68 | {0x94, "Amoeba BBT"}, /* (bad block table) */ | ||
69 | {0x9f, "BSD/OS"}, /* BSDI */ | ||
70 | {0xa0, "IBM Thinkpad hibernation"}, | ||
71 | {0xa5, "FreeBSD"}, /* various BSD flavours */ | ||
72 | {0xa6, "OpenBSD"}, | ||
73 | {0xa7, "NeXTSTEP"}, | ||
74 | {0xa8, "Darwin UFS"}, | ||
75 | {0xa9, "NetBSD"}, | ||
76 | {0xab, "Darwin boot"}, | ||
77 | {0xb7, "BSDI fs"}, | ||
78 | {0xb8, "BSDI swap"}, | ||
79 | {0xbb, "Boot Wizard hidden"}, | ||
80 | {0xbe, "Solaris boot"}, | ||
81 | {0xbf, "Solaris"}, | ||
82 | {0xc1, "DRDOS/sec (FAT-12)"}, | ||
83 | {0xc4, "DRDOS/sec (FAT-16 < 32M)"}, | ||
84 | {0xc6, "DRDOS/sec (FAT-16)"}, | ||
85 | {0xc7, "Syrinx"}, | ||
86 | {0xda, "Non-FS data"}, | ||
87 | {0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or | ||
88 | Concurrent DOS or CTOS */ | ||
89 | {0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */ | ||
90 | {0xdf, "BootIt"}, /* BootIt EMBRM */ | ||
91 | {0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT | ||
92 | extended partition */ | ||
93 | {0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */ | ||
94 | {0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended | ||
95 | partition < 1024 cyl. */ | ||
96 | {0xeb, "BeOS fs"}, | ||
97 | {0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */ | ||
98 | {0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */ | ||
99 | {0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */ | ||
100 | {0xf1, "SpeedStor"}, | ||
101 | {0xf4, "SpeedStor"}, /* SpeedStor large partition */ | ||
102 | {0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */ | ||
103 | {0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with | ||
104 | autodetect using persistent | ||
105 | superblock */ | ||
106 | {0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */ | ||
107 | {0xff, "BBT"}, /* Xenix Bad Block Table */ | ||
108 | { 0, 0 } | ||
109 | }; | ||