1 use std
::collections
::HashMap
;
3 use std
::io
::{self, Read, Write}
;
4 use std
::os
::linux
::fs
::MetadataExt
;
5 use std
::os
::unix
::ffi
::OsStrExt
;
6 use std
::path
::{Path, PathBuf}
;
8 use pxar
::accessor
::Accessor
;
9 use pxar
::encoder
::{Encoder, LinkOffset, SeqWrite}
;
12 macro_rules
! format_err
{
14 std
::io
::Error
::new(std
::io
::ErrorKind
::Other
, format
!($
($msg
)+))
20 return Err(format_err
!($
($msg
)+).into());
24 type Error
= Box
<dyn std
::error
::Error
+ Send
+ Sync
+ '
static>;
26 fn main() -> Result
<(), Error
> {
27 let mut args
= std
::env
::args_os();
32 .ok_or_else(|| format_err
!("expected a command (ls or cat)"))?
;
35 .ok_or_else(|| format_err
!("expected a valid command string (utf-8)"))?
;
37 "create" => return cmd_create(args
),
39 _
=> bail
!("valid commands are: cat, ls"),
44 .ok_or_else(|| format_err
!("expected a file name"))?
;
45 let file
= std
::fs
::File
::open(file
)?
;
46 let accessor
= Accessor
::from_file_ref(&file
)?
;
47 let dir
= accessor
.open_root()?
;
49 let mut buf
= Vec
::new();
54 .ok_or_else(|| format_err
!("no such file in archive: {:?}", file
))?
;
57 if file
.as_bytes().ends_with(b
"/") {
58 for file
in entry
.enter_directory()?
.read_dir() {
59 println
!("{:?}", file?
.file_name());
62 println
!("{:?}", entry
.metadata());
64 } else if cmd
== "cat" {
66 let entry
= if entry
.is_hardlink() {
67 accessor
.follow_hardlink(&entry
)?
71 entry
.contents()?
.read_to_end(&mut buf
)?
;
72 io
::stdout().write_all(&buf
)?
;
74 bail
!("unknown command: {}", cmd
);
81 #[derive(Eq, PartialEq, Hash)]
87 fn cmd_create(mut args
: std
::env
::ArgsOs
) -> Result
<(), Error
> {
90 .ok_or_else(|| format_err
!("expected a file name"))?
;
94 .ok_or_else(|| format_err
!("expected a directory"))?
;
96 if args
.next().is_some() {
97 bail
!("too many parameters, there can only be a single root directory in a pxar archive");
100 let dir_path
= Path
::new(&dir_path
);
102 // we use "simple" directory traversal without `openat()`
103 let meta
= Metadata
::from(std
::fs
::metadata(&dir_path
)?
);
104 let dir
= std
::fs
::read_dir(&dir_path
)?
;
106 let mut encoder
= Encoder
::create(file
, &meta
)?
;
107 add_directory(&mut encoder
, dir
, &dir_path
, &mut HashMap
::new())?
;
113 fn add_directory
<'a
, T
: SeqWrite
+ 'a
>(
114 encoder
: &mut Encoder
<T
>,
115 dir
: std
::fs
::ReadDir
,
117 hardlinks
: &mut HashMap
<HardLinkInfo
, (PathBuf
, LinkOffset
)>,
118 ) -> Result
<(), Error
> {
119 let mut file_list
= Vec
::new();
122 let file_name
= file
.file_name();
123 if file_name
== "." || file_name
== ".." {
127 file_name
.to_os_string(),
128 file
.path().to_path_buf(),
134 file_list
.sort_unstable_by(|a
, b
| (a
.0).cmp(&b
.0));
136 for (file_name
, file_path
, file_type
, file_meta
) in file_list
{
137 println
!("{:?}", file_path
);
139 let meta
= Metadata
::from(&file_meta
);
140 if file_type
.is_dir() {
141 let mut dir
= encoder
.create_directory(file_name
, &meta
)?
;
144 std
::fs
::read_dir(file_path
)?
,
149 } else if file_type
.is_symlink() {
150 todo
!("symlink handling");
151 } else if file_type
.is_file() {
152 let link_info
= HardLinkInfo
{
153 st_dev
: file_meta
.st_dev(),
154 st_ino
: file_meta
.st_ino(),
157 if file_meta
.st_nlink() > 1 {
158 if let Some((path
, offset
)) = hardlinks
.get(&link_info
) {
159 eprintln
!("Adding hardlink {:?} => {:?}", file_name
, path
);
160 encoder
.add_hardlink(file_name
, path
, *offset
)?
;
165 let offset
: LinkOffset
= encoder
.add_file(
169 &mut File
::open(&file_path
)?
,
172 if file_meta
.st_nlink() > 1 {
173 let file_path
= file_path
.strip_prefix(root_path
)?
.to_path_buf();
174 hardlinks
.insert(link_info
, (file_path
, offset
));
177 todo
!("special file handling");