]> git.sesse.net Git - bcachefs-tools-debian/blob - mount/src/lib.rs
Remove valgrind build-dep
[bcachefs-tools-debian] / mount / src / lib.rs
1 use structopt::StructOpt;
2 use anyhow::anyhow;
3
4 #[macro_export]
5 macro_rules! c_str {
6         ($lit:expr) => {
7                 unsafe { std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const std::os::raw::c_char)
8                                .to_bytes_with_nul()
9                                .as_ptr() as *const std::os::raw::c_char }
10         };
11 }
12
13 #[derive(Debug)]
14 struct ErrnoError(errno::Errno);
15 impl std::fmt::Display for ErrnoError {
16         fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
17                 self.0.fmt(f)
18         }
19 }
20 impl std::error::Error for ErrnoError {}
21
22 #[derive(Debug)]
23 pub(crate) enum KeyLocation {
24         Fail,
25         Wait,
26         Ask,
27 }
28
29 impl std::str::FromStr for KeyLocation {
30         type Err = anyhow::Error;
31         fn from_str(s: &str) -> anyhow::Result<Self> {
32                 use anyhow::anyhow;
33                 match s {
34                         "fail" => Ok(Self::Fail),
35                         "wait" => Ok(Self::Wait),
36                         "ask" => Ok(Self::Ask),
37                         _ => Err(anyhow!("invalid password option"))
38                 }
39         }
40 }
41
42 #[derive(StructOpt, Debug)]
43 /// Mount a bcachefs filesystem by its UUID.
44 struct Options {
45         /// Where the password would be loaded from.
46         ///
47         /// Possible values are:
48         /// "fail" - don't ask for password, fail if filesystem is encrypted;
49         /// "wait" - wait for password to become available before mounting;
50         /// "ask" -  prompt the user for password;
51         #[structopt(short, long, default_value = "fail")]
52         key_location: KeyLocation,
53
54         /// External UUID of the bcachefs filesystem
55         uuid: uuid::Uuid,
56
57         /// Where the filesystem should be mounted. If not set, then the filesystem
58         /// won't actually be mounted. But all steps preceeding mounting the
59         /// filesystem (e.g. asking for passphrase) will still be performed.
60         mountpoint: Option<std::path::PathBuf>,
61
62         /// Mount options
63         #[structopt(short, default_value = "")]
64         options: String,
65 }
66
67 mod filesystem;
68 mod key;
69 mod keyutils {
70         #![allow(non_upper_case_globals)]
71         #![allow(non_camel_case_types)]
72         #![allow(non_snake_case)]
73         #![allow(unused)]
74
75         include!(concat!(env!("OUT_DIR"), "/keyutils.rs"));
76 }
77
78 mod bcachefs {
79         #![allow(non_upper_case_globals)]
80         #![allow(non_camel_case_types)]
81         #![allow(non_snake_case)]
82         #![allow(unused)]
83
84         include!(concat!(env!("OUT_DIR"), "/bcachefs.rs"));
85
86         use bitfield::bitfield;
87         bitfield! {
88                 pub struct bch_scrypt_flags(u64);
89                 pub N, _: 15, 0;
90                 pub R, _: 31, 16;
91                 pub P, _: 47, 32;
92         }
93         bitfield! {
94                 pub struct bch_crypt_flags(u64);
95                 TYPE, _: 4, 0;
96         }
97         use memoffset::offset_of;
98         impl bch_sb_field_crypt {
99                 pub fn scrypt_flags(&self) -> Option<bch_scrypt_flags> {
100                         let t = bch_crypt_flags(self.flags);
101                         if t.TYPE() != bch_kdf_types::BCH_KDF_SCRYPT as u64 {
102                                 None
103                         } else {
104                                 Some(bch_scrypt_flags(self.kdf_flags))
105                         }
106                 }
107                 pub fn key(&self) -> &bch_encrypted_key {
108                         &self.key
109                 }
110         }
111         impl bch_sb {
112                 pub fn crypt(&self) -> Option<&bch_sb_field_crypt> {
113                         unsafe {
114                                 let ptr = bch2_sb_field_get(
115                                         self as *const _ as *mut _,
116                                         bch_sb_field_type::BCH_SB_FIELD_crypt,
117                                 ) as *const u8;
118                                 if ptr.is_null() {
119                                         None
120                                 } else {
121                                         let offset = offset_of!(bch_sb_field_crypt, field);
122                                         Some(&*((ptr.sub(offset)) as *const _))
123                                 }
124                         }
125                 }
126                 pub fn uuid(&self) -> uuid::Uuid {
127                         uuid::Uuid::from_bytes(self.user_uuid.b)
128                 }
129
130                 /// Get the nonce used to encrypt the superblock
131                 pub fn nonce(&self) -> nonce {
132                         use byteorder::{ReadBytesExt, LittleEndian};
133                         let mut internal_uuid = &self.uuid.b[..];
134                         let dword1 = internal_uuid.read_u32::<LittleEndian>().unwrap();
135                         let dword2 = internal_uuid.read_u32::<LittleEndian>().unwrap();
136                         nonce { d: [0, 0, dword1, dword2] }
137                 }
138         }
139         impl bch_sb_handle {
140                 pub fn sb(&self) -> &bch_sb {
141                         unsafe { &*self.sb }
142                 }
143         }
144 }
145
146 fn main_inner() -> anyhow::Result<()> {
147         use itertools::Itertools;
148         use log::{info, trace};
149
150         env_logger::init();
151         let opt = Options::from_args();
152         trace!("{:?}", opt);
153
154         let fss = filesystem::probe_filesystems()?;
155         info!("Found {} bcachefs filesystems: ", fss.len());
156         for fs in fss.values() {
157                 info!(
158                         "{} ({}): {}",
159                         fs.uuid(),
160                         if fs.encrypted() {
161                                 "encrypted"
162                         } else {
163                                 "unencrypted"
164                         },
165                         fs.devices().iter().map(|d| d.display()).join(" ")
166                 );
167         }
168
169         if let Some(fs) = fss.get(&opt.uuid) {
170                 if fs.encrypted() {
171                         info!("Making sure key is loaded for this filesystem");
172                         key::prepare_key(&fs, opt.key_location)?;
173                 }
174
175                 if let Some(p) = opt.mountpoint {
176                         fs.mount(&p, &opt.options)
177                 } else {
178                         Ok(())
179                 }
180         } else {
181                 Err(anyhow!("Filesystem {} is not found", opt.uuid))
182         }
183 }
184
185 #[no_mangle]
186 pub extern "C" fn main() {
187         if let Err(e) = main_inner() {
188                 println!("Error: {:?}", e);
189         }
190 }