Skip to content

Poor documentation of FileSystem / FATFileSystem #14150

Open
@idea--list

Description

@idea--list

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);
  }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions