2020-02-20 12:44:21 +01:00
use std ::fs ::File ;
2020-02-19 16:10:18 +01:00
use std ::io ::{ self , Read , Write } ;
2020-02-19 16:03:42 +01:00
use std ::os ::unix ::ffi ::OsStrExt ;
2020-04-24 10:41:49 +02:00
use anyhow ::{ bail , format_err , Error } ;
2020-02-19 16:03:42 +01:00
use pxar ::accessor ::Accessor ;
2020-03-04 16:39:07 +01:00
use pxar ::encoder ::{ Encoder , SeqWrite } ;
2020-04-24 10:41:49 +02:00
use pxar ::Metadata ;
2020-02-19 16:03:42 +01:00
fn main ( ) -> Result < ( ) , Error > {
2020-02-20 12:44:21 +01:00
let mut args = std ::env ::args_os ( ) ;
let _ = args . next ( ) ;
2020-02-19 16:03:42 +01:00
let cmd = args
. next ( )
. ok_or_else ( | | format_err! ( " expected a command (ls or cat) " ) ) ? ;
let cmd = cmd
. to_str ( )
. ok_or_else ( | | format_err! ( " expected a valid command string (utf-8) " ) ) ? ;
match cmd {
2020-02-20 12:44:21 +01:00
" create " = > return cmd_create ( args ) ,
2020-02-19 16:03:42 +01:00
" ls " | " cat " = > ( ) ,
_ = > bail! ( " valid commands are: cat, ls " ) ,
}
let file = args
. next ( )
. ok_or_else ( | | format_err! ( " expected a file name " ) ) ? ;
2020-02-20 11:16:35 +01:00
let accessor = Accessor ::open ( file ) ? ;
let dir = accessor . open_root_ref ( ) ? ;
2020-02-19 16:03:42 +01:00
2020-02-19 16:10:18 +01:00
let mut buf = Vec ::new ( ) ;
2020-02-19 16:03:42 +01:00
for file in args {
let entry = dir
. lookup ( & file ) ?
. ok_or_else ( | | format_err! ( " no such file in archive: {:?} " , file ) ) ? ;
2020-02-19 16:10:18 +01:00
if cmd = = " ls " {
if file . as_bytes ( ) . ends_with ( b " / " ) {
for file in entry . enter_directory ( ) ? . read_dir ( ) {
println! ( " {:?} " , file ? . file_name ( ) ) ;
}
} else {
println! ( " {:?} " , entry . metadata ( ) ) ;
2020-02-19 16:03:42 +01:00
}
2020-02-19 16:10:18 +01:00
} else if cmd = = " cat " {
buf . clear ( ) ;
entry . contents ( ) ? . read_to_end ( & mut buf ) ? ;
io ::stdout ( ) . write_all ( & buf ) ? ;
2020-02-19 16:03:42 +01:00
} else {
2020-02-19 16:10:18 +01:00
bail! ( " unknown command: {} " , cmd ) ;
2020-02-19 16:03:42 +01:00
}
}
Ok ( ( ) )
}
2020-02-20 12:44:21 +01:00
fn cmd_create ( mut args : std ::env ::ArgsOs ) -> Result < ( ) , Error > {
let file = args
. next ( )
. ok_or_else ( | | format_err! ( " expected a file name " ) ) ? ;
2020-03-04 16:39:07 +01:00
let dir_path = args
. next ( )
. ok_or_else ( | | format_err! ( " expected a directory " ) ) ? ;
if args . next ( ) . is_some ( ) {
bail! ( " too many parameters, there can only be a single root directory in a pxar archive " ) ;
2020-02-20 12:44:21 +01:00
}
2020-03-04 16:39:07 +01:00
// we use "simple" directory traversal without `openat()`
let meta = Metadata ::from ( std ::fs ::metadata ( & dir_path ) ? ) ;
let dir = std ::fs ::read_dir ( dir_path ) ? ;
let mut encoder = Encoder ::create ( file , & meta ) ? ;
add_directory ( & mut encoder , dir ) ? ;
2020-04-24 10:41:49 +02:00
encoder . finish ( ) ? ;
2020-02-20 12:44:21 +01:00
Ok ( ( ) )
}
2020-03-04 16:39:07 +01:00
fn add_directory < ' a , T : SeqWrite + ' a > (
encoder : & mut Encoder < T > ,
dir : std ::fs ::ReadDir ,
) -> Result < ( ) , Error > {
for file in dir {
let file = file ? ;
let file_name = file . file_name ( ) ;
if file_name = = " . " | | file_name = = " .. " {
continue ;
}
println! ( " {:?} " , file . path ( ) ) ;
let file_type = file . file_type ( ) ? ;
let file_meta = file . metadata ( ) ? ;
let meta = Metadata ::from ( & file_meta ) ;
if file_type . is_dir ( ) {
let mut dir = encoder . create_directory ( file_name , & meta ) ? ;
add_directory ( & mut dir , std ::fs ::read_dir ( file . path ( ) ) ? ) ? ;
2020-04-24 10:41:49 +02:00
dir . finish ( ) ? ;
2020-03-04 16:39:07 +01:00
} else if file_type . is_symlink ( ) {
todo! ( " symlink handling " ) ;
} else if file_type . is_file ( ) {
encoder . add_file (
& meta ,
file_name ,
file_meta . len ( ) ,
& mut File ::open ( file . path ( ) ) ? ,
) ? ;
} else {
todo! ( " special file handling " ) ;
}
}
Ok ( ( ) )
}