A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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
48int ipod_verbose = 0;
49
50
51/* The following string appears at the start of the firmware partition */
52static const char apple_stop_sign[] = "{{~~ /-----\\ "\
53 "{{~~ / \\ "\
54 "{{~~| | "\
55 "{{~~| S T O P | "\
56 "{{~~| | "\
57 "{{~~ \\ / "\
58 "{{~~ \\-----/ "\
59 "Copyright(C) 200"\
60 "1 Apple Computer"\
61 ", Inc.----------"\
62 "----------------"\
63 "----------------"\
64 "----------------"\
65 "----------------"\
66 "----------------"\
67 "---------------";
68
69/* Windows requires the buffer for disk I/O to be aligned in memory on a
70 multiple of the disk volume size - so we use a single global variable
71 and initialise it with ipod_alloc_buf()
72*/
73
74char* get_parttype(unsigned int pt)
75{
76 int i;
77 static char unknown[]="Unknown";
78
79 if (pt == PARTTYPE_HFS) {
80 return "HFS/HFS+";
81 }
82
83 i=0;
84 while (parttypes[i].name != NULL) {
85 if (parttypes[i].type == pt) {
86 return (parttypes[i].name);
87 }
88 i++;
89 }
90
91 return unknown;
92}
93
94off_t filesize(int fd) {
95 struct stat buf;
96
97 if (fstat(fd,&buf) < 0) {
98 perror("[ERR] Checking filesize of input file");
99 return -1;
100 } else {
101 return(buf.st_size);
102 }
103}
104
105/* Partition table parsing code taken from Rockbox */
106
107#define MAX_SECTOR_SIZE 2048
108#define SECTOR_SIZE 512
109
110static inline unsigned short le2ushort(unsigned char* buf)
111{
112 unsigned short res = (buf[1] << 8) | buf[0];
113
114 return res;
115}
116
117static inline int le2int(unsigned char* buf)
118{
119 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
120
121 return res;
122}
123
124static inline int be2int(unsigned char* buf)
125{
126 int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
127
128 return res;
129}
130
131static inline int getint16le(char* buf)
132{
133 int16_t res = (buf[1] << 8) | buf[0];
134
135 return res;
136}
137
138static inline void short2le(unsigned short val, unsigned char* addr)
139{
140 addr[0] = val & 0xFF;
141 addr[1] = (val >> 8) & 0xff;
142}
143
144static inline void int2le(unsigned int val, unsigned char* addr)
145{
146 addr[0] = val & 0xFF;
147 addr[1] = (val >> 8) & 0xff;
148 addr[2] = (val >> 16) & 0xff;
149 addr[3] = (val >> 24) & 0xff;
150}
151
152static inline void int2be(unsigned int val, unsigned char* addr)
153{
154 addr[0] = (val >> 24) & 0xff;
155 addr[1] = (val >> 16) & 0xff;
156 addr[2] = (val >> 8) & 0xff;
157 addr[3] = val & 0xFF;
158}
159
160
161#define BYTES2INT32(array,pos)\
162 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
163 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
164
165int read_partinfo(struct ipod_t* ipod, int silent)
166{
167 int i;
168 unsigned long count;
169
170 if(ipod->sectorbuf == NULL) {
171 fprintf(stderr,"[ERR] Buffer not initialized.");
172 return -1;
173 }
174
175 count = ipod_read(ipod,ipod->sector_size);
176
177 if (count <= 0) {
178 ipod_print_error(" Error reading from disk: ");
179 return -1;
180 }
181
182 memset(ipod->pinfo, 0, sizeof(ipod->pinfo));
183
184 if ((ipod->sectorbuf[510] == 0x55) && (ipod->sectorbuf[511] == 0xaa)) {
185 /* DOS partition table */
186 ipod->macpod = 0;
187 /* parse partitions */
188 for ( i = 0; i < 4; i++ ) {
189 unsigned char* ptr = ipod->sectorbuf + 0x1be + 16*i;
190 ipod->pinfo[i].type = ptr[4];
191 ipod->pinfo[i].start = BYTES2INT32(ptr, 8);
192 ipod->pinfo[i].size = BYTES2INT32(ptr, 12);
193
194 /* extended? */
195 if ( ipod->pinfo[i].type == 5 ) {
196 /* not handled yet */
197 }
198 }
199 } else if ((ipod->sectorbuf[0] == 'E') && (ipod->sectorbuf[1] == 'R')) {
200 /* Apple Partition Map */
201
202 /* APM parsing code based on the check_mac_partitions() function in
203 ipodloader2 - written by Thomas Tempelmann and released
204 under the GPL. */
205
206 int blkNo = 1;
207 int partBlkCount = 1;
208 int partBlkSizMul = ipod->sectorbuf[2] / 2;
209
210 int pmMapBlkCnt; /* # of blks in partition map */
211 int pmPyPartStart; /* physical start blk of partition */
212 int pmPartBlkCnt; /* # of blks in this partition */
213 int i = 0;
214
215 ipod->macpod = 1;
216
217 memset(ipod->pinfo,0,sizeof(ipod->pinfo));
218
219 while (blkNo <= partBlkCount) {
220 if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) {
221 fprintf(stderr,"[ERR] Seek failed whilst reading APM\n");
222 return -1;
223 }
224
225 count = ipod_read(ipod, ipod->sector_size);
226
227 if (count <= 0) {
228 ipod_print_error(" Error reading from disk: ");
229 return -1;
230 }
231
232 /* see if it's a partition entry */
233 if ((ipod->sectorbuf[0] != 'P') || (ipod->sectorbuf[1] != 'M')) {
234 /* end of partition table -> leave the loop */
235 break;
236 }
237
238 /* Extract the interesting entries */
239 pmMapBlkCnt = be2int(ipod->sectorbuf + 4);
240 pmPyPartStart = be2int(ipod->sectorbuf + 8);
241 pmPartBlkCnt = be2int(ipod->sectorbuf + 12);
242
243 /* update the number of part map blocks */
244 partBlkCount = pmMapBlkCnt;
245
246 if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_MDFW", 32)==0) {
247 /* A Firmware partition */
248 ipod->pinfo[i].start = pmPyPartStart;
249 ipod->pinfo[i].size = pmPartBlkCnt;
250 ipod->pinfo[i].type = 0;
251 i++;
252 } else if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_HFS", 32)==0) {
253 /* A HFS partition */
254 ipod->pinfo[i].start = pmPyPartStart;
255 ipod->pinfo[i].size = pmPartBlkCnt;
256 ipod->pinfo[i].type = PARTTYPE_HFS;
257 i++;
258 }
259
260 blkNo++; /* read next partition map entry */
261 }
262 } else {
263 if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
264 return -1;
265 }
266
267 /* Check that the partition table looks like an ipod:
268 1) Partition 1 is of type 0 (Empty) but isn't empty.
269 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod)
270 */
271 if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) ||
272 ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) &&
273 (ipod->pinfo[1].type != PARTTYPE_HFS))) {
274 if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n");
275 return -1;
276 }
277
278 ipod->start = ipod->pinfo[0].start*ipod->sector_size;
279 return 0;
280}
281
282int read_partition(struct ipod_t* ipod, int outfile)
283{
284 int res;
285 ssize_t n;
286 int bytesleft;
287 int chunksize;
288 int count = ipod->pinfo[0].size;
289
290 if (ipod_seek(ipod, ipod->start) < 0) {
291 return -1;
292 }
293 if(ipod->sectorbuf == NULL) {
294 fprintf(stderr,"[ERR] Buffer not initialized.");
295 return -1;
296 }
297
298 fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count);
299
300 bytesleft = count * ipod->sector_size;
301 while (bytesleft > 0) {
302 if (bytesleft > BUFFER_SIZE) {
303 chunksize = BUFFER_SIZE;
304 } else {
305 chunksize = bytesleft;
306 }
307
308 n = ipod_read(ipod, chunksize);
309
310 if (n < 0) {
311 return -1;
312 }
313
314 if (n < chunksize) {
315 fprintf(stderr,
316 "[ERR] Short read in disk_read() - requested %d, got %d\n",
317 chunksize,(int)n);
318 return -1;
319 }
320
321 bytesleft -= n;
322
323 res = write(outfile,ipod->sectorbuf,n);
324
325 if (res < 0) {
326 perror("[ERR] write in disk_read");
327 return -1;
328 }
329
330 if (res != n) {
331 fprintf(stderr,
332 "Short write - requested %d, received %d - aborting.\n",(int)n,res);
333 return -1;
334 }
335 }
336
337 fprintf(stderr,"[INFO] Done.\n");
338 return 0;
339}
340
341int write_partition(struct ipod_t* ipod, int infile)
342{
343 ssize_t res;
344 int n;
345 int bytesread;
346 int byteswritten = 0;
347 int eof;
348 int padding = 0;
349
350 if (ipod_seek(ipod, ipod->start) < 0) {
351 return -1;
352 }
353 if(ipod->sectorbuf == NULL) {
354 fprintf(stderr,"[ERR] Buffer not initialized.");
355 return -1;
356 }
357
358 fprintf(stderr,"[INFO] Writing input file to device\n");
359 bytesread = 0;
360 eof = 0;
361 while (!eof) {
362 n = read(infile,ipod->sectorbuf,BUFFER_SIZE);
363
364 if (n < 0) {
365 perror("[ERR] read in disk_write");
366 return -1;
367 }
368
369 if (n < BUFFER_SIZE) {
370 eof = 1;
371 /* We need to pad the last write to a multiple of SECTOR_SIZE */
372 if ((n % ipod->sector_size) != 0) {
373 padding = (ipod->sector_size-(n % ipod->sector_size));
374 n += padding;
375 }
376 }
377
378 bytesread += n;
379
380 res = ipod_write(ipod, n);
381
382 if (res < 0) {
383 ipod_print_error(" Error writing to disk: ");
384 fprintf(stderr,"Bytes written: %d\n",byteswritten);
385 return -1;
386 }
387
388 if (res != n) {
389 fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res);
390 return -1;
391 }
392
393 byteswritten += res;
394 }
395
396 fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",
397 byteswritten-padding,padding);
398 return 0;
399}
400
401char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE", "OSBK" };
402
403int diskmove(struct ipod_t* ipod, int delta)
404{
405 int src_start;
406 int src_end;
407 int bytesleft;
408 int chunksize;
409 int n;
410
411 src_start = ipod->ipod_directory[1].devOffset;
412 src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size +
413 ipod->ipod_directory[ipod->nimages-1].len +
414 (ipod->sector_size-1)) & ~(ipod->sector_size-1);
415 bytesleft = src_end - src_start;
416
417 if (ipod_verbose) {
418 fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta);
419 fprintf(stderr,"[VERB] src_start = %08x\n",src_start);
420 fprintf(stderr,"[VERB] src_end = %08x\n",src_end);
421 fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta);
422 fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta);
423 fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft);
424 }
425
426 while (bytesleft > 0) {
427 if (bytesleft <= BUFFER_SIZE) {
428 chunksize = bytesleft;
429 } else {
430 chunksize = BUFFER_SIZE;
431 }
432
433 if (ipod_verbose) {
434 fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n",
435 chunksize,
436 src_end-chunksize,
437 src_end-chunksize+delta,
438 (unsigned int)(ipod->start+src_end-chunksize),
439 (unsigned int)(ipod->start+src_end-chunksize+delta));
440 }
441
442
443 if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) {
444 fprintf(stderr,"[ERR] Seek failed\n");
445 return -1;
446 }
447
448 if ((n = ipod_read(ipod,chunksize)) < 0) {
449 perror("[ERR] Write failed\n");
450 return -1;
451 }
452
453 if (n < chunksize) {
454 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
455 chunksize,n);
456 return -1;
457 }
458
459 if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) {
460 fprintf(stderr,"[ERR] Seek failed\n");
461 return -1;
462 }
463
464 if ((n = ipod_write(ipod,chunksize)) < 0) {
465 perror("[ERR] Write failed\n");
466 return -1;
467 }
468
469 if (n < chunksize) {
470 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
471 ,chunksize,n);
472 return -1;
473 }
474
475 src_end -= chunksize;
476 bytesleft -= chunksize;
477 }
478
479 return 0;
480}
481
482static int rename_image(struct ipod_t* ipod, char* from, char* to)
483{
484 int n;
485 int x;
486 int found;
487 int i;
488 unsigned char* p;
489
490 /* diroffset may not be sector-aligned */
491 x = ipod->diroffset % ipod->sector_size;
492
493 if(ipod->sectorbuf == NULL) {
494 fprintf(stderr,"[ERR] Buffer not initialized.");
495 return -1;
496 }
497 /* Read directory */
498 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
499 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
500 return -1;
501 }
502
503 n=ipod_read(ipod, ipod->sector_size);
504 if (n < 0) {
505 fprintf(stderr,"[ERR] Read of directory failed.\n");
506 return -1;
507 }
508
509 p = ipod->sectorbuf + x;
510
511 /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
512 if (p[0] == 0)
513 {
514 /* Adjust diroffset */
515 ipod->diroffset += ipod->sector_size - x;
516
517 n=ipod_read(ipod, ipod->sector_size);
518 if (n < 0) {
519 fprintf(stderr,"[ERR] Read of directory failed.\n");
520 return -1;
521 }
522 p = ipod->sectorbuf;
523 }
524
525 found = 0;
526 for (i=0 ; !found && i < MAX_IMAGES; i++) {
527 if (memcmp(p + 4, from, 4) == 0) {
528 memcpy(p + 4, to, 4);
529
530 found = 1;
531 }
532 p += 40;
533 }
534
535 if (!found) {
536 fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", from);
537 return -1;
538 }
539
540 /* Write directory back to disk */
541 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
542 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
543 return -1;
544 }
545
546 n=ipod_write(ipod, ipod->sector_size);
547 if (n < 0) {
548 fprintf(stderr,"[ERR] Write of directory failed in rename_image.\n");
549 return -1;
550 }
551
552 return 0;
553}
554
555static int delete_image(struct ipod_t* ipod, char* name)
556{
557 int n;
558 int x;
559 int found;
560 int i;
561 unsigned char* p;
562
563 /* diroffset may not be sector-aligned */
564 x = ipod->diroffset % ipod->sector_size;
565
566 /* Read directory */
567 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
568 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
569 return -1;
570 }
571
572 n=ipod_read(ipod, ipod->sector_size);
573 if (n < 0) {
574 fprintf(stderr,"[ERR] Read of directory failed.\n");
575 return -1;
576 }
577
578 p = ipod->sectorbuf + x;
579
580 /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
581 if (p[0] == 0)
582 {
583 /* Adjust diroffset */
584 ipod->diroffset += ipod->sector_size - x;
585
586 n=ipod_read(ipod, ipod->sector_size);
587 if (n < 0) {
588 fprintf(stderr,"[ERR] Read of directory failed.\n");
589 return -1;
590 }
591 p = ipod->sectorbuf;
592 }
593
594 found = 0;
595 for (i=0 ; !found && i < MAX_IMAGES; i++) {
596 if (memcmp(p + 4, name, 4) == 0) {
597 memset(p, 0, 40); /* Delete directory entry */
598 found = 1;
599 }
600 p += 40;
601 }
602
603 if (!found) {
604 fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", name);
605 return -1;
606 }
607
608 /* Write directory back to disk */
609 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
610 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
611 return -1;
612 }
613
614 n=ipod_write(ipod, ipod->sector_size);
615 if (n < 0) {
616 fprintf(stderr,"[ERR] Write of directory failed in delete_image.\n");
617 return -1;
618 }
619
620 return 0;
621}
622
623int add_new_image(struct ipod_t* ipod, char* imagename, char* filename, int type)
624{
625 int length;
626 int found;
627 int i;
628 int x;
629 int n;
630 int infile;
631 int newsize;
632 unsigned long chksum=0;
633 unsigned long filechksum=0;
634 unsigned long offset;
635 unsigned char header[8]; /* Header for .ipod file */
636 unsigned char* p;
637
638 if(ipod->sectorbuf == NULL) {
639 fprintf(stderr,"[ERR] Buffer not initialized.");
640 return -1;
641 }
642#ifdef WITH_BOOTOBJS
643 if (type == FILETYPE_INTERNAL) {
644 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
645 length = ipod->bootloader_len;
646 infile = -1;
647 }
648 else
649#endif
650 {
651 /* First check that the input file is the correct type for this ipod. */
652 infile=open(filename,O_RDONLY);
653 if (infile < 0) {
654 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
655 return -1;
656 }
657
658 if (type==FILETYPE_DOT_IPOD) {
659 n = read(infile,header,8);
660 if (n < 8) {
661 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
662 close(infile);
663 return -1;
664 }
665
666 if (memcmp(header+4, ipod->modelname,4)!=0) {
667 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
668 header[4],header[5],header[6],header[7], ipod->modelname);
669 close(infile);
670 return -1;
671 }
672
673 filechksum = be2int(header);
674
675 length = filesize(infile)-8;
676 } else {
677 length = filesize(infile);
678 }
679 }
680
681 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
682
683 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
684 length,newsize);
685
686 if (newsize > BUFFER_SIZE) {
687 fprintf(stderr,"[ERR] Input file too big for buffer\n");
688 if (infile >= 0) close(infile);
689 return -1;
690 }
691
692 /* TODO: Check if we have enough space in the partition for the new image */
693
694#ifdef WITH_BOOTOBJS
695 if (type == FILETYPE_INTERNAL) {
696 memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len);
697 }
698 else
699#endif
700 {
701 fprintf(stderr,"[INFO] Reading input file...\n");
702
703 n = read(infile,ipod->sectorbuf,length);
704 if (n < 0) {
705 fprintf(stderr,"[ERR] Couldn't read input file\n");
706 close(infile);
707 return -1;
708 }
709 close(infile);
710 }
711
712 /* Pad the data with zeros */
713 memset(ipod->sectorbuf+length,0,newsize-length);
714
715 if (type==FILETYPE_DOT_IPOD) {
716 chksum = ipod->modelnum;
717 for (i = 0; i < length; i++) {
718 /* add 8 unsigned bits but keep a 32 bit sum */
719 chksum += ipod->sectorbuf[i];
720 }
721
722 if (chksum == filechksum) {
723 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
724 } else {
725 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
726 return -1;
727 }
728 }
729
730
731 offset = ipod->fwoffset + ipod->ipod_directory[ipod->nimages - 1].devOffset +
732 ipod->ipod_directory[ipod->nimages - 1].len + ipod->sector_size;
733
734 /* 2nd Gen Nano has encrypted firmware, and the sector
735 preceeding the firmware contains hashes that need to be
736 preserved. Nano 2G images include these extra 2048 (0x800)
737 bytes
738 */
739 if (ipod_seek(ipod, offset - (ipod->modelnum == 62 ? 0x800 : 0)) < 0) {
740 fprintf(stderr,"[ERR] Seek failed\n");
741 return -1;
742 }
743
744 if ((n = ipod_write(ipod,newsize)) < 0) {
745 perror("[ERR] Write failed\n");
746 return -1;
747 }
748
749 if (n < newsize) {
750 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
751 ,newsize,n);
752 return -1;
753 }
754 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
755
756 /* Now we need to create a new directory entry
757
758 NOTE: On the Nano 2G, the checksum is the checksum of the
759 unencrypted firmware. But this isn't checked by the NOR
760 bootloader (there are cryptographic hashes in the
761 firmware itself), so it doesn't matter that this is
762 wrong.
763 */
764 chksum = 0;
765 for (i = 0; i < length; i++) {
766 /* add 8 unsigned bits but keep a 32 bit sum */
767 chksum += ipod->sectorbuf[i];
768 }
769
770 x = ipod->diroffset % ipod->sector_size;
771
772 /* Read directory */
773 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
774
775 n=ipod_read(ipod, ipod->sector_size);
776 if (n < 0) { return -1; }
777
778 /* Create a new directory entry */
779
780 /* Copy OSOS or OSBK details - we assume one of them exists */
781 p = ipod->sectorbuf + x;
782 found = 0;
783 for (i = 0; !found && i < ipod->nimages; i++) {
784 if ((memcmp(p + 4, "soso", 4)==0) || (memcmp(p + 4, "kbso", 4)==0)) {
785 found = 1;
786 } else {
787 p += 40;
788 }
789 }
790
791 if (!found) {
792 fprintf(stderr,"[ERR] No OSOS or OSBK image to copy directory from\n");
793 return -1;
794 }
795
796 /* Copy directory image */
797 memcpy(ipod->sectorbuf + x + (ipod->nimages * 40), p, 40);
798 p = ipod->sectorbuf + x + (ipod->nimages * 40);
799
800 /* Modify directory. */
801 memcpy(p + 4, imagename, 4);
802 int2le(offset - ipod->fwoffset, p + 12); /* devOffset */
803 int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); /* len */
804 int2le(chksum, p + 28); /* checksum */
805
806 /* Write directory */
807 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
808 n=ipod_write(ipod, ipod->sector_size);
809 if (n < 0) { return -1; }
810
811 return 0;
812}
813
814
815int ipod_has_bootloader(struct ipod_t* ipod)
816{
817 /* The 2nd gen Nano is installed differently */
818 if (ipod->modelnum == 62) {
819 int i;
820 int has_osbk = 0;
821 /* Check if we have an OSBK image */
822 for (i = 0; i < ipod->nimages; i++) {
823 if (ipod->ipod_directory[i].ftype==FTYPE_OSBK) {
824 has_osbk = 1;
825 }
826 }
827 return has_osbk;
828 }
829 else {
830 return (ipod->ipod_directory[0].entryOffset != 0);
831 }
832}
833
834
835/*
836 Bootloader installation on the Nano2G consists of renaming the
837 OSOS image to OSBK and then writing the Rockbox bootloader as a
838 new OSOS image.
839
840 Maybe this approach can/should be adapted for other ipods, as it
841 prevents the Apple bootloader loading the original firmware into
842 RAM along with the Rockbox bootloader (and hence will give a
843 faster boot when the user just wants to start Rockbox).
844
845*/
846
847static int add_bootloader_nano2g(struct ipod_t* ipod, char* filename, int type)
848{
849 /* Check if we already have an OSBK image */
850 if (ipod_has_bootloader(ipod) == 0) {
851 /* First-time install - rename OSOS to OSBK and create new OSOS for bootloader */
852 fprintf(stderr,"[INFO] Creating OSBK backup image of original firmware\n");
853
854 if (rename_image(ipod, "soso", "kbso") < 0) {
855 fprintf(stderr,"[ERR] Could not rename OSOS image\n");
856 return -1;
857 }
858
859 /* Add our bootloader as a brand new image */
860 return add_new_image(ipod, "soso", filename, type);
861 } else {
862 /* This is an update, just replace OSOS with our bootloader */
863
864 return write_firmware(ipod, filename, type);
865 }
866}
867
868
869static int delete_bootloader_nano2g(struct ipod_t* ipod)
870{
871 /* Check if we have an OSBK image */
872 if (ipod_has_bootloader(ipod) == 0) {
873 fprintf(stderr,"[ERR] No OSBK image found - nothing to uninstall\n");
874 return -1;
875 } else {
876 /* Delete our bootloader image */
877 if (delete_image(ipod, "soso") < 0) {
878 fprintf(stderr,"[WARN] Could not delete OSOS image\n");
879 } else {
880 fprintf(stderr,"[INFO] OSOS image deleted\n");
881 }
882
883 if (rename_image(ipod, "kbso", "soso") < 0) {
884 fprintf(stderr,"[ERR] Could not rename OSBK image\n");
885 return -1;
886 }
887
888
889 fprintf(stderr,"[INFO] OSBK image renamed to OSOS - bootloader uninstalled.\n");
890 return 0;
891 }
892}
893
894
895int add_bootloader(struct ipod_t* ipod, char* filename, int type)
896{
897 int length;
898 int i;
899 int x;
900 int n;
901 int infile;
902 int paddedlength;
903 int entryOffset;
904 int delta = 0;
905 unsigned long chksum=0;
906 unsigned long filechksum=0;
907 unsigned char header[8]; /* Header for .ipod file */
908 unsigned char* bootloader_buf;
909
910 /* The 2nd gen Nano is installed differently */
911 if (ipod->modelnum == 62) {
912 return add_bootloader_nano2g(ipod, filename, type);
913 }
914 if(ipod->sectorbuf == NULL) {
915 fprintf(stderr,"[ERR] Buffer not initialized.");
916 return -1;
917 }
918
919 /* Calculate the position in the OSOS image where our bootloader will go. */
920 if (ipod->ipod_directory[0].entryOffset>0) {
921 /* Keep the same entryOffset */
922 entryOffset = ipod->ipod_directory[0].entryOffset;
923 } else {
924 entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1);
925 }
926
927#ifdef WITH_BOOTOBJS
928 if (type == FILETYPE_INTERNAL) {
929 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
930 memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
931 length = ipod->bootloader_len;
932 paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1);
933 }
934 else
935#endif
936 {
937 infile=open(filename,O_RDONLY);
938 if (infile < 0) {
939 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
940 return -1;
941 }
942
943 if (type==FILETYPE_DOT_IPOD) {
944 /* First check that the input file is the correct type for this ipod. */
945 n = read(infile,header,8);
946 if (n < 8) {
947 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
948 close(infile);
949 return -1;
950 }
951
952 if (memcmp(header+4, ipod->modelname,4)!=0) {
953 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
954 header[4],header[5],header[6],header[7], ipod->modelname);
955 close(infile);
956 return -1;
957 }
958
959 filechksum = be2int(header);
960
961 length=filesize(infile)-8;
962 } else {
963 length=filesize(infile);
964 }
965 paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
966
967 bootloader_buf = malloc(length);
968 if (bootloader_buf == NULL) {
969 fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n");
970 return -1;
971 }
972 /* Now read our bootloader - we need to check it before modifying the partition*/
973 n = read(infile,bootloader_buf,length);
974 close(infile);
975
976 if (n < 0) {
977 fprintf(stderr,"[ERR] Couldn't read input file\n");
978 return -1;
979 }
980
981 if (type==FILETYPE_DOT_IPOD) {
982 /* Calculate and confirm bootloader checksum */
983 chksum = ipod->modelnum;
984 for (i = 0; i < length; i++) {
985 /* add 8 unsigned bits but keep a 32 bit sum */
986 chksum += bootloader_buf[i];
987 }
988
989 if (chksum == filechksum) {
990 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
991 } else {
992 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
993 return -1;
994 }
995 }
996 }
997
998 if (entryOffset+paddedlength > BUFFER_SIZE) {
999 fprintf(stderr,"[ERR] Input file too big for buffer\n");
1000 return -1;
1001 }
1002
1003 if (ipod_verbose) {
1004 fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size);
1005 fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset);
1006 fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength);
1007 }
1008
1009 /* Check if we have enough space */
1010 /* TODO: Check the size of the partition. */
1011 if (ipod->nimages > 1) {
1012 if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) >
1013 ipod->ipod_directory[1].devOffset) {
1014 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
1015 delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength
1016 - ipod->ipod_directory[1].devOffset + ipod->sector_size;
1017
1018 if (diskmove(ipod, delta) < 0) {
1019 fprintf(stderr,"[ERR] Image movement failed.\n");
1020 return -1;
1021 }
1022 }
1023 }
1024
1025
1026 /* We have moved the partitions, now we can write our bootloader */
1027
1028 /* Firstly read the original firmware into ipod->sectorbuf */
1029 fprintf(stderr,"[INFO] Reading original firmware...\n");
1030 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
1031 fprintf(stderr,"[ERR] Seek failed\n");
1032 return -1;
1033 }
1034
1035 if ((n = ipod_read(ipod,entryOffset)) < 0) {
1036 perror("[ERR] Read failed\n");
1037 return -1;
1038 }
1039
1040 if (n < entryOffset) {
1041 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
1042 ,entryOffset,n);
1043 return -1;
1044 }
1045
1046#ifdef WITH_BOOTOBJS
1047 if (type == FILETYPE_INTERNAL) {
1048 memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
1049 }
1050 else
1051#endif
1052 {
1053 memcpy(ipod->sectorbuf+entryOffset,bootloader_buf,length);
1054 free(bootloader_buf);
1055 }
1056
1057 /* Calculate new checksum for combined image */
1058 chksum = 0;
1059 for (i=0;i<entryOffset + length; i++) {
1060 chksum += ipod->sectorbuf[i];
1061 }
1062
1063 /* Now write the combined firmware image to the disk */
1064
1065 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
1066 fprintf(stderr,"[ERR] Seek failed\n");
1067 return -1;
1068 }
1069
1070 if ((n = ipod_write(ipod,entryOffset+paddedlength)) < 0) {
1071 perror("[ERR] Write failed\n");
1072 return -1;
1073 }
1074
1075 if (n < (entryOffset+paddedlength)) {
1076 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
1077 ,entryOffset+paddedlength,n);
1078 return -1;
1079 }
1080
1081 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength);
1082
1083 x = ipod->diroffset % ipod->sector_size;
1084
1085 /* Read directory */
1086 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
1087 fprintf(stderr,"[ERR] Seek failed\n");
1088 return -1;
1089 }
1090
1091 n=ipod_read(ipod, ipod->sector_size);
1092 if (n < 0) {
1093 fprintf(stderr,"[ERR] Directory read failed\n");
1094 return -1;
1095 }
1096
1097 /* Update entries for image 0 */
1098 int2le(entryOffset+length,ipod->sectorbuf+x+16);
1099 int2le(entryOffset,ipod->sectorbuf+x+24);
1100 int2le(chksum,ipod->sectorbuf+x+28);
1101 int2le(0xffffffff,ipod->sectorbuf+x+36); /* loadAddr */
1102
1103 /* Update devOffset entries for other images, if we have moved them */
1104 if (delta > 0) {
1105 for (i=1;i<ipod->nimages;i++) {
1106 int2le(le2int(ipod->sectorbuf+x+i*40+12)+delta,ipod->sectorbuf+x+i*40+12);
1107 }
1108 }
1109
1110 /* Write directory */
1111 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
1112 fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x));
1113 return -1;
1114 }
1115 n=ipod_write(ipod, ipod->sector_size);
1116 if (n < 0) {
1117 fprintf(stderr,"[ERR] Directory write failed\n");
1118 return -1;
1119 }
1120
1121 return 0;
1122}
1123
1124int delete_bootloader(struct ipod_t* ipod)
1125{
1126 int length;
1127 int i;
1128 int x;
1129 int n;
1130 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
1131
1132 /* The 2nd gen Nano is installed differently */
1133 if (ipod->modelnum == 62) {
1134 return delete_bootloader_nano2g(ipod);
1135 }
1136 if(ipod->sectorbuf == NULL) {
1137 fprintf(stderr,"[ERR] Buffer not initialized.");
1138 return -1;
1139 }
1140
1141 /* Removing the bootloader involves adjusting the "length",
1142 "chksum" and "entryOffset" values in the osos image's directory
1143 entry. */
1144
1145 /* Firstly check we have a bootloader... */
1146
1147 if (ipod_has_bootloader(ipod) == 0) {
1148 fprintf(stderr,"[ERR] No bootloader found.\n");
1149 return -1;
1150 }
1151
1152 length = ipod->ipod_directory[0].entryOffset;
1153
1154 /* Read the firmware so we can calculate the checksum */
1155 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
1156
1157 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
1158 return -1;
1159 }
1160
1161 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
1162 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
1163 length,i);
1164
1165 if ((n = ipod_read(ipod,i)) < 0) {
1166 return -1;
1167 }
1168
1169 if (n < i) {
1170 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
1171 i,n);
1172 return -1;
1173 }
1174
1175 chksum = 0;
1176 for (i = 0; i < length; i++) {
1177 /* add 8 unsigned bits but keep a 32 bit sum */
1178 chksum += ipod->sectorbuf[i];
1179 }
1180
1181 /* Now write back the updated directory entry */
1182
1183 fprintf(stderr,"[INFO] Updating firmware checksum\n");
1184
1185 x = ipod->diroffset % ipod->sector_size;
1186
1187 /* Read directory */
1188 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1189
1190 n=ipod_read(ipod, ipod->sector_size);
1191 if (n < 0) { return -1; }
1192
1193 /* Update entries for image 0 */
1194 int2le(length,ipod->sectorbuf+x+16);
1195 int2le(0,ipod->sectorbuf+x+24);
1196 int2le(chksum,ipod->sectorbuf+x+28);
1197
1198 /* Write directory */
1199 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1200 n=ipod_write(ipod, ipod->sector_size);
1201 if (n < 0) { return -1; }
1202
1203 return 0;
1204}
1205
1206int write_firmware(struct ipod_t* ipod, char* filename, int type)
1207{
1208 int length;
1209 int i;
1210 int x;
1211 int n;
1212 int infile;
1213 int newsize;
1214 int bytesavailable;
1215 unsigned long chksum=0;
1216 unsigned long filechksum=0;
1217 unsigned long offset;
1218 unsigned char header[8]; /* Header for .ipod file */
1219 unsigned char* p;
1220
1221 if(ipod->sectorbuf == NULL) {
1222 fprintf(stderr,"[ERR] Buffer not initialized.");
1223 return -1;
1224 }
1225#ifdef WITH_BOOTOBJS
1226 if (type == FILETYPE_INTERNAL) {
1227 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
1228 length = ipod->bootloader_len;
1229 infile = -1;
1230 }
1231 else
1232#endif
1233 {
1234 /* First check that the input file is the correct type for this ipod. */
1235 infile=open(filename,O_RDONLY);
1236 if (infile < 0) {
1237 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
1238 return -1;
1239 }
1240
1241 if (type==FILETYPE_DOT_IPOD) {
1242 n = read(infile,header,8);
1243 if (n < 8) {
1244 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
1245 close(infile);
1246 return -1;
1247 }
1248
1249 if (memcmp(header+4, ipod->modelname,4)!=0) {
1250 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
1251 header[4],header[5],header[6],header[7], ipod->modelname);
1252 close(infile);
1253 return -1;
1254 }
1255
1256 filechksum = be2int(header);
1257
1258 length = filesize(infile)-8;
1259 } else {
1260 length = filesize(infile);
1261 }
1262 }
1263
1264 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
1265
1266 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
1267 length,newsize);
1268
1269 if (newsize > BUFFER_SIZE) {
1270 fprintf(stderr,"[ERR] Input file too big for buffer\n");
1271 if (infile >= 0) close(infile);
1272 return -1;
1273 }
1274
1275 /* Check if we have enough space */
1276 /* TODO: Check the size of the partition. */
1277 if (ipod->nimages > 1) {
1278 bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset;
1279 if (bytesavailable < newsize) {
1280 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
1281
1282 /* TODO: Implement image movement */
1283 fprintf(stderr,"[ERR] Image movement not yet implemented.\n");
1284 close(infile);
1285 return -1;
1286 }
1287 }
1288
1289#ifdef WITH_BOOTOBJS
1290 if (type == FILETYPE_INTERNAL) {
1291 memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len);
1292 }
1293 else
1294#endif
1295 {
1296 fprintf(stderr,"[INFO] Reading input file...\n");
1297 /* We now know we have enough space, so write it. */
1298 n = read(infile,ipod->sectorbuf,length);
1299 if (n < 0) {
1300 fprintf(stderr,"[ERR] Couldn't read input file\n");
1301 close(infile);
1302 return -1;
1303 }
1304 close(infile);
1305 }
1306
1307 /* Pad the data with zeros */
1308 memset(ipod->sectorbuf+length,0,newsize-length);
1309
1310 if (type==FILETYPE_DOT_IPOD) {
1311 chksum = ipod->modelnum;
1312 for (i = 0; i < length; i++) {
1313 /* add 8 unsigned bits but keep a 32 bit sum */
1314 chksum += ipod->sectorbuf[i];
1315 }
1316
1317 if (chksum == filechksum) {
1318 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
1319 } else {
1320 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
1321 return -1;
1322 }
1323 }
1324
1325
1326 offset = ipod->fwoffset+ipod->ipod_directory[ipod->ososimage].devOffset;
1327
1328 if (ipod->modelnum==62) {
1329
1330 /* 2nd Gen Nano has encrypted firmware, and the sector
1331 preceeding the firmware contains hashes that need to be
1332 preserved. Nano 2G images include these extra 2048 (0x800)
1333 bytes
1334 */
1335
1336 offset -= 0x800;
1337
1338 /* TODO: The above checks need to take into account this 0x800 bytes */
1339 }
1340
1341 if (ipod_seek(ipod, offset) < 0) {
1342 fprintf(stderr,"[ERR] Seek failed\n");
1343 return -1;
1344 }
1345
1346 if ((n = ipod_write(ipod,newsize)) < 0) {
1347 perror("[ERR] Write failed\n");
1348 return -1;
1349 }
1350
1351 if (n < newsize) {
1352 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
1353 ,newsize,n);
1354 return -1;
1355 }
1356 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
1357
1358 /* Now we need to update the "len", "entryOffset" and "chksum" fields
1359
1360 NOTE: On the Nano 2G, the checksum is the checksum of the
1361 unencrypted firmware. But this isn't checked by the NOR
1362 bootloader (there are cryptographic hashes in the
1363 firmware itself), so it doesn't matter that this is
1364 wrong.
1365 */
1366 chksum = 0;
1367 for (i = 0; i < length; i++) {
1368 /* add 8 unsigned bits but keep a 32 bit sum */
1369 chksum += ipod->sectorbuf[i];
1370 }
1371
1372 x = ipod->diroffset % ipod->sector_size;
1373
1374 /* Read directory */
1375 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1376
1377 n=ipod_read(ipod, ipod->sector_size);
1378 if (n < 0) { return -1; }
1379
1380 /* Update entries for image */
1381 p = ipod->sectorbuf + x + (ipod->ososimage * 40);
1382 int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16);
1383 int2le(0, p + 24);
1384 int2le(chksum, p + 28);
1385
1386 /* Write directory */
1387 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1388 n=ipod_write(ipod, ipod->sector_size);
1389 if (n < 0) { return -1; }
1390
1391 return 0;
1392}
1393
1394int read_firmware(struct ipod_t* ipod, char* filename, int type)
1395{
1396 int length;
1397 int i;
1398 int outfile;
1399 int n;
1400 unsigned long offset;
1401 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
1402 unsigned char header[8]; /* Header for .ipod file */
1403
1404 if(ipod->sectorbuf == NULL) {
1405 fprintf(stderr,"[ERR] Buffer not initialized.");
1406 return -1;
1407 }
1408 if (ipod->ipod_directory[ipod->ososimage].entryOffset != 0) {
1409 /* We have a bootloader... */
1410 length = ipod->ipod_directory[ipod->ososimage].entryOffset;
1411 } else {
1412 length = ipod->ipod_directory[ipod->ososimage].len;
1413 }
1414
1415 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
1416
1417 offset = ipod->fwoffset + ipod->ipod_directory[ipod->ososimage].devOffset;
1418 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
1419 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
1420 length,i);
1421
1422 if (ipod->modelnum==62) {
1423 /* 2nd Gen Nano has encrypted firmware, and we need to dump the
1424 sector preceeding the image - it contains hashes */
1425 offset -= 0x800;
1426 length += 0x800;
1427 i += 0x800;
1428 }
1429
1430 if (ipod_seek(ipod, offset)) {
1431 return -1;
1432 }
1433
1434 if ((n = ipod_read(ipod,i)) < 0) {
1435 return -1;
1436 }
1437
1438 if (n < i) {
1439 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
1440 i,n);
1441 return -1;
1442 }
1443
1444 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
1445 if (outfile < 0) {
1446 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
1447 return -1;
1448 }
1449
1450 if (type == FILETYPE_DOT_IPOD) {
1451 chksum = ipod->modelnum;
1452 for (i = 0; i < length; i++) {
1453 /* add 8 unsigned bits but keep a 32 bit sum */
1454 chksum += ipod->sectorbuf[i];
1455 }
1456
1457 int2be(chksum,header);
1458 memcpy(header+4, ipod->modelname,4);
1459
1460 n = write(outfile,header,8);
1461 if (n != 8) {
1462 fprintf(stderr,"[ERR] Write error - %d\n",n);
1463 }
1464 }
1465
1466 n = write(outfile,ipod->sectorbuf,length);
1467 if (n != length) {
1468 fprintf(stderr,"[ERR] Write error - %d\n",n);
1469 }
1470 close(outfile);
1471
1472 return 0;
1473}
1474
1475int read_directory(struct ipod_t* ipod)
1476{
1477 ssize_t n;
1478 int x;
1479 unsigned char* p;
1480 unsigned short version;
1481
1482 ipod->nimages=0;
1483
1484 /* Read firmware partition header (first 512 bytes of disk - but
1485 let's read a whole sector) */
1486
1487 if (ipod_seek(ipod, ipod->start) < 0) {
1488 fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n",
1489 (unsigned int)(ipod->start));
1490 return -1;
1491 }
1492
1493 n=ipod_read(ipod, ipod->sector_size);
1494 if (n < 0) {
1495 fprintf(stderr,"[ERR] ipod_read(ipod,0x%08x) failed in read_directory()\n", ipod->sector_size);
1496 return -1;
1497 }
1498
1499 if (memcmp(ipod->sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) {
1500 fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n");
1501 return -1;
1502 }
1503
1504 if (memcmp(ipod->sectorbuf+0x100,"]ih[",4)!=0) {
1505 fprintf(stderr,"[ERR] Bad firmware directory\n");
1506 return -1;
1507 }
1508
1509 version = le2ushort(ipod->sectorbuf+0x10a);
1510 if ((version != 2) && (version != 3)) {
1511 fprintf(stderr,"[ERR] Unknown firmware format version %04x\n",
1512 version);
1513 }
1514 ipod->diroffset=le2int(ipod->sectorbuf+0x104) + 0x200;
1515
1516 /* diroffset may not be sector-aligned */
1517 x = ipod->diroffset % ipod->sector_size;
1518
1519 /* Read directory */
1520 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
1521 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
1522 return -1;
1523 }
1524
1525 n=ipod_read(ipod, ipod->sector_size);
1526 if (n < 0) {
1527 fprintf(stderr,"[ERR] Read of directory failed.\n");
1528 return -1;
1529 }
1530
1531 p = ipod->sectorbuf + x;
1532
1533 /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
1534 if (p[0] == 0)
1535 {
1536 /* Adjust diroffset */
1537 ipod->diroffset += ipod->sector_size - x;
1538
1539 n=ipod_read(ipod, ipod->sector_size);
1540 if (n < 0) {
1541 fprintf(stderr,"[ERR] Read of directory failed.\n");
1542 return -1;
1543 }
1544 p = ipod->sectorbuf;
1545 }
1546
1547 ipod->ososimage = -1;
1548 while ((ipod->nimages < MAX_IMAGES) && (p < (ipod->sectorbuf + x + 400)) &&
1549 ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) {
1550 p+=4;
1551 if (memcmp(p,"soso",4)==0) {
1552 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS;
1553 ipod->ososimage = ipod->nimages;
1554 } else if (memcmp(p,"crsr",4)==0) {
1555 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC;
1556 } else if (memcmp(p,"dpua",4)==0) {
1557 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD;
1558 } else if (memcmp(p,"kbso",4)==0) {
1559 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSBK;
1560 } else if (memcmp(p,"ebih",4)==0) {
1561 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE;
1562 } else {
1563 fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n",
1564 p[0],p[1],p[2],p[3]);
1565 }
1566 p+=4;
1567 ipod->ipod_directory[ipod->nimages].id=le2int(p);
1568 p+=4;
1569 ipod->ipod_directory[ipod->nimages].devOffset=le2int(p);
1570 p+=4;
1571 ipod->ipod_directory[ipod->nimages].len=le2int(p);
1572 p+=4;
1573 ipod->ipod_directory[ipod->nimages].addr=le2int(p);
1574 p+=4;
1575 ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p);
1576 p+=4;
1577 ipod->ipod_directory[ipod->nimages].chksum=le2int(p);
1578 p+=4;
1579 ipod->ipod_directory[ipod->nimages].vers=le2int(p);
1580 p+=4;
1581 ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p);
1582 p+=4;
1583 ipod->nimages++;
1584 }
1585
1586 if (ipod->ososimage < 0) {
1587 fprintf(stderr,"[ERR] No OSOS image found.\n");
1588 return -1;
1589 }
1590
1591 if ((ipod->nimages > 1) && (version==2)) {
1592 /* The 3g firmware image doesn't appear to have a version, so
1593 let's make one up... Note that this is never written back to the
1594 ipod, so it's OK to do. */
1595
1596 if (ipod->ipod_directory[ipod->ososimage].vers == 0) { ipod->ipod_directory[ipod->ososimage].vers = 3; }
1597
1598 ipod->fwoffset = ipod->start;
1599 } else {
1600 ipod->fwoffset = ipod->start + ipod->sector_size;
1601 }
1602
1603 return 0;
1604}
1605
1606int list_images(struct ipod_t* ipod)
1607{
1608 int i;
1609
1610 if (ipod_verbose) {
1611 printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n");
1612 for (i = 0 ; i < ipod->nimages; i++) {
1613 printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i,
1614 ftypename[ipod->ipod_directory[i].ftype],
1615 ipod->ipod_directory[i].id,
1616 ipod->ipod_directory[i].devOffset,
1617 ipod->ipod_directory[i].len,
1618 ipod->ipod_directory[i].addr,
1619 ipod->ipod_directory[i].entryOffset,
1620 ipod->ipod_directory[i].chksum,
1621 ipod->ipod_directory[i].vers,
1622 ipod->ipod_directory[i].loadAddr,
1623 ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1)));
1624 }
1625 }
1626
1627 printf("\n");
1628 printf("Listing firmware partition contents:\n");
1629 printf("\n");
1630
1631 for (i = 0 ; i < ipod->nimages; i++) {
1632 printf("Image %d:\n",i+1);
1633 switch(ipod->ipod_directory[i].ftype) {
1634 case FTYPE_OSOS:
1635 if (ipod->ipod_directory[i].entryOffset==0) {
1636 printf(" Main firmware - %d bytes\n",
1637 ipod->ipod_directory[i].len);
1638 } else {
1639 printf(" Main firmware - %d bytes\n",
1640 ipod->ipod_directory[i].entryOffset);
1641 printf(" Third-party bootloader - %d bytes\n",
1642 ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset);
1643 }
1644 break;
1645 default:
1646 printf(" %s - %d bytes\n",
1647 ftypename[ipod->ipod_directory[i].ftype],
1648 ipod->ipod_directory[i].len);
1649 }
1650 }
1651 printf("\n");
1652
1653 return 0;
1654}
1655
1656int getmodel(struct ipod_t* ipod, int ipod_version)
1657{
1658 switch (ipod_version) {
1659 case 0x01:
1660 ipod->modelstr="1st or 2nd Generation";
1661 ipod->modelnum = 19;
1662 ipod->modelname = "1g2g";
1663 ipod->targetname = "ipod1g2g";
1664#ifdef WITH_BOOTOBJS
1665 ipod->bootloader = ipod1g2g;
1666 ipod->bootloader_len = LEN_ipod1g2g;
1667#endif
1668 break;
1669 case 0x02:
1670 ipod->modelstr="3rd Generation";
1671 ipod->modelnum = 7;
1672 ipod->modelname = "ip3g";
1673 ipod->targetname = "ipod3g";
1674#ifdef WITH_BOOTOBJS
1675 ipod->bootloader = ipod3g;
1676 ipod->bootloader_len = LEN_ipod3g;
1677#endif
1678 break;
1679 case 0x40:
1680 ipod->modelstr="1st Generation Mini";
1681 ipod->modelnum = 9;
1682 ipod->modelname = "mini";
1683 ipod->targetname = "ipodmini1g";
1684#ifdef WITH_BOOTOBJS
1685 ipod->bootloader = ipodmini1g;
1686 ipod->bootloader_len = LEN_ipodmini1g;
1687#endif
1688 break;
1689 case 0x50:
1690 ipod->modelstr="4th Generation";
1691 ipod->modelnum = 8;
1692 ipod->modelname = "ip4g";
1693 ipod->targetname = "ipod4gray";
1694#ifdef WITH_BOOTOBJS
1695 ipod->bootloader = ipod4g;
1696 ipod->bootloader_len = LEN_ipod4g;
1697#endif
1698 break;
1699 case 0x60:
1700 ipod->modelstr="Photo/Color";
1701 ipod->modelnum = 3;
1702 ipod->modelname = "ipco";
1703 ipod->targetname = "ipodcolor";
1704#ifdef WITH_BOOTOBJS
1705 ipod->bootloader = ipodcolor;
1706 ipod->bootloader_len = LEN_ipodcolor;
1707#endif
1708 break;
1709 case 0x70:
1710 ipod->modelstr="2nd Generation Mini";
1711 ipod->modelnum = 11;
1712 ipod->modelname = "mn2g";
1713 ipod->targetname = "ipodmini2g";
1714#ifdef WITH_BOOTOBJS
1715 ipod->bootloader = ipodmini2g;
1716 ipod->bootloader_len = LEN_ipodmini2g;
1717#endif
1718 break;
1719 case 0xc0:
1720 ipod->modelstr="1st Generation Nano";
1721 ipod->modelnum = 4;
1722 ipod->modelname = "nano";
1723 ipod->targetname = "ipodnano1g";
1724#ifdef WITH_BOOTOBJS
1725 ipod->bootloader = ipodnano1g;
1726 ipod->bootloader_len = LEN_ipodnano1g;
1727#endif
1728 break;
1729 case 0xb0:
1730 ipod->modelstr="Video (aka 5th Generation)";
1731 ipod->modelnum = 5;
1732 ipod->modelname = "ipvd";
1733 ipod->targetname = "ipodvideo";
1734#ifdef WITH_BOOTOBJS
1735 ipod->bootloader = ipodvideo;
1736 ipod->bootloader_len = LEN_ipodvideo;
1737#endif
1738 break;
1739 case 0x100:
1740 ipod->modelstr="2nd Generation Nano";
1741 ipod->modelnum = 62;
1742 ipod->modelname = "nn2x";
1743 ipod->targetname = "ipodnano2g";
1744#ifdef WITH_BOOTOBJS
1745 ipod->bootloader = ipodnano2g;
1746 ipod->bootloader_len = LEN_ipodnano2g;
1747#endif
1748 break;
1749 default:
1750 ipod->modelname = NULL;
1751 ipod->modelnum = 0;
1752 ipod->targetname = NULL;
1753#ifdef WITH_BOOTOBJS
1754 ipod->bootloader = NULL;
1755 ipod->bootloader_len = 0;
1756#endif
1757 return -1;
1758 }
1759 return 0;
1760}
1761
1762/* returns number of found ipods or -1 if no ipods found and permission
1763 * for raw disc access was denied. */
1764int ipod_scan(struct ipod_t* ipod)
1765{
1766 int i;
1767 int n = 0;
1768 int ipod_version;
1769 struct ipod_t ipod_found;
1770 int denied = 0;
1771 int result;
1772
1773 printf("[INFO] Scanning disk devices...\n");
1774
1775 for (i = 0; i <= 25 ; i++) {
1776#ifdef __WIN32__
1777 sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i);
1778#elif defined(linux) || defined (__linux)
1779 sprintf(ipod->diskname,"/dev/sd%c",'a'+i);
1780#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
1781 || defined(__bsdi__) || defined(__DragonFly__)
1782 sprintf(ipod->diskname,"/dev/da%d",i);
1783#elif defined(__APPLE__) && defined(__MACH__)
1784 sprintf(ipod->diskname,"/dev/disk%d",i);
1785#else
1786 #error No disk paths defined for this platform
1787#endif
1788 if ((result = ipod_open(ipod, 1)) < 0) {
1789 if(result == -2) {
1790 denied++;
1791 }
1792 ipod_close(ipod);
1793 continue;
1794 }
1795
1796 if (read_partinfo(ipod,1) < 0) {
1797 ipod_close(ipod);
1798 continue;
1799 }
1800
1801 if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) {
1802 ipod_close(ipod);
1803 continue;
1804 }
1805
1806 if (read_directory(ipod) < 0) {
1807 ipod_close(ipod);
1808 continue;
1809 }
1810
1811 ipod_version=(ipod->ipod_directory[ipod->ososimage].vers>>8);
1812 ipod->ramsize = 0;
1813#ifdef __WIN32__
1814 /* Windows requires the ipod in R/W mode for SCSI Inquiry.
1815 * ipod_reopen_rw does unmount the player on OS X so do this on
1816 * W32 only during scanning. */
1817 ipod_reopen_rw(ipod);
1818#endif
1819 ipod_get_xmlinfo(ipod);
1820 ipod_get_ramsize(ipod);
1821 if (getmodel(ipod,ipod_version) < 0) {
1822 ipod_close(ipod);
1823 continue;
1824 }
1825
1826#ifdef __WIN32__
1827 printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n",
1828 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i);
1829#else
1830 printf("[INFO] Ipod found - %s (\"%s\") - %s\n",
1831 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname);
1832#endif
1833 n++;
1834 /* save the complete ipod_t structure for match. The for loop might
1835 * overwrite it, so we need to restore it later if only one found. */
1836 memcpy(&ipod_found, ipod, sizeof(struct ipod_t));
1837 ipod_close(ipod);
1838 }
1839
1840 if (n==1) {
1841 /* restore the ipod_t structure, it might have been overwritten */
1842 memcpy(ipod, &ipod_found, sizeof(struct ipod_t));
1843 }
1844 else if(n == 0 && denied) {
1845 printf("[ERR] FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied);
1846#ifdef __WIN32__
1847 printf("[ERR] You need to run this program with administrator priviledges!\n");
1848#else
1849 printf("[ERR] You need permissions for raw disc access for this program to work!\n");
1850#endif
1851 }
1852 return (n == 0 && denied) ? -1 : n;
1853}
1854
1855static void put_int32le(uint32_t x, unsigned char* p)
1856{
1857 p[0] = x & 0xff;
1858 p[1] = (x >> 8) & 0xff;
1859 p[2] = (x >> 16) & 0xff;
1860 p[3] = (x >> 24) & 0xff;
1861}
1862
1863int write_dos_partition_table(struct ipod_t* ipod)
1864{
1865 unsigned char* p;
1866 int i, n;
1867 uint32_t type;
1868
1869 /* Only support 512-byte sectors at the moment */
1870 if ( ipod->sector_size != 512 )
1871 {
1872 fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n");
1873 return -1;
1874 }
1875 if(ipod->sectorbuf == NULL) {
1876 fprintf(stderr,"[ERR] Buffer not initialized.");
1877 return -1;
1878 }
1879
1880 /* Firstly zero the entire MBR */
1881 memset(ipod->sectorbuf, 0, ipod->sector_size);
1882
1883 /* Now add the partition info */
1884 for (i=0; i < 4 ; i++)
1885 {
1886 p = ipod->sectorbuf + 0x1be + i*16;
1887
1888 /* Ensure first partition is type 0, and second is 0xb */
1889 if (i==0) { type = 0; }
1890 else if (i==1) { type = 0xb; }
1891 else { type = ipod->pinfo[i].type; }
1892
1893 put_int32le(type, p + 4);
1894 put_int32le(ipod->pinfo[i].start, p + 8);
1895 put_int32le(ipod->pinfo[i].size, p + 12);
1896 }
1897
1898 /* Finally add the magic */
1899 ipod->sectorbuf[0x1fe] = 0x55;
1900 ipod->sectorbuf[0x1ff] = 0xaa;
1901
1902 if (ipod_seek(ipod, 0) < 0) {
1903 fprintf(stderr,"[ERR] Seek failed writing MBR\n");
1904 return -1;
1905 }
1906
1907 /* Write MBR */
1908 if ((n = ipod_write(ipod, ipod->sector_size)) < 0) {
1909 perror("[ERR] Write failed\n");
1910 return -1;
1911 }
1912
1913 return 0;
1914}
1915
1916/* Get the XML Device Information, as documented here:
1917
1918 http://www.ipodlinux.org/wiki/Device_Information
1919*/
1920
1921int ipod_get_xmlinfo(struct ipod_t* ipod)
1922{
1923 unsigned char hdr[255];
1924 unsigned char buf[255];
1925 char* p;
1926 int psize;
1927 int npages;
1928 int i;
1929
1930 if (ipod_scsi_inquiry(ipod, 0xc0, buf, sizeof(buf)) < 0)
1931 {
1932 fprintf(stderr,"[ERR] Sending SCSI Command failed.\n");
1933 return -1;
1934 }
1935
1936 /* Reading directly into hdr[] causes problems (for an unknown reason) on
1937 win32 */
1938 memcpy(hdr, buf, sizeof(hdr));
1939
1940 npages = hdr[3];
1941
1942 psize = npages * 0xf8; /* Hopefully this is enough. */
1943
1944 ipod->xmlinfo = malloc(psize);
1945 ipod->xmlinfo_len = 0;
1946
1947 if (ipod->xmlinfo == NULL) {
1948 fprintf(stderr,"[ERR] Could not allocate RAM for xmlinfo\n");
1949 return -1;
1950 }
1951
1952 p = ipod->xmlinfo;
1953
1954 for (i=0; i < npages; i++) {
1955 if (ipod_scsi_inquiry(ipod, hdr[i+4], buf, sizeof(buf)) < 0) {
1956 fprintf(stderr,"[ERR] Sending SCSI Command failed.\n");
1957 return -1;
1958 }
1959
1960 if ((buf[3] + ipod->xmlinfo_len) > psize) {
1961 fprintf(stderr,"[ERR] Ran out of memory reading xmlinfo\n");
1962 free(ipod->xmlinfo);
1963 ipod->xmlinfo = NULL;
1964 ipod->xmlinfo_len = 0;
1965 return -1;
1966 }
1967
1968 memcpy(p, buf + 4, buf[3]);
1969 p += buf[3];
1970 ipod->xmlinfo_len += buf[3];
1971 }
1972
1973 /* NULL-terminate the XML info */
1974 *p = 0;
1975
1976 fprintf(stderr,"[INFO] Read XML info (%d bytes)\n",ipod->xmlinfo_len);
1977
1978 return 0;
1979}
1980
1981void ipod_get_ramsize(struct ipod_t* ipod)
1982{
1983 const char needle[] = "<key>RAM</key>\n<integer>";
1984 char* p;
1985
1986 if (ipod->xmlinfo == NULL)
1987 return;
1988
1989 p = strstr(ipod->xmlinfo, needle);
1990
1991 if (p) {
1992 ipod->ramsize = atoi(p + sizeof(needle) - 1);
1993 }
1994}
1995