Skip to main content

libblkid_rs/
cache.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use std::{
6    ffi::{CStr, CString},
7    path::Path,
8    ptr,
9};
10
11use either::Either;
12
13use libblkid_rs_sys::blkid_cache;
14use libc::{c_void, free};
15
16use crate::{
17    consts::BlkidDevFlags,
18    dev::{BlkidDev, BlkidDevIter},
19    err::{BlkidErr, Result},
20};
21
22/// Data structure representing cache in libblkid
23pub struct BlkidCache(blkid_cache, bool);
24
25impl BlkidCache {
26    pub(crate) fn as_mut_ptr(&mut self) -> *mut blkid_cache {
27        &mut self.0 as *mut _
28    }
29
30    /// Save changes to cache file
31    pub fn put_cache(&mut self) {
32        unsafe { libblkid_rs_sys::blkid_put_cache(self.0) };
33        self.1 = true;
34    }
35
36    /// Allocate and initialize cache handler
37    ///
38    /// Use None for filename to use the default cache path
39    pub fn get_cache(filename: Option<&Path>) -> Result<Self> {
40        let mut cache = ptr::null_mut();
41        let filename_cstring = match filename {
42            Some(fname) => Some(CString::new(fname.to_str().ok_or(BlkidErr::InvalidConv)?)?),
43            None => None,
44        };
45        errno!(unsafe {
46            libblkid_rs_sys::blkid_get_cache(
47                &mut cache as *mut _,
48                filename_cstring
49                    .as_ref()
50                    .map(|s| s.as_ptr())
51                    .unwrap_or(ptr::null_mut()),
52            )
53        })?;
54        Ok(BlkidCache(cache, false))
55    }
56
57    /// Removes non-existent devices from cache
58    pub fn gc_cache(&mut self) {
59        unsafe { libblkid_rs_sys::blkid_gc_cache(self.0) }
60    }
61
62    /// Create an iterator from the cached devices
63    pub fn iter(&self) -> BlkidDevIter {
64        BlkidDevIter::new(unsafe { libblkid_rs_sys::blkid_dev_iterate_begin(self.0) })
65    }
66
67    /// Probe for all block devices
68    pub fn probe_all(&mut self) -> Result<()> {
69        errno!(unsafe { libblkid_rs_sys::blkid_probe_all(self.0) })
70    }
71
72    /// Probe for all new block devices
73    pub fn probe_all_new(&mut self) -> Result<()> {
74        errno!(unsafe { libblkid_rs_sys::blkid_probe_all_new(self.0) })
75    }
76
77    /// Probe for all removable block devices
78    pub fn probe_all_removable(&mut self) -> Result<()> {
79        errno!(unsafe { libblkid_rs_sys::blkid_probe_all_removable(self.0) })
80    }
81
82    /// Find a device by device name in the cache.
83    ///
84    /// Use the `BlkidDevConst::Create` flag to create an empty cache entry.
85    pub fn get_dev(&self, devname: &Path, flags: BlkidDevFlags) -> Result<BlkidDev> {
86        let devname_cstring =
87            CString::new(devname.to_str().ok_or(BlkidErr::InvalidConv)?.as_bytes())?;
88        Ok(BlkidDev::new(unsafe {
89            libblkid_rs_sys::blkid_get_dev(self.0, devname_cstring.as_ptr(), flags.into())
90        }))
91    }
92
93    /// Get the value associated with a tag (e.g. TYPE) for a given device
94    pub fn get_tag_value(&self, tag_name: &str, devname: &Path) -> Result<String> {
95        let tag_name_cstring = CString::new(tag_name.as_bytes())?;
96        let devname_cstring =
97            CString::new(devname.to_str().ok_or(BlkidErr::InvalidConv)?.as_bytes())?;
98        let ptr = errno_ptr!(unsafe {
99            libblkid_rs_sys::blkid_get_tag_value(
100                self.0,
101                tag_name_cstring.as_ptr(),
102                devname_cstring.as_ptr(),
103            )
104        })?;
105        let string = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string();
106        unsafe { libc::free(ptr as *mut libc::c_void) };
107        Ok(string)
108    }
109
110    /// Get the device name for a specific `NAME=value` tag pair in the cache
111    pub fn get_devname(&self, token_or_pair: Either<&str, (&str, &str)>) -> Result<String> {
112        let (name, value) = match token_or_pair {
113            Either::Left(token) => {
114                if !token.contains('=') {
115                    return Err(BlkidErr::Other(
116                        "Token input requires the format NAME=value".to_string(),
117                    ));
118                }
119                let mut split = token.split('=');
120                match (split.next(), split.next()) {
121                    (Some(name), Some(value)) => (name, value),
122                    (_, _) => {
123                        return Err(BlkidErr::Other(
124                            "Token input requires the format NAME=value".to_string(),
125                        ));
126                    }
127                }
128            }
129            Either::Right((name, value)) => (name, value),
130        };
131        let name_cstring = CString::new(name.as_bytes())?;
132        let value_cstring = CString::new(value.as_bytes())?;
133        let ptr = errno_ptr!(unsafe {
134            libblkid_rs_sys::blkid_get_devname(
135                self.0,
136                name_cstring.as_ptr(),
137                value_cstring.as_ptr(),
138            )
139        })?;
140        let string = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string();
141        unsafe { libc::free(ptr as *mut libc::c_void) };
142        Ok(string)
143    }
144
145    /// Find the device with the specified tag
146    pub fn find_dev_with_tag(&self, type_: &str, value: &str) -> Result<BlkidDev> {
147        let type_cstring = CString::new(type_)?;
148        let value_cstring = CString::new(value)?;
149        let ptr = errno_ptr!(unsafe {
150            libblkid_rs_sys::blkid_find_dev_with_tag(
151                self.0,
152                type_cstring.as_ptr(),
153                value_cstring.as_ptr(),
154            )
155        })?;
156        Ok(BlkidDev::new(ptr))
157    }
158
159    /// Verify that a device in the cache exists and remove it if it does not.
160    pub fn verify(&mut self, dev: BlkidDev) -> Option<BlkidDev> {
161        let ptr = unsafe { libblkid_rs_sys::blkid_verify(self.0, dev.as_ptr()) };
162        if ptr.is_null() {
163            None
164        } else {
165            Some(BlkidDev::new(ptr))
166        }
167    }
168}
169
170impl Drop for BlkidCache {
171    fn drop(&mut self) {
172        if !self.1 {
173            unsafe { free(self.0 as *mut c_void) }
174        }
175    }
176}