Lazza / RecuperaBit

A tool for forensic file system reconstruction.
GNU General Public License v3.0
534 stars 75 forks source link

recover breaks when custom properties appear as nodes #123

Open senpro-ingwersenk opened 7 months ago

senpro-ingwersenk commented 7 months ago

Hello!

One of our customers has experienced a massive data crash due to a blackout. While restoring (restore 2 5), the following error appears:

INFO:root:Restoring #1106279:OECustomProperty Root/Restore_Test/RESTORED-alt/2012_Jan/Docs_CT.eml:OECustomProperty
ERROR:root:IOError when trying to create /mnt/transfer/Partition2/Root/Restore_Test/RESTORED-alt/2012_Jan/Docs_CT.eml:OECustomProperty
Traceback (most recent call last):
  File "/root/RecuperaBit/main.py", line 374, in <module>
    main()
  File "/root/RecuperaBit/main.py", line 371, in main
    interpret(cmd, arguments, parts, shorthands, args.outputdir)
  File "/root/RecuperaBit/main.py", line 181, in interpret
    logic.recursive_restore(myfile, part, partition_dir)
  File "/root/RecuperaBit/recuperabit/logic.py", line 267, in recursive_restore
    recursive_restore(child, part, outputdir, make_dirs=False)
  File "/root/RecuperaBit/recuperabit/logic.py", line 267, in recursive_restore
    recursive_restore(child, part, outputdir, make_dirs=False)
  File "/root/RecuperaBit/recuperabit/logic.py", line 267, in recursive_restore
    recursive_restore(child, part, outputdir, make_dirs=False)
  [Previous line repeated 1 more time]
  File "/root/RecuperaBit/recuperabit/logic.py", line 262, in recursive_restore
    os.utime(restore_path, (atime, mtime))
FileNotFoundError: [Errno 2] No such file or directory

Those are custom props. Can I skip them, somehow? This is from a HyperV VHDX mounted on a Debian VM.

Thanks!

senpro-ingwersenk commented 7 months ago

I have yet to get into the shell (it is still assigning and linking entries) but this could help.

Some files don't have any contents BUT attributes, so writing attributes to a file that doesn't exist, won't work. So, this monkey-patch works around that.

Long term, it should be considered if those empty files are to be touched too, causing them to have empty contents instead (if not file_exists(): touch()). However, this could end up causing an absolute ton of empty files, so this should probably be a switch instead for the user to decide (i.e. --touch-ghost-files).

diff --git a/recuperabit/logic.py b/recuperabit/logic.py
index a0b2411..776e1d6 100644
--- a/recuperabit/logic.py
+++ b/recuperabit/logic.py
@@ -259,7 +259,10 @@ def recursive_restore(node, part, outputdir, make_dirs=True):
     if mtime is not None:
         atime = time.mktime(atime.astimezone().timetuple())
         mtime = time.mktime(mtime.astimezone().timetuple())
-        os.utime(restore_path, (atime, mtime))
+        try:
+          os.utime(restore_path, (atime, mtime))
+        except:
+          logging.error(u"Could not set timestamps (utime, atime) on %s", restore_path)

     if is_directory:
         for child in node.children: