#![allow(dead_code)] use core::cmp::min; use alloc::{ string::{String, ToString}, sync::Arc, vec::Vec, }; use crate::{ include::bindings::bindings::{ EEXIST, EILSEQ, EINVAL, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY, EPERM, EROFS, }, io::{device::LBA_SIZE, SeekFrom}, kwarn, libs::vec_cursor::VecCursor, }; use super::{ fs::{Cluster, FATFileSystem, MAX_FILE_SIZE}, utils::decode_u8_ascii, }; #[derive(Debug, Clone, Copy, Default)] pub struct FileAttributes { value: u8, } /// FAT表中,关于每个簇的信息 #[derive(Debug, Eq, PartialEq)] pub enum FATEntry { /// 当前簇未使用 Unused, /// 当前簇是坏簇 Bad, /// 当前簇是整个FAT簇链的最后一个簇 EndOfChain, /// 在整个链中,当前簇的下一个簇的值 Next(Cluster), } /// FAT目录项的枚举类型 #[derive(Debug, Clone)] pub enum FATDirEntry { File(FATFile), VolId(FATFile), Dir(FATDir), UnInit, } /// FAT文件系统中的文件 #[derive(Debug, Default, Clone)] pub struct FATFile { /// 文件的第一个簇 pub first_cluster: Cluster, /// 文件名 pub file_name: String, /// 文件对应的短目录项 pub short_dir_entry: ShortDirEntry, /// 文件目录项的起始、终止簇。格式:(簇,簇内偏移量) pub loc: ((Cluster, u64), (Cluster, u64)), } impl FATFile { /// @brief 获取文件大小 #[inline] pub fn size(&self) -> u64 { return self.short_dir_entry.file_size as u64; } /// @brief 设置当前文件大小(仅仅更改short_dir_entry内的值) #[inline] pub fn set_size(&mut self, size: u32) { self.short_dir_entry.file_size = size; } /// @brief 从文件读取数据。读取的字节数与buf长度相等 /// /// @param buf 输出缓冲区 /// @param offset 起始位置在文件中的偏移量 /// /// @return Ok(usize) 成功读取到的字节数 /// @return Err(i32) 读取时出现错误,返回错误码 pub fn read(&self, fs: &Arc, buf: &mut [u8], offset: u64) -> Result { if offset >= self.size() { return Ok(0); } // 文件内的簇偏移量 let start_cluster_number: u64 = offset / fs.bytes_per_cluster(); // 计算对应在分区内的簇号 let mut current_cluster = if let Some(c) = fs.get_cluster_by_relative(self.first_cluster, start_cluster_number as usize) { c } else { return Ok(0); }; let bytes_remain: u64 = self.size() - offset; // 计算簇内偏移量 let mut in_cluster_offset: u64 = offset % fs.bytes_per_cluster(); let to_read_size: usize = min(buf.len(), bytes_remain as usize); let mut start = 0; let mut read_ok = 0; loop { // 当前簇已经读取完,尝试读取下一个簇 if in_cluster_offset >= fs.bytes_per_cluster() { if let Some(FATEntry::Next(c)) = fs.get_fat_entry(current_cluster).ok() { current_cluster = c; in_cluster_offset %= fs.bytes_per_cluster(); } else { break; } } // 计算下一次读取,能够读多少字节 let end_len: usize = min( to_read_size - read_ok, min( (fs.bytes_per_cluster() - in_cluster_offset) as usize, buf.len() - read_ok, ), ); // 从磁盘上读取数据 let offset = fs.cluster_bytes_offset(current_cluster) + in_cluster_offset; let r = fs.partition.disk().device().read_at( offset as usize, end_len, &mut buf[start..start + end_len], )?; // 更新偏移量计数信息 read_ok += r; start += r; in_cluster_offset += r as u64; if read_ok == to_read_size { break; } } // todo: 更新时间信息 return Ok(read_ok); } /// @brief 向文件写入数据。写入的字节数与buf长度相等 /// /// @param buf 输入缓冲区 /// @param offset 起始位置在文件中的偏移量 /// /// @return Ok(usize) 成功写入的字节数 /// @return Err(i32) 写入时出现错误,返回错误码 pub fn write( &mut self, fs: &Arc, buf: &[u8], offset: u64, ) -> Result { self.ensure_len(fs, offset, buf.len() as u64)?; // 要写入的第一个簇的簇号 let start_cluster_num = offset / fs.bytes_per_cluster(); // 获取要写入的第一个簇 let mut current_cluster: Cluster = if let Some(c) = fs.get_cluster_by_relative(self.first_cluster, start_cluster_num as usize) { c } else { return Ok(0); }; let mut in_cluster_bytes_offset: u64 = offset % fs.bytes_per_cluster(); let mut start: usize = 0; let mut write_ok: usize = 0; // 循环写入数据 loop { if in_cluster_bytes_offset >= fs.bytes_per_cluster() { if let Some(FATEntry::Next(c)) = fs.get_fat_entry(current_cluster).ok() { current_cluster = c; in_cluster_bytes_offset = in_cluster_bytes_offset % fs.bytes_per_cluster(); } else { break; } } let end_len = min( (fs.bytes_per_cluster() - in_cluster_bytes_offset) as usize, buf.len() - write_ok, ); // 计算本次写入位置在磁盘上的偏移量 let offset = fs.cluster_bytes_offset(current_cluster) + in_cluster_bytes_offset; // 写入磁盘 let w: usize = fs.partition.disk().device().write_at( offset as usize, end_len, &buf[start..start + end_len], )?; // 更新偏移量数据 write_ok += w; start += w; in_cluster_bytes_offset += w as u64; if write_ok == buf.len() { break; } } // todo: 更新时间信息 return Ok(write_ok); } /// @brief 确保文件从指定偏移量开始,仍有长度为len的空间。 /// 如果文件大小不够,就尝试分配更多的空间给这个文件。 /// /// @param fs 当前文件所属的文件系统 /// @param offset 起始位置在文件内的字节偏移量 /// @param len 期待的空闲空间长度 /// /// @return Ok(()) 经过操作后,offset后面具有长度至少为len的空闲空间 /// @return Err(i32) 处理过程中出现了异常。 fn ensure_len(&mut self, fs: &Arc, offset: u64, len: u64) -> Result<(), i32> { // 文件内本身就还有空余的空间 if offset + len <= self.size() { return Ok(()); } // 如果文件大小为0,证明它还没有分配簇,因此分配一个簇给它 if self.size() == 0 { // first_cluster应当为0,否则将产生空间泄露的错误 assert_eq!(self.first_cluster, Cluster::default()); self.first_cluster = fs.allocate_cluster(None)?; self.short_dir_entry.set_first_cluster(self.first_cluster); } // 计算文件的最后一个簇中有多少空闲空间 let in_cluster_offset = self.size() % fs.bytes_per_cluster(); let bytes_remain_in_cluster = fs.bytes_per_cluster() - in_cluster_offset; // 计算还需要申请多少空间 let extra_bytes = min((offset + len) - self.size(), MAX_FILE_SIZE - self.size()); // 如果还需要更多的簇 if bytes_remain_in_cluster < extra_bytes { let clusters_to_allocate = (extra_bytes - bytes_remain_in_cluster + fs.bytes_per_cluster() - 1) / fs.bytes_per_cluster(); let last_cluster = if let Some(c) = fs.get_last_cluster(self.first_cluster) { c } else { kwarn!("FAT: last cluster not found, File = {self:?}"); return Err(-(EINVAL as i32)); }; // 申请簇 let mut current_cluster: Cluster = last_cluster; for _ in 0..clusters_to_allocate { current_cluster = fs.allocate_cluster(Some(current_cluster))?; } } // 如果文件被扩展,则清空刚刚被扩展的部分的数据 if offset > self.size() { // 文件内的簇偏移 let start_cluster: u64 = self.size() / fs.bytes_per_cluster(); let start_cluster: Cluster = fs .get_cluster_by_relative(self.first_cluster, start_cluster as usize) .unwrap(); // 计算当前文件末尾在磁盘上的字节偏移量 let start_offset: u64 = fs.cluster_bytes_offset(start_cluster) + self.size() % fs.bytes_per_cluster(); // 扩展之前,最后一个簇内还剩下多少字节的空间 let bytes_remain: u64 = fs.bytes_per_cluster() - (self.size() % fs.bytes_per_cluster()); // 计算在扩展之后的最后一个簇内,文件的终止字节 let cluster_offset_start = offset / fs.bytes_per_cluster(); // 扩展后,文件的最后 let end_cluster: Cluster = fs .get_cluster_by_relative(self.first_cluster, cluster_offset_start as usize) .unwrap(); if start_cluster != end_cluster { self.zero_range(fs, start_offset, start_offset + bytes_remain)?; } else { self.zero_range(fs, start_offset, start_offset + offset - self.size())?; } } // 计算文件的新大小 let new_size = self.size() + extra_bytes; self.set_size(new_size as u32); // 计算短目录项所在的位置,更新短目录项 let short_entry_offset = fs.cluster_bytes_offset(self.loc.1 .0) + self.loc.1 .1; // todo: 更新时间信息 // 把短目录项写入磁盘 self.short_dir_entry.flush(fs, short_entry_offset)?; return Ok(()); } /// @brief 把磁盘上[range_start, range_end)范围的数据清零 /// /// @param range_start 磁盘上起始位置(单位:字节) /// @param range_end 磁盘上终止位置(单位:字节) fn zero_range( &self, fs: &Arc, range_start: u64, range_end: u64, ) -> Result<(), i32> { if range_end <= range_start { return Ok(()); } let zeroes: Vec = vec![0u8; (range_end - range_start) as usize]; fs.partition.disk().device().write_at( range_start as usize, zeroes.len(), zeroes.as_slice(), )?; return Ok(()); } /// @brief 截断文件的内容,并设置新的文件大小。如果new_size大于当前文件大小,则不做操作。 /// /// @param new_size 新的文件大小,如果它大于当前文件大小,则不做操作。 /// /// @return Ok(()) 操作成功 /// @return Err(i32) 在操作时出现错误 pub fn truncate(&mut self, fs: &Arc, new_size: u64) -> Result<(), i32> { if new_size >= self.size() { return Ok(()); } let new_last_cluster = new_size / fs.bytes_per_cluster(); if let Some(begin_delete) = fs.get_cluster_by_relative(self.first_cluster, (new_last_cluster + 1) as usize) { fs.deallocate_cluster(begin_delete)?; }; self.set_size(new_size as u32); // 计算短目录项在磁盘内的字节偏移量 let short_entry_offset = fs.cluster_bytes_offset((self.loc.1).0) + (self.loc.1).1; self.short_dir_entry.flush(fs, short_entry_offset)?; return Ok(()); } } /// FAT文件系统中的文件夹 #[derive(Debug, Default, Clone)] pub struct FATDir { /// 目录的第一个簇 pub first_cluster: Cluster, /// 该字段仅对FAT12、FAT16生效 pub root_offset: Option, /// 文件夹名称 pub dir_name: String, pub short_dir_entry: Option, /// 文件的起始、终止簇。格式:(簇,簇内偏移量) pub loc: Option<((Cluster, u64), (Cluster, u64))>, } impl FATDir { /// @brief 获得用于遍历当前目录的迭代器 /// /// @param fs 当前目录所在的文件系统 pub fn to_iter(&self, fs: Arc) -> FATDirIter { return FATDirIter { current_cluster: self.first_cluster, offset: self.root_offset.unwrap_or(0), is_root: self.is_root(), fs: fs, }; } /// @brief 判断当前目录是否为根目录(仅对FAT12和FAT16生效) #[inline] pub fn is_root(&self) -> bool { return self.root_offset.is_some(); } /// @brief 获取当前目录所占用的大小 pub fn size(&self, fs: &Arc) -> u64 { return fs.num_clusters_chain(self.first_cluster) * fs.bytes_per_cluster(); } /// @brief 在目录项中,寻找num_free个连续空闲目录项 /// /// @param num_free 需要的空闲目录项数目. /// @param fs 当前文件夹属于的文件系统 /// /// @return Ok(Option<(第一个符合条件的空闲目录项所在的簇,簇内偏移量)) /// @return Err(错误码) pub fn find_free_entries( &self, num_free: u64, fs: Arc, ) -> Result, i32> { let mut free = 0; let mut current_cluster: Cluster = self.first_cluster; let mut offset = self.root_offset.unwrap_or(0); // 第一个符合条件的空闲目录项 let mut first_free: Option<(Cluster, u64)> = None; loop { // 如果当前簇没有空间了,并且当前不是FAT12和FAT16的根目录,那么就读取下一个簇。 if offset >= fs.bytes_per_cluster() && !self.is_root() { // 成功读取下一个簇 if let Some(FATEntry::Next(c)) = fs.get_fat_entry(current_cluster).ok() { current_cluster = c; // 计算簇内偏移量 offset = offset % fs.bytes_per_cluster(); } else { // 读取失败,当前已经是最后一个簇,退出循环 break; } } // 如果当前目录是FAT12和FAT16的根目录,且已经读取完,就直接返回。 if self.is_root() && offset > fs.root_dir_end_bytes_offset().unwrap() { return Ok(None); } let e_offset = fs.cluster_bytes_offset(current_cluster) + offset; let entry: FATRawDirEntry = get_raw_dir_entry(&fs, e_offset)?; match entry { FATRawDirEntry::Free | FATRawDirEntry::FreeRest => { if free == 0 { first_free = Some((current_cluster, offset)); } free += 1; if free == num_free { // kdebug!("first_free = {first_free:?}, current_free = ({current_cluster:?}, {offset})"); return Ok(first_free); } } // 遇到一个不空闲的目录项,那么重新开始计算空闲目录项 _ => { free = 0; } } offset += FATRawDirEntry::DIR_ENTRY_LEN; } // 剩余的需要获取的目录项 let remain_entries = num_free - free; // 计算需要申请多少个簇 let clusters_required = (remain_entries * FATRawDirEntry::DIR_ENTRY_LEN + fs.bytes_per_cluster() - 1) / fs.bytes_per_cluster(); let mut first_cluster = Cluster::default(); let mut prev_cluster = current_cluster; // kdebug!( // "clusters_required={clusters_required}, prev_cluster={prev_cluster:?}, free ={free}" // ); // 申请簇 for i in 0..clusters_required { let c: Cluster = fs.allocate_cluster(Some(prev_cluster))?; if i == 0 { first_cluster = c; } prev_cluster = c; } if free > 0 { // 空闲目录项跨越了簇,返回第一个空闲目录项 return Ok(first_free); } else { // 空闲目录项是在全新的簇开始的 return Ok(Some((first_cluster, 0))); } } /// @brief 在当前目录中寻找目录项 /// /// @param name 目录项的名字 /// @param expect_dir 该值为Some时有效。如果期待目标目录项是文件夹,那么值为Some(true), 否则为Some(false). /// @param short_name_gen 短目录项名称生成器 /// @param fs 当前目录所属的文件系统 /// /// @return Ok(FATDirEntry) 找到期待的目录项 /// @return Err(i32) 错误码 pub fn find_entry( &self, name: &str, expect_dir: Option, mut short_name_gen: Option<&mut ShortNameGenerator>, fs: Arc, ) -> Result { LongDirEntry::validate_long_name(name)?; // 迭代当前目录下的文件/文件夹 for e in self.to_iter(fs) { if e.eq_name(name) { if expect_dir.is_some() && Some(e.is_dir()) != expect_dir { if e.is_dir() { // 期望得到文件,但是是文件夹 return Err(-(EISDIR as i32)); } else { // 期望得到文件夹,但是是文件 return Err(-(ENOTDIR as i32)); } } // 找到期望的目录项 return Ok(e); } if let Some(ref mut sng) = short_name_gen { sng.add_name(&e.short_name_raw()) } } // 找不到文件/文件夹 return Err(-(ENOENT as i32)); } /// @brief 在当前目录下打开文件,获取FATFile结构体 pub fn open_file(&self, name: &str, fs: Arc) -> Result { let f: FATFile = self.find_entry(name, Some(false), None, fs)?.to_file()?; return Ok(f); } /// @brief 在当前目录下打开文件夹,获取FATDir结构体 pub fn open_dir(&self, name: &str, fs: Arc) -> Result { let d: FATDir = self.find_entry(name, Some(true), None, fs)?.to_dir()?; return Ok(d); } /// @brief 在当前文件夹下创建文件。 /// /// @param name 文件名 /// @param fs 当前文件夹所属的文件系统 pub fn create_file(&self, name: &str, fs: &Arc) -> Result { let r: Result = self.check_existence(name, Some(false), fs.clone()); // 检查错误码,如果能够表明目录项已经存在,则返回-EEXIST if r.is_err() { let err_val = r.unwrap_err(); if err_val == (-(EISDIR as i32)) || err_val == (-(ENOTDIR as i32)) { return Err(-(EEXIST as i32)); } else { return Err(err_val); } } match r.unwrap() { FATDirEntryOrShortName::ShortName(short_name) => { // 确认名称是一个可行的长文件名 LongDirEntry::validate_long_name(name)?; // 创建目录项 let x: Result = self .create_dir_entries( name.trim(), &short_name, None, FileAttributes { value: FileAttributes::ARCHIVE, }, fs.clone(), ) .map(|e| e.to_file())?; return x; } FATDirEntryOrShortName::DirEntry(_) => { // 已经存在这样的一个目录项了 return Err(-(EEXIST as i32)); } } } pub fn create_dir(&self, name: &str, fs: &Arc) -> Result { let r: Result = self.check_existence(name, Some(true), fs.clone()); // kdebug!("check existence ok"); // 检查错误码,如果能够表明目录项已经存在,则返回-EEXIST if r.is_err() { let err_val = r.unwrap_err(); if err_val == (-(EISDIR as i32)) || err_val == (-(ENOTDIR as i32)) { return Err(-(EEXIST as i32)); } else { return Err(err_val); } } match r.unwrap() { // 文件夹不存在,创建文件夹 FATDirEntryOrShortName::ShortName(short_name) => { LongDirEntry::validate_long_name(name)?; // 目标目录项 let mut short_entry = ShortDirEntry::default(); // kdebug!("to allocate cluster"); let first_cluster: Cluster = fs.allocate_cluster(None)?; short_entry.set_first_cluster(first_cluster); // kdebug!("to create dot"); // === 接下来在子目录中创建'.'目录项和'..'目录项 let mut offset = 0; // '.'目录项 let mut dot_entry = ShortDirEntry::default(); dot_entry.name = ShortNameGenerator::new(".").generate().unwrap(); dot_entry.attributes.value = FileAttributes::DIRECTORY; dot_entry.set_first_cluster(first_cluster); // todo: 设置创建、访问时间 dot_entry.flush(&fs, fs.cluster_bytes_offset(first_cluster) + offset)?; drop(dot_entry); // 偏移量加上一个目录项的长度 offset += FATRawDirEntry::DIR_ENTRY_LEN; // kdebug!("to create dot dot"); // '..'目录项 let mut dot_dot_entry = ShortDirEntry::default(); dot_dot_entry.name = ShortNameGenerator::new("..").generate().unwrap(); dot_dot_entry.attributes.value = FileAttributes::DIRECTORY; dot_dot_entry.set_first_cluster(self.first_cluster); // todo: 设置创建、访问时间 dot_dot_entry.flush(&fs, fs.cluster_bytes_offset(first_cluster) + offset)?; // kdebug!("to create dentries"); // 在当前目录下创建目标目录项 let res = self .create_dir_entries( name.trim(), &short_name, Some(short_entry), FileAttributes { value: FileAttributes::DIRECTORY, }, fs.clone(), ) .map(|e| e.to_dir())?; // kdebug!("create dentries ok"); return res; } FATDirEntryOrShortName::DirEntry(_) => { // 已经存在这样的一个目录项了 return Err(-(EEXIST as i32)); } } } /// @brief 检查目录项在当前文件夹下是否存在 /// /// @param name 目录项的名字 /// @param expect_dir 该值为Some时有效。如果期待目标目录项是文件夹,那么值为Some(true), 否则为Some(false). /// @param fs 当前目录所属的文件系统 /// /// @return Ok(FATDirEntryOrShortName::DirEntry) 找到期待的目录项 /// @return Ok(FATDirEntryOrShortName::ShortName) 当前文件夹下不存在指定的目录项,因此返回一个可行的短文件名 /// @return Err(i32) 错误码 pub fn check_existence( &self, name: &str, expect_dir: Option, fs: Arc, ) -> Result { let mut sng = ShortNameGenerator::new(name); loop { let e: Result = self.find_entry(name, expect_dir, Some(&mut sng), fs.clone()); match e { Ok(e) => { // 找到,返回目录项 return Ok(FATDirEntryOrShortName::DirEntry(e)); } Err(e) => { // 如果没找到,则不返回错误 if e == -(ENOENT as i32) { } else { // 其他错误,则返回 return Err(e); } } } // 没找到文件,则生成短文件名 if let Ok(name) = sng.generate() { return Ok(FATDirEntryOrShortName::ShortName(name)); } sng.next_iteration(); } } /// @brief 创建一系列的目录项 /// /// @param long_name 长文件名 /// @param short_name 短文件名 /// @param short_dentry 可选的生成好的短目录项结构体 /// @param attrs FAT目录项的属性 /// @param fs 当前文件夹所属的文件系统 /// /// @return Ok(FATDirEntry) FAT目录项的枚举类型(目录项链条的最后一个长目录项) fn create_dir_entries( &self, long_name: &str, short_name: &[u8; 11], short_dentry: Option, attrs: FileAttributes, fs: Arc, ) -> Result { let mut short_dentry: ShortDirEntry = short_dentry.unwrap_or(ShortDirEntry::default()); short_dentry.name = short_name.clone(); short_dentry.attributes = attrs; // todo: 设置创建时间、修改时间 let mut long_name_gen: LongNameEntryGenerator = LongNameEntryGenerator::new(long_name, short_dentry.checksum()); let num_entries = long_name_gen.num_entries() as u64; // kdebug!("to find free entries"); let free_entries: Option<(Cluster, u64)> = self.find_free_entries(num_entries, fs.clone())?; // 目录项开始位置 let start_loc: (Cluster, u64) = match free_entries { Some(c) => c, None => return Err(-(ENOSPC as i32)), }; let offsets: Vec<(Cluster, u64)> = FATDirEntryOffsetIter::new(fs.clone(), start_loc, num_entries, None).collect(); // 迭代长目录项 for off in &offsets.as_slice()[..offsets.len() - 1] { // 获取生成的下一个长目录项 let long_entry: LongDirEntry = long_name_gen.next().unwrap(); // 获取这个长目录项在磁盘内的字节偏移量 let bytes_offset = fs.cluster_bytes_offset(off.0) + off.1; long_entry.flush(fs.clone(), bytes_offset)?; } let start: (Cluster, u64) = offsets[0]; let end: (Cluster, u64) = *offsets.last().unwrap(); // 短目录项在磁盘上的字节偏移量 let offset = fs.cluster_bytes_offset(end.0) + end.1; short_dentry.flush(&fs, offset)?; return Ok(short_dentry.to_dir_entry_with_long_name(long_name.to_string(), (start, end))); } /// @brief 判断当前目录是否为空 /// /// @return true 当前目录为空 /// @return false 当前目录不为空 pub fn is_empty(&self, fs: Arc) -> bool { for e in self.to_iter(fs) { let s = e.short_name(); if s == "." || s == ".." { continue; } else { return false; } } return true; } /// @brief 从当前文件夹中删除文件或者文件夹。如果目标文件夹不为空,则不能删除,返回-ENOTEMPTY. /// /// @param fs 当前FATDir所属的文件系统 /// @param name 目录项的名字 /// @param remove_clusters 是否删除与指定的目录项相关联的数据簇 /// /// @return Ok() 成功时无返回值 /// @return Err(i32) 如果目标文件夹不为空,则不能删除,返回-ENOTEMPTY. 或者返回底层传上来的错误 pub fn remove( &self, fs: Arc, name: &str, remove_clusters: bool, ) -> Result<(), i32> { let e: FATDirEntry = self.find_entry(name, None, None, fs.clone())?; // 判断文件夹是否为空,如果空,则不删除,报错。 if e.is_dir() && !(e.to_dir().unwrap().is_empty(fs.clone())) { return Err(-(ENOTEMPTY as i32)); } if e.first_cluster().cluster_num >= 2 && remove_clusters { // 删除与指定的目录项相关联的数据簇 fs.deallocate_cluster_chain(e.first_cluster())?; } if e.get_dir_range().is_some() { self.remove_dir_entries(fs, e.get_dir_range().unwrap())?; } return Ok(()); } /// @brief 在当前目录中删除多个目录项 /// /// @param fs 当前目录所属的文件系统 /// @param cluster_range 要删除的目录项的范围(以簇+簇内偏移量的形式表示) fn remove_dir_entries( &self, fs: Arc, cluster_range: ((Cluster, u64), (Cluster, u64)), ) -> Result<(), i32> { // 收集所有的要移除的目录项 let offsets: Vec<(Cluster, u64)> = FATDirEntryOffsetIter::new(fs.clone(), cluster_range.0, 15, Some(cluster_range.1)) .collect(); // 逐个设置这些目录项为“空闲”状态 for off in offsets { let disk_bytes_offset = fs.cluster_bytes_offset(off.0) + off.1; let mut short_entry = ShortDirEntry::default(); short_entry.name[0] = 0xe5; short_entry.flush(&fs, disk_bytes_offset)?; } return Ok(()); } /// @brief 根据名字在当前文件夹下寻找目录项 /// /// @return Ok(FATDirEntry) 目标目录项 /// @return Err(i32) 底层传上来的错误码 pub fn get_dir_entry(&self, fs: Arc, name: &str) -> Result { if name == "." || name == "/" { return Ok(FATDirEntry::Dir(self.clone())); } LongDirEntry::validate_long_name(name)?; return self.find_entry(name, None, None, fs); } /// @brief 在当前目录内,重命名一个目录项 /// pub fn rename( &self, fs: Arc, old_name: &str, new_name: &str, ) -> Result { // 判断源目录项是否存在 let old_dentry: FATDirEntry = if let FATDirEntryOrShortName::DirEntry(dentry) = self.check_existence(old_name, None, fs.clone())? { dentry } else { // 如果目标目录项不存在,则返回错误 return Err(-(ENOENT as i32)); }; let short_name = if let FATDirEntryOrShortName::ShortName(s) = self.check_existence(new_name, None, fs.clone())? { s } else { // 如果目标目录项存在,那么就返回错误 return Err(-(EEXIST as i32)); }; let old_short_dentry: Option = old_dentry.short_dir_entry(); if let Some(se) = old_short_dentry { // 删除原来的目录项 self.remove(fs.clone(), old_dentry.name().as_str(), false)?; // 创建新的目录项 let new_dentry: FATDirEntry = self.create_dir_entries( new_name, &short_name, Some(se), se.attributes, fs.clone(), )?; return Ok(new_dentry); } else { // 不允许对根目录项进行重命名 return Err(-(EPERM as i32)); } } } impl FileAttributes { pub const READ_ONLY: u8 = 1 << 0; pub const HIDDEN: u8 = 1 << 1; pub const SYSTEM: u8 = 1 << 2; pub const VOLUME_ID: u8 = 1 << 3; pub const DIRECTORY: u8 = 1 << 4; pub const ARCHIVE: u8 = 1 << 5; pub const LONG_NAME: u8 = FileAttributes::READ_ONLY | FileAttributes::HIDDEN | FileAttributes::SYSTEM | FileAttributes::VOLUME_ID; /// @brief 判断属性是否存在 #[inline] pub fn contains(&self, attr: u8) -> bool { return (self.value & attr) != 0; } pub fn new(attr: u8) -> Self { return Self { value: attr }; } } /// FAT32的短目录项 #[derive(Debug, Clone, Copy, Default)] pub struct ShortDirEntry { /// short name name: [u8; 11], /// 目录项属性 (见 FileAttributes ) attributes: FileAttributes, /// Windows NT系统的保留字段。用来表示短目录项文件名。 /// EXT|BASE => 8(BASE).3(EXT) /// BASE:LowerCase(8),UpperCase(0) /// EXT:LowerCase(16),UpperCase(0) nt_res: u8, /// 文件创建时间的毫秒级时间戳 crt_time_tenth: u8, /// 创建时间 crt_time: u16, /// 创建日期 crt_date: u16, /// 最后一次访问日期 lst_acc_date: u16, /// High word of first cluster(0 for FAT12 and FAT16) fst_clus_hi: u16, /// 最后写入时间 wrt_time: u16, /// 最后写入日期 wrt_date: u16, /// Low word of first cluster fst_clus_lo: u16, /// 文件大小 file_size: u32, } /// FAT32的长目录项 #[derive(Debug, Clone, Copy, Default)] pub struct LongDirEntry { /// 长目录项的序号 ord: u8, /// 长文件名的第1-5个字符,每个字符占2bytes name1: [u16; 5], /// 目录项属性必须为ATTR_LONG_NAME file_attrs: FileAttributes, /// Entry Type: 如果为0,则说明这是长目录项的子项 /// 非零值是保留的。 dirent_type: u8, /// 短文件名的校验和 checksum: u8, /// 长文件名的第6-11个字符,每个字符占2bytes name2: [u16; 6], /// 必须为0 first_clus_low: u16, /// 长文件名的12-13个字符,每个字符占2bytes name3: [u16; 2], } impl LongDirEntry { /// 长目录项的字符串长度(单位:word) pub const LONG_NAME_STR_LEN: usize = 13; /// @brief 初始化一个新的长目录项 /// /// @param ord 顺序 /// @param name_part 长目录项名称的数组(长度必须为13) /// @param check_sum 短目录项的校验和 /// /// @return Self 初始化好的长目录项对象 fn new(ord: u8, name_part: &[u16], check_sum: u8) -> Self { let mut result = LongDirEntry::default(); result.ord = ord; result .insert_name(name_part) .expect("Name part's len should be equal to 13."); result.file_attrs.value = FileAttributes::LONG_NAME; result.dirent_type = 0; result.checksum = check_sum; // 该字段需要外层的代码手动赋值 result.first_clus_low = 0; return result; } /// @brief 填写长目录项的名称字段。 /// /// @param name_part 要被填入当前长目录项的名字(数组长度必须为13) /// /// @return Ok(()) /// @return Err(i32) 错误码 fn insert_name(&mut self, name_part: &[u16]) -> Result<(), i32> { if name_part.len() != Self::LONG_NAME_STR_LEN { return Err(-(EINVAL as i32)); } self.name1.copy_from_slice(&name_part[0..5]); self.name2.copy_from_slice(&name_part[5..11]); self.name3.copy_from_slice(&name_part[11..13]); return Ok(()); } /// @brief 将当前长目录项的名称字段,原样地拷贝到一个长度为13的u16数组中。 /// @param dst 拷贝的目的地,一个[u16]数组,长度必须为13。 pub fn copy_name_to_slice(&self, dst: &mut [u16]) -> Result<(), i32> { if dst.len() != Self::LONG_NAME_STR_LEN { return Err(-(EINVAL as i32)); } dst[0..5].copy_from_slice(&self.name1); dst[5..11].copy_from_slice(&self.name2); dst[11..13].copy_from_slice(&self.name3); return Ok(()); } /// @brief 是否为最后一个长目录项 /// /// @return true 是最后一个长目录项 /// @return false 不是最后一个长目录项 pub fn is_last(&self) -> bool { return self.ord & 0x40 > 0; } /// @brief 校验字符串是否符合长目录项的命名要求 /// /// @return Ok(()) 名称合法 /// @return Err(i32) 名称不合法,返回错误码 pub fn validate_long_name(mut name: &str) -> Result<(), i32> { // 去除首尾多余的空格 name = name.trim(); // 名称不能为0 if name.len() == 0 { return Err(-(EINVAL as i32)); } // 名称长度不能大于255 if name.len() > 255 { return Err(-(ENAMETOOLONG as i32)); } // 检查是否符合命名要求 for c in name.chars() { match c { 'a'..='z' | 'A'..='Z' | '0'..='9' => {} '\u{80}'..='\u{ffff}' => {} '$' | '%' | '\'' | '-' | '_' | '@' | '~' | '`' | '!' | '(' | ')' | '{' | '}' | '^' | '#' | '&' => {} '+' | ',' | ';' | '=' | '[' | ']' | '.' | ' ' => {} _ => { return Err(-(EILSEQ as i32)); } } } return Ok(()); } /// @brief 把当前长目录项写入磁盘 /// /// @param fs 对应的文件系统 /// @param disk_bytes_offset 长目录项所在位置对应的在磁盘上的字节偏移量 /// /// @return Ok(()) /// @return Err(i32) 错误码 pub fn flush(&self, fs: Arc, disk_bytes_offset: u64) -> Result<(), i32> { // 从磁盘读取数据 let blk_offset = fs.get_in_block_offset(disk_bytes_offset); let lba = fs.get_lba_from_offset( fs.bytes_to_sector(fs.get_in_partition_bytes_offset(disk_bytes_offset)), ); let mut v: Vec = Vec::new(); v.resize(1 * fs.lba_per_sector() * LBA_SIZE, 0); fs.partition .disk() .read_at(lba, 1 * fs.lba_per_sector(), &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); // 切换游标到对应位置 cursor.seek(SeekFrom::SeekSet(blk_offset as i64))?; // 写入数据 cursor.write_u8(self.ord)?; for b in &self.name1 { cursor.write_u16(*b)?; } cursor.write_u8(self.file_attrs.value)?; cursor.write_u8(self.dirent_type)?; cursor.write_u8(self.checksum)?; for b in &self.name2 { cursor.write_u16(*b)?; } cursor.write_u16(self.first_clus_low)?; for b in &self.name3 { cursor.write_u16(*b)?; } // 把修改后的长目录项刷入磁盘 fs.partition .disk() .write_at(lba, 1 * fs.lba_per_sector(), cursor.as_slice())?; fs.partition.disk().sync()?; return Ok(()); } } impl ShortDirEntry { const PADDING: u8 = ' ' as u8; /// @brief 判断当前目录项是否为文件夹 /// /// @return true 是文件夹 /// @return false 不是文件夹 pub fn is_dir(&self) -> bool { return (self.attributes.contains(FileAttributes::DIRECTORY)) && (!self.attributes.contains(FileAttributes::VOLUME_ID)); } /// @brief 判断当前目录项是否为文件 /// /// @return true 是文件 /// @return false 不是文件 pub fn is_file(&self) -> bool { return (!self.attributes.contains(FileAttributes::DIRECTORY)) && (!self.attributes.contains(FileAttributes::VOLUME_ID)); } /// @brief 判断当前目录项是否为卷号 /// /// @return true 是卷号 /// @return false 不是卷号 pub fn is_volume_id(&self) -> bool { return (!self.attributes.contains(FileAttributes::DIRECTORY)) && self.attributes.contains(FileAttributes::VOLUME_ID); } /// @brief 将短目录项的名字转换为String fn name_to_string(&self) -> String { // 计算基础名的长度 let base_len = self.name[..8] .iter() .rposition(|x| *x != ShortDirEntry::PADDING) .map(|len| len + 1) .unwrap_or(0); // 计算扩展名的长度 let ext_len = self.name[8..] .iter() .rposition(|x| *x != ShortDirEntry::PADDING) .map(|len| len + 1) .unwrap_or(0); // 声明存储完整名字的数组(包含“.”) let mut name = [ShortDirEntry::PADDING; 12]; // 拷贝基础名 name[..base_len].copy_from_slice(&self.name[..base_len]); // 拷贝扩展名,并计算总的长度 let total_len = if ext_len > 0 { name[base_len] = '.' as u8; name[base_len + 1..base_len + 1 + ext_len].copy_from_slice(&self.name[8..8 + ext_len]); // 总长度为基础名长度+点号+扩展名长度 base_len + 1 + ext_len } else { base_len }; if name[0] == 0x05 { name[0] = 0xe5; } let iter = name[..total_len].iter().map(|c| decode_u8_ascii(*c)); // 返回最终的字符串 return String::from_iter(iter); } /// @brief 将短目录项结构体,转换为FATDirEntry枚举类型 /// /// @param loc 当前文件的起始、终止簇。格式:(簇,簇内偏移量) /// @return 生成的FATDirENtry枚举类型 pub fn to_dir_entry(&self, loc: (Cluster, u64)) -> FATDirEntry { // 当前文件的第一个簇 let first_cluster = Cluster::new(((self.fst_clus_hi as u64) << 16) | (self.fst_clus_lo as u64)); // 当前是文件或卷号 if self.is_file() || self.is_volume_id() { let mut file: FATFile = FATFile::default(); file.file_name = self.name_to_string(); file.first_cluster = first_cluster; file.short_dir_entry = self.clone(); file.loc = (loc, loc); // 根据当前短目录项的类型的不同,返回对应的枚举类型。 if self.is_file() { return FATDirEntry::File(file); } else { return FATDirEntry::VolId(file); } } else { // 当前是文件夹 let mut dir = FATDir::default(); dir.dir_name = self.name_to_string(); dir.first_cluster = first_cluster; dir.root_offset = None; dir.short_dir_entry = Some(self.clone()); dir.loc = Some((loc, loc)); return FATDirEntry::Dir(dir); } } /// @brief 将短目录项结构体,转换为FATDirEntry枚举类型. 并且,该短目录项具有对应的长目录项。 /// 因此,需要传入从长目录项获得的完整的文件名 /// /// @param name 从长目录项获取的完整文件名 /// @param loc 当前文件的起始、终止簇。格式:(簇,簇内偏移量) /// @return 生成的FATDirENtry枚举类型 pub fn to_dir_entry_with_long_name( &self, name: String, loc: ((Cluster, u64), (Cluster, u64)), ) -> FATDirEntry { // 当前文件的第一个簇 let first_cluster = Cluster::new(((self.fst_clus_hi as u64) << 16) | (self.fst_clus_lo as u64)); if self.is_file() || self.is_volume_id() { let mut file = FATFile::default(); file.first_cluster = first_cluster; file.file_name = name; file.loc = loc; file.short_dir_entry = self.clone(); if self.is_file() { return FATDirEntry::File(file); } else { return FATDirEntry::VolId(file); } } else { let mut dir = FATDir::default(); dir.first_cluster = first_cluster; dir.dir_name = name; dir.loc = Some(loc); dir.short_dir_entry = Some(self.clone()); dir.root_offset = None; return FATDirEntry::Dir(dir); } } /// @brief 计算短目录项的名称的校验和 fn checksum(&self) -> u8 { let mut result = 0; for c in &self.name { result = (result << 7) + (result >> 1) + *c; } return result; } /// @brief 把当前短目录项写入磁盘 /// /// @param fs 对应的文件系统 /// @param disk_bytes_offset 短目录项所在位置对应的在磁盘上的字节偏移量 /// /// @return Ok(()) /// @return Err(i32) 错误码 pub fn flush(&self, fs: &Arc, disk_bytes_offset: u64) -> Result<(), i32> { // 从磁盘读取数据 let blk_offset = fs.get_in_block_offset(disk_bytes_offset); let lba = fs.get_lba_from_offset( fs.bytes_to_sector(fs.get_in_partition_bytes_offset(disk_bytes_offset)), ); let mut v: Vec = Vec::new(); v.resize(1 * fs.lba_per_sector() * LBA_SIZE, 0); fs.partition .disk() .read_at(lba, 1 * fs.lba_per_sector(), &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); // 切换游标到对应位置 cursor.seek(SeekFrom::SeekSet(blk_offset as i64))?; cursor.write_exact(&self.name)?; cursor.write_u8(self.attributes.value)?; cursor.write_u8(self.nt_res)?; cursor.write_u8(self.crt_time_tenth)?; cursor.write_u16(self.crt_time)?; cursor.write_u16(self.crt_date)?; cursor.write_u16(self.lst_acc_date)?; cursor.write_u16(self.fst_clus_hi)?; cursor.write_u16(self.wrt_time)?; cursor.write_u16(self.wrt_date)?; cursor.write_u16(self.fst_clus_lo)?; cursor.write_u32(self.file_size)?; // 把修改后的长目录项刷入磁盘 fs.partition .disk() .write_at(lba, 1 * fs.lba_per_sector(), cursor.as_slice())?; fs.partition.disk().sync()?; return Ok(()); } /// @brief 设置短目录项的“第一个簇”字段的值 pub fn set_first_cluster(&mut self, cluster: Cluster) { self.fst_clus_lo = (cluster.cluster_num & 0x0000ffff) as u16; self.fst_clus_hi = ((cluster.cluster_num & 0xffff0000) >> 16) as u16; } } /// @brief FAT文件系统标准定义的目录项 #[derive(Debug, Clone)] pub enum FATRawDirEntry { /// 短目录项 Short(ShortDirEntry), /// 长目录项 Long(LongDirEntry), /// 当前目录项的Name[0]==0xe5, 是空闲目录项 Free, /// 当前目录项的Name[0]==0xe5, 是空闲目录项,且在这之后没有被分配过的目录项了。 FreeRest, } impl FATRawDirEntry { /// 每个目录项的长度(单位:字节) pub const DIR_ENTRY_LEN: u64 = 32; /// @brief 判断当前目录项是否为这个文件的最后一个目录项 fn is_last(&self) -> bool { match self { &Self::Short(_) => { return true; } &Self::Long(l) => { return l.is_last(); } _ => { return false; } } } /// @brief 判断当前目录项是否为长目录项 fn is_long(&self) -> bool { if let Self::Long(_) = self { return true; } else { return false; } } /// @brief 判断当前目录项是否为短目录项 fn is_short(&self) -> bool { if let Self::Short(_) = self { return true; } else { return false; } } } /// @brief FAT文件系统的目录项迭代器 #[derive(Debug)] pub struct FATDirIter { /// 当前正在迭代的簇 current_cluster: Cluster, /// 当前正在迭代的簇的簇内偏移量 offset: u64, /// True for the root directories of FAT12 and FAT16 is_root: bool, /// 指向当前文件系统的指针 fs: Arc, } impl FATDirIter { /// @brief 迭代当前inode的目录项(获取下一个目录项) /// /// @return Ok(Cluster, u64, Option) /// Cluster: 下一个要读取的簇号 /// u64: 下一个要读取的簇内偏移量 /// Option: 读取到的目录项(如果没有读取到,就返回失败) /// @return Err(错误码) 可能出现了内部错误,或者是磁盘错误等。具体原因看错误码。 fn get_dir_entry(&mut self) -> Result<(Cluster, u64, Option), i32> { loop { // 如果当前簇已经被读完,那么尝试获取下一个簇 if self.offset >= self.fs.bytes_per_cluster() && !self.is_root { match self.fs.get_fat_entry(self.current_cluster)? { FATEntry::Next(c) => { // 获得下一个簇的信息 self.current_cluster = c; self.offset %= self.fs.bytes_per_cluster(); } _ => { // 没有下一个簇了,返回None return Ok((self.current_cluster, self.offset, None)); } } } // 如果当前是FAT12/FAT16文件系统,并且当前inode是根目录项。 // 如果offset大于根目录项的最大大小(已经遍历完根目录),那么就返回None if self.is_root && self.offset > self.fs.root_dir_end_bytes_offset().unwrap() { return Ok((self.current_cluster, self.offset, None)); } // 获取簇在磁盘内的字节偏移量 let offset: u64 = self.fs.cluster_bytes_offset(self.current_cluster) + self.offset; // 从磁盘读取原始的dentry let raw_dentry: FATRawDirEntry = get_raw_dir_entry(&self.fs, offset)?; // 由于迭代顺序从前往后,因此: // 如果找到1个短目录项,那么证明有一个完整的entry被找到,因此返回。 // 如果找到1个长目录项,那么,就依次往下迭代查找,直到找到一个短目录项,然后返回结果。这里找到的所有的目录项,都属于同一个文件/文件夹。 match raw_dentry { FATRawDirEntry::Short(s) => { // 当前找到一个短目录项,更新offset之后,直接返回 self.offset += FATRawDirEntry::DIR_ENTRY_LEN; return Ok(( self.current_cluster, self.offset, Some(s.to_dir_entry(( self.current_cluster, self.offset - FATRawDirEntry::DIR_ENTRY_LEN, ))), )); } FATRawDirEntry::Long(_) => { // 当前找到一个长目录项 // 声明一个数组,来容纳所有的entry。(先把最后一个entry放进去) let mut long_name_entries: Vec = vec![raw_dentry]; let start_offset: u64 = self.offset; let start_cluster: Cluster = self.current_cluster; self.offset += FATRawDirEntry::DIR_ENTRY_LEN; // 由于在FAT文件系统中,文件名最长为255字节,因此,最多有20个长目录项以及1个短目录项。 // 由于上面已经塞了1个长目录项,因此接下来最多需要迭代20次 // 循环查找目录项,直到遇到1个短目录项,或者是空闲目录项 for _ in 0..20 { // 如果当前簇已经被读完,那么尝试获取下一个簇 if self.offset >= self.fs.bytes_per_cluster() && !self.is_root { match self.fs.get_fat_entry(self.current_cluster)? { FATEntry::Next(c) => { // 获得下一个簇的信息 self.current_cluster = c; self.offset %= self.fs.bytes_per_cluster(); } _ => { // 没有下一个簇了,退出迭代 break; } } } // 如果当前是FAT12/FAT16文件系统,并且当前inode是根目录项。 // 如果offset大于根目录项的最大大小(已经遍历完根目录),那么就退出迭代 if self.is_root && self.offset > self.fs.root_dir_end_bytes_offset().unwrap() { break; } // 获取簇在磁盘内的字节偏移量 let offset: u64 = self.fs.cluster_bytes_offset(self.current_cluster) + self.offset; // 从磁盘读取原始的dentry let raw_dentry: FATRawDirEntry = get_raw_dir_entry(&self.fs, offset)?; match raw_dentry { FATRawDirEntry::Short(_) => { // 当前遇到1个短目录项,证明当前文件/文件夹的所有dentry都被读取完了,因此在将其加入数组后,退出迭代。 long_name_entries.push(raw_dentry); break; } FATRawDirEntry::Long(_) => { // 当前遇到1个长目录项,将其加入数组,然后更新offset,继续迭代。 long_name_entries.push(raw_dentry); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; } _ => { // 遇到了空闲簇,但没遇到短目录项,说明文件系统出错了,退出。 break; } } } // kdebug!("collect dentries done. long_name_entries={long_name_entries:?}"); let dir_entry: Result = FATDirEntry::new( long_name_entries, ( (start_cluster, start_offset), (self.current_cluster, self.offset), ), ); // kdebug!("dir_entry={:?}", dir_entry); match dir_entry { Ok(d) => { // kdebug!("dir_entry ok"); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; return Ok((self.current_cluster, self.offset, Some(d))); } Err(_) => { // kdebug!("dir_entry err, e={}", e); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; } } } FATRawDirEntry::Free => { // 当前目录项是空的 self.offset += FATRawDirEntry::DIR_ENTRY_LEN; } FATRawDirEntry::FreeRest => { // 当前目录项是空的,且之后都是空的,因此直接返回 return Ok((self.current_cluster, self.offset, None)); } } } } } /// 为DirIter实现迭代器trait impl Iterator for FATDirIter { type Item = FATDirEntry; fn next(&mut self) -> Option { match self.get_dir_entry() { Ok((cluster, offset, result)) => { self.current_cluster = cluster; self.offset = offset; return result; } Err(_) => { return None; } } } } impl FATDirEntry { /// @brief 构建FATDirEntry枚举类型 /// /// @param long_name_entries 长目录项的数组。 /// 格式:[第20个(或者是最大ord的那个), 19, 18, ..., 1, 短目录项] /// /// @return Ok(FATDirEntry) 构建好的FATDirEntry类型的对象 /// @return Err(i32) 错误码 pub fn new( mut long_name_entries: Vec, loc: ((Cluster, u64), (Cluster, u64)), ) -> Result { if long_name_entries.is_empty() { return Err(-(EINVAL as i32)); } if !long_name_entries[0].is_last() || !long_name_entries.last().unwrap().is_short() { // 存在孤立的目录项,文件系统出现异常,因此返回错误,表明其只读。 // TODO: 标记整个FAT文件系统为只读的 return Err(-(EROFS as i32)); } // 取出短目录项(位于vec的末尾) let short_dentry: ShortDirEntry = match long_name_entries.pop().unwrap() { FATRawDirEntry::Short(s) => s, _ => unreachable!(), }; let mut extractor = LongNameExtractor::new(); for entry in &long_name_entries { match entry { &FATRawDirEntry::Long(l) => { extractor.process(l)?; } _ => { return Err(-(EROFS as i32)); } } } // 检验校验和是否正确 if extractor.validate_checksum(&short_dentry) { // 校验和正确,返回一个长目录项 return Ok(short_dentry.to_dir_entry_with_long_name(extractor.to_string(), loc)); } else { // 校验和不相同,认为文件系统出错 return Err(-(EROFS as i32)); } } /// @brief 获取短目录项的名字 pub fn short_name(&self) -> String { match self { FATDirEntry::File(f) | FATDirEntry::VolId(f) => { return f.short_dir_entry.name_to_string(); } FATDirEntry::Dir(d) => match d.short_dir_entry { Some(s) => { return s.name_to_string(); } None => { return String::from("/"); } }, FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 获取短目录项结构体 pub fn short_dir_entry(&self) -> Option { match &self { FATDirEntry::File(f) => { return Some(f.short_dir_entry); } FATDirEntry::Dir(d) => { return d.short_dir_entry; } FATDirEntry::VolId(s) => { return Some(s.short_dir_entry); } FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 获取目录项的第一个簇的簇号 pub fn first_cluster(&self) -> Cluster { match self { FATDirEntry::File(f) => { return f.first_cluster; } FATDirEntry::Dir(d) => { return d.first_cluster; } FATDirEntry::VolId(s) => { return s.first_cluster; } FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 获取当前目录项所占用的簇的范围 /// /// @return (起始簇,簇内偏移量), (终止簇,簇内偏移量) pub fn get_dir_range(&self) -> Option<((Cluster, u64), (Cluster, u64))> { match self { FATDirEntry::File(f) => Some(f.loc), FATDirEntry::Dir(d) => d.loc, FATDirEntry::VolId(s) => Some(s.loc), FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 获取原始的短目录项名(FAT标准规定的) pub fn short_name_raw(&self) -> [u8; 11] { match self { FATDirEntry::File(f) => { return f.short_dir_entry.name; } FATDirEntry::Dir(d) => match d.short_dir_entry { // 存在短目录项,直接返回 Some(s) => { return s.name; } // 是根目录项 None => { let mut s = [0x20u8; 11]; s[0] = '/' as u8; return s; } }, FATDirEntry::VolId(s) => { return s.short_dir_entry.name; } FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 获取目录项的名字 pub fn name(&self) -> String { match self { FATDirEntry::File(f) => { return f.file_name.clone(); } FATDirEntry::VolId(s) => { return s.file_name.clone(); } FATDirEntry::Dir(d) => { return d.dir_name.clone(); } FATDirEntry::UnInit => unreachable!("FATFS: FATDirEntry uninitialized."), } } /// @brief 判断目录项是否为文件 pub fn is_file(&self) -> bool { match self { &FATDirEntry::File(_) | &FATDirEntry::VolId(_) => true, _ => false, } } /// @brief 判断目录项是否为文件夹 pub fn is_dir(&self) -> bool { match &self { &FATDirEntry::Dir(_) => true, _ => false, } } /// @brief 判断目录项是否为Volume id pub fn is_vol_id(&self) -> bool { match self { &FATDirEntry::VolId(_) => true, _ => false, } } /// @brief 判断FAT目录项的名字与给定的是否相等 /// /// 由于FAT32对大小写不敏感,因此将字符都转为大写,然后比较 /// /// @return bool 相等 => true /// 不相等 => false pub fn eq_name(&self, name: &str) -> bool { // 由于FAT32对大小写不敏感,因此将字符都转为大写,然后比较。 let binding = self.short_name(); let short_name = binding.chars().flat_map(|c| c.to_uppercase()); let binding = self.name(); let long_name = binding.chars().flat_map(|c| c.to_uppercase()); let name = name.chars().flat_map(|c| c.to_uppercase()); let long_name_matches: bool = long_name.eq(name.clone()); let short_name_matches: bool = short_name.eq(name); return long_name_matches || short_name_matches; } /// @brief 将FATDirEntry转换为FATFile对象 pub fn to_file(&self) -> Result { if self.is_file() == false { return Err(-(EISDIR as i32)); } match &self { FATDirEntry::File(f) | FATDirEntry::VolId(f) => { return Ok(f.clone()); } _ => unreachable!(), } } /// @brief 将FATDirEntry转换为FATDir对象 pub fn to_dir(&self) -> Result { if self.is_dir() == false { return Err(-(ENOTDIR as i32)); } match &self { FATDirEntry::Dir(d) => { return Ok(d.clone()); } _ => unreachable!(), } } } /// 用于生成短目录项文件名的生成器。 #[derive(Debug, Default)] pub struct ShortNameGenerator { /// 短目录项的名字 name: [u8; 11], /// 生成器的标志位(使用impl里面的mask来解析) flags: u8, /// 基础名的长度 basename_len: u8, /// 对于文件名形如(TE021F~1.TXT)的,短前缀+校验码的短目录项,该字段表示基础名末尾数字的对应位。 checksum_bitmask: u16, /// Fletcher-16 Checksum(与填写到ShortDirEntry里面的不一样) checksum: u16, /// 对于形如(TEXTFI~1.TXT)的短目录项名称,其中的数字的bitmask(第0位置位则表示这个数字是0) suffix_bitmask: u16, } impl ShortNameGenerator { /// 短目录项的名称的长度 const SHORT_NAME_LEN: usize = 8; // ===== flags标志位的含义 ===== const IS_LOSSY: u8 = (1 << 0); const IS_EXACT_MATCH: u8 = (1 << 1); const IS_DOT: u8 = (1 << 2); const IS_DOTDOT: u8 = (1 << 3); /// 名称被完全拷贝 const NAME_FITS: u8 = (1 << 4); /// @brief 初始化一个短目录项名称生成器 pub fn new(mut name: &str) -> Self { name = name.trim(); let mut short_name: [u8; 11] = [0x20u8; 11]; if name == "." { short_name[0] = '.' as u8; } if name == ".." { short_name[0] = '.' as u8; short_name[1] = '.' as u8; } // @name_fits: 名称是否被完全拷贝 // @basename_len: 基础名的长度 // @is_lossy: 是否存在不合法的字符 let (name_fits, basename_len, is_lossy) = match name.rfind('.') { Some(index) => { // 文件名里面有".", 且index为最右边的点号所在的下标(bytes index) // 拷贝基础名 let (b_len, fits, b_lossy) = Self::copy_part(&mut short_name[..Self::SHORT_NAME_LEN], &name[..index]); // 拷贝扩展名 let (_, ext_fits, ext_lossy) = Self::copy_part( &mut short_name[Self::SHORT_NAME_LEN..Self::SHORT_NAME_LEN + 3], &name[index + 1..], ); (fits && ext_fits, b_len, b_lossy || ext_lossy) } None => { // 文件名中,不存在"." let (b_len, fits, b_lossy) = Self::copy_part(&mut short_name[..Self::SHORT_NAME_LEN], &name); (fits, b_len, b_lossy) } }; let mut flags: u8 = 0; // 设置flags if is_lossy { flags |= Self::IS_LOSSY; } if name == "." { flags |= Self::IS_DOT; } if name == ".." { flags |= Self::IS_DOTDOT; } if name_fits { flags |= Self::NAME_FITS; } return ShortNameGenerator { name: short_name, flags: flags, basename_len: basename_len, checksum: Self::fletcher_16_checksum(name), ..Default::default() }; } /// @brief 拷贝字符串到一个u8数组 /// /// @return (u8, bool, bool) /// return.0: 拷贝了的字符串的长度 /// return.1: 是否完全拷贝完整个字符串 /// return.2: 拷贝过程中,是否出现了不合法字符 fn copy_part(dest: &mut [u8], src: &str) -> (u8, bool, bool) { let mut dest_len: usize = 0; let mut lossy_conv = false; for c in src.chars() { // 如果src还有字符,而dest已经满了,那么表示没有完全拷贝完。 if dest_len == dest.len() { return (dest_len as u8, false, lossy_conv); } if c == ' ' || c == '.' { lossy_conv = true; continue; } let cp: char = match c { 'a'..='z' | 'A'..='Z' | '0'..='9' => c, '$' | '%' | '\'' | '-' | '_' | '@' | '~' | '`' | '!' | '(' | ')' | '{' | '}' | '^' | '#' | '&' => c, _ => '_', }; // 判断是否存在不符合条件的字符 lossy_conv = lossy_conv || c != cp; // 拷贝字符 dest[dest_len] = c.to_ascii_uppercase() as u8; dest_len += 1; } // 返回结果 return (dest_len as u8, true, lossy_conv); } fn fletcher_16_checksum(name: &str) -> u16 { let mut sum1: u16 = 0; let mut sum2: u16 = 0; for c in name.chars() { sum1 = (sum1 + (c as u16)) % 0xff; sum2 = (sum1 + sum2) & 0xff; } return (sum2 << 8) | sum1; } /// @brief 更新生成器的状态 /// 当长目录项不存在的时候,需要调用这个函数来更新生成器的状态 pub fn add_name(&mut self, name: &[u8; 11]) { // === 判断名称是否严格的完全匹配 if name == &self.name { self.flags |= Self::IS_EXACT_MATCH; } // === 检查是否存在长前缀的格式冲突。对于这样的短目录项名称:(TEXTFI~1.TXT) // 获取名称前缀 let prefix_len = min(self.basename_len, 6) as usize; // 获取后缀的那个数字 let num_suffix: Option = if name[prefix_len] as char == '~' { (name[prefix_len + 1] as char).to_digit(10) } else { None }; // 判断扩展名是否匹配 let ext_matches: bool = name[8..] == self.name[8..]; if name[..prefix_len] == self.name[..prefix_len] // 基础名前缀相同 && num_suffix.is_some() // 基础名具有数字后缀 && ext_matches // 扩展名相匹配 { let num = num_suffix.unwrap(); self.suffix_bitmask |= 1 << num; } // === 检查是否存在短前缀+校验和的冲突,文件名形如:(TE021F~1.TXT) let prefix_len = min(self.basename_len, 2) as usize; let num_suffix: Option = if name[prefix_len + 4] as char == '~' { (name[prefix_len + 1] as char).to_digit(10) } else { None }; if name[..prefix_len] == self.name[..prefix_len] && num_suffix.is_some() && ext_matches { // 获取短文件名中的校验码字段 let checksum_result: Result< Result, core::str::Utf8Error, > = core::str::from_utf8(&name[prefix_len..prefix_len + 4]) .map(|s| u16::from_str_radix(s, 16)); // 如果校验码相同 if checksum_result == Ok(Ok(self.checksum)) { let num = num_suffix.unwrap(); // 置位checksum_bitmask中,基础名末尾数字的对应位 self.checksum_bitmask |= 1 << num; } } } pub fn generate(&self) -> Result<[u8; 11], i32> { if self.is_dot() || self.is_dotdot() { return Ok(self.name); } // 如果当前名字不存在不合法的字符,且名称被完整拷贝,但是exact match为false,可以认为名称没有冲突,直接返回 if !self.is_lossy() && self.name_fits() && !self.is_exact_match() { return Ok(self.name); } // 尝试使用长前缀(6字符) for i in 1..5 { if self.suffix_bitmask & (1 << i) == 0 { return Ok(self.build_prefixed_name(i as u32, false)); } } // 尝试使用短前缀+校验码 for i in 1..10 { if self.checksum_bitmask & (1 << i) == 0 { return Ok(self.build_prefixed_name(i as u32, true)); } } // 由于产生太多的冲突,因此返回错误(“短文件名已经存在”) return Err(-(EEXIST as i32)); } pub fn next_iteration(&mut self) { // 在下一次迭代中,尝试一个不同的校验和 self.checksum = (core::num::Wrapping(self.checksum) + core::num::Wrapping(1)).0; // 清空bitmask self.suffix_bitmask = 0; self.checksum_bitmask = 0; } /// @brief 构造具有前缀的短目录项名称 /// /// @param num 这是第几个重名的前缀名 /// @param with_checksum 前缀名中是否包含校验码 /// /// @return 构造好的短目录项名称数组 fn build_prefixed_name(&self, num: u32, with_checksum: bool) -> [u8; 11] { let mut buf: [u8; 11] = [0x20u8; 11]; let prefix_len: usize = if with_checksum { let prefix_len: usize = min(self.basename_len as usize, 2); buf[..prefix_len].copy_from_slice(&self.name[..prefix_len]); buf[prefix_len..prefix_len + 4].copy_from_slice(&Self::u16_to_u8_array(self.checksum)); prefix_len + 4 } else { let prefix_len = min(self.basename_len as usize, 6); buf[..prefix_len].copy_from_slice(&self.name[..prefix_len]); prefix_len }; buf[prefix_len] = '~' as u8; buf[prefix_len + 1] = char::from_digit(num, 10).unwrap() as u8; buf[8..].copy_from_slice(&self.name[8..]); return buf; } /// @brief 将一个u16数字转换为十六进制大写字符串对应的ascii数组。 /// 举例:将x=12345转换为16进制字符串“3039”对应的ascii码数组:[51,48,51,57] fn u16_to_u8_array(x: u16) -> [u8; 4] { let c1 = char::from_digit((x as u32 >> 12) & 0xf, 16) .unwrap() .to_ascii_uppercase() as u8; let c2 = char::from_digit((x as u32 >> 8) & 0xf, 16) .unwrap() .to_ascii_uppercase() as u8; let c3 = char::from_digit((x as u32 >> 4) & 0xf, 16) .unwrap() .to_ascii_uppercase() as u8; let c4 = char::from_digit((x as u32 >> 0) & 0xf, 16) .unwrap() .to_ascii_uppercase() as u8; return [c1, c2, c3, c4]; } #[inline] fn is_lossy(&self) -> bool { return (self.flags & Self::IS_LOSSY) > 0; } #[inline] fn is_exact_match(&self) -> bool { return (self.flags & Self::IS_EXACT_MATCH) > 0; } #[inline] fn is_dot(&self) -> bool { return (self.flags & Self::IS_DOT) > 0; } #[inline] fn is_dotdot(&self) -> bool { return (self.flags & Self::IS_DOTDOT) > 0; } #[inline] fn name_fits(&self) -> bool { return (self.flags & Self::NAME_FITS) > 0; } } /// 从多个LongName中提取完整文件名字段的提取器 struct LongNameExtractor { name: Vec, checksum: u8, index: u8, } impl LongNameExtractor { fn new() -> Self { return LongNameExtractor { name: Vec::new(), checksum: 0, index: 0, }; } /// @brief 提取长目录项的名称 /// @param longname_dentry 长目录项 /// 请注意,必须倒序输入长目录项对象 fn process(&mut self, longname_dentry: LongDirEntry) -> Result<(), i32> { let is_last: bool = longname_dentry.is_last(); let index: u8 = longname_dentry.ord & 0x1f; if index == 0 { self.name.clear(); return Err(-(EROFS as i32)); } // 如果是最后一个LongDirEntry,则初始化当前生成器 if is_last { self.index = index; self.checksum = longname_dentry.checksum; self.name .resize(index as usize * LongDirEntry::LONG_NAME_STR_LEN, 0); } else if self.index == 0 || index != self.index - 1 || self.checksum != longname_dentry.checksum { // 如果当前index为0,或者index不连续,或者是校验和不同,那么认为文件系统损坏,清除生成器的名称字段 // TODO: 对文件系统的变为只读状态状况的拦截 self.name.clear(); return Err(-(EROFS as i32)); } else { // 由于dentry倒序输入,因此index是每次减1的 self.index -= 1; } let pos: usize = ((index - 1) as usize) * LongDirEntry::LONG_NAME_STR_LEN; // 将当前目录项的值,拷贝到生成器的数组中 longname_dentry .copy_name_to_slice(&mut self.name[pos..pos + LongDirEntry::LONG_NAME_STR_LEN])?; return Ok(()); } /// @brief 返回名称的长度 #[inline] fn len(&self) -> usize { return self.name.len(); } /// @brief 返回抽取得到的名称字符串 fn to_string(&self) -> String { let mut s = String::from_utf16_lossy(self.name.as_slice()); // 计算字符串的长度。如果字符串中有\0,那么就截取字符串的前面部分 if let Some(len) = s.find('\u{0}') { s.truncate(len); } return s; } /// @brief 判断校验码是否与指定的短目录项的校验码相同 /// /// @return bool 相同 => true /// 不同 => false fn validate_checksum(&self, short_dentry: &ShortDirEntry) -> bool { return self.checksum == short_dentry.checksum(); } } /// @brief 长目录项生成器 #[derive(Debug)] struct LongNameEntryGenerator { name: Vec, // 短目录项的校验和 checksum: u8, // 当前迭代器的索引 idx: u8, /// 最后一个目录项的索引 last_index: u8, } impl LongNameEntryGenerator { /// @brief 初始化长目录项生成器 /// /// @param name 长文件名数组 /// @param checksum 短目录项的校验和 pub fn new(name: &str, checksum: u8) -> Self { let mut name: Vec = name.chars().map(|c| c as u16).collect(); let padding_bytes: usize = (13 - (name.len() % 13)) % 13; // 填充最后一个长目录项的文件名 for i in 0..padding_bytes { if i == 0 { name.push(0); } else { name.push(0xffff); } } // 先从最后一个长目录项开始生成 let start_index = (name.len() / 13) as u8; return LongNameEntryGenerator { name: name, checksum: checksum, idx: start_index, last_index: start_index, }; } /// @brief 返回要生成的长目录项的总数 pub fn num_entries(&self) -> u8 { return self.last_index + 1; } } impl Iterator for LongNameEntryGenerator { type Item = LongDirEntry; fn next(&mut self) -> Option { match self.idx { 0 => { return None; } // 最后一个长目录项 n if n == self.last_index => { // 最后一个长目录项的ord需要与0x40相或 let ord: u8 = n | 0x40; let start_idx = ((n - 1) * 13) as usize; self.idx -= 1; return Some(LongDirEntry::new( ord, &self.name.as_slice()[start_idx..start_idx + 13], self.checksum, )); } n => { // 其它的长目录项 let start_idx = ((n - 1) * 13) as usize; self.idx -= 1; return Some(LongDirEntry::new( n, &self.name.as_slice()[start_idx..start_idx + 13], self.checksum, )); } } } } #[derive(Debug)] pub enum FATDirEntryOrShortName { DirEntry(FATDirEntry), ShortName([u8; 11]), } /// @brief 对FAT目录项的迭代器(基于簇和簇内偏移量) #[derive(Debug)] struct FATDirEntryOffsetIter { /// 当前迭代的偏移量(下一次迭代要返回的值) current_offset: (Cluster, u64), /// 截止迭代的位置(end_offset所在的位置也会被迭代器返回) end_offset: Option<(Cluster, u64)>, /// 属于的文件系统 fs: Arc, /// 当前已经迭代了多少次 index: u64, /// 总共要迭代多少次 len: u64, /// 如果end_offset不为None,该字段表示“是否已经到达了迭代终点” fin: bool, } impl FATDirEntryOffsetIter { /// @brief 初始化FAT目录项的迭代器(基于簇和簇内偏移量) /// /// @param fs 属于的文件系统 /// @param start 起始偏移量 /// @param len 要迭代的次数 /// @param end_offset 截止迭代的位置(end_offset所在的位置也会被迭代器返回) /// /// @return 构建好的迭代器对象 pub fn new( fs: Arc, start: (Cluster, u64), len: u64, end_offset: Option<(Cluster, u64)>, ) -> Self { return FATDirEntryOffsetIter { current_offset: start, end_offset, fs, index: 0, len, fin: false, }; } } impl Iterator for FATDirEntryOffsetIter { type Item = (Cluster, u64); fn next(&mut self) -> Option { if self.index == self.len || self.fin { return None; } let r: (Cluster, u64) = self.current_offset; // 计算新的字节偏移量 let mut new_offset = r.1 + FATRawDirEntry::DIR_ENTRY_LEN; let mut new_cluster: Cluster = r.0; // 越过了当前簇,则获取下一个簇 if new_offset >= self.fs.bytes_per_cluster() { new_offset %= self.fs.bytes_per_cluster(); match self.fs.get_fat_entry(new_cluster) { Ok(FATEntry::Next(c)) => { new_cluster = c; } // 没有下一个簇了 _ => { self.fin = true; } } } if let Some(off) = self.end_offset { // 判断当前簇是否是要求停止搜索的最后一个位置 self.fin = off == self.current_offset; } // 更新当前迭代的偏移量 self.current_offset = (new_cluster, new_offset); self.index += 1; return Some(r); } } /// @brief 根据磁盘内字节偏移量,读取磁盘,并生成一个FATRawDirEntry对象 pub fn get_raw_dir_entry( fs: &Arc, in_disk_bytes_offset: u64, ) -> Result { // 块内偏移量 let blk_offset: u64 = fs.get_in_block_offset(in_disk_bytes_offset); let lba = fs.get_lba_from_offset( fs.bytes_to_sector(fs.get_in_partition_bytes_offset(in_disk_bytes_offset)), ); // let step1 = fs.get_in_partition_bytes_offset(in_disk_bytes_offset); // let step2 = fs.bytes_to_sector(step1); // let lba = fs.get_lba_from_offset(step2); // kdebug!("step1={step1}, step2={step2}, lba={lba}"); let mut v: Vec = Vec::new(); v.resize(1 * LBA_SIZE, 0); fs.partition.disk().read_at(lba, 1, &mut v)?; let mut cursor: VecCursor = VecCursor::new(v); // 切换游标到对应位置 cursor.seek(SeekFrom::SeekSet(blk_offset as i64))?; let dir_0 = cursor.read_u8()?; match dir_0 { 0x00 => { return Ok(FATRawDirEntry::FreeRest); } 0xe5 => { return Ok(FATRawDirEntry::Free); } _ => { cursor.seek(SeekFrom::SeekCurrent(10))?; let file_attr: FileAttributes = FileAttributes::new(cursor.read_u8()?); // 指针回到目录项的开始处 cursor.seek(SeekFrom::SeekSet(blk_offset as i64))?; if file_attr.contains(FileAttributes::LONG_NAME) { // 当前目录项是一个长目录项 let mut long_dentry = LongDirEntry::default(); long_dentry.ord = cursor.read_u8()?; cursor.read_u16_into(&mut long_dentry.name1)?; long_dentry.file_attrs = FileAttributes::new(cursor.read_u8()?); long_dentry.dirent_type = cursor.read_u8()?; long_dentry.checksum = cursor.read_u8()?; cursor.read_u16_into(&mut long_dentry.name2)?; long_dentry.first_clus_low = cursor.read_u16()?; cursor.read_u16_into(&mut long_dentry.name3)?; return Ok(FATRawDirEntry::Long(long_dentry)); } else { // 当前目录项是一个短目录项 let mut short_dentry = ShortDirEntry::default(); cursor.read_exact(&mut short_dentry.name)?; short_dentry.attributes = FileAttributes::new(cursor.read_u8()?); short_dentry.nt_res = cursor.read_u8()?; short_dentry.crt_time_tenth = cursor.read_u8()?; short_dentry.crt_time = cursor.read_u16()?; short_dentry.crt_date = cursor.read_u16()?; short_dentry.lst_acc_date = cursor.read_u16()?; short_dentry.fst_clus_hi = cursor.read_u16()?; short_dentry.wrt_time = cursor.read_u16()?; short_dentry.wrt_date = cursor.read_u16()?; short_dentry.fst_clus_lo = cursor.read_u16()?; short_dentry.file_size = cursor.read_u32()?; return Ok(FATRawDirEntry::Short(short_dentry)); } } } }