427 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* This file manages the inode table.  There are procedures to allocate and
 | 
						|
 * deallocate inodes, acquire, erase, and release them, and read and write
 | 
						|
 * them from the disk.
 | 
						|
 *
 | 
						|
 * The entry points into this file are
 | 
						|
 *   get_inode:       search inode table for a given inode; if not there,
 | 
						|
 *                 read it
 | 
						|
 *   put_inode:       indicate that an inode is no longer needed in memory
 | 
						|
 *   update_times: update atime, ctime, and mtime
 | 
						|
 *   rw_inode:       read a disk block and extract an inode, or corresp. write
 | 
						|
 *   dup_inode:       indicate that someone else is using an inode table entry
 | 
						|
 *   find_inode:   retrieve pointer to inode in inode cache
 | 
						|
 *
 | 
						|
 * Created (MFS based):
 | 
						|
 *   February 2010 (Evgeniy Ivanov)
 | 
						|
 */
 | 
						|
 | 
						|
#include "fs.h"
 | 
						|
#include <string.h>
 | 
						|
#include "buf.h"
 | 
						|
#include "inode.h"
 | 
						|
#include "super.h"
 | 
						|
#include <minix/vfsif.h>
 | 
						|
 | 
						|
FORWARD _PROTOTYPE( void icopy, (struct inode *rip, d_inode *dip,
 | 
						|
				 int direction, int norm));
 | 
						|
FORWARD _PROTOTYPE( void addhash_inode, (struct inode *node)        );
 | 
						|
