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

Add options to recover data from a broken RDB disk #206

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion amitools/fs/block/Block.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class Block:
ST_USERDIR = 2
ST_FILE = -3 & 0xFFFFFFFF

ignore_errors = False

def __init__(self, blkdev, blk_num, is_type=0, is_sub_type=0, chk_loc=5):
self.valid = False
self.blkdev = blkdev
Expand All @@ -36,6 +38,7 @@ def __init__(self, blkdev, blk_num, is_type=0, is_sub_type=0, chk_loc=5):
self.is_type = is_type
self.is_sub_type = is_sub_type
self.chk_loc = chk_loc
self.errors = []

def __str__(self):
return "%s:@%d" % (self.__class__.__name__, self.blk_num)
Expand Down Expand Up @@ -70,7 +73,7 @@ def read(self):
self._read_data()
self._get_types()
self._get_chksum()
self.valid = self.valid_types and self.valid_chksum
self.valid = self.valid_types and (self.valid_chksum or Block.ignore_errors)

def write(self):
if self.data == None:
Expand Down Expand Up @@ -129,9 +132,11 @@ def _get_types(self):
self.valid_types = True
if self.is_type != 0:
if self.type != self.is_type:
self.errors.append("Block #%d type mismatch (is %d but expected %d)" % (self.blk_num, self.type, self.is_type))
self.valid_types = False
if self.is_sub_type != 0:
if self.sub_type != self.is_sub_type:
self.errors.append("Block #%d subtype mismatch (is %d but expected %d)" % (self.blk_num, self.sub_type, self.is_sub_type))
self.valid_types = False

def _put_types(self):
Expand All @@ -144,6 +149,8 @@ def _get_chksum(self):
self.got_chksum = self._get_long(self.chk_loc)
self.calc_chksum = self._calc_chksum()
self.valid_chksum = self.got_chksum == self.calc_chksum
if not self.valid_chksum:
self.errors.append("Block #%d checksum is invalid" % (self.blk_num))

def _put_chksum(self):
self.calc_chksum = self._calc_chksum()
Expand Down
7 changes: 7 additions & 0 deletions amitools/fs/rdb/FileSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from amitools.fs.block.rdb.LoadSegBlock import *
from amitools.util.HexDump import *
import amitools.fs.DosType as DosType
import logging


class FileSystem:
Expand Down Expand Up @@ -153,6 +154,12 @@ def get_desc(self):
"dev_node": dev_node,
}

def log_errors(self):
for i in self.lsegs:
for e in i.errors:
logging.warning(e)


# ----- edit -----

def clear_flags(self):
Expand Down
10 changes: 8 additions & 2 deletions amitools/fs/rdb/Partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
from amitools.fs.blkdev.PartBlockDevice import PartBlockDevice
import amitools.util.ByteSize as ByteSize
import amitools.fs.DosType as DosType
import logging


class Partition:
def __init__(self, blkdev, blk_num, num, cyl_blks, rdisk):
def __init__(self, blkdev, blk_num, num, cyl_blks, block_bytes, rdisk=None):
self.blkdev = blkdev
self.blk_num = blk_num
self.num = num
self.cyl_blks = cyl_blks
self.block_bytes = block_bytes
self.rdisk = rdisk
self.block_bytes = rdisk.block_bytes
self.part_blk = None

def get_next_partition_blk(self):
Expand Down Expand Up @@ -156,6 +157,11 @@ def get_desc(self):
"automount": automount,
}

def log_errors(self):
for e in self.part_blk.errors:
logging.warning(e)


# ----- Import/Export -----

def export_data(self, file_name):
Expand Down
25 changes: 15 additions & 10 deletions amitools/fs/rdb/RDisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,36 @@ def open(self):
self.parts = []
num = 0
while part_blk != Block.no_blk:
p = Partition(self.rawblk, part_blk, num, self.rdb.log_drv.cyl_blks, self)
num += 1
if not p.read():
p = Partition(self.rawblk, part_blk, num, self.rdb.log_drv.cyl_blks, self.block_bytes, self)
ok = p.read()
p.log_errors()
if not ok:
self.valid = False
return False
self.parts.append(p)
# store used block
self.used_blks.append(p.get_blk_num())
# next partition
part_blk = p.get_next_partition_blk()
num += 1

