THE
POLY
ONE
FILE
SYSTEM
(c)1997 Jeff Weeks and Code X software
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.