summaryrefslogtreecommitdiffstats
path: root/tools/ipodpatcher/ipodpatcher.c
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-12-13 08:57:06 +0000
committerDave Chapman <dave@dchapman.com>2006-12-13 08:57:06 +0000
commit45895df35d9e7bdd7389b533aba344fbbbe4b42b (patch)
tree2844c4c46f28162c6d7c2993af0dbb4c35b9f669 /tools/ipodpatcher/ipodpatcher.c
parent29a13872d747ec8754195ed607ca1627b577e2e2 (diff)
downloadrockbox-45895df35d9e7bdd7389b533aba344fbbbe4b42b.tar.gz
rockbox-45895df35d9e7bdd7389b533aba344fbbbe4b42b.tar.bz2
rockbox-45895df35d9e7bdd7389b533aba344fbbbe4b42b.zip
Initial CVS commit ofipodpatcher - v0.2 release from 16 Jan 2006
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11744 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'tools/ipodpatcher/ipodpatcher.c')
-rw-r--r--tools/ipodpatcher/ipodpatcher.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/tools/ipodpatcher/ipodpatcher.c b/tools/ipodpatcher/ipodpatcher.c
new file mode 100644
index 0000000000..7d311c835a
--- /dev/null
+++ b/tools/ipodpatcher/ipodpatcher.c
@@ -0,0 +1,443 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Dave Chapman
+ *
+ * error(), lock_volume() and unlock_volume() functions and inspiration taken
+ * from:
+ * RawDisk - Direct Disk Read/Write Access for NT/2000/XP
+ * Copyright (c) 2003 Jan Kiszka
+ * http://www.stud.uni-hannover.de/user/73174/RawDisk/
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <windows.h>
+#include <winioctl.h>
+
+#include "parttypes.h"
+
+/* Windows requires the buffer for disk I/O to be aligned in memory on a
+ multiple of the disk volume size - so we use a single global variable
+ and initialise it in main()
+*/
+unsigned char* sectorbuf;
+
+char* get_parttype(int pt)
+{
+ int i;
+ static char unknown[]="Unknown";
+
+ i=0;
+ while (parttypes[i].name != NULL) {
+ if (parttypes[i].type == pt) {
+ return (parttypes[i].name);
+ }
+ i++;
+ }
+
+ return unknown;
+}
+
+void error(char* msg)
+{
+ char* pMsgBuf;
+
+ printf(msg);
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pMsgBuf,
+ 0, NULL);
+ printf(pMsgBuf);
+ LocalFree(pMsgBuf);
+}
+
+int lock_volume(HANDLE hDisk)
+{
+ DWORD dummy;
+
+ return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0,
+ &dummy, NULL);
+}
+
+int unlock_volume(HANDLE hDisk)
+{
+ DWORD dummy;
+
+ return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0,
+ &dummy, NULL);
+}
+
+off_t filesize(int fd) {
+ struct stat buf;
+
+ if (fstat(fd,&buf) < 0) {
+ perror("[ERR] Checking filesize of input file");
+ return -1;
+ } else {
+ return(buf.st_size);
+ }
+}
+
+
+/* Size of buffer for disk I/O */
+#define BUFFER_SIZE 32*1024
+
+/* Partition table parsing code taken from Rockbox */
+
+#define SECTOR_SIZE 512
+
+struct partinfo {
+ unsigned long start; /* first sector (LBA) */
+ unsigned long size; /* number of sectors */
+ unsigned char type;
+};
+
+#define BYTES2INT32(array,pos) \
+ ((long)array[pos] | ((long)array[pos+1] << 8 ) | \
+ ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
+
+void display_partinfo(struct partinfo* pinfo)
+{
+ int i;
+
+ printf("Part Start Sector End Sector Size (MB) Type\n");
+ for ( i = 0; i < 4; i++ ) {
+ if (pinfo[i].start != 0) {
+ printf(" %d %10ld %10ld %10.1f %s (0x%02x)\n",i,pinfo[i].start,pinfo[i].start+pinfo[i].size-1,pinfo[i].size/2048.0,get_parttype(pinfo[i].type),pinfo[i].type);
+ }
+ }
+}
+
+
+int read_partinfo(HANDLE dh, struct partinfo* pinfo)
+{
+ int i;
+ unsigned char sector[SECTOR_SIZE];
+ unsigned long count;
+
+ if (!ReadFile(dh, sector, SECTOR_SIZE, &count, NULL)) {
+ error(" Error reading from disk: ");
+ return -1;
+ }
+
+ /* check that the boot sector is initialized */
+ if ( (sector[510] != 0x55) ||
+ (sector[511] != 0xaa)) {
+ fprintf(stderr,"Bad boot sector signature\n");
+ return 0;
+ }
+
+ if (memcmp(&sector[71],"iPod",4) != 0) {
+ fprintf(stderr,"Drive is not an iPod, aborting\n");
+ return -1;
+ }
+
+ /* parse partitions */
+ for ( i = 0; i < 4; i++ ) {
+ unsigned char* ptr = sector + 0x1be + 16*i;
+ pinfo[i].type = ptr[4];
+ pinfo[i].start = BYTES2INT32(ptr, 8);
+ pinfo[i].size = BYTES2INT32(ptr, 12);
+
+ /* extended? */
+ if ( pinfo[i].type == 5 ) {
+ /* not handled yet */
+ }
+ }
+
+ return 0;
+}
+
+int disk_read(HANDLE dh, int outfile,unsigned long start, unsigned long count)
+{
+ int res;
+ unsigned long n;
+ int bytesleft;
+ int chunksize;
+
+ fprintf(stderr,"[INFO] Seeking to sector %ld\n",start);
+
+ if (SetFilePointer(dh, start*SECTOR_SIZE, NULL, FILE_BEGIN)==0xffffffff) {
+ error(" Seek error ");
+ return -1;
+ }
+
+ fprintf(stderr,"[INFO] Writing %ld sectors to output file\n",count);
+
+ bytesleft = count * SECTOR_SIZE;
+ while (bytesleft > 0) {
+ if (bytesleft > BUFFER_SIZE) {
+ chunksize = BUFFER_SIZE;
+ } else {
+ chunksize = bytesleft;
+ }
+
+ if (!ReadFile(dh, sectorbuf, chunksize, &n, NULL)) {
+ error("[ERR] read in disk_read");
+ return -1;
+ }
+
+ if (n < chunksize) {
+ fprintf(stderr,"[ERR] Short read in disk_read() - requested %d, got %lu\n",chunksize,n);
+ return -1;
+ }
+
+ bytesleft -= n;
+
+ res = write(outfile,sectorbuf,n);
+
+ if (res < 0) {
+ perror("[ERR] write in disk_read");
+ return -1;
+ }
+
+ if (res != n) {
+ fprintf(stderr,"Short write - requested %d, received %d - aborting.\n",SECTOR_SIZE,res);
+ return -1;
+ }
+ }
+
+ fprintf(stderr,"[INFO] Done.\n");
+ return 0;
+}
+
+int disk_write(HANDLE dh, int infile,unsigned long start)
+{
+ unsigned long res;
+ int n;
+ int bytesread;
+ int byteswritten = 0;
+ int eof;
+ int padding = 0;
+
+ if (SetFilePointer(dh, start*SECTOR_SIZE, NULL, FILE_BEGIN)==0xffffffff) {
+ error(" Seek error ");
+ return -1;
+ }
+
+ fprintf(stderr,"[INFO] Writing input file to device\n");
+ bytesread = 0;
+ eof = 0;
+ while (!eof) {
+ n = read(infile,sectorbuf,BUFFER_SIZE);
+
+ if (n < 0) {
+ perror("[ERR] read in disk_write");
+ return -1;
+ }
+
+ if (n < BUFFER_SIZE) {
+ eof = 1;
+ /* We need to pad the last write to a multiple of SECTOR_SIZE */
+ if ((n % SECTOR_SIZE) != 0) {
+ padding = (SECTOR_SIZE-(n % SECTOR_SIZE));
+ n += padding;
+ }
+ }
+
+ bytesread += n;
+
+ if (!WriteFile(dh, sectorbuf, n, &res, NULL)) {
+ error(" Error writing to disk: ");
+ fprintf(stderr,"Bytes written: %d\n",byteswritten);
+ return -1;
+ }
+
+ if (res != n) {
+ fprintf(stderr,"Short write - requested %d, received %lu - aborting.\n",n,res);
+ return -1;
+ }
+
+ byteswritten += res;
+ }
+
+ fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",byteswritten-padding,padding);
+ return 0;
+}
+
+
+void print_usage(void) {
+ fprintf(stderr,"Usage: ipodpatcher [-i|r|w] DISKNO [file]\n");
+ fprintf(stderr," -i Display iPod's partition information (default)\n");
+ fprintf(stderr," -r Read firmware partition to file\n");
+ fprintf(stderr," -w Write file to firmware partition\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your ipod's hard disk.\n");
+ fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk0, the next disk\n");
+ fprintf(stderr,"will be disk 1 etc. ipodpatcher will refuse to access a disk unless it\n");
+ fprintf(stderr,"can identify it as being an ipod.\n");
+ fprintf(stderr,"\n");
+}
+
+enum {
+ NONE,
+ SHOW_INFO,
+ READ,
+ WRITE
+};
+
+int main(int argc, char* argv[])
+{
+ int i;
+ struct partinfo pinfo[4]; /* space for 4 partitions on 1 drive */
+ int res;
+ int outfile;
+ int infile;
+ int mode = SHOW_INFO;
+ int p = 0;
+ int diskno = -1;
+ char diskname[32];
+ HANDLE dh;
+ char* filename = NULL;
+ off_t inputsize;
+
+ fprintf(stderr,"ipodpatcher v0.2 - (C) Dave Chapman 2005\n");
+ fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n");
+ fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+ if (argc <= 1) {
+ print_usage();
+ return 1;
+ }
+
+ i = 1;
+ while (i < argc) {
+ if (strncmp(argv[i],"-i",2)==0) {
+ mode=SHOW_INFO;
+ } else if (strncmp(argv[i],"-r",2)==0) {
+ mode = READ;
+ } else if (strncmp(argv[i],"-w",2)==0) {
+ mode = WRITE;
+ } else {
+ if (argv[i][0] == '-') {
+ fprintf(stderr,"Unknown option %s\n",argv[i]);
+ return 1;
+ } else {
+ if (diskno == -1) {
+ diskno = atoi(argv[i]);
+ } else if (filename==NULL) {
+ filename = argv[i];
+ } else {
+ fprintf(stderr,"Too many arguments: %s\n",argv[i]);
+ return 1;
+ }
+ }
+ }
+ i++;
+ }
+
+ if ((mode==NONE) || (diskno==-1) || ((mode!=SHOW_INFO) && (filename==NULL))) {
+ print_usage();
+ return 1;
+ }
+
+ snprintf(diskname,sizeof(diskname),"\\\\.\\PhysicalDrive%d",diskno);
+
+ fprintf(stderr,"[INFO] Reading partition table from %s\n",diskname);
+
+ /* The ReadFile function requires a memory buffer aligned to a multiple of
+ the disk sector size. */
+ sectorbuf = VirtualAlloc(NULL, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE);
+ if (sectorbuf == NULL) {
+ error(" Error allocating a buffer: ");
+ return 2;
+ }
+
+ dh = CreateFile(diskname, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
+
+ if (dh == INVALID_HANDLE_VALUE) {
+ error(" Error opening disk: ");
+ return 2;
+ }
+
+ if (!lock_volume(dh)) {
+ error(" Error locking disk: ");
+ return 2;
+ }
+
+ if (read_partinfo(dh,pinfo) < 0) {
+ return 2;
+ }
+
+ display_partinfo(pinfo);
+
+ if (pinfo[p].start==0) {
+ fprintf(stderr,"[ERR] Specified partition (%d) does not exist:\n",p);
+ display_partinfo(pinfo);
+ return 3;
+ }
+
+ if (mode==READ) {
+ outfile = open(filename,O_CREAT|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE);
+ if (outfile < 0) {
+ perror(filename);
+ return 4;
+ }
+
+ res = disk_read(dh,outfile,pinfo[p].start,pinfo[p].size);
+
+ close(outfile);
+ } else if (mode==WRITE) {
+ /* Close existing file and re-open for writing */
+ unlock_volume(dh);
+ CloseHandle(dh);
+
+ dh = CreateFile(diskname, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
+
+ if (dh == INVALID_HANDLE_VALUE) {
+ error(" Error opening disk: ");
+ return 2;
+ }
+
+ if (!lock_volume(dh)) {
+ error(" Error locking disk: ");
+ return 2;
+ }
+
+ infile = open(filename,O_RDONLY|O_BINARY);
+ if (infile < 0) {
+ perror(filename);
+ return 2;
+ }
+
+ /* Check filesize is <= partition size */
+ inputsize=filesize(infile);
+ if (inputsize > 0) {
+ if (inputsize <= (pinfo[p].size*SECTOR_SIZE)) {
+ fprintf(stderr,"[INFO] Input file is %lu bytes\n",inputsize);
+ res = disk_write(dh,infile,pinfo[p].start);
+ } else {
+ fprintf(stderr,"[ERR] File is too large for firmware partition, aborting.\n");
+ }
+ }
+
+ close(infile);
+ }
+
+ unlock_volume(dh);
+ CloseHandle(dh);
+ return 0;
+}