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