summaryrefslogtreecommitdiffstats
path: root/sys/gnu/fs/ext2fs/ext2_linux_ialloc.c
diff options
context:
space:
mode:
authordyson <dyson@FreeBSD.org>1995-11-05 23:25:12 +0000
committerdyson <dyson@FreeBSD.org>1995-11-05 23:25:12 +0000
commit65a271ba6d03e08c6a9486e645d658f5a04f03a6 (patch)
tree3c31656291a1b60c5b4c604354e037da124df62e /sys/gnu/fs/ext2fs/ext2_linux_ialloc.c
parentb345e18c9359a0789ec4ae3961566e3a7925d481 (diff)
downloadFreeBSD-src-65a271ba6d03e08c6a9486e645d658f5a04f03a6.zip
FreeBSD-src-65a271ba6d03e08c6a9486e645d658f5a04f03a6.tar.gz
Main code for the ext2fs filesystem. Please refer to the COPYRIGHT.INFO
file for GPL restrictions. This code was ported to the BSD platform by Godmar Back <gback@facility.cs.utah.edu> and specifically to FreeBSD by John Dyson. This code is still green and should be used with caution. Additional changes to UFS necessary to make this code work will be commited seperately. Submitted by: Godmar Back <gback@facility.cs.utah.edu> Obtained from: Lites/Mach4
Diffstat (limited to 'sys/gnu/fs/ext2fs/ext2_linux_ialloc.c')
-rw-r--r--sys/gnu/fs/ext2fs/ext2_linux_ialloc.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/sys/gnu/fs/ext2fs/ext2_linux_ialloc.c b/sys/gnu/fs/ext2fs/ext2_linux_ialloc.c
new file mode 100644
index 0000000..62e7938
--- /dev/null
+++ b/sys/gnu/fs/ext2fs/ext2_linux_ialloc.c
@@ -0,0 +1,520 @@
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ */
+
+/*
+ * The free inodes are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext2_read_super).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <gnu/ext2fs/ext2_fs.h>
+#include <gnu/ext2fs/ext2_fs_sb.h>
+#include <gnu/ext2fs/fs.h>
+#include <sys/stat.h>
+
+#if (i386)
+#include <gnu/ext2fs/i386-bitops.h>
+#else
+#error please provide bit operation functions
+#endif
+
+/* this is supposed to mark a buffer dirty on ready for delayed writing
+ */
+void mark_buffer_dirty(struct buf *bh)
+{
+ bh->b_flags |= B_DELWRI;
+ bh->b_flags &= ~(B_READ | B_ERROR);
+}
+
+/*
+ this should write a buffer immediately w/o releasing it
+ */
+int ll_w_block(struct buf * bp, int waitfor)
+{
+ bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI);
+ bp->b_flags |= B_WRITEINPROG;
+ bp->b_vp->v_numoutput++;
+#if defined(__FreeBSD__)
+ vfs_busy_pages(bp, 1);
+#endif
+ VOP_STRATEGY(bp);
+ return waitfor ? biowait(bp) : 0;
+}
+
+struct ext2_group_desc * get_group_desc (struct mount * mp,
+ unsigned int block_group,
+ struct buffer_head ** bh)
+{
+ struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs;
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext2_group_desc * gdp;
+
+ if (block_group >= sb->s_groups_count)
+ panic ("get_group_desc: "
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->s_groups_count);
+
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ if (!sb->s_group_desc[group_desc])
+ panic ( "get_group_desc:"
+ "Group descriptor not loaded - "
+ "block_group = %d, group_desc = %lu, desc = %lu",
+ block_group, group_desc, desc);
+ gdp = (struct ext2_group_desc *)
+ sb->s_group_desc[group_desc]->b_data;
+ if (bh)
+ *bh = sb->s_group_desc[group_desc];
+ return gdp + desc;
+}
+
+static void read_inode_bitmap (struct mount * mp,
+ unsigned long block_group,
+ unsigned int bitmap_nr)
+{
+ struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs;
+ struct ext2_group_desc * gdp;
+ struct buffer_head * bh;
+ int error;
+
+ gdp = get_group_desc (mp, block_group, NULL);
+ if (error = bread (VFSTOUFS(mp)->um_devvp,
+ fsbtodb(sb, gdp->bg_inode_bitmap),
+ sb->s_blocksize,
+ NOCRED, &bh))
+ panic ( "read_inode_bitmap:"
+ "Cannot read inode bitmap - "
+ "block_group = %lu, inode_bitmap = %lu",
+ block_group, (unsigned long) gdp->bg_inode_bitmap);
+ sb->s_inode_bitmap_number[bitmap_nr] = block_group;
+ sb->s_inode_bitmap[bitmap_nr] = bh;
+}
+
+/*
+ * load_inode_bitmap loads the inode bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ */
+static int load_inode_bitmap (struct mount * mp,
+ unsigned int block_group)
+{
+ struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs;
+ int i, j;
+ unsigned long inode_bitmap_number;
+ struct buffer_head * inode_bitmap;
+
+ if (block_group >= sb->s_groups_count)
+ panic ("load_inode_bitmap:"
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->s_groups_count);
+ if (sb->s_loaded_inode_bitmaps > 0 &&
+ sb->s_inode_bitmap_number[0] == block_group)
+ return 0;
+ if (sb->s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->s_inode_bitmap[block_group]) {
+ if (sb->s_inode_bitmap_number[block_group] !=
+ block_group)
+ panic ( "load_inode_bitmap:"
+ "block_group != inode_bitmap_number");
+ else
+ return block_group;
+ } else {
+ read_inode_bitmap (mp, block_group, block_group);
+ return block_group;
+ }
+ }
+
+ for (i = 0; i < sb->s_loaded_inode_bitmaps &&
+ sb->s_inode_bitmap_number[i] != block_group;
+ i++)
+ ;
+ if (i < sb->s_loaded_inode_bitmaps &&
+ sb->s_inode_bitmap_number[i] == block_group) {
+ inode_bitmap_number = sb->s_inode_bitmap_number[i];
+ inode_bitmap = sb->s_inode_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sb->s_inode_bitmap_number[j] =
+ sb->s_inode_bitmap_number[j - 1];
+ sb->s_inode_bitmap[j] =
+ sb->s_inode_bitmap[j - 1];
+ }
+ sb->s_inode_bitmap_number[0] = inode_bitmap_number;
+ sb->s_inode_bitmap[0] = inode_bitmap;
+ } else {
+ if (sb->s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->s_loaded_inode_bitmaps++;
+ else
+ brelse (sb->s_inode_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
+ for (j = sb->s_loaded_inode_bitmaps - 1; j > 0; j--) {
+ sb->s_inode_bitmap_number[j] =
+ sb->s_inode_bitmap_number[j - 1];
+ sb->s_inode_bitmap[j] =
+ sb->s_inode_bitmap[j - 1];
+ }
+ read_inode_bitmap (mp, block_group, 0);
+ }
+ return 0;
+}
+
+
+void ext2_free_inode (struct inode * inode)
+{
+ struct ext2_sb_info * sb;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ if (!inode)
+ return;
+
+ if (inode->i_nlink) {
+ printf ("ext2_free_inode: inode has nlink=%d\n",
+ inode->i_nlink);
+ return;
+ }
+
+ ext2_debug ("freeing inode %lu\n", inode->i_number);
+
+ sb = inode->i_e2fs;
+ lock_super (DEVVP(inode));
+ if (inode->i_number < EXT2_FIRST_INO ||
+ inode->i_number > sb->s_es->s_inodes_count) {
+ printf ("free_inode reserved inode or nonexistent inode");
+ unlock_super (DEVVP(inode));
+ return;
+ }
+ es = sb->s_es;
+ block_group = (inode->i_number - 1) / EXT2_INODES_PER_GROUP(sb);
+ bit = (inode->i_number - 1) % EXT2_INODES_PER_GROUP(sb);
+ bitmap_nr = load_inode_bitmap (ITOV(inode)->v_mount, block_group);
+ bh = sb->s_inode_bitmap[bitmap_nr];
+ if (!clear_bit (bit, bh->b_data))
+ printf ( "ext2_free_inode:"
+ "bit already cleared for inode %lu", inode->i_number);
+ else {
+ gdp = get_group_desc (ITOV(inode)->v_mount, block_group, &bh2);
+ gdp->bg_free_inodes_count++;
+ if (S_ISDIR(inode->i_mode))
+ gdp->bg_used_dirs_count--;
+ mark_buffer_dirty(bh2);
+ es->s_free_inodes_count++;
+ }
+ mark_buffer_dirty(bh);
+/*** XXX
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+***/
+ sb->s_dirt = 1;
+ unlock_super (DEVVP(inode));
+}
+
+#if linux
+/*
+ * This function increments the inode version number
+ *
+ * This may be used one day by the NFS server
+ */
+static void inc_inode_version (struct inode * inode,
+ struct ext2_group_desc *gdp,
+ int mode)
+{
+ unsigned long inode_block;
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+
+ inode_block = gdp->bg_inode_table + (((inode->i_number - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) /
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
+ if (!bh) {
+ printf ("inc_inode_version Cannot load inode table block - "
+ "inode=%lu, inode_block=%lu\n",
+ inode->i_number, inode_block);
+ inode->u.ext2_i.i_version = 1;
+ return;
+ }
+ raw_inode = ((struct ext2_inode *) bh->b_data) +
+ (((inode->i_number - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) %
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ raw_inode->i_version++;
+ inode->u.ext2_i.i_version = raw_inode->i_version;
+ mark_buffer_dirty(bh, 1);
+ brelse (bh);
+}
+
+#endif /* linux */
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory\'s block
+ * group to find a free inode.
+ */
+/*
+ * this functino has been reduced to the actual 'find the inode number' part
+ */
+ino_t ext2_new_inode (const struct inode * dir, int mode)
+{
+ struct ext2_sb_info * sb;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ int i, j, avefreei;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_group_desc * tmp;
+ struct ext2_super_block * es;
+
+ if (!dir)
+ return 0;
+ sb = dir->i_e2fs;
+
+ lock_super (DEVVP(dir));
+ es = sb->s_es;
+repeat:
+ gdp = NULL; i=0;
+
+ if (S_ISDIR(mode)) {
+ avefreei = es->s_free_inodes_count /
+ sb->s_groups_count;
+/* I am not yet convinced that this next bit is necessary.
+ i = dir->u.ext2_i.i_block_group;
+ for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
+ tmp = get_group_desc (sb, i, &bh2);
+ if ((tmp->bg_used_dirs_count << 8) <
+ tmp->bg_free_inodes_count) {
+ gdp = tmp;
+ break;
+ }
+ else
+ i = ++i % sb->u.ext2_sb.s_groups_count;
+ }
+*/
+ if (!gdp) {
+ for (j = 0; j < sb->s_groups_count; j++) {
+ tmp = get_group_desc(ITOV(dir)->v_mount,j,&bh2);
+ if (tmp->bg_free_inodes_count &&
+ tmp->bg_free_inodes_count >= avefreei) {
+ if (!gdp ||
+ (tmp->bg_free_blocks_count >
+ gdp->bg_free_blocks_count)) {
+ i = j;
+ gdp = tmp;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Try to place the inode in its parent directory
+ */
+ i = dir->i_block_group;
+ tmp = get_group_desc (ITOV(dir)->v_mount, i, &bh2);
+ if (tmp->bg_free_inodes_count)
+ gdp = tmp;
+ else
+ {
+ /*
+ * Use a quadratic hash to find a group with a
+ * free inode
+ */
+ for (j = 1; j < sb->s_groups_count; j <<= 1) {
+ i += j;
+ if (i >= sb->s_groups_count)
+ i -= sb->s_groups_count;
+ tmp = get_group_desc(ITOV(dir)->v_mount,i,&bh2);
+ if (tmp->bg_free_inodes_count) {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ if (!gdp) {
+ /*
+ * That failed: try linear search for a free inode
+ */
+ i = dir->i_block_group + 1;
+ for (j = 2; j < sb->s_groups_count; j++) {
+ if (++i >= sb->s_groups_count)
+ i = 0;
+ tmp = get_group_desc(ITOV(dir)->v_mount,i,&bh2);
+ if (tmp->bg_free_inodes_count) {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!gdp) {
+ unlock_super (DEVVP(dir));
+ return 0;
+ }
+ bitmap_nr = load_inode_bitmap (ITOV(dir)->v_mount, i);
+ bh = sb->s_inode_bitmap[bitmap_nr];
+ if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
+ EXT2_INODES_PER_GROUP(sb))) <
+ EXT2_INODES_PER_GROUP(sb)) {
+ if (set_bit (j, bh->b_data)) {
+ printf ( "ext2_new_inode:"
+ "bit already set for inode %d", j);
+ goto repeat;
+ }
+/* Linux now does the following:
+ mark_buffer_dirty(bh, 1);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+*/
+ mark_buffer_dirty(bh);
+ } else {
+ if (gdp->bg_free_inodes_count != 0) {
+ printf ( "ext2_new_inode:"
+ "Free inodes count corrupted in group %d",
+ i);
+ unlock_super (DEVVP(dir));
+ return 0;
+ }
+ goto repeat;
+ }
+ j += i * EXT2_INODES_PER_GROUP(sb) + 1;
+ if (j < EXT2_FIRST_INO || j > es->s_inodes_count) {
+ printf ( "ext2_new_inode:"
+ "reserved inode or inode > inodes count - "
+ "block_group = %d,inode=%d", i, j);
+ unlock_super (DEVVP(dir));
+ return 0;
+ }
+ gdp->bg_free_inodes_count--;
+ if (S_ISDIR(mode))
+ gdp->bg_used_dirs_count++;
+ mark_buffer_dirty(bh2);
+ es->s_free_inodes_count--;
+ /* mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); */
+ sb->s_dirt = 1;
+ unlock_super (DEVVP(dir));
+ return j;
+}
+
+unsigned long ext2_count_free_inodes (struct mount * mp)
+{
+#ifdef EXT2FS_DEBUG
+ struct ext2_sb_info *sb = VFSTOUFS(mp)->um_e2fs;
+ struct ext2_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ lock_super (VFSTOUFS(mp)->um_devvp);
+ es = sb->s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->s_groups_count; i++) {
+ gdp = get_group_desc (mp, i, NULL);
+ desc_count += gdp->bg_free_inodes_count;
+ bitmap_nr = load_inode_bitmap (mp, i);
+ x = ext2_count_free (sb->s_inode_bitmap[bitmap_nr],
+ EXT2_INODES_PER_GROUP(sb) / 8);
+ ext2_debug ("group %d: stored = %d, counted = %lu\n",
+ i, gdp->bg_free_inodes_count, x);
+ bitmap_count += x;
+ }
+ ext2_debug("stored = %lu, computed = %lu, %lu\n",
+ es->s_free_inodes_count, desc_count, bitmap_count);
+ unlock_super (VFSTOUFS(mp)->um_devvp);
+ return desc_count;
+#else
+ return VFSTOUFS(mp)->um_e2fsb->s_free_inodes_count;
+#endif
+}
+
+#ifdef LATER
+void ext2_check_inodes_bitmap (struct mount * mp)
+{
+ struct ext2_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ gdp = get_group_desc (sb, i, NULL);
+ desc_count += gdp->bg_free_inodes_count;
+ bitmap_nr = load_inode_bitmap (sb, i);
+ x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
+ EXT2_INODES_PER_GROUP(sb) / 8);
+ if (gdp->bg_free_inodes_count != x)
+ printf ( "ext2_check_inodes_bitmap:"
+ "Wrong free inodes count in group %d, "
+ "stored = %d, counted = %lu", i,
+ gdp->bg_free_inodes_count, x);
+ bitmap_count += x;
+ }
+ if (es->s_free_inodes_count != bitmap_count)
+ printf ( "ext2_check_inodes_bitmap:"
+ "Wrong free inodes count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long) es->s_free_inodes_count, bitmap_count);
+ unlock_super (sb);
+}
+#endif
+
OpenPOWER on IntegriCloud