1
1
use crate :: attribute_bits:: AttributeBits ;
2
+ use crate :: display_bytes:: DisplayBytes ;
2
3
use crate :: error:: * ;
3
4
use crate :: opt:: Opt ;
4
5
use crate :: table:: Table ;
5
6
use crate :: types:: PartitionTypeGUID ;
6
7
use crate :: uuid:: { convert_str_to_array, generate_random_uuid, Uuid } ;
8
+ use count_zeroes:: CountZeroes ;
7
9
#[ cfg( target_os = "linux" ) ]
8
10
use gptman:: linux:: reread_partition_table;
9
11
use gptman:: { GPTPartitionEntry , GPT } ;
12
+ use linefeed:: { DefaultTerminal , Signal , Terminal } ;
10
13
use std:: fs;
11
- use std:: io:: { Read , Seek , SeekFrom } ;
14
+ use std:: io:: { Read , Seek , SeekFrom , Write } ;
12
15
use std:: path:: { Path , PathBuf } ;
13
16
14
- const BYTE_UNITS : & [ & str ] = & [ "kB" , "MB" , "GB" , "TB" , "PB" , "EB" , "ZB" , "YB" ] ;
15
-
16
- fn format_bytes ( value : u64 ) -> String {
17
- BYTE_UNITS
18
- . iter ( )
19
- . enumerate ( )
20
- . map ( |( i, u) | ( value / 1000_u64 . pow ( i as u32 + 1 ) , u) )
21
- . take_while ( |( i, _) | * i > 10 )
22
- . map ( |( i, u) | format ! ( "{} {}" , i, u) )
23
- . last ( )
24
- . unwrap_or_else ( || format ! ( "{} B " , value) )
25
- }
17
+ const REFRESH_INTERVAL : std:: time:: Duration = std:: time:: Duration :: from_secs ( 1 ) ;
26
18
27
19
macro_rules! ask_with_default {
28
20
( $ask: expr, $parser: expr, $prompt: expr, $default: expr) => {
89
81
"Z" => randomize ( gpt) ,
90
82
"s" => swap_partition_index ( gpt, ask) ?,
91
83
"C" => copy_all_partitions ( gpt, & opt. device , ask) ?,
84
+ "z" => count_zeroes ( gpt, & opt. device , ask) ?,
92
85
x => println ! ( "{}: unknown command" , x) ,
93
86
}
94
87
@@ -116,13 +109,22 @@ fn help() {
116
109
println ! ( " S toggle the GUID specific bits" ) ;
117
110
println ! ( " t change a partition type" ) ;
118
111
println ! ( " u change partition UUID" ) ;
112
+ println ! ( " z check how empty a partition physically is (number of empty blocks)" ) ;
119
113
println ! ( " Z randomize disk GUID and all partition's GUID" ) ;
120
114
println ! ( ) ;
121
115
println ! ( " q exit without saving" ) ;
122
116
println ! ( " w write table to disk and exit" ) ;
123
117
println ! ( ) ;
124
118
}
125
119
120
+ fn base_path ( path : & Path ) -> String {
121
+ let mut base_path = path. display ( ) . to_string ( ) ;
122
+ if base_path. ends_with ( char:: is_numeric) {
123
+ base_path += "p" ;
124
+ }
125
+ base_path
126
+ }
127
+
126
128
fn open_and_print ( opt : & Opt , path : & Path , disk_order : bool ) -> Result < ( ) > {
127
129
let mut f = fs:: File :: open ( path) ?;
128
130
let len = f. seek ( SeekFrom :: End ( 0 ) ) ?;
@@ -389,7 +391,7 @@ pub fn print(opt: &Opt, path: &Path, gpt: &GPT, len: u64, disk_order: bool) -> R
389
391
gpt. align,
390
392
gpt. align * gpt. sector_size
391
393
) ;
392
- println ! ( "Disk size: {} ({} bytes)" , format_bytes ( len) , len) ;
394
+ println ! ( "Disk size: {} ({} bytes)" , DisplayBytes :: new ( len) , len) ;
393
395
println ! (
394
396
"Usable sectors: {}-{} ({} sectors)" ,
395
397
gpt. header. first_usable_lba, gpt. header. last_usable_lba, usable,
@@ -402,14 +404,14 @@ pub fn print(opt: &Opt, path: &Path, gpt: &GPT, len: u64, disk_order: bool) -> R
402
404
"{}-{} ({})" ,
403
405
i,
404
406
i + l - 1 ,
405
- format_bytes ( l * gpt. sector_size) . trim ( )
407
+ DisplayBytes :: new ( l * gpt. sector_size) ,
406
408
) )
407
409
. collect:: <Vec <_>>( )
408
410
. join( ", " ) ,
409
411
) ;
410
412
println ! (
411
413
"Usable space: {} ({} bytes)" ,
412
- format_bytes ( usable * gpt. sector_size) ,
414
+ DisplayBytes :: new ( usable * gpt. sector_size) ,
413
415
usable * gpt. sector_size,
414
416
) ;
415
417
println ! ( "Disk identifier: {}" , gpt. header. disk_guid. display_uuid( ) ) ;
@@ -441,10 +443,7 @@ pub fn print(opt: &Opt, path: &Path, gpt: &GPT, len: u64, disk_order: bool) -> R
441
443
Column :: Name => table. add_cell ( "Name" ) ,
442
444
}
443
445
}
444
- let mut base_path = path. display ( ) . to_string ( ) ;
445
- if base_path. ends_with ( char:: is_numeric) {
446
- base_path += "p" ;
447
- }
446
+ let base_path = base_path ( path) ;
448
447
449
448
let mut partitions: Vec < _ > = gpt. iter ( ) . filter ( |( _, x) | x. is_used ( ) ) . collect ( ) ;
450
449
@@ -459,7 +458,9 @@ pub fn print(opt: &Opt, path: &Path, gpt: &GPT, len: u64, disk_order: bool) -> R
459
458
Column :: Start => table. add_cell_rtl ( & format ! ( "{}" , p. starting_lba) ) ,
460
459
Column :: End => table. add_cell_rtl ( & format ! ( "{}" , p. ending_lba) ) ,
461
460
Column :: Sectors => table. add_cell_rtl ( & format ! ( "{}" , p. size( ) ?) ) ,
462
- Column :: Size => table. add_cell_rtl ( & format_bytes ( p. size ( ) ? * gpt. sector_size ) ) ,
461
+ Column :: Size => table. add_cell_rtl (
462
+ & DisplayBytes :: new_padded ( p. size ( ) ? * gpt. sector_size ) . to_string ( ) ,
463
+ ) ,
463
464
Column :: Type => {
464
465
table. add_cell ( p. partition_type_guid . display_partition_type_guid ( ) . as_str ( ) )
465
466
}
@@ -899,7 +900,7 @@ where
899
900
"Copy partition {} of {} sectors ({}):" ,
900
901
src_i,
901
902
size,
902
- format_bytes ( size_in_bytes)
903
+ DisplayBytes :: new ( size_in_bytes)
903
904
) ;
904
905
let dst_i = ask_free_slot ( dst_gpt, ask) ?;
905
906
let starting_lba = ask_starting_lba ( dst_gpt, ask, size) ?;
@@ -922,3 +923,71 @@ where
922
923
923
924
Ok ( ( ) )
924
925
}
926
+
927
+ fn count_zeroes < F > ( gpt : & mut GPT , path : & Path , ask : & F ) -> Result < ( ) >
928
+ where
929
+ F : Fn ( & str ) -> Result < String > ,
930
+ {
931
+ let i = ask_used_slot ( gpt, ask) ?;
932
+ let base_path = base_path ( path) ;
933
+ let partition_path = format ! ( "{}{}" , base_path, i) ;
934
+
935
+ let mut f = fs:: File :: open ( partition_path) ?;
936
+ let len = f. seek ( std:: io:: SeekFrom :: End ( 0 ) ) ?;
937
+ f. seek ( std:: io:: SeekFrom :: Start ( 0 ) ) ?;
938
+ let stdout = std:: io:: stdout ( ) ;
939
+ let mut stdout_lock = stdout. lock ( ) ;
940
+ let mut t1 = std:: time:: Instant :: now ( ) ;
941
+ let t2 = std:: time:: Instant :: now ( ) ;
942
+ let progress_len = DisplayBytes :: new ( len) ;
943
+
944
+ macro_rules! display_progress {
945
+ ( $zeroes: expr, $count: expr) => { {
946
+ let _ = write!(
947
+ stdout_lock,
948
+ "\u{001b} [2K\r {}/{} zeroes: {} ({:.2}%) speed: {}/s" ,
949
+ DisplayBytes :: new( $count) ,
950
+ progress_len,
951
+ DisplayBytes :: new( $zeroes) ,
952
+ $zeroes as f64 / $count as f64 * 100.0 ,
953
+ DisplayBytes :: new( $count. checked_div( t2. elapsed( ) . as_secs( ) ) . unwrap_or( 0 ) ) ,
954
+ ) ;
955
+ let _ = stdout_lock. flush( ) ;
956
+ } } ;
957
+ }
958
+
959
+ let mut res = Ok ( ( ) ) ;
960
+ let terminal = DefaultTerminal :: new ( ) ?;
961
+ let mut terminal_lock = terminal. lock_read ( ) ;
962
+ let prev_terminal_state = terminal_lock. prepare ( true , Signal :: Interrupt . into ( ) ) ?;
963
+
964
+ let ( zeroes, count) = f. count_zeroes ( |zeroes : u64 , count : u64 | {
965
+ if t1. elapsed ( ) >= REFRESH_INTERVAL {
966
+ display_progress ! ( zeroes, count) ;
967
+ t1 = std:: time:: Instant :: now ( ) ;
968
+
969
+ match terminal_lock. wait_for_input ( Some ( std:: time:: Duration :: ZERO ) ) {
970
+ Err ( err) => {
971
+ res = Err ( err. into ( ) ) ;
972
+ false
973
+ }
974
+ Ok ( true ) => {
975
+ res = Err ( "Interrupted!" . into ( ) ) ;
976
+ false
977
+ }
978
+ Ok ( false ) => true ,
979
+ }
980
+ } else {
981
+ true
982
+ }
983
+ } ) ?;
984
+
985
+ display_progress ! ( zeroes, count) ;
986
+ let _ = writeln ! ( stdout_lock) ;
987
+
988
+ let mut sink = Vec :: new ( ) ;
989
+ let _ = terminal_lock. read ( & mut sink) ;
990
+ terminal_lock. restore ( prev_terminal_state) ?;
991
+
992
+ res
993
+ }
0 commit comments