dir_entry.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. //! # The Defination of Ext4 Directory Entry
  2. //!
  3. //! A directory is a series of data blocks and that each block contains a
  4. //! linear array of directory entries.
  5. use super::crc::*;
  6. use super::Ext4Block;
  7. use super::Ext4Superblock;
  8. use crate::constants::*;
  9. use crate::prelude::*;
  10. use alloc::string::FromUtf8Error;
  11. #[repr(C)]
  12. pub union Ext4DirEnInner {
  13. pub name_length_high: u8, // 高8位的文件名长度
  14. pub inode_type: FileType, // 引用的inode的类型(在rev >= 0.5中)
  15. }
  16. impl Debug for Ext4DirEnInner {
  17. fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
  18. unsafe {
  19. write!(
  20. f,
  21. "Ext4DirEnInternal {{ name_length_high: {:?} }}",
  22. self.name_length_high
  23. )
  24. }
  25. }
  26. }
  27. impl Default for Ext4DirEnInner {
  28. fn default() -> Self {
  29. Self {
  30. name_length_high: 0,
  31. }
  32. }
  33. }
  34. #[repr(C)]
  35. #[derive(Debug)]
  36. pub struct Ext4DirEntry {
  37. inode: u32, // 该目录项指向的inode的编号
  38. rec_len: u16, // 到下一个目录项的距离
  39. name_len: u8, // 低8位的文件名长度
  40. inner: Ext4DirEnInner, // 联合体成员
  41. name: [u8; 255], // 文件名
  42. }
  43. impl Default for Ext4DirEntry {
  44. fn default() -> Self {
  45. Self {
  46. inode: 0,
  47. rec_len: 0,
  48. name_len: 0,
  49. inner: Ext4DirEnInner::default(),
  50. name: [0; 255],
  51. }
  52. }
  53. }
  54. impl<T> TryFrom<&[T]> for Ext4DirEntry {
  55. type Error = u64;
  56. fn try_from(data: &[T]) -> core::result::Result<Self, u64> {
  57. let data = data;
  58. Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
  59. }
  60. }
  61. impl Ext4DirEntry {
  62. pub fn name(&self) -> core::result::Result<String, FromUtf8Error> {
  63. let name_len = self.name_len as usize;
  64. let name = &self.name[..name_len];
  65. String::from_utf8(name.to_vec())
  66. }
  67. pub fn compare_name(&self, name: &str) -> bool {
  68. &self.name[..name.len()] == name.as_bytes()
  69. }
  70. pub fn set_name(&mut self, name: &str) {
  71. self.name_len = name.len() as u8;
  72. self.name[..name.len()].copy_from_slice(name.as_bytes());
  73. }
  74. pub fn rec_len(&self) -> u16 {
  75. self.rec_len
  76. }
  77. pub fn set_rec_len(&mut self, len: u16) {
  78. self.rec_len = len;
  79. }
  80. pub fn inode(&self) -> u32 {
  81. self.inode
  82. }
  83. pub fn set_inode(&mut self, inode: u32) {
  84. self.inode = inode;
  85. }
  86. /// Unused directory entries are signified by inode = 0
  87. pub fn unused(&self) -> bool {
  88. self.inode == 0
  89. }
  90. /// Set the dir entry's inode type given the corresponding inode mode
  91. pub fn set_entry_type(&mut self, inode_mode: u16) {
  92. self.inner.inode_type = inode_mode2file_type(inode_mode);
  93. }
  94. /// Get the required size to save this directory entry, 4-byte aligned
  95. pub fn required_size(name_len: usize) -> usize {
  96. // u32 + u16 + u8 + Ext4DirEnInner + name -> align to 4
  97. (core::mem::size_of::<Ext4FakeDirEntry>() + name_len + 3) / 4 * 4
  98. }
  99. /// Get the used size of this directory entry, 4-bytes alighed
  100. pub fn used_size(&self) -> usize {
  101. Self::required_size(self.name_len as usize)
  102. }
  103. pub fn calc_csum(&self, s: &Ext4Superblock, blk_data: &[u8]) -> u32 {
  104. let ino_index = self.inode;
  105. let ino_gen = 0 as u32;
  106. let uuid = s.uuid();
  107. let mut csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
  108. csum = ext4_crc32c(csum, &ino_index.to_le_bytes(), 4);
  109. csum = ext4_crc32c(csum, &ino_gen.to_le_bytes(), 4);
  110. let mut data = [0u8; 0xff4];
  111. unsafe {
  112. core::ptr::copy_nonoverlapping(blk_data.as_ptr(), data.as_mut_ptr(), blk_data.len());
  113. }
  114. csum = ext4_crc32c(csum, &data[..], 0xff4);
  115. csum
  116. }
  117. pub fn write_to_blk(&self, dst_blk: &mut Ext4Block, offset: usize) {
  118. let count = core::mem::size_of::<Ext4DirEntry>() / core::mem::size_of::<u8>();
  119. let data = unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, count) };
  120. dst_blk.block_data.splice(
  121. offset..offset + core::mem::size_of::<Ext4DirEntry>(),
  122. data.iter().cloned(),
  123. );
  124. // assert_eq!(dst_blk.block_data[offset..offset + core::mem::size_of::<Ext4DirEntry>()], data[..]);
  125. }
  126. pub fn copy_to_byte_slice(&self, slice: &mut [u8], offset: usize) {
  127. let de_ptr = self as *const Ext4DirEntry as *const u8;
  128. let slice_ptr = slice as *mut [u8] as *mut u8;
  129. let count = core::mem::size_of::<Ext4DirEntry>();
  130. unsafe {
  131. core::ptr::copy_nonoverlapping(de_ptr, slice_ptr.add(offset), count);
  132. }
  133. }
  134. }
  135. #[repr(C)]
  136. #[derive(Debug, Clone, Copy, Default)]
  137. pub struct Ext4DirEntryTail {
  138. pub reserved_zero1: u32,
  139. pub rec_len: u16,
  140. pub reserved_zero2: u8,
  141. pub reserved_ft: u8,
  142. pub checksum: u32, // crc32c(uuid+inum+dirblock)
  143. }
  144. impl Ext4DirEntryTail {
  145. pub fn from(data: &mut [u8], blocksize: usize) -> Option<Self> {
  146. unsafe {
  147. let ptr = data as *mut [u8] as *mut u8;
  148. let t = *(ptr.add(blocksize - core::mem::size_of::<Ext4DirEntryTail>())
  149. as *mut Ext4DirEntryTail);
  150. if t.reserved_zero1 != 0 || t.reserved_zero2 != 0 {
  151. log::info!("t.reserved_zero1");
  152. return None;
  153. }
  154. if t.rec_len.to_le() != core::mem::size_of::<Ext4DirEntryTail>() as u16 {
  155. log::info!("t.rec_len");
  156. return None;
  157. }
  158. if t.reserved_ft != 0xDE {
  159. log::info!("t.reserved_ft");
  160. return None;
  161. }
  162. Some(t)
  163. }
  164. }
  165. pub fn set_csum(&mut self, s: &Ext4Superblock, diren: &Ext4DirEntry, blk_data: &[u8]) {
  166. self.checksum = diren.calc_csum(s, blk_data);
  167. }
  168. pub fn copy_to_byte_slice(&self, slice: &mut [u8], offset: usize) {
  169. let de_ptr = self as *const Ext4DirEntryTail as *const u8;
  170. let slice_ptr = slice as *mut [u8] as *mut u8;
  171. let count = core::mem::size_of::<Ext4DirEntryTail>();
  172. unsafe {
  173. core::ptr::copy_nonoverlapping(de_ptr, slice_ptr.add(offset), count);
  174. }
  175. }
  176. }
  177. pub struct Ext4DirSearchResult<'a> {
  178. pub block: Ext4Block<'a>,
  179. pub dentry: Ext4DirEntry,
  180. }
  181. impl<'a> Ext4DirSearchResult<'a> {
  182. pub fn new(block: Ext4Block<'a>, dentry: Ext4DirEntry) -> Self {
  183. Self { block, dentry }
  184. }
  185. }
  186. /// Fake dir entry. A normal entry without `name` field`
  187. #[repr(C)]
  188. pub struct Ext4FakeDirEntry {
  189. inode: u32,
  190. entry_length: u16,
  191. name_length: u8,
  192. inode_type: u8,
  193. }
  194. #[derive(PartialEq, Eq, Clone, Copy, Debug)]
  195. #[repr(u8)]
  196. pub enum FileType {
  197. Unknown,
  198. RegularFile,
  199. Directory,
  200. CharacterDev,
  201. BlockDev,
  202. Fifo,
  203. Socket,
  204. SymLink,
  205. }
  206. pub fn inode_mode2file_type(inode_mode: u16) -> FileType {
  207. let file_type = (inode_mode & EXT4_INODE_MODE_TYPE_MASK) as usize;
  208. match file_type {
  209. EXT4_INODE_MODE_FILE => FileType::RegularFile,
  210. EXT4_INODE_MODE_DIRECTORY => FileType::Directory,
  211. EXT4_INODE_MODE_CHARDEV => FileType::CharacterDev,
  212. EXT4_INODE_MODE_BLOCKDEV => FileType::BlockDev,
  213. EXT4_INODE_MODE_FIFO => FileType::Fifo,
  214. EXT4_INODE_MODE_SOCKET => FileType::Socket,
  215. EXT4_INODE_MODE_SOFTLINK => FileType::SymLink,
  216. _ => FileType::Unknown,
  217. }
  218. }
  219. pub fn file_type2inode_mode(dirent_type: FileType) -> u16 {
  220. let inode_mode = match dirent_type {
  221. FileType::RegularFile => EXT4_INODE_MODE_FILE,
  222. FileType::Directory => EXT4_INODE_MODE_DIRECTORY,
  223. FileType::SymLink => EXT4_INODE_MODE_SOFTLINK,
  224. FileType::CharacterDev => EXT4_INODE_MODE_CHARDEV,
  225. FileType::BlockDev => EXT4_INODE_MODE_BLOCKDEV,
  226. FileType::Fifo => EXT4_INODE_MODE_FIFO,
  227. FileType::Socket => EXT4_INODE_MODE_SOCKET,
  228. _ => EXT4_INODE_MODE_FILE,
  229. };
  230. inode_mode as u16
  231. }