initial version
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "unitree"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4.40"
|
||||||
|
num_enum = "0.7.3"
|
||||||
181
src/main.rs
Normal file
181
src/main.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::{Read, Write, ErrorKind};
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use chrono::DateTime;
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive,Debug)]
|
||||||
|
#[repr(u16)]
|
||||||
|
enum CommandType {
|
||||||
|
Unspec = 0,
|
||||||
|
Subvol = 1,
|
||||||
|
Snapshot = 2,
|
||||||
|
MkFile = 3,
|
||||||
|
MkDir = 4,
|
||||||
|
MkNod = 5,
|
||||||
|
MkFifo = 6,
|
||||||
|
MkSock = 7,
|
||||||
|
Symlink = 8,
|
||||||
|
Rename = 9,
|
||||||
|
Link = 10,
|
||||||
|
Unlink = 11,
|
||||||
|
RmDir = 12,
|
||||||
|
SetXAttr = 13,
|
||||||
|
RemoveXAttr = 14,
|
||||||
|
Write = 15,
|
||||||
|
Clone = 16,
|
||||||
|
Truncate = 17,
|
||||||
|
Chmod = 18,
|
||||||
|
Chown = 19,
|
||||||
|
UTimes = 20,
|
||||||
|
End = 21,
|
||||||
|
UpdateExtent = 22,
|
||||||
|
FAllocate = 23,
|
||||||
|
FileAttr = 24,
|
||||||
|
EncodedWrite = 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive,Debug,Clone,Copy)]
|
||||||
|
#[repr(u16)]
|
||||||
|
enum TLVType {
|
||||||
|
Unspec = 0,
|
||||||
|
UUID = 1,
|
||||||
|
CTransID = 2,
|
||||||
|
Ino = 3,
|
||||||
|
Size = 4,
|
||||||
|
Mode = 5,
|
||||||
|
UID = 6,
|
||||||
|
GID = 7,
|
||||||
|
RDev = 8,
|
||||||
|
CTime = 9,
|
||||||
|
MTime = 10,
|
||||||
|
ATime = 11,
|
||||||
|
OTime = 12,
|
||||||
|
XAttrName = 13,
|
||||||
|
XAttrData = 14,
|
||||||
|
Path = 15,
|
||||||
|
PathTo = 16,
|
||||||
|
PathLink = 17,
|
||||||
|
FileOffset = 18,
|
||||||
|
Data = 19,
|
||||||
|
CloneUUID = 20,
|
||||||
|
CloneCTransID = 21,
|
||||||
|
ClonePath = 22,
|
||||||
|
CloneOffset = 23,
|
||||||
|
CloneLen = 24,
|
||||||
|
FAllocateMode = 25,
|
||||||
|
FileAttr = 26,
|
||||||
|
UnencodedFileLen = 27,
|
||||||
|
UnencodedLen = 28,
|
||||||
|
UnencodedOffset = 29,
|
||||||
|
Compression = 30,
|
||||||
|
Encryption = 31,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UUID(u128);
|
||||||
|
|
||||||
|
impl Display for UUID {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
let x = self.0.to_le_bytes();
|
||||||
|
write!(f,
|
||||||
|
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||||
|
x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Time {
|
||||||
|
secs: i64,
|
||||||
|
nanos: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Time {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(f, "{}", DateTime::from_timestamp(self.secs, self.nanos).ok_or(std::fmt::Error)?)
|
||||||
|
|
||||||
|
// write!(f, "{}.{:09}", self.secs, self.nanos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tlv(ty: TLVType, data: &[u8]) -> Option<Box<dyn Display>> {
|
||||||
|
match ty {
|
||||||
|
TLVType::CTransID | TLVType::Ino | TLVType::Size | TLVType::Mode | TLVType::UID |
|
||||||
|
TLVType::GID | TLVType::RDev | TLVType::FileOffset | TLVType::CloneCTransID |
|
||||||
|
TLVType::CloneOffset | TLVType::CloneLen | TLVType::FileAttr | TLVType::UnencodedFileLen |
|
||||||
|
TLVType::UnencodedLen | TLVType::UnencodedOffset =>
|
||||||
|
Some(Box::new(u64::from_le_bytes(data.try_into().ok()?))),
|
||||||
|
TLVType::FAllocateMode | TLVType::Compression | TLVType::Encryption =>
|
||||||
|
Some(Box::new(u32::from_le_bytes(data.try_into().ok()?))),
|
||||||
|
TLVType::XAttrName | TLVType::Path | TLVType::PathTo | TLVType::PathLink | TLVType::ClonePath =>
|
||||||
|
Some(Box::new(String::from_utf8(data.to_vec()).ok()?)),
|
||||||
|
TLVType::UUID | TLVType::CloneUUID =>
|
||||||
|
Some(Box::new(UUID(u128::from_le_bytes(data.try_into().ok()?)))),
|
||||||
|
TLVType::CTime | TLVType::MTime | TLVType::ATime | TLVType::OTime => {
|
||||||
|
let secs = i64::from_le_bytes(data[0..8].try_into().ok()?);
|
||||||
|
let nanos = u32::from_le_bytes(data[8..12].try_into().ok()?);
|
||||||
|
Some(Box::new(Time {secs, nanos}))
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! out {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
let result = writeln!(std::io::stdout(), $($arg)*);
|
||||||
|
if let Some(e) = result.err().filter(|e| e.kind() != ErrorKind::BrokenPipe) {
|
||||||
|
panic!("I/O error: {}", &e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut contents: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
File::open("../btrfs_send_test")?.read_to_end(&mut contents)?;
|
||||||
|
|
||||||
|
out!("Magic: {}", &String::from_utf8(contents[..12].to_vec())?);
|
||||||
|
out!("Version: {}", u32::from_le_bytes(contents[13..17].try_into()?));
|
||||||
|
out!("");
|
||||||
|
|
||||||
|
let mut offset: usize = 17;
|
||||||
|
|
||||||
|
while offset < contents.len() {
|
||||||
|
let len = u32::from_le_bytes(contents[offset .. offset + 4].try_into()?);
|
||||||
|
let cmd = CommandType::try_from_primitive(
|
||||||
|
u16::from_le_bytes(
|
||||||
|
contents[offset + 4 .. offset + 6].try_into()?
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
let _checksum = u32::from_le_bytes(contents[offset + 6.. offset + 10].try_into()?);
|
||||||
|
|
||||||
|
out!("{cmd:?}");
|
||||||
|
|
||||||
|
let mut inner_offset: usize = 0;
|
||||||
|
while inner_offset < len as usize {
|
||||||
|
let tlvtype = TLVType::try_from_primitive(
|
||||||
|
u16::from_le_bytes(
|
||||||
|
contents[offset + inner_offset + 10 .. offset + inner_offset + 12].try_into()?
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
let tlvlen = u16::from_le_bytes(contents[offset + inner_offset + 12 .. offset + inner_offset + 14].try_into()?);
|
||||||
|
|
||||||
|
let tlvvalue = parse_tlv(
|
||||||
|
tlvtype,
|
||||||
|
&contents[offset + inner_offset + 14 .. offset + inner_offset + 14 + tlvlen as usize]
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(tlv) = tlvvalue {
|
||||||
|
out!(" {tlvtype:?}: {tlv}");
|
||||||
|
} else {
|
||||||
|
out!(" {tlvtype:?}: ({tlvlen} bytes)");
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_offset += tlvlen as usize + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += len as usize + 10; // 10 byte header
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user