[ / main / writing / polyfs ]

    The polyOne File System



(c)1997 Jeff Weeks and Code X software

Sample source code:
pformat.zip




    Introduction




Please note that these are the current specifications of the Poly One file system. They are, however, subject to change. In time I will post the final revision, until then, these are the only records.




The Poly One file system is not the simplest file system available, but that doesn't mean it's complicated. What complications that do exist are for the purpose of making the file system fully extendible and easy to use.




Typical file systems treat directories as files. I go one step further and treat files as directories. Besides being infinitely recursive :) this system allows file extensions (not the DOS type) and extra possibilities in the future.




Using the file system is simple. As in UNIX, directories are separated by the forward slash (/). Files, however, are accessed a bit differently. Files can actually have associated files (attributes, so-to-speak) stored right with them. For example, to access a file's icon, you might use the following syntax:

    /directory/file(icon)



The exact syntax is still being decided upon. The following are the top contenders:

    file.attrib
    file(attrib)
    file:attrib
    file,attrib



To specify a device (for example, a floppy or tape drive) with which to read the path from, simply specify the device name before the path:

    fd0:/directory/file



Typically though, drives will be (automatically) mounted directly into the file system. For instance, when you insert a floppy disk into the drive, PolyOS will automatically notice this and mount the floppy drive's files in a directory such as /floppy. Either way of accessing a file (mounted or not) should be available at all times




    Low Level Details




    Blocks and Inodes




First off, I think I should describe the block and inode system I use in the Poly One file system. It is very much like the Second Extended file system used in Linux. The smallest allocatable unit of the Poly One file system is the block. The block size can be changed through the superblock, but is typically 1024, 2048 or 4096 bytes long.




The inode is a small structure which describes a file, and it's location. Each file has an inode. Unlike the Second Extended file system, however, the Poly One file system will not have a limited number of inodes. By default, at format time, space will be reserved for some inodes. If that space is filled, another chunk of space will be reserved for more inodes. Not only does this save space on the device, but it optomizes file access as well. Inodes will be interleaved every so often between files which means to update a file the device will probably not have to seek as far to find the inode.




All inodes together are collectively called the inode table. The inode table itself is described by one inode, whose location is described in the superblock. The format of the 128 byte inode is as follows.

    dw      type
    dw      subtype
    dw      permissions
    dw      links_count
    dd      size
    dd      access_time
    dd      creation_time
    dd      modification_time
    dd      user_id
    dd      group_id
    dd      flags
    dd      reserved1
    dd      reserved2
    dd      reserved3
    dd      start_block_1
    dd      length_1
    dd      start_block_2
    dd      length_2
    dd      start_block_3
    dd      length_3
    dd      start_block_4
    dd      length_4
    dd      start_block_5
    dd      length_5
    dd      start_block_6
    dd      length_6
    dd      start_block_7
    dd      length_7
    dd      start_block_8
    dd      length_8
    dd      start_block_9
    dd      length_9
    dd      start_block_10
    dd      length_10



This system allows for advanced file typing (magic numbers). There is a major type (type) and a minor file type (subtype). The type field contains a number which represents a general file type, while the subtype field contains a number representing a more specific version of the general file type. Actual file typing codes are, as of yet, undetermined.




The permissions field contains the file permissions of the file. There are four permissions in the Poly One file system; Reading, writting, executing, and deleting. Many file systems do not distinguish between deleting and writting but I feel there should be such a distinction. Perhaps you want a person to be able to write to the file, but not delete it. Ofcourse, in such a situation a user might erase everything and save a 0 byte file. To prevent this from happening, when the write bit is set, but the delete bit is not set, the user should only be allowed to append to the file.




The links_count field contains the number of links that exist to this file. Links (or aliases) are created by using the file type < link > and by putting the inode number of the destination file in the start_block_1 field. This will make the link act as though it is the destination file. As long as files do not change inode numbers as they are modified (moved, renamed, etc) then the links will continue to work properly.




Size, unsurprisingly, contains the size of the file. It allows for files up to approximately 4.2949 billion bytes. I don't think this will be a problem for quite some time. Atleast not in the home PC market.




Next are three fields which contain time and date information about the file. These do not follow the typical C convention of the seconds that have passed since 00:00:00 GMT, January 1, 1970. Instead, the first word contains the number of days passed since 00:00:00 GMT, January 1, 1990. The next word contains the number of seconds into that day.




Next comes the user_id and group_id. These describe the owner of the file. Poly One supports up to approximately 4.2949 billion users and that same number of user groups.




The flags field contains extra information about how to handle the file. The exact purposes of each bit is undertermined at the moment. However, I have a proliminary list setup.

    Bit 0   Secure Delete
    With set, the file's blocks will be zerod when the file is deleted.  This
    completely erases the files contents, instead of just it's inode.
    Bit 1   Immutable file
    When set the file cannot be changed in any way.



Next are ten start/length fields. These bring up an interesting feature of the Poly One file system; It is run length encoded. For efficiency reasons ten RLE data chunks are included right in the inode. If the file happens to span over ten RLE chunks then the last RLE chunk will actually reference a block which will contain more RLE chunks of the file.




    Directories




