1 /**
2  * BSD 2-Clause License
3  *
4  * Copyright (c) 2021, Khaled Emara
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 use std::ffi::CString;
29 use std::io::{BufRead, Seek, SeekFrom};
30 
31 use super::attr::Attr;
32 use super::attr_bptree::AttrBtree;
33 use super::attr_leaf::AttrLeaf;
34 use super::attr_shortform::AttrShortform;
35 use super::bmbt_rec::BmbtRec;
36 use super::btree::{BmbtKey, BmdrBlock, Btree, XfsBmbtPtr};
37 use super::definitions::*;
38 use super::dinode_core::{DinodeCore, XfsDinodeFmt};
39 use super::dir3::Dir3;
40 use super::dir3_block::Dir2Block;
41 use super::dir3_bptree::Dir2Btree;
42 use super::dir3_leaf::Dir2Leaf;
43 use super::dir3_node::Dir2Node;
44 use super::dir3_sf::Dir2Sf;
45 use super::file::File;
46 use super::file_btree::FileBtree;
47 use super::file_extent_list::FileExtentList;
48 use super::sb::Sb;
49 use super::symlink_extent::SymlinkExtents;
50 
51 use byteorder::BigEndian;
52 use byteorder::ReadBytesExt;
53 use libc::{mode_t, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG};
54 
55 pub const LITERAL_AREA_OFFSET: u8 = 0xb0;
56 
57 #[derive(Debug)]
58 pub enum DiU {
59     DiDir2Sf(Dir2Sf),
60     DiBmx(Vec<BmbtRec>),
61     DiBmbt((BmdrBlock, Vec<BmbtKey>, Vec<XfsBmbtPtr>)),
62     DiSymlink(Vec<u8>),
63 }
64 
65 #[derive(Debug)]
66 pub enum DiA {
67     DiAttrsf(AttrShortform),
68     DiAbmx(Vec<BmbtRec>),
69     DiAbmbt((BmdrBlock, Vec<BmbtKey>, Vec<XfsBmbtPtr>)),
70 }
71 
72 #[derive(Debug)]
73 pub struct Dinode {
74     pub di_core: DinodeCore,
75     pub di_u: DiU,
76     pub di_a: Option<DiA>,
77 }
78 
79 impl Dinode {
from<R: BufRead + Seek>( buf_reader: &mut R, superblock: &Sb, inode_number: XfsIno, ) -> Dinode80     pub fn from<R: BufRead + Seek>(
81         buf_reader: &mut R,
82         superblock: &Sb,
83         inode_number: XfsIno,
84     ) -> Dinode {
85         let ag_no: XfsAgnumber =
86             (inode_number >> (superblock.sb_agblklog + superblock.sb_inopblog)) as u32;
87         if ag_no >= superblock.sb_agcount {
88             panic!("Wrong AG number!");
89         }
90 
91         let ag_blk: XfsAgblock =
92             ((inode_number >> superblock.sb_inopblog) & ((1 << superblock.sb_agblklog) - 1)) as u32;
93         let blk_ino = (inode_number & ((1 << superblock.sb_inopblog) - 1)) as u32;
94 
95         let off = ag_no * superblock.sb_agblocks * superblock.sb_blocksize;
96         let off = off + ag_blk * superblock.sb_blocksize;
97         let off = off + blk_ino * (superblock.sb_inodesize as u32);
98 
99         buf_reader.seek(SeekFrom::Start(off as u64)).unwrap();
100         let di_core = DinodeCore::from(buf_reader);
101 
102         let di_u: Option<DiU>;
103         match (di_core.di_mode as mode_t) & S_IFMT {
104             S_IFREG => match di_core.di_format {
105                 XfsDinodeFmt::Extents => {
106                     let mut bmx = Vec::<BmbtRec>::new();
107                     for _i in 0..di_core.di_nextents {
108                         bmx.push(BmbtRec::from(buf_reader.by_ref()))
109                     }
110                     di_u = Some(DiU::DiBmx(bmx));
111                 }
112                 XfsDinodeFmt::Btree => {
113                     let bmbt = BmdrBlock::from(buf_reader.by_ref());
114 
115                     let mut keys = Vec::<BmbtKey>::new();
116                     for _i in 0..bmbt.bb_numrecs {
117                         keys.push(BmbtKey::from(buf_reader.by_ref()))
118                     }
119 
120                     let mut pointers = Vec::<XfsBmbtPtr>::new();
121                     for _i in 0..bmbt.bb_numrecs {
122                         let pointer = buf_reader.read_u64::<BigEndian>().unwrap();
123                         pointers.push(pointer)
124                     }
125 
126                     di_u = Some(DiU::DiBmbt((bmbt, keys, pointers)));
127                 }
128                 _ => {
129                     panic!("Directory format not yet supported.");
130                 }
131             },
132             S_IFDIR => match di_core.di_format {
133                 XfsDinodeFmt::Local => {
134                     let dir_sf = Dir2Sf::from(buf_reader.by_ref());
135                     di_u = Some(DiU::DiDir2Sf(dir_sf));
136                 }
137                 XfsDinodeFmt::Extents => {
138                     let mut bmx = Vec::<BmbtRec>::new();
139                     for _i in 0..di_core.di_nextents {
140                         bmx.push(BmbtRec::from(buf_reader.by_ref()))
141                     }
142                     di_u = Some(DiU::DiBmx(bmx));
143                 }
144                 XfsDinodeFmt::Btree => {
145                     let bmbt = BmdrBlock::from(buf_reader.by_ref());
146 
147                     let mut keys = Vec::<BmbtKey>::new();
148                     for _i in 0..bmbt.bb_numrecs {
149                         keys.push(BmbtKey::from(buf_reader.by_ref()))
150                     }
151 
152                     let mut pointers = Vec::<XfsBmbtPtr>::new();
153                     for _i in 0..bmbt.bb_numrecs {
154                         let pointer = buf_reader.read_u64::<BigEndian>().unwrap();
155                         pointers.push(pointer)
156                     }
157 
158                     di_u = Some(DiU::DiBmbt((bmbt, keys, pointers)));
159                 }
160                 _ => {
161                     panic!("Directory format not yet supported.");
162                 }
163             },
164             S_IFLNK => match di_core.di_format {
165                 XfsDinodeFmt::Local => {
166                     let mut data = Vec::<u8>::with_capacity(di_core.di_size as usize);
167                     for _i in 0..di_core.di_size {
168                         let byte = buf_reader.read_u8().unwrap();
169                         data.push(byte)
170                     }
171                     di_u = Some(DiU::DiSymlink(data))
172                 }
173                 XfsDinodeFmt::Extents => {
174                     let mut bmx = Vec::<BmbtRec>::new();
175                     for _i in 0..di_core.di_nextents {
176                         bmx.push(BmbtRec::from(buf_reader.by_ref()))
177                     }
178                     di_u = Some(DiU::DiBmx(bmx));
179                 }
180                 _ => {
181                     panic!("Unexpected format for symlink");
182                 }
183             },
184             _ => panic!("Inode type not yet supported."),
185         }
186 
187         buf_reader.seek(SeekFrom::Start(off as u64)).unwrap();
188         buf_reader
189             .seek(SeekFrom::Current(
190                 (LITERAL_AREA_OFFSET as i64) + ((di_core.di_forkoff as i64) * 8),
191             ))
192             .unwrap();
193 
194         let di_a: Option<DiA>;
195         if di_core.di_forkoff != 0 {
196             match di_core.di_aformat {
197                 XfsDinodeFmt::Local => {
198                     let attr_shortform = AttrShortform::from(buf_reader.by_ref());
199                     di_a = Some(DiA::DiAttrsf(attr_shortform));
200                 }
201                 XfsDinodeFmt::Extents => {
202                     let mut bmx = Vec::<BmbtRec>::new();
203                     for _i in 0..di_core.di_anextents {
204                         bmx.push(BmbtRec::from(buf_reader.by_ref()))
205                     }
206                     di_a = Some(DiA::DiAbmx(bmx));
207                 }
208                 XfsDinodeFmt::Btree => {
209                     let bmbt = BmdrBlock::from(buf_reader.by_ref());
210 
211                     let mut keys = Vec::<BmbtKey>::new();
212                     for _i in 0..bmbt.bb_numrecs {
213                         keys.push(BmbtKey::from(buf_reader.by_ref()))
214                     }
215 
216                     let mut pointers = Vec::<XfsBmbtPtr>::new();
217                     for _i in 0..bmbt.bb_numrecs {
218                         let pointer = buf_reader.read_u64::<BigEndian>().unwrap();
219                         pointers.push(pointer)
220                     }
221 
222                     di_a = Some(DiA::DiAbmbt((bmbt, keys, pointers)));
223                 }
224                 _ => {
225                     panic!("Attributes format not yet supported.");
226                 }
227             }
228         } else {
229             di_a = None;
230         }
231 
232         Dinode {
233             di_core,
234             di_u: di_u.unwrap(),
235             di_a,
236         }
237     }
238 
get_dir<R: BufRead + Seek>( &self, buf_reader: &mut R, superblock: &Sb, ) -> Box<dyn Dir3<R>>239     pub fn get_dir<R: BufRead + Seek>(
240         &self,
241         buf_reader: &mut R,
242         superblock: &Sb,
243     ) -> Box<dyn Dir3<R>> {
244         match &self.di_u {
245             DiU::DiDir2Sf(dir) => Box::new(dir.clone()),
246             DiU::DiBmx(bmx) => {
247                 if bmx.len() == 1 {
248                     Box::new(Dir2Block::from(
249                         buf_reader.by_ref(),
250                         superblock,
251                         bmx[0].br_startblock,
252                     ))
253                 } else if bmx.len() > 4 {
254                     Box::new(Dir2Node::from(bmx.clone(), superblock.sb_blocksize))
255                 } else {
256                     Box::new(Dir2Leaf::from(buf_reader.by_ref(), superblock, bmx))
257                 }
258             }
259             DiU::DiBmbt((bmbt, keys, pointers)) => Box::new(Dir2Btree::from(
260                 bmbt.clone(),
261                 keys.clone(),
262                 pointers.clone(),
263                 superblock.sb_blocksize,
264             )),
265             _ => {
266                 panic!("Unsupported dir format!");
267             }
268         }
269     }
270 
get_file<R: BufRead + Seek>( &self, _buf_reader: &mut R, superblock: &Sb, ) -> Box<dyn File<R>>271     pub fn get_file<R: BufRead + Seek>(
272         &self,
273         _buf_reader: &mut R,
274         superblock: &Sb,
275     ) -> Box<dyn File<R>> {
276         match &self.di_u {
277             DiU::DiBmx(bmx) => Box::new(FileExtentList {
278                 bmx: bmx.clone(),
279                 size: self.di_core.di_size,
280                 block_size: superblock.sb_blocksize,
281             }),
282             DiU::DiBmbt((bmdr, keys, pointers)) => Box::new(FileBtree {
283                 btree: Btree {
284                     bmdr: bmdr.clone(),
285                     keys: keys.clone(),
286                     ptrs: pointers.clone(),
287                 },
288                 size: self.di_core.di_size,
289                 block_size: superblock.sb_blocksize,
290             }),
291             _ => {
292                 panic!("Unsupported file format!");
293             }
294         }
295     }
296 
get_link_data<R: BufRead + Seek>(&self, buf_reader: &mut R, superblock: &Sb) -> CString297     pub fn get_link_data<R: BufRead + Seek>(&self, buf_reader: &mut R, superblock: &Sb) -> CString {
298         match &self.di_u {
299             DiU::DiSymlink(data) => CString::new(data.clone()).unwrap(),
300             DiU::DiBmx(bmx) => SymlinkExtents::get_target(buf_reader.by_ref(), bmx, superblock),
301             _ => {
302                 panic!("Unsupported link format!");
303             }
304         }
305     }
306 
get_attrs<R: BufRead + Seek>( &self, buf_reader: &mut R, superblock: &Sb, ) -> Option<Box<dyn Attr<R>>>307     pub fn get_attrs<R: BufRead + Seek>(
308         &self,
309         buf_reader: &mut R,
310         superblock: &Sb,
311     ) -> Option<Box<dyn Attr<R>>> {
312         match &self.di_a {
313             Some(DiA::DiAttrsf(attr)) => Some(Box::new(attr.clone())),
314             Some(DiA::DiAbmx(bmx)) => {
315                 if self.di_core.di_anextents > 0 {
316                     buf_reader.seek(SeekFrom::Current(8)).unwrap();
317                     let magic = buf_reader.read_u16::<BigEndian>().unwrap();
318                     buf_reader.seek(SeekFrom::Current(-8)).unwrap();
319 
320                     match magic {
321                         XFS_ATTR3_LEAF_MAGIC => {
322                             return Some(Box::new(AttrLeaf::from(
323                                 buf_reader.by_ref(),
324                                 superblock,
325                                 bmx.clone(),
326                             )));
327                         }
328                         XFS_DA3_NODE_MAGIC => {
329                             return Some(Box::new(AttrLeaf::from(
330                                 buf_reader.by_ref(),
331                                 superblock,
332                                 bmx.clone(),
333                             )));
334                         }
335                         _ => panic!("Unknown magic number!"),
336                     }
337                 } else {
338                     None
339                 }
340             }
341             Some(DiA::DiAbmbt((bmdr, keys, pointers))) => Some(Box::new(AttrBtree {
342                 btree: Btree {
343                     bmdr: bmdr.clone(),
344                     keys: keys.clone(),
345                     ptrs: pointers.clone(),
346                 },
347                 total_size: -1,
348             })),
349             None => None,
350         }
351     }
352 }
353