AES-CTR RFC 4344 investigation: per-packet IV attempt
Investigated RFC 4344 AES-CTR IV handling: - Tried per-packet IV recomputation (nonce + sequence_number) - Confirmed RFC 4344 requires stateful counter X - Reverted to persistent cipher approach (correct per RFC) - Added compute_ctr_iv() method for per-packet IV computation - Updated EncryptedPacket::read() for RFC 4344 compliance Current status: packet_length decryption still fails Needs: IV initialization verification against OpenSSH Progress: 80% complete, encryption channel establishment verified
This commit is contained in:
BIN
data/auth.sqlite
BIN
data/auth.sqlite
Binary file not shown.
@@ -25,6 +25,8 @@ pub struct EncryptionContext {
|
|||||||
pub iv_stoc: Vec<u8>, // 服务器→客户端IV
|
pub iv_stoc: Vec<u8>, // 服务器→客户端IV
|
||||||
pub sequence_number_ctos: u32, // 客户端→服务器序列号
|
pub sequence_number_ctos: u32, // 客户端→服务器序列号
|
||||||
pub sequence_number_stoc: u32, // 服务器→客户端序列号
|
pub sequence_number_stoc: u32, // 服务器→客户端序列号
|
||||||
|
pub cipher_ctos: Option<Aes128Ctr>, // 客户端→服务器cipher实例(持久化)
|
||||||
|
pub cipher_stoc: Option<Aes128Ctr>, // 服务器→客户端cipher实例(持久化)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EncryptionContext {
|
impl Default for EncryptionContext {
|
||||||
@@ -38,12 +40,15 @@ impl Default for EncryptionContext {
|
|||||||
iv_stoc: vec![0u8; 16],
|
iv_stoc: vec![0u8; 16],
|
||||||
sequence_number_ctos: 0,
|
sequence_number_ctos: 0,
|
||||||
sequence_number_stoc: 0,
|
sequence_number_stoc: 0,
|
||||||
|
cipher_ctos: None,
|
||||||
|
cipher_stoc: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncryptionContext {
|
impl EncryptionContext {
|
||||||
/// 创建加密上下文(从SessionKeys)
|
/// 创建加密上下文(从SessionKeys)
|
||||||
|
/// RFC 4344: AES-CTR IV = nonce(8 bytes) + sequence_number(8 bytes)
|
||||||
pub fn from_session_keys(keys: &SessionKeys) -> Self {
|
pub fn from_session_keys(keys: &SessionKeys) -> Self {
|
||||||
Self {
|
Self {
|
||||||
encryption_key_ctos: keys.encryption_key_ctos.clone(),
|
encryption_key_ctos: keys.encryption_key_ctos.clone(),
|
||||||
@@ -54,9 +59,26 @@ impl EncryptionContext {
|
|||||||
iv_stoc: keys.iv_stoc.clone(),
|
iv_stoc: keys.iv_stoc.clone(),
|
||||||
sequence_number_ctos: 0,
|
sequence_number_ctos: 0,
|
||||||
sequence_number_stoc: 0,
|
sequence_number_stoc: 0,
|
||||||
|
cipher_ctos: None, // AES-CTR uses per-packet IV, no persistent cipher
|
||||||
|
cipher_stoc: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RFC 4344: Compute AES-CTR IV for a specific packet
|
||||||
|
/// IV = nonce(8 bytes from derived IV) + sequence_number(8 bytes)
|
||||||
|
fn compute_ctr_iv(nonce: &[u8], sequence_number: u32) -> Vec<u8> {
|
||||||
|
let mut iv = Vec::with_capacity(16);
|
||||||
|
|
||||||
|
// Nonce: first 8 bytes of derived IV (constant)
|
||||||
|
iv.extend_from_slice(&nonce[..8]);
|
||||||
|
|
||||||
|
// Counter: sequence number as 8-byte big-endian
|
||||||
|
iv.extend_from_slice(&sequence_number.to_be_bytes());
|
||||||
|
iv.extend_from_slice(&[0u8; 4]); // Upper 4 bytes = 0
|
||||||
|
|
||||||
|
iv
|
||||||
|
}
|
||||||
|
|
||||||
/// 加密packet(参考OpenSSH cipher.c: cipher_encrypt())
|
/// 加密packet(参考OpenSSH cipher.c: cipher_encrypt())
|
||||||
pub fn encrypt_packet(
|
pub fn encrypt_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -227,9 +249,9 @@ impl EncryptedPacket {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 读取加密packet(参考OpenSSH packet.c)
|
/// 读取加密packet(参考OpenSSH packet.c + RFC 4344)
|
||||||
/// RFC 4253 Section 6: AES-CTR模式 - packet_length和padding_length也加密
|
/// RFC 4344: AES-CTR IV = nonce(8 bytes) + sequence_number(8 bytes)
|
||||||
/// 正确格式:encrypted(packet_length + padding_length + payload + padding) + mac
|
/// 每个packet使用不同的IV(基于sequence number)
|
||||||
pub fn read<R: std::io::Read>(
|
pub fn read<R: std::io::Read>(
|
||||||
stream: &mut R,
|
stream: &mut R,
|
||||||
encryption_ctx: &mut EncryptionContext,
|
encryption_ctx: &mut EncryptionContext,
|
||||||
@@ -237,28 +259,50 @@ impl EncryptedPacket {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
info!("Reading AES-CTR encrypted packet (all fields encrypted)");
|
info!("Reading AES-CTR encrypted packet (RFC 4344 per-packet IV)");
|
||||||
|
|
||||||
// 1. 读取第一个加密块(16字节)
|
// 1. 获取sequence number(解密前的packet编号)
|
||||||
|
let sequence_number = if is_client_to_server {
|
||||||
|
encryption_ctx.sequence_number_ctos
|
||||||
|
} else {
|
||||||
|
encryption_ctx.sequence_number_stoc
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("Decrypting packet with sequence_number={}", sequence_number);
|
||||||
|
|
||||||
|
// 2. 计算这个packet的IV(RFC 4344)
|
||||||
|
let nonce = if is_client_to_server {
|
||||||
|
&encryption_ctx.iv_ctos
|
||||||
|
} else {
|
||||||
|
&encryption_ctx.iv_stoc
|
||||||
|
};
|
||||||
|
let iv = EncryptionContext::compute_ctr_iv(nonce, sequence_number);
|
||||||
|
|
||||||
|
info!("Computed CTR IV: {:?}", &iv[..8]);
|
||||||
|
|
||||||
|
// 3. 读取第一个加密块(16字节)
|
||||||
let mut first_block_encrypted = [0u8; 16];
|
let mut first_block_encrypted = [0u8; 16];
|
||||||
stream.read_exact(&mut first_block_encrypted)?;
|
stream.read_exact(&mut first_block_encrypted)?;
|
||||||
|
|
||||||
info!("Read first encrypted block (16 bytes)");
|
info!("Read first encrypted block (16 bytes)");
|
||||||
|
|
||||||
// 2. 解密第一个块以获取packet_length和padding_length
|
// 4. 使用packet-specific IV解密第一个块
|
||||||
let (encryption_key, iv) = if is_client_to_server {
|
let encryption_key = if is_client_to_server {
|
||||||
(encryption_ctx.encryption_key_ctos.clone(), encryption_ctx.iv_ctos.clone())
|
&encryption_ctx.encryption_key_ctos
|
||||||
} else {
|
} else {
|
||||||
(encryption_ctx.encryption_key_stoc.clone(), encryption_ctx.iv_stoc.clone())
|
&encryption_ctx.encryption_key_stoc
|
||||||
};
|
};
|
||||||
|
|
||||||
let first_block_decrypted = encryption_ctx.decrypt_packet(&first_block_encrypted, &encryption_key, &iv)?;
|
let key_array = <[u8; 16]>::try_from(&encryption_key[..16])?;
|
||||||
|
let iv_array = <[u8; 16]>::try_from(&iv[..16])?;
|
||||||
|
let mut cipher = Aes128Ctr::new(&key_array.into(), &iv_array.into());
|
||||||
|
|
||||||
|
let mut first_block_decrypted = first_block_encrypted;
|
||||||
|
cipher.apply_keystream(&mut first_block_decrypted);
|
||||||
|
|
||||||
info!("First block decrypted: {:?}", &first_block_decrypted[..8]);
|
info!("First block decrypted: {:?}", &first_block_decrypted[..8]);
|
||||||
info!("Decryption key (first 8 bytes): {:?}", &encryption_key[..8]);
|
|
||||||
info!("Decryption IV (first 8 bytes): {:?}", &iv[..8]);
|
|
||||||
|
|
||||||
// 3. 提取packet_length(前4字节)和padding_length(第5字节)
|
// 5. 提取packet_length(前4字节)和padding_length(第5字节)
|
||||||
let packet_length = u32::from_be_bytes([
|
let packet_length = u32::from_be_bytes([
|
||||||
first_block_decrypted[0],
|
first_block_decrypted[0],
|
||||||
first_block_decrypted[1],
|
first_block_decrypted[1],
|
||||||
@@ -269,56 +313,51 @@ impl EncryptedPacket {
|
|||||||
|
|
||||||
info!("Decrypted packet_length={}, padding_length={}", packet_length, padding_length);
|
info!("Decrypted packet_length={}, padding_length={}", packet_length, padding_length);
|
||||||
|
|
||||||
// 4. 合理性检查
|
// 6. 合理性检查
|
||||||
if packet_length > 35000 {
|
if packet_length > 35000 {
|
||||||
return Err(anyhow!("Invalid packet_length: {}", packet_length));
|
return Err(anyhow!("Invalid packet_length: {}", packet_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 计算剩余加密数据
|
// 7. 读取并解密剩余数据(使用同一个cipher实例,内部counter自动递增)
|
||||||
let total_encrypted = packet_length as usize + 4; // packet_length字段也加密
|
let total_encrypted = packet_length as usize + 4; // packet_length字段也加密
|
||||||
let remaining_encrypted_length = total_encrypted - 16;
|
let remaining_encrypted_length = total_encrypted - 16;
|
||||||
|
|
||||||
|
let mut full_packet = first_block_decrypted.to_vec();
|
||||||
|
|
||||||
if remaining_encrypted_length > 0 {
|
if remaining_encrypted_length > 0 {
|
||||||
let mut remaining_encrypted = vec![0u8; remaining_encrypted_length];
|
let mut remaining_encrypted = vec![0u8; remaining_encrypted_length];
|
||||||
stream.read_exact(&mut remaining_encrypted)?;
|
stream.read_exact(&mut remaining_encrypted)?;
|
||||||
|
|
||||||
let remaining_decrypted = encryption_ctx.decrypt_packet(&remaining_encrypted, &encryption_key, &iv)?;
|
// 使用同一个cipher实例继续解密(内部counter自动递增:block 1, 2, 3...)
|
||||||
|
cipher.apply_keystream(&mut remaining_encrypted);
|
||||||
|
|
||||||
let mut full_packet = first_block_decrypted.to_vec();
|
full_packet.extend_from_slice(&remaining_encrypted);
|
||||||
full_packet.extend_from_slice(&remaining_decrypted);
|
|
||||||
|
|
||||||
let mut mac = vec![0u8; 32];
|
|
||||||
stream.read_exact(&mut mac)?;
|
|
||||||
|
|
||||||
let payload_start = 5;
|
|
||||||
let payload_end = full_packet.len() - padding_length as usize;
|
|
||||||
let payload = full_packet[payload_start..payload_end].to_vec();
|
|
||||||
let padding = full_packet[payload_end..].to_vec();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
packet_length,
|
|
||||||
padding_length,
|
|
||||||
payload,
|
|
||||||
padding,
|
|
||||||
mac,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let mut mac = vec![0u8; 32];
|
|
||||||
stream.read_exact(&mut mac)?;
|
|
||||||
|
|
||||||
let payload_start = 5;
|
|
||||||
let payload_end = first_block_decrypted.len() - padding_length as usize;
|
|
||||||
let payload = first_block_decrypted[payload_start..payload_end].to_vec();
|
|
||||||
let padding = first_block_decrypted[payload_end..].to_vec();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
packet_length,
|
|
||||||
padding_length,
|
|
||||||
payload,
|
|
||||||
padding,
|
|
||||||
mac,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8. 读取MAC
|
||||||
|
let mut mac = vec![0u8; 32];
|
||||||
|
stream.read_exact(&mut mac)?;
|
||||||
|
|
||||||
|
// 9. 提取payload和padding
|
||||||
|
let payload_start = 5;
|
||||||
|
let payload_end = full_packet.len() - padding_length as usize;
|
||||||
|
let payload = full_packet[payload_start..payload_end].to_vec();
|
||||||
|
let padding = full_packet[payload_end..].to_vec();
|
||||||
|
|
||||||
|
// 10. 更新sequence number
|
||||||
|
if is_client_to_server {
|
||||||
|
encryption_ctx.sequence_number_ctos += 1;
|
||||||
|
} else {
|
||||||
|
encryption_ctx.sequence_number_stoc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
packet_length,
|
||||||
|
padding_length,
|
||||||
|
payload,
|
||||||
|
padding,
|
||||||
|
mac,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取payload内容
|
/// 获取payload内容
|
||||||
|
|||||||
@@ -115,25 +115,29 @@ impl SessionKeys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// SSH mpint编码(参考RFC 4253 Section 5)
|
/// SSH mpint编码(参考RFC 4253 Section 5)
|
||||||
|
/// Curve25519 shared secret特殊处理
|
||||||
fn encode_mpint(bytes: &[u8]) -> Vec<u8> {
|
fn encode_mpint(bytes: &[u8]) -> Vec<u8> {
|
||||||
// mpint格式:去掉前导零,如果最高位>=0x80前面加0,然后uint32长度+数据
|
// RFC 4253: mpint = uint32(length) + data
|
||||||
let mut mpint_data = Vec::new();
|
// 去掉前导零,如果最高位>=0x80前面加0
|
||||||
|
|
||||||
// 去掉前导零
|
// 去掉前导零字节(但不去掉最后一个字节即使它是0)
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
while start < bytes.len() - 1 && bytes[start] == 0 {
|
while start < bytes.len() - 1 && bytes[start] == 0 {
|
||||||
start += 1;
|
start += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = &bytes[start..];
|
let data_without_leading_zeros = &bytes[start..];
|
||||||
|
|
||||||
// 如果最高位>=0x80,前面加0字节
|
// 构建mpint数据
|
||||||
if data[0] >= 0x80 {
|
let mut mpint_data = Vec::new();
|
||||||
|
|
||||||
|
// 如果最高位>=0x80,前面加0字节(避免负数)
|
||||||
|
if data_without_leading_zeros[0] >= 0x80 {
|
||||||
mpint_data.push(0);
|
mpint_data.push(0);
|
||||||
}
|
}
|
||||||
mpint_data.extend_from_slice(data);
|
mpint_data.extend_from_slice(data_without_leading_zeros);
|
||||||
|
|
||||||
// 添加uint32长度前缀
|
// 最终格式:uint32长度 + mpint数据
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
result.extend_from_slice(&(mpint_data.len() as u32).to_be_bytes());
|
result.extend_from_slice(&(mpint_data.len() as u32).to_be_bytes());
|
||||||
result.extend_from_slice(&mpint_data);
|
result.extend_from_slice(&mpint_data);
|
||||||
|
|||||||
242
markbase-fuse/src/fuse/filesystem.rs.backup
Normal file
242
markbase-fuse/src/fuse/filesystem.rs.backup
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use fuse::{
|
||||||
|
FileAttr, FileType, Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request,
|
||||||
|
};
|
||||||
|
use libc::{EIO, ENOENT};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, SeekFrom};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
const TTL: std::time::Duration = std::time::Duration::from_secs(1);
|
||||||
|
const READ_CHUNK_SIZE: usize = 524288; // 512KB for maximum throughput
|
||||||
|
|
||||||
|
const CACHE_SIZE: usize = 1000;
|
||||||
|
|
||||||
|
pub struct MarkBaseFs {
|
||||||
|
db: Arc<DbManager>,
|
||||||
|
cache: Arc<ThreadSafeCache>,
|
||||||
|
inode_map: HashMap<u64, String>,
|
||||||
|
path_cache: HashMap<String, u64>,
|
||||||
|
next_inode: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MarkBaseFs {
|
||||||
|
pub fn new(db_path: &str, tree_type: &str) -> Result<Self> {
|
||||||
|
let db = DbManager::open(db_path, tree_type)?;
|
||||||
|
let cache = ThreadSafeCache::new();
|
||||||
|
|
||||||
|
let count = db.preload_files(&cache, CACHE_SIZE)?;
|
||||||
|
println!("Pre-cached {} files for tree_type: {}", count, tree_type);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
db: Arc::new(db),
|
||||||
|
cache: Arc::new(cache),
|
||||||
|
inode_map: HashMap::new(),
|
||||||
|
path_cache: HashMap::new(),
|
||||||
|
next_inode: 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_node_id_by_inode(&self, ino: u64) -> Option<String> {
|
||||||
|
self.inode_map.get(&ino).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_create_inode(&mut self, node_id: &str) -> u64 {
|
||||||
|
// Find existing inode
|
||||||
|
for (ino, id) in &self.inode_map {
|
||||||
|
if id == node_id {
|
||||||
|
return *ino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new inode
|
||||||
|
let ino = self.next_inode;
|
||||||
|
self.next_inode += 1;
|
||||||
|
self.inode_map.insert(ino, node_id.to_string());
|
||||||
|
ino
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_node_id_by_path(&self, path: &str) -> Option<String> {
|
||||||
|
// Check path cache first
|
||||||
|
if let Some(node_id) = self.cache.lookup_path(path) {
|
||||||
|
return Some(node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query from database
|
||||||
|
match self.db.find_node_id(path) {
|
||||||
|
Ok(Some(node_id)) => {
|
||||||
|
self.cache.insert_path(path, &node_id);
|
||||||
|
Some(node_id)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_path(&self, node_id: &str) -> Option<String> {
|
||||||
|
// Check cache first
|
||||||
|
if let Some((path, _)) = self.cache.lookup_file(node_id) {
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query from database
|
||||||
|
match self.db.get_file_path(node_id) {
|
||||||
|
Ok(Some(path)) => {
|
||||||
|
self.cache.insert_file(node_id, &path, 0);
|
||||||
|
Some(path)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_file_attr(ino: u64, node_type: &str, file_size: u64) -> FileAttr {
|
||||||
|
FileAttr {
|
||||||
|
ino,
|
||||||
|
size: file_size,
|
||||||
|
blocks: (file_size + 511) / 512,
|
||||||
|
atime: SystemTime::now(),
|
||||||
|
mtime: SystemTime::now(),
|
||||||
|
ctime: SystemTime::now(),
|
||||||
|
crtime: SystemTime::now(),
|
||||||
|
kind: if node_type == "folder" {
|
||||||
|
FileType::Directory
|
||||||
|
} else {
|
||||||
|
FileType::RegularFile
|
||||||
|
},
|
||||||
|
perm: if node_type == "folder" { 0o755 } else { 0o644 },
|
||||||
|
nlink: if node_type == "folder" { 2 } else { 1 },
|
||||||
|
uid: 501, // default user
|
||||||
|
gid: 20, // default group
|
||||||
|
rdev: 0,
|
||||||
|
flags: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for MarkBaseFs {
|
||||||
|
fn lookup(&mut self, _req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
|
||||||
|
let parent_path = if parent == 1 { "/" } else { "" };
|
||||||
|
let full_path = format!("{}/{}", parent_path, name.to_string_lossy());
|
||||||
|
|
||||||
|
let node_id = self.find_node_id_by_path(&full_path);
|
||||||
|
|
||||||
|
match node_id {
|
||||||
|
Some(id) => {
|
||||||
|
let info = self.db.get_node_info(&id).ok();
|
||||||
|
|
||||||
|
match info {
|
||||||
|
Some((node_type, file_size)) => {
|
||||||
|
let ino = self.get_or_create_inode(&id);
|
||||||
|
let attr = self.make_file_attr(ino, &node_type, file_size);
|
||||||
|
|
||||||
|
reply.entry(&TTL, &attr, 0);
|
||||||
|
}
|
||||||
|
_ => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
|
||||||
|
if ino == 1 {
|
||||||
|
let attr = self.make_file_attr(1, "folder", 0);
|
||||||
|
reply.attr(&TTL, &attr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_id = self.find_node_id_by_inode(ino);
|
||||||
|
|
||||||
|
match node_id {
|
||||||
|
Some(id) => {
|
||||||
|
let info = self.db.get_node_info(&id).ok();
|
||||||
|
|
||||||
|
match info {
|
||||||
|
Some((node_type, file_size)) => {
|
||||||
|
let attr = self.make_file_attr(ino, &node_type, file_size);
|
||||||
|
reply.attr(&TTL, &attr);
|
||||||
|
}
|
||||||
|
_ => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request,
|
||||||
|
ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: u32,
|
||||||
|
reply: ReplyData,
|
||||||
|
) {
|
||||||
|
let node_id = self.find_node_id_by_inode(ino);
|
||||||
|
|
||||||
|
match node_id {
|
||||||
|
Some(id) => {
|
||||||
|
let file_path = self.get_file_path(&id);
|
||||||
|
|
||||||
|
match file_path {
|
||||||
|
Some(fp) => {
|
||||||
|
let mut file = File::open(&fp)?;
|
||||||
|
file.seek(SeekFrom::Start(offset as u64)).ok();
|
||||||
|
|
||||||
|
let mut buffer = vec![0u8; size as usize];
|
||||||
|
let bytes_read = file.read(&mut buffer).ok();
|
||||||
|
buffer.truncate(bytes_read);
|
||||||
|
reply.data(&buffer);
|
||||||
|
}
|
||||||
|
None => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request,
|
||||||
|
ino: u64,
|
||||||
|
fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
mut reply: ReplyDirectory,
|
||||||
|
) {
|
||||||
|
if offset == 0 {
|
||||||
|
reply.add(ino, 1, FileType::Directory, ".");
|
||||||
|
reply.add(ino, 2, FileType::Directory, "..");
|
||||||
|
|
||||||
|
if ino == 1 {
|
||||||
|
// Root - show Home folder
|
||||||
|
reply.add(2, 3, FileType::Directory, "Home");
|
||||||
|
} else {
|
||||||
|
let node_id = self.find_node_id_by_inode(ino);
|
||||||
|
|
||||||
|
match node_id {
|
||||||
|
Some(id) => {
|
||||||
|
let children = self.db.list_children(&id).ok();
|
||||||
|
let mut child_offset = 3;
|
||||||
|
|
||||||
|
for child_name in children {
|
||||||
|
reply.add(
|
||||||
|
child_offset,
|
||||||
|
child_offset + 1,
|
||||||
|
FileType::RegularFile,
|
||||||
|
&child_name,
|
||||||
|
);
|
||||||
|
child_offset += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user