As said before, directories will be treated as files. These files will be the same as any other except that they are of type < directory > and contain information about the files in the directory. The directory will consist of a bunch of file description chunks, each one looking like the following.

    dd      file_inode
    dd      attrib_inode
    dw      record_length
    db      name_length
    db      character_encoding
    db 256  filename

This structure is fairly simple. The first field contains the inode which describes the actual file. The next field contains the inode which describes the attributes directory. The attributes directory is just like any other directory, except that it's purpose is to hold extra files related to the root file (this is how we get the syntax, file(attrib)).




The next field contains the length of the entire record. The name_length field in not sufficient enough because records will be dword aligned for efficiency reasons. The name_length field contains the length of the file name. This allows for variable length file names up to 256 characters long, and no wasted space.




The character_encoding field is there to select which UniCode language to use to display the file. This allows for an internationalized file system. Then, after that is a variable length (up to 256 characters long) field which contains the actual filename.




    The SuperBlock




Now let's look at the Poly One superblock. It is located at a fixed offset from the begining of the phsical disk (1024 bytes), and is also 1024 bytes long. It's structure is as follows:

    dd      magic
    dd      os_inode
    dd      root_inode
    dd      inode_table
    dd      inode_bitmap
    dd      block_bitmap
    db      block_size
    db      state
    db      error_behaviour
    db      pad
    dw      os_version
    dw      fs_version
    dd      creator_os
    dd      num_inodes
    dd      free_inodes
    dd      num_blocks
    dd      free_blocks
    dd      first_data_block
    dw      max_mnt_count
    dw      mnt_count



First things first, the magic field contains a four byte identification. For the Poly One file system, this field will contain 'PFS1' The next item is somewhat new as far as I know. Most file systems require the kernel to be located at location 0 of the disk, and to span adjacent sectors. My file system does not place this restriction on the kernel. It can be located anywhere. The kernel, therefore, is described by the inode referenced in the os_inode field. The root_inode, similarly to the os_inode field, contains the inode which references the root directory.




Next comes the inode table. For what should be obvious reasons this field does not contain an inode number, but rather a block offset to an inode. The inode table is a group of blocks which actually contain all the inodes. Inodes are discussed above.




Next are two bitmap inodes. Each of these point to a bitmap which contains information about blocks or inodes. If a certain bit in the bitmap is set, then the corresponding block or inode is allocated, else it is unallocated.




The block_size, obviously, contains the size of block for this file system. It, however, isn't the actual byte count. Instead, a value of 0 represents a 1024 byte block size. 1 represents 2048, 2 represents 4096 3 represents 8192, and so on. To calculate the actual block size, use the following equation.

    block_size = 1024 << superblock.block_size;



The state field contains information about the file system. At the moment, only the first bit is used. It represents wether the file system is currently mounted, or not. This also allows the OS to check if the file system was shut down correctly. If the OS mounts the file system and the mounted bit is set, then the file system must not have been shut down correctly, therefore a check should be forced.




The error_behaviour field tells the OS what behaviour it should take if an error in the file system is detected. The specifications are currently not present at this time. After this, you might be intrigued to find a byte which does absolutely nothing. Or does it? That byte actually dword aligns entries in the superblock. This will optomize the file system for 32-bit processors.




After that are two version fields. The first byte contains the major version number and the second byte contains the minor version number, therefore, each version field is a word length. A value of 258, for example, would result in a version number of 1.02 because the first byte is 1, while the second is 2.




The creator_os field symbolizes what OS created this file system. A value of 0 represents PolyOS. As you can see, there is room for over 4 billion OSs!




The num_inodes field, simply enough, contains the number of inodes in this file system. free_inodes, contains the number of unallocated inodes. The same descriptions will also suffice for the num_blocks and free_blocks fields. Just replace the word inode, with block.




The first_data_block field tells the OS where the actual information begins. It is there for future enhancements.




The variables max_mnt_count and mnt_count hold the number of times the file system has been mounted, and the maximum number of times it can be mounted before a check is forced.




    Implementation




PolyOS will implement a certain standardized directory structure with the Poly One file system as well. It is similar to the UNIX directory structure with some differences. It is as follows.

    /users/user_name
    /binaries
    /data/binary_name
    /libraries
    /include
    /system
    /source
    /devices



The users directory holds all the different user's home directories. Programs are stored in different standardized directories. The program's binaries (executables) are stored in the /binaries directory. The data files for that program are stored in a subdirectory of /data under the same name as the executable. This system allows for an interesting feature. By making a link to an executable, and running the executable from this link the program may run differently, because it will be accessing the data files in the /data/link_name directory rather than the regular /data/executable_name directory. In other words, polymorphic behaviour can be exhibited just by running an executable through a link!




The /libraries directory holds all the shared libraries available on the system. Creating new shared libraries is not endorsed in PolyOS because it just creates another dependancy for the executable. If a library exists that will do what you need, use it. If you must create a new library, be sure to make it a generalized library so that other applications may use it as well.




The /include directory contains all the header files available on the system. Creating new header files is also not endorsed in PolyOS for the same reason creating new libraries is not




The /system directory contains information about how the system is currently configured. All global options will be located in this directory.




The /source directory is the reccomended directory to keep all source code currently available on the system. All source packages should exist as sub directories off of the /source directory.




The /devices directory contains pseudo files representing all the devices on the system. As in UNIX, device I/O will be taken out through files.