]> git.sesse.net Git - bcachefs-tools-debian/blob - rust-src/src/cmd_list.rs
3f86b8cd427db4f18b287d8c973d3a07cc912cd6
[bcachefs-tools-debian] / rust-src / src / cmd_list.rs
1 use atty::Stream;
2 use log::{error};
3 use bch_bindgen::bcachefs;
4 use bch_bindgen::opt_set;
5 use bch_bindgen::fs::Fs;
6 use bch_bindgen::bkey::BkeySC;
7 use bch_bindgen::btree::BtreeTrans;
8 use bch_bindgen::btree::BtreeIter;
9 use bch_bindgen::btree::BtreeNodeIter;
10 use bch_bindgen::btree::BtreeIterFlags;
11 use clap::Parser;
12 use std::ffi::{CStr, OsStr, c_int, c_char};
13 use std::os::unix::ffi::OsStrExt;
14
15 fn list_keys(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
16     let trans = BtreeTrans::new(fs);
17     let mut iter = BtreeIter::new(&trans, opt.btree, opt.start,
18         BtreeIterFlags::ALL_SNAPSHOTS|
19         BtreeIterFlags::PREFETCH);
20
21     while let Some(k) = iter.peek_and_restart()? {
22         if k.k.p > opt.end {
23             break;
24         }
25
26         println!("{}", k.to_text(fs));
27         iter.advance();
28     }
29
30     Ok(())
31 }
32
33 fn list_btree_formats(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
34     let trans = BtreeTrans::new(fs);
35     let mut iter = BtreeNodeIter::new(&trans, opt.btree, opt.start,
36         0, opt.level,
37         BtreeIterFlags::PREFETCH);
38
39     while let Some(b) = iter.peek_and_restart()? {
40         if b.key.k.p > opt.end {
41             break;
42         }
43
44         println!("{}", b.to_text(fs));
45         iter.advance();
46     }
47
48     Ok(())
49 }
50
51 fn list_btree_nodes(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
52     let trans = BtreeTrans::new(fs);
53     let mut iter = BtreeNodeIter::new(&trans, opt.btree, opt.start,
54         0, opt.level,
55         BtreeIterFlags::PREFETCH);
56
57     while let Some(b) = iter.peek_and_restart()? {
58         if b.key.k.p > opt.end {
59             break;
60         }
61
62         println!("{}", BkeySC::from(&b.key).to_text(fs));
63         iter.advance();
64     }
65
66     Ok(())
67 }
68
69 fn list_nodes_ondisk(fs: &Fs, opt: Cli) -> anyhow::Result<()> {
70     let trans = BtreeTrans::new(fs);
71     let mut iter = BtreeNodeIter::new(&trans, opt.btree, opt.start,
72         0, opt.level,
73         BtreeIterFlags::PREFETCH);
74
75     while let Some(b) = iter.peek_and_restart()? {
76         if b.key.k.p > opt.end {
77             break;
78         }
79
80         println!("{}", b.ondisk_to_text(fs));
81         iter.advance();
82     }
83
84     Ok(())
85 }
86
87 #[derive(Clone, clap::ValueEnum)]
88 enum Mode {
89     Keys,
90     Formats,
91     Nodes,
92     NodesOndisk,
93 }
94
95 #[derive(Parser)]
96 struct Cli {
97     /// Btree to list from
98     #[arg(short, long, default_value_t=bcachefs::btree_id::BTREE_ID_extents)]
99     btree:      bcachefs::btree_id,
100
101     /// Btree depth to descend to (0 == leaves)
102     #[arg(short, long, default_value_t=0)]
103     level:      u32,
104
105     /// Start position to list from
106     #[arg(short, long, default_value="POS_MIN")]
107     start:      bcachefs::bpos,
108
109     /// End position
110     #[arg(short, long, default_value="SPOS_MAX")]
111     end:        bcachefs::bpos,
112
113     #[arg(short, long, default_value="keys")]
114     mode:       Mode,
115
116     /// Check (fsck) the filesystem first
117     #[arg(short, long, default_value_t=false)]
118     fsck:       bool,
119
120     /// Force color on/off. Default: autodetect tty
121     #[arg(short, long, action = clap::ArgAction::Set, default_value_t=atty::is(Stream::Stdout))]
122     colorize:   bool,
123    
124     /// Verbose mode
125     #[arg(short, long)]
126     verbose:    bool,
127
128     #[arg(required(true))]
129     devices:    Vec<std::path::PathBuf>,
130 }
131
132 fn cmd_list_inner(opt: Cli) -> anyhow::Result<()> {
133     let mut fs_opts: bcachefs::bch_opts = Default::default();
134
135     opt_set!(fs_opts, nochanges,        1);
136     opt_set!(fs_opts, norecovery,       1);
137     opt_set!(fs_opts, degraded,         1);
138     opt_set!(fs_opts, errors,           bcachefs::bch_error_actions::BCH_ON_ERROR_continue as u8);
139
140     if opt.fsck {
141         opt_set!(fs_opts, fix_errors,   bcachefs::fsck_err_opts::FSCK_FIX_yes as u8);
142         opt_set!(fs_opts, norecovery,   0);
143     }
144
145     if opt.verbose {
146         opt_set!(fs_opts, verbose,      1);
147     }
148
149     let fs = Fs::open(&opt.devices, fs_opts)?;
150
151     match opt.mode {
152         Mode::Keys          => list_keys(&fs, opt),
153         Mode::Formats       => list_btree_formats(&fs, opt),
154         Mode::Nodes         => list_btree_nodes(&fs, opt),
155         Mode::NodesOndisk   => list_nodes_ondisk(&fs, opt),
156     }
157 }
158
159 #[no_mangle]
160 pub extern "C" fn cmd_list(argc: c_int, argv: *const *const c_char) {
161     let argv: Vec<_> = (0..argc)
162         .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
163         .map(|i| OsStr::from_bytes(i.to_bytes()))
164         .collect();
165
166     let opt = Cli::parse_from(argv);
167     colored::control::set_override(opt.colorize);
168     if let Err(e) = cmd_list_inner(opt) {
169         error!("Fatal error: {}", e);
170     }
171 }