Friday, August 24, 2007

Object Oriented Skelix File System

Prefatory note: I wrote this all in about an hour, so the chances of there being typos is extremely high...so I will revise this as needed. Also the code is in the D programming language.

Introduction

We previously covered the Skelix File System. Today we will be programming an alternative to it in the D programming language. The D programming language was created as an alternative to C++. It has a cleaner syntax, and is a far superior language. That is why we shall use it. One short coming of the language is that it has a garbage collector, which will require us to override the new() and delete() operators. NOTE NOTE NOTE: that this code WILL NOT compile in the skelix operating system because there is no kmalloc() or kfree() functions. Otherwise this code would compile. If someone wants to make these functions, go right ahead :)

The BitMap Class

Let us begin by typing into the command prompt $ touch bitmap.d and then use your favorite editor to open it. We begin by writing:
module bitmap;

class bitmap
{
 /* To be implemented */
}
This is as far as we normally get with our first code. Now we need to add some methods and fields, not to mention constructors and destructors. Let us begin with the constructors and destructors. Recall that D has a garbage collector. We need to override the new() and delete() operators such that it will call the kmalloc() and kfree() functions respectively. That means we need to import the kmalloc() and kfree() functions into the code.
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /* fields to be implemented */
 /* constructors to be implemented */

 new(uint sz)
 {
  void *p;
  p = kmalloc(sz);
  return p;
 }
 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}
Alternatively one could use the garbage collector to make sure that one does not run out of memory. If that were the case, the class would be:
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /* fields to be implemented */
 /* constructors to be implemented */

 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  if (!p)
  {
   printf("ERROR! OUT OF MEMORY!\n");
   return;
  }
  gc.addRange(p, p + sz);
  return p;
 }

 delete(void *p)
 {
  if(p)
  {
   gc.removeRange(p);
   kfree(p);
  }
 }

 /* methods to be implemented */
}
I will not use the garbage collector at all in my approach, so I won't use these operators. They can work, unless Walter Brighton changes things up, but I'm just too old school to use the damn garbage collector! Now, we need to add some fields to the bitmap. The bitmap will refer to a buffer which we usually represent as a void pointer in C. In D however we shall use a void array. It is important to note that in order to avoid calling the garbage collector, we need to avoid: dynamic arrays (arrays without a defined length), and associative arrays (which I still don't understand what the hell it is). So, we will take in as an argument to the constructor the number of blocks the bitmap takes up (represented as a ushort numBlocks) and have a void[] buffer as a field.
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /* fields to be implemented */
 ubyte[] buffer;

 /** Constructor of the bitmap
   *
   * The bitmap is supposed to take up some number
   * of blocks for the file system. There is a
   * inode bitmap and a block bitmap.
   *
   * @PARAM: ushort numBlocks is the number of blocks
   *     that the bitmap takes up.
   */
 public this(ushort numBlocks)
 {
  buffer = new ubyte[512*numBlocks];
  buffer[0]=0;
 }
 public ~this()
 {
  //may possibly be implemented?
 }

 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  if (!p)
  {
   printf("ERROR! OUT OF MEMORY!\n");
   return;
  }
  gc.addRange(p, p + sz);
  return p;
 }

 delete(void *p)
 {
  if(p)
  {
   gc.removeRange(p);
   kfree(p);
  }
 }

 /* methods to be implemented */

}
Now we need to implement methods to manipulate the buffer. Recall in Skelix the operations to set a bit, clear a bit, and test a bit in a given buffer void *s is:
void
setb(void *s, unsigned int i) {
 unsigned char *v = s;
 v += i>>3;
 *v |= 1<<(7-(i%8));
}

void
clrb(void *s, unsigned int i) {
 unsigned char *v = s;
 v += i>>3;
 *v &= ~(1<<(7-(i%8)));
}

