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::collections::HashMap;
29 use std::io::{BufRead, Seek, SeekFrom};
30 use std::mem;
31 
32 use super::da_btree::hashname;
33 use super::definitions::*;
34 use super::dinode::Dinode;
35 use super::dir3::{
36     Dir2DataEntry, Dir2DataUnused, Dir2LeafEntry, Dir3, Dir3DataHdr, XfsDir2Dataptr,
37 };
38 use super::sb::Sb;
39 use super::utils::{get_file_type, FileKind};
40 
41 use byteorder::{BigEndian, ReadBytesExt};
42 use fuse::{FileAttr, FileType};
43 use libc::{c_int, ENOENT, S_IFMT};
44 use time::Timespec;
45 
46 pub const XFS_DIR2_DATA_FD_COUNT: usize = 3;
47 
48 #[derive(Debug)]
49 pub struct Dir2BlockTail {
50     pub count: u32,
51     pub stale: u32,
52 }
53 
54 impl Dir2BlockTail {
from<T: BufRead>(buf_reader: &mut T) -> Dir2BlockTail55     pub fn from<T: BufRead>(buf_reader: &mut T) -> Dir2BlockTail {
56         let count = buf_reader.read_u32::<BigEndian>().unwrap();
57         let stale = buf_reader.read_u32::<BigEndian>().unwrap();
58 
59         Dir2BlockTail { count, stale }
60     }
61 }
62 
63 #[derive(Debug)]
64 pub struct Dir2BlockDisk {
65     pub hdr: Dir3DataHdr,
66     // pub u: Vec<Dir2DataUnion>,
67     pub leaf: Vec<Dir2LeafEntry>,
68     pub tail: Dir2BlockTail,
69 }
70 
71 impl Dir2BlockDisk {
from<T: BufRead + Seek>(buf_reader: &mut T, offset: u64, size: u32) -> Dir2BlockDisk72     pub fn from<T: BufRead + Seek>(buf_reader: &mut T, offset: u64, size: u32) -> Dir2BlockDisk {
73         buf_reader.seek(SeekFrom::Start(offset)).unwrap();
74 
75         let hdr = Dir3DataHdr::from(buf_reader.by_ref());
76 
77         buf_reader
78             .seek(SeekFrom::Start(
79                 offset + (size as u64) - (mem::size_of::<Dir2BlockTail>() as u64),
80             ))
81             .unwrap();
82 
83         let tail = Dir2BlockTail::from(buf_reader.by_ref());
84 
85         let data_end = offset + (size as u64)
86             - (mem::size_of::<Dir2BlockTail>() as u64)
87             - ((mem::size_of::<Dir2LeafEntry>() as u64) * (tail.count as u64));
88 
89         buf_reader.seek(SeekFrom::Start(data_end)).unwrap();
90 
91         let mut leaf = Vec::<Dir2LeafEntry>::new();
92         for _i in 0..tail.count {
93             let leaf_entry = Dir2LeafEntry::from(buf_reader.by_ref());
94             leaf.push(leaf_entry);
95         }
96 
97         Dir2BlockDisk { hdr, leaf, tail }
98     }
99 
get_data_end(&self, offset: u64, directory_block_size: u32) -> u64100     pub fn get_data_end(&self, offset: u64, directory_block_size: u32) -> u64 {
101         offset + (directory_block_size as u64)
102             - (mem::size_of::<Dir2BlockTail>() as u64)
103             - ((mem::size_of::<Dir2LeafEntry>() as u64) * (self.tail.count as u64))
104     }
105 }
106 
107 #[derive(Debug)]
108 pub struct Dir2Block {
109     pub offset: u64,
110     pub data_end: u64,
111     pub hashes: HashMap<XfsDahash, XfsDir2Dataptr>,
112 }
113 
114 impl Dir2Block {
from<T: BufRead + Seek>( buf_reader: &mut T, superblock: &Sb, start_block: u64, ) -> Dir2Block115     pub fn from<T: BufRead + Seek>(
116         buf_reader: &mut T,
117         superblock: &Sb,
118         start_block: u64,
119     ) -> Dir2Block {
120         let offset = start_block * (superblock.sb_blocksize as u64);
121         let dir_blk_size = superblock.sb_blocksize * (1 << superblock.sb_dirblklog);
122 
123         let dir_disk = Dir2BlockDisk::from(buf_reader.by_ref(), offset, dir_blk_size);
124 
125         let data_end = dir_disk.get_data_end(offset, dir_blk_size);
126 
127         let mut hashes = HashMap::new();
128         for leaf_entry in dir_disk.leaf {
129             hashes.insert(leaf_entry.hashval, leaf_entry.address);
130         }
131 
132         Dir2Block {
133             offset,
134             data_end,
135             hashes,
136         }
137     }
138 }
139 
140 impl<R: BufRead + Seek> Dir3<R> for Dir2Block {
lookup( &self, buf_reader: &mut R, super_block: &Sb, name: &str, ) -> Result<(FileAttr, u64), c_int>141     fn lookup(
142         &self,
143         buf_reader: &mut R,
144         super_block: &Sb,
145         name: &str,
146     ) -> Result<(FileAttr, u64), c_int> {
147         let hash = hashname(name);
148         if let Some(address) = self.hashes.get(&hash) {
149             let address = (*address as u64) * 8;
150 
151             buf_reader
152                 .seek(SeekFrom::Start(self.offset + address))
153                 .unwrap();
154             let entry = Dir2DataEntry::from(buf_reader.by_ref());
155 
156             let dinode = Dinode::from(buf_reader.by_ref(), super_block, entry.inumber);
157 
158             let kind = get_file_type(FileKind::Mode(dinode.di_core.di_mode))?;
159 
160             let attr = FileAttr {
161                 ino: entry.inumber,
162                 size: dinode.di_core.di_size as u64,
163                 blocks: dinode.di_core.di_nblocks,
164                 atime: Timespec {
165                     sec: dinode.di_core.di_atime.t_sec as i64,
166                     nsec: dinode.di_core.di_atime.t_nsec,
167                 },
168                 mtime: Timespec {
169                     sec: dinode.di_core.di_mtime.t_sec as i64,
170                     nsec: dinode.di_core.di_mtime.t_nsec,
171                 },
172                 ctime: Timespec {
173                     sec: dinode.di_core.di_ctime.t_sec as i64,
174                     nsec: dinode.di_core.di_ctime.t_nsec,
175                 },
176                 crtime: Timespec { sec: 0, nsec: 0 },
177                 kind,
178                 perm: dinode.di_core.di_mode & (!(S_IFMT as u16)),
179                 nlink: dinode.di_core.di_nlink,
180                 uid: dinode.di_core.di_uid,
181                 gid: dinode.di_core.di_gid,
182                 rdev: 0,
183                 flags: 0,
184             };
185 
186             Ok((attr, dinode.di_core.di_gen.into()))
187         } else {
188             Err(ENOENT)
189         }
190     }
191 
next( &self, buf_reader: &mut R, _super_block: &Sb, offset: i64, ) -> Result<(XfsIno, i64, FileType, String), c_int>192     fn next(
193         &self,
194         buf_reader: &mut R,
195         _super_block: &Sb,
196         offset: i64,
197     ) -> Result<(XfsIno, i64, FileType, String), c_int> {
198         let mut next = offset == 0;
199         let offset = if offset == 0 {
200             mem::size_of::<Dir3DataHdr>() as i64
201         } else {
202             offset
203         };
204         buf_reader
205             .seek(SeekFrom::Start(self.offset + (offset as u64)))
206             .unwrap();
207 
208         while buf_reader.stream_position().unwrap() < self.data_end {
209             let freetag = buf_reader.read_u16::<BigEndian>().unwrap();
210             buf_reader.seek(SeekFrom::Current(-2)).unwrap();
211 
212             if freetag == 0xffff {
213                 Dir2DataUnused::from(buf_reader.by_ref());
214             } else if next {
215                 let entry = Dir2DataEntry::from(buf_reader.by_ref());
216 
217                 let kind = get_file_type(FileKind::Type(entry.ftype))?;
218 
219                 let name = entry.name;
220 
221                 return Ok((entry.inumber, entry.tag.into(), kind, name));
222             } else {
223                 let length = Dir2DataEntry::get_length(buf_reader.by_ref());
224                 buf_reader.seek(SeekFrom::Current(length)).unwrap();
225 
226                 next = true;
227             }
228         }
229 
230         Err(ENOENT)
231     }
232 }
233