magicrescue-1.1.8/ChangeLog0000644000175000017500000025222111274314776013676 0ustar jbjjbjFor less detailed change information, see the NEWS file ------------------------------------------------------------------------ r824 | jbj | 2009-11-03 10:00:58 +0100 (Tue, 03 Nov 2009) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/mbox A /magicrescue/trunk/recipes/mbox-mozilla-inbox A /magicrescue/trunk/recipes/mbox-mozilla-sent New mbox recipes ------------------------------------------------------------------------ r822 | jbj | 2009-10-01 21:13:49 +0200 (Thu, 01 Oct 2009) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/src/magicrescue.c It appears that there exists a program named safecat. Move tools dir to beginning of path so our safecat takes precedence. ------------------------------------------------------------------------ r821 | jbj | 2009-10-01 14:53:12 +0200 (Thu, 01 Oct 2009) | 1 line Changed paths: M /magicrescue/trunk/config.d/50getrlimit M /magicrescue/trunk/new_NEWS Raise maximum virtual memory usage for helper programs from 50MB to 1024MB. ------------------------------------------------------------------------ r820 | jbj | 2009-09-04 22:22:31 +0200 (Fri, 04 Sep 2009) | 1 line Changed paths: M /magicrescue/trunk/recipes/zip fix a small oops in release ------------------------------------------------------------------------ r818 | jbj | 2009-09-04 19:48:26 +0200 (Fri, 04 Sep 2009) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.7 ------------------------------------------------------------------------ r817 | jbj | 2009-09-04 19:44:36 +0200 (Fri, 04 Sep 2009) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/zip Apply patch to fix problems in zip recipe ------------------------------------------------------------------------ r794 | jbj | 2009-02-15 22:20:29 +0100 (Sun, 15 Feb 2009) | 1 line Changed paths: M /magicrescue/trunk/release.sh Update release script to point to new server ------------------------------------------------------------------------ r792 | jbj | 2009-02-15 22:14:04 +0100 (Sun, 15 Feb 2009) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.6 ------------------------------------------------------------------------ r791 | jbj | 2009-02-15 21:58:09 +0100 (Sun, 15 Feb 2009) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/ppm Add PPM recipe from Daniel J Blueman ------------------------------------------------------------------------ r790 | jbj | 2009-02-15 21:38:46 +0100 (Sun, 15 Feb 2009) | 1 line Changed paths: M /magicrescue/trunk/recipes/nikon-raw Modifications for the Nikon recipe from Daniel J Blueman ------------------------------------------------------------------------ r773 | jbj | 2008-10-29 08:12:57 +0100 (Wed, 29 Oct 2008) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod typo ------------------------------------------------------------------------ r771 | jbj | 2008-10-06 18:55:59 +0200 (Mon, 06 Oct 2008) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/nikon-raw Add recipe for Nikon raw photos ------------------------------------------------------------------------ r603 | jbj | 2008-01-17 14:04:15 +0100 (Thu, 17 Jan 2008) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.5 ------------------------------------------------------------------------ r602 | jbj | 2008-01-17 12:33:57 +0100 (Thu, 17 Jan 2008) | 1 line Changed paths: M /magicrescue/trunk/configure Workaround for Solaris /bin/sh non-POSIX 'trap' command ------------------------------------------------------------------------ r590 | jbj | 2007-12-31 16:25:04 +0100 (Mon, 31 Dec 2007) | 1 line Changed paths: M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/magicrescue.c M /magicrescue/trunk/src/recipe.c M /magicrescue/trunk/src/recipe.h M /magicrescue/trunk/src/scanner.c M /magicrescue/trunk/src/scanner.h M /magicrescue/trunk/src/util.c M /magicrescue/trunk/src/util.h M /magicrescue/trunk/tools/textextract.c Change buffers to signed string types to silence gcc warnings ------------------------------------------------------------------------ r589 | jbj | 2007-12-31 15:37:57 +0100 (Mon, 31 Dec 2007) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r588 | jbj | 2007-12-31 15:13:24 +0100 (Mon, 31 Dec 2007) | 1 line Changed paths: A /magicrescue/trunk/recipes/canon-cr2 Add a recipe for Canon CR2 RAW files ------------------------------------------------------------------------ r490 | jbj | 2007-06-22 10:49:38 +0200 (Fri, 22 Jun 2007) | 1 line Changed paths: M /magicrescue/trunk/configure typo ------------------------------------------------------------------------ r489 | jbj | 2007-06-21 22:30:14 +0200 (Thu, 21 Jun 2007) | 1 line Changed paths: M /magicrescue/trunk/configure Syntax fix to make configure script work on the dash shell (Debian, Ubuntu, ...) ------------------------------------------------------------------------ r488 | jbj | 2007-06-03 23:30:57 +0200 (Sun, 03 Jun 2007) | 1 line Changed paths: M /magicrescue/trunk/doc/dupemap.pod typo ------------------------------------------------------------------------ r483 | jbj | 2007-05-01 22:14:43 +0200 (Tue, 01 May 2007) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/flac Add flac recipe ------------------------------------------------------------------------ r333 | jbj | 2004-08-15 11:19:08 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/configure Proper cleanup after configure ------------------------------------------------------------------------ r332 | jbj | 2004-08-15 01:31:23 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/README No users know what X/OPEN is ------------------------------------------------------------------------ r330 | jbj | 2004-08-15 01:16:20 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.4 ------------------------------------------------------------------------ r329 | jbj | 2004-08-15 01:10:37 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r328 | jbj | 2004-08-15 01:05:27 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: A /magicrescue/trunk/config.config M /magicrescue/trunk/config.d/50cygwin M /magicrescue/trunk/configure Overhaul of configure to make it more independent of Magic Rescue. Also fixes the deletion of config.h by ./configure --help. ------------------------------------------------------------------------ r327 | jbj | 2004-08-15 00:43:25 +0200 (Sun, 15 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in Remove nonsense dependency ------------------------------------------------------------------------ r326 | jbj | 2004-08-14 16:02:21 +0200 (Sat, 14 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/src/magicrescue.c M /magicrescue/trunk/src/scanner.c Remove ugly workaround and fix the real bug instead ------------------------------------------------------------------------ r324 | jbj | 2004-08-10 22:33:04 +0200 (Tue, 10 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.3 ------------------------------------------------------------------------ r323 | jbj | 2004-08-10 22:04:25 +0200 (Tue, 10 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/tools/oleextract.pl Work around cygwin problem with msoffice ------------------------------------------------------------------------ r322 | jbj | 2004-08-10 20:37:26 +0200 (Tue, 10 Aug 2004) | 2 lines Changed paths: M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/recur.c M /magicrescue/trunk/src/scanner.c M /magicrescue/trunk/tools/textextract.c silence warnings from ./configure --strict ------------------------------------------------------------------------ r321 | jbj | 2004-08-10 20:24:12 +0200 (Tue, 10 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r320 | jbj | 2004-08-10 20:22:18 +0200 (Tue, 10 Aug 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c Temporary fix to make it pass the testsuite ------------------------------------------------------------------------ r312 | jbj | 2004-07-12 21:01:11 +0200 (Mon, 12 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl magicrescue -b doesn't work with textextract -r ------------------------------------------------------------------------ r311 | jbj | 2004-07-11 01:42:15 +0200 (Sun, 11 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c oops ------------------------------------------------------------------------ r310 | jbj | 2004-07-11 01:31:54 +0200 (Sun, 11 Jul 2004) | 1 line Changed paths: A /magicrescue/trunk/tests/samples/gimp-xcf (from /magicrescue/trunk/tests/samples/xcf:309) A /magicrescue/trunk/tests/samples/gpl D /magicrescue/trunk/tests/samples/xcf New samples in test suite ------------------------------------------------------------------------ r309 | jbj | 2004-07-11 00:53:00 +0200 (Sun, 11 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod M /magicrescue/trunk/recipes/gpl M /magicrescue/trunk/recipes/gzip M /magicrescue/trunk/recipes/perl M /magicrescue/trunk/src/magicrescue.c M /magicrescue/trunk/tools/textextract.c Make allow_overlap and textextract -r take an argument in bytes ------------------------------------------------------------------------ r308 | jbj | 2004-07-10 15:04:11 +0200 (Sat, 10 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r307 | jbj | 2004-07-10 15:03:56 +0200 (Sat, 10 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c Better textextract ------------------------------------------------------------------------ r306 | jbj | 2004-07-08 23:12:12 +0200 (Thu, 08 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/gimp-xcf A /magicrescue/trunk/tests/samples/xcf A /magicrescue/trunk/tools/gimp-resave.pl Add GIMP xcf file support ------------------------------------------------------------------------ r305 | jbj | 2004-07-07 18:26:45 +0200 (Wed, 07 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm M /magicrescue/trunk/src/find_dbm.h Support debian's gdbm-ndbm.h ------------------------------------------------------------------------ r304 | jbj | 2004-07-06 21:37:48 +0200 (Tue, 06 Jul 2004) | 1 line Changed paths: M /magicrescue/trunk/src/util.c cosmetics ------------------------------------------------------------------------ r303 | jbj | 2004-06-16 16:39:40 +0200 (Wed, 16 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/dupemap.pod Change dupemap docs for better results ------------------------------------------------------------------------ r302 | jbj | 2004-06-14 21:11:29 +0200 (Mon, 14 Jun 2004) | 1 line Changed paths: A /magicrescue/trunk/recipes/gpl A /magicrescue/trunk/tools/script_rename.pl First attempt at GPL recipe ------------------------------------------------------------------------ r301 | jbj | 2004-06-14 21:09:33 +0200 (Mon, 14 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c More TODO ------------------------------------------------------------------------ r300 | jbj | 2004-06-14 00:28:50 +0200 (Mon, 14 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c More plans for textextract ------------------------------------------------------------------------ r299 | jbj | 2004-06-14 00:07:29 +0200 (Mon, 14 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/magicsort bugfix: don't change only the first invalid character to _ ------------------------------------------------------------------------ r298 | jbj | 2004-06-13 23:41:23 +0200 (Sun, 13 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c Replace textextract's goto-flow with function-flow. Rename var output to outfd ------------------------------------------------------------------------ r297 | jbj | 2004-06-13 23:37:20 +0200 (Sun, 13 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/perl Write more info ------------------------------------------------------------------------ r296 | jbj | 2004-06-13 20:58:36 +0200 (Sun, 13 Jun 2004) | 4 lines Changed paths: M /magicrescue/trunk/tools/textextract.c tools/textextract: - Added -r option to search for BOF as well as EOF - Set last_letter to NULL when uninitialized ------------------------------------------------------------------------ r295 | jbj | 2004-06-12 21:08:52 +0200 (Sat, 12 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl Test -b argument in some of the iterations ------------------------------------------------------------------------ r294 | jbj | 2004-06-07 23:06:47 +0200 (Mon, 07 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c Restructuring of textextract.c ------------------------------------------------------------------------ r293 | jbj | 2004-06-07 22:08:13 +0200 (Mon, 07 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod More pointers to other tools ------------------------------------------------------------------------ r292 | jbj | 2004-06-07 21:10:48 +0200 (Mon, 07 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl oops ------------------------------------------------------------------------ r291 | jbj | 2004-06-06 23:04:12 +0200 (Sun, 06 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl Proper exception handling in tests.pl ------------------------------------------------------------------------ r290 | jbj | 2004-06-06 22:44:20 +0200 (Sun, 06 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl update tests.pl to new binary location ------------------------------------------------------------------------ r289 | jbj | 2004-06-06 20:47:52 +0200 (Sun, 06 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/magicsort (from /magicrescue/trunk/src/magicsort:286) M /magicrescue/trunk/src D /magicrescue/trunk/src/magicsort Move to-be-installed programs to the root source directory ------------------------------------------------------------------------ r288 | jbj | 2004-06-06 20:41:08 +0200 (Sun, 06 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c textextract: max line length ------------------------------------------------------------------------ r287 | jbj | 2004-06-04 21:29:37 +0200 (Fri, 04 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/textextract.c remove tailfile option, add options for score thresholds ------------------------------------------------------------------------ r284 | jbj | 2004-06-04 21:08:19 +0200 (Fri, 04 Jun 2004) | 1 line Changed paths: M /magicrescue/trunk/src/extract.c M /magicrescue/trunk/src/magicrescue.h Close unused file descriptor when executing external command with pipe ------------------------------------------------------------------------ r283 | jbj | 2004-05-30 23:02:20 +0200 (Sun, 30 May 2004) | 5 lines Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/recipes/perl M /magicrescue/trunk/src/util.c M /magicrescue/trunk/src/util.h M /magicrescue/trunk/tools/safecat.c M /magicrescue/trunk/tools/textextract.c - textextract -M option for max file size - textextract takes output file argument instead of writing to stdout - atol_calc moved to util.c - comments in Makefile.in ------------------------------------------------------------------------ r282 | jbj | 2004-05-30 21:44:15 +0200 (Sun, 30 May 2004) | 1 line Changed paths: M /magicrescue/trunk/tests/tests.pl Two bugs in test script ------------------------------------------------------------------------ r281 | jbj | 2004-05-30 17:14:13 +0200 (Sun, 30 May 2004) | 1 line Changed paths: A /magicrescue/trunk/tests/samples/perl M /magicrescue/trunk/tests/tests.pl Better test suite ------------------------------------------------------------------------ r280 | jbj | 2004-05-30 01:38:47 +0200 (Sun, 30 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/recipes/perl M /magicrescue/trunk/tools A /magicrescue/trunk/tools/textextract.c Add preliminary ASCII text extraction ------------------------------------------------------------------------ r279 | jbj | 2004-05-30 01:36:34 +0200 (Sun, 30 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/dupemap.pod More warnings for delete commands ------------------------------------------------------------------------ r278 | jbj | 2004-05-17 21:54:12 +0200 (Mon, 17 May 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r277 | jbj | 2004-05-17 21:52:48 +0200 (Mon, 17 May 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh A /magicrescue/trunk/tests A /magicrescue/trunk/tests/samples A /magicrescue/trunk/tests/samples/avi A /magicrescue/trunk/tests/samples/elf A /magicrescue/trunk/tests/samples/gzip A /magicrescue/trunk/tests/samples/jpeg-exif A /magicrescue/trunk/tests/samples/jpeg-jfif A /magicrescue/trunk/tests/samples/mp3-id3v1 A /magicrescue/trunk/tests/samples/mp3-id3v2 A /magicrescue/trunk/tests/samples/msoffice A /magicrescue/trunk/tests/samples/png A /magicrescue/trunk/tests/samples/zip A /magicrescue/trunk/tests/tests.pl Add test suite ------------------------------------------------------------------------ r276 | jbj | 2004-05-17 21:39:02 +0200 (Mon, 17 May 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/src/magicrescue.c Fixed minor buffer-overlap bug discovered by the new test suite (which will be committed Real Soon Now) ------------------------------------------------------------------------ r275 | jbj | 2004-05-11 23:18:34 +0200 (Tue, 11 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c paranthesis for clarity ------------------------------------------------------------------------ r274 | jbj | 2004-05-07 23:53:25 +0200 (Fri, 07 May 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/mp3-id3v1 typo in comment ------------------------------------------------------------------------ r272 | jbj | 2004-05-06 22:49:50 +0200 (Thu, 06 May 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.2 ------------------------------------------------------------------------ r271 | jbj | 2004-05-06 22:43:22 +0200 (Thu, 06 May 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/zip Some .jar files have bad CRC on all their contents ------------------------------------------------------------------------ r270 | jbj | 2004-05-06 22:30:54 +0200 (Thu, 06 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod Wording/formatting in manual ------------------------------------------------------------------------ r269 | jbj | 2004-05-05 22:21:59 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/80magicrescue_defs M /magicrescue/trunk/configure The name of the package is 'Magic Rescue' ------------------------------------------------------------------------ r268 | jbj | 2004-05-05 22:19:05 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/configure Change echo[12] slightly ------------------------------------------------------------------------ r267 | jbj | 2004-05-05 22:13:05 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/array.h M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/magicrescue.h M /magicrescue/trunk/src/recipe.h M /magicrescue/trunk/src/recur.c M /magicrescue/trunk/src/recur.h M /magicrescue/trunk/src/scanner.h M /magicrescue/trunk/src/util.h Proper header inclusion ------------------------------------------------------------------------ r266 | jbj | 2004-05-05 22:12:42 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c redundant var ------------------------------------------------------------------------ r265 | jbj | 2004-05-05 22:12:21 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/configure Make --strict work on glibc again ------------------------------------------------------------------------ r264 | jbj | 2004-05-05 21:52:16 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c oops ------------------------------------------------------------------------ r263 | jbj | 2004-05-05 20:46:57 +0200 (Wed, 05 May 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Add all docs to release ------------------------------------------------------------------------ r262 | jbj | 2004-05-04 22:00:49 +0200 (Tue, 04 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c Source comments in magicrescue.c ------------------------------------------------------------------------ r261 | jbj | 2004-05-04 21:28:09 +0200 (Tue, 04 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/find_dbm.h oops ------------------------------------------------------------------------ r260 | jbj | 2004-05-04 19:49:05 +0200 (Tue, 04 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/find_dbm.h Store 0-length keys in the DBMs that support it ------------------------------------------------------------------------ r259 | jbj | 2004-05-03 22:52:19 +0200 (Mon, 03 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/src/dupemap.c A /magicrescue/trunk/src/recur.c (from /magicrescue/trunk/src/dupemap.c:258) A /magicrescue/trunk/src/recur.h (from /magicrescue/trunk/src/dupemap.c:258) M /magicrescue/trunk/src/util.c M /magicrescue/trunk/src/util.h Move directory recursion functions to recur.c ------------------------------------------------------------------------ r258 | jbj | 2004-05-03 22:02:15 +0200 (Mon, 03 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/dupemap.c Any Berkeley DB version can now be used ------------------------------------------------------------------------ r257 | jbj | 2004-05-03 21:55:37 +0200 (Mon, 03 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod Change utility names to urls ------------------------------------------------------------------------ r256 | jbj | 2004-05-03 21:44:16 +0200 (Mon, 03 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/dupemap.pod Remove overly complicated examples from usage info ------------------------------------------------------------------------ r255 | jbj | 2004-05-03 21:42:48 +0200 (Mon, 03 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/dupemap.pod M /magicrescue/trunk/doc/magicrescue.pod M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/magicrescue.c Implement and document the -I option for magicrescue and dupemap ------------------------------------------------------------------------ r254 | jbj | 2004-05-02 22:53:04 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r253 | jbj | 2004-05-02 22:19:30 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in fix 'make uninstall' ------------------------------------------------------------------------ r252 | jbj | 2004-05-02 22:04:33 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in .PHONY update ------------------------------------------------------------------------ r251 | jbj | 2004-05-02 21:55:56 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod magicsort now has manpage ------------------------------------------------------------------------ r250 | jbj | 2004-05-02 21:55:36 +0200 (Sun, 02 May 2004) | 3 lines Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/doc/dupemap.pod A /magicrescue/trunk/doc/magicsort.pod - make distclean removes binaries - document magicsort ------------------------------------------------------------------------ r249 | jbj | 2004-05-02 21:35:41 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/magicrescue.c wording ------------------------------------------------------------------------ r248 | jbj | 2004-05-02 21:09:07 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/util.c proper fallback when atoll is missing ------------------------------------------------------------------------ r247 | jbj | 2004-05-02 20:54:55 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/doc/magicrescue.pod M /magicrescue/trunk/src/magicrescue.c Document -O ------------------------------------------------------------------------ r246 | jbj | 2004-05-02 20:10:16 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c periods ------------------------------------------------------------------------ r245 | jbj | 2004-05-02 20:08:33 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c M /magicrescue/trunk/src/util.c M /magicrescue/trunk/src/util.h Revival of the -O option ------------------------------------------------------------------------ r244 | jbj | 2004-05-02 20:07:40 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in oops ------------------------------------------------------------------------ r243 | jbj | 2004-05-02 19:39:08 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/src/dupemap.c M /magicrescue/trunk/src/util.c M /magicrescue/trunk/src/util.h M /magicrescue/trunk/tools/inputseek.c Change dupemap and inputseek to new build style ------------------------------------------------------------------------ r242 | jbj | 2004-05-02 19:38:13 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/safecat.c safecat cosmetics ------------------------------------------------------------------------ r241 | jbj | 2004-05-02 19:13:18 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c variable name blocksize -> bufsize ------------------------------------------------------------------------ r240 | jbj | 2004-05-02 19:11:54 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/magicrescue.c variable name rlp -> rl ------------------------------------------------------------------------ r239 | jbj | 2004-05-02 19:10:22 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/src/array.c A /magicrescue/trunk/src/array.h (from /magicrescue/trunk/src/common.h:235) D /magicrescue/trunk/src/common.h M /magicrescue/trunk/src/extract.c M /magicrescue/trunk/src/magicrescue.c M /magicrescue/trunk/src/recipe.c A /magicrescue/trunk/src/recipe.h (from /magicrescue/trunk/src/common.h:235) M /magicrescue/trunk/src/scanner.c A /magicrescue/trunk/src/scanner.h (from /magicrescue/trunk/src/common.h:235) M /magicrescue/trunk/src/util.c A /magicrescue/trunk/src/util.h (from /magicrescue/trunk/src/common.h:235) Split common.h into multiple header files ------------------------------------------------------------------------ r238 | jbj | 2004-05-02 18:55:48 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/90dep oops ------------------------------------------------------------------------ r237 | jbj | 2004-05-02 18:49:18 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/90dep wording ------------------------------------------------------------------------ r236 | jbj | 2004-05-02 18:47:06 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/config.d/80magicrescue_defs (from /magicrescue/trunk/config.d/90magicrescue_defs:235) A /magicrescue/trunk/config.d/90dep D /magicrescue/trunk/config.d/90magicrescue_defs M /magicrescue/trunk/configure Dependency generation at configure time ------------------------------------------------------------------------ r235 | jbj | 2004-05-02 17:38:48 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/config.d/50off_t M /magicrescue/trunk/config.d/90magicrescue_defs A /magicrescue/trunk/src/array.c (from /magicrescue/trunk/src/magicrescue.c:232) A /magicrescue/trunk/src/common.h (from /magicrescue/trunk/src/magicrescue.c:232) M /magicrescue/trunk/src/dupemap.c A /magicrescue/trunk/src/extract.c (from /magicrescue/trunk/src/magicrescue.c:232) A /magicrescue/trunk/src/largefile.h (from /magicrescue/trunk/src/magicrescue.c:232) M /magicrescue/trunk/src/magicrescue.c A /magicrescue/trunk/src/magicrescue.h (from /magicrescue/trunk/src/magicrescue.c:232) A /magicrescue/trunk/src/recipe.c (from /magicrescue/trunk/src/magicrescue.c:232) A /magicrescue/trunk/src/scanner.c (from /magicrescue/trunk/src/magicrescue.c:232) A /magicrescue/trunk/src/util.c (from /magicrescue/trunk/src/magicrescue.c:232) Split magicrescue.c into multiple files ------------------------------------------------------------------------ r234 | jbj | 2004-05-02 16:07:06 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in clean config.log with distclean ------------------------------------------------------------------------ r233 | jbj | 2004-05-02 14:46:21 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/doc A /magicrescue/trunk/doc/dupemap.pod (from /magicrescue/trunk/dupemap.pod:231) A /magicrescue/trunk/doc/magicrescue.pod (from /magicrescue/trunk/magicrescue.pod:231) D /magicrescue/trunk/dupemap.pod D /magicrescue/trunk/magicrescue.pod docs go in doc/ ------------------------------------------------------------------------ r232 | jbj | 2004-05-02 14:38:10 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/config.d/10magicrescue_defs M /magicrescue/trunk/config.d/90magicrescue_defs D /magicrescue/trunk/dupemap.c D /magicrescue/trunk/find_dbm.h D /magicrescue/trunk/magicrescue.c D /magicrescue/trunk/magicsort A /magicrescue/trunk/src A /magicrescue/trunk/src/dupemap.c (from /magicrescue/trunk/dupemap.c:220) A /magicrescue/trunk/src/find_dbm.h (from /magicrescue/trunk/find_dbm.h:220) A /magicrescue/trunk/src/magicrescue.c (from /magicrescue/trunk/magicrescue.c:231) A /magicrescue/trunk/src/magicsort (from /magicrescue/trunk/magicsort:220) Sources and binaries go in src/ ------------------------------------------------------------------------ r231 | jbj | 2004-05-02 10:52:30 +0200 (Sun, 02 May 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Oops, scanner_compare never worked. The problem should only affect performance, not correctness. ------------------------------------------------------------------------ r229 | jbj | 2004-05-01 19:14:32 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.1 ------------------------------------------------------------------------ r228 | jbj | 2004-05-01 19:13:20 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r227 | jbj | 2004-05-01 13:16:07 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm Try GDBM first because it has known behavior ------------------------------------------------------------------------ r226 | jbj | 2004-05-01 10:43:40 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/README Two periods after space; Describe dependencies; Remove obsolete info. ------------------------------------------------------------------------ r225 | jbj | 2004-05-01 10:42:50 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm Try last, it doesn't seem to be common ------------------------------------------------------------------------ r224 | jbj | 2004-05-01 10:23:34 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Use seperate buffer for temporary file name ------------------------------------------------------------------------ r223 | jbj | 2004-05-01 00:21:03 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50cygwin period ------------------------------------------------------------------------ r222 | jbj | 2004-05-01 00:13:02 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/mp3-id3v1 Solaris has no grep -q ------------------------------------------------------------------------ r221 | jbj | 2004-05-01 00:00:34 +0200 (Sat, 01 May 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r220 | jbj | 2004-04-30 23:52:32 +0200 (Fri, 30 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c Make dupemap not just compile, but also work, on KNOPPIX ------------------------------------------------------------------------ r219 | jbj | 2004-04-30 23:45:41 +0200 (Fri, 30 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm M /magicrescue/trunk/find_dbm.h KNOPPIX compatibility for dupemap ------------------------------------------------------------------------ r218 | jbj | 2004-04-30 23:19:53 +0200 (Fri, 30 Apr 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/new_NEWS - If argument to -r is a directory, read all files within it. - Give better error messages when recipe parsing fails ------------------------------------------------------------------------ r217 | jbj | 2004-04-30 23:17:23 +0200 (Fri, 30 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/elf typo ------------------------------------------------------------------------ r216 | jbj | 2004-04-29 23:06:38 +0200 (Thu, 29 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c trivial cleanup ------------------------------------------------------------------------ r215 | jbj | 2004-04-29 22:57:41 +0200 (Thu, 29 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r214 | jbj | 2004-04-29 22:52:00 +0200 (Thu, 29 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/mp3-id3v1 M /magicrescue/trunk/recipes/mp3-id3v2 Merge shared_scanner branch, which adds the -b option, implemented as a scanner that can be shared between recipes. Also changes the anti-overlap code to use a global list instead of being per-recipe. ------------------------------------------------------------------------ r209 | jbj | 2004-04-28 23:18:11 +0200 (Wed, 28 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS Recipe search path bug 2/2 ------------------------------------------------------------------------ r208 | jbj | 2004-04-28 23:00:07 +0200 (Wed, 28 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Recipe search path bug 1/2 ------------------------------------------------------------------------ r201 | jbj | 2004-04-28 20:46:35 +0200 (Wed, 28 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/new_NEWS Remove -b option from trunk for now ------------------------------------------------------------------------ r200 | jbj | 2004-04-25 19:50:45 +0200 (Sun, 25 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/zip Guess when a zip file is a JAR ------------------------------------------------------------------------ r199 | jbj | 2004-04-25 18:08:05 +0200 (Sun, 25 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/zip minor change in dd parameter ------------------------------------------------------------------------ r198 | jbj | 2004-04-25 17:02:38 +0200 (Sun, 25 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/msoffice A /magicrescue/trunk/recipes/zip Add zip recipe ------------------------------------------------------------------------ r196 | jbj | 2004-04-24 17:47:26 +0200 (Sat, 24 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/jpeg-exif M /magicrescue/trunk/recipes/jpeg-jfif Preserve non-image markers in jpeg files ------------------------------------------------------------------------ r195 | jbj | 2004-04-21 19:29:08 +0200 (Wed, 21 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c remove whitespace from scanner_string_init's scoretable ------------------------------------------------------------------------ r194 | jbj | 2004-04-21 19:28:36 +0200 (Wed, 21 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news about -b ------------------------------------------------------------------------ r193 | jbj | 2004-04-21 00:17:08 +0200 (Wed, 21 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod Add -b option for aligning to block boundaries ------------------------------------------------------------------------ r192 | jbj | 2004-04-20 08:47:03 +0200 (Tue, 20 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.pod M /magicrescue/trunk/magicrescue.pod Use two spaces after period in the docs. Rearrange the DATABASE section of dupemap.pod ------------------------------------------------------------------------ r191 | jbj | 2004-04-20 08:46:21 +0200 (Tue, 20 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c Correct case in title; ignore $TMP if it's the null string ------------------------------------------------------------------------ r190 | jbj | 2004-04-14 23:46:21 +0200 (Wed, 14 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.pod M /magicrescue/trunk/magicrescue.pod Refer to other file recovery tools ------------------------------------------------------------------------ r187 | jbj | 2004-04-13 13:24:02 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS Release 1.1.0 (again) ------------------------------------------------------------------------ r186 | jbj | 2004-04-13 13:21:34 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/tools/checkrecipe Correct dupemap usage and sed script ------------------------------------------------------------------------ r184 | jbj | 2004-04-13 13:12:47 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.1.0 ------------------------------------------------------------------------ r183 | jbj | 2004-04-13 12:22:14 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c update usage info for -M ------------------------------------------------------------------------ r182 | jbj | 2004-04-13 12:20:33 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c include sys headers before others ------------------------------------------------------------------------ r181 | jbj | 2004-04-13 12:18:35 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c Fix the trailing slash fix ------------------------------------------------------------------------ r180 | jbj | 2004-04-13 12:10:00 +0200 (Tue, 13 Apr 2004) | 4 lines Changed paths: M /magicrescue/trunk/config.d/50getrlimit D /magicrescue/trunk/config.d/50lstat M /magicrescue/trunk/config.d/50off_t M /magicrescue/trunk/dupemap.c M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/tools/inputseek.c M /magicrescue/trunk/tools/safecat.c - Solaris lstat test didn't work - Removed _XOPEN_SOURCE define, it caused too many problems - Added the _LARGEFILE64_SOURCE define to the last two C programs ------------------------------------------------------------------------ r179 | jbj | 2004-04-13 11:15:56 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: A /magicrescue/trunk/config.d/50lstat M /magicrescue/trunk/dupemap.c Workaround for Solaris lstat ------------------------------------------------------------------------ r178 | jbj | 2004-04-13 11:09:10 +0200 (Tue, 13 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/10cc M /magicrescue/trunk/config.d/50atoll M /magicrescue/trunk/config.d/50cygwin M /magicrescue/trunk/config.d/50dbm M /magicrescue/trunk/config.d/50getrlimit M /magicrescue/trunk/config.d/50off_t M /magicrescue/trunk/config.d/50perl M /magicrescue/trunk/configure Change spacing in configure's echo1 ------------------------------------------------------------------------ r177 | jbj | 2004-04-12 23:53:16 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c Solaris S_ISREG doesn't work ------------------------------------------------------------------------ r176 | jbj | 2004-04-12 23:52:29 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/gzip_rename.pl Support perl 5.6.1 ------------------------------------------------------------------------ r175 | jbj | 2004-04-12 22:43:03 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/release.sh Update all docs for release ------------------------------------------------------------------------ r174 | jbj | 2004-04-12 22:27:15 +0200 (Mon, 12 Apr 2004) | 14 lines Changed paths: M /magicrescue/trunk/dupemap.c M /magicrescue/trunk/dupemap.pod Major overhaul of dupemap, including: - remove the 1pass operation in favor of the -d option, turning everything backwards - make creation/removal of temporary db invisible to the user - install signal handler to leave db in a good state on interrupt - remove -v option, just use report operation - handle the special case of the "/" path - store a 0-length, but valid, pointer to make gdbm happy - fix bug where each file was scanned twice in 1pass mode - turn all appickable functions static - update the documentation to reflect the new way of doing things - Put F<> instead of C<> around pathnames in pod ------------------------------------------------------------------------ r173 | jbj | 2004-04-12 22:23:24 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.pod Put F<> instead of C<> around pathnames ------------------------------------------------------------------------ r172 | jbj | 2004-04-12 21:55:39 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r171 | jbj | 2004-04-12 21:55:29 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm Make gdbm preferred over Berkeley DB when configuring ------------------------------------------------------------------------ r170 | jbj | 2004-04-12 16:10:08 +0200 (Mon, 12 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.pod Usage example now assumes 'make install' has completed ------------------------------------------------------------------------ r169 | jbj | 2004-04-10 19:32:00 +0200 (Sat, 10 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r168 | jbj | 2004-04-10 18:34:15 +0200 (Sat, 10 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Include dupemap.1 in release ------------------------------------------------------------------------ r167 | jbj | 2004-04-10 18:33:59 +0200 (Sat, 10 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50dbm Newer gdbm versions have libgdbm_compat for ndbm ------------------------------------------------------------------------ r166 | jbj | 2004-04-10 17:39:48 +0200 (Sat, 10 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/configure M /magicrescue/trunk/dupemap.c M /magicrescue/trunk/find_dbm.h M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/release.sh Solaris fixes and minor bugfixes ------------------------------------------------------------------------ r165 | jbj | 2004-04-08 21:43:13 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in GNU-isms for the Makefile ------------------------------------------------------------------------ r164 | jbj | 2004-04-08 21:42:55 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c oops again, == has higher precedense than & ------------------------------------------------------------------------ r163 | jbj | 2004-04-08 20:21:04 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.pod M /magicrescue/trunk/magicrescue.pod Docs: wording and addition of hideously complicated pipelines ------------------------------------------------------------------------ r162 | jbj | 2004-04-08 19:41:46 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Oops, flags to -M weren't properly interpreted ------------------------------------------------------------------------ r161 | jbj | 2004-04-08 19:34:20 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/laola.pl Oops, laola.pl didn't get installed because it wasn't executable ------------------------------------------------------------------------ r160 | jbj | 2004-04-08 00:52:53 +0200 (Thu, 08 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/tools/checkrecipe Change -M option to take an argument ------------------------------------------------------------------------ r159 | jbj | 2004-04-07 23:45:17 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/checkrecipe Make checkrecipe read from stdin instead of invoking magicrescue itself ------------------------------------------------------------------------ r158 | jbj | 2004-04-07 23:28:46 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/mp3-id3v1 M /magicrescue/trunk/recipes/mp3-id3v2 Improved documentation for mp3 recipes ------------------------------------------------------------------------ r157 | jbj | 2004-04-07 23:11:17 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Rename output file before printing its name ------------------------------------------------------------------------ r156 | jbj | 2004-04-07 22:37:05 +0200 (Wed, 07 Apr 2004) | 5 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/avi M /magicrescue/trunk/recipes/elf M /magicrescue/trunk/recipes/gzip M /magicrescue/trunk/recipes/jpeg-exif M /magicrescue/trunk/recipes/jpeg-jfif M /magicrescue/trunk/recipes/mp3-id3v1 M /magicrescue/trunk/recipes/mp3-id3v2 M /magicrescue/trunk/recipes/msoffice M /magicrescue/trunk/recipes/png M /magicrescue/trunk/tools/gzip_rename.pl M /magicrescue/trunk/tools/ole_rename.pl - Use sh -c properly, changing $0 to $1 in recipe commands - Quote "$1", allowing output directories with funny characters in them - Implement proper renaming of output files, now magicrescue performs the actual rename itself, making sure nothing is overwritten. ------------------------------------------------------------------------ r155 | jbj | 2004-04-07 20:09:47 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c M /magicrescue/trunk/dupemap.pod use O_EXCL on the database for 1pass ------------------------------------------------------------------------ r154 | jbj | 2004-04-07 17:15:23 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.pod M /magicrescue/trunk/magicrescue.pod dupemap docs ------------------------------------------------------------------------ r153 | jbj | 2004-04-07 00:19:21 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c New naming conventions for output file names: print basename instead of offset when there are over 10 input files ------------------------------------------------------------------------ r152 | jbj | 2004-04-07 00:17:13 +0200 (Wed, 07 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/gzip_rename.pl regex fix ------------------------------------------------------------------------ r151 | jbj | 2004-04-06 22:57:03 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/dupemap.c M /magicrescue/trunk/dupemap.pod Better 1pass operation for dupemap ------------------------------------------------------------------------ r150 | jbj | 2004-04-06 20:34:41 +0200 (Tue, 06 Apr 2004) | 4 lines Changed paths: M /magicrescue/trunk/dupemap.c - Allow running with no path operands - Add 1pass mode for scanning and deleting dupes. I'm not happy with it, though. ------------------------------------------------------------------------ r149 | jbj | 2004-04-06 19:04:59 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.pod make clean after configuring; install dupemap; refer to dupemap in magicrescue.pod ------------------------------------------------------------------------ r148 | jbj | 2004-04-06 17:54:05 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk svn:ignore dupemap ------------------------------------------------------------------------ r147 | jbj | 2004-04-06 17:44:46 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in A /magicrescue/trunk/config.d/50dbm A /magicrescue/trunk/dupemap.c A /magicrescue/trunk/dupemap.pod A /magicrescue/trunk/find_dbm.h M /magicrescue/trunk/new_NEWS Added dupemap and related configure checks ------------------------------------------------------------------------ r146 | jbj | 2004-04-06 17:30:48 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk ignore manpages ------------------------------------------------------------------------ r145 | jbj | 2004-04-06 17:27:33 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.pod configure fixes, magicrescue -d documentation ------------------------------------------------------------------------ r144 | jbj | 2004-04-06 12:55:16 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/configure Seperate compilation and linking step in conftest ------------------------------------------------------------------------ r143 | jbj | 2004-04-06 12:39:11 +0200 (Tue, 06 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/10cc M /magicrescue/trunk/configure Don't export variables when we don't have to ------------------------------------------------------------------------ r142 | jbj | 2004-04-05 20:21:43 +0200 (Mon, 05 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/mp3-id3v1 M /magicrescue/trunk/recipes/mp3-id3v2 info about mp3 recipe order ------------------------------------------------------------------------ r141 | jbj | 2004-04-04 14:10:17 +0200 (Sun, 04 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/magicrescue.pod doc fixes ------------------------------------------------------------------------ r140 | jbj | 2004-04-03 21:05:24 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.pod manpage typo ------------------------------------------------------------------------ r139 | jbj | 2004-04-03 21:03:39 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Makefile needs to be up to date before remaking manpage ------------------------------------------------------------------------ r138 | jbj | 2004-04-03 20:16:23 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/README D /magicrescue/trunk/README.recipes M /magicrescue/trunk/magicrescue.c A /magicrescue/trunk/magicrescue.pod M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/release.sh Replace README files with manpage ------------------------------------------------------------------------ r137 | jbj | 2004-04-03 20:01:13 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/50off_t M /magicrescue/trunk/magicrescue.c Add another LARGEFILE define, documented for Solaris ------------------------------------------------------------------------ r136 | jbj | 2004-04-03 17:56:10 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/tools/pngextract.pl Set executable bit on pngextract.pl ------------------------------------------------------------------------ r135 | jbj | 2004-04-03 17:50:32 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/config.d/10cc M /magicrescue/trunk/configure More workarounds for the non-POSIXness of /bin/sh on Solaris ------------------------------------------------------------------------ r134 | jbj | 2004-04-03 16:00:45 +0200 (Sat, 03 Apr 2004) | 1 line Changed paths: D /magicrescue/trunk/config.d/00cc A /magicrescue/trunk/config.d/10cc (from /magicrescue/trunk/config.d/00cc:131) M /magicrescue/trunk/configure Workarounds for buggy Solaris shell and compiler ------------------------------------------------------------------------ r133 | jbj | 2004-04-02 10:37:26 +0200 (Fri, 02 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c putenv keeps its argument, so we duplicate it ------------------------------------------------------------------------ r132 | jbj | 2004-04-02 10:11:25 +0200 (Fri, 02 Apr 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c setenv -> putenv for Solaris ------------------------------------------------------------------------ r127 | jbj | 2004-03-27 12:30:21 +0100 (Sat, 27 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk M /magicrescue/trunk/config.d/50atoll M /magicrescue/trunk/config.d/50getrlimit M /magicrescue/trunk/config.d/50perl M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/release.sh M /magicrescue/trunk/tools/inputseek.c - Solaris fixes - generation of config.log ------------------------------------------------------------------------ r125 | jbj | 2004-03-26 18:17:33 +0100 (Fri, 26 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.0.4 ------------------------------------------------------------------------ r124 | jbj | 2004-03-26 18:14:10 +0100 (Fri, 26 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS - Add missing close/fclose - fd -> fh for a libc FILE* ------------------------------------------------------------------------ r123 | jbj | 2004-03-26 16:00:50 +0100 (Fri, 26 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS Fix rounding bug ------------------------------------------------------------------------ r122 | jbj | 2004-03-25 23:15:55 +0100 (Thu, 25 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/tools/checkrecipe enable checkrecipe to compare file sizes ------------------------------------------------------------------------ r121 | jbj | 2004-03-25 17:43:46 +0100 (Thu, 25 Mar 2004) | 5 lines Changed paths: M /magicrescue/trunk/magicrescue.c - Use both position and sequence number as a key for output filenames. Using only offsets broke checkrecipe for msoffice files. A better solution would be a more formal rename method. - Reset skip_bytes between files ------------------------------------------------------------------------ r120 | jbj | 2004-03-25 17:15:43 +0100 (Thu, 25 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/README.recipes D /magicrescue/trunk/checkrecipe A /magicrescue/trunk/tools/checkrecipe (from /magicrescue/trunk/checkrecipe:110) Move checkrecipe script to tools/ ------------------------------------------------------------------------ r119 | jbj | 2004-03-25 17:09:45 +0100 (Thu, 25 Mar 2004) | 3 lines Changed paths: A /magicrescue/trunk/config.d/50getrlimit M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/tools/ole_rename.pl - Use setrlimit to kill memory-exhausting processes - configure check for setrlimit ------------------------------------------------------------------------ r118 | jbj | 2004-03-25 15:55:46 +0100 (Thu, 25 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Make release.sh output helpful info for maintainer ------------------------------------------------------------------------ r117 | jbj | 2004-03-25 11:34:58 +0100 (Thu, 25 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh fixes for nightly build script ------------------------------------------------------------------------ r116 | jbj | 2004-03-25 11:25:34 +0100 (Thu, 25 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Nightly build support in release script ------------------------------------------------------------------------ r110 | jbj | 2004-03-23 16:23:10 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.0.3 ------------------------------------------------------------------------ r109 | jbj | 2004-03-23 14:49:41 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/config.d/50cygwin M /magicrescue/trunk/config.d/50off_t A /magicrescue/trunk/config.d/50perl M /magicrescue/trunk/tools/oleextract.pl More cygwin fixes and warnings ------------------------------------------------------------------------ r108 | jbj | 2004-03-23 13:10:34 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/tools/inputseek.c M /magicrescue/trunk/tools/safecat.c staticness, constness, and compilation with gcc's strictness options ------------------------------------------------------------------------ r107 | jbj | 2004-03-23 12:00:28 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/configure Don't leave config.h files on error ------------------------------------------------------------------------ r106 | jbj | 2004-03-23 10:48:41 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Huge whitespace update ------------------------------------------------------------------------ r105 | jbj | 2004-03-23 10:35:00 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/checkrecipe M /magicrescue/trunk/magicsort M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/tools/elfextract.pl M /magicrescue/trunk/tools/gzip_rename.pl M /magicrescue/trunk/tools/mp3extract.pl M /magicrescue/trunk/tools/ole_rename.pl M /magicrescue/trunk/tools/oleextract.pl M /magicrescue/trunk/tools/pngextract.pl /usr/bin/perl -> /usr/bin/env perl ------------------------------------------------------------------------ r104 | jbj | 2004-03-23 10:21:56 +0100 (Tue, 23 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/config.d/90magicrescue_defs M /magicrescue/trunk/configure More configure cleanups ------------------------------------------------------------------------ r103 | jbj | 2004-03-23 09:12:51 +0100 (Tue, 23 Mar 2004) | 3 lines Changed paths: A /magicrescue/trunk/config.d A /magicrescue/trunk/config.d/00cc A /magicrescue/trunk/config.d/50atoll A /magicrescue/trunk/config.d/50cygwin A /magicrescue/trunk/config.d/50off_t M /magicrescue/trunk/configure - configure tests moved out of main script and into modules - configure tests for sizeof off_t and the C compiler ------------------------------------------------------------------------ r102 | jbj | 2004-03-22 23:16:49 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/configure more cygwin fixes, now it can install ------------------------------------------------------------------------ r101 | jbj | 2004-03-22 21:49:38 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c make overlap unsigned, fixing cygwin problem ------------------------------------------------------------------------ r100 | jbj | 2004-03-22 21:14:13 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/recipes/msoffice wording ------------------------------------------------------------------------ r99 | jbj | 2004-03-22 21:05:35 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/configure M /magicrescue/trunk/tools/inputseek.c Support platforms without atoll (i.e. cygwin) ------------------------------------------------------------------------ r98 | jbj | 2004-03-22 18:32:47 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/msoffice A /magicrescue/trunk/tools/laola.pl A /magicrescue/trunk/tools/ole_rename.pl M /magicrescue/trunk/tools/oleextract.pl OLE extractor can now guess the file type and rename to reflect it ------------------------------------------------------------------------ r97 | jbj | 2004-03-22 18:20:02 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c casting fix ------------------------------------------------------------------------ r96 | jbj | 2004-03-22 00:08:40 +0100 (Mon, 22 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/msoffice A /magicrescue/trunk/tools/oleextract.pl Support for Microsoft Office files (OLE container) ------------------------------------------------------------------------ r95 | jbj | 2004-03-21 17:58:56 +0100 (Sun, 21 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/elf M /magicrescue/trunk/tools/elfextract.pl More validity checking on ELF files ------------------------------------------------------------------------ r94 | jbj | 2004-03-21 17:39:05 +0100 (Sun, 21 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/elf A /magicrescue/trunk/tools/elfextract.pl Add ELF recipe ------------------------------------------------------------------------ r93 | jbj | 2004-03-21 14:14:03 +0100 (Sun, 21 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in D /magicrescue/trunk/tools/Makefile Don't do recursive make ------------------------------------------------------------------------ r92 | jbj | 2004-03-20 20:45:49 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Rearranged functions so program execution flows from the bottom up, like most C programs ------------------------------------------------------------------------ r91 | jbj | 2004-03-20 20:26:37 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/recipes/gzip (from /magicrescue/trunk/recipes/gzip-deflate:89) D /magicrescue/trunk/recipes/gzip-any D /magicrescue/trunk/recipes/gzip-deflate Remove gzip-any recipe, because the gzip.org tool only supports deflate anyway ------------------------------------------------------------------------ r90 | jbj | 2004-03-20 20:26:01 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicsort Make magicsort work with new naming scheme ------------------------------------------------------------------------ r89 | jbj | 2004-03-20 20:06:23 +0100 (Sat, 20 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/README.recipes M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/recipes/gzip-any M /magicrescue/trunk/recipes/gzip-deflate A /magicrescue/trunk/tools/gzip_rename.pl - Add postextract directive to recipe format - Restore the original file names from the gzip recipes sometimes ------------------------------------------------------------------------ r88 | jbj | 2004-03-20 17:16:35 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS Output file names are now the match offset, not a sequence number ------------------------------------------------------------------------ r87 | jbj | 2004-03-20 15:57:25 +0100 (Sat, 20 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS - Don't overwrite existing output files - Install a signal handler to report progress before death ------------------------------------------------------------------------ r86 | jbj | 2004-03-20 14:28:52 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/tools M /magicrescue/trunk/tools/Makefile A /magicrescue/trunk/tools/inputseek.c Replace the -O option with tools/inputseek ------------------------------------------------------------------------ r85 | jbj | 2004-03-20 14:27:02 +0100 (Sat, 20 Mar 2004) | 1 line Changed paths: D /magicrescue/trunk/recipes/gzip A /magicrescue/trunk/recipes/gzip-any (from /magicrescue/trunk/recipes/gzip:78) A /magicrescue/trunk/recipes/gzip-deflate (from /magicrescue/trunk/recipes/gzip:78) Split gzip recipe to support non-deflate compression ------------------------------------------------------------------------ r84 | jbj | 2004-03-18 22:20:38 +0100 (Thu, 18 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/README D /magicrescue/trunk/commands M /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/recipes/avi A /magicrescue/trunk/tools (from /magicrescue/trunk/commands:80) R /magicrescue/trunk/tools/Makefile (from /magicrescue/trunk/commands/Makefile:78) R /magicrescue/trunk/tools/mp3extract.pl (from /magicrescue/trunk/commands/mp3extract.pl:78) R /magicrescue/trunk/tools/pngextract.pl (from /magicrescue/trunk/commands/pngextract.pl:78) R /magicrescue/trunk/tools/safecat.c (from /magicrescue/trunk/commands/safecat.c:78) rename commands -> tools ------------------------------------------------------------------------ r83 | jbj | 2004-03-18 22:14:58 +0100 (Thu, 18 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh Argument checking for release.sh ------------------------------------------------------------------------ r82 | jbj | 2004-03-15 15:48:54 +0100 (Mon, 15 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/Makefile.in - make install also installs READMEs - make install uses mkdir -p ------------------------------------------------------------------------ r81 | jbj | 2004-03-15 15:17:02 +0100 (Mon, 15 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README more usage info ------------------------------------------------------------------------ r80 | jbj | 2004-03-15 14:41:24 +0100 (Mon, 15 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/commands svn:ignore property set on commands ------------------------------------------------------------------------ r79 | jbj | 2004-03-15 14:40:57 +0100 (Mon, 15 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk M /magicrescue/trunk/release svn:ignore property set ------------------------------------------------------------------------ r78 | jbj | 2004-03-15 13:19:57 +0100 (Mon, 15 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/release.sh Release 1.0.2 ------------------------------------------------------------------------ r77 | jbj | 2004-03-15 12:17:21 +0100 (Mon, 15 Mar 2004) | 2 lines Changed paths: M /magicrescue/trunk/magicrescue.c int -> long for variables that may be > 16bit ------------------------------------------------------------------------ r76 | jbj | 2004-03-15 12:01:15 +0100 (Mon, 15 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c - Don't use libc to parse hex numbers - define _XOPEN_SOURCE for portability ------------------------------------------------------------------------ r75 | jbj | 2004-03-15 11:35:40 +0100 (Mon, 15 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/recipes/mp3-id3v2 - Change -O to only affect first device - Readability improvement for mp3-id3v2 recipe ------------------------------------------------------------------------ r74 | jbj | 2004-03-15 11:23:42 +0100 (Mon, 15 Mar 2004) | 4 lines Changed paths: M /magicrescue/trunk/commands/mp3extract.pl M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS - Use setrlimit to prevent core files - Rename -o to -O - Change mp3 extractor for use with checkrecipe ------------------------------------------------------------------------ r73 | jbj | 2004-03-15 01:10:56 +0100 (Mon, 15 Mar 2004) | 1 line Changed paths: D /magicrescue/trunk/recipes/mp3-id3 A /magicrescue/trunk/recipes/mp3-id3v1 (from /magicrescue/trunk/recipes/mp3-noid3:72) A /magicrescue/trunk/recipes/mp3-id3v2 (from /magicrescue/trunk/recipes/mp3-id3:70) D /magicrescue/trunk/recipes/mp3-noid3 More correct mp3 recipe names ------------------------------------------------------------------------ r72 | jbj | 2004-03-15 01:09:03 +0100 (Mon, 15 Mar 2004) | 4 lines Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/README.recipes M /magicrescue/trunk/commands/mp3extract.pl M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/gzip M /magicrescue/trunk/recipes/mp3-noid3 - Anti-overlap code - mp3 extractor is fast enough to actually be used now - Update READMEs to describe the new scanner code ------------------------------------------------------------------------ r71 | jbj | 2004-03-14 12:23:17 +0100 (Sun, 14 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c - Cleanups - Set LC_ALL=C on startup ------------------------------------------------------------------------ r70 | jbj | 2004-03-14 11:55:08 +0100 (Sun, 14 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/commands/mp3extract.pl M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/magicsort M /magicrescue/trunk/recipes/avi M /magicrescue/trunk/recipes/gzip M /magicrescue/trunk/recipes/jpeg-exif M /magicrescue/trunk/recipes/jpeg-jfif M /magicrescue/trunk/recipes/mp3-id3 M /magicrescue/trunk/recipes/mp3-noid3 M /magicrescue/trunk/recipes/png Merge changes from the tbm-scan branch ------------------------------------------------------------------------ r61 | jbj | 2004-03-07 20:30:50 +0100 (Sun, 07 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Oops, remove debug printf ------------------------------------------------------------------------ r60 | jbj | 2004-03-07 20:11:50 +0100 (Sun, 07 Mar 2004) | 5 lines Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS - Fixed a bug causing around 8 bytes for every 100KB to be ignored - Better memory handling for the list of operations - Handle invalid match operation names - Handle EOF nicer ------------------------------------------------------------------------ r59 | jbj | 2004-03-07 20:05:41 +0100 (Sun, 07 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile.in M /magicrescue/trunk/configure configure now supports environment variables, like autoconf ------------------------------------------------------------------------ r58 | jbj | 2004-03-07 03:16:00 +0100 (Sun, 07 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/configure More auto*-compatible configure script ------------------------------------------------------------------------ r57 | jbj | 2004-03-06 00:47:41 +0100 (Sat, 06 Mar 2004) | 4 lines Changed paths: M /magicrescue/trunk/magicrescue.c - Simplified some code for clarification - Reduced number of lseek() calls - Added source code comments for the hairy parts ------------------------------------------------------------------------ r56 | jbj | 2004-03-05 23:10:52 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: D /magicrescue/trunk/Makefile A /magicrescue/trunk/Makefile.in (from /magicrescue/trunk/Makefile:55) M /magicrescue/trunk/README A /magicrescue/trunk/configure M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS Add an autoconf-like (but not autoconf!) build system ------------------------------------------------------------------------ r55 | jbj | 2004-03-05 22:34:16 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile make install target ------------------------------------------------------------------------ r54 | jbj | 2004-03-05 21:49:29 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/recipes/avi M /magicrescue/trunk/recipes/mp3-id3 M /magicrescue/trunk/recipes/mp3-noid3 M /magicrescue/trunk/recipes/png search path for commands ------------------------------------------------------------------------ r53 | jbj | 2004-03-05 19:50:10 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/checkrecipe M /magicrescue/trunk/magicrescue.c Add search path for recipe files ------------------------------------------------------------------------ r52 | jbj | 2004-03-05 17:40:27 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c add char operation for completeness ------------------------------------------------------------------------ r51 | jbj | 2004-03-05 15:57:00 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS news ------------------------------------------------------------------------ r50 | jbj | 2004-03-05 15:55:05 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README A /magicrescue/trunk/magicsort Add magicsort ------------------------------------------------------------------------ r49 | jbj | 2004-03-05 15:08:27 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README.recipes A /magicrescue/trunk/checkrecipe (from /magicrescue/trunk/recipetest:48) D /magicrescue/trunk/recipetest recipetest -> checkrecipe ------------------------------------------------------------------------ r48 | jbj | 2004-03-05 15:03:40 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README.recipes D /magicrescue/trunk/provemewrong.pl A /magicrescue/trunk/recipetest (from /magicrescue/trunk/provemewrong.pl:41) provemewrong.pl -> recipetest ------------------------------------------------------------------------ r47 | jbj | 2004-03-05 14:16:36 +0100 (Fri, 05 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c convert -> extract f -> fd ------------------------------------------------------------------------ r46 | jbj | 2004-03-05 11:25:03 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.0.1 ------------------------------------------------------------------------ r45 | jbj | 2004-03-05 11:10:21 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/gzip don't recompress gzipped files ------------------------------------------------------------------------ r44 | jbj | 2004-03-05 11:09:44 +0100 (Fri, 05 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c show the recipe name, not extension, in info output ------------------------------------------------------------------------ r43 | jbj | 2004-03-04 23:30:33 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS A /magicrescue/trunk/recipes/gzip added gzip recipe ------------------------------------------------------------------------ r42 | jbj | 2004-03-04 22:40:53 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/commands/mp3extract.pl A /magicrescue/trunk/recipes/mp3-id3 A /magicrescue/trunk/recipes/mp3-noid3 mp3 extractor ------------------------------------------------------------------------ r41 | jbj | 2004-03-04 22:39:38 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/provemewrong.pl option -m to -r ------------------------------------------------------------------------ r40 | jbj | 2004-03-04 21:23:38 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/recipes/png commands should be executable ------------------------------------------------------------------------ r39 | jbj | 2004-03-04 21:22:27 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/commands/safecat.c M /magicrescue/trunk/recipes/avi configurable limits for safecat ------------------------------------------------------------------------ r38 | jbj | 2004-03-04 21:14:18 +0100 (Thu, 04 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile commands can have rare dependencies, so use make -k ------------------------------------------------------------------------ r37 | jbj | 2004-03-04 21:13:31 +0100 (Thu, 04 Mar 2004) | 3 lines Changed paths: M /magicrescue/trunk/magicrescue.c Use caps for arguments in usage More correct memory initialization ------------------------------------------------------------------------ r36 | jbj | 2004-03-02 13:44:19 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README More hints on recipe writing ------------------------------------------------------------------------ r35 | jbj | 2004-03-02 13:28:24 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/README.recipes References to web site in readme ------------------------------------------------------------------------ r34 | jbj | 2004-03-02 13:26:30 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile A /magicrescue/trunk/commands/Makefile A /magicrescue/trunk/commands/safecat.c M /magicrescue/trunk/new_NEWS M /magicrescue/trunk/recipes/avi Improved .avi recipe with commands/safecat.c ------------------------------------------------------------------------ r33 | (no author) | 2004-03-02 11:47:20 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile webdav test again... ------------------------------------------------------------------------ r32 | (no author) | 2004-03-02 11:42:29 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile reverse test ------------------------------------------------------------------------ r31 | (no author) | 2004-03-02 11:39:40 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile webdav test... ------------------------------------------------------------------------ r30 | (no author) | 2004-03-02 11:37:52 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/new_NEWS NEWS entry ------------------------------------------------------------------------ r29 | jbj | 2004-03-02 10:08:03 +0100 (Tue, 02 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c Nicer memory management ------------------------------------------------------------------------ r28 | jbj | 2004-03-01 22:08:42 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/NEWS M /magicrescue/trunk/new_NEWS Release 1.0 ------------------------------------------------------------------------ r27 | jbj | 2004-03-01 21:55:40 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README.recipes describe provemewrong.pl ------------------------------------------------------------------------ r26 | jbj | 2004-03-01 21:53:36 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/provemewrong.pl wording: magic config -> recipe ------------------------------------------------------------------------ r25 | jbj | 2004-03-01 21:48:22 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/release.sh verbose changelog ------------------------------------------------------------------------ r24 | jbj | 2004-03-01 21:44:45 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README README wording ------------------------------------------------------------------------ r23 | jbj | 2004-03-01 21:34:11 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README A /magicrescue/trunk/README.recipes M /magicrescue/trunk/magicrescue.c Description of recipe format ------------------------------------------------------------------------ r22 | jbj | 2004-03-01 20:45:15 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/recipes/avi M /magicrescue/trunk/recipes/jpeg-exif M /magicrescue/trunk/recipes/jpeg-jfif M /magicrescue/trunk/recipes/png Add 'extension' keyword in recipes ------------------------------------------------------------------------ r21 | jbj | 2004-03-01 19:43:25 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README M /magicrescue/trunk/magicrescue.c M /magicrescue/trunk/new_NEWS Change the term 'magic config' to 'recipe' ------------------------------------------------------------------------ r20 | jbj | 2004-03-01 19:21:29 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/README D /magicrescue/trunk/magic A /magicrescue/trunk/recipes (from /magicrescue/trunk/magic:10) R /magicrescue/trunk/recipes/avi (from /magicrescue/trunk/magic/avi:13) R /magicrescue/trunk/recipes/jpeg-exif (from /magicrescue/trunk/magic/jpeg-exif:13) R /magicrescue/trunk/recipes/jpeg-jfif (from /magicrescue/trunk/magic/jpeg-jfif:13) R /magicrescue/trunk/recipes/png (from /magicrescue/trunk/magic/png:15) magic -> recipes ------------------------------------------------------------------------ r19 | jbj | 2004-03-01 14:27:19 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/release A /magicrescue/trunk/release.sh Add release.sh ------------------------------------------------------------------------ r18 | jbj | 2004-03-01 14:01:28 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/NEWS A /magicrescue/trunk/new_NEWS add NEWS files ------------------------------------------------------------------------ r17 | jbj | 2004-03-01 13:40:53 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/COPYING M /magicrescue/trunk/magicrescue.c Add GPL license ------------------------------------------------------------------------ r16 | jbj | 2004-03-01 12:50:55 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: A /magicrescue/trunk/provemewrong.pl Added provemewrong.pl for testing ------------------------------------------------------------------------ r15 | jbj | 2004-03-01 12:47:31 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/commands/pngextract.pl M /magicrescue/trunk/magic/png png files can be small ------------------------------------------------------------------------ r14 | jbj | 2004-03-01 11:36:24 +0100 (Mon, 01 Mar 2004) | 2 lines Changed paths: A /magicrescue/trunk/README Add a short readme ------------------------------------------------------------------------ r13 | jbj | 2004-03-01 10:56:30 +0100 (Mon, 01 Mar 2004) | 1 line Changed paths: M /magicrescue/trunk/magic/avi M /magicrescue/trunk/magic/jpeg-exif M /magicrescue/trunk/magic/jpeg-jfif M /magicrescue/trunk/magic/png M /magicrescue/trunk/magicrescue.c Comments in magic files ------------------------------------------------------------------------ r12 | jbj | 2004-02-29 23:33:41 +0100 (Sun, 29 Feb 2004) | 1 line Changed paths: M /magicrescue/trunk/Makefile gcc -> cc ------------------------------------------------------------------------ r11 | jbj | 2004-02-29 23:27:29 +0100 (Sun, 29 Feb 2004) | 1 line Changed paths: M /magicrescue/trunk/magicrescue.c handle lseek() errors ------------------------------------------------------------------------ r10 | jbj | 2004-02-29 22:55:51 +0100 (Sun, 29 Feb 2004) | 2 lines Changed paths: A /magicrescue A /magicrescue/trunk A /magicrescue/trunk/Makefile A /magicrescue/trunk/commands A /magicrescue/trunk/commands/pngextract.pl A /magicrescue/trunk/magic A /magicrescue/trunk/magic/avi A /magicrescue/trunk/magic/jpeg-exif A /magicrescue/trunk/magic/jpeg-jfif A /magicrescue/trunk/magic/png A /magicrescue/trunk/magicrescue.c Initial import of magicrescue ------------------------------------------------------------------------ magicrescue-1.1.8/doc/dupemap.10000644000175000017500000003655211274314765014413 0ustar jbjjbj.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "DUPEMAP 1" .TH DUPEMAP 1 "2008-06-26" "1.1.8" "Magic Rescue" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" dupemap \- Creates a database of file checksums and uses it to eliminate duplicates .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBdupemap\fR [ \fIoptions\fR ] [ \fB\-d\fR \fIdatabase\fR ] \fIoperation\fR \fIpath...\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBdupemap\fR recursively scans each \fIpath\fR to find checksums of file contents. Directories are searched through in no particular order. Its actions depend on whether the \fB\-d\fR option is given, and on the \fIoperation\fR parameter, which must be a comma-seperated list of \fBscan\fR, \fBreport\fR, \fBdelete\fR: .SS "Without \fB\-d\fP" .IX Subsection "Without -d" \&\fBdupemap\fR will take action when it sees the same checksum repeated more than once, i.e. it simply finds duplicates recursively. The action depends on \&\fIoperation\fR: .IP "\fBreport\fR" 7 .IX Item "report" Report what files are encountered more than once, printing their names to standard output. .IP "\fBdelete\fR[\fB,report\fR]" 7 .IX Item "delete[,report]" Delete files that are encountered more than once. Print their names if \&\fBreport\fR is also given. .Sp \&\fI\s-1WARNING:\s0\fR use the \fBreport\fR operation first to see what will be deleted. .Sp \&\fI\s-1WARNING:\s0\fR You are advised to make a backup of the target first, e.g. with \&\f(CW\*(C`cp \-al\*(C'\fR (for \s-1GNU\s0 cp) to create hard links recursively. .SS "With \fB\-d\fP" .IX Subsection "With -d" The \fIdatabase\fR argument to \fB\-d\fR will denote a database file (see the \&\*(L"\s-1DATABASE\s0\*(R" section in this manual for details) to read from or write to. In this mode, the \fBscan\fR operation should be run on one \fIpath\fR, followed by the \&\fBreport\fR or \fBdelete\fR operation on another (\fInot the same!\fR) \fIpath\fR. .IP "\fBscan\fR" 7 .IX Item "scan" Add the checksum of each file to \fIdatabase\fR. This operation must be run initially to create the database. To start over, you must manually delete the database file(s) (see the \*(L"\s-1DATABASE\s0\*(R" section). .IP "\fBreport\fR" 7 .IX Item "report" Print each file name if its checksum is found in \fIdatabase\fR. .IP "\fBdelete\fR[\fB,report\fR]" 7 .IX Item "delete[,report]" Delete each file if its checksum is found in \fIdatabase\fR. If \fBreport\fR is also present, print the name of each deleted file. .Sp \&\fI\s-1WARNING:\s0\fR if you run \fBdupemap delete\fR on the same \fIpath\fR you just ran \&\fBdupemap scan\fR on, it will \fIdelete every file!\fR The idea of these options is to scan one \fIpath\fR and delete files in a second \fIpath\fR. .Sp \&\fI\s-1WARNING:\s0\fR use the \fBreport\fR operation first to see what will be deleted. .Sp \&\fI\s-1WARNING:\s0\fR You are advised to make a backup of the target first, e.g. with \&\f(CW\*(C`cp \-al\*(C'\fR (for \s-1GNU\s0 cp) to create hard links recursively. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-d\fR \fIdatabase\fR" 7 .IX Item "-d database" Use \fIdatabase\fR as an on-disk database to read from or write to. See the \&\*(L"\s-1DESCRIPTION\s0\*(R" section above about how this influences the operation of \&\fBdupemap\fR. .IP "\fB\-I\fR \fIfile\fR" 7 .IX Item "-I file" Reads input files from \fIfile\fR in addition to those listed on the command line. If \fIfile\fR is \f(CW\*(C`\-\*(C'\fR, read from standard input. Each line will be interpreted as a file name. .Sp The paths given here will \s-1NOT\s0 be scanned recursively. Directories will be ignored and symlinks will be followed. .IP "\fB\-m\fR \fIminsize\fR" 7 .IX Item "-m minsize" Ignore files below this size. .IP "\fB\-M\fR \fImaxsize\fR" 7 .IX Item "-M maxsize" Ignore files above this size. .SH "USAGE" .IX Header "USAGE" .SS "General usage" .IX Subsection "General usage" The easiest operations to understand is when the \fB\-d\fR option is not given. To delete all duplicate files in \fI/tmp/recovered\-files\fR, do: .PP .Vb 1 \& $ dupemap delete /tmp/recovered\-files .Ve .PP Often, \fBdupemap scan\fR is run to produce a checksum database of all files in a directory tree. Then \fBdupemap delete\fR is run on another directory, possibly following \fBdupemap report\fR. For example, to delete all files in \&\fI/tmp/recovered\-files\fR that already exist in \fI\f(CI$HOME\fI\fR, do this: .PP .Vb 2 \& $ dupemap \-d homedir.map scan $HOME \& $ dupemap \-d homedir.map delete,report /tmp/recovered\-files .Ve .SS "Usage with magicrescue" .IX Subsection "Usage with magicrescue" The main application for \fBdupemap\fR is to take some pain out of performing undelete operations with \fBmagicrescue\fR(1). The reason is that \fBmagicrescue\fR will extract every single file of the specified type on the block device, so undeleting files requires you to find a few files out of hundreds, which can take a long time if done manually. What we want to do is to only extract the documents that don't exist on the file system already. .PP In the following scenario, you have accidentally deleted some important Word documents in Windows. If this were a real-world scenario, then by all means use The Sleuth Kit. However, \fBmagicrescue\fR will work even when the directory entries were overwritten, i.e. more files were stored in the same folder later. .PP You boot into Linux and change to a directory with lots of space. Mount the Windows partition, preferably read-only (especially with \s-1NTFS\s0), and create the directories we will use. .PP .Vb 2 \& $ mount \-o ro /dev/hda1 /mnt/windows \& $ mkdir healthy_docs rescued_docs .Ve .PP Extract all the healthy Word documents with \fBmagicrescue\fR and build a database of their checksums. It may seem a little redundant to send all the documents through \fBmagicrescue\fR first, but the reason is that this process may modify them (e.g. stripping trailing garbage), and therefore their checksum will not be the same as the original documents. Also, it will find documents embedded inside other files, such as uncompressed zip archives or files with the wrong extension. .PP .Vb 4 \& $ find /mnt/windows \-type f \e \& |magicrescue \-I\- \-r msoffice \-d healthy_docs \& $ dupemap \-d healthy_docs.map scan healthy_docs \& $ rm \-rf healthy_docs .Ve .PP Now rescue all \f(CW\*(C`msoffice\*(C'\fR documents from the block device and get rid of everything that's not a *.doc. .PP .Vb 2 \& $ magicrescue \-Mo \-r msoffice \-d rescued_docs /dev/hda1 \e \& |grep \-v \*(Aq\e.doc$\*(Aq|xargs rm \-f .Ve .PP Remove all the rescued documents that also appear on the file system, and remove duplicates. .PP .Vb 2 \& $ dupemap \-d healthy_docs.map delete,report rescued_docs \& $ dupemap delete,report rescued_docs .Ve .PP The \fIrescued_docs\fR folder should now contain only a few files. This will be the undeleted files and some documents that were not stored in contiguous blocks (use that defragger ;\-)). .SS "Usage with fsck" .IX Subsection "Usage with fsck" In this scenario (based on a true story), you have a hard disk that's gone bad. You have managed to \fIdd\fR about 80% of the contents into the file \fIdiskimage\fR, and you have an old backup from a few months ago. The disk is using reiserfs on Linux. .PP First, use fsck to make the file system usable again. It will find many nameless files and put them in \fIlost+found\fR. You need to make sure there is some free space on the disk image, so fsck has something to work with. .PP .Vb 6 \& $ cp diskimage diskimage.bak \& $ dd if=/dev/zero bs=1M count=2048 >> diskimage \& $ reiserfsck \-\-rebuild\-tree diskimage \& $ mount \-o loop diskimage /mnt \& $ ls /mnt/lost+found \& (tons of files) .Ve .PP Our strategy will be to restore the system with the old backup as a base and merge the two other sets of files (\fI/mnt/lost+found\fR and \fI/mnt\fR) into the backup after eliminating duplicates. Therefore we create a checksum database of the directory we have unpacked the backup in. .PP .Vb 1 \& $ dupemap \-d backup.map scan ~/backup .Ve .PP Next, we eliminate all the files from the rescued image that are also present in the backup. .PP .Vb 1 \& $ dupemap \-d backup.map delete,report /mnt .Ve .PP We also want to remove duplicates from \fIlost+found\fR, and we want to get rid of any files that are also present in the other directories in \fI/mnt\fR. .PP .Vb 3 \& $ dupemap delete,report /mnt/lost+found \& $ ls /mnt|grep \-v lost+found|xargs dupemap \-d mnt.map scan \& $ dupemap \-d mnt.map delete,report /mnt/lost+found .Ve .PP This should leave only the files in \fI/mnt\fR that have changed since the last backup or got corrupted. Particularly, the contents of \fI/mnt/lost+found\fR should now be reduced enough to manually sort through them (or perhaps use \&\fBmagicsort\fR(1)). .SS "Primitive intrusion detection" .IX Subsection "Primitive intrusion detection" You can use \fBdupemap\fR to see what files change on your system. This is one of the more exotic uses, and it's only included for inspiration. .PP First, you map the whole file system. .PP .Vb 1 \& $ dupemap \-d old.map scan / .Ve .PP Then you come back a few days/weeks later and run \fBdupemap report\fR. This will give you a view of what \fIhas not\fR changed. To see what \fIhas\fR changed, you need a list of the whole file system. You can get this list along with preparing a new map easily. Both lists need to be sorted to be compared. .PP .Vb 2 \& $ dupemap \-d old.map report /|sort > unchanged_files \& $ dupemap \-d current.map scan /|sort > current_files .Ve .PP All that's left to do is comparing these files and preparing for next week. This assumes that the dbm appends the \f(CW\*(C`.db\*(C'\fR extension to database files. .PP .Vb 2 \& $ diff unchanged_files current_files > changed_files \& $ mv current.map.db old.map.db .Ve .SH "DATABASE" .IX Header "DATABASE" The actual database file(s) written by \fBdupecheck\fR will have some relation to the \fIdatabase\fR argument, but most implementations append an extension. For example, Berkeley \s-1DB\s0 names the files \fIdatabase\fR\fB.db\fR, while Solaris and \s-1GDBM\s0 creates both a \fIdatabase\fR\fB.dir\fR and \fIdatabase\fR\fB.pag\fR file. .PP \&\fBdupecheck\fR depends on a database library for storing the checksums. It currently requires the POSIX-standardized \fBndbm\fR library, which must be present on XSI-compliant UNIXes. Implementations are not required to handle hash key collisions, and a faliure to do that could make \fBdupecheck\fR delete too many files. I haven't heard of such an implementation, though. .PP The current checksum algorithm is the file's \s-1CRC32\s0 combined with its size. Both values are stored in native byte order, and because of varying type sizes the database is \fInot\fR portable across architectures, compilers and operating systems. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBmagicrescue\fR(1), \fBweeder\fR(1) .PP This tool does the same thing \fBweeder\fR does, except that \fBweeder\fR cannot seem to handle many files without crashing, and it has no largefile support. .SH "BUGS" .IX Header "BUGS" There is a tiny chance that two different files can have the same checksum and size. The probability of this happening is around 1 to 10^14, and since \&\fBdupemap\fR is part of the Magic Rescue package, which deals with disaster recovery, that chance becomes an insignificant part of the game. You should consider this if you apply \fBdupemap\fR to other applications, especially if they are security-related (see next paragraph). .PP It is possible to craft a file to have a known \s-1CRC32\s0. You need to keep this in mind if you use \fBdupemap\fR on untrusted data. A solution to this could be to implement an option for using \s-1MD5\s0 checksums instead. .SH "AUTHOR" .IX Header "AUTHOR" Jonas Jensen .SH "LATEST VERSION" .IX Header "LATEST VERSION" This tool is part of Magic Rescue. You can find the latest version at magicrescue-1.1.8/doc/magicrescue.10000644000175000017500000005166411274314764015247 0ustar jbjjbj.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MAGICRESCUE 1" .TH MAGICRESCUE 1 "2008-10-29" "1.1.8" "Magic Rescue" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" magicrescue \- Scans a block device and extracts known file types by looking at magic bytes. .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmagicrescue\fR [ \fIoptions\fR ] \fIdevices\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" Magic Rescue opens \fIdevices\fR for reading, scans them for file types it knows how to recover and calls an external program to extract them. It looks at \&\*(L"magic bytes\*(R" in file contents, so it can be used both as an undelete utility and for recovering a corrupted drive or partition. It works on any file system, but on very fragmented file systems it can only recover the first chunk of each file. These chunks are sometimes as big as 50MB, however. .PP To invoke \fBmagicrescue\fR, you must specify at least one device and the \fB\-d\fR and \fB\-r\fR options. See the \*(L"\s-1USAGE\s0\*(R" section in this manual for getting started. .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fB\-b\fR \fIblocksize\fR" 7 .IX Item "-b blocksize" Default: 1. This will direct \fBmagicrescue\fR to only consider files that start at a multiple of the \fIblocksize\fR argument. The option applies only to the recipes following it, so by specifying it multiple times it can be used to get different behavior for different recipes. .Sp Using this option you can usually get better performance, but fewer files will be found. In particular, files with leading garbage (e.g. many mp3 files) and files contained inside other files are likely to be skipped. Also, some file systems don't align small files to block boundaries, so those won't be found this way either. .Sp If you don't know your file system's block size, just use the value 512, which is almost always the hardware sector size. .IP "\fB\-d\fR \fIdirectory\fR" 7 .IX Item "-d directory" Mandatory. Output directory for found files. Make sure you have plenty of free space in this directory, especially when extracting very common file types such as jpeg or gzip files. Also make sure the file system is able to handle thousands of files in a single directory, i.e. don't use \s-1FAT\s0 if you are extracting many files. .Sp You should not place the output directory on the same block device you are trying to rescue files from. This might add the same file to the block device ahead of the current reading position, causing \fBmagicrescue\fR to find the same file again later. In the worst theoretical case, this could cause a loop where the same file is extracted thousands of times until disk space is exhausted. You are also likely to overwrite the deleted files you were looking for in the first place. .IP "\fB\-r\fR \fIrecipe\fR" 7 .IX Item "-r recipe" Mandatory. Recipe name, file, or directory. Specify this as either a plain name (e.g. \f(CW\*(C`jpeg\-jfif\*(C'\fR) or a path (e.g. \fIrecipes/jpeg\-jfif\fR). If it doesn't find such a file in the current directory, it will look in \fI./recipes\fR and \&\fIPREFIX/share/magicrescue/recipes\fR, where \fI\s-1PREFIX\s0\fR is the path you installed to, e.g. \fI/usr/local\fR. .Sp If \fIrecipe\fR is a directory, all files in that directory will be treated as recipes. .Sp Browse the \fIPREFIX/share/magicrescue/recipes\fR directory to see what recipes are available. A recipe is a text file, and you should read the comments inside it before using it. Either use the recipe as it is or copy it somewhere and modify it. .Sp For information on creating your own recipes, see the \*(L"\s-1RECIPES\s0\*(R" section. .IP "\fB\-I\fR \fIfile\fR" 7 .IX Item "-I file" Reads input files from \fIfile\fR in addition to those listed on the command line. If \fIfile\fR is \f(CW\*(C`\-\*(C'\fR, read from standard input. Each line will be interpreted as a file name. .IP "\fB\-M\fR \fIoutput_mode\fR" 7 .IX Item "-M output_mode" Produce machine-readable output to stdout. \fIoutput_mode\fR can be: .RS 7 .IP "\fBi\fR" 4 .IX Item "i" Print each input file name before processing .IP "\fBo\fR" 4 .IX Item "o" Print each output file name after processing .IP "\fBio\fR" 4 .IX Item "io" Print both input and output file names. Input file names will be prefixed by \&\f(CW\*(C`i\*(C'\fR and a space. Output file names will be prefixed by \f(CW\*(C`o\*(C'\fR and a space. .RE .RS 7 .Sp Nothing else will be written to standard output in this mode. .RE .IP "\fB\-O\fR [\fB+\fR|\fB\-\fR|\fB=\fR][\fB0x\fR]\fIoffset\fR" 7 .IX Item "-O [+|-|=][0x]offset" Resume from the specified \fIoffset\fR in the first device. If prefixed with \&\fB0x\fR it will be interpreted as a hex number. .Sp The number may be prefixed with a sign: .RS 7 .IP "\fB=\fR" 4 .IX Item "=" Seek to an absolute position (default) .IP "\fB+\fR" 4 .IX Item "+" Seek to a relative position. On regular files this does the same as the above. .IP "\fB\-\fR" 4 .IX Item "-" Seek to \s-1EOF\s0, minus the offset. .RE .RS 7 .RE .SH "USAGE" .IX Header "USAGE" Say you have destroyed the file system on /dev/hdb1 and you want to extract all the jpeg files you lost. This guide assumes you have installed Magic Rescue in \fI/usr/local\fR, which is the default. .PP Make sure \s-1DMA\s0 and other optimizations are enabled on your disk, or it will take hours. In Linux, use hdparm to set these options: .PP .Vb 1 \& $ hdparm \-d 1 \-c 1 \-u 1 /dev/hdb .Ve .PP Choose your output directory, somewhere with lots of disk space. .PP .Vb 1 \& $ mkdir ~/output .Ve .PP Look in the \fI/usr/local/share/magicrescue/recipes\fR directory for the recipes you want. Magic Rescue comes with recipes for some common file types, and you can make your own too (see the next section). Open the recipes you want to use in a text editor and read their comments. Most recipes require 3rd party software to work, and you may want to modify some parameters (such as \&\fBmin_output_file\fR) to suit your needs. .PP Then invoke \fBmagicrescue\fR .PP .Vb 1 \& $ magicrescue \-r jpeg\-jfif \-r jpeg\-exif \-d ~/output /dev/hdb1 .Ve .PP It will scan through your entire hard disk, so it may take a while. You can stop it and resume later of you want to. To do so, interrupt it (with \s-1CTRL+C\s0) and note the progress information saying what address it got to. Then restart it later with the \fB\-O\fR option. .PP When it has finished you will probably find thousands of .jpg files in \&\fI~/output\fR, including things you never knew was in your browser cache. Sorting through all those files can be a huge task, so you may want to use software or scripts to do it. .PP First, try to eliminate duplicates with the \fBdupemap\fR(1) tool included in this package. .PP .Vb 1 \& $ dupemap delete,report ~/output .Ve .PP If you are performing an undelete operation you will want to get rid of all the rescued files that also appear on the live file system. See the \&\fBdupemap\fR(1) manual for instructions on doing this. .PP If that's not enough, you can use use \fBmagicsort\fR(1) to get a better overview: .PP .Vb 1 \& $ magicsort ~/output .Ve .SH "RECIPES" .IX Header "RECIPES" .SS "Creating recipe files" .IX Subsection "Creating recipe files" A recipe file is a relatively simple file of 3\-5 lines of text. It describes how to recognise the beginning of the file and what to do when a file is recognised. For example, all jfif images start with the bytes \f(CW\*(C`0xff 0xd8\*(C'\fR. At the 6th byte will be the string \f(CW\*(C`JFIF\*(C'\fR. Look at \fIrecipes/jpeg\-jfif\fR in the source distribution to follow this example. .PP Matching magic data is done with a \*(L"match operation\*(R" that looks like this: .PP \&\fIoffset\fR \fIoperation\fR \fIparameter\fR .PP where \fIoffset\fR is a decimal integer saying how many bytes from the beginning of the file this data is located, \fIoperation\fR refers to a built-in match operation in \fBmagicrescue\fR, and \fIparameter\fR is specific to that operation. .IP "\(bu" 4 The \fBstring\fR operation matches a string of any length. In the jfif example this is four bytes. You can use escape characters, like \f(CW\*(C`\en\*(C'\fR or \f(CW\*(C`\exA7\*(C'\fR. .IP "\(bu" 4 The \fBint32\fR operation matches 4 bytes ANDed with a bit mask. To match all four bytes, use the bit mask \f(CW\*(C`FFFFFFFF\*(C'\fR. If you have no idea what a bit mask is, just use the \fBstring\fR operation instead. The mask \f(CW\*(C`FFFF0000\*(C'\fR in the jfif example matches the first two bytes. .IP "\(bu" 4 The \fBchar\fR operation is like \*(L"string\*(R", except it only matches a single character. .PP To learn these patterns for a given file type, look at files of the desired type in a hex editor, search through the resource files for the \fBfile\fR(1) utility () and/or search the Internet for a reference on the format. .PP If all the operations match, we have found the start of the file. Finding the end of the file is a much harder problem, and therefore it is delegated to an external shell command, which is named by the \fBcommand\fR directive. This command receives the block device's file descriptor on stdin and must write to the file given to it in the \f(CW$1\fR variable. Apart from that, the command can do anything it wants to try and extract the file. .PP For some file types (such as jpeg), a tool already exists that can do this. However, many programs misbehave when told to read from the middle of a huge block device. Some seek to byte 0 before reading (can be fixed by prefixing cat|, but some refuse to work on a file they can't seek in). Others try to read the whole file into memory before doing anything, which will of course fail on a muti-gigabyte block device. And some fail completely to parse a partially corrupted file. .PP This means that you may have to write your own tool or wrap an existing program in some scripts that make it behave better. For example, this could be to extract the first 10MB into a temporary file and let the program work on that. Or perhaps you can use \fItools/safecat\fR if the file may be very large. .SS "Recipe format reference" .IX Subsection "Recipe format reference" Empty lines and lines starting with \f(CW\*(C`#\*(C'\fR will be skipped. A recipe contains a series of match operations to find the content and a series of directives to specify what to do with it. .PP Lines of the format \fIoffset\fR \fIoperation\fR \fIparameter\fR will add a match operation to the list. Match operations will be tried in the order they appear in the recipe, and they must all match for the recipe to succeed. The \&\fIoffset\fR describes what offset this data will be found at, counting from the beginning of the file. \fIoperation\fR can have the following values: .IP "\fBstring\fR \fIstring\fR" 7 .IX Item "string string" The parameter is a character sequence that may contain escape sequences such as \exFF. .IP "\fBchar\fR \fIcharacter\fR" 7 .IX Item "char character" The parameter is a single character (byte), or an escape sequence. .IP "\fBint32\fR \fIvalue\fR \fIbitmask\fR" 7 .IX Item "int32 value bitmask" Both \fIvalue\fR and \fIbitmask\fR are expressed as 8\-character hex strings. \&\fIbitmask\fR will be ANDed with the data, and the result will be compared to \fIvalue\fR. The byte order is as you see it in the hex editor, i.e. big-endian. .PP The first match operation in a recipe is special, it will be used to scan through the file. Only the \fBchar\fR and \fBstring\fR operations can be used there. To add more operation types, look at the instructions in \fImagicrescue.c\fR. .PP A line that doesn't start with an integer is a directive. This can be: .IP "\fBextension\fR \fIext\fR" 7 .IX Item "extension ext" Mandatory. \fIext\fR names the file extension for this type, such as \f(CW\*(C`jpg\*(C'\fR. .IP "\fBcommand\fR \fIcommand\fR" 7 .IX Item "command command" Mandatory. When all the match operations succeed, this \fIcommand\fR will be executed to extract the file from the block device. \fIcommand\fR is passed to the shell with the block device's file descriptor (seeked to the right byte) on stdin. The shell variable \f(CW$1\fR will contain the file its output should be written to, and it must respect this. Otherwise \fBmagicrescue\fR cannot tell whether it succeeded. .IP "\fBrename\fR \fIcommand\fR" 7 .IX Item "rename command" Optional. After a successful extraction this command will be run. Its purpose is to gather enough information about the file to rename it to something more meaningful. The script must not perform the rename command itself, but it should write to standard output the string \f(CW\*(C`RENAME\*(C'\fR, followed by a space, followed by the new file name. Nothing else must be written to standard output. If the file should not be renamed, nothing should be written to standard output. Standard input and \f(CW$1\fR will work like with the \fBcommand\fR directive. .IP "\fBmin_output_file\fR \fIsize\fR" 7 .IX Item "min_output_file size" Default: 100. Output files less than this size will be deleted. .IP "\fBallow_overlap\fR \fIbytes\fR" 7 .IX Item "allow_overlap bytes" By default, recipes will not match on overlapping byte ranges. \&\fBallow_overlap\fR disables this, and it should always be used for recipes where the extracted file may be larger than it was on disk. If \fIbytes\fR is negative, overlap checking will be completely disabled. Otherwise, overlap checking will be in effect for everything but the last \fIbytes\fR of the output. For example, if the output may be up to 512 bytes bigger than the input, \fBallow_overlap\fR should be set to 512. .PP To test whether your recipe actually works, either just run it on your hard disk or use the \fItools/checkrecipe\fR script to pick out files that should match but don't. .PP If you have created a recipe that works, please mail it to me at jbj@knef.dk so I can include it in the distribution. .SH "WHEN TO NOT USE MAGIC RESCUE" .IX Header "WHEN TO NOT USE MAGIC RESCUE" Magic Rescue is not meant to be a universal application for file recovery. It will give good results when you are extracting known file types from an unusable file system, but for many other cases there are better tools available. .IP "\(bu" 4 If there are intact partitions present somewhere, use \fBgpart\fR to find them. .IP "\(bu" 4 If file system's internal data structures are more or less undamaged, use \&\fBThe Sleuth Kit\fR. At the time of writing, it only supports \s-1NTFS\s0, \s-1FAT\s0, ext[23] and \s-1FFS\s0, though. .IP "\(bu" 4 If Magic Rescue does not have a recipe for the file type you are trying to recover, try \fBforemost\fR instead. It recognizes more file types, but in most cases it extracts them simply by copying out a fixed number of bytes after it has found the start of the file. This makes postprocessing the output files more difficult. .PP In many cases you will want to use Magic Rescue in addition to the tools mentioned above. They are not mutually exclusive, e.g. combining \&\fBmagicrescue\fR with \fBdls\fR from The Sleuth Kit could give good results. In many cases you'll want to use \fBmagicrescue\fR to extract its known file types and another utility to extract the rest. .PP When combining the results of more than one tool, \fBdupemap\fR(1) can be used to eliminate duplicates. .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "Similar programs" 4 .IX Item "Similar programs" .RS 4 .PD 0 .IP "\fBgpart\fR(8)" 4 .IX Item "gpart(8)" .PD . Tries to rebuild the partition table by scanning the disk for lost partitions. .IP "\fBforemost\fR(1)" 4 .IX Item "foremost(1)" . Does the same thing as \fBmagicrescue\fR, except that its \*(L"recipes\*(R" are less complex. Finding the end of the file must happen by either matching an \s-1EOF\s0 string or just extracting a fixed number of bytes every time. It supports more file types than Magic Rescue, but extracted files usually have lots of trailing garbage, so removal of duplicates and sorting by size is not possible. .IP "\fBThe Sleuth Kit\fR" 4 .IX Item "The Sleuth Kit" . This popular package of utilities is extremely useful for undeleting files from a FAT/NTFS/ext2/ext3/FFS file system that's not completely corrupted. Most of the utilities are not very useful if the file system has been corrupted or overwritten. It is based on The Coroner's Toolkit (). .IP "\s-1JPEG\s0 recovery tools" 4 .IX Item "JPEG recovery tools" This seems to be the file type most people are trying to recover. Available utilities include , , and . .RE .RS 4 .RE .IP "Getting disk images from failed disks" 4 .IX Item "Getting disk images from failed disks" \&\fBdd\fR(1), \fBrescuept\fR(1), , , , .IP "Processing \fBmagicrescue\fR's output" 4 .IX Item "Processing magicrescue's output" \&\fBdupemap\fR(1), \fBfile\fR(1), \fBmagicsort\fR(1), .IP "Authoring recipes" 4 .IX Item "Authoring recipes" \&\fBmagic\fR(4), \fBhexedit\fR(1), .IP "Filesystem-specific undelete utilities" 4 .IX Item "Filesystem-specific undelete utilities" There are too many to count them, especially for ext2 and \s-1FAT\s0. Find them on Google and Freshmeat. .SH "AUTHOR" .IX Header "AUTHOR" Jonas Jensen .SH "LATEST VERSION" .IX Header "LATEST VERSION" You can find the latest version at magicrescue-1.1.8/doc/magicsort.10000644000175000017500000001055411274314765014742 0ustar jbjjbj.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .ie \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .el \{\ . de IX .. .\} .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MAGICSORT 1" .TH MAGICSORT 1 "2008-06-26" "1.1.8" "Magic Rescue" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" magicsort \- Categorize files by their \fBfile\fR(1) magic .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmagicsort\fR \fIdirectory\fR .SH "DESCRIPTION" .IX Header "DESCRIPTION" Invokes the system's \fBfile\fR utility on all files in \fIdirectory\fR non-recursively. For each different string that \fBfile\fR outputs it will create a sub-directory of that name and move the file in there. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBmagicrescue\fR(1), \fBdupemap\fR(1) .SH "AUTHOR" .IX Header "AUTHOR" Jonas Jensen .SH "LATEST VERSION" .IX Header "LATEST VERSION" This tool is part of Magic Rescue. You can find the latest version at magicrescue-1.1.8/COPYING0000644000175000017500000004311511030775346013151 0ustar jbjjbj GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. magicrescue-1.1.8/Makefile.in0000644000175000017500000000420411030775346014157 0ustar jbjjbjSHELL = /bin/sh PROGRAMS = magicrescue dupemap tools/inputseek tools/safecat \ tools/textextract DOCS = doc/magicrescue.1 doc/dupemap.1 doc/magicsort.1 # object file lists MAGICRESCUE_OBJS = src/magicrescue.o src/array.o src/extract.o src/recipe.o \ src/scanner.o src/util.o DUPEMAP_OBJS = src/dupemap.o src/recur.o INPUTSEEK_OBJS = tools/inputseek.o src/util.o SAFECAT_OBJS = tools/safecat.o src/util.o TEXTEXTRACT_OBJS = tools/textextract.o src/util.o # default target all: $(PROGRAMS) docs # program targets magicrescue: $(MAGICRESCUE_OBJS) $(CC) -o $@ $(LDFLAGS) $(MAGICRESCUE_OBJS) dupemap: $(DUPEMAP_OBJS) $(CC) -o $@ $(LDFLAGS) $(DBM_LDFLAGS) $(DUPEMAP_OBJS) tools/inputseek: $(INPUTSEEK_OBJS) $(CC) -o $@ $(LDFLAGS) $(INPUTSEEK_OBJS) tools/safecat: $(SAFECAT_OBJS) $(CC) -o $@ $(LDFLAGS) $(SAFECAT_OBJS) tools/textextract: $(TEXTEXTRACT_OBJS) $(CC) -o $@ $(LDFLAGS) $(TEXTEXTRACT_OBJS) # implicit targets .c.o: $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $< .o: $(CC) -o $@ $(LDFLAGS) $< .pod.1: pod2man --release "$(RELEASE)" --center "Magic Rescue" $< $@ # phony targets docs: $(DOCS) clean: rm -f src/*.o tools/*.o distclean: clean rm -f $(PROGRAMS) Makefile config.h config.log docs-clean: rm -f $(DOCS) maintainer-clean: distclean docs-clean install: all [ -d $(PREFIX) ] mkdir -p $(PREFIX)/share/magicrescue/tools \ $(PREFIX)/share/magicrescue/recipes \ $(PREFIX)/man/man1 \ $(PREFIX)/bin cp magicrescue$(EXE) dupemap$(EXE) magicsort $(PREFIX)/bin/ cp recipes/* $(PREFIX)/share/magicrescue/recipes/ cp $(DOCS) $(PREFIX)/man/man1 for f in tools/*; do \ if [ -x "$$f" ]; then \ cp -f "$$f" $(PREFIX)/share/magicrescue/tools/; \ fi; \ done uninstall: [ -d $(PREFIX) ] rm -f $(PREFIX)/bin/magicrescue$(EXE) rm -f $(PREFIX)/bin/dupemap$(EXE) rm -f $(PREFIX)/bin/magicsort for f in $(DOCS); do \ rm -f "$(PREFIX)/man/man1/`basename $$f`"; \ done rm -rf $(PREFIX)/share/magicrescue/tools rm -rf $(PREFIX)/share/magicrescue/recipes -rmdir $(PREFIX)/share/magicrescue .PHONY: all clean distclean docs-clean maintainer-clean install uninstall docs .SUFFIXES: .1 .pod .POSIX: magicrescue-1.1.8/NEWS0000644000175000017500000000672211274314765012624 0ustar jbjjbjVersion 1.1.8: Wed Nov 4 16:12:53 CET 2009 - Add mbox recipes, contributed by Jeff Bucove. - Raise maximum virtual memory usage for helper programs from 50MB to 1024MB. It turns out that mencoder needs around 150MB on 64-bit systems. - Place tools directory first in path to avoid name clashes with other programs. Version 1.1.7: Fri Sep 4 19:48:14 CEST 2009 - Update zip recipe to work with latest version of Zip and latest OpenOffice.org file formats (thanks to Fernando) Version 1.1.6: Sun Feb 15 22:13:54 CET 2009 - Recipe for Nikon raw photos - Recipe for large PPM files Version 1.1.5: Thu Jan 17 14:03:49 CET 2008 - Recipe for Flac files - Recipe for Canon CR2 RAW files - Configure script compatibility fix for Debian-based Linuxes Version 1.1.4: Sun Aug 15 01:11:27 CEST 2004 - Fixed a minor bug that prevented some not yet written recipes from working perfectly Version 1.1.3: Tue Aug 10 22:32:42 CEST 2004 - Fixed a minor bug to make jpeg matching 100% reliable instead of 99.4% - Added GIMP xcf recipe - Added text extraction and experimental recipes for perl and gpl - Added test suite to ensure correctness - Support for dupemap on Debian and others - Extraction of msoffice on cygwin Version 1.1.2: Thu May 6 22:47:52 CEST 2004 - Added the -O option for easy resuming from any offset - Added the -I option for easier usage with find(1) - Reorganized source code - Wrote more documentation Version 1.1.1: Sat May 1 19:14:14 CEST 2004 - Added recipe for zip files and OpenOffice.org documents - Added -b option for block alignment - If argument to -r is a directory, read all files within it - Anti-overlapping between files is now global instead of per-recipe - Fixed bug that made anti-overlap code skip too much sometimes - Fixed bug that prevented the use of an absolute path for recipes - dupemap works on KNOPPIX now Version 1.1.0: Tue Apr 13 13:23:28 CEST 2004 - Transformed README files into proper manpages and wrote more documentation - Added dupemap, another tool for getting a better overview of the output - Recipes take the output file name in $1 now, instead of $0 - Changed the output file format to adapt to "find|xargs magicrescue" usage - Make magicrescue aware of output-file renaming - Ported to Solaris - Added more choices for machine-readable output Version 1.0.4: Fri Mar 26 18:17:05 CET 2004 - tools/checkrecipe was broken for some file types, fixed that - Fixed a rounding bug that could lead to segfaulting on some rare data - Limits memory to 50MB per process, to prevent helper programs from exhausting memory on invalid input Version 1.0.3: Tue Mar 23 16:02:19 CET 2004 - New recipes for Microsoft Office formats and ELF - Recipe for gzip now extracts file names, if available - Proper support was added for resuming an interrupted run - Output file names now reflect match offsets - Portability fixes, especially for Cygwin Version 1.0.2: Mon Mar 15 13:17:48 CET 2004 - Much improved mp3 recipe - Anti-overlapping prevents the same recipe from extracting part of the same file twice - Added magicsort for categorizing the output files - Magic Rescue now has a "normal" build system (./configure, make, make install) - Bugfixes in the buffer overlapping code - Prevents helper programs from leaving core files Version 1.0.1: Fri Mar 5 11:15:04 CET 2004 - Nicer memory management - Improved .avi recipe - mp3 recipe - gzip recipe Version 1.0.0: Mon Mar 1 21:56:45 CET 2004 - Initial public release - Has recipe parser and comes with recipes for avi, jpeg and png magicrescue-1.1.8/README0000644000175000017500000000175711030775346013004 0ustar jbjjbjMagic Rescue ------------ Magic Rescue scans a block device for file types it knows how to recover and calls an external program to extract them. It looks at "magic bytes" in file contents, so it can be used both as an undelete utility and for recovering a corrupted drive or partition. As long as the file data is there, it will find it. It works on any file system, but on very fragmented file systems it can only recover the first chunk of each file. Practical experience (this program was not written for fun) shows, however, that chunks of 30-50MB are not uncommon. Find the latest version at http://jbj.rapanden.dk/magicrescue/ Building -------- There are no build requirements other than a C library and a UNIXish system. To use the dupemap(1) utility, you must have the NDBM compatibility header, which either comes with your system or the development libraries of GDBM or Berkeley DB. ./configure && make && make install The "make install" step is optional. Using ----- man magicrescue magicrescue-1.1.8/config.config0000644000175000017500000000002311030775346014541 0ustar jbjjbjapp="Magic Rescue" magicrescue-1.1.8/config.d/10cc0000644000175000017500000000125311030775345014252 0ustar jbjjbj#!/bin/sh try_cc() { echo1 "Checking whether the C compiler ($CC $CPPFLAGS $CFLAGS) works..." if conftest; then echo2 yes return 0 else echo2 no return 1 fi } # Try to sense what flags the compiler will accept all_tries() { try_cc && return 0 CFLAGS="-O" try_cc && return 0 CFLAGS= try_cc && return 0 CPPFLAGS= try_cc && return 0 return 1 } cat > conftest.c << EOF #include int main() { return 0; } EOF if all_tries; then true; else cat << EOF configure: Aborting. Your C compiler ($CC $CPPFLAGS $CFLAGS) does not seem to work. See config.log for details. EOF rm -f conftest conftest.c exit 1 fi magicrescue-1.1.8/config.d/10magicrescue_defs0000644000175000017500000000005211030775345017151 0ustar jbjjbj#!/bin/sh CPPFLAGS="$CPPFLAGS -I. -Isrc" magicrescue-1.1.8/config.d/50atoll0000644000175000017500000000027211030775345015004 0ustar jbjjbj#!/bin/sh # the atoll function appeared in C99 cat > conftest.c << EOF #include int main() { return (int)atoll("0"); } EOF conftest_define HAVE_ATOLL "Checking for atoll..." magicrescue-1.1.8/config.d/50cygwin0000644000175000017500000000135211030775345015171 0ustar jbjjbj#!/bin/sh # Cygwin needs some special handling for .exe files MAKE_VARS="$MAKE_VARS EXE" cat > conftest.c << EOF int main() { #ifdef __CYGWIN__ return 0; #else return 1; #endif } EOF echo1 "Checking for Cygwin..." if conftest; then echo2 yes cat << EOF configure: WARNING: At the time of writing, you cannot use Magic Rescue directly on Cygwin's block devices, such as /dev/sda1. This is caused by a bug in either Cygwin or Windows. Copying the block device out to a file and running magicrescue on the file works. Also, many of Cygwin's included utilities lack support for large files, which means that you have to either recompile them or split your file into 2GB chunks. EOF EXE=.exe else echo2 no fi magicrescue-1.1.8/config.d/50dbm0000644000175000017500000000272311030775345014436 0ustar jbjjbj#!/bin/sh try_dbm() { dbmdef="$1" link_possible="$2" bak_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -D$dbmdef" if conftest_compile; then CPPFLAGS="$bak_CPPFLAGS" for flag in "" $link_possible; do flag="`echo $flag|sed 's/./-l&/'`" echo "trying to link with flags [$flag]" >&5 bak_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $flag" if conftest_link; then # we found it! LDFLAGS="$bak_LDFLAGS" DBM_LDFLAGS="$flag" env_vars="$env_vars DBM_LDFLAGS" echo "#define $dbmdef" >> config.h echo "#define HAVE_NDBM" >> config.h return 0 else LDFLAGS="$bak_LDFLAGS" fi done return 1 else CPPFLAGS="$bak_CPPFLAGS" return 1 fi } find_ndbm() { try_dbm DBM_H_GDBM "gdbm gdbm_compat" && return 0 try_dbm DBM_H_DEB "gdbm gdbm_compat" && return 0 try_dbm DBM_H_UNIX "db db1 gdbm gdbm_compat ndbm" && return 0 try_dbm DBM_H_DB_H "db db1 ndbm gdbm gdbm_compat" && return 0 try_dbm DBM_H_DB "db db1 gdbm gdbm_compat ndbm" && return 0 try_dbm DBM_H_DB1 "db1 db" && return 0 return 1 } cat > conftest.c << EOF #include #include "find_dbm.h" int main() { DBM *db = dbm_open(".", O_RDONLY, 0); /* will fail at run-time */ datum my_datum; my_datum.dptr = 0; my_datum.dsize = (my_datum.dptr == 0 && db == 0); return 0; } EOF echo1 "Checking for ndbm.h..." if find_ndbm; then echo2 "yes" else echo2 "no" fi magicrescue-1.1.8/config.d/50getrlimit0000644000175000017500000000056311261125127015665 0ustar jbjjbj#!/bin/sh cat > conftest.c << EOF #include #include #include int main() { struct rlimit rlp; return !(getrlimit(RLIMIT_CORE, &rlp) == 0 && getrlimit(RLIMIT_AS , &rlp) == 0); } EOF if conftest_define HAVE_GETRLIMIT "Checking for getrlimit..."; then echo "#define MAX_MEMORY 1024*1024*1024" >> config.h fi magicrescue-1.1.8/config.d/50off_t0000644000175000017500000000103611030775345014765 0ustar jbjjbj#!/bin/sh # Size of off_t should be at least 8 cat > conftest.c << EOF #include "largefile.h" #include #include int main() { printf("%d", sizeof(off_t)); if (sizeof(off_t) < 8) return 1; else { return 0; } } EOF echo1 "Checking the size of off_t..." if conftest; then echo else echo " < 8" cat << EOF configure: WARNING: Your system does not seem to support large files (i.e. files > 2GB). You will not be able to use Magic Rescue on files or partitions above that size. EOF fi magicrescue-1.1.8/config.d/50perl0000644000175000017500000000204311030775345014631 0ustar jbjjbj#!/bin/sh # These checks are warnings only echo1 "Checking for perl..." lf="`perl -V:uselargefiles 2>/dev/null`" if [ 0 -ne $? ]; then echo2 "not found" cat << EOF You do not appear to have perl on your system. Even though perl is not required for using magicrescue, you should be aware that many of the included recipes and tools require it. EOF else if echo "$lf"|grep define >/dev/null; then if /usr/bin/env perl /dev/null 2>&1; then echo2 "ok" else echo2 "almost" cat << EOF You have perl on your system, but invoking it with "/usr/bin/env perl" does not work. Unless your system is completely broken or nonstandard, please report this issue. In the mean time you need to change the first line of all the included perl scripts you wish to use, if any. EOF fi else echo2 "present" cat << EOF You have perl, but support for large files is not enabled. You should either recompile perl with -Duselargefiles or chop your files into 2GB pieces to use any of the recipes that require perl. EOF fi fi magicrescue-1.1.8/config.d/80magicrescue_defs0000644000175000017500000000025511030775345017165 0ustar jbjjbj#!/bin/sh cat >> config.h << EOF #define COMMAND_PATH "$prefix/share/magicrescue/tools" #define RECIPE_PATH "$prefix/share/magicrescue/recipes" #include "largefile.h" EOF magicrescue-1.1.8/config.d/90dep0000644000175000017500000000053411030775345014446 0ustar jbjjbj#!/bin/sh echo1 "Finding dependencies..." if $CC -MM $CPPFLAGS $CFLAGS src/magicrescue.c >/dev/null 2>&5; then exec 6> Makefile.dep for dir in src tools; do for f in $dir/*.c; do $CC -MM $CPPFLAGS $CFLAGS "$f" 2>&5|sed "1s%^%$dir/%" >&6 done done echo2 ok exec 6>&- else rm -f Makefile.dep echo2 "skipped" fi magicrescue-1.1.8/configure0000755000175000017500000001032611030775346014023 0ustar jbjjbj#!/bin/sh # This is a flexible yet simple configure script that can be used instead of # GNU autoconf for simple projects written in C. It depends on the file # config.config for application-specific globals and the files in config.d/ for # tests to perform. # Copyright (C) 2004 Jonas Jensen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Default options # app="the application" prefix=/usr/local env_vars="CC CFLAGS CPPFLAGS LDFLAGS" def_CC=cc def_CFLAGS="-O3 -Wall" MAKE_VARS= if [ -f config.config ]; then . ./config.config fi # # Helper functions # echo1() { echo $ECHO_N "$* $ECHO_C"; } echo2() { echo "$ECHO_T$*"; } conftest() { conftest_compile && conftest_link } conftest_compile() { cat >&5 << EOF Compiling: --------------------------- EOF cat conftest.c >&5 echo "---------------------------" >&5 echo "$CC -c $CPPFLAGS $CFLAGS -o conftest.o conftest.c" >&5 rm -f conftest.o $CC -c $CPPFLAGS $CFLAGS -o conftest.o conftest.c >&5 2>&5 \ && [ -f conftest.o ] } conftest_link() { echo "$CC -o conftest $LDFLAGS conftest.o" >&5 echo >&5 $CC -o conftest $LDFLAGS conftest.o >&5 2>&5 \ && [ -x conftest ] && ./conftest 2>&5 } conftest_define() { echo1 "$2" if conftest; then echo "#define $1 1" >> config.h echo2 "yes" return 0 else echo2 "no" return 1 fi } # # Initialize # exec 5>config.log date >&5 [ -d config.d ] || { echo "Directory config.d not found. Run configure from the source root." exit 1 } if mv -f config.h config.h.bak 2>/dev/null; then trap "mv config.h.bak config.h" 0 fi cat > config.h << EOF /* XXX This file was automatically generated by configure. */ EOF # # Environment # for var in $env_vars; do eval "$var=\${$var:-\$def_$var}" done # # Parse arguments # for option do case "$option" in --help) cat << EOF This will configure $app for your system. Options: --prefix=DIR Path to install under [$prefix] CC=COMMAND C compiler to use [$CC] CFLAGS=OPTIONS C compiler flags [$CFLAGS] EOF exit ;; --strict) CFLAGS="$CFLAGS -std=c9x -W -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls" echo "#define _POSIX_C_SOURCE 200112L" >> config.h echo "#define _XOPEN_SOURCE 600" >> config.h ;; --*=*) arg=`echo "$option"|cut -d= -f 2-` case "$option" in --prefix=*) prefix="$arg" ;; *) echo "configure: warning: ignoring option $option" ;; esac ;; *=*) #export "$option" # doesn't work on Solaris var=`echo "$option"|cut -d= -f 1` arg=`echo "$option"|cut -d= -f 2-` eval "$var='$arg'" esac done # # Do tests # # echo test, stolen from GNU autoconf. This really shows UNIX at its worst... case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac for f in config.d/*; do cat >&5 << EOF configure: PERFORMING TEST $f EOF . "$f" done # # Clean up # trap "" 0 rm -f config.h.bak conftest.c conftest.o conftest # # Generate Makefile # make clean >/dev/null 2>&1 rm -f Makefile 2>/dev/null || chmod 644 Makefile cat > Makefile << EOF # XXX This Makefile is automatically generated by configure. # XXX Modify Makefile.in to change it. PREFIX = $prefix EOF for var in $env_vars $MAKE_VARS; do eval "val=\"\$$var\"" echo "$var = $val" >> Makefile done cat "Makefile.in" >> Makefile if [ -f Makefile.dep ]; then cat Makefile.dep >> Makefile rm -f Makefile.dep fi chmod 444 Makefile echo echo "Done. Now type 'make' to compile" magicrescue-1.1.8/doc/dupemap.pod0000644000175000017500000002441611030775346015025 0ustar jbjjbj=head1 NAME dupemap - Creates a database of file checksums and uses it to eliminate duplicates =head1 SYNOPSIS B [ I ] [ B<-d> I ] I I =head1 DESCRIPTION B recursively scans each I to find checksums of file contents. Directories are searched through in no particular order. Its actions depend on whether the B<-d> option is given, and on the I parameter, which must be a comma-seperated list of B, B, B: =head2 Without B<-d> B will take action when it sees the same checksum repeated more than once, i.e. it simply finds duplicates recursively. The action depends on I: =over 7 =item B Report what files are encountered more than once, printing their names to standard output. =item B[B<,report>] Delete files that are encountered more than once. Print their names if B is also given. I use the B operation first to see what will be deleted. I You are advised to make a backup of the target first, e.g. with C (for GNU cp) to create hard links recursively. =back =head2 With B<-d> The I argument to B<-d> will denote a database file (see the L section in this manual for details) to read from or write to. In this mode, the B operation should be run on one I, followed by the B or B operation on another (I) I. =over 7 =item B Add the checksum of each file to I. This operation must be run initially to create the database. To start over, you must manually delete the database file(s) (see the L section). =item B Print each file name if its checksum is found in I. =item B[B<,report>] Delete each file if its checksum is found in I. If B is also present, print the name of each deleted file. I if you run B on the same I you just ran B on, it will I The idea of these options is to scan one I and delete files in a second I. I use the B operation first to see what will be deleted. I You are advised to make a backup of the target first, e.g. with C (for GNU cp) to create hard links recursively. =back =head1 OPTIONS =over 7 =item B<-d> I Use I as an on-disk database to read from or write to. See the L section above about how this influences the operation of B. =item B<-I> I Reads input files from I in addition to those listed on the command line. If I is C<->, read from standard input. Each line will be interpreted as a file name. The paths given here will NOT be scanned recursively. Directories will be ignored and symlinks will be followed. =item B<-m> I Ignore files below this size. =item B<-M> I Ignore files above this size. =back =head1 USAGE =head2 General usage The easiest operations to understand is when the B<-d> option is not given. To delete all duplicate files in F, do: $ dupemap delete /tmp/recovered-files Often, B is run to produce a checksum database of all files in a directory tree. Then B is run on another directory, possibly following B. For example, to delete all files in F that already exist in F<$HOME>, do this: $ dupemap -d homedir.map scan $HOME $ dupemap -d homedir.map delete,report /tmp/recovered-files =head2 Usage with magicrescue The main application for B is to take some pain out of performing undelete operations with B(1). The reason is that B will extract every single file of the specified type on the block device, so undeleting files requires you to find a few files out of hundreds, which can take a long time if done manually. What we want to do is to only extract the documents that don't exist on the file system already. In the following scenario, you have accidentally deleted some important Word documents in Windows. If this were a real-world scenario, then by all means use The Sleuth Kit. However, B will work even when the directory entries were overwritten, i.e. more files were stored in the same folder later. You boot into Linux and change to a directory with lots of space. Mount the Windows partition, preferably read-only (especially with NTFS), and create the directories we will use. $ mount -o ro /dev/hda1 /mnt/windows $ mkdir healthy_docs rescued_docs Extract all the healthy Word documents with B and build a database of their checksums. It may seem a little redundant to send all the documents through B first, but the reason is that this process may modify them (e.g. stripping trailing garbage), and therefore their checksum will not be the same as the original documents. Also, it will find documents embedded inside other files, such as uncompressed zip archives or files with the wrong extension. $ find /mnt/windows -type f \ |magicrescue -I- -r msoffice -d healthy_docs $ dupemap -d healthy_docs.map scan healthy_docs $ rm -rf healthy_docs Now rescue all C documents from the block device and get rid of everything that's not a *.doc. $ magicrescue -Mo -r msoffice -d rescued_docs /dev/hda1 \ |grep -v '\.doc$'|xargs rm -f Remove all the rescued documents that also appear on the file system, and remove duplicates. $ dupemap -d healthy_docs.map delete,report rescued_docs $ dupemap delete,report rescued_docs The F folder should now contain only a few files. This will be the undeleted files and some documents that were not stored in contiguous blocks (use that defragger ;-)). =head2 Usage with fsck In this scenario (based on a true story), you have a hard disk that's gone bad. You have managed to F
about 80% of the contents into the file F, and you have an old backup from a few months ago. The disk is using reiserfs on Linux. First, use fsck to make the file system usable again. It will find many nameless files and put them in F. You need to make sure there is some free space on the disk image, so fsck has something to work with. $ cp diskimage diskimage.bak $ dd if=/dev/zero bs=1M count=2048 >> diskimage $ reiserfsck --rebuild-tree diskimage $ mount -o loop diskimage /mnt $ ls /mnt/lost+found (tons of files) Our strategy will be to restore the system with the old backup as a base and merge the two other sets of files (F and F) into the backup after eliminating duplicates. Therefore we create a checksum database of the directory we have unpacked the backup in. $ dupemap -d backup.map scan ~/backup Next, we eliminate all the files from the rescued image that are also present in the backup. $ dupemap -d backup.map delete,report /mnt We also want to remove duplicates from F, and we want to get rid of any files that are also present in the other directories in F. $ dupemap delete,report /mnt/lost+found $ ls /mnt|grep -v lost+found|xargs dupemap -d mnt.map scan $ dupemap -d mnt.map delete,report /mnt/lost+found This should leave only the files in F that have changed since the last backup or got corrupted. Particularly, the contents of F should now be reduced enough to manually sort through them (or perhaps use B(1)). =head2 Primitive intrusion detection You can use B to see what files change on your system. This is one of the more exotic uses, and it's only included for inspiration. First, you map the whole file system. $ dupemap -d old.map scan / Then you come back a few days/weeks later and run B. This will give you a view of what I changed. To see what I changed, you need a list of the whole file system. You can get this list along with preparing a new map easily. Both lists need to be sorted to be compared. $ dupemap -d old.map report /|sort > unchanged_files $ dupemap -d current.map scan /|sort > current_files All that's left to do is comparing these files and preparing for next week. This assumes that the dbm appends the C<.db> extension to database files. $ diff unchanged_files current_files > changed_files $ mv current.map.db old.map.db =head1 DATABASE The actual database file(s) written by B will have some relation to the I argument, but most implementations append an extension. For example, Berkeley DB names the files IB<.db>, while Solaris and GDBM creates both a IB<.dir> and IB<.pag> file. B depends on a database library for storing the checksums. It currently requires the POSIX-standardized B library, which must be present on XSI-compliant UNIXes. Implementations are not required to handle hash key collisions, and a faliure to do that could make B delete too many files. I haven't heard of such an implementation, though. The current checksum algorithm is the file's CRC32 combined with its size. Both values are stored in native byte order, and because of varying type sizes the database is I portable across architectures, compilers and operating systems. =head1 SEE ALSO B(1), B(1) This tool does the same thing B does, except that B cannot seem to handle many files without crashing, and it has no largefile support. =head1 BUGS There is a tiny chance that two different files can have the same checksum and size. The probability of this happening is around 1 to 10^14, and since B is part of the Magic Rescue package, which deals with disaster recovery, that chance becomes an insignificant part of the game. You should consider this if you apply B to other applications, especially if they are security-related (see next paragraph). It is possible to craft a file to have a known CRC32. You need to keep this in mind if you use B on untrusted data. A solution to this could be to implement an option for using MD5 checksums instead. =head1 AUTHOR Jonas Jensen =head1 LATEST VERSION This tool is part of Magic Rescue. You can find the latest version at L magicrescue-1.1.8/doc/magicrescue.pod0000644000175000017500000003657611102006354015655 0ustar jbjjbj=head1 NAME magicrescue - Scans a block device and extracts known file types by looking at magic bytes. =head1 SYNOPSIS B [ I ] I =head1 DESCRIPTION Magic Rescue opens I for reading, scans them for file types it knows how to recover and calls an external program to extract them. It looks at "magic bytes" in file contents, so it can be used both as an undelete utility and for recovering a corrupted drive or partition. It works on any file system, but on very fragmented file systems it can only recover the first chunk of each file. These chunks are sometimes as big as 50MB, however. To invoke B, you must specify at least one device and the B<-d> and B<-r> options. See the L section in this manual for getting started. =head1 OPTIONS =over 7 =item B<-b> I Default: 1. This will direct B to only consider files that start at a multiple of the I argument. The option applies only to the recipes following it, so by specifying it multiple times it can be used to get different behavior for different recipes. Using this option you can usually get better performance, but fewer files will be found. In particular, files with leading garbage (e.g. many mp3 files) and files contained inside other files are likely to be skipped. Also, some file systems don't align small files to block boundaries, so those won't be found this way either. If you don't know your file system's block size, just use the value 512, which is almost always the hardware sector size. =item B<-d> I Mandatory. Output directory for found files. Make sure you have plenty of free space in this directory, especially when extracting very common file types such as jpeg or gzip files. Also make sure the file system is able to handle thousands of files in a single directory, i.e. don't use FAT if you are extracting many files. You should not place the output directory on the same block device you are trying to rescue files from. This might add the same file to the block device ahead of the current reading position, causing B to find the same file again later. In the worst theoretical case, this could cause a loop where the same file is extracted thousands of times until disk space is exhausted. You are also likely to overwrite the deleted files you were looking for in the first place. =item B<-r> I Mandatory. Recipe name, file, or directory. Specify this as either a plain name (e.g. C) or a path (e.g. F). If it doesn't find such a file in the current directory, it will look in F<./recipes> and F, where I is the path you installed to, e.g. F. If I is a directory, all files in that directory will be treated as recipes. Browse the F directory to see what recipes are available. A recipe is a text file, and you should read the comments inside it before using it. Either use the recipe as it is or copy it somewhere and modify it. For information on creating your own recipes, see the L section. =item B<-I> I Reads input files from I in addition to those listed on the command line. If I is C<->, read from standard input. Each line will be interpreted as a file name. =item B<-M> I Produce machine-readable output to stdout. I can be: =over =item B Print each input file name before processing =item B Print each output file name after processing =item B Print both input and output file names. Input file names will be prefixed by C and a space. Output file names will be prefixed by C and a space. =back Nothing else will be written to standard output in this mode. =item B<-O> [B<+>|B<->|B<=>][B<0x>]I Resume from the specified I in the first device. If prefixed with B<0x> it will be interpreted as a hex number. The number may be prefixed with a sign: =over =item B<=> Seek to an absolute position (default) =item B<+> Seek to a relative position. On regular files this does the same as the above. =item B<-> Seek to EOF, minus the offset. =back =back =head1 USAGE Say you have destroyed the file system on /dev/hdb1 and you want to extract all the jpeg files you lost. This guide assumes you have installed Magic Rescue in F, which is the default. Make sure DMA and other optimizations are enabled on your disk, or it will take hours. In Linux, use hdparm to set these options: $ hdparm -d 1 -c 1 -u 1 /dev/hdb Choose your output directory, somewhere with lots of disk space. $ mkdir ~/output Look in the F directory for the recipes you want. Magic Rescue comes with recipes for some common file types, and you can make your own too (see the next section). Open the recipes you want to use in a text editor and read their comments. Most recipes require 3rd party software to work, and you may want to modify some parameters (such as B) to suit your needs. Then invoke B $ magicrescue -r jpeg-jfif -r jpeg-exif -d ~/output /dev/hdb1 It will scan through your entire hard disk, so it may take a while. You can stop it and resume later of you want to. To do so, interrupt it (with CTRL+C) and note the progress information saying what address it got to. Then restart it later with the B<-O> option. When it has finished you will probably find thousands of .jpg files in F<~/output>, including things you never knew was in your browser cache. Sorting through all those files can be a huge task, so you may want to use software or scripts to do it. First, try to eliminate duplicates with the B(1) tool included in this package. $ dupemap delete,report ~/output If you are performing an undelete operation you will want to get rid of all the rescued files that also appear on the live file system. See the B(1) manual for instructions on doing this. If that's not enough, you can use use B(1) to get a better overview: $ magicsort ~/output =head1 RECIPES =head2 Creating recipe files A recipe file is a relatively simple file of 3-5 lines of text. It describes how to recognise the beginning of the file and what to do when a file is recognised. For example, all jfif images start with the bytes C<0xff 0xd8>. At the 6th byte will be the string C. Look at F in the source distribution to follow this example. Matching magic data is done with a "match operation" that looks like this: I I I where I is a decimal integer saying how many bytes from the beginning of the file this data is located, I refers to a built-in match operation in B, and I is specific to that operation. =over =item * The B operation matches a string of any length. In the jfif example this is four bytes. You can use escape characters, like C<\n> or C<\xA7>. =item * The B operation matches 4 bytes ANDed with a bit mask. To match all four bytes, use the bit mask C. If you have no idea what a bit mask is, just use the B operation instead. The mask C in the jfif example matches the first two bytes. =item * The B operation is like "string", except it only matches a single character. =back To learn these patterns for a given file type, look at files of the desired type in a hex editor, search through the resource files for the B(1) utility (L) and/or search the Internet for a reference on the format. If all the operations match, we have found the start of the file. Finding the end of the file is a much harder problem, and therefore it is delegated to an external shell command, which is named by the B directive. This command receives the block device's file descriptor on stdin and must write to the file given to it in the C<$1> variable. Apart from that, the command can do anything it wants to try and extract the file. For some file types (such as jpeg), a tool already exists that can do this. However, many programs misbehave when told to read from the middle of a huge block device. Some seek to byte 0 before reading (can be fixed by prefixing cat|, but some refuse to work on a file they can't seek in). Others try to read the whole file into memory before doing anything, which will of course fail on a muti-gigabyte block device. And some fail completely to parse a partially corrupted file. This means that you may have to write your own tool or wrap an existing program in some scripts that make it behave better. For example, this could be to extract the first 10MB into a temporary file and let the program work on that. Or perhaps you can use F if the file may be very large. =head2 Recipe format reference Empty lines and lines starting with C<#> will be skipped. A recipe contains a series of match operations to find the content and a series of directives to specify what to do with it. Lines of the format I I I will add a match operation to the list. Match operations will be tried in the order they appear in the recipe, and they must all match for the recipe to succeed. The I describes what offset this data will be found at, counting from the beginning of the file. I can have the following values: =over 7 =item B I The parameter is a character sequence that may contain escape sequences such as \xFF. =item B I The parameter is a single character (byte), or an escape sequence. =item B I I Both I and I are expressed as 8-character hex strings. I will be ANDed with the data, and the result will be compared to I. The byte order is as you see it in the hex editor, i.e. big-endian. =back The first match operation in a recipe is special, it will be used to scan through the file. Only the B and B operations can be used there. To add more operation types, look at the instructions in F. A line that doesn't start with an integer is a directive. This can be: =over 7 =item B I Mandatory. I names the file extension for this type, such as C. =item B I Mandatory. When all the match operations succeed, this I will be executed to extract the file from the block device. I is passed to the shell with the block device's file descriptor (seeked to the right byte) on stdin. The shell variable C<$1> will contain the file its output should be written to, and it must respect this. Otherwise B cannot tell whether it succeeded. =item B I Optional. After a successful extraction this command will be run. Its purpose is to gather enough information about the file to rename it to something more meaningful. The script must not perform the rename command itself, but it should write to standard output the string C, followed by a space, followed by the new file name. Nothing else must be written to standard output. If the file should not be renamed, nothing should be written to standard output. Standard input and C<$1> will work like with the B directive. =item B I Default: 100. Output files less than this size will be deleted. =item B I By default, recipes will not match on overlapping byte ranges. B disables this, and it should always be used for recipes where the extracted file may be larger than it was on disk. If I is negative, overlap checking will be completely disabled. Otherwise, overlap checking will be in effect for everything but the last I of the output. For example, if the output may be up to 512 bytes bigger than the input, B should be set to 512. =back To test whether your recipe actually works, either just run it on your hard disk or use the F script to pick out files that should match but don't. If you have created a recipe that works, please mail it to me at jbj@knef.dk so I can include it in the distribution. =head1 WHEN TO NOT USE MAGIC RESCUE Magic Rescue is not meant to be a universal application for file recovery. It will give good results when you are extracting known file types from an unusable file system, but for many other cases there are better tools available. =over =item * If there are intact partitions present somewhere, use B to find them. =item * If file system's internal data structures are more or less undamaged, use B. At the time of writing, it only supports NTFS, FAT, ext[23] and FFS, though. =item * If Magic Rescue does not have a recipe for the file type you are trying to recover, try B instead. It recognizes more file types, but in most cases it extracts them simply by copying out a fixed number of bytes after it has found the start of the file. This makes postprocessing the output files more difficult. =back In many cases you will want to use Magic Rescue in addition to the tools mentioned above. They are not mutually exclusive, e.g. combining B with B from The Sleuth Kit could give good results. In many cases you'll want to use B to extract its known file types and another utility to extract the rest. When combining the results of more than one tool, B(1) can be used to eliminate duplicates. =head1 SEE ALSO =over =item Similar programs =over =item B(8) L. Tries to rebuild the partition table by scanning the disk for lost partitions. =item B(1) L. Does the same thing as B, except that its "recipes" are less complex. Finding the end of the file must happen by either matching an EOF string or just extracting a fixed number of bytes every time. It supports more file types than Magic Rescue, but extracted files usually have lots of trailing garbage, so removal of duplicates and sorting by size is not possible. =item B L. This popular package of utilities is extremely useful for undeleting files from a FAT/NTFS/ext2/ext3/FFS file system that's not completely corrupted. Most of the utilities are not very useful if the file system has been corrupted or overwritten. It is based on The Coroner's Toolkit (L). =item JPEG recovery tools This seems to be the file type most people are trying to recover. Available utilities include L, L, and L. =back =item Getting disk images from failed disks B
(1), B(1), L, L, L, L =item Processing B's output B(1), B(1), B(1), L =item Authoring recipes B(4), B(1), L =item Filesystem-specific undelete utilities There are too many to count them, especially for ext2 and FAT. Find them on Google and Freshmeat. =back =head1 AUTHOR Jonas Jensen =head1 LATEST VERSION You can find the latest version at L magicrescue-1.1.8/doc/magicsort.pod0000644000175000017500000000106511030775346015355 0ustar jbjjbj=head1 NAME magicsort - Categorize files by their B(1) magic =head1 SYNOPSIS B I =head1 DESCRIPTION Invokes the system's B utility on all files in I non-recursively. For each different string that B outputs it will create a sub-directory of that name and move the file in there. =head1 SEE ALSO B(1), B(1) =head1 AUTHOR Jonas Jensen =head1 LATEST VERSION This tool is part of Magic Rescue. You can find the latest version at L magicrescue-1.1.8/magicsort0000755000175000017500000000137511030775346014036 0ustar jbjjbj#!/usr/bin/env perl use strict; my $dir = shift; if (!$dir or !chdir($dir)) { print "Usage: magicsort DIRECTORY\n", "Will invoke your system's file(1) utility to categorize all the files found\n", "by magicrescue.\n"; exit 1; } opendir DH, "." or die "opening $dir: $!\n"; while (defined(my $file = readdir DH)) { next unless -f $file; open FILE, "-|", "file", $file or die "Executing file: $!\n"; my $idstring = ; close FILE; chomp $idstring; $idstring =~ s{[/\x00-\x1F]}{_}g; if (length($idstring) - 3 < length($file)) { print STDERR "Invalid idstring: $idstring"; next; } my $dir = substr($idstring, length($file) + 2); mkdir $dir; rename $file, "$dir/$file" or warn "Cannot move $file: $!\n"; } magicrescue-1.1.8/recipes/avi0000644000175000017500000000056411261177403014247 0ustar jbjjbj# Extracts avi files. # Depends on MPlayer's mencoder: http://www.mplayerhq.hu/ 0 string RIFF 8 string AVI\x20 # mencoder will keep reading after the avi ends, so we use tools/safecat to # stop it when it's not outputting useful things anymore. extension avi command safecat -d 30M -u 2M -t 200M "$1"|mencoder -nocache -o "$1" -ovc copy -oac copy - min_output_file 102400 magicrescue-1.1.8/recipes/canon-cr20000644000175000017500000000042111030775345015245 0ustar jbjjbj# Extract (at most 30 MB) Canon CR2 RAW files. These are used by the Powershot # and EOS digital cameras. # This recipe was contributed by Daniel J Blueman 0 string II* 8 string CR extension CR2 command dd bs=1024k count=30 of="$1" 2>/dev/null; dcraw -i "$1" || rm -f "$1" magicrescue-1.1.8/recipes/elf0000644000175000017500000000044311030775345014235 0ustar jbjjbj# Extracts ELF executable files. # Depends on perl from http://perl.com, and readelf and objdump from GNU # binutils: http://sources.redhat.com/binutils/ 0 string \x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00 extension elf command elfextract.pl "$1" min_output_file 1024 magicrescue-1.1.8/recipes/flac0000644000175000017500000000057311030775345014400 0ustar jbjjbj# Extracts flac sound files: http://flac.sourceforge.net/ # Contributed by Richard Zidlicky 0 string fLaC extension flac # Allow infinite overlap because the recompressed file may be much larger than # the original one.. at least in theory allow_overlap -1 # hope that flac will do the right thing command flac -d - | flac -8 - > "$1" magicrescue-1.1.8/recipes/gimp-xcf0000644000175000017500000000172611030775345015206 0ustar jbjjbj# Extracts Gimp's XCF file format # # Requires: The GIMP, perl, and the Gimp perl module. Only tested on GIMP 2.x. # # You will find the "Gimp" module on CPAN or in your Linux/UNIX distribution. # In GIMP 1.2 it's included with the gimp source. # # If your distribution doesn't include it, fire up the CPAN shell as root: # $ perl -MCPAN -e shell # You can probably skip the manual configuration. In the CPAN shell, type: # $ install Gimp # This will install version 2 of the module, to match The GIMP version 2. # To use this recipe you need to start the GIMP Perl Server. Start up The GIMP # and run it from the Xtns->Perl->Server menu (nothing will happen visually # when clicking). If that menu is not present, the plugin/module is probably # not correctly installed. You can also start it from the command line: # $ gimp --batch '(extension-perl-server 0 0 0)' 0 string gimp xcf\x20 extension xcf command dd of="$1" bs=1024k count=40; gimp-resave.pl "$1" || rm -f "$1" magicrescue-1.1.8/recipes/gpl0000644000175000017500000000136311030775345014253 0ustar jbjjbj# Extracts source code with the GPL header in it # # Depends on perl for renaming 0 string This program is free software; you can redistribute it and/or extension gpl # Max file size is 200K. # You might want to lower this value if your files are smaller. A smaller value # here will prevent different files from being mixed into the same output file. # Delete the file if it doesn't seem to be the GPL header statement, or if it # seems to be the GPL itself. command textextract -r 1024 -M 200K "$1"; if grep "General Public License" "$1" >/dev/null; then if grep "signature of Ty Coon" "$1" >/dev/null; then rm -f "$1"; fi; else rm -f "$1"; fi rename script_rename.pl "$1" # Because of the -r option to textextract allow_overlap 1024 # vim: wm=0 magicrescue-1.1.8/recipes/gzip0000644000175000017500000000132111030775345014434 0ustar jbjjbj# Extracts gzip files compressed with the deflate algorithm (i.e. all modern # gzip files) # Depends on gzip: http://www.gzip.org/ # Format defined at http://www.faqs.org/rfcs/rfc1952.html # First byte: magic 0x1f 0 char \x1f # The next 4 bytes: magic 0x8b, compression method 0x08 (deflate), flags (bits # 5, 6 and 7 are reserved), first byte of mtime (little endian?!) 1 int32 8B080000 FFffE000 # Last byte of mtime (assuming before year 2038), "Extra flags" (only the # values 2 and 4 are used by deflate) 7 int32 00000000 80F90000 extension unzipped # Allow infinite overlap because the expanded file may be much larger than the # compressed one. allow_overlap -1 command gzip -d > "$1" rename gzip_rename.pl "$1" magicrescue-1.1.8/recipes/jpeg-exif0000644000175000017500000000046111030775345015345 0ustar jbjjbj# Extracts jpeg files with the Exif magic bytes. These usually originate from # digital camaras or other devices. # Depends on jpegtran from libjpeg: http://freshmeat.net/projects/libjpeg/ # See also jpeg-jfif 6 string Exif 0 int32 ffd80000 ffff0000 extension jpg command jpegtran -copy all -outfile "$1" magicrescue-1.1.8/recipes/jpeg-jfif0000644000175000017500000000045711030775345015335 0ustar jbjjbj# Extracts jpeg files with the JFIF magic bytes. These are usually created by # graphics manipulation programs. # Depends on jpegtran from libjpeg: http://freshmeat.net/projects/libjpeg/ # See also jpeg-exif 6 string JFIF 0 int32 ffd80000 ffff0000 extension jpg command jpegtran -copy all -outfile "$1" magicrescue-1.1.8/recipes/mp3-id3v10000644000175000017500000000240111030775345015106 0ustar jbjjbj# Extracts mp3 files. # Depends on perl and mpg123 from http://www.mpg123.de/ # # If you use this together with the mp3-id3v2 recipe, specify this recipe # AFTER the other. Otherwise files will be extracted twice. # # Currently this recipe is very slow because the header to match against is # way too small. Therefore the command is executed very often, and it could # take a whole day to complete. # The good thing is that it will match every single frame, so even if an mp3 # file is split in 10 fragments it will find every one of them (possibly losing # one frame where each split happens). Putting the pieces together won't be an # easy puzzle, though. 0 char \xff 0 int32 FFfa0000 FFfe0000 extension mp3 # The command below is a bit hairy, here's how it works: # We copy out a 100KB sample. If mpg123 can decode the sample without spitting # out errors we try to let it play those 100KB into "$1.pcm". Then we call the # extractor, which first sees if "$1.pcm" is big enough, and then tries to # extract the file. command dd bs=102400 count=1 of="$1" 2>/dev/null; if mpg123 -t "$1" 2>&1|egrep 'Illegal|too large|not allowed|Giving up' >/dev/null; then rm -f "$1"; else mpg123 -s "$1" > "$1.pcm" 2>/dev/null; mp3extract.pl "$1"; rm -f "$1.pcm"; fi min_output_file 102401 magicrescue-1.1.8/recipes/mp3-id3v20000644000175000017500000000126111030775345015112 0ustar jbjjbj# Extracts mp3 files with the ID3v2 tag. # Depends on perl and mpg123 from http://www.mpg123.de/ # # If you use this together with the mp3-id3v1 recipe, specify this recipe # BEFORE the other. Otherwise files will be extracted twice. # # From http://www.id3.org/id3v2.4.0-structure.txt: # An ID3v2 tag can be detected with the following pattern: # 49 44 33 yy yy xx zz zz zz zz # where yy is the version, xx are flags (only bit 4-7 used) and zz are integers # less than 0x80 # We assume that ID3v2 versions only come as 2.x.y, where x and y is less than 7 0 string ID3 2 int32 00000000 00f8F80f 6 int32 00000000 80808080 extension mp3 command mp3extract.pl "$1" min_output_file 51200 magicrescue-1.1.8/recipes/msoffice0000644000175000017500000000234211030775345015262 0ustar jbjjbj# Extracts Microsoft's OLE container format, used by newer versions of # Microsoft Office (>= Word 6.0) and old versions of StarOffice (<= 5.0). The # format is documented at http://user.cs.tu-berlin.de/~schwartz/pmh/guide.html # # The files it extracts can be Word, Powerpoint, Excel or other interesting # things, but many weird OLE fragments will be pulled out as well. All known # formats are listed near the top of tools/ole_rename.pl. # This recipe will NOT recover Microsoft Access files, WordPerfect files, # OpenOffice.org files, or StarOffice >= 6.0 files. # It will try to guess the file type, but if it fails to guess something you # have identified manually, please send sample files to jbj@knef.dk. # # To recover files from OpenOffice.org or StarOffice >= 6.0, use the "zip" # recipe instead (these files are just zip files with some xml content) # # Depends on perl: http://perl.com 0 string \xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1 extension ole command oleextract.pl > "$1" rename ole_rename.pl "$1" # There must be at least 4 512-byte blocks: the header, a big block depot, the # root entry, and something useful. Files around 4KB are usually embedded OLE # objects, and Office documents start appearing above 10KB. min_output_file 2048 magicrescue-1.1.8/recipes/nikon-raw0000644000175000017500000000044411146076441015374 0ustar jbjjbj# Extract (at most 30 MB) Nikon NEF RAW files. These are used by Nikon dSLR # cameras. # Contributed by Brian E Lavender and refined by Daniel J Blueman. 0 string MM\x00\x2a 6 int32 00080019 ffffffff extension NEF command dd bs=1024k count=30 of="$1" 2>/dev/null; dcraw -i "$1" || rm -f "$1" magicrescue-1.1.8/recipes/perl0000644000175000017500000000133511030775345014432 0ustar jbjjbj# Extracts perl scripts. # # Depends on perl to do renames. 0 string #!/usr/bin/perl extension pl # Max file size (-M) is 600K. The reference is latex2html, which is 580K. No # perl script could be bigger than that monster... # You might want to lower this value if your files are smaller. A smaller value # here will prevent different scripts from being mixed into the same output # file. # Max line length (-l) is 5*80 bytes. You may want to increase this, depending # on your coding style. command textextract -l 400 -M 600K "$1" # If the file seems to be a module, rename it to .pm rename perl -ne 'if (($name) = /^package\s+([A-Z][A-Za-z_0-9:]*);/) { $name =~ s/:+/-/g; print "RENAME $name.pm\n"; exit; }' "$1" # vim: wm=0 magicrescue-1.1.8/recipes/png0000644000175000017500000000023111030775345014246 0ustar jbjjbj# Extracts png images. # Depends on perl: http://perl.com 0 string \x89PNG\x0d\x0a\x1a\x0a extension png command pngextract.pl > "$1" min_output_file 20 magicrescue-1.1.8/recipes/ppm0000644000175000017500000000050211146100661014246 0ustar jbjjbj# Extract (at most 36 MB) 8-bit PPM image file. Assumes dimensions greater # or equal to 1000 by 1000 pixels. May not match PPM files that contain # comments in the header or unusual whitespace. # Contributed by Daniel J Blueman 0 string P6\n 13 string 255\n extension ppm command dd bs=1024k count=36 of="$1" 2>/dev/null magicrescue-1.1.8/recipes/zip0000644000175000017500000000162611250273276014275 0ustar jbjjbj# Extracts PKZIP files, including OpenOffice.org and StarOffice 6+ documents. # Depends on Info-ZIP from http://www.info-zip.org # # Files that seem to be OpenOffice.org or StarOffice documents will be renamed # to *.ooo # magic 0 string PK\x03\x04 # high byte of "version made by" 5 char \x00 # high byte of "version needed to extract" #7 char \x00 extension zip # This command takes a 50MB chunk of the file and tries to repair it. If the # zip files you are looking for are less than 50MB, you can lower the value of # the count= parameter for performance. command dd bs=1024k count=50 of="$1.tmp" 2>/dev/null; zip -qF "$1.tmp" --out "$1" ; rm -f "$1.tmp" # Try to guess if it's a JAR archive or an OpenOffice.org file, and rename # accordingly rename if unzip -l "$1"|grep META-INF/MANIFEST.MF >/dev/null 2>&1; then echo "RENAME jar"; elif unzip -p "$1" content.xml >/dev/null 2>&1; then echo "RENAME ooo"; fi magicrescue-1.1.8/src/array.c0000644000175000017500000000305311030775345014163 0ustar jbjjbj/* * Magic Rescue array routines * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include void array_init(struct array *array, size_t el_len) { array->el_len = el_len; array->elements = 0; array->data = NULL; } void array_destroy(struct array *array) { free(array->data); } void *array_el(struct array *array, size_t index) { return (char *)array->data + index * array->el_len; } size_t array_count(struct array *array) { return array->elements; } void *array_add(struct array *array, void *el) { array->data = realloc(array->data, ++array->elements * array->el_len); if (el) { char *tmp = array->data; memcpy(&tmp[(array->elements-1) * array->el_len], el, array->el_len); } return array_el(array, array->elements-1); } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/array.h0000644000175000017500000000112211030775345014163 0ustar jbjjbj#ifndef _ARRAY_H #include struct array { size_t el_len; size_t elements; void *data; }; void array_init(struct array *array, size_t el_len); void array_destroy(struct array *array); void *array_el(struct array *array, size_t index); size_t array_count(struct array *array); void *array_add(struct array *array, void *el); #define array_foreach(array, el) \ for ((el)=(array)->data; \ (char *)(el) < (char *)(array)->data + \ (array)->elements*(array)->el_len; \ (el) = (void *)((char *)(el) + (array)->el_len)) #define _ARRAY_H #endif /* _ARRAY_H */ magicrescue-1.1.8/src/dupemap.c0000644000175000017500000003016711030775345014506 0ustar jbjjbj/* * dupemap, part of Magic Rescue * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #ifdef HAVE_NDBM #include #include #include #include #include #include #include #include #include #include #include #include "find_dbm.h" #include "recur.h" #include "util.h" enum operation { SCAN=1, REPORT=2, DELETE=4, HASDB=8 }; enum mode { MODE_READDB=1, MODE_WRITEDB=2, MODE_DELETE=4, MODE_REPORT=8, MODE_TEMPDB=16 }; /* * * DATABASE CODE * * An implementation of the database layer should provide a dbhandle typedef * and the csdb_{open,open_tempdb,exists,store,close} functions. No * values are stored in the database, only the existence of keys matters. * */ typedef struct { DBM *db; char *tempdir; } dbhandle; static int csdb_open_tempdb(dbhandle *db) { int i = 0, rv; char *env_tmp = getenv("TMP"); if (!env_tmp || !*env_tmp) env_tmp = "/tmp"; db->tempdir = malloc(PATH_MAX); do { snprintf(db->tempdir, PATH_MAX, "%s/dupemap_%d", env_tmp, i++); rv = mkdir(db->tempdir, 0700); } while (rv != 0 && errno == EEXIST); if (rv != 0) { perror("making temporary directory"); free(db->tempdir); return -1; } else { char file[PATH_MAX]; snprintf(file, PATH_MAX, "%s/tempdb", db->tempdir); db->db = dbm_open(file, O_RDWR | O_CREAT, 0666); if (!db->db) { perror("dbm_open"); free(db->tempdir); return -1; } } return 0; } static int csdb_open(dbhandle *db, const char *file, enum mode mode) { int openmode = mode & MODE_READDB ? O_RDONLY : O_RDWR | O_CREAT; db->tempdir = NULL; db->db = dbm_open(file, openmode, 0666); if (!db->db) { perror("dbm_open"); return -1; } return 0; } static int csdb_exists(dbhandle *db, void *key, int len) { datum d, value; d.dptr = key; d.dsize = len; value = dbm_fetch(db->db, d); return value.dptr != NULL; } static int csdb_store(dbhandle *db, void *key, int len) { datum d, value; d.dptr = key; d.dsize = len; value.dptr = ""; value.dsize = DBM_KEY_LEN; dbm_store(db->db, d, value, DBM_INSERT); return 0; } static int csdb_close(dbhandle *db) { dbm_close(db->db); if (db->tempdir) { rm_rf(db->tempdir); free(db->tempdir); } return 0; } /* * * CHECKSUM CODE * * An implementation should provide the checksum_t typedef and the * checksum_calc function. * */ /* This CRC32 implementation is derived from Weeder * */ static const unsigned long crctab[256] = { 0x0, 0x04C11DB7,0x09823B6E,0x0D4326D9,0x130476DC,0x17C56B6B,0x1A864DB2,0x1E475005, 0x2608EDB8,0x22C9F00F,0x2F8AD6D6,0x2B4BCB61,0x350C9B64,0x31CD86D3,0x3C8EA00A, 0x384FBDBD,0x4C11DB70,0x48D0C6C7,0x4593E01E,0x4152FDA9,0x5F15ADAC,0x5BD4B01B, 0x569796C2,0x52568B75,0x6A1936C8,0x6ED82B7F,0x639B0DA6,0x675A1011,0x791D4014, 0x7DDC5DA3,0x709F7B7A,0x745E66CD,0x9823B6E0,0x9CE2AB57,0x91A18D8E,0x95609039, 0x8B27C03C,0x8FE6DD8B,0x82A5FB52,0x8664E6E5,0xBE2B5B58,0xBAEA46EF,0xB7A96036, 0xB3687D81,0xAD2F2D84,0xA9EE3033,0xA4AD16EA,0xA06C0B5D,0xD4326D90,0xD0F37027, 0xDDB056FE,0xD9714B49,0xC7361B4C,0xC3F706FB,0xCEB42022,0xCA753D95,0xF23A8028, 0xF6FB9D9F,0xFBB8BB46,0xFF79A6F1,0xE13EF6F4,0xE5FFEB43,0xE8BCCD9A,0xEC7DD02D, 0x34867077,0x30476DC0,0x3D044B19,0x39C556AE,0x278206AB,0x23431B1C,0x2E003DC5, 0x2AC12072,0x128E9DCF,0x164F8078,0x1B0CA6A1,0x1FCDBB16,0x018AEB13,0x054BF6A4, 0x0808D07D,0x0CC9CDCA,0x7897AB07,0x7C56B6B0,0x71159069,0x75D48DDE,0x6B93DDDB, 0x6F52C06C,0x6211E6B5,0x66D0FB02,0x5E9F46BF,0x5A5E5B08,0x571D7DD1,0x53DC6066, 0x4D9B3063,0x495A2DD4,0x44190B0D,0x40D816BA,0xACA5C697,0xA864DB20,0xA527FDF9, 0xA1E6E04E,0xBFA1B04B,0xBB60ADFC,0xB6238B25,0xB2E29692,0x8AAD2B2F,0x8E6C3698, 0x832F1041,0x87EE0DF6,0x99A95DF3,0x9D684044,0x902B669D,0x94EA7B2A,0xE0B41DE7, 0xE4750050,0xE9362689,0xEDF73B3E,0xF3B06B3B,0xF771768C,0xFA325055,0xFEF34DE2, 0xC6BCF05F,0xC27DEDE8,0xCF3ECB31,0xCBFFD686,0xD5B88683,0xD1799B34,0xDC3ABDED, 0xD8FBA05A,0x690CE0EE,0x6DCDFD59,0x608EDB80,0x644FC637,0x7A089632,0x7EC98B85, 0x738AAD5C,0x774BB0EB,0x4F040D56,0x4BC510E1,0x46863638,0x42472B8F,0x5C007B8A, 0x58C1663D,0x558240E4,0x51435D53,0x251D3B9E,0x21DC2629,0x2C9F00F0,0x285E1D47, 0x36194D42,0x32D850F5,0x3F9B762C,0x3B5A6B9B,0x0315D626,0x07D4CB91,0x0A97ED48, 0x0E56F0FF,0x1011A0FA,0x14D0BD4D,0x19939B94,0x1D528623,0xF12F560E,0xF5EE4BB9, 0xF8AD6D60,0xFC6C70D7,0xE22B20D2,0xE6EA3D65,0xEBA91BBC,0xEF68060B,0xD727BBB6, 0xD3E6A601,0xDEA580D8,0xDA649D6F,0xC423CD6A,0xC0E2D0DD,0xCDA1F604,0xC960EBB3, 0xBD3E8D7E,0xB9FF90C9,0xB4BCB610,0xB07DABA7,0xAE3AFBA2,0xAAFBE615,0xA7B8C0CC, 0xA379DD7B,0x9B3660C6,0x9FF77D71,0x92B45BA8,0x9675461F,0x8832161A,0x8CF30BAD, 0x81B02D74,0x857130C3,0x5D8A9099,0x594B8D2E,0x5408ABF7,0x50C9B640,0x4E8EE645, 0x4A4FFBF2,0x470CDD2B,0x43CDC09C,0x7B827D21,0x7F436096,0x7200464F,0x76C15BF8, 0x68860BFD,0x6C47164A,0x61043093,0x65C52D24,0x119B4BE9,0x155A565E,0x18197087, 0x1CD86D30,0x029F3D35,0x065E2082,0x0B1D065B,0x0FDC1BEC,0x3793A651,0x3352BBE6, 0x3E119D3F,0x3AD08088,0x2497D08D,0x2056CD3A,0x2D15EBE3,0x29D4F654,0xC5A92679, 0xC1683BCE,0xCC2B1D17,0xC8EA00A0,0xD6AD50A5,0xD26C4D12,0xDF2F6BCB,0xDBEE767C, 0xE3A1CBC1,0xE760D676,0xEA23F0AF,0xEEE2ED18,0xF0A5BD1D,0xF464A0AA,0xF9278673, 0xFDE69BC4,0x89B8FD09,0x8D79E0BE,0x803AC667,0x84FBDBD0,0x9ABC8BD5,0x9E7D9662, 0x933EB0BB,0x97FFAD0C,0xAFB010B1,0xAB710D06,0xA6322BDF,0xA2F33668,0xBCB4666D, 0xB8757BDA,0xB5365D03,0xB1F740B4 }; typedef struct { long crc; off_t filesize; } checksum_t; #define BUFLEN ( 1 << 16 ) static int checksum_calc(checksum_t *checksum, const char *file) { static char buf[BUFLEN]; ssize_t bytes_read; off_t _length = 0; long _crc = 0; FILE *fp; memset(checksum, 0, sizeof checksum); fp = fopen (file, "rb"); if (fp == NULL) { return -1; } while ((bytes_read = fread(buf, 1, BUFLEN, fp)) > 0) { char *cp = buf; _length += bytes_read; while (bytes_read--) _crc = (_crc << 8) ^ crctab[((_crc >> 24) ^ *(cp++)) & 0xFF]; } if (ferror(fp)) { fclose(fp); return -1; } fclose(fp); checksum->filesize = _length; for (; _length; _length >>=8 ) _crc = (_crc << 8) ^ crctab[((_crc >> 24) ^ _length) & 0xFF]; checksum->crc = ~_crc & 0xFFFFFFFF; return 0; } /* * * MAIN PROGRAM * */ static dbhandle *global_dbhandle = NULL; static off_t min_size = 1, max_size = 0; static enum mode mode; static void signal_beforedeath(int signo) { fprintf(stderr, "\ndupemap: killed by signal %d\n", signo); csdb_close(global_dbhandle); exit(0); } static enum mode parse_mode(char *str, int hasdb) { char *tok; enum operation op = 0; unsigned int i; struct { char *opname; int opval; } opnames[] = { { "scan", SCAN }, { "report", REPORT }, { "delete", DELETE }, }; struct { enum operation op; enum mode mode; } op2mode[] = { { HASDB | SCAN, MODE_WRITEDB }, { HASDB | SCAN | REPORT, MODE_WRITEDB | MODE_REPORT }, { HASDB | REPORT, MODE_READDB | MODE_REPORT }, { HASDB | DELETE, MODE_READDB | MODE_DELETE }, { HASDB | DELETE | REPORT, MODE_READDB | MODE_DELETE | MODE_REPORT }, { REPORT , MODE_TEMPDB | MODE_REPORT }, { DELETE , MODE_TEMPDB | MODE_DELETE }, { DELETE | REPORT , MODE_TEMPDB | MODE_DELETE | MODE_REPORT }, { SCAN | REPORT , MODE_TEMPDB | MODE_REPORT }, { SCAN | DELETE , MODE_TEMPDB | MODE_DELETE }, { SCAN | DELETE | REPORT, MODE_TEMPDB | MODE_DELETE | MODE_REPORT }, }; for (tok = strtok(str, ", "); tok; tok = strtok(NULL, ", ")) { for (i = 0; i < sizeof(opnames)/sizeof(*opnames); i++) { if (strcmp(tok, opnames[i].opname) == 0) op |= opnames[i].opval; } } if (hasdb) op |= HASDB; for (i = 0; i < sizeof(op2mode)/sizeof(*op2mode); i++) { if (op2mode[i].op == op) return op2mode[i].mode; } return 0; } static void scan_file(const char *name, const struct stat *st, dbhandle *db) { checksum_t checksum; if (!(st->st_mode & S_IFREG && st->st_size >= min_size && (max_size > 0 ? st->st_size <= max_size : 1) && checksum_calc(&checksum, name) == 0 )) return; if (mode & MODE_TEMPDB) { if (csdb_exists(db, &checksum, sizeof checksum)) { if (mode & MODE_REPORT) printf("%s\n", name); if (mode & MODE_DELETE) unlink(name); } else { csdb_store(db, &checksum, sizeof checksum); } } else if (mode & MODE_WRITEDB) { if (mode & MODE_REPORT) printf("%s\n", name); csdb_store(db, &checksum, sizeof checksum); } else if (mode & MODE_READDB && csdb_exists(db, &checksum, sizeof checksum)) { if (mode & MODE_REPORT) printf("%s\n", name); if (mode & MODE_DELETE) unlink(name); } } static void usage(void) { printf( "Usage: dupemap [OPTIONS] OPERATION PATH...\n" "Where OPERATION is one of the operations listed in the manpage.\n" "\n" "Options:\n" " -d DATABASE Read/write from a database on disk\n" " -I FILE Read input file names from this file (\"-\" for stdin)\n" " -m MINSIZE Exclude files below this size\n" " -M MAXSIZE Exclude files above this size\n" ); } int main(int argc, char **argv) { struct recur *recur; int c; char name[PATH_MAX], *dbname = NULL; char *file_names_from = NULL; struct stat st; dbhandle db; while ((c = getopt(argc, argv, "d:m:I:M:")) >= 0) { switch (c) { case 'd': dbname = optarg; break; case 'I': file_names_from = optarg; break; case 'm': #ifdef HAVE_ATOLL min_size = (off_t)atoll(optarg); #else min_size = (off_t)atol(optarg); #endif if (min_size <= 0) min_size = 1; break; case 'M': #ifdef HAVE_ATOLL max_size = (off_t)atoll(optarg); #else max_size = (off_t)atol(optarg); #endif break; default: fprintf(stderr, "Error parsing options.\n"); usage(); return 1; } } argv += optind; argc -= optind; if (argc < 1) { usage(); return 1; } mode = parse_mode(argv[0], dbname != NULL); if (!mode) { fprintf(stderr, "Invalid operation\n"); return 1; } if (dbname) { if (csdb_open(&db, dbname, mode) != 0) { fprintf(stderr, "Cannot open database\n"); return 1; } } else { if (csdb_open_tempdb(&db) != 0) { fprintf(stderr, "Cannot open temporary database\n"); return 1; } } global_dbhandle = &db; signal(SIGINT, signal_beforedeath); signal(SIGTERM, signal_beforedeath); signal(SIGSEGV, signal_beforedeath); signal(SIGPIPE, signal_beforedeath); if (argc > 1) { recur = recur_open(argv + 1); if (!recur) { csdb_close(&db); return 0; } while (recur_next(recur, name, &st) == 0) { scan_file(name, &st, &db); } recur_close(recur); } if (file_names_from) { FILE *fh = strcmp(file_names_from, "-") == 0 ? stdin : fopen(file_names_from, "r"); if (!fh) { fprintf(stderr, "Opening %s: %s\n", file_names_from, strerror(errno)); csdb_close(&db); return 1; } while (fgets(name, sizeof name, fh)) { name[strlen(name)-1] = '\0'; /* kill newline */ if (stat(name, &st) == 0) scan_file(name, &st, &db); } fclose(fh); } csdb_close(&db); global_dbhandle = NULL; return 0; } #else /* not HAVE_DBM */ # include int main() { printf( "dupemap was not compiled because no ndbm.h was found on your system. Please\n" "install the development packages for Berkeley DB or GDBM and recompile.\n" ); return 0; } #endif /* not HAVE_DBM */ magicrescue-1.1.8/src/extract.c0000644000175000017500000001040111261177311014505 0ustar jbjjbj/* * Magic Rescue file extraction and post-processing * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "util.h" #include "array.h" #include "recipe.h" #include "magicrescue.h" void compose_name(char *name, off_t offset, const char *extension) { struct stat st; long i = 0; do { if (name_mode == MODE_DEVICE) { snprintf(name, PATH_MAX, "%s/%012llX-%ld.%s", output_dir, (long long)offset, i++, extension); } else /*if (name_mode == MODE_FILES)*/ { snprintf(name, PATH_MAX, "%s/%s-%ld.%s", output_dir, progress.device_basename, i++, extension); } } while (lstat(name, &st) == 0); } int run_shell(int fd, off_t offset, const char *command, const char *argument, int *stdout_pipe) { pid_t pid; int status = -1; int pipes[2]; if (stdout_pipe && pipe(pipes) != 0) { perror("pipe()"); return -1; } if (lseek(fd, offset, SEEK_SET) == (off_t)-1) { perror("lseek()"); return -1; } pid = fork(); if (pid == 0) { dup2(fd, 0); if (stdout_pipe) { dup2(pipes[1], 1); close(pipes[0]); close(pipes[1]); } else if (machine_output) dup2(2, 1); /* send program's stdout to stderr */ execl("/bin/sh", "/bin/sh", "-c", command, "sh", argument, NULL); perror("Executing /bin/sh"); exit(1); } else if (stdout_pipe) { close(pipes[1]); *stdout_pipe = pipes[0]; return 0; } else if (pid > 0) { wait(&status); } return status; } void rename_output(int fd, off_t offset, const char *command, char *origname) { int outfd; char mvbuf[2*PATH_MAX]; /* it has to be semi-large */ ssize_t got, has = 0; char *rename_pos = mvbuf; if (run_shell(fd, offset, command, origname, &outfd) != 0) return; while ((got = read(outfd, mvbuf + has, sizeof(mvbuf)-has - 1)) > 0) { has += got; } close(outfd); wait(NULL); mvbuf[has] = '\0'; if (has > 7 && (strncmp(rename_pos, "RENAME ", 7) == 0 || (fprintf(stderr, "Warning: garbage on rename stdout\n"), 0) || ((rename_pos = strstr(mvbuf, "\nRENAME ")) && rename_pos++)|| (rename_pos = strstr(mvbuf, "RENAME "))) ) { char *nlpos; rename_pos += 7; if ((nlpos = strchr(rename_pos, '\n'))) *nlpos = '\0'; if (strlen(rename_pos) < 128) { char newname[PATH_MAX]; compose_name(newname, offset, rename_pos); rename(origname, newname); strcpy(origname, newname); } } } /** Calls an external program to extract a file from fd */ off_t extract(int fd, struct recipe *r, off_t offset) { char outfile[PATH_MAX]; struct stat st; compose_name(outfile, offset, r->extension); if (run_shell(fd, offset, r->command, outfile, NULL) == -1) return -1; if (stat(outfile, &st) == 0) { if (st.st_size < r->min_output_file) { fprintf(stderr, "Output too small, removing\n"); unlink(outfile); } else { if (r->rename) { rename_output(fd, offset, r->rename, outfile); } if (r->postextract) { run_shell(fd, offset, r->postextract, outfile, NULL); } if ((machine_output & OUT_IO) == OUT_IO) printf("o %s\n", outfile); else if (machine_output & OUT_O) printf("%s\n", outfile); else fprintf(stderr, "%s: %lld bytes\n", outfile, (long long int)st.st_size); return st.st_size; } } else { fprintf(stderr, "No output file\n"); } return 0; } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/find_dbm.h0000644000175000017500000000067411030775345014622 0ustar jbjjbj#define DB_DBM_HSEARCH 1 #ifdef DBM_H_UNIX # include # define DBM_KEY_LEN 1 #endif #ifdef DBM_H_DB1 # include # define DBM_KEY_LEN 0 #endif #ifdef DBM_H_GDBM # include # define DBM_KEY_LEN 0 #endif #ifdef DBM_H_DEB # include # define DBM_KEY_LEN 0 #endif #ifdef DBM_H_DB_H # include # define DBM_KEY_LEN 1 #endif #ifdef DBM_H_DB # include # define DBM_KEY_LEN 1 #endif magicrescue-1.1.8/src/largefile.h0000644000175000017500000000012311030775345014777 0ustar jbjjbj#define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE magicrescue-1.1.8/src/magicrescue.c0000644000175000017500000004206611261177342015342 0ustar jbjjbj/* * Magic Rescue main program * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #ifdef HAVE_GETRLIMIT # include # include #endif /* HAVE_GETRLIMIT */ #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "array.h" #include "recipe.h" #include "scanner.h" #include "magicrescue.h" struct range { off_t begin; off_t end; }; /* An array of struct scanner. Each scanner can contain one or more recipes. */ static struct array scanners; /* An array of struct range. Used by the anti-overlap code to list ranges that * have already been extracted and should not be extracted again. * A value of begin < 0 means that a slot is unused. */ static struct array skip_ranges; char *output_dir = NULL; enum OUTPUT_MODE machine_output = OUT_HUMAN; enum NAME_MODE name_mode = MODE_DEVICE; /* Structure containing the current input file name and position */ struct progress progress; /* This value is the max length of a chunk of bytes to be looked at when * scanning. It is constant after all recipes have been parsed. */ static ssize_t overlap = 0; /* General-purpose buffer. Mostly used to hold the raw data when scanning. */ static char *buf; static const size_t bufsize = 102400; /* Signal handler for fatal signals */ static void signal_beforedeath(int signo) { fprintf(stderr, "\nmagicrescue: killed by signal %d at offset 0x%llX in file %s\n", signo, (long long)progress.position, progress.device); if (progress.position) fprintf(stderr, "Use the -O option to resume from this offset later\n"); exit(0); } /* Add a scanner to the global scanners list, or reuses an existing entry if * an identical one exists. * Returns a pointer to the added scanner. */ static struct scanner *scanner_add_or_reuse(struct scanner *scanner) { struct scanner *el; array_foreach(&scanners, el) { if (scanner_compare(scanner, el) == 0) { scanner_destroy(scanner); return el; } } return array_add(&scanners, scanner); } /* Free the global scanners list. */ static void scanners_free(void) { struct scanner *scanner; array_foreach(&scanners, scanner) { scanner_destroy(scanner); } array_destroy(&scanners); } /* Scans scanbuf with all scanners in the global list, testing all recipes on * them. * Returns 1 if an external program has been called, which means the file * position has changed. Returns -1 on fatal error. */ static int scan_buf(const char *scanbuf, ssize_t scanbuf_len, int fd, off_t scanbuf_filepos) { int filepos_changed = 0; struct scanner *scanner; struct recipe *r; struct range *range; /* clean up ranges we have gone past, and see if we can skip some of the * buffer */ array_foreach(&skip_ranges, range) { if (range->begin < 0) continue; if (range->end < scanbuf_filepos) { range->begin = -1; } else if (range->end > scanbuf_filepos + scanbuf_len) { return 0; } else if (range->begin <= scanbuf_filepos && range->end > scanbuf_filepos) { scanbuf += range->end - scanbuf_filepos; scanbuf_len -= range->end - scanbuf_filepos; scanbuf_filepos = range->end; } } array_foreach(&scanners, scanner) { const char *p = scanbuf + scanner->offset; while (p - scanbuf < scanbuf_len && (p = scanner->func(p, scanbuf_len - (p-scanbuf) + scanner->offset, &scanner->param, scanbuf_filepos + (p-scanbuf)))) { off_t filepos; p -= scanner->offset; filepos = scanbuf_filepos + (p-scanbuf); array_foreach(&skip_ranges, range) { if (range->begin >= 0 && filepos > range->begin && filepos < range->end) { goto keep_scanning; } } array_foreach(&scanner->recipes, r) { struct operation *op; int ops_matched = 1; array_foreach(&r->ops, op) { if (!op->func(p + op->offset, &op->param)) { ops_matched = 0; break; } } if (ops_matched) { off_t output_size; fprintf(stderr, "Found %s at 0x%llX\n", r->name, (long long)filepos); output_size = extract(fd, r, filepos); filepos_changed = 1; if (output_size < 0) return -1; if (r->allow_overlap >= 0 && output_size > r->allow_overlap) { struct range *tmp; range = NULL; /* find an available range, or add one */ array_foreach(&skip_ranges, tmp) { if (tmp->begin < 0) { range = tmp; } } if (!range) range = array_add(&skip_ranges, NULL); range->begin = filepos; range->end = filepos + output_size - r->allow_overlap; break; /* try no more recipes */ } } } keep_scanning: p += scanner->offset + 1; } } return filepos_changed; } /* Opens and scans an entire device for matches with the selected scanners. */ static void scan_disk(const char *device, const char *offset) { ssize_t got; int fd = 0; int result, firsttime = 1; char *readbuf = buf, *scanbuf = buf; size_t readsize = bufsize; off_t offset_before_read; if (strcmp(device, "-") != 0 && (fd = open(device, O_RDONLY)) == -1) { fprintf(stderr, "opening %s: %s\n", device, strerror(errno)); return; } offset_before_read = rich_seek(fd, offset); if (offset_before_read == (off_t)-1) { fprintf(stderr, "lseek failed on %s: %s\n", device, strerror(errno)); close(fd); return; } snprintf(progress.device, sizeof progress.device, "%s", device); { char *tmp = strrchr(device, '/'); snprintf(progress.device_basename, sizeof progress.device_basename, "%s", tmp ? tmp+1 : device); } if ((machine_output & OUT_IO) == OUT_IO) printf("i %s\n", device); else if (machine_output & OUT_I) printf("%s\n", device); /* In the first iteration, the scan buffer and read buffer overlap like * this: * SSSSSSSSSSSSSSSSS * RRRRRRRRRRRRRRRRRRRRR * * For the rest of the iterations they overlap like this: * SSSSSSSSSSSSSSSSS * RRRRRRRRRRRRRRRRR * * After each iteration the unscanned part of readbuf will be copied to the * start of scanbuf, and new data will be read into readbuf */ while ((got = read(fd, readbuf, readsize)) > overlap) { progress.position = offset_before_read; result = scan_buf(scanbuf, got + (readbuf - scanbuf) - overlap, fd, offset_before_read - (readbuf - scanbuf)); if (result == -1) { close(fd); return; } else if (result) { if (lseek(fd, offset_before_read + got, SEEK_SET) == (off_t)-1) { perror("lseek()"); close(fd); return; } } memcpy(scanbuf, readbuf + got - overlap, overlap); if (firsttime) { firsttime = 0; readbuf += overlap; readsize -= overlap; } offset_before_read += got; } if (got == -1) { fprintf(stderr, "Read error on %s at %lld bytes: %s\n", device, (long long int)offset_before_read, strerror(errno)); if (errno == EIO) { fprintf(stderr, "Note that on some block devices this just " "means end of file.\n"); } } else { fprintf(stderr, "Scanning %s finished at %lldMB\n", device, (long long int)(offset_before_read >> 20)); } close(fd); /* Reset all ranges after processing a file */ { struct range *range; array_foreach(&skip_ranges, range) { range->begin = -1; } } } /* Parse a recipe file, adding it to the global list of scanners. If * global_scanner is not NULL, it points to a scanner that will be forced into * this recipe. * Returns 0 on success, -1 on fatal error. */ static int parse_recipe(const char *recipefile, struct scanner *global_scanner) { struct scanner *scanner = global_scanner; struct recipe r; FILE *fh = NULL; char **sp, *search_path[] = { "recipes", NULL, NULL }; #ifdef RECIPE_PATH search_path[1] = RECIPE_PATH; #endif fh = fopen(recipefile, "r"); for (sp = search_path; !fh && *sp; sp++) { char path[PATH_MAX]; snprintf(path, sizeof path, "%s/%s", *sp, recipefile); fh = fopen(path, "r"); } if (!fh) { fprintf(stderr, "Opening %s: %s\n", recipefile, strerror(errno)); return -1; } recipe_init(&r); { const char *basename = strrchr(recipefile, '/'); r.name = strdup(basename ? basename + 1 : recipefile); } while (fgets(buf, bufsize, fh)) { char opname[64]; int magic_offset, param_offset; buf[strlen(buf) - 1] = '\0'; /* kill trailing newline */ if (sscanf(buf, "%d %63s %n", &magic_offset, opname, ¶m_offset) >= 2) { ssize_t len = 0; char *param = buf + param_offset; if (!scanner) { /* Try to make it the scanner function */ if (strcmp(opname, "string") == 0 || strcmp(opname, "char") == 0) { struct string string; string_init(&string, buf + param_offset); scanner = scanner_new(); scanner->offset = magic_offset; if (string.l == 1) { scanner->func = scanner_char; scanner->param.c = string.s[0]; string_destroy(&string); len = 1; } else if (string.l > 1) { scanner->func = scanner_string; scanner->param.scanstring.string = string; scanner_string_init(&scanner->param); len = string.l; } } if (scanner) { scanner->extra_len = scanner->offset + len - 1; } } if (len <= 0) { /* If it did not become a scanner function it will become an * operation */ struct operation *op = array_add(&r.ops, NULL); op->offset = magic_offset; if (strcmp(opname, "string") == 0 || strcmp(opname, "char") == 0) { op->func = op_string; string_init(&op->param.string, param); len = op->param.string.l; } else if (strcmp(opname, "int32") == 0) { char buf_val[16], buf_mask[16]; op->func = op_int32; if (sscanf(param, "%15s %15s", buf_val, buf_mask) != 2) { fprintf(stderr, "%s: invalid parameter for %s: %s\n", recipefile, opname, param); fclose(fh); return -1; } op->param.int32.val = hextoll(buf_val); op->param.int32.mask = hextoll(buf_mask); op->param.int32.val &= op->param.int32.mask; len = 4; } /* To add more operations: - Add another type to union param if you have to. - Define a test function like op_string that returns 1 on success and 0 on faliure. - Add another "else if" clause here. It must assign to op->func and to len. - The value assigned to len should be the maximum number of bytes your test function might read. Never read more bytes than this. - Add a case to op_destroy of cleanup is needed The test functions you add should be general and reusable, such as string, int, md5, regexp. To make them specific they should take a parameter. */ } if (len <= 0) { fprintf(stderr, "Invalid operation '%s'\n", opname); fclose(fh); return -1; } len += magic_offset; if (overlap < len) overlap = len; } else if (buf[0] != '#' && buf[0] != '\0') { if (sscanf(buf, "extension %63s", r.extension) == 1) { /* do nothing */ } else if (strncmp(buf, "command ", 8) == 0) { r.command = strdup(buf + 8); } else if (strncmp(buf, "postextract ", 12) == 0) { r.postextract = strdup(buf + 12); } else if (strncmp(buf, "rename ", 7) == 0) { r.rename = strdup(buf + 7); } else if (sscanf(buf, "min_output_file %ld", &r.min_output_file) == 1) { /* do nothing */ } else if (sscanf(buf, "allow_overlap %d", &r.allow_overlap) == 1) { /* do nothing */ } else { fprintf(stderr, "Invalid line in %s: %s\n", recipefile, buf); fclose(fh); return -1; } } } if (ferror(fh)) { fclose(fh); fprintf(stderr, "fgets(): %s: %s\n", recipefile, strerror(errno)); return -1; } fclose(fh); if (!scanner || !r.command) { fprintf(stderr, "Invalid recipe file %s.\n" "It must contain one scanner directive, one command directive and at least\n" "one match directive\n", recipefile); return -1; } scanner = scanner_add_or_reuse(scanner); array_add(&scanner->recipes, &r); /* Round overlap to machine word boundary, for faster memcpy */ overlap += sizeof(long) - 1; overlap &= ~(sizeof(long) - 1); return 0; } static void usage(void) { printf( "Usage: magicrescue [-I FILE] [-M MODE] [-O [+-=][0x]OFFSET] [-b BLOCKSIZE]\n" "\t-d OUTPUT_DIR -r RECIPE1 [-r RECIPE2 [...]] DEVICE1 [DEVICE2 [...]]\n" "\n" " -b Only consider files starting at a multiple of BLOCKSIZE.\n" " -d Mandatory. Output directory for found files.\n" " -r Mandatory. Recipe name, file or directory.\n" " -I Read input file names from this file (\"-\" for stdin)\n" " -M Produce machine-readable output to stdout.\n" " -O Resume from specified offset (hex or decimal) in the first device.\n" "\n"); } int main(int argc, char **argv) { int c; struct scanner *global_scanner = NULL; char *start_offset = NULL; char *file_names_from = NULL; array_init(&scanners, sizeof(struct scanner)); array_init(&skip_ranges, sizeof(struct range)); /* Some recipes depend on parsing error messages from various programs */ putenv("LC_ALL=C"); #ifdef HAVE_GETRLIMIT /* Some helper programs will segfault or exhaust memory on malformed * input. This should prevent core files and swap storms */ { struct rlimit rl; rl.rlim_cur = rl.rlim_max = 0; setrlimit(RLIMIT_CORE, &rl); if (getrlimit(RLIMIT_AS, &rl) == 0 && ( rl.rlim_max == RLIM_INFINITY || rl.rlim_max > MAX_MEMORY)) { rl.rlim_cur = rl.rlim_max = MAX_MEMORY; setrlimit(RLIMIT_AS, &rl); } } #endif /* HAVE_GETRLIMIT */ progress.position = 0; progress.device[0] = progress.device_basename[0] = '\0'; buf = malloc(bufsize); sprintf(buf, "PATH=%s%s%s:%s", "tools", #ifdef COMMAND_PATH ":", COMMAND_PATH, #else "", "", #endif getenv("PATH")); putenv(strdup(buf)); while ((c = getopt(argc, argv, "b:d:r:I:M:O:")) >= 0) { switch (c) { case 'b': { long boundary = atol(optarg); if (global_scanner) scanner_destroy(global_scanner); if (boundary <= 1) { global_scanner = NULL; } else { if (boundary & (boundary - 1)) { fprintf(stderr, "Error: block size must be a multiple of two\n"); return 1; } global_scanner = scanner_new(); global_scanner->func = scanner_block; global_scanner->param.block = boundary; } } break; case 'd': { struct stat dirstat; if (stat(optarg, &dirstat) == 0 && dirstat.st_mode & S_IFDIR) { output_dir = optarg; } else { fprintf(stderr, "Invalid directory %s\n", optarg); return 1; } } break; case 'r': { struct stat st; DIR *dh; if (stat(optarg, &st) == 0 && S_ISDIR(st.st_mode) && (dh = opendir(optarg))) { struct dirent *dirent; char fullname[PATH_MAX]; while ((dirent = readdir(dh))) { if (dirent->d_name[0] != '.') { snprintf(fullname, PATH_MAX, "%s/%s", optarg, dirent->d_name); if (parse_recipe(fullname, global_scanner) != 0) { return 1; } } } closedir(dh); } else if (parse_recipe(optarg, global_scanner) != 0) { return 1; } } break; case 'I': file_names_from = optarg; break; case 'M': if (strchr(optarg, 'i')) machine_output |= OUT_I; if (strchr(optarg, 'o')) machine_output |= OUT_O; setvbuf(stdout, NULL, _IOLBF, 0); break; case 'O': start_offset = optarg; break; default: fprintf(stderr, "Error parsing options.\n"); usage(); return 1; } } if (array_count(&scanners) == 0 || !output_dir) { usage(); return 1; } name_mode = (argc-optind>5 || file_names_from) ? MODE_FILES : MODE_DEVICE; signal(SIGINT, signal_beforedeath); signal(SIGTERM, signal_beforedeath); signal(SIGSEGV, signal_beforedeath); signal(SIGPIPE, signal_beforedeath); while (optind < argc) { scan_disk(argv[optind++], start_offset); start_offset = NULL; } if (file_names_from) { char inputfile[PATH_MAX]; FILE *fh = strcmp(file_names_from, "-") == 0 ? stdin : fopen(file_names_from, "r"); if (!fh) { fprintf(stderr, "Opening %s: %s\n", file_names_from, strerror(errno)); return 1; } while (fgets(inputfile, sizeof inputfile, fh)) { inputfile[strlen(inputfile)-1] = '\0'; /* kill newline */ scan_disk(inputfile, NULL); } fclose(fh); } scanners_free(); array_destroy(&skip_ranges); free(buf); return 0; } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/magicrescue.h0000644000175000017500000000140311030775345015336 0ustar jbjjbj#ifndef _MAGICRESCUE_H #include "util.h" enum OUTPUT_MODE { OUT_HUMAN = 0, OUT_I = 1, OUT_O = 2, OUT_IO = 3 }; enum NAME_MODE { MODE_DEVICE, MODE_FILES }; struct progress { off_t position; char device[PATH_MAX]; char device_basename[PATH_MAX]; }; extern char *output_dir; extern enum OUTPUT_MODE machine_output; extern enum NAME_MODE name_mode; extern struct progress progress; /* * Extraction */ void compose_name(char *name, off_t offset, const char *extension); int run_shell(int fd, off_t offset, const char *command, const char *argument, int *stdout_pipe); void rename_output(int fd, off_t offset, const char *command, char *origname); off_t extract(int fd, struct recipe *r, off_t offset); # define _MAGICRESCUE_H #endif /* _MAGICRESCUE_H */ magicrescue-1.1.8/src/recipe.c0000644000175000017500000000400011030775345014305 0ustar jbjjbj/* * Magic Rescue recipe code * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include "util.h" #include "array.h" #include "recipe.h" int op_string(const char *s, union param *p) { return memcmp(s, p->string.s, p->string.l) == 0; } int op_int32(const char *s_signed, union param *p) { const unsigned char *s = (unsigned char *)s_signed; return ((s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3]) & p->int32.mask) == p->int32.val; } void op_destroy(struct operation *op) { /* Primitive multi-dispatch. * Add a case here for every operation type that requires destruction */ if (op->func == op_string) { string_destroy(&op->param.string); } } void recipe_init(struct recipe *r) { array_init(&r->ops, sizeof(struct operation)); r->extension[0] = '\0'; r->command = NULL; r->postextract = NULL; r->rename = NULL; r->min_output_file = 100; r->name = NULL; r->allow_overlap = 0; } void recipe_destroy(struct recipe *r) { struct operation *op; array_foreach(&r->ops, op) { op_destroy(op); } array_destroy(&r->ops); free(r->command); free(r->postextract); free(r->rename); free(r->name); } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/recipe.h0000644000175000017500000000133411030775345014321 0ustar jbjjbj#ifndef _RECIPE_H #include "array.h" union param { struct string string; struct { unsigned long val; unsigned long mask; } int32; }; typedef int (*op_function)(const char *, union param *); struct operation { op_function func; union param param; int offset; }; struct recipe { struct array ops; char extension[64]; char *command; char *postextract; char *rename; long min_output_file; char *name; int allow_overlap; }; int op_string(const char *s, union param *p); int op_int32(const char *s, union param *p); void op_destroy(struct operation *op); void recipe_init(struct recipe *r); void recipe_destroy(struct recipe *r); #define _RECIPE_H #endif /* _RECIPE_H */ magicrescue-1.1.8/src/recur.c0000644000175000017500000000765611030775345014202 0ustar jbjjbj/* * Magic Rescue directory recursion functions * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "recur.h" #include "util.h" void rm_rf(const char *dir) { struct dirent *dirent; struct stat st; DIR *dh = opendir(dir); char fullname[PATH_MAX]; if (!dh) return; while ((dirent = readdir(dh))) { if ( strcmp(dirent->d_name, "." ) == 0 || strcmp(dirent->d_name, "..") == 0 || snprintf(fullname, PATH_MAX, "%s/%s", dir, dirent->d_name) >= PATH_MAX || lstat(fullname, &st) != 0) { continue; } if (S_ISDIR(st.st_mode)) rm_rf(fullname); else unlink(fullname); } closedir(dh); rmdir(dir); } static void parentdir(char *s) { char *p; for (p = &s[strlen(s) - 1]; p >= s; p--) { if (*p == '/') { *p = '\0'; return; } } } struct dirstack *dirstack_open(const char *path) { struct dirstack *stack; DIR *dh = opendir(path); if (!dh) return NULL; stack = malloc(sizeof(struct dirstack)); stack->dirs[0] = dh; stack->pos = stack->dirs; strcpy(stack->prefix, path); { char *s = &stack->prefix[strlen(stack->prefix)-1]; if (*s == '/') *s = '\0'; } return stack; } void dirstack_close(struct dirstack *stack) { free(stack); } int dirstack_next(struct dirstack *stack, char *fullname, struct stat *st_arg) { struct dirent *dirent; struct stat st; if (stack == NULL) return -1; do { while ((dirent = readdir(*stack->pos)) == NULL) { closedir(*stack->pos); if (stack->pos == stack->dirs) { return -1; } --stack->pos; parentdir(stack->prefix); } } while ( strcmp(dirent->d_name, "." ) == 0 || strcmp(dirent->d_name, "..") == 0 || snprintf(fullname, PATH_MAX, "%s/%s", stack->prefix, dirent->d_name) >= PATH_MAX || lstat(fullname, &st) != 0); if (S_ISDIR(st.st_mode) && stack->pos - stack->dirs + 1 < RECUR_MAXDEPTH && (stack->pos[1] = opendir(fullname))) { ++stack->pos; strcpy(stack->prefix, fullname); } if (st_arg) *st_arg = st; return 0; } struct recur *recur_open(char **paths) { struct recur *recur; if (paths == NULL || paths[0] == NULL || strlen(paths[0]) >= PATH_MAX) return NULL; recur = malloc(sizeof(struct recur)); recur->list = paths; recur->stack = NULL; return recur; } void recur_close(struct recur *recur) { free(recur->stack); free(recur); } int recur_next(struct recur *recur, char *name, struct stat *st_arg) { int rv = -1; while (recur->stack == NULL || (rv = dirstack_next(recur->stack, name, st_arg)) != 0) { const char *cur = recur->list[0]; recur->list++; if (!cur) return -1; dirstack_close(recur->stack); recur->stack = dirstack_open(cur); if (!recur->stack) { struct stat st; if (lstat(cur, &st) == 0) { strcpy(name, cur); if (st_arg) *st_arg = st; return 0; } else { fprintf(stderr, "%s: %s\n", cur, strerror(errno)); } } } return rv; } magicrescue-1.1.8/src/recur.h0000644000175000017500000000116311030775345014172 0ustar jbjjbj#ifndef _RECUR_H #include #include #define RECUR_MAXDEPTH 100 struct dirstack { DIR *dirs[RECUR_MAXDEPTH]; DIR **pos; char prefix[PATH_MAX]; }; struct recur { char **list; struct dirstack *stack; }; void rm_rf(const char *dir); struct dirstack *dirstack_open(const char *path); void dirstack_close(struct dirstack *stack); int dirstack_next(struct dirstack *stack, char *fullname, struct stat *st_arg); struct recur *recur_open(char **paths); void recur_close(struct recur *recur); int recur_next(struct recur *recur, char *name, struct stat *st_arg); # define _RECUR_H #endif magicrescue-1.1.8/src/scanner.c0000644000175000017500000001074311030775345014502 0ustar jbjjbj/* * Magic Rescue scanner code * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include "util.h" #include "array.h" #include "recipe.h" #include "scanner.h" const char *scanner_char(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset) { return memchr(scanbuf, param->c, scanbuf_len); } const char *scanner_block(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset) { long extrabytes = (long)offset & (param->block-1); if (extrabytes) { if ((size_t)(param->block - extrabytes) < scanbuf_len) return scanbuf + param->block - extrabytes; else return NULL; } return scanbuf; } const char *scanner_string(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset) { const char *p = scanbuf + param->scanstring.magicoff; struct string string = param->scanstring.string; char magicchar = param->scanstring.magicchar; scanbuf_len += param->scanstring.magicoff; while ((size_t)(p - scanbuf) < scanbuf_len && (p = memchr(p, magicchar, scanbuf_len - (p-scanbuf)))) { p -= param->scanstring.magicoff; if (memcmp(p, string.s, string.l) == 0) { return p; } p += param->scanstring.magicoff + 1; } return NULL; } void scanner_string_init(union scan_param *param) { unsigned char winner = '\0', c; size_t i, winner_off = 0; /* This table represents the number of million occurrences of each character * on my 9GB sample partition. We want to select the character that has the * smallest score. */ static const int scoretable[] = { 1747,126,57,39,66,30,26,25,63,45,91,20,35,22,22,36,33,20,21,17,25,17,16,15,23, 18,16,15,21,15,15,15,318,18,40,27,86,21,23,22,36,33,28,21,38,40,50,50,94,63,45, 39,43,35,34,31,44,34,30,29,43,35,41,18,29,69,32,40,59,63,39,32,27,40,20,23,41, 34,38,32,44,24,40,47,47,31,26,23,24,20,25,20,25,21,16,79,18,123,45,83,79,182, 130,49,54,115,24,33,86,61,107,106,63,20,111,103,151,64,36,32,37,35,20,18,21,22, 15,15,29,20,15,40,21,28,15,14,17,74,14,67,16,33,14,13,28,15,14,15,16,15,13,12, 14,13,14,13,15,13,14,13,17,14,13,15,16,13,13,14,15,13,14,13,14,13,13,13,15,13, 12,13,16,14,18,14,18,14,14,13,15,15,14,14,28,19,16,21,22,13,17,23,15,16,13,13, 14,13,14,13,19,14,16,14,14,13,13,13,17,16,13,14,14,16,14,14,18,14,14,14,15,16, 13,14,39,21,13,17,20,14,14,15,18,13,13,14,16,14,17,16,19,15,16,17,21,20,24,179}; for (i = 0; i < param->scanstring.string.l; i++) { c = param->scanstring.string.s[i]; if (scoretable[c] < scoretable[winner]) { winner = c; winner_off = i; } } param->scanstring.magicchar = winner; param->scanstring.magicoff = winner_off; } struct scanner *scanner_new(void) { struct scanner *scanner = calloc(1, sizeof(struct scanner)); array_init(&scanner->recipes, sizeof(struct recipe)); scanner->func = NULL; return scanner; } int scanner_compare(const struct scanner *a, const struct scanner *b) { if (a->func == b->func && a->offset == b->offset) { if (a->func == scanner_string && a->param.scanstring.string.l == b->param.scanstring.string.l) { return memcmp( a->param.scanstring.string.s, b->param.scanstring.string.s, a->param.scanstring.string.l); } return memcmp(&a->param, &b->param, sizeof(a->param)); } return -1; } void scanner_destroy(struct scanner *scanner) { struct recipe *recipe; array_foreach(&scanner->recipes, recipe) { recipe_destroy(recipe); } array_destroy(&scanner->recipes); /* Primitive multi-dispatch. * Add a case here for every operation type that requires destruction */ if (scanner->func == scanner_string) { string_destroy(&scanner->param.scanstring.string); } } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/scanner.h0000644000175000017500000000176711030775345014515 0ustar jbjjbj#ifndef _SCANNER_H #include #include "array.h" #include "recipe.h" union scan_param { char c; long block; struct { struct string string; char magicchar; int magicoff; } scanstring; }; typedef const char *(*scan_function)(const char *, size_t, union scan_param *, off_t); struct scanner { scan_function func; union scan_param param; int offset; int extra_len; struct array recipes; }; const char *scanner_char(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset); const char *scanner_block(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset); const char *scanner_string(const char *scanbuf, size_t scanbuf_len, union scan_param *param, off_t offset); void scanner_string_init(union scan_param *param); struct scanner *scanner_new(void); int scanner_compare(const struct scanner *a, const struct scanner *b); void scanner_destroy(struct scanner *scanner); #define _SCANNER_H #endif /* _SCANNER_H */ magicrescue-1.1.8/src/util.c0000644000175000017500000000657311030775345014034 0ustar jbjjbj/* * Magic Rescue misc helper code * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include "util.h" /** Returns the value (0-15) of a single hex digit. Returns 0 on error. */ int hex2num(char c) { return (c >= '0' && c <= '9' ? c-'0' : (c >= 'a' && c <= 'f' ? c-'a'+10 : (c >= 'A' && c <= 'F' ? c-'A'+10 : 0))); } long long hextoll(const char *str) { long long result = 0; size_t i, len = strlen(str); for (i = 0; i < len; i++) { result |= (long long)hex2num(str[i]) << 4*((len-1) - i); } return result; } long atol_calc(const char *str) { if (str[0] != '\0') { long result = atol(str); switch (str[strlen(str)-1]) { case 'G': result *= 1024; case 'M': result *= 1024; case 'k': case 'K': result *= 1024; } return result; } else { return 0; } } off_t rich_seek(int fd, const char *string) { off_t offset; int whence = SEEK_SET; if (string) { const char *stroffset = string; int sign = 1; if (strchr("=+-", stroffset[0])) { switch (stroffset[0]) { case '+': whence = SEEK_CUR; break; case '-': whence = SEEK_END; sign = -1; break; } stroffset++; } if (stroffset[0] == '0' && stroffset[1] == 'x') { offset = (off_t)hextoll(stroffset + 2); } else { #ifdef HAVE_ATOLL offset = (off_t)atoll(stroffset); #else errno = ENOSYS; fprintf(stderr, "Get a C99 compiler or specify offset in hex\n"); return -1; #endif /* not HAVE_ATOLL */ } offset *= sign; } else { offset = 0; whence = SEEK_CUR; } return lseek(fd, offset, whence); } /** Initializes a struct string from a 0-terminated string, parsing escape * sequences. The struct string will not be 0-terminated. */ void string_init(struct string *dst, const char *src) { const size_t slen = strlen(src); size_t i; dst->l = 0; dst->s = malloc(slen); for (i = 0; i < slen; i++) { if (src[i] == '\\') { if (src[i+1] == 'x' && i + 3 < slen) { dst->s[dst->l++] = (hex2num(src[i+2])<<4) | hex2num(src[i+3]); i += 3; } else { i++; dst->s[dst->l++] = ( src[i] == 'a' ? '\a' : ( src[i] == 'b' ? '\b' : ( src[i] == 'f' ? '\f' : ( src[i] == 'n' ? '\n' : ( src[i] == 'r' ? '\r' : ( src[i] == 't' ? '\t' : ( src[i] == 'v' ? '\v' : ( src[i] )))))))); } } else { dst->s[dst->l++] = src[i]; } } /* dst->s = realloc(dst->s, dst->l); // who cares? */ } void string_destroy(struct string *string) { free(string->s); } /* vim: ts=8 sw=4 noet tw=80 */ magicrescue-1.1.8/src/util.h0000644000175000017500000000065511030775345014034 0ustar jbjjbj#ifndef _UTIL_H #include #include #ifndef PATH_MAX # define PATH_MAX 4096 #endif struct string { char *s; size_t l; }; int hex2num(char c); long long hextoll(const char *str); long atol_calc(const char *str); off_t rich_seek(int fd, const char *string); void string_init(struct string *dst, const char *src); void string_destroy(struct string *string); #define _UTIL_H #endif /* _UTIL_H */ magicrescue-1.1.8/tools/checkrecipe0000755000175000017500000000314711030775345015451 0ustar jbjjbj#!/usr/bin/env perl use strict; if (@ARGV and $ARGV[0] eq "--help") { print q(This script will test magicrescue on existing files on your hard disk to see if your recipe is good enough. It can be very useful when creating or modifying a recipe. Usage: ./magicrescue -Mio OPTIONS FILES|tools/checkrecipe [OPTIONS] Options: -s VALUE Size tolerance, in percent. Specifies when checkrecipe should complain about the output file size being different from the input. Usage examples: find / -name \*.png -print0 \ |xargs -0 ./magicrescue -Mio -r png -d /tmp/test-output 2>/dev/null \ |tools/checkrecipe or slocate \*.png|sed "s/['\"\\\\[:blank:]]/\\\\\\\\&/g" \ |xargs ./magicrescue -Mio -r png -d /tmp/test-output 2>/dev/null \ |tools/checkrecipe ); exit; } my $size_tolerance = 0; while (defined(my $arg = shift(@ARGV))) { if ($arg eq "-s") { $size_tolerance = shift(@ARGV)/100; } else { die "$0: Unknown option $arg. Use --help for usage info.\n"; } } my ($curfile, $cur_ok); while () { if (/^i (.*)$/) { if ($curfile and !$cur_ok and -s $curfile) { print "$curfile: not extracted\n"; } $curfile = $1; $cur_ok = 0; } elsif (/^o (.*)$/) { my $outfile = $1; if ($curfile and $cur_ok) { print "$curfile: extracted again\n"; } else { my ($insize, $outsize) = (-s $curfile, -s $outfile); if ($size_tolerance >= 0 and $insize and -f $outfile and abs($outsize - $insize)/$insize > $size_tolerance) { print "$curfile: is $insize bytes, extracted $outsize bytes\n"; } } $cur_ok = 1; unlink $outfile or warn "unlinking $outfile: $!"; } } magicrescue-1.1.8/tools/elfextract.pl0000755000175000017500000000232711030775345015756 0ustar jbjjbj#!/usr/bin/env perl use strict; my $min_size = 99; my $out = shift or die; cat($out, $min_size); if (0 == open READELF, "-|") { open STDERR, ">/dev/null"; exec qw(readelf -h), $out or die; } my ($offset, $shsize, $shcount) = (0, 0, 0); while () { if (/Start of section headers.*?(\d+)/) { $offset = $1 } elsif (/Size of section headers.*?(\d+)/) { $shsize = $1 } elsif (/Number of section headers.*?(\d+)/) { $shcount = $1 } } close READELF; wait; my $size = $offset + $shsize * $shcount; if ($offset and $shsize and $shcount and $size > $min_size and $size < 20_000_000) { cat($out, $size - $min_size); chmod 0755, $out; if (0 == fork) { open STDOUT, ">/dev/null"; open STDERR, ">&STDOUT"; exec qw(objdump -x), $out or die; } wait; if ($?) { print "objdump exit status $?, removing output\n"; unlink $out; } } sub cat { my ($file, $bytes) = @_; my $blocksize = 10240; my $buf; open OUT, ">>$file" or die; for (my $written = 0; $written < $bytes; $written += $blocksize) { my $left = $bytes - $written; if ($left > $blocksize) { $left = $blocksize } read STDIN, $buf, $left or die; print OUT $buf or die; } close OUT; } magicrescue-1.1.8/tools/gimp-resave.pl0000755000175000017500000000233211030775345016030 0ustar jbjjbj#!/usr/bin/perl use strict; use Cwd; use Fcntl qw(:seek); use Gimp qw(:auto); Gimp::init; my $exitval = 1; unless (@ARGV) { print "Usage: $0 FILENAME Before using this you must run the Gimp perl server: \$ gimp --batch '(extension-perl-server 0 0 0)'\n"; exit 1; } my $file = $ARGV[0]; # The Gimp server might be in a different working directory than this script if ($file !~ m[^/]) { $file = getcwd() ."/$file"; } # Open the file and see if it's completely corrupted open FH, $file or die "Opening $file: $!\n"; seek FH, 14, SEEK_CUR or die "seek: $!\n"; my $buf; 12 == read FH, $buf, 12 or die "read: $!\n"; close FH; my ($x, $y, $mode) = unpack("N3", $buf); if ($x > 10_000 or $y > 10_000 or $mode > 10) { print STDERR "$0: bad image: $x x $y, mode $mode\n"; exit 1; } # Prevent message boxes popping up all over the place gimp_message_set_handler(1); my $img = gimp_xcf_load(0, $file, $file); eval { my $layer = gimp_image_get_active_layer($img); gimp_xcf_save(0, $layer, $file, $file); $exitval = 0; print STDERR "Successfully resaved image\n"; }; if ($@) { print STDERR "$@"; } # This must always be called, or Gimp will leak memory gimp_image_delete($img); Gimp::end; exit $exitval; magicrescue-1.1.8/tools/gzip_rename.pl0000755000175000017500000000143011030775345016107 0ustar jbjjbj#!/usr/bin/env perl use strict; use constant FCONT => 1<<1; use constant FEXTRA => 1<<2; use constant FNAME => 1<<3; my $file = $ARGV[0]; unless (@ARGV and -f $file) { die "Usage: gzip_rename.pl FILENAME < orig-data\n"; } my $buf; read STDIN, $buf, 10 or exit; my $flags = (unpack "C4", $buf)[3]; exit unless defined $flags; exit unless $flags & FNAME; if ($flags & FCONT) { read STDIN, $buf, 2 or exit; } if ($flags & FEXTRA) { read STDIN, $buf, 2 or exit; my $len = unpack("v", $buf); # unsigned little-endian 16-bit exit if $len > 10240; read STDIN, $buf, $len or exit; } read STDIN, $buf, 130 or exit; my $origname = unpack("Z130", $buf); if ($origname and length($origname) < 128 and $origname !~ m[[/\x00-\x1F]]) { print "RENAME $origname\n"; } magicrescue-1.1.8/tools/inputseek.c0000644000175000017500000000134011030775345015422 0ustar jbjjbj#include "config.h" #include #include #include #include #include #include "util.h" int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "Usage: inputseek [+-=][0x]OFFSET COMMAND [ARG1 [ARG2 ...]]\n" "\n" " Where OFFSET is the byte position to seek standard input to. I can be\n" " written in hex or decimal, and may include a prefix:\n" " = Seek to an absolute position (default)\n" " + Seek forward to a relative position\n" " - Seek to EOF, minus the offset\n" ); return 1; } if (rich_seek(0, argv[1]) == (off_t)-1) { perror("lseek on stdin"); return 1; } execvp(argv[2], &argv[2]); perror("exec"); return 1; } magicrescue-1.1.8/tools/laola.pl0000755000175000017500000014165711030775345014717 0ustar jbjjbj# # $Id: laola.pl,v 0.5.1.5 1997/07/01 00:06:42 schwartz Rel $ # # laola.pl, LAOLA filesystem. # # This perl 4 library gives raw access to "Ole/Com" documents. These are # documents like created by Microsoft Word 6.0+ or newer Star Divisions # Word by using so called "Structured Storage" technology. Write access # still is nearly not supported, but will be done one day. This library # is part of LAOLA, a distribution this file should have come along with. # It can be found at: # # http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html # or # http://www.cs.tu-berlin.de/~schwartz/pmh/index.html # # Copyright (C) 1996, 1997 Martin Schwartz # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, you should find it at: # # http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/COPYING # # Diese Veröffentlichung erfolgt ohne Berücksichtigung eines eventuellen # Patentschutzes. Warennamen werden ohne Gewährleistung einer freien # Verwendung benutzt. ;-) # # Contact: schwartz@cs.tu-berlin.de # # # Really important topics still MISSING until now: # # - human rights and civil rights where _you_live_ # - Reformfraktion president for Technische Universität Berlin # # - creating documents # - sensible error handling... # - many property set things: # * documentation of variable types # * code page support # - opening multiple documents at a time # - consistant name giving, checked against MS' # # Please refer to the Quick Reference at Laolas home page for further # explanations. # # # Abbreviations # # bbd Big Block Depot # pps Property Storage # ppset Property Set # ppss Property Set Storage # sb Start Block # sbd Small Block Depot # tss Time Stamp Seconds # tsd Time Stamp Days # ## ## "public" ## sub laola_open_document { &laola'laola_open_document; } sub laola_close_document { &laola'laola_close_document; } sub laola_pps_get_name { &laola'laola_pps_get_name; } sub laola_pps_get_date { &laola'laola_pps_get_date; } sub laola_is_directory { &laola'laola_is_directory; } sub laola_get_directory { &laola'laola_get_directory; } sub laola_get_dirhandles { &laola'laola_get_dirhandles; } sub laola_is_file { &laola'laola_is_file; } sub laola_get_filesize { &laola'laola_get_filesize; } sub laola_get_file { &laola'laola_get_file; } sub laola_is_root { &laola'laola_is_root; } # # writing # sub laola_modify_file { &laola'laola_modify_file; } # # property set handling # sub laola_is_file_ppset { &laola'laola_is_file_ppset; } sub laola_ppset_get_dictionary { &laola'laola_ppset_get_dictionary; } sub laola_ppset_get_idset { &laola'laola_ppset_get_idset; } sub laola_ppset_get_property { &laola'laola_ppset_get_property; } # # trash handling # sub laola_get_trashsize { &laola'laola_get_trashsize; } sub laola_get_trash { &laola'laola_get_trash; } sub laola_modify_trash { &laola'laola_modify_trash; } package laola; $laola_date = "03/25/97"; changable_options: { $optional_do_iobuf=0; # 0: don't cache 1: cache whole compound document $optional_do_debug=0; # 0: don't debug 1: print some debugging information } ## ## File and directory handling ## sub laola_open_document { ## # # "ok"||$error = laola_open_document($filename [,$openmode [,$streambuf]]); # # openmode bitmask (0 is default): # # Bit 0: 0 read only 1 read and write # Bit 4: 0 file mode 1 buffer mode # local($status)=""; open_doc1: { &init_vars(); if ( ($status=&init_io(@_)) ne "ok") { last; } if ( ($status=&init_doc()) ne "ok") { &laola_close_document(); last; } return "ok"; } $status; } sub laola_close_document { ## # # "ok" = laola_close_document([streambuf]) # if ($openmode & 0x10) { if (defined $_[0]) { $_[0]=$iobuf; } } else { &flush_cache(); &clean_file(); } &init_vars(); return "ok"; } sub laola_is_directory { ## # # 1||0 = laola_is_directory($pps) # local($pps)=shift; (!$pps || ($pps_type[$pps] == 1)); } sub laola_is_file { ## # # 1||0 = laola_is_file($pps) # ($pps_type[shift] == 2); } sub laola_is_root { ## # # 1||0 = laola_is_root($pps) # ($pps_type[shift] == 5); } sub laola_get_dirhandles { ## # # @pps = laola_get_dirhandles($pps); # local($start)=shift; local (@chain) = (); local (%chaincontrol) = (); (!$start || &laola_is_directory($start)) && &get_ppss_chain($pps_dir[$start]) ; @chain; } sub laola_get_directory { ## # # %pps_names = laola_get_directory($pps); # local(%pps_namehandle)=(); for (&laola_get_dirhandles) { $pps_namehandle{&laola_pps_get_name($_)} = $_; } %pps_namehandle; } sub laola_pps_get_name { ## # # $name_of_pps = laola_pps_get_name($pps); # $pps_name[shift]; } sub laola_pps_get_date { ## # # ($day,$month,$year,$hour,$min,$sec)||0 = laola_pps_get_date($pps) # (1..31, 1..12, 1601..., 0..23, 0..59, 0.x .. 59.x) # local($pps)=shift; &laola_is_directory($pps) && &filetime_to_time($pps_ts2s[$pps], $pps_ts2d[$pps]); } sub laola_get_filesize { ## # # $filesize || 0 = laola_get_filesize($pps); # local($pps)=shift; &laola_is_file($pps) && $pps_size[$pps]; } sub laola_get_file { ## # # "ok"||$error = laola_get_file($pps, extern $buf [,$offset, $size]); # &rw_file("r", @_); } sub laola_modify_file { ## # # "ok"||$error = laola_modify_file($pps,extern $buf, $offset, $size); # return "Laola: File is write protected!" if !io_writable; &rw_file("w", @_); } ## ## Property set handling ## sub laola_is_file_ppset { ## # # ppset_type || 0 = laola_is_file_ppset($pps) # ppset_type e {1, 5} # local($pps)=shift; (&laola_is_file($pps)) && ( (&laola_pps_get_name($pps) =~ /^\05/) && 5 || (&laola_pps_get_name($pps) =~ /^\01CompObj$/) && 1 ); } sub laola_ppset_get_dictionary { ## # # ("ok", %dictionary)||$error = laola_ppset_get_dictionary($pps) # local($pps)=shift; local($status) = &load_propertyset($pps); if ($status ne "ok") { return $status; } else { return ("ok", %ppset_dictionary); } } sub laola_ppset_get_idset { ## # # ("ok", %ppset_idset) || $error = laola_ppset_get_idset($pps); # local($pps)=shift; local($status) = &load_propertyset($pps); return $status if $status ne "ok"; local(%ts)=(); foreach $key (keys %ppset_fido) { $ts{$key} = $ppset_dictionary{$key}; } ("ok", %ts); } sub laola_ppset_get_property { ## # # ($type,@mixed)||("error",$error)=laola_ppset_get_property($pps, $id) # local($pps, $id)=@_; local($type, $l, $var, @var); local($o, $n); local($status)= &load_propertyset($pps); return ("error", $status) if $status ne "ok"; return "" if !defined $ppset_fido{$id}; $n = int($id / 0x1000); $o = $ppset_o[$n]+$ppset_fido{$id}; if ($ppset_type == 5) { #return ("error", "Property Identifier is invalid.") if $id < 2; ($type, $l, @var) = &ppset_get_property($o); return ($type, @var); } elsif ($ppset_type == 1) { ($l, $var) = &ppset_get_var(0x1e, $o); return (0x1e, $var); } } ## ## Trash handling ## sub laola_get_trashsize { ## # # $sizeof_trash_section = laola_get_trashsize($type) # &get_trash_size(@_); } sub laola_get_trash { ## # # "ok"||$error = laola_get_trash ($type, extern $buf [,$offset,$size]); # &rw_trash("r", @_); } sub laola_modify_trash { ## # # "ok"||$error = laola_modify_trash ($type, extern $buf [,$offset,$size]); # return "Laola: File is write protected!" if !io_writable; &rw_trash("w", @_); } ## ## "private" ## global_init: { &var_init(); &filetime_init(); &propertyset_type_init(); $[=0; } # # laola_open_document -> # sub init_vars { # laola_open_document->init_vars # laola_close_document->init_vars internal: { $infilename=undef; $filesize=undef; $openmode=undef; $io_writable=undef; $curfile=undef; @curfile_iolist = (); $iobuf=undef; @iobuf_modify_a=(); @iobuf_modify_l=(); } &init_propertyset(); OLEstructure: { # unknown header things that matter: # ? $version=undef; # word(1a) # ? $revision=undef; # word(18) # ? $bigunknown=undef; # byte(1e) # known header things that matter: $header_size=0x200; $big_block_size=undef; # word(1e) $small_block_size=undef; # word(20) $num_of_bbd_blocks=undef; # long(2c) $root_startblock=undef; # long(30) $sbd_startblock=undef; # long(3c) $ext_startblock=undef; # long(44) $num_of_ext_blocks=undef; # long(48) # property storage things @pps_name=(); # 0 .. pps_sizeofname #pps_sizeofname=(); # word(40) @pps_type=(); # byte(42) @pps_uk0=(); # byte(43) @pps_prev=(); # long(44) @pps_next=(); # long(48) @pps_dir=(); # long(4c) @pps_ts1s=(); # long(64) @pps_ts1d=(); # long(68) @pps_ts2s=(); # long(6c) @pps_ts2d=(); # long(70) @pps_sb=(); # long(74) @pps_size=(); # long(78) } various: { $maxblock=undef; $maxsmallblock=undef; # block depot blocks # - these blocks are building the block depots @bbd_list=(); @sbd_list=(); # block depot tables @bbd=(); @sbd=(); # contents blocks @root_list=(); @sb_list=(); blockusage: { @bb_usage=(); # big blocks usage @sb_usage=(); # small blocks usage $usage_known=undef; } trash: { %trashsize=(); @trash1_o=(); @trash1_l=(); @trash2_o=(); @trash2_l=(); @trash3_o=(); @trash3_l=(); @trash4_o=(); @trash4_l=(); $trash_known=undef; } } } sub init_io { ($infilename, $openmode) = @_; if ($openmode & 0x10) { return &init_stream; } else { return &init_file; } } sub init_stream { return "No stream data available!" if !defined $_[2]; #$openmode &= 0xfffffffe; # clear writeable flag $optional_do_iobuf=1; $iobuf = $_[2]; $filesize = length($iobuf); if ( (&read_long(0) != 0xe011cfd0) || (&read_long(4) != 0xe11ab1a1) ) { return "\"$infilename\" is no Ole / Compound Document!\n"; } "ok"; } sub init_file { local($status); return "\"$infilename\" does not exist!" if ! -e $infilename; return "\"$infilename\" is a directory!" if -d $infilename; return "\"$infilename\" is no proper file!" if ! -f $infilename; return "Cannot read \"$infilename\"!" if ! -r $infilename; if ($openmode & 1) { return "\"$infilename\" is write protected!" if ! -w $infilename; $io_writable = 1; $status = open(IO, '+<'.$infilename); } else { $io_writable = 0; $status = open(IO, $infilename); } return "Cannot open \"$infilename\"!" if !$status; binmode(IO); if ($io_writable) { select(IO); $|=1; select(STDOUT); } if ( (&read_long(0) != 0xe011cfd0) || (&read_long(4) != 0xe11ab1a1) ) { return "\"$infilename\" is no Ole / Compound Document!\n"; } $filesize = -s $infilename; read_iobuf: { if ($optional_do_iobuf) { if (!&myread(0, $filesize, $iobuf, 0)) { undef $iobuf; } } } "ok"; } sub init_doc { # read bbd, # get bbd -> root-chain, get bbd -> sbd-chain local($i, $tmp)=(undef, undef); local(@tmp)=undef; header_information: { $big_block_size=1<<&read_word(0x1e); $small_block_size=1<<&read_word(0x20); $num_of_bbd_blocks=&read_long(0x2c); $root_startblock=&read_long(0x30); $sbd_startblock=&read_long(0x3c); $ext_startblock=&read_long(0x44); $num_of_ext_blocks=&read_long(0x48); $maxsmallblock= int ( &read_long( $header_size + $root_startblock*$big_block_size + 0x78 ) / $small_block_size -1 ); } internal: { $maxblock = int ( ($filesize-$header_size) / $big_block_size -1); return "Document is corrupt - size is too small." if $maxblock < 1; } # read big block depot read_bbd: { $max_in_header = int ( ($header_size-0x4c)/4 ); $todo = $num_of_bbd_blocks; $num = $todo; $num = $max_in_header if $num_of_bbd_blocks > $max_in_header; for ($i=0; $i<$num; $i++) { push (@bbd_list, &read_long(0x4c+4*$i)); } $todo -= $num; $next = $ext_startblock; while ($todo > 0) { $num = $todo; $num = ($big_block_size-4)/4 if $todo>(($big_block_size-4)/4); $o = $header_size + $next*$big_block_size; for ($i=0; $i<$num; $i++) { push (@bbd_list, &read_long($o+4*$i)); } $todo -= $num; $next = &read_long($o+4*$num); } $tmp=""; &rw_iolist("r", $tmp, &get_iolist(3, 0, 0xffffffff, 0, @bbd_list) ); @bbd = unpack ($vtype{"l"}.($maxblock+1), $tmp); } # read small block depot read_sbd: { $tmp=""; @sbd_list=&get_list_from_depot($sbd_startblock, 1); &rw_iolist("r", $tmp, &get_iolist(3, 0, 0xffffffff, 0, @sbd_list) ); @sbd = unpack ($vtype{"l"}.($maxsmallblock+1), $tmp); } root_and_sb_chains: { @root_list=&get_list_from_depot($root_startblock, 1); return "Document is corrupt - no root entry." if !@root_list; @sb_list=&get_list_from_depot ( &read_long ( $header_size + $root_startblock*$big_block_size + 0x74 ), 1 ); } read_PropertyStorages: { &read_ppss(0); # # If there are many property storages, they will be loaded # dynamically. If there are few (I randomly chosed 50), they # all will be read (ditto for debugging). # last if $#root_list>50 || !$optional_do_debug; local($buf)=""; local($i, $nl); &rw_iolist("r", $buf, &get_iolist(3, 0, 0xffffffff, 0, @root_list) ); print "\n\n" ."---------------------------------------------\n" ."LAOLA INTERNAL start of debugging information\n\n" ." n size chain typ name date\n" ; for ($i=0; $i<=($#root_list+1)*4; $i++) { &read_ppss_buf($i, $buf); &debug_report_pps($i) if $optional_do_debug; } print "\n" ."LAOLA INTERNAL end of debugging information\n" ."-------------------------------------------\n\n" ; } &report_blockuse_statistic() if $optional_do_debug; "ok"; } ## ## laola_close_document -> ## sub clean_file { close(IO); } ## ## -------------------------- File IO ------------------------------ ## sub rw_file { # # "ok"||error = rw_file("r"||"w", $pps_handle, extern $buf [,$offset, $size]) # local($maxarg)=$#_; local($rw, $pps) = @_[0..1]; return "Laola: pps is no file!" if !&laola_is_file($pps); return "Laola: no method \"$rw\"!" if !($rw =~ /^[rw]$/i); local($status, $offset, $size) = &get_default_iosize($pps_size[$pps], $rw, @_[2..$maxarg]); return $status if $status ne "ok"; return "Bad document structure!" if ! &get_curfile_iolist($pps); return "ok" if &rw_iolist($rw, $_[2], &get_iolist(4, $offset, $size)); $rw =~ /^r$/i ? "Laola: read error!" : "Laola: write error!"; } sub get_default_iosize { # # ("ok", $offset, $size) || $error = # get_default_iosize (defsize, "r"||"w", extern buf, offset, size) # local($maxarg)=$#_; local($defsize, $rw) = @_[0..1]; local($offset, $size) = @_[3..4]; if (!$size) { if ($rw =~ /^r$/i) { if ($maxarg < 4) { # read default: read trashsize $offset=0; $size=$defsize; } else { # read zero size: no problem $_[2]=""; } } else { if ($maxarg < 4) { # write default: not allowed! return "Laola: write error! Unknown size."; } else { # write zero size: no problem } } } ("ok", $offset, $size); } sub get_curfile_iolist { # # 1||0 = get_curfile_iolist($pps) # # Gets the iolist for the current file $pps # if ($curfile) { return 1 if $curfile==$pps; } @curfile_iolist = &get_iolist( $pps_size[$pps]>=0x1000, 0, $pps_size[$pps], $pps_sb[$pps] ); $curfile = $pps; 1; } sub get_all_filehandles { # # &get_all_filehandles(starting directory) # # !recursive! # Recurse over all files and directories, # return all file handles as @files. # local($directory_pps)=shift; local(@dir)=&laola_get_dirhandles($directory_pps); local(@files)=(); local(%filescontrol)=(); foreach $entry (@dir) { if (!$filescontrol{$entry}) { $filescontrol{$entry} = 1; if (&laola_is_file($entry)) { push (@files, $entry) } elsif (&laola_is_directory($entry)) { push (@files, &get_all_filehandles($entry)); } } else { print STDERR "This document is corrupt!\n"; } } @files; } ## ## --------------------- Property Set Handling ------------------------- ## sub propertyset_type_init { %ppset_vtype = ( 0x00, "empty", 0x01, "null", 0x02, "i2", 0x03, "i4", 0x04, "r4", 0x05, "r8", 0x06, "cy", 0x07, "date", 0x08, "bstr", 0x0a, "error", 0x0b, "bool", 0x0c, "variant", 0x11, "ui1", 0x12, "ui2", 0x13, "ui4", 0x14, "i8", 0x15, "ui8", 0x1e, "lpstr", 0x1f, "lpwstr", 0x40, "filetime", 0x41, "blob", 0x42, "stream", 0x43, "storage", 0x44, "streamed_object", 0x45, "stored_object", 0x46, "blobobject", 0x48, "clsid", 0x49, "cf", 0xfff, "typemask", ); local(@type) = keys %ppset_vtype; for (@type) { $ppset_vtype{$_+0x1000} = $ppset_vtype{$_}.'[]'; } # \05 %ppset_SummaryInformation = ( 2, "title", 3, "subject", 4, "authress", 5, "keywords", 6, "comments", 7, "template", 8, "lastauthress", 9, "revnumber", 10, "edittime", 11, "lastprinted", 12, "create_dtm_ro", 13, "lastsave_dtm", 14, "pagecount", 15, "wordcount", 16, "charcount", 17, "thumbnail", 18, "appname", 19, "security" ); %ppset_DocumentSummaryInformation = ( 15, "organization" ); # \01CompObj %ppset_CompObj = ( 0, "doc_long", 1, "doc_class", 2, "doc_spec" ); } sub load_dictionary { # # "ok"||"done"||0 = load_dictionary($pps) # local($pps)=shift; &load_dictionary_defaults($pps); local($i, $n, $o, $ps); local($did, $dname, $l); foreach $id (keys %ppset_fido_dict) { next if !$ppset_fido_dict{$id}; $ps = int($id/0x1000); $o = $ppset_o[$ps]+$ppset_fido_dict{$id}; $n = &get_long($o, $ppset_buf); $o+=4; for (; $n; $n--) { $did = &get_long($o, $ppset_buf); $o+=4; ($l, $dname) = &ppset_get_var(0x1e, $o); $o+=$l; $ppset_dictionary{$did+$ps*0x1000} = $dname; } } return "ok"; } sub load_dictionary_defaults { local($name)=&laola_pps_get_name($pps); if ($name eq "\05SummaryInformation") { %ppset_dictionary = %ppset_SummaryInformation; return "ok"; } elsif ($name eq "\05DocumentSummaryInformation") { %ppset_dictionary = %ppset_DocumentSummaryInformation; return "ok"; } elsif ($name eq "\01CompObj") { %ppset_dictionary = %ppset_CompObj; return "ok"; } return 0; } sub load_propertyset { local($pps)=shift; local($status)=""; check_current: { if ($ppset_current && $pps && ($ppset_current == $pps)) { $status="ok"; last; } if (!&laola_is_file_ppset($pps)) { $status="This is not a property set handle."; last; } &init_propertyset(); if (!&laola_get_file($pps, $ppset_buf)) { $status="Cannot load property set."; } $ppset_type = &laola_is_file_ppset($pps); } return $status if $status; if ($ppset_type == 5) { $status = &load_propertyset_05($pps); return $status if $status ne "ok"; } elsif ($ppset_type == 1) { $status = &load_propertyset_01CompObj($pps); return $status if $status ne "ok"; } else { return "Unknown property set!"; } $status = &load_dictionary($pps); return $status; } sub init_propertyset { # !global! property set things $ppset_current=undef; # current property storage handle $ppset_type=undef; # \05, \01CompObj $ppset_buf=undef; # buffer for whole property %ppset_fido=(); # $ppset_fido{Identifier}=Offset; # Format Pairs of $ppset_current %ppset_fido_dict=(); # Dictionaries %ppset_fido_cp=(); # Code pages $ppset_codepage=undef; %ppset_dictionary=(); structure_05: { # 05 ppsets # Header $ppset_byteorder=undef; # word (0) {0xfffe} $ppset_format=undef; # word (2) {0} $ppset_osver=undef; # word (4) {lbyte=version hbyte=revision} $ppset_os=undef; # word (6) {0=win16|1=mac|2=win32) @ppset_clsid=(); # class identifier (8) {e.g. @0} $ppset_reserved=undef; # long (18) {>=1} # FormatIDOffset @ppset_fmtid=(); # format identifier (1c) @ppset_o=(); # ppset_o[0]: long (2c) # PropertySectionHeader @ppset_size=(); # word ($ppset_o[]) @ppset_num=(); # long ($ppset_o[]+4) } #structure_01CompObj: { #$ppset_uk1=undef; # word (0) {0x0001} #$ppset_byteorder=undef; # word (2) {0xfffe} #$ppset_osver=undef; # word (4) {lbyte=version hbyte=revision} #$ppset_os=undef; # word (6) {0=win16|1=mac|2=win32) # { ff ff ff ff 00 09 02 00 00 00 00 00 # c0 00 00 00 00 00 00 46 } #@ppset_o=(); # 0x1c #} } sub load_propertyset_01CompObj { local($pps)=shift; set_current: { $ppset_current = $pps; get_structure: { $ppset_byteorder = &get_word(0x02, $ppset_buf); $ppset_osver = &get_word(0x04, $ppset_buf); $ppset_os = &get_word(0x06, $ppset_buf); @ppset_o = (0x1c); } check_structure: { if ($ppset_byteorder !=0xfffe) { return "Cannot understand property set."; } } } get_offsets: { local($i); local($offset, $length)=(0, 0); for ($i=0; $i<3; $i++) { $length = &get_long($ppset_o[0] + $offset, $ppset_buf); last if !$length; $ppset_fido{$i} = $offset; $offset = $offset + 4 + $length; } } "ok"; } sub load_propertyset_05 { local($pps)=shift; set_current: { $ppset_current = $pps; get_structure: { ($ppset_byteorder, $ppset_format, $ppset_osver, $ppset_os) = &get_nword(4, 0, $ppset_buf) ; @ppset_clsid = &get_uuid(0x08, $ppset_buf); $ppset_reserved = &get_long(0x18, $ppset_buf); @ppset_fmtid = &get_uuid(0x1c, $ppset_buf); $ppset_o[0] = &get_word(0x2c, $ppset_buf); $ppset_size[0] = &get_word($ppset_o[0], $ppset_buf); $ppset_num[0] = &get_word($ppset_o[0]+4, $ppset_buf); } check_structure: { $status="Cannot understand property set."; last if $ppset_byteorder != 0xfffe; last if $ppset_format != 0; last if $ppset_reserved < 1; last if $ppset_o[0] < 0x30; $status=""; } } return $status if $status; get_ids_and_offsets: { local($i, $id, $n, $num, $fido); local($o)=$ppset_o[0]; for ($n=0; $n<$ppset_reserved; $n++) { # default dictionary and codepage $ppset_fido_dict{$n*0x1000+0} = 0; $ppset_fido_cp{$n*0x1000+1} = 0x4e4; $num=&get_word($o+4, $ppset_buf); for ($i=0; $i<$num; $i++) { $id = &get_long($o+8+$i*8, $ppset_buf); if ($n) { $id = $i if $id>1; # ! hacky ! } $fido = &get_long($o+8+$i*8+4, $ppset_buf); if ($id>1) { $ppset_fido{$n*0x1000+$id} = $fido; } elsif ($id==1) { $ppset_fido_cp{$n*0x1000+1} = $fido; } elsif ($id==0) { $ppset_fido_dict{$n*0x1000} = $fido; } } $o+=&get_word($o, $ppset_buf); $ppset_o[$n+1]=$o; } } # todo: code page "ok"; } sub ppset_get_property { # # ($type, $size, @mixed)||("error", $debuginfo) = ppset_get_property($offset) # local($o_begin)=$_[0]; local($o)=$o_begin; local($type) = &get_long($o, $ppset_buf); if (! ($type & 0x1000)) { return ($type, &ppset_get_var($type, $o+4)); } else { local(@mixed)=(); local($n)=&get_long($o+4, $ppset_buf); $o+=8; local($t, $l, @var); for (; $n; $n--) { @var=(); ($l, @var) = &ppset_get_var($type^0x1000, $o); push (@mixed, 1+($#var+1), $type^0x1000, @var); $o+=$l; } return ($type, $o-$o_begin, @mixed); } } sub ppset_get_var { # # ($size, @var) = &ppset_get_var($type, $offset); # local($type, $o)=@_; if (!$type || $type == 0x01) { # empty, null return (0, ""); } elsif ($type == 0x02) { # i2 local($tmp) = &get_word($o, $ppset_buf); $tmp = - (($tmp^0xffff) +1) if ($tmp & 0x8000); return (2, $tmp); } elsif ($type == 0x03) { # i4 local($tmp) = &get_long($o, $ppset_buf); $tmp = - (($tmp^0xffffffff) +1) if ($tmp & 0x80000000); return (4, $tmp); } elsif ($type == 0x04) { # real return (4, unpack("f", substr($ppset_buf, $o, 4)) ); } elsif ($type == 0x05) { # double return (8, unpack("d", substr($ppset_buf, $o, 8)) ); } elsif ($type == 0x0a) { # error return (4, &get_word($o, $ppset_buf)); } elsif ($type == 0x0b) { # bool (0==false, -1==true) return (4, &get_long($o, $ppset_buf)); } elsif ($type == 0x0c) { # variant local($t, $l, @var); $t = &get_long($o, $ppset_buf); ($l, @var) = &ppset_get_var($t, $o+4); return (4+$l, $t, @var); } elsif ($type == 0x11) { # ui1 return (1, &get_byte($o, $ppset_buf)); } elsif ($type == 0x12) { # ui2 return (2, &get_word($o, $ppset_buf)); } elsif ($type == 0x13) { # ui4 return (4, &get_long($o, $ppset_buf)); } elsif ($type == 0x1e) { # lpstr local($l)=&get_long($o, $ppset_buf); if ($l) { return (4+$l, substr($ppset_buf, $o+4, $l-1)); } else { return (4, ""); } } elsif ($type==0x40) { # filetime return (8, &filetime_to_time(&get_nlong(2, $o, $ppset_buf)) ); } else { return ( "error", sprintf("(offset=%x, type=%x, buf[0]=%x)", $o, $type, &get_long($o+4, $ppset_buf) ) ); } } ## ## Basic laola data types ## sub var_init { # # At this work I still don't trust in signed integers, therefore I # prefer the unsigned 0xffffffff to -1 (don't beat me) # $vtype{"c"}="C"; $vsize{"c"}=1; # unsigned char $vtype{"w"}="v"; $vsize{"w"}=2; # 0xfe21 == 21 fe $vtype{"l"}="V"; $vsize{"l"}=4; # 0xfe21abde == de ab 21 fe } sub get_chars { # # get_chars ($offset, $number, extern $sourcebuf); # substr($_[2], $_[0], $_[1]); } sub read_chars { # # read_chars ($offset, $number); # local($tmp)=""; &myread($_[0], $_[1], $tmp) && $tmp; } # get_thing ($offset, extern $buf); sub get_byte { &get_var("c", @_); } sub get_word { &get_var("w", @_); } sub get_long { &get_var("l", @_); } sub get_var { unpack ($vtype{$_[0]}, substr($_[2], $_[1], $vsize{$_[0]})); } # get_nthing ($n, $offset, extern $buf); sub get_nbyte { &get_nvar("c", @_); } sub get_nword { &get_nvar("w", @_); } sub get_nlong { &get_nvar("l", @_); } sub get_nvar { unpack ($vtype{$_[0]}.$_[1], substr($_[3], $_[2], $vsize{$_[0]}*$_[1])); } # read_thing ($offset); sub read_byte { &read_var("c", @_); } sub read_word { &read_var("w", @_); } sub read_long { &read_var("l", @_); } sub read_var { unpack ($vtype{$_[0]}, &read_chars($_[1], $vsize{$_[0]})); } # read_nthing ($n, $offset); sub read_nbyte { &read_nvar("c", @_); } sub read_nword { &read_nvar("w", @_); } sub read_nlong { &read_nvar("l", @_); } sub read_nvar { unpack ($vtype{$_[0]}.$_[1], &read_chars($_[2], $vsize{$_[0]}*$_[1])); } ## ## --------------------------- IO handling ------------------------------ ## sub myio { # # 1||0= myio("r"||"w", $file_offset, $num_of_chars, $extern_var [,$var_offset]) # $_ = shift; /^r$/i ? &myread : /^w$/i ? &mywrite : 0; } sub myread { # # 1||0 = myread($file_offset, $num_of_chars, $extern_var [,$var_offset]) # local($varoffset)= $_[3] || 0; if ($optional_do_iobuf && $iobuf) { substr($_[2], $varoffset, $_[1])=substr($iobuf, $_[0], $_[1]); return 1; } else { seek(IO, $_[0], 0) && (read(IO,$_[2],$_[1],$varoffset) == $_[1]); } } sub mywrite { # # 1||0 = mywrite($file_offset, $num_of_chars, $extern_var [,$var_offset]) # return 0 if !$io_writable; local($varoffset)= $_[3] || 0; local($tmp) = substr($_[2], $varoffset, $_[1]); $tmp .= "\00" x ($_[1]-length($tmp)); if ($optional_do_iobuf && $iobuf) { substr($iobuf, $_[0], $_[1]) = $tmp; push(@iobuf_modify_a, $_[0]); push(@iobuf_modify_l, $_[1]); return 1; } else { seek(IO, $_[0], 0) && print IO $tmp; } } sub flush_cache { # # void = flush_cache() # # flush io cache, if caching is turned on # return if !($optional_do_iobuf && $iobuf); &rw_iolist("w", $iobuf, &aggregate_iolist(2, @iobuf_modify_a, @iobuf_modify_l) ); @iobuf_modify_a=(); @iobuf_modify_l=(); } ## ## The "logical" core of laola ## sub get_ppss_chain { # # @blocks = get_ppss_chain($ppss) # # !recursive! # local($ppss) = @_; return if $ppss == 0xffffffff; if ($chaincontrol{$ppss}) { # Recursive entry! @chain = (); print STDERR "This document is corrupt!\n"; return; } else { &read_ppss($ppss); $chaincontrol{$ppss}=1; } &get_ppss_chain ( $pps_prev[$ppss] ); push(@chain, $ppss); &get_ppss_chain ( $pps_next[$ppss] ); } sub read_ppss_buf { # # "ok" = read_ppss_buf ($i, extern $buf) # local($i)=$_[0]; local($nl); return "ok" if $pps_name[$i]; return if ! ($nl = &get_word($i*0x80+0x40, $_[1])); $pps_name[$i] = &pps_name_to_string($i*0x80, $nl, $_[1]); ($pps_type[$i], $pps_uk0[$i], $pps_prev[$i], $pps_next[$i], $pps_dir[$i]) = unpack($vtype{"c"}."2".$vtype{"l"}."3", substr($_[1], $i*0x80+0x42, $vsize{"c"}*2+$vsize{"l"}*3)) ; ($pps_ts1s[$i], $pps_ts1d[$i], $pps_ts2s[$i], $pps_ts2d[$i], $pps_sb[$i], $pps_size[$i]) = &get_nlong(6, $i*0x80+0x64, $_[1]) ; "ok"; } sub read_ppss { # # "ok" = read_ppss ($i) # local($i)=shift; return "ok" if $pps_name[$i]; local($buf)=""; &rw_iolist("r", $buf, &get_iolist(3, $i*0x80, 0x80, 0, @root_list)); local($nl); return if ! ($nl = &get_word(0x40, $buf)); $pps_name[$i] = &pps_name_to_string(0, $nl, $buf); ($pps_type[$i], $pps_uk0[$i], $pps_prev[$i], $pps_next[$i], $pps_dir[$i])= unpack($vtype{"c"}."2".$vtype{"l"}."3", substr($buf, 0x42, $vsize{"c"}*2+$vsize{"l"}*3) ) ; ($pps_ts1s[$i], $pps_ts1d[$i], $pps_ts2s[$i], $pps_ts2d[$i], $pps_sb[$i], $pps_size[$i]) = unpack( $vtype{"l"}."6", substr($buf, 0x64, $vsize{"l"}*6) ); "ok"; } sub get_list_from_depot { # # @blocks = get_list_from_depot ($start, depottype) # # Read a block chain starting with block $start out of a either # depot @bbd (for $t) or depot @sbd (for !$t). # local($start, $t)=@_; local(@chain)=(); return @chain if $start == 0xfffffffe; push (@chain, $start); while ( ($start = $t?$bbd[$start]:$sbd[$start]) != 0xfffffffe ) { push(@chain, $start); } @chain; } sub get_iolist { # # @iolist = get_iolist ($depottype, $offset, $size, $startblock [,@depot]) # # This is the main IO logic. Returns the iolist for a data stream according # to depot type $t. The stream may start at offset $offset and can have a # size $size. If size is bigger than the total size of the stream according # to its depot, it will be cut correctly. (So if you want to read until the # files end without knowing how many bytes that are, take 0xffffffff as size). # # depottype $t: # 0 small block (for @sbd) small block depot # 1 big block (for @bbd) big block depot # 2 small block (for @_[4..$#]) some small blocks # 3 big block (for @_[4..$#]) some big blocks # 4 variable (for @curfile_iolist) iolist of current file # 5 variable (for @_[4..$#] == (@o, @l)) some iolist # local($t, $offset, $size, $sb) = (shift||0, shift||0, shift||0, shift||0); local($di); local($bs, $max); local(@empty)=(); return @empty if !$size; local($begin, $done, $len); local(@o)=(); local(@l)=(); $bs = ($t==1 || $t==3) ? $big_block_size : $small_block_size; if ($t<2) { # To skip these offsets, stream chains would have to be resolved # before. } elsif ($t<4) { $max = $#_; # Skip whole blocks, when offset given $sb += int ($offset / $bs); $offset -= int ($offset / $bs) * $bs; } elsif ($t==4) { $max = ($#curfile_iolist-1)/2; } elsif ($t==5) { $max = ($#_-1)/2; } else { return @empty; } $done = 0; for ( $di=$sb; ($t<2) ? ($di!=0xfffffffe): ($di<=$max); $di=&next_dl ) { last if ($done == $size); if ($t==4) { $bs = $curfile_iolist[$max+1+$di]; } elsif ($t==5) { $bs = $_[$max+1+$di]; } if ($offset) { if ($bs <= $offset) { $offset -= $bs; next; } else { $begin = &depot_offset + $offset; $len = $bs - $offset; $offset = 0; } } else { $begin = &depot_offset; $len = $bs; } if ( ($done+$len) > $size ) { $len = $size - $done; } if ( !@o || ($o[$#o]+$l[$#l])!=$begin ) { push(@o, $begin); push(@l, $len); } else { $l[$#l]+=$len; } $done += $len; } (@o, @l); } sub next_dl { # get_iolist:next_dl # # index = depot ($di==index, $t==depothandle) # # Returns next chain link of depot @bbd ($t) or @sbd (!$t) # return $sbd[$di] if !$t; return $bbd[$di] if $t==1; $di+1; } sub depot_offset { # get_iolist:depot_offset # # offset = depot_offset ($di==index, $t==depottype) # return (($sb_list[$di/8]+1)*8 + ($di%8))*$small_block_size if $t==0; return $header_size + $di*$big_block_size if $t==1; return (($sb_list[$_[$di]/8]+1)*8 + ($_[$di]%8))*$small_block_size if $t==2; return $header_size + $_[$di]*$big_block_size if $t==3; return ($curfile_iolist[$di]) if $t==4; return ($_[$di]) if $t==5; } sub aggregate_iolist { # # (@offsets, @lengths)||() = aggregate_iolist(method,@offsets,@lengths) # # method: # 1 @offsets shall be sorted, no overlap allowed # 2 @offsets shall be sorted, overlap is allowed # 3 @offsets are sorted, no overlap allowed # 4 @offsets are sorted, overlap is allowed # local($method)=shift; local(@empty)=(); return @empty if ($method<1)||($method>4); # Don't know method! local($max)=int(($#_+1)/2); local($i, $j); local(@o_in)=(); local(@l_in)=(); local(%o_in)=(); local(@o_out)=(); local(@l_out)=(); local($offset, $len); # # Sort # if ( ($method==1) || ($method==2)) { # sort offsets for ($i=0; $i<$max; $i++) { next if !$_[$max+$i]; if ($o_in{$_[$i]}) { return @empty if $method==1; # Data chunks overlap! $o_in{$_[$i]}=$i if $_[$max+$i]>$o_in{$_[$i]}; } else { $o_in{$_[$i]}=$i; } } foreach $key (sort {$a <=> $b} keys %o_in) { push(@o_in, $_[$o_in{$key}]); push(@l_in, $_[$max + $o_in{$key}]); } } else { @o_in=@_[0..($max-1)]; @l_in=@_[$max..$#_]; } # # Aggregate # $offset=$o_in[0]; $len=$l_in[0]; for ($i=1; $i<=($#o_in+1); $i++) { if ( ($i==($#o_in+1)) || ($o_in[$i]<$offset) || ($o_in[$i]>($offset+$len)) ) { push(@o_out, $offset); push(@l_out, $len); $offset=$o_in[$i]; $len=$l_in[$i]; } elsif ($o_in[$i]<($offset+$len)) { return @empty if ($type==1 || $type==3); # Data chunks overlap! if ( ($o_in[$i]+$l_in[$i]) > ($offset+$len) ) { $len=$o_in[$i]+$l_in[$i]-$offset; } } else { $len += $l_in[$i]; } } (@o_out, @l_out); } sub rw_iolist { # # 1||0 = rw_iolist("r"||"w", extern buf, @offsets, @lengths); # . read or write global chunklist # local($done, $i, $l); local($max) = int(($#_-2+1)/2); $done=0; for ($i=0; $i<$max; $i++) { next if ! ($l = $_[2+$i+$max]); if (&myio($_[0], $_[2+$i], $l, $_[1], $done)) { $done += $l; } else { # io error! return 0; } } 1; } ## ## ---------------------- Property Set Handling -------------------------- ## sub pps_name_to_string { # # $string = pps_name_to_string($offset, $pps_name_len, extern $buf) # local($l)=$_[1]-2; local($i); local($tmp)=""; for ($i=0; $i<$l; $i+=2) { $tmp.=substr($_[2], $_[0]+$i, 1); } $tmp; } sub learn_guids { @guids = ("dsi", "si"); $guid_dsi="\0x5DocumentSummaryInformation"; @guid_dsi=( 0xd5cdd502, 0x2e9c, 0x101b, "\0x93\0x97\0x08\0x00\0x2b\0x2c\0xf9\0xae" ); $guid_si="\0x5SummaryInformation"; @guid_si=( 0xf29f85e0, 0x4ff9, 0x1068, "\0xab\0x91\0x08\0x00\0x2b\0x27\0xb3\0xd9" ); } sub get_uuid { local($o)=$_[0]; ( &get_long($o, $_[1]), &get_word($o+4, $_[1]), &get_word($o+6, $_[1]), &get_chars($o+8, 8, $_[1]) ); } # # This section refers to pps_ts2 and pps_ts1, the one ore two timestamps # used for each "Storage" Property Set. It seems, that the second timestamp # gets actualized, when changing the storage. The first stamp is sometimes # used, sometimes unused. # # The stamp is a 64 bit ulong. It counts every second 10 * 10 ^ 6, # starting at 01/01/1601. When the 64 bit int gets evaluated as # two 32 bit integers, the faster running ("least significant long") # can hold just 0x100000000 / 10000000.0 (about 429.5) seconds. So the # slower running ("most significant long") increments every 429.5 seconds. # sub filetime_init { @monsum = ( 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ); $a_minute = 60 * 10000000.0 / (0x10000000 * 16); } sub is_schaltjahr { local($year)=shift; !($year%4) && ($year%100 || !($year%400) ) && 1; } sub filetime_years_to_days { local($year)=shift; int($year-1600) * 365 + int( ($year-1600) / 4 ) - int( ($year-1600) / 100 ) + int( ($year-1600) / 400 ) ; } sub filetime_to_time { local($ds, $dd)=@_; local($day, $month, $year, $hour, $min, $sec); local($i, $m, $d, $dsum, $tmpsec); $dsum = $dd + ($ds / (0x10000000 * 16.0)); $d= int( $dsum/($a_minute*60*24) )+1; $m= $dsum - ($d-1)*$a_minute*60*24; $year = int( $d/365.2425 ) + 1601; $d -= &filetime_years_to_days($year-1); for( $i=11; $i && ($d <= $monsum[$i+&is_schaltjahr($year)*12]); $i--) {} $month = $i+1; $day = $d - $monsum[$i+&is_schaltjahr($year)*12]; $hour = int( $m / ($a_minute*60) ); $min = int( $m/$a_minute - $hour*60 ); $sec = ( ($m/$a_minute - $hour*60 - $min) * 60); ($day, $month, $year, $hour, $min, $sec); } sub time_to_filetime { local($day, $month, $year, $hour, $min, $sec)=@_; local($d, $tss, $tsd); $d = &filetime_years_to_days($year-1) + $monsum[$month-1 + &is_schaltjahr($year)*12] + $day-1; $tsd = (24*60*$d + 60*$hour +$min +$sec/60.0) * $a_minute; $tss = ($tsd-int($tsd)) * 0x10000000 * 16; ( int($tss), int($tsd) ); } ## ## ------------------------- Trash Handling ------------------------------ ## sub make_blockuse_statistic { # # block statistic: # 0 == irregular free (block depot entry != -1) (== undef) # 1 == regular free (block depot entry == -1) # 2 == used for ole system # 3 == used for ole application # return 1 if $usage_known; local($i, @list); # default: all small and big blocks are undef # # regular system data # # ole system blocks for (@bbd_list, @sbd_list, @root_list, @sb_list) { $bb_usage[$_]=2; } # free blocks according to block depots for (@bbd) { $bb_usage[$_]=1 if $bbd[$_]==0xffffffff; } for (@sbd) { $sb_usage[$_]=1 if $sbd[$_]==0xffffffff; } # # OLE application blocks # foreach $file (&get_all_filehandles(0)) { if ($pps_size[$file]>=0x1000) { for (&get_list_from_depot($pps_sb[$file], 1)) { $bb_usage[$_]=3; } } else { for (&get_list_from_depot($pps_sb[$file], 0)) { $sb_usage[$_]=3; } } } $usage_known=1; } sub get_trash_info { # # void get_trash_info(); # # Trash types: # # 0 == all # 1 == unused big blocks # 2 == unused small blocks # 4 == unused file space, according to sizeof pps_size (incl. root_entry) # 8 == unused system space (header, sb_table, bb_table) # return 1 if $trash_known; &make_blockuse_statistic(); local(@o, @l); local(@list); local($size, $m); local($i); local($begin, $len); unused_big_blocks: { $size=0; @list=(); for ($i=0; $i<=$maxblock; $i++) { push(@list, $i) if $bb_usage[$i]<=1; } @trash1_o = &get_iolist(3, 0, 0xfffffff, 0, @list); @trash1_l = splice(@trash1_o, ($#trash1_o+1)/2); $m=$#trash1_o; for ($i=0; $i<=$m; $i++) { $size+=$trash1_l[$i]; } $trashsize{1}=$size; } unused_small_blocks: { $size=0; @list=(); for ($i=0; $i<=$maxsmallblock; $i++) { push(@list, $i) if $sb_usage[$i]<=1; } @trash2_o = &get_iolist(2, 0, 0xfffffff, 0, @list); @trash2_l = splice(@trash2_o, ($#trash2_o+1)/2); $m=$#trash2_o; for ($i=0; $i<=$m; $i++) { $size+=$trash2_l[$i]; } $trashsize{2}=$size; } unused_file_space: { $size=0; # 3.1. normal files foreach $file (&get_all_filehandles(0)) { @o = &get_iolist( $pps_size[$file]>=0x1000 && 1, $pps_size[$file], 0xffffffff, $pps_sb[$file] ); push(@trash3_l, splice(@o, ($#o+1)/2)); push(@trash3_o, @o); } $m=$#trash3_o; for ($i=0; $i<=$m; $i++) { $size+=$trash3_l[$i]; } # 3.2. system file of root_entry (small block file) @list = (); while (($#list+$#sbd+2) % 8) { push(@list, $#list+$#sbd+2); } @o = &get_iolist(2, 0, 0xfffffff, 0, @list); @l = splice(@o, ($#o+1)/2); push(@trash3_o, @o); push(@trash3_l, @l); $m=$#o; for ($i=0; $i<=$m; $i++) { $size+=$l[$i]; } $trashsize{3}=$size; } unused_system_space: { $size=0; # 4.1. header block $begin = 0x4c + $num_of_bbd_blocks*4; $len = $header_size - $begin; push(@trash4_o, $begin); push(@trash4_l, $len); $size+=$len; # 4.2. big block depot @o = &get_iolist(3, ($maxblock+1)*4, 0xffffffff, 0, @bbd_list); @l = splice(@o, ($#o+1)/2); push(@trash4_o, @o); push(@trash4_l, @l); $m=$#o; for ($i=0; $i<=$m; $i++) { $size+=$l[$i]; } # 4.3. small block depot @o = &get_iolist(3, ($maxsmallblock+1)*4, 0xffffffff, 0, @sbd_list); @l = splice(@o, ($#o+1)/2); push(@trash4_o, @o); push(@trash4_l, @l); $m=$#o; for ($i=0; $i<=$m; $i++) { $size+=$l[$i]; } $trashsize{4}=$size; } $trash_known=1; } sub get_trash_size { local($type)=shift; $type = (1|2|4|8) if !$type; &get_trash_info(); local($trashsize)=0; $trashsize += $trashsize{1} if $type & 1; $trashsize += $trashsize{2} if $type & 2; $trashsize += $trashsize{3} if $type & 4; $trashsize += $trashsize{4} if $type & 8; $trashsize; } sub rw_trash { # # "ok"||error = rw_trash("r"||"w", $type, extern $buf [,$offset,$size]) # local($maxarg)=$#_; &get_trash_info(); local($rw, $type) = @_[0..1]; $type = (1|2|4|8) if !$type; local($status, $offset, $size) = &get_default_iosize(&laola_get_trashsize($type), $rw, @_[2..$maxarg]); return $status if $status ne "ok"; local(@o)=(); local(@l)=(); if ($type & 1) { push (@o, @trash1_o); push (@l, @trash1_l); } if ($type & 2) { push (@o, @trash2_o); push (@l, @trash2_l); } if ($type & 4) { push (@o, @trash3_o); push (@l, @trash3_l); } if ($type & 8) { push (@o, @trash4_o); push (@l, @trash4_l); } return "ok" if &rw_iolist( $rw, $_[2], &get_iolist(5, $offset, $size, 0, &aggregate_iolist(1, @o, @l)) ); "Laola: IO Error!"; } ## ## ----------------------------- Debugging ------------------------------- ## # # Some debug information. Switch it on via $optional_do_debug=1 # Information will be shown directly after opening any document. # sub debug_report_pps { local($i)=shift; local($out)=""; local($tmp, $tmp2)=""; return if !$pps_name[$i]; $out = sprintf ("%2x", $i); $out .= $pps_uk0[$i]==1 ? ": " : sprintf ("#%-2x", $pps_uk0[$i]); if (&laola_is_directory($i)) { $out .= "--> "; } elsif (&laola_is_file($i)) { $out .= sprintf ("%-5x ", &laola_get_filesize($i)); } else { $out .= " "; } if ($pps_prev[$i]==0xffffffff) { $out .= " ."; } else { $out .= sprintf ("%3x", $pps_prev[$i]); } if ($pps_next[$i]==0xffffffff) { $out .= " ."; } else { $out .= sprintf ("%3x", $pps_next[$i]); } if ($pps_dir[$i]==0xffffffff) { $out .= " ."; } else { $out .= sprintf ("%3x", $pps_dir[$i]); } if (&laola_is_file_ppset($i)) { $out .= " set"; } else { $out .= " pp "; } ($tmp=$pps_name[$i]) =~ s/[^_a-zA-Z0-9]/ /g; $out .= sprintf (" \"%s\"",$tmp); $out .= " " x (50 - length($out)); if ($pps_ts2d[$i]) { $out .= sprintf (" %d.%d.%d %02d.%02d:%02d", &filetime_to_time($pps_ts2s[$i], $pps_ts2d[$i]) ); } print "$out\n"; } sub report_blockuse_statistic { return 1; print "--- LAOLA internal, begin block statistic ---\n\n"; &make_blockuse_statistic(); local($i, $j, $m); local(@o, @l); print "Big blocks:\n"; for ($i=0; $i<4; $i++) { @o=(); @l=(); $m=$#bb_usage; for ($j=0; $j<=$m; $j++) { next if $bb_usage[$j]!=$i; push(@o, $j); push(@l, 1); } &report_blockuse_list($i, &aggregate_iolist(1, @o, @l)); } print "Small blocks:\n"; for ($i=0; $i<4; $i++) { @o=(); @l=(); $m=$#sb_usage; for ($j=0; $j<=$m; $j++) { next if $sb_usage[$j]!=$i; push(@o, $j); push(@l, 1); } &report_blockuse_list($i, &aggregate_iolist(1, @o, @l)); } print "\n--- LAOLA internal, end block statistic ---\n\n"; } sub report_blockuse_list { local($type)=shift; return if !@_; local(%info)=(0, "Trash", 1, "Free", 2, "System", 3, "Application"); local($max)=($#_+1)/2; local($i); local($o, $l); print "Type $type {$info{$type}} = ("; for ($i=0; $i<$max; $i++) { $o=$_[$i]; $l=$_[$max+$i]; if ($l==1) { printf (" %x ", $o); } else { printf (" %x-%x ", $o, $o+$l-1); } } print ")\n"; } sub report_trash_statistic { return; &get_trash_info(); print "Trash statistic.\n"; print "Free big block chunks: (\n"; &report_trash_list($trashsize{1}, @trash1_o, @trash1_l); print "\nFree small block chunks: (\n"; &report_trash_list($trashsize{2}, @trash2_o, @trash2_l); print "\nUnused file space: (\n"; &report_trash_list($trashsize{3}, @trash3_o, @trash3_l); print "\nUnused system space: (\n"; &report_trash_list($trashsize{4}, @trash4_o, @trash4_l); print "\nSummary: (\n"; &report_trash_list( $trashsize{1}+$trashsize{2}+$trashsize{3}+$trashsize{4}, &aggregate_iolist( 1, @trash1_o, @trash2_o, @trash3_o, @trash4_o, @trash1_l, @trash2_l, @trash3_l, @trash4_l ) ); } sub report_trash_list { local($size)=shift; local(@o)=@_; local(@l)=splice(@o, ($#o+1)/2); local($i, $m); printf (" %d elements, size=%x\n", $#o+1, $size); $m=$#o; for ($i=0; $i<=$m; $i++) { printf (" offset %5x (len %x)\n", $o[$i], $l[$i]); } print ")\n"; } sub print_iolist { local(@o)=@_; local(@l)=splice(@o, ($#o+1)/2); local($i); $m=$#o; for ($i=0; $i<=$m; $i++) { printf(" o=%6x (%x)\n", $o[$i], $l[$i]); } } "Atomkraft? Nein, danke!" magicrescue-1.1.8/tools/mp3extract.pl0000755000175000017500000000304411030775345015704 0ustar jbjjbj#!/usr/bin/env perl use strict; # This script tries to decode the first part of the mp3 with mpg123, and if # that fails it will truncate the mp3 at that faliure point. # I could also use mp3_check, perhaps it is more correct. However, users are # not likely to have it installed. my $max_bytes = 40*1024*1024; my $min_bytes = 1024; my $min_pcm = 11000; my $ofile = $ARGV[0] or die; if (-f "$ofile.pcm" and (-s _) < $min_pcm) { unlink $ofile; exit 1; } copy_out_file(); my $size = find_size(); if ($size) { eval { truncate($ofile, $size + 128) }; if ($@) { print STDERR "\n\n\ntruncate() failed: $!\nKilling output\n\n\n\n"; unlink($ofile); } } else { print STDERR "Invalid mp3 file\n"; unlink($ofile); } sub copy_out_file { my ($read_now, $read_total) = (0, 0); open my $fd, ">>", $ofile or die; while ($read_total < $max_bytes and $read_now = read STDIN, my $buf, 10240) { $read_total += $read_now; print {$fd} $buf or die; } close $fd; } sub find_size { my $pid = open my $mpg123, "-|" or mpg123(); while (<$mpg123>) { if (/at offset (0x[a-zA-Z0-9]+)\.$/) { kill TERM => $pid; #TODO: mpg123 could already be dead wait; close $mpg123; my $offset = oct $1; return ($offset < $min_bytes or $offset > $max_bytes) ? 0 : $offset; } } return (-s $ofile < $max_bytes ? (-s _) - 128 : 0); } sub mpg123 { open STDERR, ">&STDOUT" or die; open STDOUT, ">/dev/null" or die; exec qw(mpg123 -s -c), $ofile; die "Executing mpg123: $!\n"; } magicrescue-1.1.8/tools/ole_rename.pl0000755000175000017500000000471411030775345015725 0ustar jbjjbj#!/usr/bin/env perl use strict; # Attempts to guess the file type of an OLE container file and rename it # accordingly. # Depends on an ancient perl4 script from 1998 named laola.pl to parse the # file. It would be better to use the OLE::Storage module from CPAN, but that # module is broken at the time of writing. # This is not a hash, because it is important that the extensions are tried in # order of importance. my @extensions = ( WordDocument => "doc", PowerPointDocument => "ppt", Workbook => "xls", # New Excel versions StarWriterDocument => "sdw", StarCalcDocument => "sdc", StarDrawDocument3 => "sdd", # Could also be .sda Book => "xls", # Excel 5.0 Quill => "pub", # Microsoft Publisher PP40 => "pot", # PowerPoint Template, or...? WPG20 => "wordperfect_unknown", # What is this? PerfectOffice_MAIN => "wb3", # Are all such files from Quattro Pro? Some # may be .shw presentations. EquationNative => "equation", StarBaseDocument => "starbase", WorkspaceState => "opt", # MS Visual Studio SentenceExceptList => "staroffice_dictionary", StarBASIC => "starbasic", SIG1 => "staroffice_unknown", # What is this? ); my %extensions = @extensions; # for quick lookup # When invoked by magicrescue, laola.pl should be in the PATH push @INC, grep /tools/, split /:/, $ENV{PATH}; require 'laola.pl'; my $file = $ARGV[0]; unless (@ARGV and -f $file) { die "Usage: ole_rename.pl FILENAME\n"; } # LAOLA does not do much sanity checking, a corrupted file can send it into # a memory-exhausting loop. Using alarm here is basically a hack for systems # where magicrescue can't do setrlimit to set max memory usage. $SIG{ALRM} = sub { die "Timed out" }; alarm 10; my $extension = ""; my $status = laola_open_document($file); $status eq "ok" or die "laola_open_document failed: $status\n"; foreach my $pps (laola_get_dirhandles(0)) { my $name = laola_pps_get_name($pps); $name =~ s/[^-\w]//g; next unless $extensions{$name}; for (my $i = 0; $i < @extensions; $i += 2) { if ($name eq $extensions[$i]) { $extension = $extensions[$i+1]; last; # we keep looking even though we have found the format, because # the real format always seems to be closest to the end. } } } laola_close_document(); alarm 0; if ($extension) { print "RENAME $extension"; } magicrescue-1.1.8/tools/oleextract.pl0000755000175000017500000000345011030775345015765 0ustar jbjjbj#!/usr/bin/env perl use strict; # # This script loosely follows the LAOLA file system described at # http://user.cs.tu-berlin.de/~schwartz/pmh/guide.html. It only looks at the # "big block depots", as they seem to be sufficient for finding the length of # the file. use Fcntl qw(:seek); my $buf; # We use sysread/sysseek here to work around problems on cygwin sysread STDIN, $buf, 512; if (substr($buf, 0, 8) ne "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1") { die "Not an MS OLE file\n"; } my $num_of_bbd_blocks = unpack("V", substr($buf, 0x2c)); if ($num_of_bbd_blocks == 0 or $num_of_bbd_blocks > 320) { # Max 320 bbd means the file can only be 128*512*320 = 20MB die "Corrupted file, too many big block depots\n"; } my $max_block = 128*$num_of_bbd_blocks; my @bbds = unpack("V$num_of_bbd_blocks", substr($buf, 0x4c)); my $block_count = 0; my $prevblock = -1; my $block_index = 0; foreach my $block (@bbds) { if ($block > $max_block or $block == $prevblock) { die "Corrupted file, bbd block number out of bounds\n"; } sysseek STDIN, 512*($block - $prevblock - 1), SEEK_CUR or die "sysseek failed: $!\n"; 512 == sysread STDIN, $buf, 512 or die "read failed: $!\n"; my @blockmap = unpack("V128", $buf); for (my $i = 0; $i < 128; $i++) { if ($blockmap[$i] != 0xFFffFFff) { $block_count = $block_index * 128 + $i + 2; } if ($blockmap[$i] < 0xFFffFFfd and $blockmap[$i] > $max_block) { die "Corrupted file, block number too high\n"; } } $prevblock = $block; $block_index++; } if ($block_count > 0) { sysseek STDIN, -512*($bbds[@bbds - 1] + 2), SEEK_CUR # seek to first block or die "seek failed: $!\n"; for (1 .. $block_count) { 512 == sysread STDIN, $buf, 512 or die "read failed: $!\n"; print $buf or die "write failed: $!\n"; } } magicrescue-1.1.8/tools/pngextract.pl0000755000175000017500000000147611030775345016000 0ustar jbjjbj#!/usr/bin/env perl use strict; my $max_file_len = 10_000_000; my ($buf, $len, $type); read(STDIN, $buf, 8) == 8 or die "header read error: $!\n"; $buf eq "\211PNG\r\n\032\n" or die "bad magic\n"; print $buf; my $written = 8; while (read(STDIN, $buf, 4) == 4) { $len = unpack("N", $buf) + 8; if ($len > $max_file_len) { die "Invalid chunk length $len\n"; } print $buf; if (read(STDIN, $buf, $len) != $len) { die "read error: $!\n"; } $written += $len + 4; if ($written > $max_file_len) { print STDERR "File too long, aborting\n"; exit 1; } $type = unpack("a4", $buf); if ($type !~ /^[a-zA-Z]{4}$/) { print STDERR "Invalid type code, aborting\n"; exit 1; } print $buf; if ($type eq 'IEND') { print STDERR "Successfully extracted png file\n"; exit 0; } } magicrescue-1.1.8/tools/safecat.c0000644000175000017500000000610611030775345015026 0ustar jbjjbj/* * Magic Rescue * Copyright (C) 2004 Jonas Jensen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #include #include #include "util.h" static void usage(void) { fprintf(stderr, "Usage: safecat [-dut] FILENAME | COMMAND\n" "Copies verbatim from standard input to standard output. Standard output is\n" "expected to be piped into COMMAND to produce FILENAME. This file will be\n" "checked for size, and if that size falls below certain limits the program\n" "will exit.\n" "\n" " -d Maximum difference between bytes in and out of the command\n" " -t Maximum bytes the command is allowed to produce\n" " -u Maximum bytes to pipe into a command that has stopped producing output\n" "\n" " The values for the -dut options can be postfixed with G, M or K.\n" ); } int main(int argc, char **argv) { long max_diff = 5*1024*1024; long max_unchanged = 1*1024*1024; long max_total = 50*1024*1024; char *ofile; const size_t bufsize = 102400; char *buf; int c; while ((c = getopt(argc, argv, "d:u:t:")) >= 0) { switch (c) { case 'd': max_diff = atol_calc(optarg); break; case 'u': max_unchanged = atol_calc(optarg); break; case 't': max_total = atol_calc(optarg); break; default: fprintf(stderr, "Error parsing options.\n"); usage(); return 1; } } if (argc - optind != 1 || strcmp(argv[optind], "--help") == 0) { usage(); return 1; } ofile = argv[optind]; buf = malloc(bufsize); { off_t total_in = 0, total_out = 0, in_at_last_out = 0; struct stat st; ssize_t read_count, write_count; const char *buf_offset; do { read_count = read(0, buf, bufsize); if (read_count <= 0) break; buf_offset = buf; do { write_count = write(1, buf_offset, read_count); if (write_count <= 0) goto end_loop; read_count -= write_count; buf_offset += write_count; total_in += write_count; } while (read_count > 0); if (stat(ofile, &st) == 0) { if (total_out != st.st_size) { total_out = st.st_size; in_at_last_out = total_in; } } else { total_out = 0; } } while ( max_total > total_out && max_diff > total_in - total_out && max_unchanged > total_in - in_at_last_out); } end_loop: free(buf); return 0; } magicrescue-1.1.8/tools/script_rename.pl0000755000175000017500000000043411030775345016445 0ustar jbjjbj#!/usr/bin/env perl use strict; my $file = shift or die "Usage: script_rename.pl FILE\n"; open SCRIPT, "<", $file or die "Opening $file: $!\n"; my $line1 =