---------------------
The Minix file system
---------------------

ELKS uses the Minix file system. There are three versions of the Minix file 
system. The first one, implemented in ELKS, features a maximum disk size of 
64 Mb and a maximum file size of 64 Mb. The other two versions have the same 
maximum file size but support disks up to 2 GB or 2 TB respectively. The 
maximum file name length is 14 bytes in the file system version ELKS 
implemented.

The structure of a Minix file system consists of six sections:

*Boot Block* first block, reserved for boot loader code.

*Super Block* second block, stores the Super Block with information about 
the Minix File System. This is e.g.the number of inodes and zones, the size 
of the inode and zone bitmaps and the starting block of the data area.

*Inode Map* section made up of bits, where one bit represents one inode. 
Tracks used and unused inodes. A "1" is used to represent a used block and 
a "0" is for a free block.

*Zone Map* section made up of bits to track used and unused zones in the data 
area. A "1" is used to represent a used zone and a "0" is for a free zone.

*Inode area* each file or directory is represented as an inode, which records 
metadata including type (file, directory, block, char, pipe), IDs for user 
and group, three timestamps that record the date and time of last access, last 
modification and last status change. An inode also contains a list of addresses 
that point to the zones in the data area where the file or directory data is 
actually stored.

*Data area* majority of volume which contains the files and directories data.

