blkid 1.0.1

Safe blkid-sys wrapper
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
use crate::{
    error::{c_result, BlkIdError, BlkIdResult},
    part_list::PartList,
    path_to_cstring,
    topology::Topology,
    PartitionsFlags, SuperblocksFlags,
};
use blkid_sys::*;
use std::{
    collections::HashMap,
    ffi::{CStr, CString},
    path::Path,
    ptr,
};

/// Low-level probing setting
///
/// The probing routines are grouped together into separate chains. Currently, the library provides
/// superblocks, partitions and topology chains.
///
/// The probing routines is possible to filter (enable/disable) by type (e.g. fstype "vfat" or
/// partype "gpt") or by usage flags (e.g. BLKID_USAGE_RAID). These filters are per-chain. Note that
/// always when you touch the chain filter the current probing position is reset and probing starts
/// from scratch. It means that the chain filter should not be modified during probing, for example
/// in loop where you call [`Self::do_probe`].
///
/// The probing routines inside the chain are mutually exclusive by default - only few probing
/// routines are marked as "tolerant". The "tolerant" probing routines are used for filesystem
/// which can share the same device with any other filesystem. The [`Self::safeprobe`] checks for
/// the "tolerant" flag.
///
/// The `superblocks` chain is enabled by default. The all others chains is necessary to enable by
/// `enable_'CHAINNAME'()`.
pub struct Prober(pub(crate) blkid_probe);

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ProbeState {
    Success,
    Done,
    NothingDetected,
    Ambivalent,
}

impl Drop for Prober {
    fn drop(&mut self) {
        unsafe { blkid_free_probe(self.0) }
    }
}

impl Prober {
    /// Create newly allocated `probe` struct.
    pub fn new() -> BlkIdResult<Self> {
        let probe = unsafe { c_result(blkid_new_probe()) }?;
        Ok(Self(probe))
    }

    /// Create newly allocated `probe` struct by filename.
    /// `filename` can be either regular file or device
    pub fn new_from_filename<P: AsRef<Path>>(filename: P) -> BlkIdResult<Self> {
        let path = path_to_cstring(filename)?;
        let probe = unsafe { c_result(blkid_new_probe_from_filename(path.as_ptr())) }?;
        Ok(Self(probe))
    }

    /// Calls probing functions in all enabled chains. The superblocks chain is enabled by default.
    /// Stores result from only one probing function.
    ///
    /// # Note
    ///
    /// It's necessary to call this routine in a loop to get results from all probing functions in
    /// all chains. The probing is reset by [`Self::reset`] or by filter functions.
    ///
    /// Returns the following possible states:
    /// * [`ProberState::Success`]
    /// * [`ProberState::Done`]
    ///
    /// # Exapmles
    ///
    /// * Basic case - use the first result only
    /// ```ignore, compile_fail
    /// let prober = Prober::new().unwrap();
    ///
    /// if prober.do_probe() == ProbeState::success {
    ///     let value_map = prober.get_values_map().unwrap();
    ///     println!("{:#?}", value_map);
    /// }
    /// ```
    /// * Advanced case - probe for all signatures
    /// ```ignore, compile_fail
    /// let prober = Prober::new().unwrap();
    ///
    /// while prober.do_probe() == ProbeState::Done {
    ///     let value_map = prober.get_values_map().unwrap();
    ///     println!("{:#?}", value_map);
    /// }
    /// ```
    pub fn do_probe(&self) -> BlkIdResult<ProbeState> {
        let ret_code = unsafe { blkid_do_probe(self.0) };

        match ret_code {
            0 => Ok(ProbeState::Success),
            1 => Ok(ProbeState::Done),
            _ => Err(BlkIdError::Io(std::io::Error::last_os_error())),
        }
    }

    /// This function gathers probing results from all enabled chains and checks for ambivalent
    /// results (e.g. more filesystems on the device).
    ///
    /// This is string-based `NAME=value` interface only.
    ///
    /// # Note
    ///
    /// Suberblocks chain -- the function does not check for filesystems when a `RAID` signature is
    /// detected. The function also does not check for collision between `RAID`s. The first detected
    /// `RAID` is returned. The function checks for collision between partition table and `RAID`
    /// signature - it's recommended to enable partitions chain ([`Self::enable_partitions`])
    /// together with superblocks chain (enabled by default).
    ///
    /// Returns the following possible states:
    /// * [`ProberState::Success`]
    /// * [`ProberState::NothingDetected`]
    /// * [`ProberState::Ambivalent`]
    pub fn do_safe_probe(&self) -> BlkIdResult<ProbeState> {
        let ret_code = unsafe { blkid_do_safeprobe(self.0) };

        match ret_code {
            0 => Ok(ProbeState::Success),
            1 => Ok(ProbeState::NothingDetected),
            -2 => Ok(ProbeState::Ambivalent),
            _ => Err(BlkIdError::Io(std::io::Error::last_os_error())),
        }
    }

