fatfs: Added erase disk to format

Right now, many users are trying out many different filesystems.
Unfortunately, this can leave partially written filesystems on disk
in various states.

A very common pattern for using embedded filesystems is to attempt
a mount, and on failure, format the storage with the filesystem.

Unfortunately, this simply doesn't work if you try to change the
filesystem being used on a piece of storage. Filesystems don't always
use the same regions of storage, and can leave enough metadata lying
around from old filesystems to trick a different mount into thinking a
valid filesystem exists on disk. The filesystems we have were never
designed to check for malicious modification and can't protect against
arbitrary changes.

That being said, it's caused enough problems for users, so as a
workaround this patch adds a disk erase to the FAT filesystem format.
The most common error happens when you use LittleFS, followed by FAT,
followed again by LittleFS.

No other combination of filesystem usage has shown a similar failure,
but it is possible after extensive filesystem use, so it is still
suggested to force a format of the storage when changing filesystems.
pull/6866/head
Christopher Haster 2018-05-03 03:05:38 -05:00
parent 16ab71864e
commit e77d57e0bc
1 changed files with 62 additions and 3 deletions

View File

@ -333,24 +333,83 @@ int FATFileSystem::unmount()
int FATFileSystem::format(BlockDevice *bd, bd_size_t cluster_size)
{
FATFileSystem fs;
int err = fs.mount(bd, false);
fs.lock();
int err = bd->init();
if (err) {
fs.unlock();
return err;
}
// erase first handful of blocks
bd_size_t header = 2*bd->get_erase_size();
err = bd->erase(0, header);
if (err) {
bd->deinit();
fs.unlock();
return err;
}
if (bd->get_erase_value() < 0) {
// erase is unknown, need to write 1s
bd_size_t program_size = bd->get_program_size();
void *buf = malloc(program_size);
if (!buf) {
bd->deinit();
fs.unlock();
return -ENOMEM;
}
memset(buf, 0xff, program_size);
for (bd_addr_t i = 0; i < header; i += program_size) {
err = bd->program(buf, i, program_size);
if (err) {
free(buf);
bd->deinit();
fs.unlock();
return err;
}
}
free(buf);
}
// trim entire device to indicate it is unneeded
err = bd->trim(0, bd->size());
if (err) {
bd->deinit();
fs.unlock();
return err;
}
err = bd->deinit();
if (err) {
fs.unlock();
return err;
}
err = fs.mount(bd, false);
if (err) {
fs.unlock();
return err;
}
// Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster)
fs.lock();
FRESULT res = f_mkfs(fs._fsid, FM_ANY | FM_SFD, cluster_size, NULL, 0);
fs.unlock();
if (res != FR_OK) {
fs.unmount();
fs.unlock();
return fat_error_remap(res);
}
err = fs.unmount();
if (err) {
fs.unlock();
return err;
}
fs.unlock();
return 0;
}