/* (C) 2003 XDA Developers itsme@xs4all.nl * * $Header: /var/db/cvs/xda-devtools/itsutils/src/pdocread.cpp,v 1.6 2005/06/12 23:33:58 itsme Exp $ * * this file implements reading from and writing to * the m-sys tffs chip on the sp3 * * * the disk device can be specified with: * - a handle -h 0x12345678 * - a storename + partitionname -s TrueFFS -p Part00 * - a devicename -d DSK1: * * a device can be read in the following ways: * - wince diskread -w * - tffs diskread *default* * - tffs binary partition read -n * - tffs one time programmable read -o * * todo: problem reading 0x10000 from bdk1 on sp3 * ... issue is that sector size is not the same everywhere * * todo: change interface, such that data can be read in other sizes than only sector sized chunks. * * todo: pdocread 0x800 0x1aff800 os.nb * sometimes crashes with activesync error. * */ #include #include "ItsUtils.h" #include "tffsreader.h" #include "dllversion.h" #include "debug.h" #include "args.h" #include #include "stringutils.h" #include "vectorutils.h" #include #define READCHUNKSIZE 65536 bool g_ignoreerror= false; bool g_bVerbose= false; class TFFSDeviceSpec; void FindDeviceSize(const TFFSDeviceSpec& tffs); bool CopyTFFSToFile(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, DWORD dwLength, const std::string& outfilename); bool HexdumpTFFSToStdout(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, DWORD dwLength); bool CopyFileToTFFS(const std::string& infilename, ULONGLONG llFileOffset, const TFFSDeviceSpec& tffs, ULONGLONG llOffset, DWORD dwLength); bool ITReadDisk(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead); bool ITWriteDisk(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, const BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead); bool ITTFFSGetInfo(const TFFSDeviceSpec& tffs, DWORD &dwSectorSize, ULONGLONG &llDiskSize); bool ITLogDiskInfo(const TFFSDeviceSpec& tffs); bool ITGetStoreMgrList(std::vector& list); bool ITGetPartitionList(const TFFSDeviceSpec& tffs, std::vector& list); bool ITGetSTRGHandleList(std::vector &list); std::string sizestring(ULONGLONG size); ULONGLONG GetFileSize(const std::string& filename); bool ListDevices(); bool ListSTRGHandles(); bool GetDefaultDiskName(TFFSDeviceSpec& tffs); bool GetDefaultDiskHandle(TFFSDeviceSpec& tffs); // may contain: // "", "DSK1:", 1 // "TRUEFFS_DOC", "Part01", 1 class TFFSDeviceSpec { public: TFFSDeviceSpec() : dwBinaryPartitionNr(BP_TFFSSECTOR), dwHandle(0), dwSectorSize(0), llDiskSize(0) {} std::string devicename; std::string partitionname; DWORD dwBinaryPartitionNr; DWORD dwHandle; DWORD dwSectorSize; ULONGLONG llDiskSize; void GetDiskInfo() { if (dwSectorSize==0 && !ITTFFSGetInfo(*this, dwSectorSize, llDiskSize)) { debug("WARNING: using default 512 bytes for sectorsize\n"); dwSectorSize= 512; llDiskSize= 0; } } bool ReadTFFS(ULONGLONG llOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead) const { if (g_bVerbose) printf("reading 0x%x bytes from 0x%I64x with %hs %hs\n", dwBytesWanted, llOffset, accessname().c_str(), description().c_str()); return ITReadDisk(*this, llOffset, buffer, dwBytesWanted, pdwNumberOfBytesRead); } bool WriteTFFS(ULONGLONG llOffset, const BYTE *buffer, DWORD dwBufferSize, DWORD *pdwNumberOfBytesWritten) const { if (g_bVerbose) printf("writing 0x%x bytes to 0x%I64x with %hs %hs\n", dwBufferSize, llOffset, accessname().c_str(), description().c_str()); return ITWriteDisk(*this, llOffset, buffer, dwBufferSize, pdwNumberOfBytesWritten); } std::string accessname() const { switch(dwBinaryPartitionNr) { case BP_TFFSSECTOR: return "tffsread"; case BP_WINCESECTOR: return "winceread"; case BP_OTPSECTOR: return "otpread"; default: return stringformat("bdk%d", dwBinaryPartitionNr); } } std::string description() const { if (partitionname.empty()) return devicename; else return devicename + "/" + partitionname; } }; void usage(const std::string& cmd) { if (cmd.substr(0,8)=="pdocread") { printf("Usage: pdocread [options] start [ length [ filename ] ]\n"); printf(" when no length is specified, 512 bytes are assumed\n"); printf(" when no filename is specified, a hexdump is printed\n"); } else if (cmd.substr(0,9)=="pdocwrite") { printf("Usage: pdocwrite [options] filename [ start [ length ] ]\n"); printf(" when no start offset is specified, the start of the device is assumed\n"); printf(" when no length is specified, the whole file ( minus the -s OFS ) is written\n"); } else { printf("ERROR: expecting to be called either 'pdocread' or 'pdocwrite' - %hs\n", cmd.c_str()); return; } printf(" -t : find exact disk size\n"); printf(" -l : list all diskdevices\n"); printf(" -v : be verbose\n"); printf(" -s OFS : seek into source file ( for writing only )\n"); printf(" -b SIZE: specify sectorsize to use when accessing disk\n"); printf("Source:\n"); printf(" -d NAME : devicename or storename\n"); printf(" -p NAME : partitionname\n"); printf(" -h HANDLE : directly specify handle\n"); printf(" either specify -d and optionally -p, or specify -h\n"); printf("Method:\n"); printf(" -n NUM : binarypartition number ( normal p if omitted )\n"); printf(" -w : read via windows disk api\n"); printf(" -o : read OTP area\n"); printf("if the filename is omitted, the data is hexdumped to stdout\n"); printf("if no length is specified, 512 bytes are printed\n"); printf("\n"); printf("numbers can be specified as hex (ex: 0x8000) or decimal (ex: 32768)\n"); } std::string GetFileFromPath(const std::string& name) { size_t lastslash= name.find_last_of("\\/"); if (lastslash==name.npos) return name; return name.substr(lastslash+1); } int main( int argc, char *argv[]) { DebugStdOut(); std::string cmd= tolower(GetFileFromPath(argv[0])); bool bWriting= false; if (cmd.substr(0,8)=="pdocread") { bWriting= false; } else if (cmd.substr(0,9)=="pdocwrite") { bWriting= true; } else { printf("ERROR: don't know what I am : %hs\n", cmd.c_str()); return 1; } ULONGLONG llDiskOffset=0; DWORD dwLength=0; std::string filename; TFFSDeviceSpec tffs; ULONGLONG llFileOffset= 0; bool bDoListDevices= false; bool bDoFindDeviceSize= false; StringList args; for (int i=1 ; i list; if (!ITGetStoreMgrList(list)) { error("GetDefaultDiskName: ITGetStoreMgrList"); return false; } if (list.empty()) { if (g_bVerbose) debug("ERROR: could not find default device in \\StoreMgr\n"); return false; } for (size_t iStore=0 ; iStore plist; tffs.devicename= ToString(list[iStore].szDeviceName); if (!ITGetPartitionList(tffs, plist)) { continue; } if (plist.empty()) { continue; } tffs.partitionname= ToString(plist[0].szPartitionName); return true; } if (g_bVerbose) debug("WARNING: could not find device with partitions to use as default\n"); return false; } bool GetDefaultDiskHandle(TFFSDeviceSpec& tffs) { std::vector hlist; if (!ITGetSTRGHandleList(hlist)) { error("ITGetSTRGHandleList"); return false; } if (hlist.empty()) { if (g_bVerbose) debug("ERROR: could not find default device in STRG handle list\n"); return false; } tffs.dwHandle= hlist[0]; return true; } bool ListDevices() { std::vector list; if (!ITGetStoreMgrList(list)) { error("ListDevices: ITGetStoreMgrList"); return false; } for (size_t iStore=0 ; iStore plist; TFFSDeviceSpec tffs; tffs.devicename= ToString(list[iStore].szDeviceName); if (!ITGetPartitionList(tffs, plist)) { error("ITGetPartitionList"); continue; } for (size_t iPart=0 ; iPart hlist; if (!ITGetSTRGHandleList(hlist)) { error("ITGetSTRGHandleList"); return false; } if (hlist.size()) { debug("STRG handles: "); for (size_t i=0 ; itffs.dwSectorSize) dwWanted &= ~(tffs.dwSectorSize-1); // if not at sector boundary, or less than one sector to read, first fill buffer if (llOffset&(tffs.dwSectorSize-1) || dwLength=dwHigh) return 0; if (dwLow+1==dwHigh) return dwLow; DWORD dwMiddle = (dwLow + dwHigh)/2; if (testSector(tffs, dwMiddle)) return findInRange(tffs, dwMiddle, dwHigh); else return findInRange(tffs, dwLow, dwMiddle); } std::string sizestring(ULONGLONG size) { return (size>(ULONGLONG)1024*1024*1024*1024)? stringformat("%6.2fT", (double)size/((ULONGLONG)1024*1024*1024*1024)) : (size>(ULONGLONG)1024*1024*1024)? stringformat("%6.2fG", (double)size/((ULONGLONG)1024*1024*1024)) : (size>(ULONGLONG)1024*1024)? stringformat("%6.2fM", (double)size/((ULONGLONG)1024*1024)) : (size>(ULONGLONG)1024)? stringformat("%6.2fk", (double)size/(ULONGLONG)1024) : stringformat("%6.2f", (double)size); } void FindDeviceSize(const TFFSDeviceSpec& tffs) { DWORD sectornr= 0x40000000/tffs.dwSectorSize; // random initial estimate : 1G if (testSector(tffs, sectornr)) { do { sectornr *= 2; } while (testSector(tffs, sectornr)); sectornr /= 2; } else { do { sectornr /= 2; } while (sectornr && !testSector(tffs, sectornr)); } sectornr= findInRange(tffs, sectornr, sectornr*2); ULONGLONG size= UInt32x32To64(sectornr+1,tffs.dwSectorSize); debug("real nr of sectors: %d - %hsbyte (0x%I64x)\n", sectornr+1, sizestring(size).c_str(), size); } bool HexdumpTFFSToStdout(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, DWORD dwLength) { debug("HexdumpTFFSToStdout(0x%I64x, 0x%x)\n", llOffset, dwLength); ByteVector buffer; buffer.resize(READCHUNKSIZE); while (dwLength) { DWORD dwWanted= min(dwLength+((DWORD)llOffset&(tffs.dwSectorSize-1)), buffer.size()); if (dwWanted&(tffs.dwSectorSize-1)) dwWanted = (dwWanted|(tffs.dwSectorSize-1))+1; DWORD dwNumberOfBytesRead; if (!tffs.ReadTFFS(llOffset, vectorptr(buffer), dwWanted, &dwNumberOfBytesRead)) return false; if (dwNumberOfBytesRead==0) { debug("WARNING: no more data\n"); break; } DWORD dwBufferOffset= (DWORD)llOffset&(tffs.dwSectorSize-1); DWORD dwNeeded= min(dwLength, dwNumberOfBytesRead); debug("ofs=%I64x len=%08lx want=%08lx read=%08lx buf=%08lx need=%08lx\n", llOffset, dwLength, dwWanted, dwNumberOfBytesRead, dwBufferOffset, dwNeeded); bighexdump((DWORD)llOffset, ByteVector(vectorptr(buffer)+dwBufferOffset, vectorptr(buffer)+dwBufferOffset+dwNeeded)); dwLength -= dwNeeded; llOffset += dwNeeded; } return true; } bool CopyTFFSToFile(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, DWORD dwLength, const std::string& outfilename) { debug("CopyTFFSToFile(0x%I64x, 0x%x, %s)\n", llOffset, dwLength, outfilename.c_str()); HANDLE hDest = CreateFile(outfilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hDest) { error("Unable to open host/destination file"); return false; } ByteVector buffer; buffer.resize(READCHUNKSIZE); while (dwLength) { DWORD dwWanted= min(dwLength+((DWORD)llOffset&(tffs.dwSectorSize-1)), buffer.size()); if (dwWanted&(tffs.dwSectorSize-1)) dwWanted = (dwWanted|(tffs.dwSectorSize-1))+1; DWORD dwNumberOfBytesRead; if (!tffs.ReadTFFS(llOffset&~(tffs.dwSectorSize-1), vectorptr(buffer), dwWanted, &dwNumberOfBytesRead)) return false; if (dwNumberOfBytesRead==0) { debug("WARNING: no more data\n"); break; } DWORD dwBufferOffset= (DWORD)llOffset&(tffs.dwSectorSize-1); DWORD dwNeeded= min(dwLength, dwNumberOfBytesRead); DWORD dwNumWritten; if (!WriteFile(hDest, vectorptr(buffer)+dwBufferOffset, dwNeeded, &dwNumWritten, NULL)) { error("Error Writing file"); return false; } dwLength -= dwNeeded; llOffset += dwNeeded; } CloseHandle (hDest); return true; } ULONGLONG GetFileSize(const std::string& filename) { HANDLE hSrc = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hSrc) { error("Unable to open file %hs", filename.c_str()); return 0; } ULARGE_INTEGER uli; uli.LowPart= GetFileSize(hSrc, &uli.HighPart); CloseHandle(hSrc); return uli.QuadPart; } //****************************************************************************** // functions calling cerapiinvoke. bool ITWriteDisk(const TFFSDeviceSpec& tffs, ULONGLONG llDiskOffset, const BYTE *buffer, DWORD dwBufferSize, DWORD *pdwNumberOfBytesWritten) { int insize= sizeof(WriteDiskParams)+dwBufferSize; WriteDiskParams *inbuf= (WriteDiskParams*)LocalAlloc(LPTR, insize); DWORD outsize=0; WriteDiskResult *outbuf=NULL; wcsncpy(inbuf->szDeviceName, ToWString(tffs.devicename).c_str(), DEVICENAMESIZE); wcsncpy(inbuf->szPartitionName, ToWString(tffs.partitionname).c_str(), PARTITIONNAMESIZE); inbuf->dwBinaryPartitionNr= tffs.dwBinaryPartitionNr; inbuf->hDisk= tffs.dwHandle; inbuf->llOffset= llDiskOffset; inbuf->dwSize= dwBufferSize; inbuf->dwSectorSize= tffs.dwSectorSize; memcpy(inbuf->buffer, buffer, dwBufferSize); HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITWriteDisk", insize, (BYTE*)inbuf, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { error(res, "ITWriteDisk"); return false; } if (outbuf==NULL) { debug("ERROR: ITWriteDisk: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } *pdwNumberOfBytesWritten= outbuf->dwNumberOfBytesWritten; LocalFree(outbuf); return true; } // ........................................ // reading bool ITReadDisk(const TFFSDeviceSpec& tffs, ULONGLONG llOffset, BYTE *buffer, DWORD dwBytesWanted, DWORD *pdwNumberOfBytesRead) { ReadDiskParams inbuf; DWORD outsize=0; ReadDiskResult *outbuf=NULL; if (g_bVerbose) debug("readdisk('%s', '%s', %d, %08lx, %08lx)\n", tffs.devicename.c_str(), tffs.partitionname.c_str(), tffs.dwBinaryPartitionNr, llOffset, dwBytesWanted); wcsncpy(inbuf.szDeviceName, ToWString(tffs.devicename).c_str(), DEVICENAMESIZE); wcsncpy(inbuf.szPartitionName, ToWString(tffs.partitionname).c_str(), PARTITIONNAMESIZE); inbuf.dwBinaryPartitionNr= tffs.dwBinaryPartitionNr; inbuf.hDisk= tffs.dwHandle; inbuf.llOffset= llOffset; inbuf.dwSize= dwBytesWanted; inbuf.dwSectorSize= tffs.dwSectorSize; outbuf= NULL; outsize= 0; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITReadDisk", sizeof(ReadDiskParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITReadDisk"); return false; } if (outbuf==NULL) { if (!g_ignoreerror) debug("ERROR: ITReadDisk: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } if (g_bVerbose) debug("readdisk: %08lx actually read\n", outbuf->dwNumberOfBytesRead); memcpy(buffer, outbuf->buffer, outbuf->dwNumberOfBytesRead); *pdwNumberOfBytesRead= outbuf->dwNumberOfBytesRead; LocalFree(outbuf); return true; } //.................................... bool ITGetStoreMgrList(std::vector& list) { DWORD outsize=0; GetStoreMgrListResult *outbuf=NULL; outbuf= NULL; outsize= 0; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITGetStoreMgrList", 0, NULL, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITGetStoreMgrList"); return false; } if (outbuf==NULL) { debug("ERROR: ITGetStoreMgrList: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } list.clear(); for (int i=0 ; inStores ; i++) list.push_back(outbuf->storeInfo[i]); LocalFree(outbuf); return true; } bool ITGetPartitionList(const TFFSDeviceSpec& tffs, std::vector& list) { GetPartitionListParams inbuf; DWORD outsize=0; GetPartitionListResult *outbuf=NULL; wcsncpy(inbuf.szDeviceName, ToWString(tffs.devicename).c_str(), DEVICENAMESIZE); inbuf.hStore= tffs.dwHandle; outbuf= NULL; outsize= 0; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITGetPartitionList", sizeof(GetPartitionListParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITGetPartitionList"); return false; } if (outbuf==NULL) { debug("ERROR: ITGetPartitionList: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } list.clear(); for (int i=0 ; inPartitions ; i++) list.push_back(outbuf->partInfo[i]); LocalFree(outbuf); return true; } bool ITGetSTRGHandleList(std::vector &list) { DWORD outsize=0; GetSTRGHandleListResult *outbuf=NULL; outbuf= NULL; outsize= 0; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITGetSTRGHandleList", 0, NULL, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITGetSTRGHandleList"); return false; } if (outbuf==NULL) { debug("ERROR: ITGetSTRGHandleList: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } list.clear(); for (int i=0 ; inHandles ; i++) list.push_back(outbuf->handle[i]); LocalFree(outbuf); return true; } bool ITTFFSGetInfo(const TFFSDeviceSpec& tffs, DWORD &dwSectorSize, ULONGLONG &llDiskSize) { TFFSGetInfoParams inbuf; DWORD outsize=0; TFFSGetInfoResult *outbuf=NULL; wcsncpy(inbuf.szDeviceName, ToWString(tffs.devicename).c_str(), DEVICENAMESIZE); wcsncpy(inbuf.szPartitionName, ToWString(tffs.partitionname).c_str(), PARTITIONNAMESIZE); inbuf.dwBinaryPartitionNr= tffs.dwBinaryPartitionNr; inbuf.hDisk= tffs.dwHandle; outbuf= NULL; outsize= 0; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITTFFSGetInfo", sizeof(TFFSGetInfoParams), (BYTE*)&inbuf, &outsize, (BYTE**)&outbuf, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITTFFSGetInfo"); return false; } if (outbuf==NULL) { debug("ERROR: ITTFFSGetInfo: outbuf==NULL\n"); SetLastError(ERROR_INTERNAL_ERROR); return false; } dwSectorSize= outbuf->dwSectorSize; llDiskSize= outbuf->llDiskSize; debug("\n%d partitions, %d binary partitions\ncustomerid=%08lx uniqueid=%s\n", outbuf->dwNrPartitions, outbuf->dwNrofBinPartitions, outbuf->customerid, hexdump((BYTE*)&outbuf->uniqueid, 16).c_str()); LocalFree(outbuf); return true; } bool ITLogDiskInfo(const TFFSDeviceSpec& tffs) { LogDiskInfoParams inbuf; DWORD outsize=0; wcsncpy(inbuf.szDeviceName, ToWString(tffs.devicename).c_str(), DEVICENAMESIZE); wcsncpy(inbuf.szPartitionName, ToWString(tffs.partitionname).c_str(), PARTITIONNAMESIZE); inbuf.dwBinaryPartitionNr= tffs.dwBinaryPartitionNr; inbuf.hDisk= tffs.dwHandle; HRESULT res= CeRapiInvoke(L"\\Windows\\ItsUtils.dll", L"ITLogDiskInfo", sizeof(TFFSGetInfoParams), (BYTE*)&inbuf, &outsize, NULL, NULL, 0); if (res) { if (!g_ignoreerror) error(res, "ITLogDiskInfo"); return false; } return true; }