FORWARD _PROTOTYPE( void unhash_inode, (struct inode *node)         );
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                fs_putnode                                                 *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int fs_putnode(void)
 | 
						|
{
 | 
						|
/* Find the inode specified by the request message and decrease its counter.*/
 | 
						|
 | 
						|
  struct inode *rip;
 | 
						|
  int count;
 | 
						|
 | 
						|
  rip = find_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR);
 | 
						|
 | 
						|
  if (!rip) {
 | 
						|
	printf("%s:%d put_inode: inode #%d dev: %d not found\n", __FILE__,
 | 
						|
		__LINE__, (ino_t) fs_m_in.REQ_INODE_NR, fs_dev);
 | 
						|
	panic("fs_putnode failed");
 | 
						|
  }
 | 
						|
 | 
						|
  count = fs_m_in.REQ_COUNT;
 | 
						|
  if (count <= 0) {
 | 
						|
	printf("%s:%d put_inode: bad value for count: %d\n", __FILE__,
 | 
						|
		__LINE__, count);
 | 
						|
	panic("fs_putnode failed");
 | 
						|
  } else if (count > rip->i_count) {
 | 
						|
	printf("%s:%d put_inode: count too high: %d > %d\n", __FILE__,
 | 
						|
		__LINE__, count, rip->i_count);
 | 
						|
	panic("fs_putnode failed");
 | 
						|
  }
 | 
						|
 | 
						|
  /* Decrease reference counter, but keep one reference;
 | 
						|
   * it will be consumed by put_inode().
 | 
						|
   */
 | 
						|
  rip->i_count -= count - 1;
 | 
						|
  put_inode(rip);
 | 
						|
 | 
						|
  return(OK);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                init_inode_cache                                           *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void init_inode_cache()
 | 
						|
{
 | 
						|
  struct inode *rip;
 | 
						|
  struct inodelist *rlp;
 | 
						|
 | 
						|
  inode_cache_hit = 0;
 | 
						|
  inode_cache_miss = 0;
 | 
						|
 | 
						|
  /* init free/unused list */
 | 
						|
  TAILQ_INIT(&unused_inodes);
 | 
						|
 | 
						|
  /* init hash lists */
 | 
						|
  for (rlp = &hash_inodes[0]; rlp < &hash_inodes[INODE_HASH_SIZE]; ++rlp)
 | 
						|
	LIST_INIT(rlp);
 | 
						|
 | 
						|
  /* add free inodes to unused/free list */
 | 
						|
  for (rip = &inode[0]; rip < &inode[NR_INODES]; ++rip) {
 | 
						|
	rip->i_num = NO_ENTRY;
 | 
						|
	TAILQ_INSERT_HEAD(&unused_inodes, rip, i_unused);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                addhash_inode                                              *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void addhash_inode(struct inode *node)
 | 
						|
{
 | 
						|
  int hashi = node->i_num & INODE_HASH_MASK;
 | 
						|
 | 
						|
  /* insert into hash table */
 | 
						|
  LIST_INSERT_HEAD(&hash_inodes[hashi], node, i_hash);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                unhash_inode                                               *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void unhash_inode(struct inode *node)
 | 
						|
{
 | 
						|
  /* remove from hash table */
 | 
						|
  LIST_REMOVE(node, i_hash);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                get_inode                                                  *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC struct inode *get_inode(
 | 
						|
  dev_t dev,          /* device on which inode resides */
 | 
						|
  ino_t numb            /* inode number (ANSI: may not be unshort) */
 | 
						|
)
 | 
						|
{
 | 
						|
/* Find the inode in the hash table. If it is not there, get a free inode
 | 
						|
 * load it from the disk if it's necessary and put on the hash list
 | 
						|
 */
 | 
						|
  register struct inode *rip;
 | 
						|
  int hashi;
 | 
						|
  int i;
 | 
						|
 | 
						|
  hashi = (int) numb & INODE_HASH_MASK;
 | 
						|
 | 
						|
  /* Search inode in the hash table */
 | 
						|
  LIST_FOREACH(rip, &hash_inodes[hashi], i_hash) {
 | 
						|
	if (rip->i_num == numb && rip->i_dev == dev) {
 | 
						|
		/* If unused, remove it from the unused/free list */
 | 
						|
		if (rip->i_count == 0) {
 | 
						|
			inode_cache_hit++;
 | 
						|
			TAILQ_REMOVE(&unused_inodes, rip, i_unused);
 | 
						|
		}
 | 
						|
		++rip->i_count;
 | 
						|
		return(rip);
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  inode_cache_miss++;
 | 
						|
 | 
						|
  /* Inode is not on the hash, get a free one */
 | 
						|
  if (TAILQ_EMPTY(&unused_inodes)) {
 | 
						|
	err_code = ENFILE;
 | 
						|
	return(NULL);
 | 
						|
  }
 | 
						|
  rip = TAILQ_FIRST(&unused_inodes);
 | 
						|
 | 
						|
  /* If not free unhash it */
 | 
						|
  if (rip->i_num != NO_ENTRY)
 | 
						|
	unhash_inode(rip);
 | 
						|
 | 
						|
  /* Inode is not unused any more */
 | 
						|
  TAILQ_REMOVE(&unused_inodes, rip, i_unused);
 | 
						|
 | 
						|
  /* Load the inode. */
 | 
						|
  rip->i_dev = dev;
 | 
						|
  rip->i_num = numb;
 | 
						|
  rip->i_count = 1;
 | 
						|
  if (dev != NO_DEV)
 | 
						|
	rw_inode(rip, READING);    /* get inode from disk */
 | 
						|
  rip->i_update = 0;        /* all the times are initially up-to-date */
 | 
						|
  rip->i_last_dpos = 0;     /* no dentries searched for yet */
 | 
						|
  rip->i_bsearch = NO_BLOCK;
 | 
						|
  rip->i_last_pos_bl_alloc = 0;
 | 
						|
  rip->i_last_dentry_size = 0;
 | 
						|
  rip->i_mountpoint= FALSE;
 | 
						|
 | 
						|
  rip->i_preallocation = opt.use_prealloc;
 | 
						|
  rip->i_prealloc_count = rip->i_prealloc_index = 0;
 | 
						|
 | 
						|
  for (i = 0; i < EXT2_PREALLOC_BLOCKS; i++) {
 | 
						|
	if (rip->i_prealloc_blocks[i] != NO_BLOCK) {
 | 
						|
		/* Actually this should never happen */
 | 
						|
		free_block(rip->i_sp, rip->i_prealloc_blocks[i]);
 | 
						|
		rip->i_prealloc_blocks[i] = NO_BLOCK;
 | 
						|
		ext2_debug("Warning: Unexpected preallocated block.");
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  /* Add to hash */
 | 
						|
  addhash_inode(rip);
 | 
						|
 | 
						|
  return(rip);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                find_inode                                                 *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC struct inode *find_inode(
 | 
						|
  dev_t dev,          /* device on which inode resides */
 | 
						|
  ino_t numb            /* inode number (ANSI: may not be unshort) */
 | 
						|
)
 | 
						|
{
 | 
						|
/* Find the inode specified by the inode and device number. */
 | 
						|
  struct inode *rip;
 | 
						|
  int hashi;
 | 
						|
 | 
						|
  hashi = (int) numb & INODE_HASH_MASK;
 | 
						|
 | 
						|
  /* Search inode in the hash table */
 | 
						|
  LIST_FOREACH(rip, &hash_inodes[hashi], i_hash) {
 | 
						|
	if (rip->i_count > 0 && rip->i_num == numb && rip->i_dev == dev) {
 | 
						|
		return(rip);
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  return(NULL);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                put_inode                                                  *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void put_inode(
 | 
						|
  register struct inode *rip     /* pointer to inode to be released */
 | 
						|
)
 | 
						|
{
 | 
						|
/* The caller is no longer using this inode. If no one else is using it either
 | 
						|
 * write it back to the disk immediately. If it has no links, truncate it and
 | 
						|
 * return it to the pool of available inodes.
 | 
						|
 */
 | 
						|
 | 
						|
  if (rip == NULL)
 | 
						|
	return;    /* checking here is easier than in caller */
 | 
						|
 | 
						|
  if (rip->i_count < 1)
 | 
						|
	panic("put_inode: i_count already below 1", rip->i_count);
 | 
						|
 | 
						|
  if (--rip->i_count == 0) {    /* i_count == 0 means no one is using it now */
 | 
						|
	if (rip->i_links_count == NO_LINK) {
 | 
						|
		/* i_nlinks == NO_LINK means free the inode. */
 | 
						|
		/* return all the disk blocks */
 | 
						|
 | 
						|
		/* Ignore errors by truncate_inode in case inode is a block
 | 
						|
		 * special or character special file.
 | 
						|
		 */
 | 
						|
		(void) truncate_inode(rip, (off_t) 0);
 | 
						|
		/* free inode clears I_TYPE field, since it's used there */
 | 
						|
		rip->i_dirt = DIRTY;
 | 
						|
		free_inode(rip);
 | 
						|
	}
 | 
						|
 | 
						|
	rip->i_mountpoint = FALSE;
 | 
						|
	if (rip->i_dirt == DIRTY) rw_inode(rip, WRITING);
 | 
						|
 | 
						|
	discard_preallocated_blocks(rip); /* Return blocks to the filesystem */
 | 
						|
 | 
						|
	if (rip->i_links_count == NO_LINK) {
 | 
						|
		/* free, put at the front of the LRU list */
 | 
						|
		unhash_inode(rip);
 | 
						|
		rip->i_num = NO_ENTRY;
 | 
						|
		TAILQ_INSERT_HEAD(&unused_inodes, rip, i_unused);
 | 
						|
	} else {
 | 
						|
		/* unused, put at the back of the LRU (cache it) */
 | 
						|
		TAILQ_INSERT_TAIL(&unused_inodes, rip, i_unused);
 | 
						|
	}
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                update_times                                               *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void update_times(
 | 
						|
  register struct inode *rip     /* pointer to inode to be read/written */
 | 
						|
)
 | 
						|
{
 | 
						|
/* Various system calls are required by the standard to update atime, ctime,
 | 
						|
 * or mtime.  Since updating a time requires sending a message to the clock
 | 
						|
 * task--an expensive business--the times are marked for update by setting
 | 
						|
 * bits in i_update. When a stat, fstat, or sync is done, or an inode is
 | 
						|
 * released, update_times() may be called to actually fill in the times.
 | 
						|
 */
 | 
						|
 | 
						|
  time_t cur_time;
 | 
						|
  struct super_block *sp;
 | 
						|
 | 
						|
  sp = rip->i_sp;         /* get pointer to super block. */
 | 
						|
  if (sp->s_rd_only)
 | 
						|
	return;             /* no updates for read-only file systems */
 | 
						|
 | 
						|
  cur_time = clock_time();
 | 
						|
  if (rip->i_update & ATIME)
 | 
						|
	rip->i_atime = cur_time;
 | 
						|
  if (rip->i_update & CTIME)
 | 
						|
	rip->i_ctime = cur_time;
 | 
						|
  if (rip->i_update & MTIME)
 | 
						|
	rip->i_mtime = cur_time;
 | 
						|
  rip->i_update = 0;          /* they are all up-to-date now */
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                rw_inode                                                   *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void rw_inode(
 | 
						|
  register struct inode *rip,         /* pointer to inode to be read/written */
 | 
						|
  int rw_flag                         /* READING or WRITING */
 | 
						|
)
 | 
						|
{
 | 
						|
/* An entry in the inode table is to be copied to or from the disk. */
 | 
						|
 | 
						|
  register struct buf *bp;
 | 
						|
  register struct super_block *sp;
 | 
						|
  register struct group_desc *gd;
 | 
						|
  register d_inode *dip;
 | 
						|
  u32_t block_group_number;
 | 
						|
  block_t b, offset;
 | 
						|
 | 
						|
  /* Get the block where the inode resides. */
 | 
						|
  sp = get_super(rip->i_dev);     /* get pointer to super block */
 | 
						|
  rip->i_sp = sp;        /* inode must contain super block pointer */
 | 
						|
 | 
						|
  block_group_number = (rip->i_num - 1) / sp->s_inodes_per_group;
 | 
						|
 | 
						|
  gd = get_group_desc(block_group_number);
 | 
						|
 | 
						|
  if (gd == NULL)
 | 
						|
	panic("can't get group_desc to read/write inode");
 | 
						|
 | 
						|
  offset = ((rip->i_num - 1) % sp->s_inodes_per_group) * EXT2_INODE_SIZE(sp);
 | 
						|
  /* offset requires shifting, since each block contains several inodes,
 | 
						|
   * e.g. inode 2 is stored in bklock 0.
 | 
						|
   */
 | 
						|
  b = (block_t) gd->inode_table + (offset >> sp->s_blocksize_bits);
 | 
						|
  bp = get_block(rip->i_dev, b, NORMAL);
 | 
						|
 | 
						|
  offset &= (sp->s_block_size - 1);
 | 
						|
  dip = (d_inode*) (bp->b_data + offset);
 | 
						|
 | 
						|
  /* Do the read or write. */
 | 
						|
  if (rw_flag == WRITING) {
 | 
						|
	if (rip->i_update)
 | 
						|
		update_times(rip);    /* times need updating */
 | 
						|
	if (sp->s_rd_only == FALSE)
 | 
						|
		bp->b_dirt = DIRTY;
 | 
						|
  }
 | 
						|
 | 
						|
  icopy(rip, dip, rw_flag, TRUE);
 | 
						|
 | 
						|
  put_block(bp, INODE_BLOCK);
 | 
						|
  rip->i_dirt = CLEAN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				icopy					     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE void icopy(
 | 
						|
  register struct inode *rip,	/* pointer to the in-core inode struct */
 | 
						|
  register d_inode *dip,	/* pointer to the on-disk struct */
 | 
						|
  int direction,		/* READING (from disk) or WRITING (to disk) */
 | 
						|
  int norm			/* TRUE = do not swap bytes; FALSE = swap */
 | 
						|
)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  if (direction == READING) {
 | 
						|
	/* Copy inode to the in-core table, swapping bytes if need be. */
 | 
						|
	rip->i_mode    = conv2(norm,dip->i_mode);
 | 
						|
	rip->i_uid     = conv2(norm,dip->i_uid);
 | 
						|
	rip->i_size    = conv4(norm,dip->i_size);
 | 
						|
	rip->i_atime   = conv4(norm,dip->i_atime);
 | 
						|
	rip->i_ctime   = conv4(norm,dip->i_ctime);
 | 
						|
	rip->i_mtime   = conv4(norm,dip->i_mtime);
 | 
						|
	rip->i_dtime   = conv4(norm,dip->i_dtime);
 | 
						|
	rip->i_gid     = conv2(norm,dip->i_gid);
 | 
						|
	rip->i_links_count  = conv2(norm,dip->i_links_count);
 | 
						|
	rip->i_blocks	= conv4(norm,dip->i_blocks);
 | 
						|
	rip->i_flags	= conv4(norm,dip->i_flags);
 | 
						|
	/* Minix doesn't touch osd1 and osd2 either, so just copy. */
 | 
						|
	memcpy(&rip->osd1, &dip->osd1, sizeof(rip->osd1));
 | 
						|
	for (i = 0; i < EXT2_N_BLOCKS; i++)
 | 
						|
		rip->i_block[i] = conv4(norm, dip->i_block[i]);
 | 
						|
	rip->i_generation = conv4(norm,dip->i_generation);
 | 
						|
	rip->i_file_acl	= conv4(norm,dip->i_file_acl);
 | 
						|
	rip->i_dir_acl  = conv4(norm,dip->i_dir_acl);
 | 
						|
	rip->i_faddr	= conv4(norm,dip->i_faddr);
 | 
						|
	memcpy(&rip->osd2, &dip->osd2, sizeof(rip->osd2));
 | 
						|
  } else {
 | 
						|
	/* Copying inode to disk from the in-core table. */
 | 
						|
	dip->i_mode    = conv2(norm,rip->i_mode);
 | 
						|
	dip->i_uid     = conv2(norm,rip->i_uid);
 | 
						|
	dip->i_size    = conv4(norm,rip->i_size);
 | 
						|
	dip->i_atime   = conv4(norm,rip->i_atime);
 | 
						|
	dip->i_ctime   = conv4(norm,rip->i_ctime);
 | 
						|
	dip->i_mtime   = conv4(norm,rip->i_mtime);
 | 
						|
	dip->i_dtime   = conv4(norm,rip->i_dtime);
 | 
						|
	dip->i_gid     = conv2(norm,rip->i_gid);
 | 
						|
	dip->i_links_count  = conv2(norm,rip->i_links_count);
 | 
						|
	dip->i_blocks	= conv4(norm,rip->i_blocks);
 | 
						|
	dip->i_flags	= conv4(norm,rip->i_flags);
 | 
						|
	/* Minix doesn't touch osd1 and osd2 either, so just copy. */
 | 
						|
	memcpy(&dip->osd1, &rip->osd1, sizeof(dip->osd1));
 | 
						|
	for (i = 0; i < EXT2_N_BLOCKS; i++)
 | 
						|
		dip->i_block[i] = conv4(norm, rip->i_block[i]);
 | 
						|
	dip->i_generation  = conv4(norm,rip->i_generation);
 | 
						|
	dip->i_file_acl = conv4(norm,rip->i_file_acl);
 | 
						|
	dip->i_dir_acl	= conv4(norm,rip->i_dir_acl);
 | 
						|
	dip->i_faddr	= conv4(norm,rip->i_faddr);
 | 
						|
	memcpy(&dip->osd2, &rip->osd2, sizeof(dip->osd2));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                dup_inode                                                  *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC void dup_inode(
 | 
						|
  struct inode *ip         /* The inode to be duplicated. */
 | 
						|
)
 | 
						|
{
 | 
						|
/* This routine is a simplified form of get_inode() for the case where
 | 
						|
 * the inode pointer is already known.
 | 
						|
 */
 | 
						|
  ip->i_count++;
 | 
						|
}
 |