![btrfs delta snapshot: 전송된 데이터에서 UUID 찾기](https://linux55.com/image/126829/btrfs%20delta%20snapshot%3A%20%EC%A0%84%EC%86%A1%EB%90%9C%20%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%97%90%EC%84%9C%20UUID%20%EC%B0%BE%EA%B8%B0.png)
btrfs
btrfs send
다음을 사용하여 증분 스냅샷을 찍고 있습니다 .btrfs receive
초기 스냅샷 snapshot_0
과 send
파일 데이터 부터 시작한다고 가정해 보겠습니다.
$ sudo btrfs send snapshot_0 -f snapshot_0.data
그런 다음 몇 가지 사항을 변경하고 새 스냅샷을 생성한 snapshot_1
후 다음과 같이 diff 스냅샷을 찍습니다.
$ 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
?snapshot_0-1.data
업데이트: 방금 알아냈어요보내기/받기를 위한 디자인 고려 사항.
두 번째 업데이트: btrfs-snapshots-diff.py
[github.com]이러한 서비스가 제공될 수 있습니다.
(저는 다음에도 질문을 게시했습니다.askubuntu.com)
답변1
코드로 시작btrfs-스냅샷-diff.py [github.com]내 필요에 따라 스크립트를 만들 수 있습니다. 나는 이런 식으로 s를 얻을 수 있습니다 uuid
:
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
그런 다음 사용되는 코드는 다음과 같습니다.
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)