Btrfs
btrfs 增量快照:在發送的數據中查找 UUID
我正在
btrfs
使用btrfs send
和做增量快照btrfs receive
假設我從初始快照開始
snapshot_0
並將send
數據寫入文件$ sudo btrfs send snapshot_0 -f snapshot_0.data
然後進行一些更改,創建一個新快照
snapshot_1
並按照以下方式拍攝差異快照$ sudo btrfs send -p snapshot_0 snapshot_1 -f snapshot_0-1.data
現在我有兩個文件
snapshot_0.data
和snapshot_0-1.data
. 我知道我可以使用$ sudo btrfs subvolume show snapshot_0 $ sudo btrfs subvolume show snapshot_1
為了從實際快照中獲取
UUID
和Parent UUID
(或)。Received UUID
我的問題是:有沒有辦法
UUID
從我的數據文件snapshot_0.data
中獲取這些 ssnapshot_0-1.data
?更新:我剛剛在發送/接收上找到了設計說明。
第二次更新:
btrfs-snapshots-diff.py
$$ github.com $$可以提供這個;調查…(我還在askubuntu.com上發布了這個問題)
從btrfs-snapshots-diff.py的程式碼開始$$ github.com $$能夠根據我的需要製作腳本。我可以用這種方式來獲取
uuid
s:with BtrfsStream('snapshot_0-1.data') as btrfs_stream: print(btrfs_stream.get_send_command()) # ('BTRFS_SEND_C_SNAPSHOT', # (UUID('01234567-89ab-cdef-0123-456789abcdef'), # UUID('fedcba98-7654-3210-fedc-ba9876543210')))
類
BtrfsStream
如下。我對原始程式碼做了一些修改:
- python 3(而不是 python 2)
- 迭代文件而不是將其全部讀入記憶體
- 添加
contextmanager
功能以便在with
satement中使用它然後使用的程式碼是:
from struct import unpack import io from uuid import UUID class BtrfsStream: # From btrfs/send.h send_cmds = ( 'BTRFS_SEND_C_UNSPEC BTRFS_SEND_C_SUBVOL BTRFS_SEND_C_SNAPSHOT ' 'BTRFS_SEND_C_MKFILE BTRFS_SEND_C_MKDIR BTRFS_SEND_C_MKNOD ' 'BTRFS_SEND_C_MKFIFO BTRFS_SEND_C_MKSOCK BTRFS_SEND_C_SYMLINK ' 'BTRFS_SEND_C_RENAME BTRFS_SEND_C_LINK BTRFS_SEND_C_UNLINK ' 'BTRFS_SEND_C_RMDIR BTRFS_SEND_C_SET_XATTR BTRFS_SEND_C_REMOVE_XATTR ' 'BTRFS_SEND_C_WRITE BTRFS_SEND_C_CLONE BTRFS_SEND_C_TRUNCATE ' 'BTRFS_SEND_C_CHMOD BTRFS_SEND_C_CHOWN BTRFS_SEND_C_UTIMES ' 'BTRFS_SEND_C_END BTRFS_SEND_C_UPDATE_EXTENT').split() send_attrs = ( 'BTRFS_SEND_A_UNSPEC BTRFS_SEND_A_UUID BTRFS_SEND_A_CTRANSID ' 'BTRFS_SEND_A_INO BTRFS_SEND_A_SIZE BTRFS_SEND_A_MODE ' 'BTRFS_SEND_A_UID BTRFS_SEND_A_GID BTRFS_SEND_A_RDEV ' 'BTRFS_SEND_A_CTIME BTRFS_SEND_A_MTIME BTRFS_SEND_A_ATIME ' 'BTRFS_SEND_A_OTIME BTRFS_SEND_A_XATTR_NAME ' 'BTRFS_SEND_A_XATTR_DATA BTRFS_SEND_A_PATH BTRFS_SEND_A_PATH_TO ' 'BTRFS_SEND_A_PATH_LINK BTRFS_SEND_A_FILE_OFFSET BTRFS_SEND_A_DATA ' 'BTRFS_SEND_A_CLONE_UUID BTRFS_SEND_A_CLONE_CTRANSID ' 'BTRFS_SEND_A_CLONE_PATH BTRFS_SEND_A_CLONE_OFFSET ' 'BTRFS_SEND_A_CLONE_LEN').split() # From btrfs/ioctl.h:#define BTRFS_UUID_SIZE 16 BTRFS_UUID_SIZE = 16 HEADER_SIZE = 17 # Headers length l_head = 10 l_tlv = 4 def __init__(self, path): ''' ''' self.path = path self._stream = None def __enter__(self): ''' enter for context manager ''' self._stream = open(self.path, 'rb') self._read_header() return self def __exit__(self, exc_type, exc_val, exc_tb): ''' exit for context manager ''' self._stream.close() def _read_header(self): ''' read the header ''' header = self.read(BtrfsStream.HEADER_SIZE, assert_lengh=False) if len(header) < BtrfsStream.HEADER_SIZE: raise IOError('Invalid stream length\n') magic, null, self.version = unpack('<12scI', header) if magic != b'btrfs-stream': raise IOError('Not a Btrfs stream!') def seek(self, offset, whence=io.SEEK_SET): ''' seek to a given point ''' self._stream.seek(offset) def tell(self): ''' tell where we are ''' return self._stream.tell() def read(self, n_bytes, assert_lengh=True): ''' try to read n_bytes ''' tell_before = self.tell() btes = self._stream.read(n_bytes) if assert_lengh is True and len(btes) != n_bytes: msg = ('could only read {} instead of {} at offset {}' ).format(len(btes), n_bytes, tell_before) raise IOError(msg) return btes def tlv_get(self, attr_type): attr, l_attr = unpack('<HH', self.read(BtrfsStream.l_tlv)) if self.send_attrs[attr] != attr_type: raise ValueError('Unexpected attribute %s' % self.send_attrs[attr]) ret, = unpack('<H', self.read(2)) return ret def _tlv_get_string(self, attr_type): attr, l_attr = unpack('<HH', self.read(BtrfsStream.l_tlv)) if self.send_attrs[attr] != attr_type: raise ValueError('Unexpected attribute %s' % self.send_attrs[attr]) ret, = unpack('<%ds' % l_attr, self.read(l_attr)) return ret def _tlv_get_u64(self, attr_type): attr, l_attr = unpack('<HH', self.read(BtrfsStream.l_tlv)) if self.send_attrs[attr] != attr_type: raise ValueError('Unexpected attribute %s' % self.send_attrs[attr]) ret, = unpack('<Q', self.read(l_attr)) return ret def _tlv_get_uuid(self, attr_type): attr, l_attr = unpack('<HH', self.read(BtrfsStream.l_tlv)) if self.send_attrs[attr] != attr_type: raise ValueError('Unexpected attribute %s' % self.send_attrs[attr]) return UUID(bytes=self.read(l_attr)) def get_send_command(self): ''' search uuids only. ''' # start at the right point in the file self.seek(BtrfsStream.HEADER_SIZE) while True: l_cmd, cmd, crc = unpack('<IHI', self.read(BtrfsStream.l_head)) tell_before_cmd = self.tell() try: command = self.send_cmds[cmd] except: raise ValueError('Unkown command %d' % cmd) if command == 'BTRFS_SEND_C_SNAPSHOT': self._tlv_get_string('BTRFS_SEND_A_PATH') uuid = self._tlv_get_uuid('BTRFS_SEND_A_UUID') self._tlv_get_u64('BTRFS_SEND_A_CTRANSID') clone_uuid = self._tlv_get_uuid('BTRFS_SEND_A_CLONE_UUID') return 'BTRFS_SEND_C_SNAPSHOT', (uuid, clone_uuid) elif command == 'BTRFS_SEND_C_SUBVOL': self._tlv_get_string('BTRFS_SEND_A_PATH') uuid = self._tlv_get_uuid('BTRFS_SEND_A_UUID') return 'BTRFS_SEND_C_SUBVOL', (uuid, ) elif command == 'BTRFS_SEND_C_CLONE': self._tlv_get_string('BTRFS_SEND_A_PATH') self._tlv_get_u64('BTRFS_SEND_A_FILE_OFFSET') self._tlv_get_u64('BTRFS_SEND_A_CLONE_LEN') clone_uuid = self._tlv_get_uuid('BTRFS_SEND_A_CLONE_UUID') return 'BTRFS_SEND_C_CLONE', (clone_uuid, ) elif command == 'BTRFS_SEND_C_END': return self.seek(tell_before_cmd + l_cmd)