BenLunt wrote:
- Super block (your "header" structure) that is backed up some where. For example, if the primary header (super block) is at LBA 1024, a backup (secondary) header is at: LBA = ((block count / 2) + 1)
This makes sense. I wasn't sure I would still need it if I added journaling, but I've been thinking about. This is what GPT does as well. I would also add a checksum to detect for corruption.
BenLunt wrote:
[*]Bitmap(s) of allocated blocks.
I was actually thinking of using the same algorithm I used for memory allocation. A table of allocation entries (containing offset and size), with one of the entries pointing to the table itself. The table is then able to reallocate itself somewhere else when it needs to grow. It would be a LOT easier to implementation for disk space allocation because disk space is linear and memory is in chunks. The bitmap approach is nice because it is simple and very space efficient. The table method, the way I plan on using it, is also efficient but I'm not sure how they compare. Bitmaps, the way I understand them, lose their efficiency as the block size decreases.
I'll have to think about this a bit more.
BenLunt wrote:
[*]unlimited file name lengths, but only using what space is needed. i.e.: No wasted space. (i.e.: currently if the file name is "a.img", 0xC0 - strlen("a.img") is a lot of wasted space).
For example, a file system I was/is a part of is the
LeanFS, and it uses a count of 16-byte blocks for filenames, wasting at most 15 bytes of space per filename, yet allowing a file name to be quite long (four digits if I remember correctly).
Another file system I use, and created myself for numerous reasons, is the
FYSFS, and allows unlimited file name sizes, though can waste around 80 bytes of space.
I agree, the name field has to be a little bit more efficient. While I don't think it's an issue in practice, I think it definitely needs to be fixed. Sixteen byte blocks would work well. I might do that. I could also try to approach that ELF files took and store file names in a string table, using an index to reference the name. I'm not sure, though, if it would be worth the complexity. Sixteen byte blocks sound many times easier to implement.
BenLunt wrote:
(Side note: FYSFS was my first file system project, other than the (in)famous FAT file system. I wanted to create something that I could use to test my kernel with to make sure it was completely FS independent, as well as the enjoyment of creating a file system. It served its purpose and now I use the LeanFS file system as my default choice.)
LeanFS sounds pretty neat! Does it have a URL?
BenLunt wrote:
[*]Streams: The LeanFS uses Extents to indicate how many blocks are allocated for a specific part of the file's data.
Code: Select all
struct STREAM {
bit64u start_lba; // starting LBA relative to some point: start of volume, start of band, start of ????
bit32u count; // count of consecutive blocks used for this stream
};
Currently BMFS uses byte indices instead of block indices, relative to the beginning of the disk. It does not store the block count, just the current size of the data.
The block count is stored in the allocation table. This difference just reflects the difference in bitmap and table approach.
BenLunt wrote:
[*]Bands: The LeanFS uses bands, or sections of the media to store all data needed for that part of the file system. For example, the Inode, Metadata, file name, *and* allocation bitmap for any specific file is within a section of the media. This way, if for some reason a portion of the media is destroyed, any remaining bands (sections) are still intact and can be extracted 100%.
This makes sense. It would be helpful, in this case, to know the disk geometry to ensure that the contents are separated physically. I'll have to add more callback functions to the disk geometry. It would be helpful to have a separate band for backup structures as well, I would imagine. I'll add it to the to-do list.
BenLunt wrote:
[*]Symbolic links: Allows for numerous "Directory entries" to point to the same file. This has a huge advantage in common used code bases.
For example, let's say that you have a standard library (source) file that is linked to multiple projects. Rather than having to use the same, some times long path for each project, you can simply create a Symbolic Link directory entry right in the project's main path and use it as a link to the actual library (source) file.
There are draw backs to this though. What if you move the original file and your file system doesn't have the capabilities to update all links?
I have been planning on supporting them for a few months now. The way I was planning on doing it was by having a path translation table. Moving a file or symbolic link would involve updating the paths in the translation table. But I'm still not quite sure. I would like to learn more about the corner cases like that, so I can make sure that the behavior is a mostly consistent with other file systems.
BenLunt wrote:
[*]Meta Data: You have already implemented some of this with your directory entry, but what about non-file system specific meta data items.
For example, the LeanFS allows data to be stored in a file's Inode or a separate Inode linked to this Inode to store non-file system specific meta data, which allows the OS to store information about the app, user, network, etc. currently using the file.[/list]
I agree. In another file system implementation I did, files and directories had completely different meta data (except for the name, of course). The directory data contained (among a few other things) the number of subdirectories and files in that directory. Subdirectories came first and then files second. This allowed for the two entries to be completely different, without causing too much of an implementation difficulty. I might take this approach again.
BenLunt wrote:
As for a Journal and encryption, these are good things to have for a file system, but be careful, both of these items will make a simple file system, instantly not so simple anymore.
At this point, I'm focused on the simplicity in the API. You can opt out of encryption and journaling by having a field in the superblock that says so. At the very least, having callbacks for encryption is a must. It keeps the implementation simple while still allowing for encryption to be trivially added.
BenLunt wrote:
There are a few other things I want to list:
- Variable boot sector space: The FAT file system allows a boot sector code block to use as much disk space as needed (though not all implementations account for this). The Linux Ext 2/3/4 file systems do not. Two blocks. Period. Ouch. Give a variable spaced boot sector allowing a single block or <exaggeration> half the drive if needed</exaggeration>.
- Store the last state of the volume in the super block (header). For example, was the volume previously successfully dismounted? If not, don't try to mount it without first trying to fix it.
- Use magic numbers. i.e.: Give specific, static, values to certain structures of the file system. For example, if the primary super block is ever lost, you can search the disk somewhere in the range of ((block size / 2) - 16) -> ((block size / 2) + 16) for a block that contains certain "magic" numbers to see if there is a valid back up super block. This also allows your driver to verify that you actually loaded a valid Inode before modifying the data it may or may not point to.
Extra boot sector space can be added in BMFS by offsetting all the read and write calls by
. I don't want to add extra space in the layout specification, and I might even take away the 512-byte offset, because it can be set by the caller.
Thanks for all the feedback! You've given me a lot to check off before the next release. I'm looking forward to your feedback when I can make the next BMFS post.