Skip to content

Commit

Permalink
Added support for file deletion and the related test
Browse files Browse the repository at this point in the history
  • Loading branch information
glbsalazar committed Oct 10, 2020
1 parent bbf6da4 commit 866fb80
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
disk.img
163 changes: 163 additions & 0 deletions examples/delete_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//! # Tests the Embedded SDMMC Library
//! ```bash
//! $ cargo run --example delete_test -- /dev/mmcblk0
//! $ cargo run --example delete_test -- /dev/sda
//! ```
//!
//! If you pass a block device it should be unmounted. No testing has been
//! performed with Windows raw block devices - please report back if you try
//! this! There is a gzipped example disk image which you can gunzip and test
//! with if you don't have a suitable block device.
//!
//! ```bash
//! zcat ./disk.img.gz > ./disk.img
//! $ cargo run --example delete_test -- ./disk.img
//! ```
extern crate embedded_sdmmc;

const FILE_TO_DELETE: &'static str = "DELETE.TXT";

use embedded_sdmmc::{
Block, BlockCount, BlockDevice, BlockIdx, Controller, Error, Mode, TimeSource, Timestamp,
VolumeIdx,
};
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::Path;

#[derive(Debug)]
struct LinuxBlockDevice {
file: RefCell<File>,
print_blocks: bool,
}

impl LinuxBlockDevice {
fn new<P>(device_name: P, print_blocks: bool) -> Result<LinuxBlockDevice, std::io::Error>
where
P: AsRef<Path>,
{
Ok(LinuxBlockDevice {
file: RefCell::new(
OpenOptions::new()
.read(true)
.write(true)
.open(device_name)?,
),
print_blocks,
})
}
}

impl BlockDevice for LinuxBlockDevice {
type Error = std::io::Error;

fn read(
&self,
blocks: &mut [Block],
start_block_idx: BlockIdx,
reason: &str,
) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
for block in blocks.iter_mut() {
self.file.borrow_mut().read_exact(&mut block.contents)?;
if self.print_blocks {
println!(
"Read block ({}) {:?}: {:?}",
reason, start_block_idx, &block
);
}
}
Ok(())
}

fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
self.file
.borrow_mut()
.seek(SeekFrom::Start(start_block_idx.into_bytes()))?;
for block in blocks.iter() {
self.file.borrow_mut().write_all(&block.contents)?;
if self.print_blocks {
println!("Wrote: {:?}", &block);
}
}
Ok(())
}

fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
let num_blocks = self.file.borrow().metadata().unwrap().len() / 512;
Ok(BlockCount(num_blocks as u32))
}
}

struct Clock;

impl TimeSource for Clock {
fn get_timestamp(&self) -> Timestamp {
Timestamp {
year_since_1970: 0,
zero_indexed_month: 0,
zero_indexed_day: 0,
hours: 0,
minutes: 0,
seconds: 0,
}
}
}

fn main() {
env_logger::init();
let mut args = std::env::args().skip(1);
let filename = args.next().unwrap_or_else(|| "/dev/mmcblk0".into());
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
let lbd = LinuxBlockDevice::new(filename, print_blocks)
.map_err(Error::DeviceError)
.unwrap();
println!("lbd: {:?}", lbd);
let mut controller = Controller::new(lbd, Clock);
for volume_idx in 0..=3 {
let volume = controller.get_volume(VolumeIdx(volume_idx));
println!("volume {}: {:#?}", volume_idx, volume);
if let Ok(mut volume) = volume {
let root_dir = controller.open_root_dir(&volume).unwrap();
println!("\tListing root directory:");
controller
.iterate_dir(&volume, &root_dir, |x| {
println!("\t\tFound: {:?}", x);
})
.unwrap();
println!("\nCreating file {}...", FILE_TO_DELETE);
// This will panic if the file already exists, use ReadWriteCreateOrAppend or
// ReadWriteCreateOrTruncate instead
let mut f = controller
.open_file_in_dir(
&mut volume,
&root_dir,
FILE_TO_DELETE,
Mode::ReadWriteCreate,
)
.unwrap();

println!("\tFinding {}...", FILE_TO_DELETE);
println!(
"\tFound {}?: {:?}",
FILE_TO_DELETE,
controller.find_directory_entry(&volume, &root_dir, FILE_TO_DELETE)
);
controller.close_file(&volume, f).unwrap();

controller.delete_file_in_dir(&volume, &root_dir, FILE_TO_DELETE).unwrap();

println!("\tFinding {}...", FILE_TO_DELETE);
println!(
"\tFound {}?: {:?}",
FILE_TO_DELETE,
controller.find_directory_entry(&volume, &root_dir, FILE_TO_DELETE)
);
}
}
}
111 changes: 111 additions & 0 deletions src/fat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,117 @@ impl FatVolume {
}
}

