Description
Description of defect
The documentation of FileSystem / FATFileSystem is rather poor. It does not tell any word about the blockdevice being initialized before actually mounting the filesystem. In the application code you never see blockdevice->init();
just filesystem.mount(blockdevice);
Even if you dig deeper to find FATFileSystem::mount, you simply will not see any obvious signs of initializing the blockdevice.
As a consequence after closing the file and unmounting the blockdevice you think you are done and it is safe to remove the SD card and you can also reinsert it as your application code begins with filesystem.mount(blockdevice);
That might be why no example code contains blockdevice->deinit(); after unmounting.
Then you write your code and get confused why everything just breaks if you dare to reinsert an SD card after you carefully closed the file and unmounted the blockdevice... this affects a lot of people.
Took me one complete day googling with lots of results but only 1 thread gave some hint.
Target(s) affected by this defect ?
Probably all
Toolchain(s) (name and version) displaying this defect ?
ARMC6.14
What version of Mbed-os are you using (tag or sha) ?
Mbed OS 6.6
What version(s) of tools are you using. List all that apply (E.g. mbed-cli)
Mbed Studio V1.3.1
How is this defect reproduced ?
Compile and run this code, then uncomment blockdevice->deinit();
at the end of unmountFS()
, then recompile, rerun and note the difference: (10 seconds interval should be enough to remove/reinsert the card)
#include "mbed.h"
#include <cstdio>
#include <errno.h>
#include <functional>
#include <stdio.h>
#include "SDBlockDevice.h"
// Maximum number of elements in buffer
#define BUFFER_MAX_LEN 10
#define FORCE_REFORMAT false
// This will take the system's default block device
SDBlockDevice *blockdevice =
new SDBlockDevice(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO,
MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
// Instead of the default block device, you can define your own block device.
// For example: HeapBlockDevice with size of 2048 bytes, read size 1, write size
// 1 and erase size 512. #include "HeapBlockDevice.h" BlockDevice *blockdevice = new
// HeapBlockDevice(2048, 1, 1, 512);
#include "FATFileSystem.h"
FATFileSystem filesystem("fs");
// Set up the button to trigger an erase
InterruptIn irq(BUTTON1);
DigitalOut rLED(LED_RED, LED_OFF);
int err = 0;
bool toggle = 0;
FILE *f;
void flipToggle() {
toggle = !toggle;
rLED = !rLED;
}
void erase() {
printf("Initializing the block device... ");
fflush(stdout);
int err = blockdevice->init();
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
printf("Erasing the block device... ");
fflush(stdout);
err = blockdevice->erase(0, blockdevice->size());
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
printf("Deinitializing the block device... ");
fflush(stdout);
err = blockdevice->deinit();
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
}
void mountFS() {
printf("Mounting the filesystem... ");
fflush(stdout);
err = filesystem.mount(blockdevice);
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err || FORCE_REFORMAT) {
// Reformat if we can't mount the filesystem
printf("formatting... ");
fflush(stdout);
err = filesystem.reformat(blockdevice);
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
}
}
void openFile() {
printf("Opening \"/fs/numbers.txt\"... ");
fflush(stdout);
f = fopen("/fs/numbers.txt", "r+");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
// Create the numbers file if it doesn't exist
printf("No file found, creating a new file... ");
fflush(stdout);
f = fopen("/fs/numbers.txt", "w+");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
for (int i = 0; i < 10; i++) {
printf("\rWriting numbers (%d/%d)... ", i, 10);
fflush(stdout);
err = fprintf(f, " %d\n", i);
if (err < 0) {
printf("Fail :(\n");
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
printf("\rWriting numbers (%d/%d)... OK\n", 10, 10);
printf("Seeking file... ");
fflush(stdout);
err = fseek(f, 0, SEEK_SET);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
}
void incrementNumbers() {
for (int i = 0; i < 10; i++) {
printf("\rIncrementing numbers (%d/%d)... ", i, 10);
fflush(stdout);
// Get current stream position
long pos = ftell(f);
// Parse out the number and increment
char buf[BUFFER_MAX_LEN];
if (!fgets(buf, BUFFER_MAX_LEN, f)) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
char *endptr;
int32_t number = strtol(buf, &endptr, 10);
if ((errno == ERANGE) || // The number is too small/large
(endptr == buf) || // No character was read
(*endptr && *endptr != '\n') // The whole input was not converted
) {
continue;
}
number += 1;
// Seek to beginning of number
fseek(f, pos, SEEK_SET);
// Store number
fprintf(f, " %d\n", number);
// Flush between write and read on same file
fflush(f);
}
printf("\rIncrementing numbers (%d/%d)... OK\n", 10, 10);
}
void closeFile() {
printf("Closing \"/fs/numbers.txt\"... ");
fflush(stdout);
err = fclose(f);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
void displayDir() {
printf("Opening the root directory... ");
fflush(stdout);
DIR *d = opendir("/fs/");
printf("%s\n", (!d ? "Fail :(" : "OK"));
if (!d) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
printf("root directory:\n");
while (true) {
struct dirent *e = readdir(d);
if (!e) {
break;
}
printf(" %s\n", e->d_name);
}
printf("Closing the root directory... ");
fflush(stdout);
err = closedir(d);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
void displayFContent() {
printf("Opening \"/fs/numbers.txt\"... ");
fflush(stdout);
f = fopen("/fs/numbers.txt", "r");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
printf("numbers:\n");
while (!feof(f)) {
int c = fgetc(f);
printf("%c", c);
}
printf("\rClosing \"/fs/numbers.txt\"... ");
fflush(stdout);
err = fclose(f);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
void unmountFS() {
printf("Unmounting... ");
fflush(stdout);
err = filesystem.unmount();
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(-err), err);
}
//blockdevice->deinit();
}
static auto erase_event = mbed_event_queue()->make_user_allocated_event(erase);
// Entry point for the example
int main() {
printf("--- Mbed OS filesystem example ---\n");
// Setup the erase event on button press, use the event queue
// to avoid running in interrupt context
irq.fall(std::ref(erase_event));
// Try to mount the filesystem
mountFS();
// Open the numbers file
openFile();
// Go through and increment the numbers
incrementNumbers();
// Close the file which also flushes any cached writes
closeFile();
// Display the root directory
displayDir();
// Display the numbers file
displayFContent();
// Tidy up
unmountFS();
printf("Mbed OS filesystem example done!\n");
ThisThread::sleep_for(10000ms);
while (1) {
mountFS();
openFile();
incrementNumbers();
closeFile();
displayDir();
displayFContent();
unmountFS();
ThisThread::sleep_for(10000ms);
}
}