    /// This function gathers probing results from all enabled chains. Same as
    /// [`Self::do_safe_probe`] but does not check for collision between probing result.
    ///
    /// Returns the following possible states:
    /// * [`ProberState::Success`]
    /// * [`ProberState::NothingDetected`]
    pub fn do_full_probe(&self) -> BlkIdResult<ProbeState> {
        let ret_code = unsafe { blkid_do_safeprobe(self.0) };

        match ret_code {
            0 => Ok(ProbeState::Success),
            1 => Ok(ProbeState::NothingDetected),
            _ => Err(BlkIdError::Io(std::io::Error::last_os_error())),
        }
    }

    /// Erases the current signature detected by prober. The prober has to be open in `O_RDWR` mode,
    /// `BLKID_SUBLKS_MAGIC` or/and `BLKID_PARTS_MAGIC` flags has to be enabled. That means that you
    /// should use [`Self::set_device`] with options above.
    ///
    /// After successful signature removing the prober will be moved one step back and the next
    /// [`Self::probe`] call will again call previously called probing function.
    ///
    /// # Examples
    ///
    /// ```ignore, compile_fail
    /// use std::os::unix::io::AsRawFd;
    /// use std::fs::OpenOptions;
    ///
    /// let mut file = OpenOptions::new()
    ///     .read(true)
    ///     .write(true)
    ///     .open("/dev/sda");
    /// let fd: RawFd = file.as_raw_fd();
    ///
    /// let mut prober = Prober::new().unwrap();
    /// prober.set_device(fd, 0, None).unwrap();
    ///
    /// while prober.do_probe() == ProbeState::Success {
    ///     prober.do_wipe(false).unwrap();
    /// }
    /// ```
    pub fn do_wipe(&self, dry_run: bool) -> BlkIdResult<ProbeState> {
        let ret_code = unsafe { blkid_do_wipe(self.0, dry_run as i32) };

        match ret_code {
            0 => Ok(ProbeState::Success),
            1 => Ok(ProbeState::Done),
            _ => Err(BlkIdError::Io(std::io::Error::last_os_error())),
        }
    }

    /// Retrieve the Nth item `(Name, Value)` in the probing result, (0..self.numof_values())
    pub fn get_value(&self, num: i32) -> BlkIdResult<(String, String)> {
        let mut name_ptr: *const ::libc::c_char = ptr::null();
        let mut data_ptr: *const ::libc::c_char = ptr::null();
        let mut len = 0;

        unsafe {
            c_result(blkid_probe_get_value(
                self.0,
                num,
                &mut name_ptr,
                &mut data_ptr,
                &mut len,
            ))
        }?;

        let name_value = unsafe { CStr::from_ptr(name_ptr).to_str()?.to_owned() };
        let data_value = unsafe { CStr::from_ptr(data_ptr).to_str()?.to_owned() };
        Ok((name_value, data_value))
    }

    /// Retrieve a `HashMap` of all the probed values
    pub fn get_values_map(&self) -> BlkIdResult<HashMap<String, String>> {
        let numof_values = self.numof_values()?;
        let mut map = HashMap::with_capacity(numof_values as usize);

        for i in 0..numof_values {
            let (key, value) = self.get_value(i)?;
            map.insert(key, value);
        }

        Ok(map)
    }

    /// Check if device has the specified value
    pub fn has_value(&self, name: &str) -> BlkIdResult<bool> {
        let name = CString::new(name)?;
        unsafe { c_result(blkid_probe_has_value(self.0, name.as_ptr())).map(|val| val == 1) }
    }

    /// Value by specified `name`
    ///
    /// # Note
    ///
    /// You should call [`Self::do_probe`] before using this
    pub fn lookup_value(&self, name: &str) -> BlkIdResult<String> {
        let name = CString::new(name)?;
        let mut data_ptr: *const ::libc::c_char = ptr::null();
        let mut len = 0;
        unsafe {
            c_result(blkid_probe_lookup_value(
                self.0,
                name.as_ptr(),
                &mut data_ptr,
                &mut len,
            ))
        }?;

        let data_value = unsafe { CStr::from_ptr(data_ptr).to_str()?.to_owned() };
        Ok(data_value)
    }

    /// Number of values in probing result
    pub fn numof_values(&self) -> BlkIdResult<i32> {
        unsafe { c_result(blkid_probe_numof_values(self.0)) }
    }