# read filesystems
fs_blk = self.rdb.fs_list
self.fs = []
num = 0
while fs_blk != PartitionBlock.no_blk:
fs = FileSystem(self.rawblk, fs_blk, num)
num += 1
if not fs.read():
ok = fs.read()
fs.log_errors()
if not ok:
self.valid = False
return False
self.fs.append(fs)
# store used blocks
self.used_blks += fs.get_blk_nums()
# next partition
fs_blk = fs.get_next_fs_blk()
num += 1

# TODO: add bad block blocks

Expand Down Expand Up @@ -286,7 +290,7 @@ def find_filesystem_by_string(self, s):
# ----- edit -----

def create(
self, disk_geo, rdb_cyls=1, hi_rdb_blk=0, disk_names=None, ctrl_names=None
self, disk_geo, rdb_cyls=1, hi_rdb_blk=0, disk_names=None, ctrl_names=None, blk_num=0
):
cyls = disk_geo.cyls
heads = disk_geo.heads
Expand Down Expand Up @@ -336,16 +340,17 @@ def create(
ctrl_revision,
)
self.block_bytes = self.rawblk.block_bytes
self.rdb = RDBlock(self.rawblk)
self.rdb = RDBlock(self.rawblk, blk_num)
self.rdb.create(
phy_drv, log_drv, drv_id, block_size=self.block_bytes, flags=flags
)
self.rdb.write()

self.used_blks = [self.rdb.blk_num]
self.max_blks = self.rdb.log_drv.rdb_blk_hi + 1
self.valid = True

def write():
self.rdb.write()

def resize(self, new_lo_cyl=None, new_hi_cyl=None, adjust_physical=False):
# if the rdb region has to be minimized then check if no partition
# is in the way
Expand Down Expand Up @@ -630,7 +635,7 @@ def add_partition(
self.rawblk.flush()
# create partition object and add to partition list
blk_per_cyl = blk_per_trk * heads
p = Partition(self.rawblk, blk_num, len(self.parts), blk_per_cyl, self)
p = Partition(self.rawblk, blk_num, len(self.parts), blk_per_cyl, self.block_bytes, self)
p.read()
self.parts.append(p)
return p
Expand Down
67 changes: 67 additions & 0 deletions amitools/fs/rdb/Recovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from amitools.fs.block.rdb.RDBlock import *
from .FileSystem import FileSystem
from .Partition import Partition
from .RDisk import RDisk

class Recovery:
def __init__(self, rawblk, block_bytes, cyl_blks, reserved_cyls):
self.rawblk = rawblk
self.max_blks = cyl_blks * reserved_cyls
self.block_bytes = block_bytes
self.cyl_blks = cyl_blks
self.pnum = 0
self.fnum = 0

def read(self):
res = []
for i in range(self.max_blks):
blk = self.try_block_read(i)
if blk is not None:
res.append(blk)
self.block_map = res

def try_block_read(self, i):
p = Partition(self.rawblk, i, self.pnum, self.cyl_blks, self.block_bytes)
if p.read():
self.pnum += 1
return p
f = FileSystem(self.rawblk, i, self.fnum)
if f.read():
self.fnum += 1
return f
r = RDBlock(self.rawblk, i)
if r.read():
return r
return None

def dump(self):
for i in self.block_map:
print(i.dump())

def build_rdisk(self):
blkdev = self.rawblk
rdb = None
parts = []
fs = []
for i in self.block_map:
t = type(i)
if t is RDBlock:
rdb = i
if t is Partition:
parts.append(i)
if t is FileSystem:
fs.append(i)
pass

if rdb == None:
rdb = RDBlock(blkdev)

rdisk = RDisk(blkdev)
rdisk.create(blkdev.geo, rdb_cyls=self.cyl_blks, blk_num=rdb.blk_num)

for i in parts:
rdisk.parts.append(i)
for i in fs:
rdisk.fs.append(i)

return rdisk
Loading