int
testb(void *s, unsigned int i) {
 unsigned char *v = s;
 v += i>>3;
 return (*v&(1<<(7-(i%8)))) !=0;
}
/* from Skelix revision 7 /fs/fs.c */
So we shall implement these methods in our class:
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /* fields to be implemented */
 ubyte[] buffer;

 /** Constructor of the bitmap
   *
   * The bitmap is supposed to take up some number
   * of blocks for the file system. There is a
   * inode bitmap and a block bitmap.
   *
   * @PARAM: ushort numBlocks is the number of blocks
   *     that the bitmap takes up.
   */
 public this(ushort numBlocks)
 {
  buffer = new ubyte[512*numBlocks];
  buffer[0]=0;
 }
 public ~this()
 {
  //may possibly be implemented?
 }

 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /** The Set Bit method.
   * 
   * The set bit method sets a bit in the buffer
   * provided the bit is in the buffer. It sets
   * the bit to be 1 if it is 0, otherwise it 
   * does nothing.
   *
   * @PARAM: uint i is the bit number to set.
   *
   **/
 void setBit(uint i) {
  if(i < (buffer.length*8))
  {
   ubyte[] v = cast(ubyte)buffer;
   v += i>>3;
   *v |= 1<<(7-(i%8));
  }
 }

 /** The Clear Bit method
   *
   * The clear bit method sets the bit in the buffer
   * to be 0. It first checks to make sure that the 
   * bit is in the buffer.
   *
   * @PARAM: uint i bit number referring to
   *      the bit that we want to clear. 
   **/
 void clearBit(uint i) {
  if(i < (buffer.length*8))
  {
   unsigned char *v = cast(ubyte)buffer;
   v += i>>3;
   *v &= ~(1<<(7-(i%8)));
  }
 }

 /** The Test Bit method
   *
   * The test bit method checks if the bit in the
   * buffer is occupied or free. It returns 1 if
   * the bit is occupied, 0 if it is free, and -1
   * if it is out of bounds.
   *
   * @PARAM: uint i is the bit we wish to
   *     test to see if it is free or occupied.
   * @RETURNS: 1 if the bit is occupied.
   * @RETURNS: 0 if the bit is free.
   * @RETURNS: -1 if the bit is out of bounds.
   **/
 int testBit(uint i) {
  if(i < (buffer.length*8))
  {
   unsigned char *v = cast(ubyte)buffer;
   v += i>>3;
   return (*v&(1<<(7-(i%8)))) !=0;
  }
  return -1;
 }
}
One last thing that we would like to have in our bitmap class is a getBuffer() method which will be used by a member function of the struct SUPER_BLOCK:
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /**
   * The buffer which represents the blocks which
   * the bitmap takes up. Recall that file system
   * uses two bitmaps which takes up some number of
   * blocks. The first bitmap is the inode bitmap
   * the second is the data block bitmap. This is
   * the in-memory representation of a bitmap.
   */
 public ubyte[] buffer;

 /** Constructor of the bitmap
   *
   * The bitmap is supposed to take up some number
   * of blocks for the file system. There is a
   * inode bitmap and a block bitmap.
   *
   * @PARAM: ushort numBlocks is the number of blocks
   *     that the bitmap takes up.
   */
 public this(ushort numBlocks)
 {
  buffer = new ubyte[512*numBlocks];
  buffer[0]=0;
 }
 public ~this()
 {
  //may possibly be implemented?
 }

 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }


 /** The Set Bit method.
   * 
   * The set bit method sets a bit in the buffer
   * provided the bit is in the buffer. If the bit
   * is 1 (free), then it is set to be 0 (occupied).
   *
   * @PARAM: uint i is the bit number to set.
   *
   **/
 void setBit(uint i) {
  if(i < (buffer.length*8))
  {
   ubyte[] v = cast(ubyte)buffer;
   v += i>>3;
   *v |= 1<<(7-(i%8));
  }
 }

 /** The Clear Bit method
   *
   * The clear bit method sets the bit in the buffer
   * to be 1. It first checks to make sure that the 
   * bit is in the buffer.
   *
   * @PARAM: uint i bit number referring to
   *      the bit that we want to clear. 
   **/
 void clearBit(uint i) {
  if(i < (buffer.length*8))
  {
   unsigned char *v = cast(ubyte)buffer;
   v += i>>3;
   *v &= ~(1<<(7-(i%8)));
  }
 }

 /** The Test Bit method
   *
   * The test bit method checks if the bit in the
   * buffer is occupied or free. It returns 1 if
   * the bit is free, 0 if it is occupied, and -1
   * if it is out of bounds.
   *
   * @PARAM: uint i is the bit we wish to
   *     test to see if it is free or occupied.
   * @RETURNS: 1 if the bit is free.
   * @RETURNS: 0 if the bit is occupied.
   * @RETURNS: -1 if the bit is out of bounds.
   **/
 int testBit(uint i) {
  if(i < (buffer.length*8))
  {
   unsigned char *v = cast(ubyte)buffer;
   v += i>>3;
   return (*v&(1<<(7-(i%8)))) !=0;
  }
  return -1;
 }
}
The last thing that we will do, simply because it saves memory and so much more time later on, is we'll make the buffer a public field rather than a private one. This allows us to get rid of the getBuffer() and setBuffer() methods. There we have the bitmap class done as far as we are concerned. The next module we want to deal with is the inode module.

The Inode Module

The Inode is the atom of the file system. We begin as normal by typing into the command line $ touch inode.d and then open it with our favorite editor (emacs :P) and start out writing
module inode;

class inode
{
 /* to be implemented */
}
If we look into the revision 7 of the Skelix operating system we find the file /fs/inc/fs.h which we will quote selectively as we go along. First we shall note the struct INODE and various macros dealing with it:
#define FT_NML 1
#define FT_DIR 2

struct INODE {
 unsigned int i_mode;  /* file mode */
 unsigned int i_size;  /* size in bytes */
 unsigned int i_block[8];
};

extern struct INODE iroot;

/* from Skelix revision 7 /fs/inc/fs.h */
So we will take the struct fields and make them private fields in the inode class, make the macros const uints, and a global variable (the root inode). Let us put these things in our code:
module inode;

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */
 /* new() and delete() to be implemented */
 /* methods to be implemented */
}
The reason why we keep the same name for these fields rather than make the common sense names is because we want to modify as little code as possible...yes, I am lazier than you. Now we want to also override the new() and delete() operators, which is simply the same thing that we've done before. We simply call extern (C) for the kmalloc() and kfree() methods:
module inode;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}
In the skelix /fs/inc/fs.h file we continue to analyze and find:
#define MAX_NAME_LEN 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};
We follow our method of changing macros into functions or consts. However, we will keep the struct DIR_ENTRY as a struct in the D module because D structs are simple data types. So we add to our module:
module inode;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}

const uint MAX_NAME_LEN = 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};
Now we look again at the /fs/inc/fs.h file again and we find:
struct SUPER_BLOCK {
 unsigned char sb_magic;
 /* DPT 0x08 */
 unsigned int sb_start;
 /* DPT 0x0c */
 unsigned int sb_blocks;

 unsigned int sb_dmap_blks;
 unsigned int sb_imap_blks;
 unsigned int sb_inode_blks;
};

#define FST_FS 0x2e   /* normal partition */
#define FST_SW 0x2f   /* swap partition */

/* skelix revision 7 /fs/inc/fs.h */
It is important that the super block remains a struct in D, so we shall continue our approach thus far of changing macros into consts and translate the struct:
module inode;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}

const uint MAX_NAME_LEN = 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};

struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */
Now we are going to add the macros that we have stuffled so hard to avoid:
#define ABS_BOOT_BLK(sb)  ((sb).sb_start)
#define ABS_SUPER_BLK(sb)  ((ABS_BOOT_BLK(sb))+1)
#define ABS_DMAP_BLK(sb)  ((ABS_SUPER_BLK(sb))+1)
#define ABS_IMAP_BLK(sb)  ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks)
#define ABS_INODE_BLK(sb)  ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks)
#define ABS_DATA_BLK(sb)  ((ABS_INODE_BLK(sb))+INODE_BLKS)