    /// Block device number, or 0 for regular file
    pub fn get_devno(&self) -> u64 {
        unsafe { blkid_probe_get_devno(self.0) }
    }

    /// File descriptor for assigned device/file
    pub fn get_fd(&self) -> i32 {
        unsafe { blkid_probe_get_fd(self.0) }
    }

    /// Block device logical sector size (`BLKSSZGET` ioctl, default 512)
    pub fn get_sector_size(&self) -> u32 {
        unsafe { blkid_probe_get_sectorsize(self.0) }
    }

    /// Set logical sector size.
    ///
    /// Note that [`Self::set_device`] resets this setting. Use it after [`Self::set_device`] and
    /// before any probing call.
    #[cfg(blkid = "2.30")]
    pub fn set_sector_size(&self, size: u32) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_set_sectorsize(self.0, size)).map(|_| ()) }
    }

    /// 512-byte sector count
    pub fn get_sectors(&self) -> BlkIdResult<i64> {
        unsafe { c_result(blkid_probe_get_sectors(self.0)) }
    }

    /// Size of probing area in bytes as defined by [`Self::set_device`]. If the size of the probing
    /// area is unrestricted then this function returns the real size of device
    pub fn get_size(&self) -> BlkIdResult<i64> {
        unsafe { c_result(blkid_probe_get_size(self.0)) }
    }

    /// Offset of probing area as defined by [`Self::set_device`]
    pub fn get_offset(&self) -> BlkIdResult<i64> {
        unsafe { c_result(blkid_probe_get_offset(self.0)) }
    }

    /// Device number of the wholedisk, or 0 for regular files
    pub fn get_wholedisk_devno(&self) -> u64 {
        unsafe { blkid_probe_get_wholedisk_devno(self.0) }
    }

    /// If device is wholedisk
    pub fn is_wholedisk(&self) -> bool {
        unsafe { blkid_probe_is_wholedisk(self.0) == 1 }
    }

    /// Modifies in-memory cached data from the device. The specified range is zeroized.
    /// This is usable together with [`Self::step_back`]. The next [`Self::do_probe`] will not see
    /// specified area.
    ///
    /// Note that this is usable for already (by library) read data, and this function is not a way
    /// how to hide any large areas on your device.
    ///
    /// The [`Self::reset_buffers`] reverts all.
    #[cfg(blkid = "2.31")]
    pub fn hide_range(&self, offset: u64, size: u64) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_hide_range(self.0, offset, size)).map(|_| ()) }
    }

    /// Reuse all already read buffers from the device. The buffers may be modified by
    /// [`Self::hide_range`]. This resets and free all cached buffers. The next [`Self::do_probe`]
    /// will read all data from the device.
    #[cfg(blkid = "2.31")]
    pub fn reset_buffers(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_reset_buffers(self.0)).map(|_| ()) }
    }

    /// This function move pointer to the probing chain one step back - it means that the
    /// previously used probing function will be called again in the next [`Self::do_probe`] call.
    ///
    /// This is necessary for example if you erase or modify on-disk superblock according to the
    /// current libblkid probing result.
    ///
    /// Note that [`Self::hide_range`] changes semantic of this function and cached buffers are
    /// not reset, but library uses in-memory modified buffers to call the next probing function.
    ///
    /// # Examples
    ///
    /// ```ignore, compile_fail
    /// let prober = Prober::new_from_filename("/dev/sda");
    /// TODO: coplete this example
    /// ```
    #[cfg(blkid = "2.23")]
    pub fn step_back(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_step_back(self.0)).map(|_| ()) }
    }

    /// Assigns the device to probe control struct, resets internal buffers and resets the current
    /// probing.
    ///
    /// `fd`: device file descriptor
    /// `offset`: begin of probing area
    /// `size`: size of probing area (`None` means whole device/file)
    pub fn set_device(&mut self, fd: i32, offset: i64, size: Option<i64>) -> BlkIdResult<()> {
        let size = size.unwrap_or(0);
        unsafe { c_result(blkid_probe_set_device(self.0, fd, offset, size)).map(|_| ()) }
    }

    /// Zeroize probing results and resets the current probing (this has impact to [`Self::do_probe`]
    /// only). This function does not touch probing filters and keeps assigned device.
    pub fn reset_probe(&self) {
        unsafe { blkid_reset_probe(self.0) }
    }

    /// Enables/disables the superblocks probing for non-binary interface.
    pub fn enable_superblocks(&self, enable: bool) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_enable_superblocks(self.0, enable as i32)).map(|_| ()) }
    }

    /// If known filesystem type
    pub fn known_fstype(fstype: &str) -> BlkIdResult<bool> {
        let fstype = CString::new(fstype)?;
        Ok(unsafe { blkid_known_fstype(fstype.as_ptr()) == 1 })
    }

    // TODO: implement
    // pub fn superblocks_get_name() {}

    // TODO: implement
    // pub fn filter_superblocks_type() {}

    // TODO: implement
    // pub fn filter_superblocks_usage() {}

    /// Inverts superblocks probing filter
    pub fn invert_superblocks_filter(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_invert_superblocks_filter(self.0)).map(|_| ()) }
    }

    /// Resets superblocks probing filter
    pub fn reset_superblocks_filter(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_reset_superblocks_filter(self.0)).map(|_| ()) }
    }

    /// Sets probing flags to the superblocks prober. This function is optional, the default are
    /// [`Superblocks::DEFAULT`] flags.
    pub fn set_superblocks_flags(&self, flags: SuperblocksFlags) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_set_superblocks_flags(self.0, flags.bits())).map(|_| ()) }
    }

    /// Enables/disables the partitions probing for non-binary interface
    pub fn enable_partitions(&self, enable: bool) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_enable_partitions(self.0, enable as i32)).map(|_| ()) }
    }

    /// Sets probing flags to the partitions prober. This function is optional
    pub fn set_partitions_flags(&self, flags: PartitionsFlags) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_set_partitions_flags(self.0, flags.bits())).map(|_| ()) }
    }

    // TODO: implement
    // pub fn filter_partitions_type() {}

    /// Inverts partitions probing filter
    pub fn invert_partitions_filter(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_invert_partitions_filter(self.0)).map(|_| ()) }
    }

    /// Resets partitions probing filter
    pub fn reset_partitions_filter(&self) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_reset_partitions_filter(self.0)).map(|_| ()) }
    }

    /// If known partition table type
    pub fn known_pttype(pttype: &str) -> BlkIdResult<bool> {
        let pttype = CString::new(pttype)?;
        Ok(unsafe { blkid_known_pttype(pttype.as_ptr()) == 1 })
    }

    /// Returns name of a supported partition.
    #[cfg(blkid = "2.30")]
    pub fn partitions_get_name(idx: usize) -> BlkIdResult<String> {
        let mut name: *const ::libc::c_char = ptr::null();
        unsafe { c_result(blkid_partitions_get_name(idx.try_into().unwrap(), &mut name)) }?;
        let name = unsafe { CStr::from_ptr(name).to_str()?.to_owned() };
        Ok(name)
    }

    /// Returns [`PartList`] object.
    ///
    /// This is a binary interface for partitions.
    ///
    /// This is independent on `Self::do_[safe,full]_probe()` and [`Self::enable_partitions`] calls.
    ///
    /// # WARNING
    ///
    /// The returned object will be overwritten by the next [`Self::part_list`] call for the same
    /// prober. If you want to use more [`PartList`] objects in the same time you have to create
    /// more [`Prober`] handlers.
    pub fn part_list(&self) -> BlkIdResult<PartList> {
        unsafe { c_result(blkid_probe_get_partitions(self.0)).map(PartList) }
    }

    /// Enables/disables the topology probing for non-binary interface
    pub fn enable_topology(&self, enable: bool) -> BlkIdResult<()> {
        unsafe { c_result(blkid_probe_enable_topology(self.0, enable as i32)).map(|_| ()) }
    }

    /// Returns topology.
    ///
    /// This is a binary interface for topology values.
    ///
    /// This is independent on `Self::do_[safe,full]_probe()` and [`Self::enable_partitions`] calls.
    ///
    /// # WARNING
    ///
    /// The returned object will be overwritten by the next [`Self::topology`] call for the same
    /// prober. If you want to use more [`Topology`] objects in the same time you have to create
    /// more [`Prober`] handlers.
    pub fn topology(&self) -> BlkIdResult<Topology> {
        unsafe { c_result(blkid_probe_get_topology(self.0)).map(Topology) }
    }

    /// Sets extra hint for low-level prober. If the hint is set by NAME=value notation than value
    /// is ignored. The [`Self::set_device`] and [`Self::reset_probe`] resets all hints.
    ///
    /// The hints are optional way how to force libblkid probing functions to check for example
    /// another location.
    #[cfg(blkid = "2.37")]
    pub fn set_hint(&self, hint_name: &str, offset: u64) -> BlkIdResult<()> {
        let name = CString::new(hint_name)?;
        unsafe { c_result(blkid_probe_set_hint(self.0, name.as_ptr(), offset)).map(|_| ()) }
    }

    /// Removes all previously defined probing hints. See also [`Self::set_hint`]
    #[cfg(blkid = "2.37")]
    pub fn reset_hints(&self) {
        unsafe { blkid_probe_reset_hints(self.0) }
    }
}