You are not logged in.

#1 2025-05-29 23:05:41

bloemelau
Member
Registered: 2016-02-27
Posts: 4

Recovery of long lost btrfs partition

This story is from ~5-6 years ago. I gave my backup drive to my girlfriend with Windows. I installed some btrfs compat layer so she could actually use it. Yeah, bad idea. The drive was corrupted... I remember I frantically tried to recover the disk as quickly as possible, throwing all kind of btrfs commands at it, without actually understanding what they do... I imagine I might have broken it even more...

So back to today, she's my wife now. And I still kind of want to recover that disk... Suffice to say I don't remember what I did back then.
I took an image of the disk, and tried my hand again, but yeah I don't get that far with the actual tools...

So I tried:

$ fsck.btrfs /mnt/seagate/corrupted-hardrive-copy/partition1
If you wish to check the consistency of a BTRFS filesystem or repair a damaged filesystem, see btrfs(8) subcommand 'check'.

$ btrfs check /mnt/seagate/corrupted-hardrive-copy/partition1
Opening filesystem to check...
No valid Btrfs found on /mnt/seagate/corrupted-hardrive-copy/partition1
ERROR: cannot open file system

$ btrfs check -s 2 --force --read only /mnt/seagate/corrupted-hardrive-copy/partition1
using SB copy 2, bytenr 274877906944                       
Opening filesystem to check...                             
No valid Btrfs found on /mnt/seagate/corrupted-hardrive-copy/partition1                                              
ERROR: cannot open file system

So for some reason that immediately blocks. I knew from my previous attempt from years before that using binwalk I got some files out of it. But by now, I kind of forgot what files are even on this disk so I tried to enumerate the filenames on it with this project, to evaluate if it's even still worth it: https://github.com/danobi/btrfs-walk.git

(Apply my patch for getting rid of the unaligned dereference errors in current rust:

diff --git a/src/main.rs b/src/main.rs
index a47b1d0..7fc634c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,7 +13,7 @@ use chunk_tree::{ChunkTreeCache, ChunkTreeKey, ChunkTreeValue};
 mod tree;

 /// Physical address of the first superblock
-const BTRFS_SUPERBLOCK_OFFSET: u64 = 0x10_000;
+const BTRFS_SUPERBLOCK_OFFSET: u64 =  274877906944; //0x10_000;
 const BTRFS_SUPERBLOCK_MAGIC: [u8; 8] = *b"_BHRfS_M";

 #[derive(Debug, StructOpt)]
@@ -169,12 +169,12 @@ fn read_chunk_tree(
     superblock: &BtrfsSuperblock,
 ) -> Result<()> {
     let header = tree::parse_btrfs_header(root).expect("failed to parse chunk root header");
-    unsafe {
-        println!(
-            "chunk tree node level={}, bytenr={}, nritems={}",
-            header.level, header.bytenr, header.nritems
-        );
-    }
+    println!(
+        "chunk tree node level={}, bytenr={}, nritems={}",
+        header.level,
+        { let a = header.bytenr; a },
+        { let a = header.nritems; a }
+    );

     // Level 0 is leaf node, !0 is internal node
     if header.level == 0 {
@@ -226,19 +226,22 @@ fn read_fs_tree_root(
 ) -> Result<Vec<u8>> {
     let header =
         tree::parse_btrfs_header(root_tree_root).expect("failed to parse root tree root header");
-    unsafe {
-        println!(
-            "root tree root level={}, bytenr={}, nritems={}",
-            header.level, header.bytenr, header.nritems
-        );
-    }
+    println!(
+        "root tree root level={}, bytenr={}, nritems={}",
+        header.level,
+        { let a = header.bytenr; a },
+        { let a = header.nritems; a }
+    );
+

     if header.level != 0 {
         bail!("Root tree root is not a leaf node");
     }

     let items = tree::parse_btrfs_leaf(root_tree_root)?;
+    dbg!(items.len());
     for item in items.iter().rev() {
+        dbg!(item.key.objectid);
         if item.key.objectid != BTRFS_FS_TREE_OBJECTID || item.key.ty != BTRFS_ROOT_ITEM_KEY {
             continue;
         }
@@ -256,12 +259,11 @@ fn read_fs_tree_root(
         let mut node = vec![0; superblock.node_size as usize];
         file.read_exact_at(&mut node, physical)?;

-        unsafe {
-            println!(
-                "fs tree root at logical offset={}, physical offset={}, size={}",
-                root_item.bytenr, physical, superblock.node_size,
-            );
-        }
+        println!(
+            "fs tree root at logical offset={}, physical offset={}, size={}",
+            { let a = root_item.bytenr; a },
+            physical,
+            { let a = superblock.node_size; a });

         return Ok(node);
     }
@@ -334,12 +336,12 @@ fn walk_fs_tree(
     cache: &ChunkTreeCache,
 ) -> Result<()> {
     let header = tree::parse_btrfs_header(node)?;
-    unsafe {
-        println!(
-            "fs tree node level={}, bytenr={}, nritems={}",
-            header.level, header.bytenr, header.nritems
-        );
-    }
+    println!(
+        "fs tree node level={}, bytenr={}, nritems={}",
+        header.level,
+        { let a = header.bytenr; a },
+        { let a = header.nritems; a }
+    );

     // Leaf node
     if header.level == 0 {
@@ -381,7 +383,9 @@ fn walk_fs_tree(
                         .ok_or_else(|| {
                             anyhow!("Failed to find inode_ref for inode={}", current_inode_nr)
                         })?;
-                unsafe { assert_eq!(current_key.objectid, current_inode_nr) };
+                assert_eq!(
+                    { let a = current_key.objectid; a },
+                    current_inode_nr);

                 // `current_key.offset` is parent inode # of `current_inode`
                 if current_key.offset == current_inode_nr {

)

It panics with the default superblock, but with superblock 2 it actually does seem to find stuff. The magic is there etc, etc. It prints:

 
warning: 2 stripes detected but only processing 1
chunk tree root at logical offset=22052864, physical offset=22052864, size=8388608
chunk tree node level=1, bytenr=22052864, nritems=5
chunk tree node level=0, bytenr=22069248, nritems=154
chunk tree node level=0, bytenr=22134784, nritems=155
chunk tree node level=0, bytenr=22151168, nritems=155
chunk tree node level=0, bytenr=22167552, nritems=154
chunk tree node level=0, bytenr=22085632, nritems=80
root tree root at logical offset=47972352, physical offset=56360960, size=1073741824
root tree root level=0, bytenr=262144, nritems=1
[src/main.rs:242:5] items.len() = 1
[src/main.rs:244:9] item.key.objectid = 0

thread 'main' panicked at src/main.rs:452:10:
failed to read fs tree root: Failed to find root tree item for fs tree root
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Still an error, but it gets to parse a lot of stuff. So to me, it seems there's still a lot there, and I'm just not issueing the right btrfs commands?

Any input on what to try would be appreciated!

Offline

#2 2025-05-29 23:50:22

topcat01
Member
Registered: 2019-09-17
Posts: 273

Re: Recovery of long lost btrfs partition

The #btrfs irc channel on libera is very active. Can you ask there?

Offline

Board footer

Powered by FluxBB