Skip to content

Commit 860e072

Browse files
authored
Merge pull request #133 from rust-embedded-community/fix-seeking
Fix seeking
2 parents ee72ddc + 92c8ca9 commit 860e072

File tree

3 files changed

+101
-68
lines changed

3 files changed

+101
-68
lines changed

src/filesystem/files.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,10 @@ pub(crate) struct FileInfo {
211211
pub(crate) file_id: RawFile,
212212
/// The unique ID for the volume this directory is on
213213
pub(crate) volume_id: RawVolume,
214-
/// The current cluster, and how many bytes that short-cuts us
214+
/// The last cluster we accessed, and how many bytes that short-cuts us.
215+
///
216+
/// This saves us walking from the very start of the FAT chain when we move
217+
/// forward through a file.
215218
pub(crate) current_cluster: (u32, ClusterId),
216219
/// How far through the file we've read (in bytes).
217220
pub(crate) current_offset: u32,
@@ -236,41 +239,30 @@ impl FileInfo {
236239

237240
/// Seek to a new position in the file, relative to the start of the file.
238241
pub fn seek_from_start(&mut self, offset: u32) -> Result<(), FileError> {
239-
if offset <= self.entry.size {
240-
self.current_offset = offset;
241-
if offset < self.current_cluster.0 {
242-
// Back to start
243-
self.current_cluster = (0, self.entry.cluster);
244-
}
245-
Ok(())
246-
} else {
247-
Err(FileError::InvalidOffset)
242+
if offset > self.entry.size {
243+
return Err(FileError::InvalidOffset);
248244
}
245+
self.current_offset = offset;
246+
Ok(())
249247
}
250248

251249
/// Seek to a new position in the file, relative to the end of the file.
252250
pub fn seek_from_end(&mut self, offset: u32) -> Result<(), FileError> {
253-
if offset <= self.entry.size {
254-
self.current_offset = self.entry.size - offset;
255-
if offset < self.current_cluster.0 {
256-
// Back to start
257-
self.current_cluster = (0, self.entry.cluster);
258-
}
259-
Ok(())
260-
} else {
261-
Err(FileError::InvalidOffset)
251+
if offset > self.entry.size {
252+
return Err(FileError::InvalidOffset);
262253
}
254+
self.current_offset = self.entry.size - offset;
255+
Ok(())
263256
}
264257

265258
/// Seek to a new position in the file, relative to the current position.
266259
pub fn seek_from_current(&mut self, offset: i32) -> Result<(), FileError> {
267260
let new_offset = i64::from(self.current_offset) + i64::from(offset);
268-
if new_offset >= 0 && new_offset <= i64::from(self.entry.size) {
269-
self.current_offset = new_offset as u32;
270-
Ok(())
271-
} else {
272-
Err(FileError::InvalidOffset)
261+
if new_offset < 0 || new_offset > i64::from(self.entry.size) {
262+
return Err(FileError::InvalidOffset);
273263
}
264+
self.current_offset = new_offset as u32;
265+
Ok(())
274266
}
275267

276268
/// Amount of file left to read.
@@ -281,7 +273,6 @@ impl FileInfo {
281273
/// Update the file's length.
282274
pub(crate) fn update_length(&mut self, new: u32) {
283275
self.entry.size = new;
284-
self.entry.size = new;
285276
}
286277
}
287278

src/volume_mgr.rs

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ where
624624
let (block_idx, block_offset, block_avail) = self.find_data_on_disk(
625625
volume_idx,
626626
&mut current_cluster,
627+
self.open_files[file_idx].entry.cluster,
627628
self.open_files[file_idx].current_offset,
628629
)?;
629630
self.open_files[file_idx].current_cluster = current_cluster;
@@ -701,44 +702,45 @@ where
701702
written, bytes_to_write, current_cluster
702703
);
703704
let current_offset = self.open_files[file_idx].current_offset;
704-
let (block_idx, block_offset, block_avail) =
705-
match self.find_data_on_disk(volume_idx, &mut current_cluster, current_offset) {
706-
Ok(vars) => {
707-
debug!(
708-
"Found block_idx={:?}, block_offset={:?}, block_avail={}",
709-
vars.0, vars.1, vars.2
710-
);
711-
vars
712-
}
713-
Err(Error::EndOfFile) => {
714-
debug!("Extending file");
715-
match self.open_volumes[volume_idx].volume_type {
716-
VolumeType::Fat(ref mut fat) => {
717-
if fat
718-
.alloc_cluster(
719-
&self.block_device,
720-
Some(current_cluster.1),
721-
false,
722-
)
723-
.is_err()
724-
{
725-
return Err(Error::DiskFull);
726-
}
727-
debug!("Allocated new FAT cluster, finding offsets...");
728-
let new_offset = self
729-
.find_data_on_disk(
730-
volume_idx,
731-
&mut current_cluster,
732-
self.open_files[file_idx].current_offset,
733-
)
734-
.map_err(|_| Error::AllocationError)?;
735-
debug!("New offset {:?}", new_offset);
736-
new_offset
705+
let (block_idx, block_offset, block_avail) = match self.find_data_on_disk(
706+
volume_idx,
707+
&mut current_cluster,
708+
self.open_files[file_idx].entry.cluster,
709+
current_offset,
710+
) {
711+
Ok(vars) => {
712+
debug!(
713+
"Found block_idx={:?}, block_offset={:?}, block_avail={}",
714+
vars.0, vars.1, vars.2
715+
);
716+
vars
717+
}
718+
Err(Error::EndOfFile) => {
719+
debug!("Extending file");
720+
match self.open_volumes[volume_idx].volume_type {
721+
VolumeType::Fat(ref mut fat) => {
722+
if fat
723+
.alloc_cluster(&self.block_device, Some(current_cluster.1), false)
724+
.is_err()
725+
{
726+
return Err(Error::DiskFull);
737727
}
728+
debug!("Allocated new FAT cluster, finding offsets...");
729+
let new_offset = self
730+
.find_data_on_disk(
731+
volume_idx,
732+
&mut current_cluster,
733+
self.open_files[file_idx].entry.cluster,
734+
self.open_files[file_idx].current_offset,
735+
)
736+
.map_err(|_| Error::AllocationError)?;
737+
debug!("New offset {:?}", new_offset);
738+
new_offset
738739
}
739740
}
740-
Err(e) => return Err(e),
741-
};
741+
}
742+
Err(e) => return Err(e),
743+
};
742744
let mut blocks = [Block::new()];
743745
let to_copy = core::cmp::min(block_avail, bytes_to_write - written);
744746
if block_offset != 0 {
@@ -1043,18 +1045,33 @@ where
10431045

10441046
/// This function turns `desired_offset` into an appropriate block to be
10451047
/// read. It either calculates this based on the start of the file, or
1046-
/// from the last cluster we read - whichever is better.
1048+
/// from the given start point - whichever is better.
1049+
///
1050+
/// Returns:
1051+
///
1052+
/// * the index for the block on the disk that contains the data we want,
1053+
/// * the byte offset into that block for the data we want, and
1054+
/// * how many bytes remain in that block.
10471055
fn find_data_on_disk(
10481056
&self,
10491057
volume_idx: usize,
10501058
start: &mut (u32, ClusterId),
1059+
file_start: ClusterId,
10511060
desired_offset: u32,
10521061
) -> Result<(BlockIdx, usize, usize), Error<D::Error>> {
10531062
let bytes_per_cluster = match &self.open_volumes[volume_idx].volume_type {
10541063
VolumeType::Fat(fat) => fat.bytes_per_cluster(),
10551064
};
1065+
// do we need to be before our start point?
1066+
if desired_offset < start.0 {
1067+
// user wants to go backwards - start from the beginning of the file
1068+
// because the FAT is only a singly-linked list.
1069+
start.0 = 0;
1070+
start.1 = file_start;
1071+
}
10561072
// How many clusters forward do we need to go?
10571073
let offset_from_cluster = desired_offset - start.0;
1074+
// walk through the FAT chain
10581075
let num_clusters = offset_from_cluster / bytes_per_cluster;
10591076
let mut block_cache = BlockCache::empty();
10601077
for _ in 0..num_clusters {
@@ -1065,7 +1082,7 @@ where
10651082
};
10661083
start.0 += bytes_per_cluster;
10671084
}
1068-
// How many blocks in are we?
1085+
// How many blocks in are we now?
10691086
let offset_from_cluster = desired_offset - start.0;
10701087
assert!(offset_from_cluster < bytes_per_cluster);
10711088
let num_blocks = BlockCount(offset_from_cluster / Block::LEN_U32);

tests/read_file.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,27 +150,31 @@ fn read_file_backwards() {
150150

151151
const CHUNK_SIZE: u32 = 100;
152152
let length = volume_mgr.file_length(test_file).expect("file length");
153-
let mut offset = length - CHUNK_SIZE;
154153
let mut read = 0;
155154

155+
// go to end
156+
volume_mgr.file_seek_from_end(test_file, 0).expect("seek");
157+
156158
// We're going to read the file backwards in chunks of 100 bytes. This
157159
// checks we didn't make any assumptions about only going forwards.
158160
while read < length {
161+
// go to start of next chunk
159162
volume_mgr
160-
.file_seek_from_start(test_file, offset)
163+
.file_seek_from_current(test_file, -(CHUNK_SIZE as i32))
161164
.expect("seek");
165+
// read chunk
162166
let mut buffer = [0u8; CHUNK_SIZE as usize];
163167
let len = volume_mgr.read(test_file, &mut buffer).expect("read");
164168
assert_eq!(len, CHUNK_SIZE as usize);
165169
contents.push_front(buffer.to_vec());
166170
read += CHUNK_SIZE;
167-
if offset >= CHUNK_SIZE {
168-
offset -= CHUNK_SIZE;
169-
}
171+
// go to start of chunk we just read
172+
volume_mgr
173+
.file_seek_from_current(test_file, -(CHUNK_SIZE as i32))
174+
.expect("seek");
170175
}
171176

172177
assert_eq!(read, length);
173-
assert_eq!(offset, 0);
174178

175179
let flat: Vec<u8> = contents.iter().flatten().copied().collect();
176180

@@ -180,6 +184,27 @@ fn read_file_backwards() {
180184
assert_eq!(&hash[..], TEST_DAT_SHA256_SUM);
181185
}
182186

187+
#[test]
188+
fn read_file_with_odd_seek() {
189+
let time_source = utils::make_time_source();
190+
let disk = utils::make_block_device(utils::DISK_SOURCE).unwrap();
191+
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(disk, time_source);
192+
193+
let mut volume = volume_mgr
194+
.open_volume(embedded_sdmmc::VolumeIdx(0))
195+
.unwrap();
196+
let mut root_dir = volume.open_root_dir().unwrap();
197+
let mut f = root_dir
198+
.open_file_in_dir("64MB.DAT", embedded_sdmmc::Mode::ReadOnly)
199+
.unwrap();
200+
f.seek_from_start(0x2c).unwrap();
201+
while f.offset() < 1000000 {
202+
let mut buffer = [0u8; 2048];
203+
f.read(&mut buffer).unwrap();
204+
f.seek_from_current(-1024).unwrap();
205+
}
206+
}
207+
183208
// ****************************************************************************
184209
//
185210
// End Of File

0 commit comments

Comments
 (0)