#define INODE_BIT_BLKS  1 /* 512*8 = 4096 inodes */
#define INODES_PER_BLK  (512/sizeof(struct INODE))
#define INODE_BLKS   \
 ((INODE_BIT_BLKS*512*8+INODES_PER_BLK-1)/(INODES_PER_BLK))

/* From skelix revision 7 /fs/inc/fs.h */
Well, we follow our same routine and change these into inline functions or consts:
module inode;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}

const uint MAX_NAME_LEN = 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};

inline uint ABS_BOOT_BLK(SUPER_BLOCK sb) 
{ return ((sb).sb_start); }
inline uint ABS_SUPER_BLK(SUPER_BLOCK sb) 
{ return ((ABS_BOOT_BLK(sb))+1); }
inline uint ABS_DMAP_BLK(SUPER_BLOCK sb) 
{ return ((ABS_SUPER_BLK(sb))+1); }
inline uint ABS_IMAP_BLK(SUPER_BLOCK sb) 
{ return ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks); }
inline uint ABS_INODE_BLK(SUPER_BLOCK sb) 
{ return ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks); }
inline uint ABS_DATA_BLK(SUPER_BLOCK sb) 
{ return ((ABS_INODE_BLK(sb))+INODE_BLKS); }

const uint INODE_BIT_BLKS = 1; /* 512*8 = 4096 inodes */
const uint INODES_PER_BLK = (512/sizeof(struct INODE));
const uint INODE_BLKS  = 
((INODE_BIT_BLKS*512*8+INODES_PER_BLK-1)/(INODES_PER_BLK));


struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */
So you think we're done with this module? Dream on, the revision 7 of the skelix operating system comes with three (for lack of a better word) "sub versions": dpt, fs, and root. We just finished dealing with fs, now we shall investigate the methods for root. Basically we need to add some member functions to the superblock...and the inode class. From the /root/fs.c file we have several new methods (prototyped in /root/inc/fs.h): void verify_fs(void); void verify_dir(void); unsigned int alloc_blk(struct SUPER_BLOCK *sb);void free_blk(struct SUPER_BLOCK *sb, unsigned int n); and void install_color(void). We will add most of these functions. The first two are going to be in our fs module. The next two are going to be member functions of our super block. Let us therefore investigate them here:
unsigned int
alloc_blk(struct SUPER_BLOCK *sb) {
 unsigned int i = 0, j = 0, n = 0, m = 0;
 unsigned char sect[512] = {0};

 n = ABS_DMAP_BLK(*sb);
 for (; isb_dmap_blks; ++i) {
  hd_rw(n, HD_READ, 1, sect);
  for (j=0; j<512*8; ++j) {
   if (testb(sect, j)) {
    ++m;
   } else {   /* gotcha */
    setb(sect, j);
    if (m >= sb->sb_blocks)
     return 0;
    else {
     hd_rw(n, HD_WRITE, 1, sect);
     return ABS_BOOT_BLK(*sb) + m;
    }
   }
  }
  ++n;
 }
 return 0;
}

void
free_blk(struct SUPER_BLOCK *sb, unsigned int n) {
 unsigned char sect[512] = {0};
 unsigned int t = (n-ABS_BOOT_BLK(*sb))/(512*8)+ABS_DMAP_BLK(*sb);
 hd_rw(t, HD_READ, 1, sect);
 clrb(sect, (n-ABS_BOOT_BLK(*sb))%(512*8));
 hd_rw(t, HD_WRITE, 1, sect);
}

/* From revision 7 of skelix /root/fs.c */
These methods are added to the SUPER_BLOCK struct:
module inode;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

const uint FT_NML = 1;
const uint FT_NML = 2;

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
}

const uint MAX_NAME_LEN = 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};

inline uint ABS_BOOT_BLK(SUPER_BLOCK sb) 
{ return ((sb).sb_start); }
inline uint ABS_SUPER_BLK(SUPER_BLOCK sb) 
{ return ((ABS_BOOT_BLK(sb))+1); }
inline uint ABS_DMAP_BLK(SUPER_BLOCK sb) 
{ return ((ABS_SUPER_BLK(sb))+1); }
inline uint ABS_IMAP_BLK(SUPER_BLOCK sb) 
{ return ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks); }
inline uint ABS_INODE_BLK(SUPER_BLOCK sb) 
{ return ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks); }
inline uint ABS_DATA_BLK(SUPER_BLOCK sb) 
{ return ((ABS_INODE_BLK(sb))+INODE_BLKS); }

const uint INODE_BIT_BLKS = 1; /* 512*8 = 4096 inodes */
const uint INODES_PER_BLK = (512/sizeof(struct INODE));
const uint INODE_BLKS  = 
((INODE_BIT_BLKS*512*8+INODES_PER_BLK-1)/(INODES_PER_BLK));

/* hd_rw is prototyped as
void
hd_rw(unsigned int lba, unsigned int com,
   unsigned int sects_to_access, void *buf); 
also some useful definitions:
#define HD_READ  0x20
#define HD_WRITE 0x30 
*/

extern (C) hd_rw(uint lba, uint com, uint sects_to_access, void[] buf);
const uint HD_READ = 0x20;
const uint HD_WRITE = 0x30;


struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;

 uint alloc_blk() {
  uint i = 0, j = 0, n = 0, m = 0;
  bitmap sect = new bitmap(1);

  n = ABS_DMAP_BLK((*this));
  for (; i<(*this).sb_dmap_blks; ++i) {
   hd_rw(n, HD_READ, 1, sect.buffer);
   for (j=0; j<512*8; ++j) {
    if (sect.testBit(j)) {
     ++m;
    } else {   /* gotcha */
     sect.setBit(j);
     if (m >= (*this).sb_blocks)
     {
      delete sect;
      return 0;
     }
     else {
      hd_rw(n, HD_WRITE, 1, sect.buffer);
      delete sect;
      return ABS_BOOT_BLK((*this)) + m;
     }
    }
   }
   ++n;
  }
  delete sect;
  return 0;
 }

 void free_blk(uint n) {
  bitmap sect = new bitmap(1);
  uint t = (n-ABS_BOOT_BLK((*this)))/(512*8)+ABS_DMAP_BLK((*this));
  hd_rw(t, HD_READ, 1, sect.buffer);
  sect.clearBit(n-ABS_BOOT_BLK((*this)))%(512*8);
  hd_rw(t, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */
There are a few more operations that we would like to add to the superblock struct (in retrospect, perhaps it would have been better to have made it a class...less efficiency from a memory perspective). The operations from the Skelix revision 7 /root/fs.c are:
static int
alloc_inode(struct SUPER_BLOCK *sb) {
 unsigned char sect[512] = {0};
 int i = 0;
 hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
 for (; i<512; ++i) {
  if (! testb(sect, i)) {
   setb(sect, i);
   hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
   break;
  }
 }
 return (i==512)?-1:i;
}

static void
free_inode(struct SUPER_BLOCK *sb, int n) {
 unsigned char sect[512] = {0};
 hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
 clrb(sect, n);
 hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
}

/* Skelix revision 7 /root/fs.c */
Because I am getting lazy, I will simply copy/paste the relevant code from the inode module:
/* hd_rw is prototyped as
void
hd_rw(unsigned int lba, unsigned int com,
   unsigned int sects_to_access, void *buf); 
also some useful definitions:
#define HD_READ  0x20
#define HD_WRITE 0x30 
*/

extern (C) hd_rw(uint lba, uint com, uint sects_to_access, void[] buf);
const uint HD_READ = 0x20;
const uint HD_WRITE = 0x30;


struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;

 uint alloc_blk() {
  uint i = 0, j = 0, n = 0, m = 0;
  bitmap sect = new bitmap(1);

  n = ABS_DMAP_BLK((*this));
  for (; i<(*this).sb_dmap_blks; ++i) {
   hd_rw(n, HD_READ, 1, sect.buffer);
   for (j=0; j<512*8; ++j) {
    if (sect.testBit(j)) {
     ++m;
    } else {   /* gotcha */
     sect.setBit(j);
     if (m >= (*this).sb_blocks)
     {
      delete sect;
      return 0;
     }
     else {
      hd_rw(n, HD_WRITE, 1, sect.buffer);
      delete sect;
      return ABS_BOOT_BLK((*this)) + m;
     }
    }
   }
   ++n;
  }
  delete sect;
  return 0;
 }

 void free_blk(uint n) {
  bitmap sect = new bitmap(1);
  uint t = (n-ABS_BOOT_BLK((*this)))/(512*8)+ABS_DMAP_BLK((*this));
  hd_rw(t, HD_READ, 1, sect.buffer);
  sect.clearBit(n-ABS_BOOT_BLK((*this)))%(512*8);
  hd_rw(t, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
 static int alloc_inode() {
  bitmap sect = new bitmap(1);
  int i = 0;
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  for (; i<512; ++i) {
   if (! sect.testBit(i)) {
    sect.setBit(i);
    hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
    break;
   }
  }
  delete bitmap;
  return (i==512)?-1:i;
 }

 static void free_inode(int n) {
  bitmap sect = new bitmap(1);
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  sect.clearBit(n);
  hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
  delete sect;
 }
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */
Some more final SUPER_BLOCK operations to add would be:
static struct INODE *
iget(struct SUPER_BLOCK *sb, INODE *inode, int n) {
 unsigned char sect[512] = {0};
 int i = n/INODES_PER_BLK;
 int j = n%INODES_PER_BLK;

 hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
 memcpy(inode, sect+j*sizeof(struct INODE), sizeof(struct INODE));
 return inode;
}

static void
iput(struct SUPER_BLOCK *sb, INODE *inode, int n) {
 unsigned char sect[512] = {0};
 int i = n/INODES_PER_BLK;
 int j = n%INODES_PER_BLK;
 hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
 memcpy(sect+j*sizeof(struct INODE), inode, sizeof(struct INODE));
 hd_rw(ABS_INODE_BLK(*sb)+i, HD_WRITE, 1, sect);
}

static struct INODE iroot = {FT_DIR, 2*sizeof(struct DIR_ENTRY), {0,}};

/* Skelix revision 7 /root/fs.c */
We then put these to work in our SUPER_BLOCK structure:
/* hd_rw is prototyped as
void
hd_rw(unsigned int lba, unsigned int com,
   unsigned int sects_to_access, void *buf); 
also some useful definitions:
#define HD_READ  0x20
#define HD_WRITE 0x30 
*/

extern (C) hd_rw(uint lba, uint com, uint sects_to_access, void[] buf);
const uint HD_READ = 0x20;
const uint HD_WRITE = 0x30;


struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;

 uint alloc_blk() {
  uint i = 0, j = 0, n = 0, m = 0;
  bitmap sect = new bitmap(1);

  n = ABS_DMAP_BLK((*this));
  for (; i<(*this).sb_dmap_blks; ++i) {
   hd_rw(n, HD_READ, 1, sect.buffer);
   for (j=0; j<512*8; ++j) {
    if (sect.testBit(j)) {
     ++m;
    } else {   /* gotcha */
     sect.setBit(j);
     if (m >= (*this).sb_blocks)
     {
      delete sect;
      return 0;
     }
     else {
      hd_rw(n, HD_WRITE, 1, sect.buffer);
      delete sect;
      return ABS_BOOT_BLK((*this)) + m;
     }
    }
   }
   ++n;
  }
  delete sect;
  return 0;
 }

 void free_blk(uint n) {
  bitmap sect = new bitmap(1);
  uint t = (n-ABS_BOOT_BLK((*this)))/(512*8)+ABS_DMAP_BLK((*this));
  hd_rw(t, HD_READ, 1, sect.buffer);
  sect.clearBit(n-ABS_BOOT_BLK((*this)))%(512*8);
  hd_rw(t, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
 int alloc_inode() {
  bitmap sect = new bitmap(1);
  int i = 0;
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  for (; i<512; ++i) {
   if (! sect.testBit(i)) {
    sect.setBit(i);
    hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
    break;
   }
  }
  delete sect;
  return (i==512)?-1:i;
 }

 static void free_inode(int n) {
  bitmap sect = new bitmap(1);
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  sect.clearBit(n);
  hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
  delete sect;
 }
 static INODE* iget(INODE *inode, int n) {
  bitmap sect = new bitmap(1);
  int i = n/INODES_PER_BLK;
  int j = n%INODES_PER_BLK;

  hd_rw(ABS_INODE_BLK(*this)+i, HD_READ, 1, sect.buffer);
  memcpy(inode, sect.buffer+j*(INODE.sizeof), INODE.sizeof);
  delete sect;
  return inode;
 }

 static void iput(INODE *inode, int n) {
  bitmap sect = new bitmap(1);
  int i = n/INODES_PER_BLK;
  int j = n%INODES_PER_BLK;
  hd_rw(ABS_INODE_BLK(*this)+i, HD_READ, 1, sect.buffer);
  memcpy(sect.buffer+j*(INODE.sizeof), inode, INODE.sizeof);
  hd_rw(ABS_INODE_BLK(*this)+i, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */

static inode iroot = new inode(FT_DIR);
iroot.setSize(2*(DIR_ENTRY.sizeof));
iroot.setBlock(0, 0);
Now we have condemned ourselves to commit several methods and a constructor type to the inode. This is not too bad, we could have screwed up worse. We shall then integrate these things into the inode class:
class INODE
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */
 public this(uint mode)
 {
  i_mode = mode;
 }
 public ~this()
 {
  
 }

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
  kfree(p);
 }

 /* methods to be implemented */
 public void setSize(uint size)
 {
  i_size = size;
 }
 public bool setBlock(ushort arrayIndex, uint address)
 {
  if(arrayIndex < 8)
  {
   i_block[arrayIndex] = address;
   return true;
  }
  return false;
 }
}
We have only a few functions left in the skelix file system, let us examine one:
static void
stat(INODE *inode) {
 unsigned int i = 0;
 char sect[512] = {0};
 struct DIR_ENTRY *de;

 kprintf(KPL_DUMP, "======== stat / ========\n");
 switch (inode->i_mode) {
 case FT_NML:
  kprintf(KPL_DUMP, "File, ");
  break;
 case FT_DIR:
  kprintf(KPL_DUMP, "Dir,  ");
  break;
 default:
  kprintf(KPL_PANIC, "UNKNOWN FILE TYPE!!");
  halt();
 }
 kprintf(KPL_DUMP, "Size: %d, ", inode->i_size);
 kprintf(KPL_DUMP, "Blocks: ");
 for (; i<8; ++i)
  kprintf(KPL_DUMP, "%d, ", inode->i_block[i]);
 hd_rw(inode->i_block[0], HD_READ, 1, sect);
 switch (inode->i_mode) {
 case FT_DIR:
  kprintf(KPL_DUMP, "\nName\tINode\n");
  de = (struct DIR_ENTRY *)sect;
  for (i=0; ii_size/sizeof(struct DIR_ENTRY); ++i) {
   kprintf(KPL_DUMP, "%s\t%x\n", de[i].de_name, de[i].de_inode);
  }
  break;
 default:
  break;
 }
}

/* From Skelix Revision 7 /root/fs.c */
We simply "plug and chug" this into the inode class:
extern (C) enum KP_LEVEL {KPL_DUMP, KPL_PANIC};
extern (C) void kprintf(enum KP_LEVEL, const char []fmt, ...);
extern (C) void halt();

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */
 public this(uint mode)
 {
  i_mode = mode;
 }
 public ~this()
 {
  
 }

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }
 delete(void *p)
 {
  kfree(p);
 }

 /**
   * This sets the size of the inode to be a given size.
   *
   * @PARAM: uint size the size we are setting
   *     the inode to be.
   **/
 public void setSize(uint size)
 {
  i_size = size;
 }
 /**
   * This sets an address for the inode.
   *
   * @PARAM: ushort arrayIndex is the index of
   *     the i_block array that we are setting.
   * @PARAM: uint address is the address
   *     we are setting the thing to be.
   * @RETURNS: true if the arrayIndex is valid.
   * @RETURNS: false otherwise.
   **/
 public bool setBlock(ushort arrayIndex, uint address)
 {
  if(arrayIndex < 8)
  {
   i_block[arrayIndex] = address;
   return true;
  }
  return false;
 }
 /**
   * The stat method basically prints out to the
   * screen everything you need to know about the
   * inode. If it's a directory, it prints out the
   * contents of the directory. If it's a file, it
   * prints out nothing special. Regardless, it 
   * prints out the size of the inode, and the block
   * addresses.
   *
   * @PARAM: void
   * @RETURNS: void
   **/
 public void stat() {
  uint i = 0;
  bitmap sect = new bitmap(1);
  DIR_ENTRY[(i_size/sizeof(struct DIR_ENTRY))] *de;

  kprintf(KPL_DUMP, "======== stat / ========\n");
  switch (i_mode) {
  case FT_NML:
   kprintf(KPL_DUMP, "File, ");
   break;
  case FT_DIR:
   kprintf(KPL_DUMP, "Dir,  ");
   break;
  default:
   kprintf(KPL_PANIC, "UNKNOWN FILE TYPE!!");
   halt();
  }
  kprintf(KPL_DUMP, "Size: %d, ", i_size);
  kprintf(KPL_DUMP, "Blocks: ");
  for (; i<8; ++i)
   kprintf(KPL_DUMP, "%d, ", i_block[i]);
  hd_rw(i_block[0], HD_READ, 1, sect.buffer);
  switch (i_mode) {
  case FT_DIR: //I think this needs to be re-implemented...
   kprintf(KPL_DUMP, "\nName\tINode\n");
   de = cast(struct DIR_ENTRY *)sect.buffer;
   for (i=0; i < de.length; ++i) {
    kprintf(KPL_DUMP, "%s\t%x\n", de[i].de_name, de[i].de_inode);
   }
   break;
  default:
   break;
  }
 }
}
So to tie it all together, we end up with two modules (one for superblock and the other for the inode):
module inode;
import bitmap;

extern (C) enum KP_LEVEL {KPL_DUMP, KPL_PANIC};
extern (C) void kprintf(enum KP_LEVEL, const char []fmt, ...);
extern (C) void halt();

class inode
{
 private uint i_mode;
 private uint i_size;
 private uint[8] i_block;

 /* constructors to be implemented */
 public this(uint mode)
 {
  i_mode = mode;
 }
 public ~this()
 {
  
 }

 /* new() and delete() operators */
 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }
 delete(void *p)
 {
  kfree(p);
 }

 /**
   * This sets the size of the inode to be a given size.
   *
   * @PARAM: uint size the size we are setting
   *     the inode to be.
   **/
 public void setSize(uint size)
 {
  i_size = size;
 }
 /**
   * This sets an address for the inode.
   *
   * @PARAM: ushort arrayIndex is the index of
   *     the i_block array that we are setting.
   * @PARAM: uint address is the address
   *     we are setting the thing to be.
   * @RETURNS: true if the arrayIndex is valid.
   * @RETURNS: false otherwise.
   **/
 public bool setBlock(ushort arrayIndex, uint address)
 {
  if(arrayIndex < 8)
  {
   i_block[arrayIndex] = address;
   return true;
  }
  return false;
 }
 /**
   * The stat method basically prints out to the
   * screen everything you need to know about the
   * inode. If it's a directory, it prints out the
   * contents of the directory. If it's a file, it
   * prints out nothing special. Regardless, it 
   * prints out the size of the inode, and the block
   * addresses.
   *
   * @PARAM: void
   * @RETURNS: void
   **/
 public void stat() {
  uint i = 0;
  bitmap sect = new bitmap(1);
  DIR_ENTRY[(i_size/sizeof(struct DIR_ENTRY))] *de;

  kprintf(KPL_DUMP, "======== stat / ========\n");
  switch (i_mode) {
  case FT_NML:
   kprintf(KPL_DUMP, "File, ");
   break;
  case FT_DIR:
   kprintf(KPL_DUMP, "Dir,  ");
   break;
  default:
   kprintf(KPL_PANIC, "UNKNOWN FILE TYPE!!");
   halt();
  }
  kprintf(KPL_DUMP, "Size: %d, ", i_size);
  kprintf(KPL_DUMP, "Blocks: ");
  for (; i<8; ++i)
   kprintf(KPL_DUMP, "%d, ", i_block[i]);
  hd_rw(i_block[0], HD_READ, 1, sect.buffer);
  switch (i_mode) {
  case FT_DIR: //I think this needs to be re-implemented...
   kprintf(KPL_DUMP, "\nName\tINode\n");
   de = cast(struct DIR_ENTRY *)sect.buffer;
   for (i=0; i < de.length; ++i) {
    kprintf(KPL_DUMP, "%s\t%x\n", de[i].de_name, de[i].de_inode);
   }
   break;
  default:
   break;
  }
 }
}


const uint MAX_NAME_LEN = 11

struct DIR_ENTRY {
 char de_name[MAX_NAME_LEN];
 int de_inode;
};

extern INODE iroot;
module superblock;
import bitmap;
import inode;

inline uint ABS_BOOT_BLK(SUPER_BLOCK sb) { return ((sb).sb_start); }
inline uint ABS_SUPER_BLK(SUPER_BLOCK sb) { return ((ABS_BOOT_BLK(sb))+1); }
inline uint ABS_DMAP_BLK(SUPER_BLOCK sb) { return ((ABS_SUPER_BLK(sb))+1); }
inline uint ABS_IMAP_BLK(SUPER_BLOCK sb) { return ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks); }
inline uint ABS_INODE_BLK(SUPER_BLOCK sb) { return ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks); }
inline uint ABS_DATA_BLK(SUPER_BLOCK sb) { return ((ABS_INODE_BLK(sb))+INODE_BLKS); }

const uint INODE_BIT_BLKS = 1; /* 512*8 = 4096 inodes */
const uint INODES_PER_BLK = (512/(INODE.sizeof));
const uint INODE_BLKS  = ((INODE_BIT_BLKS*512*8+INODES_PER_BLK-1)/(INODES_PER_BLK));

extern (C) hd_rw(uint lba, uint com, uint sects_to_access, void[] buf);
const uint HD_READ = 0x20;
const uint HD_WRITE = 0x30;

struct SUPER_BLOCK {
 ubyte sb_magic;
 /* DPT 0x08 */
 uint sb_start;
 /* DPT 0x0c */
 uint sb_blocks;

 uint sb_dmap_blks;
 uint sb_imap_blks;
 uint sb_inode_blks;

 uint alloc_blk() {
  uint i = 0, j = 0, n = 0, m = 0;
  bitmap sect = new bitmap(1);

  n = ABS_DMAP_BLK((*this));
  for (; i<(*this).sb_dmap_blks; ++i) {
   hd_rw(n, HD_READ, 1, sect.buffer);
   for (j=0; j<512*8; ++j) {
    if (sect.testBit(j)) {
     ++m;
    } else {   /* gotcha */
     sect.setBit(j);
     if (m >= (*this).sb_blocks)
     {
      delete sect;
      return 0;
     }
     else {
      hd_rw(n, HD_WRITE, 1, sect.buffer);
      delete sect;
      return ABS_BOOT_BLK((*this)) + m;
     }
    }
   }
   ++n;
  }
  delete sect;
  return 0;
 }

 void free_blk(uint n) {
  bitmap sect = new bitmap(1);
  uint t = (n-ABS_BOOT_BLK((*this)))/(512*8)+ABS_DMAP_BLK((*this));
  hd_rw(t, HD_READ, 1, sect.buffer);
  sect.clearBit(n-ABS_BOOT_BLK((*this)))%(512*8);
  hd_rw(t, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
 int alloc_inode() {
  bitmap sect = new bitmap(1);
  int i = 0;
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  for (; i<512; ++i) {
   if (! sect.testBit(i)) {
    sect.setBit(i);
    hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
    break;
   }
  }
  delete sect;
  return (i==512)?-1:i;
 }

 static void free_inode(int n) {
  bitmap sect = new bitmap(1);
  hd_rw(ABS_IMAP_BLK(*this), HD_READ, 1, sect.buffer);
  sect.clearBit(n);
  hd_rw(ABS_IMAP_BLK(*this), HD_WRITE, 1, sect.buffer);
  delete sect;
 }
 static inode* iget(INODE *inode, int n) {
  bitmap sect = new bitmap(1);
  int i = n/INODES_PER_BLK;
  int j = n%INODES_PER_BLK;

  hd_rw(ABS_INODE_BLK(*this)+i, HD_READ, 1, sect.buffer);
  memcpy(inode, sect.buffer+j*(INODE.sizeof), INODE.sizeof);
  delete sect;
  return inode;
 }

 static void iput(INODE *inode, int n) {
  bitmap sect = new bitmap(1);
  int i = n/INODES_PER_BLK;
  int j = n%INODES_PER_BLK;
  hd_rw(ABS_INODE_BLK(*this)+i, HD_READ, 1, sect.buffer);
  memcpy(sect.buffer+j*(INODE.sizeof), inode, INODE.sizeof);
  hd_rw(ABS_INODE_BLK(*this)+i, HD_WRITE, 1, sect.buffer);
  delete sect;
 }
};

const uint FST_FS = 0x2e;   /* normal partition */
const uint FST_SW = 0x2f;   /* swap partition */

static inode iroot = new inode(FT_DIR);
iroot.setSize(2*(DIR_ENTRY.sizeof));
iroot.setBlock(0, 0);
There you have it, your superblock and inode modules. Now we need to turn our attention to the actual file system itself.

The FS Module

The fs module is the module that people would be using if they would like to use a system call like read(), open(), etc. The last two remaining functions in the skelix file system are in /root/fs.c:
static void
check_root(void) {
 struct SUPER_BLOCK sb;
 unsigned char sect[512] = {0};
 struct DIR_ENTRY *de = NULL;

 sb.sb_start = *(unsigned int *)(HD0_ADDR);
 hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
 memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
 hd_rw(ABS_IMAP_BLK(sb), HD_READ, 1, sect);
 if (! testb(sect, 0)) {
  kprintf(KPL_DUMP, "/ has not been created, creating....\t\t\t\t\t  ");
  if (alloc_inode(&sb) != 0) {
   kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
   halt();
  }
  iroot.i_block[0] = alloc_blk(&sb);
  iput(&sb, &iroot, 0);
  
  de = (struct DIR_ENTRY *)sect;
  strcpy(de->de_name, ".");
  de->de_inode = 0;
  ++de;
  strcpy(de->de_name, "..");
  de->de_inode = -1;
  hd_rw(iroot.i_block[0], HD_WRITE, 1, sect);
  kprintf(KPL_DUMP, "[DONE]");
 }
 iget(&sb, &iroot, 0);
 hd_rw(iroot.i_block[0], HD_READ, 1, sect);
 de = (struct DIR_ENTRY *)sect;

 if ((strcmp(de[0].de_name, ".")) || (de[0].de_inode) ||
  (strcmp(de[1].de_name, "..")) || (de[1].de_inode) != -1) {
  kprintf(KPL_PANIC, "File system is corrupted!!!\n");
  halt();
 }
}

void
verify_dir(void) {
 unsigned char sect[512] = {0};
 unsigned int *q = (unsigned int *)(HD0_ADDR);
 struct INODE inode;
 struct SUPER_BLOCK sb;

 sb.sb_start = q[0];
 hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
 check_root();
 memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
 stat(iget(&sb, &inode, 0));
}

/* From the revision 7 of skelix /root/fs.c */
We now translate this into D as usual we start by writing ot the command line: $ touch fs.d and then open it with our favorite editor. We then start ripping away at it:
module fs;
import inode;
import bitmap;
import superblock;

extern (C) void halt();

/* begin macros from C */
const uint IDT_ADDR = 0x80000;
const uint IDT_SIZE = (256*8);
const uint GDT_ADDR = ((IDT_ADDR)+IDT_SIZE);
const uint GDT_ENTRIES = 5;
const uint GDT_SIZE = (8*GDT_ENTRIES);
const uint HD0_ADDR = (GDT_ADDR+GDT_SIZE);
const uint HD0_SIZE = (4*8);
/* end macros from C */


void check_root() {
 SUPER_BLOCK sb;
 bitmap sect = new bitmap(1);
 DIR_ENTRY[((i_size/(DIR_ENTRY.sizeof))] *de = NULL;

 sb.sb_start = *(unsigned int *)(HD0_ADDR);
 hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect.buffer);
 memcpy(&sb, sect.buffer, SUPER_BLOCK.sizeof);
 hd_rw(ABS_IMAP_BLK(sb), HD_READ, 1, sect.buffer);
 if (! sect.testBit(0)) {
  kprintf(KPL_DUMP, "/ has not been created, creating....\t\t\t\t\t  ");
  if ((&sb).alloc_inode() != 0) {
   kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
   halt();
  }
  iroot.setBlock(0,(&sb).alloc_blk());
  (&sb).iput(&iroot, 0);
  
  de = cast(struct DIR_ENTRY *)(sect.buffer);
  strcpy(de.de_name, ".");
  de.de_inode = 0;
  ++de;
  strcpy(de.de_name, "..");
  de.de_inode = -1;
  hd_rw(iroot.getBlock(0), HD_WRITE, 1, sect.buffer);
  kprintf(KPL_DUMP, "[DONE]");
 }
 (&sb).iget(&iroot, 0);
 hd_rw(iroot.getBlock(0), HD_READ, 1, sect.buffer);
 de = cast(struct DIR_ENTRY *)(sect.buffer);

 if ((strcmp(de[0].de_name, ".")) || (de[0].de_inode) || (strcmp(de[1].de_name, "..")) || (de[1].de_inode) != -1) {
  kprintf(KPL_PANIC, "File system is corrupted!!!\n");
  halt();
 }
}

void verify_dir() {
 bitmap sect = new bitmap(1);
 unsigned int *q = cast(unsigned int *)(HD0_ADDR);
 INODE inode;
 SUPER_BLOCK sb;

 sb.sb_start = q[0];
 hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect.buffer);
 check_root();
 memcpy(&sb, sect.buffer, SUPER_BLOCK.sizeof);
 ((&sb).iget(&inode, 0)).stat();
}
This I think is all we have to do in order to change the skelix file system into using the D programming language. Unfortunately as I have stressed earlier in this piece, we cannot actually implement this since Skelix does not implement kfree() and kmalloc(). It should not be too hard to implement a virtual file system for Skelix now using the D programming language...creating an abstract vnode class which the inode class extends, etc.

The Bitmap Module Reconsidered

I have a problem with the implementation with the bitmap module...largely because it still uses the garbage collector due to its somewhat dynamic array. Well, what if we make our bitmap class a linked list of these classes? That way the array remains fixed, and the bitmap can be several blocks big...and no garbage collector is needed! The revised module is below:
module bitmap;

extern (C) void *kmalloc(uint size);
extern (C) void kfree(void *ptr);

class bitmap
{
 /**
   * The buffer which represents the blocks which
   * the bitmap takes up. Recall that file system
   * uses two bitmaps which takes up some number of
   * blocks. The first bitmap is the inode bitmap
   * the second is the data block bitmap. This is
   * the in-memory representation of a bitmap.
   */
  public ubyte[512] buffer;
  private bitmap* next;

 /** Constructor of the bitmap
   *
   * The bitmap is supposed to take up some number
   * of blocks for the file system. There is a
   * inode bitmap and a block bitmap.
   *
   * @PARAM: ushort numBlocks is the number of blocks
   *     that the bitmap takes up.
   */
 public this(ushort numBlocks)
 {
 buffer[0]=0;
 if(numBlocks > 1)
  next = &(new bitmap(numBlocks-1, false);

 }
 public this(ushort numBlocks, bool first)
 {
 if(numBlocks > 1)
  next = &(new bitmap(numBlocks-1, false);

 }
 public ~this()
 {
  //may possibly be implemented?
 }

 new(uint sz)
 {
  void* p;
  p = kmalloc(sz);
  return p;
 }

 delete(void *p)
 {
 delete next;
 kfree(p);
 }


 /** The Set Bit method.
   * 
   * The set bit method sets a bit in the buffer
   * provided the bit is in the buffer. If the bit
   * is 1 (free), then it is set to be 0 (occupied).
   *
   * @PARAM: uint i is the bit number to set.
   *
   **/
 void setBit(uint i) {
  if(i < (buffer.length*8))
  {
 if(uint > buffer.length*8)
  next.setBit(i-512);
 else { 
  ubyte[] v = cast(ubyte)buffer;
  v += i>>3;
  *v |= 1<<(7-(i%8));
 }
  }
 }

 /** The Clear Bit method
   *
   * The clear bit method sets the bit in the buffer
   * to be 1. It first checks to make sure that the 
   * bit is in the buffer.
   *
   * @PARAM: uint i bit number referring to
   *      the bit that we want to clear. 
   **/
 void clearBit(uint i) {
  if(i < (buffer.length*8))
  {
 if(uint > buffer.length*8)
  next.clearBit(i-512);
 else { 
    unsigned char *v = cast(ubyte)buffer;
    v += i>>3;
    *v &= ~(1<<(7-(i%8)));
 }
  }
 }

 /** The Test Bit method
   *
   * The test bit method checks if the bit in the
   * buffer is occupied or free. It returns 1 if
   * the bit is free, 0 if it is occupied, and -1
   * if it is out of bounds.
   *
   * @PARAM: uint i is the bit we wish to
   *     test to see if it is free or occupied.
   * @RETURNS: 1 if the bit is free.
   * @RETURNS: 0 if the bit is occupied.
   * @RETURNS: -1 if the bit is out of bounds.
   **/
 int testBit(uint i) {
  if(i < (buffer.length*8))
  {
 if(uint > buffer.length*8)
  return next.testBit(i-512);
 else { 
    unsigned char *v = cast(ubyte)buffer;
    v += i>>3;
    return (*v&(1<<(7-(i%8)))) !=0;
 }
  }
  return -1;
 }
}

Revision History

Revision 0: 24 August 2007 - published. Revision 1: 24 August 2007 - revised code snippets to fit on the page. Revision 2: 24 August 2007 - revised the bitmap class. Revision 3: 29 August 2007 - revised the inode class fields.

No comments: