src/pxar/sequential_decoder.rs: remove callback from new()
And use an extra functzion set_callback() to configure that. Also rewrite pxar/fuse.rs and implement a generic Session (will get further cleanups with next patches).
This commit is contained in:
parent
49fddd985c
commit
f701d0335e
|
@ -21,7 +21,7 @@ pub struct Shell {
|
||||||
/// List of paths selected for a restore
|
/// List of paths selected for a restore
|
||||||
selected: HashSet<Vec<u8>>,
|
selected: HashSet<Vec<u8>>,
|
||||||
/// Decoder instance for the current pxar archive
|
/// Decoder instance for the current pxar archive
|
||||||
decoder: Decoder<BufferedDynamicReader<RemoteChunkReader>, fn(&Path) -> Result<(), Error>>,
|
decoder: Decoder<BufferedDynamicReader<RemoteChunkReader>>,
|
||||||
/// Root directory for the give archive as stored in the catalog
|
/// Root directory for the give archive as stored in the catalog
|
||||||
root: Vec<DirEntry>,
|
root: Vec<DirEntry>,
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ impl Shell {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut catalog: CatalogReader<std::fs::File>,
|
mut catalog: CatalogReader<std::fs::File>,
|
||||||
archive_name: &str,
|
archive_name: &str,
|
||||||
decoder: Decoder<BufferedDynamicReader<RemoteChunkReader>, fn(&Path) -> Result<(), Error>>
|
decoder: Decoder<BufferedDynamicReader<RemoteChunkReader>>
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let catalog_root = catalog.root()?;
|
let catalog_root = catalog.root()?;
|
||||||
// The root for the given archive as stored in the catalog
|
// The root for the given archive as stored in the catalog
|
||||||
|
|
|
@ -1032,7 +1032,8 @@ async fn restore_do(param: Value) -> Result<Value, Error> {
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
|
|
||||||
let feature_flags = pxar::flags::DEFAULT;
|
let feature_flags = pxar::flags::DEFAULT;
|
||||||
let mut decoder = pxar::SequentialDecoder::new(&mut reader, feature_flags, |path| {
|
let mut decoder = pxar::SequentialDecoder::new(&mut reader, feature_flags);
|
||||||
|
decoder.set_callback(move |path| {
|
||||||
if verbose {
|
if verbose {
|
||||||
eprintln!("{:?}", path);
|
eprintln!("{:?}", path);
|
||||||
}
|
}
|
||||||
|
@ -1706,11 +1707,8 @@ async fn mount_do(param: Value, pipe: Option<RawFd>) -> Result<Value, Error> {
|
||||||
let most_used = index.find_most_used_chunks(8);
|
let most_used = index.find_most_used_chunks(8);
|
||||||
let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, most_used);
|
let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, most_used);
|
||||||
let reader = BufferedDynamicReader::new(index, chunk_reader);
|
let reader = BufferedDynamicReader::new(index, chunk_reader);
|
||||||
let decoder =
|
//let decoder = pxar::Decoder::new(Box::<dyn pxar::fuse::ReadSeek>::new(reader))?;
|
||||||
pxar::Decoder::<Box<dyn pxar::fuse::ReadSeek>, fn(&Path) -> Result<(), Error>>::new(
|
let decoder = pxar::Decoder::new(reader)?;
|
||||||
Box::new(reader),
|
|
||||||
|_| Ok(()),
|
|
||||||
)?;
|
|
||||||
let options = OsStr::new("ro,default_permissions");
|
let options = OsStr::new("ro,default_permissions");
|
||||||
let mut session = pxar::fuse::Session::from_decoder(decoder, &options, pipe.is_none())
|
let mut session = pxar::fuse::Session::from_decoder(decoder, &options, pipe.is_none())
|
||||||
.map_err(|err| format_err!("pxar mount failed: {}", err))?;
|
.map_err(|err| format_err!("pxar mount failed: {}", err))?;
|
||||||
|
@ -1822,14 +1820,11 @@ async fn catalog_shell(param: Value) -> Result<Value, Error> {
|
||||||
let most_used = index.find_most_used_chunks(8);
|
let most_used = index.find_most_used_chunks(8);
|
||||||
let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config.clone(), most_used);
|
let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config.clone(), most_used);
|
||||||
let reader = BufferedDynamicReader::new(index, chunk_reader);
|
let reader = BufferedDynamicReader::new(index, chunk_reader);
|
||||||
let decoder =
|
let mut decoder = pxar::Decoder::new(reader)?;
|
||||||
pxar::Decoder::<BufferedDynamicReader<RemoteChunkReader>, fn(&Path) -> Result<(), Error>>::new(
|
decoder.set_callback(|path| {
|
||||||
reader,
|
println!("{:?}", path);
|
||||||
|path| {
|
Ok(())
|
||||||
println!("{:?}", path);
|
});
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let tmpfile = client.download(CATALOG_NAME, tmpfile).await?;
|
let tmpfile = client.download(CATALOG_NAME, tmpfile).await?;
|
||||||
let index = DynamicIndexReader::new(tmpfile)
|
let index = DynamicIndexReader::new(tmpfile)
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn dump_archive_from_reader<R: std::io::Read>(
|
||||||
feature_flags: u64,
|
feature_flags: u64,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut decoder = pxar::SequentialDecoder::new(reader, feature_flags, |_| Ok(()));
|
let mut decoder = pxar::SequentialDecoder::new(reader, feature_flags);
|
||||||
|
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
let mut out = stdout.lock();
|
let mut out = stdout.lock();
|
||||||
|
@ -70,7 +70,8 @@ fn extract_archive_from_reader<R: std::io::Read>(
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
pattern: Option<Vec<pxar::MatchPattern>>
|
pattern: Option<Vec<pxar::MatchPattern>>
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut decoder = pxar::SequentialDecoder::new(reader, feature_flags, |path| {
|
let mut decoder = pxar::SequentialDecoder::new(reader, feature_flags);
|
||||||
|
decoder.set_callback(move |path| {
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("{:?}", path);
|
println!("{:?}", path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ impl PxarDecodeWriter {
|
||||||
|
|
||||||
let child = thread::spawn(move|| {
|
let child = thread::spawn(move|| {
|
||||||
let mut reader = unsafe { std::fs::File::from_raw_fd(rx) };
|
let mut reader = unsafe { std::fs::File::from_raw_fd(rx) };
|
||||||
let mut decoder = pxar::SequentialDecoder::new(&mut reader, pxar::flags::DEFAULT, |path| {
|
let mut decoder = pxar::SequentialDecoder::new(&mut reader, pxar::flags::DEFAULT);
|
||||||
|
decoder.set_callback(move |path| {
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("{:?}", path);
|
println!("{:?}", path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ pub struct DirectoryEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This one needs Read+Seek
|
// This one needs Read+Seek
|
||||||
pub struct Decoder<R: Read + Seek, F: Fn(&Path) -> Result<(), Error>> {
|
pub struct Decoder<R: Read + Seek> {
|
||||||
inner: SequentialDecoder<R, F>,
|
inner: SequentialDecoder<R>,
|
||||||
root_start: u64,
|
root_start: u64,
|
||||||
root_end: u64,
|
root_end: u64,
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,16 @@ pub struct Decoder<R: Read + Seek, F: Fn(&Path) -> Result<(), Error>> {
|
||||||
const HEADER_SIZE: u64 = std::mem::size_of::<PxarHeader>() as u64;
|
const HEADER_SIZE: u64 = std::mem::size_of::<PxarHeader>() as u64;
|
||||||
const GOODBYE_ITEM_SIZE: u64 = std::mem::size_of::<PxarGoodbyeItem>() as u64;
|
const GOODBYE_ITEM_SIZE: u64 = std::mem::size_of::<PxarGoodbyeItem>() as u64;
|
||||||
|
|
||||||
impl<R: Read + Seek, F: Fn(&Path) -> Result<(), Error>> Decoder<R, F> {
|
impl<R: Read + Seek> Decoder<R> {
|
||||||
pub fn new(mut reader: R, callback: F) -> Result<Self, Error> {
|
pub fn new(mut reader: R) -> Result<Self, Error> {
|
||||||
let root_end = reader.seek(SeekFrom::End(0))?;
|
let root_end = reader.seek(SeekFrom::End(0))?;
|
||||||
|
let inner = SequentialDecoder::new(reader, super::flags::DEFAULT);
|
||||||
|
|
||||||
|
Ok(Self { inner, root_start: 0, root_end })
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
pub fn set_callback<F: Fn(&Path) -> Result<(), Error> + 'static>(&mut self, callback: F ) {
|
||||||
inner: SequentialDecoder::new(reader, super::flags::DEFAULT, callback),
|
self.inner.set_callback(callback);
|
||||||
root_start: 0,
|
|
||||||
root_end,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root(&mut self) -> Result<DirectoryEntry, Error> {
|
pub fn root(&mut self) -> Result<DirectoryEntry, Error> {
|
||||||
|
|
525
src/pxar/fuse.rs
525
src/pxar/fuse.rs
|
@ -41,13 +41,6 @@ lazy_static! {
|
||||||
static ref CHILD_PARENT: Mutex<HashMap<u64, u64>> = Mutex::new(HashMap::new());
|
static ref CHILD_PARENT: Mutex<HashMap<u64, u64>> = Mutex::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callback function for `super::decoder::Decoder`.
|
|
||||||
///
|
|
||||||
/// At the moment, this is only needed to satisfy the `SequentialDecoder`.
|
|
||||||
fn decoder_callback(_path: &Path) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
type Inode = u64;
|
type Inode = u64;
|
||||||
type Offset = u64;
|
type Offset = u64;
|
||||||
/// FFI types for easier readability
|
/// FFI types for easier readability
|
||||||
|
@ -88,14 +81,10 @@ struct FuseArgs {
|
||||||
allocated: c_int,
|
allocated: c_int,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to create ReadSeek Decoder trait objects.
|
|
||||||
pub trait ReadSeek: Read + Seek {}
|
|
||||||
impl<T: Read + Seek> ReadSeek for T {}
|
|
||||||
|
|
||||||
/// `Context` for callback functions providing the decoder, caches and the
|
/// `Context` for callback functions providing the decoder, caches and the
|
||||||
/// offset within the archive for the i-node given by the caller.
|
/// offset within the archive for the i-node given by the caller.
|
||||||
struct Context {
|
struct Context<R: Read + Seek> {
|
||||||
decoder: Decoder<Box<dyn ReadSeek>, fn(&Path) -> Result<(), Error>>,
|
decoder: Decoder<R>,
|
||||||
goodbye_cache: Option<(Inode, Vec<(PxarGoodbyeItem, Offset, Offset)>)>,
|
goodbye_cache: Option<(Inode, Vec<(PxarGoodbyeItem, Offset, Offset)>)>,
|
||||||
attr_cache: Option<(Inode, PxarAttributes)>,
|
attr_cache: Option<(Inode, PxarAttributes)>,
|
||||||
ino_offset: Offset,
|
ino_offset: Offset,
|
||||||
|
@ -103,9 +92,10 @@ struct Context {
|
||||||
|
|
||||||
/// `Session` stores a pointer to the session context and is used to mount the
|
/// `Session` stores a pointer to the session context and is used to mount the
|
||||||
/// archive to the given mountpoint.
|
/// archive to the given mountpoint.
|
||||||
pub struct Session {
|
pub struct Session<R: Read + Seek> {
|
||||||
ptr: MutPtr,
|
ptr: MutPtr,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
_phantom: std::marker::PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Operations` defines the callback function table of supported operations.
|
/// `Operations` defines the callback function table of supported operations.
|
||||||
|
@ -161,7 +151,27 @@ struct Operations {
|
||||||
copy_file_range: Option<extern fn(req: Request, ino_in: u64, off_in: c_int, fi_in: MutPtr, ino_out: u64, off_out: c_int, fi_out: MutPtr, len: size_t, flags: c_int)>,
|
copy_file_range: Option<extern fn(req: Request, ino_in: u64, off_in: c_int, fi_in: MutPtr, ino_out: u64, off_out: c_int, fi_out: MutPtr, len: size_t, flags: c_int)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session<BufReader<File>> {
|
||||||
|
/// Create a new low level fuse session.
|
||||||
|
///
|
||||||
|
/// `Session` is created using the provided mount options and sets the
|
||||||
|
/// default signal handlers.
|
||||||
|
/// Options have to be provided as comma separated OsStr, e.g.
|
||||||
|
/// ("ro,default_permissions").
|
||||||
|
pub fn new(archive_path: &Path, options: &OsStr, verbose: bool) -> Result<Self, Error> {
|
||||||
|
let file = File::open(archive_path)?;
|
||||||
|
let args = Self::setup_args(options, verbose)?;
|
||||||
|
let oprs = Self::setup_callbacks();
|
||||||
|
|
||||||
|
// By storing the decoder as userdata of the session, each request may
|
||||||
|
// access it.
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let decoder = Decoder::new(reader)?;
|
||||||
|
Self::setup_session(decoder, args, oprs, verbose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <R: Read + Seek> Session<R> {
|
||||||
fn setup_args(options: &OsStr, verbose: bool) -> Result<Vec<CString>, Error> {
|
fn setup_args(options: &OsStr, verbose: bool) -> Result<Vec<CString>, Error> {
|
||||||
// First argument should be the executable name
|
// First argument should be the executable name
|
||||||
let mut arguments = vec![
|
let mut arguments = vec![
|
||||||
|
@ -181,18 +191,18 @@ impl Session {
|
||||||
let mut oprs = Operations::default();
|
let mut oprs = Operations::default();
|
||||||
oprs.init = Some(init);
|
oprs.init = Some(init);
|
||||||
oprs.destroy = Some(destroy);
|
oprs.destroy = Some(destroy);
|
||||||
oprs.lookup = Some(lookup);
|
oprs.lookup = Some(Self::lookup);
|
||||||
oprs.getattr = Some(getattr);
|
oprs.getattr = Some(Self::getattr);
|
||||||
oprs.readlink = Some(readlink);
|
oprs.readlink = Some(Self::readlink);
|
||||||
oprs.open = Some(open);
|
oprs.open = Some(Self::open);
|
||||||
oprs.read = Some(read);
|
oprs.read = Some(Self::read);
|
||||||
oprs.opendir = Some(opendir);
|
oprs.opendir = Some(Self::opendir);
|
||||||
oprs.readdir = Some(readdir);
|
oprs.readdir = Some(Self::readdir);
|
||||||
oprs
|
oprs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_session(
|
fn setup_session(
|
||||||
decoder: Decoder<Box<dyn ReadSeek>, fn(&Path) -> Result<(), Error>>,
|
decoder: Decoder<R>,
|
||||||
args: Vec<CString>,
|
args: Vec<CString>,
|
||||||
oprs: Operations,
|
oprs: Operations,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
@ -234,27 +244,10 @@ impl Session {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ptr: session_ptr,
|
ptr: session_ptr,
|
||||||
verbose,
|
verbose,
|
||||||
|
_phantom: std::marker::PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new low level fuse session.
|
|
||||||
///
|
|
||||||
/// `Session` is created using the provided mount options and sets the
|
|
||||||
/// default signal handlers.
|
|
||||||
/// Options have to be provided as comma separated OsStr, e.g.
|
|
||||||
/// ("ro,default_permissions").
|
|
||||||
pub fn new(archive_path: &Path, options: &OsStr, verbose: bool) -> Result<Self, Error> {
|
|
||||||
let file = File::open(archive_path)?;
|
|
||||||
let args = Self::setup_args(options, verbose)?;
|
|
||||||
let oprs = Self::setup_callbacks();
|
|
||||||
|
|
||||||
// By storing the decoder as userdata of the session, each request may
|
|
||||||
// access it.
|
|
||||||
let reader: Box<dyn ReadSeek> = Box::new(BufReader::new(file));
|
|
||||||
let decoder = Decoder::new(reader, decoder_callback as fn(&Path) -> Result<(), Error>)?;
|
|
||||||
Self::setup_session(decoder, args, oprs, verbose)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new low level fuse session using the given `Decoder`.
|
/// Create a new low level fuse session using the given `Decoder`.
|
||||||
///
|
///
|
||||||
/// `Session` is created using the provided mount options and sets the
|
/// `Session` is created using the provided mount options and sets the
|
||||||
|
@ -262,7 +255,7 @@ impl Session {
|
||||||
/// Options have to be provided as comma separated OsStr, e.g.
|
/// Options have to be provided as comma separated OsStr, e.g.
|
||||||
/// ("ro,default_permissions").
|
/// ("ro,default_permissions").
|
||||||
pub fn from_decoder(
|
pub fn from_decoder(
|
||||||
decoder: Decoder<Box<dyn ReadSeek>, fn(&Path) -> Result<(), Error>>,
|
decoder: Decoder<R>,
|
||||||
options: &OsStr,
|
options: &OsStr,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
|
@ -316,9 +309,230 @@ impl Session {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a context providing an exclusive mutable reference to the `Context`.
|
||||||
|
///
|
||||||
|
/// Each callback function needing access to the `Context` can easily get an
|
||||||
|
/// exclusive handle by running the code inside this context.
|
||||||
|
/// Responses with error code can easily be generated by returning with the
|
||||||
|
/// error code.
|
||||||
|
/// The error code will be used to reply to libfuse.
|
||||||
|
fn run_in_context<F>(req: Request, inode: u64, code: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Context<R>) -> Result<(), i32>,
|
||||||
|
{
|
||||||
|
let boxed_ctx = unsafe {
|
||||||
|
let ptr = fuse_req_userdata(req) as *mut Mutex<Context<R>>;
|
||||||
|
Box::from_raw(ptr)
|
||||||
|
};
|
||||||
|
let result = boxed_ctx
|
||||||
|
.lock()
|
||||||
|
.map(|mut ctx| {
|
||||||
|
ctx.ino_offset = match inode {
|
||||||
|
FUSE_ROOT_ID => ctx.decoder.root_end_offset() - GOODBYE_ITEM_SIZE,
|
||||||
|
_ => inode,
|
||||||
|
};
|
||||||
|
code(&mut ctx)
|
||||||
|
})
|
||||||
|
.unwrap_or(Err(libc::EIO));
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
unsafe {
|
||||||
|
let _res = fuse_reply_err(req, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release ownership of boxed context, do not drop it.
|
||||||
|
let _ = Box::into_raw(boxed_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup `name` in the directory referenced by `parent` i-node.
|
||||||
|
///
|
||||||
|
/// Inserts also the child and parent file offset in the hashmap to quickly
|
||||||
|
/// obtain the parent offset based on the child offset.
|
||||||
|
/// Caches goodbye table of parent and attributes of child, if found.
|
||||||
|
extern "C" fn lookup(req: Request, parent: u64, name: StrPtr) {
|
||||||
|
let filename = unsafe { CStr::from_ptr(name) };
|
||||||
|
let hash = super::format_definition::compute_goodbye_hash(filename.to_bytes());
|
||||||
|
|
||||||
|
Self::run_in_context(req, parent, |mut ctx| {
|
||||||
|
// find_ goodbye_entry() will also update the goodbye cache
|
||||||
|
let (child_offset, entry, attr, payload_size) =
|
||||||
|
find_goodbye_entry(&mut ctx, &filename, hash)?;
|
||||||
|
ctx.attr_cache = Some((child_offset, attr));
|
||||||
|
let child_inode = calculate_inode(child_offset, ctx.decoder.root_end_offset());
|
||||||
|
|
||||||
|
let e = EntryParam {
|
||||||
|
inode: child_inode,
|
||||||
|
generation: 1,
|
||||||
|
attr: stat(child_inode, &entry, payload_size)?,
|
||||||
|
attr_timeout: std::f64::MAX,
|
||||||
|
entry_timeout: std::f64::MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the parent for this child entry. Used to get parent offset if
|
||||||
|
// only child offset is known.
|
||||||
|
CHILD_PARENT
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| libc::EIO)?
|
||||||
|
.insert(child_offset, ctx.ino_offset);
|
||||||
|
let _res = unsafe { fuse_reply_entry(req, Some(&e)) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn getattr(req: Request, inode: u64, _fileinfo: MutPtr) {
|
||||||
|
Self::run_in_context(req, inode, |ctx| {
|
||||||
|
let (_, entry, attr, payload_size) = ctx
|
||||||
|
.decoder
|
||||||
|
.attributes(ctx.ino_offset)
|
||||||
|
.map_err(|_| libc::EIO)?;
|
||||||
|
ctx.attr_cache = Some((ctx.ino_offset, attr));
|
||||||
|
let attr = stat(inode, &entry, payload_size)?;
|
||||||
|
let _res = unsafe {
|
||||||
|
// Since fs is read-only, the timeout can be max.
|
||||||
|
let timeout = std::f64::MAX;
|
||||||
|
fuse_reply_attr(req, Some(&attr), timeout)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn readlink(req: Request, inode: u64) {
|
||||||
|
Self::run_in_context(req, inode, |ctx| {
|
||||||
|
let (target, _) = ctx
|
||||||
|
.decoder
|
||||||
|
.read_link(ctx.ino_offset)
|
||||||
|
.map_err(|_| libc::EIO)?;
|
||||||
|
let link = CString::new(target.into_os_string().into_vec()).map_err(|_| libc::EIO)?;
|
||||||
|
let _ret = unsafe { fuse_reply_readlink(req, link.as_ptr()) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn open(req: Request, inode: u64, fileinfo: MutPtr) {
|
||||||
|
Self::run_in_context(req, inode, |ctx| {
|
||||||
|
ctx.decoder.open(ctx.ino_offset).map_err(|_| libc::ENOENT)?;
|
||||||
|
let _ret = unsafe { fuse_reply_open(req, fileinfo) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn read(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
|
||||||
|
Self::run_in_context(req, inode, |ctx| {
|
||||||
|
let mut data = ctx
|
||||||
|
.decoder
|
||||||
|
.read(ctx.ino_offset, size, offset as u64)
|
||||||
|
.map_err(|_| libc::EIO)?;
|
||||||
|
|
||||||
|
let _res = unsafe {
|
||||||
|
let len = data.len();
|
||||||
|
let dptr = data.as_mut_ptr() as *mut c_char;
|
||||||
|
fuse_reply_buf(req, dptr, len)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the directory referenced by the given inode for reading.
|
||||||
|
///
|
||||||
|
/// This simply checks if the inode references a valid directory, no internal
|
||||||
|
/// state identifies the directory as opened.
|
||||||
|
extern "C" fn opendir(req: Request, inode: u64, fileinfo: MutPtr) {
|
||||||
|
Self::run_in_context(req, inode, |ctx| {
|
||||||
|
let (_, entry, _, _) = ctx
|
||||||
|
.decoder
|
||||||
|
.attributes(ctx.ino_offset)
|
||||||
|
.map_err(|_| libc::EIO)?;
|
||||||
|
if (entry.mode as u32 & libc::S_IFMT) != libc::S_IFDIR {
|
||||||
|
return Err(libc::ENOENT);
|
||||||
|
}
|
||||||
|
let _ret = unsafe { fuse_reply_open(req, fileinfo) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read and return the entries of the directory referenced by i-node.
|
||||||
|
///
|
||||||
|
/// Replies to the request with the entries fitting into a buffer of length
|
||||||
|
/// `size`, as requested by the caller.
|
||||||
|
/// `offset` identifies the start index of entries to return. This is used on
|
||||||
|
/// repeated calls, occurring if not all entries fitted into the buffer.
|
||||||
|
/// The goodbye table of the directory is cached in order to speedup repeated
|
||||||
|
/// calls occurring when not all entries fitted in the reply buffer.
|
||||||
|
extern "C" fn readdir(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
|
||||||
|
let offset = offset as usize;
|
||||||
|
|
||||||
|
Self::run_in_context(req, inode, |mut ctx| {
|
||||||
|
update_goodbye_cache(&mut ctx)?;
|
||||||
|
let gb_table = &ctx.goodbye_cache.as_ref().unwrap().1;
|
||||||
|
let n_entries = gb_table.len();
|
||||||
|
let mut buf = ReplyBuf::new(req, size, offset);
|
||||||
|
|
||||||
|
if offset < n_entries {
|
||||||
|
for e in gb_table[offset..gb_table.len()].iter() {
|
||||||
|
let (filename, entry, _, payload_size) =
|
||||||
|
ctx.decoder.attributes(e.1).map_err(|_| libc::EIO)?;
|
||||||
|
let name = CString::new(filename.as_bytes()).map_err(|_| libc::EIO)?;
|
||||||
|
let item_offset = find_offset(&entry, e.1, e.2);
|
||||||
|
let item_inode = calculate_inode(item_offset, ctx.decoder.root_end_offset());
|
||||||
|
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
||||||
|
match buf.add_entry(&name, &attr) {
|
||||||
|
Ok(ReplyBufState::Okay) => {}
|
||||||
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
|
Err(_) => return Err(libc::EIO),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current directory entry "."
|
||||||
|
if offset <= n_entries {
|
||||||
|
let (_, entry, _, payload_size) = ctx
|
||||||
|
.decoder
|
||||||
|
.attributes(ctx.ino_offset)
|
||||||
|
.map_err(|_| libc::EIO)?;
|
||||||
|
// No need to calculate i-node for current dir, since it is given as parameter
|
||||||
|
let attr = stat(inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
||||||
|
let name = CString::new(".").unwrap();
|
||||||
|
match buf.add_entry(&name, &attr) {
|
||||||
|
Ok(ReplyBufState::Okay) => {}
|
||||||
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
|
Err(_) => return Err(libc::EIO),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add parent directory entry ".."
|
||||||
|
if offset <= n_entries + 1 {
|
||||||
|
let parent_off = if inode == FUSE_ROOT_ID {
|
||||||
|
ctx.decoder.root_end_offset() - GOODBYE_ITEM_SIZE
|
||||||
|
} else {
|
||||||
|
let guard = CHILD_PARENT.lock().map_err(|_| libc::EIO)?;
|
||||||
|
*guard.get(&ctx.ino_offset).ok_or_else(|| libc::EIO)?
|
||||||
|
};
|
||||||
|
let (_, entry, _, payload_size) =
|
||||||
|
ctx.decoder.attributes(parent_off).map_err(|_| libc::EIO)?;
|
||||||
|
let item_inode = calculate_inode(parent_off, ctx.decoder.root_end_offset());
|
||||||
|
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
||||||
|
let name = CString::new("..").unwrap();
|
||||||
|
match buf.add_entry(&name, &attr) {
|
||||||
|
Ok(ReplyBufState::Okay) => {}
|
||||||
|
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
||||||
|
Err(_) => return Err(libc::EIO),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.reply_filled()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Session {
|
impl <R: Read + Seek> Drop for Session<R> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
fuse_session_unmount(self.ptr);
|
fuse_session_unmount(self.ptr);
|
||||||
|
@ -328,41 +542,6 @@ impl Drop for Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a context providing an exclusive mutable reference to the `Context`.
|
|
||||||
///
|
|
||||||
/// Each callback function needing access to the `Context` can easily get an
|
|
||||||
/// exclusive handle by running the code inside this context.
|
|
||||||
/// Responses with error code can easily be generated by returning with the
|
|
||||||
/// error code.
|
|
||||||
/// The error code will be used to reply to libfuse.
|
|
||||||
fn run_in_context<F>(req: Request, inode: u64, code: F)
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Context) -> Result<(), i32>,
|
|
||||||
{
|
|
||||||
let boxed_ctx = unsafe {
|
|
||||||
let ptr = fuse_req_userdata(req) as *mut Mutex<Context>;
|
|
||||||
Box::from_raw(ptr)
|
|
||||||
};
|
|
||||||
let result = boxed_ctx
|
|
||||||
.lock()
|
|
||||||
.map(|mut ctx| {
|
|
||||||
ctx.ino_offset = match inode {
|
|
||||||
FUSE_ROOT_ID => ctx.decoder.root_end_offset() - GOODBYE_ITEM_SIZE,
|
|
||||||
_ => inode,
|
|
||||||
};
|
|
||||||
code(&mut ctx)
|
|
||||||
})
|
|
||||||
.unwrap_or(Err(libc::EIO));
|
|
||||||
|
|
||||||
if let Err(err) = result {
|
|
||||||
unsafe {
|
|
||||||
let _res = fuse_reply_err(req, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release ownership of boxed context, do not drop it.
|
|
||||||
let _ = Box::into_raw(boxed_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the correct offset for the item based on its `PxarEntry` mode
|
/// Return the correct offset for the item based on its `PxarEntry` mode
|
||||||
///
|
///
|
||||||
|
@ -414,7 +593,7 @@ struct EntryParam {
|
||||||
|
|
||||||
/// Update the goodbye table to the one corresponding to the i-node offset, both
|
/// Update the goodbye table to the one corresponding to the i-node offset, both
|
||||||
/// given in the `Context`.
|
/// given in the `Context`.
|
||||||
fn update_goodbye_cache(mut ctx: &mut Context) -> Result<(), i32> {
|
fn update_goodbye_cache<R: Read + Seek>(mut ctx: &mut Context<R>) -> Result<(), i32> {
|
||||||
if let Some((off, _)) = &ctx.goodbye_cache {
|
if let Some((off, _)) = &ctx.goodbye_cache {
|
||||||
if *off == ctx.ino_offset {
|
if *off == ctx.ino_offset {
|
||||||
// Cache contains already the correct goodbye table
|
// Cache contains already the correct goodbye table
|
||||||
|
@ -444,8 +623,8 @@ fn update_goodbye_cache(mut ctx: &mut Context) -> Result<(), i32> {
|
||||||
/// The matching items archive offset, entry and payload size are returned.
|
/// The matching items archive offset, entry and payload size are returned.
|
||||||
/// If there is no entry with matching `filename` and `hash` a `libc::ENOENT` is
|
/// If there is no entry with matching `filename` and `hash` a `libc::ENOENT` is
|
||||||
/// returned.
|
/// returned.
|
||||||
fn find_goodbye_entry(
|
fn find_goodbye_entry<R: Read + Seek>(
|
||||||
mut ctx: &mut Context,
|
mut ctx: &mut Context<R>,
|
||||||
filename: &CStr,
|
filename: &CStr,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
) -> Result<(u64, PxarEntry, PxarAttributes, u64), i32> {
|
) -> Result<(u64, PxarEntry, PxarAttributes, u64), i32> {
|
||||||
|
@ -477,41 +656,6 @@ fn find_goodbye_entry(
|
||||||
Err(libc::ENOENT)
|
Err(libc::ENOENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup `name` in the directory referenced by `parent` i-node.
|
|
||||||
///
|
|
||||||
/// Inserts also the child and parent file offset in the hashmap to quickly
|
|
||||||
/// obtain the parent offset based on the child offset.
|
|
||||||
/// Caches goodbye table of parent and attributes of child, if found.
|
|
||||||
extern "C" fn lookup(req: Request, parent: u64, name: StrPtr) {
|
|
||||||
let filename = unsafe { CStr::from_ptr(name) };
|
|
||||||
let hash = super::format_definition::compute_goodbye_hash(filename.to_bytes());
|
|
||||||
|
|
||||||
run_in_context(req, parent, |mut ctx| {
|
|
||||||
// find_ goodbye_entry() will also update the goodbye cache
|
|
||||||
let (child_offset, entry, attr, payload_size) =
|
|
||||||
find_goodbye_entry(&mut ctx, &filename, hash)?;
|
|
||||||
ctx.attr_cache = Some((child_offset, attr));
|
|
||||||
let child_inode = calculate_inode(child_offset, ctx.decoder.root_end_offset());
|
|
||||||
|
|
||||||
let e = EntryParam {
|
|
||||||
inode: child_inode,
|
|
||||||
generation: 1,
|
|
||||||
attr: stat(child_inode, &entry, payload_size)?,
|
|
||||||
attr_timeout: std::f64::MAX,
|
|
||||||
entry_timeout: std::f64::MAX,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the parent for this child entry. Used to get parent offset if
|
|
||||||
// only child offset is known.
|
|
||||||
CHILD_PARENT
|
|
||||||
.lock()
|
|
||||||
.map_err(|_| libc::EIO)?
|
|
||||||
.insert(child_offset, ctx.ino_offset);
|
|
||||||
let _res = unsafe { fuse_reply_entry(req, Some(&e)) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `libc::stat` with the provided i-node, entry and payload size
|
/// Create a `libc::stat` with the provided i-node, entry and payload size
|
||||||
fn stat(inode: u64, entry: &PxarEntry, payload_size: u64) -> Result<libc::stat, i32> {
|
fn stat(inode: u64, entry: &PxarEntry, payload_size: u64) -> Result<libc::stat, i32> {
|
||||||
|
@ -540,82 +684,6 @@ fn stat(inode: u64, entry: &PxarEntry, payload_size: u64) -> Result<libc::stat,
|
||||||
Ok(attr)
|
Ok(attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn getattr(req: Request, inode: u64, _fileinfo: MutPtr) {
|
|
||||||
run_in_context(req, inode, |ctx| {
|
|
||||||
let (_, entry, attr, payload_size) = ctx
|
|
||||||
.decoder
|
|
||||||
.attributes(ctx.ino_offset)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
ctx.attr_cache = Some((ctx.ino_offset, attr));
|
|
||||||
let attr = stat(inode, &entry, payload_size)?;
|
|
||||||
let _res = unsafe {
|
|
||||||
// Since fs is read-only, the timeout can be max.
|
|
||||||
let timeout = std::f64::MAX;
|
|
||||||
fuse_reply_attr(req, Some(&attr), timeout)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn readlink(req: Request, inode: u64) {
|
|
||||||
run_in_context(req, inode, |ctx| {
|
|
||||||
let (target, _) = ctx
|
|
||||||
.decoder
|
|
||||||
.read_link(ctx.ino_offset)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
let link = CString::new(target.into_os_string().into_vec()).map_err(|_| libc::EIO)?;
|
|
||||||
let _ret = unsafe { fuse_reply_readlink(req, link.as_ptr()) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn open(req: Request, inode: u64, fileinfo: MutPtr) {
|
|
||||||
run_in_context(req, inode, |ctx| {
|
|
||||||
ctx.decoder.open(ctx.ino_offset).map_err(|_| libc::ENOENT)?;
|
|
||||||
let _ret = unsafe { fuse_reply_open(req, fileinfo) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn read(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
|
|
||||||
run_in_context(req, inode, |ctx| {
|
|
||||||
let mut data = ctx
|
|
||||||
.decoder
|
|
||||||
.read(ctx.ino_offset, size, offset as u64)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
|
|
||||||
let _res = unsafe {
|
|
||||||
let len = data.len();
|
|
||||||
let dptr = data.as_mut_ptr() as *mut c_char;
|
|
||||||
fuse_reply_buf(req, dptr, len)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open the directory referenced by the given inode for reading.
|
|
||||||
///
|
|
||||||
/// This simply checks if the inode references a valid directory, no internal
|
|
||||||
/// state identifies the directory as opened.
|
|
||||||
extern "C" fn opendir(req: Request, inode: u64, fileinfo: MutPtr) {
|
|
||||||
run_in_context(req, inode, |ctx| {
|
|
||||||
let (_, entry, _, _) = ctx
|
|
||||||
.decoder
|
|
||||||
.attributes(ctx.ino_offset)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
if (entry.mode as u32 & libc::S_IFMT) != libc::S_IFDIR {
|
|
||||||
return Err(libc::ENOENT);
|
|
||||||
}
|
|
||||||
let _ret = unsafe { fuse_reply_open(req, fileinfo) };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// State of ReplyBuf after last add_entry call
|
/// State of ReplyBuf after last add_entry call
|
||||||
enum ReplyBufState {
|
enum ReplyBufState {
|
||||||
/// Entry was successfully added to ReplyBuf
|
/// Entry was successfully added to ReplyBuf
|
||||||
|
@ -686,76 +754,3 @@ impl ReplyBuf {
|
||||||
Ok(ReplyBufState::Okay)
|
Ok(ReplyBufState::Okay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read and return the entries of the directory referenced by i-node.
|
|
||||||
///
|
|
||||||
/// Replies to the request with the entries fitting into a buffer of length
|
|
||||||
/// `size`, as requested by the caller.
|
|
||||||
/// `offset` identifies the start index of entries to return. This is used on
|
|
||||||
/// repeated calls, occurring if not all entries fitted into the buffer.
|
|
||||||
/// The goodbye table of the directory is cached in order to speedup repeated
|
|
||||||
/// calls occurring when not all entries fitted in the reply buffer.
|
|
||||||
extern "C" fn readdir(req: Request, inode: u64, size: size_t, offset: c_int, _fileinfo: MutPtr) {
|
|
||||||
let offset = offset as usize;
|
|
||||||
|
|
||||||
run_in_context(req, inode, |mut ctx| {
|
|
||||||
update_goodbye_cache(&mut ctx)?;
|
|
||||||
let gb_table = &ctx.goodbye_cache.as_ref().unwrap().1;
|
|
||||||
let n_entries = gb_table.len();
|
|
||||||
let mut buf = ReplyBuf::new(req, size, offset);
|
|
||||||
|
|
||||||
if offset < n_entries {
|
|
||||||
for e in gb_table[offset..gb_table.len()].iter() {
|
|
||||||
let (filename, entry, _, payload_size) =
|
|
||||||
ctx.decoder.attributes(e.1).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new(filename.as_bytes()).map_err(|_| libc::EIO)?;
|
|
||||||
let item_offset = find_offset(&entry, e.1, e.2);
|
|
||||||
let item_inode = calculate_inode(item_offset, ctx.decoder.root_end_offset());
|
|
||||||
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
match buf.add_entry(&name, &attr) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add current directory entry "."
|
|
||||||
if offset <= n_entries {
|
|
||||||
let (_, entry, _, payload_size) = ctx
|
|
||||||
.decoder
|
|
||||||
.attributes(ctx.ino_offset)
|
|
||||||
.map_err(|_| libc::EIO)?;
|
|
||||||
// No need to calculate i-node for current dir, since it is given as parameter
|
|
||||||
let attr = stat(inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new(".").unwrap();
|
|
||||||
match buf.add_entry(&name, &attr) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add parent directory entry ".."
|
|
||||||
if offset <= n_entries + 1 {
|
|
||||||
let parent_off = if inode == FUSE_ROOT_ID {
|
|
||||||
ctx.decoder.root_end_offset() - GOODBYE_ITEM_SIZE
|
|
||||||
} else {
|
|
||||||
let guard = CHILD_PARENT.lock().map_err(|_| libc::EIO)?;
|
|
||||||
*guard.get(&ctx.ino_offset).ok_or_else(|| libc::EIO)?
|
|
||||||
};
|
|
||||||
let (_, entry, _, payload_size) =
|
|
||||||
ctx.decoder.attributes(parent_off).map_err(|_| libc::EIO)?;
|
|
||||||
let item_inode = calculate_inode(parent_off, ctx.decoder.root_end_offset());
|
|
||||||
let attr = stat(item_inode, &entry, payload_size).map_err(|_| libc::EIO)?;
|
|
||||||
let name = CString::new("..").unwrap();
|
|
||||||
match buf.add_entry(&name, &attr) {
|
|
||||||
Ok(ReplyBufState::Okay) => {}
|
|
||||||
Ok(ReplyBufState::Overfull) => return buf.reply_filled(),
|
|
||||||
Err(_) => return Err(libc::EIO),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.reply_filled()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,18 +30,22 @@ use crate::tools::fs;
|
||||||
use crate::tools::xattr;
|
use crate::tools::xattr;
|
||||||
|
|
||||||
// This one need Read, but works without Seek
|
// This one need Read, but works without Seek
|
||||||
pub struct SequentialDecoder<R: Read, F: Fn(&Path) -> Result<(), Error>> {
|
pub struct SequentialDecoder<R: Read> {
|
||||||
reader: R,
|
reader: R,
|
||||||
feature_flags: u64,
|
feature_flags: u64,
|
||||||
allow_existing_dirs: bool,
|
allow_existing_dirs: bool,
|
||||||
skip_buffer: Vec<u8>,
|
skip_buffer: Vec<u8>,
|
||||||
callback: F,
|
callback: Option<Box<dyn Fn(&Path) -> Result<(), Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const HEADER_SIZE: u64 = std::mem::size_of::<PxarHeader>() as u64;
|
const HEADER_SIZE: u64 = std::mem::size_of::<PxarHeader>() as u64;
|
||||||
|
|
||||||
impl<R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<R, F> {
|
impl<R: Read> SequentialDecoder<R> {
|
||||||
pub fn new(reader: R, feature_flags: u64, callback: F) -> Self {
|
|
||||||
|
pub fn new(
|
||||||
|
reader: R,
|
||||||
|
feature_flags: u64,
|
||||||
|
) -> Self {
|
||||||
let skip_buffer = vec::undefined(64 * 1024);
|
let skip_buffer = vec::undefined(64 * 1024);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -49,10 +53,14 @@ impl<R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<R, F> {
|
||||||
feature_flags,
|
feature_flags,
|
||||||
allow_existing_dirs: false,
|
allow_existing_dirs: false,
|
||||||
skip_buffer,
|
skip_buffer,
|
||||||
callback,
|
callback: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_callback<F: Fn(&Path) -> Result<(), Error> + 'static>(&mut self, callback: F ) {
|
||||||
|
self.callback = Some(Box::new(callback));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_allow_existing_dirs(&mut self, allow: bool) {
|
pub fn set_allow_existing_dirs(&mut self, allow: bool) {
|
||||||
self.allow_existing_dirs = allow;
|
self.allow_existing_dirs = allow;
|
||||||
}
|
}
|
||||||
|
@ -793,7 +801,9 @@ impl<R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<R, F> {
|
||||||
let (target, _offset) = self.read_hardlink(head.size)?;
|
let (target, _offset) = self.read_hardlink(head.size)?;
|
||||||
let target_path = base_path.join(&target);
|
let target_path = base_path.join(&target);
|
||||||
if dirs.last_dir_fd().is_some() {
|
if dirs.last_dir_fd().is_some() {
|
||||||
(self.callback)(&full_path)?;
|
if let Some(ref callback) = self.callback {
|
||||||
|
(callback)(&full_path)?;
|
||||||
|
}
|
||||||
hardlink(&target_path, &full_path)?;
|
hardlink(&target_path, &full_path)?;
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -826,7 +836,9 @@ impl<R: Read, F: Fn(&Path) -> Result<(), Error>> SequentialDecoder<R, F> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if fd.is_some() {
|
if fd.is_some() {
|
||||||
(self.callback)(&full_path)?;
|
if let Some(ref callback) = self.callback {
|
||||||
|
(callback)(&full_path)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match ifmt {
|
match ifmt {
|
||||||
|
|
Loading…
Reference in New Issue