BMFS

From OSDev Wiki
Jump to navigation Jump to search

BMFS

BMFS is a simple file system in active development that supports regular files and directories.

Disclaimer

I am a contributor of BMFS. There's plenty of other file system implementations that could be used in an OS project.

BMFS, while it has existed for some time, is considered a new project. Be wary of bugs.

A Little History

BMFS was a file system initially designed by Ian Seyler for the BareMetal-OS project. Since then, it has been put into a separate project and the development continued there.

In it's initial design, it only supported a root directory with a maximum of 64 files. The idea was to have a simple file system that supported just enough of a file system for a database program to use.

The development continued and the design changed a bit, and now it supports sub-directories, file permissions, user and group IDs, and more.

It also comes with a user space program to construct file systems and examine their contents, and a FUSE binding to use BMFS on Linux systems.

The project is now being constantly improved, and one of its design goals is to be used in any other operating system.

Building BMFS

While using the instructions from the project is the most reliable way to build the project, this should also help build the project.

Starting by cloning the project using git, and entering the directory.

   git clone https://github.com/ReturnInfinity/BMFS
   cd BMFS

Then use GNU Make to build the project, specifying that you are cross compiling using the CONFIG variable.

   make CONFIG=cross

The cross compiler here defaults to x86_64-none-elf-gcc. If you would like to use a different target, use the CROSS_COMPILE variable like this:

   make CONFIG=cross CROSS_COMPILE=aarch64-none-elf-

Once the project is built, you can install it in a location of your choosing by using the PREFIX variable, and the install target.

   make CONFIG=cross install PREFIX=/home/taylor/.local

Now, you have the library libbmfs.a installed at /home/taylor/.local/lib and the headers installed at /home/taylor/include

That means you'll have to pass those paths to GCC, by doing something like this:

   gcc -I /home/taylor/.local/include -L /home/taylor/.local/lib

You can also install it to /usr/local, to avoid having to do that.

Initializing the File System

Once the project is built, and your build system has the information to use the library, you can start writing code.

The header bmfs/bmfs.h is there for convenience to include all the headers used in the project.

Start by including that header in your code.

   #include <bmfs/bmfs.h>
   
   void open_file(const char *path) {
       /* Do stuff here */
   }

Once that's done, you'll need to tell BMFS how to read the disk that contains the file system.

You can do that using the BMFSDisk structure.

   int my_disk_read(void *disk_data, void *buf, bmfs_uint64 len, bmfs_uint64 *read_len);
   
   int my_disk_seek(void *disk_data, bmfs_uint64_t pos, int whence);
   
   void open_file(const char *path) {
   
       struct BMFSDisk disk;
   
       bmfs_disk_init(&disk);
       disk.seek = my_disk_seek;
       disk.read = my_disk_read;
   }
   
   /* Implement my_disk_read and my_disk_seek */

Now that the disk is initialized, you can initialize the file system structure and read the file system contents.

   int open_file(const char *path) {
   
       /* Initialize disk here */
   
       struct BMFS bmfs;
   
       bmfs_init(&bmfs);
   
       bmfs_set_disk(&bmfs, &disk);
   
       int err = bmfs_import(&bmfs);
       if (err != 0) {
           kprintf("Failed to import BMFS file system.\n");
           return -1;
       }
   }

Opening a File

With the disk and the file system initialized, you can open the file.

   int show_file(const char *path) {
   
       /* Initialize disk here. */
   
       /* Initialize file system header. */
   
       struct BMFSFile file;
   
       bmfs_file_init(&file);
   
       err = bmfs_open_file(&bmfs, &file, path);
       if (err == BMFS_ENOENT) {
           kprintf("Entry '%s' does not exist.\n", path);
       } else if (err == BMFS_EISDIR) {
           kprintf("Entry '%s' is a directory.\n", path);
       } else if (err != 0) {
           kprintf("Failed to open '%s'.\n", path);
           return -1;
       }
   
       bmfs_file_set_mode(&file, BMFS_MODE_READ);
   
       char buf[512];
   
       while (bmfs_file_eof(&file)) {
   
           bmfs_uint64 read_count = 0;
   
           err = bmfs_file_read(&file, buf, 512, &read_count);
           if (err != 0)
               break;
   
           my_print_function(buf, read_count);
       }
   }

Opening a Directory

Opening a directory is similar to opening a file.

You'll have to initialize the file system the same as you did for the file.

   int list_dir(const char *path) {
   
       /* Initialize the file system here. */
   
       struct BMFSDir dir;
   
       bmfs_dir_init(&dir);
   
       int err = bmfs_open_dir(&bmfs, &dir, path);
       if (err == BMFS_ENOTDIR) {
           kprintf("Entry '%s' is not a directory.\n", path);
           return -1;
       } else if (err == BMFS_ENOENT) {
           kprintf("Directory '%s' does not exist.\n", path);
           return -1;
       } else if (err != 0) {
           kprintf("Failed to open directory '%s'.\n", path);
           return -1;
       }
       
       for (;;) {
           const struct BMFSEntry *entry = bmfs_dir_next(&dir);
           if (entry == BMFS_NULL)
               break;
           
           kprintf("Entry: %s\n", entry->Name);
       }
   }

Creating a File System on the Development Machine

Sometimes, in creating an operating system, a file system has to be made in the development machine to transfer system files (such as programs or shared libraries).

For that reason, the BMFS utility program was designed. It allows a user to create a file system on a disk image and read and write to the file system.

To build the utility program, just use the standard 'make' and 'make install' commands in the project directory.

   git clone https://github.com/ReturnInfinity/BMFS
   cd BMFS
   make
   sudo make install

Create a disk image called 'bmfs.img' using the 'init' command.

   bmfs init

To use a different name, use the `--disk` option.

   bmfs --disk my-file-system.img init

Using `bmfs.img` is nice, because all the commands default to this name.

The rest of this tutorial assumes you have used the name 'bmfs.img'.

If this is not the case, just specify the name using the '--disk' option for each command.

To create a directory, us the 'mkdir' command.

   bmfs mkdir /drivers

To transfer a file from the host system to the image, us the 'cp' command.

   bmfs cp ahci-driver.sys /drivers/ahci.sys

To list the contents of a directory, us the 'ls' command.

   bmfs ls /drivers

Reporting Bugs

If you think there is a bug in the file system implementation, report it on the project issue tracker [1].

Going Further

The library can do even more than that and new features are added continuously.

Visit the Doxygen generated documentation [2] for more information.