/// Delete an entry from the given directory
pub(crate) fn delete_directory_entry<D, T>(
&self,
controller: &mut Controller<D, T>,
dir: &Directory,
name: &str,
) -> Result<(), Error<D::Error>>
where
D: BlockDevice,
T: TimeSource,
{
let match_name = ShortFileName::create_from_str(name).map_err(Error::FilenameError)?;
let mut blocks = [Block::new()];
match &self.fat_specific_info {
FatSpecificInfo::Fat16(fat16_info) => {
let mut current_cluster = Some(dir.cluster);
let mut first_dir_block_num = match dir.cluster {
Cluster::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
_ => self.cluster_to_block(dir.cluster),
};
let dir_size = match dir.cluster {
Cluster::ROOT_DIR => BlockCount(
((u32::from(fat16_info.root_entries_count) * 32) + (Block::LEN as u32 - 1))
/ Block::LEN as u32,
),
_ => BlockCount(u32::from(self.blocks_per_cluster)),
};

while let Some(cluster) = current_cluster {
for block in first_dir_block_num.range(dir_size) {
controller
.block_device
.read(&mut blocks, block, "read_dir")
.map_err(Error::DeviceError)?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
if dir_entry.is_end() {
// Can quit early
return Err(Error::FileNotFound);
} else if dir_entry.matches(&match_name) {
// let mut blockWrite = &blocks[0][start..end];
// blockWrite[0] = 0xE5;
let mut blocks = [Block::new()];
blocks[0].contents[0] = 0xE5;
controller
.block_device
.write(&blocks, block)
.map_err(Error::DeviceError)?;
return Ok(());
}
}
}
if cluster != Cluster::ROOT_DIR {
current_cluster = match self.next_cluster(controller, cluster) {
Ok(n) => {
first_dir_block_num = self.cluster_to_block(n);
Some(n)
}
_ => None,
};
} else {
current_cluster = None;
}
}
Err(Error::FileNotFound)
}
FatSpecificInfo::Fat32(fat32_info) => {
let match_name =
ShortFileName::create_from_str(name).map_err(Error::FilenameError)?;
let mut current_cluster = match dir.cluster {
Cluster::ROOT_DIR => Some(fat32_info.first_root_dir_cluster),
_ => Some(dir.cluster),
};
let mut blocks = [Block::new()];
while let Some(cluster) = current_cluster {
let block_idx = self.cluster_to_block(cluster);
for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
controller
.block_device
.read(&mut blocks, block, "read_dir")
.map_err(Error::DeviceError)?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
if dir_entry.is_end() {
// Can quit early
return Err(Error::FileNotFound);
} else if dir_entry.matches(&match_name) {
let mut blocks = blocks;
blocks[0].contents[start] = 0xE5;
controller
.block_device
.write(&blocks, block)
.map_err(Error::DeviceError)?;
return Ok(());
}
}
}
current_cluster = match self.next_cluster(controller, cluster) {
Ok(n) => Some(n),
_ => None,
}
}
Err(Error::FileNotFound)
}
}
}

/// Finds the next free cluster after the start_cluster and before end_cluster
pub(crate) fn find_next_free_cluster<D, T>(
&self,
Expand Down
40 changes: 40 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ where
DirAlreadyOpen,
/// You can't open a directory as a file
OpenedDirAsFile,
/// You can't delete a directory as a file
DeleteDirAsFile,
/// You can't delete an open file
FileIsOpen,
/// We can't do that yet
Unsupported,
/// Tried to read beyond end of file
Expand Down Expand Up @@ -585,6 +589,42 @@ where
Ok(file)
}

/// Delete a closed file with the given full path, if exists.
pub fn delete_file_in_dir(
&mut self,
volume: &Volume,
dir: &Directory,
name: &str,
) -> Result<(), Error<D::Error>> {
debug!(
"delete_file(volume={:?}, dir={:?}, filename={:?}",
volume, dir, name
);
let dir_entry = match &volume.volume_type {
VolumeType::Fat(fat) => fat.find_directory_entry(self, dir, name),
};

let dir_entry = match dir_entry {
Ok(entry) => entry,
_ => return Err(Error::FileNotFound),
};

if dir_entry.attributes.is_directory() {
return Err(Error::DeleteDirAsFile);
}

let target = (volume.idx, dir_entry.cluster);
for d in self.open_files.iter_mut() {
if *d == target {
return Err(Error::FileIsOpen);
}
}

match &volume.volume_type {
VolumeType::Fat(fat) => return fat.delete_directory_entry(self, dir, name),
};
}

/// Read from an open file.
pub fn read(
&mut self,
Expand Down

0 comments on commit 866fb80

Please sign in to comment.