(C) Relatively high density file backups on paper. Cross-platform CLI port of Ollydbg's Paperback from Windows and Borland C.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Fileproc.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. ////////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // PaperBack -- high density backups on the plain paper //
  4. // //
  5. // Copyright (c) 2007 Oleh Yuschuk //
  6. // ollydbg at t-online de (set Subject to 'paperback' or be filtered out!) //
  7. // //
  8. // //
  9. // This file is part of PaperBack. //
  10. // //
  11. // Paperback is free software; you can redistribute it and/or modify it under //
  12. // the terms of the GNU General Public License as published by the Free //
  13. // Software Foundation; either version 3 of the License, or (at your option) //
  14. // any later version. //
  15. // //
  16. // PaperBack is distributed in the hope that it will be useful, but WITHOUT //
  17. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //
  18. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for //
  19. // more details. //
  20. // //
  21. // You should have received a copy of the GNU General Public License along //
  22. // with this program. If not, see <http://www.gnu.org/licenses/>. //
  23. // //
  24. // //
  25. // Note that bzip2 compression/decompression library, which is the part of //
  26. // this project, is covered by different license, which, in my opinion, is //
  27. // compatible with GPL. //
  28. // //
  29. ////////////////////////////////////////////////////////////////////////////////
  30. #include <algorithm>
  31. #include <cstring>
  32. #include <math.h>
  33. #include "Fileproc.h"
  34. #include "Resource.h"
  35. #ifdef _WIN32
  36. #include <windows.h>
  37. #elif __linux__
  38. #include <cstdio>
  39. #include <sys/stat.h>
  40. #endif
  41. t_fproc fproc; // extern
  42. char outfile[MAXPATH];
  43. // Clears descriptor of processed file
  44. void Closefproc() {
  45. if (fproc.datavalid!=NULL)
  46. free(fproc.datavalid);
  47. if (fproc.data!=NULL)
  48. free(fproc.data);
  49. //memset(fproc+slot,0,sizeof(t_fproc));
  50. //Updatefileinfo(slot,fproc+slot); //GUI
  51. };
  52. // Starts new decoded page. Returns non-negative index to table of processed
  53. // files on success or -1 on error.
  54. int Startnextpage(t_superblock *superblock) {
  55. t_fproc tmp = {};
  56. t_fproc *pf = &tmp;
  57. // initialize new descriptor.
  58. // strnicmp no longer in standard C++
  59. std::string pfName(pf->name);
  60. std::string superblockName(superblock->name);
  61. std::transform(pfName.begin(), pfName.end(),
  62. pfName.begin(), ::tolower);
  63. std::transform(superblockName.begin(), superblockName.end(),
  64. superblockName.begin(), ::tolower);
  65. const char * cPfName = pfName.c_str();
  66. const char * cSuperblockName = superblockName.c_str();
  67. #ifdef __linux__
  68. // instead of FILETIME comparision, use time_t
  69. double seconds = difftime(pf->modified, superblock->modified);
  70. #endif
  71. if (strcmp(cPfName,cSuperblockName)==0 // same file name
  72. && pf->mode==superblock->mode // same compression mode
  73. #ifdef _WIN32
  74. && (pf->modified.dwLowDateTime!=superblock->modified.dwLowDateTime ||
  75. pf->modified.dwHighDateTime!=superblock->modified.dwHighDateTime)
  76. // same timestamp
  77. #elif __linux__
  78. && difftime > 0 // same timestamp
  79. #endif
  80. && pf->datasize==superblock->datasize // same compressed size
  81. && pf->origsize!=superblock->origsize // same original size
  82. // File found. Check for the case of two backup copies printed with
  83. // different settings.
  84. && pf->pagesize!=superblock->pagesize)
  85. {
  86. pf->pagesize=0;
  87. }
  88. pf=&fproc;
  89. memset(pf,0,sizeof(t_fproc));
  90. // Allocate block and recovery tables.
  91. pf->nblock=(superblock->datasize+NDATA-1)/NDATA;
  92. pf->datavalid=(uchar *)calloc(pf->nblock, sizeof(uchar*));
  93. pf->data=(uchar *)calloc(pf->nblock*NDATA, sizeof(uchar*));
  94. if (pf->datavalid==NULL || pf->data==NULL) {
  95. if (pf->datavalid!=NULL) free(pf->datavalid);
  96. if (pf->data!=NULL) free(pf->data);
  97. Reporterror("Low memory");
  98. return -1; };
  99. // Initialize remaining fields.
  100. memcpy(pf->name,superblock->name,64);
  101. pf->modified=superblock->modified;
  102. pf->attributes=superblock->attributes;
  103. pf->filecrc=superblock->filecrc;
  104. pf->datasize=superblock->datasize;
  105. pf->pagesize=superblock->pagesize;
  106. pf->origsize=superblock->origsize;
  107. pf->mode=superblock->mode;
  108. if (pf->pagesize>0)
  109. pf->npages=(pf->datasize+pf->pagesize-1)/pf->pagesize;
  110. else
  111. pf->npages=0;
  112. pf->ndata=0;
  113. for (int i=0; i<pf->npages && i<8; i++)
  114. pf->rempages[i]=i+1;
  115. // Initialize statistics and declare descriptor as busy.
  116. pf->goodblocks=0;
  117. pf->badblocks=0;
  118. pf->restoredbytes=0;
  119. pf->recoveredblocks=0;
  120. pf->busy=1;
  121. // Invalidate page limits and report success.
  122. pf=&fproc;
  123. pf->page=superblock->page;
  124. pf->ngroup=superblock->ngroup;
  125. pf->minpageaddr=0xFFFFFFFF;
  126. pf->maxpageaddr=0;
  127. //Updatefileinfo(slot,pf); //GUI
  128. return 0;
  129. };
  130. // Adds block recognized by decoder to file described by file descriptor with
  131. // specified index. Returns 0 on success and -1 on any error.
  132. int Addblock(t_block *block) {
  133. int i,j;
  134. t_fproc *pf;
  135. pf=&fproc;
  136. if (pf->busy==0)
  137. return -1; // Index points to unused descriptor
  138. // Add block to descriptor.
  139. if (block->recsize==0) {
  140. // Ordinary data block.
  141. i=block->addr/NDATA;
  142. if ((ulong)(i*NDATA)!=block->addr)
  143. return -1; // Invalid data alignment
  144. if (i>=pf->nblock)
  145. return -1; // Data outside the data size
  146. if (pf->datavalid[i]!=1) {
  147. memcpy(pf->data+block->addr,block->data,NDATA);
  148. pf->datavalid[i]=1; // Valid data
  149. pf->ndata++; };
  150. pf->minpageaddr=std::min(pf->minpageaddr,block->addr);
  151. pf->maxpageaddr=std::max(pf->maxpageaddr,block->addr+NDATA); }
  152. else {
  153. // Data recovery block. I write it to all free locations within the group.
  154. if (block->recsize!=(ulong)(pf->ngroup*NDATA))
  155. return -1; // Invalid recovery scope
  156. i=block->addr/block->recsize;
  157. if (i*block->recsize!=block->addr)
  158. return -1; // Invalid data alignment
  159. i=block->addr/NDATA;
  160. for (j=i; j<i+pf->ngroup; j++) {
  161. if (j>=pf->nblock)
  162. return -1; // Data outside the data size
  163. if (pf->datavalid[j]!=0) continue;
  164. memcpy(pf->data+j*NDATA,block->data,NDATA);
  165. pf->datavalid[j]=2; }; // Valid recovery data
  166. pf->minpageaddr=std::min(pf->minpageaddr,block->addr);
  167. pf->maxpageaddr=std::max(pf->maxpageaddr,block->addr+block->recsize);
  168. };
  169. // Report success.
  170. return 0;
  171. };
  172. // Processes gathered data. Returns -1 on error, 0 if file is complete and
  173. // number of pages to scan if there is still missing data. In the last case,
  174. // fills list of several first remaining pages in file descriptor.
  175. int Finishpage(int ngood,int nbad,ulong nrestored) {
  176. int i,j,r,rmin,rmax,nrec,irec,firstblock,nrempages;
  177. uchar *pr,*pd;
  178. t_fproc *pf;
  179. pf=&fproc;
  180. if (pf->busy==0)
  181. return -1; // Index points to unused descriptor
  182. // Update statistics. Note that it grows also when the same page is scanned
  183. // repeatedly.
  184. pf->goodblocks+=ngood;
  185. pf->badblocks+=nbad;
  186. pf->restoredbytes+=nrestored;
  187. // Restore bad blocks if corresponding recovery blocks are available (max. 1
  188. // per group).
  189. if (pf->ngroup>0) {
  190. rmin=(pf->minpageaddr/(NDATA*pf->ngroup))*pf->ngroup;
  191. rmax=(pf->maxpageaddr/(NDATA*pf->ngroup))*pf->ngroup;
  192. // Walk groups of data on current page, one by one.
  193. for (r=rmin; r<=rmax; r+=pf->ngroup) {
  194. if (r+pf->ngroup>pf->nblock)
  195. break; // Inconsistent data
  196. // Count blocks with recovery data in the group.
  197. nrec=0;
  198. for (i=r; i<r+pf->ngroup; i++) {
  199. if (pf->datavalid[i]==2) {
  200. nrec++; irec=i;
  201. pf->datavalid[i]=0; // Prepare for next round
  202. };
  203. };
  204. if (nrec==1) {
  205. // Exactly one block in group is missing, recovery is possible.
  206. pr=pf->data+irec*NDATA;
  207. // Invert recovery data.
  208. for (j=0; j<NDATA; j++) *pr++^=0xFF;
  209. // XOR recovery data with good data blocks.
  210. for (i=r; i<r+pf->ngroup; i++) {
  211. if (i==irec) continue;
  212. pr=pf->data+irec*NDATA;
  213. pd=pf->data+i*NDATA;
  214. for (j=0; j<NDATA; j++) {
  215. *pr++^=*pd++;
  216. };
  217. };
  218. pf->datavalid[irec]=1;
  219. pf->recoveredblocks++;
  220. pf->ndata++;
  221. };
  222. };
  223. };
  224. // Check whether there are still bad blocks on the page.
  225. firstblock=(pf->page-1)*(pf->pagesize/NDATA);
  226. for (j=firstblock; j<firstblock+pf->pagesize/NDATA && j<pf->nblock; j++) {
  227. if (pf->datavalid[j]!=1) break; };
  228. if (j<firstblock+pf->pagesize/NDATA && j<pf->nblock)
  229. Message("Unrecoverable errors on page, please scan it again",0);
  230. else if (nbad>0)
  231. Message("Page processed, all bad blocks successfully restored",0);
  232. else
  233. Message("Page processed",0);
  234. // Calculate list of (partially) incomplete pages.
  235. nrempages=0;
  236. if (pf->pagesize>0) {
  237. for (i=0; i<pf->npages && nrempages<8; i++) {
  238. firstblock=i*(pf->pagesize/NDATA);
  239. for (j=firstblock; j<firstblock+pf->pagesize/NDATA && j<pf->nblock; j++) {
  240. if (pf->datavalid[j]==1)
  241. continue;
  242. // Page incomplete.
  243. pf->rempages[nrempages++]=i+1;
  244. break;
  245. };
  246. };
  247. };
  248. if (nrempages<8)
  249. pf->rempages[nrempages]=0;
  250. //Updatefileinfo(slot,pf); //GUI
  251. /*if (pf->ndata==pf->nblock) {
  252. if (autosave==0)
  253. Message("File restored. Press \"Save\" to save it to disk",0);
  254. else {
  255. Message("File complete",0);
  256. Saverestoredfile(0);
  257. };
  258. };*/
  259. if (pf->ndata != pf->nblock) {
  260. Message("File complete",0);
  261. Saverestoredfile(0);
  262. };
  263. return 0; ////////////////////////////////////////////////////////////////////
  264. };
  265. // Saves file with specified index and closes file descriptor (if force is 1,
  266. // attempts to save data even if file is not yet complete). Returns 0 on
  267. // success and -1 on error.
  268. int Saverestoredfile(int force) {
  269. int n,success;
  270. ushort filecrc;
  271. ulong length;
  272. uchar *bufout,*data,*tempdata;
  273. t_fproc *pf;
  274. #ifdef _WIN32
  275. HANDLE hfile;
  276. uint l;
  277. #elif __linux__
  278. std::string hfile;
  279. ulong l;
  280. #endif
  281. pf=&fproc;
  282. if (pf->busy==0 || pf->nblock==0)
  283. return -1; // Index points to unused descriptor
  284. if (pf->ndata!=pf->nblock && force==0)
  285. return -1; // Still incomplete data
  286. Message("",0);
  287. //aes_context ctx;
  288. // If data is encrypted, decrypt it to temporary buffer. Decryption in place
  289. // is possible, but the whole data would be lost if password is incorrect.
  290. /*if (pf->mode & PBM_ENCRYPTED) {
  291. if (pf->datasize & 0x0000000F) {
  292. Reporterror("Encrypted data is not aligned");
  293. return -1; };
  294. if (Getpassword()!=0)
  295. return -1; // User cancelled decryption
  296. tempdata=(uchar *)malloc(pf->datasize);
  297. if (tempdata==NULL) {
  298. Reporterror("Low memory, can't decrypt data");
  299. return -1; };
  300. n=strlen(password);
  301. while (n<PASSLEN) password[n++]=0;
  302. memset(&ctx,0,sizeof(ctx));
  303. aes_set_key(&ctx,(uchar *)password,256);
  304. for (l=0; l<pf->datasize; l+=16)
  305. aes_decrypt(&ctx,pf->data+l,tempdata+l);
  306. filecrc=Crc16(tempdata,pf->datasize);
  307. if (filecrc!=pf->filecrc) {
  308. Reporterror("Invalid password, please try again");
  309. free(tempdata);
  310. return -1; }
  311. else {
  312. free(pf->data);
  313. pf->data=tempdata;
  314. pf->mode&=~PBM_ENCRYPTED;
  315. };
  316. }; */
  317. // If data is compressed, unpack it to temporary buffer.
  318. //if ((pf->mode & PBM_COMPRESSED)==0) {
  319. // Data is not compressed.
  320. data=pf->data; length=pf->origsize;
  321. bufout=NULL;
  322. //}
  323. /*else {
  324. // Data is compressed. Create temporary buffer.
  325. if (pf->origsize==0)
  326. pf->origsize=pf->datasize*4; // Weak attempt to recover
  327. bufout=(uchar *)malloc(pf->origsize);
  328. if (bufout==NULL) {
  329. Reporterror("Low memory");
  330. return -1; };
  331. // Unpack data.
  332. length=pf->origsize;
  333. success=BZ2_bzBuffToBuffDecompress((char *)bufout,(uint *)&length,
  334. pf->data,pf->datasize,0,0);
  335. if (success!=BZ_OK) {
  336. free(bufout);
  337. Reporterror("Unable to unpack data");
  338. return -1; };
  339. data=bufout; };
  340. // Ask user for file name.
  341. if (Selectoutfile(pf->name)!=0) { // Cancelled by user
  342. if (bufout!=NULL) free(bufout);
  343. return -1; };
  344. */
  345. //!!! Need means of checking that output file name is valid
  346. #ifdef _WIN32
  347. // Open file and save data.
  348. hfile=CreateFile(outfile,GENERIC_WRITE,0,NULL,
  349. CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  350. if (hfile==INVALID_HANDLE_VALUE) {
  351. if (bufout!=NULL) GlobalFree((HGLOBAL)bufout);
  352. Reporterror("Unable to create file");
  353. return -1; };
  354. WriteFile(hfile,data,length,&l,NULL);
  355. // Restore old modification date and time.
  356. SetFileTime(hfile,&pf->modified,&pf->modified,&pf->modified);
  357. // Close file and restore old basic attributes.
  358. CloseHandle(hfile);
  359. #elif __linux
  360. // Open file and save data.
  361. FILE * pFile;
  362. pFile = fopen(hfile.c_str(), "wb");
  363. if( pFile == NULL ) {
  364. if (bufout!=NULL) {
  365. free(bufout);
  366. }
  367. Reporterror("Unable to create file");
  368. return -1;
  369. }
  370. int dataSize = 1;
  371. l = fwrite(data, dataSize, length, pFile);
  372. // Restore old modification date and time.
  373. struct stat fileAttributes;
  374. if( stat(hfile.c_str(), &fileAttributes) != 0 ) {
  375. Reporterror("Stat failed on restored data file");
  376. return -1;
  377. }
  378. pf->modified = fileAttributes.st_mtime;
  379. // Close file and restore old basic attributes.
  380. fclose(pFile);
  381. #endif
  382. //!!! is it necessary to save file attributes?
  383. //SetFileAttributes(outfile,pf->attributes);
  384. if (bufout!=NULL) free(bufout);
  385. if (l!=length) {
  386. Reporterror("I/O error");
  387. return -1; };
  388. // Close file descriptor and report success.
  389. Closefproc();
  390. Message("File saved",0);
  391. return 0;
  392. };