diskit/virtual_diskit/
helpers.rs

1use std::{
2    collections::HashMap,
3    ffi::{OsStr, OsString},
4    io::{Error, ErrorKind},
5    path::{Component, Path},
6};
7
8use super::implementation::{Inode, InodeInner, VirtualDiskitInner};
9
10impl VirtualDiskitInner
11{
12    pub fn get_inode_by_id(&self, id: usize) -> Result<&Inode, Error>
13    {
14        if self.content.len() <= id
15        {
16            Err(ErrorKind::NotFound.into())
17        }
18        else
19        {
20            Ok(&self.content[id])
21        }
22    }
23
24    pub fn get_mut_inode_by_id(&mut self, id: usize) -> Result<&mut Inode, Error>
25    {
26        if self.content.len() <= id
27        {
28            Err(ErrorKind::NotFound.into())
29        }
30        else
31        {
32            Ok(&mut self.content[id])
33        }
34    }
35
36    pub fn get_inode_by_path_and_dir(
37        &self,
38        dir: &HashMap<OsString, usize>,
39        path: &OsStr,
40    ) -> Result<&Inode, Error>
41    {
42        self.get_inode_by_id(*dir.get(path).ok_or(ErrorKind::NotFound)?)
43    }
44
45    pub fn get_inode_by_path_and_inode(&self, inode: &Inode, path: &OsStr)
46        -> Result<&Inode, Error>
47    {
48        if let InodeInner::Dir(dir) = &inode.inner
49        {
50            self.get_inode_by_path_and_dir(dir, path)
51        }
52        else
53        {
54            Err(From::from(ErrorKind::NotADirectory))
55        }
56    }
57
58    pub fn get_first_walkdir_pos(
59        &self,
60        path: &Path,
61        contents_first: bool,
62    ) -> Result<Vec<usize>, Error>
63    {
64        if !contents_first
65        {
66            return Ok(vec![]);
67        }
68
69        let mut inode = self
70            .get_inode_by_full_path(path)
71            .and_then(|x| x.map_err(|_| ErrorKind::NotFound.into()))?;
72        let mut rv = 0;
73
74        while let Some((_, inode_id)) = Self::get_dir_as_iterator(inode)
75            .ok()
76            .and_then(|mut x| x.next())
77        {
78            inode = self.get_inode_by_id(*inode_id)?;
79            rv += 1;
80        }
81
82        Ok(vec![0; rv])
83    }
84
85    pub fn get_dir_from_inode(inode: &Inode) -> Result<&HashMap<OsString, usize>, Error>
86    {
87        match &inode.inner
88        {
89            InodeInner::Dir(dir) => Ok(dir),
90            _ => Err(ErrorKind::NotADirectory.into()),
91        }
92    }
93
94    pub fn get_reference_to_pwd(&self) -> Result<&Inode, Error>
95    {
96        let mut inode = &self.content[0];
97
98        for component in self.pwd.components().skip(1)
99        {
100            match component
101            {
102                Component::ParentDir =>
103                {
104                    inode = self.get_inode_by_path_and_dir(
105                        Self::get_dir_from_inode(inode)?,
106                        OsStr::new(".."),
107                    )?;
108                }
109                Component::Normal(path) =>
110                {
111                    inode =
112                        self.get_inode_by_path_and_dir(Self::get_dir_from_inode(inode)?, path)?;
113                }
114                // Does nothing
115                Component::CurDir =>
116                {}
117                // As far a I understood it, Component::RootDir can
118                // only happen if there is a prefix (which is only
119                // possible under windows), because the first element
120                // is skipped in the for loop.
121                _ => panic!("Windows is not supported"),
122            }
123        }
124
125        // Guarantees that the pwd is actually a directory.
126        let _ = Self::get_dir_from_inode(inode)?;
127
128        Ok(inode)
129    }
130
131    pub fn get_inode_by_full_path(&self, path: &Path) -> Result<Result<&Inode, &Inode>, Error>
132    {
133        let mut inode = if path.starts_with("/")
134        {
135            &self.content[0]
136        }
137        else
138        {
139            self.get_reference_to_pwd()?
140        };
141
142        let mut not_found = false;
143
144        for component in path.components()
145        {
146            if not_found
147            {
148                return Err(ErrorKind::NotFound.into());
149            }
150
151            if !matches!(inode.inner, InodeInner::Dir(_))
152            {
153                // TODO: Check if this line is correct.
154                return Err(ErrorKind::NotADirectory.into());
155            }
156
157            inode = match component
158            {
159                Component::CurDir => self.get_inode_by_path_and_inode(inode, OsStr::new("."))?,
160                Component::ParentDir =>
161                {
162                    self.get_inode_by_path_and_inode(inode, OsStr::new(".."))?
163                }
164                Component::Normal(path) =>
165                {
166                    let val = self.get_inode_by_path_and_inode(inode, path);
167
168                    if val
169                        .as_ref()
170                        .err()
171                        .map_or(false, |x| x.kind() == ErrorKind::NotFound)
172                    {
173                        not_found = true;
174                        continue;
175                    }
176
177                    val?
178                }
179                Component::RootDir => &self.content[0],
180                Component::Prefix(_) => panic!("Windows is not supported"),
181            };
182        }
183
184        if not_found
185        {
186            return Ok(Err(inode));
187        }
188
189        Ok(Ok(inode))
190    }
191
192    pub fn get_dir_as_iterator(
193        inode: &Inode,
194    ) -> Result<impl Iterator<Item = (&OsString, &usize)>, Error>
195    {
196        Ok(Self::get_dir_from_inode(inode)?
197            .iter()
198            .filter(|(path, _)| *path != "." && *path != ".."))
199    }
200
201    pub fn check_pos_path(&self, root_inode: &Inode, pos: &[usize]) -> Option<()>
202    {
203        let mut inode = root_inode;
204
205        for &index in pos
206        {
207            let (_, &content_inode) = Self::get_dir_as_iterator(inode).ok()?.nth(index)?;
208
209            inode = self.get_inode_by_id(content_inode).ok()?;
210        }
211
212        Some(())
213    }
214}
215
216// These methods are only needed if the `trash` feature is activated.
217#[cfg(feature = "trash")]
218impl VirtualDiskitInner
219{
220    pub fn get_mut_dir_from_inode(inode: &mut Inode)
221        -> Result<&mut HashMap<OsString, usize>, Error>
222    {
223        match &mut inode.inner
224        {
225            InodeInner::Dir(dir) => Ok(dir),
226            _ => Err(ErrorKind::NotADirectory.into()),
227        }
228    }
229
230    pub fn get_dir_by_id(&self, inode: usize) -> Result<&HashMap<OsString, usize>, Error>
231    {
232        match &self.content[inode].inner
233        {
234            InodeInner::Dir(dir) => Ok(dir),
235            _ => Err(ErrorKind::NotADirectory.into()),
236        }
237    }
238
239    pub fn get_mut_inode_by_full_path(&mut self, path: &Path) -> Result<&mut Inode, Error>
240    {
241        self.get_mut_inode_by_id(
242            self.get_inode_by_full_path(path)?
243                .map_err(|_| ErrorKind::NotFound)?
244                .id,
245        )
246    }
247
248    pub fn collect_inodes(&self, inode: &Inode, vec: &mut Vec<usize>) -> Result<(), Error>
249    {
250        vec.push(inode.id);
251        if let InodeInner::Dir(dir) = &inode.inner
252        {
253            dir.iter()
254                .map(|(_, &id)| {
255                    self.get_inode_by_id(id)
256                        .and_then(|inode| self.collect_inodes(inode, vec))
257                })
258                .collect::<Result<Vec<_>, _>>()?;
259        }
260
261        Ok(())
262    }
263}