The directory and file entries containing the file names are in the data area. 
First, read the root inode (which is inode #1), then traverse its zones to 
find directory entries. Each directory entry contains the inode index of the 
directory, so you recursively proceed.


Now here is a tour of the Minix file system. It was included as a note by 
Al Woodhull within the documentation of the mintools package.

This is a guided tour of a Minix 1.6.30 empty 360K file system. This file 
system will initially have 127 i-nodes (of 128 total), and 348 blocks 
(of 360 total) available. The basic data was generated using xd, a hex 
dump program.

The bootblock occupies the first 1024 byte block, it is all zeros initially:

000000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
(snip)
0000003F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

The second block is the superblock. Only a small part of the block is needed 
to hold the necessary information. The definition is in /usr/src/fs/super.h.

EXTERN struct super_block {
  ino_t s_ninodes;              /* # usable inodes on the minor device */
  zone1_t  s_nzones;            /* total device size, including bit maps etc */
  short s_imap_blocks;          /* # of blocks used by inode bit map */
  short s_zmap_blocks;          /* # of blocks used by zone bit map */
  zone1_t s_firstdatazone;      /* number of first data zone */
  short s_log_zone_size;        /* log2 of blocks/zone */
  off_t s_max_size;             /* maximum file size on this device */
  short s_magic;                /* magic number to recognize super-blocks */
  short s_pad;                  /* try to avoid compiler-dependent padding */
  zone_t s_zones;               /* number of zones (replaces s_nzones in V2) */
... additional fields are added to the structure in memory.
} super_block[NR_SUPERS];

This is an example of a super block:

000000400  80 00 00 00 01 00 01 00  0c 00 00 00 00 1c 04 04 ................
000000410  68 24 00 00 68 01 00 00  00 00 00 00 00 00 00 00 h$..h...........
(snip)
0000007F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

Here is an explanation of the data in this hex dump:

400-401: 0x0080 = 128 inodes
402-403: 0x0000 ???? total size (zero for V2, but look here if f.s. is V1)
404-405: 0x0001 = 1 i-node map block
406-407: 0x0001 = 1 zone map block
408-409: 0x000c = 12 first data zone
40a-40b: 0      = 1 block/zone
40c-40f: 0x04041c00 max file size on this device (?)
410-411: 0x2468  magic number for V2 f.s. ; defined in /usr/src/fs/const.h
412-413: 0x0000  padding (why?)
414-417: 0x00000168 = 360 blocks total size (number used by V2)

The next block is the i-node bit map. Note that in this newly created file 
system the first byte shows two i-nodes already in use. One of the bits 
corresponds to the root directory i-node. The other bit is for i-node zero, 
which is always marked as used, since it doesn't exist -- an inode pointer 
of zero in a directory entry indicates an available directory entry.

Although this 360K file system was created with space for 128 nodes reserved, 
which would require 16 bytes in the bit map (actually 17 bytes, since the bit 
map maps 129 i-nodes including the non-existent, never-available i-node zero) 
the whole block is zeroed out. The file system can read the maximum extent of 
the i-node bitmap from the superblock, and so doesn't need to have excess bits 
in the block mapped out. See the discussion below of the the zone bit map.

000000800  03 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
000000810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
(snip)
000000BF0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

The next block holds the the zone bit map. Here again, two zones are shown as 
used. One corresponds to the block in which the root directory is located, and 
the other corresponds to zone zero, which, like i-node zero, doesn't exist and 
is thus marked as never available.

A zero-valued pointer in a list of zones will always be treated as a nil 
pointer. A zero-valued pointer does not necessarily indicate the end of a file 
has been found, however, Minix can have sparse files.

As with the i-node bit map, the total number of zones can be found from the 
superblock. In this example, a 360K file system requires 43 bytes for the zone 
bit map, but the additional bits in the zone bit map block are not mapped out. 
This makes it easy to copy from a small floppy disk to a larger ramdisk. For 
instance, this 360K file system could be copied directly to a ramdisk of up to 
8192K. After copying all that would be necessary would be to patch the 
superblock to reflect the expanded number of zones available.

Note that, although the i-node bit map could also be expanded to handle 8192 
i-nodes, in fact the space allocated to i-nodes in this 360K file system is 
only 8192 bytes, and thus the 128 i-nodes already provided for, each requiring 
64 bytes, already require the full space available.

000000C00  03 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
000000C10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
000000C20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
(snip)
000000FF0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

I-nodes: definition from /usr/src/fs/type.h:

typedef struct {                /* V2.x disk inode */
  mode_t d2_mode;               /* file type, protection, etc. */
  u16_t d2_nlinks;              /* how many links to this file. HACK! */
  uid_t d2_uid;                 /* user id of the file's owner. */
  u16_t d2_gid;                 /* group number HACK! */
  off_t d2_size;                /* current file size in bytes */
  time_t d2_atime;              /* when was file data last accessed */
  time_t d2_mtime;              /* when was file data last changed */
  time_t d2_ctime;              /* when was inode data last changed */
  zone_t d2_zone[V2_NR_TZONES]; /* block nums for direct, ind, and dbl ind */
} d2_inode;

Example: The i-nodes in Minix 1.6.30 each require 64 bytes, and thus a single 
1024 byte block can hold 16 i-nodes. In this example of a 360K file system the 
next 8 blocks are reserved for 128 inodes. The first one is already filled in, 
it is the i-node of the root directory. Note, however, that the last part of the 
i-node, the block list, is mostly filled with zeros, since only one block is in 
use by the root directory file.

1000-1001: 0x41ff mode drwxrwxrwx
1002-1003: 0x0002 number of links
1004-1005: 0x0002 owner id (daemon)
1006-1007: 0x0002 group id (daemon)
1008-100b: 0x00000020 file size
100c-100f: 0x2f9d205c access time         (the order is different from OSDI 
1010-1013: 0x2f9d1fed modification time    1st ed fig 5-7, although this fig
1014-1017: 0x00000000 inode change time    is not in 2nd ed. But see fig 5-4)
1018-101b: 0x0000000c 1st block used
101c-1033: 0x00000000 2nd through 7th direct blocks
1034-1037: 0x00000000 indirect block
1038-103b: 0x00000000 double indirect block
103c-103f: 0x00000000 triple indirect block
000001000  ff 41 02 00 02 00 02 00  20 00 00 00 5c 20 9d 2f   .A...... ...\ ./
000001010  ed 1f 9d 2f 00 00 00 00  0c 00 00 00 00 00 00 00 .../............
000001020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
000001030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
(snip)
000002FF0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

Following the i-nodes are the data blocks. Only one is in use in this empty file 
system, it contains the root directory. The structure of a directory entry is 
defined in /usr/include/sys/dir.h:

struct direct {
  ino_t d_ino;
  char d_name[DIRSIZ];
};

The root directory, of a newly created file system contains "." and ".." entries. 
(Because this dump uses the "." character to represent non-ASCII characters in 
the ASCII part of the dump this is hard to see, but note that the hexadecimal 
representation of the ASCII "." is 0x2e).

In Minix 1.6.30 the i-node pointers are 16 bit values and precede the file names, 
which may have up to 14 characters. Since the root directory is its own parent, 
both the "." and ".." entries point to i-node one.

000003000  01 00 2e 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
000003010  01 00 2e 2e 00 00 00 00  00 00 00 00 00 00 00 00 ................
000003020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................
(snip)
0000033F0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................

The second block is the first one available, and will be the first block used 
for the first file or directory created in the root directory. In this case it 
is initially filled with 0xf6 bytes, since this pattern was written by the disk 
formatting program. If this disk had been recycled, with a new file system 
created on a previously used disk, the data on the rest of the disk could be 
any kind of garbage left over from the previous use.

000003400  f6 f6 f6 f6 f6 f6 f6 f6  f6 f6 f6 f6 f6 f6 f6 f6 ................
(snip)
