0

I am working on some ext2 filesystem stuff for a school project (implement ls, mkdir, that kind of thing) and have found that I am generating a lot of redundant code for tasks where I need to traverse an inode's i_block. I have functions to count the number of dir entries, search the dir entries for a strcmp name match, reading data, writing data... traversing i_block seems common to many problems. I am attempting to write something akin to an iterator for the i_block to remove this redundancy.

I am wondering what would be a good way to do this? are there examples where this or something similar is done in linux system code? or is this simply a bad idea.

The code I have come up with thus far:

    // returns block number located at iter position
    // accepts a minode which is a struct wrapping an inode (in memory inode)
    // accepts an iter which will self mutate and should start at 0
    int iter_i_block(minode *mip, int *iter) {
      static char buf[BLKSIZE]; // static buffer
      // buffer number used to check if a new block needs to be read in
      static int bufno;
      // inode number used to determine if we are working on a new inode
      static int ino; 
      // block number to return
      int bno;
      // flag for if this a different inode than last time
      int new_ino = 0;

      if (ino != mip->ino) {
        ino = mip->ino;
        new_ino = 1;
      }
      // direct blocks
      if (*iter < 12) {
        bno = mip->inode.i_block[*iter];
        (*iter)++;
        bufno = bno;
        return bno;
      }

      // indirect blocks
      if (*iter < 12 + BLKSIZE_1024 / sizeof(int)) {
        if (!mip->inode.i_block[12])
          return 0;
        if (new_ino || bufno != 12)
          get_block(mip->mount_entry, mip->inode.i_block[12], buf);
        bufno = 12;
        bno = *((int *)buf + (*iter - 12));
        (*iter)++;
        return bno;
      }

      // double indirect blocks (not shown)
      // triple indirect blocks (not shown)
      return 0;
    }

Any advice is appreciated! Thank you

Ace.C
  • 1,201
  • 12
  • 22
  • it is not clear what is your question – alinsoar Apr 11 '19 at 07:37
  • It seems like a reasonable idea. You might consider modeling the invocation of the iterator after the `queue(3)` macros -- e.g. `LIST_FOREACH` -- where you have a macro that handles initialization, iteration and end-checking in one fell swoop. One other comment: using static variables within the iterator seems like a poor implementation choice. I would instead put those variables into a structure. Then let the caller just declare the structure and pass it to the macro to initialize and transform it as you iterate (this way your framework will be usable in multiple threads simultaneously). – Gil Hamilton Apr 11 '19 at 16:23

1 Answers1

0

Here is what I am going with for now

Thanks Gil Hamilton for suggesting to use a struct

typedef struct blk_iter {
  struct minode *mip;
  // buf contains the nth block
  unsigned int nth;
  // direct block (buf), indirection block(map1),
  // double indirection(map2), triple indirection(map3);
  char buf[BLKSIZE_1024], map1[BLKSIZE_1024], map2[BLKSIZE_1024],
      map3[BLKSIZE_1024];
} blk_iter;

// returns a char* buffer of BLKSIZE on success
// null on failure (nothing more to read)
// must start from nth = -1
char *get_blk(blk_iter *it, int lbk) {
  // calculations for convience, could be macros
  int blks_per = BLKSIZE_1024 / sizeof(int);
  int direct_start = 0, direct_end = 12, indirect_start = direct_end,
      indirect_end = direct_end + blks_per, double_start = indirect_end,
      double_end = indirect_end + blks_per * blks_per,
      triple_start = double_end,
      triple_end = double_end + blks_per * blks_per * blks_per;
  // pointers for shorter names
  unsigned int *i_block = it->mip->inode.i_block;
  mount_entry *me = it->mip->mount_entry;
  // null check
  if (!it || !it->mip)
    return 0;
  // get blocks based on lbk
  if (lbk < direct_end) {
    // get direct block
    get_block(me, i_block[lbk], it->buf);
  } else if (lbk < indirect_end) {
    // get indirect block
    if (!(it->nth >= indirect_start && it->nth < indirect_end))
      // check if map1 cached
      get_block(me, i_block[12], it->map1);
    get_block(me, it->map1[lbk - indirect_start], it->buf);
  } else if (lbk < double_end) {
    // get double indirect block
    if (!(it->nth >= double_start && it->nth < double_end))
      // check if map2 cached
      get_block(me, i_block[13], it->map2);
    if (!((lbk - double_start) / blks_per ==
          (it->nth - double_start) / blks_per))
      // check if map1 cached
      get_block(me, it->map2[(lbk - double_start) / blks_per], it->map1);
    get_block(me, it->map1[(lbk - double_start) % blks_per], it->buf);
  } else if (lbk < triple_end) {
    // triple  indirect blocks
    if (!(it->nth >= triple_start && it->nth < triple_end))
      // check if map3 cached
      get_block(me, i_block[12], it->map3);
    if (!((lbk - triple_start) / (blks_per * blks_per) ==
          (it->nth - triple_start) / (blks_per * blks_per)))
      // check if map2 cached
      get_block(me, it->map3[(lbk - triple_start) / (blks_per * blks_per)],
                it->map2);
    if (!((lbk - triple_start) / blks_per ==
          (it->nth - triple_start) / blks_per))
      // check if map1 cached
      get_block(me, it->map2[(lbk - triple_start) / blks_per], it->map1);
    get_block(me, it->map1[(lbk - triple_start) % blks_per], it->buf);
  }
  it->nth = lbk;
  return it->buf;
}
Ace.C
  • 1,201
  • 12
  • 22