Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement FAT table write/read and setting the volume dirty status #132

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from

Conversation

Murmele
Copy link

@Murmele Murmele commented May 30, 2024

The dirty flag will be set when opening the volume to indicate that the FAT table might be out of date. In case of an unexpected close without unmount in the next mount it is visible that the FAT table is outdated and can be reread by the operating system (https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system). Without this, the file must always closed before removing the sd card because otherwise the FAT table is outdated and so the files are not visible when mounting the sd card on a computer. For embedded systems it is quite likely that unexpect end can happen and always closing the file after a single write operation is a performance issue. For readonly this dirty flag is not required and therefore it will not set and reset when opening/closing a volume. This is same as the linux kernel is doing.

https://unix.stackexchange.com/questions/230181/why-does-linux-mark-fat-as-dirty-simply-due-to-mounting-it Linux

kernel is doing it during mount, so there is no need to ckeck it every time during a new write

@thejpster
Copy link
Member

It appears the tests are failing - possible because the API changed?

pub fn open_raw_volume(
&mut self,
volume_idx: VolumeIdx,
read_only: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer an enum VolumeOpenMode { ReadOnly, ReadWrite }, otherwise the caller writes:

thing.open_raw_volume(VolumeIdx(0), true)

and it's totally unclear what true means from the context.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 2fd18cb

///
/// We do not support GUID Partition Table disks. Nor do we support any
/// concept of drive letters - that is for a higher layer to handle.
fn _open_volume(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added an argument to open_raw_volume, so should we just add an argument to open_volume, and not have the two extra methods? Not sure.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it like this, so it is downward compatible

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then should we keep open_raw_volume backwards compatible too?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for? I though because it will be used mostly internally so the impact is lower?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a public API, which I use in my projects.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change also to the same structure as for open_volume or do you prefer the argument way for both?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I prefer it with the argument.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect then I will change it also for the open_volume

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 70a5fdb

}
Ok(())
}
#[cfg(test)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests go in mod test {}.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 9d6865a

let volume_idx = self.get_volume_by_id(volume)?;
if !self.open_volumes[volume_idx].read_only {
let VolumeType::Fat(volume_type) = &self.open_volumes[volume_idx].volume_type;
Copy link
Member

@thejpster thejpster Jun 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to write this code on the assumption that a non-FAT filesystem will be added in the future. The irrefutable match here will break when that happens. I think set_volume_status_dirty doesn't need volume_type as an argument - it should take volume_idx instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this

/// Sets the volume status dirty to dirty if true, to not dirty if false
fn set_volume_status_dirty(
&self,
volume: &FatVolume,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should take volume_idx and handle the different types of volume internally. It should also skip updating the FAT if the FAT is already marked as dirty.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 56a07fe

if !read_only {
let idx = self.get_volume_by_id(v)?;
let VolumeType::Fat(volume_type) = &self.open_volumes[idx].volume_type;
self.set_volume_status_dirty(volume_type, true)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to mark it as dirty until the first write occurs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I read the comment thread from the kernel. Yeah, I can see the value in marking it dirty on first mount.

Note to self - the Neotron OS will need changing to ensure the volume is unmounted when the last file is closed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this flag does not fix my problem haha, I have to update somehow the fat, at least Linux does not check the fat and repairs it if needed. I thought when the bit is cleared (is dirty) during mount it will be "repaired" and the FAT table gets update, but this is not the case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Linux will run an fsck on a block device unless you ask it to. It is Windows that prompts to run Scandisk if it things the volume wasn't cleanly shut down.

Copy link
Member

@thejpster thejpster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea - left a few notes.

@Murmele
Copy link
Author

Murmele commented Jun 1, 2024

It appears the tests are failing - possible because the API changed?

I will have a look into it next days

@Murmele Murmele marked this pull request as ready for review June 3, 2024 20:27
@Murmele Murmele force-pushed the volume_status_dirty branch 4 times, most recently from c155268 to c1c57d3 Compare June 5, 2024 18:06
@thejpster
Copy link
Member

So we already have functions for updating the FAT: FatVolume::update_fat. Could we just create some new ClusterId variants which represent clean/dirty volumes and use the update_fat function to write them to ClusterIdx 1?

@Murmele
Copy link
Author

Murmele commented Jun 6, 2024

So we already have functions for updating the FAT: FatVolume::update_fat. Could we just create some new ClusterId variants which represent clean/dirty volumes and use the update_fat function to write them to ClusterIdx 1?

My idea was now with this flag, after a restart of my device I check this flag and if dirty I wanna update somehow the dir entries (I think this is currently my problem I have, if I don't update the dirEntry (maybe the size property?) it will not shown in the file explorer correctly). I have to check if I can get there somehow (Maybe iterating through the complete FAT and setting the new file size?).

Sorry right now I am not that deep into the topic to understand your idea. I will think about and doing some research.

But reparing the FAT could be done in another MR

In my case the sd card never leaves the device but at the end over usb storage access shall be established, so I can easily update the FAT at that point

Murmele added 14 commits July 7, 2024 09:06
…me dirty status

Description: The dirty flag will be set when opening the volume to indicate that the FAT table might be out of date. In case of an unexpected close without unmount in the next mount it is visible that the FAT table is outdated and can be reread by the operating system (https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system). Without this, the file must always closed before removing the sd card because otherwise the FAT table is outdated and so the files are not visible when mounting the sd card on a computer. For embedded systems it is quite likely that unexpect end can happen and always closing the file after a single write operation is a performance issue. For readonly this dirty flag is not required and therefore it will not set and reset when opening/closing a volume.

https://unix.stackexchange.com/questions/230181/why-does-linux-mark-fat-as-dirty-simply-due-to-mounting-it
Linux kernel is doing it during mount, so there is no need to ckeck it every time during a new write
Reason: makes the funktionality more clear and less error prone
so it can be also used outside of tests
…eady set

Reason: the function can determine it self.
@Murmele
Copy link
Author

Murmele commented Jul 7, 2024

I fixed the tests

@thejpster
Copy link
Member

So we already have functions for updating the FAT: FatVolume::update_fat. Could we just create some new ClusterId variants which represent clean/dirty volumes and use the update_fat function to write them to ClusterIdx 1?

So I like the read-only and read-write changes. If they were isolated I'd happily merge them.

But to return to my earlier comment and perhaps clarify - I don't think we need a whole new module for writing a couple of FAT entries to indicate dirty/clean, when we already have functions which write FAT entries for the purposes of tracking cluster allocations for files. Either all the FAT writing code needs to go in to the new module, or the clean/dirty bits need to be set with the existing function.

@Murmele
Copy link
Author

Murmele commented Sep 9, 2024

So we already have functions for updating the FAT: FatVolume::update_fat. Could we just create some new ClusterId variants which represent clean/dirty volumes and use the update_fat function to write them to ClusterIdx 1?

So I like the read-only and read-write changes. If they were isolated I'd happily merge them.

But to return to my earlier comment and perhaps clarify - I don't think we need a whole new module for writing a couple of FAT entries to indicate dirty/clean, when we already have functions which write FAT entries for the purposes of tracking cluster allocations for files. Either all the FAT writing code needs to go in to the new module, or the clean/dirty bits need to be set with the existing function.

I will have a look into it. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants