Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6 GB metadata.db but no files? :-( #12

Open
hopeseekr opened this issue Nov 9, 2021 · 13 comments
Open

6 GB metadata.db but no files? :-( #12

hopeseekr opened this issue Nov 9, 2021 · 13 comments

Comments

@hopeseekr
Copy link

root@rescue /mnt/omg # ls -lh
total 6.2G
-r--r--r-- 1 root root 6.2G Nov  9 14:09 metadata
drwxr-xr-x 1 root root  224 Sep 13 16:49 rescue
root@rescue /mnt/omg # ls rescue/
root@rescue /mnt/omg # 

Am I screwed? I'm trying to recover file names. Photorec has recovered 150 GB of data but it has no file names...

@cblichmann
Copy link
Owner

I have not touched this repo in quite a while.
Do you happen to know the kernel version that created the filesystem or that was last used to access it? That'd help me to check whether there were and on-disk changes that the tool doesn't understand.

@hopeseekr
Copy link
Author

Linux 5.4 something... the current LTS in Arch...

@giacomoferretti
Copy link

Happened to me too, but I don't remember which kernel version created that BTRFS partition... ☹️

@giacomoferretti
Copy link

Ok, it doesn't work even when I create a new BTRFS partition.
BTW, this repo is still helpful because I can retrieve some files using the inline data in the metadata.db using an external program.

truncate -s 1G tmp.img
mkfs.btrfs -f tmp.img
mkdir tmp_mount
mount tmp.img tmp_mount
mkdir tmp_mount/folder
touch tmp_mount/folder/file
umount tmp_mount
btrfscue identify tmp.img
btrfscue recon --id UUID_FSID --metadata metadata.db tmp.img
btrfscue --metadata metadata.db mount tmp.img tmp_mount

@dfcamara
Copy link

I'm having the same isssue. The filesystem was last acessed using Fedora Linux 37, kernel 6.2.14-200.fc37.x86_64. Now running btrfscue on Fedora Linux 38, kernel 6.2.14-300.fc38.x86_64.

@dfcamara
Copy link

Ok, it doesn't work even when I create a new BTRFS partition. BTW, this repo is still helpful because I can retrieve some files using the inline data in the metadata.db using an external program.

truncate -s 1G tmp.img
mkfs.btrfs -f tmp.img
mkdir tmp_mount
mount tmp.img tmp_mount
mkdir tmp_mount/folder
touch tmp_mount/folder/file
umount tmp_mount
btrfscue identify tmp.img
btrfscue recon --id UUID_FSID --metadata metadata.db tmp.img
btrfscue --metadata metadata.db mount tmp.img tmp_mount

What external program did you use to access metadata.db contents? What type of file is metadata.db?

@giacomoferretti
Copy link

giacomoferretti commented May 27, 2023

metadata.db is a bbolt database.

I didn't recover much from it, only small files.
At least I could export the list of files (lost files), and with photorec recovered the majority of it.

The external program I was referring to is a script that I made. Let me recover it and I will share it here.

@dfcamara
Copy link

dfcamara commented May 28, 2023

Thanks. During installation of Fedora Linux 38 I inadvertently deleted the format of my second disk (NVMe). This disk contains one partition formatted with Btrfs using all the disk space without a partition table. I was able to recover my disk (all file data and metadata) using btrfs-select-super utility to overwrite the primary superblock with a backup copy.
sudo btrfs-select-super -s 2 /dev/nvme1n1

@Dikakus
Copy link

Dikakus commented Jul 14, 2023

Hi. Same issue here:

Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023 aarch64 GNU/Linux

Using mount option with a 4.9GB metadata file I only can see a metadata file and a void "rescue" directory.

@yunginnanet
Copy link

metadata.db is a bbolt database.

I didn't recover much from it, only small files. At least I could export the list of files (lost files), and with photorec recovered the majority of it.

The external program I was referring to is a script that I made. Let me recover it and I will share it here.

so...

@giacomoferretti
Copy link

I don't remember which one I used, but it's probably this:

package main

import (
	"encoding/gob"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"strings"

	bolt "go.etcd.io/bbolt"

	"github.com/giacomoferretti/bbolt-dump/internal/btrfs"
	"github.com/giacomoferretti/bbolt-dump/internal/btrfscue"
)

type Inode struct {
	Generation  uint64
	Inode       uint64
	ParentInode uint64
	Name        string
	IsFile      bool
	FullPath    string
	Size        uint64
}

var inodeData []Inode

func createDir(inode Inode) {
	f := path.Join(os.Args[1], inode.FullPath)
	if !inode.IsFile {
		os.MkdirAll(f, os.ModePerm)
	}
}

func createFile(inode Inode, data []byte) {
	f := path.Join(os.Args[1], inode.FullPath)
	log.Printf("[GEN:%v] Writing %v bytes to %v...", inode.Generation, inode.Size, f)
	if inode.IsFile {
		if err := os.WriteFile(f, data, 0644); err != nil {
			log.Fatal(err)
		}
	}
}

func readFileFromDisk(offset, length uint64) []byte {
	fmt.Fprintf(os.Stderr, "Reading offset %v and length %v\n", offset, length)
	f, err := os.Open(os.Args[4])
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	_, err = f.Seek(int64(offset), io.SeekStart)
	if err != nil {
		log.Fatal(err)
	}

	ret := make([]byte, length)
	_, err = f.Read(ret)
	if err != nil {
		log.Fatal(err)
	}

	return ret
}

func findInode(inode uint64) (Inode, error) {
	for _, v := range inodeData {
		if v.Inode == inode {
			return v, nil
		}
	}

	return Inode{}, errors.New("inode not found")
}

func dbProcessEntry(key, value []byte) error {
	btrfsKeyV2 := btrfscue.BtrfscueParseDbKey(key)
	btrfsKey, data := btrfscue.BtrfscueParseDbValue(value)

	// Print data
	dataHex := hex.EncodeToString(data)
	dataSanitized := string(data)
	dataSanitized = strings.Replace(dataSanitized, "\n", "\\x0a", -1)
	dataSanitized = strings.Replace(dataSanitized, "\b", "\\x0b", -1)
	dataPrint := ""
	if btrfsKey.Size > 0 {
		dataPrint = fmt.Sprintf(" %s, %s", dataSanitized, dataHex)
	}

	if btrfsKeyV2.Type == 108 {
		fmt.Printf("[G:%v O:%v ID:%v OF1:%v T:%v OF2:%v S:%v]%s\n", btrfsKeyV2.Generation, btrfsKeyV2.Owner, btrfsKeyV2.ObjectID, btrfsKeyV2.Offset, btrfsKeyV2.Type, btrfsKey.Offset, btrfsKey.Size, dataPrint)

		// BTRFS_EXTENT_DATA_KEY
		inode := btrfsKeyV2.ObjectID
		offset := btrfsKeyV2.Offset
		file_extent_item := btrfs.ParseFileExtentItem(data)

		targetInode, err := findInode(inode)
		if err != nil {
			log.Fatal(err)
		}

		// Inline data
		if file_extent_item.Type == 0 {
			// Check length
			if file_extent_item.RamBytes != uint64(len(data[21:])) {
				log.Fatal("WRONG LENGTH ON EXTENT_DATA")
			}

			if btrfsKeyV2.Generation == targetInode.Generation {
				createFile(targetInode, data[21:])
			}
		} else if file_extent_item.Type == 1 {
			file_extent_item_disk := btrfs.ParseFileExtentItemDisk(data[21:])
			fmt.Printf(" └─ FILE_EXTENT_ITEM = %v\n", file_extent_item_disk)

			if btrfsKeyV2.Generation == targetInode.Generation {
				createFile(targetInode, readFileFromDisk(file_extent_item_disk.DiskBytenr, targetInode.Size))
			}
		}
		fmt.Printf(" └─ BTRFS_EXTENT_DATA_KEY - INODE:%v OFFSET:%v = %v\n", inode, offset, file_extent_item)
	}

	return nil
}

func main() {
	// Check arguments
	if len(os.Args) != 5 {
		fmt.Fprintf(os.Stderr, "Usage: %v <output_folder> <output.bin> <metadata.db> <disk.img>\n", os.Args[0])
		os.Exit(1)
	}

	// GOB DECODE
	dataFile, err := os.Open(os.Args[2])
	if err != nil {
		log.Fatal(err)
	}
	dataDecoder := gob.NewDecoder(dataFile)
	err = dataDecoder.Decode(&inodeData)
	if err != nil {
		log.Fatal(err)
	}
	dataFile.Close()

	// Create folder
	for _, v := range inodeData {
		if !v.IsFile {
			createDir(v)
		}
	}

	// Open bbolt database
	db, err := bolt.Open(os.Args[3], 0600, &bolt.Options{ReadOnly: true})
	if err != nil {
		log.Fatalln(err)
	}
	defer db.Close()

	// Process all entries on "index"
	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("index"))

		b.ForEach(dbProcessEntry)
		return nil
	})
	if err != nil {
		log.Fatalln(err)
	}
}

@giacomoferretti
Copy link

Check https://github.com/giacomoferretti/btrfscue-metadata-extract for a full source.

@thomas725
Copy link

thomas725 commented Dec 12, 2023

same issue here, had hoped to gain access to my data using this project but btrfscue ls doesn't produce any output, and mount only shows me

  • my ~20gb metadata.db file (without the .db extension inside the mount directory)
  • and an empty "rescue" folder.

UPDATE: I found a much simpler and nicer way to recover my data from my broken btrfs disk:
mount -t btrfs -o ro,rescue=all /dev/disk /mnt/
by reading through the mount options here: https://btrfs.readthedocs.io/en/latest/Administration.html#mount-options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants