ZFS corrupted data
Volodymyr Kostyrko
c.kworr at gmail.com
Tue Jul 27 20:18:30 UTC 2010
27.07.2010 22:26, Lars Hartmann wrote:
>> How much time has passed from the time you resumed you machine up to the
>> time you rebooted the machine?
> about 2minutes.
>
> I hope these informations helped you.
>
Zpool seems to look fine. You can try importing it with more recent ZFS
version, I don't recall which exactly but some time ago there was new
option -F added which resulted in trying more then last transaction on
importing pool. You will need recent SystemRescueCD or OpenSolaris.
Also you can try this script but be warned - it does destroy data. You
will need to save first megabyte and last two megabytes from each pool
device just in case. After that you can use this script to lookup last
transactions and drop some of them.
--
Sphinx of black quartz judge my vow.
-------------- next part --------------
#!/usr/bin/env python3.1
# (c) 2010 c.kworr at gmail.com
import io, optparse, os, time, struct
class VdevBootBlock:
'Holds info about this vdev.'
__slots__ = frozenset(('addr', 'blocksize', 'magic', 'offset', 'size', 'version'))
magic = b'\x0c\xb1\x07\xb0\xf5\x02'
blocksize = 1 << 13 # 8k
def __init__(self, source, address):
block = source.read(address, self.blocksize)
self.addr = ()
self.version = 0
if block[0:6] == self.magic:
self.version = struct.unpack('Q', block[8:16])[0]
self.offset = struct.unpack('Q', block[16:24])[0]
self.size = struct.unpack('Q', block[24:32])[0]
self.addr += address,
def __eq__(self, other):
return(type(other) == type(self) and self.version == other.version and self.offset == other.offset and self.size == other.size)
def __repr__(self):
return('VB: addr:{} version:{} offset:{} size:{}'.format(self.addr, self.version, self.offset, self.size))
class Uberblock:
'Holds all info about one uberblock.'
__slots__ = frozenset(('addr', 'magic', 'version', 'txg', 'guid_sum', 'timestamp', 'rootbp', 'blocksize'))
magic = b'\x0c\xb1\xba\x00' # b10c 00ba, oo-ba bloc!
blocksize = 1 << 10 # 1k
def __init__(self, source, address):
block = source.read(address, self.blocksize)
self.addr = ()
self.version = 0
if block[0:4] == self.magic:
self.version = struct.unpack('Q', block[8:16])[0]
self.txg = struct.unpack('Q', block[16:24])[0]
self.guid_sum = struct.unpack('Q', block[24:32])[0]
self.timestamp = struct.unpack('Q', block[32:40])[0]
self.rootbp = struct.unpack('Q', block[40:48])[0]
self.addr += address,
def __eq__(self, other):
return(self.version == other.version and self.txg == other.txg and self.guid_sum == other.guid_sum and self.timestamp == other.timestamp and self.rootbp == other.rootbp)
def __repr__(self):
return('UB: addr:{} version:{} txg:{} guid_sum:{} timestamp:{} rootbp:{}'.format(self.addr, self.version, self.txg, self.guid_sum, time.strftime("%d %b %Y %H:%M:%S", time.localtime(self.timestamp)), self.rootbp))
class NvData:
'Contents of the nvpair list.'
__slots__ = frozenset(('blocksize', 'decsize', 'encmethod', 'endian', 'encsize'))
blocksize = (1<<17) - (1<<14) # 128k - 16k
def __init__(self, source, address):
block = source.read(address, self.blocksize)
self.encmethod = struct.unpack('B', block[0:1])[0]
self.endian = struct.unpack('B', block[1:2])[0]
self.encsize = struct.unpack('I', block[4:8])[0]
self.decsize = struct.unpack('I', block[8:12])[0]
def __repr__(self):
return('NV: encmethod:{} endian:{} encsize:{} decsize:{}'.format(self.encmethod, self.endian, self.encsize, self.decsize))
class SourceDevice:
__slots__ = frozenset(('__file', 'devsize', 'vdev_label_size', 'vboot', 'uberblocks'))
vdev_label_size = 1 << 18
def __init__(self, name):
assert os.access(name, os.R_OK), 'Please specify readable device to work on.'
self.__file = open(options.device, 'rb')
assert self.__file.seekable(), "Can't seek file."
# checking device size
self.devsize = self.__file.seek(0, os.SEEK_END)
print('Detected size:', self.devsize, 'bytes.')
# checking blocks alignment and aligning them accordingly
vblocks = int(self.devsize / self.vdev_label_size)
self.uberblocks = {}
self.vboot = None
for vdev_addr in (0, self.vdev_label_size, (vblocks - 2) * self.vdev_label_size, (vblocks - 1) * self.vdev_label_size):
# checking vboot headers
vboot_addr = vdev_addr + (1 << 13)
vb = VdevBootBlock(self, vboot_addr)
if vb.version > 0:
if self.vboot == None:
self.vboot = vb
elif self.vboot != vb:
print('Found different VdevBootBlock.')
print('Old:', self.vboot)
print('New:', vb)
else:
self.vboot.addr += vb.addr
# XXX: check nvpairs
#nv = NvData(self, vboot_addr + (1 << 13))
#print(nv)
# checking uberblocks
ublocks_addr = vdev_addr + (1 << 17)
for ublock_num in range (0, 128):
ub = Uberblock(self, ublocks_addr + ublock_num * (1 << 10))
if ub.version > 0:
if not ub.txg in self.uberblocks:
self.uberblocks[ub.txg] = ub
elif not self.uberblocks[ub.txg] == ub:
print('Found incorrect uberblock copy.')
print('Old: ', self.uberblocks[ub.txg])
print('New: ', ub)
else:
self.uberblocks[ub.txg].addr += ub.addr
def read(self, seek, size):
self.__file.seek(seek)
return(self.__file.read(size))
def check_block(block_number, block, uberblocks):
if len(block) == 0:
return
ub = Uberblock(block, block_number)
if ub.version > 0:
if not ub.txg in uberblocks:
uberblocks[ub.txg] = ub
elif not uberblocks[ub.txg] == ub:
print('Found incorrect uberblock copy.')
print('Old: ', uberblocks[ub.txg])
print('New: ', ub)
else:
uberblocks[ub.txg].addr += block_number,
vb = VdevBootBlock(block, block_number)
if vb.version > 0:
print(vb)
# For now we need only the device name
parser = optparse.OptionParser()
parser.add_option('-d', '--device', action = 'store', dest = 'device', help = 'device to check', metavar = 'string')
parser.add_option('-r', '--rollback', action = 'store_true', dest = 'rollback', help = 'ask transaction number to rollback to', metavar = 'bool', default = False)
(options, args) = parser.parse_args()
assert options.device != None, 'Please specify device to work on.'
source = SourceDevice(options.device)
# Printing found vdev boot
print(source.vboot)
# Printing found uberblocks
for txg in source.uberblocks:
print(source.uberblocks[txg])
if options.rollback:
strip_to = int(input('What transaction you want rollback to? '))
with open(options.device, '+b') as w_source:
first = True
for txg in source.uberblocks:
if txg > strip_to:
if first:
first = False
print('Refusing to drop oldest transaction.')
continue
for addr in source.uberblocks[txg].addr:
w_source.seek(addr)
print('Zeroing address', addr, 'from transaction', txg, '.')
w_source.write(b'\0' * Uberblock.blocksize)
if first:
first = False
More information about the freebsd-fs
mailing list